- if (pktin->type == SSH2_MSG_CHANNEL_DATA ||
- pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
- char *data;
- int length;
- unsigned i = ssh_pkt_getuint32(pktin);
- struct ssh_channel *c;
- c = find234(ssh->channels, &i, ssh_channelfind);
- if (!c)
- continue; /* nonexistent channel */
- if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
- ssh_pkt_getuint32(pktin) != SSH2_EXTENDED_DATA_STDERR)
- continue; /* extended but not stderr */
- ssh_pkt_getstring(pktin, &data, &length);
- if (data) {
- int bufsize = 0;
- c->v.v2.locwindow -= length;
- switch (c->type) {
- case CHAN_MAINSESSION:
- bufsize =
- from_backend(ssh->frontend, pktin->type ==
- SSH2_MSG_CHANNEL_EXTENDED_DATA,
- data, length);
- break;
- case CHAN_X11:
- bufsize = x11_send(c->u.x11.s, data, length);
- break;
- case CHAN_SOCKDATA:
- bufsize = pfd_send(c->u.pfd.s, data, length);
- break;
- case CHAN_AGENT:
- while (length > 0) {
- if (c->u.a.lensofar < 4) {
- int l = min(4 - c->u.a.lensofar, length);
- memcpy(c->u.a.msglen + c->u.a.lensofar,
- data, l);
- data += l;
- length -= l;
- c->u.a.lensofar += l;
- }
- if (c->u.a.lensofar == 4) {
- c->u.a.totallen =
- 4 + GET_32BIT(c->u.a.msglen);
- c->u.a.message = snewn(c->u.a.totallen,
- unsigned char);
- memcpy(c->u.a.message, c->u.a.msglen, 4);
- }
- if (c->u.a.lensofar >= 4 && length > 0) {
- int l =
- min(c->u.a.totallen - c->u.a.lensofar,
- length);
- memcpy(c->u.a.message + c->u.a.lensofar,
- data, l);
- data += l;
- length -= l;
- c->u.a.lensofar += l;
- }
- if (c->u.a.lensofar == c->u.a.totallen) {
- void *reply;
- int replylen;
- if (agent_query(c->u.a.message,
- c->u.a.totallen,
- &reply, &replylen,
- ssh_agentf_callback, c))
- ssh_agentf_callback(c, reply, replylen);
- sfree(c->u.a.message);
- c->u.a.lensofar = 0;
- }
- }
- bufsize = 0;
- break;
- }
- /*
- * If we are not buffering too much data,
- * enlarge the window again at the remote side.
- */
- if (bufsize < OUR_V2_WINSIZE)
- ssh2_set_window(c, OUR_V2_WINSIZE - bufsize);
- }
- } else if (pktin->type == SSH2_MSG_CHANNEL_EOF) {
- unsigned i = ssh_pkt_getuint32(pktin);
- struct ssh_channel *c;
-
- c = find234(ssh->channels, &i, ssh_channelfind);
- if (!c)
- continue; /* nonexistent channel */
-
- if (c->type == CHAN_X11) {
- /*
- * Remote EOF on an X11 channel means we should
- * wrap up and close the channel ourselves.
- */
- x11_close(c->u.x11.s);
- sshfwd_close(c);
- } else if (c->type == CHAN_AGENT) {
- sshfwd_close(c);
- } else if (c->type == CHAN_SOCKDATA) {
- pfd_close(c->u.pfd.s);
- sshfwd_close(c);
- }
- } else if (pktin->type == SSH2_MSG_CHANNEL_CLOSE) {
- unsigned i = ssh_pkt_getuint32(pktin);
- struct ssh_channel *c;
-
- c = find234(ssh->channels, &i, ssh_channelfind);
- if (!c || ((int)c->remoteid) == -1) {
- bombout(("Received CHANNEL_CLOSE for %s channel %d\n",
- c ? "half-open" : "nonexistent", i));
- crStopV;
- }
- /* Do pre-close processing on the channel. */
- switch (c->type) {
- case CHAN_MAINSESSION:
- ssh->mainchan = NULL;
- update_specials_menu(ssh->frontend);
- break;
- case CHAN_X11:
- if (c->u.x11.s != NULL)
- x11_close(c->u.x11.s);
- sshfwd_close(c);
- break;
- case CHAN_AGENT:
- sshfwd_close(c);
- break;
- case CHAN_SOCKDATA:
- if (c->u.pfd.s != NULL)
- pfd_close(c->u.pfd.s);
- sshfwd_close(c);
- break;
- }
- if (c->closes == 0) {
- s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(s->pktout, c->remoteid);
- ssh2_pkt_send(ssh, s->pktout);
- }
- del234(ssh->channels, c);
- bufchain_clear(&c->v.v2.outbuffer);
- sfree(c);
-
- /*
- * See if that was the last channel left open.
- * (This is only our termination condition if we're
- * not running in -N mode.)
- */
- if (!ssh->cfg.ssh_no_shell && count234(ssh->channels) == 0) {
- logevent("All channels closed. Disconnecting");
-#if 0
- /*
- * We used to send SSH_MSG_DISCONNECT here,
- * because I'd believed that _every_ conforming
- * SSH2 connection had to end with a disconnect
- * being sent by at least one side; apparently
- * I was wrong and it's perfectly OK to
- * unceremoniously slam the connection shut
- * when you're done, and indeed OpenSSH feels
- * this is more polite than sending a
- * DISCONNECT. So now we don't.
- */
- s->pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(s->pktout, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(s->pktout, "All open channels closed");
- ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, s->pktout);
-#endif
- ssh_closing((Plug)ssh, NULL, 0, 0);
- crStopV;
- }
- continue; /* remote sends close; ignore (FIXME) */
- } else if (pktin->type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
- unsigned i = ssh_pkt_getuint32(pktin);
- struct ssh_channel *c;
- c = find234(ssh->channels, &i, ssh_channelfind);
- if (!c)
- continue; /* nonexistent channel */
- if (c->type != CHAN_SOCKDATA_DORMANT)
- continue; /* dunno why they're confirming this */
- c->remoteid = ssh_pkt_getuint32(pktin);
- c->type = CHAN_SOCKDATA;
- c->v.v2.remwindow = ssh_pkt_getuint32(pktin);
- c->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
- if (c->u.pfd.s)
- pfd_confirm(c->u.pfd.s);
- if (c->closes) {
- /*
- * We have a pending close on this channel,
- * which we decided on before the server acked
- * the channel open. So now we know the
- * remoteid, we can close it again.
- */
- s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(s->pktout, c->remoteid);
- ssh2_pkt_send(ssh, s->pktout);
- }
- } else if (pktin->type == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
- static const char *const reasons[] = {
- "<unknown reason code>",
- "Administratively prohibited",
- "Connect failed",
- "Unknown channel type",
- "Resource shortage",
- };
- unsigned i = ssh_pkt_getuint32(pktin);
- unsigned reason_code;
- char *reason_string;
- int reason_length;
- char *message;
- struct ssh_channel *c;
- c = find234(ssh->channels, &i, ssh_channelfind);
- if (!c)
- continue; /* nonexistent channel */
- if (c->type != CHAN_SOCKDATA_DORMANT)
- continue; /* dunno why they're failing this */
-
- 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);
- message = dupprintf("Forwarded connection refused by"
- " server: %s [%.*s]", reasons[reason_code],
- reason_length, reason_string);
- logevent(message);
- sfree(message);
-
- pfd_close(c->u.pfd.s);
-
- del234(ssh->channels, c);
- sfree(c);
- } else if (pktin->type == SSH2_MSG_CHANNEL_REQUEST) {
- unsigned localid;
- char *type;
- int typelen, want_reply;
- int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */
- struct ssh_channel *c;
-
- localid = ssh_pkt_getuint32(pktin);
- ssh_pkt_getstring(pktin, &type, &typelen);
- want_reply = ssh2_pkt_getbool(pktin);
-
- /*
- * First, check that the channel exists. Otherwise,
- * we can instantly disconnect with a rude message.
- */
- c = find234(ssh->channels, &localid, ssh_channelfind);
- if (!c) {
- char buf[80];
- sprintf(buf, "Received channel request for nonexistent"
- " channel %d", localid);
- logevent(buf);
- s->pktout = ssh2_pkt_init(SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(s->pktout, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(s->pktout, buf);
- ssh2_pkt_addstring(s->pktout, "en"); /* language tag */
- ssh2_pkt_send_noqueue(ssh, s->pktout);
- connection_fatal(ssh->frontend, "%s", buf);
- ssh_closing((Plug)ssh, NULL, 0, 0);
- crStopV;
- }
-
- /*
- * Having got the channel number, we now look at
- * the request type string to see if it's something
- * we recognise.
- */
- if (c == ssh->mainchan) {
- /*
- * We recognise "exit-status" and "exit-signal" on
- * the primary channel.
- */
- if (typelen == 11 &&
- !memcmp(type, "exit-status", 11)) {
-
- ssh->exitcode = ssh_pkt_getuint32(pktin);
- logeventf(ssh, "Server sent command exit status %d",
- ssh->exitcode);
- reply = SSH2_MSG_CHANNEL_SUCCESS;
-
- } else if (typelen == 11 &&
- !memcmp(type, "exit-signal", 11)) {
-
- int is_plausible = TRUE, is_int = FALSE;
- char *fmt_sig = "", *fmt_msg = "";
- char *msg;
- int msglen = 0, core = FALSE;
- /* ICK: older versions of OpenSSH (e.g. 3.4p1)
- * provide an `int' for the signal, despite its
- * having been a `string' in the drafts since at
- * least 2001. (Fixed in session.c 1.147.) Try to
- * infer which we can safely parse it as. */
- {
- unsigned char *p = pktin->body +
- pktin->savedpos;
- long len = pktin->length - pktin->savedpos;
- unsigned long num = GET_32BIT(p); /* what is it? */
- /* If it's 0, it hardly matters; assume string */
- if (num == 0) {
- is_int = FALSE;
- } else {
- int maybe_int = FALSE, maybe_str = FALSE;
-#define CHECK_HYPOTHESIS(offset, result) \
- do { \
- long q = offset; \
- if (q >= 0 && q+4 <= len) { \
- q = q + 4 + GET_32BIT(p+q); \
- if (q >= 0 && q+4 <= len && \
- (q = q + 4 + GET_32BIT(p+q)) && q == len) \
- result = TRUE; \
- } \
- } while(0)
- CHECK_HYPOTHESIS(4+1, maybe_int);
- CHECK_HYPOTHESIS(4+num+1, maybe_str);
-#undef CHECK_HYPOTHESIS
- if (maybe_int && !maybe_str)
- is_int = TRUE;
- else if (!maybe_int && maybe_str)
- is_int = FALSE;
- else
- /* Crikey. Either or neither. Panic. */
- is_plausible = FALSE;
- }
- }
- if (is_plausible) {
- if (is_int) {
- /* Old non-standard OpenSSH. */
- int signum = ssh_pkt_getuint32(pktin);
- fmt_sig = dupprintf(" %d", signum);
- } else {
- /* As per the drafts. */
- char *sig;
- int siglen;
- ssh_pkt_getstring(pktin, &sig, &siglen);
- /* Signal name isn't supposed to be blank, but
- * let's cope gracefully if it is. */
- if (siglen) {
- fmt_sig = dupprintf(" \"%.*s\"",
- siglen, sig);
- }
- }
- core = ssh2_pkt_getbool(pktin);
- ssh_pkt_getstring(pktin, &msg, &msglen);
- if (msglen) {
- fmt_msg = dupprintf(" (\"%.*s\")", msglen, msg);
- }
- /* ignore lang tag */
- } /* else don't attempt to parse */
- logeventf(ssh, "Server exited on signal%s%s%s",
- fmt_sig, core ? " (core dumped)" : "",
- fmt_msg);
- if (*fmt_sig) sfree(fmt_sig);
- if (*fmt_msg) sfree(fmt_msg);
- reply = SSH2_MSG_CHANNEL_SUCCESS;
-
- }
- } else {
- /*
- * This is a channel request we don't know
- * about, so we now either ignore the request
- * or respond with CHANNEL_FAILURE, depending
- * on want_reply.
- */
- reply = SSH2_MSG_CHANNEL_FAILURE;
- }
- if (want_reply) {
- s->pktout = ssh2_pkt_init(reply);
- ssh2_pkt_adduint32(s->pktout, c->remoteid);
- ssh2_pkt_send(ssh, s->pktout);
- }
- } else if (pktin->type == SSH2_MSG_GLOBAL_REQUEST) {
- char *type;
- int typelen, want_reply;
-
- ssh_pkt_getstring(pktin, &type, &typelen);
- want_reply = ssh2_pkt_getbool(pktin);
-
- /*
- * We currently don't support any global requests
- * at all, so we either ignore the request or
- * respond with REQUEST_FAILURE, depending on
- * want_reply.
- */
- if (want_reply) {
- s->pktout = ssh2_pkt_init(SSH2_MSG_REQUEST_FAILURE);
- ssh2_pkt_send(ssh, s->pktout);
- }
- } else if (pktin->type == SSH2_MSG_CHANNEL_OPEN) {
- char *type;
- int typelen;
- char *peeraddr;
- int peeraddrlen;
- int peerport;
- char *error = NULL;
- struct ssh_channel *c;
- unsigned remid, winsize, pktsize;
- ssh_pkt_getstring(pktin, &type, &typelen);
- c = snew(struct ssh_channel);
- c->ssh = ssh;
-
- remid = ssh_pkt_getuint32(pktin);
- winsize = ssh_pkt_getuint32(pktin);
- pktsize = ssh_pkt_getuint32(pktin);
-
- if (typelen == 3 && !memcmp(type, "x11", 3)) {
- char *addrstr;
-
- ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
- addrstr = snewn(peeraddrlen+1, char);
- memcpy(addrstr, peeraddr, peeraddrlen);
- addrstr[peeraddrlen] = '\0';
- peerport = ssh_pkt_getuint32(pktin);
-
- logeventf(ssh, "Received X11 connect request from %s:%d",
- addrstr, peerport);
-
- if (!ssh->X11_fwd_enabled)
- error = "X11 forwarding is not enabled";
- else if (x11_init(&c->u.x11.s, ssh->cfg.x11_display, c,
- ssh->x11auth, addrstr, peerport,
- &ssh->cfg) != NULL) {
- error = "Unable to open an X11 connection";
- } else {
- logevent("Opening X11 forward connection succeeded");
- c->type = CHAN_X11;
- }