New command-line option in Plink (and PuTTY, though it's less useful
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Mon, 28 Aug 2006 15:12:37 +0000 (15:12 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Mon, 28 Aug 2006 15:12:37 +0000 (15:12 +0000)
there): `plink host -nc host2:port' causes the SSH connection's main
channel to be replaced with a direct-tcpip connection to the
specified destination. This feature is mainly designed for use as a
local proxy: setting your local proxy command to `plink %proxyhost
-nc %host:%port' lets you tunnel SSH over SSH with a minimum of
fuss. Works on all platforms.

git-svn-id: svn://svn.tartarus.org/sgt/putty@6823 cda61777-01e9-0310-a592-d414129be87e

cmdline.c
doc/config.but
doc/using.but
putty.h
ssh.c
unix/uxplink.c
windows/winplink.c

index e834342..79f2981 100644 (file)
--- a/cmdline.c
+++ b/cmdline.c
@@ -249,6 +249,28 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg)
        cfg->portfwd[sizeof(cfg->portfwd) - 2] = '\0';
        ptr[strlen(ptr)+1] = '\000';    /* append 2nd '\000' */
     }
+    if ((!strcmp(p, "-nc"))) {
+       char *host, *portp;
+
+       RETURN(2);
+       UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK);
+       SAVEABLE(0);
+
+       host = portp = value;
+       while (*portp && *portp != ':')
+           portp++;
+       if (*portp) {
+           unsigned len = portp - host;
+           if (len >= sizeof(cfg->ssh_nc_host))
+               len = sizeof(cfg->ssh_nc_host) - 1;
+           strncpy(cfg->ssh_nc_host, value, len);
+           cfg->ssh_nc_host[sizeof(cfg->ssh_nc_host) - 1] = '\0';
+           cfg->ssh_nc_port = atoi(portp+1);
+       } else {
+           cmdline_error("-nc expects argument of form 'host:port'");
+           return ret;
+       }
+    }
     if (!strcmp(p, "-m")) {
        char *filename, *command;
        int cmdlen, cmdsize;
index 42fa3aa..e678db0 100644 (file)
@@ -1807,6 +1807,11 @@ output streams.
 This could be used, for instance, to talk to some kind of network proxy
 that PuTTY does not natively support; or you could tunnel a connection
 over something other than TCP/IP entirely.
+
+If you want your local proxy command to make a secondary SSH
+connection to a proxy host and then tunnel the primary connection
+over that, you might well want the \c{-nc} command-line option in
+Plink. See \k{using-cmdline-ncmode} for more information.
 }
 
 \S{config-proxy-exclude} Excluding parts of the network from proxying
index 0fb7127..af98d90 100644 (file)
@@ -796,6 +796,47 @@ at all} checkbox in the SSH panel of the PuTTY configuration box
 This option is not available in the file transfer tools PSCP and
 PSFTP.
 
