- dtucker@cvs.openbsd.org 2010/01/11 01:39:46
     [ssh_config channels.c ssh.1 channels.h ssh.c]
     Add a 'netcat mode' (ssh -W).  This connects stdio on the client to a
     single port forward on the server.  This allows, for example, using ssh as
     a ProxyCommand to route connections via intermediate servers.
     bz #1618, man page help from jmc@, ok markus@
diff --git a/ssh.c b/ssh.c
index ee30e2b..b86a764 100644
--- a/ssh.c
+++ b/ssh.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh.c,v 1.330 2010/01/09 23:04:13 dtucker Exp $ */
+/* $OpenBSD: ssh.c,v 1.331 2010/01/11 01:39:46 dtucker Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -133,6 +133,10 @@
  */
 int fork_after_authentication_flag = 0;
 
+/* forward stdio to remote host and port */
+char *stdio_forward_host = NULL;
+int stdio_forward_port = 0;
+
 /*
  * General data structure for command line options and options configurable
  * in configuration files.  See readconf.h.
@@ -186,7 +190,8 @@
 "           [-i identity_file] [-L [bind_address:]port:host:hostport]\n"
 "           [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port]\n"
 "           [-R [bind_address:]port:host:hostport] [-S ctl_path]\n"
-"           [-w local_tun[:remote_tun]] [user@]hostname [command]\n"
+"           [-W host:port] [-w local_tun[:remote_tun]]\n"
+"           [user@]hostname [command]\n"
 	);
 	exit(255);
 }
@@ -276,7 +281,7 @@
 
  again:
 	while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx"
-	    "ACD:F:I:KL:MNO:PR:S:TVw:XYy")) != -1) {
+	    "ACD:F:I:KL:MNO:PR:S:TVw:W:XYy")) != -1) {
 		switch (opt) {
 		case '1':
 			options.protocol = SSH_PROTO_1;
@@ -389,6 +394,22 @@
 				exit(255);
 			}
 			break;
+		case 'W':
+			if (parse_forward(&fwd, optarg, 1, 0)) {
+				stdio_forward_host = fwd.listen_host;
+				stdio_forward_port = fwd.listen_port;
+				xfree(fwd.connect_host);
+			} else {
+				fprintf(stderr,
+				    "Bad stdio forwarding specification '%s'\n",
+				    optarg);
+				exit(255);
+			}
+			no_tty_flag = 1;
+			no_shell_flag = 1;
+			options.clear_forwardings = 1;
+			options.exit_on_forward_failure = 1;
+			break;
 		case 'q':
 			options.log_level = SYSLOG_LEVEL_QUIET;
 			break;
@@ -871,11 +892,41 @@
 }
 
 static void
+client_cleanup_stdio_fwd(int id, void *arg)
+{
+	debug("stdio forwarding: done");
+	cleanup_exit(0);
+}
+
+static int
+client_setup_stdio_fwd(const char *host_to_connect, u_short port_to_connect)
+{
+	Channel *c;
+
+	debug3("client_setup_stdio_fwd %s:%d", host_to_connect,
+	    port_to_connect);
+	if ((c = channel_connect_stdio_fwd(host_to_connect, port_to_connect))
+	    == NULL)
+		return 0;
+	channel_register_cleanup(c->self, client_cleanup_stdio_fwd, 0);
+	return 1;
+}
+
+static void
 ssh_init_forwarding(void)
 {
 	int success = 0;
 	int i;
 
+	if (stdio_forward_host != NULL) {
+		if (!compat20) {
+			fatal("stdio forwarding require Protocol 2");
+		}
+		if (!client_setup_stdio_fwd(stdio_forward_host,
+		    stdio_forward_port))
+			fatal("Failed to connect in stdio forward mode.");
+	}
+
 	/* Initiate local TCP/IP port forwardings. */
 	for (i = 0; i < options.num_local_forwards; i++) {
 		debug("Local connections to %.200s:%d forwarded to remote "