Introduce a function sshfwd_unclean_close(), supplied by ssh.c to
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Thu, 8 Dec 2011 19:15:58 +0000 (19:15 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Thu, 8 Dec 2011 19:15:58 +0000 (19:15 +0000)
subsidiary network modules like portfwd.c. To be called when the
subsidiary module experiences a socket error: it sends an emergency
CHANNEL_CLOSE (not just outgoing CHANNEL_EOF), and immediately deletes
the local side of the channel. (I've invented a new channel type in
ssh.c called CHAN_ZOMBIE, for channels whose original local side has
already been thrown away and they're just hanging around waiting to
receive the acknowledging CHANNEL_CLOSE.)

As a result of this and the last few commits, I can now run a port
forwarding session in which a local socket error occurs on a forwarded
port, and PuTTY now handles it apparently correctly, closing both the
SSH channel and the local socket and then actually recognising that
it's OK to terminate when all _other_ channels have been closed.
Previously the channel corresponding to the duff connection would
linger around (because of net_pending_errors never being called), and
keep being selected on (hence chewing CPU), and inhibit program
termination at the end of the session (because not all channels were
closed).

git-svn-id: svn://svn.tartarus.org/sgt/putty@9364 cda61777-01e9-0310-a592-d414129be87e

portfwd.c
ssh.c
ssh.h

index cd66a98..39ff597 100644 (file)
--- a/portfwd.c
+++ b/portfwd.c
@@ -61,17 +61,19 @@ static int pfd_closing(Plug plug, const char *error_msg, int error_code,
 {
     struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;
 
-    /*
-     * We have no way to communicate down the forwarded connection,
-     * so if an error occurred on the socket, we just ignore it
-     * and treat it like a proper close.
-     *
-     * FIXME: except we could initiate a full close here instead of
-     * just an outgoing EOF? ssh.c currently has no API for that, but
-     * it could.
-     */
-    if (pr->c)
-       sshfwd_write_eof(pr->c);
+    if (error_msg) {
+        /*
+         * Socket error. Slam the connection instantly shut.
+         */
+        sshfwd_unclean_close(pr->c);
+    } else {
+        /*
+         * Ordinary EOF received on socket. Send an EOF on the SSH
+         * channel.
+         */
+        if (pr->c)
+            sshfwd_write_eof(pr->c);
+    }
 
     return 1;
 }
diff --git a/ssh.c b/ssh.c
index 6d5ebd4..b4c634d 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -558,7 +558,15 @@ enum {                                    /* channel types */
     CHAN_X11,
     CHAN_AGENT,
     CHAN_SOCKDATA,
-    CHAN_SOCKDATA_DORMANT             /* one the remote hasn't confirmed */
+    CHAN_SOCKDATA_DORMANT,            /* one the remote hasn't confirmed */
+    /*
+     * CHAN_ZOMBIE is used to indicate a channel for which we've
+     * already destroyed the local data source: for instance, if a
+     * forwarded port experiences a socket error on the local side, we
+     * immediately destroy its local socket and turn the SSH channel
+     * into CHAN_ZOMBIE.
+     */
+    CHAN_ZOMBIE
 };
 
 /*
@@ -4250,6 +4258,34 @@ void sshfwd_write_eof(struct ssh_channel *c)
     ssh_channel_try_eof(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)
+        return;
+
+    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);
+        break;
+      case CHAN_SOCKDATA:
+      case CHAN_SOCKDATA_DORMANT:
+        pfd_close(c->u.pfd.s);
+        break;
+    }
+    c->type = CHAN_ZOMBIE;
+    ssh2_channel_check_close(c);
+}
+
 int sshfwd_write(struct ssh_channel *c, char *buf, int len)
 {
     Ssh ssh = c->ssh;
diff --git a/ssh.h b/ssh.h
index 60c54e4..1ccc074 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -11,6 +11,7 @@ struct ssh_channel;
 
 extern int sshfwd_write(struct ssh_channel *c, char *, int);
 extern void sshfwd_write_eof(struct ssh_channel *c);
+extern void sshfwd_unclean_close(struct ssh_channel *c);
 extern void sshfwd_unthrottle(struct ssh_channel *c, int bufsize);
 
 /*