From 820ebe3b54a21cfb8d42e43c567d952415c1d05d Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 5 Apr 2003 11:45:21 +0000 Subject: [PATCH] Dynamic port forwarding by means of a local SOCKS server. Fully supports SOCKS 4, SOCKS 4A and SOCKS 5 (well, actually IPv6 in SOCKS 5 isn't supported, but it'll be no difficulty once I actually get round to it). Thanks to Chas Honton for his `stone soup' patch: I didn't end up actually using any of his code, but it galvanised me into doing it properly myself :-) git-svn-id: svn://svn.tartarus.org/sgt/putty@3055 cda61777-01e9-0310-a592-d414129be87e --- cmdline.c | 43 +++++---- config.c | 33 ++++--- plink.c | 1 + portfwd.c | 290 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- putty.h | 13 ++- ssh.c | 114 ++++++++++++++--------- ssh.h | 1 + unix/uxplink.c | 1 + 8 files changed, 394 insertions(+), 102 deletions(-) diff --git a/cmdline.c b/cmdline.c index 0038ad17..eb98f606 100644 --- a/cmdline.c +++ b/cmdline.c @@ -166,44 +166,47 @@ int cmdline_process_param(char *p, char *value, int need_save, Config *cfg) strncpy(cfg->username, value, sizeof(cfg->username)); cfg->username[sizeof(cfg->username) - 1] = '\0'; } - if ((!strcmp(p, "-L") || !strcmp(p, "-R"))) { + if ((!strcmp(p, "-L") || !strcmp(p, "-R") || !strcmp(p, "-D"))) { char *fwd, *ptr, *q, *qq; - int i=0; + int dynamic, i=0; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); + dynamic = !strcmp(p, "-D"); fwd = value; ptr = cfg->portfwd; /* if multiple forwards, find end of list */ - if (ptr[0]=='R' || ptr[0]=='L') { + if (ptr[0]=='R' || ptr[0]=='L' || ptr[0] == 'D') { for (i = 0; i < sizeof(cfg->portfwd) - 2; i++) if (ptr[i]=='\000' && ptr[i+1]=='\000') break; ptr = ptr + i + 1; /* point to next forward slot */ } - ptr[0] = p[1]; /* insert a 'L' or 'R' at the start */ + ptr[0] = p[1]; /* insert a 'L', 'R' or 'D' at the start */ if (strlen(fwd) > sizeof(cfg->portfwd) - i - 2) { cmdline_error("out of space for port forwardings"); return ret; } strncpy(ptr+1, fwd, sizeof(cfg->portfwd) - i); - /* - * We expect _at least_ two colons in this string. The - * possible formats are `sourceport:desthost:destport', or - * `sourceip:sourceport:desthost:destport' if you're - * specifying a particular loopback address. We need to - * replace the one between source and dest with a \t; this - * means we must find the second-to-last colon in the - * string. - */ - q = qq = strchr(ptr, ':'); - while (qq) { - char *qqq = strchr(qq+1, ':'); - if (qqq) - q = qq; - qq = qqq; + if (!dynamic) { + /* + * We expect _at least_ two colons in this string. The + * possible formats are `sourceport:desthost:destport', + * or `sourceip:sourceport:desthost:destport' if you're + * specifying a particular loopback address. We need to + * replace the one between source and dest with a \t; + * this means we must find the second-to-last colon in + * the string. + */ + q = qq = strchr(ptr, ':'); + while (qq) { + char *qqq = strchr(qq+1, ':'); + if (qqq) + q = qq; + qq = qqq; + } + if (q) *q = '\t'; /* replace second-last colon with \t */ } - if (q) *q = '\t'; /* replace second-last colon with \t */ cfg->portfwd[sizeof(cfg->portfwd) - 1] = '\0'; cfg->portfwd[sizeof(cfg->portfwd) - 2] = '\0'; ptr[strlen(ptr)+1] = '\000'; /* append two '\000' */ diff --git a/config.c b/config.c index 7204a6c3..0ce6f063 100644 --- a/config.c +++ b/config.c @@ -634,24 +634,31 @@ static void portfwd_handler(union control *ctrl, void *dlg, if (ctrl == pfd->addbutton) { char str[sizeof(cfg->portfwd)]; char *p; - if (dlg_radiobutton_get(pfd->direction, dlg) == 0) + int whichbutton = dlg_radiobutton_get(pfd->direction, dlg); + if (whichbutton == 0) str[0] = 'L'; - else + else if (whichbutton == 1) str[0] = 'R'; + else + str[0] = 'D'; dlg_editbox_get(pfd->sourcebox, dlg, str+1, sizeof(str) - 2); if (!str[1]) { dlg_error_msg(dlg, "You need to specify a source port number"); return; } p = str + strlen(str); - *p++ = '\t'; - dlg_editbox_get(pfd->destbox, dlg, p, sizeof(str)-1 - (p - str)); - if (!*p || !strchr(p, ':')) { - dlg_error_msg(dlg, - "You need to specify a destination address\n" - "in the form \"host.name:port\""); - return; - } + if (str[0] != 'D') { + *p++ = '\t'; + dlg_editbox_get(pfd->destbox, dlg, p, + sizeof(str)-1 - (p - str)); + if (!*p || !strchr(p, ':')) { + dlg_error_msg(dlg, + "You need to specify a destination address\n" + "in the form \"host.name:port\""); + return; + } + } else + *p = '\0'; p = cfg->portfwd; while (*p) { while (*p) @@ -1550,11 +1557,13 @@ void setup_config_box(struct controlbox *b, struct sesslist *sesslist, pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67, HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd), P(NULL)); - pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 2, + pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd), "Local", 'l', P(NULL), - "Remote", 'm', P(NULL), NULL); + "Remote", 'm', P(NULL), + "Dynamic", 'y', P(NULL), + NULL); ctrl_tabdelay(s, pfd->addbutton); ctrl_columns(s, 1, 100); diff --git a/plink.c b/plink.c index 3848214c..d61bbeb5 100644 --- a/plink.c +++ b/plink.c @@ -207,6 +207,7 @@ static void usage(void) printf(" -batch disable all interactive prompts\n"); printf("The following options only apply to SSH connections:\n"); printf(" -pw passw login with specified password\n"); + printf(" -D listen-port Dynamic SOCKS-based port forwarding\n"); printf(" -L listen-port:host:port Forward local port to " "remote address\n"); printf(" -R listen-port:host:port Forward remote port to" diff --git a/portfwd.c b/portfwd.c index 00c6cfce..03cbd67c 100644 --- a/portfwd.c +++ b/portfwd.c @@ -51,11 +51,6 @@ (cp)[0] = (value) >> 8, \ (cp)[1] = (value) ) -struct pfwd_queue { - struct pfwd_queue *next; - char *buf; -}; - struct PFwdPrivate { const struct plug_function_table *fn; /* the above variable absolutely *must* be the first in this structure */ @@ -63,14 +58,34 @@ struct PFwdPrivate { void *backhandle; /* instance of SSH backend itself */ /* Note that backhandle need not be filled in if c is non-NULL */ Socket s; - char hostname[128]; int throttled, throttle_override; - int port; int ready; - struct pfwd_queue *waiting; + /* + * `dynamic' does double duty. It's set to 0 for an ordinary + * forwarded port, and nonzero for SOCKS-style dynamic port + * forwarding; but it also represents the state of the SOCKS + * exchange. + */ + int dynamic; + /* + * `hostname' and `port' are the real hostname and port, once + * we know what we're connecting to; they're unused for this + * purpose while conducting a local SOCKS exchange, which means + * we can also use them as a buffer and pointer for reading + * data from the SOCKS client. + */ + char hostname[256]; + int port; + /* + * When doing dynamic port forwarding, we can receive + * connection data before we are actually able to send it; so + * we may have to temporarily hold some in a dynamically + * allocated buffer here. + */ + void *buffer; + int buflen; }; - static int pfd_closing(Plug plug, char *error_msg, int error_code, int calling_back) { @@ -81,7 +96,8 @@ static int pfd_closing(Plug plug, char *error_msg, int error_code, * so if an error occurred on the socket, we just ignore it * and treat it like a proper close. */ - sshfwd_close(pr->c); + if (pr->c) + sshfwd_close(pr->c); pfd_close(pr->s); return 1; } @@ -89,6 +105,212 @@ static int pfd_closing(Plug plug, char *error_msg, int error_code, static int pfd_receive(Plug plug, int urgent, char *data, int len) { struct PFwdPrivate *pr = (struct PFwdPrivate *) plug; + if (pr->dynamic) { + while (len--) { + if (pr->port >= lenof(pr->hostname)) { + if ((pr->dynamic >> 12) == 4) { + /* Send back a SOCKS 4 error before closing. */ + char data[8]; + memset(data, 0, sizeof(data)); + data[1] = 91; /* generic `request rejected' */ + sk_write(pr->s, data, 8); + } + pfd_close(pr->s); + return 1; + } + pr->hostname[pr->port++] = *data++; + + /* + * Now check what's in the buffer to see if it's a + * valid and complete message in the SOCKS exchange. + */ + if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 4) && + pr->hostname[0] == 4) { + /* + * SOCKS 4. + */ + if (pr->dynamic == 1) + pr->dynamic = 0x4000; + if (pr->port < 2) continue;/* don't have command code yet */ + if (pr->hostname[1] != 1) { + /* Send back a SOCKS 4 error before closing. */ + char data[8]; + memset(data, 0, sizeof(data)); + data[1] = 91; /* generic `request rejected' */ + sk_write(pr->s, data, 8); + pfd_close(pr->s); + return 1; + } + if (pr->port < 8) continue; /* haven't started username */ + if (pr->hostname[pr->port-1] != 0) + continue; /* haven't _finished_ username */ + /* + * Now we have a full SOCKS 4 request. Check it to + * see if it's a SOCKS 4A request. + */ + if (pr->hostname[4] == 0 && pr->hostname[5] == 0 && + pr->hostname[6] == 0 && pr->hostname[7] != 0) { + /* + * It's SOCKS 4A. So if we haven't yet + * collected the host name, we should continue + * waiting for data in order to do so; if we + * have, we can go ahead. + */ + int len; + if (pr->dynamic == 0x4000) { + pr->dynamic = 0x4001; + continue; + } + pr->hostname[0] = 0; /* reply version code */ + pr->hostname[1] = 90; /* request granted */ + sk_write(pr->s, pr->hostname, 8); + pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2); + len = strlen(pr->hostname+8); + memmove(pr->hostname, pr->hostname + 8 + len + 1, + lenof(pr->hostname) - (8 + len + 1)); + goto connect; + } else { + /* + * It's SOCKS 4, which means we should format + * the IP address into the hostname string and + * then just go. + */ + pr->hostname[0] = 0; /* reply version code */ + pr->hostname[1] = 90; /* request granted */ + sk_write(pr->s, pr->hostname, 8); + pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2); + sprintf(pr->hostname, "%d.%d.%d.%d", + (unsigned char)pr->hostname[4], + (unsigned char)pr->hostname[5], + (unsigned char)pr->hostname[6], + (unsigned char)pr->hostname[7]); + goto connect; + } + } + + if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 5) && + pr->hostname[0] == 5) { + /* + * SOCKS 5. + */ + if (pr->dynamic == 1) + pr->dynamic = 0x5000; + + if (pr->dynamic == 0x5000) { + int i, method; + char data[2]; + /* + * We're receiving a set of method identifiers. + */ + if (pr->port < 2) continue;/* no method count yet */ + if (pr->port < 2 + (unsigned char)pr->hostname[1]) + continue; /* no methods yet */ + method = 0xFF; /* invalid */ + for (i = 0; i < (unsigned char)pr->hostname[1]; i++) + if (pr->hostname[2+i] == 0) { + method = 0;/* no auth */ + break; + } + data[0] = 5; + data[1] = method; + sk_write(pr->s, data, 2); + pr->dynamic = 0x5001; + pr->port = 0; /* re-empty the buffer */ + continue; + } + + if (pr->dynamic == 0x5001) { + int atype, alen; + if (pr->port < 6) continue; + atype = (unsigned char)pr->hostname[3]; + if (atype == 1) /* IPv4 address */ + alen = 4; + if (atype == 4) /* IPv6 address */ + alen = 16; + if (atype == 3) /* domain name has leading length */ + alen = 1 + (unsigned char)pr->hostname[4]; + if (pr->port < 6 + alen) continue; + if (pr->hostname[1] != 1 || pr->hostname[2] != 0) { + pr->hostname[1] = 1; /* generic failure */ + pr->hostname[2] = 0; /* reserved */ + sk_write(pr->s, pr->hostname, pr->port); + pfd_close(pr->s); + return 1; + } + /* + * Now we have a viable connect request. Switch + * on atype. + */ + pr->port = GET_16BIT_MSB_FIRST(pr->hostname+4+alen); + if (atype == 1) { + pr->hostname[1] = 0; /* succeeded */ + sk_write(pr->s, pr->hostname, alen + 6); + sprintf(pr->hostname, "%d.%d.%d.%d", + (unsigned char)pr->hostname[4], + (unsigned char)pr->hostname[5], + (unsigned char)pr->hostname[6], + (unsigned char)pr->hostname[7]); + goto connect; + } else if (atype == 3) { + pr->hostname[1] = 0; /* succeeded */ + sk_write(pr->s, pr->hostname, alen + 6); + memmove(pr->hostname, pr->hostname + 5, alen-1); + pr->hostname[alen-1] = '\0'; + goto connect; + } else { + /* + * Unknown address type. (FIXME: support IPv6!) + */ + pr->hostname[1] = 8; /* atype not supported */ + sk_write(pr->s, pr->hostname, pr->port); + pfd_close(pr->s); + return 1; + } + } + } + + /* + * If we get here without either having done `continue' + * or `goto connect', it must be because there is no + * sensible interpretation of what's in our buffer. So + * close the connection rudely. + */ + pfd_close(pr->s); + return 1; + } + return 1; + + /* + * We come here when we're ready to make an actual + * connection. + */ + connect: + + pr->c = new_sock_channel(pr->backhandle, pr->s); + if (pr->c == NULL) { + pfd_close(pr->s); + return 1; + } else { + /* asks to forward to the specified host/port for this */ + ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding"); + } + pr->dynamic = 0; + + /* + * Now freeze the socket until the SSH server confirms the + * connection. + */ + sk_set_frozen(pr->s, 1); + /* + * If there's any data remaining in our current buffer, + * save it to be sent on pfd_confirm(). + */ + if (len > 0) { + pr->buffer = snewn(len, char); + memcpy(pr->buffer, data, len); + pr->buflen = len; + } + } if (pr->ready) { if (sshfwd_write(pr->c, data, len) > 0) { pr->throttled = 1; @@ -102,7 +324,8 @@ static void pfd_sent(Plug plug, int bufsize) { struct PFwdPrivate *pr = (struct PFwdPrivate *) plug; - sshfwd_unthrottle(pr->c, bufsize); + if (pr->c) + sshfwd_unthrottle(pr->c, bufsize); } /* @@ -133,6 +356,7 @@ char *pfd_newconnect(Socket *s, char *hostname, int port, void *c, * Open socket. */ pr = snew(struct PFwdPrivate); + pr->buffer = NULL; pr->fn = &fn_table; pr->throttled = pr->throttle_override = 0; pr->ready = 1; @@ -169,6 +393,7 @@ static int pfd_accepting(Plug p, void *sock) org = (struct PFwdPrivate *)p; pr = snew(struct PFwdPrivate); + pr->buffer = NULL; pr->fn = &fn_table; pr->c = NULL; @@ -180,22 +405,27 @@ static int pfd_accepting(Plug p, void *sock) return err != NULL; } - pr->c = new_sock_channel(org->backhandle, s); + sk_set_private_ptr(s, pr); - strcpy(pr->hostname, org->hostname); - pr->port = org->port; pr->throttled = pr->throttle_override = 0; pr->ready = 0; - pr->waiting = NULL; - sk_set_private_ptr(s, pr); - - if (pr->c == NULL) { - sfree(pr); - return 1; + if (org->dynamic) { + pr->dynamic = 1; + pr->port = 0; /* hostname buffer is so far empty */ + sk_set_frozen(s, 0); /* we want to receive SOCKS _now_! */ } else { - /* asks to forward to the specified host/port for this */ - ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding"); + strcpy(pr->hostname, org->hostname); + pr->port = org->port; + pr->c = new_sock_channel(org->backhandle, s); + + if (pr->c == NULL) { + sfree(pr); + return 1; + } else { + /* asks to forward to the specified host/port for this */ + ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding"); + } } return 0; @@ -223,13 +453,17 @@ char *pfd_addforward(char *desthost, int destport, char *srcaddr, int port, * Open socket. */ pr = snew(struct PFwdPrivate); + pr->buffer = NULL; pr->fn = &fn_table; pr->c = NULL; - strcpy(pr->hostname, desthost); - pr->port = destport; + if (desthost) { + strcpy(pr->hostname, desthost); + pr->port = destport; + pr->dynamic = 0; + } else + pr->dynamic = 1; pr->throttled = pr->throttle_override = 0; pr->ready = 0; - pr->waiting = NULL; pr->backhandle = backhandle; pr->s = s = new_listener(srcaddr, port, (Plug) pr, @@ -253,6 +487,7 @@ void pfd_close(Socket s) pr = (struct PFwdPrivate *) sk_get_private_ptr(s); + sfree(pr->buffer); sfree(pr); sk_close(s); @@ -302,4 +537,9 @@ void pfd_confirm(Socket s) pr->ready = 1; sk_set_frozen(s, 0); sk_write(s, NULL, 0); + if (pr->buffer) { + sshfwd_write(pr->c, pr->buffer, pr->buflen); + sfree(pr->buffer); + pr->buffer = NULL; + } } diff --git a/putty.h b/putty.h index d1c89a25..cff103bb 100644 --- a/putty.h +++ b/putty.h @@ -442,7 +442,18 @@ struct config_tag { /* port forwarding */ int lport_acceptall; /* accept conns from hosts other than localhost */ int rport_acceptall; /* same for remote forwarded ports (SSH2 only) */ - char portfwd[1024]; /* [LR]localport\thost:port\000[LR]localport\thost:port\000\000 */ + /* + * The port forwarding string contains a number of + * NUL-terminated substrings, terminated in turn by an empty + * string (i.e. a second NUL immediately after the previous + * one). Each string can be of one of the following forms: + * + * [LR]localport\thost:port + * [LR]localaddr:localport\thost:port + * Dlocalport + * Dlocaladdr:localport + */ + char portfwd[1024]; /* SSH bug compatibility modes */ int sshbug_ignore1, sshbug_plainpw1, sshbug_rsa1, sshbug_hmac2, sshbug_derivekey2, sshbug_rsapad2, diff --git a/ssh.c b/ssh.c index b696e46a..74726251 100644 --- a/ssh.c +++ b/ssh.c @@ -3149,30 +3149,34 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt) if (n < 255) sports[n++] = *ssh->portfwd_strptr++; } sports[n] = 0; - if (*ssh->portfwd_strptr == '\t') - ssh->portfwd_strptr++; - n = 0; - while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != ':') { - if (n < 255) host[n++] = *ssh->portfwd_strptr++; - } - host[n] = 0; - if (*ssh->portfwd_strptr == ':') + if (type != 'D') { + if (*ssh->portfwd_strptr == '\t') + ssh->portfwd_strptr++; + n = 0; + while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != ':') { + if (n < 255) host[n++] = *ssh->portfwd_strptr++; + } + host[n] = 0; + if (*ssh->portfwd_strptr == ':') + ssh->portfwd_strptr++; + n = 0; + while (*ssh->portfwd_strptr) { + if (n < 255) dports[n++] = *ssh->portfwd_strptr++; + } + dports[n] = 0; ssh->portfwd_strptr++; - n = 0; - while (*ssh->portfwd_strptr) { - if (n < 255) dports[n++] = *ssh->portfwd_strptr++; - } - dports[n] = 0; - ssh->portfwd_strptr++; - dport = atoi(dports); - dserv = 0; - if (dport == 0) { - dserv = 1; - dport = net_service_lookup(dports); - if (!dport) { - logeventf(ssh, "Service lookup failed for" - " destination port \"%s\"", dports); + dport = atoi(dports); + dserv = 0; + if (dport == 0) { + dserv = 1; + dport = net_service_lookup(dports); + if (!dport) { + logeventf(ssh, "Service lookup failed for" + " destination port \"%s\"", dports); + } } + } else { + while (*ssh->portfwd_strptr) ssh->portfwd_strptr++; } sport = atoi(sports); sserv = 0; @@ -3197,6 +3201,15 @@ static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt) host, (int)(dserv ? strlen(dports) : 0), dports, dserv, "(", dport, dserv, ")"); + } else if (type == 'D') { + pfd_addforward(NULL, -1, *saddr ? saddr : NULL, + sport, ssh, &ssh->cfg); + logeventf(ssh, "Local port %.*s%.*s%.*s%.*s%d%.*s" + " doing SOCKS dynamic forwarding", + (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL, + (int)(*saddr?1:0), ":", + (int)(sserv ? strlen(sports) : 0), sports, + sserv, "(", sport, sserv, ")"); } else { struct ssh_rportfwd *pf; pf = snew(struct ssh_rportfwd); @@ -5230,30 +5243,34 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) if (n < 255) sports[n++] = *ssh->portfwd_strptr++; } sports[n] = 0; - if (*ssh->portfwd_strptr == '\t') - ssh->portfwd_strptr++; - n = 0; - while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != ':') { - if (n < 255) host[n++] = *ssh->portfwd_strptr++; - } - host[n] = 0; - if (*ssh->portfwd_strptr == ':') + if (type != 'D') { + if (*ssh->portfwd_strptr == '\t') + ssh->portfwd_strptr++; + n = 0; + while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != ':') { + if (n < 255) host[n++] = *ssh->portfwd_strptr++; + } + host[n] = 0; + if (*ssh->portfwd_strptr == ':') + ssh->portfwd_strptr++; + n = 0; + while (*ssh->portfwd_strptr) { + if (n < 255) dports[n++] = *ssh->portfwd_strptr++; + } + dports[n] = 0; ssh->portfwd_strptr++; - n = 0; - while (*ssh->portfwd_strptr) { - if (n < 255) dports[n++] = *ssh->portfwd_strptr++; - } - dports[n] = 0; - ssh->portfwd_strptr++; - dport = atoi(dports); - dserv = 0; - if (dport == 0) { - dserv = 1; - dport = net_service_lookup(dports); - if (!dport) { - logeventf(ssh, "Service lookup failed for destination" - " port \"%s\"", dports); + dport = atoi(dports); + dserv = 0; + if (dport == 0) { + dserv = 1; + dport = net_service_lookup(dports); + if (!dport) { + logeventf(ssh, "Service lookup failed for destination" + " port \"%s\"", dports); + } } + } else { + while (*ssh->portfwd_strptr) ssh->portfwd_strptr++; } sport = atoi(sports); sserv = 0; @@ -5278,6 +5295,15 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt) host, (int)(dserv ? strlen(dports) : 0), dports, dserv, "(", dport, dserv, ")"); + } else if (type == 'D') { + pfd_addforward(NULL, -1, *saddr ? saddr : NULL, + sport, ssh, &ssh->cfg); + logeventf(ssh, "Local port %.*s%.*s%.*s%.*s%d%.*s" + " doing SOCKS dynamic forwarding", + (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL, + (int)(*saddr?1:0), ":", + (int)(sserv ? strlen(sports) : 0), sports, + sserv, "(", sport, sserv, ")"); } else { struct ssh_rportfwd *pf; pf = snew(struct ssh_rportfwd); diff --git a/ssh.h b/ssh.h index 766e2e3c..e0f82f34 100644 --- a/ssh.h +++ b/ssh.h @@ -259,6 +259,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org); /* Exports from portfwd.c */ extern char *pfd_newconnect(Socket * s, char *hostname, int port, void *c, const Config *cfg); +/* desthost == NULL indicates dynamic (SOCKS) port forwarding */ extern char *pfd_addforward(char *desthost, int destport, char *srcaddr, int port, void *backhandle, const Config *cfg); extern void pfd_close(Socket s); diff --git a/unix/uxplink.c b/unix/uxplink.c index 451d1121..9fce0560 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -263,6 +263,7 @@ static void usage(void) printf(" -batch disable all interactive prompts\n"); printf("The following options only apply to SSH connections:\n"); printf(" -pw passw login with specified password\n"); + printf(" -D listen-port Dynamic SOCKS-based port forwarding\n"); printf(" -L listen-port:host:port Forward local port to " "remote address\n"); printf(" -R listen-port:host:port Forward remote port to" -- 2.11.0