Allow using non-standard usbmuxd socket address via environment variable

By using USBMUXD_SOCKET_ADDRESS environment variable, it is possible
to make libusbmuxd connect to the specified address. The value needs
to be in format ADDRESS:PORT (or UNIX:PATH on unix systems). If no port
number is specified or parsing fails, the standard socket address (or
unix domain socket file path) will be used silently.
diff --git a/common/socket.c b/common/socket.c
index 0fb4ba8..a2f8eec 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -1,8 +1,8 @@
 /*
  * socket.c
  *
+ * Copyright (C) 2012-2018 Nikias Bassen <nikias@gmx.li>
  * Copyright (C) 2012 Martin Szulecki <m.szulecki@libimobiledevice.org>
- * Copyright (C) 2012 Nikias Bassen <nikias@gmx.li>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -19,6 +19,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
 #include <stdio.h>
 #include <stddef.h>
 #include <stdlib.h>
@@ -29,6 +32,7 @@
 #include <sys/stat.h>
 #ifdef WIN32
 #include <winsock2.h>
+#include <ws2tcpip.h>
 #include <windows.h>
 static int wsa_init = 0;
 #else
@@ -38,10 +42,12 @@
 #include <netinet/tcp.h>
 #include <netdb.h>
 #include <arpa/inet.h>
+#include <fcntl.h>
 #endif
 #include "socket.h"
 
 #define RECV_TIMEOUT 20000
+#define CONNECT_TIMEOUT 5000
 
 static int verbose = 0;
 
@@ -82,7 +88,7 @@
 	strncpy(name.sun_path, filename, sizeof(name.sun_path));
 	name.sun_path[sizeof(name.sun_path) - 1] = '\0';
 
-	if (bind(sock, (struct sockaddr *) &name, sizeof(name)) < 0) {
+	if (bind(sock, (struct sockaddr*)&name, sizeof(name)) < 0) {
 		perror("bind");
 		socket_close(sock);
 		return -1;
@@ -143,7 +149,6 @@
 		return -1;
 	}
 #endif
-
 	// and connect to 'filename'
 	name.sun_family = AF_UNIX;
 	strncpy(name.sun_path, filename, sizeof(name.sun_path));
@@ -221,9 +226,13 @@
 	int sfd = -1;
 	int yes = 1;
 	int bufsize = 0x20000;
-	struct hostent *hp;
-	struct sockaddr_in saddr;
+	struct addrinfo hints;
+	struct addrinfo *result, *rp;
+	char portstr[8];
+	int res;
 #ifdef WIN32
+	u_long l_yes = 1;
+	u_long l_no = 0;
 	WSADATA wsa_data;
 	if (!wsa_init) {
 		if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) {
@@ -232,6 +241,8 @@
 		}
 		wsa_init = 1;
 	}
+#else
+	int flags = 0;
 #endif
 
 	if (!addr) {
@@ -239,29 +250,80 @@
 		return -1;
 	}
 
-	if ((hp = gethostbyname(addr)) == NULL) {
+	memset(&hints, '\0', sizeof(struct addrinfo));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = 0;
+	hints.ai_protocol = IPPROTO_TCP;
+
+	sprintf(portstr, "%d", port);
+
+	res = getaddrinfo(addr, portstr, &hints, &result);
+	if (res != 0) {
+		fprintf(stderr, "%s: getaddrinfo: %s\n", __func__, gai_strerror(res));
+		return -1;
+	}
+
+	for (rp = result; rp != NULL; rp = rp->ai_next) {
+		sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+		if (sfd == -1) {
+			continue;
+		}
+
+		if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
+			perror("setsockopt()");
+			close(sfd);
+			continue;
+		}
+
+#ifdef WIN32
+		ioctlsocket(sfd, FIONBIO, &l_yes);
+#else
+		fcntl(sfd, F_SETFL, O_NONBLOCK);
+#endif
+
+		if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1) {
+			break;
+		}
+#ifdef WIN32
+		if (WSAGetLastError() == WSAEWOULDBLOCK)
+#else
+		if (errno == EINPROGRESS)
+#endif
+		{
+			fd_set fds;
+			FD_ZERO(&fds);
+			FD_SET(sfd, &fds);
+
+			struct timeval timeout;
+			timeout.tv_sec = CONNECT_TIMEOUT / 1000;
+			timeout.tv_usec = (CONNECT_TIMEOUT - (timeout.tv_sec * 1000)) * 1000;
+			if (select(sfd + 1, NULL, &fds, NULL, &timeout) == 1) {
+				int so_error;
+				socklen_t len = sizeof(so_error);
+				getsockopt(sfd, SOL_SOCKET, SO_ERROR, (void*)&so_error, &len);
+				if (so_error == 0) {
+					break;
+				}
+			}
+		}
+		close(sfd);
+	}
+
+	freeaddrinfo(result);
+
+	if (rp == NULL) {
 		if (verbose >= 2)
-			fprintf(stderr, "%s: unknown host '%s'\n", __func__, addr);
+			fprintf(stderr, "%s: Could not connect to %s:%d\n", __func__, addr, port);
 		return -1;
 	}
 
-	if (!hp->h_addr) {
-		if (verbose >= 2)
-			fprintf(stderr, "%s: gethostbyname returned NULL address!\n",
-					__func__);
-		return -1;
-	}
-
-	if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
-		perror("socket()");
-		return -1;
-	}
-
-	if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
-		perror("setsockopt()");
-		socket_close(sfd);
-		return -1;
-	}
+#ifdef WIN32
+	ioctlsocket(sfd, FIONBIO, &l_no);
+#else
+	flags = fcntl(sfd, F_GETFL, 0);
+	fcntl(sfd, F_SETFL, flags & (~O_NONBLOCK));
+#endif
 
 #ifdef SO_NOSIGPIPE
 	if (setsockopt(sfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) {
@@ -283,17 +345,6 @@
 		perror("Could not set receive buffer for socket");
 	}
 
-	memset((void *) &saddr, 0, sizeof(saddr));
-	saddr.sin_family = AF_INET;
-	saddr.sin_addr.s_addr = *(uint32_t *) hp->h_addr;
-	saddr.sin_port = htons(port);
-
-	if (connect(sfd, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) {
-		perror("connect");
-		socket_close(sfd);
-		return -2;
-	}
-
 	return sfd;
 }
 
diff --git a/configure.ac b/configure.ac
index ab056b6..d64e116 100644
--- a/configure.ac
+++ b/configure.ac
@@ -74,6 +74,7 @@
   *mingw32*|*cygwin*)
     AC_MSG_RESULT([yes])
     win32=true
+    AC_DEFINE(WINVER, 0x0501, [minimum Windows version])
     ;;
   darwin*)
     AC_MSG_RESULT([no])
diff --git a/src/libusbmuxd.c b/src/libusbmuxd.c
index 0aaae24..30fa207 100644
--- a/src/libusbmuxd.c
+++ b/src/libusbmuxd.c
@@ -143,6 +143,50 @@
  */
 static int connect_usbmuxd_socket()
 {
+	char *usbmuxd_socket_addr = getenv("USBMUXD_SOCKET_ADDRESS");
+	if (usbmuxd_socket_addr) {
+		if (strncmp(usbmuxd_socket_addr, "UNIX:", 5) == 0) {
+#if defined(WIN32) || defined(__CYGWIN__)
+			/* not supported, ignore */
+#else
+			if (usbmuxd_socket_addr[5] != '\0') {
+				return socket_connect_unix(usbmuxd_socket_addr+5);
+			}
+#endif
+		} else {
+			uint16_t port = 0;
+			char *p = strrchr(usbmuxd_socket_addr, ':');
+			if (p) {
+				char *endp = NULL;
+				long l_port = strtol(p+1, &endp, 10);
+				if (endp && *endp == '\0') {
+					if (l_port > 0 && l_port < 65536) {
+						port = (uint16_t)l_port;
+					}
+				}
+			}
+			if (p && port > 0) {
+				char *connect_addr = NULL;
+				if (usbmuxd_socket_addr[0] == '[') {
+					connect_addr = strdup(usbmuxd_socket_addr+1);
+					connect_addr[p - usbmuxd_socket_addr - 1] = '\0';
+					p = strrchr(connect_addr, ']');
+					if (p) {
+						*p = '\0';
+					}
+				} else {
+					connect_addr = strdup(usbmuxd_socket_addr);
+					connect_addr[p - usbmuxd_socket_addr] = '\0';
+				}
+				if (connect_addr && *connect_addr != '\0') {
+					int res = socket_connect(connect_addr, port);
+					free(connect_addr);
+					return res;
+				}
+				free(connect_addr);
+			}
+		}
+	}
 #if defined(WIN32) || defined(__CYGWIN__)
 	return socket_connect("127.0.0.1", USBMUXD_SOCKET_PORT);
 #else