From 84328ddb72b558b47ac1e59f15c7fce1653bd550 Mon Sep 17 00:00:00 2001 From: simon Date: Thu, 30 Dec 2004 17:48:35 +0000 Subject: [PATCH] Jacob points out that when we reconfigure port forwarding, we ought to be destroying old ones _before_ creating new ones, so that we can reuse a port for a new purpose without colliding with ourselves. Also fixed port forwarding, which my IPv6 checkin had completely funted :-) git-svn-id: svn://svn.tartarus.org/sgt/putty@5049 cda61777-01e9-0310-a592-d414129be87e --- ssh.c | 306 ++++++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 156 insertions(+), 150 deletions(-) diff --git a/ssh.c b/ssh.c index 170253b2..163b2ea3 100644 --- a/ssh.c +++ b/ssh.c @@ -516,7 +516,7 @@ struct ssh_rportfwd { * it. */ struct ssh_portfwd { - int keep; + enum { DESTROY, KEEP, CREATE } status; int type; unsigned sport, dport; char *saddr, *daddr; @@ -864,6 +864,10 @@ static int ssh_portcmp(void *av, void *bv) return +1; if (a->type < b->type) return -1; + if (a->addressfamily > b->addressfamily) + return +1; + if (a->addressfamily < b->addressfamily) + return -1; if ( (i = nullstrcmp(a->saddr, b->saddr)) != 0) return i < 0 ? -1 : +1; if (a->sport > b->sport) @@ -3670,43 +3674,42 @@ static void ssh_rportfwd_succfail(Ssh ssh, struct Packet *pktin, void *ctx) static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) { - char address_family, type; - int n; - int sport,dport,sserv,dserv; - char sports[256], dports[256], saddr[256], host[256]; - const char *portfwd_strptr; - - portfwd_strptr = cfg->portfwd; + const char *portfwd_strptr = cfg->portfwd; + struct ssh_portfwd *epf; + int i; if (!ssh->portfwds) { ssh->portfwds = newtree234(ssh_portcmp); } else { /* * Go through the existing port forwardings and tag them - * with keep==FALSE. Any that we want to keep will be - * re-enabled as we go through the configuration and find - * out which bits are the same as they were before. + * with status==DESTROY. Any that we want to keep will be + * re-enabled (status==KEEP) as we go through the + * configuration and find out which bits are the same as + * they were before. */ struct ssh_portfwd *epf; int i; for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++) - epf->keep = FALSE; + epf->status = DESTROY; } while (*portfwd_strptr) { + char address_family, type; + int sport,dport,sserv,dserv; + char sports[256], dports[256], saddr[256], host[256]; + int n; + address_family = 'A'; type = 'L'; - while (*portfwd_strptr && *portfwd_strptr != '\t') { - if (*portfwd_strptr == 'A' || - *portfwd_strptr == '4' || - *portfwd_strptr == '6') - address_family = *portfwd_strptr; - else if (*portfwd_strptr == 'L' || - *portfwd_strptr == 'R' || - *portfwd_strptr == 'D') - type = *portfwd_strptr; - portfwd_strptr++; - } + if (*portfwd_strptr == 'A' || + *portfwd_strptr == '4' || + *portfwd_strptr == '6') + address_family = *portfwd_strptr++; + if (*portfwd_strptr == 'L' || + *portfwd_strptr == 'R' || + *portfwd_strptr == 'D') + type = *portfwd_strptr++; saddr[0] = '\0'; @@ -3777,18 +3780,12 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) if (sport && dport) { /* Set up a description of the source port. */ struct ssh_portfwd *pfrec, *epfrec; - char *sportdesc; - sportdesc = dupprintf("%.*s%.*s%.*s%.*s%d%.*s", - (int)(*saddr?strlen(saddr):0), *saddr?saddr:NULL, - (int)(*saddr?1:0), ":", - (int)(sserv ? strlen(sports) : 0), sports, - sserv, "(", sport, sserv, ")"); pfrec = snew(struct ssh_portfwd); pfrec->type = type; pfrec->saddr = *saddr ? dupstr(saddr) : NULL; pfrec->sport = sport; - pfrec->daddr = dupstr(host); + pfrec->daddr = *host ? dupstr(host) : NULL; pfrec->dport = dport; pfrec->local = NULL; pfrec->remote = NULL; @@ -3801,41 +3798,124 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) /* * We already have a port forwarding with precisely * these parameters. Hence, no need to do anything; - * simply tag the existing one as `keep'. + * simply tag the existing one as KEEP. */ - epfrec->keep = TRUE; + epfrec->status = KEEP; free_portfwd(pfrec); - } else if (type == 'L') { - /* Verbose description of the destination port */ - char *dportdesc = dupprintf("%s:%.*s%.*s%d%.*s", - host, - (int)(dserv ? strlen(dports) : 0), dports, - dserv, "(", dport, dserv, ")"); - const char *err = pfd_addforward(host, dport, - *saddr ? saddr : NULL, - sport, ssh, &ssh->cfg, - &pfrec->local, - pfrec->addressfamily); - - logeventf(ssh, "Local %sport %s forward to %s%s%s", - pfrec->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " : - pfrec->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "", - sportdesc, dportdesc, - err ? " failed: " : "", err); + } else { + pfrec->status = CREATE; + } + } + } + + /* + * Now go through and destroy any port forwardings which were + * not re-enabled. + */ + for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++) + if (epf->status == DESTROY) { + char *message; + + message = dupprintf("%s port forwarding from %s%s%d", + epf->type == 'L' ? "local" : + epf->type == 'R' ? "remote" : "dynamic", + epf->saddr ? epf->saddr : "", + epf->saddr ? ":" : "", + epf->sport); + + if (epf->type != 'D') { + char *msg2 = dupprintf("%s to %s:%d", message, + epf->daddr, epf->dport); + sfree(message); + message = msg2; + } + + logeventf(ssh, "Cancelling %s", message); + sfree(message); + + if (epf->remote) { + struct ssh_rportfwd *rpf = epf->remote; + struct Packet *pktout; + + /* + * Cancel the port forwarding at the server + * end. + */ + if (ssh->version == 1) { + /* + * We cannot cancel listening ports on the + * server side in SSH1! There's no message + * to support it. Instead, we simply remove + * the rportfwd record from the local end + * so that any connections the server tries + * to make on it are rejected. + */ + } else { + pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST); + ssh2_pkt_addstring(pktout, "cancel-tcpip-forward"); + ssh2_pkt_addbool(pktout, 0);/* _don't_ want reply */ + if (epf->saddr) { + ssh2_pkt_addstring(pktout, epf->saddr); + } else if (ssh->cfg.rport_acceptall) { + ssh2_pkt_addstring(pktout, "0.0.0.0"); + } else { + ssh2_pkt_addstring(pktout, "127.0.0.1"); + } + ssh2_pkt_adduint32(pktout, epf->sport); + ssh2_pkt_send(ssh, pktout); + } + + del234(ssh->rportfwds, rpf); + free_rportfwd(rpf); + } else if (epf->local) { + pfd_terminate(epf->local); + } + + delpos234(ssh->portfwds, i); + free_portfwd(epf); + i--; /* so we don't skip one in the list */ + } + + /* + * And finally, set up any new port forwardings (status==CREATE). + */ + for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++) + if (epf->status == CREATE) { + char *sportdesc, *dportdesc; + sportdesc = dupprintf("%s%s%d", + epf->saddr ? epf->saddr : "", + epf->saddr ? ":" : "", + epf->sport); + if (epf->type == 'D') { + dportdesc = NULL; + } else { + dportdesc = dupprintf("%s:%d", epf->daddr, epf->dport); + } + + if (epf->type == 'L') { + const char *err = pfd_addforward(epf->daddr, epf->dport, + epf->saddr, epf->sport, + ssh, &ssh->cfg, + &epf->local, + epf->addressfamily); - sfree(dportdesc); - } else if (type == 'D') { + logeventf(ssh, "Local %sport %s forwarding to %s%s%s", + epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " : + epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "", + sportdesc, dportdesc, + err ? " failed: " : "", err ? err : ""); + } else if (epf->type == 'D') { const char *err = pfd_addforward(NULL, -1, - *saddr ? saddr : NULL, - sport, ssh, &ssh->cfg, - &pfrec->local, - pfrec->addressfamily); - - logeventf(ssh, "Local %sport %s SOCKS dynamic forward%s%s", - pfrec->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " : - pfrec->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "", - sportdesc, - err ? " setup failed: " : "", err); + epf->saddr, epf->sport, + ssh, &ssh->cfg, + &epf->local, + epf->addressfamily); + + logeventf(ssh, "Local %sport %s SOCKS dynamic forwarding%s%s", + epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " : + epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "", + sportdesc, + err ? " failed: " : "", err ? err : ""); } else { struct ssh_rportfwd *pf; @@ -3850,30 +3930,28 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) } pf = snew(struct ssh_rportfwd); - strcpy(pf->dhost, host); - pf->dport = dport; - pf->sport = sport; + strncpy(pf->dhost, epf->daddr, lenof(pf->dhost)-1); + pf->dhost[lenof(pf->dhost)-1] = '\0'; + pf->dport = epf->dport; + pf->sport = epf->sport; if (add234(ssh->rportfwds, pf) != pf) { logeventf(ssh, "Duplicate remote port forwarding to %s:%d", - host, dport); + epf->daddr, epf->dport); sfree(pf); } else { logeventf(ssh, "Requesting remote port %s" - " forward to %s:%.*s%.*s%d%.*s", - sportdesc, host, - (int)(dserv ? strlen(dports) : 0), dports, - dserv, "(", dport, dserv, ")"); + " forward to %s", sportdesc, dportdesc); pf->sportdesc = sportdesc; sportdesc = NULL; - pfrec->remote = pf; - pf->pfrec = pfrec; + epf->remote = pf; + pf->pfrec = epf; if (ssh->version == 1) { send_packet(ssh, SSH1_CMSG_PORT_FORWARD_REQUEST, - PKT_INT, sport, - PKT_STR, host, - PKT_INT, dport, + PKT_INT, epf->sport, + PKT_STR, epf->daddr, + PKT_INT, epf->dport, PKT_END); ssh_queue_handler(ssh, SSH1_SMSG_SUCCESS, SSH1_SMSG_FAILURE, @@ -3883,14 +3961,14 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST); ssh2_pkt_addstring(pktout, "tcpip-forward"); ssh2_pkt_addbool(pktout, 1);/* want reply */ - if (*saddr) { - ssh2_pkt_addstring(pktout, saddr); + if (epf->saddr) { + ssh2_pkt_addstring(pktout, epf->saddr); } else if (ssh->cfg.rport_acceptall) { ssh2_pkt_addstring(pktout, "0.0.0.0"); } else { ssh2_pkt_addstring(pktout, "127.0.0.1"); } - ssh2_pkt_adduint32(pktout, sport); + ssh2_pkt_adduint32(pktout, epf->sport); ssh2_pkt_send(ssh, pktout); ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS, @@ -3900,80 +3978,8 @@ static void ssh_setup_portfwd(Ssh ssh, const Config *cfg) } } sfree(sportdesc); + sfree(dportdesc); } - } - - /* - * Now go through and destroy any port forwardings which were - * not re-enabled. - */ - { - struct ssh_portfwd *epf; - int i; - for (i = 0; (epf = index234(ssh->portfwds, i)) != NULL; i++) - if (!epf->keep) { - char *message; - - message = dupprintf("%s port forwarding from %s%s%d", - epf->type == 'L' ? "local" : - epf->type == 'R' ? "remote" : "dynamic", - epf->saddr ? epf->saddr : "", - epf->saddr ? ":" : "", - epf->sport); - - if (epf->type != 'D') { - char *msg2 = dupprintf("%s to %s:%d", message, - epf->daddr, epf->dport); - sfree(message); - message = msg2; - } - - logeventf(ssh, "Cancelling %s", message); - sfree(message); - - if (epf->remote) { - struct ssh_rportfwd *rpf = epf->remote; - struct Packet *pktout; - - /* - * Cancel the port forwarding at the server - * end. - */ - if (ssh->version == 1) { - /* - * We cannot cancel listening ports on the - * server side in SSH1! There's no message - * to support it. Instead, we simply remove - * the rportfwd record from the local end - * so that any connections the server tries - * to make on it are rejected. - */ - } else { - pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST); - ssh2_pkt_addstring(pktout, "cancel-tcpip-forward"); - ssh2_pkt_addbool(pktout, 0);/* _don't_ want reply */ - if (epf->saddr) { - ssh2_pkt_addstring(pktout, epf->saddr); - } else if (ssh->cfg.rport_acceptall) { - ssh2_pkt_addstring(pktout, "0.0.0.0"); - } else { - ssh2_pkt_addstring(pktout, "127.0.0.1"); - } - ssh2_pkt_adduint32(pktout, epf->sport); - ssh2_pkt_send(ssh, pktout); - } - - del234(ssh->rportfwds, rpf); - free_rportfwd(rpf); - } else if (epf->local) { - pfd_terminate(epf->local); - } - - delpos234(ssh->portfwds, i); - free_portfwd(epf); - i--; /* so we don't skip one in the list */ - } - } } static void ssh1_smsg_stdout_stderr_data(Ssh ssh, struct Packet *pktin) -- 2.11.0