]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - ssh.c
Get rid of the error-return mechanism from x11_init.
[PuTTY.git] / ssh.c
diff --git a/ssh.c b/ssh.c
index 99410795677c3615cf7cb3bf98b38ad3b8957090..ee7fb44e33f36cf4c23de1dcf9beaae8ab9cf48e 100644 (file)
--- a/ssh.c
+++ b/ssh.c
 #define TRUE 1
 #endif
 
-#define SSH1_MSG_DISCONNECT                       1    /* 0x1 */
-#define SSH1_SMSG_PUBLIC_KEY                      2    /* 0x2 */
-#define SSH1_CMSG_SESSION_KEY                     3    /* 0x3 */
-#define SSH1_CMSG_USER                            4    /* 0x4 */
-#define SSH1_CMSG_AUTH_RSA                        6    /* 0x6 */
-#define SSH1_SMSG_AUTH_RSA_CHALLENGE              7    /* 0x7 */
-#define SSH1_CMSG_AUTH_RSA_RESPONSE               8    /* 0x8 */
-#define SSH1_CMSG_AUTH_PASSWORD                   9    /* 0x9 */
-#define SSH1_CMSG_REQUEST_PTY                     10   /* 0xa */
-#define SSH1_CMSG_WINDOW_SIZE                     11   /* 0xb */
-#define SSH1_CMSG_EXEC_SHELL                      12   /* 0xc */
-#define SSH1_CMSG_EXEC_CMD                        13   /* 0xd */
-#define SSH1_SMSG_SUCCESS                         14   /* 0xe */
-#define SSH1_SMSG_FAILURE                         15   /* 0xf */
-#define SSH1_CMSG_STDIN_DATA                      16   /* 0x10 */
-#define SSH1_SMSG_STDOUT_DATA                     17   /* 0x11 */
-#define SSH1_SMSG_STDERR_DATA                     18   /* 0x12 */
-#define SSH1_CMSG_EOF                             19   /* 0x13 */
-#define SSH1_SMSG_EXIT_STATUS                     20   /* 0x14 */
-#define SSH1_MSG_CHANNEL_OPEN_CONFIRMATION        21   /* 0x15 */
-#define SSH1_MSG_CHANNEL_OPEN_FAILURE             22   /* 0x16 */
-#define SSH1_MSG_CHANNEL_DATA                     23   /* 0x17 */
-#define SSH1_MSG_CHANNEL_CLOSE                    24   /* 0x18 */
-#define SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION       25   /* 0x19 */
-#define SSH1_SMSG_X11_OPEN                        27   /* 0x1b */
-#define SSH1_CMSG_PORT_FORWARD_REQUEST            28   /* 0x1c */
-#define SSH1_MSG_PORT_OPEN                        29   /* 0x1d */
-#define SSH1_CMSG_AGENT_REQUEST_FORWARDING        30   /* 0x1e */
-#define SSH1_SMSG_AGENT_OPEN                      31   /* 0x1f */
-#define SSH1_MSG_IGNORE                           32   /* 0x20 */
-#define SSH1_CMSG_EXIT_CONFIRMATION               33   /* 0x21 */
-#define SSH1_CMSG_X11_REQUEST_FORWARDING          34   /* 0x22 */
-#define SSH1_CMSG_AUTH_RHOSTS_RSA                 35   /* 0x23 */
-#define SSH1_MSG_DEBUG                            36   /* 0x24 */
-#define SSH1_CMSG_REQUEST_COMPRESSION             37   /* 0x25 */
-#define SSH1_CMSG_AUTH_TIS                        39   /* 0x27 */
-#define SSH1_SMSG_AUTH_TIS_CHALLENGE              40   /* 0x28 */
-#define SSH1_CMSG_AUTH_TIS_RESPONSE               41   /* 0x29 */
-#define SSH1_CMSG_AUTH_CCARD                      70   /* 0x46 */
-#define SSH1_SMSG_AUTH_CCARD_CHALLENGE            71   /* 0x47 */
-#define SSH1_CMSG_AUTH_CCARD_RESPONSE             72   /* 0x48 */
-
-#define SSH1_AUTH_RHOSTS                          1    /* 0x1 */
-#define SSH1_AUTH_RSA                             2    /* 0x2 */
-#define SSH1_AUTH_PASSWORD                        3    /* 0x3 */
-#define SSH1_AUTH_RHOSTS_RSA                      4    /* 0x4 */
-#define SSH1_AUTH_TIS                             5    /* 0x5 */
-#define SSH1_AUTH_CCARD                           16   /* 0x10 */
-
-#define SSH1_PROTOFLAG_SCREEN_NUMBER              1    /* 0x1 */
-/* Mask for protoflags we will echo back to server if seen */
-#define SSH1_PROTOFLAGS_SUPPORTED                 0    /* 0x1 */
-
-#define SSH2_MSG_DISCONNECT                       1    /* 0x1 */
-#define SSH2_MSG_IGNORE                           2    /* 0x2 */
-#define SSH2_MSG_UNIMPLEMENTED                    3    /* 0x3 */
-#define SSH2_MSG_DEBUG                            4    /* 0x4 */
-#define SSH2_MSG_SERVICE_REQUEST                  5    /* 0x5 */
-#define SSH2_MSG_SERVICE_ACCEPT                   6    /* 0x6 */
-#define SSH2_MSG_KEXINIT                          20   /* 0x14 */
-#define SSH2_MSG_NEWKEYS                          21   /* 0x15 */
-#define SSH2_MSG_KEXDH_INIT                       30   /* 0x1e */
-#define SSH2_MSG_KEXDH_REPLY                      31   /* 0x1f */
-#define SSH2_MSG_KEX_DH_GEX_REQUEST               30   /* 0x1e */
-#define SSH2_MSG_KEX_DH_GEX_GROUP                 31   /* 0x1f */
-#define SSH2_MSG_KEX_DH_GEX_INIT                  32   /* 0x20 */
-#define SSH2_MSG_KEX_DH_GEX_REPLY                 33   /* 0x21 */
-#define SSH2_MSG_KEXRSA_PUBKEY                    30    /* 0x1e */
-#define SSH2_MSG_KEXRSA_SECRET                    31    /* 0x1f */
-#define SSH2_MSG_KEXRSA_DONE                      32    /* 0x20 */
-#define SSH2_MSG_USERAUTH_REQUEST                 50   /* 0x32 */
-#define SSH2_MSG_USERAUTH_FAILURE                 51   /* 0x33 */
-#define SSH2_MSG_USERAUTH_SUCCESS                 52   /* 0x34 */
-#define SSH2_MSG_USERAUTH_BANNER                  53   /* 0x35 */
-#define SSH2_MSG_USERAUTH_PK_OK                   60   /* 0x3c */
-#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ        60   /* 0x3c */
-#define SSH2_MSG_USERAUTH_INFO_REQUEST            60   /* 0x3c */
-#define SSH2_MSG_USERAUTH_INFO_RESPONSE           61   /* 0x3d */
-#define SSH2_MSG_GLOBAL_REQUEST                   80   /* 0x50 */
-#define SSH2_MSG_REQUEST_SUCCESS                  81   /* 0x51 */
-#define SSH2_MSG_REQUEST_FAILURE                  82   /* 0x52 */
-#define SSH2_MSG_CHANNEL_OPEN                     90   /* 0x5a */
-#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION        91   /* 0x5b */
-#define SSH2_MSG_CHANNEL_OPEN_FAILURE             92   /* 0x5c */
-#define SSH2_MSG_CHANNEL_WINDOW_ADJUST            93   /* 0x5d */
-#define SSH2_MSG_CHANNEL_DATA                     94   /* 0x5e */
-#define SSH2_MSG_CHANNEL_EXTENDED_DATA            95   /* 0x5f */
-#define SSH2_MSG_CHANNEL_EOF                      96   /* 0x60 */
-#define SSH2_MSG_CHANNEL_CLOSE                    97   /* 0x61 */
-#define SSH2_MSG_CHANNEL_REQUEST                  98   /* 0x62 */
-#define SSH2_MSG_CHANNEL_SUCCESS                  99   /* 0x63 */
-#define SSH2_MSG_CHANNEL_FAILURE                  100  /* 0x64 */
-#define SSH2_MSG_USERAUTH_GSSAPI_RESPONSE               60
-#define SSH2_MSG_USERAUTH_GSSAPI_TOKEN                  61
-#define SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE      63
-#define SSH2_MSG_USERAUTH_GSSAPI_ERROR                  64
-#define SSH2_MSG_USERAUTH_GSSAPI_ERRTOK                 65
-#define SSH2_MSG_USERAUTH_GSSAPI_MIC                    66
-
 /*
  * Packet type contexts, so that ssh2_pkt_type can correctly decode
  * the ambiguous type numbers back into the correct type strings.
@@ -141,22 +42,6 @@ typedef enum {
     SSH2_PKTCTX_KBDINTER
 } Pkt_ACtx;
 
-#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT 1  /* 0x1 */
-#define SSH2_DISCONNECT_PROTOCOL_ERROR            2    /* 0x2 */
-#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED       3    /* 0x3 */
-#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED 4   /* 0x4 */
-#define SSH2_DISCONNECT_MAC_ERROR                 5    /* 0x5 */
-#define SSH2_DISCONNECT_COMPRESSION_ERROR         6    /* 0x6 */
-#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE     7    /* 0x7 */
-#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED 8       /* 0x8 */
-#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE   9    /* 0x9 */
-#define SSH2_DISCONNECT_CONNECTION_LOST           10   /* 0xa */
-#define SSH2_DISCONNECT_BY_APPLICATION            11   /* 0xb */
-#define SSH2_DISCONNECT_TOO_MANY_CONNECTIONS      12   /* 0xc */
-#define SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER    13   /* 0xd */
-#define SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE 14      /* 0xe */
-#define SSH2_DISCONNECT_ILLEGAL_USER_NAME         15   /* 0xf */
-
 static const char *const ssh2_disconnect_reasons[] = {
     NULL,
     "host not allowed to connect",
@@ -176,13 +61,6 @@ static const char *const ssh2_disconnect_reasons[] = {
     "illegal user name",
 };
 
-#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED     1    /* 0x1 */
-#define SSH2_OPEN_CONNECT_FAILED                  2    /* 0x2 */
-#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE            3    /* 0x3 */
-#define SSH2_OPEN_RESOURCE_SHORTAGE               4    /* 0x4 */
-
-#define SSH2_EXTENDED_DATA_STDERR                 1    /* 0x1 */
-
 /*
  * Various remote-bug flags.
  */
@@ -405,9 +283,6 @@ static char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
 /* Enumeration values for fields in SSH-1 packets */
 enum {
     PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM,
-    /* These values are for communicating relevant semantics of
-     * fields to the packet logging code. */
-    PKTT_OTHER, PKTT_PASSWORD, PKTT_DATA
 };
 
 /*
@@ -460,14 +335,14 @@ struct Packet;
 static struct Packet *ssh1_pkt_init(int pkt_type);
 static struct Packet *ssh2_pkt_init(int pkt_type);
 static void ssh_pkt_ensure(struct Packet *, int length);
-static void ssh_pkt_adddata(struct Packet *, void *data, int len);
+static void ssh_pkt_adddata(struct Packet *, const void *data, int len);
 static void ssh_pkt_addbyte(struct Packet *, unsigned char value);
 static void ssh2_pkt_addbool(struct Packet *, unsigned char value);
 static void ssh_pkt_adduint32(struct Packet *, unsigned long value);
 static void ssh_pkt_addstring_start(struct Packet *);
-static void ssh_pkt_addstring_str(struct Packet *, char *data);
-static void ssh_pkt_addstring_data(struct Packet *, char *data, int len);
-static void ssh_pkt_addstring(struct Packet *, char *data);
+static void ssh_pkt_addstring_str(struct Packet *, const char *data);
+static void ssh_pkt_addstring_data(struct Packet *, const char *data, int len);
+static void ssh_pkt_addstring(struct Packet *, const char *data);
 static unsigned char *ssh2_mpint_fmt(Bignum b, int *len);
 static void ssh1_pkt_addmp(struct Packet *, Bignum b);
 static void ssh2_pkt_addmp(struct Packet *, Bignum b);
@@ -525,7 +400,7 @@ static void ssh_channel_destroy(struct ssh_channel *c);
 const static struct ssh_signkey *hostkey_algs[] = { &ssh_rsa, &ssh_dss };
 
 const static struct ssh_mac *macs[] = {
-    &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
+    &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5
 };
 const static struct ssh_mac *buggymacs[] = {
     &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5
@@ -588,14 +463,6 @@ struct outstanding_channel_request {
     struct outstanding_channel_request *next;
 };
 
-/*
- * little structure to keep track of outstanding WINDOW_ADJUSTs
- */
-struct winadj {
-    struct winadj *next;
-    unsigned size;
-};
-
 /*
  * 2-3-4 tree storing channels.
  */
@@ -674,12 +541,13 @@ struct ssh_channel {
            unsigned char *message;
            unsigned char msglen[4];
            unsigned lensofar, totallen;
+            int outstanding_requests;
        } a;
        struct ssh_x11_channel {
-           Socket s;
+           struct X11Connection *xconn;
        } x11;
        struct ssh_pfd_channel {
-           Socket s;
+            struct PortForwarding *pf;
        } pfd;
     } u;
 };
@@ -715,12 +583,20 @@ struct ssh_portfwd; /* forward declaration */
 
 struct ssh_rportfwd {
     unsigned sport, dport;
-    char dhost[256];
+    char *shost, *dhost;
     char *sportdesc;
     struct ssh_portfwd *pfrec;
 };
-#define free_rportfwd(pf) ( \
-    ((pf) ? (sfree((pf)->sportdesc)) : (void)0 ), sfree(pf) )
+
+static void free_rportfwd(struct ssh_rportfwd *pf)
+{
+    if (pf) {
+        sfree(pf->sportdesc);
+        sfree(pf->shost);
+        sfree(pf->dhost);
+        sfree(pf);
+    }
+}
 
 /*
  * Separately to the rportfwd tree (which is for looking up port
@@ -737,29 +613,44 @@ struct ssh_portfwd {
     char *sserv, *dserv;
     struct ssh_rportfwd *remote;
     int addressfamily;
-    void *local;
+    struct PortListener *local;
 };
 #define free_portfwd(pf) ( \
     ((pf) ? (sfree((pf)->saddr), sfree((pf)->daddr), \
             sfree((pf)->sserv), sfree((pf)->dserv)) : (void)0 ), sfree(pf) )
 
 struct Packet {
-    long length;           /* length of `data' actually used */
+    long length;           /* length of packet: see below */
     long forcepad;         /* SSH-2: force padding to at least this length */
     int type;              /* only used for incoming packets */
     unsigned long sequence; /* SSH-2 incoming sequence number */
     unsigned char *data;    /* allocated storage */
     unsigned char *body;    /* offset of payload within `data' */
-    long savedpos;         /* temporary index into `data' (for strings) */
+    long savedpos;         /* dual-purpose saved packet position: see below */
     long maxlen;           /* amount of storage allocated for `data' */
     long encrypted_len;            /* for SSH-2 total-size counting */
 
     /*
-     * State associated with packet logging
+     * A note on the 'length' and 'savedpos' fields above.
+     *
+     * Incoming packets are set up so that pkt->length is measured
+     * relative to pkt->body, which itself points to a few bytes after
+     * pkt->data (skipping some uninteresting header fields including
+     * the packet type code). The ssh_pkt_get* functions all expect
+     * this setup, and they also use pkt->savedpos to indicate how far
+     * through the packet being decoded they've got - and that, too,
+     * is an offset from pkt->body rather than pkt->data.
+     *
+     * During construction of an outgoing packet, however, pkt->length
+     * is measured relative to the base pointer pkt->data, and
+     * pkt->body is not really used for anything until the packet is
+     * ready for sending. In this mode, pkt->savedpos is reused as a
+     * temporary variable by the addstring functions, which write out
+     * a string length field and then keep going back and updating it
+     * as more data is appended to the subsequent string data field;
+     * pkt->savedpos stores the offset (again relative to pkt->data)
+     * of the start of the string data field.
      */
