Set cfg.ssh_simple if there are no forwardings.
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index 7a697a1..d1a2c80 100644 (file)
--- a/ssh.c
+++ b/ssh.c
 #define SSH1_SMSG_AUTH_CCARD_CHALLENGE            71   /* 0x47 */
 #define SSH1_CMSG_AUTH_CCARD_RESPONSE             72   /* 0x48 */
 
+#define SSH1_AUTH_RHOSTS                          1    /* 0x1 */
+#define SSH1_AUTH_RSA                             2    /* 0x2 */
+#define SSH1_AUTH_PASSWORD                        3    /* 0x3 */
+#define SSH1_AUTH_RHOSTS_RSA                      4    /* 0x4 */
 #define SSH1_AUTH_TIS                             5    /* 0x5 */
 #define SSH1_AUTH_CCARD                           16   /* 0x10 */
 
@@ -529,6 +533,14 @@ enum {                                    /* channel types */
 };
 
 /*
+ * little structure to keep track of outstanding WINDOW_ADJUSTs
+ */
+struct winadj {
+    struct winadj *next;
+    unsigned size;
+};
+
+/*
  * 2-3-4 tree storing channels.
  */
 struct ssh_channel {
@@ -557,6 +569,18 @@ struct ssh_channel {
            unsigned remwindow, remmaxpkt;
            /* locwindow is signed so we can cope with excess data. */
            int locwindow, locmaxwin;
+           /*
+            * remlocwin is the amount of local window that we think
+            * the remote end had available to it after it sent the
+            * last data packet or window adjust ack.
+            */
+           int remlocwin;
+           /*
+            * These store the list of window adjusts that haven't
+            * been acked.
+            */
+           struct winadj *winadj_head, *winadj_tail;
+           enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state;
        } v2;
     } v;
     union {
@@ -663,7 +687,7 @@ static void ssh_special(void *handle, Telnet_Special);
 static int ssh2_try_send(struct ssh_channel *c);
 static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len);
 static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
-static void ssh2_set_window(struct ssh_channel *c, unsigned newwin);
+static void ssh2_set_window(struct ssh_channel *c, int newwin);
 static int ssh_sendbuffer(void *handle);
 static int ssh_do_close(Ssh ssh, int notify_exit);
 static unsigned long ssh_pkt_getuint32(struct Packet *pkt);
@@ -3066,6 +3090,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
     ssh->v1_remote_protoflags = ssh_pkt_getuint32(pktin);
     s->supported_ciphers_mask = ssh_pkt_getuint32(pktin);
     s->supported_auths_mask = ssh_pkt_getuint32(pktin);
+    if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA))
+       s->supported_auths_mask &= ~(1 << SSH1_AUTH_RSA);
 
     ssh->v1_local_protoflags =
        ssh->v1_remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED;
@@ -3319,7 +3345,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
 
     crWaitUntil(pktin);
 
-    if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA)) {
+    if ((s->supported_auths_mask & (1 << SSH1_AUTH_RSA)) == 0) {
        /* We must not attempt PK auth. Pretend we've already tried it. */
        s->tried_publickey = s->tried_agent = 1;
     } else {
@@ -3776,6 +3802,10 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
            }
        }
        if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
