- djm@cvs.openbsd.org 2009/02/12 03:00:56
     [canohost.c canohost.h channels.c channels.h clientloop.c readconf.c]
     [readconf.h serverloop.c ssh.c]
     support remote port forwarding with a zero listen port (-R0:...) to
     dyamically allocate a listen port at runtime (this is actually
     specified in rfc4254); bz#1003 ok markus@
diff --git a/channels.c b/channels.c
index 0b1c34c..dea60ba 100644
--- a/channels.c
+++ b/channels.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.294 2009/01/22 09:49:57 djm Exp $ */
+/* $OpenBSD: channels.c,v 1.295 2009/02/12 03:00:56 djm Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -2460,7 +2460,8 @@
 }
 
 static int
-channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port,
+channel_setup_fwd_listener(int type, const char *listen_addr,
+    u_short listen_port, int *allocated_listen_port,
     const char *host_to_connect, u_short port_to_connect, int gateway_ports)
 {
 	Channel *c;
@@ -2468,6 +2469,7 @@
 	struct addrinfo hints, *ai, *aitop;
 	const char *host, *addr;
 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+	in_port_t *lport_p;
 
 	host = (type == SSH_CHANNEL_RPORT_LISTENER) ?
 	    listen_addr : host_to_connect;
@@ -2536,10 +2538,29 @@
 		}
 		return 0;
 	}
-
+	if (allocated_listen_port != NULL)
+		*allocated_listen_port = 0;
 	for (ai = aitop; ai; ai = ai->ai_next) {
-		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+		switch (ai->ai_family) {
+		case AF_INET:
+			lport_p = &((struct sockaddr_in *)ai->ai_addr)->
+			    sin_port;
+			break;
+		case AF_INET6:
+			lport_p = &((struct sockaddr_in6 *)ai->ai_addr)->
+			    sin6_port;
+			break;
+		default:
 			continue;
+		}
+		/*
+		 * If allocating a port for -R forwards, then use the
+		 * same port for all address families.
+		 */
+		if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 &&
+		    allocated_listen_port != NULL && *allocated_listen_port > 0)
+			*lport_p = htons(*allocated_listen_port);
+
 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
 		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
 			error("channel_setup_fwd_listener: getnameinfo failed");
@@ -2555,7 +2576,8 @@
 
 		channel_set_reuseaddr(sock);
 
-		debug("Local forwarding listening on %s port %s.", ntop, strport);
+		debug("Local forwarding listening on %s port %s.",
+		    ntop, strport);
 
 		/* Bind the socket to the address. */
 		if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
@@ -2574,6 +2596,19 @@
 			close(sock);
 			continue;
 		}
+
+		/*
+		 * listen_port == 0 requests a dynamically allocated port -
+		 * record what we got.
+		 */
+		if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 &&
+		    allocated_listen_port != NULL &&
+		    *allocated_listen_port == 0) {
+			*allocated_listen_port = get_sock_port(sock, 1);
+			debug("Allocated listen port %d",
+			    *allocated_listen_port);
+		}
+
 		/* Allocate a channel number for the socket. */
 		c = channel_new("port listener", type, sock, sock, -1,
 		    CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT,
@@ -2616,17 +2651,18 @@
     const char *host_to_connect, u_short port_to_connect, int gateway_ports)
 {
 	return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER,
-	    listen_host, listen_port, host_to_connect, port_to_connect,
+	    listen_host, listen_port, NULL, host_to_connect, port_to_connect,
 	    gateway_ports);
 }
 
 /* protocol v2 remote port fwd, used by sshd */
 int
 channel_setup_remote_fwd_listener(const char *listen_address,
-    u_short listen_port, int gateway_ports)
+    u_short listen_port, int *allocated_listen_port, int gateway_ports)
 {
 	return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER,
-	    listen_address, listen_port, NULL, 0, gateway_ports);
+	    listen_address, listen_port, allocated_listen_port,
+	    NULL, 0, gateway_ports);
 }
 
 /*