Update docs for change to UTF-8 by default, and emphasise UTF-8 more generally.
[sgt/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index c419a89..6ee5528 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -525,7 +525,7 @@ static void ssh_channel_destroy(struct ssh_channel *c);
 const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
 
 const static struct ssh_mac *macs[] = {
-    &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
+    &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
 };
 const static struct ssh_mac *buggymacs[] = {
     &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5
@@ -589,14 +589,6 @@ struct outstanding_channel_request {
 };
 
 /*
- * 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 {
@@ -674,6 +666,7 @@ struct ssh_channel {
            unsigned char *message;
            unsigned char msglen[4];
            unsigned lensofar, totallen;
+            int outstanding_requests;
        } a;
        struct ssh_x11_channel {
            Socket s;
@@ -779,7 +772,7 @@ static int ssh_do_close(Ssh ssh, int notify_exit);
 static unsigned long ssh_pkt_getuint32(struct Packet *pkt);
 static int ssh2_pkt_getbool(struct Packet *pkt);
 static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length);
-static void ssh2_timer(void *ctx, long now);
+static void ssh2_timer(void *ctx, unsigned long now);
 static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
                              struct Packet *pktin);
 static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin);
@@ -988,7 +981,7 @@ struct ssh_tag {
     unsigned long incoming_data_size, outgoing_data_size, deferred_data_size;
     unsigned long max_data_size;
     int kex_in_progress;
-    long next_rekey, last_rekey;
+    unsigned long next_rekey, last_rekey;
     char *deferred_rekey_reason;    /* points to STATIC string; don't free */
 
     /*
@@ -3209,6 +3202,7 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen)
     Ssh ssh = c->ssh;
     void *sentreply = reply;
 
+    c->u.a.outstanding_requests--;
     if (!sentreply) {
        /* Fake SSH_AGENT_FAILURE. */
        sentreply = "\0\0\0\1\5";
@@ -3228,6 +3222,12 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen)
     }
     if (reply)
        sfree(reply);
+    /*
+     * If we've already seen an incoming EOF but haven't sent an
+     * outgoing one, this may be the moment to send it.
+     */
+    if (c->u.a.outstanding_requests == 0 && (c->closes & CLOSES_RCVD_EOF))
+        sshfwd_write_eof(c);
 }
 
 /*
@@ -4618,9 +4618,9 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
                        /* XXX: rport_acceptall may not represent
                         * what was used to open the original connection,
                         * since it's reconfigurable. */
-                       ssh2_pkt_addstring(pktout, "0.0.0.0");
+                       ssh2_pkt_addstring(pktout, "");
                    } else {
-                       ssh2_pkt_addstring(pktout, "127.0.0.1");
+                       ssh2_pkt_addstring(pktout, "localhost");
                    }
                    ssh2_pkt_adduint32(pktout, epf->sport);
                    ssh2_pkt_send(ssh, pktout);
@@ -4733,9 +4733,9 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
                        if (epf->saddr) {
                            ssh2_pkt_addstring(pktout, epf->saddr);
                        } else if (conf_get_int(conf, CONF_rport_acceptall)) {
-                           ssh2_pkt_addstring(pktout, "0.0.0.0");
+                           ssh2_pkt_addstring(pktout, "");
                        } else {
-                           ssh2_pkt_addstring(pktout, "127.0.0.1");
+                           ssh2_pkt_addstring(pktout, "localhost");
                        }
                        ssh2_pkt_adduint32(pktout, epf->sport);
                        ssh2_pkt_send(ssh, pktout);
@@ -4835,6 +4835,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin)
        c->type = CHAN_AGENT;   /* identify channel type */
        c->u.a.lensofar = 0;
        c->u.a.message = NULL;
+       c->u.a.outstanding_requests = 0;
        add234(ssh->channels, c);
        send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
                    PKT_INT, c->remoteid, PKT_INT, c->localid,