+           if ((s->supported_auths_mask & (1 << SSH1_AUTH_PASSWORD)) == 0) {
+               bombout(("No supported authentication methods available"));
+               crStop(0);
+           }
            s->cur_prompt->to_server = TRUE;
            s->cur_prompt->name = dupstr("SSH password");
            add_prompt(s->cur_prompt, dupprintf("%.90s@%.90s's password: ",
@@ -6172,7 +6202,7 @@ static void ssh2_try_send_and_unthrottle(struct ssh_channel *c)
 /*
  * Potentially enlarge the window on an SSH-2 channel.
  */
-static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
+static void ssh2_set_window(struct ssh_channel *c, int newwin)
 {
     Ssh ssh = c->ssh;
 
@@ -6193,7 +6223,51 @@ static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
      */
     if (newwin / 2 >= c->v.v2.locwindow) {
        struct Packet *pktout;
+       struct winadj *wa;
 
+       /*
+        * In order to keep track of how much window the client
+        * actually has available, we'd like it to acknowledge each
+        * WINDOW_ADJUST.  We can't do that directly, so we accompany
+        * it with a CHANNEL_REQUEST that has to be acknowledged.
+        *
+        * This is only necessary if we're opening the window wide.
+        * If we're not, then throughput is being constrained by
+        * something other than the maximum window size anyway.
+        *
+        * We also only send this if the main channel has finished its
+        * initial CHANNEL_REQUESTs and installed the default
+        * CHANNEL_FAILURE handler, so as not to risk giving it
+        * unexpected CHANNEL_FAILUREs.
+        */
+       if (newwin == c->v.v2.locmaxwin &&
+           ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE]) {
+           pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+           ssh2_pkt_adduint32(pktout, c->remoteid);
+           ssh2_pkt_addstring(pktout, "winadj@putty.projects.tartarus.org");
+           ssh2_pkt_addbool(pktout, TRUE);
+           ssh2_pkt_send(ssh, pktout);
+
+           /*
+            * CHANNEL_FAILURE doesn't come with any indication of
+            * what message caused it, so we have to keep track of the
+            * outstanding CHANNEL_REQUESTs ourselves.
+            */
+           wa = snew(struct winadj);
+           wa->size = newwin - c->v.v2.locwindow;
+           wa->next = NULL;
+           if (!c->v.v2.winadj_head)
+               c->v.v2.winadj_head = wa;
+           else
+               c->v.v2.winadj_tail->next = wa;
+           c->v.v2.winadj_tail = wa;
+           if (c->v.v2.throttle_state != UNTHROTTLED)
+               c->v.v2.throttle_state = UNTHROTTLING;
+       } else {
+           /* Pretend the WINDOW_ADJUST was acked immediately. */
+           c->v.v2.remlocwin = newwin;
+           c->v.v2.throttle_state = THROTTLED;
+       }
        pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
        ssh2_pkt_adduint32(pktout, c->remoteid);
        ssh2_pkt_adduint32(pktout, newwin - c->v.v2.locwindow);
@@ -6202,6 +6276,38 @@ static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
     }
 }
 
+static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin)
+{
+    /*
+     * The only time this should get called is for "winadj@putty"
+     * messages sent above.  All other channel requests are either
+     * sent with want_reply false or are sent before this handler gets
+     * installed.
+     */
+    unsigned i = ssh_pkt_getuint32(pktin);
+    struct ssh_channel *c;
+    struct winadj *wa;
+
+    c = find234(ssh->channels, &i, ssh_channelfind);
+    if (!c)
+       return;                        /* nonexistent channel */
+    wa = c->v.v2.winadj_head;
+    if (!wa)
+       logevent("excess SSH_MSG_CHANNEL_FAILURE");
+    else {
+       c->v.v2.winadj_head = wa->next;
+       c->v.v2.remlocwin += wa->size;
+       sfree(wa);
+       /*
+        * winadj messages are only sent when the window is fully open,
+        * so if we get an ack of one, we know any pending unthrottle
+        * is complete.
+        */
+       if (c->v.v2.throttle_state == UNTHROTTLING)
+           c->v.v2.throttle_state = UNTHROTTLED;
+    }
+}
+
 static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
 {
     unsigned i = ssh_pkt_getuint32(pktin);
@@ -6229,6 +6335,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
     if (data) {
        int bufsize = 0;
        c->v.v2.locwindow -= length;
+       c->v.v2.remlocwin -= length;
        switch (c->type) {
          case CHAN_MAINSESSION:
            bufsize =
@@ -6286,6 +6393,14 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
            break;
        }
        /*
+        * If it looks like the remote end hit the end of its window,
+        * and we didn't want it to do that, think about using a
+        * larger window.
+        */
+       if (c->v.v2.remlocwin <= 0 && c->v.v2.throttle_state == UNTHROTTLED &&
+           c->v.v2.locmaxwin < 0x40000000)
+           c->v.v2.locmaxwin += OUR_V2_WINSIZE;
+       /*
         * If we are not buffering too much data,
         * enlarge the window again at the remote side.
         * If we are buffering too much, we may still
@@ -6763,6 +6878,9 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
        c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE;
        c->v.v2.remwindow = winsize;
        c->v.v2.remmaxpkt = pktsize;
+       c->v.v2.remlocwin = OUR_V2_WINSIZE;
+       c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL;
+       c->v.v2.throttle_state = UNTHROTTLED;
        bufchain_init(&c->v.v2.outbuffer);
        add234(ssh->channels, c);
        pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
@@ -7972,7 +8090,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        ssh2_pkt_addstring(s->pktout, "direct-tcpip");
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
        ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin =
+           ssh->mainchan->v.v2.remlocwin =
            ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
+       ssh->mainchan->v.v2.winadj_head = NULL;
+       ssh->mainchan->v.v2.winadj_tail = NULL;
+       ssh->mainchan->v.v2.throttle_state = UNTHROTTLED;
        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);
@@ -8015,7 +8137,11 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        ssh2_pkt_addstring(s->pktout, "session");
        ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
        ssh->mainchan->v.v2.locwindow = ssh->mainchan->v.v2.locmaxwin =
+           ssh->mainchan->v.v2.remlocwin =
            ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
+       ssh->mainchan->v.v2.winadj_head = NULL;
+       ssh->mainchan->v.v2.winadj_tail = NULL;
+       ssh->mainchan->v.v2.throttle_state = UNTHROTTLED;
        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_send(ssh, s->pktout);
@@ -8329,6 +8455,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        ssh_special(ssh, TS_EOF);
 
     /*
+     * All the initial channel requests are done, so install the default
+     * failure handler.
+     */
+    ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_failure;
+
+    /*
      * Transfer data!
      */
     if (ssh->ldisc)
@@ -9082,8 +9214,9 @@ static void ssh_unthrottle(void *handle, int bufsize)
            ssh1_throttle(ssh, -1);
        }
     } else {
-       ssh2_set_window(ssh->mainchan,
-                       ssh->mainchan->v.v2.locmaxwin - bufsize);
+       if (ssh->mainchan)
+           ssh2_set_window(ssh->mainchan,
+                           ssh->mainchan->v.v2.locmaxwin - bufsize);
     }
 }
 
@@ -9107,6 +9240,9 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org)
        ssh2_pkt_addstring(pktout, "direct-tcpip");
        ssh2_pkt_adduint32(pktout, c->localid);
        c->v.v2.locwindow = c->v.v2.locmaxwin = OUR_V2_WINSIZE;
+       c->v.v2.remlocwin = OUR_V2_WINSIZE;
+       c->v.v2.winadj_head = c->v.v2.winadj_head = NULL;
+       c->v.v2.throttle_state = UNTHROTTLED;
        ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */
        ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT);      /* our max pkt size */
        ssh2_pkt_addstring(pktout, hostname);