- if (ispkt) {
- if (ssh->pktin.type == SSH2_MSG_CHANNEL_DATA ||
- ssh->pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
- char *data;
- int length;
- unsigned i = ssh2_pkt_getuint32(ssh);
- struct ssh_channel *c;
- c = find234(ssh->channels, &i, ssh_channelfind);
- if (!c)
- continue; /* nonexistent channel */
- if (ssh->pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
- ssh2_pkt_getuint32(ssh) != SSH2_EXTENDED_DATA_STDERR)
- continue; /* extended but not stderr */
- ssh2_pkt_getstring(ssh, &data, &length);
- if (data) {
- int bufsize;
- c->v.v2.locwindow -= length;
- switch (c->type) {
- case CHAN_MAINSESSION:
- bufsize =
- from_backend(ssh->frontend, ssh->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, *sentreply;
- int replylen;
- agent_query(c->u.a.message,
- c->u.a.totallen, &reply,
- &replylen);
- if (reply)
- sentreply = reply;
- else {
- /* Fake SSH_AGENT_FAILURE. */
- sentreply = "\0\0\0\1\5";
- replylen = 5;
- }
- ssh2_add_channel_data(c, sentreply, replylen);
- s->try_send = TRUE;
- if (reply)
- sfree(reply);
- 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 (ssh->pktin.type == SSH2_MSG_CHANNEL_EOF) {
- unsigned i = ssh2_pkt_getuint32(ssh);
- 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 (ssh->pktin.type == SSH2_MSG_CHANNEL_CLOSE) {
- unsigned i = ssh2_pkt_getuint32(ssh);
- 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));
- }
- /* Do pre-close processing on the channel. */
- switch (c->type) {
- case CHAN_MAINSESSION:
- break; /* nothing to see here, move along */
- 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) {
- ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(ssh, c->remoteid);
- ssh2_pkt_send(ssh);
- }
- del234(ssh->channels, c);
- bufchain_clear(&c->v.v2.outbuffer);
- sfree(c);
-
- /*
- * See if that was the last channel left open.
- */
- if (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.
- */
- ssh2_pkt_init(ssh, SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(ssh, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(ssh, "All open channels closed");
- ssh2_pkt_addstring(ssh, "en"); /* language tag */
- ssh2_pkt_send(ssh);
-#endif
- ssh_closing((Plug)ssh, NULL, 0, 0);
- crReturnV;
- }
- continue; /* remote sends close; ignore (FIXME) */
- } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
- unsigned i = ssh2_pkt_getuint32(ssh);
- struct ssh_channel *c;
- c = find234(ssh->channels, &i, ssh_channelfind);
- if (!c || c->closes)
- continue; /* nonexistent or closing channel */
- c->v.v2.remwindow += ssh2_pkt_getuint32(ssh);
- s->try_send = TRUE;
- } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
- unsigned i = ssh2_pkt_getuint32(ssh);
- 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 = ssh2_pkt_getuint32(ssh);
- c->type = CHAN_SOCKDATA;
- c->v.v2.remwindow = ssh2_pkt_getuint32(ssh);
- c->v.v2.remmaxpkt = ssh2_pkt_getuint32(ssh);
- 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.
- */
- ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_CLOSE);
- ssh2_pkt_adduint32(ssh, c->remoteid);
- ssh2_pkt_send(ssh);
- }
- } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
- unsigned i = ssh2_pkt_getuint32(ssh);
- 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 */
-
- logevent("Forwarded connection refused by server");
-
- pfd_close(c->u.pfd.s);
-
- del234(ssh->channels, c);
- sfree(c);
- } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_REQUEST) {
- unsigned localid;
- char *type;
- int typelen, want_reply;
- struct ssh_channel *c;
-
- localid = ssh2_pkt_getuint32(ssh);
- ssh2_pkt_getstring(ssh, &type, &typelen);
- want_reply = ssh2_pkt_getbool(ssh);
-
- /*
- * 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);
- ssh2_pkt_init(ssh, SSH2_MSG_DISCONNECT);
- ssh2_pkt_adduint32(ssh, SSH2_DISCONNECT_BY_APPLICATION);
- ssh2_pkt_addstring(ssh, buf);
- ssh2_pkt_addstring(ssh, "en"); /* language tag */
- ssh2_pkt_send(ssh);
- connection_fatal(ssh->frontend, "%s", buf);
- ssh_closing((Plug)ssh, NULL, 0, 0);
- crReturnV;
- }
-
- /*
- * Having got the channel number, we now look at
- * the request type string to see if it's something
- * we recognise.
- */
- if (typelen == 11 && !memcmp(type, "exit-status", 11) &&
- c == ssh->mainchan) {
- /* We recognise "exit-status" on the primary channel. */
- char buf[100];
- ssh->exitcode = ssh2_pkt_getuint32(ssh);
- sprintf(buf, "Server sent command exit status %d",
- ssh->exitcode);
- logevent(buf);
- if (want_reply) {
- ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_SUCCESS);
- ssh2_pkt_adduint32(ssh, c->remoteid);
- ssh2_pkt_send(ssh);
- }
- } 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.
- */
- if (want_reply) {
- ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_FAILURE);
- ssh2_pkt_adduint32(ssh, c->remoteid);
- ssh2_pkt_send(ssh);
- }
- }
- } else if (ssh->pktin.type == SSH2_MSG_GLOBAL_REQUEST) {
- char *type;
- int typelen, want_reply;
-
- ssh2_pkt_getstring(ssh, &type, &typelen);
- want_reply = ssh2_pkt_getbool(ssh);
-
- /*
- * 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) {
- ssh2_pkt_init(ssh, SSH2_MSG_REQUEST_FAILURE);
- ssh2_pkt_send(ssh);
- }
- } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_OPEN) {
- char *type;
- int typelen;
- char *peeraddr;
- int peeraddrlen;
- int port;
- char *error = NULL;
- struct ssh_channel *c;
- unsigned remid, winsize, pktsize;
- ssh2_pkt_getstring(ssh, &type, &typelen);
- c = snew(struct ssh_channel);
- c->ssh = ssh;
-
- remid = ssh2_pkt_getuint32(ssh);
- winsize = ssh2_pkt_getuint32(ssh);
- pktsize = ssh2_pkt_getuint32(ssh);
- ssh2_pkt_getstring(ssh, &peeraddr, &peeraddrlen);
- port = ssh2_pkt_getuint32(ssh);
-
- if (typelen == 3 && !memcmp(type, "x11", 3)) {
- char *addrstr = snewn(peeraddrlen+1, char);
- memcpy(addrstr, peeraddr, peeraddrlen);
- peeraddr[peeraddrlen] = '\0';
-
- 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, port,
- &ssh->cfg) != NULL) {
- error = "Unable to open an X11 connection";
- } else {
- c->type = CHAN_X11;
- }