@@ -5060,6 +5061,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
                if (c->u.a.lensofar == c->u.a.totallen) {
                    void *reply;
                    int replylen;
+                    c->u.a.outstanding_requests++;
                    if (agent_query(c->u.a.message,
                                    c->u.a.totallen,
                                    &reply, &replylen,
@@ -6580,6 +6582,21 @@ static void ssh2_channel_init(struct ssh_channel *c)
 }
 
 /*
+ * Construct the common parts of a CHANNEL_OPEN.
+ */
+static struct Packet *ssh2_chanopen_init(struct ssh_channel *c, char *type)
+{
+    struct Packet *pktout;
+
+    pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
+    ssh2_pkt_addstring(pktout, type);
+    ssh2_pkt_adduint32(pktout, c->localid);
+    ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */
+    ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT);      /* our max pkt size */
+    return 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.
@@ -6840,6 +6857,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
                if (c->u.a.lensofar == c->u.a.totallen) {
                    void *reply;
                    int replylen;
+                    c->u.a.outstanding_requests++;
                    if (agent_query(c->u.a.message,
                                    c->u.a.totallen,
                                    &reply, &replylen,
@@ -6978,8 +6996,10 @@ static void ssh2_channel_got_eof(struct ssh_channel *c)
     if (c->type == CHAN_X11) {
        x11_send_eof(c->u.x11.s);
     } else if (c->type == CHAN_AGENT) {
-        /* Manufacture an outgoing EOF in response to the incoming one. */
-        sshfwd_write_eof(c);
+        if (c->u.a.outstanding_requests == 0) {
+            /* Manufacture an outgoing EOF in response to the incoming one. */
+            sshfwd_write_eof(c);
+        }
     } else if (c->type == CHAN_SOCKDATA) {
        pfd_send_eof(c->u.pfd.s);
     } else if (c->type == CHAN_MAINSESSION) {
@@ -7401,6 +7421,7 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
        else {
            c->type = CHAN_AGENT;       /* identify channel type */
            c->u.a.lensofar = 0;
+            c->u.a.outstanding_requests = 0;
        }
     } else {
        error = "Unsupported channel type requested";
@@ -9109,66 +9130,30 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
      */
     if (conf_get_int(ssh->conf, CONF_ssh_no_shell)) {
        ssh->mainchan = NULL;
-    } else if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) {
-       /*
-        * Just start a direct-tcpip channel and use it as the main
-        * channel.
-        */
+    } else {
        ssh->mainchan = snew(struct ssh_channel);
        ssh->mainchan->ssh = ssh;
        ssh2_channel_init(ssh->mainchan);
-       logeventf(ssh,
-                 "Opening direct-tcpip channel to %s:%d in place of session",
-                 conf_get_str(ssh->conf, CONF_ssh_nc_host),
-                 conf_get_int(ssh->conf, CONF_ssh_nc_port));
-       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
-       ssh2_pkt_addstring(s->pktout, "direct-tcpip");
-       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
-       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, conf_get_str(ssh->conf, CONF_ssh_nc_host));
-       ssh2_pkt_adduint32(s->pktout, conf_get_int(ssh->conf, CONF_ssh_nc_port));
-       /*
-        * There's nothing meaningful to put in the originator
-        * fields, but some servers insist on syntactically correct
-        * information.
-        */
-       ssh2_pkt_addstring(s->pktout, "0.0.0.0");
-       ssh2_pkt_adduint32(s->pktout, 0);
-       ssh2_pkt_send(ssh, s->pktout);
 
-       crWaitUntilV(pktin);
-       if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
-           bombout(("Server refused to open a direct-tcpip channel"));
-           crStopV;
-           /* FIXME: error data comes back in FAILURE packet */
-       }
-       if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) {
-           bombout(("Server's channel confirmation cited wrong channel"));
-           crStopV;
+       if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) {
+           /*
+            * Just start a direct-tcpip channel and use it as the main
+            * channel.
+            */
+           ssh_send_port_open(ssh->mainchan,
+                              conf_get_str(ssh->conf, CONF_ssh_nc_host),
+                              conf_get_int(ssh->conf, CONF_ssh_nc_port),
+                              "main channel");
+           ssh->ncmode = TRUE;
+       } else {
+           s->pktout = ssh2_chanopen_init(ssh->mainchan, "session");
+           logevent("Opening session as main channel");
+           ssh2_pkt_send(ssh, s->pktout);
+           ssh->ncmode = FALSE;
        }
-       ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
-       ssh->mainchan->halfopen = FALSE;
-       ssh->mainchan->type = CHAN_MAINSESSION;
-       ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
-       ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
-       add234(ssh->channels, ssh->mainchan);
-       update_specials_menu(ssh->frontend);
-       logevent("Opened direct-tcpip channel");
-       ssh->ncmode = TRUE;
-    } else {
-       ssh->mainchan = snew(struct ssh_channel);
-       ssh->mainchan->ssh = ssh;
-       ssh2_channel_init(ssh->mainchan);
-       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
-       ssh2_pkt_addstring(s->pktout, "session");
-       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
-       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);
        crWaitUntilV(pktin);
        if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
-           bombout(("Server refused to open a session"));
+           bombout(("Server refused to open channel"));
            crStopV;
            /* FIXME: error data comes back in FAILURE packet */
        }
@@ -9183,8 +9168,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
        add234(ssh->channels, ssh->mainchan);
        update_specials_menu(ssh->frontend);
-       logevent("Opened channel for session");
-       ssh->ncmode = FALSE;
+       logevent("Opened main channel");
     }
 
     /*
@@ -9226,100 +9210,92 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
      */
     ssh_setup_portfwd(ssh, ssh->conf);
 
