1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-25 09:12:24 +00:00

Fix `disconnect': arrange that we keep track of when we're expecting

to see the server slam the TCP connection shut (i.e. almost never,
unless it's just sent us an SSH_MSG_DISCONNECT), and treat an
unexpected closure as a non-clean session termination. Previously
any server-initiated connection closure was being treated as a clean
exit, which was a hangover from the good old Telnet-only days.

[originally from svn r5098]
[this svn revision also touched putty-wishlist]
This commit is contained in:
Simon Tatham 2005-01-11 19:33:41 +00:00
parent 95368801d3
commit f3b5bddd32

40
ssh.c
View File

@ -561,7 +561,7 @@ static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len);
static void ssh_throttle_all(Ssh ssh, int enable, int bufsize); static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
static void ssh2_set_window(struct ssh_channel *c, unsigned newwin); static void ssh2_set_window(struct ssh_channel *c, unsigned newwin);
static int ssh_sendbuffer(void *handle); static int ssh_sendbuffer(void *handle);
static void ssh_do_close(Ssh ssh); static int ssh_do_close(Ssh ssh, int notify_exit);
static unsigned long ssh_pkt_getuint32(struct Packet *pkt); static unsigned long ssh_pkt_getuint32(struct Packet *pkt);
static int ssh2_pkt_getbool(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 ssh_pkt_getstring(struct Packet *pkt, char **p, int *length);
@ -642,6 +642,7 @@ struct ssh_tag {
tree234 *channels; /* indexed by local id */ tree234 *channels; /* indexed by local id */
struct ssh_channel *mainchan; /* primary session channel */ struct ssh_channel *mainchan; /* primary session channel */
int exitcode; int exitcode;
int close_expected;
tree234 *rportfwds, *portfwds; tree234 *rportfwds, *portfwds;
@ -772,7 +773,7 @@ static void logeventf(Ssh ssh, const char *fmt, ...)
#define bombout(msg) \ #define bombout(msg) \
do { \ do { \
char *text = dupprintf msg; \ char *text = dupprintf msg; \
ssh_do_close(ssh); \ ssh_do_close(ssh, FALSE); \
logevent(text); \ logevent(text); \
connection_fatal(ssh->frontend, "%s", text); \ connection_fatal(ssh->frontend, "%s", text); \
sfree(text); \ sfree(text); \
@ -2368,16 +2369,19 @@ static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
crFinishV; crFinishV;
} }
static void ssh_do_close(Ssh ssh) static int ssh_do_close(Ssh ssh, int notify_exit)
{ {
int i; int i, ret = 0;
struct ssh_channel *c; struct ssh_channel *c;
ssh->state = SSH_STATE_CLOSED; ssh->state = SSH_STATE_CLOSED;
if (ssh->s) { if (ssh->s) {
sk_close(ssh->s); sk_close(ssh->s);
ssh->s = NULL; ssh->s = NULL;
notify_remote_exit(ssh->frontend); if (notify_exit)
notify_remote_exit(ssh->frontend);
else
ret = 1;
} }
/* /*
* Now we must shut down any port and X forwardings going * Now we must shut down any port and X forwardings going
@ -2399,20 +2403,29 @@ static void ssh_do_close(Ssh ssh)
sfree(c); sfree(c);
} }
} }
return ret;
} }
static int ssh_closing(Plug plug, const char *error_msg, int error_code, static int ssh_closing(Plug plug, const char *error_msg, int error_code,
int calling_back) int calling_back)
{ {
Ssh ssh = (Ssh) plug; Ssh ssh = (Ssh) plug;
ssh_do_close(ssh); int need_notify = ssh_do_close(ssh, FALSE);
if (!error_msg && !ssh->close_expected) {
error_msg = "Server unexpectedly closed network connection";
}
if (error_msg) { if (error_msg) {
/* A socket error has occurred. */ /* A socket error has occurred. */
logevent(error_msg); logevent(error_msg);
connection_fatal(ssh->frontend, "%s", error_msg); connection_fatal(ssh->frontend, "%s", error_msg);
} else { } else {
/* Otherwise, the remote side closed the connection normally. */ logevent("Server closed network connection");
} }
if (need_notify)
notify_remote_exit(ssh->frontend);
return 0; return 0;
} }
@ -2421,7 +2434,7 @@ static int ssh_receive(Plug plug, int urgent, char *data, int len)
Ssh ssh = (Ssh) plug; Ssh ssh = (Ssh) plug;
ssh_gotdata(ssh, (unsigned char *)data, len); ssh_gotdata(ssh, (unsigned char *)data, len);
if (ssh->state == SSH_STATE_CLOSED) { if (ssh->state == SSH_STATE_CLOSED) {
ssh_do_close(ssh); ssh_do_close(ssh, TRUE);
return 0; return 0;
} }
return 1; return 1;
@ -2922,6 +2935,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
* Terminate. * Terminate.
*/ */
logevent("No username provided. Abandoning session."); logevent("No username provided. Abandoning session.");
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0); ssh_closing((Plug)ssh, NULL, 0, 0);
crStop(1); crStop(1);
} }
@ -3270,6 +3284,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
PKT_END); PKT_END);
logevent("Unable to authenticate"); logevent("Unable to authenticate");
connection_fatal(ssh->frontend, "Unable to authenticate"); connection_fatal(ssh->frontend, "Unable to authenticate");
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0); ssh_closing((Plug)ssh, NULL, 0, 0);
crStop(1); crStop(1);
} }
@ -4322,6 +4337,7 @@ static void ssh1_smsg_exit_status(Ssh ssh, struct Packet *pktin)
* encrypted packet, we close the session once * encrypted packet, we close the session once
* we've sent EXIT_CONFIRMATION. * we've sent EXIT_CONFIRMATION.
*/ */
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0); ssh_closing((Plug)ssh, NULL, 0, 0);
} }
@ -5600,6 +5616,7 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
ssh2_pkt_addstring(s->pktout, "en"); /* language tag */ ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
ssh2_pkt_send_noqueue(ssh, s->pktout); ssh2_pkt_send_noqueue(ssh, s->pktout);
#endif #endif
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0); ssh_closing((Plug)ssh, NULL, 0, 0);
} }
} }
@ -5700,6 +5717,7 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
ssh2_pkt_addstring(pktout, "en"); /* language tag */ ssh2_pkt_addstring(pktout, "en"); /* language tag */
ssh2_pkt_send_noqueue(ssh, pktout); ssh2_pkt_send_noqueue(ssh, pktout);
connection_fatal(ssh->frontend, "%s", buf); connection_fatal(ssh->frontend, "%s", buf);
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0); ssh_closing((Plug)ssh, NULL, 0, 0);
return; return;
} }
@ -6053,6 +6071,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
* Terminate. * Terminate.
*/ */
logevent("No username provided. Abandoning session."); logevent("No username provided. Abandoning session.");
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0); ssh_closing((Plug)ssh, NULL, 0, 0);
crStopV; crStopV;
} }
@ -6614,6 +6633,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
logevent("Unable to authenticate"); logevent("Unable to authenticate");
connection_fatal(ssh->frontend, connection_fatal(ssh->frontend,
"Unable to authenticate"); "Unable to authenticate");
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0); ssh_closing((Plug)ssh, NULL, 0, 0);
crStopV; crStopV;
} }
@ -6812,6 +6832,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
" methods available"); " methods available");
ssh2_pkt_addstring(s->pktout, "en"); /* language tag */ ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
ssh2_pkt_send_noqueue(ssh, s->pktout); ssh2_pkt_send_noqueue(ssh, s->pktout);
ssh->close_expected = TRUE;
ssh_closing((Plug)ssh, NULL, 0, 0); ssh_closing((Plug)ssh, NULL, 0, 0);
crStopV; crStopV;
} }
@ -7393,6 +7414,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
ssh->kex_ctx = NULL; ssh->kex_ctx = NULL;
ssh->hostkey = NULL; ssh->hostkey = NULL;
ssh->exitcode = -1; ssh->exitcode = -1;
ssh->close_expected = FALSE;
ssh->state = SSH_STATE_PREPACKET; ssh->state = SSH_STATE_PREPACKET;
ssh->size_needed = FALSE; ssh->size_needed = FALSE;
ssh->eof_needed = FALSE; ssh->eof_needed = FALSE;
@ -7548,7 +7570,7 @@ static void ssh_free(void *handle)
ssh->crcda_ctx = NULL; ssh->crcda_ctx = NULL;
} }
if (ssh->s) if (ssh->s)
ssh_do_close(ssh); ssh_do_close(ssh, TRUE);
expire_timer_context(ssh); expire_timer_context(ssh);
if (ssh->pinger) if (ssh->pinger)
pinger_free(ssh->pinger); pinger_free(ssh->pinger);