+ int bufsize;
+ if (c->closes & CLOSES_SENT_EOF)
+ return; /* don't send on channels we've EOFed */
+ bufsize = ssh2_try_send(c);
+ if (bufsize == 0) {
+ switch (c->type) {
+ case CHAN_MAINSESSION:
+ /* stdin need not receive an unthrottle
+ * notification since it will be polled */
+ break;
+ case CHAN_X11:
+ x11_unthrottle(c->u.x11.s);
+ break;
+ case CHAN_AGENT:
+ /* agent sockets are request/response and need no
+ * buffer management */
+ break;
+ case CHAN_SOCKDATA:
+ pfd_unthrottle(c->u.pfd.s);
+ break;
+ }
+ }
+}
+
+/*
+ * Set up most of a new ssh_channel for SSH-2.
+ */
+static void ssh2_channel_init(struct ssh_channel *c)
+{
+ Ssh ssh = c->ssh;
+ c->localid = alloc_channel_id(ssh);
+ c->closes = 0;
+ c->pending_eof = FALSE;
+ c->throttling_conn = FALSE;
+ c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
+ conf_get_int(ssh->conf, CONF_ssh_simple) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
+ c->v.v2.winadj_head = c->v.v2.winadj_tail = NULL;
+ c->v.v2.throttle_state = UNTHROTTLED;
+ bufchain_init(&c->v.v2.outbuffer);
+}
+
+/*
+ * Potentially enlarge the window on an SSH-2 channel.
+ */
+static void ssh2_set_window(struct ssh_channel *c, int newwin)
+{
+ Ssh ssh = c->ssh;
+
+ /*
+ * Never send WINDOW_ADJUST for a channel that the remote side has
+ * already sent EOF on; there's no point, since it won't be
+ * sending any more data anyway. Ditto if _we've_ already sent
+ * CLOSE.
+ */
+ if (c->closes & (CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE))
+ return;
+
+ /*
+ * If the remote end has a habit of ignoring maxpkt, limit the
+ * window so that it has no choice (assuming it doesn't ignore the
+ * window as well).
+ */
+ if ((ssh->remote_bugs & BUG_SSH2_MAXPKT) && newwin > OUR_V2_MAXPKT)
+ newwin = OUR_V2_MAXPKT;
+
+ /*
+ * Only send a WINDOW_ADJUST if there's significantly more window
+ * available than the other end thinks there is. This saves us
+ * sending a WINDOW_ADJUST for every character in a shell session.
+ *
+ * "Significant" is arbitrarily defined as half the window size.
+ */
+ if (newwin / 2 >= c->v.v2.locwindow) {
+ struct Packet *pktout;
+ struct winadj *wa;
+
+ /*
+ * In order to keep track of how much window the client
+ * actually has available, we'd like it to acknowledge each
+ * WINDOW_ADJUST. We can't do that directly, so we accompany
+ * it with a CHANNEL_REQUEST that has to be acknowledged.
+ *
+ * This is only necessary if we're opening the window wide.
+ * If we're not, then throughput is being constrained by
+ * something other than the maximum window size anyway.
+ *
+ * We also only send this if the main channel has finished its
+ * initial CHANNEL_REQUESTs and installed the default
+ * CHANNEL_FAILURE handler, so as not to risk giving it
+ * unexpected CHANNEL_FAILUREs.
+ */
+ if (newwin == c->v.v2.locmaxwin &&
+ ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE]) {
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_addstring(pktout, "winadj@putty.projects.tartarus.org");
+ ssh2_pkt_addbool(pktout, TRUE);
+ ssh2_pkt_send(ssh, pktout);
+
+ /*
+ * CHANNEL_FAILURE doesn't come with any indication of
+ * what message caused it, so we have to keep track of the
+ * outstanding CHANNEL_REQUESTs ourselves.
+ */
+ wa = snew(struct winadj);
+ wa->size = newwin - c->v.v2.locwindow;
+ wa->next = NULL;
+ if (!c->v.v2.winadj_head)
+ c->v.v2.winadj_head = wa;
+ else
+ c->v.v2.winadj_tail->next = wa;
+ c->v.v2.winadj_tail = wa;
+ if (c->v.v2.throttle_state != UNTHROTTLED)
+ c->v.v2.throttle_state = UNTHROTTLING;
+ } else {
+ /* Pretend the WINDOW_ADJUST was acked immediately. */
+ c->v.v2.remlocwin = newwin;
+ c->v.v2.throttle_state = THROTTLED;
+ }
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_adduint32(pktout, newwin - c->v.v2.locwindow);
+ ssh2_pkt_send(ssh, pktout);
+ c->v.v2.locwindow = newwin;
+ }
+}
+
+/*
+ * Find the channel associated with a message. If there's no channel,
+ * or it's not properly open, make a noise about it and return NULL.
+ */
+static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin)
+{
+ unsigned localid = ssh_pkt_getuint32(pktin);
+ struct ssh_channel *c;
+
+ c = find234(ssh->channels, &localid, ssh_channelfind);
+ if (!c ||
+ (c->halfopen && pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION &&
+ pktin->type != SSH2_MSG_CHANNEL_OPEN_FAILURE)) {
+ char *buf = dupprintf("Received %s for %s channel %u",
+ ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx,
+ pktin->type),
+ c ? "half-open" : "nonexistent", localid);
+ ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+ sfree(buf);
+ return NULL;
+ }
+ return c;
+}
+
+static int ssh2_handle_winadj_response(struct ssh_channel *c)
+{
+ struct winadj *wa = c->v.v2.winadj_head;
+ if (!wa)
+ return FALSE;
+ c->v.v2.winadj_head = wa->next;
+ c->v.v2.remlocwin += wa->size;
+ sfree(wa);
+ /*
+ * winadj messages are only sent when the window is fully open, so
+ * if we get an ack of one, we know any pending unthrottle is
+ * complete.
+ */
+ if (c->v.v2.throttle_state == UNTHROTTLING)
+ c->v.v2.throttle_state = UNTHROTTLED;
+ /*
+ * We may now initiate channel-closing procedures, if that winadj
+ * was the last thing outstanding before we send CHANNEL_CLOSE.
+ */
+ ssh2_channel_check_close(c);
+ return TRUE;
+}
+
+static void ssh2_msg_channel_success(Ssh ssh, struct Packet *pktin)
+{
+ /*
+ * This should never get called. All channel requests are either
+ * sent with want_reply false, are sent before this handler gets
+ * installed, or are "winadj@putty" requests, which servers should
+ * never respond to with success.
+ *
+ * However, at least one server ("boks_sshd") is known to return
+ * SUCCESS for channel requests it's never heard of, such as
+ * "winadj@putty". Raised with foxt.com as bug 090916-090424, but
+ * for the sake of a quiet life, we handle it just the same as the
+ * expected FAILURE.
+ */
+ struct ssh_channel *c;
+
+ c = ssh2_channel_msg(ssh, pktin);
+ if (!c)
+ return;
+ if (!ssh2_handle_winadj_response(c))
+ ssh_disconnect(ssh, NULL,
+ "Received unsolicited SSH_MSG_CHANNEL_SUCCESS",
+ SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+}
+
+static void ssh2_msg_channel_failure(Ssh ssh, struct Packet *pktin)
+{
+ /*
+ * The only time this should get called is for "winadj@putty"
+ * messages sent above. All other channel requests are either
+ * sent with want_reply false or are sent before this handler gets
+ * installed.
+ */
+ struct ssh_channel *c;
+
+ c = ssh2_channel_msg(ssh, pktin);
+ if (!c)
+ return;
+ if (!ssh2_handle_winadj_response(c))
+ ssh_disconnect(ssh, NULL,
+ "Received unsolicited SSH_MSG_CHANNEL_FAILURE",
+ SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
+}
+
+static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
+{
+ struct ssh_channel *c;
+ c = ssh2_channel_msg(ssh, pktin);
+ if (!c)
+ return;
+ if (!(c->closes & CLOSES_SENT_EOF)) {
+ c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
+ ssh2_try_send_and_unthrottle(ssh, c);
+ }
+}
+
+static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
+{
+ char *data;
+ int length;
+ struct ssh_channel *c;
+ c = ssh2_channel_msg(ssh, pktin);
+ if (!c)
+ return;
+ if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
+ ssh_pkt_getuint32(pktin) != SSH2_EXTENDED_DATA_STDERR)
+ return; /* extended but not stderr */
+ ssh_pkt_getstring(pktin, &data, &length);
+ if (data) {
+ int bufsize = 0;
+ c->v.v2.locwindow -= length;
+ c->v.v2.remlocwin -= 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) {
+ unsigned int l = min(4 - c->u.a.lensofar,
+ (unsigned)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) {
+ unsigned int l =
+ min(c->u.a.totallen - c->u.a.lensofar,
+ (unsigned)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.message = NULL;
+ c->u.a.lensofar = 0;
+ }
+ }
+ bufsize = 0;
+ break;
+ }
+ /*
+ * If it looks like the remote end hit the end of its window,
+ * and we didn't want it to do that, think about using a
+ * larger window.
+ */
+ if (c->v.v2.remlocwin <= 0 && c->v.v2.throttle_state == UNTHROTTLED &&
+ c->v.v2.locmaxwin < 0x40000000)
+ c->v.v2.locmaxwin += OUR_V2_WINSIZE;
+ /*
+ * If we are not buffering too much data,
+ * enlarge the window again at the remote side.
+ * If we are buffering too much, we may still
+ * need to adjust the window if the server's
+ * sent excess data.
+ */
+ ssh2_set_window(c, bufsize < c->v.v2.locmaxwin ?
+ c->v.v2.locmaxwin - bufsize : 0);
+ /*
+ * If we're either buffering way too much data, or if we're
+ * buffering anything at all and we're in "simple" mode,
+ * throttle the whole channel.
+ */
+ if ((bufsize > c->v.v2.locmaxwin ||
+ (conf_get_int(ssh->conf, CONF_ssh_simple) && bufsize > 0)) &&
+ !c->throttling_conn) {
+ c->throttling_conn = 1;
+ ssh_throttle_conn(ssh, +1);
+ }
+ }
+}
+
+static void ssh_channel_destroy(struct ssh_channel *c)
+{
+ Ssh ssh = c->ssh;
+
+ 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);
+ logevent("Forwarded X11 connection terminated");
+ break;
+ case CHAN_AGENT:
+ sfree(c->u.a.message);
+ break;
+ case CHAN_SOCKDATA:
+ if (c->u.pfd.s != NULL)
+ pfd_close(c->u.pfd.s);
+ logevent("Forwarded port closed");
+ break;
+ }
+
+ del234(ssh->channels, c);
+ if (ssh->version == 2)
+ 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->version == 2 &&
+ !conf_get_int(ssh->conf, CONF_ssh_no_shell) &&
+ count234(ssh->channels) == 0) {
+ /*
+ * We used to send SSH_MSG_DISCONNECT here,
+ * because I'd believed that _every_ conforming
+ * SSH-2 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.
+ */
+ ssh_disconnect(ssh, "All channels closed", NULL, 0, TRUE);
+ }
+}
+
+static void ssh2_channel_check_close(struct ssh_channel *c)
+{
+ Ssh ssh = c->ssh;
+ struct Packet *pktout;
+
+ if ((c->closes & (CLOSES_SENT_EOF | CLOSES_RCVD_EOF | CLOSES_SENT_CLOSE))
+ == (CLOSES_SENT_EOF | CLOSES_RCVD_EOF) && !c->v.v2.winadj_head) {
+ /*
+ * We have both sent and received EOF, and we have no
+ * outstanding winadj channel requests, which means the
+ * channel is in final wind-up. But we haven't sent CLOSE, so
+ * let's do so now.
+ */
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ c->closes |= CLOSES_SENT_CLOSE;
+ }
+
+ if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) {
+ /*
+ * We have both sent and received CLOSE, which means we're
+ * completely done with the channel.
+ */
+ ssh_channel_destroy(c);
+ }
+}
+
+static void ssh2_channel_got_eof(struct ssh_channel *c)
+{
+ if (c->closes & CLOSES_RCVD_EOF)
+ return; /* already seen EOF */
+ c->closes |= CLOSES_RCVD_EOF;
+
+ if (c->type == CHAN_X11) {
+ x11_send_eof(c->u.x11.s);
+ } else if (c->type == CHAN_AGENT) {
+ /* Manufacture an outgoing EOF in response to the incoming one. */
+ sshfwd_write_eof(c);
+ } else if (c->type == CHAN_SOCKDATA) {
+ pfd_send_eof(c->u.pfd.s);
+ } else if (c->type == CHAN_MAINSESSION) {
+ Ssh ssh = c->ssh;
+
+ if (!ssh->sent_console_eof &&
+ (from_backend_eof(ssh->frontend) || ssh->got_pty)) {
+ /*
+ * Either from_backend_eof told us that the front end
+ * wants us to close the outgoing side of the connection
+ * as soon as we see EOF from the far end, or else we've
+ * unilaterally decided to do that because we've allocated
+ * a remote pty and hence EOF isn't a particularly
+ * meaningful concept.
+ */
+ sshfwd_write_eof(c);
+ }
+ ssh->sent_console_eof = TRUE;
+ }
+
+ ssh2_channel_check_close(c);
+}
+
+static void ssh2_msg_channel_eof(Ssh ssh, struct Packet *pktin)
+{
+ struct ssh_channel *c;
+
+ c = ssh2_channel_msg(ssh, pktin);
+ if (!c)
+ return;
+ ssh2_channel_got_eof(c);
+}
+
+static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
+{
+ struct ssh_channel *c;
+
+ c = ssh2_channel_msg(ssh, pktin);
+ if (!c)
+ return;
+
+ /*
+ * 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);
+
+ /*
+ * 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.s, 1);
+ break;
+ case CHAN_SOCKDATA:
+ pfd_override_throttle(c->u.pfd.s, 1);
+ break;
+ }
+
+ /*
+ * 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_SOCKDATA_DORMANT)
+ return; /* dunno why they're confirming this */
+ c->remoteid = ssh_pkt_getuint32(pktin);
+ c->halfopen = FALSE;
+ 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->pending_eof)
+ ssh_channel_try_eof(c);
+}
+
+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_SOCKDATA_DORMANT)
+ return; /* 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);
+ logeventf(ssh, "Forwarded connection refused by server: %s [%.*s]",
+ reasons[reason_code], reason_length, reason_string);
+
+ pfd_close(c->u.pfd.s);
+
+ 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;
+ ssh_pkt_getstring(pktin, &type, &typelen);
+ want_reply = ssh2_pkt_getbool(pktin);
+
+ /*
+ * 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 of RFC 4254 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))!= 0) && 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;
+ }
+ }
+ ssh->exitcode = 128; /* means `unknown signal' */
+ if (is_plausible) {
+ if (is_int) {
+ /* Old non-standard OpenSSH. */
+ int signum = ssh_pkt_getuint32(pktin);
+ fmt_sig = dupprintf(" %d", signum);
+ ssh->exitcode = 128 + signum;
+ } else {
+ /* As per RFC 4254. */
+ 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);
+ }
+
+ /*
+ * Really hideous method of translating the
+ * signal description back into a locally
+ * meaningful number.
+ */
+
+ if (0)
+ ;
+#define TRANSLATE_SIGNAL(s) \
+ else if (siglen == lenof(#s)-1 && !memcmp(sig, #s, siglen)) \
+ ssh->exitcode = 128 + SIG ## s
+#ifdef SIGABRT
+ TRANSLATE_SIGNAL(ABRT);
+#endif
+#ifdef SIGALRM
+ TRANSLATE_SIGNAL(ALRM);
+#endif
+#ifdef SIGFPE
+ TRANSLATE_SIGNAL(FPE);
+#endif
+#ifdef SIGHUP
+ TRANSLATE_SIGNAL(HUP);
+#endif
+#ifdef SIGILL
+ TRANSLATE_SIGNAL(ILL);
+#endif
+#ifdef SIGINT
+ TRANSLATE_SIGNAL(INT);
+#endif
+#ifdef SIGKILL
+ TRANSLATE_SIGNAL(KILL);
+#endif
+#ifdef SIGPIPE
+ TRANSLATE_SIGNAL(PIPE);
+#endif
+#ifdef SIGQUIT
+ TRANSLATE_SIGNAL(QUIT);
+#endif
+#ifdef SIGSEGV
+ TRANSLATE_SIGNAL(SEGV);
+#endif
+#ifdef SIGTERM
+ TRANSLATE_SIGNAL(TERM);
+#endif
+#ifdef SIGUSR1
+ TRANSLATE_SIGNAL(USR1);
+#endif
+#ifdef SIGUSR2
+ TRANSLATE_SIGNAL(USR2);
+#endif
+#undef TRANSLATE_SIGNAL
+ else
+ ssh->exitcode = 128;
+ }
+ 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) {
+ pktout = ssh2_pkt_init(reply);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_send(ssh, pktout);
+ }
+}
+
+static void ssh2_msg_global_request(Ssh ssh, struct Packet *pktin)
+{
+ char *type;
+ int typelen, want_reply;
+ struct Packet *pktout;
+
+ 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) {
+ pktout = ssh2_pkt_init(SSH2_MSG_REQUEST_FAILURE);
+ ssh2_pkt_send(ssh, pktout);
+ }
+}
+
+static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
+{
+ char *type;
+ int typelen;
+ char *peeraddr;
+ int peeraddrlen;
+ int peerport;
+ char *error = NULL;
+ struct ssh_channel *c;
+ unsigned remid, winsize, pktsize;
+ struct Packet *pktout;
+
+ 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;
+ const char *x11err;
+
+ 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 ((x11err = x11_init(&c->u.x11.s, ssh->x11disp, c,
+ addrstr, peerport, ssh->conf)) != NULL) {
+ logeventf(ssh, "Local X11 connection failed: %s", x11err);
+ error = "Unable to open an X11 connection";
+ } else {
+ logevent("Opening X11 forward connection succeeded");
+ c->type = CHAN_X11;
+ }
+
+ sfree(addrstr);
+ } else if (typelen == 15 &&
+ !memcmp(type, "forwarded-tcpip", 15)) {
+ struct ssh_rportfwd pf, *realpf;
+ char *dummy;
+ int dummylen;
+ ssh_pkt_getstring(pktin, &dummy, &dummylen);/* skip address */
+ pf.sport = ssh_pkt_getuint32(pktin);
+ ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
+ peerport = ssh_pkt_getuint32(pktin);
+ realpf = find234(ssh->rportfwds, &pf, NULL);
+ logeventf(ssh, "Received remote port %d open request "
+ "from %s:%d", pf.sport, peeraddr, peerport);
+ if (realpf == NULL) {
+ error = "Remote port is not recognised";
+ } else {
+ const char *e = pfd_newconnect(&c->u.pfd.s,
+ realpf->dhost,
+ realpf->dport, c,
+ ssh->conf,
+ realpf->pfrec->addressfamily);
+ logeventf(ssh, "Attempting to forward remote port to "
+ "%s:%d", realpf->dhost, realpf->dport);
+ if (e != NULL) {
+ logeventf(ssh, "Port open failed: %s", e);
+ error = "Port open failed";
+ } else {
+ logevent("Forwarded port opened successfully");
+ c->type = CHAN_SOCKDATA;
+ }
+ }
+ } else if (typelen == 22 &&
+ !memcmp(type, "auth-agent@openssh.com", 22)) {
+ if (!ssh->agentfwd_enabled)
+ error = "Agent forwarding is not enabled";
+ else {
+ c->type = CHAN_AGENT; /* identify channel type */
+ c->u.a.lensofar = 0;
+ }
+ } else {
+ error = "Unsupported channel type requested";
+ }
+
+ c->remoteid = remid;
+ c->halfopen = FALSE;
+ if (error) {
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_FAILURE);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_adduint32(pktout, SSH2_OPEN_CONNECT_FAILED);
+ ssh2_pkt_addstring(pktout, error);
+ ssh2_pkt_addstring(pktout, "en"); /* language tag */
+ ssh2_pkt_send(ssh, pktout);
+ logeventf(ssh, "Rejected channel open: %s", error);
+ sfree(c);
+ } else {
+ ssh2_channel_init(c);
+ c->v.v2.remwindow = winsize;
+ c->v.v2.remmaxpkt = pktsize;
+ add234(ssh->channels, c);
+ pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
+ ssh2_pkt_adduint32(pktout, c->remoteid);
+ ssh2_pkt_adduint32(pktout, c->localid);
+ ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);
+ ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */
+ ssh2_pkt_send(ssh, pktout);
+ }
+}
+
+/*
+ * Buffer banner messages for later display at some convenient point,
+ * if we're going to display them.
+ */
+static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)
+{
+ /* Arbitrary limit to prevent unbounded inflation of buffer */
+ if (conf_get_int(ssh->conf, CONF_ssh_show_banner) &&
+ bufchain_size(&ssh->banner) <= 131072) {
+ char *banner = NULL;
+ int size = 0;
+ ssh_pkt_getstring(pktin, &banner, &size);
+ if (banner)
+ bufchain_add(&ssh->banner, banner, size);
+ }
+}
+
+/* Helper function to deal with sending tty modes for "pty-req" */
+static void ssh2_send_ttymode(void *data, char *mode, char *val)
+{
+ struct Packet *pktout = (struct Packet *)data;
+ int i = 0;
+ unsigned int arg = 0;
+ while (strcmp(mode, ssh_ttymodes[i].mode) != 0) i++;
+ if (i == lenof(ssh_ttymodes)) return;
+ switch (ssh_ttymodes[i].type) {
+ case TTY_OP_CHAR:
+ arg = ssh_tty_parse_specchar(val);
+ break;
+ case TTY_OP_BOOL:
+ arg = ssh_tty_parse_boolean(val);
+ break;
+ }
+ ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode);
+ ssh2_pkt_adduint32(pktout, arg);
+}
+
+/*
+ * Handle the SSH-2 userauth and connection layers.
+ */
+static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
+ struct Packet *pktin)
+{
+ struct do_ssh2_authconn_state {
+ enum {
+ AUTH_TYPE_NONE,
+ AUTH_TYPE_PUBLICKEY,