-    int logmode;
-    int nblanks;
-    struct logblank_t *blanks;
 };
 
 static void ssh1_protocol(Ssh ssh, void *vin, int inlen,
@@ -779,9 +670,9 @@ static int ssh_do_close(Ssh ssh, int notify_exit);
 static unsigned long ssh_pkt_getuint32(struct Packet *pkt);
 static int ssh2_pkt_getbool(struct Packet *pkt);
 static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length);
-static void ssh2_timer(void *ctx, long now);
-static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
-                            struct Packet *pktin);
+static void ssh2_timer(void *ctx, unsigned long now);
+static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
+                             struct Packet *pktin);
 static void ssh2_msg_unexpected(Ssh ssh, struct Packet *pktin);
 
 struct rdpkt1_state_tag {
@@ -839,6 +730,7 @@ struct ssh_tag {
     void *cs_comp_ctx, *sc_comp_ctx;
     const struct ssh_kex *kex;
     const struct ssh_signkey *hostkey;
+    char *hostkey_str; /* string representation, for easy checking in rekeys */
     unsigned char v2_session_id[SSH2_KEX_MAX_HASH_LEN];
     int v2_session_id_len;
     void *kex_ctx;
@@ -894,6 +786,8 @@ struct ssh_tag {
     Pkt_ACtx pkt_actx;
 
     struct X11Display *x11disp;
+    struct X11FakeAuth *x11auth;
+    tree234 *x11authtree;
 
     int version;
     int conn_throttle_count;
@@ -988,7 +882,7 @@ struct ssh_tag {
     unsigned long incoming_data_size, outgoing_data_size, deferred_data_size;
     unsigned long max_data_size;
     int kex_in_progress;
-    long next_rekey, last_rekey;
+    unsigned long next_rekey, last_rekey;
     char *deferred_rekey_reason;    /* points to STATIC string; don't free */
 
     /*
@@ -1029,25 +923,6 @@ static void bomb_out(Ssh ssh, char *text)
 
 #define bombout(msg) bomb_out(ssh, dupprintf msg)
 
-/* Functions to leave bits out of the SSH packet log file. */
-
-static void dont_log_password(Ssh ssh, struct Packet *pkt, int blanktype)
-{
-    if (conf_get_int(ssh->conf, CONF_logomitpass))
-       pkt->logmode = blanktype;
-}
-
-static void dont_log_data(Ssh ssh, struct Packet *pkt, int blanktype)
-{
-    if (ssh->logomitdata)
-       pkt->logmode = blanktype;
-}
-
-static void end_log_omission(Ssh ssh, struct Packet *pkt)
-{
-    pkt->logmode = PKTLOG_EMIT;
-}
-
 /* Helper function for common bits of parsing ttymodes. */
 static void parse_ttymodes(Ssh ssh,
                           void (*do_mode)(void *data, char *mode, char *val),
@@ -1113,7 +988,9 @@ static int ssh_rportcmp_ssh2(void *av, void *bv)
 {
     struct ssh_rportfwd *a = (struct ssh_rportfwd *) av;
     struct ssh_rportfwd *b = (struct ssh_rportfwd *) bv;
-
+    int i;
+    if ( (i = strcmp(a->shost, b->shost)) != 0)
+       return i < 0 ? -1 : +1;
     if (a->sport > b->sport)
        return +1;
     if (a->sport < b->sport)
@@ -1244,13 +1121,118 @@ static struct Packet *ssh_new_packet(void)
 
     pkt->body = pkt->data = NULL;
     pkt->maxlen = 0;
-    pkt->logmode = PKTLOG_EMIT;
-    pkt->nblanks = 0;
-    pkt->blanks = NULL;
 
     return pkt;
 }
 
+static void ssh1_log_incoming_packet(Ssh ssh, struct Packet *pkt)
+{
+    int nblanks = 0;
+    struct logblank_t blanks[4];
+    char *str;
+    int slen;
+
+    pkt->savedpos = 0;
+
+    if (ssh->logomitdata &&
+        (pkt->type == SSH1_SMSG_STDOUT_DATA ||
+         pkt->type == SSH1_SMSG_STDERR_DATA ||
+         pkt->type == SSH1_MSG_CHANNEL_DATA)) {
+        /* "Session data" packets - omit the data string. */
+        if (pkt->type == SSH1_MSG_CHANNEL_DATA)
+            ssh_pkt_getuint32(pkt);    /* skip channel id */
+        blanks[nblanks].offset = pkt->savedpos + 4;
+        blanks[nblanks].type = PKTLOG_OMIT;
+        ssh_pkt_getstring(pkt, &str, &slen);
+        if (str) {
+            blanks[nblanks].len = slen;
+            nblanks++;
+        }
+    }
+    log_packet(ssh->logctx, PKT_INCOMING, pkt->type,
+               ssh1_pkt_type(pkt->type),
+               pkt->body, pkt->length, nblanks, blanks, NULL);
+}
+
+static void ssh1_log_outgoing_packet(Ssh ssh, struct Packet *pkt)
+{
+    int nblanks = 0;
+    struct logblank_t blanks[4];
+    char *str;
+    int slen;
+
+    /*
+     * For outgoing packets, pkt->length represents the length of the
+     * whole packet starting at pkt->data (including some header), and
+     * pkt->body refers to the point within that where the log-worthy
+     * payload begins. However, incoming packets expect pkt->length to
+     * represent only the payload length (that is, it's measured from
+     * pkt->body not from pkt->data). Temporarily adjust our outgoing
+     * packet to conform to the incoming-packet semantics, so that we
+     * can analyse it with the ssh_pkt_get functions.
+     */
+    pkt->length -= (pkt->body - pkt->data);
+    pkt->savedpos = 0;
+
+    if (ssh->logomitdata &&
+        (pkt->type == SSH1_CMSG_STDIN_DATA ||
+         pkt->type == SSH1_MSG_CHANNEL_DATA)) {
+        /* "Session data" packets - omit the data string. */
+        if (pkt->type == SSH1_MSG_CHANNEL_DATA)
+            ssh_pkt_getuint32(pkt);    /* skip channel id */
+        blanks[nblanks].offset = pkt->savedpos + 4;
+        blanks[nblanks].type = PKTLOG_OMIT;
+        ssh_pkt_getstring(pkt, &str, &slen);
+        if (str) {
+            blanks[nblanks].len = slen;
+            nblanks++;
+        }
+    }
+
+    if ((pkt->type == SSH1_CMSG_AUTH_PASSWORD ||
+         pkt->type == SSH1_CMSG_AUTH_TIS_RESPONSE ||
+         pkt->type == SSH1_CMSG_AUTH_CCARD_RESPONSE) &&
+        conf_get_int(ssh->conf, CONF_logomitpass)) {
+        /* If this is a password or similar packet, blank the password(s). */
+        blanks[nblanks].offset = 0;
+        blanks[nblanks].len = pkt->length;
+        blanks[nblanks].type = PKTLOG_BLANK;
+        nblanks++;
+    } else if (pkt->type == SSH1_CMSG_X11_REQUEST_FORWARDING &&
+               conf_get_int(ssh->conf, CONF_logomitpass)) {
+        /*
+         * If this is an X forwarding request packet, blank the fake
+         * auth data.
+         *
+         * Note that while we blank the X authentication data here, we
+         * don't take any special action to blank the start of an X11
+         * channel, so using MIT-MAGIC-COOKIE-1 and actually opening
+         * an X connection without having session blanking enabled is
+         * likely to leak your cookie into the log.
+         */
+        pkt->savedpos = 0;
+        ssh_pkt_getstring(pkt, &str, &slen);
+        blanks[nblanks].offset = pkt->savedpos;
+        blanks[nblanks].type = PKTLOG_BLANK;
+        ssh_pkt_getstring(pkt, &str, &slen);
+        if (str) {
+            blanks[nblanks].len = pkt->savedpos - blanks[nblanks].offset;
+            nblanks++;
+        }
+    }
+
+    log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[12],
+               ssh1_pkt_type(pkt->data[12]),
+               pkt->body, pkt->length,
+               nblanks, blanks, NULL);
+
+    /*
+     * Undo the above adjustment of pkt->length, to put the packet
+     * back in the state we found it.
+     */
+    pkt->length += (pkt->body - pkt->data);
+}
+
 /*
  * Collect incoming data in the incoming packet buffer.
  * Decipher and verify the packet when it is completely read.
@@ -1324,7 +1306,6 @@ static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
     }
 
     st->pktin->body = st->pktin->data + st->pad + 1;
-    st->pktin->savedpos = 0;
 
     if (ssh->v1_compressing) {
        unsigned char *decompblk;
@@ -1353,37 +1334,165 @@ static struct Packet *ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
     st->pktin->type = st->pktin->body[-1];
 
     /*
-     * Log incoming packet, possibly omitting sensitive fields.
+     * Now pktin->body and pktin->length identify the semantic content
+     * of the packet, excluding the initial type byte.
      */
-    if (ssh->logctx) {
-       int nblanks = 0;
-       struct logblank_t blank;
-       if (ssh->logomitdata) {
-           int do_blank = FALSE, blank_prefix = 0;
-           /* "Session data" packets - omit the data field */
-           if ((st->pktin->type == SSH1_SMSG_STDOUT_DATA) ||
-               (st->pktin->type == SSH1_SMSG_STDERR_DATA)) {
-               do_blank = TRUE; blank_prefix = 4;
-           } else if (st->pktin->type == SSH1_MSG_CHANNEL_DATA) {
-               do_blank = TRUE; blank_prefix = 8;
-           }
-           if (do_blank) {
-               blank.offset = blank_prefix;
-               blank.len = st->pktin->length;
-               blank.type = PKTLOG_OMIT;
-               nblanks = 1;
-           }
-       }
-       log_packet(ssh->logctx,
-                  PKT_INCOMING, st->pktin->type,
-                  ssh1_pkt_type(st->pktin->type),
-                  st->pktin->body, st->pktin->length,
-                  nblanks, &blank, NULL);
-    }
+
+    if (ssh->logctx)
+        ssh1_log_incoming_packet(ssh, st->pktin);
+
+    st->pktin->savedpos = 0;
 
     crFinish(st->pktin);
 }
 
+static void ssh2_log_incoming_packet(Ssh ssh, struct Packet *pkt)
+{
+    int nblanks = 0;
+    struct logblank_t blanks[4];
+    char *str;
+    int slen;
+
+    pkt->savedpos = 0;
+
+    if (ssh->logomitdata &&
+        (pkt->type == SSH2_MSG_CHANNEL_DATA ||
+         pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) {
+        /* "Session data" packets - omit the data string. */
+        ssh_pkt_getuint32(pkt);    /* skip channel id */
+        if (pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
+            ssh_pkt_getuint32(pkt);    /* skip extended data type */
+        blanks[nblanks].offset = pkt->savedpos + 4;
+        blanks[nblanks].type = PKTLOG_OMIT;
+        ssh_pkt_getstring(pkt, &str, &slen);
+        if (str) {
+            blanks[nblanks].len = slen;
+            nblanks++;
+        }
+    }
+
+    log_packet(ssh->logctx, PKT_INCOMING, pkt->type,
+               ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->type),
+               pkt->body, pkt->length, nblanks, blanks, &pkt->sequence);
+}
+
+static void ssh2_log_outgoing_packet(Ssh ssh, struct Packet *pkt)
+{
+    int nblanks = 0;
+    struct logblank_t blanks[4];
+    char *str;
+    int slen;
+
+    /*
+     * For outgoing packets, pkt->length represents the length of the
+     * whole packet starting at pkt->data (including some header), and
+     * pkt->body refers to the point within that where the log-worthy
+     * payload begins. However, incoming packets expect pkt->length to
+     * represent only the payload length (that is, it's measured from
+     * pkt->body not from pkt->data). Temporarily adjust our outgoing
+     * packet to conform to the incoming-packet semantics, so that we
+     * can analyse it with the ssh_pkt_get functions.
+     */
+    pkt->length -= (pkt->body - pkt->data);
+    pkt->savedpos = 0;
+
+    if (ssh->logomitdata &&
+        (pkt->type == SSH2_MSG_CHANNEL_DATA ||
+         pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) {
+        /* "Session data" packets - omit the data string. */
+        ssh_pkt_getuint32(pkt);    /* skip channel id */
+        if (pkt->type == SSH2_MSG_CHANNEL_EXTENDED_DATA)
+            ssh_pkt_getuint32(pkt);    /* skip extended data type */
+        blanks[nblanks].offset = pkt->savedpos + 4;
+        blanks[nblanks].type = PKTLOG_OMIT;
+        ssh_pkt_getstring(pkt, &str, &slen);
+        if (str) {
+            blanks[nblanks].len = slen;
+            nblanks++;
+        }
+    }
+
+    if (pkt->type == SSH2_MSG_USERAUTH_REQUEST &&
+        conf_get_int(ssh->conf, CONF_logomitpass)) {
+        /* If this is a password packet, blank the password(s). */
+        pkt->savedpos = 0;
+        ssh_pkt_getstring(pkt, &str, &slen);
+        ssh_pkt_getstring(pkt, &str, &slen);
+        ssh_pkt_getstring(pkt, &str, &slen);
+        if (slen == 8 && !memcmp(str, "password", 8)) {
+            ssh2_pkt_getbool(pkt);
+            /* Blank the password field. */
+            blanks[nblanks].offset = pkt->savedpos;
+            blanks[nblanks].type = PKTLOG_BLANK;
+            ssh_pkt_getstring(pkt, &str, &slen);
+            if (str) {
+                blanks[nblanks].len = pkt->savedpos - blanks[nblanks].offset;
+                nblanks++;
+                /* If there's another password field beyond it (change of
+                 * password), blank that too. */
+                ssh_pkt_getstring(pkt, &str, &slen);
+                if (str)
+                    blanks[nblanks-1].len =
+                        pkt->savedpos - blanks[nblanks].offset;
+            }
+        }
+    } else if (ssh->pkt_actx == SSH2_PKTCTX_KBDINTER &&
+               pkt->type == SSH2_MSG_USERAUTH_INFO_RESPONSE &&
+               conf_get_int(ssh->conf, CONF_logomitpass)) {
+        /* If this is a keyboard-interactive response packet, blank
+         * the responses. */
+        pkt->savedpos = 0;
+        ssh_pkt_getuint32(pkt);
+        blanks[nblanks].offset = pkt->savedpos;
+        blanks[nblanks].type = PKTLOG_BLANK;
+        while (1) {
+            ssh_pkt_getstring(pkt, &str, &slen);
+            if (!str)
+                break;
+        }
+        blanks[nblanks].len = pkt->savedpos - blanks[nblanks].offset;
+        nblanks++;
+    } else if (pkt->type == SSH2_MSG_CHANNEL_REQUEST &&
+               conf_get_int(ssh->conf, CONF_logomitpass)) {
+        /*
+         * If this is an X forwarding request packet, blank the fake
+         * auth data.
+         *
+         * Note that while we blank the X authentication data here, we
+         * don't take any special action to blank the start of an X11
+         * channel, so using MIT-MAGIC-COOKIE-1 and actually opening
+         * an X connection without having session blanking enabled is
+         * likely to leak your cookie into the log.
+         */
+        pkt->savedpos = 0;
+        ssh_pkt_getuint32(pkt);
+        ssh_pkt_getstring(pkt, &str, &slen);
+        if (slen == 7 && !memcmp(str, "x11-req", 0)) {
+            ssh2_pkt_getbool(pkt);
+            ssh2_pkt_getbool(pkt);
+            ssh_pkt_getstring(pkt, &str, &slen);
+            blanks[nblanks].offset = pkt->savedpos;
+            blanks[nblanks].type = PKTLOG_BLANK;
+            ssh_pkt_getstring(pkt, &str, &slen);
+            if (str) {
+                blanks[nblanks].len = pkt->savedpos - blanks[nblanks].offset;
+                nblanks++;
+            }
+        }
+    }
+
+    log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5],
+               ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->data[5]),
+               pkt->body, pkt->length, nblanks, blanks,
+               &ssh->v2_outgoing_sequence);
+
+    /*
+     * Undo the above adjustment of pkt->length, to put the packet
+     * back in the state we found it.
+     */
+    pkt->length += (pkt->body - pkt->data);
+}
+
 static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
 {
     struct rdpkt2_state_tag *st = &ssh->rdpkt2_state;
@@ -1456,7 +1565,8 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
            /* See if that gives us a valid packet. */
            if (ssh->scmac->verresult(ssh->sc_mac_ctx,
                                      st->pktin->data + st->packetlen) &&
-               (st->len = GET_32BIT(st->pktin->data)) + 4 == st->packetlen)
+               ((st->len = toint(GET_32BIT(st->pktin->data))) ==
+                 st->packetlen-4))
                    break;
            if (st->packetlen >= OUR_V2_PACKETLIMIT) {
                bombout(("No valid incoming packet found"));
@@ -1489,7 +1599,7 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
        /*
         * Now get the length figure.
         */
-       st->len = GET_32BIT(st->pktin->data);
+       st->len = toint(GET_32BIT(st->pktin->data));
 
        /*
         * _Completely_ silly lengths should be stomped on before they
@@ -1581,37 +1691,19 @@ static struct Packet *ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
        }
     }
 
-    st->pktin->savedpos = 6;
-    st->pktin->body = st->pktin->data;
-    st->pktin->type = st->pktin->data[5];
-
     /*
-     * Log incoming packet, possibly omitting sensitive fields.
+     * pktin->body and pktin->length should identify the semantic
+     * content of the packet, excluding the initial type byte.
      */
-    if (ssh->logctx) {
-       int nblanks = 0;
-       struct logblank_t blank;
-       if (ssh->logomitdata) {
-           int do_blank = FALSE, blank_prefix = 0;
-           /* "Session data" packets - omit the data field */
-           if (st->pktin->type == SSH2_MSG_CHANNEL_DATA) {
-               do_blank = TRUE; blank_prefix = 8;
-           } else if (st->pktin->type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
-               do_blank = TRUE; blank_prefix = 12;
-           }
-           if (do_blank) {
-               blank.offset = blank_prefix;
-               blank.len = (st->pktin->length-6) - blank_prefix;
-               blank.type = PKTLOG_OMIT;
-               nblanks = 1;
-           }
-       }
-       log_packet(ssh->logctx, PKT_INCOMING, st->pktin->type,
-                  ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx,
-                                st->pktin->type),
-                  st->pktin->data+6, st->pktin->length-6,
-                  nblanks, &blank, &st->pktin->sequence);
-    }
+    st->pktin->type = st->pktin->data[5];
+    st->pktin->body = st->pktin->data + 6;
+    st->pktin->length = st->packetlen - 6 - st->pad;
+    assert(st->pktin->length >= 0);    /* one last double-check */
+
+    if (ssh->logctx)
+        ssh2_log_incoming_packet(ssh, st->pktin);
+
+    st->pktin->savedpos = 0;
 
     crFinish(st->pktin);
 }
@@ -1632,12 +1724,7 @@ static int s_wrpkt_prepare(Ssh ssh, struct Packet *pkt, int *offset_p)
     int len;
 
     if (ssh->logctx)
-       log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[12],
-                  ssh1_pkt_type(pkt->data[12]),
-                  pkt->body, pkt->length - (pkt->body - pkt->data),
-                  pkt->nblanks, pkt->blanks, NULL);
-    sfree(pkt->blanks); pkt->blanks = NULL;
-    pkt->nblanks = 0;
+        ssh1_log_outgoing_packet(ssh, pkt);
 
     if (ssh->v1_compressing) {
        unsigned char *compblk;
@@ -1677,6 +1764,8 @@ static int s_write(Ssh ssh, void *data, int len)
     if (ssh->logctx)
        log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data, len,
                   0, NULL, NULL);
