--- /dev/null
+++ networking/stun-ip.c
@@ -0,0 +1,161 @@
+/*
+ * Mini stun client implementation for busybox
+ * detect external address by doing a STUN request
+ * no attempt to detect NAT properties
+ *
+ * Copyright (C) 2011 by Ralf Friedl <Ralf.Friedl at online.de>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this source tree.
+ */
+#include "libbb.h"
+
+//config:config STUN_IP
+//config:	bool "stun-ip"
+//config:	default n
+//config:	help
+//config:	  determine external IP address via STUN
+//config:
+
+//applet:IF_STUN_IP(APPLET_ODDNAME(stun-ip, stun_ip, BB_DIR_USR_BIN, BB_SUID_DROP, stun_ip))
+//kbuild:lib-$(CONFIG_STUN_IP)  += stun-ip.o
+
+//usage:#define stun_ip_trivial_usage
+//usage:       "HOST"
+//usage:#define stun_ip_full_usage "\n\n"
+//usage:       "discover external address"
+
+
+// support for IPv6 is incomplete, and it is uncertain whether it is useful
+#define ENABLE_STUN_IPV6 0
+
+#if ENABLE_STUN_IPV6
+#define OPT_STRING
+enum {
+	OPT_IPV4 =  1 << 0,
+	OPT_IPV6 = (1 << 1),
+};
+#endif
+
+#define STUN_METHOD_BINDING		0x001
+
+#define STUN_ATTRIBUTE_MAPPEDADDRESS	0x0001
+
+#define STUN_AF_IPV4	0x01
+#define STUN_AF_IPV6	0x02
+
+struct stun_header {
+	uint16_t		type;
+	uint16_t		length;	// data without header
+	uint32_t		id[16 / sizeof (uint32_t)];
+};
+struct stun_attr {
+	uint16_t		type;
+	uint16_t		length;	// data without header
+	char			data[0];
+};
+struct stun_message {
+	struct stun_header	header;
+	struct stun_attr	data[0];
+};
+
+struct stun_address {
+	uint8_t			unused_0;
+	uint8_t			family;
+	uint16_t		port;
+	union {
+		struct in_addr	addr4;
+		struct in6_addr	addr6;
+	};
+	
+};
+
+int stun_ip_main(int argc UNUSED_PARAM, char **argv);
+int stun_ip_main(int argc UNUSED_PARAM, char **argv)
+{
+#if ENABLE_STUN_IPV6
+	int opt;
+#endif
+	sa_family_t af;
+	u_short port = 3478;	// nat-stun-port
+	int retry, max_retry = 3;
+	int i, fd;
+	int delay, max_delay, poll_delay;
+	struct stun_header	stun_req; // header with no extra data
+	struct pollfd pfd[1];
+	len_and_sockaddr *lsa;
+
+	//port = bb_lookup_port("nat-stun-port", "udp", 3478);
+	port = 3478;	// nat-stun-port
+	max_delay = 1000; // in ms
+	poll_delay = 100; // in ms
+
+	af = AF_UNSPEC;
+#if ENABLE_STUN_IPV6
+	opt = getopt32(argv, "^" "46" "\0" "=1");
+	if (opt & OPT_IPV4)
+		af = AF_INET;
+	if (opt & OPT_IPV6)
+		af = AF_INET6;
+#endif
+	(void)af;		// Avoid compiler warning
+
+	// create stun request
+	stun_req.type = htons (STUN_METHOD_BINDING);
+	stun_req.length = htons (0);	// no extra data
+	for (i = 0; i < sizeof (stun_req.id) / sizeof (*stun_req.id); i++)
+		stun_req.id[i] = random ();
+	//
+	lsa = xhost_and_af2sockaddr(argv[optind], port, af);
+	fd = xsocket(lsa->u.sa.sa_family, SOCK_DGRAM, 0);
+	for (delay = 0, retry = 0; delay < max_delay; delay += poll_delay, retry++) {
+		union {
+			struct stun_message	stun_msg;
+			char			data[0x1000];
+		} stun_resp;
+		char *cp, *ep;
+		int ret;
+
+		if (retry < max_retry)
+			xsendto (fd, &stun_req, sizeof (stun_req), &lsa->u.sa, lsa->len);
+		pfd[0].fd = fd;
+		pfd[0].events = POLLIN;
+		ret = poll (pfd, 1, poll_delay);
+		if (ret <= 0) {
+			//perror ("select");
+			continue;
+		}
+		ret = recv (fd, stun_resp.data, sizeof (stun_resp), 0);
+		if (ret <= 0) {
+			//perror ("recv");
+			continue;
+		}
+		cp = (char *)&stun_resp.stun_msg.data; // start of data
+		ep = (char *)stun_resp.data + ret;     // end of data
+		for (;;) {
+			struct stun_attr *stun_attr = (struct stun_attr *)cp;
+			u_short type = ntohs (stun_attr->type);
+			u_short length = ntohs (stun_attr->length);
+			cp += sizeof (struct stun_attr) + ((length + 3) & ~3);
+			if (cp > ep)
+				break;
+			if (type == STUN_ATTRIBUTE_MAPPEDADDRESS) {
+				struct stun_address *stun_addr = (struct stun_address *)stun_attr->data;
+				if (stun_addr->family == STUN_AF_IPV4) {
+					printf ("%s\n", inet_ntoa (stun_addr->addr4));
+					return 0;
+				}
+#if ENABLE_STUN_IPV6
+				if (stun_addr->family == STUN_AF_IPV6) {
+					char straddr[INET6_ADDRSTRLEN];
+					inet_ntop(AF_INET6, &stun_addr->addr6, straddr, sizeof(straddr));
+					printf ("%s\n", straddr);
+					return 0;
+				}
+#endif
+			}
+		}
+	}
+	if (ENABLE_FEATURE_CLEAN_UP)
+		close (fd);
+	return 1;
+}