-    /*
-     * Send the CHANNEL_REQUESTS for the main channel.  Each one is
-     * handled by its own little asynchronous co-routine.
-     */
+    if (ssh->mainchan && !ssh->ncmode) {
+       /*
+        * Send the CHANNEL_REQUESTS for the main session channel.
+        * Each one is handled by its own little asynchronous
+        * co-routine.
+        */
 
-    /*
-     * Potentially enable X11 forwarding.
-     */
-    /*
-     * Potentially enable X11 forwarding.
-     */
-    if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_x11_forward) &&
-       (ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
-                                         conf_get_int(ssh->conf, CONF_x11_auth), ssh->conf)))
-        ssh2_setup_x11(ssh->mainchan, NULL, NULL);
+       /* Potentially enable X11 forwarding. */
+       if (conf_get_int(ssh->conf, CONF_x11_forward) &&
+           (ssh->x11disp =
+            x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
+                              conf_get_int(ssh->conf, CONF_x11_auth),
+                              ssh->conf)))
+           ssh2_setup_x11(ssh->mainchan, NULL, NULL);
 
-    /*
-     * Potentially enable agent forwarding.
-     */
-    if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists())
-        ssh2_setup_agent(ssh->mainchan, NULL, NULL);
+       /* Potentially enable agent forwarding. */
+       if (conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists())
+           ssh2_setup_agent(ssh->mainchan, NULL, NULL);
 
-    /*
-     * Now allocate a pty for the session.
-     */
-    if (ssh->mainchan && !ssh->ncmode && !conf_get_int(ssh->conf, CONF_nopty)) {
-        ssh2_setup_pty(ssh->mainchan, NULL, NULL);
-    } else {
-       ssh->editing = ssh->echoing = 1;
-    }
+       /* Now allocate a pty for the session. */
+       if (!conf_get_int(ssh->conf, CONF_nopty))
+           ssh2_setup_pty(ssh->mainchan, NULL, NULL);
 
-    /*
-     * Send environment variables.
-     */
-    if (ssh->mainchan && !ssh->ncmode)
-        ssh2_setup_env(ssh->mainchan, NULL, NULL);
+       /* Send environment variables. */
+       ssh2_setup_env(ssh->mainchan, NULL, NULL);
 
