Major destabilisation, phase 2. This time it's the backends' turn:
authorsimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Fri, 25 Oct 2002 11:30:33 +0000 (11:30 +0000)
committersimon <simon@cda61777-01e9-0310-a592-d414129be87e>
Fri, 25 Oct 2002 11:30:33 +0000 (11:30 +0000)
each backend now stores all its internal variables in a big struct,
and each backend function gets a pointer to this struct passed to
it. This still isn't the end of the work - lots of subsidiary things
still use globals, notably all the cipher and compressor modules and
the X11 forwarding authentication stuff. But ssh.c itself has now
been transformed, and that was the really painful bit, so from here
on it all ought to be a sequence of much smaller and simpler pieces
of work.

git-svn-id: svn://svn.tartarus.org/sgt/putty@2127 cda61777-01e9-0310-a592-d414129be87e

14 files changed:
ldisc.c
plink.c
portfwd.c
psftp.c
putty.h
raw.c
rlogin.c
scp.c
ssh.c
ssh.h
telnet.c
terminal.c
terminal.h
window.c

diff --git a/ldisc.c b/ldisc.c
index a149c6d..d7b6e40 100644 (file)
--- a/ldisc.c
+++ b/ldisc.c
 
 #define ECHOING (cfg.localecho == LD_YES || \
                  (cfg.localecho == LD_BACKEND && \
-                      (back->ldisc(LD_ECHO) || term_ldisc(term, LD_ECHO))))
+                      (back->ldisc(backhandle, LD_ECHO) || \
+                          term_ldisc(term, LD_ECHO))))
 #define EDITING (cfg.localedit == LD_YES || \
                  (cfg.localedit == LD_BACKEND && \
-                      (back->ldisc(LD_EDIT) || term_ldisc(term, LD_EDIT))))
+                      (back->ldisc(backhandle, LD_EDIT) || \
+                          term_ldisc(term, LD_EDIT))))
 
 static void c_write(char *buf, int len)
 {
@@ -138,7 +140,7 @@ void ldisc_send(char *buf, int len, int interactive)
                        bsb(plen(term_buf[term_buflen - 1]));
                    term_buflen--;
                }
-               back->special(TS_EL);
+               back->special(backhandle, TS_EL);
                 /*
                  * We don't send IP, SUSP or ABORT if the user has
                  * configured telnet specials off! This breaks
@@ -147,11 +149,11 @@ void ldisc_send(char *buf, int len, int interactive)
                 if (!cfg.telnet_keyboard)
                     goto default_case;
                if (c == CTRL('C'))
-                   back->special(TS_IP);
+                   back->special(backhandle, TS_IP);
                if (c == CTRL('Z'))
-                   back->special(TS_SUSP);
+                   back->special(backhandle, TS_SUSP);
                if (c == CTRL('\\'))
-                   back->special(TS_ABORT);
+                   back->special(backhandle, TS_ABORT);
                break;
              case CTRL('R'):          /* redraw line */
                if (ECHOING) {
@@ -166,9 +168,9 @@ void ldisc_send(char *buf, int len, int interactive)
                break;
              case CTRL('D'):          /* logout or send */
                if (term_buflen == 0) {
-                   back->special(TS_EOF);
+                   back->special(backhandle, TS_EOF);
                } else {
-                   back->send(term_buf, term_buflen);
+                   back->send(backhandle, term_buf, term_buflen);
                    term_buflen = 0;
                }
                break;
@@ -204,13 +206,13 @@ void ldisc_send(char *buf, int len, int interactive)
                    /* FALLTHROUGH */
              case KCTRL('M'):         /* send with newline */
                    if (term_buflen > 0)
-                       back->send(term_buf, term_buflen);
+                       back->send(backhandle, term_buf, term_buflen);
                    if (cfg.protocol == PROT_RAW)
-                       back->send("\r\n", 2);
+                       back->send(backhandle, "\r\n", 2);
                    else if (cfg.protocol == PROT_TELNET && cfg.telnet_newline)
-                       back->special(TS_EOL);
+                       back->special(backhandle, TS_EOL);
                    else
-                       back->send("\r", 1);
+                       back->send(backhandle, "\r", 1);
                    if (ECHOING)
                        c_write("\r\n", 2);
                    term_buflen = 0;
@@ -232,7 +234,7 @@ void ldisc_send(char *buf, int len, int interactive)
        }
     } else {
        if (term_buflen != 0) {
-           back->send(term_buf, term_buflen);
+           back->send(backhandle, term_buf, term_buflen);
            while (term_buflen > 0) {
                bsb(plen(term_buf[term_buflen - 1]));
                term_buflen--;
@@ -245,33 +247,33 @@ void ldisc_send(char *buf, int len, int interactive)
                switch (buf[0]) {
                  case CTRL('M'):
                    if (cfg.protocol == PROT_TELNET && cfg.telnet_newline)
-                       back->special(TS_EOL);
+                       back->special(backhandle, TS_EOL);
                    else
-                       back->send("\r", 1);
+                       back->send(backhandle, "\r", 1);
                    break;
                  case CTRL('?'):
                  case CTRL('H'):
                    if (cfg.telnet_keyboard) {
-                       back->special(TS_EC);
+                       back->special(backhandle, TS_EC);
                        break;
                    }
                  case CTRL('C'):
                    if (cfg.telnet_keyboard) {
-                       back->special(TS_IP);
+                       back->special(backhandle, TS_IP);
                        break;
                    }
                  case CTRL('Z'):
                    if (cfg.telnet_keyboard) {
-                       back->special(TS_SUSP);
+                       back->special(backhandle, TS_SUSP);
                        break;
                    }
 
                  default:
-                   back->send(buf, len);
+                   back->send(backhandle, buf, len);
                    break;
                }
            } else
-               back->send(buf, len);
+               back->send(backhandle, buf, len);
        }
     }
 }
diff --git a/plink.c b/plink.c
index 5ac8b21..34fe30a 100644 (file)
--- a/plink.c
+++ b/plink.c
@@ -529,7 +529,8 @@ int main(int argc, char **argv)
        int nodelay = cfg.tcp_nodelay &&
            (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR);
 
-       error = back->init(NULL, cfg.host, cfg.port, &realhost, nodelay);
+       error = back->init(NULL, &backhandle, cfg.host, cfg.port,
+                          &realhost, nodelay);
        if (error) {
            fprintf(stderr, "Unable to open connection:\n%s", error);
            return 1;
@@ -585,7 +586,7 @@ int main(int argc, char **argv)
     while (1) {
        int n;
 
-       if (!sending && back->sendok()) {
+       if (!sending && back->sendok(backhandle)) {
            /*
             * Create a separate thread to read from stdin. This is
             * a total pain, but I can't find another way to do it:
@@ -677,11 +678,11 @@ int main(int argc, char **argv)
        } else if (n == 1) {
            reading = 0;
            noise_ultralight(idata.len);
-           if (connopen && back->socket() != NULL) {
+           if (connopen && back->socket(backhandle) != NULL) {
                if (idata.len > 0) {
-                   back->send(idata.buffer, idata.len);
+                   back->send(backhandle, idata.buffer, idata.len);
                } else {
-                   back->special(TS_EOF);
+                   back->special(backhandle, TS_EOF);
                }
            }
        } else if (n == 2) {
@@ -693,8 +694,8 @@ int main(int argc, char **argv)
            bufchain_consume(&stdout_data, odata.lenwritten);
            if (bufchain_size(&stdout_data) > 0)
                try_output(0);
-           if (connopen && back->socket() != NULL) {
-               back->unthrottle(bufchain_size(&stdout_data) +
+           if (connopen && back->socket(backhandle) != NULL) {
+               back->unthrottle(backhandle, bufchain_size(&stdout_data) +
                                 bufchain_size(&stderr_data));
            }
        } else if (n == 3) {
@@ -706,22 +707,22 @@ int main(int argc, char **argv)
            bufchain_consume(&stderr_data, edata.lenwritten);
            if (bufchain_size(&stderr_data) > 0)
                try_output(1);
-           if (connopen && back->socket() != NULL) {
-               back->unthrottle(bufchain_size(&stdout_data) +
+           if (connopen && back->socket(backhandle) != NULL) {
+               back->unthrottle(backhandle, bufchain_size(&stdout_data) +
                                 bufchain_size(&stderr_data));
            }
        }
-       if (!reading && back->sendbuffer() < MAX_STDIN_BACKLOG) {
+       if (!reading && back->sendbuffer(backhandle) < MAX_STDIN_BACKLOG) {
            SetEvent(idata.eventback);
            reading = 1;
        }
-       if ((!connopen || back->socket() == NULL) &&
+       if ((!connopen || back->socket(backhandle) == NULL) &&
            bufchain_size(&stdout_data) == 0 &&
            bufchain_size(&stderr_data) == 0)
            break;                     /* we closed the connection */
     }
     WSACleanup();
-    exitcode = back->exitcode();
+    exitcode = back->exitcode(backhandle);
     if (exitcode < 0) {
        fprintf(stderr, "Remote process exit code unavailable\n");
        exitcode = 1;                  /* this is an error condition */
index 919d27d..e65dd31 100644 (file)
--- a/portfwd.c
+++ b/portfwd.c
@@ -177,7 +177,7 @@ static int pfd_accepting(Plug p, void *sock)
        return err != NULL;
     }
 
-    pr->c = new_sock_channel(s);
+    pr->c = new_sock_channel(backhandle, s);
 
     strcpy(pr->hostname, org->hostname);
     pr->port = org->port;
@@ -192,7 +192,8 @@ static int pfd_accepting(Plug p, void *sock)
        return 1;
     } else {
        /* asks to forward to the specified host/port for this */
-       ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding");
+       ssh_send_port_open(backhandle, pr->c, pr->hostname,
+                          pr->port, "forwarding");
     }
 
     return 0;
diff --git a/psftp.c b/psftp.c
index f3f4c5b..a278260 100644 (file)
--- a/psftp.c
+++ b/psftp.c
@@ -1610,7 +1610,7 @@ int sftp_recvdata(char *buf, int len)
 }
 int sftp_senddata(char *buf, int len)
 {
-    back->send((unsigned char *) buf, len);
+    back->send(backhandle, (unsigned char *) buf, len);
     return 1;
 }
 
@@ -1621,7 +1621,7 @@ static void ssh_sftp_init(void)
 {
     if (sftp_ssh_socket == INVALID_SOCKET)
        return;
-    while (!back->sendok()) {
+    while (!back->sendok(backhandle)) {
        fd_set readfds;
        FD_ZERO(&readfds);
        FD_SET(sftp_ssh_socket, &readfds);
@@ -1822,7 +1822,7 @@ static int psftp_connect(char *userhost, char *user, int portnumber)
 
     back = &ssh_backend;
 
-    err = back->init(NULL, cfg.host, cfg.port, &realhost, 0);
+    err = back->init(NULL, &backhandle, cfg.host, cfg.port, &realhost, 0);
     if (err != NULL) {
        fprintf(stderr, "ssh_init: %s\n", err);
        return 1;
@@ -1921,9 +1921,9 @@ int main(int argc, char *argv[])
 
     do_sftp(mode, modeflags, batchfile);
 
-    if (back != NULL && back->socket() != NULL) {
+    if (back != NULL && back->socket(backhandle) != NULL) {
        char ch;
-       back->special(TS_EOF);
+       back->special(backhandle, TS_EOF);
        sftp_recvdata(&ch, 1);
     }
     WSACleanup();
diff --git a/putty.h b/putty.h
index 3594ceb..6c5a3ad 100644 (file)
--- a/putty.h
+++ b/putty.h
@@ -188,27 +188,28 @@ enum {
 };
 
 struct backend_tag {
-    char *(*init) (void *frontend_handle,
+    char *(*init) (void *frontend_handle, void **backend_handle,
                   char *host, int port, char **realhost, int nodelay);
     /* back->send() returns the current amount of buffered data. */
-    int (*send) (char *buf, int len);
+    int (*send) (void *handle, char *buf, int len);
     /* back->sendbuffer() does the same thing but without attempting a send */
-    int (*sendbuffer) (void);
-    void (*size) (int width, int height);
-    void (*special) (Telnet_Special code);
-    Socket(*socket) (void);
-    int (*exitcode) (void);
-    int (*sendok) (void);
-    int (*ldisc) (int);
+    int (*sendbuffer) (void *handle);
+    void (*size) (void *handle, int width, int height);
+    void (*special) (void *handle, Telnet_Special code);
+    Socket(*socket) (void *handle);
+    int (*exitcode) (void *handle);
+    int (*sendok) (void *handle);
+    int (*ldisc) (void *handle, int);
     /*
      * back->unthrottle() tells the back end that the front end
      * buffer is clearing.
      */
-    void (*unthrottle) (int);
+    void (*unthrottle) (void *handle, int);
     int default_port;
 };
 
 GLOBAL Backend *back;
+GLOBAL void *backhandle;
 
 extern struct backend_list {
     int protocol;
@@ -488,6 +489,9 @@ void term_copyall(Terminal *);
 void term_reconfig(Terminal *);
 void term_seen_key_event(Terminal *); 
 int from_backend(void *, int is_stderr, char *data, int len);
+void term_provide_resize_fn(Terminal *term,
+                           void (*resize_fn)(void *, int, int),
+                           void *resize_ctx);
 
 /*
  * Exports from logging.c.
diff --git a/raw.c b/raw.c
index 90fc8a8..f21e3ca 100644 (file)
--- a/raw.c
+++ b/raw.c
 
 #define RAW_MAX_BACKLOG 4096
 
-static Socket s = NULL;
-static int raw_bufsize;
-static void *frontend;
+typedef struct raw_backend_data {
+    const struct plug_function_table *fn;
+    /* the above field _must_ be first in the structure */
 
-static void raw_size(int width, int height);
+    Socket s;
+    int bufsize;
+    void *frontend;
+} *Raw;
 
-static void c_write(char *buf, int len)
+static void raw_size(void *handle, int width, int height);
+
+static void c_write(Raw raw, char *buf, int len)
 {
-    int backlog = from_backend(frontend, 0, buf, len);
-    sk_set_frozen(s, backlog > RAW_MAX_BACKLOG);
+    int backlog = from_backend(raw->frontend, 0, buf, len);
+    sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG);
 }
 
 static int raw_closing(Plug plug, char *error_msg, int error_code,
                       int calling_back)
 {
-    if (s) {
-        sk_close(s);
-        s = NULL;
+    Raw raw = (Raw) plug;
+
+    if (raw->s) {
+        sk_close(raw->s);
+        raw->s = NULL;
     }
     if (error_msg) {
        /* A socket error has occurred. */
@@ -42,13 +49,15 @@ static int raw_closing(Plug plug, char *error_msg, int error_code,
 
 static int raw_receive(Plug plug, int urgent, char *data, int len)
 {
-    c_write(data, len);
+    Raw raw = (Raw) plug;
+    c_write(raw, data, len);
     return 1;
 }
 
 static void raw_sent(Plug plug, int bufsize)
 {
-    raw_bufsize = bufsize;
+    Raw raw = (Raw) plug;
+    raw->bufsize = bufsize;
 }
 
 /*
@@ -59,19 +68,24 @@ static void raw_sent(Plug plug, int bufsize)
  * Also places the canonical host name into `realhost'. It must be
  * freed by the caller.
  */
-static char *raw_init(void *frontend_handle, char *host, int port,
-                     char **realhost, int nodelay)
+static char *raw_init(void *frontend_handle, void **backend_handle,
+                     char *host, int port, char **realhost, int nodelay)
 {
-    static struct plug_function_table fn_table = {
+    static const struct plug_function_table fn_table = {
        raw_closing,
        raw_receive,
        raw_sent
-    }, *fn_table_ptr = &fn_table;
-
+    };
     SockAddr addr;
     char *err;
+    Raw raw;
 
-    frontend = frontend_handle;
+    raw = smalloc(sizeof(*raw));
+    raw->fn = &fn_table;
+    raw->s = NULL;
+    *backend_handle = raw;
+
+    raw->frontend = frontend_handle;
 
     /*
      * Try to find host.
@@ -97,8 +111,8 @@ static char *raw_init(void *frontend_handle, char *host, int port,
        sprintf(buf, "Connecting to %.100s port %d", addrbuf, port);
        logevent(buf);
     }
-    s = new_connection(addr, *realhost, port, 0, 1, nodelay, &fn_table_ptr);
-    if ((err = sk_socket_error(s)))
+    raw->s = new_connection(addr, *realhost, port, 0, 1, nodelay, (Plug) raw);
+    if ((err = sk_socket_error(raw->s)))
        return err;
 
     sk_addr_free(addr);
@@ -109,28 +123,31 @@ static char *raw_init(void *frontend_handle, char *host, int port,
 /*
  * Called to send data down the raw connection.
  */
-static int raw_send(char *buf, int len)
+static int raw_send(void *handle, char *buf, int len)
 {
-    if (s == NULL)
+    Raw raw = (Raw) handle;
+
+    if (raw->s == NULL)
        return 0;
 
-    raw_bufsize = sk_write(s, buf, len);
+    raw->bufsize = sk_write(raw->s, buf, len);
 
-    return raw_bufsize;
+    return raw->bufsize;
 }
 
 /*
  * Called to query the current socket sendability status.
  */
-static int raw_sendbuffer(void)
+static int raw_sendbuffer(void *handle)
 {
-    return raw_bufsize;
+    Raw raw = (Raw) handle;
+    return raw->bufsize;
 }
 
 /*
  * Called to set the size of the window
  */
-static void raw_size(int width, int height)
+static void raw_size(void *handle, int width, int height)
 {
     /* Do nothing! */
     return;
@@ -139,35 +156,37 @@ static void raw_size(int width, int height)
 /*
  * Send raw special codes.
  */
-static void raw_special(Telnet_Special code)
+static void raw_special(void *handle, Telnet_Special code)
 {
     /* Do nothing! */
     return;
 }
 
-static Socket raw_socket(void)
+static Socket raw_socket(void *handle)
 {
-    return s;
+    Raw raw = (Raw) handle;
+    return raw->s;
 }
 
-static int raw_sendok(void)
+static int raw_sendok(void *handle)
 {
     return 1;
 }
 
-static void raw_unthrottle(int backlog)
+static void raw_unthrottle(void *handle, int backlog)
 {
-    sk_set_frozen(s, backlog > RAW_MAX_BACKLOG);
+    Raw raw = (Raw) handle;
+    sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG);
 }
 
-static int raw_ldisc(int option)
+static int raw_ldisc(void *handle, int option)
 {
     if (option == LD_EDIT || option == LD_ECHO)
        return 1;
     return 0;
 }
 
-static int raw_exitcode(void)
+static int raw_exitcode(void *handle)
 {
     /* Exit codes are a meaningless concept in the Raw protocol */
     return 0;
index 74e8268..52cf356 100644 (file)
--- a/rlogin.c
+++ b/rlogin.c
 
 #define RLOGIN_MAX_BACKLOG 4096
 
-static Socket s = NULL;
-static int rlogin_bufsize;
-static int rlogin_term_width, rlogin_term_height;
-static void *frontend;
+typedef struct rlogin_tag {
+    const struct plug_function_table *fn;
+    /* the above field _must_ be first in the structure */
 
-static void rlogin_size(int width, int height);
+    Socket s;
+    int bufsize;
+    int term_width, term_height;
+    void *frontend;
+} *Rlogin;
 
-static void c_write(char *buf, int len)
+static void rlogin_size(void *handle, int width, int height);
+
+static void c_write(Rlogin rlogin, char *buf, int len)
 {
-    int backlog = from_backend(frontend, 0, buf, len);
-    sk_set_frozen(s, backlog > RLOGIN_MAX_BACKLOG);
+    int backlog = from_backend(rlogin->frontend, 0, buf, len);
+    sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);
 }
 
 static int rlogin_closing(Plug plug, char *error_msg, int error_code,
                          int calling_back)
 {
-    if (s) {
-        sk_close(s);
-        s = NULL;
+    Rlogin rlogin = (Rlogin) plug;
+    if (rlogin->s) {
+        sk_close(rlogin->s);
+        rlogin->s = NULL;
     }
     if (error_msg) {
        /* A socket error has occurred. */
@@ -44,13 +50,14 @@ static int rlogin_closing(Plug plug, char *error_msg, int error_code,
 
 static int rlogin_receive(Plug plug, int urgent, char *data, int len)
 {
+    Rlogin rlogin = (Rlogin) plug;
     if (urgent == 2) {
        char c;
 
        c = *data++;
        len--;
        if (c == '\x80')
-           rlogin_size(rlogin_term_width, rlogin_term_height);
+           rlogin_size(rlogin, rlogin->term_width, rlogin->term_height);
        /*
         * We should flush everything (aka Telnet SYNCH) if we see
         * 0x02, and we should turn off and on _local_ flow control
@@ -72,14 +79,15 @@ static int rlogin_receive(Plug plug, int urgent, char *data, int len)
            firstbyte = 0;
        }
        if (len > 0)
-            c_write(data, len);
+            c_write(rlogin, data, len);
     }
     return 1;
 }
 
 static void rlogin_sent(Plug plug, int bufsize)
 {
-    rlogin_bufsize = bufsize;
+    Rlogin rlogin = (Rlogin) plug;
+    rlogin->bufsize = bufsize;
 }
 
 /*
@@ -90,21 +98,25 @@ static void rlogin_sent(Plug plug, int bufsize)
  * Also places the canonical host name into `realhost'. It must be
  * freed by the caller.
  */
-static char *rlogin_init(void *frontend_handle,
+static char *rlogin_init(void *frontend_handle, void **backend_handle,
                         char *host, int port, char **realhost, int nodelay)
 {
-    static struct plug_function_table fn_table = {
+    static const struct plug_function_table fn_table = {
        rlogin_closing,
        rlogin_receive,
        rlogin_sent
-    }, *fn_table_ptr = &fn_table;
-
+    };
     SockAddr addr;
     char *err;
+    Rlogin rlogin;
 
-    frontend = frontend_handle;
-    rlogin_term_width = cfg.width;
-    rlogin_term_height = cfg.height;
+    rlogin = smalloc(sizeof(*rlogin));
+    rlogin->fn = &fn_table;
+    rlogin->s = NULL;
+    rlogin->frontend = frontend_handle;
+    rlogin->term_width = cfg.width;
+    rlogin->term_height = cfg.height;
+    *backend_handle = rlogin;
 
     /*
      * Try to find host.
@@ -130,8 +142,9 @@ static char *rlogin_init(void *frontend_handle,
        sprintf(buf, "Connecting to %.100s port %d", addrbuf, port);
        logevent(buf);
     }
-    s = new_connection(addr, *realhost, port, 1, 0, nodelay, &fn_table_ptr);
-    if ((err = sk_socket_error(s)))
+    rlogin->s = new_connection(addr, *realhost, port, 1, 0,
+                              nodelay, (Plug) rlogin);
+    if ((err = sk_socket_error(rlogin->s)))
        return err;
 
     sk_addr_free(addr);
@@ -143,16 +156,16 @@ static char *rlogin_init(void *frontend_handle,
     {
        char z = 0;
        char *p;
-       sk_write(s, &z, 1);
-       sk_write(s, cfg.localusername, strlen(cfg.localusername));
-       sk_write(s, &z, 1);
-       sk_write(s, cfg.username, strlen(cfg.username));
-       sk_write(s, &z, 1);
-       sk_write(s, cfg.termtype, strlen(cfg.termtype));
-       sk_write(s, "/", 1);
+       sk_write(rlogin->s, &z, 1);
+       sk_write(rlogin->s, cfg.localusername, strlen(cfg.localusername));
+       sk_write(rlogin->s, &z, 1);
+       sk_write(rlogin->s, cfg.username, strlen(cfg.username));
+       sk_write(rlogin->s, &z, 1);
+       sk_write(rlogin->s, cfg.termtype, strlen(cfg.termtype));
+       sk_write(rlogin->s, "/", 1);
        for (p = cfg.termspeed; isdigit(*p); p++);
-       sk_write(s, cfg.termspeed, p - cfg.termspeed);
-       rlogin_bufsize = sk_write(s, &z, 1);
+       sk_write(rlogin->s, cfg.termspeed, p - cfg.termspeed);
+       rlogin->bufsize = sk_write(rlogin->s, &z, 1);
     }
 
     return NULL;
@@ -161,76 +174,85 @@ static char *rlogin_init(void *frontend_handle,
 /*
  * Called to send data down the rlogin connection.
  */
-static int rlogin_send(char *buf, int len)
+static int rlogin_send(void *handle, char *buf, int len)
 {
-    if (s == NULL)
+    Rlogin rlogin = (Rlogin) handle;
+
+    if (rlogin->s == NULL)
        return 0;
 
-    rlogin_bufsize = sk_write(s, buf, len);
+    rlogin->bufsize = sk_write(rlogin->s, buf, len);
 
-    return rlogin_bufsize;
+    return rlogin->bufsize;
 }
 
 /*
  * Called to query the current socket sendability status.
  */
-static int rlogin_sendbuffer(void)
+static int rlogin_sendbuffer(void *handle)
 {
-    return rlogin_bufsize;
+    Rlogin rlogin = (Rlogin) handle;
+    return rlogin->bufsize;
 }
 
 /*
  * Called to set the size of the window
  */
-static void rlogin_size(int width, int height)
+static void rlogin_size(void *handle, int width, int height)
 {
+    Rlogin rlogin = (Rlogin) handle;
     char b[12] = { '\xFF', '\xFF', 0x73, 0x73, 0, 0, 0, 0, 0, 0, 0, 0 };
 
-    rlogin_term_width = width;
-    rlogin_term_height = height;
+    rlogin->term_width = width;
+    rlogin->term_height = height;
 
-    if (s == NULL)
+    if (rlogin->s == NULL)
        return;
 
-    b[6] = rlogin_term_width >> 8;
-    b[7] = rlogin_term_width & 0xFF;
-    b[4] = rlogin_term_height >> 8;
-    b[5] = rlogin_term_height & 0xFF;
-    rlogin_bufsize = sk_write(s, b, 12);
+    b[6] = rlogin->term_width >> 8;
+    b[7] = rlogin->term_width & 0xFF;
+    b[4] = rlogin->term_height >> 8;
+    b[5] = rlogin->term_height & 0xFF;
+    rlogin->bufsize = sk_write(rlogin->s, b, 12);
     return;
 }
 
 /*
  * Send rlogin special codes.
  */
-static void rlogin_special(Telnet_Special code)
+static void rlogin_special(void *handle, Telnet_Special code)
 {
     /* Do nothing! */
     return;
 }
 
-static Socket rlogin_socket(void)
+static Socket rlogin_socket(void *handle)
 {
-    return s;
+    Rlogin rlogin = (Rlogin) handle;
+    return rlogin->s;
 }
 
-static int rlogin_sendok(void)
+static int rlogin_sendok(void *handle)
 {
+    Rlogin rlogin = (Rlogin) handle;
     return 1;
 }
 
-static void rlogin_unthrottle(int backlog)
+static void rlogin_unthrottle(void *handle, int backlog)
 {
-    sk_set_frozen(s, backlog > RLOGIN_MAX_BACKLOG);
+    Rlogin rlogin = (Rlogin) handle;
+    sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG);
 }
 
-static int rlogin_ldisc(int option)
+static int rlogin_ldisc(void *handle, int option)
 {
+    Rlogin rlogin = (Rlogin) handle;
     return 0;
 }
 
-static int rlogin_exitcode(void)
+static int rlogin_exitcode(void *handle)
 {
+    Rlogin rlogin = (Rlogin) handle;
     /* If we ever implement RSH, we'll probably need to do this properly */
     return 0;
 }
diff --git a/scp.c b/scp.c
index ec3575b..25f29b4 100644 (file)
--- a/scp.c
+++ b/scp.c
@@ -408,7 +408,7 @@ static void ssh_scp_init(void)
 {
     if (scp_ssh_socket == INVALID_SOCKET)
        return;
-    while (!back->sendok()) {
+    while (!back->sendok(backhandle)) {
        fd_set readfds;
        FD_ZERO(&readfds);
        FD_SET(scp_ssh_socket, &readfds);
@@ -416,7 +416,7 @@ static void ssh_scp_init(void)
            return;                    /* doom */
        select_result((WPARAM) scp_ssh_socket, (LPARAM) FD_READ);
     }
-    using_sftp = !ssh_fallback_cmd;
+    using_sftp = !ssh_fallback_cmd(backhandle);
 }
 
 /*
@@ -434,9 +434,9 @@ static void bump(char *fmt, ...)
     tell_str(stderr, str);
     errs++;
 
-    if (back != NULL && back->socket() != NULL) {
+    if (back != NULL && back->socket(backhandle) != NULL) {
        char ch;
-       back->special(TS_EOF);
+       back->special(backhandle, TS_EOF);
        ssh_scp_recv(&ch, 1);
     }
 
@@ -565,7 +565,7 @@ static void do_cmd(char *host, char *user, char *cmd)
 
     back = &ssh_backend;
 
-    err = back->init(NULL, cfg.host, cfg.port, &realhost, 0);
+    err = back->init(NULL, &backhandle, cfg.host, cfg.port, &realhost, 0);
     if (err != NULL)
        bump("ssh_init: %s", err);
     ssh_scp_init();
@@ -712,7 +712,7 @@ int sftp_recvdata(char *buf, int len)
 }
 int sftp_senddata(char *buf, int len)
 {
-    back->send((unsigned char *) buf, len);
+    back->send(backhandle, (unsigned char *) buf, len);
     return 1;
 }
 
@@ -824,7 +824,7 @@ void scp_source_setup(char *target, int shouldbedir)
        if (!fxp_init()) {
            tell_user(stderr, "unable to initialise SFTP: %s", fxp_error());
            errs++;
-           return 1;
+           return;
        }
 
        if (!fxp_stat(target, &attrs) ||
@@ -850,8 +850,8 @@ int scp_send_errmsg(char *str)
     if (using_sftp) {
        /* do nothing; we never need to send our errors to the server */
     } else {
-       back->send("\001", 1);         /* scp protocol error prefix */
-       back->send(str, strlen(str));
+       back->send(backhandle, "\001", 1);/* scp protocol error prefix */
+       back->send(backhandle, str, strlen(str));
     }
     return 0;                         /* can't fail */
 }
@@ -866,7 +866,7 @@ int scp_send_filetimes(unsigned long mtime, unsigned long atime)
     } else {
        char buf[80];
        sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime);
-       back->send(buf, strlen(buf));
+       back->send(backhandle, buf, strlen(buf));
        return response();
     }
 }
@@ -894,9 +894,9 @@ int scp_send_filename(char *name, unsigned long size, int modes)
     } else {
        char buf[40];
        sprintf(buf, "C%04o %lu ", modes, size);
-       back->send(buf, strlen(buf));
-       back->send(name, strlen(name));
-       back->send("\n", 1);
+       back->send(backhandle, buf, strlen(buf));
+       back->send(backhandle, name, strlen(name));
+       back->send(backhandle, "\n", 1);
        return response();
     }
 }
@@ -915,7 +915,7 @@ int scp_send_filedata(char *data, int len)
        scp_sftp_fileoffset = uint64_add32(scp_sftp_fileoffset, len);
        return 0;
     } else {
-       int bufsize = back->send(data, len);
+       int bufsize = back->send(backhandle, data, len);
 
        /*
         * If the network transfer is backing up - that is, the
@@ -926,7 +926,7 @@ int scp_send_filedata(char *data, int len)
        while (bufsize > MAX_SCP_BUFSIZE) {
            if (!scp_process_network_event())
                return 1;
-           bufsize = back->sendbuffer();
+           bufsize = back->sendbuffer(backhandle);
        }
 
        return 0;
@@ -953,7 +953,7 @@ int scp_send_finish(void)
        scp_has_times = 0;
        return 0;
     } else {
-       back->send("", 1);
+       back->send(backhandle, "", 1);
        return response();
     }
 }
@@ -1010,9 +1010,9 @@ int scp_send_dirname(char *name, int modes)
     } else {
        char buf[40];
        sprintf(buf, "D%04o 0 ", modes);
-       back->send(buf, strlen(buf));
-       back->send(name, strlen(name));
-       back->send("\n", 1);
+       back->send(backhandle, buf, strlen(buf));
+       back->send(backhandle, name, strlen(name));
+       back->send(backhandle, "\n", 1);
        return response();
     }
 }
@@ -1023,7 +1023,7 @@ int scp_send_enddir(void)
        sfree(scp_sftp_remotepath);
        return 0;
     } else {
-       back->send("E\n", 2);
+       back->send(backhandle, "E\n", 2);
        return response();
     }
 }
@@ -1118,7 +1118,7 @@ int scp_sink_setup(char *source, int preserve, int recursive)
 int scp_sink_init(void)
 {
     if (!using_sftp) {
-       back->send("", 1);
+       back->send(backhandle, "", 1);
     }
     return 0;
 }
@@ -1404,14 +1404,14 @@ int scp_get_sink_action(struct scp_sink_action *act)
              case '\02':                      /* fatal error */
                bump("%s", act->buf);
              case 'E':
-               back->send("", 1);
+               back->send(backhandle, "", 1);
                act->action = SCP_SINK_ENDDIR;
                return 0;
              case 'T':
                if (sscanf(act->buf, "%ld %*d %ld %*d",
                           &act->mtime, &act->atime) == 2) {
                    act->settime = 1;
-                   back->send("", 1);
+                   back->send(backhandle, "", 1);
                    continue;          /* go round again */
                }
                bump("Protocol error: Illegal time format");
@@ -1455,7 +1455,7 @@ int scp_accept_filexfer(void)
        sfree(scp_sftp_currentname);
        return 0;
     } else {
-       back->send("", 1);
+       back->send(backhandle, "", 1);
        return 0;                      /* can't fail */
     }
 }
@@ -1487,7 +1487,7 @@ int scp_finish_filerecv(void)
        fxp_close(scp_sftp_filehandle);
        return 0;
     } else {
-       back->send("", 1);
+       back->send(backhandle, "", 1);
        return response();
     }
 }
@@ -2234,9 +2234,9 @@ int main(int argc, char *argv[])
            tolocal(argc, argv);
     }
 
-    if (back != NULL && back->socket() != NULL) {
+    if (back != NULL && back->socket(backhandle) != NULL) {
        char ch;
-       back->special(TS_EOF);
+       back->special(backhandle, TS_EOF);
        ssh_scp_recv(&ch, 1);
     }
     WSACleanup();
diff --git a/ssh.c b/ssh.c
index 258fd89..115e902 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -31,8 +31,8 @@ void logeventf(char *fmt, ...)
     logevent(stuff);
 }
 
-#define bombout(msg) ( ssh_state = SSH_STATE_CLOSED, \
-                          (s ? sk_close(s), s = NULL : 0), \
+#define bombout(msg) ( ssh->state = SSH_STATE_CLOSED, \
+                          (ssh->s ? sk_close(ssh->s), ssh->s = NULL : 0), \
                           logeventf msg, connection_fatal msg )
 
 #define SSH1_MSG_DISCONNECT                       1    /* 0x1 */
@@ -185,10 +185,8 @@ static const char *const ssh2_disconnect_reasons[] = {
 #define BUG_SSH2_DERIVEKEY                       32
 #define BUG_SSH2_DH_GEX                          64
 
-static int ssh_pkt_ctx = 0;
-
 #define translate(x) if (type == x) return #x
-#define translatec(x,ctx) if (type == x && (ssh_pkt_ctx & ctx)) return #x
+#define translatec(x,ctx) if (type == x && (pkt_ctx & ctx)) return #x
 char *ssh1_pkt_type(int type)
 {
     translate(SSH1_MSG_DISCONNECT);
@@ -234,7 +232,7 @@ char *ssh1_pkt_type(int type)
     translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);
     return "unknown";
 }
-char *ssh2_pkt_type(int type)
+char *ssh2_pkt_type(int pkt_ctx, int type)
 {
     translate(SSH2_MSG_DISCONNECT);
     translate(SSH2_MSG_IGNORE);
@@ -292,24 +290,28 @@ char *ssh2_pkt_type(int type)
 enum { PKT_END, PKT_INT, PKT_CHAR, PKT_DATA, PKT_STR, PKT_BIGNUM };
 
 /* Coroutine mechanics for the sillier bits of the code */
-#define crBegin1       static int crLine = 0;
-#define crBegin2       switch(crLine) { case 0:;
-#define crBegin                crBegin1; crBegin2;
-#define crFinish(z)    } crLine = 0; return (z)
-#define crFinishV      } crLine = 0; return
+#define crBegin(v)     { int *crLine = &v; switch(v) { case 0:;
+#define crState(t) \
+    struct t *s; \
+    if (!ssh->t) ssh->t = smalloc(sizeof(struct t)); \
+    s = ssh->t;
+#define crFinish(z)    } *crLine = 0; return (z); }
+#define crFinishV      } *crLine = 0; return; }
 #define crReturn(z)    \
        do {\
-           crLine=__LINE__; return (z); case __LINE__:;\
+           *crLine =__LINE__; return (z); case __LINE__:;\
        } while (0)
 #define crReturnV      \
        do {\
-           crLine=__LINE__; return; case __LINE__:;\
+           *crLine=__LINE__; return; case __LINE__:;\
        } while (0)
-#define crStop(z)      do{ crLine = 0; return (z); }while(0)
-#define crStopV                do{ crLine = 0; return; }while(0)
+#define crStop(z)      do{ *crLine = 0; return (z); }while(0)
+#define crStopV                do{ *crLine = 0; return; }while(0)
 #define crWaitUntil(c) do { crReturn(0); } while (!(c))
 #define crWaitUntilV(c)        do { crReturnV; } while (!(c))
 
+typedef struct ssh_tag *Ssh;
+
 extern char *x11_init(Socket *, char *, void *);
 extern void x11_close(Socket);
 extern int x11_send(Socket, char *, int);
@@ -325,17 +327,17 @@ extern void pfd_confirm(Socket s);
 extern void pfd_unthrottle(Socket s);
 extern void pfd_override_throttle(Socket s, int enable);
 
-static void ssh2_pkt_init(int pkt_type);
-static void ssh2_pkt_addbool(unsigned char value);
-static void ssh2_pkt_adduint32(unsigned long value);
-static void ssh2_pkt_addstring_start(void);
-static void ssh2_pkt_addstring_str(char *data);
-static void ssh2_pkt_addstring_data(char *data, int len);
-static void ssh2_pkt_addstring(char *data);
+static void ssh2_pkt_init(Ssh, int pkt_type);
+static void ssh2_pkt_addbool(Ssh, unsigned char value);
+static void ssh2_pkt_adduint32(Ssh, unsigned long value);
+static void ssh2_pkt_addstring_start(Ssh);
+static void ssh2_pkt_addstring_str(Ssh, char *data);
+static void ssh2_pkt_addstring_data(Ssh, char *data, int len);
+static void ssh2_pkt_addstring(Ssh, char *data);
 static char *ssh2_mpint_fmt(Bignum b, int *len);
-static void ssh2_pkt_addmp(Bignum b);
-static int ssh2_pkt_construct(void);
-static void ssh2_pkt_send(void);
+static void ssh2_pkt_addmp(Ssh, Bignum b);
+static int ssh2_pkt_construct(Ssh);
+static void ssh2_pkt_send(Ssh);
 
 /*
  * Buffer management constants. There are several of these for
@@ -425,6 +427,7 @@ enum {                                     /* channel types */
  * 2-3-4 tree storing channels.
  */
 struct ssh_channel {
+    Ssh ssh;                          /* pointer back to main context */
     unsigned remoteid, localid;
     int type;
     /*
@@ -506,97 +509,130 @@ struct Packet {
     long maxlen;
 };
 
-static SHA_State exhash, exhashbase;
-
-static Socket s = NULL;
-
-static unsigned char session_key[32];
-static int ssh1_compressing;
-static int ssh1_remote_protoflags;
-static int ssh1_local_protoflags;
-static int ssh_agentfwd_enabled;
-static int ssh_X11_fwd_enabled;
-static int ssh_remote_bugs;
-static const struct ssh_cipher *cipher = NULL;
-static const struct ssh2_cipher *cscipher = NULL;
-static const struct ssh2_cipher *sccipher = NULL;
-static const struct ssh_mac *csmac = NULL;
-static const struct ssh_mac *scmac = NULL;
-static const struct ssh_compress *cscomp = NULL;
-static const struct ssh_compress *sccomp = NULL;
-static const struct ssh_kex *kex = NULL;
-static const struct ssh_signkey *hostkey = NULL;
-static unsigned char ssh2_session_id[20];
-
-static char *savedhost;
-static int savedport;
-static int ssh_send_ok;
-static int ssh_echoing, ssh_editing;
-
-static void *frontend;
-
-static int ssh_term_width, ssh_term_height;
-
-static tree234 *ssh_channels;         /* indexed by local id */
-static struct ssh_channel *mainchan;   /* primary session channel */
-static int ssh_exitcode = -1;
-
-static tree234 *ssh_rportfwds;
-
-static enum {
-    SSH_STATE_PREPACKET,
-    SSH_STATE_BEFORE_SIZE,
-    SSH_STATE_INTERMED,
-    SSH_STATE_SESSION,
-    SSH_STATE_CLOSED
-} ssh_state = SSH_STATE_PREPACKET;
-
-static int size_needed = FALSE, eof_needed = FALSE;
-
-static struct Packet pktin = { 0, 0, NULL, NULL, 0 };
-static struct Packet pktout = { 0, 0, NULL, NULL, 0 };
-static unsigned char *deferred_send_data = NULL;
-static int deferred_len = 0, deferred_size = 0;
-
-/*
- * Gross hack: pscp will try to start SFTP but fall back to scp1 if
- * that fails. This variable is the means by which scp.c can reach
- * into the SSH code and find out which one it got.
- */
-int ssh_fallback_cmd = 0;
-
-static int ssh_version;
-static int ssh1_throttle_count;
-static int ssh_overall_bufsize;
-static int ssh_throttled_all;
-static int ssh1_stdout_throttling;
-static void (*ssh_protocol) (unsigned char *in, int inlen, int ispkt);
-static void ssh1_protocol(unsigned char *in, int inlen, int ispkt);
-static void ssh2_protocol(unsigned char *in, int inlen, int ispkt);
-static void ssh_size(int width, int height);
-static void ssh_special(Telnet_Special);
+static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt);
+static void ssh2_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt);
+static void ssh_size(void *handle, int width, int height);
+static void ssh_special(void *handle, Telnet_Special);
 static int ssh2_try_send(struct ssh_channel *c);
-static void ssh2_add_channel_data(struct ssh_channel *c, char *buf,
-                                 int len);
-static void ssh_throttle_all(int enable, int bufsize);
+static void ssh2_add_channel_data(struct ssh_channel *c, char *buf, int len);
+static void ssh_throttle_all(Ssh ssh, int enable, int bufsize);
 static void ssh2_set_window(struct ssh_channel *c, unsigned newwin);
-static int (*s_rdpkt) (unsigned char **data, int *datalen);
-static int ssh_sendbuffer(void);
+static int ssh_sendbuffer(void *handle);
 
-static struct rdpkt1_state_tag {
+struct rdpkt1_state_tag {
     long len, pad, biglen, to_read;
     unsigned long realcrc, gotcrc;
     unsigned char *p;
     int i;
     int chunk;
-} rdpkt1_state;
+};
 
-static struct rdpkt2_state_tag {
+struct rdpkt2_state_tag {
     long len, pad, payload, packetlen, maclen;
     int i;
     int cipherblk;
     unsigned long incoming_sequence;
-} rdpkt2_state;
+};
+
+struct ssh_tag {
+    const struct plug_function_table *fn;
+    /* the above field _must_ be first in the structure */
+
+    SHA_State exhash, exhashbase;
+
+    Socket s;
+
+    unsigned char session_key[32];
+    int v1_compressing;
+    int v1_remote_protoflags;
+    int v1_local_protoflags;
+    int agentfwd_enabled;
+    int X11_fwd_enabled;
+    int remote_bugs;
+    const struct ssh_cipher *cipher;
+    const struct ssh2_cipher *cscipher, *sccipher;
+    const struct ssh_mac *csmac, *scmac;
+    const struct ssh_compress *cscomp, *sccomp;
+    const struct ssh_kex *kex;
+    const struct ssh_signkey *hostkey;
+    unsigned char v2_session_id[20];
+
+    char *savedhost;
+    int savedport;
+    int send_ok;
+    int echoing, editing;
+
+    void *frontend;
+
+    int term_width, term_height;
+
+    tree234 *channels;                /* indexed by local id */
+    struct ssh_channel *mainchan;      /* primary session channel */
+    int exitcode;
+
+    tree234 *rportfwds;
+
+    enum {
+       SSH_STATE_PREPACKET,
+       SSH_STATE_BEFORE_SIZE,
+       SSH_STATE_INTERMED,
+       SSH_STATE_SESSION,
+       SSH_STATE_CLOSED
+    } state;
+
+    int size_needed, eof_needed;
+
+    struct Packet pktin;
+    struct Packet pktout;
+    unsigned char *deferred_send_data;
+    int deferred_len, deferred_size;
+
+    /*
+     * Gross hack: pscp will try to start SFTP but fall back to
+     * scp1 if that fails. This variable is the means by which
+     * scp.c can reach into the SSH code and find out which one it
+     * got.
+     */
+    int fallback_cmd;
+
+    /*
+     * Used for username and password input.
+     */
+    char *userpass_input_buffer;
+    int userpass_input_buflen;
+    int userpass_input_bufpos;
+    int userpass_input_echo;
+
+    char *portfwd_strptr;
+    int pkt_ctx;
+
+    int version;
+    int v1_throttle_count;
+    int overall_bufsize;
+    int throttled_all;
+    int v1_stdout_throttling;
+    int v2_outgoing_sequence;
+
+    int ssh1_rdpkt_crstate;
+    int ssh2_rdpkt_crstate;
+    int do_ssh_init_crstate;
+    int ssh_gotdata_crstate;
+    int ssh1_protocol_crstate;
+    int do_ssh1_login_crstate;
+    int do_ssh2_transport_crstate;
+    int do_ssh2_authconn_crstate;
+
+    void *do_ssh_init_state;
+    void *do_ssh1_login_state;
+    void *do_ssh2_transport_state;
+    void *do_ssh2_authconn_state;
+
+    struct rdpkt1_state_tag rdpkt1_state;
+    struct rdpkt2_state_tag rdpkt2_state;
+
+    void (*protocol) (Ssh ssh, unsigned char *in, int inlen, int ispkt);
+    int (*s_rdpkt) (Ssh ssh, unsigned char **data, int *datalen);
+};
 
 static int ssh_channelcmp(void *av, void *bv)
 {
@@ -645,7 +681,7 @@ static int ssh_rportcmp_ssh2(void *av, void *bv)
     return 0;
 }
 
-static int alloc_channel_id(void)
+static int alloc_channel_id(Ssh ssh)
 {
     const unsigned CHANNEL_NUMBER_OFFSET = 256;
     unsigned low, high, mid;
@@ -660,13 +696,13 @@ static int alloc_channel_id(void)
      * everything in that sequence must have ID equal to its tree
      * index plus CHANNEL_NUMBER_OFFSET.)
      */
-    tsize = count234(ssh_channels);
+    tsize = count234(ssh->channels);
 
     low = -1;
     high = tsize;
     while (high - low > 1) {
        mid = (high + low) / 2;
-       c = index234(ssh_channels, mid);
+       c = index234(ssh->channels, mid);
        if (c->localid == mid + CHANNEL_NUMBER_OFFSET)
            low = mid;                 /* this one is fine */
        else
@@ -678,12 +714,12 @@ static int alloc_channel_id(void)
      */
     {
        unsigned i = low + 1 + CHANNEL_NUMBER_OFFSET;
-       assert(NULL == find234(ssh_channels, &i, ssh_channelfind));
+       assert(NULL == find234(ssh->channels, &i, ssh_channelfind));
     }
     return low + 1 + CHANNEL_NUMBER_OFFSET;
 }
 
-static void c_write(char *buf, int len)
+static void c_write(Ssh ssh, char *buf, int len)
 {
     if ((flags & FLAG_STDERR)) {
        int i;
@@ -692,23 +728,23 @@ static void c_write(char *buf, int len)
                fputc(buf[i], stderr);
        return;
     }
-    from_backend(frontend, 1, buf, len);
+    from_backend(ssh->frontend, 1, buf, len);
 }
 
-static void c_write_untrusted(char *buf, int len)
+static void c_write_untrusted(Ssh ssh, char *buf, int len)
 {
     int i;
     for (i = 0; i < len; i++) {
        if (buf[i] == '\n')
-           c_write("\r\n", 2);
+           c_write(ssh, "\r\n", 2);
        else if ((buf[i] & 0x60) || (buf[i] == '\r'))
-           c_write(buf + i, 1);
+           c_write(ssh, buf + i, 1);
     }
 }
 
-static void c_write_str(char *buf)
+static void c_write_str(Ssh ssh, char *buf)
 {
-    c_write(buf, strlen(buf));
+    c_write(ssh, buf, strlen(buf));
 }
 
 /*
@@ -719,16 +755,16 @@ static void c_write_str(char *buf)
  * Return the additional nr of bytes needed, or 0 when
  * a complete packet is available.
  */
-static int ssh1_rdpkt(unsigned char **data, int *datalen)
+static int ssh1_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
 {
-    struct rdpkt1_state_tag *st = &rdpkt1_state;
+    struct rdpkt1_state_tag *st = &ssh->rdpkt1_state;
 
-    crBegin;
+    crBegin(ssh->ssh1_rdpkt_crstate);
 
   next_packet:
 
-    pktin.type = 0;
-    pktin.length = 0;
+    ssh->pktin.type = 0;
+    ssh->pktin.length = 0;
 
     for (st->i = st->len = 0; st->i < 4; st->i++) {
        while ((*datalen) == 0)
@@ -739,18 +775,15 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen)
 
     st->pad = 8 - (st->len % 8);
     st->biglen = st->len + st->pad;
-    pktin.length = st->len - 5;
+    ssh->pktin.length = st->len - 5;
 
-    if (pktin.maxlen < st->biglen) {
-       pktin.maxlen = st->biglen;
-       pktin.data = (pktin.data == NULL ? smalloc(st->biglen + APIEXTRA) :
-                     srealloc(pktin.data, st->biglen + APIEXTRA));
-       if (!pktin.data)
-           fatalbox("Out of memory");
+    if (ssh->pktin.maxlen < st->biglen) {
+       ssh->pktin.maxlen = st->biglen;
+       ssh->pktin.data = srealloc(ssh->pktin.data, st->biglen + APIEXTRA);
     }
 
     st->to_read = st->biglen;
-    st->p = pktin.data;
+    st->p = ssh->pktin.data;
     while (st->to_read > 0) {
        st->chunk = st->to_read;
        while ((*datalen) == 0)
@@ -764,85 +797,84 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen)
        st->to_read -= st->chunk;
     }
 
-    if (cipher && detect_attack(pktin.data, st->biglen, NULL)) {
+    if (ssh->cipher && detect_attack(ssh->pktin.data, st->biglen, NULL)) {
         bombout(("Network attack (CRC compensation) detected!"));
         crReturn(0);
     }
 
-    if (cipher)
-       cipher->decrypt(pktin.data, st->biglen);
+    if (ssh->cipher)
+       ssh->cipher->decrypt(ssh->pktin.data, st->biglen);
 
-    st->realcrc = crc32(pktin.data, st->biglen - 4);
-    st->gotcrc = GET_32BIT(pktin.data + st->biglen - 4);
+    st->realcrc = crc32(ssh->pktin.data, st->biglen - 4);
+    st->gotcrc = GET_32BIT(ssh->pktin.data + st->biglen - 4);
     if (st->gotcrc != st->realcrc) {
        bombout(("Incorrect CRC received on packet"));
        crReturn(0);
     }
 
-    pktin.body = pktin.data + st->pad + 1;
+    ssh->pktin.body = ssh->pktin.data + st->pad + 1;
 
-    if (ssh1_compressing) {
+    if (ssh->v1_compressing) {
        unsigned char *decompblk;
        int decomplen;
-       zlib_decompress_block(pktin.body - 1, pktin.length + 1,
+       zlib_decompress_block(ssh->pktin.body - 1, ssh->pktin.length + 1,
                              &decompblk, &decomplen);
 
-       if (pktin.maxlen < st->pad + decomplen) {
-           pktin.maxlen = st->pad + decomplen;
-           pktin.data = srealloc(pktin.data, pktin.maxlen + APIEXTRA);
-           pktin.body = pktin.data + st->pad + 1;
-           if (!pktin.data)
-               fatalbox("Out of memory");
+       if (ssh->pktin.maxlen < st->pad + decomplen) {
+           ssh->pktin.maxlen = st->pad + decomplen;
+           ssh->pktin.data = srealloc(ssh->pktin.data,
+                                      ssh->pktin.maxlen + APIEXTRA);
+           ssh->pktin.body = ssh->pktin.data + st->pad + 1;
        }
 
-       memcpy(pktin.body - 1, decompblk, decomplen);
+       memcpy(ssh->pktin.body - 1, decompblk, decomplen);
        sfree(decompblk);
-       pktin.length = decomplen - 1;
+       ssh->pktin.length = decomplen - 1;
     }
 
-    pktin.type = pktin.body[-1];
+    ssh->pktin.type = ssh->pktin.body[-1];
 
-    log_packet(PKT_INCOMING, pktin.type, ssh1_pkt_type(pktin.type),
-              pktin.body, pktin.length);
+    log_packet(PKT_INCOMING, ssh->pktin.type, ssh1_pkt_type(ssh->pktin.type),
+              ssh->pktin.body, ssh->pktin.length);
 
-    if (pktin.type == SSH1_SMSG_STDOUT_DATA ||
-       pktin.type == SSH1_SMSG_STDERR_DATA ||
-       pktin.type == SSH1_MSG_DEBUG ||
-       pktin.type == SSH1_SMSG_AUTH_TIS_CHALLENGE ||
-       pktin.type == SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
-       long stringlen = GET_32BIT(pktin.body);
-       if (stringlen + 4 != pktin.length) {
+    if (ssh->pktin.type == SSH1_SMSG_STDOUT_DATA ||
+       ssh->pktin.type == SSH1_SMSG_STDERR_DATA ||
+       ssh->pktin.type == SSH1_MSG_DEBUG ||
+       ssh->pktin.type == SSH1_SMSG_AUTH_TIS_CHALLENGE ||
+       ssh->pktin.type == SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
+       long stringlen = GET_32BIT(ssh->pktin.body);
+       if (stringlen + 4 != ssh->pktin.length) {
            bombout(("Received data packet with bogus string length"));
            crReturn(0);
        }
     }
 
-    if (pktin.type == SSH1_MSG_DEBUG) {
+    if (ssh->pktin.type == SSH1_MSG_DEBUG) {
        /* log debug message */
        char buf[512];
-       int stringlen = GET_32BIT(pktin.body);
+       int stringlen = GET_32BIT(ssh->pktin.body);
        strcpy(buf, "Remote debug message: ");
        if (stringlen > 480)
            stringlen = 480;
-       memcpy(buf + 8, pktin.body + 4, stringlen);
+       memcpy(buf + 8, ssh->pktin.body + 4, stringlen);
        buf[8 + stringlen] = '\0';
        logevent(buf);
        goto next_packet;
-    } else if (pktin.type == SSH1_MSG_IGNORE) {
+    } else if (ssh->pktin.type == SSH1_MSG_IGNORE) {
        /* do nothing */
        goto next_packet;
     }
 
-    if (pktin.type == SSH1_MSG_DISCONNECT) {
+    if (ssh->pktin.type == SSH1_MSG_DISCONNECT) {
        /* log reason code in disconnect message */
        char buf[256];
-       unsigned msglen = GET_32BIT(pktin.body);
+       unsigned msglen = GET_32BIT(ssh->pktin.body);
        unsigned nowlen;
        strcpy(buf, "Remote sent disconnect: ");
        nowlen = strlen(buf);
        if (msglen > sizeof(buf) - nowlen - 1)
            msglen = sizeof(buf) - nowlen - 1;
-       memcpy(buf + nowlen, pktin.body + 4, msglen);
+       memcpy(buf + nowlen, ssh->pktin.body + 4, msglen);
        buf[nowlen + msglen] = '\0';
        /* logevent(buf); (this is now done within the bombout macro) */
        bombout(("Server sent disconnect message:\n\"%s\"", buf+nowlen));
@@ -852,32 +884,25 @@ static int ssh1_rdpkt(unsigned char **data, int *datalen)
     crFinish(0);
 }
 
-static int ssh2_rdpkt(unsigned char **data, int *datalen)
+static int ssh2_rdpkt(Ssh ssh, unsigned char **data, int *datalen)
 {
-    struct rdpkt2_state_tag *st = &rdpkt2_state;
+    struct rdpkt2_state_tag *st = &ssh->rdpkt2_state;
 
-    crBegin;
+    crBegin(ssh->ssh2_rdpkt_crstate);
 
   next_packet:
-    pktin.type = 0;
-    pktin.length = 0;
-    if (sccipher)
-       st->cipherblk = sccipher->blksize;
+    ssh->pktin.type = 0;
+    ssh->pktin.length = 0;
+    if (ssh->sccipher)
+       st->cipherblk = ssh->sccipher->blksize;
     else
        st->cipherblk = 8;
     if (st->cipherblk < 8)
        st->cipherblk = 8;
 
-    if (pktin.maxlen < st->cipherblk) {
-       pktin.maxlen = st->cipherblk;
-       pktin.data =
-           (pktin.data ==
-            NULL ? smalloc(st->cipherblk +
-                           APIEXTRA) : srealloc(pktin.data,
-                                                st->cipherblk +
-                                                APIEXTRA));
-       if (!pktin.data)
-           fatalbox("Out of memory");
+    if (ssh->pktin.maxlen < st->cipherblk) {
+       ssh->pktin.maxlen = st->cipherblk;
+       ssh->pktin.data = srealloc(ssh->pktin.data, st->cipherblk + APIEXTRA);
     }
 
     /*
@@ -887,18 +912,18 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen)
     for (st->i = st->len = 0; st->i < st->cipherblk; st->i++) {
        while ((*datalen) == 0)
            crReturn(st->cipherblk - st->i);
-       pktin.data[st->i] = *(*data)++;
+       ssh->pktin.data[st->i] = *(*data)++;
        (*datalen)--;
     }
 
-    if (sccipher)
-       sccipher->decrypt(pktin.data, st->cipherblk);
+    if (ssh->sccipher)
+       ssh->sccipher->decrypt(ssh->pktin.data, st->cipherblk);
 
     /*
      * Now get the length and padding figures.
      */
-    st->len = GET_32BIT(pktin.data);
-    st->pad = pktin.data[4];
+    st->len = GET_32BIT(ssh->pktin.data);
+    st->pad = ssh->pktin.data[4];
 
     /*
      * _Completely_ silly lengths should be stomped on before they
@@ -914,27 +939,21 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen)
      */
     st->payload = st->len - st->pad - 1;
 
-    pktin.length = st->payload + 5;
+    ssh->pktin.length = st->payload + 5;
 
     /*
      * So now we can work out the total packet length.
      */
     st->packetlen = st->len + 4;
-    st->maclen = scmac ? scmac->len : 0;
+    st->maclen = ssh->scmac ? ssh->scmac->len : 0;
 
     /*
      * Adjust memory allocation if packet is too big.
      */
-    if (pktin.maxlen < st->packetlen + st->maclen) {
-       pktin.maxlen = st->packetlen + st->maclen;
-       pktin.data =
-           (pktin.data ==
-            NULL ? smalloc(pktin.maxlen + APIEXTRA) : srealloc(pktin.data,
-                                                               pktin.maxlen
-                                                               +
-                                                               APIEXTRA));
-       if (!pktin.data)
-           fatalbox("Out of memory");
+    if (ssh->pktin.maxlen < st->packetlen + st->maclen) {
+       ssh->pktin.maxlen = st->packetlen + st->maclen;
+       ssh->pktin.data = srealloc(ssh->pktin.data,
+                                  ssh->pktin.maxlen + APIEXTRA);
     }
 
     /*
@@ -944,20 +963,20 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen)
         st->i++) {
        while ((*datalen) == 0)
            crReturn(st->packetlen + st->maclen - st->i);
-       pktin.data[st->i] = *(*data)++;
+       ssh->pktin.data[st->i] = *(*data)++;
        (*datalen)--;
     }
     /* Decrypt everything _except_ the MAC. */
-    if (sccipher)
-       sccipher->decrypt(pktin.data + st->cipherblk,
-                         st->packetlen - st->cipherblk);
+    if (ssh->sccipher)
+       ssh->sccipher->decrypt(ssh->pktin.data + st->cipherblk,
+                              st->packetlen - st->cipherblk);
 
     /*
      * Check the MAC.
      */
-    if (scmac
-       && !scmac->verify(pktin.data, st->len + 4,
-                         st->incoming_sequence)) {
+    if (ssh->scmac
+       && !ssh->scmac->verify(ssh->pktin.data, st->len + 4,
+                              st->incoming_sequence)) {
        bombout(("Incorrect MAC received on packet"));
        crReturn(0);
     }
@@ -969,32 +988,28 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen)
     {
        unsigned char *newpayload;
        int newlen;
-       if (sccomp && sccomp->decompress(pktin.data + 5, pktin.length - 5,
-                                        &newpayload, &newlen)) {
-           if (pktin.maxlen < newlen + 5) {
-               pktin.maxlen = newlen + 5;
-               pktin.data =
-                   (pktin.data ==
-                    NULL ? smalloc(pktin.maxlen +
-                                   APIEXTRA) : srealloc(pktin.data,
-                                                        pktin.maxlen +
-                                                        APIEXTRA));
-               if (!pktin.data)
-                   fatalbox("Out of memory");
+       if (ssh->sccomp &&
+           ssh->sccomp->decompress(ssh->pktin.data + 5, ssh->pktin.length - 5,
+                                   &newpayload, &newlen)) {
+           if (ssh->pktin.maxlen < newlen + 5) {
+               ssh->pktin.maxlen = newlen + 5;
+               ssh->pktin.data = srealloc(ssh->pktin.data,
+                                          ssh->pktin.maxlen + APIEXTRA);
            }
-           pktin.length = 5 + newlen;
-           memcpy(pktin.data + 5, newpayload, newlen);
+           ssh->pktin.length = 5 + newlen;
+           memcpy(ssh->pktin.data + 5, newpayload, newlen);
            sfree(newpayload);
        }
     }
 
-    pktin.savedpos = 6;
-    pktin.type = pktin.data[5];
+    ssh->pktin.savedpos = 6;
+    ssh->pktin.type = ssh->pktin.data[5];
 
-    log_packet(PKT_INCOMING, pktin.type, ssh2_pkt_type(pktin.type),
-              pktin.data+6, pktin.length-6);
+    log_packet(PKT_INCOMING, ssh->pktin.type,
+              ssh2_pkt_type(ssh->pkt_ctx, ssh->pktin.type),
+              ssh->pktin.data+6, ssh->pktin.length-6);
 
-    switch (pktin.type) {
+    switch (ssh->pktin.type) {
         /*
          * These packets we must handle instantly.
          */
@@ -1002,8 +1017,8 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen)
         {
             /* log reason code in disconnect message */
             char buf[256];
-            int reason = GET_32BIT(pktin.data + 6);
-            unsigned msglen = GET_32BIT(pktin.data + 10);
+            int reason = GET_32BIT(ssh->pktin.data + 6);
+            unsigned msglen = GET_32BIT(ssh->pktin.data + 10);
             unsigned nowlen;
             if (reason > 0 && reason < lenof(ssh2_disconnect_reasons)) {
                 sprintf(buf, "Received disconnect message (%s)",
@@ -1017,7 +1032,7 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen)
             nowlen = strlen(buf);
             if (msglen > sizeof(buf) - nowlen - 1)
                 msglen = sizeof(buf) - nowlen - 1;
-            memcpy(buf + nowlen, pktin.data + 14, msglen);
+            memcpy(buf + nowlen, ssh->pktin.data + 14, msglen);
             buf[nowlen + msglen] = '\0';
             logevent(buf);
             bombout(("Server sent disconnect message\ntype %d (%s):\n\"%s\"",
@@ -1034,14 +1049,14 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen)
        {
            /* log the debug message */
            char buf[512];
-           /* int display = pktin.body[6]; */
-           int stringlen = GET_32BIT(pktin.data+7);
+           /* int display = ssh->pktin.body[6]; */
+           int stringlen = GET_32BIT(ssh->pktin.data+7);
            int prefix;
            strcpy(buf, "Remote debug message: ");
            prefix = strlen(buf);
-           if (stringlen > sizeof(buf)-prefix-1)
+           if (stringlen > (int)(sizeof(buf)-prefix-1))
                stringlen = sizeof(buf)-prefix-1;
-           memcpy(buf + prefix, pktin.data + 11, stringlen);
+           memcpy(buf + prefix, ssh->pktin.data + 11, stringlen);
            buf[prefix + stringlen] = '\0';
            logevent(buf);
        }
@@ -1089,16 +1104,16 @@ static int ssh2_rdpkt(unsigned char **data, int *datalen)
          * For anything else we send SSH2_MSG_UNIMPLEMENTED.
          */
       default:
-       ssh2_pkt_init(SSH2_MSG_UNIMPLEMENTED);
-       ssh2_pkt_adduint32(st->incoming_sequence - 1);
-       ssh2_pkt_send();
+       ssh2_pkt_init(ssh, SSH2_MSG_UNIMPLEMENTED);
+       ssh2_pkt_adduint32(ssh, st->incoming_sequence - 1);
+       ssh2_pkt_send(ssh);
         break;
     }
 
     crFinish(0);
 }
 
-static void ssh1_pktout_size(int len)
+static void ssh1_pktout_size(Ssh ssh, int len)
 {
     int pad, biglen;
 
@@ -1106,91 +1121,88 @@ static void ssh1_pktout_size(int len)
     pad = 8 - (len % 8);
     biglen = len + pad;
 
-    pktout.length = len - 5;
-    if (pktout.maxlen < biglen) {
-       pktout.maxlen = biglen;
+    ssh->pktout.length = len - 5;
+    if (ssh->pktout.maxlen < biglen) {
+       ssh->pktout.maxlen = biglen;
 #ifdef MSCRYPTOAPI
        /* Allocate enough buffer space for extra block
         * for MS CryptEncrypt() */
-       pktout.data = (pktout.data == NULL ? smalloc(biglen + 12) :
-                      srealloc(pktout.data, biglen + 12));
+       ssh->pktout.data = srealloc(ssh->pktout.data, biglen + 12);
 #else
-       pktout.data = (pktout.data == NULL ? smalloc(biglen + 4) :
-                      srealloc(pktout.data, biglen + 4));
+       ssh->pktout.data = srealloc(ssh->pktout.data, biglen + 4);
 #endif
-       if (!pktout.data)
-           fatalbox("Out of memory");
     }
-    pktout.body = pktout.data + 4 + pad + 1;
+    ssh->pktout.body = ssh->pktout.data + 4 + pad + 1;
 }
 
-static void s_wrpkt_start(int type, int len)
+static void s_wrpkt_start(Ssh ssh, int type, int len)
 {
-    ssh1_pktout_size(len);
-    pktout.type = type;
+    ssh1_pktout_size(ssh, len);
+    ssh->pktout.type = type;
 }
 
-static int s_wrpkt_prepare(void)
+static int s_wrpkt_prepare(Ssh ssh)
 {
     int pad, len, biglen, i;
     unsigned long crc;
 
-    pktout.body[-1] = pktout.type;
+    ssh->pktout.body[-1] = ssh->pktout.type;
 
-    log_packet(PKT_OUTGOING, pktout.type, ssh1_pkt_type(pktout.type),
-              pktout.body, pktout.length);
+    log_packet(PKT_OUTGOING, ssh->pktout.type, ssh1_pkt_type(ssh->pktout.type),
+              ssh->pktout.body, ssh->pktout.length);
 
-    if (ssh1_compressing) {
+    if (ssh->v1_compressing) {
        unsigned char *compblk;
        int complen;
-       zlib_compress_block(pktout.body - 1, pktout.length + 1,
+       zlib_compress_block(ssh->pktout.body - 1, ssh->pktout.length + 1,
                            &compblk, &complen);
-       ssh1_pktout_size(complen - 1);
-       memcpy(pktout.body - 1, compblk, complen);
+       ssh1_pktout_size(ssh, complen - 1);
+       memcpy(ssh->pktout.body - 1, compblk, complen);
        sfree(compblk);
     }
 
-    len = pktout.length + 5;          /* type and CRC */
+    len = ssh->pktout.length + 5;             /* type and CRC */
     pad = 8 - (len % 8);
     biglen = len + pad;
 
     for (i = 0; i < pad; i++)
-       pktout.data[i + 4] = random_byte();
-    crc = crc32(pktout.data + 4, biglen - 4);
-    PUT_32BIT(pktout.data + biglen, crc);
-    PUT_32BIT(pktout.data, len);
+       ssh->pktout.data[i + 4] = random_byte();
+    crc = crc32(ssh->pktout.data + 4, biglen - 4);
+    PUT_32BIT(ssh->pktout.data + biglen, crc);
+    PUT_32BIT(ssh->pktout.data, len);
 
-    if (cipher)
-       cipher->encrypt(pktout.data + 4, biglen);
+    if (ssh->cipher)
+       ssh->cipher->encrypt(ssh->pktout.data + 4, biglen);
 
     return biglen + 4;
 }
 
-static void s_wrpkt(void)
+static void s_wrpkt(Ssh ssh)
 {
     int len, backlog;
-    len = s_wrpkt_prepare();
-    backlog = sk_write(spktout.data, len);
+    len = s_wrpkt_prepare(ssh);
+    backlog = sk_write(ssh->s, ssh->pktout.data, len);
     if (backlog > SSH_MAX_BACKLOG)
-       ssh_throttle_all(1, backlog);
+       ssh_throttle_all(ssh, 1, backlog);
 }
 
-static void s_wrpkt_defer(void)
+static void s_wrpkt_defer(Ssh ssh)
 {
     int len;
-    len = s_wrpkt_prepare();
-    if (deferred_len + len > deferred_size) {
-       deferred_size = deferred_len + len + 128;
-       deferred_send_data = srealloc(deferred_send_data, deferred_size);
+    len = s_wrpkt_prepare(ssh);
+    if (ssh->deferred_len + len > ssh->deferred_size) {
+       ssh->deferred_size = ssh->deferred_len + len + 128;
+       ssh->deferred_send_data = srealloc(ssh->deferred_send_data,
+                                          ssh->deferred_size);
     }
-    memcpy(deferred_send_data + deferred_len, pktout.data, len);
-    deferred_len += len;
+    memcpy(ssh->deferred_send_data + ssh->deferred_len, ssh->pktout.data, len);
+    ssh->deferred_len += len;
 }
 
 /*
  * Construct a packet with the specified contents.
  */
-static void construct_packet(int pkttype, va_list ap1, va_list ap2)
+static void construct_packet(Ssh ssh, int pkttype, va_list ap1, va_list ap2)
 {
     unsigned char *p, *argp, argchar;
     unsigned long argint;
@@ -1227,8 +1239,8 @@ static void construct_packet(int pkttype, va_list ap1, va_list ap2)
        }
     }
 
-    s_wrpkt_start(pkttype, pktlen);
-    p = pktout.body;
+    s_wrpkt_start(ssh, pkttype, pktlen);
+    p = ssh->pktout.body;
 
     while ((argtype = va_arg(ap2, int)) != PKT_END) {
        switch (argtype) {
@@ -1263,22 +1275,22 @@ static void construct_packet(int pkttype, va_list ap1, va_list ap2)
     }
 }
 
-static void send_packet(int pkttype, ...)
+static void send_packet(Ssh ssh, int pkttype, ...)
 {
     va_list ap1, ap2;
     va_start(ap1, pkttype);
     va_start(ap2, pkttype);
-    construct_packet(pkttype, ap1, ap2);
-    s_wrpkt();
+    construct_packet(ssh, pkttype, ap1, ap2);
+    s_wrpkt(ssh);
 }
 
-static void defer_packet(int pkttype, ...)
+static void defer_packet(Ssh ssh, int pkttype, ...)
 {
     va_list ap1, ap2;
     va_start(ap1, pkttype);
     va_start(ap2, pkttype);
-    construct_packet(pkttype, ap1, ap2);
-    s_wrpkt_defer();
+    construct_packet(ssh, pkttype, ap1, ap2);
+    s_wrpkt_defer(ssh);
 }
 
 static int ssh_versioncmp(char *a, char *b)
@@ -1301,7 +1313,6 @@ static int ssh_versioncmp(char *a, char *b)
     return 0;
 }
 
-
 /*
  * Utility routines for putting an SSH-protocol `string' and
  * `uint32' into a SHA state.
@@ -1325,66 +1336,62 @@ static void sha_uint32(SHA_State * s, unsigned i)
 /*
  * SSH2 packet construction functions.
  */
-static void ssh2_pkt_ensure(int length)
+static void ssh2_pkt_ensure(Ssh ssh, int length)
 {
-    if (pktout.maxlen < length) {
-       pktout.maxlen = length + 256;
-       pktout.data =
-           (pktout.data ==
-            NULL ? smalloc(pktout.maxlen +
-                           APIEXTRA) : srealloc(pktout.data,
-                                                pktout.maxlen +
-                                                APIEXTRA));
-       if (!pktout.data)
+    if (ssh->pktout.maxlen < length) {
+       ssh->pktout.maxlen = length + 256;
+       ssh->pktout.data = srealloc(ssh->pktout.data,
+                                   ssh->pktout.maxlen + APIEXTRA);
+       if (!ssh->pktout.data)
            fatalbox("Out of memory");
     }
 }
-static void ssh2_pkt_adddata(void *data, int len)
+static void ssh2_pkt_adddata(Ssh ssh, void *data, int len)
 {
-    pktout.length += len;
-    ssh2_pkt_ensure(pktout.length);
-    memcpy(pktout.data + pktout.length - len, data, len);
+    ssh->pktout.length += len;
+    ssh2_pkt_ensure(ssh, ssh->pktout.length);
+    memcpy(ssh->pktout.data + ssh->pktout.length - len, data, len);
 }
-static void ssh2_pkt_addbyte(unsigned char byte)
+static void ssh2_pkt_addbyte(Ssh ssh, unsigned char byte)
 {
-    ssh2_pkt_adddata(&byte, 1);
+    ssh2_pkt_adddata(ssh, &byte, 1);
 }
-static void ssh2_pkt_init(int pkt_type)
+static void ssh2_pkt_init(Ssh ssh, int pkt_type)
 {
-    pktout.length = 5;
-    ssh2_pkt_addbyte((unsigned char) pkt_type);
+    ssh->pktout.length = 5;
+    ssh2_pkt_addbyte(ssh, (unsigned char) pkt_type);
 }
-static void ssh2_pkt_addbool(unsigned char value)
+static void ssh2_pkt_addbool(Ssh ssh, unsigned char value)
 {
-    ssh2_pkt_adddata(&value, 1);
+    ssh2_pkt_adddata(ssh, &value, 1);
 }
-static void ssh2_pkt_adduint32(unsigned long value)
+static void ssh2_pkt_adduint32(Ssh ssh, unsigned long value)
 {
     unsigned char x[4];
     PUT_32BIT(x, value);
-    ssh2_pkt_adddata(x, 4);
+    ssh2_pkt_adddata(ssh, x, 4);
 }
-static void ssh2_pkt_addstring_start(void)
+static void ssh2_pkt_addstring_start(Ssh ssh)
 {
-    ssh2_pkt_adduint32(0);
-    pktout.savedpos = pktout.length;
+    ssh2_pkt_adduint32(ssh, 0);
+    ssh->pktout.savedpos = ssh->pktout.length;
 }
-static void ssh2_pkt_addstring_str(char *data)
+static void ssh2_pkt_addstring_str(Ssh ssh, char *data)
 {
-    ssh2_pkt_adddata(data, strlen(data));
-    PUT_32BIT(pktout.data + pktout.savedpos - 4,
-             pktout.length - pktout.savedpos);
+    ssh2_pkt_adddata(ssh, data, strlen(data));
+    PUT_32BIT(ssh->pktout.data + ssh->pktout.savedpos - 4,
+             ssh->pktout.length - ssh->pktout.savedpos);
 }
-static void ssh2_pkt_addstring_data(char *data, int len)
+static void ssh2_pkt_addstring_data(Ssh ssh, char *data, int len)
 {
-    ssh2_pkt_adddata(data, len);
-    PUT_32BIT(pktout.data + pktout.savedpos - 4,
-             pktout.length - pktout.savedpos);
+    ssh2_pkt_adddata(ssh, data, len);
+    PUT_32BIT(ssh->pktout.data + ssh->pktout.savedpos - 4,
+             ssh->pktout.length - ssh->pktout.savedpos);
 }
-static void ssh2_pkt_addstring(char *data)
+static void ssh2_pkt_addstring(Ssh ssh, char *data)
 {
-    ssh2_pkt_addstring_start();
-    ssh2_pkt_addstring_str(data);
+    ssh2_pkt_addstring_start(ssh);
+    ssh2_pkt_addstring_str(ssh, data);
 }
 static char *ssh2_mpint_fmt(Bignum b, int *len)
 {
@@ -1403,28 +1410,28 @@ static char *ssh2_mpint_fmt(Bignum b, int *len)
     *len = n + 1 - i;
     return p;
 }
-static void ssh2_pkt_addmp(Bignum b)
+static void ssh2_pkt_addmp(Ssh ssh, Bignum b)
 {
     unsigned char *p;
     int len;
     p = ssh2_mpint_fmt(b, &len);
-    ssh2_pkt_addstring_start();
-    ssh2_pkt_addstring_data(p, len);
+    ssh2_pkt_addstring_start(ssh);
+    ssh2_pkt_addstring_data(ssh, p, len);
     sfree(p);
 }
 
 /*
  * Construct an SSH2 final-form packet: compress it, encrypt it,
  * put the MAC on it. Final packet, ready to be sent, is stored in
- * pktout.data. Total length is returned.
+ * ssh->pktout.data. Total length is returned.
  */
-static int ssh2_pkt_construct(void)
+static int ssh2_pkt_construct(Ssh ssh)
 {
     int cipherblk, maclen, padding, i;
-    static unsigned long outgoing_sequence = 0;
 
-    log_packet(PKT_OUTGOING, pktout.data[5], ssh2_pkt_type(pktout.data[5]),
-              pktout.data + 6, pktout.length - 6);
+    log_packet(PKT_OUTGOING, ssh->pktout.data[5],
+              ssh2_pkt_type(ssh->pkt_ctx, ssh->pktout.data[5]),
+              ssh->pktout.data + 6, ssh->pktout.length - 6);
 
     /*
      * Compress packet payload.
@@ -1432,10 +1439,11 @@ static int ssh2_pkt_construct(void)
     {
        unsigned char *newpayload;
        int newlen;
-       if (cscomp && cscomp->compress(pktout.data + 5, pktout.length - 5,
-                                      &newpayload, &newlen)) {
-           pktout.length = 5;
-           ssh2_pkt_adddata(newpayload, newlen);
+       if (ssh->cscomp &&
+           ssh->cscomp->compress(ssh->pktout.data + 5, ssh->pktout.length - 5,
+                                 &newpayload, &newlen)) {
+           ssh->pktout.length = 5;
+           ssh2_pkt_adddata(ssh, newpayload, newlen);
            sfree(newpayload);
        }
     }
@@ -1444,40 +1452,40 @@ static int ssh2_pkt_construct(void)
      * Add padding. At least four bytes, and must also bring total
      * length (minus MAC) up to a multiple of the block size.
      */
-    cipherblk = cscipher ? cscipher->blksize : 8;      /* block size */
+    cipherblk = ssh->cscipher ? ssh->cscipher->blksize : 8;  /* block size */
     cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */
     padding = 4;
     padding +=
-       (cipherblk - (pktout.length + padding) % cipherblk) % cipherblk;
-    maclen = csmac ? csmac->len : 0;
-    ssh2_pkt_ensure(pktout.length + padding + maclen);
-    pktout.data[4] = padding;
+       (cipherblk - (ssh->pktout.length + padding) % cipherblk) % cipherblk;
+    maclen = ssh->csmac ? ssh->csmac->len : 0;
+    ssh2_pkt_ensure(ssh, ssh->pktout.length + padding + maclen);
+    ssh->pktout.data[4] = padding;
     for (i = 0; i < padding; i++)
-       pktout.data[pktout.length + i] = random_byte();
-    PUT_32BIT(pktout.data, pktout.length + padding - 4);
-    if (csmac)
-       csmac->generate(pktout.data, pktout.length + padding,
-                       outgoing_sequence);
-    outgoing_sequence++;              /* whether or not we MACed */
-
-    if (cscipher)
-       cscipher->encrypt(pktout.data, pktout.length + padding);
-
-    /* Ready-to-send packet starts at pktout.data. We return length. */
-    return pktout.length + padding + maclen;
+       ssh->pktout.data[ssh->pktout.length + i] = random_byte();
+    PUT_32BIT(ssh->pktout.data, ssh->pktout.length + padding - 4);
+    if (ssh->csmac)
+       ssh->csmac->generate(ssh->pktout.data, ssh->pktout.length + padding,
+                            ssh->v2_outgoing_sequence);
+    ssh->v2_outgoing_sequence++;       /* whether or not we MACed */
+
+    if (ssh->cscipher)
+       ssh->cscipher->encrypt(ssh->pktout.data, ssh->pktout.length + padding);
+
+    /* Ready-to-send packet starts at ssh->pktout.data. We return length. */
+    return ssh->pktout.length + padding + maclen;
 }
 
 /*
  * Construct and send an SSH2 packet immediately.
  */
-static void ssh2_pkt_send(void)
+static void ssh2_pkt_send(Ssh ssh)
 {
     int len;
     int backlog;
-    len = ssh2_pkt_construct();
-    backlog = sk_write(spktout.data, len);
+    len = ssh2_pkt_construct(ssh);
+    backlog = sk_write(ssh->s, ssh->pktout.data, len);
     if (backlog > SSH_MAX_BACKLOG)
-       ssh_throttle_all(1, backlog);
+       ssh_throttle_all(ssh, 1, backlog);
 }
 
 /*
@@ -1491,30 +1499,31 @@ static void ssh2_pkt_send(void)
  * NOT be used as an m4-style `defer' allowing packets to be
  * constructed in one order and sent in another.
  */
-static void ssh2_pkt_defer(void)
+static void ssh2_pkt_defer(Ssh ssh)
 {
-    int len = ssh2_pkt_construct();
-    if (deferred_len + len > deferred_size) {
-       deferred_size = deferred_len + len + 128;
-       deferred_send_data = srealloc(deferred_send_data, deferred_size);
+    int len = ssh2_pkt_construct(ssh);
+    if (ssh->deferred_len + len > ssh->deferred_size) {
+       ssh->deferred_size = ssh->deferred_len + len + 128;
+       ssh->deferred_send_data = srealloc(ssh->deferred_send_data,
+                                          ssh->deferred_size);
     }
-    memcpy(deferred_send_data + deferred_len, pktout.data, len);
-    deferred_len += len;
+    memcpy(ssh->deferred_send_data + ssh->deferred_len, ssh->pktout.data, len);
+    ssh->deferred_len += len;
 }
 
 /*
  * Send the whole deferred data block constructed by
  * ssh2_pkt_defer() or SSH1's defer_packet().
  */
-static void ssh_pkt_defersend(void)
+static void ssh_pkt_defersend(Ssh ssh)
 {
     int backlog;
-    backlog = sk_write(s, deferred_send_data, deferred_len);
-    deferred_len = deferred_size = 0;
-    sfree(deferred_send_data);
-    deferred_send_data = NULL;
+    backlog = sk_write(ssh->s, ssh->deferred_send_data, ssh->deferred_len);
+    ssh->deferred_len = ssh->deferred_size = 0;
+    sfree(ssh->deferred_send_data);
+    ssh->deferred_send_data = NULL;
     if (backlog > SSH_MAX_BACKLOG)
-       ssh_throttle_all(1, backlog);
+       ssh_throttle_all(ssh, 1, backlog);
 }
 
 #if 0
@@ -1543,44 +1552,44 @@ static void sha_mpint(SHA_State * s, Bignum b)
 /*
  * SSH2 packet decode functions.
  */
-static unsigned long ssh2_pkt_getuint32(void)
+static unsigned long ssh2_pkt_getuint32(Ssh ssh)
 {
     unsigned long value;
-    if (pktin.length - pktin.savedpos < 4)
+    if (ssh->pktin.length - ssh->pktin.savedpos < 4)
        return 0;                      /* arrgh, no way to decline (FIXME?) */
-    value = GET_32BIT(pktin.data + pktin.savedpos);
-    pktin.savedpos += 4;
+    value = GET_32BIT(ssh->pktin.data + ssh->pktin.savedpos);
+    ssh->pktin.savedpos += 4;
     return value;
 }
-static int ssh2_pkt_getbool(void)
+static int ssh2_pkt_getbool(Ssh ssh)
 {
     unsigned long value;
-    if (pktin.length - pktin.savedpos < 1)
+    if (ssh->pktin.length - ssh->pktin.savedpos < 1)
        return 0;                      /* arrgh, no way to decline (FIXME?) */
-    value = pktin.data[pktin.savedpos] != 0;
-    pktin.savedpos++;
+    value = ssh->pktin.data[ssh->pktin.savedpos] != 0;
+    ssh->pktin.savedpos++;
     return value;
 }
-static void ssh2_pkt_getstring(char **p, int *length)
+static void ssh2_pkt_getstring(Ssh ssh, char **p, int *length)
 {
     *p = NULL;
     *length = 0;
-    if (pktin.length - pktin.savedpos < 4)
+    if (ssh->pktin.length - ssh->pktin.savedpos < 4)
        return;
-    *length = GET_32BIT(pktin.data + pktin.savedpos);
-    pktin.savedpos += 4;
-    if (pktin.length - pktin.savedpos < *length)
+    *length = GET_32BIT(ssh->pktin.data + ssh->pktin.savedpos);
+    ssh->pktin.savedpos += 4;
+    if (ssh->pktin.length - ssh->pktin.savedpos < *length)
        return;
-    *p = pktin.data + pktin.savedpos;
-    pktin.savedpos += *length;
+    *p = ssh->pktin.data + ssh->pktin.savedpos;
+    ssh->pktin.savedpos += *length;
 }
-static Bignum ssh2_pkt_getmp(void)
+static Bignum ssh2_pkt_getmp(Ssh ssh)
 {
     char *p;
     int length;
     Bignum b;
 
-    ssh2_pkt_getstring(&p, &length);
+    ssh2_pkt_getstring(ssh, &p, &length);
     if (!p)
        return NULL;
     if (p[0] & 0x80) {
@@ -1598,7 +1607,7 @@ static Bignum ssh2_pkt_getmp(void)
  * fiddle with the signature packet if necessary for
  * BUG_SSH2_RSA_PADDING.
  */
-static void ssh2_add_sigblob(void *pkblob_v, int pkblob_len,
+static void ssh2_add_sigblob(Ssh ssh, void *pkblob_v, int pkblob_len,
                             void *sigblob_v, int sigblob_len)
 {
     unsigned char *pkblob = (unsigned char *)pkblob_v;
@@ -1611,7 +1620,7 @@ static void ssh2_add_sigblob(void *pkblob_v, int pkblob_len,
      * 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) &&
        (GET_32BIT(pkblob) == 7 && !memcmp(pkblob+4, "ssh-rsa", 7))) {
        int pos, len, siglen;
 
@@ -1636,19 +1645,19 @@ static void ssh2_add_sigblob(void *pkblob_v, int pkblob_len,
 
        if (len != siglen) {
            unsigned char newlen[4];
-           ssh2_pkt_addstring_start();
-           ssh2_pkt_addstring_data(sigblob, pos);
+           ssh2_pkt_addstring_start(ssh);
+           ssh2_pkt_addstring_data(ssh, sigblob, pos);
            /* dmemdump(sigblob, pos); */
            pos += 4;                  /* point to start of actual sig */
            PUT_32BIT(newlen, len);
-           ssh2_pkt_addstring_data(newlen, 4);
+           ssh2_pkt_addstring_data(ssh, newlen, 4);
            /* dmemdump(newlen, 4); */
            newlen[0] = 0;
            while (len-- > siglen) {
-               ssh2_pkt_addstring_data(newlen, 1);
+               ssh2_pkt_addstring_data(ssh, newlen, 1);
                /* dmemdump(newlen, 1); */
            }
-           ssh2_pkt_addstring_data(sigblob+pos, siglen);
+           ssh2_pkt_addstring_data(ssh, sigblob+pos, siglen);
            /* dmemdump(sigblob+pos, siglen); */
            return;
        }
@@ -1656,15 +1665,15 @@ static void ssh2_add_sigblob(void *pkblob_v, int pkblob_len,
        /* Otherwise fall through and do it the easy way. */
     }
 
-    ssh2_pkt_addstring_start();
-    ssh2_pkt_addstring_data(sigblob, sigblob_len);
+    ssh2_pkt_addstring_start(ssh);
+    ssh2_pkt_addstring_data(ssh, sigblob, sigblob_len);
 }
 
 /*
  * Examine the remote side's version string and compare it against
  * a list of known buggy implementations.
  */
-static void ssh_detect_bugs(char *vstring)
+static void ssh_detect_bugs(Ssh ssh, char *vstring)
 {
     char *imp;                        /* pointer to implementation part */
     imp = vstring;
@@ -1673,7 +1682,7 @@ static void ssh_detect_bugs(char *vstring)
     imp += strcspn(imp, "-");
     if (*imp) imp++;
 
-    ssh_remote_bugs = 0;
+    ssh->remote_bugs = 0;
 
     if (cfg.sshbug_ignore1 == BUG_ON ||
        (cfg.sshbug_ignore1 == BUG_AUTO &&
@@ -1685,7 +1694,7 @@ static void ssh_detect_bugs(char *vstring)
         * to use a different defence against password length
         * sniffing.
         */
-       ssh_remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE;
+       ssh->remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE;
        logevent("We believe remote version has SSH1 ignore bug");
     }
 
@@ -1697,7 +1706,7 @@ static void ssh_detect_bugs(char *vstring)
         * handle having a null and a random length of data after
         * the password.
         */
-       ssh_remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD;
+       ssh->remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD;
        logevent("We believe remote version needs a plain SSH1 password");
     }
 
@@ -1709,7 +1718,7 @@ static void ssh_detect_bugs(char *vstring)
         * RSA authentication and will panic and die if they see
         * an AUTH_RSA message.
         */
-       ssh_remote_bugs |= BUG_CHOKES_ON_RSA;
+       ssh->remote_bugs |= BUG_CHOKES_ON_RSA;
        logevent("We believe remote version can't handle RSA authentication");
     }
 
@@ -1721,7 +1730,7 @@ static void ssh_detect_bugs(char *vstring)
        /*
         * These versions have the HMAC bug.
         */
-       ssh_remote_bugs |= BUG_SSH2_HMAC;
+       ssh->remote_bugs |= BUG_SSH2_HMAC;
        logevent("We believe remote version has SSH2 HMAC bug");
     }
 
@@ -1733,7 +1742,7 @@ static void ssh_detect_bugs(char *vstring)
         * include the literal shared secret in the hashes that
         * generate the keys).
         */
-       ssh_remote_bugs |= BUG_SSH2_DERIVEKEY;
+       ssh->remote_bugs |= BUG_SSH2_DERIVEKEY;
        logevent("We believe remote version has SSH2 key-derivation bug");
     }
 
@@ -1744,7 +1753,7 @@ static void ssh_detect_bugs(char *vstring)
        /*
         * These versions have the SSH2 RSA padding bug.
         */
-       ssh_remote_bugs |= BUG_SSH2_RSA_PADDING;
+       ssh->remote_bugs |= BUG_SSH2_RSA_PADDING;
        logevent("We believe remote version has SSH2 RSA padding bug");
     }
 
@@ -1752,141 +1761,146 @@ static void ssh_detect_bugs(char *vstring)
        /*
         * These versions have the SSH2 DH GEX bug.
         */
-       ssh_remote_bugs |= BUG_SSH2_DH_GEX;
+       ssh->remote_bugs |= BUG_SSH2_DH_GEX;
        logevent("We believe remote version has SSH2 DH group exchange bug");
     }
 }
 
-static int do_ssh_init(unsigned char c)
+static int do_ssh_init(Ssh ssh, unsigned char c)
 {
-    static int vslen;
-    static char version[10];
-    static char *vstring;
-    static int vstrsize;
-    static char *vlog;
-    static int i;
-    static int proto1, proto2;
+    struct do_ssh_init_state {
+       int vslen;
+       char version[10];
+       char *vstring;
+       int vstrsize;
+       int i;
+       int proto1, proto2;
+    };
+    crState(do_ssh_init_state);
 
-    crBegin;
+    crBegin(ssh->do_ssh_init_crstate);
 
     /* Search for the string "SSH-" in the input. */
-    i = 0;
+    s->i = 0;
     while (1) {
        static const int transS[] = { 1, 2, 2, 1 };
        static const int transH[] = { 0, 0, 3, 0 };
        static const int transminus[] = { 0, 0, 0, -1 };
        if (c == 'S')
-           i = transS[i];
+           s->i = transS[s->i];
        else if (c == 'H')
-           i = transH[i];
+           s->i = transH[s->i];
        else if (c == '-')
-           i = transminus[i];
+           s->i = transminus[s->i];
        else
-           i = 0;
-       if (i < 0)
+           s->i = 0;
+       if (s->i < 0)
            break;
        crReturn(1);                   /* get another character */
     }
 
-    vstrsize = 16;
-    vstring = smalloc(vstrsize);
-    strcpy(vstring, "SSH-");
-    vslen = 4;
-    i = 0;
+    s->vstrsize = 16;
+    s->vstring = smalloc(s->vstrsize);
+    strcpy(s->vstring, "SSH-");
+    s->vslen = 4;
+    s->i = 0;
     while (1) {
        crReturn(1);                   /* get another char */
-       if (vslen >= vstrsize - 1) {
-           vstrsize += 16;
-           vstring = srealloc(vstring, vstrsize);
+       if (s->vslen >= s->vstrsize - 1) {
+           s->vstrsize += 16;
+           s->vstring = srealloc(s->vstring, s->vstrsize);
        }
-       vstring[vslen++] = c;
-       if (i >= 0) {
+       s->vstring[s->vslen++] = c;
+       if (s->i >= 0) {
            if (c == '-') {
-               version[i] = '\0';
-               i = -1;
-           } else if (i < sizeof(version) - 1)
-               version[i++] = c;
+               s->version[s->i] = '\0';
+               s->i = -1;
+           } else if (s->i < sizeof(s->version) - 1)
+               s->version[s->i++] = c;
        } else if (c == '\n')
            break;
     }
 
-    ssh_agentfwd_enabled = FALSE;
-    rdpkt2_state.incoming_sequence = 0;
+    ssh->agentfwd_enabled = FALSE;
+    ssh->rdpkt2_state.incoming_sequence = 0;
 
-    vstring[vslen] = 0;
-    vlog = smalloc(20 + vslen);
-    vstring[strcspn (vstring, "\r\n")] = '\0'; /* remove end-of-line chars */
-    sprintf(vlog, "Server version: %s", vstring);
-    logevent(vlog);
-    ssh_detect_bugs(vstring);
-    sfree(vlog);
+    s->vstring[s->vslen] = 0;
+    s->vstring[strcspn(s->vstring, "\r\n")] = '\0';/* remove EOL chars */
+    {
+       char *vlog;
+       vlog = smalloc(20 + s->vslen);
+       sprintf(vlog, "Server version: %s", s->vstring);
+       logevent(vlog);
+       sfree(vlog);
+    }
+    ssh_detect_bugs(ssh, s->vstring);
 
     /*
      * Decide which SSH protocol version to support.
      */
 
     /* Anything strictly below "2.0" means protocol 1 is supported. */
-    proto1 = ssh_versioncmp(version, "2.0") < 0;
+    s->proto1 = ssh_versioncmp(s->version, "2.0") < 0;
     /* Anything greater or equal to "1.99" means protocol 2 is supported. */
-    proto2 = ssh_versioncmp(version, "1.99") >= 0;
+    s->proto2 = ssh_versioncmp(s->version, "1.99") >= 0;
 
-    if (cfg.sshprot == 0 && !proto1) {
+    if (cfg.sshprot == 0 && !s->proto1) {
        bombout(("SSH protocol version 1 required by user but not provided by server"));
        crReturn(0);
     }
-    if (cfg.sshprot == 3 && !proto2) {
+    if (cfg.sshprot == 3 && !s->proto2) {
        bombout(("SSH protocol version 2 required by user but not provided by server"));
        crReturn(0);
     }
 
-    if (proto2 && (cfg.sshprot >= 2 || !proto1)) {
+    if (s->proto2 && (cfg.sshprot >= 2 || !s->proto1)) {
        /*
         * Use v2 protocol.
         */
        char verstring[80], vlog[100];
        sprintf(verstring, "SSH-2.0-%s", sshver);
-       SHA_Init(&exhashbase);
+       SHA_Init(&ssh->exhashbase);
        /*
         * Hash our version string and their version string.
         */
-       sha_string(&exhashbase, verstring, strlen(verstring));
-       sha_string(&exhashbase, vstring, strcspn(vstring, "\r\n"));
+       sha_string(&ssh->exhashbase, verstring, strlen(verstring));
+       sha_string(&ssh->exhashbase, s->vstring, strcspn(s->vstring, "\r\n"));
        sprintf(vlog, "We claim version: %s", verstring);
        logevent(vlog);
        strcat(verstring, "\n");
        logevent("Using SSH protocol version 2");
-       sk_write(s, verstring, strlen(verstring));
-       ssh_protocol = ssh2_protocol;
-       ssh_version = 2;
-       s_rdpkt = ssh2_rdpkt;
+       sk_write(ssh->s, verstring, strlen(verstring));
+       ssh->protocol = ssh2_protocol;
+       ssh->version = 2;
+       ssh->s_rdpkt = ssh2_rdpkt;
     } else {
        /*
         * Use v1 protocol.
         */
        char verstring[80], vlog[100];
        sprintf(verstring, "SSH-%s-%s",
-               (ssh_versioncmp(version, "1.5") <= 0 ? version : "1.5"),
+               (ssh_versioncmp(s->version, "1.5") <= 0 ? s->version : "1.5"),
                sshver);
        sprintf(vlog, "We claim version: %s", verstring);
        logevent(vlog);
        strcat(verstring, "\n");
 
        logevent("Using SSH protocol version 1");
-       sk_write(s, verstring, strlen(verstring));
-       ssh_protocol = ssh1_protocol;
-       ssh_version = 1;
-       s_rdpkt = ssh1_rdpkt;
+       sk_write(ssh->s, verstring, strlen(verstring));
+       ssh->protocol = ssh1_protocol;
+       ssh->version = 1;
+       ssh->s_rdpkt = ssh1_rdpkt;
     }
-    ssh_state = SSH_STATE_BEFORE_SIZE;
+    ssh->state = SSH_STATE_BEFORE_SIZE;
 
-    sfree(vstring);
+    sfree(s->vstring);
 
     crFinish(0);
 }
 
-static void ssh_gotdata(unsigned char *data, int datalen)
+static void ssh_gotdata(Ssh ssh, unsigned char *data, int datalen)
 {
-    crBegin;
+    crBegin(ssh->ssh_gotdata_crstate);
 
     /*
      * To begin with, feed the characters one by one to the
@@ -1895,10 +1909,10 @@ static void ssh_gotdata(unsigned char *data, int datalen)
      * exchange and can move on to packet discipline.
      */
     while (1) {
-       int ret;
+       int ret;                       /* need not be kept across crReturn */
        if (datalen == 0)
            crReturnV;                 /* more data please */
-       ret = do_ssh_init(*data);
+       ret = do_ssh_init(ssh, *data);
        data++;
        datalen--;
        if (ret == 0)
@@ -1915,12 +1929,12 @@ static void ssh_gotdata(unsigned char *data, int datalen)
        crReturnV;
     while (1) {
        while (datalen > 0) {
-           if (s_rdpkt(&data, &datalen) == 0) {
-               if (ssh_state == SSH_STATE_CLOSED) {
+           if (ssh->s_rdpkt(ssh, &data, &datalen) == 0) {
+               if (ssh->state == SSH_STATE_CLOSED) {
                    return;
                }
-               ssh_protocol(NULL, 0, 1);
-               if (ssh_state == SSH_STATE_CLOSED) {
+               ssh->protocol(ssh, NULL, 0, 1);
+               if (ssh->state == SSH_STATE_CLOSED) {
                    return;
                }
            }
@@ -1933,10 +1947,11 @@ static void ssh_gotdata(unsigned char *data, int datalen)
 static int ssh_closing(Plug plug, char *error_msg, int error_code,
                       int calling_back)
 {
-    ssh_state = SSH_STATE_CLOSED;
-    if (s) {
-        sk_close(s);
-        s = NULL;
+    Ssh ssh = (Ssh) plug;
+    ssh->state = SSH_STATE_CLOSED;
+    if (ssh->s) {
+        sk_close(ssh->s);
+        ssh->s = NULL;
     }
     if (error_msg) {
        /* A socket error has occurred. */
@@ -1950,11 +1965,12 @@ static int ssh_closing(Plug plug, char *error_msg, int error_code,
 
 static int ssh_receive(Plug plug, int urgent, char *data, int len)
 {
-    ssh_gotdata(data, len);
-    if (ssh_state == SSH_STATE_CLOSED) {
-       if (s) {
-           sk_close(s);
-           s = NULL;
+    Ssh ssh = (Ssh) plug;
+    ssh_gotdata(ssh, data, len);
+    if (ssh->state == SSH_STATE_CLOSED) {
+       if (ssh->s) {
+           sk_close(ssh->s);
+           ssh->s = NULL;
        }
        return 0;
     }
@@ -1963,12 +1979,13 @@ static int ssh_receive(Plug plug, int urgent, char *data, int len)
 
 static void ssh_sent(Plug plug, int bufsize)
 {
+    Ssh ssh = (Ssh) plug;
     /*
      * If the send backlog on the SSH socket itself clears, we
      * should unthrottle the whole world if it was throttled.
      */
     if (bufsize < SSH_MAX_BACKLOG)
-       ssh_throttle_all(0, bufsize);
+       ssh_throttle_all(ssh, 0, bufsize);
 }
 
 /*
@@ -1977,37 +1994,27 @@ static void ssh_sent(Plug plug, int bufsize)
  * Also places the canonical host name into `realhost'. It must be
  * freed by the caller.
  */
-static char *connect_to_host(char *host, int port, char **realhost, int nodelay)
+static char *connect_to_host(Ssh ssh, char *host, int port,
+                            char **realhost, int nodelay)
 {
-    static struct plug_function_table fn_table = {
+    static const struct plug_function_table fn_table = {
        ssh_closing,
        ssh_receive,
        ssh_sent,
        NULL
-    }, *fn_table_ptr = &fn_table;
+    };
 
     SockAddr addr;
     char *err;
-#ifdef FWHACK
-    char *FWhost;
-    int FWport;
-#endif
 
-    savedhost = smalloc(1 + strlen(host));
-    if (!savedhost)
+    ssh->savedhost = smalloc(1 + strlen(host));
+    if (!ssh->savedhost)
        fatalbox("Out of memory");
-    strcpy(savedhost, host);
+    strcpy(ssh->savedhost, host);
 
     if (port < 0)
        port = 22;                     /* default ssh port */
-    savedport = port;
-
-#ifdef FWHACK
-    FWhost = host;
-    FWport = port;
-    host = FWSTR;
-    port = 23;
-#endif
+    ssh->savedport = port;
 
     /*
      * Try to find host.
@@ -2021,10 +2028,6 @@ static char *connect_to_host(char *host, int port, char **realhost, int nodelay)
     if ((err = sk_addr_error(addr)))
        return err;
 
-#ifdef FWHACK
-    *realhost = strdup(FWhost);
-#endif
-
     /*
      * Open socket.
      */
@@ -2034,37 +2037,28 @@ static char *connect_to_host(char *host, int port, char **realhost, int nodelay)
        sprintf(buf, "Connecting to %.100s port %d", addrbuf, port);
        logevent(buf);
     }
-    s = new_connection(addr, *realhost, port, 0, 1, nodelay, &fn_table_ptr);
-    if ((err = sk_socket_error(s))) {
-       s = NULL;
+    ssh->fn = &fn_table;
+    ssh->s = new_connection(addr, *realhost, port, 0, 1, nodelay, (Plug) ssh);
+    if ((err = sk_socket_error(ssh->s))) {
+       ssh->s = NULL;
        return err;
     }
 
-#ifdef FWHACK
-    sk_write(s, "connect ", 8);
-    sk_write(s, FWhost, strlen(FWhost));
-    {
-       char buf[20];
-       sprintf(buf, " %d\n", FWport);
-       sk_write(s, buf, strlen(buf));
-    }
-#endif
-
     return NULL;
 }
 
 /*
  * Throttle or unthrottle the SSH connection.
  */
-static void ssh1_throttle(int adjust)
+static void ssh1_throttle(Ssh ssh, int adjust)
 {
-    int old_count = ssh1_throttle_count;
-    ssh1_throttle_count += adjust;
-    assert(ssh1_throttle_count >= 0);
-    if (ssh1_throttle_count && !old_count) {
-       sk_set_frozen(s, 1);
-    } else if (!ssh1_throttle_count && old_count) {
-       sk_set_frozen(s, 0);
+    int old_count = ssh->v1_throttle_count;
+    ssh->v1_throttle_count += adjust;
+    assert(ssh->v1_throttle_count >= 0);
+    if (ssh->v1_throttle_count && !old_count) {
+       sk_set_frozen(ssh->s, 1);
+    } else if (!ssh->v1_throttle_count && old_count) {
+       sk_set_frozen(ssh->s, 0);
     }
 }
 
@@ -2072,18 +2066,18 @@ static void ssh1_throttle(int adjust)
  * Throttle or unthrottle _all_ local data streams (for when sends
  * on the SSH connection itself back up).
  */
-static void ssh_throttle_all(int enable, int bufsize)
+static void ssh_throttle_all(Ssh ssh, int enable, int bufsize)
 {
     int i;
     struct ssh_channel *c;
 
-    if (enable == ssh_throttled_all)
+    if (enable == ssh->throttled_all)
        return;
-    ssh_throttled_all = enable;
-    ssh_overall_bufsize = bufsize;
-    if (!ssh_channels)
+    ssh->throttled_all = enable;
+    ssh->overall_bufsize = bufsize;
+    if (!ssh->channels)
        return;
-    for (i = 0; NULL != (c = index234(ssh_channels, i)); i++) {
+    for (i = 0; NULL != (c = index234(ssh->channels, i)); i++) {
        switch (c->type) {
          case CHAN_MAINSESSION:
            /*
@@ -2104,21 +2098,17 @@ static void ssh_throttle_all(int enable, int bufsize)
 }
 
 /*
- * Username and password input, abstracted off into reusable
- * routines (hopefully even reusable between SSH1 and SSH2!).
+ * Username and password input, abstracted off into routines
+ * reusable in several places - even between SSH1 and SSH2.
  */
-static char *ssh_userpass_input_buffer;
-static int ssh_userpass_input_buflen;
-static int ssh_userpass_input_bufpos;
-static int ssh_userpass_input_echo;
 
 /* Set up a username or password input loop on a given buffer. */
-void setup_userpass_input(char *buffer, int buflen, int echo)
+void setup_userpass_input(Ssh ssh, char *buffer, int buflen, int echo)
 {
-    ssh_userpass_input_buffer = buffer;
-    ssh_userpass_input_buflen = buflen;
-    ssh_userpass_input_bufpos = 0;
-    ssh_userpass_input_echo = echo;
+    ssh->userpass_input_buffer = buffer;
+    ssh->userpass_input_buflen = buflen;
+    ssh->userpass_input_bufpos = 0;
+    ssh->userpass_input_echo = echo;
 }
 
 /*
@@ -2127,7 +2117,7 @@ void setup_userpass_input(char *buffer, int buflen, int echo)
  * buffer), <0 for failure (user hit ^C/^D, bomb out and exit), 0
  * for inconclusive (keep waiting for more input please).
  */
-int process_userpass_input(unsigned char *in, int inlen)
+int process_userpass_input(Ssh ssh, unsigned char *in, int inlen)
 {
     char c;
 
@@ -2135,24 +2125,24 @@ int process_userpass_input(unsigned char *in, int inlen)
        switch (c = *in++) {
          case 10:
          case 13:
-           ssh_userpass_input_buffer[ssh_userpass_input_bufpos] = 0;
-           ssh_userpass_input_buffer[ssh_userpass_input_buflen-1] = 0;
+           ssh->userpass_input_buffer[ssh->userpass_input_bufpos] = 0;
+           ssh->userpass_input_buffer[ssh->userpass_input_buflen-1] = 0;
            return +1;
            break;
          case 8:
          case 127:
-           if (ssh_userpass_input_bufpos > 0) {
-               if (ssh_userpass_input_echo)
-                   c_write_str("\b \b");
-               ssh_userpass_input_bufpos--;
+           if (ssh->userpass_input_bufpos > 0) {
+               if (ssh->userpass_input_echo)
+                   c_write_str(ssh, "\b \b");
+               ssh->userpass_input_bufpos--;
            }
            break;
          case 21:
          case 27:
-           while (ssh_userpass_input_bufpos > 0) {
-               if (ssh_userpass_input_echo)
-                   c_write_str("\b \b");
-               ssh_userpass_input_bufpos--;
+           while (ssh->userpass_input_bufpos > 0) {
+               if (ssh->userpass_input_echo)
+                   c_write_str(ssh, "\b \b");
+               ssh->userpass_input_bufpos--;
            }
            break;
          case 3:
@@ -2162,10 +2152,10 @@ int process_userpass_input(unsigned char *in, int inlen)
          default:
            if (((c >= ' ' && c <= '~') ||
                 ((unsigned char) c >= 160))
-               && ssh_userpass_input_bufpos < ssh_userpass_input_buflen-1) {
-               ssh_userpass_input_buffer[ssh_userpass_input_bufpos++] = c;
-               if (ssh_userpass_input_echo)
-                   c_write(&c, 1);
+               && ssh->userpass_input_bufpos < ssh->userpass_input_buflen-1) {
+               ssh->userpass_input_buffer[ssh->userpass_input_bufpos++] = c;
+               if (ssh->userpass_input_echo)
+                   c_write(ssh, &c, 1);
            }
            break;
        }
@@ -2176,39 +2166,55 @@ int process_userpass_input(unsigned char *in, int inlen)
 /*
  * Handle the key exchange and user authentication phases.
  */
-static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
+static int do_ssh1_login(Ssh ssh, unsigned char *in, int inlen, int ispkt)
 {
     int i, j;
-    static int len;
-    static unsigned char *rsabuf, *keystr1, *keystr2;
     unsigned char cookie[8];
     struct RSAKey servkey, hostkey;
     struct MD5Context md5c;
-    static unsigned long supported_ciphers_mask, supported_auths_mask;
-    static int tried_publickey, tried_agent;
-    static int tis_auth_refused, ccard_auth_refused;
-    static unsigned char session_id[16];
-    static int cipher_type;
-    static char username[100];
-    static void *publickey_blob;
-    int publickey_bloblen;
-
-    crBegin;
+    struct do_ssh1_login_state {
+       int len;
+       unsigned char *rsabuf, *keystr1, *keystr2;
+       unsigned long supported_ciphers_mask, supported_auths_mask;
+       int tried_publickey, tried_agent;
+       int tis_auth_refused, ccard_auth_refused;
+       unsigned char session_id[16];
+       int cipher_type;
+       char username[100];
+       void *publickey_blob;
+       int publickey_bloblen;
+       char password[100];
+       char prompt[200];
+       int pos;
+       char c;
+       int pwpkt_type;
+       unsigned char request[5], *response, *p;
+       int responselen;
+       int keyi, nkeys;
+       int authed;
+       struct RSAKey key;
+       Bignum challenge;
+       char *commentp;
+       int commentlen;
+    };
+    crState(do_ssh1_login_state);
+
+    crBegin(ssh->do_ssh1_login_crstate);
 
     if (!ispkt)
        crWaitUntil(ispkt);
 
-    if (pktin.type != SSH1_SMSG_PUBLIC_KEY) {
+    if (ssh->pktin.type != SSH1_SMSG_PUBLIC_KEY) {
        bombout(("Public key packet not received"));
        crReturn(0);
     }
 
     logevent("Received public keys");
 
-    memcpy(cookie, pktin.body, 8);
+    memcpy(cookie, ssh->pktin.body, 8);
 
-    i = makekey(pktin.body + 8, &servkey, &keystr1, 0);
-    j = makekey(pktin.body + 8 + i, &hostkey, &keystr2, 0);
+    i = makekey(ssh->pktin.body + 8, &servkey, &s->keystr1, 0);
+    j = makekey(ssh->pktin.body + 8 + i, &hostkey, &s->keystr2, 0);
 
     /*
      * Log the host key fingerprint.
@@ -2223,27 +2229,27 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
        logevent(logmsg);
     }
 
-    ssh1_remote_protoflags = GET_32BIT(pktin.body + 8 + i + j);
-    supported_ciphers_mask = GET_32BIT(pktin.body + 12 + i + j);
-    supported_auths_mask = GET_32BIT(pktin.body + 16 + i + j);
+    ssh->v1_remote_protoflags = GET_32BIT(ssh->pktin.body + 8 + i + j);
+    s->supported_ciphers_mask = GET_32BIT(ssh->pktin.body + 12 + i + j);
+    s->supported_auths_mask = GET_32BIT(ssh->pktin.body + 16 + i + j);
 
-    ssh1_local_protoflags =
-       ssh1_remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED;
-    ssh1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER;
+    ssh->v1_local_protoflags =
+       ssh->v1_remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED;
+    ssh->v1_local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER;
 
     MD5Init(&md5c);
-    MD5Update(&md5c, keystr2, hostkey.bytes);
-    MD5Update(&md5c, keystr1, servkey.bytes);
-    MD5Update(&md5c, pktin.body, 8);
-    MD5Final(session_id, &md5c);
+    MD5Update(&md5c, s->keystr2, hostkey.bytes);
+    MD5Update(&md5c, s->keystr1, servkey.bytes);
+    MD5Update(&md5c, ssh->pktin.body, 8);
+    MD5Final(s->session_id, &md5c);
 
     for (i = 0; i < 32; i++)
-       session_key[i] = random_byte();
+       ssh->session_key[i] = random_byte();
 
-    len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);
+    s->len = (hostkey.bytes > servkey.bytes ? hostkey.bytes : servkey.bytes);
 
-    rsabuf = smalloc(len);
-    if (!rsabuf)
+    s->rsabuf = smalloc(s->len);
+    if (!s->rsabuf)
        fatalbox("Out of memory");
 
     /*
@@ -2260,23 +2266,23 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
            fatalbox("Out of memory");
        rsastr_fmt(keystr, &hostkey);
        rsa_fingerprint(fingerprint, sizeof(fingerprint), &hostkey);
-       verify_ssh_host_key(savedhost, savedport, "rsa", keystr,
+       verify_ssh_host_key(ssh->savedhost, ssh->savedport, "rsa", keystr,
                            fingerprint);
        sfree(keystr);
     }
 
     for (i = 0; i < 32; i++) {
-       rsabuf[i] = session_key[i];
+       s->rsabuf[i] = ssh->session_key[i];
        if (i < 16)
-           rsabuf[i] ^= session_id[i];
+           s->rsabuf[i] ^= s->session_id[i];
     }
 
     if (hostkey.bytes > servkey.bytes) {
-       rsaencrypt(rsabuf, 32, &servkey);
-       rsaencrypt(rsabuf, servkey.bytes, &hostkey);
+       rsaencrypt(s->rsabuf, 32, &servkey);
+       rsaencrypt(s->rsabuf, servkey.bytes, &hostkey);
     } else {
-       rsaencrypt(rsabuf, 32, &hostkey);
-       rsaencrypt(rsabuf, hostkey.bytes, &servkey);
+       rsaencrypt(s->rsabuf, 32, &hostkey);
+       rsaencrypt(s->rsabuf, hostkey.bytes, &servkey);
     }
 
     logevent("Encrypted session key");
@@ -2284,6 +2290,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
     {
        int cipher_chosen = 0, warn = 0;
        char *cipher_string = NULL;
+       int i;
        for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) {
            int next_cipher = cfg.ssh_cipherlist[i];
            if (next_cipher == CIPHER_WARN) {
@@ -2294,19 +2301,19 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                logevent("AES not supported in SSH1, skipping");
            } else {
                switch (next_cipher) {
-                 case CIPHER_3DES:     cipher_type = SSH_CIPHER_3DES;
+                 case CIPHER_3DES:     s->cipher_type = SSH_CIPHER_3DES;
                                        cipher_string = "3DES"; break;
-                 case CIPHER_BLOWFISH: cipher_type = SSH_CIPHER_BLOWFISH;
+                 case CIPHER_BLOWFISH: s->cipher_type = SSH_CIPHER_BLOWFISH;
                                        cipher_string = "Blowfish"; break;
-                 case CIPHER_DES:      cipher_type = SSH_CIPHER_DES;
+                 case CIPHER_DES:      s->cipher_type = SSH_CIPHER_DES;
                                        cipher_string = "single-DES"; break;
                }
-               if (supported_ciphers_mask & (1 << cipher_type))
+               if (s->supported_ciphers_mask & (1 << s->cipher_type))
                    cipher_chosen = 1;
            }
        }
        if (!cipher_chosen) {
-           if ((supported_ciphers_mask & (1 << SSH_CIPHER_3DES)) == 0)
+           if ((s->supported_ciphers_mask & (1 << SSH_CIPHER_3DES)) == 0)
                bombout(("Server violates SSH 1 protocol by not "
                         "supporting 3DES encryption"));
            else
@@ -2320,7 +2327,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
            askcipher(cipher_string, 0);
     }
 
-    switch (cipher_type) {
+    switch (s->cipher_type) {
       case SSH_CIPHER_3DES:
        logevent("Using 3DES encryption");
        break;
@@ -2332,24 +2339,25 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
        break;
     }
 
-    send_packet(SSH1_CMSG_SESSION_KEY,
-               PKT_CHAR, cipher_type,
+    send_packet(ssh, SSH1_CMSG_SESSION_KEY,
+               PKT_CHAR, s->cipher_type,
                PKT_DATA, cookie, 8,
-               PKT_CHAR, (len * 8) >> 8, PKT_CHAR, (len * 8) & 0xFF,
-               PKT_DATA, rsabuf, len,
-               PKT_INT, ssh1_local_protoflags, PKT_END);
+               PKT_CHAR, (s->len * 8) >> 8, PKT_CHAR, (s->len * 8) & 0xFF,
+               PKT_DATA, s->rsabuf, s->len,
+               PKT_INT, ssh->v1_local_protoflags, PKT_END);
 
     logevent("Trying to enable encryption...");
 
-    sfree(rsabuf);
+    sfree(s->rsabuf);
 
-    cipher = cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 :
-       cipher_type == SSH_CIPHER_DES ? &ssh_des : &ssh_3des;
-    cipher->sesskey(session_key);
+    ssh->cipher = (s->cipher_type == SSH_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 :
+                  s->cipher_type == SSH_CIPHER_DES ? &ssh_des :
+                  &ssh_3des);
+    ssh->cipher->sesskey(ssh->session_key);
 
     crWaitUntil(ispkt);
 
-    if (pktin.type != SSH1_SMSG_SUCCESS) {
+    if (ssh->pktin.type != SSH1_SMSG_SUCCESS) {
        bombout(("Encryption not successfully enabled"));
        crReturn(0);
     }
@@ -2361,151 +2369,140 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
        if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
            if (ssh_get_line && !ssh_getline_pw_only) {
                if (!ssh_get_line("login as: ",
-                                 username, sizeof(username), FALSE)) {
+                                 s->username, sizeof(s->username), FALSE)) {
                    /*
                     * get_line failed to get a username.
                     * Terminate.
                     */
                    logevent("No username provided. Abandoning session.");
-                   ssh_state = SSH_STATE_CLOSED;
+                   ssh->state = SSH_STATE_CLOSED;
                    crReturn(1);
                }
            } else {
-               static int ret;
-               c_write_str("login as: ");
-               ssh_send_ok = 1;
+               int ret;               /* need not be kept over crReturn */
+               c_write_str(ssh, "login as: ");
+               ssh->send_ok = 1;
 
-               setup_userpass_input(username, sizeof(username), 1);
+               setup_userpass_input(ssh, s->username, sizeof(s->username), 1);
                do {
                    crWaitUntil(!ispkt);
-                   ret = process_userpass_input(in, inlen);
+                   ret = process_userpass_input(ssh, in, inlen);
                } while (ret == 0);
                if (ret < 0)
                    cleanup_exit(0);
-               c_write_str("\r\n");
+               c_write_str(ssh, "\r\n");
            }
        } else {
-           strncpy(username, cfg.username, sizeof(username));
-           username[sizeof(username)-1] = '\0';
+           strncpy(s->username, cfg.username, sizeof(s->username));
+           s->username[sizeof(s->username)-1] = '\0';
        }
 
-       send_packet(SSH1_CMSG_USER, PKT_STR, username, PKT_END);
+       send_packet(ssh, SSH1_CMSG_USER, PKT_STR, s->username, PKT_END);
        {
-           char userlog[22 + sizeof(username)];
-           sprintf(userlog, "Sent username \"%s\"", username);
+           char userlog[22 + sizeof(s->username)];
+           sprintf(userlog, "Sent username \"%s\"", s->username);
            logevent(userlog);
            if (flags & FLAG_INTERACTIVE &&
                (!((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)))) {
                strcat(userlog, "\r\n");
-               c_write_str(userlog);
+               c_write_str(ssh, userlog);
            }
        }
     }
 
     crWaitUntil(ispkt);
 
-    if ((ssh_remote_bugs & BUG_CHOKES_ON_RSA)) {
+    if ((ssh->remote_bugs & BUG_CHOKES_ON_RSA)) {
        /* We must not attempt PK auth. Pretend we've already tried it. */
-       tried_publickey = tried_agent = 1;
+       s->tried_publickey = s->tried_agent = 1;
     } else {
-       tried_publickey = tried_agent = 0;
+       s->tried_publickey = s->tried_agent = 0;
     }
-    tis_auth_refused = ccard_auth_refused = 0;
+    s->tis_auth_refused = s->ccard_auth_refused = 0;
     /* Load the public half of cfg.keyfile so we notice if it's in Pageant */
     if (*cfg.keyfile) {
-       if (!rsakey_pubblob(cfg.keyfile, &publickey_blob, &publickey_bloblen))
-           publickey_blob = NULL;
+       if (!rsakey_pubblob(cfg.keyfile,
+                           &s->publickey_blob, &s->publickey_bloblen))
+           s->publickey_blob = NULL;
     } else
-       publickey_blob = NULL;
+       s->publickey_blob = NULL;
 
-    while (pktin.type == SSH1_SMSG_FAILURE) {
-       static char password[100];
-       static char prompt[200];
-       static int pos;
-       static char c;
-       static int pwpkt_type;
-       pwpkt_type = SSH1_CMSG_AUTH_PASSWORD;
+    while (ssh->pktin.type == SSH1_SMSG_FAILURE) {
+       s->pwpkt_type = SSH1_CMSG_AUTH_PASSWORD;
 
-       if (agent_exists() && !tried_agent) {
+       if (agent_exists() && !s->tried_agent) {
            /*
             * Attempt RSA authentication using Pageant.
             */
-           static unsigned char request[5], *response, *p;
-           static int responselen;
-           static int i, nkeys;
-           static int authed = FALSE;
            void *r;
 
-           tried_agent = 1;
+           s->authed = FALSE;
+           s->tried_agent = 1;
            logevent("Pageant is running. Requesting keys.");
 
            /* Request the keys held by the agent. */
-           PUT_32BIT(request, 1);
-           request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
-           agent_query(request, 5, &r, &responselen);
-           response = (unsigned char *) r;
-           if (response && responselen >= 5 &&
-               response[4] == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
-               p = response + 5;
-               nkeys = GET_32BIT(p);
-               p += 4;
+           PUT_32BIT(s->request, 1);
+           s->request[4] = SSH1_AGENTC_REQUEST_RSA_IDENTITIES;
+           agent_query(s->request, 5, &r, &s->responselen);
+           s->response = (unsigned char *) r;
+           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->p += 4;
                {
                    char buf[64];
-                   sprintf(buf, "Pageant has %d SSH1 keys", nkeys);
+                   sprintf(buf, "Pageant has %d SSH1 keys", s->nkeys);
                    logevent(buf);
                }
-               for (i = 0; i < nkeys; i++) {
-                   static struct RSAKey key;
-                   static Bignum challenge;
-                   static char *commentp;
-                   static int commentlen;
-
+               for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
                    {
                        char buf[64];
-                       sprintf(buf, "Trying Pageant key #%d", i);
+                       sprintf(buf, "Trying Pageant key #%d", s->keyi);
                        logevent(buf);
                    }
-                   if (publickey_blob &&
-                       !memcmp(p, publickey_blob, publickey_bloblen)) {
+                   if (s->publickey_blob &&
+                       !memcmp(s->p, s->publickey_blob,
+                               s->publickey_bloblen)) {
                        logevent("This key matches configured key file");
-                       tried_publickey = 1;
+                       s->tried_publickey = 1;
                    }
-                   p += 4;
-                   p += ssh1_read_bignum(p, &key.exponent);
-                   p += ssh1_read_bignum(p, &key.modulus);
-                   commentlen = GET_32BIT(p);
-                   p += 4;
-                   commentp = p;
-                   p += commentlen;
-                   send_packet(SSH1_CMSG_AUTH_RSA,
-                               PKT_BIGNUM, key.modulus, PKT_END);
+                   s->p += 4;
+                   s->p += ssh1_read_bignum(s->p, &s->key.exponent);
+                   s->p += ssh1_read_bignum(s->p, &s->key.modulus);
+                   s->commentlen = GET_32BIT(s->p);
+                   s->p += 4;
+                   s->commentp = s->p;
+                   s->p += s->commentlen;
+                   send_packet(ssh, SSH1_CMSG_AUTH_RSA,
+                               PKT_BIGNUM, s->key.modulus, PKT_END);
                    crWaitUntil(ispkt);
-                   if (pktin.type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
+                   if (ssh->pktin.type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
                        logevent("Key refused");
                        continue;
                    }
                    logevent("Received RSA challenge");
-                   ssh1_read_bignum(pktin.body, &challenge);
+                   ssh1_read_bignum(ssh->pktin.body, &s->challenge);
                    {
                        char *agentreq, *q, *ret;
                        void *vret;
                        int len, retlen;
                        len = 1 + 4;   /* message type, bit count */
-                       len += ssh1_bignum_length(key.exponent);
-                       len += ssh1_bignum_length(key.modulus);
-                       len += ssh1_bignum_length(challenge);
+                       len += ssh1_bignum_length(s->key.exponent);
+                       len += ssh1_bignum_length(s->key.modulus);
+                       len += ssh1_bignum_length(s->challenge);
                        len += 16;     /* session id */
                        len += 4;      /* response format */
                        agentreq = smalloc(4 + len);
                        PUT_32BIT(agentreq, len);
                        q = agentreq + 4;
                        *q++ = SSH1_AGENTC_RSA_CHALLENGE;
-                       PUT_32BIT(q, bignum_bitcount(key.modulus));
+                       PUT_32BIT(q, bignum_bitcount(s->key.modulus));
                        q += 4;
-                       q += ssh1_write_bignum(q, key.exponent);
-                       q += ssh1_write_bignum(q, key.modulus);
-                       q += ssh1_write_bignum(q, challenge);
-                       memcpy(q, session_id, 16);
+                       q += ssh1_write_bignum(q, s->key.exponent);
+                       q += ssh1_write_bignum(q, s->key.modulus);
+                       q += ssh1_write_bignum(q, s->challenge);
+                       memcpy(q, s->session_id, 16);
                        q += 16;
                        PUT_32BIT(q, 1);        /* response format */
                        agent_query(agentreq, len + 4, &vret, &retlen);
@@ -2514,21 +2511,22 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                        if (ret) {
                            if (ret[4] == SSH1_AGENT_RSA_RESPONSE) {
                                logevent("Sending Pageant's response");
-                               send_packet(SSH1_CMSG_AUTH_RSA_RESPONSE,
+                               send_packet(ssh, SSH1_CMSG_AUTH_RSA_RESPONSE,
                                            PKT_DATA, ret + 5, 16,
                                            PKT_END);
                                sfree(ret);
                                crWaitUntil(ispkt);
-                               if (pktin.type == SSH1_SMSG_SUCCESS) {
+                               if (ssh->pktin.type == SSH1_SMSG_SUCCESS) {
                                    logevent
                                        ("Pageant's response accepted");
                                    if (flags & FLAG_VERBOSE) {
-                                       c_write_str
-                                           ("Authenticated using RSA key \"");
-                                       c_write(commentp, commentlen);
-                                       c_write_str("\" from agent\r\n");
+                                       c_write_str(ssh, "Authenticated using"
+                                                   " RSA key \"");
+                                       c_write(ssh, s->commentp,
+                                               s->commentlen);
+                                       c_write_str(ssh, "\" from agent\r\n");
                                    }
-                                   authed = TRUE;
+                                   s->authed = TRUE;
                                } else
                                    logevent
                                        ("Pageant's response not accepted");
@@ -2541,87 +2539,81 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                            logevent("No reply received from Pageant");
                        }
                    }
-                   freebn(key.exponent);
-                   freebn(key.modulus);
-                   freebn(challenge);
-                   if (authed)
+                   freebn(s->key.exponent);
+                   freebn(s->key.modulus);
+                   freebn(s->challenge);
+                   if (s->authed)
                        break;
                }
            }
-           if (authed)
+           if (s->authed)
                break;
        }
-       if (*cfg.keyfile && !tried_publickey)
-           pwpkt_type = SSH1_CMSG_AUTH_RSA;
+       if (*cfg.keyfile && !s->tried_publickey)
+           s->pwpkt_type = SSH1_CMSG_AUTH_RSA;
 
        if (cfg.try_tis_auth &&
-           (supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
-           !tis_auth_refused) {
-           pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
+           (s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
+           !s->tis_auth_refused) {
+           s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE;
            logevent("Requested TIS authentication");
-           send_packet(SSH1_CMSG_AUTH_TIS, PKT_END);
+           send_packet(ssh, SSH1_CMSG_AUTH_TIS, PKT_END);
            crWaitUntil(ispkt);
-           if (pktin.type != SSH1_SMSG_AUTH_TIS_CHALLENGE) {
+           if (ssh->pktin.type != SSH1_SMSG_AUTH_TIS_CHALLENGE) {
                logevent("TIS authentication declined");
                if (flags & FLAG_INTERACTIVE)
-                   c_write_str("TIS authentication refused.\r\n");
-               tis_auth_refused = 1;
+                   c_write_str(ssh, "TIS authentication refused.\r\n");
+               s->tis_auth_refused = 1;
                continue;
            } else {
-               int challengelen = ((pktin.body[0] << 24) |
-                                   (pktin.body[1] << 16) |
-                                   (pktin.body[2] << 8) |
-                                   (pktin.body[3]));
+               int challengelen = GET_32BIT(ssh->pktin.body);
                logevent("Received TIS challenge");
-               if (challengelen > sizeof(prompt) - 1)
-                   challengelen = sizeof(prompt) - 1;  /* prevent overrun */
-               memcpy(prompt, pktin.body + 4, challengelen);
+               if (challengelen > sizeof(s->prompt) - 1)
+                   challengelen = sizeof(s->prompt) - 1;/* prevent overrun */
+               memcpy(s->prompt, ssh->pktin.body + 4, challengelen);
                /* Prompt heuristic comes from OpenSSH */
-               strncpy(prompt + challengelen,
-                       memchr(prompt, '\n', challengelen) ?
+               strncpy(s->prompt + challengelen,
+                       memchr(s->prompt, '\n', challengelen) ?
                        "": "\r\nResponse: ",
-                       (sizeof prompt) - challengelen);
-               prompt[(sizeof prompt) - 1] = '\0';
+                       (sizeof s->prompt) - challengelen);
+               s->prompt[(sizeof s->prompt) - 1] = '\0';
            }
        }
        if (cfg.try_tis_auth &&
-           (supported_auths_mask & (1 << SSH1_AUTH_CCARD)) &&
-           !ccard_auth_refused) {
-           pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
+           (s->supported_auths_mask & (1 << SSH1_AUTH_CCARD)) &&
+           !s->ccard_auth_refused) {
+           s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE;
            logevent("Requested CryptoCard authentication");
-           send_packet(SSH1_CMSG_AUTH_CCARD, PKT_END);
+           send_packet(ssh, SSH1_CMSG_AUTH_CCARD, PKT_END);
            crWaitUntil(ispkt);
-           if (pktin.type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
+           if (ssh->pktin.type != SSH1_SMSG_AUTH_CCARD_CHALLENGE) {
                logevent("CryptoCard authentication declined");
-               c_write_str("CryptoCard authentication refused.\r\n");
-               ccard_auth_refused = 1;
+               c_write_str(ssh, "CryptoCard authentication refused.\r\n");
+               s->ccard_auth_refused = 1;
                continue;
            } else {
-               int challengelen = ((pktin.body[0] << 24) |
-                                   (pktin.body[1] << 16) |
-                                   (pktin.body[2] << 8) |
-                                   (pktin.body[3]));
+               int challengelen = GET_32BIT(ssh->pktin.body);
                logevent("Received CryptoCard challenge");
-               if (challengelen > sizeof(prompt) - 1)
-                   challengelen = sizeof(prompt) - 1;  /* prevent overrun */
-               memcpy(prompt, pktin.body + 4, challengelen);
-               strncpy(prompt + challengelen,
-                       memchr(prompt, '\n', challengelen) ?
+               if (challengelen > sizeof(s->prompt) - 1)
+                   challengelen = sizeof(s->prompt) - 1;/* prevent overrun */
+               memcpy(s->prompt, ssh->pktin.body + 4, challengelen);
+               strncpy(s->prompt + challengelen,
+                       memchr(s->prompt, '\n', challengelen) ?
                        "" : "\r\nResponse: ",
-                       sizeof(prompt) - challengelen);
-               prompt[sizeof(prompt) - 1] = '\0';
+                       sizeof(s->prompt) - challengelen);
+               s->prompt[sizeof(s->prompt) - 1] = '\0';
            }
        }
-       if (pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
-           sprintf(prompt, "%.90s@%.90s's password: ",
-                   username, savedhost);
+       if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
+           sprintf(s->prompt, "%.90s@%.90s's password: ",
+                   s->username, ssh->savedhost);
        }
-       if (pwpkt_type == SSH1_CMSG_AUTH_RSA) {
+       if (s->pwpkt_type == SSH1_CMSG_AUTH_RSA) {
            char *comment = NULL;
            int type;
            char msgbuf[256];
            if (flags & FLAG_VERBOSE)
-               c_write_str("Trying public key authentication.\r\n");
+               c_write_str(ssh, "Trying public key authentication.\r\n");
            sprintf(msgbuf, "Trying public key \"%.200s\"", cfg.keyfile);
            logevent(msgbuf);
            type = key_type(cfg.keyfile);
@@ -2629,17 +2621,17 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                sprintf(msgbuf, "Key is of wrong type (%s)",
                        key_type_to_str(type));
                logevent(msgbuf);
-               c_write_str(msgbuf);
-               c_write_str("\r\n");
-               tried_publickey = 1;
+               c_write_str(ssh, msgbuf);
+               c_write_str(ssh, "\r\n");
+               s->tried_publickey = 1;
                continue;
            }
            if (!rsakey_encrypted(cfg.keyfile, &comment)) {
                if (flags & FLAG_VERBOSE)
-                   c_write_str("No passphrase required.\r\n");
+                   c_write_str(ssh, "No passphrase required.\r\n");
                goto tryauth;
            }
-           sprintf(prompt, "Passphrase for key \"%.100s\": ", comment);
+           sprintf(s->prompt, "Passphrase for key \"%.100s\": ", comment);
            sfree(comment);
        }
 
@@ -2649,108 +2641,116 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
         * authentication.
         */
        if (ssh_get_line) {
-           if (!ssh_get_line(prompt, password, sizeof(password), TRUE)) {
+           if (!ssh_get_line(s->prompt, s->password,
+                             sizeof(s->password), TRUE)) {
                /*
                 * get_line failed to get a password (for example
                 * because one was supplied on the command line
                 * which has already failed to work). Terminate.
                 */
-               send_packet(SSH1_MSG_DISCONNECT,
+               send_packet(ssh, SSH1_MSG_DISCONNECT,
                            PKT_STR, "No more passwords available to try",
                            PKT_END);
                logevent("Unable to authenticate");
                connection_fatal("Unable to authenticate");
-               ssh_state = SSH_STATE_CLOSED;
+               ssh->state = SSH_STATE_CLOSED;
                crReturn(1);
            }
        } else {
            /* Prompt may have come from server. We've munged it a bit, so
             * we know it to be zero-terminated at least once. */
-           static int ret;
-           c_write_untrusted(prompt, strlen(prompt));
-           pos = 0;
+           int ret;                   /* need not be saved over crReturn */
+           c_write_untrusted(ssh, s->prompt, strlen(s->prompt));
+           s->pos = 0;
 
-           setup_userpass_input(password, sizeof(password), 0);
+           setup_userpass_input(ssh, s->password, sizeof(s->password), 0);
            do {
                crWaitUntil(!ispkt);
-               ret = process_userpass_input(in, inlen);
+               ret = process_userpass_input(ssh, in, inlen);
            } while (ret == 0);
            if (ret < 0)
                cleanup_exit(0);
-           c_write_str("\r\n");
+           c_write_str(ssh, "\r\n");
        }
 
       tryauth:
-       if (pwpkt_type == SSH1_CMSG_AUTH_RSA) {
+       if (s->pwpkt_type == SSH1_CMSG_AUTH_RSA) {
            /*
             * Try public key authentication with the specified
             * key file.
             */
-           static struct RSAKey pubkey;
-           static Bignum challenge, response;
-           static int i;
-           static unsigned char buffer[32];
-
-           tried_publickey = 1;
-           i = loadrsakey(cfg.keyfile, &pubkey, password);
-           if (i == 0) {
-               c_write_str("Couldn't load private key from ");
-               c_write_str(cfg.keyfile);
-               c_write_str(".\r\n");
-               continue;              /* go and try password */
-           }
-           if (i == -1) {
-               c_write_str("Wrong passphrase.\r\n");
-               tried_publickey = 0;
-               continue;              /* try again */
+           s->tried_publickey = 1;
+           
+           {
+               int ret = loadrsakey(cfg.keyfile, &s->key, s->password);
+               if (ret == 0) {
+                   c_write_str(ssh, "Couldn't load private key from ");
+                   c_write_str(ssh, cfg.keyfile);
+                   c_write_str(ssh, ".\r\n");
+                   continue;          /* go and try password */
+               }
+               if (ret == -1) {
+                   c_write_str(ssh, "Wrong passphrase.\r\n");
+                   s->tried_publickey = 0;
+                   continue;          /* try again */
+               }
            }
 
            /*
             * Send a public key attempt.
             */
-           send_packet(SSH1_CMSG_AUTH_RSA,
-                       PKT_BIGNUM, pubkey.modulus, PKT_END);
+           send_packet(ssh, SSH1_CMSG_AUTH_RSA,
+                       PKT_BIGNUM, s->key.modulus, PKT_END);
 
            crWaitUntil(ispkt);
-           if (pktin.type == SSH1_SMSG_FAILURE) {
-               c_write_str("Server refused our public key.\r\n");
+           if (ssh->pktin.type == SSH1_SMSG_FAILURE) {
+               c_write_str(ssh, "Server refused our public key.\r\n");
                continue;              /* go and try password */
            }
-           if (pktin.type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
+           if (ssh->pktin.type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
                bombout(("Bizarre response to offer of public key"));
                crReturn(0);
            }
-           ssh1_read_bignum(pktin.body, &challenge);
-           response = rsadecrypt(challenge, &pubkey);
-           freebn(pubkey.private_exponent);    /* burn the evidence */
 
-           for (i = 0; i < 32; i++) {
-               buffer[i] = bignum_byte(response, 31 - i);
-           }
+           {
+               int i;
+               unsigned char buffer[32];
+               Bignum challenge, response;
+
+               ssh1_read_bignum(ssh->pktin.body, &challenge);
+               response = rsadecrypt(challenge, &s->key);
+               freebn(s->key.private_exponent);/* burn the evidence */
 
-           MD5Init(&md5c);
-           MD5Update(&md5c, buffer, 32);
-           MD5Update(&md5c, session_id, 16);
-           MD5Final(buffer, &md5c);
+               for (i = 0; i < 32; i++) {
+                   buffer[i] = bignum_byte(response, 31 - i);
+               }
+
+               MD5Init(&md5c);
+               MD5Update(&md5c, buffer, 32);
+               MD5Update(&md5c, s->session_id, 16);
+               MD5Final(buffer, &md5c);
 
-           send_packet(SSH1_CMSG_AUTH_RSA_RESPONSE,
-                       PKT_DATA, buffer, 16, PKT_END);
+               send_packet(ssh, SSH1_CMSG_AUTH_RSA_RESPONSE,
+                           PKT_DATA, buffer, 16, PKT_END);
+
+               freebn(challenge);
+               freebn(response);
+           }
 
            crWaitUntil(ispkt);
-           if (pktin.type == SSH1_SMSG_FAILURE) {
+           if (ssh->pktin.type == SSH1_SMSG_FAILURE) {
                if (flags & FLAG_VERBOSE)
-                   c_write_str
-                       ("Failed to authenticate with our public key.\r\n");
+                   c_write_str(ssh, "Failed to authenticate with"
+                               " our public key.\r\n");
                continue;              /* go and try password */
-           } else if (pktin.type != SSH1_SMSG_SUCCESS) {
-               bombout(
-                       ("Bizarre response to RSA authentication response"));
+           } else if (ssh->pktin.type != SSH1_SMSG_SUCCESS) {
+               bombout(("Bizarre response to RSA authentication response"));
                crReturn(0);
            }
 
            break;                     /* we're through! */
        } else {
-           if (pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
+           if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) {
                /*
                 * Defence against traffic analysis: we send a
                 * whole bunch of packets containing strings of
@@ -2789,7 +2789,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                 * For this server we are left with no defences
                 * against password length sniffing.
                 */
-               if (!(ssh_remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
+               if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) {
                    /*
                     * The server can deal with SSH1_MSG_IGNORE, so
                     * we can use the primary defence.
@@ -2797,7 +2797,7 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                    int bottom, top, pwlen, i;
                    char *randomstr;
 
-                   pwlen = strlen(password);
+                   pwlen = strlen(s->password);
                    if (pwlen < 16) {
                        bottom = 0;    /* zero length passwords are OK! :-) */
                        top = 15;
@@ -2812,8 +2812,8 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
 
                    for (i = bottom; i <= top; i++) {
                        if (i == pwlen)
-                           defer_packet(pwpkt_type, PKT_STR, password,
-                                        PKT_END);
+                           defer_packet(ssh, s->pwpkt_type,
+                                        PKT_STR, s->password, PKT_END);
                        else {
                            for (j = 0; j < i; j++) {
                                do {
@@ -2821,37 +2821,37 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                                } while (randomstr[j] == '\0');
                            }
                            randomstr[i] = '\0';
-                           defer_packet(SSH1_MSG_IGNORE,
+                           defer_packet(ssh, SSH1_MSG_IGNORE,
                                         PKT_STR, randomstr, PKT_END);
                        }
                    }
                    logevent("Sending password with camouflage packets");
-                   ssh_pkt_defersend();
+                   ssh_pkt_defersend(ssh);
                } 
-               else if (!(ssh_remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {
+               else if (!(ssh->remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) {
                    /*
                     * The server can't deal with SSH1_MSG_IGNORE
                     * but can deal with padded passwords, so we
                     * can use the secondary defence.
                     */
                    char string[64];
-                   char *s;
+                   char *ss;
                    int len;
 
-                   len = strlen(password);
+                   len = strlen(s->password);
                    if (len < sizeof(string)) {
-                       s = string;
-                       strcpy(string, password);
+                       ss = string;
+                       strcpy(string, s->password);
                        len++;         /* cover the zero byte */
                        while (len < sizeof(string)) {
                            string[len++] = (char) random_byte();
                        }
                    } else {
-                       s = password;
+                       ss = s->password;
                    }
                    logevent("Sending length-padded password");
-                   send_packet(pwpkt_type, PKT_INT, len,
-                               PKT_DATA, s, len, PKT_END);
+                   send_packet(ssh, s->pwpkt_type, PKT_INT, len,
+                               PKT_DATA, ss, len, PKT_END);
                } else {
                    /*
                     * The server has _both_
@@ -2860,28 +2860,24 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
                     * therefore nothing we can do.
                     */
                    int len;
-                   len = strlen(password);
+                   len = strlen(s->password);
                    logevent("Sending unpadded password");
-                   send_packet(pwpkt_type, PKT_INT, len,
-                               PKT_DATA, password, len, PKT_END);
+                   send_packet(ssh, s->pwpkt_type, PKT_INT, len,
+                               PKT_DATA, s->password, len, PKT_END);
                }
            } else {
-               send_packet(pwpkt_type, PKT_STR, password, PKT_END);
+               send_packet(ssh, s->pwpkt_type, PKT_STR, s->password, PKT_END);
            }
        }
        logevent("Sent password");
-       memset(password, 0, strlen(password));
+       memset(s->password, 0, strlen(s->password));
        crWaitUntil(ispkt);
-       if (pktin.type == SSH1_SMSG_FAILURE) {
+       if (ssh->pktin.type == SSH1_SMSG_FAILURE) {
            if (flags & FLAG_VERBOSE)
-               c_write_str("Access denied\r\n");
+               c_write_str(ssh, "Access denied\r\n");
            logevent("Authentication refused");
-       } else if (pktin.type == SSH1_MSG_DISCONNECT) {
-           logevent("Received disconnect request");
-           ssh_state = SSH_STATE_CLOSED;
-           crReturn(1);
-       } else if (pktin.type != SSH1_SMSG_SUCCESS) {
-           bombout(("Strange packet received, type %d", pktin.type));
+       } else if (ssh->pktin.type != SSH1_SMSG_SUCCESS) {
+           bombout(("Strange packet received, type %d", ssh->pktin.type));
            crReturn(0);
        }
     }
@@ -2893,6 +2889,8 @@ static int do_ssh1_login(unsigned char *in, int inlen, int ispkt)
 
 void sshfwd_close(struct ssh_channel *c)
 {
+    Ssh ssh = c->ssh;
+
     if (c && !c->closes) {
        /*
         * If the channel's remoteid is -1, we have sent
@@ -2902,13 +2900,13 @@ void sshfwd_close(struct ssh_channel *c)
         * open, we can close it then.
         */
        if (((int)c->remoteid) != -1) {
-           if (ssh_version == 1) {
-               send_packet(SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
+           if (ssh->version == 1) {
+               send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
                            PKT_END);
            } else {
-               ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
-               ssh2_pkt_adduint32(c->remoteid);
-               ssh2_pkt_send();
+               ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_CLOSE);
+               ssh2_pkt_adduint32(ssh, c->remoteid);
+               ssh2_pkt_send(ssh);
            }
        }
        c->closes = 1;                 /* sent MSG_CLOSE */
@@ -2925,8 +2923,10 @@ void sshfwd_close(struct ssh_channel *c)
 
 int sshfwd_write(struct ssh_channel *c, char *buf, int len)
 {
-    if (ssh_version == 1) {
-       send_packet(SSH1_MSG_CHANNEL_DATA,
+    Ssh ssh = c->ssh;
+
+    if (ssh->version == 1) {
+       send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
                    PKT_INT, c->remoteid,
                    PKT_INT, len, PKT_DATA, buf, len, PKT_END);
        /*
@@ -2945,43 +2945,45 @@ int sshfwd_write(struct ssh_channel *c, char *buf, int len)
 
 void sshfwd_unthrottle(struct ssh_channel *c, int bufsize)
 {
-    if (ssh_version == 1) {
+    Ssh ssh = c->ssh;
+
+    if (ssh->version == 1) {
        if (c->v.v1.throttling && bufsize < SSH1_BUFFER_LIMIT) {
            c->v.v1.throttling = 0;
-           ssh1_throttle(-1);
+           ssh1_throttle(ssh, -1);
        }
     } else {
        ssh2_set_window(c, OUR_V2_WINSIZE - bufsize);
     }
 }
 
-static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
+static void ssh1_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
 {
-    crBegin;
+    crBegin(ssh->ssh1_protocol_crstate);
 
     random_init();
 
-    while (!do_ssh1_login(in, inlen, ispkt)) {
+    while (!do_ssh1_login(ssh, in, inlen, ispkt)) {
        crReturnV;
     }
-    if (ssh_state == SSH_STATE_CLOSED)
+    if (ssh->state == SSH_STATE_CLOSED)
        crReturnV;
 
     if (cfg.agentfwd && agent_exists()) {
        logevent("Requesting agent forwarding");
-       send_packet(SSH1_CMSG_AGENT_REQUEST_FORWARDING, PKT_END);
+       send_packet(ssh, SSH1_CMSG_AGENT_REQUEST_FORWARDING, PKT_END);
        do {
            crReturnV;
        } while (!ispkt);
-       if (pktin.type != SSH1_SMSG_SUCCESS
-           && pktin.type != SSH1_SMSG_FAILURE) {
+       if (ssh->pktin.type != SSH1_SMSG_SUCCESS
+           && ssh->pktin.type != SSH1_SMSG_FAILURE) {
            bombout(("Protocol confusion"));
            crReturnV;
-       } else if (pktin.type == SSH1_SMSG_FAILURE) {
+       } else if (ssh->pktin.type == SSH1_SMSG_FAILURE) {
            logevent("Agent forwarding refused");
        } else {
            logevent("Agent forwarding enabled");
-           ssh_agentfwd_enabled = TRUE;
+           ssh->agentfwd_enabled = TRUE;
        }
     }
 
@@ -2989,60 +2991,59 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
        char proto[20], data[64];
        logevent("Requesting X11 forwarding");
        x11_invent_auth(proto, sizeof(proto), data, sizeof(data));
-       if (ssh1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
-           send_packet(SSH1_CMSG_X11_REQUEST_FORWARDING,
+       if (ssh->v1_local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) {
+           send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
                        PKT_STR, proto, PKT_STR, data,
                        PKT_INT, 0, PKT_END);
        } else {
-           send_packet(SSH1_CMSG_X11_REQUEST_FORWARDING,
+           send_packet(ssh, SSH1_CMSG_X11_REQUEST_FORWARDING,
                        PKT_STR, proto, PKT_STR, data, PKT_END);
        }
        do {
            crReturnV;
        } while (!ispkt);
-       if (pktin.type != SSH1_SMSG_SUCCESS
-           && pktin.type != SSH1_SMSG_FAILURE) {
+       if (ssh->pktin.type != SSH1_SMSG_SUCCESS
+           && ssh->pktin.type != SSH1_SMSG_FAILURE) {
            bombout(("Protocol confusion"));
            crReturnV;
-       } else if (pktin.type == SSH1_SMSG_FAILURE) {
+       } else if (ssh->pktin.type == SSH1_SMSG_FAILURE) {
            logevent("X11 forwarding refused");
        } else {
            logevent("X11 forwarding enabled");
-           ssh_X11_fwd_enabled = TRUE;
+           ssh->X11_fwd_enabled = TRUE;
        }
     }
 
     {
        char type;
-       static char *e;
        int n;
        int sport,dport,sserv,dserv;
        char sports[256], dports[256], host[256];
        char buf[1024];
        struct servent *se;
 
-       ssh_rportfwds = newtree234(ssh_rportcmp_ssh1);
+       ssh->rportfwds = newtree234(ssh_rportcmp_ssh1);
         /* Add port forwardings. */
-       e = cfg.portfwd;
-       while (*e) {
-           type = *e++;
+       ssh->portfwd_strptr = cfg.portfwd;
+       while (*ssh->portfwd_strptr) {
+           type = *ssh->portfwd_strptr++;
            n = 0;
-           while (*e && *e != '\t')
-               sports[n++] = *e++;
+           while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != '\t')
+               sports[n++] = *ssh->portfwd_strptr++;
            sports[n] = 0;
-           if (*e == '\t')
-               e++;
+           if (*ssh->portfwd_strptr == '\t')
+               ssh->portfwd_strptr++;
            n = 0;
-           while (*e && *e != ':')
-               host[n++] = *e++;
+           while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != ':')
+               host[n++] = *ssh->portfwd_strptr++;
            host[n] = 0;
-           if (*e == ':')
-               e++;
+           if (*ssh->portfwd_strptr == ':')
+               ssh->portfwd_strptr++;
            n = 0;
-           while (*e)
-               dports[n++] = *e++;
+           while (*ssh->portfwd_strptr)
+               dports[n++] = *ssh->portfwd_strptr++;
            dports[n] = 0;
-           e++;
+           ssh->portfwd_strptr++;
            dport = atoi(dports);
            dserv = 0;
            if (dport == 0) {
@@ -3087,7 +3088,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                    pf = smalloc(sizeof(*pf));
                    strcpy(pf->dhost, host);
                    pf->dport = dport;
-                   if (add234(ssh_rportfwds, pf) != pf) {
+                   if (add234(ssh->rportfwds, pf) != pf) {
                        sprintf(buf, 
                                "Duplicate remote port forwarding to %s:%d",
                                host, dport);
@@ -3102,7 +3103,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                            dserv ? strlen(dports) : 0, dports,
                            dserv, "(", dport, dserv, ")");
                        logevent(buf);
-                       send_packet(SSH1_CMSG_PORT_FORWARD_REQUEST,
+                       send_packet(ssh, SSH1_CMSG_PORT_FORWARD_REQUEST,
                                    PKT_INT, sport,
                                    PKT_STR, host,
                                    PKT_INT, dport,
@@ -3110,13 +3111,13 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                        do {
                            crReturnV;
                        } while (!ispkt);
-                       if (pktin.type != SSH1_SMSG_SUCCESS
-                           && pktin.type != SSH1_SMSG_FAILURE) {
+                       if (ssh->pktin.type != SSH1_SMSG_SUCCESS
+                           && ssh->pktin.type != SSH1_SMSG_FAILURE) {
                            bombout(("Protocol confusion"));
                            crReturnV;
-                       } else if (pktin.type == SSH1_SMSG_FAILURE) {
-                           c_write_str("Server refused port forwarding\r\n");
-                           ssh_editing = ssh_echoing = 1;
+                       } else if (ssh->pktin.type == SSH1_SMSG_FAILURE) {
+                           c_write_str(ssh, "Server refused port"
+                                       " forwarding\r\n");
                        }
                        logevent("Remote port forwarding enabled");
                    }
@@ -3126,42 +3127,42 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
     }
 
     if (!cfg.nopty) {
-       send_packet(SSH1_CMSG_REQUEST_PTY,
+       send_packet(ssh, SSH1_CMSG_REQUEST_PTY,
                    PKT_STR, cfg.termtype,
-                   PKT_INT, ssh_term_height,
-                   PKT_INT, ssh_term_width,
+                   PKT_INT, ssh->term_height,
+                   PKT_INT, ssh->term_width,
                    PKT_INT, 0, PKT_INT, 0, PKT_CHAR, 0, PKT_END);
-       ssh_state = SSH_STATE_INTERMED;
+       ssh->state = SSH_STATE_INTERMED;
        do {
            crReturnV;
        } while (!ispkt);
-       if (pktin.type != SSH1_SMSG_SUCCESS
-           && pktin.type != SSH1_SMSG_FAILURE) {
+       if (ssh->pktin.type != SSH1_SMSG_SUCCESS
+           && ssh->pktin.type != SSH1_SMSG_FAILURE) {
            bombout(("Protocol confusion"));
            crReturnV;
-       } else if (pktin.type == SSH1_SMSG_FAILURE) {
-           c_write_str("Server refused to allocate pty\r\n");
-           ssh_editing = ssh_echoing = 1;
+       } else if (ssh->pktin.type == SSH1_SMSG_FAILURE) {
+           c_write_str(ssh, "Server refused to allocate pty\r\n");
+           ssh->editing = ssh->echoing = 1;
        }
        logevent("Allocated pty");
     } else {
-       ssh_editing = ssh_echoing = 1;
+       ssh->editing = ssh->echoing = 1;
     }
 
     if (cfg.compression) {
-       send_packet(SSH1_CMSG_REQUEST_COMPRESSION, PKT_INT, 6, PKT_END);
+       send_packet(ssh, SSH1_CMSG_REQUEST_COMPRESSION, PKT_INT, 6, PKT_END);
        do {
            crReturnV;
        } while (!ispkt);
-       if (pktin.type != SSH1_SMSG_SUCCESS
-           && pktin.type != SSH1_SMSG_FAILURE) {
+       if (ssh->pktin.type != SSH1_SMSG_SUCCESS
+           && ssh->pktin.type != SSH1_SMSG_FAILURE) {
            bombout(("Protocol confusion"));
            crReturnV;
-       } else if (pktin.type == SSH1_SMSG_FAILURE) {
-           c_write_str("Server refused to compress\r\n");
+       } else if (ssh->pktin.type == SSH1_SMSG_FAILURE) {
+           c_write_str(ssh, "Server refused to compress\r\n");
        }
        logevent("Started compression");
-       ssh1_compressing = TRUE;
+       ssh->v1_compressing = TRUE;
        zlib_compress_init();
        zlib_decompress_init();
     }
@@ -3178,99 +3179,102 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
        
        if (cfg.ssh_subsys && cfg.remote_cmd_ptr2) {
            cmd = cfg.remote_cmd_ptr2;
-           ssh_fallback_cmd = TRUE;
+           ssh->fallback_cmd = TRUE;
        }
        if (*cmd)
-           send_packet(SSH1_CMSG_EXEC_CMD, PKT_STR, cmd, PKT_END);
+           send_packet(ssh, SSH1_CMSG_EXEC_CMD, PKT_STR, cmd, PKT_END);
        else
-           send_packet(SSH1_CMSG_EXEC_SHELL, PKT_END);
+           send_packet(ssh, SSH1_CMSG_EXEC_SHELL, PKT_END);
        logevent("Started session");
     }
 
-    ssh_state = SSH_STATE_SESSION;
-    if (size_needed)
-       ssh_size(ssh_term_width, ssh_term_height);
-    if (eof_needed)
-       ssh_special(TS_EOF);
+    ssh->state = SSH_STATE_SESSION;
+    if (ssh->size_needed)
+       ssh_size(ssh, ssh->term_width, ssh->term_height);
+    if (ssh->eof_needed)
+       ssh_special(ssh, TS_EOF);
 
     ldisc_send(NULL, 0, 0);           /* cause ldisc to notice changes */
-    ssh_send_ok = 1;
-    ssh_channels = newtree234(ssh_channelcmp);
+    ssh->send_ok = 1;
+    ssh->channels = newtree234(ssh_channelcmp);
     while (1) {
        crReturnV;
        if (ispkt) {
-           if (pktin.type == SSH1_SMSG_STDOUT_DATA ||
-               pktin.type == SSH1_SMSG_STDERR_DATA) {
-               long len = GET_32BIT(pktin.body);
+           if (ssh->pktin.type == SSH1_SMSG_STDOUT_DATA ||
+               ssh->pktin.type == SSH1_SMSG_STDERR_DATA) {
+               long len = GET_32BIT(ssh->pktin.body);
                int bufsize =
-                   from_backend(frontend, pktin.type == SSH1_SMSG_STDERR_DATA,
-                                pktin.body + 4, len);
-               if (!ssh1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {
-                   ssh1_stdout_throttling = 1;
-                   ssh1_throttle(+1);
+                   from_backend(ssh->frontend,
+                                ssh->pktin.type == SSH1_SMSG_STDERR_DATA,
+                                ssh->pktin.body + 4, len);
+               if (!ssh->v1_stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) {
+                   ssh->v1_stdout_throttling = 1;
+                   ssh1_throttle(ssh, +1);
                }
-           } else if (pktin.type == SSH1_MSG_DISCONNECT) {
-               ssh_state = SSH_STATE_CLOSED;
+           } else if (ssh->pktin.type == SSH1_MSG_DISCONNECT) {
+               ssh->state = SSH_STATE_CLOSED;
                logevent("Received disconnect request");
                crReturnV;
-           } else if (pktin.type == SSH1_SMSG_X11_OPEN) {
+           } else if (ssh->pktin.type == SSH1_SMSG_X11_OPEN) {
                /* Remote side is trying to open a channel to talk to our
                 * X-Server. Give them back a local channel number. */
                struct ssh_channel *c;
 
                logevent("Received X11 connect request");
                /* Refuse if X11 forwarding is disabled. */
-               if (!ssh_X11_fwd_enabled) {
-                   send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
-                               PKT_INT, GET_32BIT(pktin.body), PKT_END);
+               if (!ssh->X11_fwd_enabled) {
+                   send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
+                               PKT_INT, GET_32BIT(ssh->pktin.body), PKT_END);
                    logevent("Rejected X11 connect request");
                } else {
                    c = smalloc(sizeof(struct ssh_channel));
+                   c->ssh = ssh;
 
                    if (x11_init(&c->u.x11.s, cfg.x11_display, c) != NULL) {
                        logevent("opening X11 forward connection failed");
                        sfree(c);
-                       send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
-                                   PKT_INT, GET_32BIT(pktin.body),
+                       send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
+                                   PKT_INT, GET_32BIT(ssh->pktin.body),
                                    PKT_END);
                    } else {
                        logevent
                            ("opening X11 forward connection succeeded");
-                       c->remoteid = GET_32BIT(pktin.body);
-                       c->localid = alloc_channel_id();
+                       c->remoteid = GET_32BIT(ssh->pktin.body);
+                       c->localid = alloc_channel_id(ssh);
                        c->closes = 0;
                        c->v.v1.throttling = 0;
                        c->type = CHAN_X11;     /* identify channel type */
-                       add234(ssh_channels, c);
-                       send_packet(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
+                       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");
                    }
                }
-           } else if (pktin.type == SSH1_SMSG_AGENT_OPEN) {
+           } else if (ssh->pktin.type == SSH1_SMSG_AGENT_OPEN) {
                /* Remote side is trying to open a channel to talk to our
                 * agent. Give them back a local channel number. */
                struct ssh_channel *c;
 
                /* Refuse if agent forwarding is disabled. */
-               if (!ssh_agentfwd_enabled) {
-                   send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
-                               PKT_INT, GET_32BIT(pktin.body), PKT_END);
+               if (!ssh->agentfwd_enabled) {
+                   send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
+                               PKT_INT, GET_32BIT(ssh->pktin.body), PKT_END);
                } else {
                    c = smalloc(sizeof(struct ssh_channel));
-                   c->remoteid = GET_32BIT(pktin.body);
-                   c->localid = alloc_channel_id();
+                   c->ssh = ssh;
+                   c->remoteid = GET_32BIT(ssh->pktin.body);
+                   c->localid = alloc_channel_id(ssh);
                    c->closes = 0;
                    c->v.v1.throttling = 0;
                    c->type = CHAN_AGENT;       /* identify channel type */
                    c->u.a.lensofar = 0;
-                   add234(ssh_channels, c);
-                   send_packet(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
+                   add234(ssh->channels, c);
+                   send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
                                PKT_INT, c->remoteid, PKT_INT, c->localid,
                                PKT_END);
                }
-           } else if (pktin.type == SSH1_MSG_PORT_OPEN) {
+           } else if (ssh->pktin.type == SSH1_MSG_PORT_OPEN) {
                /* 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;
@@ -3279,9 +3283,10 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                char host[256], buf[1024];
                char *p, *h, *e;
                c = smalloc(sizeof(struct ssh_channel));
+               c->ssh = ssh;
 
-               hostsize = GET_32BIT(pktin.body+4);
-               for(h = host, p = pktin.body+8; hostsize != 0; hostsize--) {
+               hostsize = GET_32BIT(ssh->pktin.body+4);
+               for(h = host, p = ssh->pktin.body+8; hostsize != 0; hostsize--) {
                    if (h+1 < host+sizeof(host))
                        *h++ = *p;
                    p++;
@@ -3292,12 +3297,12 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                strcpy(pf.dhost, host);
                pf.dport = port;
 
-               if (find234(ssh_rportfwds, &pf, NULL) == NULL) {
+               if (find234(ssh->rportfwds, &pf, NULL) == NULL) {
                    sprintf(buf, "Rejected remote port open request for %s:%d",
                            host, port);
                    logevent(buf);
-                    send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
-                                PKT_INT, GET_32BIT(pktin.body), PKT_END);
+                    send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
+                                PKT_INT, GET_32BIT(ssh->pktin.body), PKT_END);
                } else {
                    sprintf(buf, "Received remote port open request for %s:%d",
                            host, port);
@@ -3308,29 +3313,29 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                        sprintf(buf, "Port open failed: %s", e);
                        logevent(buf);
                        sfree(c);
-                       send_packet(SSH1_MSG_CHANNEL_OPEN_FAILURE,
-                                   PKT_INT, GET_32BIT(pktin.body),
+                       send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_FAILURE,
+                                   PKT_INT, GET_32BIT(ssh->pktin.body),
                                    PKT_END);
                    } else {
-                       c->remoteid = GET_32BIT(pktin.body);
-                       c->localid = alloc_channel_id();
+                       c->remoteid = GET_32BIT(ssh->pktin.body);
+                       c->localid = alloc_channel_id(ssh);
                        c->closes = 0;
                        c->v.v1.throttling = 0;
                        c->type = CHAN_SOCKDATA;        /* identify channel type */
-                       add234(ssh_channels, c);
-                       send_packet(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
+                       add234(ssh->channels, c);
+                       send_packet(ssh, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION,
                                    PKT_INT, c->remoteid, PKT_INT,
                                    c->localid, PKT_END);
                        logevent("Forwarded port opened successfully");
                    }
                }
 
-           } else if (pktin.type == SSH1_MSG_CHANNEL_OPEN_CONFIRMATION) {
-               unsigned int remoteid = GET_32BIT(pktin.body);
-               unsigned int localid = GET_32BIT(pktin.body+4);
+           } else if (ssh->pktin.type == SSH1_MSG_CHANNEL_OPEN_CONFIRMATION) {
+               unsigned int remoteid = GET_32BIT(ssh->pktin.body);
+               unsigned int localid = GET_32BIT(ssh->pktin.body+4);
                struct ssh_channel *c;
 
-               c = find234(ssh_channels, &remoteid, ssh_channelfind);
+               c = find234(ssh->channels, &remoteid, ssh_channelfind);
                if (c && c->type == CHAN_SOCKDATA_DORMANT) {
                    c->remoteid = localid;
                    c->type = CHAN_SOCKDATA;
@@ -3345,33 +3350,33 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                     * the channel open. So now we know the
                     * remoteid, we can close it again.
                     */
-                   send_packet(SSH1_MSG_CHANNEL_CLOSE, PKT_INT, c->remoteid,
-                               PKT_END);
+                   send_packet(ssh, SSH1_MSG_CHANNEL_CLOSE,
+                               PKT_INT, c->remoteid, PKT_END);
                }
 
-           } else if (pktin.type == SSH1_MSG_CHANNEL_OPEN_FAILURE) {
-               unsigned int remoteid = GET_32BIT(pktin.body);
-               unsigned int localid = GET_32BIT(pktin.body+4);
+           } else if (ssh->pktin.type == SSH1_MSG_CHANNEL_OPEN_FAILURE) {
+               unsigned int remoteid = GET_32BIT(ssh->pktin.body);
+               unsigned int localid = GET_32BIT(ssh->pktin.body+4);
                struct ssh_channel *c;
 
-               c = find234(ssh_channels, &remoteid, ssh_channelfind);
+               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);
-                   del234(ssh_channels, c);
+                   del234(ssh->channels, c);
                    sfree(c);
                }
 
-           } else if (pktin.type == SSH1_MSG_CHANNEL_CLOSE ||
-                      pktin.type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION) {
+           } else if (ssh->pktin.type == SSH1_MSG_CHANNEL_CLOSE ||
+                      ssh->pktin.type == SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION) {
                /* Remote side closes a channel. */
-               unsigned i = GET_32BIT(pktin.body);
+               unsigned i = GET_32BIT(ssh->pktin.body);
                struct ssh_channel *c;
-               c = find234(ssh_channels, &i, ssh_channelfind);
+               c = find234(ssh->channels, &i, ssh_channelfind);
                if (c && ((int)c->remoteid) != -1) {
                    int closetype;
                    closetype =
-                       (pktin.type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);
+                       (ssh->pktin.type == SSH1_MSG_CHANNEL_CLOSE ? 1 : 2);
 
                    if ((c->closes == 0) && (c->type == CHAN_X11)) {
                        logevent("Forwarded X11 connection terminated");
@@ -3388,28 +3393,28 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
 
                    c->closes |= (closetype << 2);   /* seen this message */
                    if (!(c->closes & closetype)) {
-                       send_packet(pktin.type, PKT_INT, c->remoteid,
+                       send_packet(ssh, ssh->pktin.type, PKT_INT, c->remoteid,
                                    PKT_END);
                        c->closes |= closetype;      /* sent it too */
                    }
 
                    if (c->closes == 15) {
-                       del234(ssh_channels, c);
+                       del234(ssh->channels, c);
                        sfree(c);
                    }
                } else {
                    bombout(("Received CHANNEL_CLOSE%s for %s channel %d\n",
-                            pktin.type == SSH1_MSG_CHANNEL_CLOSE ? "" :
+                            ssh->pktin.type == SSH1_MSG_CHANNEL_CLOSE ? "" :
                             "_CONFIRMATION", c ? "half-open" : "nonexistent",
                             i));
                }
-           } else if (pktin.type == SSH1_MSG_CHANNEL_DATA) {
+           } else if (ssh->pktin.type == SSH1_MSG_CHANNEL_DATA) {
                /* Data sent down one of our channels. */
-               int i = GET_32BIT(pktin.body);
-               int len = GET_32BIT(pktin.body + 4);
-               unsigned char *p = pktin.body + 8;
+               int i = GET_32BIT(ssh->pktin.body);
+               int len = GET_32BIT(ssh->pktin.body + 4);
+               unsigned char *p = ssh->pktin.body + 8;
                struct ssh_channel *c;
-               c = find234(ssh_channels, &i, ssh_channelfind);
+               c = find234(ssh->channels, &i, ssh_channelfind);
                if (c) {
                    int bufsize;
                    switch (c->type) {
@@ -3459,7 +3464,7 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                                    sentreply = "\0\0\0\1\5";
                                    replylen = 5;
                                }
-                               send_packet(SSH1_MSG_CHANNEL_DATA,
+                               send_packet(ssh, SSH1_MSG_CHANNEL_DATA,
                                            PKT_INT, c->remoteid,
                                            PKT_INT, replylen,
                                            PKT_DATA, sentreply, replylen,
@@ -3475,21 +3480,21 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                    }
                    if (!c->v.v1.throttling && bufsize > SSH1_BUFFER_LIMIT) {
                        c->v.v1.throttling = 1;
-                       ssh1_throttle(+1);
+                       ssh1_throttle(ssh, +1);
                    }
                }
-           } else if (pktin.type == SSH1_SMSG_SUCCESS) {
+           } else if (ssh->pktin.type == SSH1_SMSG_SUCCESS) {
                /* may be from EXEC_SHELL on some servers */
-           } else if (pktin.type == SSH1_SMSG_FAILURE) {
+           } else if (ssh->pktin.type == SSH1_SMSG_FAILURE) {
                /* may be from EXEC_SHELL on some servers
                 * if no pty is available or in other odd cases. Ignore */
-           } else if (pktin.type == SSH1_SMSG_EXIT_STATUS) {
+           } else if (ssh->pktin.type == SSH1_SMSG_EXIT_STATUS) {
                char buf[100];
-               ssh_exitcode = GET_32BIT(pktin.body);
+               ssh->exitcode = GET_32BIT(ssh->pktin.body);
                sprintf(buf, "Server sent command exit status %d",
-                       ssh_exitcode);
+                       ssh->exitcode);
                logevent(buf);
-               send_packet(SSH1_CMSG_EXIT_CONFIRMATION, PKT_END);
+               send_packet(ssh, SSH1_CMSG_EXIT_CONFIRMATION, PKT_END);
                 /*
                  * In case `helpful' firewalls or proxies tack
                  * extra human-readable text on the end of the
@@ -3497,16 +3502,16 @@ static void ssh1_protocol(unsigned char *in, int inlen, int ispkt)
                  * encrypted packet, we close the session once
                  * we've sent EXIT_CONFIRMATION.
                  */
-                ssh_state = SSH_STATE_CLOSED;
+                ssh->state = SSH_STATE_CLOSED;
                 crReturnV;
            } else {
-               bombout(("Strange packet received: type %d", pktin.type));
+               bombout(("Strange packet received: type %d", ssh->pktin.type));
                crReturnV;
            }
        } else {
            while (inlen > 0) {
                int len = min(inlen, 512);
-               send_packet(SSH1_CMSG_STDIN_DATA,
+               send_packet(ssh, SSH1_CMSG_STDIN_DATA,
                            PKT_INT, len, PKT_DATA, in, len, PKT_END);
                in += len;
                inlen -= len;
@@ -3548,13 +3553,13 @@ static int in_commasep_string(char *needle, char *haystack, int haylen)
 /*
  * SSH2 key creation method.
  */
-static void ssh2_mkkey(Bignum K, char *H, char *sessid, char chr,
+static void ssh2_mkkey(Ssh ssh, Bignum K, char *H, char *sessid, char chr,
                       char *keyspace)
 {
     SHA_State s;
     /* First 20 bytes. */
     SHA_Init(&s);
-    if (!(ssh_remote_bugs & BUG_SSH2_DERIVEKEY))
+    if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
        sha_mpint(&s, K);
     SHA_Bytes(&s, H, 20);
     SHA_Bytes(&s, &chr, 1);
@@ -3562,7 +3567,7 @@ static void ssh2_mkkey(Bignum K, char *H, char *sessid, char chr,
     SHA_Final(&s, keyspace);
     /* Next 20 bytes. */
     SHA_Init(&s);
-    if (!(ssh_remote_bugs & BUG_SSH2_DERIVEKEY))
+    if (!(ssh->remote_bugs & BUG_SSH2_DERIVEKEY))
        sha_mpint(&s, K);
     SHA_Bytes(&s, H, 20);
     SHA_Bytes(&s, keyspace, 20);
@@ -3572,67 +3577,70 @@ static void ssh2_mkkey(Bignum K, char *H, char *sessid, char chr,
 /*
  * Handle the SSH2 transport layer.
  */
-static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
+static int do_ssh2_transport(Ssh ssh, unsigned char *in, int inlen, int ispkt)
 {
-    static int i, j, len, nbits, pbits, warn;
-    static char *str;
-    static Bignum p, g, e, f, K;
-    static int kex_init_value, kex_reply_value;
-    static const struct ssh_mac **maclist;
-    static int nmacs;
-    static const struct ssh2_cipher *cscipher_tobe = NULL;
-    static const struct ssh2_cipher *sccipher_tobe = NULL;
-    static const struct ssh_mac *csmac_tobe = NULL;
-    static const struct ssh_mac *scmac_tobe = NULL;
-    static const struct ssh_compress *cscomp_tobe = NULL;
-    static const struct ssh_compress *sccomp_tobe = NULL;
-    static char *hostkeydata, *sigdata, *keystr, *fingerprint;
-    static int hostkeylen, siglen;
-    static void *hkey;                /* actual host key */
-    static unsigned char exchange_hash[20];
-    static unsigned char keyspace[40];
-    static int n_preferred_ciphers;
-    static const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
-    static const struct ssh_compress *preferred_comp;
-    static int cipherstr_started;
-    static int first_kex;
-
-    crBegin;
+    struct do_ssh2_transport_state {
+       int nbits, pbits, warn;
+       Bignum p, g, e, f, K;
+       int kex_init_value, kex_reply_value;
+       const struct ssh_mac **maclist;
+       int nmacs;
+       const struct ssh2_cipher *cscipher_tobe;
+       const struct ssh2_cipher *sccipher_tobe;
+       const struct ssh_mac *csmac_tobe;
+       const struct ssh_mac *scmac_tobe;
+       const struct ssh_compress *cscomp_tobe;
+       const struct ssh_compress *sccomp_tobe;
+       char *hostkeydata, *sigdata, *keystr, *fingerprint;
+       int hostkeylen, siglen;
+       void *hkey;                    /* actual host key */
+       unsigned char exchange_hash[20];
+       int n_preferred_ciphers;
+       const struct ssh2_ciphers *preferred_ciphers[CIPHER_MAX];
+       const struct ssh_compress *preferred_comp;
+       int first_kex;
+    };
+    crState(do_ssh2_transport_state);
+
+    crBegin(ssh->do_ssh2_transport_crstate);
+
+    s->cscipher_tobe = s->sccipher_tobe = NULL;
+    s->csmac_tobe = s->scmac_tobe = NULL;
+    s->cscomp_tobe = s->sccomp_tobe = NULL;
+
     random_init();
-    first_kex = 1;
+    s->first_kex = 1;
 
-    /*
-     * Set up the preferred ciphers. (NULL => warn below here)
-     */
-    n_preferred_ciphers = 0;
-    for (i = 0; i < CIPHER_MAX; i++) {
-       switch (cfg.ssh_cipherlist[i]) {
-         case CIPHER_BLOWFISH:
-           preferred_ciphers[n_preferred_ciphers] = &ssh2_blowfish;
-           n_preferred_ciphers++;
-           break;
-         case CIPHER_DES:
-           if (cfg.ssh2_des_cbc) {
-               preferred_ciphers[n_preferred_ciphers] = &ssh2_des;
-               n_preferred_ciphers++;
-           }
-           break;
-         case CIPHER_3DES:
-           preferred_ciphers[n_preferred_ciphers] = &ssh2_3des;
-           n_preferred_ciphers++;
-           break;
-         case CIPHER_AES:
-           preferred_ciphers[n_preferred_ciphers] = &ssh2_aes;
-           n_preferred_ciphers++;
-           break;
-         case CIPHER_WARN:
-           /* Flag for later. Don't bother if it's the last in
-            * the list. */
-           if (i < CIPHER_MAX - 1) {
-               preferred_ciphers[n_preferred_ciphers] = NULL;
-               n_preferred_ciphers++;
+    {
+       int i;
+       /*
+        * Set up the preferred ciphers. (NULL => warn below here)
+        */
+       s->n_preferred_ciphers = 0;
+       for (i = 0; i < CIPHER_MAX; i++) {
+           switch (cfg.ssh_cipherlist[i]) {
+             case CIPHER_BLOWFISH:
+               s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_blowfish;
+               break;
+             case CIPHER_DES:
+               if (cfg.ssh2_des_cbc) {
+                   s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_des;
+               }
+               break;
+             case CIPHER_3DES:
+               s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_3des;
+               break;
+             case CIPHER_AES:
+               s->preferred_ciphers[s->n_preferred_ciphers++] = &ssh2_aes;
+               break;
+             case CIPHER_WARN:
+               /* Flag for later. Don't bother if it's the last in
+                * the list. */
+               if (i < CIPHER_MAX - 1) {
+                   s->preferred_ciphers[s->n_preferred_ciphers++] = NULL;
+               }
+               break;
            }
-           break;
        }
     }
 
@@ -3640,232 +3648,241 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
      * Set up preferred compression.
      */
     if (cfg.compression)
-       preferred_comp = &ssh_zlib;
+       s->preferred_comp = &ssh_zlib;
     else
-       preferred_comp = &ssh_comp_none;
+       s->preferred_comp = &ssh_comp_none;
 
     /*
      * Be prepared to work around the buggy MAC problem.
      */
-    if (ssh_remote_bugs & BUG_SSH2_HMAC)
-       maclist = buggymacs, nmacs = lenof(buggymacs);
+    if (ssh->remote_bugs & BUG_SSH2_HMAC)
+       s->maclist = buggymacs, s->nmacs = lenof(buggymacs);
     else
-       maclist = macs, nmacs = lenof(macs);
+       s->maclist = macs, s->nmacs = lenof(macs);
 
   begin_key_exchange:
-    /*
-     * Construct and send our key exchange packet.
-     */
-    ssh2_pkt_init(SSH2_MSG_KEXINIT);
-    for (i = 0; i < 16; i++)
-       ssh2_pkt_addbyte((unsigned char) random_byte());
-    /* List key exchange algorithms. */
-    ssh2_pkt_addstring_start();
-    for (i = 0; i < lenof(kex_algs); i++) {
-       if (kex_algs[i] == &ssh_diffiehellman_gex &&
-           (ssh_remote_bugs & BUG_SSH2_DH_GEX))
-           continue;
-       ssh2_pkt_addstring_str(kex_algs[i]->name);
-       if (i < lenof(kex_algs) - 1)
-           ssh2_pkt_addstring_str(",");
-    }
-    /* List server host key algorithms. */
-    ssh2_pkt_addstring_start();
-    for (i = 0; i < lenof(hostkey_algs); i++) {
-       ssh2_pkt_addstring_str(hostkey_algs[i]->name);
-       if (i < lenof(hostkey_algs) - 1)
-           ssh2_pkt_addstring_str(",");
-    }
-    /* List client->server encryption algorithms. */
-    ssh2_pkt_addstring_start();
-    cipherstr_started = 0;
-    for (i = 0; i < n_preferred_ciphers; i++) {
-       const struct ssh2_ciphers *c = preferred_ciphers[i];
-       if (!c) continue;              /* warning flag */
-       for (j = 0; j < c->nciphers; j++) {
-           if (cipherstr_started)
-               ssh2_pkt_addstring_str(",");
-           ssh2_pkt_addstring_str(c->list[j]->name);
-           cipherstr_started = 1;
+    {
+       int i, j, cipherstr_started;
+
+       /*
+        * Construct and send our key exchange packet.
+        */
+       ssh2_pkt_init(ssh, SSH2_MSG_KEXINIT);
+       for (i = 0; i < 16; i++)
+           ssh2_pkt_addbyte(ssh, (unsigned char) random_byte());
+       /* List key exchange algorithms. */
+       ssh2_pkt_addstring_start(ssh);
+       for (i = 0; i < lenof(kex_algs); i++) {
+           if (kex_algs[i] == &ssh_diffiehellman_gex &&
+               (ssh->remote_bugs & BUG_SSH2_DH_GEX))
+               continue;
+           ssh2_pkt_addstring_str(ssh, kex_algs[i]->name);
+           if (i < lenof(kex_algs) - 1)
+               ssh2_pkt_addstring_str(ssh, ",");
        }
-    }
-    /* List server->client encryption algorithms. */
-    ssh2_pkt_addstring_start();
-    cipherstr_started = 0;
-    for (i = 0; i < n_preferred_ciphers; i++) {
-       const struct ssh2_ciphers *c = preferred_ciphers[i];
-       if (!c) continue; /* warning flag */
-       for (j = 0; j < c->nciphers; j++) {
-           if (cipherstr_started)
-               ssh2_pkt_addstring_str(",");
-           ssh2_pkt_addstring_str(c->list[j]->name);
-           cipherstr_started = 1;
+       /* List server host key algorithms. */
+       ssh2_pkt_addstring_start(ssh);
+       for (i = 0; i < lenof(hostkey_algs); i++) {
+           ssh2_pkt_addstring_str(ssh, hostkey_algs[i]->name);
+           if (i < lenof(hostkey_algs) - 1)
+               ssh2_pkt_addstring_str(ssh, ",");
        }
+       /* List client->server encryption algorithms. */
+       ssh2_pkt_addstring_start(ssh);
+       cipherstr_started = 0;
+       for (i = 0; i < s->n_preferred_ciphers; i++) {
+           const struct ssh2_ciphers *c = s->preferred_ciphers[i];
+           if (!c) continue;          /* warning flag */
+           for (j = 0; j < c->nciphers; j++) {
+               if (cipherstr_started)
+                   ssh2_pkt_addstring_str(ssh, ",");
+               ssh2_pkt_addstring_str(ssh, c->list[j]->name);
+               cipherstr_started = 1;
+           }
+       }
+       /* List server->client encryption algorithms. */
+       ssh2_pkt_addstring_start(ssh);
+       cipherstr_started = 0;
+       for (i = 0; i < s->n_preferred_ciphers; i++) {
+           const struct ssh2_ciphers *c = s->preferred_ciphers[i];
+           if (!c) continue; /* warning flag */
+           for (j = 0; j < c->nciphers; j++) {
+               if (cipherstr_started)
+                   ssh2_pkt_addstring_str(ssh, ",");
+               ssh2_pkt_addstring_str(ssh, c->list[j]->name);
+               cipherstr_started = 1;
+           }
+       }
+       /* List client->server MAC algorithms. */
+       ssh2_pkt_addstring_start(ssh);
+       for (i = 0; i < s->nmacs; i++) {
+           ssh2_pkt_addstring_str(ssh, s->maclist[i]->name);
+           if (i < s->nmacs - 1)
+               ssh2_pkt_addstring_str(ssh, ",");
+       }
+       /* List server->client MAC algorithms. */
+       ssh2_pkt_addstring_start(ssh);
+       for (i = 0; i < s->nmacs; i++) {
+           ssh2_pkt_addstring_str(ssh, s->maclist[i]->name);
+           if (i < s->nmacs - 1)
+               ssh2_pkt_addstring_str(ssh, ",");
+       }
+       /* List client->server compression algorithms. */
+       ssh2_pkt_addstring_start(ssh);
+       for (i = 0; i < lenof(compressions) + 1; i++) {
+           const struct ssh_compress *c =
+               i == 0 ? s->preferred_comp : compressions[i - 1];
+           ssh2_pkt_addstring_str(ssh, c->name);
+           if (i < lenof(compressions))
+               ssh2_pkt_addstring_str(ssh, ",");
+       }
+       /* List server->client compression algorithms. */
+       ssh2_pkt_addstring_start(ssh);
+       for (i = 0; i < lenof(compressions) + 1; i++) {
+           const struct ssh_compress *c =
+               i == 0 ? s->preferred_comp : compressions[i - 1];
+           ssh2_pkt_addstring_str(ssh, c->name);
+           if (i < lenof(compressions))
+               ssh2_pkt_addstring_str(ssh, ",");
+       }
+       /* List client->server languages. Empty list. */
+       ssh2_pkt_addstring_start(ssh);
+       /* List server->client languages. Empty list. */
+       ssh2_pkt_addstring_start(ssh);
+       /* First KEX packet does _not_ follow, because we're not that brave. */
+       ssh2_pkt_addbool(ssh, FALSE);
+       /* Reserved. */
+       ssh2_pkt_adduint32(ssh, 0);
     }
-    /* List client->server MAC algorithms. */
-    ssh2_pkt_addstring_start();
-    for (i = 0; i < nmacs; i++) {
-       ssh2_pkt_addstring_str(maclist[i]->name);
-       if (i < nmacs - 1)
-           ssh2_pkt_addstring_str(",");
-    }
-    /* List server->client MAC algorithms. */
-    ssh2_pkt_addstring_start();
-    for (i = 0; i < nmacs; i++) {
-       ssh2_pkt_addstring_str(maclist[i]->name);
-       if (i < nmacs - 1)
-           ssh2_pkt_addstring_str(",");
-    }
-    /* List client->server compression algorithms. */
-    ssh2_pkt_addstring_start();
-    for (i = 0; i < lenof(compressions) + 1; i++) {
-       const struct ssh_compress *c =
-           i == 0 ? preferred_comp : compressions[i - 1];
-       ssh2_pkt_addstring_str(c->name);
-       if (i < lenof(compressions))
-           ssh2_pkt_addstring_str(",");
-    }
-    /* List server->client compression algorithms. */
-    ssh2_pkt_addstring_start();
-    for (i = 0; i < lenof(compressions) + 1; i++) {
-       const struct ssh_compress *c =
-           i == 0 ? preferred_comp : compressions[i - 1];
-       ssh2_pkt_addstring_str(c->name);
-       if (i < lenof(compressions))
-           ssh2_pkt_addstring_str(",");
-    }
-    /* List client->server languages. Empty list. */
-    ssh2_pkt_addstring_start();
-    /* List server->client languages. Empty list. */
-    ssh2_pkt_addstring_start();
-    /* First KEX packet does _not_ follow, because we're not that brave. */
-    ssh2_pkt_addbool(FALSE);
-    /* Reserved. */
-    ssh2_pkt_adduint32(0);
 
-    exhash = exhashbase;
-    sha_string(&exhash, pktout.data + 5, pktout.length - 5);
+    ssh->exhash = ssh->exhashbase;
+    sha_string(&ssh->exhash, ssh->pktout.data + 5, ssh->pktout.length - 5);
 
-    ssh2_pkt_send();
+    ssh2_pkt_send(ssh);
 
     if (!ispkt)
        crWaitUntil(ispkt);
-    sha_string(&exhash, pktin.data + 5, pktin.length - 5);
+    sha_string(&ssh->exhash, ssh->pktin.data + 5, ssh->pktin.length - 5);
 
     /*
      * Now examine the other side's KEXINIT to see what we're up
      * to.
      */
-    if (pktin.type != SSH2_MSG_KEXINIT) {
-       bombout(("expected key exchange packet from server"));
-       crReturn(0);
-    }
-    kex = NULL;
-    hostkey = NULL;
-    cscipher_tobe = NULL;
-    sccipher_tobe = NULL;
-    csmac_tobe = NULL;
-    scmac_tobe = NULL;
-    cscomp_tobe = NULL;
-    sccomp_tobe = NULL;
-    pktin.savedpos += 16;             /* skip garbage cookie */
-    ssh2_pkt_getstring(&str, &len);    /* key exchange algorithms */
-    for (i = 0; i < lenof(kex_algs); i++) {
-       if (kex_algs[i] == &ssh_diffiehellman_gex &&
-           (ssh_remote_bugs & BUG_SSH2_DH_GEX))
-           continue;
-       if (in_commasep_string(kex_algs[i]->name, str, len)) {
-           kex = kex_algs[i];
-           break;
+    {
+       char *str;
+       int i, j, len;
+
+       if (ssh->pktin.type != SSH2_MSG_KEXINIT) {
+           bombout(("expected key exchange packet from server"));
+           crReturn(0);
        }
-    }
-    ssh2_pkt_getstring(&str, &len);    /* host key algorithms */
-    for (i = 0; i < lenof(hostkey_algs); i++) {
-       if (in_commasep_string(hostkey_algs[i]->name, str, len)) {
-           hostkey = hostkey_algs[i];
-           break;
+       ssh->kex = NULL;
+       ssh->hostkey = NULL;
+       s->cscipher_tobe = NULL;
+       s->sccipher_tobe = NULL;
+       s->csmac_tobe = NULL;
+       s->scmac_tobe = NULL;
+       s->cscomp_tobe = NULL;
+       s->sccomp_tobe = NULL;
+       ssh->pktin.savedpos += 16;              /* skip garbage cookie */
+       ssh2_pkt_getstring(ssh, &str, &len);    /* key exchange algorithms */
+       for (i = 0; i < lenof(kex_algs); i++) {
+           if (kex_algs[i] == &ssh_diffiehellman_gex &&
+               (ssh->remote_bugs & BUG_SSH2_DH_GEX))
+               continue;
+           if (in_commasep_string(kex_algs[i]->name, str, len)) {
+               ssh->kex = kex_algs[i];
+               break;
+           }
        }
-    }
-    ssh2_pkt_getstring(&str, &len);    /* client->server cipher */
-    warn = 0;
-    for (i = 0; i < n_preferred_ciphers; i++) {
-       const struct ssh2_ciphers *c = preferred_ciphers[i];
-       if (!c) {
-           warn = 1;
-       } else {
-           for (j = 0; j < c->nciphers; j++) {
-               if (in_commasep_string(c->list[j]->name, str, len)) {
-                   cscipher_tobe = c->list[j];
-                   break;
+       ssh2_pkt_getstring(ssh, &str, &len);    /* host key algorithms */
+       for (i = 0; i < lenof(hostkey_algs); i++) {
+           if (in_commasep_string(hostkey_algs[i]->name, str, len)) {
+               ssh->hostkey = hostkey_algs[i];
+               break;
+           }
+       }
+       ssh2_pkt_getstring(ssh, &str, &len);    /* client->server cipher */
+       s->warn = 0;
+       for (i = 0; i < s->n_preferred_ciphers; i++) {
+           const struct ssh2_ciphers *c = s->preferred_ciphers[i];
+           if (!c) {
+               s->warn = 1;
+           } else {
+               for (j = 0; j < c->nciphers; j++) {
+                   if (in_commasep_string(c->list[j]->name, str, len)) {
+                       s->cscipher_tobe = c->list[j];
+                       break;
+                   }
                }
            }
+           if (s->cscipher_tobe) {
+               if (s->warn)
+                   askcipher(s->cscipher_tobe->name, 1);
+               break;
+           }
        }
-       if (cscipher_tobe) {
-           if (warn)
-               askcipher(cscipher_tobe->name, 1);
-           break;
+       if (!s->cscipher_tobe) {
+           bombout(("Couldn't agree a client-to-server cipher (available: %s)", str));
+           crReturn(0);
        }
-    }
-    if (!cscipher_tobe) {
-       bombout(("Couldn't agree a client-to-server cipher (available: %s)", str));
-       crReturn(0);
-    }
 
-    ssh2_pkt_getstring(&str, &len);    /* server->client cipher */
-    warn = 0;
-    for (i = 0; i < n_preferred_ciphers; i++) {
-       const struct ssh2_ciphers *c = preferred_ciphers[i];
-       if (!c) {
-           warn = 1;
-       } else {
-           for (j = 0; j < c->nciphers; j++) {
-               if (in_commasep_string(c->list[j]->name, str, len)) {
-                   sccipher_tobe = c->list[j];
-                   break;
+       ssh2_pkt_getstring(ssh, &str, &len);    /* server->client cipher */
+       s->warn = 0;
+       for (i = 0; i < s->n_preferred_ciphers; i++) {
+           const struct ssh2_ciphers *c = s->preferred_ciphers[i];
+           if (!c) {
+               s->warn = 1;
+           } else {
+               for (j = 0; j < c->nciphers; j++) {
+                   if (in_commasep_string(c->list[j]->name, str, len)) {
+                       s->sccipher_tobe = c->list[j];
+                       break;
+                   }
                }
            }
+           if (s->sccipher_tobe) {
+               if (s->warn)
+                   askcipher(s->sccipher_tobe->name, 2);
+               break;
+           }
        }
-       if (sccipher_tobe) {
-           if (warn)
-               askcipher(sccipher_tobe->name, 2);
-           break;
+       if (!s->sccipher_tobe) {
+           bombout(("Couldn't agree a server-to-client cipher (available: %s)", str));
+           crReturn(0);
        }
-    }
-    if (!sccipher_tobe) {
-       bombout(("Couldn't agree a server-to-client cipher (available: %s)", str));
-       crReturn(0);
-    }
 
-    ssh2_pkt_getstring(&str, &len);    /* client->server mac */
-    for (i = 0; i < nmacs; i++) {
-       if (in_commasep_string(maclist[i]->name, str, len)) {
-           csmac_tobe = maclist[i];
-           break;
+       ssh2_pkt_getstring(ssh, &str, &len);    /* client->server mac */
+       for (i = 0; i < s->nmacs; i++) {
+           if (in_commasep_string(s->maclist[i]->name, str, len)) {
+               s->csmac_tobe = s->maclist[i];
+               break;
+           }
        }
-    }
-    ssh2_pkt_getstring(&str, &len);    /* server->client mac */
-    for (i = 0; i < nmacs; i++) {
-       if (in_commasep_string(maclist[i]->name, str, len)) {
-           scmac_tobe = maclist[i];
-           break;
+       ssh2_pkt_getstring(ssh, &str, &len);    /* server->client mac */
+       for (i = 0; i < s->nmacs; i++) {
+           if (in_commasep_string(s->maclist[i]->name, str, len)) {
+               s->scmac_tobe = s->maclist[i];
+               break;
+           }
        }
-    }
-    ssh2_pkt_getstring(&str, &len);    /* client->server compression */
-    for (i = 0; i < lenof(compressions) + 1; i++) {
-       const struct ssh_compress *c =
-           i == 0 ? preferred_comp : compressions[i - 1];
-       if (in_commasep_string(c->name, str, len)) {
-           cscomp_tobe = c;
-           break;
+       ssh2_pkt_getstring(ssh, &str, &len);  /* client->server compression */
+       for (i = 0; i < lenof(compressions) + 1; i++) {
+           const struct ssh_compress *c =
+               i == 0 ? s->preferred_comp : compressions[i - 1];
+           if (in_commasep_string(c->name, str, len)) {
+               s->cscomp_tobe = c;
+               break;
+           }
        }
-    }
-    ssh2_pkt_getstring(&str, &len);    /* server->client compression */
-    for (i = 0; i < lenof(compressions) + 1; i++) {
-       const struct ssh_compress *c =
-           i == 0 ? preferred_comp : compressions[i - 1];
-       if (in_commasep_string(c->name, str, len)) {
-           sccomp_tobe = c;
-           break;
+       ssh2_pkt_getstring(ssh, &str, &len);  /* server->client compression */
+       for (i = 0; i < lenof(compressions) + 1; i++) {
+           const struct ssh_compress *c =
+               i == 0 ? s->preferred_comp : compressions[i - 1];
+           if (in_commasep_string(c->name, str, len)) {
+               s->sccomp_tobe = c;
+               break;
+           }
        }
     }
 
@@ -3877,89 +3894,90 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     {
        int csbits, scbits;
 
-       csbits = cscipher_tobe->keylen;
-       scbits = sccipher_tobe->keylen;
-       nbits = (csbits > scbits ? csbits : scbits);
+       csbits = s->cscipher_tobe->keylen;
+       scbits = s->sccipher_tobe->keylen;
+       s->nbits = (csbits > scbits ? csbits : scbits);
     }
     /* The keys only have 160-bit entropy, since they're based on
      * a SHA-1 hash. So cap the key size at 160 bits. */
-    if (nbits > 160)
-       nbits = 160;
+    if (s->nbits > 160)
+       s->nbits = 160;
 
     /*
      * If we're doing Diffie-Hellman group exchange, start by
      * requesting a group.
      */
-    if (kex == &ssh_diffiehellman_gex) {
+    if (ssh->kex == &ssh_diffiehellman_gex) {
        logevent("Doing Diffie-Hellman group exchange");
-       ssh_pkt_ctx |= SSH2_PKTCTX_DHGEX;
+       ssh->pkt_ctx |= SSH2_PKTCTX_DHGEX;
        /*
         * Work out how big a DH group we will need to allow that
         * much data.
         */
-       pbits = 512 << ((nbits - 1) / 64);
-       ssh2_pkt_init(SSH2_MSG_KEX_DH_GEX_REQUEST);
-       ssh2_pkt_adduint32(pbits);
-       ssh2_pkt_send();
+       s->pbits = 512 << ((s->nbits - 1) / 64);
+       ssh2_pkt_init(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST);
+       ssh2_pkt_adduint32(ssh, s->pbits);
+       ssh2_pkt_send(ssh);
 
        crWaitUntil(ispkt);
-       if (pktin.type != SSH2_MSG_KEX_DH_GEX_GROUP) {
+       if (ssh->pktin.type != SSH2_MSG_KEX_DH_GEX_GROUP) {
            bombout(("expected key exchange group packet from server"));
            crReturn(0);
        }
-       p = ssh2_pkt_getmp();
-       g = ssh2_pkt_getmp();
-       dh_setup_group(p, g);
-       kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
-       kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
+       s->p = ssh2_pkt_getmp(ssh);
+       s->g = ssh2_pkt_getmp(ssh);
+       dh_setup_group(s->p, s->g);
+       s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
+       s->kex_reply_value = SSH2_MSG_KEX_DH_GEX_REPLY;
     } else {
-       ssh_pkt_ctx |= SSH2_PKTCTX_DHGROUP1;
+       ssh->pkt_ctx |= SSH2_PKTCTX_DHGROUP1;
        dh_setup_group1();
-       kex_init_value = SSH2_MSG_KEXDH_INIT;
-       kex_reply_value = SSH2_MSG_KEXDH_REPLY;
+       s->kex_init_value = SSH2_MSG_KEXDH_INIT;
+       s->kex_reply_value = SSH2_MSG_KEXDH_REPLY;
     }
 
     logevent("Doing Diffie-Hellman key exchange");
     /*
      * Now generate and send e for Diffie-Hellman.
      */
-    e = dh_create_e(nbits * 2);
-    ssh2_pkt_init(kex_init_value);
-    ssh2_pkt_addmp(e);
-    ssh2_pkt_send();
+    s->e = dh_create_e(s->nbits * 2);
+    ssh2_pkt_init(ssh, s->kex_init_value);
+    ssh2_pkt_addmp(ssh, s->e);
+    ssh2_pkt_send(ssh);
 
     crWaitUntil(ispkt);
-    if (pktin.type != kex_reply_value) {
+    if (ssh->pktin.type != s->kex_reply_value) {
        bombout(("expected key exchange reply packet from server"));
        crReturn(0);
     }
-    ssh2_pkt_getstring(&hostkeydata, &hostkeylen);
-    f = ssh2_pkt_getmp();
-    ssh2_pkt_getstring(&sigdata, &siglen);
+    ssh2_pkt_getstring(ssh, &s->hostkeydata, &s->hostkeylen);
+    s->f = ssh2_pkt_getmp(ssh);
+    ssh2_pkt_getstring(ssh, &s->sigdata, &s->siglen);
 
-    K = dh_find_K(f);
+    s->K = dh_find_K(s->f);
 
-    sha_string(&exhash, hostkeydata, hostkeylen);
-    if (kex == &ssh_diffiehellman_gex) {
-       sha_uint32(&exhash, pbits);
-       sha_mpint(&exhash, p);
-       sha_mpint(&exhash, g);
+    sha_string(&ssh->exhash, s->hostkeydata, s->hostkeylen);
+    if (ssh->kex == &ssh_diffiehellman_gex) {
+       sha_uint32(&ssh->exhash, s->pbits);
+       sha_mpint(&ssh->exhash, s->p);
+       sha_mpint(&ssh->exhash, s->g);
     }
-    sha_mpint(&exhash, e);
-    sha_mpint(&exhash, f);
-    sha_mpint(&exhash, K);
-    SHA_Final(&exhash, exchange_hash);
+    sha_mpint(&ssh->exhash, s->e);
+    sha_mpint(&ssh->exhash, s->f);
+    sha_mpint(&ssh->exhash, s->K);
+    SHA_Final(&ssh->exhash, s->exchange_hash);
 
     dh_cleanup();
 
 #if 0
     debug(("Exchange hash is:\n"));
-    dmemdump(exchange_hash, 20);
+    dmemdump(s->exchange_hash, 20);
 #endif
 
-    hkey = hostkey->newkey(hostkeydata, hostkeylen);
-    if (!hkey ||
-       !hostkey->verifysig(hkey, sigdata, siglen, exchange_hash, 20)) {
+    s->hkey = ssh->hostkey->newkey(s->hostkeydata, s->hostkeylen);
+    if (!s->hkey ||
+       !ssh->hostkey->verifysig(s->hkey, s->sigdata, s->siglen,
+                                s->exchange_hash, 20)) {
        bombout(("Server's host key did not match the signature supplied"));
        crReturn(0);
     }
@@ -3968,29 +3986,29 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
      * Authenticate remote host: verify host key. (We've already
      * checked the signature of the exchange hash.)
      */
-    keystr = hostkey->fmtkey(hkey);
-    fingerprint = hostkey->fingerprint(hkey);
-    verify_ssh_host_key(savedhost, savedport, hostkey->keytype,
-                       keystr, fingerprint);
-    if (first_kex) {                  /* don't bother logging this in rekeys */
+    s->keystr = ssh->hostkey->fmtkey(s->hkey);
+    s->fingerprint = ssh->hostkey->fingerprint(s->hkey);
+    verify_ssh_host_key(ssh->savedhost, ssh->savedport, ssh->hostkey->keytype,
+                       s->keystr, s->fingerprint);
+    if (s->first_kex) {                       /* don't bother logging this in rekeys */
        logevent("Host key fingerprint is:");
-       logevent(fingerprint);
+       logevent(s->fingerprint);
     }
-    sfree(fingerprint);
-    sfree(keystr);
-    hostkey->freekey(hkey);
+    sfree(s->fingerprint);
+    sfree(s->keystr);
+    ssh->hostkey->freekey(s->hkey);
 
     /*
      * Send SSH2_MSG_NEWKEYS.
      */
-    ssh2_pkt_init(SSH2_MSG_NEWKEYS);
-    ssh2_pkt_send();
+    ssh2_pkt_init(ssh, SSH2_MSG_NEWKEYS);
+    ssh2_pkt_send(ssh);
 
     /*
      * Expect SSH2_MSG_NEWKEYS from server.
      */
     crWaitUntil(ispkt);
-    if (pktin.type != SSH2_MSG_NEWKEYS) {
+    if (ssh->pktin.type != SSH2_MSG_NEWKEYS) {
        bombout(("expected new-keys packet from server"));
        crReturn(0);
     }
@@ -3998,32 +4016,36 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
     /*
      * Create and initialise session keys.
      */
-    cscipher = cscipher_tobe;
-    sccipher = sccipher_tobe;
-    csmac = csmac_tobe;
-    scmac = scmac_tobe;
-    cscomp = cscomp_tobe;
-    sccomp = sccomp_tobe;
-    cscomp->compress_init();
-    sccomp->decompress_init();
+    ssh->cscipher = s->cscipher_tobe;
+    ssh->sccipher = s->sccipher_tobe;
+    ssh->csmac = s->csmac_tobe;
+    ssh->scmac = s->scmac_tobe;
+    ssh->cscomp = s->cscomp_tobe;
+    ssh->sccomp = s->sccomp_tobe;
+    ssh->cscomp->compress_init();
+    ssh->sccomp->decompress_init();
     /*
      * Set IVs after keys. Here we use the exchange hash from the
      * _first_ key exchange.
      */
-    if (first_kex)
-       memcpy(ssh2_session_id, exchange_hash, sizeof(exchange_hash));
-    ssh2_mkkey(K, exchange_hash, ssh2_session_id, 'C', keyspace);
-    cscipher->setcskey(keyspace);
-    ssh2_mkkey(K, exchange_hash, ssh2_session_id, 'D', keyspace);
-    sccipher->setsckey(keyspace);
-    ssh2_mkkey(K, exchange_hash, ssh2_session_id, 'A', keyspace);
-    cscipher->setcsiv(keyspace);
-    ssh2_mkkey(K, exchange_hash, ssh2_session_id, 'B', keyspace);
-    sccipher->setsciv(keyspace);
-    ssh2_mkkey(K, exchange_hash, ssh2_session_id, 'E', keyspace);
-    csmac->setcskey(keyspace);
-    ssh2_mkkey(K, exchange_hash, ssh2_session_id, 'F', keyspace);
-    scmac->setsckey(keyspace);
+    {
+       unsigned char keyspace[40];
+       if (s->first_kex)
+           memcpy(ssh->v2_session_id, s->exchange_hash,
+                  sizeof(s->exchange_hash));
+       ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'C',keyspace);
+       ssh->cscipher->setcskey(keyspace);
+       ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'D',keyspace);
+       ssh->sccipher->setsckey(keyspace);
+       ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'A',keyspace);
+       ssh->cscipher->setcsiv(keyspace);
+       ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'B',keyspace);
+       ssh->sccipher->setsciv(keyspace);
+       ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'E',keyspace);
+       ssh->csmac->setcskey(keyspace);
+       ssh2_mkkey(ssh,s->K,s->exchange_hash,ssh->v2_session_id,'F',keyspace);
+       ssh->scmac->setsckey(keyspace);
+    }
 
     /*
      * If this is the first key exchange phase, we must pass the
@@ -4033,10 +4055,10 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
      * exchange phases, we don't pass SSH2_MSG_NEWKEYS on, because
      * it would only confuse the layer above.
      */
-    if (!first_kex) {
+    if (!s->first_kex) {
        crReturn(0);
     }
-    first_kex = 0;
+    s->first_kex = 0;
 
     /*
      * Now we're encrypting. Begin returning 1 to the protocol main
@@ -4044,7 +4066,7 @@ static int do_ssh2_transport(unsigned char *in, int inlen, int ispkt)
      * transport. If we ever see a KEXINIT, we must go back to the
      * start.
      */
-    while (!(ispkt && pktin.type == SSH2_MSG_KEXINIT)) {
+    while (!(ispkt && ssh->pktin.type == SSH2_MSG_KEXINIT)) {
        crReturn(1);
     }
     logevent("Server initiated key re-exchange");
@@ -4067,6 +4089,8 @@ static void ssh2_add_channel_data(struct ssh_channel *c, char *buf,
  */
 static int ssh2_try_send(struct ssh_channel *c)
 {
+    Ssh ssh = c->ssh;
+
     while (c->v.v2.remwindow > 0 && bufchain_size(&c->v.v2.outbuffer) > 0) {
        int len;
        void *data;
@@ -4075,11 +4099,11 @@ static int ssh2_try_send(struct ssh_channel *c)
            len = c->v.v2.remwindow;
        if ((unsigned)len > c->v.v2.remmaxpkt)
            len = c->v.v2.remmaxpkt;
-       ssh2_pkt_init(SSH2_MSG_CHANNEL_DATA);
-       ssh2_pkt_adduint32(c->remoteid);
-       ssh2_pkt_addstring_start();
-       ssh2_pkt_addstring_data(data, len);
-       ssh2_pkt_send();
+       ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_DATA);
+       ssh2_pkt_adduint32(ssh, c->remoteid);
+       ssh2_pkt_addstring_start(ssh);
+       ssh2_pkt_addstring_data(ssh, data, len);
+       ssh2_pkt_send(ssh);
        bufchain_consume(&c->v.v2.outbuffer, len);
        c->v.v2.remwindow -= len;
     }
@@ -4096,6 +4120,8 @@ static int ssh2_try_send(struct ssh_channel *c)
  */
 static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
 {
+    Ssh ssh = c->ssh;
+
     /*
      * Never send WINDOW_ADJUST for a channel that the remote side
      * already thinks it's closed; there's no point, since it won't
@@ -4105,10 +4131,10 @@ static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
        return;
 
     if (newwin > c->v.v2.locwindow) {
-       ssh2_pkt_init(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
-       ssh2_pkt_adduint32(c->remoteid);
-       ssh2_pkt_adduint32(newwin - c->v.v2.locwindow);
-       ssh2_pkt_send();
+       ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+       ssh2_pkt_adduint32(ssh, c->remoteid);
+       ssh2_pkt_adduint32(ssh, newwin - c->v.v2.locwindow);
+       ssh2_pkt_send(ssh);
        c->v.v2.locwindow = newwin;
     }
 }
@@ -4116,44 +4142,56 @@ static void ssh2_set_window(struct ssh_channel *c, unsigned newwin)
 /*
  * Handle the SSH2 userauth and connection layers.
  */
-static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
+static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
 {
-    static enum {
-       AUTH_INVALID, AUTH_PUBLICKEY_AGENT, AUTH_PUBLICKEY_FILE,
-       AUTH_PASSWORD,
-        AUTH_KEYBOARD_INTERACTIVE
-    } method;
-    static enum {
-       AUTH_TYPE_NONE,
-       AUTH_TYPE_PUBLICKEY,
-       AUTH_TYPE_PUBLICKEY_OFFER_LOUD,
-       AUTH_TYPE_PUBLICKEY_OFFER_QUIET,
-       AUTH_TYPE_PASSWORD,
-       AUTH_TYPE_KEYBOARD_INTERACTIVE,
-       AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET
-    } type;
-    static int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter;
-    static int tried_pubkey_config, tried_agent, tried_keyb_inter;
-    static int kbd_inter_running;
-    static int we_are_in;
-    static int num_prompts, curr_prompt, echo;
-    static char username[100];
-    static int got_username;
-    static char pwprompt[200];
-    static char password[100];
-    static void *publickey_blob;
-    static int publickey_bloblen;
-
-    crBegin;
+    struct do_ssh2_authconn_state {
+       enum {
+           AUTH_INVALID, AUTH_PUBLICKEY_AGENT, AUTH_PUBLICKEY_FILE,
+               AUTH_PASSWORD,
+               AUTH_KEYBOARD_INTERACTIVE
+       } method;
+       enum {
+           AUTH_TYPE_NONE,
+               AUTH_TYPE_PUBLICKEY,
+               AUTH_TYPE_PUBLICKEY_OFFER_LOUD,
+               AUTH_TYPE_PUBLICKEY_OFFER_QUIET,
+               AUTH_TYPE_PASSWORD,
+               AUTH_TYPE_KEYBOARD_INTERACTIVE,
+               AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET
+       } type;
+       int gotit, need_pw, can_pubkey, can_passwd, can_keyb_inter;
+       int tried_pubkey_config, tried_agent, tried_keyb_inter;
+       int kbd_inter_running;
+       int we_are_in;
+       int num_prompts, curr_prompt, echo;
+       char username[100];
+       int got_username;
+       char pwprompt[200];
+       char password[100];
+       void *publickey_blob;
+       int publickey_bloblen;
+       unsigned char request[5], *response, *p;
+       int responselen;
+       int keyi, nkeys;
+       int authed;
+       char *pkblob, *alg, *commentp;
+       int pklen, alglen, commentlen;
+       int siglen, retlen, len;
+       char *q, *agentreq, *ret;
+       int try_send;
+    };
+    crState(do_ssh2_authconn_state);
+
+    crBegin(ssh->do_ssh2_authconn_crstate);
 
     /*
      * Request userauth protocol, and await a response to it.
      */
-    ssh2_pkt_init(SSH2_MSG_SERVICE_REQUEST);
-    ssh2_pkt_addstring("ssh-userauth");
-    ssh2_pkt_send();
+    ssh2_pkt_init(ssh, SSH2_MSG_SERVICE_REQUEST);
+    ssh2_pkt_addstring(ssh, "ssh-userauth");
+    ssh2_pkt_send(ssh);
     crWaitUntilV(ispkt);
-    if (pktin.type != SSH2_MSG_SERVICE_ACCEPT) {
+    if (ssh->pktin.type != SSH2_MSG_SERVICE_ACCEPT) {
        bombout(("Server refused user authentication protocol"));
        crReturnV;
     }
@@ -4161,10 +4199,10 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     /*
      * We repeat this whole loop, including the username prompt,
      * until we manage a successful authentication. If the user
-     * types the wrong _password_, they are sent back to the
-     * beginning to try another username. (If they specify a
-     * username in the config, they are never asked, even if they
-     * do give a wrong password.)
+     * types the wrong _password_, they can be sent back to the
+     * beginning to try another username, if this is configured on.
+     * (If they specify a username in the config, they are never
+     * asked, even if they do give a wrong password.)
      * 
      * I think this best serves the needs of
      * 
@@ -4182,13 +4220,13 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
      *    the username they will want to be able to get back and
      *    retype it!
      */
-    username[0] = '\0';
-    got_username = FALSE;
+    s->username[0] = '\0';
+    s->got_username = FALSE;
     do {
        /*
         * Get a username.
         */
-       if (got_username && !cfg.change_username) {
+       if (s->got_username && !cfg.change_username) {
            /*
             * We got a username last time round this loop, and
             * with change_username turned off we don't try to get
@@ -4197,87 +4235,88 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
        } else if ((flags & FLAG_INTERACTIVE) && !*cfg.username) {
            if (ssh_get_line && !ssh_getline_pw_only) {
                if (!ssh_get_line("login as: ",
-                                 username, sizeof(username), FALSE)) {
+                                 s->username, sizeof(s->username), FALSE)) {
                    /*
                     * get_line failed to get a username.
                     * Terminate.
                     */
                    logevent("No username provided. Abandoning session.");
-                   ssh_state = SSH_STATE_CLOSED;
+                   ssh->state = SSH_STATE_CLOSED;
                    crReturnV;
                }
            } else {
-               static int ret;
-               c_write_str("login as: ");
-               ssh_send_ok = 1;
-               setup_userpass_input(username, sizeof(username), 1);
+               int ret;               /* need not be saved across crReturn */
+               c_write_str(ssh, "login as: ");
+               ssh->send_ok = 1;
+               setup_userpass_input(ssh, s->username, sizeof(s->username), 1);
                do {
                    crWaitUntilV(!ispkt);
-                   ret = process_userpass_input(in, inlen);
+                   ret = process_userpass_input(ssh, in, inlen);
                } while (ret == 0);
                if (ret < 0)
                    cleanup_exit(0);
            }
-           c_write_str("\r\n");
-           username[strcspn(username, "\n\r")] = '\0';
+           c_write_str(ssh, "\r\n");
+           s->username[strcspn(s->username, "\n\r")] = '\0';
        } else {
            char stuff[200];
-           strncpy(username, cfg.username, sizeof(username));
-           username[sizeof(username)-1] = '\0';
+           strncpy(s->username, cfg.username, sizeof(s->username));
+           s->username[sizeof(s->username)-1] = '\0';
            if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE)) {
-               sprintf(stuff, "Using username \"%s\".\r\n", username);
-               c_write_str(stuff);
+               sprintf(stuff, "Using username \"%s\".\r\n", s->username);
+               c_write_str(ssh, stuff);
            }
        }
-       got_username = TRUE;
+       s->got_username = TRUE;
 
        /*
         * Send an authentication request using method "none": (a)
         * just in case it succeeds, and (b) so that we know what
         * authentication methods we can usefully try next.
         */
-       ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-
-       ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-       ssh2_pkt_addstring(username);
-       ssh2_pkt_addstring("ssh-connection");   /* service requested */
-       ssh2_pkt_addstring("none");    /* method */
-       ssh2_pkt_send();
-       type = AUTH_TYPE_NONE;
-       gotit = FALSE;
-       we_are_in = FALSE;
-
-       tried_pubkey_config = FALSE;
-       tried_agent = FALSE;
-       tried_keyb_inter = FALSE;
-       kbd_inter_running = FALSE;
+       ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+
+       ssh2_pkt_init(ssh, SSH2_MSG_USERAUTH_REQUEST);
+       ssh2_pkt_addstring(ssh, s->username);
+       ssh2_pkt_addstring(ssh, "ssh-connection");/* service requested */
+       ssh2_pkt_addstring(ssh, "none");    /* method */
+       ssh2_pkt_send(ssh);
+       s->type = AUTH_TYPE_NONE;
+       s->gotit = FALSE;
+       s->we_are_in = FALSE;
+
+       s->tried_pubkey_config = FALSE;
+       s->tried_agent = FALSE;
+       s->tried_keyb_inter = FALSE;
+       s->kbd_inter_running = FALSE;
        /* Load the pub half of cfg.keyfile so we notice if it's in Pageant */
        if (*cfg.keyfile) {
            int keytype;
            logeventf("Reading private key file \"%.150s\"", cfg.keyfile);
            keytype = key_type(cfg.keyfile);
-           if (keytype == SSH_KEYTYPE_SSH2)
-               publickey_blob = ssh2_userkey_loadpub(cfg.keyfile, NULL,
-                                                     &publickey_bloblen);
-           else {
+           if (keytype == SSH_KEYTYPE_SSH2) {
+               s->publickey_blob =
+                   ssh2_userkey_loadpub(cfg.keyfile, NULL,
+                                        &s->publickey_bloblen);
+           } else {
                char msgbuf[256];
                logeventf("Unable to use this key file (%s)",
                        key_type_to_str(keytype));
                sprintf(msgbuf, "Unable to use key file \"%.150s\" (%s)\r\n",
                        cfg.keyfile, key_type_to_str(keytype));
-               c_write_str(msgbuf);
-               publickey_blob = NULL;
+               c_write_str(ssh, msgbuf);
+               s->publickey_blob = NULL;
            }
        } else
-           publickey_blob = NULL;
+           s->publickey_blob = NULL;
 
        while (1) {
            /*
             * Wait for the result of the last authentication request.
             */
-           if (!gotit)
+           if (!s->gotit)
                crWaitUntilV(ispkt);
-           while (pktin.type == SSH2_MSG_USERAUTH_BANNER) {
+           while (ssh->pktin.type == SSH2_MSG_USERAUTH_BANNER) {
                char *banner;
                int size;
                /*
@@ -4289,20 +4328,20 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                 * output of (say) plink.)
                 */
                if (flags & (FLAG_VERBOSE | FLAG_INTERACTIVE)) {
-                   ssh2_pkt_getstring(&banner, &size);
+                   ssh2_pkt_getstring(ssh, &banner, &size);
                    if (banner)
-                       c_write_untrusted(banner, size);
+                       c_write_untrusted(ssh, banner, size);
                }
                crWaitUntilV(ispkt);
            }
-           if (pktin.type == SSH2_MSG_USERAUTH_SUCCESS) {
+           if (ssh->pktin.type == SSH2_MSG_USERAUTH_SUCCESS) {
                logevent("Access granted");
-               we_are_in = TRUE;
+               s->we_are_in = TRUE;
                break;
            }
 
-           if (kbd_inter_running &&
-               pktin.type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
+           if (s->kbd_inter_running &&
+               ssh->pktin.type == SSH2_MSG_USERAUTH_INFO_REQUEST) {
                /*
                 * This is either a further set-of-prompts packet
                 * in keyboard-interactive authentication, or it's
@@ -4310,27 +4349,27 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                 * set. In the former case, we must reset the
                 * curr_prompt variable.
                 */
-               if (!gotit)
-                   curr_prompt = 0;
-           } else if (pktin.type != SSH2_MSG_USERAUTH_FAILURE) {
+               if (!s->gotit)
+                   s->curr_prompt = 0;
+           } else if (ssh->pktin.type != SSH2_MSG_USERAUTH_FAILURE) {
                bombout(("Strange packet received during authentication: type %d",
-                        pktin.type));
+                        ssh->pktin.type));
                crReturnV;
            }
 
-           gotit = FALSE;
+           s->gotit = FALSE;
 
            /*
             * OK, we're now sitting on a USERAUTH_FAILURE message, so
             * we can look at the string in it and know what we can
             * helpfully try next.
             */
-           if (pktin.type == SSH2_MSG_USERAUTH_FAILURE) {
+           if (ssh->pktin.type == SSH2_MSG_USERAUTH_FAILURE) {
                char *methods;
                int methlen;
-               ssh2_pkt_getstring(&methods, &methlen);
-               kbd_inter_running = FALSE;
-               if (!ssh2_pkt_getbool()) {
+               ssh2_pkt_getstring(ssh, &methods, &methlen);
+               s->kbd_inter_running = FALSE;
+               if (!ssh2_pkt_getbool(ssh)) {
                    /*
                     * We have received an unequivocal Access
                     * Denied. This can translate to a variety of
@@ -4352,38 +4391,38 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                     * whole loop so as to go back to the username
                     * prompt.
                     */
-                   if (type == AUTH_TYPE_NONE) {
+                   if (s->type == AUTH_TYPE_NONE) {
                        /* do nothing */
-                   } else if (type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD ||
-                              type == AUTH_TYPE_PUBLICKEY_OFFER_QUIET) {
-                       if (type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD)
-                           c_write_str("Server refused our key\r\n");
+                   } else if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD ||
+                              s->type == AUTH_TYPE_PUBLICKEY_OFFER_QUIET) {
+                       if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD)
+                           c_write_str(ssh, "Server refused our key\r\n");
                        logevent("Server refused public key");
-                   } else if (type == AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET) {
+                   } else if (s->type==AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET) {
                        /* server declined keyboard-interactive; ignore */
                    } else {
-                       c_write_str("Access denied\r\n");
+                       c_write_str(ssh, "Access denied\r\n");
                        logevent("Access denied");
-                       if (type == AUTH_TYPE_PASSWORD) {
-                           we_are_in = FALSE;
+                       if (s->type == AUTH_TYPE_PASSWORD) {
+                           s->we_are_in = FALSE;
                            break;
                        }
                    }
                } else {
-                   c_write_str("Further authentication required\r\n");
+                   c_write_str(ssh, "Further authentication required\r\n");
                    logevent("Further authentication required");
                }
 
-               can_pubkey =
+               s->can_pubkey =
                    in_commasep_string("publickey", methods, methlen);
-               can_passwd =
+               s->can_passwd =
                    in_commasep_string("password", methods, methlen);
-               can_keyb_inter = cfg.try_ki_auth &&
+               s->can_keyb_inter = cfg.try_ki_auth &&
                    in_commasep_string("keyboard-interactive", methods, methlen);
            }
 
-           method = 0;
-           ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+           s->method = 0;
+           ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
 
            /*
             * Most password/passphrase prompts will be
@@ -4391,163 +4430,160 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
             * Exception is that some keyboard-interactive prompts
             * can be echoing, in which case we'll set this to 1.
             */
-           echo = 0;
+           s->echo = 0;
 
-           if (!method && can_pubkey && agent_exists() && !tried_agent) {
+           if (!s->method && s->can_pubkey &&
+               agent_exists() && !s->tried_agent) {
                /*
                 * Attempt public-key authentication using Pageant.
                 */
-               static unsigned char request[5], *response, *p;
-               static int responselen;
-               static int i, nkeys;
-               static int authed = FALSE;
                void *r;
+               s->authed = FALSE;
 
-               ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-               ssh_pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
+               ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+               ssh->pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
 
-               tried_agent = TRUE;
+               s->tried_agent = TRUE;
 
                logevent("Pageant is running. Requesting keys.");
 
                /* Request the keys held by the agent. */
-               PUT_32BIT(request, 1);
-               request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
-               agent_query(request, 5, &r, &responselen);
-               response = (unsigned char *) r;
-               if (response && responselen >= 5 &&
-                   response[4] == SSH2_AGENT_IDENTITIES_ANSWER) {
-                   p = response + 5;
-                   nkeys = GET_32BIT(p);
-                   p += 4;
+               PUT_32BIT(s->request, 1);
+               s->request[4] = SSH2_AGENTC_REQUEST_IDENTITIES;
+               agent_query(s->request, 5, &r, &s->responselen);
+               s->response = (unsigned char *) r;
+               if (s->response && s->responselen >= 5 &&
+                   s->response[4] == SSH2_AGENT_IDENTITIES_ANSWER) {
+                   s->p = s->response + 5;
+                   s->nkeys = GET_32BIT(s->p);
+                   s->p += 4;
                    {
                        char buf[64];
-                       sprintf(buf, "Pageant has %d SSH2 keys", nkeys);
+                       sprintf(buf, "Pageant has %d SSH2 keys", s->nkeys);
                        logevent(buf);
                    }
-                   for (i = 0; i < nkeys; i++) {
-                       static char *pkblob, *alg, *commentp;
-                       static int pklen, alglen, commentlen;
-                       static int siglen, retlen, len;
-                       static char *q, *agentreq, *ret;
+                   for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
                        void *vret;
 
                        {
                            char buf[64];
-                           sprintf(buf, "Trying Pageant key #%d", i);
+                           sprintf(buf, "Trying Pageant key #%d", s->keyi);
                            logevent(buf);
                        }
-                       pklen = GET_32BIT(p);
-                       p += 4;
-                       if (publickey_blob &&
-                           pklen == publickey_bloblen &&
-                           !memcmp(p, publickey_blob, publickey_bloblen)) {
+                       s->pklen = GET_32BIT(s->p);
+                       s->p += 4;
+                       if (s->publickey_blob &&
+                           s->pklen == s->publickey_bloblen &&
+                           !memcmp(s->p, s->publickey_blob,
+                                   s->publickey_bloblen)) {
                            logevent("This key matches configured key file");
-                           tried_pubkey_config = 1;
+                           s->tried_pubkey_config = 1;
                        }
-                       pkblob = p;
-                       p += pklen;
-                       alglen = GET_32BIT(pkblob);
-                       alg = pkblob + 4;
-                       commentlen = GET_32BIT(p);
-                       p += 4;
-                       commentp = p;
-                       p += commentlen;
-                       ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-                       ssh2_pkt_addstring(username);
-                       ssh2_pkt_addstring("ssh-connection");   /* service requested */
-                       ssh2_pkt_addstring("publickey");        /* method */
-                       ssh2_pkt_addbool(FALSE);        /* no signature included */
-                       ssh2_pkt_addstring_start();
-                       ssh2_pkt_addstring_data(alg, alglen);
-                       ssh2_pkt_addstring_start();
-                       ssh2_pkt_addstring_data(pkblob, pklen);
-                       ssh2_pkt_send();
+                       s->pkblob = s->p;
+                       s->p += s->pklen;
+                       s->alglen = GET_32BIT(s->pkblob);
+                       s->alg = s->pkblob + 4;
+                       s->commentlen = GET_32BIT(s->p);
+                       s->p += 4;
+                       s->commentp = s->p;
+                       s->p += s->commentlen;
+                       ssh2_pkt_init(ssh, SSH2_MSG_USERAUTH_REQUEST);
+                       ssh2_pkt_addstring(ssh, s->username);
+                       ssh2_pkt_addstring(ssh, "ssh-connection");      /* service requested */
+                       ssh2_pkt_addstring(ssh, "publickey");   /* method */
+                       ssh2_pkt_addbool(ssh, FALSE);   /* no signature included */
+                       ssh2_pkt_addstring_start(ssh);
+                       ssh2_pkt_addstring_data(ssh, s->alg, s->alglen);
+                       ssh2_pkt_addstring_start(ssh);
+                       ssh2_pkt_addstring_data(ssh, s->pkblob, s->pklen);
+                       ssh2_pkt_send(ssh);
 
                        crWaitUntilV(ispkt);
-                       if (pktin.type != SSH2_MSG_USERAUTH_PK_OK) {
+                       if (ssh->pktin.type != SSH2_MSG_USERAUTH_PK_OK) {
                            logevent("Key refused");
                            continue;
                        }
 
                        if (flags & FLAG_VERBOSE) {
-                           c_write_str
-                               ("Authenticating with public key \"");
-                           c_write(commentp, commentlen);
-                           c_write_str("\" from agent\r\n");
+                           c_write_str(ssh, "Authenticating with "
+                                       "public key \"");
+                           c_write(ssh, s->commentp, s->commentlen);
+                           c_write_str(ssh, "\" from agent\r\n");
                        }
 
                        /*
                         * Server is willing to accept the key.
                         * Construct a SIGN_REQUEST.
                         */
-                       ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-                       ssh2_pkt_addstring(username);
-                       ssh2_pkt_addstring("ssh-connection");   /* service requested */
-                       ssh2_pkt_addstring("publickey");        /* method */
-                       ssh2_pkt_addbool(TRUE);
-                       ssh2_pkt_addstring_start();
-                       ssh2_pkt_addstring_data(alg, alglen);
-                       ssh2_pkt_addstring_start();
-                       ssh2_pkt_addstring_data(pkblob, pklen);
-
-                       siglen = pktout.length - 5 + 4 + 20;
-                       len = 1;       /* message type */
-                       len += 4 + pklen;       /* key blob */
-                       len += 4 + siglen;      /* data to sign */
-                       len += 4;      /* flags */
-                       agentreq = smalloc(4 + len);
-                       PUT_32BIT(agentreq, len);
-                       q = agentreq + 4;
-                       *q++ = SSH2_AGENTC_SIGN_REQUEST;
-                       PUT_32BIT(q, pklen);
-                       q += 4;
-                       memcpy(q, pkblob, pklen);
-                       q += pklen;
-                       PUT_32BIT(q, siglen);
-                       q += 4;
+                       ssh2_pkt_init(ssh, SSH2_MSG_USERAUTH_REQUEST);
+                       ssh2_pkt_addstring(ssh, s->username);
+                       ssh2_pkt_addstring(ssh, "ssh-connection");      /* service requested */
+                       ssh2_pkt_addstring(ssh, "publickey");   /* method */
+                       ssh2_pkt_addbool(ssh, TRUE);
+                       ssh2_pkt_addstring_start(ssh);
+                       ssh2_pkt_addstring_data(ssh, s->alg, s->alglen);
+                       ssh2_pkt_addstring_start(ssh);
+                       ssh2_pkt_addstring_data(ssh, s->pkblob, s->pklen);
+
+                       s->siglen = ssh->pktout.length - 5 + 4 + 20;
+                       s->len = 1;       /* message type */
+                       s->len += 4 + s->pklen; /* key blob */
+                       s->len += 4 + s->siglen;        /* data to sign */
+                       s->len += 4;      /* flags */
+                       s->agentreq = smalloc(4 + s->len);
+                       PUT_32BIT(s->agentreq, s->len);
+                       s->q = s->agentreq + 4;
+                       *s->q++ = SSH2_AGENTC_SIGN_REQUEST;
+                       PUT_32BIT(s->q, s->pklen);
+                       s->q += 4;
+                       memcpy(s->q, s->pkblob, s->pklen);
+                       s->q += s->pklen;
+                       PUT_32BIT(s->q, s->siglen);
+                       s->q += 4;
                        /* Now the data to be signed... */
-                       PUT_32BIT(q, 20);
-                       q += 4;
-                       memcpy(q, ssh2_session_id, 20);
-                       q += 20;
-                       memcpy(q, pktout.data + 5, pktout.length - 5);
-                       q += pktout.length - 5;
+                       PUT_32BIT(s->q, 20);
+                       s->q += 4;
+                       memcpy(s->q, ssh->v2_session_id, 20);
+                       s->q += 20;
+                       memcpy(s->q, ssh->pktout.data + 5,
+                              ssh->pktout.length - 5);
+                       s->q += ssh->pktout.length - 5;
                        /* And finally the (zero) flags word. */
-                       PUT_32BIT(q, 0);
-                       agent_query(agentreq, len + 4, &vret, &retlen);
-                       ret = vret;
-                       sfree(agentreq);
-                       if (ret) {
-                           if (ret[4] == SSH2_AGENT_SIGN_RESPONSE) {
+                       PUT_32BIT(s->q, 0);
+                       agent_query(s->agentreq, s->len + 4, &vret, &s->retlen);
+                       s->ret = vret;
+                       sfree(s->agentreq);
+                       if (s->ret) {
+                           if (s->ret[4] == SSH2_AGENT_SIGN_RESPONSE) {
                                logevent("Sending Pageant's response");
-                               ssh2_add_sigblob(pkblob, pklen,
-                                                ret + 9, GET_32BIT(ret + 5));
-                               ssh2_pkt_send();
-                               authed = TRUE;
+                               ssh2_add_sigblob(ssh, s->pkblob, s->pklen,
+                                                s->ret + 9,
+                                                GET_32BIT(s->ret + 5));
+                               ssh2_pkt_send(ssh);
+                               s->authed = TRUE;
                                break;
                            } else {
                                logevent
                                    ("Pageant failed to answer challenge");
-                               sfree(ret);
+                               sfree(s->ret);
                            }
                        }
                    }
-                   if (authed)
+                   if (s->authed)
                        continue;
                }
            }
 
-           if (!method && can_pubkey && publickey_blob
-               && !tried_pubkey_config) {
+           if (!s->method && s->can_pubkey && s->publickey_blob
+               && !s->tried_pubkey_config) {
                unsigned char *pub_blob;
                char *algorithm, *comment;
                int pub_blob_len;
 
-               tried_pubkey_config = TRUE;
+               s->tried_pubkey_config = TRUE;
 
-               ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-               ssh_pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
+               ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+               ssh->pkt_ctx |= SSH2_PKTCTX_PUBLICKEY;
 
                /*
                 * Try the public key supplied in the configuration.
@@ -4558,21 +4594,21 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                pub_blob = ssh2_userkey_loadpub(cfg.keyfile, &algorithm,
                                                &pub_blob_len);
                if (pub_blob) {
-                   ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-                   ssh2_pkt_addstring(username);
-                   ssh2_pkt_addstring("ssh-connection");       /* service requested */
-                   ssh2_pkt_addstring("publickey");    /* method */
-                   ssh2_pkt_addbool(FALSE);    /* no signature included */
-                   ssh2_pkt_addstring(algorithm);
-                   ssh2_pkt_addstring_start();
-                   ssh2_pkt_addstring_data(pub_blob, pub_blob_len);
-                   ssh2_pkt_send();
+                   ssh2_pkt_init(ssh, SSH2_MSG_USERAUTH_REQUEST);
+                   ssh2_pkt_addstring(ssh, s->username);
+                   ssh2_pkt_addstring(ssh, "ssh-connection");  /* service requested */
+                   ssh2_pkt_addstring(ssh, "publickey");       /* method */
+                   ssh2_pkt_addbool(ssh, FALSE);       /* no signature included */
+                   ssh2_pkt_addstring(ssh, algorithm);
+                   ssh2_pkt_addstring_start(ssh);
+                   ssh2_pkt_addstring_data(ssh, pub_blob, pub_blob_len);
+                   ssh2_pkt_send(ssh);
                    logevent("Offered public key");     /* FIXME */
 
                    crWaitUntilV(ispkt);
-                   if (pktin.type != SSH2_MSG_USERAUTH_PK_OK) {
-                       gotit = TRUE;
-                       type = AUTH_TYPE_PUBLICKEY_OFFER_LOUD;
+                   if (ssh->pktin.type != SSH2_MSG_USERAUTH_PK_OK) {
+                       s->gotit = TRUE;
+                       s->type = AUTH_TYPE_PUBLICKEY_OFFER_LOUD;
                        continue;      /* key refused; give up on it */
                    }
 
@@ -4582,58 +4618,58 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                     * the key.
                     */
                    if (ssh2_userkey_encrypted(cfg.keyfile, &comment)) {
-                       sprintf(pwprompt,
+                       sprintf(s->pwprompt,
                                "Passphrase for key \"%.100s\": ",
                                comment);
-                       need_pw = TRUE;
+                       s->need_pw = TRUE;
                    } else {
-                       need_pw = FALSE;
+                       s->need_pw = FALSE;
                    }
-                   c_write_str("Authenticating with public key \"");
-                   c_write_str(comment);
-                   c_write_str("\"\r\n");
-                   method = AUTH_PUBLICKEY_FILE;
+                   c_write_str(ssh, "Authenticating with public key \"");
+                   c_write_str(ssh, comment);
+                   c_write_str(ssh, "\"\r\n");
+                   s->method = AUTH_PUBLICKEY_FILE;
                }
            }
 
-           if (!method && can_keyb_inter && !tried_keyb_inter) {
-               method = AUTH_KEYBOARD_INTERACTIVE;
-               type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
-               tried_keyb_inter = TRUE;
+           if (!s->method && s->can_keyb_inter && !s->tried_keyb_inter) {
+               s->method = AUTH_KEYBOARD_INTERACTIVE;
+               s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
+               s->tried_keyb_inter = TRUE;
 
-               ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-               ssh_pkt_ctx |= SSH2_PKTCTX_KBDINTER;
+               ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+               ssh->pkt_ctx |= SSH2_PKTCTX_KBDINTER;
 
-               ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-               ssh2_pkt_addstring(username);
-               ssh2_pkt_addstring("ssh-connection");   /* service requested */
-               ssh2_pkt_addstring("keyboard-interactive");     /* method */
-               ssh2_pkt_addstring(""); /* lang */
-               ssh2_pkt_addstring("");
-               ssh2_pkt_send();
+               ssh2_pkt_init(ssh, SSH2_MSG_USERAUTH_REQUEST);
+               ssh2_pkt_addstring(ssh, s->username);
+               ssh2_pkt_addstring(ssh, "ssh-connection");      /* service requested */
+               ssh2_pkt_addstring(ssh, "keyboard-interactive");        /* method */
+               ssh2_pkt_addstring(ssh, ""); /* lang */
+               ssh2_pkt_addstring(ssh, "");
+               ssh2_pkt_send(ssh);
 
                crWaitUntilV(ispkt);
-               if (pktin.type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
-                   if (pktin.type == SSH2_MSG_USERAUTH_FAILURE)
-                       gotit = TRUE;
+               if (ssh->pktin.type != SSH2_MSG_USERAUTH_INFO_REQUEST) {
+                   if (ssh->pktin.type == SSH2_MSG_USERAUTH_FAILURE)
+                       s->gotit = TRUE;
                    logevent("Keyboard-interactive authentication refused");
-                   type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
+                   s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET;
                    continue;
                }
 
-               kbd_inter_running = TRUE;
-               curr_prompt = 0;
+               s->kbd_inter_running = TRUE;
+               s->curr_prompt = 0;
            }
 
-           if (kbd_inter_running) {
-               method = AUTH_KEYBOARD_INTERACTIVE;
-               type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
-               tried_keyb_inter = TRUE;
+           if (s->kbd_inter_running) {
+               s->method = AUTH_KEYBOARD_INTERACTIVE;
+               s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
+               s->tried_keyb_inter = TRUE;
 
-               ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-               ssh_pkt_ctx |= SSH2_PKTCTX_KBDINTER;
+               ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+               ssh->pkt_ctx |= SSH2_PKTCTX_KBDINTER;
 
-               if (curr_prompt == 0) {
+               if (s->curr_prompt == 0) {
                    /*
                     * We've got a fresh USERAUTH_INFO_REQUEST.
                     * Display header data, and start going through
@@ -4642,111 +4678,112 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    char *name, *inst, *lang;
                    int name_len, inst_len, lang_len;
 
-                   ssh2_pkt_getstring(&name, &name_len);
-                   ssh2_pkt_getstring(&inst, &inst_len);
-                   ssh2_pkt_getstring(&lang, &lang_len);
+                   ssh2_pkt_getstring(ssh, &name, &name_len);
+                   ssh2_pkt_getstring(ssh, &inst, &inst_len);
+                   ssh2_pkt_getstring(ssh, &lang, &lang_len);
                    if (name_len > 0) {
-                       c_write_untrusted(name, name_len);
-                       c_write_str("\r\n");
+                       c_write_untrusted(ssh, name, name_len);
+                       c_write_str(ssh, "\r\n");
                    }
                    if (inst_len > 0) {
-                       c_write_untrusted(inst, inst_len);
-                       c_write_str("\r\n");
+                       c_write_untrusted(ssh, inst, inst_len);
+                       c_write_str(ssh, "\r\n");
                    }
-                   num_prompts = ssh2_pkt_getuint32();
+                   s->num_prompts = ssh2_pkt_getuint32(ssh);
                }
 
                /*
                 * If there are prompts remaining in the packet,
                 * display one and get a response.
                 */
-               if (curr_prompt < num_prompts) {
+               if (s->curr_prompt < s->num_prompts) {
                    char *prompt;
                    int prompt_len;
 
-                   ssh2_pkt_getstring(&prompt, &prompt_len);
+                   ssh2_pkt_getstring(ssh, &prompt, &prompt_len);
                    if (prompt_len > 0) {
-                       strncpy(pwprompt, prompt, sizeof(pwprompt));
-                       pwprompt[prompt_len < sizeof(pwprompt) ?
-                                prompt_len : sizeof(pwprompt)-1] = '\0';
+                       strncpy(s->pwprompt, prompt, sizeof(s->pwprompt));
+                       s->pwprompt[prompt_len < sizeof(s->pwprompt) ?
+                                   prompt_len : sizeof(s->pwprompt)-1] = '\0';
                    } else {
-                       strcpy(pwprompt,
+                       strcpy(s->pwprompt,
                               "<server failed to send prompt>: ");
                    }
-                   echo = ssh2_pkt_getbool();
-                   need_pw = TRUE;
+                   s->echo = ssh2_pkt_getbool(ssh);
+                   s->need_pw = TRUE;
                } else
-                   need_pw = FALSE;
+                   s->need_pw = FALSE;
            }
 
-           if (!method && can_passwd) {
-               method = AUTH_PASSWORD;
-               ssh_pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
-               ssh_pkt_ctx |= SSH2_PKTCTX_PASSWORD;
-               sprintf(pwprompt, "%.90s@%.90s's password: ", username,
-                       savedhost);
-               need_pw = TRUE;
+           if (!s->method && s->can_passwd) {
+               s->method = AUTH_PASSWORD;
+               ssh->pkt_ctx &= ~SSH2_PKTCTX_AUTH_MASK;
+               ssh->pkt_ctx |= SSH2_PKTCTX_PASSWORD;
+               sprintf(s->pwprompt, "%.90s@%.90s's password: ", s->username,
+                       ssh->savedhost);
+               s->need_pw = TRUE;
            }
 
-           if (need_pw) {
+           if (s->need_pw) {
                if (ssh_get_line) {
-                   if (!ssh_get_line(pwprompt, password,
-                                     sizeof(password), TRUE)) {
+                   if (!ssh_get_line(s->pwprompt, s->password,
+                                     sizeof(s->password), TRUE)) {
                        /*
                         * get_line failed to get a password (for
                         * example because one was supplied on the
                         * command line which has already failed to
                         * work). Terminate.
                         */
-                       ssh2_pkt_init(SSH2_MSG_DISCONNECT);
-                       ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION);
-                       ssh2_pkt_addstring
-                           ("No more passwords available to try");
-                       ssh2_pkt_addstring("en");       /* language tag */
-                       ssh2_pkt_send();
+                       ssh2_pkt_init(ssh, SSH2_MSG_DISCONNECT);
+                       ssh2_pkt_adduint32(ssh,SSH2_DISCONNECT_BY_APPLICATION);
+                       ssh2_pkt_addstring(ssh, "No more passwords available"
+                                          " to try");
+                       ssh2_pkt_addstring(ssh, "en");  /* language tag */
+                       ssh2_pkt_send(ssh);
                        logevent("Unable to authenticate");
                        connection_fatal("Unable to authenticate");
-                       ssh_state = SSH_STATE_CLOSED;
+                       ssh->state = SSH_STATE_CLOSED;
                        crReturnV;
                    }
                } else {
-                   static int ret;
-                   c_write_untrusted(pwprompt, strlen(pwprompt));
-                   ssh_send_ok = 1;
+                   int ret;           /* need not be saved across crReturn */
+                   c_write_untrusted(ssh, s->pwprompt, strlen(s->pwprompt));
+                   ssh->send_ok = 1;
 
-                   setup_userpass_input(password, sizeof(password), echo);
+                   setup_userpass_input(ssh, s->password,
+                                        sizeof(s->password), s->echo);
                    do {
                        crWaitUntilV(!ispkt);
-                       ret = process_userpass_input(in, inlen);
+                       ret = process_userpass_input(ssh, in, inlen);
                    } while (ret == 0);
                    if (ret < 0)
                        cleanup_exit(0);
-                   c_write_str("\r\n");
+                   c_write_str(ssh, "\r\n");
                }
            }
 
-           if (method == AUTH_PUBLICKEY_FILE) {
+           if (s->method == AUTH_PUBLICKEY_FILE) {
                /*
                 * We have our passphrase. Now try the actual authentication.
                 */
                struct ssh2_userkey *key;
 
-               key = ssh2_load_userkey(cfg.keyfile, password);
+               key = ssh2_load_userkey(cfg.keyfile, s->password);
                if (key == SSH2_WRONG_PASSPHRASE || key == NULL) {
                    if (key == SSH2_WRONG_PASSPHRASE) {
-                       c_write_str("Wrong passphrase\r\n");
-                       tried_pubkey_config = FALSE;
+                       c_write_str(ssh, "Wrong passphrase\r\n");
+                       s->tried_pubkey_config = FALSE;
                    } else {
-                       c_write_str("Unable to load private key\r\n");
-                       tried_pubkey_config = TRUE;
+                       c_write_str(ssh, "Unable to load private key\r\n");
+                       s->tried_pubkey_config = TRUE;
                    }
                    /* Send a spurious AUTH_NONE to return to the top. */
-                   ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-                   ssh2_pkt_addstring(username);
-                   ssh2_pkt_addstring("ssh-connection");       /* service requested */
-                   ssh2_pkt_addstring("none"); /* method */
-                   ssh2_pkt_send();
-                   type = AUTH_TYPE_NONE;
+                   ssh2_pkt_init(ssh, SSH2_MSG_USERAUTH_REQUEST);
+                   ssh2_pkt_addstring(ssh, s->username);
+                   ssh2_pkt_addstring(ssh, "ssh-connection");  /* service requested */
+                   ssh2_pkt_addstring(ssh, "none");    /* method */
+                   ssh2_pkt_send(ssh);
+                   s->type = AUTH_TYPE_NONE;
                } else {
                    unsigned char *pkblob, *sigblob, *sigdata;
                    int pkblob_len, sigblob_len, sigdata_len;
@@ -4756,15 +4793,15 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                     * has announced that it's willing to accept it.
                     * Hallelujah. Generate a signature and send it.
                     */
-                   ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-                   ssh2_pkt_addstring(username);
-                   ssh2_pkt_addstring("ssh-connection");       /* service requested */
-                   ssh2_pkt_addstring("publickey");    /* method */
-                   ssh2_pkt_addbool(TRUE);
-                   ssh2_pkt_addstring(key->alg->name);
+                   ssh2_pkt_init(ssh, SSH2_MSG_USERAUTH_REQUEST);
+                   ssh2_pkt_addstring(ssh, s->username);
+                   ssh2_pkt_addstring(ssh, "ssh-connection");  /* service requested */
+                   ssh2_pkt_addstring(ssh, "publickey");       /* method */
+                   ssh2_pkt_addbool(ssh, TRUE);
+                   ssh2_pkt_addstring(ssh, key->alg->name);
                    pkblob = key->alg->public_blob(key->data, &pkblob_len);
-                   ssh2_pkt_addstring_start();
-                   ssh2_pkt_addstring_data(pkblob, pkblob_len);
+                   ssh2_pkt_addstring_start(ssh);
+                   ssh2_pkt_addstring_data(ssh, pkblob, pkblob_len);
 
                    /*
                     * The data to be signed is:
@@ -4774,24 +4811,24 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                     * followed by everything so far placed in the
                     * outgoing packet.
                     */
-                   sigdata_len = pktout.length - 5 + 4 + 20;
+                   sigdata_len = ssh->pktout.length - 5 + 4 + 20;
                    sigdata = smalloc(sigdata_len);
                    PUT_32BIT(sigdata, 20);
-                   memcpy(sigdata + 4, ssh2_session_id, 20);
-                   memcpy(sigdata + 24, pktout.data + 5,
-                          pktout.length - 5);
+                   memcpy(sigdata + 4, ssh->v2_session_id, 20);
+                   memcpy(sigdata + 24, ssh->pktout.data + 5,
+                          ssh->pktout.length - 5);
                    sigblob = key->alg->sign(key->data, sigdata,
                                             sigdata_len, &sigblob_len);
-                   ssh2_add_sigblob(pkblob, pkblob_len,
+                   ssh2_add_sigblob(ssh, pkblob, pkblob_len,
                                     sigblob, sigblob_len);
                    sfree(pkblob);
                    sfree(sigblob);
                    sfree(sigdata);
 
-                   ssh2_pkt_send();
-                   type = AUTH_TYPE_PUBLICKEY;
+                   ssh2_pkt_send(ssh);
+                   s->type = AUTH_TYPE_PUBLICKEY;
                }
-           } else if (method == AUTH_PASSWORD) {
+           } else if (s->method == AUTH_PASSWORD) {
                /*
                 * We send the password packet lumped tightly together with
                 * an SSH_MSG_IGNORE packet. The IGNORE packet contains a
@@ -4806,26 +4843,26 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                 * that probably doesn't have much to worry about from
                 * people who find out how long their password is!
                 */
-               ssh2_pkt_init(SSH2_MSG_USERAUTH_REQUEST);
-               ssh2_pkt_addstring(username);
-               ssh2_pkt_addstring("ssh-connection");   /* service requested */
-               ssh2_pkt_addstring("password");
-               ssh2_pkt_addbool(FALSE);
-               ssh2_pkt_addstring(password);
-               ssh2_pkt_defer();
+               ssh2_pkt_init(ssh, SSH2_MSG_USERAUTH_REQUEST);
+               ssh2_pkt_addstring(ssh, s->username);
+               ssh2_pkt_addstring(ssh, "ssh-connection");      /* service requested */
+               ssh2_pkt_addstring(ssh, "password");
+               ssh2_pkt_addbool(ssh, FALSE);
+               ssh2_pkt_addstring(ssh, s->password);
+               ssh2_pkt_defer(ssh);
                /*
                 * We'll include a string that's an exact multiple of the
                 * cipher block size. If the cipher is NULL for some
                 * reason, we don't do this trick at all because we gain
                 * nothing by it.
                 */
-               if (cscipher) {
+               if (ssh->cscipher) {
                    int stringlen, i;
 
-                   stringlen = (256 - deferred_len);
-                   stringlen += cscipher->blksize - 1;
-                   stringlen -= (stringlen % cscipher->blksize);
-                   if (cscomp) {
+                   stringlen = (256 - ssh->deferred_len);
+                   stringlen += ssh->cscipher->blksize - 1;
+                   stringlen -= (stringlen % ssh->cscipher->blksize);
+                   if (ssh->cscomp) {
                        /*
                         * Temporarily disable actual compression,
                         * so we can guarantee to get this string
@@ -4835,31 +4872,31 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                         * bytes we should adjust our string length
                         * by.
                         */
-                       stringlen -= cscomp->disable_compression();
+                       stringlen -= ssh->cscomp->disable_compression();
                    }
-                   ssh2_pkt_init(SSH2_MSG_IGNORE);
-                   ssh2_pkt_addstring_start();
+                   ssh2_pkt_init(ssh, SSH2_MSG_IGNORE);
+                   ssh2_pkt_addstring_start(ssh);
                    for (i = 0; i < stringlen; i++) {
                        char c = (char) random_byte();
-                       ssh2_pkt_addstring_data(&c, 1);
+                       ssh2_pkt_addstring_data(ssh, &c, 1);
                    }
-                   ssh2_pkt_defer();
+                   ssh2_pkt_defer(ssh);
                }
-               ssh_pkt_defersend();
+               ssh_pkt_defersend(ssh);
                logevent("Sent password");
-               type = AUTH_TYPE_PASSWORD;
-           } else if (method == AUTH_KEYBOARD_INTERACTIVE) {
-               if (curr_prompt == 0) {
-                   ssh2_pkt_init(SSH2_MSG_USERAUTH_INFO_RESPONSE);
-                   ssh2_pkt_adduint32(num_prompts);
+               s->type = AUTH_TYPE_PASSWORD;
+           } else if (s->method == AUTH_KEYBOARD_INTERACTIVE) {
+               if (s->curr_prompt == 0) {
+                   ssh2_pkt_init(ssh, SSH2_MSG_USERAUTH_INFO_RESPONSE);
+                   ssh2_pkt_adduint32(ssh, s->num_prompts);
                }
-               if (need_pw) {         /* only add pw if we just got one! */
-                   ssh2_pkt_addstring(password);
-                   memset(password, 0, sizeof(password));
-                   curr_prompt++;
+               if (s->need_pw) {      /* only add pw if we just got one! */
+                   ssh2_pkt_addstring(ssh, s->password);
+                   memset(s->password, 0, sizeof(s->password));
+                   s->curr_prompt++;
                }
-               if (curr_prompt >= num_prompts) {
-                   ssh2_pkt_send();
+               if (s->curr_prompt >= s->num_prompts) {
+                   ssh2_pkt_send(ssh);
                } else {
                    /*
                     * If there are prompts remaining, we set
@@ -4869,25 +4906,25 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                     * prompt out of the existing packet. Funky or
                     * what?
                     */
-                   gotit = TRUE;
+                   s->gotit = TRUE;
                }
-               type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
+               s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE;
            } else {
-               c_write_str
-                   ("No supported authentication methods left to try!\r\n");
-               logevent
-                   ("No supported authentications offered. Disconnecting");
-               ssh2_pkt_init(SSH2_MSG_DISCONNECT);
-               ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION);
-               ssh2_pkt_addstring
-                   ("No supported authentication methods available");
-               ssh2_pkt_addstring("en");       /* language tag */
-               ssh2_pkt_send();
-               ssh_state = SSH_STATE_CLOSED;
+               c_write_str(ssh, "No supported authentication methods"
+                           " left to try!\r\n");
+               logevent("No supported authentications offered."
+                        " Disconnecting");
+               ssh2_pkt_init(ssh, SSH2_MSG_DISCONNECT);
+               ssh2_pkt_adduint32(ssh, SSH2_DISCONNECT_BY_APPLICATION);
+               ssh2_pkt_addstring(ssh, "No supported authentication"
+                                  " methods available");
+               ssh2_pkt_addstring(ssh, "en");  /* language tag */
+               ssh2_pkt_send(ssh);
+               ssh->state = SSH_STATE_CLOSED;
                crReturnV;
            }
        }
-    } while (!we_are_in);
+    } while (!s->we_are_in);
 
     /*
      * Now we're authenticated for the connection protocol. The
@@ -4898,33 +4935,34 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
     /*
      * So now create a channel with a session in it.
      */
-    ssh_channels = newtree234(ssh_channelcmp);
-    mainchan = smalloc(sizeof(struct ssh_channel));
-    mainchan->localid = alloc_channel_id();
-    ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
-    ssh2_pkt_addstring("session");
-    ssh2_pkt_adduint32(mainchan->localid);
-    mainchan->v.v2.locwindow = OUR_V2_WINSIZE;
-    ssh2_pkt_adduint32(mainchan->v.v2.locwindow);      /* our window size */
-    ssh2_pkt_adduint32(0x4000UL);      /* our max pkt size */
-    ssh2_pkt_send();
+    ssh->channels = newtree234(ssh_channelcmp);
+    ssh->mainchan = smalloc(sizeof(struct ssh_channel));
+    ssh->mainchan->ssh = ssh;
+    ssh->mainchan->localid = alloc_channel_id(ssh);
+    ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_OPEN);
+    ssh2_pkt_addstring(ssh, "session");
+    ssh2_pkt_adduint32(ssh, ssh->mainchan->localid);
+    ssh->mainchan->v.v2.locwindow = OUR_V2_WINSIZE;
+    ssh2_pkt_adduint32(ssh, ssh->mainchan->v.v2.locwindow);/* our window size */
+    ssh2_pkt_adduint32(ssh, 0x4000UL);      /* our max pkt size */
+    ssh2_pkt_send(ssh);
     crWaitUntilV(ispkt);
-    if (pktin.type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
+    if (ssh->pktin.type != SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
        bombout(("Server refused to open a session"));
        crReturnV;
        /* FIXME: error data comes back in FAILURE packet */
     }
-    if (ssh2_pkt_getuint32() != mainchan->localid) {
+    if (ssh2_pkt_getuint32(ssh) != ssh->mainchan->localid) {
        bombout(("Server's channel confirmation cited wrong channel"));
        crReturnV;
     }
-    mainchan->remoteid = ssh2_pkt_getuint32();
-    mainchan->type = CHAN_MAINSESSION;
-    mainchan->closes = 0;
-    mainchan->v.v2.remwindow = ssh2_pkt_getuint32();
-    mainchan->v.v2.remmaxpkt = ssh2_pkt_getuint32();
-    bufchain_init(&mainchan->v.v2.outbuffer);
-    add234(ssh_channels, mainchan);
+    ssh->mainchan->remoteid = ssh2_pkt_getuint32(ssh);
+    ssh->mainchan->type = CHAN_MAINSESSION;
+    ssh->mainchan->closes = 0;
+    ssh->mainchan->v.v2.remwindow = ssh2_pkt_getuint32(ssh);
+    ssh->mainchan->v.v2.remmaxpkt = ssh2_pkt_getuint32(ssh);
+    bufchain_init(&ssh->mainchan->v.v2.outbuffer);
+    add234(ssh->channels, ssh->mainchan);
     logevent("Opened channel for session");
 
     /*
@@ -4934,38 +4972,38 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
        char proto[20], data[64];
        logevent("Requesting X11 forwarding");
        x11_invent_auth(proto, sizeof(proto), data, sizeof(data));
-       ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
-       ssh2_pkt_adduint32(mainchan->remoteid);
-       ssh2_pkt_addstring("x11-req");
-       ssh2_pkt_addbool(1);           /* want reply */
-       ssh2_pkt_addbool(0);           /* many connections */
-       ssh2_pkt_addstring(proto);
-       ssh2_pkt_addstring(data);
-       ssh2_pkt_adduint32(0);         /* screen number */
-       ssh2_pkt_send();
+       ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
+       ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
+       ssh2_pkt_addstring(ssh, "x11-req");
+       ssh2_pkt_addbool(ssh, 1);              /* want reply */
+       ssh2_pkt_addbool(ssh, 0);              /* many connections */
+       ssh2_pkt_addstring(ssh, proto);
+       ssh2_pkt_addstring(ssh, data);
+       ssh2_pkt_adduint32(ssh, 0);            /* screen number */
+       ssh2_pkt_send(ssh);
 
        do {
            crWaitUntilV(ispkt);
-           if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
-               unsigned i = ssh2_pkt_getuint32();
+           if (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+               unsigned i = ssh2_pkt_getuint32(ssh);
                struct ssh_channel *c;
-               c = find234(ssh_channels, &i, ssh_channelfind);
+               c = find234(ssh->channels, &i, ssh_channelfind);
                if (!c)
                    continue;          /* nonexistent channel */
-               c->v.v2.remwindow += ssh2_pkt_getuint32();
+               c->v.v2.remwindow += ssh2_pkt_getuint32(ssh);
            }
-       } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+       } while (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
 
-       if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
-           if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
+       if (ssh->pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
+           if (ssh->pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
                bombout(("Unexpected response to X11 forwarding request:"
-                        " packet type %d", pktin.type));
+                        " packet type %d", ssh->pktin.type));
                crReturnV;
            }
            logevent("X11 forwarding refused");
        } else {
            logevent("X11 forwarding enabled");
-           ssh_X11_fwd_enabled = TRUE;
+           ssh->X11_fwd_enabled = TRUE;
        }
     }
 
@@ -4973,7 +5011,6 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
      * Enable port forwardings.
      */
     {
-       static char *e;                /* preserve across crReturn */
        char type;
        int n;
        int sport,dport,sserv,dserv;
@@ -4981,28 +5018,28 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
        char buf[1024];
        struct servent *se;
 
-       ssh_rportfwds = newtree234(ssh_rportcmp_ssh2);
+       ssh->rportfwds = newtree234(ssh_rportcmp_ssh2);
         /* Add port forwardings. */
-       e = cfg.portfwd;
-       while (*e) {
-           type = *e++;
+       ssh->portfwd_strptr = cfg.portfwd;
+       while (*ssh->portfwd_strptr) {
+           type = *ssh->portfwd_strptr++;
            n = 0;
-           while (*e && *e != '\t')
-               sports[n++] = *e++;
+           while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != '\t')
+               sports[n++] = *ssh->portfwd_strptr++;
            sports[n] = 0;
-           if (*e == '\t')
-               e++;
+           if (*ssh->portfwd_strptr == '\t')
+               ssh->portfwd_strptr++;
            n = 0;
-           while (*e && *e != ':')
-               host[n++] = *e++;
+           while (*ssh->portfwd_strptr && *ssh->portfwd_strptr != ':')
+               host[n++] = *ssh->portfwd_strptr++;
            host[n] = 0;
-           if (*e == ':')
-               e++;
+           if (*ssh->portfwd_strptr == ':')
+               ssh->portfwd_strptr++;
            n = 0;
-           while (*e)
-               dports[n++] = *e++;
+           while (*ssh->portfwd_strptr)
+               dports[n++] = *ssh->portfwd_strptr++;
            dports[n] = 0;
-           e++;
+           ssh->portfwd_strptr++;
            dport = atoi(dports);
            dserv = 0;
            if (dport == 0) {
@@ -5048,7 +5085,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    strcpy(pf->dhost, host);
                    pf->dport = dport;
                    pf->sport = sport;
-                   if (add234(ssh_rportfwds, pf) != pf) {
+                   if (add234(ssh->rportfwds, pf) != pf) {
                        sprintf(buf, 
                                "Duplicate remote port forwarding to %s:%d",
                                host, dport);
@@ -5063,33 +5100,33 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                            dserv ? strlen(dports) : 0, dports,
                            dserv, "(", dport, dserv, ")");
                        logevent(buf);
-                       ssh2_pkt_init(SSH2_MSG_GLOBAL_REQUEST);
-                       ssh2_pkt_addstring("tcpip-forward");
-                       ssh2_pkt_addbool(1);/* want reply */
+                       ssh2_pkt_init(ssh, SSH2_MSG_GLOBAL_REQUEST);
+                       ssh2_pkt_addstring(ssh, "tcpip-forward");
+                       ssh2_pkt_addbool(ssh, 1);/* want reply */
                        if (cfg.rport_acceptall)
-                           ssh2_pkt_addstring("0.0.0.0");
+                           ssh2_pkt_addstring(ssh, "0.0.0.0");
                        else
-                           ssh2_pkt_addstring("127.0.0.1");
-                       ssh2_pkt_adduint32(sport);
-                       ssh2_pkt_send();
+                           ssh2_pkt_addstring(ssh, "127.0.0.1");
+                       ssh2_pkt_adduint32(ssh, sport);
+                       ssh2_pkt_send(ssh);
 
                        do {
                            crWaitUntilV(ispkt);
-                           if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
-                               unsigned i = ssh2_pkt_getuint32();
+                           if (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+                               unsigned i = ssh2_pkt_getuint32(ssh);
                                struct ssh_channel *c;
-                               c = find234(ssh_channels, &i, ssh_channelfind);
+                               c = find234(ssh->channels, &i, ssh_channelfind);
                                if (!c)
                                    continue;/* nonexistent channel */
-                               c->v.v2.remwindow += ssh2_pkt_getuint32();
+                               c->v.v2.remwindow += ssh2_pkt_getuint32(ssh);
                            }
-                       } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+                       } while (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
 
-                       if (pktin.type != SSH2_MSG_REQUEST_SUCCESS) {
-                           if (pktin.type != SSH2_MSG_REQUEST_FAILURE) {
+                       if (ssh->pktin.type != SSH2_MSG_REQUEST_SUCCESS) {
+                           if (ssh->pktin.type != SSH2_MSG_REQUEST_FAILURE) {
                                bombout(("Unexpected response to port "
                                         "forwarding request: packet type %d",
-                                        pktin.type));
+                                        ssh->pktin.type));
                                crReturnV;
                            }
                            logevent("Server refused this port forwarding");
@@ -5107,34 +5144,34 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
      */
     if (cfg.agentfwd && agent_exists()) {
        logevent("Requesting OpenSSH-style agent forwarding");
-       ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
-       ssh2_pkt_adduint32(mainchan->remoteid);
-       ssh2_pkt_addstring("auth-agent-req@openssh.com");
-       ssh2_pkt_addbool(1);           /* want reply */
-       ssh2_pkt_send();
+       ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
+       ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
+       ssh2_pkt_addstring(ssh, "auth-agent-req@openssh.com");
+       ssh2_pkt_addbool(ssh, 1);              /* want reply */
+       ssh2_pkt_send(ssh);
 
        do {
            crWaitUntilV(ispkt);
-           if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
-               unsigned i = ssh2_pkt_getuint32();
+           if (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+               unsigned i = ssh2_pkt_getuint32(ssh);
                struct ssh_channel *c;
-               c = find234(ssh_channels, &i, ssh_channelfind);
+               c = find234(ssh->channels, &i, ssh_channelfind);
                if (!c)
                    continue;          /* nonexistent channel */
-               c->v.v2.remwindow += ssh2_pkt_getuint32();
+               c->v.v2.remwindow += ssh2_pkt_getuint32(ssh);
            }
-       } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+       } while (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
 
-       if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
-           if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
+       if (ssh->pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
+           if (ssh->pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
                bombout(("Unexpected response to agent forwarding request:"
-                        " packet type %d", pktin.type));
+                        " packet type %d", ssh->pktin.type));
                crReturnV;
            }
            logevent("Agent forwarding refused");
        } else {
            logevent("Agent forwarding enabled");
-           ssh_agentfwd_enabled = TRUE;
+           ssh->agentfwd_enabled = TRUE;
        }
     }
 
@@ -5142,45 +5179,45 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
      * Now allocate a pty for the session.
      */
     if (!cfg.nopty) {
-       ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
-       ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
-       ssh2_pkt_addstring("pty-req");
-       ssh2_pkt_addbool(1);           /* want reply */
-       ssh2_pkt_addstring(cfg.termtype);
-       ssh2_pkt_adduint32(ssh_term_width);
-       ssh2_pkt_adduint32(ssh_term_height);
-       ssh2_pkt_adduint32(0);         /* pixel width */
-       ssh2_pkt_adduint32(0);         /* pixel height */
-       ssh2_pkt_addstring_start();
-       ssh2_pkt_addstring_data("\0", 1);       /* TTY_OP_END, no special options */
-       ssh2_pkt_send();
-       ssh_state = SSH_STATE_INTERMED;
+       ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
+       ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);       /* recipient channel */
+       ssh2_pkt_addstring(ssh, "pty-req");
+       ssh2_pkt_addbool(ssh, 1);              /* want reply */
+       ssh2_pkt_addstring(ssh, cfg.termtype);
+       ssh2_pkt_adduint32(ssh, ssh->term_width);
+       ssh2_pkt_adduint32(ssh, ssh->term_height);
+       ssh2_pkt_adduint32(ssh, 0);            /* pixel width */
+       ssh2_pkt_adduint32(ssh, 0);            /* pixel height */
+       ssh2_pkt_addstring_start(ssh);
+       ssh2_pkt_addstring_data(ssh, "\0", 1);  /* TTY_OP_END, no special options */
+       ssh2_pkt_send(ssh);
+       ssh->state = SSH_STATE_INTERMED;
 
        do {
            crWaitUntilV(ispkt);
-           if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
-               unsigned i = ssh2_pkt_getuint32();
+           if (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+               unsigned i = ssh2_pkt_getuint32(ssh);
                struct ssh_channel *c;
-               c = find234(ssh_channels, &i, ssh_channelfind);
+               c = find234(ssh->channels, &i, ssh_channelfind);
                if (!c)
                    continue;          /* nonexistent channel */
-               c->v.v2.remwindow += ssh2_pkt_getuint32();
+               c->v.v2.remwindow += ssh2_pkt_getuint32(ssh);
            }
-       } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+       } while (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
 
-       if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
-           if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
+       if (ssh->pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
+           if (ssh->pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
                bombout(("Unexpected response to pty request:"
-                        " packet type %d", pktin.type));
+                        " packet type %d", ssh->pktin.type));
                crReturnV;
            }
-           c_write_str("Server refused to allocate pty\r\n");
-           ssh_editing = ssh_echoing = 1;
+           c_write_str(ssh, "Server refused to allocate pty\r\n");
+           ssh->editing = ssh->echoing = 1;
        } else {
            logevent("Allocated pty");
        }
     } else {
-       ssh_editing = ssh_echoing = 1;
+       ssh->editing = ssh->echoing = 1;
     }
 
     /*
@@ -5192,7 +5229,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
        int subsys;
        char *cmd;
 
-       if (ssh_fallback_cmd) {
+       if (ssh->fallback_cmd) {
            subsys = cfg.ssh_subsys2;
            cmd = cfg.remote_cmd_ptr2;
        } else {
@@ -5200,36 +5237,36 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
            cmd = cfg.remote_cmd_ptr;
        }
 
-       ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
-       ssh2_pkt_adduint32(mainchan->remoteid); /* recipient channel */
+       ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
+       ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);       /* recipient channel */
        if (subsys) {
-           ssh2_pkt_addstring("subsystem");
-           ssh2_pkt_addbool(1);               /* want reply */
-           ssh2_pkt_addstring(cmd);
+           ssh2_pkt_addstring(ssh, "subsystem");
+           ssh2_pkt_addbool(ssh, 1);          /* want reply */
+           ssh2_pkt_addstring(ssh, cmd);
        } else if (*cmd) {
-           ssh2_pkt_addstring("exec");
-           ssh2_pkt_addbool(1);               /* want reply */
-           ssh2_pkt_addstring(cmd);
+           ssh2_pkt_addstring(ssh, "exec");
+           ssh2_pkt_addbool(ssh, 1);          /* want reply */
+           ssh2_pkt_addstring(ssh, cmd);
        } else {
-           ssh2_pkt_addstring("shell");
-           ssh2_pkt_addbool(1);               /* want reply */
+           ssh2_pkt_addstring(ssh, "shell");
+           ssh2_pkt_addbool(ssh, 1);          /* want reply */
        }
-       ssh2_pkt_send();
+       ssh2_pkt_send(ssh);
        do {
            crWaitUntilV(ispkt);
-           if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
-               unsigned i = ssh2_pkt_getuint32();
+           if (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+               unsigned i = ssh2_pkt_getuint32(ssh);
                struct ssh_channel *c;
-               c = find234(ssh_channels, &i, ssh_channelfind);
+               c = find234(ssh->channels, &i, ssh_channelfind);
                if (!c)
                    continue;          /* nonexistent channel */
-               c->v.v2.remwindow += ssh2_pkt_getuint32();
+               c->v.v2.remwindow += ssh2_pkt_getuint32(ssh);
            }
-       } while (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
-       if (pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
-           if (pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
+       } while (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+       if (ssh->pktin.type != SSH2_MSG_CHANNEL_SUCCESS) {
+           if (ssh->pktin.type != SSH2_MSG_CHANNEL_FAILURE) {
                bombout(("Unexpected response to shell/command request:"
-                        " packet type %d", pktin.type));
+                        " packet type %d", ssh->pktin.type));
                crReturnV;
            }
            /*
@@ -5238,9 +5275,9 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
             * not, and if the fallback command exists, try falling
             * back to it before complaining.
             */
-           if (!ssh_fallback_cmd && cfg.remote_cmd_ptr2 != NULL) {
+           if (!ssh->fallback_cmd && cfg.remote_cmd_ptr2 != NULL) {
                logevent("Primary command failed; attempting fallback");
-               ssh_fallback_cmd = TRUE;
+               ssh->fallback_cmd = TRUE;
                continue;
            }
            bombout(("Server refused to start a shell/command"));
@@ -5251,42 +5288,41 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
        break;
     }
 
-    ssh_state = SSH_STATE_SESSION;
-    if (size_needed)
-       ssh_size(ssh_term_width, ssh_term_height);
-    if (eof_needed)
-       ssh_special(TS_EOF);
+    ssh->state = SSH_STATE_SESSION;
+    if (ssh->size_needed)
+       ssh_size(ssh, ssh->term_width, ssh->term_height);
+    if (ssh->eof_needed)
+       ssh_special(ssh, TS_EOF);
 
     /*
      * Transfer data!
      */
     ldisc_send(NULL, 0, 0);           /* cause ldisc to notice changes */
-    ssh_send_ok = 1;
+    ssh->send_ok = 1;
     while (1) {
-       static int try_send;
        crReturnV;
-       try_send = FALSE;
+       s->try_send = FALSE;
        if (ispkt) {
-           if (pktin.type == SSH2_MSG_CHANNEL_DATA ||
-               pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
+           if (ssh->pktin.type == SSH2_MSG_CHANNEL_DATA ||
+               ssh->pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA) {
                char *data;
                int length;
-               unsigned i = ssh2_pkt_getuint32();
+               unsigned i = ssh2_pkt_getuint32(ssh);
                struct ssh_channel *c;
-               c = find234(ssh_channels, &i, ssh_channelfind);
+               c = find234(ssh->channels, &i, ssh_channelfind);
                if (!c)
                    continue;          /* nonexistent channel */
-               if (pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
-                   ssh2_pkt_getuint32() != SSH2_EXTENDED_DATA_STDERR)
+               if (ssh->pktin.type == SSH2_MSG_CHANNEL_EXTENDED_DATA &&
+                   ssh2_pkt_getuint32(ssh) != SSH2_EXTENDED_DATA_STDERR)
                    continue;          /* extended but not stderr */
-               ssh2_pkt_getstring(&data, &length);
+               ssh2_pkt_getstring(ssh, &data, &length);
                if (data) {
                    int bufsize;
                    c->v.v2.locwindow -= length;
                    switch (c->type) {
                      case CHAN_MAINSESSION:
                        bufsize =
-                           from_backend(frontend, pktin.type ==
+                           from_backend(ssh->frontend, ssh->pktin.type ==
                                         SSH2_MSG_CHANNEL_EXTENDED_DATA,
                                         data, length);
                        break;
@@ -5335,9 +5371,8 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                                    sentreply = "\0\0\0\1\5";
                                    replylen = 5;
                                }
-                               ssh2_add_channel_data(c, sentreply,
-                                                     replylen);
-                               try_send = TRUE;
+                               ssh2_add_channel_data(c, sentreply, replylen);
+                               s->try_send = TRUE;
                                if (reply)
                                    sfree(reply);
                                sfree(c->u.a.message);
@@ -5354,15 +5389,11 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    if (bufsize < OUR_V2_WINSIZE)
                        ssh2_set_window(c, OUR_V2_WINSIZE - bufsize);
                }
-           } else if (pktin.type == SSH2_MSG_DISCONNECT) {
-               ssh_state = SSH_STATE_CLOSED;
-               logevent("Received disconnect message");
-               crReturnV;
-           } else if (pktin.type == SSH2_MSG_CHANNEL_EOF) {
-               unsigned i = ssh2_pkt_getuint32();
+           } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_EOF) {
+               unsigned i = ssh2_pkt_getuint32(ssh);
                struct ssh_channel *c;
 
-               c = find234(ssh_channels, &i, ssh_channelfind);
+               c = find234(ssh->channels, &i, ssh_channelfind);
                if (!c)
                    continue;          /* nonexistent channel */
 
@@ -5379,11 +5410,11 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    pfd_close(c->u.pfd.s);
                    sshfwd_close(c);
                }
-           } else if (pktin.type == SSH2_MSG_CHANNEL_CLOSE) {
-               unsigned i = ssh2_pkt_getuint32();
+           } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_CLOSE) {
+               unsigned i = ssh2_pkt_getuint32(ssh);
                struct ssh_channel *c;
 
-               c = find234(ssh_channels, &i, ssh_channelfind);
+               c = find234(ssh->channels, &i, ssh_channelfind);
                if (!c || ((int)c->remoteid) == -1) {
                    bombout(("Received CHANNEL_CLOSE for %s channel %d\n",
                             c ? "half-open" : "nonexistent", i));
@@ -5407,18 +5438,18 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    break;
                }
                if (c->closes == 0) {
-                   ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
-                   ssh2_pkt_adduint32(c->remoteid);
-                   ssh2_pkt_send();
+                   ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_CLOSE);
+                   ssh2_pkt_adduint32(ssh, c->remoteid);
+                   ssh2_pkt_send(ssh);
                }
-               del234(ssh_channels, c);
+               del234(ssh->channels, c);
                bufchain_clear(&c->v.v2.outbuffer);
                sfree(c);
 
                /*
                 * See if that was the last channel left open.
                 */
-               if (count234(ssh_channels) == 0) {
+               if (count234(ssh->channels) == 0) {
 #if 0
                     /*
                      * We used to send SSH_MSG_DISCONNECT here,
@@ -5432,36 +5463,36 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                      * DISCONNECT. So now we don't.
                      */
                    logevent("All channels closed. Disconnecting");
-                   ssh2_pkt_init(SSH2_MSG_DISCONNECT);
-                   ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION);
-                   ssh2_pkt_addstring("All open channels closed");
-                   ssh2_pkt_addstring("en");   /* language tag */
-                   ssh2_pkt_send();
+                   ssh2_pkt_init(ssh, SSH2_MSG_DISCONNECT);
+                   ssh2_pkt_adduint32(ssh, SSH2_DISCONNECT_BY_APPLICATION);
+                   ssh2_pkt_addstring(ssh, "All open channels closed");
+                   ssh2_pkt_addstring(ssh, "en");      /* language tag */
+                   ssh2_pkt_send(ssh);
 #endif
-                   ssh_state = SSH_STATE_CLOSED;
+                   ssh->state = SSH_STATE_CLOSED;
                    crReturnV;
                }
                continue;              /* remote sends close; ignore (FIXME) */
-           } else if (pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
-               unsigned i = ssh2_pkt_getuint32();
+           } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_WINDOW_ADJUST) {
+               unsigned i = ssh2_pkt_getuint32(ssh);
                struct ssh_channel *c;
-               c = find234(ssh_channels, &i, ssh_channelfind);
+               c = find234(ssh->channels, &i, ssh_channelfind);
                if (!c)
                    continue;          /* nonexistent channel */
-               c->v.v2.remwindow += ssh2_pkt_getuint32();
-               try_send = TRUE;
-           } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
-               unsigned i = ssh2_pkt_getuint32();
+               c->v.v2.remwindow += ssh2_pkt_getuint32(ssh);
+               s->try_send = TRUE;
+           } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION) {
+               unsigned i = ssh2_pkt_getuint32(ssh);
                struct ssh_channel *c;
-               c = find234(ssh_channels, &i, ssh_channelfind);
+               c = find234(ssh->channels, &i, ssh_channelfind);
                if (!c)
                    continue;          /* nonexistent channel */
                if (c->type != CHAN_SOCKDATA_DORMANT)
                    continue;          /* dunno why they're confirming this */
-               c->remoteid = ssh2_pkt_getuint32();
+               c->remoteid = ssh2_pkt_getuint32(ssh);
                c->type = CHAN_SOCKDATA;
-               c->v.v2.remwindow = ssh2_pkt_getuint32();
-               c->v.v2.remmaxpkt = ssh2_pkt_getuint32();
+               c->v.v2.remwindow = ssh2_pkt_getuint32(ssh);
+               c->v.v2.remmaxpkt = ssh2_pkt_getuint32(ssh);
                if (c->u.pfd.s)
                    pfd_confirm(c->u.pfd.s);
                if (c->closes) {
@@ -5471,14 +5502,14 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                     * the channel open. So now we know the
                     * remoteid, we can close it again.
                     */
-                   ssh2_pkt_init(SSH2_MSG_CHANNEL_CLOSE);
-                   ssh2_pkt_adduint32(c->remoteid);
-                   ssh2_pkt_send();
+                   ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_CLOSE);
+                   ssh2_pkt_adduint32(ssh, c->remoteid);
+                   ssh2_pkt_send(ssh);
                }
-           } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
-               unsigned i = ssh2_pkt_getuint32();
+           } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_OPEN_FAILURE) {
+               unsigned i = ssh2_pkt_getuint32(ssh);
                struct ssh_channel *c;
-               c = find234(ssh_channels, &i, ssh_channelfind);
+               c = find234(ssh->channels, &i, ssh_channelfind);
                if (!c)
                    continue;          /* nonexistent channel */
                if (c->type != CHAN_SOCKDATA_DORMANT)
@@ -5488,35 +5519,35 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
 
                pfd_close(c->u.pfd.s);
 
-               del234(ssh_channels, c);
+               del234(ssh->channels, c);
                sfree(c);
-           } else if (pktin.type == SSH2_MSG_CHANNEL_REQUEST) {
+           } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_REQUEST) {
                unsigned localid;
                char *type;
                int typelen, want_reply;
                struct ssh_channel *c;
 
-               localid = ssh2_pkt_getuint32();
-               ssh2_pkt_getstring(&type, &typelen);
-               want_reply = ssh2_pkt_getbool();
+               localid = ssh2_pkt_getuint32(ssh);
+               ssh2_pkt_getstring(ssh, &type, &typelen);
+               want_reply = ssh2_pkt_getbool(ssh);
 
                /*
                 * First, check that the channel exists. Otherwise,
                 * we can instantly disconnect with a rude message.
                 */
-               c = find234(ssh_channels, &localid, ssh_channelfind);
+               c = find234(ssh->channels, &localid, ssh_channelfind);
                if (!c) {
                    char buf[80];
                    sprintf(buf, "Received channel request for nonexistent"
                            " channel %d", localid);
                    logevent(buf);
-                   ssh2_pkt_init(SSH2_MSG_DISCONNECT);
-                   ssh2_pkt_adduint32(SSH2_DISCONNECT_BY_APPLICATION);
-                   ssh2_pkt_addstring(buf);
-                   ssh2_pkt_addstring("en");   /* language tag */
-                   ssh2_pkt_send();
+                   ssh2_pkt_init(ssh, SSH2_MSG_DISCONNECT);
+                   ssh2_pkt_adduint32(ssh, SSH2_DISCONNECT_BY_APPLICATION);
+                   ssh2_pkt_addstring(ssh, buf);
+                   ssh2_pkt_addstring(ssh, "en");      /* language tag */
+                   ssh2_pkt_send(ssh);
                    connection_fatal("%s", buf);
-                   ssh_state = SSH_STATE_CLOSED;
+                   ssh->state = SSH_STATE_CLOSED;
                    crReturnV;
                }
 
@@ -5526,17 +5557,17 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                 * we recognise.
                 */
                if (typelen == 11 && !memcmp(type, "exit-status", 11) &&
-                   c == mainchan) {
+                   c == ssh->mainchan) {
                    /* We recognise "exit-status" on the primary channel. */
                    char buf[100];
-                   ssh_exitcode = ssh2_pkt_getuint32();
+                   ssh->exitcode = ssh2_pkt_getuint32(ssh);
                    sprintf(buf, "Server sent command exit status %d",
-                           ssh_exitcode);
+                           ssh->exitcode);
                    logevent(buf);
                    if (want_reply) {
-                       ssh2_pkt_init(SSH2_MSG_CHANNEL_SUCCESS);
-                       ssh2_pkt_adduint32(c->remoteid);
-                       ssh2_pkt_send();
+                       ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_SUCCESS);
+                       ssh2_pkt_adduint32(ssh, c->remoteid);
+                       ssh2_pkt_send(ssh);
                    }
                } else {
                    /*
@@ -5546,17 +5577,17 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                     * on want_reply.
                     */
                    if (want_reply) {
-                       ssh2_pkt_init(SSH2_MSG_CHANNEL_FAILURE);
-                       ssh2_pkt_adduint32(c->remoteid);
-                       ssh2_pkt_send();
+                       ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_FAILURE);
+                       ssh2_pkt_adduint32(ssh, c->remoteid);
+                       ssh2_pkt_send(ssh);
                    }
                }
-           } else if (pktin.type == SSH2_MSG_GLOBAL_REQUEST) {
+           } else if (ssh->pktin.type == SSH2_MSG_GLOBAL_REQUEST) {
                char *type;
                int typelen, want_reply;
 
-               ssh2_pkt_getstring(&type, &typelen);
-               want_reply = ssh2_pkt_getbool();
+               ssh2_pkt_getstring(ssh, &type, &typelen);
+               want_reply = ssh2_pkt_getbool(ssh);
 
                 /*
                  * We currently don't support any global requests
@@ -5565,24 +5596,25 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                  * want_reply.
                  */
                 if (want_reply) {
-                    ssh2_pkt_init(SSH2_MSG_REQUEST_FAILURE);
-                    ssh2_pkt_send();
+                    ssh2_pkt_init(ssh, SSH2_MSG_REQUEST_FAILURE);
+                    ssh2_pkt_send(ssh);
                }
-           } else if (pktin.type == SSH2_MSG_CHANNEL_OPEN) {
+           } else if (ssh->pktin.type == SSH2_MSG_CHANNEL_OPEN) {
                char *type;
                int typelen;
                char *error = NULL;
                struct ssh_channel *c;
                unsigned remid, winsize, pktsize;
-               ssh2_pkt_getstring(&type, &typelen);
+               ssh2_pkt_getstring(ssh, &type, &typelen);
                c = smalloc(sizeof(struct ssh_channel));
+               c->ssh = ssh;
 
-               remid = ssh2_pkt_getuint32();
-               winsize = ssh2_pkt_getuint32();
-               pktsize = ssh2_pkt_getuint32();
+               remid = ssh2_pkt_getuint32(ssh);
+               winsize = ssh2_pkt_getuint32(ssh);
+               pktsize = ssh2_pkt_getuint32(ssh);
 
                if (typelen == 3 && !memcmp(type, "x11", 3)) {
-                   if (!ssh_X11_fwd_enabled)
+                   if (!ssh->X11_fwd_enabled)
                        error = "X11 forwarding is not enabled";
                    else if (x11_init(&c->u.x11.s, cfg.x11_display, c) !=
                             NULL) {
@@ -5595,9 +5627,9 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    struct ssh_rportfwd pf, *realpf;
                    char *dummy;
                    int dummylen;
-                   ssh2_pkt_getstring(&dummy, &dummylen);/* skip address */
-                   pf.sport = ssh2_pkt_getuint32();
-                   realpf = find234(ssh_rportfwds, &pf, NULL);
+                   ssh2_pkt_getstring(ssh, &dummy, &dummylen);/* skip address */
+                   pf.sport = ssh2_pkt_getuint32(ssh);
+                   realpf = find234(ssh->rportfwds, &pf, NULL);
                    if (realpf == NULL) {
                        error = "Remote port is not recognised";
                    } else {
@@ -5618,7 +5650,7 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
                    }
                } else if (typelen == 22 &&
                           !memcmp(type, "auth-agent@openssh.com", 3)) {
-                   if (!ssh_agentfwd_enabled)
+                   if (!ssh->agentfwd_enabled)
                        error = "Agent forwarding is not enabled";
                    else {
                        c->type = CHAN_AGENT;   /* identify channel type */
@@ -5630,46 +5662,46 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
 
                c->remoteid = remid;
                if (error) {
-                   ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_FAILURE);
-                   ssh2_pkt_adduint32(c->remoteid);
-                   ssh2_pkt_adduint32(SSH2_OPEN_CONNECT_FAILED);
-                   ssh2_pkt_addstring(error);
-                   ssh2_pkt_addstring("en");   /* language tag */
-                   ssh2_pkt_send();
+                   ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_OPEN_FAILURE);
+                   ssh2_pkt_adduint32(ssh, c->remoteid);
+                   ssh2_pkt_adduint32(ssh, SSH2_OPEN_CONNECT_FAILED);
+                   ssh2_pkt_addstring(ssh, error);
+                   ssh2_pkt_addstring(ssh, "en");      /* language tag */
+                   ssh2_pkt_send(ssh);
                    sfree(c);
                } else {
-                   c->localid = alloc_channel_id();
+                   c->localid = alloc_channel_id(ssh);
                    c->closes = 0;
                    c->v.v2.locwindow = OUR_V2_WINSIZE;
                    c->v.v2.remwindow = winsize;
                    c->v.v2.remmaxpkt = pktsize;
                    bufchain_init(&c->v.v2.outbuffer);
-                   add234(ssh_channels, c);
-                   ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
-                   ssh2_pkt_adduint32(c->remoteid);
-                   ssh2_pkt_adduint32(c->localid);
-                   ssh2_pkt_adduint32(c->v.v2.locwindow);
-                   ssh2_pkt_adduint32(0x4000UL);       /* our max pkt size */
-                   ssh2_pkt_send();
+                   add234(ssh->channels, c);
+                   ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
+                   ssh2_pkt_adduint32(ssh, c->remoteid);
+                   ssh2_pkt_adduint32(ssh, c->localid);
+                   ssh2_pkt_adduint32(ssh, c->v.v2.locwindow);
+                   ssh2_pkt_adduint32(ssh, 0x4000UL);  /* our max pkt size */
+                   ssh2_pkt_send(ssh);
                }
            } else {
-               bombout(("Strange packet received: type %d", pktin.type));
+               bombout(("Strange packet received: type %d", ssh->pktin.type));
                crReturnV;
            }
        } else {
            /*
             * We have spare data. Add it to the channel buffer.
             */
-           ssh2_add_channel_data(mainchan, in, inlen);
-           try_send = TRUE;
+           ssh2_add_channel_data(ssh->mainchan, in, inlen);
+           s->try_send = TRUE;
        }
-       if (try_send) {
+       if (s->try_send) {
            int i;
            struct ssh_channel *c;
            /*
             * Try to send data on all channels if we can.
             */
-           for (i = 0; NULL != (c = index234(ssh_channels, i)); i++) {
+           for (i = 0; NULL != (c = index234(ssh->channels, i)); i++) {
                int bufsize = ssh2_try_send(c);
                if (bufsize == 0) {
                    switch (c->type) {
@@ -5699,11 +5731,11 @@ static void do_ssh2_authconn(unsigned char *in, int inlen, int ispkt)
 /*
  * Handle the top-level SSH2 protocol.
  */
-static void ssh2_protocol(unsigned char *in, int inlen, int ispkt)
+static void ssh2_protocol(Ssh ssh, unsigned char *in, int inlen, int ispkt)
 {
-    if (do_ssh2_transport(in, inlen, ispkt) == 0)
+    if (do_ssh2_transport(ssh, in, inlen, ispkt) == 0)
        return;
-    do_ssh2_authconn(in, inlen, ispkt);
+    do_ssh2_authconn(ssh, in, inlen, ispkt);
 }
 
 /*
@@ -5711,28 +5743,69 @@ static void ssh2_protocol(unsigned char *in, int inlen, int ispkt)
  *
  * Returns an error message, or NULL on success.
  */
-static char *ssh_init(void *frontend_handle,
+static char *ssh_init(void *frontend_handle, void **backend_handle,
                      char *host, int port, char **realhost, int nodelay)
 {
     char *p;
+    Ssh ssh;
+
+    ssh = smalloc(sizeof(*ssh));
+    ssh->s = NULL;
+    ssh->cipher = NULL;
+    ssh->cscipher = NULL;
+    ssh->sccipher = NULL;
+    ssh->csmac = NULL;
+    ssh->scmac = NULL;
+    ssh->cscomp = NULL;
+    ssh->sccomp = NULL;
+    ssh->kex = NULL;
+    ssh->hostkey = NULL;
+    ssh->exitcode = -1;
+    ssh->state = SSH_STATE_PREPACKET;
+    ssh->size_needed = FALSE;
+    ssh->eof_needed = FALSE;
+    {
+       static const struct Packet empty = { 0, 0, NULL, NULL, 0 };
+       ssh->pktin = ssh->pktout = empty;
+    }
+    ssh->deferred_send_data = NULL;
+    ssh->deferred_len = 0;
+    ssh->deferred_size = 0;
+    ssh->fallback_cmd = 0;
+    ssh->pkt_ctx = 0;
+    ssh->v2_outgoing_sequence = 0;
+    ssh->ssh1_rdpkt_crstate = 0;
+    ssh->ssh2_rdpkt_crstate = 0;
+    ssh->do_ssh_init_crstate = 0;
+    ssh->ssh_gotdata_crstate = 0;
+    ssh->ssh1_protocol_crstate = 0;
+    ssh->do_ssh1_login_crstate = 0;
+    ssh->do_ssh2_transport_crstate = 0;
+    ssh->do_ssh2_authconn_crstate = 0;
+    ssh->do_ssh_init_state = NULL;
+    ssh->do_ssh1_login_state = NULL;
+    ssh->do_ssh2_transport_state = NULL;
+    ssh->do_ssh2_authconn_state = NULL;
+
+    *backend_handle = ssh;
 
 #ifdef MSCRYPTOAPI
     if (crypto_startup() == 0)
        return "Microsoft high encryption pack not installed!";
 #endif
 
-    frontend = frontend_handle;
-    ssh_term_width = cfg.width;
-    ssh_term_height = cfg.height;
+    ssh->frontend = frontend_handle;
+    ssh->term_width = cfg.width;
+    ssh->term_height = cfg.height;
 
-    ssh_send_ok = 0;
-    ssh_editing = 0;
-    ssh_echoing = 0;
-    ssh1_throttle_count = 0;
-    ssh_overall_bufsize = 0;
-    ssh_fallback_cmd = 0;
+    ssh->send_ok = 0;
+    ssh->editing = 0;
+    ssh->echoing = 0;
+    ssh->v1_throttle_count = 0;
+    ssh->overall_bufsize = 0;
+    ssh->fallback_cmd = 0;
 
-    p = connect_to_host(host, port, realhost, nodelay);
+    p = connect_to_host(ssh, host, port, realhost, nodelay);
     if (p != NULL)
        return p;
 
@@ -5742,24 +5815,27 @@ static char *ssh_init(void *frontend_handle,
 /*
  * Called to send data down the Telnet connection.
  */
-static int ssh_send(char *buf, int len)
+static int ssh_send(void *handle, char *buf, int len)
 {
-    if (s == NULL || ssh_protocol == NULL)
+    Ssh ssh = (Ssh) handle;
+
+    if (ssh == NULL || ssh->s == NULL || ssh->protocol == NULL)
        return 0;
 
-    ssh_protocol(buf, len, 0);
+    ssh->protocol(ssh, buf, len, 0);
 
-    return ssh_sendbuffer();
+    return ssh_sendbuffer(ssh);
 }
 
 /*
  * Called to query the current amount of buffered stdin data.
  */
-static int ssh_sendbuffer(void)
+static int ssh_sendbuffer(void *handle)
 {
+    Ssh ssh = (Ssh) handle;
     int override_value;
 
-    if (s == NULL || ssh_protocol == NULL)
+    if (ssh == NULL || ssh->s == NULL || ssh->protocol == NULL)
        return 0;
 
     /*
@@ -5767,16 +5843,17 @@ static int ssh_sendbuffer(void)
      * size on that to any individual buffer on the stdin channel.
      */
     override_value = 0;
-    if (ssh_throttled_all)
-       override_value = ssh_overall_bufsize;
+    if (ssh->throttled_all)
+       override_value = ssh->overall_bufsize;
 
-    if (ssh_version == 1) {
+    if (ssh->version == 1) {
        return override_value;
-    } else if (ssh_version == 2) {
-       if (!mainchan || mainchan->closes > 0)
+    } else if (ssh->version == 2) {
+       if (!ssh->mainchan || ssh->mainchan->closes > 0)
            return override_value;
        else
-           return override_value + bufchain_size(&mainchan->v.v2.outbuffer);
+           return (override_value +
+                   bufchain_size(&ssh->mainchan->v.v2.outbuffer));
     }
 
     return 0;
@@ -5785,37 +5862,40 @@ static int ssh_sendbuffer(void)
 /*
  * Called to set the size of the window from SSH's POV.
  */
-static void ssh_size(int width, int height)
+static void ssh_size(void *handle, int width, int height)
 {
-    ssh_term_width = width;
-    ssh_term_height = height;
+    Ssh ssh = (Ssh) handle;
+
+    ssh->term_width = width;
+    ssh->term_height = height;
 
-    switch (ssh_state) {
+    switch (ssh->state) {
       case SSH_STATE_BEFORE_SIZE:
       case SSH_STATE_PREPACKET:
       case SSH_STATE_CLOSED:
        break;                         /* do nothing */
       case SSH_STATE_INTERMED:
-       size_needed = TRUE;            /* buffer for later */
+       ssh->size_needed = TRUE;       /* buffer for later */
        break;
       case SSH_STATE_SESSION:
        if (!cfg.nopty) {
            if (!term)
                return;
-           if (ssh_version == 1) {
-               send_packet(SSH1_CMSG_WINDOW_SIZE,
-                           PKT_INT, ssh_term_height, PKT_INT, ssh_term_width,
+           if (ssh->version == 1) {
+               send_packet(ssh, SSH1_CMSG_WINDOW_SIZE,
+                           PKT_INT, ssh->term_height,
+                           PKT_INT, ssh->term_width,
                            PKT_INT, 0, PKT_INT, 0, PKT_END);
            } else {
-               ssh2_pkt_init(SSH2_MSG_CHANNEL_REQUEST);
-               ssh2_pkt_adduint32(mainchan->remoteid);
-               ssh2_pkt_addstring("window-change");
-               ssh2_pkt_addbool(0);
-               ssh2_pkt_adduint32(ssh_term_width);
-               ssh2_pkt_adduint32(ssh_term_height);
-               ssh2_pkt_adduint32(0);
-               ssh2_pkt_adduint32(0);
-               ssh2_pkt_send();
+               ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_REQUEST);
+               ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
+               ssh2_pkt_addstring(ssh, "window-change");
+               ssh2_pkt_addbool(ssh, 0);
+               ssh2_pkt_adduint32(ssh, ssh->term_width);
+               ssh2_pkt_adduint32(ssh, ssh->term_height);
+               ssh2_pkt_adduint32(ssh, 0);
+               ssh2_pkt_adduint32(ssh, 0);
+               ssh2_pkt_send(ssh);
            }
        }
        break;
@@ -5827,55 +5907,59 @@ static void ssh_size(int width, int height)
  * can send an EOF and collect resulting output (e.g. `plink
  * hostname sort').
  */
-static void ssh_special(Telnet_Special code)
+static void ssh_special(void *handle, Telnet_Special code)
 {
+    Ssh ssh = (Ssh) handle;
+
     if (code == TS_EOF) {
-       if (ssh_state != SSH_STATE_SESSION) {
+       if (ssh->state != SSH_STATE_SESSION) {
            /*
             * Buffer the EOF in case we are pre-SESSION, so we can
             * send it as soon as we reach SESSION.
             */
            if (code == TS_EOF)
-               eof_needed = TRUE;
+               ssh->eof_needed = TRUE;
            return;
        }
-       if (ssh_version == 1) {
-           send_packet(SSH1_CMSG_EOF, PKT_END);
+       if (ssh->version == 1) {
+           send_packet(ssh, SSH1_CMSG_EOF, PKT_END);
        } else {
-           ssh2_pkt_init(SSH2_MSG_CHANNEL_EOF);
-           ssh2_pkt_adduint32(mainchan->remoteid);
-           ssh2_pkt_send();
+           ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_EOF);
+           ssh2_pkt_adduint32(ssh, ssh->mainchan->remoteid);
+           ssh2_pkt_send(ssh);
        }
        logevent("Sent EOF message");
     } else if (code == TS_PING) {
-       if (ssh_state == SSH_STATE_CLOSED
-           || ssh_state == SSH_STATE_PREPACKET) return;
-       if (ssh_version == 1) {
-           if (!(ssh_remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
-               send_packet(SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
+       if (ssh->state == SSH_STATE_CLOSED
+           || ssh->state == SSH_STATE_PREPACKET) return;
+       if (ssh->version == 1) {
+           if (!(ssh->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE))
+               send_packet(ssh, SSH1_MSG_IGNORE, PKT_STR, "", PKT_END);
        } else {
-           ssh2_pkt_init(SSH2_MSG_IGNORE);
-           ssh2_pkt_addstring_start();
-           ssh2_pkt_send();
+           ssh2_pkt_init(ssh, SSH2_MSG_IGNORE);
+           ssh2_pkt_addstring_start(ssh);
+           ssh2_pkt_send(ssh);
        }
     } else {
        /* do nothing */
     }
 }
 
-void *new_sock_channel(Socket s)
+void *new_sock_channel(void *handle, Socket s)
 {
+    Ssh ssh = (Ssh) handle;
     struct ssh_channel *c;
     c = smalloc(sizeof(struct ssh_channel));
+    c->ssh = ssh;
 
     if (c) {
        c->remoteid = -1;              /* to be set when open confirmed */
-       c->localid = alloc_channel_id();
+       c->localid = alloc_channel_id(ssh);
        c->closes = 0;
        c->type = CHAN_SOCKDATA_DORMANT;/* identify channel type */
        c->u.pfd.s = s;
        bufchain_init(&c->v.v2.outbuffer);
-       add234(ssh_channels, c);
+       add234(ssh->channels, c);
     }
     return c;
 }
@@ -5884,78 +5968,96 @@ void *new_sock_channel(Socket s)
  * This is called when stdout/stderr (the entity to which
  * from_backend sends data) manages to clear some backlog.
  */
-void ssh_unthrottle(int bufsize)
+void ssh_unthrottle(void *handle, int bufsize)
 {
-    if (ssh_version == 1) {
-       if (ssh1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) {
-           ssh1_stdout_throttling = 0;
-           ssh1_throttle(-1);
+    Ssh ssh = (Ssh) handle;
+    if (ssh->version == 1) {
+       if (ssh->v1_stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) {
+           ssh->v1_stdout_throttling = 0;
+           ssh1_throttle(ssh, -1);
        }
     } else {
-       if (mainchan && mainchan->closes == 0)
-           ssh2_set_window(mainchan, OUR_V2_WINSIZE - bufsize);
+       if (ssh->mainchan && ssh->mainchan->closes == 0)
+           ssh2_set_window(ssh->mainchan, OUR_V2_WINSIZE - bufsize);
     }
 }
 
-void ssh_send_port_open(void *channel, char *hostname, int port, char *org)
+void ssh_send_port_open(void *handle, void *channel, char *hostname,
+                       int port, char *org)
 {
+    Ssh ssh = (Ssh) handle;
     struct ssh_channel *c = (struct ssh_channel *)channel;
     char buf[1024];
 
     sprintf(buf, "Opening forwarded connection to %.512s:%d", hostname, port);
     logevent(buf);
 
-    if (ssh_version == 1) {
-       send_packet(SSH1_MSG_PORT_OPEN,
+    if (ssh->version == 1) {
+       send_packet(ssh, SSH1_MSG_PORT_OPEN,
                    PKT_INT, c->localid,
                    PKT_STR, hostname,
                    PKT_INT, port,
                    //PKT_STR, <org:orgport>,
                    PKT_END);
     } else {
-       ssh2_pkt_init(SSH2_MSG_CHANNEL_OPEN);
-       ssh2_pkt_addstring("direct-tcpip");
-       ssh2_pkt_adduint32(c->localid);
+       ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_OPEN);
+       ssh2_pkt_addstring(ssh, "direct-tcpip");
+       ssh2_pkt_adduint32(ssh, c->localid);
        c->v.v2.locwindow = OUR_V2_WINSIZE;
-       ssh2_pkt_adduint32(c->v.v2.locwindow);/* our window size */
-       ssh2_pkt_adduint32(0x4000UL);      /* our max pkt size */
-       ssh2_pkt_addstring(hostname);
-       ssh2_pkt_adduint32(port);
+       ssh2_pkt_adduint32(ssh, c->v.v2.locwindow);/* our window size */
+       ssh2_pkt_adduint32(ssh, 0x4000UL);      /* our max pkt size */
+       ssh2_pkt_addstring(ssh, hostname);
+       ssh2_pkt_adduint32(ssh, port);
        /*
         * We make up values for the originator data; partly it's
         * too much hassle to keep track, and partly I'm not
         * convinced the server should be told details like that
         * about my local network configuration.
         */
-       ssh2_pkt_addstring("client-side-connection");
-       ssh2_pkt_adduint32(0);
-       ssh2_pkt_send();
+       ssh2_pkt_addstring(ssh, "client-side-connection");
+       ssh2_pkt_adduint32(ssh, 0);
+       ssh2_pkt_send(ssh);
     }
 }
 
 
-static Socket ssh_socket(void)
+static Socket ssh_socket(void *handle)
 {
-    return s;
+    Ssh ssh = (Ssh) handle;
+    return ssh->s;
 }
 
-static int ssh_sendok(void)
+static int ssh_sendok(void *handle)
 {
-    return ssh_send_ok;
+    Ssh ssh = (Ssh) handle;
+    return ssh->send_ok;
 }
 
-static int ssh_ldisc(int option)
+static int ssh_ldisc(void *handle, int option)
 {
+    Ssh ssh = (Ssh) handle;
     if (option == LD_ECHO)
-       return ssh_echoing;
+       return ssh->echoing;
     if (option == LD_EDIT)
-       return ssh_editing;
+       return ssh->editing;
     return FALSE;
 }
 
-static int ssh_return_exitcode(void)
+static int ssh_return_exitcode(void *handle)
+{
+    Ssh ssh = (Ssh) handle;
+    return ssh->exitcode;
+}
+
+/*
+ * Gross hack: pscp will try to start SFTP but fall back to scp1 if
+ * that fails. This variable is the means by which scp.c can reach
+ * into the SSH code and find out which one it got.
+ */
+extern int ssh_fallback_cmd(void *handle)
 {
-    return ssh_exitcode;
+    Ssh ssh = (Ssh) handle;
+    return ssh->fallback_cmd;
 }
 
 Backend ssh_backend = {
diff --git a/ssh.h b/ssh.h
index 76954a4..59a7b46 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -227,7 +227,7 @@ extern char sshver[];
  * that fails. This variable is the means by which scp.c can reach
  * into the SSH code and find out which one it got.
  */
-extern int ssh_fallback_cmd;
+extern int ssh_fallback_cmd(void *handle);
 
 #ifndef MSCRYPTOAPI
 void SHATransform(word32 * digest, word32 * data);
@@ -238,8 +238,11 @@ void random_add_noise(void *noise, int length);
 void random_add_heavynoise(void *noise, int length);
 
 void logevent(char *);
-void *new_sock_channel(Socket s); // allocates and register a new channel for port forwarding
-void ssh_send_port_open(void *channel, char *hostname, int port, char *org);
+
+/* Allocate and register a new channel for port forwarding */
+void *new_sock_channel(void *handle, Socket s);
+void ssh_send_port_open(void *handle, void *channel,
+                       char *hostname, int port, char *org);
 
 Bignum copybn(Bignum b);
 Bignum bn_power_2(int n);
index fdbfeda..1a85b28 100644 (file)
--- a/telnet.c
+++ b/telnet.c
 #define TRUE 1
 #endif
 
-static Socket s = NULL;
-
-static void *frontend;
-static int telnet_term_width, telnet_term_height;
-
 #define        IAC     255                    /* interpret as command: */
 #define        DONT    254                    /* you are not to use option */
 #define        DO      253                    /* please, you use option */
@@ -141,59 +136,91 @@ static char *telopt(int opt)
     return "<unknown>";
 }
 
-static void telnet_size(int width, int height);
+static void telnet_size(void *handle, int width, int height);
 
 struct Opt {
     int send;                         /* what we initially send */
     int nsend;                        /* -ve send if requested to stop it */
     int ack, nak;                     /* +ve and -ve acknowledgements */
     int option;                               /* the option code */
+    int index;                        /* index into telnet->opt_states[] */
     enum {
        REQUESTED, ACTIVE, INACTIVE, REALLY_INACTIVE
-    } state;
+    } initial_state;
+};
+
+enum {
+    OPTINDEX_NAWS,
+    OPTINDEX_TSPEED,
+    OPTINDEX_TTYPE,
+    OPTINDEX_OENV,
+    OPTINDEX_NENV,
+    OPTINDEX_ECHO,
+    OPTINDEX_WE_SGA,
+    OPTINDEX_THEY_SGA,
+    NUM_OPTS
 };
 
-static struct Opt o_naws =
-    { WILL, WONT, DO, DONT, TELOPT_NAWS, REQUESTED };
-static struct Opt o_tspeed =
-    { WILL, WONT, DO, DONT, TELOPT_TSPEED, REQUESTED };
-static struct Opt o_ttype =
-    { WILL, WONT, DO, DONT, TELOPT_TTYPE, REQUESTED };
-static struct Opt o_oenv = { WILL, WONT, DO, DONT, TELOPT_OLD_ENVIRON,
-    INACTIVE
+static const struct Opt o_naws =
+    { WILL, WONT, DO, DONT, TELOPT_NAWS, OPTINDEX_NAWS, REQUESTED };
+static const struct Opt o_tspeed =
+    { WILL, WONT, DO, DONT, TELOPT_TSPEED, OPTINDEX_TSPEED, REQUESTED };
+static const struct Opt o_ttype =
+    { WILL, WONT, DO, DONT, TELOPT_TTYPE, OPTINDEX_TTYPE, REQUESTED };
+static const struct Opt o_oenv = { WILL, WONT, DO, DONT, TELOPT_OLD_ENVIRON,
+    OPTINDEX_OENV, INACTIVE
 };
-static struct Opt o_nenv = { WILL, WONT, DO, DONT, TELOPT_NEW_ENVIRON,
-    REQUESTED
+static const struct Opt o_nenv = { WILL, WONT, DO, DONT, TELOPT_NEW_ENVIRON,
+    OPTINDEX_NENV, REQUESTED
 };
-static struct Opt o_echo =
-    { DO, DONT, WILL, WONT, TELOPT_ECHO, REQUESTED };
-static struct Opt o_we_sga =
-    { WILL, WONT, DO, DONT, TELOPT_SGA, REQUESTED };
-static struct Opt o_they_sga =
-    { DO, DONT, WILL, WONT, TELOPT_SGA, REQUESTED };
-
-static struct Opt *opts[] = {
+static const struct Opt o_echo =
+    { DO, DONT, WILL, WONT, TELOPT_ECHO, OPTINDEX_ECHO, REQUESTED };
+static const struct Opt o_we_sga =
+    { WILL, WONT, DO, DONT, TELOPT_SGA, OPTINDEX_WE_SGA, REQUESTED };
+static const struct Opt o_they_sga =
+    { DO, DONT, WILL, WONT, TELOPT_SGA, OPTINDEX_THEY_SGA, REQUESTED };
+
+static const struct Opt *const opts[] = {
     &o_naws, &o_tspeed, &o_ttype, &o_oenv, &o_nenv, &o_echo,
     &o_we_sga, &o_they_sga, NULL
 };
 
+typedef struct telnet_tag {
+    const struct plug_function_table *fn;
+    /* the above field _must_ be first in the structure */
+
+    Socket s;
+
+    void *frontend;
+    int term_width, term_height;
+
+    int opt_states[NUM_OPTS];
+
+    int echoing, editing;
+    int activated;
+    int bufsize;
+    int in_synch;
+    int sb_opt, sb_len;
+    char *sb_buf;
+    int sb_size;
+
+    enum {
+       TOP_LEVEL, SEENIAC, SEENWILL, SEENWONT, SEENDO, SEENDONT,
+           SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR
+    } state;
+
+} *Telnet;
+
 #define TELNET_MAX_BACKLOG 4096
 
-static int echoing = TRUE, editing = TRUE;
-static int activated = FALSE;
-static int telnet_bufsize;
-static int in_synch;
-static int sb_opt, sb_len;
-static char *sb_buf = NULL;
-static int sb_size = 0;
 #define SB_DELTA 1024
 
-static void c_write1(int c)
+static void c_write1(Telnet telnet, int c)
 {
     int backlog;
     char cc = (char) c;
-    backlog = from_backend(frontend, 0, &cc, 1);
-    sk_set_frozen(s, backlog > TELNET_MAX_BACKLOG);
+    backlog = from_backend(telnet->frontend, 0, &cc, 1);
+    sk_set_frozen(telnet->s, backlog > TELNET_MAX_BACKLOG);
 }
 
 static void log_option(char *sender, int cmd, int option)
@@ -206,57 +233,58 @@ static void log_option(char *sender, int cmd, int option)
     logevent(buf);
 }
 
-static void send_opt(int cmd, int option)
+static void send_opt(Telnet telnet, int cmd, int option)
 {
     unsigned char b[3];
 
     b[0] = IAC;
     b[1] = cmd;
     b[2] = option;
-    telnet_bufsize = sk_write(s, b, 3);
+    telnet->bufsize = sk_write(telnet->s, b, 3);
     log_option("client", cmd, option);
 }
 
-static void deactivate_option(struct Opt *o)
+static void deactivate_option(Telnet telnet, const struct Opt *o)
 {
-    if (o->state == REQUESTED || o->state == ACTIVE)
-       send_opt(o->nsend, o->option);
-    o->state = REALLY_INACTIVE;
+    if (telnet->opt_states[o->index] == REQUESTED ||
+       telnet->opt_states[o->index] == ACTIVE)
+       send_opt(telnet, o->nsend, o->option);
+    telnet->opt_states[o->index] = REALLY_INACTIVE;
 }
 
 /*
  * Generate side effects of enabling or disabling an option.
  */
-static void option_side_effects(struct Opt *o, int enabled)
+static void option_side_effects(Telnet telnet, const struct Opt *o, int enabled)
 {
     if (o->option == TELOPT_ECHO && o->send == DO)
-       echoing = !enabled;
+       telnet->echoing = !enabled;
     else if (o->option == TELOPT_SGA && o->send == DO)
-       editing = !enabled;
+       telnet->editing = !enabled;
     ldisc_send(NULL, 0, 0);           /* cause ldisc to notice the change */
 
     /* Ensure we get the minimum options */
-    if (!activated) {
-       if (o_echo.state == INACTIVE) {
-           o_echo.state = REQUESTED;
-           send_opt(o_echo.send, o_echo.option);
+    if (!telnet->activated) {
+       if (telnet->opt_states[o_echo.index] == INACTIVE) {
+           telnet->opt_states[o_echo.index] = REQUESTED;
+           send_opt(telnet, o_echo.send, o_echo.option);
        }
-       if (o_we_sga.state == INACTIVE) {
-           o_we_sga.state = REQUESTED;
-           send_opt(o_we_sga.send, o_we_sga.option);
+       if (telnet->opt_states[o_we_sga.index] == INACTIVE) {
+           telnet->opt_states[o_we_sga.index] = REQUESTED;
+           send_opt(telnet, o_we_sga.send, o_we_sga.option);
        }
-       if (o_they_sga.state == INACTIVE) {
-           o_they_sga.state = REQUESTED;
-           send_opt(o_they_sga.send, o_they_sga.option);
+       if (telnet->opt_states[o_they_sga.index] == INACTIVE) {
+           telnet->opt_states[o_they_sga.index] = REQUESTED;
+           send_opt(telnet, o_they_sga.send, o_they_sga.option);
        }
-       activated = TRUE;
+       telnet->activated = TRUE;
     }
 }
 
-static void activate_option(struct Opt *o)
+static void activate_option(Telnet telnet, const struct Opt *o)
 {
     if (o->send == WILL && o->option == TELOPT_NAWS)
-       telnet_size(telnet_term_width, telnet_term_height);
+       telnet_size(telnet, telnet->term_width, telnet->term_height);
     if (o->send == WILL &&
        (o->option == TELOPT_NEW_ENVIRON ||
         o->option == TELOPT_OLD_ENVIRON)) {
@@ -264,56 +292,56 @@ static void activate_option(struct Opt *o)
         * We may only have one kind of ENVIRON going at a time.
         * This is a hack, but who cares.
         */
-       deactivate_option(o->option ==
+       deactivate_option(telnet, o->option ==
                          TELOPT_NEW_ENVIRON ? &o_oenv : &o_nenv);
     }
-    option_side_effects(o, 1);
+    option_side_effects(telnet, o, 1);
 }
 
-static void refused_option(struct Opt *o)
+static void refused_option(Telnet telnet, const struct Opt *o)
 {
     if (o->send == WILL && o->option == TELOPT_NEW_ENVIRON &&
-       o_oenv.state == INACTIVE) {
-       send_opt(WILL, TELOPT_OLD_ENVIRON);
-       o_oenv.state = REQUESTED;
+       telnet->opt_states[o_oenv.index] == INACTIVE) {
+       send_opt(telnet, WILL, TELOPT_OLD_ENVIRON);
+       telnet->opt_states[o_oenv.index] = REQUESTED;
     }
-    option_side_effects(o, 0);
+    option_side_effects(telnet, o, 0);
 }
 
-static void proc_rec_opt(int cmd, int option)
+static void proc_rec_opt(Telnet telnet, int cmd, int option)
 {
-    struct Opt **o;
+    const struct Opt *const *o;
 
     log_option("server", cmd, option);
     for (o = opts; *o; o++) {
        if ((*o)->option == option && (*o)->ack == cmd) {
-           switch ((*o)->state) {
+           switch (telnet->opt_states[(*o)->index]) {
              case REQUESTED:
-               (*o)->state = ACTIVE;
-               activate_option(*o);
+               telnet->opt_states[(*o)->index] = ACTIVE;
+               activate_option(telnet, *o);
                break;
              case ACTIVE:
                break;
              case INACTIVE:
-               (*o)->state = ACTIVE;
-               send_opt((*o)->send, option);
-               activate_option(*o);
+               telnet->opt_states[(*o)->index] = ACTIVE;
+               send_opt(telnet, (*o)->send, option);
+               activate_option(telnet, *o);
                break;
              case REALLY_INACTIVE:
-               send_opt((*o)->nsend, option);
+               send_opt(telnet, (*o)->nsend, option);
                break;
            }
            return;
        } else if ((*o)->option == option && (*o)->nak == cmd) {
-           switch ((*o)->state) {
+           switch (telnet->opt_states[(*o)->index]) {
              case REQUESTED:
-               (*o)->state = INACTIVE;
-               refused_option(*o);
+               telnet->opt_states[(*o)->index] = INACTIVE;
+               refused_option(telnet, *o);
                break;
              case ACTIVE:
-               (*o)->state = INACTIVE;
-               send_opt((*o)->nsend, option);
-               option_side_effects(*o, 0);
+               telnet->opt_states[(*o)->index] = INACTIVE;
+               send_opt(telnet, (*o)->nsend, option);
+               option_side_effects(telnet, *o, 0);
                break;
              case INACTIVE:
              case REALLY_INACTIVE:
@@ -326,18 +354,18 @@ static void proc_rec_opt(int cmd, int option)
      * If we reach here, the option was one we weren't prepared to
      * cope with. So send a negative ack.
      */
-    send_opt((cmd == WILL ? DONT : WONT), option);
+    send_opt(telnet, (cmd == WILL ? DONT : WONT), option);
 }
 
-static void process_subneg(void)
+static void process_subneg(Telnet telnet)
 {
     unsigned char b[2048], *p, *q;
     int var, value, n;
     char *e;
 
-    switch (sb_opt) {
+    switch (telnet->sb_opt) {
       case TELOPT_TSPEED:
-       if (sb_len == 1 && sb_buf[0] == TELQUAL_SEND) {
+       if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) {
            char logbuf[sizeof(cfg.termspeed) + 80];
            b[0] = IAC;
            b[1] = SB;
@@ -347,7 +375,7 @@ static void process_subneg(void)
            n = 4 + strlen(cfg.termspeed);
            b[n] = IAC;
            b[n + 1] = SE;
-           telnet_bufsize = sk_write(s, b, n + 2);
+           telnet->bufsize = sk_write(telnet->s, b, n + 2);
            logevent("server:\tSB TSPEED SEND");
            sprintf(logbuf, "client:\tSB TSPEED IS %s", cfg.termspeed);
            logevent(logbuf);
@@ -355,7 +383,7 @@ static void process_subneg(void)
            logevent("server:\tSB TSPEED <something weird>");
        break;
       case TELOPT_TTYPE:
-       if (sb_len == 1 && sb_buf[0] == TELQUAL_SEND) {
+       if (telnet->sb_len == 1 && telnet->sb_buf[0] == TELQUAL_SEND) {
            char logbuf[sizeof(cfg.termtype) + 80];
            b[0] = IAC;
            b[1] = SB;
@@ -368,7 +396,7 @@ static void process_subneg(void)
                            'a' : cfg.termtype[n]);
            b[n + 4] = IAC;
            b[n + 5] = SE;
-           telnet_bufsize = sk_write(s, b, n + 6);
+           telnet->bufsize = sk_write(telnet->s, b, n + 6);
            b[n + 4] = 0;
            logevent("server:\tSB TTYPE SEND");
            sprintf(logbuf, "client:\tSB TTYPE IS %s", b + 4);
@@ -378,14 +406,14 @@ static void process_subneg(void)
        break;
       case TELOPT_OLD_ENVIRON:
       case TELOPT_NEW_ENVIRON:
-       p = sb_buf;
-       q = p + sb_len;
+       p = telnet->sb_buf;
+       q = p + telnet->sb_len;
        if (p < q && *p == TELQUAL_SEND) {
            char logbuf[50];
            p++;
-           sprintf(logbuf, "server:\tSB %s SEND", telopt(sb_opt));
+           sprintf(logbuf, "server:\tSB %s SEND", telopt(telnet->sb_opt));
            logevent(logbuf);
-           if (sb_opt == TELOPT_OLD_ENVIRON) {
+           if (telnet->sb_opt == TELOPT_OLD_ENVIRON) {
                if (cfg.rfc_environ) {
                    value = RFC_VALUE;
                    var = RFC_VAR;
@@ -416,7 +444,7 @@ static void process_subneg(void)
            }
            b[0] = IAC;
            b[1] = SB;
-           b[2] = sb_opt;
+           b[2] = telnet->sb_opt;
            b[3] = TELQUAL_IS;
            n = 4;
            e = cfg.environmt;
@@ -444,8 +472,8 @@ static void process_subneg(void)
            }
            b[n++] = IAC;
            b[n++] = SE;
-           telnet_bufsize = sk_write(s, b, n);
-           sprintf(logbuf, "client:\tSB %s IS %s", telopt(sb_opt),
+           telnet->bufsize = sk_write(telnet->s, b, n);
+           sprintf(logbuf, "client:\tSB %s IS %s", telopt(telnet->sb_opt),
                    n == 6 ? "<nothing>" : "<stuff>");
            logevent(logbuf);
        }
@@ -453,27 +481,22 @@ static void process_subneg(void)
     }
 }
 
-static enum {
-    TOP_LEVEL, SEENIAC, SEENWILL, SEENWONT, SEENDO, SEENDONT,
-    SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR
-} telnet_state = TOP_LEVEL;
-
-static void do_telnet_read(char *buf, int len)
+static void do_telnet_read(Telnet telnet, char *buf, int len)
 {
 
     while (len--) {
        int c = (unsigned char) *buf++;
 
-       switch (telnet_state) {
+       switch (telnet->state) {
          case TOP_LEVEL:
          case SEENCR:
-           if (c == NUL && telnet_state == SEENCR)
-               telnet_state = TOP_LEVEL;
+           if (c == NUL && telnet->state == SEENCR)
+               telnet->state = TOP_LEVEL;
            else if (c == IAC)
-               telnet_state = SEENIAC;
+               telnet->state = SEENIAC;
            else {
-               if (!in_synch)
-                   c_write1(c);
+               if (!telnet->in_synch)
+                   c_write1(telnet, c);
 
 #if 1
                /* I can't get the F***ing winsock to insert the urgent IAC
@@ -485,84 +508,84 @@ static void do_telnet_read(char *buf, int len)
                 * just stop hiding on the next 0xf2 and hope for the best.
                 */
                else if (c == DM)
-                   in_synch = 0;
+                   telnet->in_synch = 0;
 #endif
                if (c == CR)
-                   telnet_state = SEENCR;
+                   telnet->state = SEENCR;
                else
-                   telnet_state = TOP_LEVEL;
+                   telnet->state = TOP_LEVEL;
            }
            break;
          case SEENIAC:
            if (c == DO)
-               telnet_state = SEENDO;
+               telnet->state = SEENDO;
            else if (c == DONT)
-               telnet_state = SEENDONT;
+               telnet->state = SEENDONT;
            else if (c == WILL)
-               telnet_state = SEENWILL;
+               telnet->state = SEENWILL;
            else if (c == WONT)
-               telnet_state = SEENWONT;
+               telnet->state = SEENWONT;
            else if (c == SB)
-               telnet_state = SEENSB;
+               telnet->state = SEENSB;
            else if (c == DM) {
-               in_synch = 0;
-               telnet_state = TOP_LEVEL;
+               telnet->in_synch = 0;
+               telnet->state = TOP_LEVEL;
            } else {
                /* ignore everything else; print it if it's IAC */
                if (c == IAC) {
-                   c_write1(c);
+                   c_write1(telnet, c);
                }
-               telnet_state = TOP_LEVEL;
+               telnet->state = TOP_LEVEL;
            }
            break;
          case SEENWILL:
-           proc_rec_opt(WILL, c);
-           telnet_state = TOP_LEVEL;
+           proc_rec_opt(telnet, WILL, c);
+           telnet->state = TOP_LEVEL;
            break;
          case SEENWONT:
-           proc_rec_opt(WONT, c);
-           telnet_state = TOP_LEVEL;
+           proc_rec_opt(telnet, WONT, c);
+           telnet->state = TOP_LEVEL;
            break;
          case SEENDO:
-           proc_rec_opt(DO, c);
-           telnet_state = TOP_LEVEL;
+           proc_rec_opt(telnet, DO, c);
+           telnet->state = TOP_LEVEL;
            break;
          case SEENDONT:
-           proc_rec_opt(DONT, c);
-           telnet_state = TOP_LEVEL;
+           proc_rec_opt(telnet, DONT, c);
+           telnet->state = TOP_LEVEL;
            break;
          case SEENSB:
-           sb_opt = c;
-           sb_len = 0;
-           telnet_state = SUBNEGOT;
+           telnet->sb_opt = c;
+           telnet->sb_len = 0;
+           telnet->state = SUBNEGOT;
            break;
          case SUBNEGOT:
            if (c == IAC)
-               telnet_state = SUBNEG_IAC;
+               telnet->state = SUBNEG_IAC;
            else {
              subneg_addchar:
-               if (sb_len >= sb_size) {
+               if (telnet->sb_len >= telnet->sb_size) {
                    char *newbuf;
-                   sb_size += SB_DELTA;
-                   newbuf = (sb_buf ?
-                             srealloc(sb_buf, sb_size) :
-                             smalloc(sb_size));
+                   telnet->sb_size += SB_DELTA;
+                   newbuf = (telnet->sb_buf ?
+                             srealloc(telnet->sb_buf, telnet->sb_size) :
+                             smalloc(telnet->sb_size));
                    if (newbuf)
-                       sb_buf = newbuf;
+                       telnet->sb_buf = newbuf;
                    else
-                       sb_size -= SB_DELTA;
+                       telnet->sb_size -= SB_DELTA;
                }
-               if (sb_len < sb_size)
-                   sb_buf[sb_len++] = c;
-               telnet_state = SUBNEGOT;        /* in case we came here by goto */
+               if (telnet->sb_len < telnet->sb_size)
+                   telnet->sb_buf[telnet->sb_len++] = c;
+               telnet->state = SUBNEGOT;       /* in case we came here by goto */
            }
            break;
          case SUBNEG_IAC:
            if (c != SE)
                goto subneg_addchar;   /* yes, it's a hack, I know, but... */
            else {
-               process_subneg();
-               telnet_state = TOP_LEVEL;
+               process_subneg(telnet);
+               telnet->state = TOP_LEVEL;
            }
            break;
        }
@@ -572,9 +595,11 @@ static void do_telnet_read(char *buf, int len)
 static int telnet_closing(Plug plug, char *error_msg, int error_code,
                          int calling_back)
 {
-    if (s) {
-        sk_close(s);
-        s = NULL;
+    Telnet telnet = (Telnet) plug;
+
+    if (telnet->s) {
+        sk_close(telnet->s);
+        telnet->s = NULL;
     }
     if (error_msg) {
        /* A socket error has occurred. */
@@ -586,15 +611,17 @@ static int telnet_closing(Plug plug, char *error_msg, int error_code,
 
 static int telnet_receive(Plug plug, int urgent, char *data, int len)
 {
+    Telnet telnet = (Telnet) plug;
     if (urgent)
-       in_synch = TRUE;
-    do_telnet_read(data, len);
+       telnet->in_synch = TRUE;
+    do_telnet_read(telnet, data, len);
     return 1;
 }
 
 static void telnet_sent(Plug plug, int bufsize)
 {
-    telnet_bufsize = bufsize;
+    Telnet telnet = (Telnet) plug;
+    telnet->bufsize = bufsize;
 }
 
 /*
@@ -605,21 +632,31 @@ static void telnet_sent(Plug plug, int bufsize)
  * Also places the canonical host name into `realhost'. It must be
  * freed by the caller.
  */
-static char *telnet_init(void *frontend_handle,
+static char *telnet_init(void *frontend_handle, void **backend_handle,
                         char *host, int port, char **realhost, int nodelay)
 {
-    static struct plug_function_table fn_table = {
+    static const struct plug_function_table fn_table = {
        telnet_closing,
        telnet_receive,
        telnet_sent
-    }, *fn_table_ptr = &fn_table;
-
+    };
     SockAddr addr;
     char *err;
-
-    frontend = frontend_handle;
-    telnet_term_width = cfg.width;
-    telnet_term_height = cfg.height;
+    Telnet telnet;
+
+    telnet = smalloc(sizeof(*telnet));
+    telnet->fn = &fn_table;
+    telnet->s = NULL;
+    telnet->echoing = TRUE;
+    telnet->editing = TRUE;
+    telnet->activated = FALSE;
+    telnet->sb_buf = NULL;
+    telnet->sb_size = 0;
+    telnet->frontend = frontend_handle;
+    telnet->term_width = cfg.width;
+    telnet->term_height = cfg.height;
+    telnet->state = TOP_LEVEL;
+    *backend_handle = telnet;
 
     /*
      * Try to find host.
@@ -645,8 +682,9 @@ static char *telnet_init(void *frontend_handle,
        sprintf(buf, "Connecting to %.100s port %d", addrbuf, port);
        logevent(buf);
     }
-    s = new_connection(addr, *realhost, port, 0, 1, nodelay, &fn_table_ptr);
-    if ((err = sk_socket_error(s)))
+    telnet->s = new_connection(addr, *realhost, port, 0, 1,
+                              nodelay, (Plug) telnet);
+    if ((err = sk_socket_error(telnet->s)))
        return err;
 
     sk_addr_free(addr);
@@ -655,24 +693,25 @@ static char *telnet_init(void *frontend_handle,
      * Initialise option states.
      */
     if (cfg.passive_telnet) {
-       struct Opt **o;
+       const struct Opt *const *o;
 
        for (o = opts; *o; o++)
-           if ((*o)->state == REQUESTED)
-               (*o)->state = INACTIVE;
+           telnet->opt_states[(*o)->index] = INACTIVE;
     } else {
-       struct Opt **o;
+       const struct Opt *const *o;
 
-       for (o = opts; *o; o++)
-           if ((*o)->state == REQUESTED)
-               send_opt((*o)->send, (*o)->option);
-       activated = TRUE;
+       for (o = opts; *o; o++) {
+           telnet->opt_states[(*o)->index] = (*o)->initial_state;
+           if (telnet->opt_states[(*o)->index] == REQUESTED)
+               send_opt(telnet, (*o)->send, (*o)->option);
+       }
+       telnet->activated = TRUE;
     }
 
     /*
      * Set up SYNCH state.
      */
-    in_synch = FALSE;
+    telnet->in_synch = FALSE;
 
     return NULL;
 }
@@ -680,8 +719,9 @@ static char *telnet_init(void *frontend_handle,
 /*
  * Called to send data down the Telnet connection.
  */
-static int telnet_send(char *buf, int len)
+static int telnet_send(void *handle, char *buf, int len)
 {
+    Telnet telnet = (Telnet) handle;
     char *p;
     static unsigned char iac[2] = { IAC, IAC };
     static unsigned char cr[2] = { CR, NUL };
@@ -689,7 +729,7 @@ static int telnet_send(char *buf, int len)
     static unsigned char nl[2] = { CR, LF };
 #endif
 
-    if (s == NULL)
+    if (telnet->s == NULL)
        return 0;
 
     p = buf;
@@ -698,49 +738,51 @@ static int telnet_send(char *buf, int len)
 
        while (iswritable((unsigned char) *p) && p < buf + len)
            p++;
-       telnet_bufsize = sk_write(s, q, p - q);
+       telnet->bufsize = sk_write(telnet->s, q, p - q);
 
        while (p < buf + len && !iswritable((unsigned char) *p)) {
-           telnet_bufsize = 
-               sk_write(s, (unsigned char) *p == IAC ? iac : cr, 2);
+           telnet->bufsize = 
+               sk_write(telnet->s, (unsigned char) *p == IAC ? iac : cr, 2);
            p++;
        }
     }
 
-    return telnet_bufsize;
+    return telnet->bufsize;
 }
 
 /*
  * Called to query the current socket sendability status.
  */
-static int telnet_sendbuffer(void)
+static int telnet_sendbuffer(void *handle)
 {
-    return telnet_bufsize;
+    Telnet telnet = (Telnet) handle;
+    return telnet->bufsize;
 }
 
 /*
  * Called to set the size of the window from Telnet's POV.
  */
-static void telnet_size(int width, int height)
+static void telnet_size(void *handle, int width, int height)
 {
+    Telnet telnet = (Telnet) handle;
     unsigned char b[16];
     char logbuf[50];
 
-    telnet_term_width = width;
-    telnet_term_height = height;
+    telnet->term_width = width;
+    telnet->term_height = height;
 
-    if (s == NULL || o_naws.state != ACTIVE)
+    if (telnet->s == NULL || telnet->opt_states[o_naws.index] != ACTIVE)
        return;
     b[0] = IAC;
     b[1] = SB;
     b[2] = TELOPT_NAWS;
-    b[3] = telnet_term_width >> 8;
-    b[4] = telnet_term_width & 0xFF;
-    b[5] = telnet_term_height >> 8;
-    b[6] = telnet_term_height & 0xFF;
+    b[3] = telnet->term_width >> 8;
+    b[4] = telnet->term_width & 0xFF;
+    b[5] = telnet->term_height >> 8;
+    b[6] = telnet->term_height & 0xFF;
     b[7] = IAC;
     b[8] = SE;
-    telnet_bufsize = sk_write(s, b, 9);
+    telnet->bufsize = sk_write(telnet->s, b, 9);
     sprintf(logbuf, "client:\tSB NAWS %d,%d",
            ((unsigned char) b[3] << 8) + (unsigned char) b[4],
            ((unsigned char) b[5] << 8) + (unsigned char) b[6]);
@@ -750,118 +792,125 @@ static void telnet_size(int width, int height)
 /*
  * Send Telnet special codes.
  */
-static void telnet_special(Telnet_Special code)
+static void telnet_special(void *handle, Telnet_Special code)
 {
+    Telnet telnet = (Telnet) handle;
     unsigned char b[2];
 
-    if (s == NULL)
+    if (telnet->s == NULL)
        return;
 
     b[0] = IAC;
     switch (code) {
       case TS_AYT:
        b[1] = AYT;
-       telnet_bufsize = sk_write(s, b, 2);
+       telnet->bufsize = sk_write(telnet->s, b, 2);
        break;
       case TS_BRK:
        b[1] = BREAK;
-       telnet_bufsize = sk_write(s, b, 2);
+       telnet->bufsize = sk_write(telnet->s, b, 2);
        break;
       case TS_EC:
        b[1] = EC;
-       telnet_bufsize = sk_write(s, b, 2);
+       telnet->bufsize = sk_write(telnet->s, b, 2);
        break;
       case TS_EL:
        b[1] = EL;
-       telnet_bufsize = sk_write(s, b, 2);
+       telnet->bufsize = sk_write(telnet->s, b, 2);
        break;
       case TS_GA:
        b[1] = GA;
-       telnet_bufsize = sk_write(s, b, 2);
+       telnet->bufsize = sk_write(telnet->s, b, 2);
        break;
       case TS_NOP:
        b[1] = NOP;
-       telnet_bufsize = sk_write(s, b, 2);
+       telnet->bufsize = sk_write(telnet->s, b, 2);
        break;
       case TS_ABORT:
        b[1] = ABORT;
-       telnet_bufsize = sk_write(s, b, 2);
+       telnet->bufsize = sk_write(telnet->s, b, 2);
        break;
       case TS_AO:
        b[1] = AO;
-       telnet_bufsize = sk_write(s, b, 2);
+       telnet->bufsize = sk_write(telnet->s, b, 2);
        break;
       case TS_IP:
        b[1] = IP;
-       telnet_bufsize = sk_write(s, b, 2);
+       telnet->bufsize = sk_write(telnet->s, b, 2);
        break;
       case TS_SUSP:
        b[1] = SUSP;
-       telnet_bufsize = sk_write(s, b, 2);
+       telnet->bufsize = sk_write(telnet->s, b, 2);
        break;
       case TS_EOR:
        b[1] = EOR;
-       telnet_bufsize = sk_write(s, b, 2);
+       telnet->bufsize = sk_write(telnet->s, b, 2);
        break;
       case TS_EOF:
        b[1] = xEOF;
-       telnet_bufsize = sk_write(s, b, 2);
+       telnet->bufsize = sk_write(telnet->s, b, 2);
        break;
       case TS_EOL:
-       telnet_bufsize = sk_write(s, "\r\n", 2);
+       telnet->bufsize = sk_write(telnet->s, "\r\n", 2);
        break;
       case TS_SYNCH:
        b[1] = DM;
-       telnet_bufsize = sk_write(s, b, 1);
-       telnet_bufsize = sk_write_oob(s, b + 1, 1);
+       telnet->bufsize = sk_write(telnet->s, b, 1);
+       telnet->bufsize = sk_write_oob(telnet->s, b + 1, 1);
        break;
       case TS_RECHO:
-       if (o_echo.state == INACTIVE || o_echo.state == REALLY_INACTIVE) {
-           o_echo.state = REQUESTED;
-           send_opt(o_echo.send, o_echo.option);
+       if (telnet->opt_states[o_echo.index] == INACTIVE ||
+           telnet->opt_states[o_echo.index] == REALLY_INACTIVE) {
+           telnet->opt_states[o_echo.index] = REQUESTED;
+           send_opt(telnet, o_echo.send, o_echo.option);
        }
        break;
       case TS_LECHO:
-       if (o_echo.state == ACTIVE) {
-           o_echo.state = REQUESTED;
-           send_opt(o_echo.nsend, o_echo.option);
+       if (telnet->opt_states[o_echo.index] == ACTIVE) {
+           telnet->opt_states[o_echo.index] = REQUESTED;
+           send_opt(telnet, o_echo.nsend, o_echo.option);
        }
        break;
       case TS_PING:
-       if (o_they_sga.state == ACTIVE) {
+       if (telnet->opt_states[o_they_sga.index] == ACTIVE) {
            b[1] = NOP;
-           telnet_bufsize = sk_write(s, b, 2);
+           telnet->bufsize = sk_write(telnet->s, b, 2);
        }
        break;
     }
 }
 
-static Socket telnet_socket(void)
+static Socket telnet_socket(void *handle)
 {
-    return s;
+    Telnet telnet = (Telnet) handle;
+    return telnet->s;
 }
 
-static int telnet_sendok(void)
+static int telnet_sendok(void *handle)
 {
+    Telnet telnet = (Telnet) handle;
     return 1;
 }
 
-static void telnet_unthrottle(int backlog)
+static void telnet_unthrottle(void *handle, int backlog)
 {
-    sk_set_frozen(s, backlog > TELNET_MAX_BACKLOG);
+    Telnet telnet = (Telnet) handle;
+    sk_set_frozen(telnet->s, backlog > TELNET_MAX_BACKLOG);
 }
 
-static int telnet_ldisc(int option)
+static int telnet_ldisc(void *handle, int option)
 {
+    Telnet telnet = (Telnet) handle;
     if (option == LD_ECHO)
-       return echoing;
+       return telnet->echoing;
     if (option == LD_EDIT)
-       return editing;
+       return telnet->editing;
     return FALSE;
 }
 
-static int telnet_exitcode(void)
+static int telnet_exitcode(void *handle)
 {
+    Telnet telnet = (Telnet) handle;
     /* Telnet doesn't transmit exit codes back to the client */
     return 0;
 }
index f412244..5a6d50a 100644 (file)
@@ -328,6 +328,8 @@ Terminal *term_init(void)
     term->nbeeps = 0;
     term->lastbeep = FALSE;
     term->beep_overloaded = FALSE;
+    term->resize_fn = NULL;
+    term->resize_ctx = NULL;
 
     return term;
 }
@@ -455,7 +457,22 @@ void term_size(Terminal *term, int newrows, int newcols, int newsavelines)
 
     update_sbar(term);
     term_update(term);
-    back->size(term->cols, term->rows);
+    if (term->resize_fn)
+       term->resize_fn(term->resize_ctx, term->cols, term->rows);
+}
+
+/*
+ * Hand a function and context pointer to the terminal which it can
+ * use to notify a back end of resizes.
+ */
+void term_provide_resize_fn(Terminal *term,
+                           void (*resize_fn)(void *, int, int),
+                           void *resize_ctx)
+{
+    term->resize_fn = resize_fn;
+    term->resize_ctx = resize_ctx;
+    if (term->cols > 0 && term->rows > 0)
+       resize_fn(resize_ctx, term->cols, term->rows);
 }
 
 /*
index 04217fc..792f89b 100644 (file)
@@ -157,6 +157,9 @@ struct terminal_tag {
     wchar_t *paste_buffer;
     int paste_len, paste_pos, paste_hold;
     long last_paste;
+
+    void (*resize_fn)(void *, int, int);
+    void *resize_ctx;
 };
 
 #define in_utf(term) ((term)->utf || line_codepage==CP_UTF8)
index 1aa2090..0171d52 100644 (file)
--- a/window.c
+++ b/window.c
@@ -600,7 +600,7 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        char msg[1024], *title;
        char *realhost;
 
-       error = back->init((void *)term,
+       error = back->init((void *)term, &backhandle,
                           cfg.host, cfg.port, &realhost, cfg.tcp_nodelay);
        if (error) {
            sprintf(msg, "Unable to open connection to\n"
@@ -620,6 +620,11 @@ int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show)
        set_icon(title);
     }
 
+    /*
+     * Connect the terminal to the backend for resize purposes.
+     */
+    term_provide_resize_fn(term, back->size, backhandle);
+
     session_closed = FALSE;
 
     /*
@@ -1610,7 +1615,7 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            time_t now;
            time(&now);
            if (now - last_movement > cfg.ping_interval) {
-               back->special(TS_PING);
+               back->special(backhandle, TS_PING);
                last_movement = now;
            }
        }
@@ -1860,55 +1865,55 @@ static LRESULT CALLBACK WndProc(HWND hwnd, UINT message,
            ldisc_send(NULL, 0, 0);
            break;
          case IDM_TEL_AYT:
-           back->special(TS_AYT);
+           back->special(backhandle, TS_AYT);
            net_pending_errors();
            break;
          case IDM_TEL_BRK:
-           back->special(TS_BRK);
+           back->special(backhandle, TS_BRK);
            net_pending_errors();
            break;
          case IDM_TEL_SYNCH:
-           back->special(TS_SYNCH);
+           back->special(backhandle, TS_SYNCH);
            net_pending_errors();
            break;
          case IDM_TEL_EC:
-           back->special(TS_EC);
+           back->special(backhandle, TS_EC);
            net_pending_errors();
            break;
          case IDM_TEL_EL:
-           back->special(TS_EL);
+           back->special(backhandle, TS_EL);
            net_pending_errors();
            break;
          case IDM_TEL_GA:
-           back->special(TS_GA);
+           back->special(backhandle, TS_GA);
            net_pending_errors();
            break;
          case IDM_TEL_NOP:
-           back->special(TS_NOP);
+           back->special(backhandle, TS_NOP);
            net_pending_errors();
            break;
          case IDM_TEL_ABORT:
-           back->special(TS_ABORT);
+           back->special(backhandle, TS_ABORT);
            net_pending_errors();
            break;
          case IDM_TEL_AO:
-           back->special(TS_AO);
+           back->special(backhandle, TS_AO);
            net_pending_errors();
            break;
          case IDM_TEL_IP:
-           back->special(TS_IP);
+           back->special(backhandle, TS_IP);
            net_pending_errors();
            break;
          case IDM_TEL_SUSP:
-           back->special(TS_SUSP);
+           back->special(backhandle, TS_SUSP);
            net_pending_errors();
            break;
          case IDM_TEL_EOR:
-           back->special(TS_EOR);
+           back->special(backhandle, TS_EOR);
            net_pending_errors();
            break;
          case IDM_TEL_EOF:
-           back->special(TS_EOF);
+           back->special(backhandle, TS_EOF);
            net_pending_errors();
            break;
          case IDM_ABOUT: