]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - ssh.c
Move call to ssh2_channnel_check_close().
[PuTTY.git] / ssh.c
diff --git a/ssh.c b/ssh.c
index 4fbc01c023be435673f1fb58d34e9bc4f7225e09..48116ccdfe58912a21ed172ada59b81a0394b76d 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -89,7 +89,7 @@ static const char *const ssh2_disconnect_reasons[] = {
  * This list is derived from RFC 4254 and
  * SSH-1 RFC-1.2.31.
  */
-static const struct {
+static const struct ssh_ttymode {
     const char* const mode;
     int opcode;
     enum { TTY_OP_CHAR, TTY_OP_BOOL } type;
@@ -125,6 +125,7 @@ static const struct {
     { "IXANY",      39, TTY_OP_BOOL },
     { "IXOFF",      40, TTY_OP_BOOL },
     { "IMAXBEL",     41, TTY_OP_BOOL },
+    { "IUTF8",       42, TTY_OP_BOOL },
     { "ISIG",       50, TTY_OP_BOOL },
     { "ICANON",             51, TTY_OP_BOOL },
     { "XCASE",      52, TTY_OP_BOOL },
@@ -363,8 +364,11 @@ static int do_ssh1_login(Ssh ssh, const unsigned char *in, int inlen,
                         struct Packet *pktin);
 static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
                             struct Packet *pktin);
+static void ssh_channel_init(struct ssh_channel *c);
+static struct ssh_channel *ssh_channel_msg(Ssh ssh, struct Packet *pktin);
 static void ssh2_channel_check_close(struct ssh_channel *c);
 static void ssh_channel_destroy(struct ssh_channel *c);
+static void ssh_channel_unthrottle(struct ssh_channel *c, int bufsize);
 static void ssh2_msg_something_unimplemented(Ssh ssh, struct Packet *pktin);
 
 /*
@@ -460,7 +464,6 @@ enum {                                     /* channel types */
     CHAN_X11,
     CHAN_AGENT,
     CHAN_SOCKDATA,
-    CHAN_SOCKDATA_DORMANT,            /* one the remote hasn't confirmed */
     /*
      * CHAN_SHARING indicates a channel which is tracked here on
      * behalf of a connection-sharing downstream. We do almost nothing
@@ -982,6 +985,14 @@ struct ssh_tag {
     int cross_certifying;
 };
 
+static const char *ssh_pkt_type(Ssh ssh, int type)
+{
+    if (ssh->version == 1)
+       return ssh1_pkt_type(type);
+    else
+       return ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, type);
+}
+
 #define logevent(s) logevent(ssh->frontend, s)
 
 /* logevent, only printf-formatted. */
@@ -1009,27 +1020,37 @@ static void bomb_out(Ssh ssh, char *text)
 
 /* Helper function for common bits of parsing ttymodes. */
 static void parse_ttymodes(Ssh ssh,
-                          void (*do_mode)(void *data, char *mode, char *val),
+                           void (*do_mode)(void *data,
+                                           const struct ssh_ttymode *mode,
+                                           char *val),
                           void *data)
 {
-    char *key, *val;
+    int i;
+    const struct ssh_ttymode *mode;
+    char *val;
+    char default_val[2];
+
+    strcpy(default_val, "A");
+
+    for (i = 0; i < lenof(ssh_ttymodes); i++) {
+        mode = ssh_ttymodes + i;
+        val = conf_get_str_str_opt(ssh->conf, CONF_ttymodes, mode->mode);
+        if (!val)
+            val = default_val;
 
-    for (val = conf_get_str_strs(ssh->conf, CONF_ttymodes, NULL, &key);
-        val != NULL;
-        val = conf_get_str_strs(ssh->conf, CONF_ttymodes, key, &key)) {
        /*
         * val[0] is either 'V', indicating that an explicit value
         * follows it, or 'A' indicating that we should pass the
         * value through from the local environment via get_ttymode.
         */
        if (val[0] == 'A') {
-           val = get_ttymode(ssh->frontend, key);
+           val = get_ttymode(ssh->frontend, mode->mode);
            if (val) {
-               do_mode(data, key, val);
+               do_mode(data, mode, val);
                sfree(val);
            }
        } else
-           do_mode(data, key, val + 1);               /* skip the 'V' */
+            do_mode(data, mode, val + 1);              /* skip the 'V' */
     }
 }
 
@@ -2521,13 +2542,15 @@ static void ssh_pkt_defersend(Ssh ssh)
     if (backlog > SSH_MAX_BACKLOG)
        ssh_throttle_all(ssh, 1, backlog);
 
-    ssh->outgoing_data_size += ssh->deferred_data_size;
-    if (!ssh->kex_in_progress &&
-        !ssh->bare_connection &&
-       ssh->max_data_size != 0 &&
-       ssh->outgoing_data_size > ssh->max_data_size)
-       do_ssh2_transport(ssh, "too much data sent", -1, NULL);
-    ssh->deferred_data_size = 0;
+    if (ssh->version == 2) {
+       ssh->outgoing_data_size += ssh->deferred_data_size;
+       ssh->deferred_data_size = 0;
+       if (!ssh->kex_in_progress &&
+           !ssh->bare_connection &&
+           ssh->max_data_size != 0 &&
+           ssh->outgoing_data_size > ssh->max_data_size)
+           do_ssh2_transport(ssh, "too much data sent", -1, NULL);
+    }
 }
 
 /*
@@ -3445,7 +3468,6 @@ static int ssh_do_close(Ssh ssh, int notify_exit)
                x11_close(c->u.x11.xconn);
                break;
              case CHAN_SOCKDATA:
-             case CHAN_SOCKDATA_DORMANT:
                pfd_close(c->u.pfd.pf);
                break;
            }
@@ -4990,7 +5012,6 @@ void sshfwd_unclean_close(struct ssh_channel *c, const char *err)
                   "error: %s", err);
         break;
       case CHAN_SOCKDATA:
-      case CHAN_SOCKDATA_DORMANT:
         pfd_close(c->u.pfd.pf);
         logeventf(ssh, "Forwarded port closed due to local error: %s", err);
         break;
@@ -5014,21 +5035,11 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len)
 void sshfwd_unthrottle(struct ssh_channel *c, int bufsize)
 {
     Ssh ssh = c->ssh;
-    int buflimit;
 
     if (ssh->state == SSH_STATE_CLOSED)
        return;
 
-    if (ssh->version == 1) {
-       buflimit = SSH1_BUFFER_LIMIT;
-    } else {
-       buflimit = c->v.v2.locmaxwin;
-       ssh2_set_window(c, bufsize < buflimit ? buflimit - bufsize : 0);
-    }
-    if (c->throttling_conn && bufsize <= buflimit) {
-       c->throttling_conn = 0;
-       ssh_throttle_conn(ssh, -1);
-    }
+    ssh_channel_unthrottle(c, bufsize);
 }
 
 static void ssh_queueing_handler(Ssh ssh, struct Packet *pktin)
@@ -5511,15 +5522,11 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
        c = snew(struct ssh_channel);
        c->ssh = ssh;
 
+       ssh_channel_init(c);
        c->u.x11.xconn = x11_init(ssh->x11authtree, c, NULL, -1);
         c->remoteid = remoteid;
         c->halfopen = FALSE;
-        c->localid = alloc_channel_id(ssh);
-        c->closes = 0;
-        c->pending_eof = FALSE;
-        c->throttling_conn = 0;
         c->type = CHAN_X11;    /* identify channel type */
-        add234(ssh->channels, c);
         send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
                     PKT_INT, c->remoteid, PKT_INT,
                     c->localid, PKT_END);
@@ -5541,17 +5548,13 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin)
     } else {
        c = snew(struct ssh_channel);
        c->ssh = ssh;
+       ssh_channel_init(c);
        c->remoteid = remoteid;
        c->halfopen = FALSE;
-       c->localid = alloc_channel_id(ssh);
-       c->closes = 0;
-       c->pending_eof = FALSE;
-       c->throttling_conn = 0;
        c->type = CHAN_AGENT;   /* identify channel type */
        c->u.a.lensofar = 0;
        c->u.a.message = NULL;
        c->u.a.outstanding_requests = 0;
-       add234(ssh->channels, c);
        send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
                    PKT_INT, c->remoteid, PKT_INT, c->localid,
                    PKT_END);
@@ -5596,14 +5599,10 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
            send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
                        PKT_INT, remoteid, PKT_END);
        } else {
+           ssh_channel_init(c);
            c->remoteid = remoteid;
            c->halfopen = FALSE;
-           c->localid = alloc_channel_id(ssh);
-           c->closes = 0;
-           c->pending_eof = FALSE;
-           c->throttling_conn = 0;
            c->type = CHAN_SOCKDATA;    /* identify channel type */
-           add234(ssh->channels, c);
            send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
                        PKT_INT, c->remoteid, PKT_INT,
                        c->localid, PKT_END);
@@ -5616,15 +5615,12 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
 
 static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
 {
-    unsigned int remoteid = ssh_pkt_getuint32(pktin);
-    unsigned int localid = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
 
-    c = find234(ssh->channels, &remoteid, ssh_channelfind);
-    if (c && c->type == CHAN_SOCKDATA_DORMANT) {
-       c->remoteid = localid;
+    c = ssh_channel_msg(ssh, pktin);
+    if (c && c->type == CHAN_SOCKDATA) {
+       c->remoteid = ssh_pkt_getuint32(pktin);
        c->halfopen = FALSE;
-       c->type = CHAN_SOCKDATA;
        c->throttling_conn = 0;
        pfd_confirm(c->u.pfd.pf);
     }
@@ -5642,11 +5638,10 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
 
 static void ssh1_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
 {
-    unsigned int remoteid = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
 
-    c = find234(ssh->channels, &remoteid, ssh_channelfind);
-    if (c && c->type == CHAN_SOCKDATA_DORMANT) {
+    c = ssh_channel_msg(ssh, pktin);
+    if (c && c->type == CHAN_SOCKDATA) {
        logevent("Forwarded connection refused by server");
        pfd_close(c->u.pfd.pf);
        del234(ssh->channels, c);
@@ -5657,10 +5652,10 @@ static void ssh1_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
 static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin)
 {
     /* Remote side closes a channel. */
-    unsigned i = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
-    c = find234(ssh->channels, &i, ssh_channelfind);
-    if (c && !c->halfopen) {
+
+    c = ssh_channel_msg(ssh, pktin);
+    if (c) {
 
         if (pktin->type == SSH1_MSG_CHANNEL_CLOSE &&
             !(c->closes & CLOSES_RCVD_EOF)) {
@@ -5674,16 +5669,12 @@ static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin)
 
             switch (c->type) {
               case CHAN_X11:
-                if (c->u.x11.xconn)
-                    x11_send_eof(c->u.x11.xconn);
-                else
-                    send_close = TRUE;
+                assert(c->u.x11.xconn != NULL);
+               x11_send_eof(c->u.x11.xconn);
                break;
               case CHAN_SOCKDATA:
-                if (c->u.pfd.pf)
-                    pfd_send_eof(c->u.pfd.pf);
-                else
-                    send_close = TRUE;
+               assert(c->u.pfd.pf != NULL);
+               pfd_send_eof(c->u.pfd.pf);
                break;
               case CHAN_AGENT:
                 send_close = TRUE;
@@ -5701,8 +5692,9 @@ static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin)
             !(c->closes & CLOSES_RCVD_CLOSE)) {
 
             if (!(c->closes & CLOSES_SENT_EOF)) {
-                bombout(("Received CHANNEL_CLOSE_CONFIRMATION for channel %d"
-                         " for which we never sent CHANNEL_CLOSE\n", i));
+                bombout(("Received CHANNEL_CLOSE_CONFIRMATION for channel %u"
+                         " for which we never sent CHANNEL_CLOSE\n",
+                        c->localid));
             }
 
             c->closes |= CLOSES_RCVD_CLOSE;
@@ -5717,11 +5709,6 @@ static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin)
 
        if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes))
             ssh_channel_destroy(c);
-    } else {
-       bombout(("Received CHANNEL_CLOSE%s for %s channel %d\n",
-                pktin->type == SSH1_MSG_CHANNEL_CLOSE ? "" :
-                "_CONFIRMATION", c ? "half-open" : "nonexistent",
-                i));
     }
 }
 
@@ -5767,30 +5754,34 @@ static int ssh_agent_channel_data(struct ssh_channel *c, char *data,
     return 0;   /* agent channels never back up */
 }
 
+static int ssh_channel_data(struct ssh_channel *c, int is_stderr,
+                           char *data,  int length)
+{
+    switch (c->type) {
+      case CHAN_MAINSESSION:
+       return from_backend(c->ssh->frontend, is_stderr, data, length);
+      case CHAN_X11:
+       return x11_send(c->u.x11.xconn, data, length);
+      case CHAN_SOCKDATA:
+       return pfd_send(c->u.pfd.pf, data, length);
+      case CHAN_AGENT:
+       return ssh_agent_channel_data(c, data, length);
+    }
+    return 0;
+}
+
 static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
 {
     /* Data sent down one of our channels. */
-    int i = ssh_pkt_getuint32(pktin);
     char *p;
     int len;
     struct ssh_channel *c;
 
+    c = ssh_channel_msg(ssh, pktin);
     ssh_pkt_getstring(pktin, &p, &len);
 
-    c = find234(ssh->channels, &i, ssh_channelfind);
     if (c) {
-       int bufsize = 0;
-       switch (c->type) {
-         case CHAN_X11:
-           bufsize = x11_send(c->u.x11.xconn, p, len);
-           break;
-         case CHAN_SOCKDATA:
-           bufsize = pfd_send(c->u.pfd.pf, p, len);
-           break;
-         case CHAN_AGENT:
-           bufsize = ssh_agent_channel_data(c, p, len);
-           break;
-       }
+       int bufsize = ssh_channel_data(c, FALSE, p, len);
        if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) {
            c->throttling_conn = 1;
            ssh_throttle_conn(ssh, +1);
@@ -5814,14 +5805,13 @@ static void ssh1_smsg_exit_status(Ssh ssh, struct Packet *pktin)
 }
 
 /* Helper function to deal with sending tty modes for REQUEST_PTY */
-static void ssh1_send_ttymode(void *data, char *mode, char *val)
+static void ssh1_send_ttymode(void *data,
+                              const struct ssh_ttymode *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) {
+
+    switch (mode->type) {
       case TTY_OP_CHAR:
        arg = ssh_tty_parse_specchar(val);
        break;
@@ -5829,7 +5819,7 @@ static void ssh1_send_ttymode(void *data, char *mode, char *val)
        arg = ssh_tty_parse_boolean(val);
        break;
     }
-    ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode);
+    ssh2_pkt_addbyte(pktout, mode->opcode);
     ssh2_pkt_addbyte(pktout, arg);
 }
 
@@ -6332,6 +6322,7 @@ static void do_ssh2_transport(Ssh ssh, const void *vin, int inlen,
     crState(do_ssh2_transport_state);
 
     assert(!ssh->bare_connection);
+    assert(ssh->version == 2);
 
     crBeginState;
 
@@ -7784,20 +7775,23 @@ static int ssh_is_simple(Ssh ssh)
 }
 
 /*
- * Set up most of a new ssh_channel for SSH-2.
+ * Set up most of a new ssh_channel.
  */
-static void ssh2_channel_init(struct ssh_channel *c)
+static void ssh_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 =
-       ssh_is_simple(ssh) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
-    c->v.v2.chanreq_head = NULL;
-    c->v.v2.throttle_state = UNTHROTTLED;
-    bufchain_init(&c->v.v2.outbuffer);
+    if (ssh->version == 2) {
+       c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
+           ssh_is_simple(ssh) ? OUR_V2_BIGWIN : OUR_V2_WINSIZE;
+       c->v.v2.chanreq_head = NULL;
+       c->v.v2.throttle_state = UNTHROTTLED;
+       bufchain_init(&c->v.v2.outbuffer);
+    }
+    add234(ssh->channels, c);
 }
 
 /*
@@ -7867,6 +7861,27 @@ static struct Packet *ssh2_chanreq_init(struct ssh_channel *c,
     return pktout;
 }
 
+static void ssh_channel_unthrottle(struct ssh_channel *c, int bufsize)
+{
+    Ssh ssh = c->ssh;
+    int buflimit;
+
+    if (ssh->version == 1) {
+       buflimit = SSH1_BUFFER_LIMIT;
+    } else {
+       if (ssh_is_simple(ssh))
+           buflimit = 0;
+       else
+           buflimit = c->v.v2.locmaxwin;
+       if (bufsize < buflimit)
+           ssh2_set_window(c, buflimit - bufsize);
+    }
+    if (c->throttling_conn && bufsize <= buflimit) {
+       c->throttling_conn = 0;
+       ssh_throttle_conn(ssh, -1);
+    }
+}
+
 /*
  * Potentially enlarge the window on an SSH-2 channel.
  */
@@ -7948,25 +7963,38 @@ static void ssh2_set_window(struct ssh_channel *c, int 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.
+ * If the channel is shared, pass the message on to downstream and
+ * also return NULL (meaning the caller should ignore this message).
  */
-static struct ssh_channel *ssh2_channel_msg(Ssh ssh, struct Packet *pktin)
+static struct ssh_channel *ssh_channel_msg(Ssh ssh, struct Packet *pktin)
 {
     unsigned localid = ssh_pkt_getuint32(pktin);
     struct ssh_channel *c;
+    int halfopen_ok;
 
+    /* Is this message OK on a half-open connection? */
+    if (ssh->version == 1)
+       halfopen_ok = (pktin->type == SSH1_MSG_CHANNEL_OPEN_CONFIRMATION ||
+                      pktin->type == SSH1_MSG_CHANNEL_OPEN_FAILURE);
+    else
+       halfopen_ok = (pktin->type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION ||
+                      pktin->type == SSH2_MSG_CHANNEL_OPEN_FAILURE);
     c = find234(ssh->channels, &localid, ssh_channelfind);
-    if (!c ||
-       (c->type != CHAN_SHARING && c->halfopen &&
-         pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION &&
-        pktin->type != SSH2_MSG_CHANNEL_OPEN_FAILURE)) {
+    if (!c || (c->type != CHAN_SHARING && (c->halfopen != halfopen_ok))) {
        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_pkt_type(ssh, pktin->type),
+                             !c ? "nonexistent" :
+                             c->halfopen ? "half-open" : "open",
+                             localid);
        ssh_disconnect(ssh, NULL, buf, SSH2_DISCONNECT_PROTOCOL_ERROR, FALSE);
        sfree(buf);
        return NULL;
     }
+    if (c->type == CHAN_SHARING) {
+        share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
+                                  pktin->body, pktin->length);
+        return NULL;
+    }
     return c;
 }
 
@@ -7996,15 +8024,10 @@ static void ssh2_handle_winadj_response(struct ssh_channel *c,
 
 static void ssh2_msg_channel_response(Ssh ssh, struct Packet *pktin)
 {
-    struct ssh_channel *c = ssh2_channel_msg(ssh, pktin);
+    struct ssh_channel *c = ssh_channel_msg(ssh, pktin);
     struct outstanding_channel_request *ocr;
 
     if (!c) return;
-    if (c->type == CHAN_SHARING) {
-        share_got_pkt_from_server(c->u.sharing.ctx, pktin->type,
-                                  pktin->body, pktin->length);
-        return;
-    }
     ocr = c->v.v2.chanreq_head;
     if (!ocr) {
        ssh2_msg_unexpected(ssh, pktin);
@@ -8024,14 +8047,9 @@ static void ssh2_msg_channel_response(Ssh ssh, struct Packet *pktin)
 static void ssh2_msg_channel_window_adjust(Ssh ssh, struct Packet *pktin)
 {
     struct ssh_channel *c;
-    c = ssh2_channel_msg(ssh, pktin);
+    c = ssh_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;
-    }
     if (!(c->closes & CLOSES_SENT_EOF)) {
        c->v.v2.remwindow += ssh_pkt_getuint32(pktin);
        ssh2_try_send_and_unthrottle(ssh, c);
@@ -8042,40 +8060,22 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
 {
     char *data;
     int length;
+    unsigned ext_type = 0; /* 0 means not extended */
     struct ssh_channel *c;
-    c = ssh2_channel_msg(ssh, pktin);
+    c = ssh_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;
-    }
-    if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
-       ssh_pkt_getuint32(pktin) != SSH2_EXTENDED_DATA_STDERR)
-       return;                        /* extended but not stderr */
+    if (pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
+       ext_type = ssh_pkt_getuint32(pktin);
     ssh_pkt_getstring(pktin, &data, &length);
     if (data) {
-       int bufsize = 0;
+       int bufsize;
        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.xconn, data, length);
-           break;
-         case CHAN_SOCKDATA:
-           bufsize = pfd_send(c->u.pfd.pf, data, length);
-           break;
-         case CHAN_AGENT:
-           bufsize = ssh_agent_channel_data(c, data, length);
-           break;
-       }
+       if (ext_type != 0 && ext_type != SSH2_EXTENDED_DATA_STDERR)
+           length = 0; /* Don't do anything with unknown extended data. */
+       bufsize = ssh_channel_data(c, ext_type == SSH2_EXTENDED_DATA_STDERR,
+                                  data, length);
        /*
         * 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
@@ -8091,8 +8091,8 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
         * 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 (bufsize < c->v.v2.locmaxwin)
+           ssh2_set_window(c, c->v.v2.locmaxwin - bufsize);
        /*
         * If we're either buffering way too much data, or if we're
         * buffering anything at all and we're in "simple" mode,
@@ -8166,16 +8166,16 @@ static void ssh_channel_destroy(struct ssh_channel *c)
         update_specials_menu(ssh->frontend);
         break;
       case CHAN_X11:
-        if (c->u.x11.xconn != NULL)
-            x11_close(c->u.x11.xconn);
+        assert(c->u.x11.xconn != NULL);
+       x11_close(c->u.x11.xconn);
         logevent("Forwarded X11 connection terminated");
         break;
       case CHAN_AGENT:
         sfree(c->u.a.message);
         break;
       case CHAN_SOCKDATA:
-        if (c->u.pfd.pf != NULL)
-            pfd_close(c->u.pfd.pf);
+        assert(c->u.pfd.pf != NULL);
+       pfd_close(c->u.pfd.pf);
         logevent("Forwarded port closed");
         break;
     }
@@ -8199,6 +8199,7 @@ static void ssh2_channel_check_close(struct ssh_channel *c)
     Ssh ssh = c->ssh;
     struct Packet *pktout;
 
+    assert(ssh->version == 2);
     if (c->halfopen) {
         /*
          * If we've sent out our own CHANNEL_OPEN but not yet seen
@@ -8266,37 +8267,26 @@ static void ssh2_channel_got_eof(struct ssh_channel *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);
+    c = ssh_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;
-    }
     ssh2_channel_got_eof(c);
+    ssh2_channel_check_close(c);
 }
 
 static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
 {
     struct ssh_channel *c;
 
-    c = ssh2_channel_msg(ssh, pktin);
+    c = ssh_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;
-    }
 
     /*
      * When we receive CLOSE on a channel, we assume it comes with an
@@ -8371,24 +8361,18 @@ static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
 {
     struct ssh_channel *c;
 
-    c = ssh2_channel_msg(ssh, pktin);
+    c = ssh_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 */
+    assert(c->halfopen); /* ssh_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);
+    if (c->type == CHAN_SOCKDATA) {
+       assert(c->u.pfd.pf != NULL);
+       pfd_confirm(c->u.pfd.pf);
     } else if (c->type == CHAN_ZOMBIE) {
         /*
          * This case can occur if a local socket error occurred
@@ -8431,17 +8415,12 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
     int reason_length;
     struct ssh_channel *c;
 
-    c = ssh2_channel_msg(ssh, pktin);
+    c = ssh_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 */
+    assert(c->halfopen); /* ssh_channel_msg will have enforced this */
 
-    if (c->type == CHAN_SOCKDATA_DORMANT) {
+    if (c->type == CHAN_SOCKDATA) {
         reason_code = ssh_pkt_getuint32(pktin);
         if (reason_code >= lenof(reasons))
             reason_code = 0; /* ensure reasons[reason_code] in range */
@@ -8484,14 +8463,9 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
     struct ssh_channel *c;
     struct Packet *pktout;
 
-    c = ssh2_channel_msg(ssh, pktin);
+    c = ssh_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);
 
@@ -8846,14 +8820,13 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
        logeventf(ssh, "Rejected channel open: %s", error);
        sfree(c);
     } else {
-       ssh2_channel_init(c);
+       ssh_channel_init(c);
        c->v.v2.remwindow = winsize;
        c->v.v2.remmaxpkt = pktsize;
         if (our_winsize_override) {
             c->v.v2.locwindow = c->v.v2.locmaxwin = c->v.v2.remlocwin =
                 our_winsize_override;
         }
-       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);
@@ -8918,14 +8891,13 @@ static void ssh2_msg_userauth_banner(Ssh ssh, struct Packet *pktin)
 }
 
 /* Helper function to deal with sending tty modes for "pty-req" */
-static void ssh2_send_ttymode(void *data, char *mode, char *val)
+static void ssh2_send_ttymode(void *data,
+                              const struct ssh_ttymode *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) {
+
+    switch (mode->type) {
       case TTY_OP_CHAR:
        arg = ssh_tty_parse_specchar(val);
        break;
@@ -8933,7 +8905,7 @@ static void ssh2_send_ttymode(void *data, char *mode, char *val)
        arg = ssh_tty_parse_boolean(val);
        break;
     }
-    ssh2_pkt_addbyte(pktout, ssh_ttymodes[i].opcode);
+    ssh2_pkt_addbyte(pktout, mode->opcode);
     ssh2_pkt_adduint32(pktout, arg);
 }
 
@@ -10635,7 +10607,7 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
     } else {
        ssh->mainchan = snew(struct ssh_channel);
        ssh->mainchan->ssh = ssh;
-       ssh2_channel_init(ssh->mainchan);
+       ssh_channel_init(ssh->mainchan);
 
        if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) {
            /*
@@ -10668,7 +10640,6 @@ static void do_ssh2_authconn(Ssh ssh, const unsigned char *in, int inlen,
        ssh->mainchan->type = CHAN_MAINSESSION;
        ssh->mainchan->v.v2.remwindow = ssh_pkt_getuint32(pktin);
        ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
-       add234(ssh->channels, ssh->mainchan);
        update_specials_menu(ssh->frontend);
        logevent("Opened main channel");
     }
@@ -11274,13 +11245,12 @@ static void ssh_free(void *handle)
        while ((c = delpos234(ssh->channels, 0)) != NULL) {
            switch (c->type) {
              case CHAN_X11:
-               if (c->u.x11.xconn != NULL)
-                   x11_close(c->u.x11.xconn);
+               assert(c->u.x11.xconn != NULL);
+               x11_close(c->u.x11.xconn);
                break;
              case CHAN_SOCKDATA:
-             case CHAN_SOCKDATA_DORMANT:
-               if (c->u.pfd.pf != NULL)
-                   pfd_close(c->u.pfd.pf);
+               assert(c->u.pfd.pf != NULL);
+               pfd_close(c->u.pfd.pf);
                break;
            }
            if (ssh->version == 2) {
@@ -11707,11 +11677,10 @@ void *new_sock_channel(void *handle, struct PortForwarding *pf)
     c = snew(struct ssh_channel);
 
     c->ssh = ssh;
-    ssh2_channel_init(c);
+    ssh_channel_init(c);
     c->halfopen = TRUE;
-    c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */
+    c->type = CHAN_SOCKDATA;/* identify channel type */
     c->u.pfd.pf = pf;
-    add234(ssh->channels, c);
     return c;
 }
 
@@ -11721,10 +11690,9 @@ unsigned ssh_alloc_sharing_channel(Ssh ssh, void *sharing_ctx)
     c = snew(struct ssh_channel);
 
     c->ssh = ssh;
-    ssh2_channel_init(c);
+    ssh_channel_init(c);
     c->type = CHAN_SHARING;
     c->u.sharing.ctx = sharing_ctx;
-    add234(ssh->channels, c);
     return c->localid;
 }
 
@@ -11757,7 +11725,6 @@ void ssh_send_packet_from_downstream(Ssh ssh, unsigned id, int type,
 static void ssh_unthrottle(void *handle, int bufsize)
 {
     Ssh ssh = (Ssh) handle;
-    int buflimit;
 
     if (ssh->version == 1) {
        if (ssh->v1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) {
@@ -11765,19 +11732,8 @@ static void ssh_unthrottle(void *handle, int bufsize)
            ssh_throttle_conn(ssh, -1);
        }
     } else {
-       if (ssh->mainchan) {
-           ssh2_set_window(ssh->mainchan,
-                           bufsize < ssh->mainchan->v.v2.locmaxwin ?
-                           ssh->mainchan->v.v2.locmaxwin - bufsize : 0);
-           if (ssh_is_simple(ssh))
-               buflimit = 0;
-           else
-               buflimit = ssh->mainchan->v.v2.locmaxwin;
-           if (ssh->mainchan->throttling_conn && bufsize <= buflimit) {
-               ssh->mainchan->throttling_conn = 0;
-               ssh_throttle_conn(ssh, -1);
-           }
-       }
+       if (ssh->mainchan)
+           ssh_channel_unthrottle(ssh->mainchan, bufsize);
     }
 
     /*