Jacob points out that when we reconfigure port forwarding, we ought
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Thu, 30 Dec 2004 17:48:35 +0000 (17:48 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Thu, 30 Dec 2004 17:48:35 +0000 (17:48 +0000)
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

diff --git a/ssh.c b/ssh.c
index 170253b..163b2ea 100644 (file)
--- 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)