- djm@cvs.openbsd.org 2005/03/01 10:09:52
     [auth-options.c channels.c channels.h clientloop.c compat.c compat.h]
     [misc.c misc.h readconf.c readconf.h servconf.c ssh.1 ssh.c ssh_config.5]
     [sshd_config.5]
     bz#413: allow optional specification of bind address for port forwardings.
     Patch originally by Dan Astorian, but worked on by several people
     Adds GatewayPorts=clientspecified option on server to allow remote
     forwards to bind to client-specified ports.
diff --git a/ssh.c b/ssh.c
index 1f50727..99b25af 100644
--- a/ssh.c
+++ b/ssh.c
@@ -40,7 +40,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: ssh.c,v 1.231 2005/02/16 09:56:44 otto Exp $");
+RCSID("$OpenBSD: ssh.c,v 1.232 2005/03/01 10:09:52 djm Exp $");
 
 #include <openssl/evp.h>
 #include <openssl/err.h>
@@ -158,9 +158,10 @@
 {
 	fprintf(stderr,
 "usage: ssh [-1246AaCfgkMNnqsTtVvXxY] [-b bind_address] [-c cipher_spec]\n"
-"           [-D port] [-e escape_char] [-F configfile] [-i identity_file]\n"
-"           [-L port:host:hostport] [-l login_name] [-m mac_spec] [-O ctl_cmd]\n"
-"           [-o option] [-p port] [-R port:host:hostport] [-S ctl_path]\n"
+"           [-D [listen-host:]port] [-e escape_char] [-F configfile]\n"
+"           [-i identity_file] [-L [listen-host:]port:host:hostport]\n"
+"           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
+"           [-R [listen-host:]port:host:hostport] [-S ctl_path]\n"
 "           [user@]hostname [command]\n"
 	);
 	exit(1);
@@ -178,14 +179,13 @@
 main(int ac, char **av)
 {
 	int i, opt, exit_status;
-	u_short fwd_port, fwd_host_port;
-	char sfwd_port[6], sfwd_host_port[6];
 	char *p, *cp, *line, buf[256];
 	struct stat st;
 	struct passwd *pw;
 	int dummy;
 	extern int optind, optreset;
 	extern char *optarg;
+	Forward fwd;
 
 	__progname = ssh_get_progname(av[0]);
 	init_rng();
@@ -401,39 +401,51 @@
 			break;
 
 		case 'L':
-		case 'R':
-			if (sscanf(optarg, "%5[0123456789]:%255[^:]:%5[0123456789]",
-			    sfwd_port, buf, sfwd_host_port) != 3 &&
-			    sscanf(optarg, "%5[0123456789]/%255[^/]/%5[0123456789]",
-			    sfwd_port, buf, sfwd_host_port) != 3) {
+			if (parse_forward(&fwd, optarg))
+				add_local_forward(&options, &fwd);
+			else {
 				fprintf(stderr,
-				    "Bad forwarding specification '%s'\n",
+				    "Bad local forwarding specification '%s'\n",
 				    optarg);
-				usage();
-				/* NOTREACHED */
-			}
-			if ((fwd_port = a2port(sfwd_port)) == 0 ||
-			    (fwd_host_port = a2port(sfwd_host_port)) == 0) {
-				fprintf(stderr,
-				    "Bad forwarding port(s) '%s'\n", optarg);
 				exit(1);
 			}
-			if (opt == 'L')
-				add_local_forward(&options, fwd_port, buf,
-				    fwd_host_port);
-			else if (opt == 'R')
-				add_remote_forward(&options, fwd_port, buf,
-				    fwd_host_port);
+			break;
+
+		case 'R':
+			if (parse_forward(&fwd, optarg)) {
+				add_remote_forward(&options, &fwd);
+			} else {
+				fprintf(stderr,
+				    "Bad remote forwarding specification "
+				    "'%s'\n", optarg);
+				exit(1);
+			}
 			break;
 
 		case 'D':
-			fwd_port = a2port(optarg);
-			if (fwd_port == 0) {
+			cp = p = xstrdup(optarg);
+			memset(&fwd, '\0', sizeof(fwd));
+			fwd.connect_host = "socks";
+			if ((fwd.listen_host = hpdelim(&cp)) == NULL) {
+				fprintf(stderr, "Bad dynamic forwarding "
+				    "specification '%.100s'\n", optarg);
+				exit(1);
+			}
+			if (cp != NULL) {
+				fwd.listen_port = a2port(cp);
+				fwd.listen_host = cleanhostname(fwd.listen_host);
+			} else {
+				fwd.listen_port = a2port(fwd.listen_host);
+				fwd.listen_host = "";
+			}
+
+			if (fwd.listen_port == 0) {
 				fprintf(stderr, "Bad dynamic port '%s'\n",
 				    optarg);
 				exit(1);
 			}
-			add_local_forward(&options, fwd_port, "socks", 0);
+			add_local_forward(&options, &fwd);
+			xfree(p);
 			break;
 
 		case 'C':
@@ -842,14 +854,19 @@
 
 	/* Initiate local TCP/IP port forwardings. */
 	for (i = 0; i < options.num_local_forwards; i++) {
-		debug("Connections to local port %d forwarded to remote address %.200s:%d",
-		    options.local_forwards[i].port,
-		    options.local_forwards[i].host,
-		    options.local_forwards[i].host_port);
+		debug("Local connections to %.200s:%d forwarded to remote "
+		    "address %.200s:%d",
+		    (options.local_forwards[i].listen_host == NULL) ? 
+		    (options.gateway_ports ? "*" : "LOCALHOST") : 
+		    options.local_forwards[i].listen_host,
+		    options.local_forwards[i].listen_port,
+		    options.local_forwards[i].connect_host,
+		    options.local_forwards[i].connect_port);
 		success += channel_setup_local_fwd_listener(
-		    options.local_forwards[i].port,
-		    options.local_forwards[i].host,
-		    options.local_forwards[i].host_port,
+		    options.local_forwards[i].listen_host,
+		    options.local_forwards[i].listen_port,
+		    options.local_forwards[i].connect_host,
+		    options.local_forwards[i].connect_port,
 		    options.gateway_ports);
 	}
 	if (i > 0 && success == 0)
@@ -857,14 +874,17 @@
 
 	/* Initiate remote TCP/IP port forwardings. */
 	for (i = 0; i < options.num_remote_forwards; i++) {
-		debug("Connections to remote port %d forwarded to local address %.200s:%d",
-		    options.remote_forwards[i].port,
-		    options.remote_forwards[i].host,
-		    options.remote_forwards[i].host_port);
+		debug("Remote connections from %.200s:%d forwarded to "
+		    "local address %.200s:%d",
+		    options.remote_forwards[i].listen_host,
+		    options.remote_forwards[i].listen_port,
+		    options.remote_forwards[i].connect_host,
+		    options.remote_forwards[i].connect_port);
 		channel_request_remote_forwarding(
-		    options.remote_forwards[i].port,
-		    options.remote_forwards[i].host,
-		    options.remote_forwards[i].host_port);
+		    options.remote_forwards[i].listen_host,
+		    options.remote_forwards[i].listen_port,
+		    options.remote_forwards[i].connect_host,
+		    options.remote_forwards[i].connect_port);
 	}
 }
 
@@ -1040,12 +1060,12 @@
 		return;
 	debug("remote forward %s for: listen %d, connect %s:%d",
 	    type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure",
-	    options.remote_forwards[i].port,
-	    options.remote_forwards[i].host,
-	    options.remote_forwards[i].host_port);
+	    options.remote_forwards[i].listen_port,
+	    options.remote_forwards[i].connect_host,
+	    options.remote_forwards[i].connect_port);
 	if (type == SSH2_MSG_REQUEST_FAILURE)
-		logit("Warning: remote port forwarding failed for listen port %d",
-		    options.remote_forwards[i].port);
+		logit("Warning: remote port forwarding failed for listen "
+		    "port %d", options.remote_forwards[i].listen_port);
 }
 
 static void