+    if (!ssh->s)
+        return 0;
     return sk_write(ssh->s, (char *)data, len);
 }
 
@@ -1747,16 +1836,6 @@ static struct Packet *construct_packet(Ssh ssh, int pkttype, va_list ap)
            bn = va_arg(ap, Bignum);
            ssh1_pkt_addmp(pkt, bn);
            break;
-         /* Tokens for modifications to packet logging */
-         case PKTT_PASSWORD:
-           dont_log_password(ssh, pkt, PKTLOG_BLANK);
-           break;
-         case PKTT_DATA:
-           dont_log_data(ssh, pkt, PKTLOG_OMIT);
-           break;
-         case PKTT_OTHER:
-           end_log_omission(ssh, pkt);
-           break;
        }
     }
 
@@ -1835,17 +1914,8 @@ static void ssh_pkt_ensure(struct Packet *pkt, int length)
        if (body) pkt->body = pkt->data + offset;
     }
 }
-static void ssh_pkt_adddata(struct Packet *pkt, void *data, int len)
+static void ssh_pkt_adddata(struct Packet *pkt, const void *data, int len)
 {
-    if (pkt->logmode != PKTLOG_EMIT) {
-       pkt->nblanks++;
-       pkt->blanks = sresize(pkt->blanks, pkt->nblanks, struct logblank_t);
-       assert(pkt->body);
-       pkt->blanks[pkt->nblanks-1].offset = pkt->length -
-                                            (pkt->body - pkt->data);
-       pkt->blanks[pkt->nblanks-1].len = len;
-       pkt->blanks[pkt->nblanks-1].type = pkt->logmode;
-    }
     pkt->length += len;
     ssh_pkt_ensure(pkt, pkt->length);
     memcpy(pkt->data + pkt->length - len, data, len);
@@ -1869,17 +1939,18 @@ static void ssh_pkt_addstring_start(struct Packet *pkt)
     ssh_pkt_adduint32(pkt, 0);
     pkt->savedpos = pkt->length;
 }
-static void ssh_pkt_addstring_str(struct Packet *pkt, char *data)
+static void ssh_pkt_addstring_str(struct Packet *pkt, const char *data)
 {
     ssh_pkt_adddata(pkt, data, strlen(data));
     PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
 }
-static void ssh_pkt_addstring_data(struct Packet *pkt, char *data, int len)
+static void ssh_pkt_addstring_data(struct Packet *pkt, const char *data,
+                                   int len)
 {
     ssh_pkt_adddata(pkt, data, len);
     PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
 }
-static void ssh_pkt_addstring(struct Packet *pkt, char *data)
+static void ssh_pkt_addstring(struct Packet *pkt, const char *data)
 {
     ssh_pkt_addstring_start(pkt);
     ssh_pkt_addstring_str(pkt, data);
@@ -1923,6 +1994,7 @@ static struct Packet *ssh1_pkt_init(int pkt_type)
     pkt->length = 4 + 8;           /* space for length + max padding */
     ssh_pkt_addbyte(pkt, pkt_type);
     pkt->body = pkt->data + pkt->length;
+    pkt->type = pkt_type;
     return pkt;
 }
 
@@ -1941,6 +2013,7 @@ static struct Packet *ssh2_pkt_init(int pkt_type)
     struct Packet *pkt = ssh_new_packet();
     pkt->length = 5; /* space for packet length + padding length */
     pkt->forcepad = 0;
+    pkt->type = pkt_type;
     ssh_pkt_addbyte(pkt, (unsigned char) pkt_type);
     pkt->body = pkt->data + pkt->length; /* after packet type */
     return pkt;
@@ -1956,12 +2029,7 @@ static int ssh2_pkt_construct(Ssh ssh, struct Packet *pkt)
     int cipherblk, maclen, padding, i;
 
     if (ssh->logctx)
-       log_packet(ssh->logctx, PKT_OUTGOING, pkt->data[5],
-                  ssh2_pkt_type(ssh->pkt_kctx, ssh->pkt_actx, pkt->data[5]),
-                  pkt->body, pkt->length - (pkt->body - pkt->data),
-                  pkt->nblanks, pkt->blanks, &ssh->v2_outgoing_sequence);
-    sfree(pkt->blanks); pkt->blanks = NULL;
-    pkt->nblanks = 0;
+        ssh2_log_outgoing_packet(ssh, pkt);
 
     /*
      * Compress packet payload.
@@ -2320,7 +2388,7 @@ static void ssh_pkt_getstring(struct Packet *pkt, char **p, int *length)
     *length = 0;
     if (pkt->length - pkt->savedpos < 4)
        return;
-    len = GET_32BIT(pkt->body + pkt->savedpos);
+    len = toint(GET_32BIT(pkt->body + pkt->savedpos));
     if (len < 0)
        return;
     *length = len;
@@ -2404,7 +2472,7 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt,
      * See if this is in fact an ssh-rsa signature and a buggy
      * server; otherwise we can just do this the easy way.
      */
-    if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) &&
+    if ((ssh->remote_bugs & BUG_SSH2_RSA_PADDING) && pkblob_len > 4+7+4 &&
        (GET_32BIT(pkblob) == 7 && !memcmp(pkblob+4, "ssh-rsa", 7))) {
        int pos, len, siglen;
 
@@ -2413,8 +2481,15 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt,
         */
 
        pos = 4+7;                     /* skip over "ssh-rsa" */
-       pos += 4 + GET_32BIT(pkblob+pos);   /* skip over exponent */
-       len = GET_32BIT(pkblob+pos);   /* find length of modulus */
+        len = toint(GET_32BIT(pkblob+pos)); /* get length of exponent */
+        if (len < 0 || len > pkblob_len - pos - 4)
+            goto give_up;
+       pos += 4 + len;                /* skip over exponent */
+        if (pkblob_len - pos < 4)
+            goto give_up;
+       len = toint(GET_32BIT(pkblob+pos)); /* find length of modulus */
+        if (len < 0 || len > pkblob_len - pos - 4)
+            goto give_up;
        pos += 4;                      /* find modulus itself */
        while (len > 0 && pkblob[pos] == 0)
            len--, pos++;
@@ -2424,7 +2499,11 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt,
         * Now find the signature integer.
         */
        pos = 4+7;                     /* skip over "ssh-rsa" */
-       siglen = GET_32BIT(sigblob+pos);
+        if (sigblob_len < pos+4)
+            goto give_up;
+       siglen = toint(GET_32BIT(sigblob+pos));
+        if (siglen != sigblob_len - pos - 4)
+            goto give_up;
        /* debug(("signature length is %d\n", siglen)); */
 
        if (len != siglen) {
@@ -2446,7 +2525,10 @@ static void ssh2_add_sigblob(Ssh ssh, struct Packet *pkt,
            return;
        }
 
-       /* Otherwise fall through and do it the easy way. */
+       /* Otherwise fall through and do it the easy way. We also come
+         * here as a fallback if we discover above that the key blob
+         * is misformatted in some way. */
+      give_up:;
     }
 
     ssh2_pkt_addstring_start(pkt);
@@ -2927,11 +3009,11 @@ static int ssh_do_close(Ssh ssh, int notify_exit)
        while (NULL != (c = index234(ssh->channels, 0))) {
            switch (c->type) {
              case CHAN_X11:
-               x11_close(c->u.x11.s);
+               x11_close(c->u.x11.xconn);
                break;
              case CHAN_SOCKDATA:
              case CHAN_SOCKDATA_DORMANT:
-               pfd_close(c->u.pfd.s);
+               pfd_close(c->u.pfd.pf);
                break;
            }
            del234(ssh->channels, c); /* moving next one to index 0 */
@@ -2949,7 +3031,7 @@ static int ssh_do_close(Ssh ssh, int notify_exit)
        while (NULL != (pf = index234(ssh->portfwds, 0))) {
            /* Dispose of any listening socket. */
            if (pf->local)
-               pfd_terminate(pf->local);
+               pfl_terminate(pf->local);
            del234(ssh->portfwds, pf); /* moving next one to index 0 */
            free_portfwd(pf);
        }
@@ -3160,13 +3242,13 @@ static void ssh_throttle_all(Ssh ssh, int enable, int bufsize)
             */
            break;
          case CHAN_X11:
-           x11_override_throttle(c->u.x11.s, enable);
+           x11_override_throttle(c->u.x11.xconn, enable);
            break;
          case CHAN_AGENT:
            /* Agent channels require no buffer management. */
            break;
          case CHAN_SOCKDATA:
-           pfd_override_throttle(c->u.pfd.s, enable);
+           pfd_override_throttle(c->u.pfd.pf, enable);
            break;
        }
     }
@@ -3209,6 +3291,7 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen)
     Ssh ssh = c->ssh;
     void *sentreply = reply;
 
+    c->u.a.outstanding_requests--;
     if (!sentreply) {
        /* Fake SSH_AGENT_FAILURE. */
        sentreply = "\0\0\0\1\5";
@@ -3221,13 +3304,17 @@ static void ssh_agentf_callback(void *cv, void *reply, int replylen)
        send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
                    PKT_INT, c->remoteid,
                    PKT_INT, replylen,
-                   PKTT_DATA,
                    PKT_DATA, sentreply, replylen,
-                   PKTT_OTHER,
                    PKT_END);
     }
     if (reply)
        sfree(reply);
+    /*
+     * If we've already seen an incoming EOF but haven't sent an
+     * outgoing one, this may be the moment to send it.
+     */
+    if (c->u.a.outstanding_requests == 0 && (c->closes & CLOSES_RCVD_EOF))
+        sshfwd_write_eof(c);
 }
 
 /*
@@ -3271,7 +3358,6 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
 {
     int i, j, ret;
     unsigned char cookie[8], *ptr;
-    struct RSAKey servkey, hostkey;
     struct MD5Context md5c;
     struct do_ssh1_login_state {
        int crLine;
@@ -3299,6 +3385,7 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
        int commentlen;
         int dlgret;
        Filename *keyfile;
+        struct RSAKey servkey, hostkey;
     };
     crState(do_ssh1_login_state);
 
@@ -3321,8 +3408,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
     }
     memcpy(cookie, ptr, 8);
 
-    if (!ssh1_pkt_getrsakey(pktin, &servkey, &s->keystr1) ||
-       !ssh1_pkt_getrsakey(pktin, &hostkey, &s->keystr2)) {    
+    if (!ssh1_pkt_getrsakey(pktin, &s->servkey, &s->keystr1) ||
+       !ssh1_pkt_getrsakey(pktin, &s->hostkey, &s->keystr2)) { 
        bombout(("Failed to read SSH-1 public keys from public key packet"));
        crStop(0);
     }
@@ -3334,9 +3421,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
        char logmsg[80];
        logevent("Host key fingerprint is:");
        strcpy(logmsg, "      ");
-       hostkey.comment = NULL;
+       s->hostkey.comment = NULL;
        rsa_fingerprint(logmsg + strlen(logmsg),
-                       sizeof(logmsg) - strlen(logmsg), &hostkey);
+                       sizeof(logmsg) - strlen(logmsg), &s->hostkey);
        logevent(logmsg);
     }
 
@@ -3351,8 +3438,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
     ssh->v1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER;
 
     MD5Init(&md5c);
-    MD5Update(&md5c, s->keystr2, hostkey.bytes);
-    MD5Update(&md5c, s->keystr1, servkey.bytes);
+    MD5Update(&md5c, s->keystr2, s->hostkey.bytes);
+    MD5Update(&md5c, s->keystr1, s->servkey.bytes);
     MD5Update(&md5c, cookie, 8);
     MD5Final(s->session_id, &md5c);
 
@@ -3362,13 +3449,14 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
     /*
      * Verify that the `bits' and `bytes' parameters match.
      */
