* A channel is completely finished with when all four bits are set.
*/
int closes;
+
+ /*
+ * This flag indicates that a close is pending on the outgoing
+ * side of the channel: that is, wherever we're getting the data
+ * for this channel has sent us some data followed by EOF. We
+ * can't actually close the channel until we've finished sending
+ * the data, so we set this flag instead to remind us to
+ * initiate the closing process once our buffer is clear.
+ */
+ int pending_close;
+
/*
* True if this channel is causing the underlying connection to be
* throttled.
if (ssh->version == 1) {
send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
PKT_END);
+ c->closes = 1; /* sent MSG_CLOSE */
} else {
- struct Packet *pktout;
- pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(pktout, c->remoteid);
- ssh2_pkt_send(ssh, pktout);
+ int bytes_to_send = bufchain_size(&c->v.v2.outbuffer);
+ if (bytes_to_send > 0) {
+ /*
+ * If we still have unsent data in our outgoing
+ * buffer for this channel, we can't actually
+ * initiate a close operation yet or that data
+ * will be lost. Instead, set the pending_close
+ * flag so that when we do clear the buffer
+ * we'll start closing the channel.
+ */
+ char logmsg[160] = {'\0'};
+ sprintf(
+ logmsg,
+ "Forwarded port pending to be closed : "
+ "%d bytes remaining",
+ bytes_to_send);
+ logevent(logmsg);
+
+ c->pending_close = TRUE;
+ } else {
+ /*
+ * No locally buffered data, so we can send the
+ * close message immediately.
+ */
+ struct Packet *pktout;
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes = 1; /* sent MSG_CLOSE */
+ logevent("Nothing left to send, closing channel");
+ }
}
}
- c->closes = 1; /* sent MSG_CLOSE */
+
if (c->type == CHAN_X11) {
c->u.x11.s = NULL;
logevent("Forwarded X11 connection terminated");
rpf = del234(ssh->rportfwds, pf);
assert(rpf == pf);
+ pf->pfrec->remote = NULL;
free_rportfwd(pf);
}
}
logeventf(ssh, "Cancelling %s", message);
sfree(message);
+ /* epf->remote or epf->local may be NULL if setting up a
+ * forwarding failed. */
if (epf->remote) {
struct ssh_rportfwd *rpf = epf->remote;
struct Packet *pktout;
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
+ c->pending_close = FALSE;
c->throttling_conn = 0;
c->type = CHAN_X11; /* identify channel type */
add234(ssh->channels, c);
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
+ c->pending_close = FALSE;
c->throttling_conn = 0;
c->type = CHAN_AGENT; /* identify channel type */
c->u.a.lensofar = 0;
c->halfopen = FALSE;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
+ c->pending_close = FALSE;
c->throttling_conn = 0;
c->type = CHAN_SOCKDATA; /* identify channel type */
add234(ssh->channels, c);
return bufchain_size(&c->v.v2.outbuffer);
}
-static void ssh2_try_send_and_unthrottle(struct ssh_channel *c)
+static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)
{
int bufsize;
if (c->closes)
break;
}
}
+
+ /*
+ * If we've emptied the channel's output buffer and there's a
+ * pending close event, start the channel-closing procedure.
+ */
+ if (c->pending_close && bufchain_size(&c->v.v2.outbuffer) == 0) {
+ struct Packet *pktout;
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes = 1;
+ c->pending_close = FALSE;
+ }
}
/*
Ssh ssh = c->ssh;
c->localid = alloc_channel_id(ssh);
c->closes = 0;
+ c->pending_close = FALSE;
c->throttling_conn = FALSE;
c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
ssh->cfg.ssh_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
return;
if (!c->closes) {
c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
- ssh2_try_send_and_unthrottle(c);
+ ssh2_try_send_and_unthrottle(ssh, c);
}
}
}
while (1) {
+ char *methods = NULL;
+ int methlen = 0;
+
/*
* Wait for the result of the last authentication request.
*/
* helpfully try next.
*/
if (pktin->type == SSH2_MSG_USERAUTH_FAILURE) {
- char *methods;
- int methlen;
ssh_pkt_getstring(pktin, &methods, &methlen);
if (!ssh2_pkt_getbool(pktin)) {
/*
sfree(s->password);
} else {
+ char *str = dupprintf("No supported authentication methods available"
+ " (server sent: %.*s)",
+ methlen, methods);
- ssh_disconnect(ssh, NULL,
+ ssh_disconnect(ssh, str,
"No supported authentication methods available",
SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
FALSE);
+ sfree(str);
+
crStopV;
}
* Try to send data on all channels if we can.
*/
for (i = 0; NULL != (c = index234(ssh->channels, i)); i++)
- ssh2_try_send_and_unthrottle(c);
+ ssh2_try_send_and_unthrottle(ssh, c);
}
}
if (ssh->rportfwds) {
while ((pf = delpos234(ssh->rportfwds, 0)) != NULL)
- sfree(pf);
+ free_rportfwd(pf);
freetree234(ssh->rportfwds);
ssh->rportfwds = NULL;
}