From 52371bccdcb1b0fe98930e79ac2ab9bdad2700e2 Mon Sep 17 00:00:00 2001
From: Nikias Bassen <nikias@gmx.li>
Date: Wed, 30 Dec 2020 03:18:02 +0100
Subject: [PATCH] common: Merge socket changes from libusbmuxd

---
 common/socket.c                 | 423 ++++++++++++++++++++++++++++----
 common/socket.h                 |  10 +-
 configure.ac                    |   2 +-
 tools/idevicedebugserverproxy.c |   2 +-
 4 files changed, 388 insertions(+), 49 deletions(-)

diff --git a/common/socket.c b/common/socket.c
index a5f9a331..354e6a4e 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -34,7 +34,16 @@
 #include <winsock2.h>
 #include <ws2tcpip.h>
 #include <windows.h>
+#ifndef HAVE_GETIFADDRS
+#include <iphlpapi.h>
+#endif
 static int wsa_init = 0;
+#ifndef IFF_RUNNING
+#define IFF_RUNNING IFF_UP
+#endif
+#ifndef AI_NUMERICSERV
+#define AI_NUMERICSERV 0
+#endif
 #else
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -51,6 +60,7 @@ static int wsa_init = 0;
 #include "socket.h"
 
 #define RECV_TIMEOUT 20000
+#define SEND_TIMEOUT 10000
 #define CONNECT_TIMEOUT 5000
 
 #ifndef EAFNOSUPPORT
@@ -161,7 +171,7 @@ int socket_create_unix(const char *filename)
 		return -1;
 	}
 
-	if (listen(sock, 10) < 0) {
+	if (listen(sock, 100) < 0) {
 		perror("listen");
 		socket_close(sock);
 		return -1;
@@ -259,10 +269,14 @@ int socket_connect_unix(const char *filename)
 }
 #endif
 
-int socket_create(uint16_t port)
+int socket_create(const char* addr, uint16_t port)
 {
 	int sfd = -1;
 	int yes = 1;
+	struct addrinfo hints;
+	struct addrinfo *result, *rp;
+	char portstr[8];
+	int res;
 #ifdef WIN32
 	WSADATA wsa_data;
 	if (!wsa_init) {
@@ -273,41 +287,69 @@ int socket_create(uint16_t port)
 		wsa_init = 1;
 	}
 #endif
-	struct sockaddr_in saddr;
 
-	if (0 > (sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP))) {
-		perror("socket()");
-		return -1;
-	}
+	memset(&hints, '\0', sizeof(struct addrinfo));
+	hints.ai_family = AF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
+	hints.ai_protocol = IPPROTO_TCP;
 
-	if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
-		perror("setsockopt()");
-		socket_close(sfd);
+	sprintf(portstr, "%d", port);
+
+	if (!addr) {
+		addr = "localhost";
+	}
+	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()");
+			socket_close(sfd);
+			continue;
+		}
+
 #ifdef SO_NOSIGPIPE
