Robert de Bath's asynchronous-connect patch. Helps a lot in port
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index be34659..c03cd6d 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -428,6 +428,13 @@ static struct Packet pktout = { 0, 0, NULL, NULL, 0 };
 static unsigned char *deferred_send_data = NULL;
 static int deferred_len = 0, deferred_size = 0;
 
+/*
+ * Gross hack: pscp will try to start SFTP but fall back to scp1 if
+ * that fails. This variable is the means by which scp.c can reach
+ * into the SSH code and find out which one it got.
+ */
+int ssh_fallback_cmd = 0;
+
 static int ssh_version;
 static int ssh1_throttle_count;
 static int ssh_overall_bufsize;
@@ -500,7 +507,7 @@ static int ssh_rportcmp_ssh2(void *av, void *bv)
 {
     struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;
     struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;
-    int i;
+
     if (a->sport > b->sport)
        return +1;
     if (a->sport < b->sport)
@@ -1679,6 +1686,11 @@ static char *connect_to_host(char *host, int port, char **realhost)
     /*
      * Try to find host.
      */
+    {
+       char buf[200];
+       sprintf(buf, "Looking up host \"%.170s\"", host);
+       logevent(buf);
+    }
     addr = sk_namelookup(host, realhost);
     if ((err = sk_addr_error(addr)))
        return err;
@@ -1690,6 +1702,12 @@ static char *connect_to_host(char *host, int port, char **realhost)
     /*
      * Open socket.
      */
+    {
+       char buf[200], addrbuf[100];
+       sk_getaddr(addr, addrbuf, 100);
+       sprintf(buf, "Connecting to %.100s port %d", addrbuf, port);
+       logevent(buf);
+    }
     s = sk_new(addr, port, 0, 1, &fn_table_ptr);
     if ((err = sk_socket_error(s)))
        return err;
@@ -1862,31 +1880,45 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
 
     logevent("Encrypted session key");
 
-    switch (cfg.cipher) {
-      case CIPHER_BLOWFISH:
-       cipher_type = SSH_CIPHER_BLOWFISH;
-       break;
-      case CIPHER_DES:
-       cipher_type = SSH_CIPHER_DES;
-       break;
-      case CIPHER_3DES:
-       cipher_type = SSH_CIPHER_3DES;
-       break;
-      case CIPHER_AES:
-       c_write_str("AES not supported in SSH1, falling back to 3DES\r\n");
-       cipher_type = SSH_CIPHER_3DES;
-       break;
-    }
-    if ((supported_ciphers_mask & (1 << cipher_type)) == 0) {
-       c_write_str
-           ("Selected cipher not supported, falling back to 3DES\r\n");
-       cipher_type = SSH_CIPHER_3DES;
-       if ((supported_ciphers_mask & (1 << cipher_type)) == 0) {
-           bombout(("Server violates SSH 1 protocol by "
-                    "not supporting 3DES encryption"));
+    {
+       int cipher_chosen = 0, warn = 0;
+       char *cipher_string = NULL;
+       for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) {
+           int next_cipher = cfg.ssh_cipherlist[i];
+           if (next_cipher == CIPHER_WARN) {
+               /* If/when we choose a cipher, warn about it */
+               warn = 1;
+           } else if (next_cipher == CIPHER_AES) {
+               /* XXX Probably don't need to mention this. */
+               logevent("AES not supported in SSH1, skipping");
+           } else {
+               switch (next_cipher) {
+                 case CIPHER_3DES:     cipher_type = SSH_CIPHER_3DES;
+                                       cipher_string = "3DES"; break;
+                 case CIPHER_BLOWFISH: cipher_type = SSH_CIPHER_BLOWFISH;
+                                       cipher_string = "Blowfish"; break;
+                 case CIPHER_DES:      cipher_type = SSH_CIPHER_DES;
+                                       cipher_string = "single-DES"; break;
+               }
+               if (supported_ciphers_mask & (1 << cipher_type))
+                   cipher_chosen = 1;
+           }
+       }
+       if (!cipher_chosen) {
+           if ((supported_ciphers_mask & (1 << SSH_CIPHER_3DES)) == 0)
+               bombout(("Server violates SSH 1 protocol by not "
+                        "supporting 3DES encryption"));
+           else
+               /* shouldn't happen */
+               bombout(("No supported ciphers found"));
            crReturn(0);
        }
+
+       /* Warn about chosen cipher if necessary. */
+       if (warn)
+           askcipher(cipher_string, 0);
     }
+
     switch (cipher_type) {
       case SSH_CIPHER_3DES:
        logevent("Using 3DES encryption");
@@ -2649,12 +2681,26 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
        zlib_decompress_init();
     }
 
-    if (*cfg.remote_cmd_ptr)
-       send_packet(SSH1_CMSG_EXEC_CMD, PKT_STR, cfg.remote_cmd_ptr,
-                   PKT_END);
-    else
-       send_packet(SSH1_CMSG_EXEC_SHELL, PKT_END);
-    logevent("Started session");
+    /*
+     * Start the shell or command.
+     * 
+     * Special case: if the first-choice command is an SSH2
+     * subsystem (hence not usable here) and the second choice
+     * exists, we fall straight back to that.
+     */
+    {
+       char *cmd = cfg.remote_cmd_ptr;
+       
+       if (cfg.ssh_subsys && cfg.remote_cmd_ptr2) {
+           cmd = cfg.remote_cmd_ptr2;
+           ssh_fallback_cmd = TRUE;
+       }
+       if (*cmd)
+           send_packet(SSH1_CMSG_EXEC_CMD, PKT_STR, cmd, PKT_END);
+       else
+           send_packet(SSH1_CMSG_EXEC_SHELL, PKT_END);
+       logevent("Started session");
+    }
 
     ssh_state = SSH_STATE_SESSION;
     if (size_needed)
@@ -2674,7 +2720,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                int bufsize =
                    from_backend(pktin.type == SSH1_SMSG_STDERR_DATA,
                                 pktin.body + 4, len);
-               if (bufsize > SSH1_BUFFER_LIMIT) {
+               if (!ssh1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {
                    ssh1_stdout_throttling = 1;
                    ssh1_throttle(+1);
                }
@@ -2806,6 +2852,19 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                    pfd_confirm(c->u.pfd.s);
                }
 
+           } else if (pktin.type == SSH1_MSG_CHANNEL_OPEN_FAILURE) {
+               unsigned int remoteid = GET_32BIT(pktin.body);
+               unsigned int localid = GET_32BIT(pktin.body+4);
+               struct ssh_channel *c;
+
+               c = find234(ssh_channels, &remoteid, ssh_channelfind);
+               if (c && c->type == CHAN_SOCKDATA_DORMANT) {
+                   logevent("Forwarded connection refused by server");
+                   pfd_close(c->u.pfd.s);
+                   del234(ssh_channels, c);
+                   sfree(c);
+               }
+
            } else if (pktin.type == SSH1_MSG_CHANNEL_CLOSE ||
                       pktin.type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION) {
                /* Remote side closes a channel. */
@@ -2907,7 +2966,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                        bufsize = 0;   /* agent channels never back up */
                        break;
                    }
-                   if (bufsize > SSH1_BUFFER_LIMIT) {
+                   if (!c->v.v1.throttling && bufsize > SSH1_BUFFER_LIMIT) {
                        c->v.v1.throttling = 1;
                        ssh1_throttle(+1);
                    }
@@ -2992,7 +3051,7 @@ static void ssh2_mkkey(Bignum K, char *H, char *sessid, char chr,
  */
 static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
 {
-    static int i, j, len, nbits, pbits;
+    static int i, j, len, nbits, pbits, warn;
     static char *str;
     static Bignum p, g, e, f, K;
     static int kex_init_value, kex_reply_value;
@@ -3009,7 +3068,8 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     static void *hkey;                /* actual host key */
     static unsigned char exchange_hash[20];
     static unsigned char keyspace[40];
-    static const struct ssh2_ciphers *preferred_cipher;
+    static int n_preferred_ciphers;
+    static const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
     static const struct ssh_compress *preferred_comp;
     static int first_kex;
 
@@ -3018,21 +3078,40 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     first_kex = 1;
 
     /*
-     * Set up the preferred cipher and compression.
+     * Set up the preferred ciphers. (NULL => warn below here)
      */
-    if (cfg.cipher == CIPHER_BLOWFISH) {
-       preferred_cipher = &ssh2_blowfish;
-    } else if (cfg.cipher == CIPHER_DES) {
-       logevent("Single DES not supported in SSH2; using 3DES");
-       preferred_cipher = &ssh2_3des;
-    } else if (cfg.cipher == CIPHER_3DES) {
-       preferred_cipher = &ssh2_3des;
-    } else if (cfg.cipher == CIPHER_AES) {
-       preferred_cipher = &ssh2_aes;
-    } else {
-       /* Shouldn't happen, but we do want to initialise to _something_. */
-       preferred_cipher = &ssh2_3des;
+    n_preferred_ciphers = 0;
+    for (i = 0; i < CIPHER_MAX; i++) {
+       switch (cfg.ssh_cipherlist[i]) {
+         case CIPHER_BLOWFISH:
+           preferred_ciphers[n_preferred_ciphers] = &ssh2_blowfish;
+           n_preferred_ciphers++;
+           break;
+         case CIPHER_DES:
+           /* Not supported in SSH2; silently drop */
+           break;
+         case CIPHER_3DES:
+           preferred_ciphers[n_preferred_ciphers] = &ssh2_3des;
+           n_preferred_ciphers++;
+           break;
+         case CIPHER_AES:
+           preferred_ciphers[n_preferred_ciphers] = &ssh2_aes;
+           n_preferred_ciphers++;
+           break;
+         case CIPHER_WARN:
+           /* Flag for later. Don't bother if it's the last in
+            * the list. */
+           if (i < CIPHER_MAX - 1) {
+               preferred_ciphers[n_preferred_ciphers] = NULL;
+               n_preferred_ciphers++;
+           }
+           break;
+       }
     }
+
+    /*
+     * Set up preferred compression.
+     */
     if (cfg.compression)
        preferred_comp = &ssh_zlib;
     else
@@ -3069,23 +3148,23 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     }
     /* List client->server encryption algorithms. */
     ssh2_pkt_addstring_start();
-    for (i = 0; i < lenof(ciphers) + 1; i++) {
-       const struct ssh2_ciphers *c =
-           i == 0 ? preferred_cipher : ciphers[i - 1];
+    for (i = 0; i < n_preferred_ciphers; i++) {
+       const struct ssh2_ciphers *c = preferred_ciphers[i];
+       if (!c) continue;              /* warning flag */
        for (j = 0; j < c->nciphers; j++) {
            ssh2_pkt_addstring_str(c->list[j]->name);
-           if (i < lenof(ciphers) || j < c->nciphers - 1)
+           if (i < n_preferred_ciphers || j < c->nciphers - 1)
                ssh2_pkt_addstring_str(",");
        }
     }
     /* List server->client encryption algorithms. */
     ssh2_pkt_addstring_start();
-    for (i = 0; i < lenof(ciphers) + 1; i++) {
-       const struct ssh2_ciphers *c =
-           i == 0 ? preferred_cipher : ciphers[i - 1];
+    for (i = 0; i < n_preferred_ciphers; i++) {
+       const struct ssh2_ciphers *c = preferred_ciphers[i];
+       if (!c) continue; /* warning flag */
        for (j = 0; j < c->nciphers; j++) {
            ssh2_pkt_addstring_str(c->list[j]->name);
-           if (i < lenof(ciphers) || j < c->nciphers - 1)
+           if (i < n_preferred_ciphers || j < c->nciphers - 1)
                ssh2_pkt_addstring_str(",");
        }
     }
@@ -3171,30 +3250,44 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
        }
     }
     ssh2_pkt_getstring(&str, &len);    /* client->server cipher */
-    for (i = 0; i < lenof(ciphers) + 1; i++) {
-       const struct ssh2_ciphers *c =
-           i == 0 ? preferred_cipher : ciphers[i - 1];
-       for (j = 0; j < c->nciphers; j++) {
-           if (in_commasep_string(c->list[j]->name, str, len)) {
-               cscipher_tobe = c->list[j];
-               break;
+    warn = 0;
+    for (i = 0; i < n_preferred_ciphers; i++) {
+       const struct ssh2_ciphers *c = preferred_ciphers[i];
+       if (!c) {
+           warn = 1;
+       } else {
+           for (j = 0; j < c->nciphers; j++) {
+               if (in_commasep_string(c->list[j]->name, str, len)) {
+                   cscipher_tobe = c->list[j];
+                   break;
+               }
            }
        }
-       if (cscipher_tobe)
+       if (cscipher_tobe) {
+           if (warn)
+               askcipher(cscipher_tobe->name, 1);
            break;
+       }
     }
     ssh2_pkt_getstring(&str, &len);    /* server->client cipher */
-    for (i = 0; i < lenof(ciphers) + 1; i++) {
-       const struct ssh2_ciphers *c =
-           i == 0 ? preferred_cipher : ciphers[i - 1];
-       for (j = 0; j < c->nciphers; j++) {
-           if (in_commasep_string(c->list[j]->name, str, len)) {
-               sccipher_tobe = c->list[j];
-               break;
+    warn = 0;
+    for (i = 0; i < n_preferred_ciphers; i++) {
+       const struct ssh2_ciphers *c = preferred_ciphers[i];
+       if (!c) {
+           warn = 1;
+       } else {
+           for (j = 0; j < c->nciphers; j++) {
+               if (in_commasep_string(c->list[j]->name, str, len)) {
+                   sccipher_tobe = c->list[j];
+                   break;
+               }
            }
        }
-       if (sccipher_tobe)
+       if (sccipher_tobe) {
+           if (warn)
+               askcipher(sccipher_tobe->name, 2);
            break;
+       }
     }
     ssh2_pkt_getstring(&str, &len);    /* client->server mac */
     for (i = 0; i < nmacs; i++) {
@@ -3724,66 +3817,12 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    in_commasep_string("publickey", methods, methlen);
                can_passwd =
                    in_commasep_string("password", methods, methlen);
-               can_passwd =
-                   in_commasep_string("password", methods, methlen);
                can_keyb_inter = 
                    in_commasep_string("keyboard-interactive", methods, methlen);
            }
 
            method = 0;
 
-           if (!method && can_keyb_inter && !tried_keyb_inter) {
-               method = AUTH_KEYBOARD_INTERACTIVE;
-               type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
-               tried_keyb_inter = TRUE;
-
-               ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-               ssh2_pkt_addstring(username);
-               ssh2_pkt_addstring("ssh-connection");   /* service requested */
-               ssh2_pkt_addstring("keyboard-interactive");     /* method */
-               ssh2_pkt_addstring(""); /* lang */
-               ssh2_pkt_addstring("");
-               ssh2_pkt_send();
-
-               crWaitUntilV(ispkt);
-               if (pktin.type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
-                   if (pktin.type == SSH2_MSG_USERAUTH_FAILURE)
-                       gotit = TRUE;
-                   logevent("Keyboard-interactive authentication refused");
-                   type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
-                   continue;
-               }
-
-               kbd_inter_running = TRUE;
-           }
-
-           if (kbd_inter_running) {
-               method = AUTH_KEYBOARD_INTERACTIVE;
-               type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
-               tried_keyb_inter = TRUE;
-
-               /* We've got packet with that "interactive" info
-                  dump banners, and set its prompt as ours */
-               {
-                   char *name, *inst, *lang, *prompt;
-                   int name_len, inst_len, lang_len, prompt_len;
-                   ssh2_pkt_getstring(&name, &name_len);
-                   ssh2_pkt_getstring(&inst, &inst_len);
-                   ssh2_pkt_getstring(&lang, &lang_len);
-                   if (name_len > 0)
-                       c_write_untrusted(name, name_len);
-                   if (inst_len > 0)
-                       c_write_untrusted(inst, inst_len);
-                   num_prompts = ssh2_pkt_getuint32();
-
-                   ssh2_pkt_getstring(&prompt, &prompt_len);
-                   strncpy(pwprompt, prompt, sizeof(pwprompt));
-                   need_pw = TRUE;
-
-                   echo = ssh2_pkt_getbool();
-               }
-           }
-
            if (!method && can_pubkey && agent_exists() && !tried_agent) {
                /*
                 * Attempt public-key authentication using Pageant.
@@ -3977,6 +4016,58 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                }
            }
 
+           if (!method && can_keyb_inter && !tried_keyb_inter) {
+               method = AUTH_KEYBOARD_INTERACTIVE;
+               type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
+               tried_keyb_inter = TRUE;
+
+               ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
+               ssh2_pkt_addstring(username);
+               ssh2_pkt_addstring("ssh-connection");   /* service requested */
+               ssh2_pkt_addstring("keyboard-interactive");     /* method */
+               ssh2_pkt_addstring(""); /* lang */
+               ssh2_pkt_addstring("");
+               ssh2_pkt_send();
+
+               crWaitUntilV(ispkt);
+               if (pktin.type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
+                   if (pktin.type == SSH2_MSG_USERAUTH_FAILURE)
+                       gotit = TRUE;
+                   logevent("Keyboard-interactive authentication refused");
+                   type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
+                   continue;
+               }
+
+               kbd_inter_running = TRUE;
+           }
+
+           if (kbd_inter_running) {
+               method = AUTH_KEYBOARD_INTERACTIVE;
+               type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
+               tried_keyb_inter = TRUE;
+
+               /* We've got packet with that "interactive" info
+                  dump banners, and set its prompt as ours */
+               {
+                   char *name, *inst, *lang, *prompt;
+                   int name_len, inst_len, lang_len, prompt_len;
+                   ssh2_pkt_getstring(&name, &name_len);
+                   ssh2_pkt_getstring(&inst, &inst_len);
+                   ssh2_pkt_getstring(&lang, &lang_len);
+                   if (name_len > 0)
+                       c_write_untrusted(name, name_len);
+                   if (inst_len > 0)
+                       c_write_untrusted(inst, inst_len);
+                   num_prompts = ssh2_pkt_getuint32();
+
+                   ssh2_pkt_getstring(&prompt, &prompt_len);
+                   strncpy(pwprompt, prompt, sizeof(pwprompt));
+                   need_pw = TRUE;
+
+                   echo = ssh2_pkt_getbool();
+               }
+           }
+
            if (!method && can_passwd) {
                method = AUTH_PASSWORD;
                sprintf(pwprompt, "%.90s@%.90s's password: ", username,
@@ -4444,43 +4535,70 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     }
 
     /*
-     * Start a shell or a remote command.
+     * Start a shell or a remote command. We may have to attempt
+     * this twice if the config data has provided a second choice
+     * of command.
      */
-    ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
-    ssh2_pkt_adduint32(mainchan->remoteid);    /* recipient channel */
-    if (cfg.ssh_subsys) {
-       ssh2_pkt_addstring("subsystem");
-       ssh2_pkt_addbool(1);           /* want reply */
-       ssh2_pkt_addstring(cfg.remote_cmd_ptr);
-    } else if (*cfg.remote_cmd_ptr) {
-       ssh2_pkt_addstring("exec");
-       ssh2_pkt_addbool(1);           /* want reply */
-       ssh2_pkt_addstring(cfg.remote_cmd_ptr);
-    } else {
-       ssh2_pkt_addstring("shell");
-       ssh2_pkt_addbool(1);           /* want reply */
-    }
-    ssh2_pkt_send();
-    do {
-       crWaitUntilV(ispkt);
-       if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
-           unsigned i = ssh2_pkt_getuint32();
-           struct ssh_channel *c;
-           c = find234(ssh_channels, &i, ssh_channelfind);
-           if (!c)
-               continue;              /* nonexistent channel */
-           c->v.v2.remwindow += ssh2_pkt_getuint32();
+    while (1) {
+       int subsys;
+       char *cmd;
+
+       if (ssh_fallback_cmd) {
+           subsys = cfg.ssh_subsys2;
+           cmd = cfg.remote_cmd_ptr2;
+       } else {
+           subsys = cfg.ssh_subsys;
+           cmd = cfg.remote_cmd_ptr;
        }
-    } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
-    if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
-       if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
-           bombout(("Server got confused by shell/command request"));
+
+       ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+       ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
+       if (subsys) {
+           ssh2_pkt_addstring("subsystem");
+           ssh2_pkt_addbool(1);               /* want reply */
+           ssh2_pkt_addstring(cmd);
+       } else if (*cmd) {
+           ssh2_pkt_addstring("exec");
+           ssh2_pkt_addbool(1);               /* want reply */
+           ssh2_pkt_addstring(cmd);
+       } else {
+           ssh2_pkt_addstring("shell");
+           ssh2_pkt_addbool(1);               /* want reply */
+       }
+       ssh2_pkt_send();
+       do {
+           crWaitUntilV(ispkt);
+           if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+               unsigned i = ssh2_pkt_getuint32();
+               struct ssh_channel *c;
+               c = find234(ssh_channels, &i, ssh_channelfind);
+               if (!c)
+                   continue;          /* nonexistent channel */
+               c->v.v2.remwindow += ssh2_pkt_getuint32();
+           }
+       } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+       if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
+           if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
+               bombout(("Server got confused by shell/command request"));
+               crReturnV;
+           }
+           /*
+            * We failed to start the command. If this is the
+            * fallback command, we really are finished; if it's
+            * not, and if the fallback command exists, try falling
+            * back to it before complaining.
+            */
+           if (!ssh_fallback_cmd && cfg.remote_cmd_ptr2 != NULL) {
+               logevent("Primary command failed; attempting fallback");
+               ssh_fallback_cmd = TRUE;
+               continue;
+           }
+           bombout(("Server refused to start a shell/command"));
            crReturnV;
+       } else {
+           logevent("Started a shell/command");
        }
-       bombout(("Server refused to start a shell/command"));
-       crReturnV;
-    } else {
-       logevent("Started a shell/command");
+       break;
     }
 
     ssh_state = SSH_STATE_SESSION;
@@ -4620,22 +4738,29 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                c = find234(ssh_channels, &i, ssh_channelfind);
                if (!c)
                    continue;          /* nonexistent channel */
-               if (c->closes == 0) {
-                   ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
-                   ssh2_pkt_adduint32(c->remoteid);
-                   ssh2_pkt_send();
-               }
                /* Do pre-close processing on the channel. */
                switch (c->type) {
                  case CHAN_MAINSESSION:
                    break;             /* nothing to see here, move along */
                  case CHAN_X11:
+                   if (c->u.x11.s != NULL)
+                       x11_close(c->u.x11.s);
+                   sshfwd_close(c);
                    break;
                  case CHAN_AGENT:
+                   sshfwd_close(c);
                    break;
                  case CHAN_SOCKDATA:
+                   if (c->u.pfd.s != NULL)
+                       pfd_close(c->u.pfd.s);
+                   sshfwd_close(c);
                    break;
                }
+               if (c->closes == 0) {
+                   ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+                   ssh2_pkt_adduint32(c->remoteid);
+                   ssh2_pkt_send();
+               }
                del234(ssh_channels, c);
                bufchain_clear(&c->v.v2.outbuffer);
                sfree(c);
@@ -4677,6 +4802,21 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                c->v.v2.remmaxpkt = ssh2_pkt_getuint32();
                bufchain_init(&c->v.v2.outbuffer);
                pfd_confirm(c->u.pfd.s);
+           } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
+               unsigned i = ssh2_pkt_getuint32();
+               struct ssh_channel *c;
+               c = find234(ssh_channels, &i, ssh_channelfind);
+               if (!c)
+                   continue;          /* nonexistent channel */
+               if (c->type != CHAN_SOCKDATA_DORMANT)
+                   continue;          /* dunno why they're failing this */
+
+               logevent("Forwarded connection refused by server");
+
+               pfd_close(c->u.pfd.s);
+
+               del234(ssh_channels, c);
+               sfree(c);
            } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN) {
                char *type;
                int typelen;
@@ -4834,6 +4974,7 @@ static char *ssh_init(char *host, int port, char **realhost)
     ssh_echoing = 0;
     ssh1_throttle_count = 0;
     ssh_overall_bufsize = 0;
+    ssh_fallback_cmd = 0;
 
     p = connect_to_host(host, port, realhost);
     if (p != NULL)
@@ -4983,7 +5124,7 @@ void *new_sock_channel(Socket s)
 void ssh_unthrottle(int bufsize)
 {
     if (ssh_version == 1) {
-       if (bufsize < SSH1_BUFFER_LIMIT) {
+       if (ssh1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) {
            ssh1_stdout_throttling = 0;
            ssh1_throttle(-1);
        }