+\S2{using-cmdline-ncmode} \I{-nc}\c{-nc}: make a \i{remote network
+connection} in place of a remote shell or command
+
+The \c{-nc} option prevents Plink (or PuTTY) from attempting to
+start a shell or command on the remote server. Instead, it will
+instruct the remote server to open a network connection to a host
+name and port number specified by you, and treat that network
+connection as if it were the main session.
+
+You specify a host and port as an argument to the \c{-nc} option,
+with a colon separating the host name from the port number, like
+this:
+
+\c plink host1.example.com -nc host2.example.com:1234
+
+You might want to use this feature if you needed to make an SSH
+connection to a target host which you can only reach by going
+through a proxy host, and rather than using port forwarding you
+prefer to use the local proxy feature (see \k{config-proxy-type} for
+more about local proxies). In this situation you might select
+\q{Local} proxy type, set your local proxy command to be \cq{plink
+%proxyhost -nc %host:%port}, enter the target host name on the
+Session panel, and enter the directly reachable proxy host name on
+the Proxy panel.
+
+This feature is only available in SSH protocol version 2 (since the
+version 1 protocol assumes you will always want to run a shell). It
+is not available in the file transfer tools PSCP and PSFTP. It is
+available in PuTTY itself, although it is unlikely to be very useful
+in any tool other than Plink. Also, \c{-nc} uses the same server
+functionality as port forwarding, so it will not work if your server
+administrator has disabled port forwarding.
+
+(The option is named \c{-nc} after the Unix program
+\W{http://www.vulnwatch.org/netcat/}\c{nc}, short for \q{netcat}.
+The command \cq{plink host1 -nc host2:port} is very similar in
+functionality to \cq{plink host1 nc host2 port}, which invokes
+\c{nc} on the server and tells it to connect to the specified
+destination. However, Plink's built-in \c{-nc} option does not
+depend on the \c{nc} program being installed on the server.)
+
 \S2{using-cmdline-compress} \I{-C-upper}\c{-C}: enable \i{compression}
 
 The \c{-C} option enables compression of the data sent across the
diff --git a/putty.h b/putty.h
index 22ec825..c648921 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -458,6 +458,8 @@ struct config_tag {
     int ssh_subsys;                   /* run a subsystem rather than a command */
     int ssh_subsys2;                  /* fallback to go with remote_cmd2 */
     int ssh_no_shell;                 /* avoid running a shell */
+    char ssh_nc_host[512];            /* host to connect to in `nc' mode */
+    int ssh_nc_port;                  /* port to connect to in `nc' mode */
     /* Telnet options */
     char termtype[32];
     char termspeed[32];
diff --git a/ssh.c b/ssh.c
index 7c5c7bc..6380b09 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -729,6 +729,7 @@ struct ssh_tag {
 
     tree234 *channels;                /* indexed by local id */
     struct ssh_channel *mainchan;      /* primary session channel */
+    int ncmode;                               /* is primary channel direct-tcpip? */
     int exitcode;
     int close_expected;
     int clean_exit;
@@ -7622,7 +7623,59 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Create the main session channel.
      */
-    if (!ssh->cfg.ssh_no_shell) {
+    if (ssh->cfg.ssh_no_shell) {
+       ssh->mainchan = NULL;
+    } else if (*ssh->cfg.ssh_nc_host) {
+       /*
+        * Just start a direct-tcpip channel and use it as the main
+        * channel.
+        */
+       ssh->mainchan = snew(struct ssh_channel);
+       ssh->mainchan->ssh = ssh;
+       ssh->mainchan->localid = alloc_channel_id(ssh);
+       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
+       ssh2_pkt_addstring(s->pktout, "direct-tcpip");
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
+       ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE;
+       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */
+       ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT);      /* our max pkt size */
+       ssh2_pkt_addstring(s->pktout, ssh->cfg.ssh_nc_host);
+       ssh2_pkt_adduint32(s->pktout, ssh->cfg.ssh_nc_port);
+       /*
+        * We make up values for the originator data; partly it's
+        * too much hassle to keep track, and partly I'm not
+        * convinced the server should be told details like that
+        * about my local network configuration.
+        * The "originator IP address" is syntactically a numeric
+        * IP address, and some servers (e.g., Tectia) get upset
+        * if it doesn't match this syntax.
+        */
+       ssh2_pkt_addstring(s->pktout, "0.0.0.0");
+       ssh2_pkt_adduint32(s->pktout, 0);
+       ssh2_pkt_send(ssh, s->pktout);
+
+       crWaitUntilV(pktin);
+       if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
+           bombout(("Server refused to open a direct-tcpip channel"));
+           crStopV;
+           /* FIXME: error data comes back in FAILURE packet */
+       }
+       if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) {
+           bombout(("Server's channel confirmation cited wrong channel"));
+           crStopV;
+       }
+       ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
+       ssh->mainchan->halfopen = FALSE;
+       ssh->mainchan->type = CHAN_MAINSESSION;
+       ssh->mainchan->closes = 0;
+       ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
+       ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
+       bufchain_init(&ssh->mainchan->v.v2.outbuffer);
+       add234(ssh->channels, ssh->mainchan);
+       update_specials_menu(ssh->frontend);
+       logevent("Opened direct-tcpip channel in place of session");
+       ssh->ncmode = TRUE;
+    } else {
        ssh->mainchan = snew(struct ssh_channel);
        ssh->mainchan->ssh = ssh;
        ssh->mainchan->localid = alloc_channel_id(ssh);
@@ -7653,8 +7706,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        add234(ssh->channels, ssh->mainchan);
        update_specials_menu(ssh->frontend);
        logevent("Opened channel for session");
-    } else
-       ssh->mainchan = NULL;
+       ssh->ncmode = FALSE;
+    }
 
     /*
      * Now we have a channel, make dispatch table entries for
@@ -7677,7 +7730,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Potentially enable X11 forwarding.
      */
-    if (ssh->mainchan && ssh->cfg.x11_forward) {
+    if (ssh->mainchan && !ssh->ncmode && ssh->cfg.x11_forward) {
        char proto[20], data[64];
        logevent("Requesting X11 forwarding");
        ssh->x11auth = x11_invent_auth(proto, sizeof(proto),
@@ -7725,7 +7778,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Potentially enable agent forwarding.
      */
-    if (ssh->mainchan && ssh->cfg.agentfwd && agent_exists()) {
+    if (ssh->mainchan && !ssh->ncmode && ssh->cfg.agentfwd && agent_exists()) {
        logevent("Requesting OpenSSH-style agent forwarding");
        s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->remoteid);
@@ -7751,7 +7804,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Now allocate a pty for the session.
      */
-    if (ssh->mainchan && !ssh->cfg.nopty) {
+    if (ssh->mainchan && !ssh->ncmode && !ssh->cfg.nopty) {
        /* Unpick the terminal-speed string. */
        /* XXX perhaps we should allow no speeds to be sent. */
         ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
@@ -7801,7 +7854,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
      * Simplest thing here is to send all the requests at once, and
      * then wait for a whole bunch of successes or failures.
      */
-    if (ssh->mainchan && *ssh->cfg.environmt) {
+    if (ssh->mainchan && !ssh->ncmode && *ssh->cfg.environmt) {
        char *e = ssh->cfg.environmt;
        char *var, *varend, *val;
 
@@ -7866,7 +7919,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
      * this twice if the config data has provided a second choice
      * of command.
      */
-    if (ssh->mainchan) while (1) {
+    if (ssh->mainchan && !ssh->ncmode) while (1) {
        int subsys;
        char *cmd;
 
index 38129f9..1d3db4c 100644 (file)
@@ -813,7 +813,7 @@ int main(int argc, char **argv)
        cfg.host[p1] = '\0';
     }
 
-    if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd)
+    if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host)
        flags |= FLAG_INTERACTIVE;
 
     /*
index 562d26a..c79c1e5 100644 (file)
@@ -502,7 +502,7 @@ int main(int argc, char **argv)
        cfg.host[p1] = '\0';
     }
 
-    if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd)
+    if (!cfg.remote_cmd_ptr && !*cfg.remote_cmd && !*cfg.ssh_nc_host)
        flags |= FLAG_INTERACTIVE;
 
     /*