diff netio.c @ 1464:ad637c9e0f6f

Server chosen tcpfwd ports (#43) Server chosen tcpfwd ports
author houseofkodai <karthik@houseofkodai.in>
date Thu, 25 Jan 2018 19:51:41 +0530
parents 06d52bcb8094
children f7a53832501d
line wrap: on
line diff
--- a/netio.c	Thu Jan 25 21:58:00 2018 +0800
+++ b/netio.c	Thu Jan 25 19:51:41 2018 +0530
@@ -348,6 +348,37 @@
 
 }
 
+/* from openssh/canohost.c avoid premature-optimization */
+int get_sock_port(int sock) {
+	struct sockaddr_storage from;
+	socklen_t fromlen;
+	char strport[NI_MAXSERV];
+	int r;
+
+	/* Get IP address of client. */
+	fromlen = sizeof(from);
+	memset(&from, 0, sizeof(from));
+	if (getsockname(sock, (struct sockaddr *)&from, &fromlen) < 0) {
+		TRACE(("getsockname failed: %d", errno))
+		return 0;
+	}
+
+	/* Work around Linux IPv6 weirdness */
+	if (from.ss_family == AF_INET6)
+		fromlen = sizeof(struct sockaddr_in6);
+
+	/* Non-inet sockets don't have a port number. */
+	if (from.ss_family != AF_INET && from.ss_family != AF_INET6)
+		return 0;
+
+	/* Return port number. */
+	if ((r = getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0,
+	    strport, sizeof(strport), NI_NUMERICSERV)) != 0) {
+		TRACE(("netio.c/get_sock_port/getnameinfo NI_NUMERICSERV failed: %d", r))
+	}
+	return atoi(strport);
+}
+
 /* Listen on address:port. 
  * Special cases are address of "" listening on everything,
  * and address of NULL listening on localhost only.
@@ -400,11 +431,29 @@
 		return -1;
 	}
 
+	/*
+	 * when listening on server-assigned-port 0
+	 * the assigned ports may differ for address families (v4/v6)
+	 * causing problems for tcpip-forward
+	 * caller can do a get_socket_address to discover assigned-port
+	 * hence, use same port for all address families
+	 */
+	u_int16_t *allocated_lport_p = 0;
+	int allocated_lport = 0;
 
 	nsock = 0;
 	for (res = res0; res != NULL && nsock < sockcount;
 			res = res->ai_next) {
 
+		if (allocated_lport > 0) {
+			if (AF_INET == res->ai_family) {
+				allocated_lport_p = &((struct sockaddr_in *)res->ai_addr)->sin_port;
+			} else if (AF_INET6 == res->ai_family) {
+				allocated_lport_p = &((struct sockaddr_in6 *)res->ai_addr)->sin6_port;
+			}
+			*allocated_lport_p = htons(allocated_lport);
+		}
+
 		/* Get a socket */
 		socks[nsock] = socket(res->ai_family, res->ai_socktype,
 				res->ai_protocol);
@@ -451,6 +500,10 @@
 			continue;
 		}
 
+		if (0 == allocated_lport) {
+			allocated_lport = get_sock_port(sock);
+		}
+
 		*maxfd = MAX(*maxfd, sock);
 
 		nsock++;