From feb02b4e2d7935972dc94bb78bf1a36bb7bdd1b3 Mon Sep 17 00:00:00 2001 From: simon Date: Mon, 28 Aug 2006 15:12:37 +0000 Subject: [PATCH] New command-line option in Plink (and PuTTY, though it's less useful 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 | 22 +++++++++++++++++ doc/config.but | 5 ++++ doc/using.but | 41 ++++++++++++++++++++++++++++++++ putty.h | 2 ++ ssh.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++------- unix/uxplink.c | 2 +- windows/winplink.c | 2 +- 7 files changed, 133 insertions(+), 10 deletions(-) diff --git a/cmdline.c b/cmdline.c index e8343427..79f29816 100644 --- 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; diff --git a/doc/config.but b/doc/config.but index 42fa3aa0..e678db05 100644 --- a/doc/config.but +++ b/doc/config.but @@ -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 diff --git a/doc/using.but b/doc/using.but index 0fb7127b..af98d905 100644 --- a/doc/using.but +++ b/doc/using.but @@ -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 22ec8259..c648921b 100644 --- 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 7c5c7bce..6380b09d 100644 --- 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; diff --git a/unix/uxplink.c b/unix/uxplink.c index 38129f96..1d3db4c2 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -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; /* diff --git a/windows/winplink.c b/windows/winplink.c index 562d26af..c79c1e5d 100644 --- a/windows/winplink.c +++ b/windows/winplink.c @@ -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; /* -- 2.11.0