Don't try to use SecureZeroMemory under Winelib, since it isn't available
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index a40c00d..4bbb30b 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -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;
@@ -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);
 }
 
 /*
@@ -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;
@@ -10153,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,
@@ -10163,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);
        /*