* 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");
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);
}
}
* 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);
}
}