socket: Add new functions socket_connect_addr() and socket_addr_to_string()
diff --git a/common/socket.c b/common/socket.c
index 7111749..cf6e9eb 100644
--- a/common/socket.c
+++ b/common/socket.c
@@ -1,7 +1,7 @@
 /*
  * socket.c
  *
- * Copyright (C) 2012-2018 Nikias Bassen <nikias@gmx.li>
+ * Copyright (C) 2012-2020 Nikias Bassen <nikias@gmx.li>
  * Copyright (C) 2012 Martin Szulecki <m.szulecki@libimobiledevice.org>
  *
  * This library is free software; you can redistribute it and/or
@@ -49,6 +49,9 @@
 #define RECV_TIMEOUT 20000
 #define CONNECT_TIMEOUT 5000
 
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT 102
+#endif
 #ifndef ECONNRESET
 #define ECONNRESET 108
 #endif
@@ -63,6 +66,59 @@
 	verbose = level;
 }
 
+const char *socket_addr_to_string(struct sockaddr *addr, char *addr_out, size_t addr_out_size)
+{
+#ifdef WIN32
+	WSADATA wsa_data;
+	if (!wsa_init) {
+		if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) {
+			fprintf(stderr, "WSAStartup failed!\n");
+			ExitProcess(-1);
+		}
+		wsa_init = 1;
+	}
+	DWORD addr_out_len = addr_out_size;
+	DWORD addrlen = 0;
+
+	if (addr->sa_family == AF_INET) {
+		addrlen = sizeof(struct sockaddr_in);
+	}
+#ifdef AF_INET6
+	else if (addr->sa_family == AF_INET6) {
+		addrlen = sizeof(struct sockaddr_in6);
+	}
+#endif
+	else {
+		errno = EAFNOSUPPORT;
+		return NULL;
+	}
+
+	if (WSAAddressToString(addr, addrlen, NULL, addr_out, &addr_out_len) == 0) {
+		return addr_out;
+	}
+#else
+	const void *addrdata = NULL;
+
+	if (addr->sa_family == AF_INET) {
+		addrdata = &((struct sockaddr_in*)addr)->sin_addr;
+	}
+#ifdef AF_INET6
+	else if (addr->sa_family == AF_INET6) {
+		addrdata = &((struct sockaddr_in6*)addr)->sin6_addr;
+	}
+#endif
+	else {
+		errno = EAFNOSUPPORT;
+		return NULL;
+	}
+
+	if (inet_ntop(addr->sa_family, addrdata, addr_out, addr_out_size)) {
+		return addr_out;
+	}
+#endif
+	return NULL;
+}
+
 #ifndef WIN32
 int socket_create_unix(const char *filename)
 {
@@ -254,6 +310,124 @@
 	return sfd;
 }
 
+int socket_connect_addr(struct sockaddr* addr, uint16_t port)
+{
+	int sfd = -1;
+	int yes = 1;
+	int bufsize = 0x20000;
+	int addrlen = 0;
+#ifdef WIN32
+	u_long l_yes = 1;
+	WSADATA wsa_data;
+	if (!wsa_init) {
+		if (WSAStartup(MAKEWORD(2,2), &wsa_data) != ERROR_SUCCESS) {
+			fprintf(stderr, "WSAStartup failed!\n");
+			ExitProcess(-1);
+		}
+		wsa_init = 1;
+	}
+#endif
+
+	if (addr->sa_family == AF_INET) {
+		struct sockaddr_in* addr_in = (struct sockaddr_in*)addr;
+		addr_in->sin_port = htons(port);
+		addrlen = sizeof(struct sockaddr_in);
+	}
+#ifdef AF_INET6
+	else if (addr->sa_family == AF_INET6) {
+		struct sockaddr_in6* addr_in = (struct sockaddr_in6*)addr;
+		addr_in->sin6_port = htons(port);
+		addrlen = sizeof(struct sockaddr_in6);
+	}
+#endif
+	else {
+		fprintf(stderr, "ERROR: Unsupported address family");
+		return -1;
+	}
+
+	sfd = socket(addr->sa_family, SOCK_STREAM, IPPROTO_TCP);
+	if (sfd == -1) {
+		perror("socket()");
+		return -1;
+	}
+
+#ifdef SO_NOSIGPIPE
+	if (setsockopt(sfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) {
+		perror("setsockopt()");
+		socket_close(sfd);
+		return -1;
+	}
+#endif
+
+	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_yes);
+#else
+	int flags = fcntl(sfd, F_GETFL, 0);
+	fcntl(sfd, F_SETFL, flags | O_NONBLOCK);
+#endif
+
+	do {
+		if (connect(sfd, addr, 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) {
+					errno = 0;
+					break;
+				}
+				errno = so_error;
+			}
+		}
+		socket_close(sfd);
+		sfd = -1;
+	} while (0);
+
+	if (sfd < 0) {
+		if (verbose >= 2) {
+			char addrtxt[48];
+			socket_addr_to_string(addr, addrtxt, sizeof(addrtxt));
+			fprintf(stderr, "%s: Could not connect to %s port %d\n", __func__, addrtxt, port);
+		}
+		return -1;
+	}
+
+	if (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(int)) == -1) {
+		perror("Could not set TCP_NODELAY on socket");
+	}
+
+	if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, (void*)&bufsize, sizeof(int)) == -1) {
+		perror("Could not set send buffer for socket");
+	}
+
+	if (setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, (void*)&bufsize, sizeof(int)) == -1) {
+		perror("Could not set receive buffer for socket");
+	}
+
+	return sfd;
+}
+
 int socket_connect(const char *addr, uint16_t port)
 {
 	int sfd = -1;
@@ -265,7 +439,6 @@
 	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) {
@@ -303,6 +476,14 @@
 			continue;
 		}
 
+#ifdef SO_NOSIGPIPE
+		if (setsockopt(sfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) {
+			perror("setsockopt()");
+			socket_close(sfd);
+			return -1;
+		}
+#endif
+
 		if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (void*)&yes, sizeof(int)) == -1) {
 			perror("setsockopt()");
 			socket_close(sfd);
@@ -352,14 +533,6 @@
 		return -1;
 	}
 
-#ifdef SO_NOSIGPIPE
-	if (setsockopt(sfd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&yes, sizeof(int)) == -1) {
-		perror("setsockopt()");
-		socket_close(sfd);
-		return -1;
-	}
-#endif
-
 	if (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (void*)&yes, sizeof(int)) == -1) {
 		perror("Could not set TCP_NODELAY on socket");
 	}
diff --git a/common/socket.h b/common/socket.h
index e31de6b..38eeddf 100644
--- a/common/socket.h
+++ b/common/socket.h
@@ -1,8 +1,8 @@
 /*
  * socket.h
  *
+ * Copyright (C) 2012-2020 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
@@ -46,6 +46,7 @@
 int socket_connect_unix(const char *filename);
 #endif
 int socket_create(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);
 int socket_accept(int fd, uint16_t port);
@@ -62,4 +63,6 @@
 
 void socket_set_verbose(int level);
 
+const char *socket_addr_to_string(struct sockaddr *addr, char *addr_out, size_t addr_out_size);
+
 #endif	/* SOCKET_SOCKET_H */