-    if (hostkey.bits > hostkey.bytes * 8 ||
-       servkey.bits > servkey.bytes * 8) {
+    if (s->hostkey.bits > s->hostkey.bytes * 8 ||
+       s->servkey.bits > s->servkey.bytes * 8) {
        bombout(("SSH-1 public keys were badly formatted"));
        crStop(0);
     }
 
-    s->len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);
+    s->len = (s->hostkey.bytes > s->servkey.bytes ?
+              s->hostkey.bytes : s->servkey.bytes);
 
     s->rsabuf = snewn(s->len, unsigned char);
 
@@ -3379,11 +3467,11 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
        /*
         * First format the key into a string.
         */
-       int len = rsastr_len(&hostkey);
+       int len = rsastr_len(&s->hostkey);
        char fingerprint[100];
        char *keystr = snewn(len, char);
-       rsastr_fmt(keystr, &hostkey);
-       rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey);
+       rsastr_fmt(keystr, &s->hostkey);
+       rsa_fingerprint(fingerprint, sizeof(fingerprint), &s->hostkey);
 
         ssh_set_frozen(ssh, 1);
        s->dlgret = verify_ssh_host_key(ssh->frontend,
@@ -3417,14 +3505,14 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
            s->rsabuf[i] ^= s->session_id[i];
     }
 
-    if (hostkey.bytes > servkey.bytes) {
-       ret = rsaencrypt(s->rsabuf, 32, &servkey);
+    if (s->hostkey.bytes > s->servkey.bytes) {
+       ret = rsaencrypt(s->rsabuf, 32, &s->servkey);
        if (ret)
-           ret = rsaencrypt(s->rsabuf, servkey.bytes, &hostkey);
+           ret = rsaencrypt(s->rsabuf, s->servkey.bytes, &s->hostkey);
     } else {
-       ret = rsaencrypt(s->rsabuf, 32, &hostkey);
+       ret = rsaencrypt(s->rsabuf, 32, &s->hostkey);
        if (ret)
-           ret = rsaencrypt(s->rsabuf, hostkey.bytes, &servkey);
+           ret = rsaencrypt(s->rsabuf, s->hostkey.bytes, &s->servkey);
     }
     if (!ret) {
        bombout(("SSH-1 public key encryptions failed due to bad formatting"));
@@ -3527,21 +3615,21 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
     ssh->crcda_ctx = crcda_make_context();
     logevent("Installing CRC compensation attack detector");
 
-    if (servkey.modulus) {
-       sfree(servkey.modulus);
-       servkey.modulus = NULL;
+    if (s->servkey.modulus) {
+       sfree(s->servkey.modulus);
+       s->servkey.modulus = NULL;
     }
-    if (servkey.exponent) {
-       sfree(servkey.exponent);
-       servkey.exponent = NULL;
+    if (s->servkey.exponent) {
+       sfree(s->servkey.exponent);
+       s->servkey.exponent = NULL;
     }
-    if (hostkey.modulus) {
-       sfree(hostkey.modulus);
-       hostkey.modulus = NULL;
+    if (s->hostkey.modulus) {
+       sfree(s->hostkey.modulus);
+       s->hostkey.modulus = NULL;
     }
-    if (hostkey.exponent) {
-       sfree(hostkey.exponent);
-       hostkey.exponent = NULL;
+    if (s->hostkey.exponent) {
+       sfree(s->hostkey.exponent);
+       s->hostkey.exponent = NULL;
     }
     crWaitUntil(pktin);
 
@@ -3676,7 +3764,12 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
            if (s->response && s->responselen >= 5 &&
                s->response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
                s->p = s->response + 5;
-               s->nkeys = GET_32BIT(s->p);
+               s->nkeys = toint(GET_32BIT(s->p));
+                if (s->nkeys < 0) {
+                    logeventf(ssh, "Pageant reported negative key count %d",
+                              s->nkeys);
+                    s->nkeys = 0;
+                }
                s->p += 4;
                logeventf(ssh, "Pageant has %d SSH-1 keys", s->nkeys);
                for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
@@ -3686,22 +3779,23 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                        int n, ok = FALSE;
                        do {           /* do while (0) to make breaking easy */
                            n = ssh1_read_bignum
-                               (s->p, s->responselen-(s->p-s->response),
+                               (s->p, toint(s->responselen-(s->p-s->response)),
                                 &s->key.exponent);
                            if (n < 0)
                                break;
                            s->p += n;
                            n = ssh1_read_bignum
-                               (s->p, s->responselen-(s->p-s->response),
+                               (s->p, toint(s->responselen-(s->p-s->response)),
                                 &s->key.modulus);
                            if (n < 0)
-                           break;
+                                break;
                            s->p += n;
                            if (s->responselen - (s->p-s->response) < 4)
                                break;
-                           s->commentlen = GET_32BIT(s->p);
+                           s->commentlen = toint(GET_32BIT(s->p));
                            s->p += 4;
-                           if (s->responselen - (s->p-s->response) <
+                           if (s->commentlen < 0 ||
+                                toint(s->responselen - (s->p-s->response)) <
                                s->commentlen)
                                break;
                            s->commentp = (char *)s->p;
@@ -4155,9 +4249,8 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                for (i = bottom; i <= top; i++) {
                    if (i == pwlen) {
                        defer_packet(ssh, s->pwpkt_type,
-                                    PKTT_PASSWORD, PKT_STR,
-                                    s->cur_prompt->prompts[0]->result,
-                                    PKTT_OTHER, PKT_END);
+                                     PKT_STR,s->cur_prompt->prompts[0]->result,
+                                    PKT_END);
                    } else {
                        for (j = 0; j < i; j++) {
                            do {
@@ -4195,9 +4288,9 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                    ss = s->cur_prompt->prompts[0]->result;
                }
                logevent("Sending length-padded password");
-               send_packet(ssh, s->pwpkt_type, PKTT_PASSWORD,
+               send_packet(ssh, s->pwpkt_type,
                            PKT_INT, len, PKT_DATA, ss, len,
-                           PKTT_OTHER, PKT_END);
+                           PKT_END);
            } else {
                /*
                 * The server is believed unable to cope with
@@ -4207,14 +4300,14 @@ static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen,
                len = strlen(s->cur_prompt->prompts[0]->result);
                logevent("Sending unpadded password");
                send_packet(ssh, s->pwpkt_type,
-                           PKTT_PASSWORD, PKT_INT, len,
+                            PKT_INT, len,
                            PKT_DATA, s->cur_prompt->prompts[0]->result, len,
-                           PKTT_OTHER, PKT_END);
+                           PKT_END);
            }
        } else {
-           send_packet(ssh, s->pwpkt_type, PKTT_PASSWORD,
+           send_packet(ssh, s->pwpkt_type,
                        PKT_STR, s->cur_prompt->prompts[0]->result,
-                       PKTT_OTHER, PKT_END);
+                       PKT_END);
        }
        logevent("Sent password");
        free_prompts(s->cur_prompt);
@@ -4264,6 +4357,12 @@ static void ssh_channel_try_eof(struct ssh_channel *c)
     }
 }
 
+Conf *sshfwd_get_conf(struct ssh_channel *c)
+{
+    Ssh ssh = c->ssh;
+    return ssh->conf;
+}
+
 void sshfwd_write_eof(struct ssh_channel *c)
 {
     Ssh ssh = c->ssh;
@@ -4278,7 +4377,7 @@ void sshfwd_write_eof(struct ssh_channel *c)
     ssh_channel_try_eof(c);
 }
 
-void sshfwd_unclean_close(struct ssh_channel *c)
+void sshfwd_unclean_close(struct ssh_channel *c, const char *err)
 {
     Ssh ssh = c->ssh;
 
@@ -4287,16 +4386,18 @@ void sshfwd_unclean_close(struct ssh_channel *c)
 
     switch (c->type) {
       case CHAN_X11:
-        x11_close(c->u.x11.s);
-        logevent("Forwarded X11 connection terminated due to local error");
+        x11_close(c->u.x11.xconn);
+        logeventf(ssh, "Forwarded X11 connection terminated due to local "
+                  "error: %s", err);
         break;
       case CHAN_SOCKDATA:
       case CHAN_SOCKDATA_DORMANT:
-        pfd_close(c->u.pfd.s);
-        logevent("Forwarded port closed due to local error");
+        pfd_close(c->u.pfd.pf);
+        logeventf(ssh, "Forwarded port closed due to local error: %s", err);
         break;
     }
     c->type = CHAN_ZOMBIE;
+    c->pending_eof = FALSE;   /* this will confuse a zombie channel */
 
     ssh2_channel_check_close(c);
 }
@@ -4311,8 +4412,8 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len)
     if (ssh->version == 1) {
        send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
                    PKT_INT, c->remoteid,
-                   PKT_INT, len, PKTT_DATA, PKT_DATA, buf, len,
-                   PKTT_OTHER, PKT_END);
+                   PKT_INT, len, PKT_DATA, buf, len,
+                   PKT_END);
        /*
         * In SSH-1 we can return 0 here - implying that forwarded
         * connections are never individually throttled - because
@@ -4618,9 +4719,9 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
                        /* XXX: rport_acceptall may not represent
                         * what was used to open the original connection,
                         * since it's reconfigurable. */
-                       ssh2_pkt_addstring(pktout, "0.0.0.0");
+                       ssh2_pkt_addstring(pktout, "");
                    } else {
-                       ssh2_pkt_addstring(pktout, "127.0.0.1");
+                       ssh2_pkt_addstring(pktout, "localhost");
                    }
                    ssh2_pkt_adduint32(pktout, epf->sport);
                    ssh2_pkt_send(ssh, pktout);
@@ -4629,7 +4730,7 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
                del234(ssh->rportfwds, rpf);
                free_rportfwd(rpf);
            } else if (epf->local) {
-               pfd_terminate(epf->local);
+               pfl_terminate(epf->local);
            }
 
            delpos234(ssh->portfwds, i);
@@ -4662,29 +4763,31 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
            }
 
            if (epf->type == 'L') {
-               const char *err = pfd_addforward(epf->daddr, epf->dport,
-                                                epf->saddr, epf->sport,
-                                                ssh, conf,
-                                                &epf->local,
-                                                epf->addressfamily);
+                char *err = pfl_listen(epf->daddr, epf->dport,
+                                       epf->saddr, epf->sport,
+                                       ssh, conf, &epf->local,
+                                       epf->addressfamily);
 
                logeventf(ssh, "Local %sport %s forwarding to %s%s%s",
                          epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
                          epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
                          sportdesc, dportdesc,
                          err ? " failed: " : "", err ? err : "");
+                if (err)
+                    sfree(err);
            } else if (epf->type == 'D') {
-               const char *err = pfd_addforward(NULL, -1,
-                                                epf->saddr, epf->sport,
-                                                ssh, conf,
-                                                &epf->local,
-                                                epf->addressfamily);
+               char *err = pfl_listen(NULL, -1, epf->saddr, epf->sport,
+                                       ssh, conf, &epf->local,
+                                       epf->addressfamily);
 
                logeventf(ssh, "Local %sport %s SOCKS dynamic forwarding%s%s",
                          epf->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :
                          epf->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",
                          sportdesc,
                          err ? " failed: " : "", err ? err : "");
+
+                if (err)
+                    sfree(err);
            } else {
                struct ssh_rportfwd *pf;
 
@@ -4699,9 +4802,15 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
                }
 
                pf = snew(struct ssh_rportfwd);
-               strncpy(pf->dhost, epf->daddr, lenof(pf->dhost)-1);
-               pf->dhost[lenof(pf->dhost)-1] = '\0';
+                pf->dhost = dupstr(epf->daddr);
                pf->dport = epf->dport;
+                if (epf->saddr) {
+                    pf->shost = dupstr(epf->saddr);
+                } else if (conf_get_int(conf, CONF_rport_acceptall)) {
+                    pf->shost = dupstr("");
+                } else {
+                    pf->shost = dupstr("localhost");
+                }
                pf->sport = epf->sport;
                if (add234(ssh->rportfwds, pf) != pf) {
                    logeventf(ssh, "Duplicate remote port forwarding to %s:%d",
@@ -4730,14 +4839,8 @@ static void ssh_setup_portfwd(Ssh ssh, Conf *conf)
                        pktout = ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
                        ssh2_pkt_addstring(pktout, "tcpip-forward");
                        ssh2_pkt_addbool(pktout, 1);/* want reply */
-                       if (epf->saddr) {
-                           ssh2_pkt_addstring(pktout, epf->saddr);
-                       } else if (conf_get_int(conf, CONF_rport_acceptall)) {
-                           ssh2_pkt_addstring(pktout, "0.0.0.0");
-                       } else {
-                           ssh2_pkt_addstring(pktout, "127.0.0.1");
-                       }
-                       ssh2_pkt_adduint32(pktout, epf->sport);
+                       ssh2_pkt_addstring(pktout, pf->shost);
+                       ssh2_pkt_adduint32(pktout, pf->sport);
                        ssh2_pkt_send(ssh, pktout);
 
                        ssh_queue_handler(ssh, SSH2_MSG_REQUEST_SUCCESS,
@@ -4787,28 +4890,19 @@ static void ssh1_smsg_x11_open(Ssh ssh, struct Packet *pktin)
        c = snew(struct ssh_channel);
        c->ssh = ssh;
 
-       if (x11_init(&c->u.x11.s, ssh->x11disp, c,
-                    NULL, -1, ssh->conf) != NULL) {
-           logevent("Opening X11 forward connection failed");
-           sfree(c);
-           send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
-                       PKT_INT, remoteid, PKT_END);
-       } else {
-           logevent
-               ("Opening X11 forward connection succeeded");
-           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);
-           logevent("Opened X11 forward channel");
-       }
+       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);
+        logevent("Opened X11 forward channel");
     }
 }
 
@@ -4835,6 +4929,7 @@ static void ssh1_smsg_agent_open(Ssh ssh, struct Packet *pktin)
        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,
@@ -4846,23 +4941,17 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
 {
     /* Remote side is trying to open a channel to talk to a
      * forwarded port. Give them back a local channel number. */
-    struct ssh_channel *c;
     struct ssh_rportfwd pf, *pfp;
     int remoteid;
     int hostsize, port;
     char *host;
-    const char *e;
-    c = snew(struct ssh_channel);
-    c->ssh = ssh;
+    char *err;
 
     remoteid = ssh_pkt_getuint32(pktin);
     ssh_pkt_getstring(pktin, &host, &hostsize);
     port = ssh_pkt_getuint32(pktin);
 
-    if (hostsize >= lenof(pf.dhost))
-       hostsize = lenof(pf.dhost)-1;
-    memcpy(pf.dhost, host, hostsize);
-    pf.dhost[hostsize] = '\0';
+    pf.dhost = dupprintf(".*s", hostsize, host);
     pf.dport = port;
     pfp = find234(ssh->rportfwds, &pf, NULL);
 
@@ -4872,12 +4961,16 @@ 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 {
+        struct ssh_channel *c = snew(struct ssh_channel);
+        c->ssh = ssh;
+
        logeventf(ssh, "Received remote port open request for %s:%d",
                  pf.dhost, port);
-       e = pfd_newconnect(&c->u.pfd.s, pf.dhost, port,
-                          c, ssh->conf, pfp->pfrec->addressfamily);
-       if (e != NULL) {
-           logeventf(ssh, "Port open failed: %s", e);
+       err = pfd_connect(&c->u.pfd.pf, pf.dhost, port,
+                          c, ssh->conf, pfp->pfrec->addressfamily);
+       if (err != NULL) {
+           logeventf(ssh, "Port open failed: %s", err);
+            sfree(err);
            sfree(c);
            send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
                        PKT_INT, remoteid, PKT_END);
@@ -4896,6 +4989,8 @@ static void ssh1_msg_port_open(Ssh ssh, struct Packet *pktin)
            logevent("Forwarded port opened successfully");
        }
     }
+
+    sfree(pf.dhost);
 }
 
 static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
@@ -4910,7 +5005,7 @@ static void ssh1_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
        c->halfopen = FALSE;
        c->type = CHAN_SOCKDATA;
        c->throttling_conn = 0;
-       pfd_confirm(c->u.pfd.s);
+       pfd_confirm(c->u.pfd.pf);
     }
 
     if (c && c->pending_eof) {
@@ -4932,7 +5027,7 @@ static void ssh1_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
     c = find234(ssh->channels, &remoteid, ssh_channelfind);
     if (c && c->type == CHAN_SOCKDATA_DORMANT) {
        logevent("Forwarded connection refused by server");
-       pfd_close(c->u.pfd.s);
+       pfd_close(c->u.pfd.pf);
        del234(ssh->channels, c);
        sfree(c);
     }
@@ -4958,14 +5053,14 @@ static void ssh1_msg_channel_close(Ssh ssh, struct Packet *pktin)
 
             switch (c->type) {
               case CHAN_X11:
-                if (c->u.x11.s)
-                    x11_send_eof(c->u.x11.s);
+                if (c->u.x11.xconn)
+                    x11_send_eof(c->u.x11.xconn);
                 else
                     send_close = TRUE;
                break;
               case CHAN_SOCKDATA:
-                if (c->u.pfd.s)
-                    pfd_send_eof(c->u.pfd.s);
+                if (c->u.pfd.pf)
+                    pfd_send_eof(c->u.pfd.pf);
                 else
                     send_close = TRUE;
                break;
@@ -5024,10 +5119,10 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
        int bufsize = 0;
        switch (c->type) {
          case CHAN_X11:
-           bufsize = x11_send(c->u.x11.s, p, len);
+           bufsize = x11_send(c->u.x11.xconn, p, len);
            break;
          case CHAN_SOCKDATA:
-           bufsize = pfd_send(c->u.pfd.s, p, len);
+           bufsize = pfd_send(c->u.pfd.pf, p, len);
            break;
          case CHAN_AGENT:
            /* Data for an agent message. Buffer it. */
@@ -5060,6 +5155,7 @@ static void ssh1_msg_channel_data(Ssh ssh, struct Packet *pktin)
                if (c->u.a.lensofar == c->u.a.totallen) {
                    void *reply;
                    int replylen;
+                    c->u.a.outstanding_requests++;
                    if (agent_query(c->u.a.message,
                                    c->u.a.totallen,
                                    &reply, &replylen,
@@ -5153,47 +5249,47 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
        }
     }
 
-    if (conf_get_int(ssh->conf, CONF_x11_forward) &&
-       (ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
-                                         conf_get_int(ssh->conf, CONF_x11_auth), ssh->conf))) {
-       logevent("Requesting X11 forwarding");
-       /*
-        * Note that while we blank the X authentication data here, we don't
-        * take any special action to blank the start of an X11 channel,
-        * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection
-        * without having session blanking enabled is likely to leak your
-        * cookie into the log.
-        */
-       if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
-           send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
-                       PKT_STR, ssh->x11disp->remoteauthprotoname,
-                       PKTT_PASSWORD,
-                       PKT_STR, ssh->x11disp->remoteauthdatastring,
-                       PKTT_OTHER,
-                       PKT_INT, ssh->x11disp->screennum,
-                       PKT_END);
-       } else {
-           send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
-                       PKT_STR, ssh->x11disp->remoteauthprotoname,
-                       PKTT_PASSWORD,
-                       PKT_STR, ssh->x11disp->remoteauthdatastring,
-                       PKTT_OTHER,
-                       PKT_END);
-       }
-       do {
-           crReturnV;
-       } while (!pktin);
-       if (pktin->type != SSH1_SMSG_SUCCESS
-           && pktin->type != SSH1_SMSG_FAILURE) {
-           bombout(("Protocol confusion"));
-           crStopV;
-       } else if (pktin->type == SSH1_SMSG_FAILURE) {
-           logevent("X11 forwarding refused");
-       } else {
-           logevent("X11 forwarding enabled");
-           ssh->X11_fwd_enabled = TRUE;
-           ssh->packet_dispatch[SSH1_SMSG_X11_OPEN] = ssh1_smsg_x11_open;
-       }
+    if (conf_get_int(ssh->conf, CONF_x11_forward)) {
+        ssh->x11disp =
+            x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
+                              ssh->conf);
+        if (!ssh->x11disp) {
+            /* FIXME: return an error message from x11_setup_display */
+            logevent("X11 forwarding not enabled: unable to"
+                     " initialise X display");
+        } else {
+            ssh->x11auth = x11_invent_fake_auth
+                (ssh->x11authtree, conf_get_int(ssh->conf, CONF_x11_auth));
+            ssh->x11auth->disp = ssh->x11disp;
+
+            logevent("Requesting X11 forwarding");
+            if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
+                send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
+                            PKT_STR, ssh->x11auth->protoname,
+                            PKT_STR, ssh->x11auth->datastring,
+                            PKT_INT, ssh->x11disp->screennum,
+                            PKT_END);
+            } else {
+                send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
+                            PKT_STR, ssh->x11auth->protoname,
+                            PKT_STR, ssh->x11auth->datastring,
+                            PKT_END);
+            }
+            do {
+                crReturnV;
+            } while (!pktin);
+            if (pktin->type != SSH1_SMSG_SUCCESS
+                && pktin->type != SSH1_SMSG_FAILURE) {
+                bombout(("Protocol confusion"));
+                crStopV;
+            } else if (pktin->type == SSH1_SMSG_FAILURE) {
+                logevent("X11 forwarding refused");
+            } else {
+                logevent("X11 forwarding enabled");
+                ssh->X11_fwd_enabled = TRUE;
+                ssh->packet_dispatch[SSH1_SMSG_X11_OPEN] = ssh1_smsg_x11_open;
+            }
+        }
     }
 
     ssh_setup_portfwd(ssh, ssh->conf);
@@ -5314,8 +5410,8 @@ static void do_ssh1_connection(Ssh ssh, unsigned char *in, int inlen,
            while (inlen > 0) {
                int len = min(inlen, 512);
                send_packet(ssh, SSH1_CMSG_STDIN_DATA,
-                           PKT_INT, len,  PKTT_DATA, PKT_DATA, in, len,
-                           PKTT_OTHER, PKT_END);
+                           PKT_INT, len, PKT_DATA, in, len,
+                            PKT_END);
                in += len;
                inlen -= len;
            }
@@ -5476,7 +5572,7 @@ static void ssh2_mkkey(Ssh ssh, Bignum K, unsigned char *H, char chr,
 /*
  * Handle the SSH-2 transport layer.
  */
-static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
+static void do_ssh2_transport(Ssh ssh, void *vin, int inlen,
                             struct Packet *pktin)
 {
     unsigned char *in = (unsigned char *)vin;
@@ -5642,12 +5738,28 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
            }
        }
        /* List server host key algorithms. */
-       ssh2_pkt_addstring_start(s->pktout);
-       for (i = 0; i < lenof(hostkey_algs); i++) {
-           ssh2_pkt_addstring_str(s->pktout, hostkey_algs[i]->name);
-           if (i < lenof(hostkey_algs) - 1)
-               ssh2_pkt_addstring_str(s->pktout, ",");
-       }
+        if (!s->got_session_id) {
+            /*
+             * In the first key exchange, we list all the algorithms
+             * we're prepared to cope with.
+             */
+            ssh2_pkt_addstring_start(s->pktout);
+            for (i = 0; i < lenof(hostkey_algs); i++) {
+                ssh2_pkt_addstring_str(s->pktout, hostkey_algs[i]->name);
+                if (i < lenof(hostkey_algs) - 1)
+                    ssh2_pkt_addstring_str(s->pktout, ",");
+            }
+        } else {
+            /*
+             * In subsequent key exchanges, we list only the kex
+             * algorithm that was selected in the first key exchange,
+             * so that we keep getting the same host key and hence
+             * don't have to interrupt the user's session to ask for
+             * reverification.
+             */
+            assert(ssh->kex);
+            ssh2_pkt_addstring(s->pktout, ssh->hostkey->name);
+        }
        /* List encryption algorithms (client->server then server->client). */
        for (k = 0; k < 2; k++) {
            ssh2_pkt_addstring_start(s->pktout);
@@ -5717,7 +5829,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
     ssh2_pkt_send_noqueue(ssh, s->pktout);
 
     if (!pktin)
-       crWaitUntil(pktin);
+       crWaitUntilV(pktin);
 
     /*
      * Now examine the other side's KEXINIT to see what we're up
@@ -5729,7 +5841,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
 
        if (pktin->type != SSH2_MSG_KEXINIT) {
            bombout(("expected key exchange packet from server"));
-           crStop(0);
+           crStopV;
        }
        ssh->kex = NULL;
        ssh->hostkey = NULL;
@@ -5764,7 +5876,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        if (!ssh->kex) {
            bombout(("Couldn't agree a key exchange algorithm (available: %s)",
                     str ? str : "(null)"));
-           crStop(0);
+           crStopV;
        }
        /*
         * Note that the server's guess is considered wrong if it doesn't match
@@ -5782,7 +5894,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        if (!ssh->hostkey) {
            bombout(("Couldn't agree a host key algorithm (available: %s)",
                     str ? str : "(null)"));
-           crStop(0);
+           crStopV;
        }
 
        s->guessok = s->guessok &&
@@ -5806,7 +5918,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        if (!s->cscipher_tobe) {
            bombout(("Couldn't agree a client-to-server cipher (available: %s)",
                     str ? str : "(null)"));
-           crStop(0);
+           crStopV;
        }
 
        ssh_pkt_getstring(pktin, &str, &len);    /* server->client cipher */
@@ -5828,7 +5940,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        if (!s->sccipher_tobe) {
            bombout(("Couldn't agree a server-to-client cipher (available: %s)",
                     str ? str : "(null)"));
-           crStop(0);
+           crStopV;
        }
 
        ssh_pkt_getstring(pktin, &str, &len);    /* client->server mac */
@@ -5885,6 +5997,16 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
        ssh_pkt_getstring(pktin, &str, &len);  /* server->client language */
        s->ignorepkt = ssh2_pkt_getbool(pktin) && !s->guessok;
 
+       ssh->exhash = ssh->kex->hash->init();
+       hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c));
+       hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s));
+       hash_string(ssh->kex->hash, ssh->exhash,
+           s->our_kexinit, s->our_kexinitlen);
+       sfree(s->our_kexinit);
+        /* Include the type byte in the hash of server's KEXINIT */
+        hash_string(ssh->kex->hash, ssh->exhash,
+                    pktin->body - 1, pktin->length + 1);
+
        if (s->warn_kex) {
            ssh_set_frozen(ssh, 1);
            s->dlgret = askalg(ssh->frontend, "key-exchange algorithm",
@@ -5892,11 +6014,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
                               ssh_dialog_callback, ssh);
            if (s->dlgret < 0) {
                do {
-                   crReturn(0);
+                   crReturnV;
                    if (pktin) {
                        bombout(("Unexpected data from server while"
                                 " waiting for user response"));
-                       crStop(0);
+                       crStopV;
                    }
                } while (pktin || inlen > 0);
                s->dlgret = ssh->user_response;
@@ -5905,7 +6027,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
            if (s->dlgret == 0) {
                ssh_disconnect(ssh, "User aborted at kex warning", NULL,
                               0, TRUE);
-               crStop(0);
+               crStopV;
            }
        }
 
@@ -5917,11 +6039,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
                               ssh_dialog_callback, ssh);
            if (s->dlgret < 0) {
                do {
-                   crReturn(0);
+                   crReturnV;
                    if (pktin) {
                        bombout(("Unexpected data from server while"
                                 " waiting for user response"));
-                       crStop(0);
+                       crStopV;
                    }
                } while (pktin || inlen > 0);
                s->dlgret = ssh->user_response;
@@ -5930,7 +6052,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
            if (s->dlgret == 0) {
                ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
                               0, TRUE);
-               crStop(0);
+               crStopV;
            }
        }
 