-	if (setsockopt(sfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) {
-		perror("setsockopt()");
-		socket_close(sfd);
-		return -1;
-	}
+		if (setsockopt(sfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) {
+			perror("setsockopt()");
+			socket_close(sfd);
+			continue;
+		}
+#endif
+
+#if defined(AF_INET6) && defined(IPV6_V6ONLY)
+		if (rp->ai_family == AF_INET6) {
+			if (setsockopt(sfd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&yes, sizeof(int)) == -1) {
+				perror("setsockopt() IPV6_V6ONLY");
+			}
+		}
 #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 (bind(sfd, rp->ai_addr, rp->ai_addrlen) < 0) {
+			perror("bind()");
+			socket_close(sfd);
+			continue;
+		}
 
-	if (0 > bind(sfd, (struct sockaddr *) &saddr, sizeof(saddr))) {
-		perror("bind()");
-		socket_close(sfd);
-		return -1;
+		if (listen(sfd, 100) < 0) {
+			perror("listen()");
+			socket_close(sfd);
+			continue;
+		}
+		break;
 	}
 
-	if (listen(sfd, 1) == -1) {
-		perror("listen()");
-		socket_close(sfd);
+	freeaddrinfo(result);
+
+	if (rp == NULL) {
 		return -1;
 	}
 
@@ -344,10 +386,308 @@ static uint32_t _in6_addr_scope(struct in6_addr* addr)
 	return scope;
 }
 
+#ifndef HAVE_GETIFADDRS
+#ifdef WIN32
+
+struct ifaddrs {
+	struct ifaddrs  *ifa_next;    /* Next item in list */
+	char            *ifa_name;    /* Name of interface */
+	unsigned int     ifa_flags;   /* Flags from SIOCGIFFLAGS */
+	struct sockaddr *ifa_addr;    /* Address of interface */
+	struct sockaddr *ifa_netmask; /* Netmask of interface */
+	union {
+		struct sockaddr *ifu_broadaddr; /* Broadcast address of interface */
+		struct sockaddr *ifu_dstaddr;   /* Point-to-point destination address */
+	} ifa_ifu;
+#define                  ifa_broadaddr ifa_ifu.ifu_broadaddr
+#define                  ifa_dstaddr   ifa_ifu.ifu_dstaddr
+	void            *ifa_data;    /* Address-specific data */
+};
+
+#define WORKING_BUFFER_SIZE 15000
+#define MAX_TRIES 3
+
+static void freeifaddrs(struct ifaddrs *ifa)
+{
+	if (!ifa) {
+		return;
+	}
+	free(ifa->ifa_name);
+	free(ifa->ifa_addr);
+	free(ifa->ifa_netmask);
+	free(ifa->ifa_dstaddr);
+	freeifaddrs(ifa->ifa_next);
+	free(ifa);
+}
+
+/*
+ * getifaddrs() reference implementation for win32.
+ * Heavily based on openpgm's implementation found here:
+ * https://github.com/steve-o/openpgm/blob/master/openpgm/pgm/getifaddrs.c
+ */
+static int getifaddrs(struct ifaddrs** ifap)
+{
+	struct ifaddrs* ifa = NULL;
+
+	DWORD dwRetVal = 0;
+
+	PIP_ADAPTER_ADDRESSES pAddresses = NULL;
+	ULONG outBufLen = 0;
+	ULONG Iterations = 0;
+
+	ULONG flags = GAA_FLAG_INCLUDE_PREFIX |
+		GAA_FLAG_SKIP_ANYCAST |
+		GAA_FLAG_SKIP_DNS_SERVER |
+		GAA_FLAG_SKIP_FRIENDLY_NAME |
+		GAA_FLAG_SKIP_MULTICAST;
+
+	PIP_ADAPTER_ADDRESSES adapter = NULL;
+
+	if (!ifap) {
+		errno = EINVAL;
+		return -1;
+	}
+	*ifap = NULL;
+
+	outBufLen = WORKING_BUFFER_SIZE;
+	do {
+		pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen);
+		if (pAddresses == NULL) {
+			printf("Memory allocation failed for IP_ADAPTER_ADDRESSES struct\n");
+			return -1;
+		}
+		dwRetVal = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, pAddresses, &outBufLen);
+		if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
+			free(pAddresses);
+			pAddresses = NULL;
+		} else {
+			break;
+		}
+		Iterations++;
+	} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));
+
+	if (dwRetVal != NO_ERROR) {
+		free(pAddresses);
+		return -1;
+	}
+
+	for (adapter = pAddresses; adapter; adapter = adapter->Next) {
+		int unicastIndex = 0;
+		for (IP_ADAPTER_UNICAST_ADDRESS *unicast = adapter->FirstUnicastAddress; unicast; unicast = unicast->Next, ++unicastIndex) {
+			/* ensure IP adapter */
+			if (AF_INET != unicast->Address.lpSockaddr->sa_family && AF_INET6 != unicast->Address.lpSockaddr->sa_family) {
+				continue;
+			}
+
+			if (!ifa) {
+				ifa = malloc(sizeof(struct ifaddrs));
+				if (!ifa) {
+					errno = ENOMEM;
+					free(pAddresses);
+					return -1;
+				}
+				*ifap = ifa;
+				ifa->ifa_next = NULL;
+			} else {
+				struct ifaddrs* ifanew = malloc(sizeof(struct ifaddrs));
+				if (!ifanew) {
+					freeifaddrs(*ifap);
+					free(pAddresses);
+					errno = ENOMEM;
+					return -1;
+				}
+				ifa->ifa_next = ifanew;
+				ifa = ifanew;
+				ifa->ifa_next = NULL;
+			}
+
+			/* name */
+			ifa->ifa_name = strdup(adapter->AdapterName);
+
+			/* flags */
+			ifa->ifa_flags = 0;
+			if (IfOperStatusUp == adapter->OperStatus)
+				ifa->ifa_flags |= IFF_UP;
+			if (IF_TYPE_SOFTWARE_LOOPBACK == adapter->IfType)
+				ifa->ifa_flags |= IFF_LOOPBACK;
+			if (!(adapter->Flags & IP_ADAPTER_NO_MULTICAST))
+				ifa->ifa_flags |= IFF_MULTICAST;
+
+			/* address */
+			ifa->ifa_addr = (struct sockaddr*)malloc(sizeof(struct sockaddr_storage));
+			memcpy(ifa->ifa_addr, unicast->Address.lpSockaddr, unicast->Address.iSockaddrLength);
+
+			/* netmask */
+			ifa->ifa_netmask = (struct sockaddr*)malloc(sizeof(struct sockaddr_storage));
+			memset(ifa->ifa_netmask, 0, sizeof(struct sockaddr_storage));
+
+/* pre-Vista must hunt for matching prefix in linked list, otherwise use
+ * OnLinkPrefixLength from IP_ADAPTER_UNICAST_ADDRESS structure.
+ * FirstPrefix requires Windows XP SP1, from SP1 to pre-Vista provides a
+ * single adapter prefix for each IP address.  Vista and later provides
+ * host IP address prefix, subnet IP address, and subnet broadcast IP
+ * address.  In addition there is a multicast and broadcast address prefix.
+ */
+			ULONG prefixLength = 0;
+
+#if defined( _WIN32 ) && ( _WIN32_WINNT >= 0x0600 )
+/* For a unicast IPv4 address, any value greater than 32 is an illegal
+ * value. For a unicast IPv6 address, any value greater than 128 is an
+ * illegal value. A value of 255 is commonly used to represent an illegal
+ * value.
+ *
+ * Windows 7 SP1 returns 64 for Teredo links which is incorrect.
+ */
+
+#define IN6_IS_ADDR_TEREDO(addr) \
+	(((const uint32_t *)(addr))[0] == ntohl (0x20010000))
+
+			if (AF_INET6 == unicast->Address.lpSockaddr->sa_family &&
+/* TunnelType only applies to one interface on the adapter and no
+ * convenient method is provided to determine which.
+ */
+				TUNNEL_TYPE_TEREDO == adapter->TunnelType &&
+/* Test the interface with the known Teredo network prefix.
+ */
+				IN6_IS_ADDR_TEREDO( &((struct sockaddr_in6*)(unicast->Address.lpSockaddr))->sin6_addr) &&
+/* Test that this version is actually wrong, subsequent releases from Microsoft
+ * may resolve the issue.
+ */
+				32 != unicast->OnLinkPrefixLength)
+			{
+				prefixLength = 32;
+			}
+			else
+				prefixLength = unicast->OnLinkPrefixLength;
+#else
+/* The order of linked IP_ADAPTER_UNICAST_ADDRESS structures pointed to by
+ * the FirstUnicastAddress member does not have any relationship with the
+ * order of linked IP_ADAPTER_PREFIX structures pointed to by the FirstPrefix
+ * member.
+ *
+ * Example enumeration:
+ *    [ no subnet ]
+ *   ::1/128            - address
+ *   ff00::%1/8         - multicast (no IPv6 broadcast)
+ *   127.0.0.0/8        - subnet
+ *   127.0.0.1/32       - address
+ *   127.255.255.255/32 - subnet broadcast
+ *   224.0.0.0/4        - multicast
+ *   255.255.255.255/32 - broadcast
+ *
+ * Which differs from most adapters listing three IPv6:
+ *   fe80::%10/64       - subnet
+ *   fe80::51e9:5fe5:4202:325a%10/128 - address
+ *   ff00::%10/8        - multicast
+ *
+ * !IfOperStatusUp IPv4 addresses are skipped:
+ *   fe80::%13/64       - subnet
+ *   fe80::d530:946d:e8df:8c91%13/128 - address
+ *   ff00::%13/8        - multicast
+ *    [ no subnet  ]
+ *    [ no address ]
+ *   224.0.0.0/4        - multicast
+ *   255.255.255.255/32 - broadcast
+ *
+ * On PTP links no multicast or broadcast addresses are returned:
+ *    [ no subnet ]
+ *   fe80::5efe:10.203.9.30/128 - address
+ *    [ no multicast ]
+ *    [ no multicast ]
+ *    [ no broadcast ]
+ *
+ * Active primary IPv6 interfaces are a bit overloaded:
+ *   ::/0               - default route
+ *   2001::/32          - global subnet
+ *   2001:0:4137:9e76:2443:d6:ba87:1a2a/128 - global address
+ *   fe80::/64          - link-local subnet
+ *   fe80::2443:d6:ba87:1a2a/128 - link-local address
+ *   ff00::/8           - multicast
+ */
+
+#define IN_LINKLOCAL(a)	((((uint32_t) (a)) & 0xaffff0000) == 0xa9fe0000)
+
+			for (IP_ADAPTER_PREFIX *prefix = adapter->FirstPrefix; prefix; prefix = prefix->Next) {
+				LPSOCKADDR lpSockaddr = prefix->Address.lpSockaddr;
+				if (lpSockaddr->sa_family != unicast->Address.lpSockaddr->sa_family)
+					continue;
+/* special cases */
+/* RFC2863: IPv4 interface not up */
+				if (AF_INET == lpSockaddr->sa_family && adapter->OperStatus != IfOperStatusUp) {
+/* RFC3927: link-local IPv4 always has 16-bit CIDR */
+					if (IN_LINKLOCAL( ntohl (((struct sockaddr_in*)(unicast->Address.lpSockaddr))->sin_addr.s_addr))) {
+						prefixLength = 16;
+					}
+					break;
+				}
+/* default IPv6 route */
+				if (AF_INET6 == lpSockaddr->sa_family && 0 == prefix->PrefixLength && IN6_IS_ADDR_UNSPECIFIED( &((struct sockaddr_in6*)(lpSockaddr))->sin6_addr)) {
+					continue;
+				}
+/* Assume unicast address for first prefix of operational adapter */
+				if (AF_INET == lpSockaddr->sa_family)
+					if (IN_MULTICAST( ntohl (((struct sockaddr_in*)(lpSockaddr))->sin_addr.s_addr))) {
+						fprintf(stderr, "FATAL: first prefix is non a unicast address\n");
+						break;
+					}
+				if (AF_INET6 == lpSockaddr->sa_family)
+					if (IN6_IS_ADDR_MULTICAST( &((struct sockaddr_in6*)(lpSockaddr))->sin6_addr)) {
+						fprintf(stderr, "FATAL: first prefix is not a unicast address\n");
+						break;
+					}
+/* Assume subnet or host IP address for XP backward compatibility */
+
+				prefixLength = prefix->PrefixLength;
+				break;
+			}
+#endif /* defined( _WIN32 ) && ( _WIN32_WINNT >= 0x0600 ) */
+
+/* map prefix to netmask */
+			ifa->ifa_netmask->sa_family = unicast->Address.lpSockaddr->sa_family;
+			switch (unicast->Address.lpSockaddr->sa_family) {
+			case AF_INET:
+				if (0 == prefixLength || prefixLength > 32) {
+					prefixLength = 32;
+				}
+#if defined( _WIN32) && ( _WIN32_WINNT >= 0x0600 )
+/* Added in Vista, but no IPv6 equivalent. */
+				{
+				ULONG Mask;
+				ConvertLengthToIpv4Mask (prefixLength, &Mask);
+				((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr = Mask;	/* network order */
+				}
+#else
+/* NB: left-shift of full bit-width is undefined in C standard. */
+				((struct sockaddr_in*)ifa->ifa_netmask)->sin_addr.s_addr = htonl( 0xffffffffU << ( 32 - prefixLength ) );
+#endif
+				break;
+
+			case AF_INET6:
+				if (0 == prefixLength || prefixLength > 128) {
+					prefixLength = 128;
+				}
+				for (LONG i = prefixLength, j = 0; i > 0; i -= 8, ++j) {
+					((struct sockaddr_in6*)ifa->ifa_netmask)->sin6_addr.s6_addr[ j ] = i >= 8 ? 0xff : (ULONG)(( 0xffU << ( 8 - i ) ) & 0xffU );
+				}
+				break;
+			default:
+				break;
+			}
+		}
+	}
+	free(pAddresses);
+
+	return 0;
+}
+#else
+#error No reference implementation for getifaddrs available for this platform.
+#endif
+#endif
+
 static int32_t _sockaddr_in6_scope_id(struct sockaddr_in6* addr)
 {
 	int32_t res = -1;
-	struct ifaddrs *ifaddr, *ifa;
+	struct ifaddrs *ifaddr = NULL, *ifa = NULL;
 	uint32_t addr_scope;
 
 	/* get scope for requested address */
@@ -398,14 +738,14 @@ static int32_t _sockaddr_in6_scope_id(struct sockaddr_in6* addr)
 			if (addr->sin6_scope_id == addr_in->sin6_scope_id) {
 				res = addr_in->sin6_scope_id;
 				break;
-			} else {
-				if ((addr_in->sin6_scope_id > addr->sin6_scope_id) && (res >= 0)) {
-					// use last valid scope id as we're past the requested scope id
-					break;
-				}
-				res = addr_in->sin6_scope_id;
-				continue;
 			}
+
+			if ((addr_in->sin6_scope_id > addr->sin6_scope_id) && (res >= 0)) {
+				// use last valid scope id as we're past the requested scope id
+				break;
+			}
+			res = addr_in->sin6_scope_id;
+			continue;
 		}
 
 		/* skip loopback interface if not already matched exactly above */
@@ -594,7 +934,7 @@ int socket_connect(const char *addr, uint16_t port)
 	memset(&hints, '\0', sizeof(struct addrinfo));
 	hints.ai_family = AF_UNSPEC;
 	hints.ai_socktype = SOCK_STREAM;
-	hints.ai_flags = 0;
+	hints.ai_flags = AI_NUMERICSERV;
 	hints.ai_protocol = IPPROTO_TCP;
 
 	sprintf(portstr, "%d", port);
@@ -761,14 +1101,9 @@ int socket_accept(int fd, uint16_t port)
 	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);
