iproxy: Allow specifying source address for the listening socket
diff --git a/common/socket.c b/common/socket.c
index a47de2a..1c38965 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -255,7 +255,7 @@
 }
 #endif
 
-int socket_create(uint16_t port)
+int socket_create(const char* addr, uint16_t port)
 {
 	int sfd = -1;
 	int yes = 1;
@@ -269,9 +269,79 @@
 		wsa_init = 1;
 	}
 #endif
-	struct sockaddr_in saddr;
+	struct sockaddr* srcaddr;
+	int srcaddr_len = 0;
+	int domain = PF_INET;
 
-	if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
+#if defined(AF_INET6)
+	struct sockaddr_in6 saddr6;
+	memset((void*) &saddr6, 0, sizeof(saddr6));
+	saddr6.sin6_family = AF_INET6;
+	saddr6.sin6_port = htons(port);
+	saddr6.sin6_addr = in6addr_loopback;
+#endif
+
+	struct sockaddr_in saddr;
+	memset((void*) &saddr, 0, sizeof(saddr));
+	saddr.sin_family = AF_INET;
+	saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+	saddr.sin_port = htons(port);
+
+	if (addr) {
+		if (strchr(addr, ':')) {
+#ifdef AF_INET6
+#ifdef WIN32
+			struct sockaddr_storage ss;
+			int ss_size = sizeof(ss);
+			if (WSAStringToAddress((LPSTR)addr, AF_INET6, NULL, (struct sockaddr*)&ss, &ss_size) == 0) {
+				memcpy(&(saddr6.sin6_addr), &(((struct sockaddr_in6*)&ss)->sin6_addr), sizeof(struct in6_addr));
+			} else
+#else
+			if (inet_pton(AF_INET6, addr, &(saddr6.sin6_addr)) != 1)
+#endif
+			{
+				fprintf(stderr, "FATAL: Failed to convert '%s' to an IPv6 address.\n", addr);
+				socket_close(sfd);
+				return -1;
+			}
+			srcaddr = (struct sockaddr*)&saddr6;
+			srcaddr_len = sizeof(saddr6);
+			domain = PF_INET6;
+#else
+			fprintf(stderr, "FATAL: Got IPv6 address but AF_INET6 is not supported.\n");
+			socket_close(sfd);
+			return -1;
+#endif
+		} else {
+#ifdef WIN32
+			struct sockaddr_storage ss;
+			int ss_size = sizeof(ss);
+			if (WSAStringToAddress((LPSTR)addr, AF_INET, NULL, (struct sockaddr*)&ss, &ss_size) == 0) {
+				saddr.sin_addr.s_addr = ((struct sockaddr_in*)&ss)->sin_addr.s_addr;
+			} else
+#else
+			if (inet_pton(AF_INET, addr, &(saddr.sin_addr)) != 1)
+#endif
+			{
+				fprintf(stderr, "FATAL: Failed to convert '%s' to an IPv4 address.\n", addr);
+				socket_close(sfd);
+				return -1;
+			}
+			srcaddr = (struct sockaddr*)&saddr;
+			srcaddr_len = sizeof(saddr);
+		}
+	} else {
+#if !defined(WIN32) && defined(AF_INET6)
+		srcaddr = (struct sockaddr*)&saddr6;
+		srcaddr_len = sizeof(saddr6);
+		domain = PF_INET6;
+#else
+		srcaddr = (struct sockaddr*)&saddr;
+		srcaddr_len = sizeof(saddr);
+#endif
+	}
+
+	if ((sfd = socket(domain, SOCK_STREAM, IPPROTO_TCP)) < 0) {
 		perror("socket()");
 		return -1;
 	}
@@ -290,18 +360,13 @@
 	}
 #endif
 
