- markus@cvs.openbsd.org 2011/09/23 07:45:05
     [mux.c readconf.h channels.h compat.h compat.c ssh.c readconf.c channels.c     version.h]
     unbreak remote portforwarding with dynamic allocated listen ports:
     1) send the actual listen port in the open message (instead of 0).
        this allows multiple forwardings with a dynamic listen port
     2) update the matching permit-open entry, so we can identify where
        to connect to
     report: den at skbkontur.ru and P. Szczygielski
     feedback and ok djm@
diff --git a/channels.c b/channels.c
index 00e9af8..f6e9b4d 100644
--- a/channels.c
+++ b/channels.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: channels.c,v 1.314 2011/09/23 00:22:04 dtucker Exp $ */
+/* $OpenBSD: channels.c,v 1.315 2011/09/23 07:45:05 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -2814,8 +2814,12 @@
 		    0, "port listener", 1);
 		c->path = xstrdup(host);
 		c->host_port = port_to_connect;
-		c->listening_port = listen_port;
 		c->listening_addr = addr == NULL ? NULL : xstrdup(addr);
+		if (listen_port == 0 && allocated_listen_port != NULL &&
+		    !(datafellows & SSH_BUG_DYNAMIC_RPORT))
+			c->listening_port = *allocated_listen_port;
+		else
+			c->listening_port = listen_port;
 		success = 1;
 	}
 	if (success == 0)
@@ -2924,12 +2928,14 @@
 /*
  * Initiate forwarding of connections to port "port" on remote host through
  * the secure channel to host:port from local side.
+ * Returns handle (index) for updating the dynamic listen port with
+ * channel_update_permitted_opens().
  */
 int
 channel_request_remote_forwarding(const char *listen_host, u_short listen_port,
     const char *host_to_connect, u_short port_to_connect)
 {
-	int type, success = 0;
+	int type, success = 0, idx = -1;
 
 	/* Send the forward request to the remote side. */
 	if (compat20) {
@@ -2968,12 +2974,12 @@
 		/* Record that connection to this host/port is permitted. */
 		permitted_opens = xrealloc(permitted_opens,
 		    num_permitted_opens + 1, sizeof(*permitted_opens));
-		permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
-		permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
-		permitted_opens[num_permitted_opens].listen_port = listen_port;
-		num_permitted_opens++;
+		idx = num_permitted_opens++;
+		permitted_opens[idx].host_to_connect = xstrdup(host_to_connect);
+		permitted_opens[idx].port_to_connect = port_to_connect;
+		permitted_opens[idx].listen_port = listen_port;
 	}
-	return (success ? 0 : -1);
+	return (idx);
 }
 
 /*
@@ -3078,6 +3084,35 @@
 	all_opens_permitted = 0;
 }
 
+/*
+ * Update the listen port for a dynamic remote forward, after
+ * the actual 'newport' has been allocated. If 'newport' < 0 is
+ * passed then they entry will be invalidated.
+ */
+void
+channel_update_permitted_opens(int idx, int newport)
+{
+	if (idx < 0 || idx >= num_permitted_opens) {
+		debug("channel_update_permitted_opens: index out of range:"
+		    " %d num_permitted_opens %d", idx, num_permitted_opens);
+		return;
+	}
+	debug("%s allowed port %d for forwarding to host %s port %d",
+	    newport > 0 ? "Updating" : "Removing",
+	    newport,
+	    permitted_opens[idx].host_to_connect,
+	    permitted_opens[idx].port_to_connect);
+	if (newport >= 0)  {
+		permitted_opens[idx].listen_port = 
+		    (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport;
+	} else {
+		permitted_opens[idx].listen_port = 0;
+		permitted_opens[idx].port_to_connect = 0;
+		xfree(permitted_opens[idx].host_to_connect);
+		permitted_opens[idx].host_to_connect = NULL;
+	}
+}
+
 int
 channel_add_adm_permitted_opens(char *host, int port)
 {