@@ -5942,11 +6064,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
                               ssh_dialog_callback, ssh);
            if (s->dlgret < 0) {
                do {
-                   crReturn(0);
+                   crReturnV;
                    if (pktin) {
                        bombout(("Unexpected data from server while"
                                 " waiting for user response"));
-                       crStop(0);
+                       crStopV;
                    }
                } while (pktin || inlen > 0);
                s->dlgret = ssh->user_response;
@@ -5955,22 +6077,12 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
            if (s->dlgret == 0) {
                ssh_disconnect(ssh, "User aborted at cipher warning", NULL,
                               0, TRUE);
-               crStop(0);
+               crStopV;
            }
        }
 
-       ssh->exhash = ssh->kex->hash->init();
-       hash_string(ssh->kex->hash, ssh->exhash, ssh->v_c, strlen(ssh->v_c));
-       hash_string(ssh->kex->hash, ssh->exhash, ssh->v_s, strlen(ssh->v_s));
-       hash_string(ssh->kex->hash, ssh->exhash,
-           s->our_kexinit, s->our_kexinitlen);
-       sfree(s->our_kexinit);
-       if (pktin->length > 5)
-           hash_string(ssh->kex->hash, ssh->exhash,
-               pktin->data + 5, pktin->length - 5);
-
        if (s->ignorepkt) /* first_kex_packet_follows */
-           crWaitUntil(pktin);                /* Ignore packet */
+           crWaitUntilV(pktin);                /* Ignore packet */
     }
 
     if (ssh->kex->main_type == KEXTYPE_DH) {
@@ -6007,16 +6119,16 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
             ssh2_pkt_adduint32(s->pktout, s->pbits);
             ssh2_pkt_send_noqueue(ssh, s->pktout);
 
-            crWaitUntil(pktin);
+            crWaitUntilV(pktin);
             if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) {
                 bombout(("expected key exchange group packet from server"));
-                crStop(0);
+                crStopV;
             }
             s->p = ssh2_pkt_getmp(pktin);
             s->g = ssh2_pkt_getmp(pktin);
             if (!s->p || !s->g) {
                 bombout(("unable to read mp-ints from incoming group packet"));
-                crStop(0);
+                crStopV;
             }
             ssh->kex_ctx = dh_setup_gex(s->p, s->g);
             s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
@@ -6042,10 +6154,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
         ssh2_pkt_send_noqueue(ssh, s->pktout);
 
         set_busy_status(ssh->frontend, BUSY_WAITING); /* wait for server */
-        crWaitUntil(pktin);
+        crWaitUntilV(pktin);
         if (pktin->type != s->kex_reply_value) {
             bombout(("expected key exchange reply packet from server"));
-            crStop(0);
+            crStopV;
         }
         set_busy_status(ssh->frontend, BUSY_CPU); /* cogitate */
         ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
@@ -6053,7 +6165,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
         s->f = ssh2_pkt_getmp(pktin);
         if (!s->f) {
             bombout(("unable to parse key exchange reply packet"));
-            crStop(0);
+            crStopV;
         }
         ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
 
