+ * When we receive CLOSE on a channel, we assume it comes with an
+ * implied EOF if we haven't seen EOF yet.
+ */
+ ssh2_channel_got_eof(c);
+
+ if (!(ssh->remote_bugs & BUG_SENDS_LATE_REQUEST_REPLY)) {
+ /*
+ * It also means we stop expecting to see replies to any
+ * outstanding channel requests, so clean those up too.
+ * (ssh_chanreq_init will enforce by assertion that we don't
+ * subsequently put anything back on this list.)
+ */
+ while (c->v.v2.chanreq_head) {
+ struct outstanding_channel_request *ocr = c->v.v2.chanreq_head;
+ ocr->handler(c, NULL, ocr->ctx);
+ c->v.v2.chanreq_head = ocr->next;
+ sfree(ocr);
+ }
+ }
+
+ /*
+ * And we also send an outgoing EOF, if we haven't already, on the
+ * assumption that CLOSE is a pretty forceful announcement that
+ * the remote side is doing away with the entire channel. (If it
+ * had wanted to send us EOF and continue receiving data from us,
+ * it would have just sent CHANNEL_EOF.)
+ */
+ if (!(c->closes & CLOSES_SENT_EOF)) {
+ /*
+ * Make sure we don't read any more from whatever our local
+ * data source is for this channel.
+ */
+ switch (c->type) {
+ case CHAN_MAINSESSION:
+ ssh->send_ok = 0; /* stop trying to read from stdin */
+ break;
+ case CHAN_X11:
+ x11_override_throttle(c->u.x11.xconn, 1);
+ break;
+ case CHAN_SOCKDATA:
+ pfd_override_throttle(c->u.pfd.pf, 1);
+ break;
+ }
+
+ /*
+ * Abandon any buffered data we still wanted to send to this
+ * channel. Receiving a CHANNEL_CLOSE is an indication that
+ * the server really wants to get on and _destroy_ this
+ * channel, and it isn't going to send us any further
+ * WINDOW_ADJUSTs to permit us to send pending stuff.
+ */
+ bufchain_clear(&c->v.v2.outbuffer);
+
+ /*
+ * Send outgoing EOF.
+ */
+ sshfwd_write_eof(c);
+ }
+
+ /*
+ * Now process the actual close.
+ */
+ if (!(c->closes & CLOSES_RCVD_CLOSE)) {
+ c->closes |= CLOSES_RCVD_CLOSE;
+ ssh2_channel_check_close(c);
+ }
+}
+
+static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
+{
+ struct ssh_channel *c;
+
+ c = ssh2_channel_msg(ssh, pktin);
+ if (!c)
+ return;
+ if (c->type == CHAN_SHARING) {
+ share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
+ pktin->body, pktin->length);
+ return;
+ }
+ assert(c->halfopen); /* ssh2_channel_msg will have enforced this */
+ c->remoteid = ssh_pkt_getuint32(pktin);
+ c->halfopen = FALSE;
+ c->v.v2.remwindow = ssh_pkt_getuint32(pktin);
+ c->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
+
+ if (c->type == CHAN_SOCKDATA_DORMANT) {
+ c->type = CHAN_SOCKDATA;
+ if (c->u.pfd.pf)
+ pfd_confirm(c->u.pfd.pf);
+ } else if (c->type == CHAN_ZOMBIE) {
+ /*
+ * This case can occur if a local socket error occurred
+ * between us sending out CHANNEL_OPEN and receiving
+ * OPEN_CONFIRMATION. In this case, all we can do is
+ * immediately initiate close proceedings now that we know the
+ * server's id to put in the close message.
+ */
+ ssh2_channel_check_close(c);
+ } else {
+ /*
+ * We never expect to receive OPEN_CONFIRMATION for any
+ * *other* channel type (since only local-to-remote port
+ * forwardings cause us to send CHANNEL_OPEN after the main
+ * channel is live - all other auxiliary channel types are
+ * initiated from the server end). It's safe to enforce this
+ * by assertion rather than by ssh_disconnect, because the
+ * real point is that we never constructed a half-open channel
+ * structure in the first place with any type other than the
+ * above.
+ */
+ assert(!"Funny channel type in ssh2_msg_channel_open_confirmation");
+ }
+
+ if (c->pending_eof)
+ ssh_channel_try_eof(c); /* in case we had a pending EOF */
+}
+
+static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
+{
+ static const char *const reasons[] = {
+ "<unknown reason code>",
+ "Administratively prohibited",
+ "Connect failed",
+ "Unknown channel type",
+ "Resource shortage",
+ };
+ unsigned reason_code;
+ char *reason_string;
+ int reason_length;
+ struct ssh_channel *c;
+
+ c = ssh2_channel_msg(ssh, pktin);
+ if (!c)
+ return;
+ if (c->type == CHAN_SHARING) {
+ share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
+ pktin->body, pktin->length);
+ return;
+ }
+ assert(c->halfopen); /* ssh2_channel_msg will have enforced this */
+
+ if (c->type == CHAN_SOCKDATA_DORMANT) {
+ reason_code = ssh_pkt_getuint32(pktin);
+ if (reason_code >= lenof(reasons))
+ reason_code = 0; /* ensure reasons[reason_code] in range */
+ ssh_pkt_getstring(pktin, &reason_string, &reason_length);
+ logeventf(ssh, "Forwarded connection refused by server: %s [%.*s]",
+ reasons[reason_code], reason_length, reason_string);
+
+ pfd_close(c->u.pfd.pf);
+ } else if (c->type == CHAN_ZOMBIE) {
+ /*
+ * This case can occur if a local socket error occurred
+ * between us sending out CHANNEL_OPEN and receiving
+ * OPEN_FAILURE. In this case, we need do nothing except allow
+ * the code below to throw the half-open channel away.
+ */
+ } else {
+ /*
+ * We never expect to receive OPEN_FAILURE for any *other*
+ * channel type (since only local-to-remote port forwardings
+ * cause us to send CHANNEL_OPEN after the main channel is
+ * live - all other auxiliary channel types are initiated from
+ * the server end). It's safe to enforce this by assertion
+ * rather than by ssh_disconnect, because the real point is
+ * that we never constructed a half-open channel structure in
+ * the first place with any type other than the above.
+ */
+ assert(!"Funny channel type in ssh2_msg_channel_open_failure");
+ }
+
+ del234(ssh->channels, c);
+ sfree(c);
+}
+
+static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
+{
+ char *type;
+ int typelen, want_reply;
+ int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */
+ struct ssh_channel *c;
+ struct Packet *pktout;
+
+ c = ssh2_channel_msg(ssh, pktin);
+ if (!c)
+ return;
+ if (c->type == CHAN_SHARING) {
+ share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
+ pktin->body, pktin->length);
+ return;
+ }
+ ssh_pkt_getstring(pktin, &type, &typelen);
+ want_reply = ssh2_pkt_getbool(pktin);
+
+ if (c->closes & CLOSES_SENT_CLOSE) {
+ /*
+ * We don't reply to channel requests after we've sent
+ * CHANNEL_CLOSE for the channel, because our reply might
+ * cross in the network with the other side's CHANNEL_CLOSE
+ * and arrive after they have wound the channel up completely.
+ */
+ want_reply = FALSE;
+ }
+
+ /*
+ * Having got the channel number, we now look at