-
+	struct sockaddr_storage addr;
 	addr_len = sizeof(addr);
+
 	result = accept(fd, (struct sockaddr*)&addr, &addr_len);
 
 	return result;
@@ -825,6 +1160,10 @@ int socket_receive_timeout(int fd, void *data, size_t length, int flags,
 int socket_send(int fd, void *data, size_t length)
 {
 	int flags = 0;
+	int res = socket_check_fd(fd, FDM_WRITE, SEND_TIMEOUT);
+	if (res <= 0) {
+		return res;
+	}
 #ifdef MSG_NOSIGNAL
 	flags |= MSG_NOSIGNAL;
 #endif
diff --git a/common/socket.h b/common/socket.h
index 38eeddf5..95672706 100644
--- a/common/socket.h
+++ b/common/socket.h
@@ -45,7 +45,7 @@ typedef enum fd_mode fd_mode;
 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);
@@ -54,12 +54,12 @@ int socket_accept(int fd, uint16_t port);
 int socket_shutdown(int fd, int how);
 int socket_close(int fd);
 
-int socket_receive(int fd, void *data, size_t size);
-int socket_peek(int fd, void *data, size_t size);
-int socket_receive_timeout(int fd, void *data, size_t size, int flags,
+int socket_receive(int fd, void *data, size_t length);
+int socket_peek(int fd, void *data, size_t length);
+int socket_receive_timeout(int fd, void *data, size_t length, int flags,
 					 unsigned int timeout);
 
-int socket_send(int fd, void *data, size_t size);
+int socket_send(int fd, void *data, size_t length);
 
 void socket_set_verbose(int level);
 
diff --git a/configure.ac b/configure.ac
index 1debfbf9..82038e19 100644
--- a/configure.ac
+++ b/configure.ac
@@ -48,7 +48,7 @@ AC_TYPE_UINT32_T
 AC_TYPE_UINT8_T
 
 # Checks for library functions.
-AC_CHECK_FUNCS([asprintf strcasecmp strdup strerror strndup stpcpy vasprintf])
+AC_CHECK_FUNCS([asprintf strcasecmp strdup strerror strndup stpcpy vasprintf getifaddrs])
 
 AC_CHECK_HEADER(endian.h, [ac_cv_have_endian_h="yes"], [ac_cv_have_endian_h="no"])
 if test "x$ac_cv_have_endian_h" = "xno"; then
diff --git a/tools/idevicedebugserverproxy.c b/tools/idevicedebugserverproxy.c
index 1d8a23d8..5b42473e 100644
--- a/tools/idevicedebugserverproxy.c
+++ b/tools/idevicedebugserverproxy.c
@@ -338,7 +338,7 @@ int main(int argc, char *argv[])
 	}
 
 	/* create local socket */
-	server_fd = socket_create(local_port);
+	server_fd = socket_create("127.0.0.1", local_port);
 	if (server_fd < 0) {
 		fprintf(stderr, "Could not create socket\n");
 		result = EXIT_FAILURE;