From c6b4179107355f38804d343201876bde9181c990 Mon Sep 17 00:00:00 2001 From: simon Date: Thu, 8 Dec 2011 19:15:58 +0000 Subject: [PATCH] Introduce a function sshfwd_unclean_close(), supplied by ssh.c to 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 | 24 +++++++++++++----------- ssh.c | 38 +++++++++++++++++++++++++++++++++++++- ssh.h | 1 + 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/portfwd.c b/portfwd.c index cd66a984..39ff5972 100644 --- 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 6d5ebd44..b4c634d3 100644 --- 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 60c54e4d..1ccc0740 100644 --- 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); /* -- 2.11.0