-	memset((void *) &saddr, 0, sizeof(saddr));
-	saddr.sin_family = AF_INET;
-	saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-	saddr.sin_port = htons(port);
-
-	if (0 > bind(sfd, (struct sockaddr *) &saddr, sizeof(saddr))) {
+	if (bind(sfd, srcaddr, srcaddr_len) < 0) {
 		perror("bind()");
 		socket_close(sfd);
 		return -1;
 	}
 
-	if (listen(sfd, 1) == -1) {
+	if (listen(sfd, 1) < 0) {
 		perror("listen()");
 		socket_close(sfd);
 		return -1;
@@ -626,14 +691,13 @@
 	socklen_t addr_len;
 #endif
 	int result;
-	struct sockaddr_in addr;
-
-	memset(&addr, 0, sizeof(addr));
-	addr.sin_family = AF_INET;
-	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
-	addr.sin_port = htons(port);
-
+#ifdef AF_INET6
+	struct sockaddr_in6 addr;
 	addr_len = sizeof(addr);
+#else
+	struct sockaddr_in addr;
+	addr_len = sizeof(addr);
+#endif
 	result = accept(fd, (struct sockaddr*)&addr, &addr_len);
 
 	return result;
diff --git a/common/socket.h b/common/socket.h
index 38eeddf..f510147 100644
--- a/common/socket.h
+++ b/common/socket.h
@@ -45,7 +45,7 @@
 int socket_create_unix(const char *filename);
 int socket_connect_unix(const char *filename);
 #endif
-int socket_create(uint16_t port);
+int socket_create(const char *addr, uint16_t port);
 int socket_connect_addr(struct sockaddr *addr, uint16_t port);
 int socket_connect(const char *addr, uint16_t port);
 int socket_check_fd(int fd, fd_mode fdm, unsigned int timeout);
diff --git a/tools/iproxy.c b/tools/iproxy.c
index 8321143..e9524a4 100644
--- a/tools/iproxy.c
+++ b/tools/iproxy.c
@@ -220,6 +220,7 @@
 	  "  -u, --udid UDID    target specific device by UDID\n" \
 	  "  -n, --network      connect to network device\n" \
 	  "  -l, --local        connect to USB device (default)\n" \
+	  "  -s, --source ADDR  source address for listening socket (default 127.0.0.1)\n" \
 	  "  -h, --help         prints usage information\n" \
 	  "  -d, --debug        increase debug level\n" \
 	  "\n" \
@@ -233,6 +234,7 @@
 {
 	int mysock = -1;
 	char* device_udid = NULL;
+	char* source_addr = NULL;
 	uint16_t listen_port = 0;
 	uint16_t device_port = 0;
 	enum usbmux_lookup_options lookup_opts = 0;
@@ -243,10 +245,11 @@
 		{ "udid", required_argument, NULL, 'u' },
 		{ "local", no_argument, NULL, 'l' },
 		{ "network", no_argument, NULL, 'n' },
+		{ "source", required_argument, NULL, 's' },
 		{ NULL, 0, NULL, 0}
 	};
 	int c = 0;
-	while ((c = getopt_long(argc, argv, "dhu:ln", longopts, NULL)) != -1) {
+	while ((c = getopt_long(argc, argv, "dhu:lns:", longopts, NULL)) != -1) {
 		switch (c) {
 		case 'd':
 			libusbmuxd_set_debug_level(++debug_level);
@@ -266,6 +269,15 @@
 		case 'n':
 			lookup_opts |= DEVICE_LOOKUP_NETWORK;
 			break;
+		case 's':
+			if (!*optarg) {
+				fprintf(stderr, "ERROR: source address must not be empty!\n");
+				print_usage(argc, argv, 1);
+				return 2;
+			}
+			free(source_addr);
+			source_addr = strdup(optarg);
+			break;
 		case 'h':
 			print_usage(argc, argv, 0);
 			return 0;
@@ -307,7 +319,7 @@
 	signal(SIGPIPE, SIG_IGN);
 #endif
 	// first create the listening socket endpoint waiting for connections.
-	mysock = socket_create(listen_port);
+	mysock = socket_create(source_addr, listen_port);
 	if (mysock < 0) {
 		fprintf(stderr, "Error creating socket: %s\n", strerror(errno));
 		free(device_udid);
@@ -323,7 +335,10 @@
 		while (1) {
 			printf("waiting for connection\n");
 			c_sock = socket_accept(mysock, listen_port);
-			if (c_sock) {
+			if (c_sock < 0) {
+				fprintf(stderr, "accept: %s\n", strerror(errno));
+				break;
+			} else {
 				printf("accepted connection, fd = %d\n", c_sock);
 				cdata = (struct client_data*)malloc(sizeof(struct client_data));
 				if (!cdata) {
@@ -344,8 +359,6 @@
 				pthread_create(&acceptor, NULL, acceptor_thread, cdata);
 				pthread_detach(acceptor);
 #endif
-			} else {
-				break;
 			}
 		}
 		socket_close(c_sock);
@@ -353,6 +366,7 @@
 	}
 
 	free(device_udid);
+	free(source_addr);
 
 	return 0;
 }