@@ -6086,10 +6198,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
          * RSA key exchange. First expect a KEXRSA_PUBKEY packet
          * from the server.
          */
-        crWaitUntil(pktin);
+        crWaitUntilV(pktin);
         if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) {
             bombout(("expected RSA public key packet from server"));
-            crStop(0);
+            crStopV;
         }
 
         ssh_pkt_getstring(pktin, &s->hostkeydata, &s->hostkeylen);
@@ -6108,7 +6220,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
         if (!s->rsakey) {
             sfree(s->rsakeydata);
             bombout(("unable to parse RSA public key from server"));
-            crStop(0);
+            crStopV;
         }
 
         hash_string(ssh->kex->hash, ssh->exhash, s->rsakeydata, s->rsakeylen);
@@ -6168,11 +6280,11 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
 
         ssh_rsakex_freekey(s->rsakey);
 
-        crWaitUntil(pktin);
+        crWaitUntilV(pktin);
         if (pktin->type != SSH2_MSG_KEXRSA_DONE) {
             sfree(s->rsakeydata);
             bombout(("expected signature packet from server"));
-            crStop(0);
+            crStopV;
         }
 
         ssh_pkt_getstring(pktin, &s->sigdata, &s->siglen);
@@ -6196,44 +6308,60 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
                                 (char *)s->exchange_hash,
                                 ssh->kex->hash->hlen)) {
        bombout(("Server's host key did not match the signature supplied"));
-       crStop(0);
+       crStopV;
     }
 
-    /*
-     * Authenticate remote host: verify host key. (We've already
-     * checked the signature of the exchange hash.)
-     */
     s->keystr = ssh->hostkey->fmtkey(s->hkey);
-    s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
-    ssh_set_frozen(ssh, 1);
-    s->dlgret = verify_ssh_host_key(ssh->frontend,
-                                    ssh->savedhost, ssh->savedport,
-                                    ssh->hostkey->keytype, s->keystr,
-                                   s->fingerprint,
-                                    ssh_dialog_callback, ssh);
-    if (s->dlgret < 0) {
-        do {
-            crReturn(0);
-            if (pktin) {
-                bombout(("Unexpected data from server while waiting"
-                         " for user host key response"));
-                    crStop(0);
-            }
-        } while (pktin || inlen > 0);
-        s->dlgret = ssh->user_response;
-    }
-    ssh_set_frozen(ssh, 0);
-    if (s->dlgret == 0) {
-       ssh_disconnect(ssh, "User aborted at host key verification", NULL,
-                      0, TRUE);
-        crStop(0);
-    }
-    if (!s->got_session_id) {     /* don't bother logging this in rekeys */
-       logevent("Host key fingerprint is:");
-       logevent(s->fingerprint);
+    if (!s->got_session_id) {
+        /*
+         * Authenticate remote host: verify host key. (We've already
+         * checked the signature of the exchange hash.)
+         */
+        s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
+        ssh_set_frozen(ssh, 1);
+        s->dlgret = verify_ssh_host_key(ssh->frontend,
+                                        ssh->savedhost, ssh->savedport,
+                                        ssh->hostkey->keytype, s->keystr,
+                                        s->fingerprint,
+                                        ssh_dialog_callback, ssh);
+        if (s->dlgret < 0) {
+            do {
+                crReturnV;
+                if (pktin) {
+                    bombout(("Unexpected data from server while waiting"
+                             " for user host key response"));
+                    crStopV;
+                }
+            } while (pktin || inlen > 0);
+            s->dlgret = ssh->user_response;
+        }
+        ssh_set_frozen(ssh, 0);
+        if (s->dlgret == 0) {
+            ssh_disconnect(ssh, "User aborted at host key verification", NULL,
+                           0, TRUE);
+            crStopV;
+        }
+        logevent("Host key fingerprint is:");
+        logevent(s->fingerprint);
+        sfree(s->fingerprint);
+        /*
+         * Save this host key, to check against the one presented in
+         * subsequent rekeys.
+         */
+        ssh->hostkey_str = s->keystr;
+    } else {
+        /*
+         * In a rekey, we never present an interactive host key
+         * verification request to the user. Instead, we simply
+         * enforce that the key we're seeing this time is identical to
+         * the one we saw before.
+         */
+        if (strcmp(ssh->hostkey_str, s->keystr)) {
+            bombout(("Host key was different in repeat key exchange"));
+            crStopV;
+        }
+        sfree(s->keystr);
     }
-    sfree(s->fingerprint);
-    sfree(s->keystr);
     ssh->hostkey->freekey(s->hkey);
 
     /*
@@ -6316,10 +6444,10 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
     /*
      * Expect SSH2_MSG_NEWKEYS from server.
      */
-    crWaitUntil(pktin);
+    crWaitUntilV(pktin);
     if (pktin->type != SSH2_MSG_NEWKEYS) {
        bombout(("expected new-keys packet from server"));
-       crStop(0);
+       crStopV;
     }
     ssh->incoming_data_size = 0;       /* start counting from here */
 
@@ -6421,7 +6549,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
             */
            do_ssh2_authconn(ssh, NULL, 0, NULL);
        }
-       crReturn(1);
+       crReturnV;
     }
     if (pktin) {
        logevent("Server initiated key re-exchange");
@@ -6480,7 +6608,7 @@ static int do_ssh2_transport(Ssh ssh, void *vin, int inlen,
     }
     goto begin_key_exchange;
 
-    crFinish(1);
+    crFinishV;
 }
 
 /*
@@ -6512,9 +6640,7 @@ static int ssh2_try_send(struct ssh_channel *c)
        pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);
        ssh2_pkt_adduint32(pktout, c->remoteid);
        ssh2_pkt_addstring_start(pktout);
-       dont_log_data(ssh, pktout, PKTLOG_OMIT);
        ssh2_pkt_addstring_data(pktout, data, len);
-       end_log_omission(ssh, pktout);
        ssh2_pkt_send(ssh, pktout);
        bufchain_consume(&c->v.v2.outbuffer, len);
        c->v.v2.remwindow -= len;
@@ -6549,14 +6675,14 @@ static void ssh2_try_send_and_unthrottle(Ssh ssh, struct ssh_channel *c)
             * notification since it will be polled */
            break;
          case CHAN_X11:
-           x11_unthrottle(c->u.x11.s);
+           x11_unthrottle(c->u.x11.xconn);
            break;
          case CHAN_AGENT:
            /* agent sockets are request/response and need no
             * buffer management */
            break;
          case CHAN_SOCKDATA:
-           pfd_unthrottle(c->u.pfd.s);
+           pfd_unthrottle(c->u.pfd.pf);
            break;
        }
     }
@@ -6579,6 +6705,21 @@ static void ssh2_channel_init(struct ssh_channel *c)
     bufchain_init(&c->v.v2.outbuffer);
 }
 
+/*
+ * Construct the common parts of a CHANNEL_OPEN.
+ */
+static struct Packet *ssh2_chanopen_init(struct ssh_channel *c, char *type)
+{
+    struct Packet *pktout;
+
+    pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
+    ssh2_pkt_addstring(pktout, type);
+    ssh2_pkt_adduint32(pktout, c->localid);
+    ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */
+    ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT);      /* our max pkt size */
+    return pktout;
+}
+
 /*
  * CHANNEL_FAILURE doesn't come with any indication of what message
  * caused it, so we have to keep track of the outstanding
@@ -6804,10 +6945,10 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
                             data, length);
            break;
          case CHAN_X11:
-           bufsize = x11_send(c->u.x11.s, data, length);
+           bufsize = x11_send(c->u.x11.xconn, data, length);
            break;
          case CHAN_SOCKDATA:
-           bufsize = pfd_send(c->u.pfd.s, data, length);
+           bufsize = pfd_send(c->u.pfd.pf, data, length);
            break;
          case CHAN_AGENT:
            while (length > 0) {
@@ -6840,6 +6981,7 @@ static void ssh2_msg_channel_data(Ssh ssh, struct Packet *pktin)
                if (c->u.a.lensofar == c->u.a.totallen) {
                    void *reply;
                    int replylen;
+                    c->u.a.outstanding_requests++;
                    if (agent_query(c->u.a.message,
                                    c->u.a.totallen,
                                    &reply, &replylen,
@@ -6894,16 +7036,16 @@ static void ssh_channel_destroy(struct ssh_channel *c)
         update_specials_menu(ssh->frontend);
         break;
       case CHAN_X11:
-        if (c->u.x11.s != NULL)
-            x11_close(c->u.x11.s);
+        if (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.s != NULL)
-            pfd_close(c->u.pfd.s);
+        if (c->u.pfd.pf != NULL)
+            pfd_close(c->u.pfd.pf);
         logevent("Forwarded port closed");
         break;
     }
@@ -6943,6 +7085,15 @@ static void ssh2_channel_check_close(struct ssh_channel *c)
     Ssh ssh = c->ssh;
     struct Packet *pktout;
 
+    if (c->halfopen) {
+        /*
+         * If we've sent out our own CHANNEL_OPEN but not yet seen
+         * either OPEN_CONFIRMATION or OPEN_FAILURE in response, then
+         * it's too early to be sending close messages of any kind.
+         */
+        return;
+    }
+
     if ((!((CLOSES_SENT_EOF | CLOSES_RCVD_EOF) & ~c->closes) ||
         c->type == CHAN_ZOMBIE) &&
        !c->v.v2.chanreq_head &&
@@ -6976,12 +7127,14 @@ static void ssh2_channel_got_eof(struct ssh_channel *c)
     c->closes |= CLOSES_RCVD_EOF;
 
     if (c->type == CHAN_X11) {
-       x11_send_eof(c->u.x11.s);
+       x11_send_eof(c->u.x11.xconn);
     } else if (c->type == CHAN_AGENT) {
-        /* Manufacture an outgoing EOF in response to the incoming one. */
-        sshfwd_write_eof(c);
+        if (c->u.a.outstanding_requests == 0) {
+            /* 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);
+       pfd_send_eof(c->u.pfd.pf);
     } else if (c->type == CHAN_MAINSESSION) {
         Ssh ssh = c->ssh;
 
@@ -7044,13 +7197,22 @@ static void ssh2_msg_channel_close(Ssh ssh, struct Packet *pktin)
             ssh->send_ok = 0;     /* stop trying to read from stdin */
             break;
           case CHAN_X11:
-           x11_override_throttle(c->u.x11.s, 1);
+           x11_override_throttle(c->u.x11.xconn, 1);
            break;
          case CHAN_SOCKDATA:
-           pfd_override_throttle(c->u.pfd.s, 1);
+           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.
          */
@@ -7073,17 +7235,42 @@ static void ssh2_msg_channel_open_confirmation(Ssh ssh, struct Packet *pktin)
     c = ssh2_channel_msg(ssh, pktin);
     if (!c)
        return;
-    if (c->type != CHAN_SOCKDATA_DORMANT)
-       return;                        /* dunno why they're confirming this */
+    assert(c->halfopen); /* ssh2_channel_msg will have enforced 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->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);
+        ssh_channel_try_eof(c);        /* in case we had a pending EOF */
 }
 
 static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
@@ -7099,20 +7286,41 @@ static void ssh2_msg_channel_open_failure(Ssh ssh, struct Packet *pktin)
     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);
+    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);
@@ -7172,16 +7380,18 @@ static void ssh2_msg_channel_request(Ssh ssh, struct Packet *pktin)
                    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)
