Don't close SSH-2 channels with outstanding channel requests on local error.
[u/mdw/putty] / ssh.c
diff --git a/ssh.c b/ssh.c
index a1cb34b..b773e85 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -439,6 +439,8 @@ enum {
 #define crState(t)     crStateP(t, ssh->t)
 #define crFinish(z)    } *crLine = 0; return (z); }
 #define crFinishV      } *crLine = 0; return; }
+#define crFinishFree(z, s)     } *crLine = 0; sfree(s); return (z); }
+#define crFinishFreeV(s)       } *crLine = 0; sfree(s); return; }
 #define crReturn(z)    \
        do {\
            *crLine =__LINE__; return (z); case __LINE__:;\
@@ -4276,25 +4278,19 @@ void sshfwd_write_eof(struct ssh_channel *c)
 void sshfwd_unclean_close(struct ssh_channel *c)
 {
     Ssh ssh = c->ssh;
-    struct Packet *pktout;
 
     if (ssh->state == SSH_STATE_CLOSED)
        return;
 
-    if (!(c->closes & CLOSES_SENT_CLOSE)) {
-        pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
-        ssh2_pkt_adduint32(pktout, c->remoteid);
-        ssh2_pkt_send(ssh, pktout);
-        c->closes |= CLOSES_SENT_EOF | CLOSES_SENT_CLOSE;
-    }
-
     switch (c->type) {
       case CHAN_X11:
         x11_close(c->u.x11.s);
+        logevent("Forwarded X11 connection terminated due to local error");
         break;
       case CHAN_SOCKDATA:
       case CHAN_SOCKDATA_DORMANT:
         pfd_close(c->u.pfd.s);
+        logevent("Forwarded port closed due to local error");
         break;
     }
     c->type = CHAN_ZOMBIE;
@@ -6944,18 +6940,20 @@ static void ssh2_channel_check_close(struct ssh_channel *c)
     Ssh ssh = c->ssh;
     struct Packet *pktout;
 
-    if ((c->closes & (CLOSES_SENT_EOF | CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE))
-        == (CLOSES_SENT_EOF | CLOSES_RCVD_EOF) && !c->v.v2.chanreq_head) {
+    if ((!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) ||
+        c->type == CHAN_ZOMBIE) &&
+       !c->v.v2.chanreq_head &&
+       !(c->closes & CLOSES_SENT_CLOSE)) {
         /*
-         * We have both sent and received EOF, and we have no
-         * outstanding channel requests, which means the
-         * channel is in final wind-up. But we haven't sent CLOSE, so
-         * let's do so now.
+         * We have both sent and received EOF (or the channel is a
+         * zombie), and we have no outstanding channel requests, which
+         * means the channel is in final wind-up. But we haven't sent
+         * CLOSE, so let's do so now.
          */
        pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
        ssh2_pkt_adduint32(pktout, c->remoteid);
        ssh2_pkt_send(ssh, pktout);
-        c->closes |= CLOSES_SENT_CLOSE;
+        c->closes |= CLOSES_SENT_EOF | CLOSES_SENT_CLOSE;
     }
 
     if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) {
@@ -7513,8 +7511,7 @@ static void ssh2_maybe_setup_x11(struct ssh_channel *c, struct Packet *pktin,
                logevent("X11 forwarding refused");
        }
     }
-    sfree(s);
-    crFinishV;
+    crFinishFreeV(s);
 }
 
 static void ssh2_maybe_setup_agent(struct ssh_channel *c, struct Packet *pktin,
@@ -7545,8 +7542,7 @@ static void ssh2_maybe_setup_agent(struct ssh_channel *c, struct Packet *pktin,
                logevent("Agent forwarding refused");
        }
     }
-    sfree(s);
-    crFinishV;
+    crFinishFreeV(s);
 }
 
 static void ssh2_maybe_setup_pty(struct ssh_channel *c, struct Packet *pktin,
@@ -7599,8 +7595,7 @@ static void ssh2_maybe_setup_pty(struct ssh_channel *c, struct Packet *pktin,
     } else {
        ssh->editing = ssh->echoing = 1;
     }
-    sfree(s);
-    crFinishV;
+    crFinishFreeV(s);
 }
 
 static void ssh2_setup_env(struct ssh_channel *c, struct Packet *pktin,
@@ -7663,9 +7658,8 @@ static void ssh2_setup_env(struct ssh_channel *c, struct Packet *pktin,
            c_write_str(ssh, "Server refused to set all environment variables\r\n");
        }
     }
-out:
-    sfree(s);
-    crFinishV;
+  out:;
+    crFinishFreeV(s);
 }
 
 /*