-    /*
-     * 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.
-     */
-    if (ssh->mainchan && !ssh->ncmode) while (1) {
-       int subsys;
-       char *cmd;
+       /*
+        * 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.
+        */
+       while (1) {
+           int subsys;
+           char *cmd;
 
-       if (ssh->fallback_cmd) {
-           subsys = conf_get_int(ssh->conf, CONF_ssh_subsys2);
-           cmd = conf_get_str(ssh->conf, CONF_remote_cmd2);
-       } else {
-           subsys = conf_get_int(ssh->conf, CONF_ssh_subsys);
-           cmd = conf_get_str(ssh->conf, CONF_remote_cmd);
-       }
+           if (ssh->fallback_cmd) {
+               subsys = conf_get_int(ssh->conf, CONF_ssh_subsys2);
+               cmd = conf_get_str(ssh->conf, CONF_remote_cmd2);
+           } else {
+               subsys = conf_get_int(ssh->conf, CONF_ssh_subsys);
+               cmd = conf_get_str(ssh->conf, CONF_remote_cmd);
+           }
 
-       if (subsys) {
-           s->pktout = ssh2_chanreq_init(ssh->mainchan, "subsystem",
-                                         ssh2_response_authconn, NULL);
-           ssh2_pkt_addstring(s->pktout, cmd);
-       } else if (*cmd) {
-           s->pktout = ssh2_chanreq_init(ssh->mainchan, "exec",
-                                         ssh2_response_authconn, NULL);
-           ssh2_pkt_addstring(s->pktout, cmd);
-       } else {
-           s->pktout = ssh2_chanreq_init(ssh->mainchan, "shell",
-                                         ssh2_response_authconn, NULL);
-       }
-       ssh2_pkt_send(ssh, s->pktout);
+           if (subsys) {
+               s->pktout = ssh2_chanreq_init(ssh->mainchan, "subsystem",
+                                             ssh2_response_authconn, NULL);
+               ssh2_pkt_addstring(s->pktout, cmd);
+           } else if (*cmd) {
+               s->pktout = ssh2_chanreq_init(ssh->mainchan, "exec",
+                                             ssh2_response_authconn, NULL);
+               ssh2_pkt_addstring(s->pktout, cmd);
+           } else {
+               s->pktout = ssh2_chanreq_init(ssh->mainchan, "shell",
+                                             ssh2_response_authconn, NULL);
+           }
+           ssh2_pkt_send(ssh, s->pktout);
 
-       crWaitUntilV(pktin);
+           crWaitUntilV(pktin);
 
-       if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
-           if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
-               bombout(("Unexpected response to shell/command request:"
-                        " packet type %d", pktin->type));
+           if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
+               if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
+                   bombout(("Unexpected response to shell/command request:"
+                            " packet type %d", pktin->type));
+                   crStopV;
+               }
+               /*
+                * 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 &&
+                   *conf_get_str(ssh->conf, CONF_remote_cmd2)) {
+                   logevent("Primary command failed; attempting fallback");
+                   ssh->fallback_cmd = TRUE;
+                   continue;
+               }
+               bombout(("Server refused to start a shell/command"));
                crStopV;
+           } else {
+               logevent("Started a shell/command");
            }
-           /*
-            * 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 &&
-               *conf_get_str(ssh->conf, CONF_remote_cmd2)) {
-               logevent("Primary command failed; attempting fallback");
-               ssh->fallback_cmd = TRUE;
-               continue;
-           }
-           bombout(("Server refused to start a shell/command"));
-           crStopV;
-       } else {
-           logevent("Started a shell/command");
+           break;
        }
-       break;
+    } else {
+       ssh->editing = ssh->echoing = TRUE;
     }
 
     ssh->state = SSH_STATE_SESSION;
@@ -9329,13 +9305,6 @@ 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
-     * response handler.
-     */
-    ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_response;
-    ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_response;
-
-    /*
      * Transfer data!
      */
     if (ssh->ldisc)
@@ -9513,7 +9482,7 @@ static void ssh2_protocol_setup(Ssh ssh)
     ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug;
 }
 
-static void ssh2_timer(void *ctx, long now)
+static void ssh2_timer(void *ctx, unsigned long now)
 {
     Ssh ssh = (Ssh)ctx;
 
@@ -9521,7 +9490,7 @@ static void ssh2_timer(void *ctx, long now)
        return;
 
     if (!ssh->kex_in_progress && conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0 &&
-       now - ssh->next_rekey >= 0) {
+       now == ssh->next_rekey) {
        do_ssh2_transport(ssh, "timeout", -1, NULL);
     }
 }
@@ -9804,10 +9773,10 @@ static void ssh_reconfig(void *handle, Conf *conf)
     rekey_time = conf_get_int(conf, CONF_ssh_rekey_time);
     if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != rekey_time &&
        rekey_time != 0) {
-       long new_next = ssh->last_rekey + rekey_time*60*TICKSPERSEC;
-       long now = GETTICKCOUNT();
+       unsigned long new_next = ssh->last_rekey + rekey_time*60*TICKSPERSEC;
+       unsigned long now = GETTICKCOUNT();
 
-       if (new_next - now < 0) {
+       if (now - ssh->last_rekey > rekey_time*60*TICKSPERSEC) {
            rekeying = "timeout shortened";
        } else {
            ssh->next_rekey = schedule_timer(new_next - now, ssh2_timer, ssh);
@@ -10160,7 +10129,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org)
     Ssh ssh = c->ssh;
     struct Packet *pktout;
 
-    logeventf(ssh, "Opening forwarded connection to %s:%d", hostname, port);
+    logeventf(ssh, "Opening connection to %s:%d for %s", hostname, port, org);
 
     if (ssh->version == 1) {
        send_packet(ssh, SSH1_MSG_PORT_OPEN,
@@ -10170,11 +10139,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org)
                    /* PKT_STR, <org:orgport>, */
                    PKT_END);
     } else {
-       pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
-       ssh2_pkt_addstring(pktout, "direct-tcpip");
-       ssh2_pkt_adduint32(pktout, c->localid);
-       ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */
-       ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT);      /* our max pkt size */
+       pktout = ssh2_chanopen_init(c, "direct-tcpip");
        ssh2_pkt_addstring(pktout, hostname);
        ssh2_pkt_adduint32(pktout, port);
        /*