+#define CHECK_HYPOTHESIS(offset, result)                                \
+                    do                                                  \
+                    {                                                   \
+                        int q = toint(offset);                          \
+                        if (q >= 0 && q+4 <= len) {                     \
+                            q = toint(q + 4 + GET_32BIT(p+q));          \
+                            if (q >= 0 && q+4 <= len &&                 \
+                                ((q = toint(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
@@ -7341,7 +7551,6 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
 
     if (typelen == 3 && !memcmp(type, "x11", 3)) {
        char *addrstr;
-       const char *x11err;
 
        ssh_pkt_getstring(pktin, &peeraddr, &peeraddrlen);
        addrstr = snewn(peeraddrlen+1, char);
@@ -7354,40 +7563,43 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
 
        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");
+       else {
+            c->u.x11.xconn = x11_init(ssh->x11authtree, c,
+                                      addrstr, peerport);
            c->type = CHAN_X11;
+
+            logevent("Opened X11 forward channel");
        }
 
        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 */
+       char *shost;
+       int shostlen;
+       ssh_pkt_getstring(pktin, &shost, &shostlen);/* skip address */
+        pf.shost = dupprintf("%.*s", shostlen, shost);
        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);
+       logeventf(ssh, "Received remote port %s:%d open request "
+                 "from %s:%d", pf.shost, pf.sport, peeraddr, peerport);
+        sfree(pf.shost);
+
        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);
+           char *err = pfd_connect(&c->u.pfd.pf,
+                                    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);
+           if (err != NULL) {
+               logeventf(ssh, "Port open failed: %s", err);
+                sfree(err);
                error = "Port open failed";
            } else {
                logevent("Forwarded port opened successfully");
@@ -7401,6 +7613,8 @@ static void ssh2_msg_channel_open(Ssh ssh, struct Packet *pktin)
        else {
            c->type = CHAN_AGENT;       /* identify channel type */
            c->u.a.lensofar = 0;
+            c->u.a.message = NULL;
+            c->u.a.outstanding_requests = 0;
        }
     } else {
        error = "Unsupported channel type requested";
@@ -7468,136 +7682,123 @@ static void ssh2_send_ttymode(void *data, char *mode, char *val)
     ssh2_pkt_adduint32(pktout, arg);
 }
 
-static void ssh2_maybe_setup_x11(struct ssh_channel *c, struct Packet *pktin,
-                                void *ctx)
+static void ssh2_setup_x11(struct ssh_channel *c, struct Packet *pktin,
+                           void *ctx)
 {
-    struct ssh2_maybe_setup_x11_state {
+    struct ssh2_setup_x11_state {
        int crLine;
     };
     Ssh ssh = c->ssh;
     struct Packet *pktout;
-    crStateP(ssh2_maybe_setup_x11_state, ctx);
+    crStateP(ssh2_setup_x11_state, ctx);
 
     crBeginState;
 
-    /*
-     * Potentially enable X11 forwarding.
-     */
-    if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_x11_forward) &&
-       (ssh->x11disp = x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
-                                         conf_get_int(ssh->conf, CONF_x11_auth), ssh->conf))) {
-       logevent("Requesting X11 forwarding");
-       pktout = ssh2_chanreq_init(ssh->mainchan, "x11-req",
-                                  ssh2_maybe_setup_x11, s);
-       ssh2_pkt_addbool(pktout, 0);           /* many connections */
-       ssh2_pkt_addstring(pktout, ssh->x11disp->remoteauthprotoname);
-       /*
-        * Note that while we blank the X authentication data here, we don't
-        * take any special action to blank the start of an X11 channel,
-        * so using MIT-MAGIC-COOKIE-1 and actually opening an X connection
-        * without having session blanking enabled is likely to leak your
-        * cookie into the log.
-        */
-       dont_log_password(ssh, pktout, PKTLOG_BLANK);
-       ssh2_pkt_addstring(pktout, ssh->x11disp->remoteauthdatastring);
-       end_log_omission(ssh, pktout);
-       ssh2_pkt_adduint32(pktout, ssh->x11disp->screennum);
-       ssh2_pkt_send(ssh, pktout);
+    logevent("Requesting X11 forwarding");
+    pktout = ssh2_chanreq_init(ssh->mainchan, "x11-req",
+                               ssh2_setup_x11, s);
+    ssh2_pkt_addbool(pktout, 0);              /* many connections */
+    ssh2_pkt_addstring(pktout, ssh->x11auth->protoname);
+    ssh2_pkt_addstring(pktout, ssh->x11auth->datastring);
+    ssh2_pkt_adduint32(pktout, ssh->x11disp->screennum);
+    ssh2_pkt_send(ssh, pktout);
 
-       crWaitUntilV(pktin);
+    /* Wait to be called back with either a response packet, or NULL
+     * meaning clean up and free our data */
+    crReturnV;
 
-       if (pktin) {
-           if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) {
-               logevent("X11 forwarding enabled");
-               ssh->X11_fwd_enabled = TRUE;
-           } else
-               logevent("X11 forwarding refused");
-       }
+    if (pktin) {
+        if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) {
+            logevent("X11 forwarding enabled");
+            ssh->X11_fwd_enabled = TRUE;
+        } else
+            logevent("X11 forwarding refused");
     }
+
     crFinishFreeV;
 }
 
-static void ssh2_maybe_setup_agent(struct ssh_channel *c, struct Packet *pktin,
+static void ssh2_setup_agent(struct ssh_channel *c, struct Packet *pktin,
                                   void *ctx)
 {
-    struct ssh2_maybe_setup_agent_state {
+    struct ssh2_setup_agent_state {
        int crLine;
     };
     Ssh ssh = c->ssh;
     struct Packet *pktout;
-    crStateP(ssh2_maybe_setup_agent_state, ctx);
+    crStateP(ssh2_setup_agent_state, ctx);
 
     crBeginState;
 
-    if (ssh->mainchan && !ssh->ncmode && conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists()) {
-       logevent("Requesting OpenSSH-style agent forwarding");
-       pktout = ssh2_chanreq_init(ssh->mainchan, "auth-agent-req@openssh.com",
-                                  ssh2_maybe_setup_agent, s);
-       ssh2_pkt_send(ssh, pktout);
+    logevent("Requesting OpenSSH-style agent forwarding");
+    pktout = ssh2_chanreq_init(ssh->mainchan, "auth-agent-req@openssh.com",
+                               ssh2_setup_agent, s);
+    ssh2_pkt_send(ssh, pktout);
 
-       crWaitUntilV(pktin);
+    /* Wait to be called back with either a response packet, or NULL
+     * meaning clean up and free our data */
+    crReturnV;
 
-       if (pktin) {
-           if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) {
-               logevent("Agent forwarding enabled");
-               ssh->agentfwd_enabled = TRUE;
-           } else
-               logevent("Agent forwarding refused");
-       }
+    if (pktin) {
+        if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) {
+            logevent("Agent forwarding enabled");
+            ssh->agentfwd_enabled = TRUE;
+        } else
+            logevent("Agent forwarding refused");
     }
+
     crFinishFreeV;
 }
 
-static void ssh2_maybe_setup_pty(struct ssh_channel *c, struct Packet *pktin,
+static void ssh2_setup_pty(struct ssh_channel *c, struct Packet *pktin,
                                 void *ctx)
 {
-    struct ssh2_maybe_setup_pty_state {
+    struct ssh2_setup_pty_state {
        int crLine;
     };
     Ssh ssh = c->ssh;
     struct Packet *pktout;
-    crStateP(ssh2_maybe_setup_pty_state, ctx);
+    crStateP(ssh2_setup_pty_state, ctx);
 
     crBeginState;
 
-    if (ssh->mainchan && !ssh->ncmode && !conf_get_int(ssh->conf, CONF_nopty)) {
-       /* Unpick the terminal-speed string. */
-       /* XXX perhaps we should allow no speeds to be sent. */
-        ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
-       sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed);
-       /* Build the pty request. */
-       pktout = ssh2_chanreq_init(ssh->mainchan, "pty-req",
-                                  ssh2_maybe_setup_pty, s);
-       ssh2_pkt_addstring(pktout, conf_get_str(ssh->conf, CONF_termtype));
-       ssh2_pkt_adduint32(pktout, ssh->term_width);
-       ssh2_pkt_adduint32(pktout, ssh->term_height);
-       ssh2_pkt_adduint32(pktout, 0);         /* pixel width */
-       ssh2_pkt_adduint32(pktout, 0);         /* pixel height */
-       ssh2_pkt_addstring_start(pktout);
-       parse_ttymodes(ssh, ssh2_send_ttymode, (void *)pktout);
-       ssh2_pkt_addbyte(pktout, SSH2_TTY_OP_ISPEED);
-       ssh2_pkt_adduint32(pktout, ssh->ispeed);
-       ssh2_pkt_addbyte(pktout, SSH2_TTY_OP_OSPEED);
-       ssh2_pkt_adduint32(pktout, ssh->ospeed);
-       ssh2_pkt_addstring_data(pktout, "\0", 1); /* TTY_OP_END */
-       ssh2_pkt_send(ssh, pktout);
-       ssh->state = SSH_STATE_INTERMED;
-
-       crWaitUntilV(pktin);
+    /* Unpick the terminal-speed string. */
+    /* XXX perhaps we should allow no speeds to be sent. */
+    ssh->ospeed = 38400; ssh->ispeed = 38400; /* last-resort defaults */
+    sscanf(conf_get_str(ssh->conf, CONF_termspeed), "%d,%d", &ssh->ospeed, &ssh->ispeed);
+    /* Build the pty request. */
+    pktout = ssh2_chanreq_init(ssh->mainchan, "pty-req",
+                               ssh2_setup_pty, s);
+    ssh2_pkt_addstring(pktout, conf_get_str(ssh->conf, CONF_termtype));
+    ssh2_pkt_adduint32(pktout, ssh->term_width);
+    ssh2_pkt_adduint32(pktout, ssh->term_height);
+    ssh2_pkt_adduint32(pktout, 0);            /* pixel width */
+    ssh2_pkt_adduint32(pktout, 0);            /* pixel height */
+    ssh2_pkt_addstring_start(pktout);
+    parse_ttymodes(ssh, ssh2_send_ttymode, (void *)pktout);
+    ssh2_pkt_addbyte(pktout, SSH2_TTY_OP_ISPEED);
+    ssh2_pkt_adduint32(pktout, ssh->ispeed);
+    ssh2_pkt_addbyte(pktout, SSH2_TTY_OP_OSPEED);
+    ssh2_pkt_adduint32(pktout, ssh->ospeed);
+    ssh2_pkt_addstring_data(pktout, "\0", 1); /* TTY_OP_END */
+    ssh2_pkt_send(ssh, pktout);
+    ssh->state = SSH_STATE_INTERMED;
+
+    /* Wait to be called back with either a response packet, or NULL
+     * meaning clean up and free our data */
+    crReturnV;
 
-       if (pktin) {
-           if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) {
-               logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",
-                         ssh->ospeed, ssh->ispeed);
-               ssh->got_pty = TRUE;
-           } else {
-               c_write_str(ssh, "Server refused to allocate pty\r\n");
-               ssh->editing = ssh->echoing = 1;
-           }
-       }
-    } else {
-       ssh->editing = ssh->echoing = 1;
+    if (pktin) {
+        if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS) {
+            logeventf(ssh, "Allocated pty (ospeed %dbps, ispeed %dbps)",
+                      ssh->ospeed, ssh->ispeed);
+            ssh->got_pty = TRUE;
+        } else {
+            c_write_str(ssh, "Server refused to allocate pty\r\n");
+            ssh->editing = ssh->echoing = 1;
+        }
     }
+
     crFinishFreeV;
 }
 
@@ -7621,7 +7822,7 @@ static void ssh2_setup_env(struct ssh_channel *c, struct Packet *pktin,
      * then wait for a whole bunch of successes or failures.
      */
     s->num_env = 0;
-    if (ssh->mainchan && !ssh->ncmode) {
+    {
        char *key, *val;
 
        for (val = conf_get_str_strs(ssh->conf, CONF_environmt, NULL, &key);
@@ -7643,7 +7844,9 @@ static void ssh2_setup_env(struct ssh_channel *c, struct Packet *pktin,
        s->env_left = s->num_env;
 
        while (s->env_left > 0) {
-           crWaitUntilV(pktin);
+            /* Wait to be called back with either a response packet,
+             * or NULL meaning clean up and free our data */
+            crReturnV;
            if (!pktin) goto out;
            if (pktin->type == SSH2_MSG_CHANNEL_SUCCESS)
                s->env_ok++;
@@ -7884,13 +8087,53 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                int keyi;
                unsigned char *p;
                p = s->agent_response + 5;
-               s->nkeys = GET_32BIT(p);
+               s->nkeys = toint(GET_32BIT(p));
+
+                /*
+                 * Vet the Pageant response to ensure that the key
+                 * count and blob lengths make sense.
+                 */
+                if (s->nkeys < 0) {
+                    logeventf(ssh, "Pageant response contained a negative"
+                              " key count %d", s->nkeys);
+                    s->nkeys = 0;
+                    goto done_agent_query;
+                } else {
+                    unsigned char *q = p + 4;
+                    int lenleft = s->agent_responselen - 5 - 4;
+
+                    for (keyi = 0; keyi < s->nkeys; keyi++) {
+                        int bloblen, commentlen;
+                        if (lenleft < 4) {
+                            logeventf(ssh, "Pageant response was truncated");
+                            s->nkeys = 0;
+                            goto done_agent_query;
+                        }
+                        bloblen = toint(GET_32BIT(q));
+                        if (bloblen < 0 || bloblen > lenleft) {
+                            logeventf(ssh, "Pageant response was truncated");
+                            s->nkeys = 0;
+                            goto done_agent_query;
+                        }
+                        lenleft -= 4 + bloblen;
+                        q += 4 + bloblen;
+                        commentlen = toint(GET_32BIT(q));
+                        if (commentlen < 0 || commentlen > lenleft) {
+                            logeventf(ssh, "Pageant response was truncated");
+                            s->nkeys = 0;
+                            goto done_agent_query;
+                        }
+                        lenleft -= 4 + commentlen;
+                        q += 4 + commentlen;
+                    }
+                }
+
                p += 4;
                logeventf(ssh, "Pageant has %d SSH-2 keys", s->nkeys);
                if (s->publickey_blob) {
                    /* See if configured key is in agent. */
                    for (keyi = 0; keyi < s->nkeys; keyi++) {
-                       s->pklen = GET_32BIT(p);
+                       s->pklen = toint(GET_32BIT(p));
                        if (s->pklen == s->publickey_bloblen &&
                            !memcmp(p+4, s->publickey_blob,
                                    s->publickey_bloblen)) {
@@ -7901,7 +8144,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                            break;
                        }
                        p += 4 + s->pklen;
-                       p += GET_32BIT(p) + 4; /* comment */
+                       p += toint(GET_32BIT(p)) + 4; /* comment */
                    }
                    if (!s->pkblob_in_agent) {
                        logevent("Configured key file not in Pageant");
@@ -7911,6 +8154,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
            } else {
                 logevent("Failed to get reply from Pageant");
            }
+          done_agent_query:;
        }
 
     }
@@ -8162,13 +8406,13 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                logeventf(ssh, "Trying Pageant key #%d", s->keyi);
 
                /* Unpack key from agent response */
-               s->pklen = GET_32BIT(s->agentp);
+               s->pklen = toint(GET_32BIT(s->agentp));
                s->agentp += 4;
                s->pkblob = (char *)s->agentp;
                s->agentp += s->pklen;
-               s->alglen = GET_32BIT(s->pkblob);
+               s->alglen = toint(GET_32BIT(s->pkblob));
                s->alg = s->pkblob + 4;
-               s->commentlen = GET_32BIT(s->agentp);
+               s->commentlen = toint(GET_32BIT(s->agentp));
                s->agentp += 4;
                s->commentp = (char *)s->agentp;
                s->agentp += s->commentlen;
@@ -8272,7 +8516,9 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                    s->ret = vret;
                    sfree(s->agentreq);
                    if (s->ret) {
-                       if (s->ret[4] == SSH2_AGENT_SIGN_RESPONSE) {
+                       if (s->retlen >= 9 &&
+                            s->ret[4] == SSH2_AGENT_SIGN_RESPONSE &&
+                            GET_32BIT(s->ret + 5) <= (unsigned)(s->retlen-9)) {
                            logevent("Sending Pageant's response");
                            ssh2_add_sigblob(ssh, s->pktout,
                                             s->pkblob, s->pklen,
@@ -8802,10 +9048,8 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                    s->pktout = ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
                    ssh2_pkt_adduint32(s->pktout, s->num_prompts);
                    for (i=0; i < s->num_prompts; i++) {
-                       dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
                        ssh2_pkt_addstring(s->pktout,
                                           s->cur_prompt->prompts[i]->result);
-                       end_log_omission(ssh, s->pktout);
                    }
                    ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
 
@@ -8888,9 +9132,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                                                        /* service requested */
                ssh2_pkt_addstring(s->pktout, "password");
                ssh2_pkt_addbool(s->pktout, FALSE);
-               dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
                ssh2_pkt_addstring(s->pktout, s->password);
-               end_log_omission(ssh, s->pktout);
                ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
                logevent("Sent password");
                s->type = AUTH_TYPE_PASSWORD;
@@ -9017,12 +9259,10 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
                                                        /* service requested */
                    ssh2_pkt_addstring(s->pktout, "password");
                    ssh2_pkt_addbool(s->pktout, TRUE);
-                   dont_log_password(ssh, s->pktout, PKTLOG_BLANK);
                    ssh2_pkt_addstring(s->pktout, s->password);
                    ssh2_pkt_addstring(s->pktout,
                                       s->cur_prompt->prompts[1]->result);
                    free_prompts(s->cur_prompt);
-                   end_log_omission(ssh, s->pktout);
                    ssh2_pkt_send_with_padding(ssh, s->pktout, 256);
                    logevent("Sent new password");
                    
@@ -9119,66 +9359,30 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
      */
     if (conf_get_int(ssh->conf, CONF_ssh_no_shell)) {
        ssh->mainchan = NULL;
-    } else if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) {
-       /*
-        * Just start a direct-tcpip channel and use it as the main
-        * channel.
-        */
+    } else {
        ssh->mainchan = snew(struct ssh_channel);
        ssh->mainchan->ssh = ssh;
        ssh2_channel_init(ssh->mainchan);
-       logeventf(ssh,
-                 "Opening direct-tcpip channel to %s:%d in place of session",
-                 conf_get_str(ssh->conf, CONF_ssh_nc_host),
-                 conf_get_int(ssh->conf, CONF_ssh_nc_port));
-       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
-       ssh2_pkt_addstring(s->pktout, "direct-tcpip");
-       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
-       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */
-       ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT);      /* our max pkt size */
-       ssh2_pkt_addstring(s->pktout, conf_get_str(ssh->conf, CONF_ssh_nc_host));
-       ssh2_pkt_adduint32(s->pktout, conf_get_int(ssh->conf, CONF_ssh_nc_port));
-       /*
-        * There's nothing meaningful to put in the originator
-        * fields, but some servers insist on syntactically correct
-        * information.
-        */
-       ssh2_pkt_addstring(s->pktout, "0.0.0.0");
-       ssh2_pkt_adduint32(s->pktout, 0);
-       ssh2_pkt_send(ssh, s->pktout);
 
-       crWaitUntilV(pktin);
-       if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
-           bombout(("Server refused to open a direct-tcpip channel"));
-           crStopV;
-           /* FIXME: error data comes back in FAILURE packet */
-       }
-       if (ssh_pkt_getuint32(pktin) != ssh->mainchan->localid) {
-           bombout(("Server's channel confirmation cited wrong channel"));
-           crStopV;
+       if (*conf_get_str(ssh->conf, CONF_ssh_nc_host)) {
+           /*
+            * Just start a direct-tcpip channel and use it as the main
+            * channel.
+            */
+           ssh_send_port_open(ssh->mainchan,
+                              conf_get_str(ssh->conf, CONF_ssh_nc_host),
+                              conf_get_int(ssh->conf, CONF_ssh_nc_port),
+                              "main channel");
+           ssh->ncmode = TRUE;
+       } else {
+           s->pktout = ssh2_chanopen_init(ssh->mainchan, "session");
+           logevent("Opening session as main channel");
+           ssh2_pkt_send(ssh, s->pktout);
+           ssh->ncmode = FALSE;
        }
-       ssh->mainchan->remoteid = ssh_pkt_getuint32(pktin);
-       ssh->mainchan->halfopen = FALSE;
-       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 direct-tcpip channel");
-       ssh->ncmode = TRUE;
-    } else {
-       ssh->mainchan = snew(struct ssh_channel);
-       ssh->mainchan->ssh = ssh;
-       ssh2_channel_init(ssh->mainchan);
-       s->pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
-       ssh2_pkt_addstring(s->pktout, "session");
-       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->localid);
-       ssh2_pkt_adduint32(s->pktout, ssh->mainchan->v.v2.locwindow);/* our window size */
-       ssh2_pkt_adduint32(s->pktout, OUR_V2_MAXPKT);    /* our max pkt size */
-       ssh2_pkt_send(ssh, s->pktout);
        crWaitUntilV(pktin);
        if (pktin->type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
-           bombout(("Server refused to open a session"));
+           bombout(("Server refused to open channel"));
            crStopV;
            /* FIXME: error data comes back in FAILURE packet */
        }
@@ -9193,8 +9397,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
        ssh->mainchan->v.v2.remmaxpkt = ssh_pkt_getuint32(pktin);
        add234(ssh->channels, ssh->mainchan);
        update_specials_menu(ssh->frontend);
-       logevent("Opened channel for session");
-       ssh->ncmode = FALSE;
+       logevent("Opened main channel");
     }
 
     /*
@@ -9236,88 +9439,102 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
      */
     ssh_setup_portfwd(ssh, ssh->conf);
 
-    /*
-     * Send the CHANNEL_REQUESTS for the main channel.  Each one is
-     * handled by its own little asynchronous co-routine.
-     */
+    if (ssh->mainchan && !ssh->ncmode) {
+       /*
+        * Send the CHANNEL_REQUESTS for the main session channel.
+        * Each one is handled by its own little asynchronous
+        * co-routine.
+        */
 
-    /*
-     * Potentially enable X11 forwarding.
-     */
-    ssh2_maybe_setup_x11(ssh->mainchan, NULL, NULL);
+       /* Potentially enable X11 forwarding. */
+       if (conf_get_int(ssh->conf, CONF_x11_forward)) {
+            ssh->x11disp =
+                x11_setup_display(conf_get_str(ssh->conf, CONF_x11_display),
+                                  ssh->conf);
+            if (!ssh->x11disp) {
+                /* FIXME: return an error message from x11_setup_display */
+                logevent("X11 forwarding not enabled: unable to"
+                         " initialise X display");
+            } else {
+                ssh->x11auth = x11_invent_fake_auth
+                    (ssh->x11authtree, conf_get_int(ssh->conf, CONF_x11_auth));
+                ssh->x11auth->disp = ssh->x11disp;
+
+                ssh2_setup_x11(ssh->mainchan, NULL, NULL);
+            }
+        }
 
-    /*
-     * Potentially enable agent forwarding.
-     */
-    ssh2_maybe_setup_agent(ssh->mainchan, NULL, NULL);
+       /* Potentially enable agent forwarding. */
+       if (conf_get_int(ssh->conf, CONF_agentfwd) && agent_exists())
+           ssh2_setup_agent(ssh->mainchan, NULL, NULL);
 
-    /*
-     * Now allocate a pty for the session.
-     */
-    ssh2_maybe_setup_pty(ssh->mainchan, NULL, NULL);
+       /* Now allocate a pty for the session. */
+       if (!conf_get_int(ssh->conf, CONF_nopty))
+           ssh2_setup_pty(ssh->mainchan, NULL, NULL);
 
-    /*
-     * Send environment variables.
-     */
-    ssh2_setup_env(ssh->mainchan, NULL, NULL);
+       /* Send environment variables. */
+       ssh2_setup_env(ssh->mainchan, NULL, NULL);
 
-    /*
-     * Start a shell or a remote command. We may have to attempt
-     * this twice if the config data has provided a second choice
-     * of command.
-     */
-    if (ssh->mainchan && !ssh->ncmode) while (1) {
-       int subsys;
-       char *cmd;
+       /*
+        * Start a shell or a remote command. We may have to attempt
+        * this twice if the config data has provided a second choice
+        * of command.
+        */
+       while (1) {
+           int subsys;
+           char *cmd;
 
-       if (ssh->fallback_cmd) {
-           subsys = conf_get_int(ssh->conf, CONF_ssh_subsys2);
-           cmd = conf_get_str(ssh->conf, CONF_remote_cmd2);
-       } else {
-           subsys = conf_get_int(ssh->conf, CONF_ssh_subsys);
-           cmd = conf_get_str(ssh->conf, CONF_remote_cmd);
-       }
+           if (ssh->fallback_cmd) {
+               subsys = conf_get_int(ssh->conf, CONF_ssh_subsys2);
+               cmd = conf_get_str(ssh->conf, CONF_remote_cmd2);
+           } else {
+               subsys = conf_get_int(ssh->conf, CONF_ssh_subsys);
+               cmd = conf_get_str(ssh->conf, CONF_remote_cmd);
+           }
 
-       if (subsys) {
-           s->pktout = ssh2_chanreq_init(ssh->mainchan, "subsystem",
-                                         ssh2_response_authconn, NULL);
-           ssh2_pkt_addstring(s->pktout, cmd);
-       } else if (*cmd) {
-           s->pktout = ssh2_chanreq_init(ssh->mainchan, "exec",
-                                         ssh2_response_authconn, NULL);
-           ssh2_pkt_addstring(s->pktout, cmd);
-       } else {
-           s->pktout = ssh2_chanreq_init(ssh->mainchan, "shell",
-                                         ssh2_response_authconn, NULL);
-       }
-       ssh2_pkt_send(ssh, s->pktout);
+           if (subsys) {
+               s->pktout = ssh2_chanreq_init(ssh->mainchan, "subsystem",
+                                             ssh2_response_authconn, NULL);
+               ssh2_pkt_addstring(s->pktout, cmd);
+           } else if (*cmd) {
+               s->pktout = ssh2_chanreq_init(ssh->mainchan, "exec",
+                                             ssh2_response_authconn, NULL);
+               ssh2_pkt_addstring(s->pktout, cmd);
+           } else {
+               s->pktout = ssh2_chanreq_init(ssh->mainchan, "shell",
+                                             ssh2_response_authconn, NULL);
+           }
+           ssh2_pkt_send(ssh, s->pktout);
 
-       crWaitUntilV(pktin);
+           crWaitUntilV(pktin);
 
-       if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
-           if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
-               bombout(("Unexpected response to shell/command request:"
-                        " packet type %d", pktin->type));
+           if (pktin->type != SSH2_MSG_CHANNEL_SUCCESS) {
+               if (pktin->type != SSH2_MSG_CHANNEL_FAILURE) {
+                   bombout(("Unexpected response to shell/command request:"
+                            " packet type %d", pktin->type));
+                   crStopV;
+               }
+               /*
+                * We failed to start the command. If this is the
+                * fallback command, we really are finished; if it's
+                * not, and if the fallback command exists, try falling
+                * back to it before complaining.
+                */
+               if (!ssh->fallback_cmd &&
+                   *conf_get_str(ssh->conf, CONF_remote_cmd2)) {
+                   logevent("Primary command failed; attempting fallback");
+                   ssh->fallback_cmd = TRUE;
+                   continue;
+               }
+               bombout(("Server refused to start a shell/command"));
                crStopV;
+           } else {
+               logevent("Started a shell/command");
            }
-           /*
-            * We failed to start the command. If this is the
-            * fallback command, we really are finished; if it's
-            * not, and if the fallback command exists, try falling
-            * back to it before complaining.
-            */
-           if (!ssh->fallback_cmd &&
-               *conf_get_str(ssh->conf, CONF_remote_cmd2)) {
-               logevent("Primary command failed; attempting fallback");
-               ssh->fallback_cmd = TRUE;
-               continue;
-           }
-           bombout(("Server refused to start a shell/command"));
-           crStopV;
-       } else {
-           logevent("Started a shell/command");
+           break;
        }
-       break;
+    } else {
+       ssh->editing = ssh->echoing = TRUE;
     }
 
     ssh->state = SSH_STATE_SESSION;
@@ -9326,13 +9543,6 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen,
     if (ssh->eof_needed)
        ssh_special(ssh, TS_EOF);
 
-    /*
-     * All the initial channel requests are done, so install the default
-     * response handler.
-     */
-    ssh->packet_dispatch[SSH2_MSG_CHANNEL_SUCCESS] = ssh2_msg_channel_response;
-    ssh->packet_dispatch[SSH2_MSG_CHANNEL_FAILURE] = ssh2_msg_channel_response;
-
     /*
      * Transfer data!
      */
@@ -9511,7 +9721,7 @@ static void ssh2_protocol_setup(Ssh ssh)
     ssh->packet_dispatch[SSH2_MSG_DEBUG] = ssh2_msg_debug;
 }
 
-static void ssh2_timer(void *ctx, long now)
+static void ssh2_timer(void *ctx, unsigned long now)
 {
     Ssh ssh = (Ssh)ctx;
 
@@ -9519,7 +9729,7 @@ static void ssh2_timer(void *ctx, long now)
        return;
 
     if (!ssh->kex_in_progress && conf_get_int(ssh->conf, CONF_ssh_rekey_time) != 0 &&
-       now - ssh->next_rekey >= 0) {
+       now == ssh->next_rekey) {
        do_ssh2_transport(ssh, "timeout", -1, NULL);
     }
 }
@@ -9587,6 +9797,7 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
     ssh->kex = NULL;
     ssh->kex_ctx = NULL;
     ssh->hostkey = NULL;
+    ssh->hostkey_str = NULL;
     ssh->exitcode = -1;
     ssh->close_expected = FALSE;
     ssh->clean_exit = FALSE;
@@ -9602,6 +9813,8 @@ static const char *ssh_init(void *frontend_handle, void **backend_handle,
     ssh->pkt_kctx = SSH2_PKTCTX_NOKEX;
     ssh->pkt_actx = SSH2_PKTCTX_NOAUTH;
     ssh->x11disp = NULL;
+    ssh->x11auth = NULL;
+    ssh->x11authtree = newtree234(x11_authcmp);
     ssh->v1_compressing = FALSE;
     ssh->v2_outgoing_sequence = 0;
     ssh->ssh1_rdpkt_crstate = 0;
@@ -9680,6 +9893,7 @@ static void ssh_free(void *handle)
     Ssh ssh = (Ssh) handle;
     struct ssh_channel *c;
     struct ssh_rportfwd *pf;
+    struct X11FakeAuth *auth;
 
     if (ssh->v1_cipher_ctx)
        ssh->cipher->free_context(ssh->v1_cipher_ctx);
@@ -9714,7 +9928,7 @@ static void ssh_free(void *handle)
     while (ssh->qhead) {
        struct queued_handler *qh = ssh->qhead;
        ssh->qhead = qh->next;
-       sfree(ssh->qhead);
+       sfree(qh);
     }
     ssh->qhead = ssh->qtail = NULL;
 
@@ -9722,13 +9936,13 @@ static void ssh_free(void *handle)
        while ((c = delpos234(ssh->channels, 0)) != NULL) {
            switch (c->type) {
              case CHAN_X11:
-               if (c->u.x11.s != NULL)
-                   x11_close(c->u.x11.s);
+               if (c->u.x11.xconn != NULL)
+                   x11_close(c->u.x11.xconn);
                break;
              case CHAN_SOCKDATA:
              case CHAN_SOCKDATA_DORMANT:
-               if (c->u.pfd.s != NULL)
-                   pfd_close(c->u.pfd.s);
+               if (c->u.pfd.pf != NULL)
+                   pfd_close(c->u.pfd.pf);
                break;
            }
            if (ssh->version == 2) {
@@ -9757,6 +9971,9 @@ static void ssh_free(void *handle)
     sfree(ssh->deferred_send_data);
     if (ssh->x11disp)
        x11_free_display(ssh->x11disp);
+    while ((auth = delpos234(ssh->x11authtree, 0)) != NULL)
+        x11_free_fake_auth(auth);
+    freetree234(ssh->x11authtree);
     sfree(ssh->do_ssh_init_state);
     sfree(ssh->do_ssh1_login_state);
     sfree(ssh->do_ssh2_transport_state);
@@ -9764,6 +9981,7 @@ static void ssh_free(void *handle)
     sfree(ssh->v_c);
     sfree(ssh->v_s);
     sfree(ssh->fullhostname);
+    sfree(ssh->hostkey_str);
     if (ssh->crcda_ctx) {
        crcda_free_context(ssh->crcda_ctx);
        ssh->crcda_ctx = NULL;
@@ -9802,10 +10020,10 @@ static void ssh_reconfig(void *handle, Conf *conf)
     rekey_time = conf_get_int(conf, CONF_ssh_rekey_time);
     if (conf_get_int(ssh->conf, CONF_ssh_rekey_time) != rekey_time &&
        rekey_time != 0) {
-       long new_next = ssh->last_rekey + rekey_time*60*TICKSPERSEC;
-       long now = GETTICKCOUNT();
+       unsigned long new_next = ssh->last_rekey + rekey_time*60*TICKSPERSEC;
+       unsigned long now = GETTICKCOUNT();
 
-       if (new_next - now < 0) {
+       if (now - ssh->last_rekey > rekey_time*60*TICKSPERSEC) {
            rekeying = "timeout shortened";
        } else {
            ssh->next_rekey = schedule_timer(new_next - now, ssh2_timer, ssh);
@@ -10100,7 +10318,7 @@ static void ssh_special(void *handle, Telnet_Special code)
     }
 }
 
-void *new_sock_channel(void *handle, Socket s)
+void *new_sock_channel(void *handle, struct PortForwarding *pf)
 {
     Ssh ssh = (Ssh) handle;
     struct ssh_channel *c;
@@ -10110,7 +10328,7 @@ void *new_sock_channel(void *handle, Socket s)
     ssh2_channel_init(c);
     c->halfopen = TRUE;
     c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */
-    c->u.pfd.s = s;
+    c->u.pfd.pf = pf;
     add234(ssh->channels, c);
     return c;
 }
@@ -10158,7 +10376,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org)
     Ssh ssh = c->ssh;
     struct Packet *pktout;
 
-    logeventf(ssh, "Opening forwarded connection to %s:%d", hostname, port);
+    logeventf(ssh, "Opening connection to %s:%d for %s", hostname, port, org);
 
     if (ssh->version == 1) {
        send_packet(ssh, SSH1_MSG_PORT_OPEN,
@@ -10168,11 +10386,7 @@ void ssh_send_port_open(void *channel, char *hostname, int port, char *org)
                    /* PKT_STR, <org:orgport>, */
                    PKT_END);
     } else {
-       pktout = ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
-       ssh2_pkt_addstring(pktout, "direct-tcpip");
-       ssh2_pkt_adduint32(pktout, c->localid);
-       ssh2_pkt_adduint32(pktout, c->v.v2.locwindow);/* our window size */
-       ssh2_pkt_adduint32(pktout, OUR_V2_MAXPKT);      /* our max pkt size */
+       pktout = ssh2_chanopen_init(c, "direct-tcpip");
        ssh2_pkt_addstring(pktout, hostname);
        ssh2_pkt_adduint32(pktout, port);
        /*