Browse Source

PuTTY snapshot 59c8df41 (Remove duplicate coroutine macros - 2019-02-28)

Source commit: 07b44a3062ae50923551536397784ace9ff89d8e
Martin Prikryl 6 years ago
parent
commit
13cbc72169

+ 8 - 0
source/putty/WINDOWS/winmiscs.c

@@ -267,3 +267,11 @@ bool platform_sha1_hw_available(void)
 }
 
 #endif
+
+bool is_console_handle(HANDLE handle)
+{
+    DWORD ignored_output;
+    if (GetConsoleMode(handle, &ignored_output))
+        return true;
+    return false;
+}

+ 5 - 0
source/putty/defs.h

@@ -40,6 +40,9 @@ typedef struct RSAKey RSAKey;
 
 typedef struct BinarySink BinarySink;
 typedef struct BinarySource BinarySource;
+typedef struct stdio_sink stdio_sink;
+typedef struct bufchain_sink bufchain_sink;
+typedef struct handle_sink handle_sink;
 
 typedef struct IdempotentCallback IdempotentCallback;
 
@@ -117,6 +120,8 @@ typedef struct settings_e settings_e;
 
 typedef struct SessionSpecial SessionSpecial;
 
+typedef struct StripCtrlChars StripCtrlChars;
+
 /*
  * A small structure wrapping up a (pointer, length) pair so that it
  * can be conveniently passed to or from a function.

+ 14 - 0
source/putty/doc/man-pl.but

@@ -93,6 +93,20 @@ very useful in this context.)
 
 \dd Disable interactive prompts.
 
+\dt \cw{-sanitise-stderr}
+
+\dt \cw{-sanitise-stdout}
+
+\dt \cw{-no-sanitise-stderr}
+
+\dt \cw{-no-sanitise-stdout}
+
+\dd By default, Plink can choose to filter control characters if that
+seems appropriate, to prevent remote processes sending confusing escape
+sequences. These options override Plink's default behaviour to enable
+or disabling such filtering on the standard error and standard output
+channels.
+
 \dt \cw{-pw} \e{password}
 
 \dd Set remote password to \e{password}. \e{CAUTION:} this will likely

+ 7 - 0
source/putty/doc/man-pscp.but

@@ -94,6 +94,13 @@ very useful in this context.)
 
 \dd Disable interactive prompts.
 
+\dt \cw{-no-sanitise-stderr}
+
+\dd By default, PSCP will filter control characters from the standard error
+channel from the server, to prevent remote processes sending confusing
+escape sequences. This option forces the standard error channel to not be
+filtered.
+
 \dt \cw{-pw} \e{password}
 
 \dd Set remote password to \e{password}. \e{CAUTION:} this will likely

+ 7 - 0
source/putty/doc/man-psft.but

@@ -82,6 +82,13 @@ very useful in this context.)
 
 \dd Disable interactive prompts.
 
+\dt \cw{-no-sanitise-stderr}
+
+\dd By default, PSFTP will filter control characters from the standard error
+channel from the server, to prevent remote processes sending confusing
+escape sequences. This option forces the standard error channel to not be
+filtered.
+
 \dt \cw{-pw} \e{password}
 
 \dd Set remote password to \e{password}. \e{CAUTION:} this will likely

+ 46 - 0
source/putty/doc/plink.but

@@ -79,6 +79,8 @@ use Plink:
 \c   -share    enable use of connection sharing
 \c   -hostkey aa:bb:cc:...
 \c             manually specify a host key (may be repeated)
+\c   -sanitise-stderr, -sanitise-stdout, -no-sanitise-stderr, -no-sanitise-stdout
+\c             do/don't strip control chars from standard output/error
 \c   -m file   read remote command(s) from file
 \c   -s        remote command is an SSH subsystem (SSH-2 only)
 \c   -N        don't start a shell/command (SSH-2 only)
@@ -282,6 +284,50 @@ zero exit status if a usable \q{upstream} exists, nonzero otherwise.
 
 (This option is only meaningful with the SSH-2 protocol.)
 
+\S2{plink-option-sanitise} \I{-sanitise-stderr}\I{-sanitise-stdout}\I{-no-sanitise-stderr}\I{-no-sanitise-stdout}\c{-sanitise-}\e{stream}: control output sanitisation
+
+In some situations, Plink applies a sanitisation pass to the output
+received from the server, to strip out control characters such as
+backspace and the escape character.
+    
+The idea of this is to prevent remote processes from sending confusing
+escape sequences through the standard error channel when Plink is
+being used as a transport for something like \cw{git} or CVS. If the
+server actually wants to send an error message, it will probably be
+plain text; if the server abuses that channel to try to write over
+unexpected parts of your terminal display, Plink will try to stop it.
+
+By default, this only happens for output channels which are sent to a
+Windows console device, or a Unix terminal device. (Any output stream
+going somewhere else is likely to be needed by an 8-bit protocol and
+must not be tampered with at all.) It also stops happening if you tell
+Plink to allocate a remote pseudo-terminal (see \k{using-cmdline-pty}
+and \k{config-ssh-pty}), on the basis that in that situation you often
+\e{want} escape sequences from the server to go to your terminal.
+
+But in case Plink guesses wrong about whether you want this
+sanitisation, you can override it in either direction, using one of
+these options:
+
+\dd \c{-sanitise-stderr}
+
+\dt Sanitise server data written to Plink's standard error channel,
+regardless of terminals and consoles and remote ptys.
+
+\dd \c{-no-sanitise-stderr}
+
+\dt Do not sanitise server data written to Plink's standard error
+channel.
+
+\dd \c{-sanitise-stdout}
+
+\dt Sanitise server data written to Plink's standard output channel.
+
+\dd \c{-no-sanitise-stdout}
+
+\dt Do not sanitise server data written to Plink's standard output
+channel.
+
 \H{plink-batch} Using Plink in \i{batch files} and \i{scripts}
 
 Once you have set up Plink to be able to log in to a remote server

+ 10 - 0
source/putty/doc/pscp.but

@@ -63,6 +63,7 @@ use PSCP:
 \c   -hostkey aa:bb:cc:...
 \c             manually specify a host key (may be repeated)
 \c   -batch    disable all interactive prompts
+\c   -no-sanitise-stderr  don't strip control chars from standard error
 \c   -proxycmd command
 \c             use 'command' as local proxy
 \c   -unsafe   allow server-side wildcards (DANGEROUS)
@@ -281,6 +282,15 @@ The \c{-sftp} option forces PSCP to use the SFTP protocol or quit.
 When this option is specified, PSCP looks harder for an SFTP server,
 which may allow use of SFTP with SSH-1 depending on server setup.
 
+\S2{pscp-option-sanitise} \I{-sanitise-stderr}\I{-no-sanitise-stderr}\c{-no-sanitise-stderr}: control error message sanitisation
+
+The \c{-no-sanitise-stderr} option will cause PSCP to pass through the
+server's standard-error stream literally, without stripping control
+characters from it first. This might be useful if the server were
+sending coloured error messages, but it also gives the server the
+ability to have unexpected effects on your terminal display. For more
+discussion, see \k{plink-option-sanitise}.
+
 \S{pscp-retval} \ii{Return value}
 
 PSCP returns an \i\cw{ERRORLEVEL} of zero (success) only if the files

+ 9 - 0
source/putty/doc/psftp.but

@@ -135,6 +135,15 @@ This may help PSFTP's behaviour when it is used in automated
 scripts: using \c{-batch}, if something goes wrong at connection
 time, the batch job will fail rather than hang.
 
+\S2{psftp-option-sanitise} \I{-sanitise-stderr}\I{-no-sanitise-stderr}\c{-no-sanitise-stderr}: control error message sanitisation
+
+The \c{-no-sanitise-stderr} option will cause PSFTP to pass through the
+server's standard-error stream literally, without stripping control
+characters from it first. This might be useful if the server were
+sending coloured error messages, but it also gives the server the
+ability to have unexpected effects on your terminal display. For more
+discussion, see \k{plink-option-sanitise}.
+
 \H{psftp-commands} Running PSFTP
 
 Once you have started your PSFTP session, you will see a \c{psftp>}

+ 17 - 45
source/putty/import.c

@@ -1084,8 +1084,7 @@ struct openssh_new_key {
     /* This too points to a position within keyblob */
     ptrlen private;
 
-    unsigned char *keyblob;
-    int keyblob_len, keyblob_size;
+    strbuf *keyblob;
 };
 
 static struct openssh_new_key *load_openssh_new_key(const Filename *filename,
@@ -1103,8 +1102,7 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename,
     unsigned key_index;
 
     ret = snew(struct openssh_new_key);
-    ret->keyblob = NULL;
-    ret->keyblob_len = ret->keyblob_size = 0;
+    ret->keyblob = strbuf_new();
 
     fp = f_open(filename, "r", false);
     if (!fp) {
@@ -1153,14 +1151,7 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename,
                     goto error;
                 }
 
-                if (ret->keyblob_len + len > ret->keyblob_size) {
-                    ret->keyblob_size = ret->keyblob_len + len + 256;
-                    ret->keyblob = sresize(ret->keyblob, ret->keyblob_size,
-                                           unsigned char);
-                }
-
-                memcpy(ret->keyblob + ret->keyblob_len, out, len);
-                ret->keyblob_len += len;
+                put_data(ret->keyblob, out, len);
 
                 smemclr(out, sizeof(out));
             }
@@ -1175,12 +1166,12 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename,
     fclose(fp);
     fp = NULL;
 
-    if (ret->keyblob_len == 0 || !ret->keyblob) {
+    if (ret->keyblob->len == 0 || !ret->keyblob) {
 	errmsg = "key body not present";
 	goto error;
     }
 
-    BinarySource_BARE_INIT(src, ret->keyblob, ret->keyblob_len);
+    BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(ret->keyblob));
 
     if (strcmp(get_asciz(src), "openssh-key-v1") != 0) {
         errmsg = "new-style OpenSSH magic number missing\n";
@@ -1288,10 +1279,7 @@ static struct openssh_new_key *load_openssh_new_key(const Filename *filename,
     }
     smemclr(base64_bit, sizeof(base64_bit));
     if (ret) {
-	if (ret->keyblob) {
-            smemclr(ret->keyblob, ret->keyblob_size);
-            sfree(ret->keyblob);
-        }
+        strbuf_free(ret->keyblob);
         smemclr(ret, sizeof(*ret));
 	sfree(ret);
     }
@@ -1308,8 +1296,7 @@ static bool openssh_new_encrypted(const Filename *filename)
     if (!key)
 	return false;
     ret = (key->cipher != ON_E_NONE);
-    smemclr(key->keyblob, key->keyblob_size);
-    sfree(key->keyblob);
+    strbuf_free(key->keyblob);
     smemclr(key, sizeof(*key));
     sfree(key);
     return ret;
@@ -1483,8 +1470,7 @@ static ssh2_userkey *openssh_new_read(
             ssh_key_free(retkey->key);
         sfree(retkey);
     }
-    smemclr(key->keyblob, key->keyblob_size);
-    sfree(key->keyblob);
+    strbuf_free(key->keyblob);
     smemclr(key, sizeof(*key));
     sfree(key);
     if (errmsg_p) *errmsg_p = errmsg;
@@ -1714,8 +1700,7 @@ static bool openssh_auto_write(
 
 struct sshcom_key {
     char comment[256];                 /* allowing any length is overkill */
-    unsigned char *keyblob;
-    int keyblob_len, keyblob_size;
+    strbuf *keyblob;
 };
 
 static struct sshcom_key *load_sshcom_key(const Filename *filename,
@@ -1733,8 +1718,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename,
 
     ret = snew(struct sshcom_key);
     ret->comment[0] = '\0';
-    ret->keyblob = NULL;
-    ret->keyblob_len = ret->keyblob_size = 0;
+    ret->keyblob = strbuf_new();
 
     fp = f_open(filename, "r", false);
     if (!fp) {
@@ -1831,14 +1815,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename,
                         goto error;
                     }
 
-                    if (ret->keyblob_len + len > ret->keyblob_size) {
-                        ret->keyblob_size = ret->keyblob_len + len + 256;
-                        ret->keyblob = sresize(ret->keyblob, ret->keyblob_size,
-					       unsigned char);
-                    }
-
-                    memcpy(ret->keyblob + ret->keyblob_len, out, len);
-                    ret->keyblob_len += len;
+                    put_data(ret->keyblob, out, len);
                 }
 
 		p++;
@@ -1849,7 +1826,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename,
 	line = NULL;
     }
 
-    if (ret->keyblob_len == 0 || !ret->keyblob) {
+    if (ret->keyblob->len == 0) {
 	errmsg = "key body not present";
 	goto error;
     }
@@ -1868,10 +1845,7 @@ static struct sshcom_key *load_sshcom_key(const Filename *filename,
 	line = NULL;
     }
     if (ret) {
-	if (ret->keyblob) {
-            smemclr(ret->keyblob, ret->keyblob_size);
-            sfree(ret->keyblob);
-        }
+        strbuf_free(ret->keyblob);
         smemclr(ret, sizeof(*ret));
 	sfree(ret);
     }
@@ -1890,7 +1864,7 @@ static bool sshcom_encrypted(const Filename *filename, char **comment)
     if (!key)
         goto done;
 
-    BinarySource_BARE_INIT(src, key->keyblob, key->keyblob_len);
+    BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(key->keyblob));
 
     if (get_uint32(src) != SSHCOM_MAGIC_NUMBER)
         goto done;                     /* key is invalid */
@@ -1905,8 +1879,7 @@ static bool sshcom_encrypted(const Filename *filename, char **comment)
     done:
     if (key) {
         *comment = dupstr(key->comment);
-        smemclr(key->keyblob, key->keyblob_size);
-        sfree(key->keyblob);
+        strbuf_free(key->keyblob);
         smemclr(key, sizeof(*key));
         sfree(key);
     } else {
@@ -1983,7 +1956,7 @@ static ssh2_userkey *sshcom_read(
     if (!key)
         return NULL;
 
-    BinarySource_BARE_INIT(src, key->keyblob, key->keyblob_len);
+    BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(key->keyblob));
 
     if (get_uint32(src) != SSHCOM_MAGIC_NUMBER) {
         errmsg = "key does not begin with magic number";
@@ -2158,8 +2131,7 @@ static ssh2_userkey *sshcom_read(
     if (blob) {
         strbuf_free(blob);
     }
-    smemclr(key->keyblob, key->keyblob_size);
-    sfree(key->keyblob);
+    strbuf_free(key->keyblob);
     smemclr(key, sizeof(*key));
     sfree(key);
     if (errmsg_p) *errmsg_p = errmsg;

+ 5 - 13
source/putty/logging.c

@@ -450,14 +450,11 @@ static Filename *xlatlognam(Filename *src, char *hostname, int port,
 {
     char buf[32], *bufp;
     int size;
-    char *buffer;
-    int buflen, bufsize;
+    strbuf *buffer;
     const char *s;
     Filename *ret;
 
-    bufsize = FILENAME_MAX;
-    buffer = snewn(bufsize, char);
-    buflen = 0;
+    buffer = strbuf_new();
     s = filename_to_str(src);
 
     while (*s) {
@@ -504,20 +501,15 @@ static Filename *xlatlognam(Filename *src, char *hostname, int port,
 	    buf[0] = *s++;
 	    size = 1;
 	}
-        if (bufsize <= buflen + size) {
-            bufsize = (buflen + size) * 5 / 4 + 512;
-            buffer = sresize(buffer, bufsize, char);
-        }
         while (size-- > 0) {
             char c = *bufp++;
             if (sanitise)
                 c = filename_char_sanitise(c);
-            buffer[buflen++] = c;
+            put_byte(buffer, c);
         }
     }
-    buffer[buflen] = '\0';
 
-    ret = filename_from_str(buffer);
-    sfree(buffer);
+    ret = filename_from_str(buffer->s);
+    strbuf_free(buffer);
     return ret;
 }

+ 24 - 0
source/putty/marshal.c

@@ -234,3 +234,27 @@ ptrlen BinarySource_get_pstring(BinarySource *src)
 
     return make_ptrlen(consume(len), len);
 }
+
+static void stdio_sink_write(BinarySink *bs, const void *data, size_t len)
+{
+    stdio_sink *sink = BinarySink_DOWNCAST(bs, stdio_sink);
+    fwrite(data, 1, len, sink->fp);
+}
+
+void stdio_sink_init(stdio_sink *sink, FILE *fp)
+{
+    sink->fp = fp;
+    BinarySink_INIT(sink, stdio_sink_write);
+}
+
+static void bufchain_sink_write(BinarySink *bs, const void *data, size_t len)
+{
+    bufchain_sink *sink = BinarySink_DOWNCAST(bs, bufchain_sink);
+    bufchain_add(sink->ch, data, len);
+}
+
+void bufchain_sink_init(bufchain_sink *sink, bufchain *ch)
+{
+    sink->ch = ch;
+    BinarySink_INIT(sink, bufchain_sink_write);
+}

+ 19 - 0
source/putty/marshal.h

@@ -3,6 +3,8 @@
 
 #include "defs.h"
 
+#include <stdio.h>
+
 /*
  * A sort of 'abstract base class' or 'interface' or 'trait' which is
  * the common feature of all types that want to accept data formatted
@@ -301,4 +303,21 @@ ptrlen BinarySource_get_pstring(BinarySource *);
 mp_int *BinarySource_get_mp_ssh1(BinarySource *src);
 mp_int *BinarySource_get_mp_ssh2(BinarySource *src);
 
+/*
+ * A couple of useful standard BinarySink implementations, which live
+ * as sensibly here as anywhere else: one that makes a BinarySink
+ * whose effect is to write to a stdio stream, and one whose effect is
+ * to append to a bufchain.
+ */
+struct stdio_sink {
+    FILE *fp;
+    BinarySink_IMPLEMENTATION;
+};
+struct bufchain_sink {
+    bufchain *ch;
+    BinarySink_IMPLEMENTATION;
+};
+void stdio_sink_init(stdio_sink *sink, FILE *fp);
+void bufchain_sink_init(bufchain_sink *sink, bufchain *ch);
+
 #endif /* PUTTY_MARSHAL_H */

+ 23 - 5
source/putty/misc.h

@@ -222,12 +222,12 @@ static inline NORETURN void unreachable_internal(void) { abort(); }
 void debug_printf(const char *fmt, ...);
 void debug_memdump(const void *buf, int len, bool L);
 #define debug(...) (debug_printf(__VA_ARGS__))
-#define dmemdump(buf,len) debug_memdump (buf, len, false);
-#define dmemdumpl(buf,len) debug_memdump (buf, len, true);
+#define dmemdump(buf,len) (debug_memdump(buf, len, false))
+#define dmemdumpl(buf,len) (debug_memdump(buf, len, true))
 #else
-#define debug(...)
-#define dmemdump(buf,len)
-#define dmemdumpl(buf,len)
+#define debug(...) ((void)0)
+#define dmemdump(buf,len) ((void)0)
+#define dmemdumpl(buf,len) ((void)0)
 #endif
 
 #ifndef lenof
@@ -351,4 +351,22 @@ static inline const char *NULLTOEMPTY(const char *s)
     return s ? s : "";
 }
 
+/* StripCtrlChars, defined in stripctrl.c: an adapter you can put on
+ * the front of one BinarySink and which functions as one in turn.
+ * Interprets its input as a stream of multibyte characters in the
+ * system locale, and removes any that are not either printable
+ * characters or newlines. */
+struct StripCtrlChars {
+    BinarySink_IMPLEMENTATION;
+    /* and this is contained in a larger structure */
+};
+StripCtrlChars *stripctrl_new(
+    BinarySink *bs_out, bool permit_cr, wchar_t substitution);
+void stripctrl_free(StripCtrlChars *sanpub);
+char *stripctrl_string_ptrlen(ptrlen str);
+static inline char *stripctrl_string(const char *str)
+{
+    return stripctrl_string_ptrlen(ptrlen_from_asciz(str));
+}
+
 #endif

+ 12 - 0
source/putty/mpint.c

@@ -257,6 +257,18 @@ unsigned mp_get_bit(mp_int *x, size_t bit)
                 (bit % BIGNUM_INT_BITS));
 }
 
+uintmax_t mp_get_integer(mp_int *x)
+{
+    uintmax_t toret = 0;
+    for (size_t i = x->nw; i-- > 0 ;) {
+        /* Shift in two stages to avoid undefined behaviour if the
+         * shift count equals the integer width */
+        toret = (toret << (BIGNUM_INT_BITS/2)) << (BIGNUM_INT_BITS/2);
+        toret |= x->w[i];
+    }
+    return toret;
+}
+
 void mp_set_bit(mp_int *x, size_t bit, unsigned val)
 {
     size_t word = bit / BIGNUM_INT_BITS;

+ 6 - 0
source/putty/mpint.h

@@ -100,6 +100,12 @@ mp_int *mp_power_2(size_t power);
 uint8_t mp_get_byte(mp_int *x, size_t byte);
 unsigned mp_get_bit(mp_int *x, size_t bit);
 
+/*
+ * Retrieve the value of an mp_int as a uintmax_t, assuming it's small
+ * enough to fit.
+ */
+uintmax_t mp_get_integer(mp_int *x);
+
 /*
  * Set an mp_int bit. Again, the bit index is not considered secret.
  * Do not pass an out-of-range index, on pain of assertion failure.

+ 31 - 20
source/putty/network.h

@@ -148,23 +148,31 @@ Socket *sk_new(SockAddr *addr, int port, bool privport, bool oobinline,
 Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug,
                        bool local_host_only, int address_family);
 
-#define sk_plug(s,p) (((s)->vt->plug) (s, p))
-#define sk_close(s) (((s)->vt->close) (s))
-#define sk_write(s,buf,len) (((s)->vt->write) (s, buf, len))
-#define sk_write_oob(s,buf,len) (((s)->vt->write_oob) (s, buf, len))
-#define sk_write_eof(s) (((s)->vt->write_eof) (s))
-#define sk_flush(s) (((s)->vt->flush) (s))
-
-#define plug_log(p,type,addr,port,msg,code) \
-    (((p)->vt->log) (p, type, addr, port, msg, code))
-#define plug_closing(p,msg,code,callback) \
-    (((p)->vt->closing) (p, msg, code, callback))
-#define plug_receive(p,urgent,buf,len) \
-    (((p)->vt->receive) (p, urgent, buf, len))
-#define plug_sent(p,bufsize) \
-    (((p)->vt->sent) (p, bufsize))
-#define plug_accepting(p, constructor, ctx) \
-    (((p)->vt->accepting)(p, constructor, ctx))
+static inline Plug *sk_plug(Socket *s, Plug *p)
+{ return s->vt->plug(s, p); }
+static inline void sk_close(Socket *s)
+{ s->vt->close(s); }
+static inline size_t sk_write(Socket *s, const void *data, size_t len)
+{ return s->vt->write(s, data, len); }
+static inline size_t sk_write_oob(Socket *s, const void *data, size_t len)
+{ return s->vt->write_oob(s, data, len); }
+static inline void sk_write_eof(Socket *s)
+{ s->vt->write_eof(s); }
+static inline void sk_flush(Socket *s)
+{ s->vt->flush(s); }
+
+static inline void plug_log(
+    Plug *p, int type, SockAddr *addr, int port, const char *msg, int code)
+{ return p->vt->log(p, type, addr, port, msg, code); }
+static inline void plug_closing(
+    Plug *p, const char *msg, int code, bool calling_back)
+{ return p->vt->closing(p, msg, code, calling_back); }
+static inline void plug_receive(Plug *p, int urg, const char *data, size_t len)
+{ return p->vt->receive(p, urg, data, len); }
+static inline void plug_sent (Plug *p, size_t bufsize)
+{ return p->vt->sent(p, bufsize); }
+static inline int plug_accepting(Plug *p, accept_fn_t cons, accept_ctx_t ctx)
+{ return p->vt->accepting(p, cons, ctx); }
 
 /*
  * Special error values are returned from sk_namelookup and sk_new
@@ -172,7 +180,8 @@ Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug,
  * or return NULL if there's no problem.
  */
 const char *sk_addr_error(SockAddr *addr);
-#define sk_socket_error(s) (((s)->vt->socket_error) (s))
+static inline const char *sk_socket_error(Socket *s)
+{ return s->vt->socket_error(s); }
 
 /*
  * Set the `frozen' flag on a socket. A frozen socket is one in
@@ -191,7 +200,8 @@ const char *sk_addr_error(SockAddr *addr);
  *    associated local socket in order to avoid unbounded buffer
  *    growth.
  */
-#define sk_set_frozen(s, is_frozen) (((s)->vt->set_frozen) (s, is_frozen))
+static inline void sk_set_frozen(Socket *s, bool is_frozen)
+{ s->vt->set_frozen(s, is_frozen); }
 
 /*
  * Return a structure giving some information about the other end of
@@ -199,7 +209,8 @@ const char *sk_addr_error(SockAddr *addr);
  * not NULL, then it is dynamically allocated, and should be freed by
  * a call to sk_free_peer_info(). See below for the definition.
  */
-#define sk_peer_info(s) (((s)->vt->peer_info) (s))
+static inline SocketPeerInfo *sk_peer_info(Socket *s)
+{ return s->vt->peer_info(s); }
 
 /*
  * The structure returned from sk_peer_info, and a function to free

+ 152 - 112
source/putty/putty.h

@@ -341,7 +341,7 @@ enum {
     CIPHER_MAX			       /* no. ciphers (inc warn) */
 };
 
-enum {
+enum TriState {
     /*
      * Several different bits of the PuTTY configuration seem to be
      * three-way settings whose values are `always yes', `always
@@ -530,23 +530,39 @@ struct BackendVtable {
     int default_port;
 };
 
-#define backend_init(vt, seat, out, logctx, conf, host, port, rhost, nd, ka) \
-    ((vt)->init(seat, out, logctx, conf, host, port, rhost, nd, ka))
-#define backend_free(be) ((be)->vt->free(be))
-#define backend_reconfig(be, conf) ((be)->vt->reconfig(be, conf))
-#define backend_send(be, buf, len) ((be)->vt->send(be, buf, len))
-#define backend_sendbuffer(be) ((be)->vt->sendbuffer(be))
-#define backend_size(be, w, h) ((be)->vt->size(be, w, h))
-#define backend_special(be, code, arg) ((be)->vt->special(be, code, arg))
-#define backend_get_specials(be) ((be)->vt->get_specials(be))
-#define backend_connected(be) ((be)->vt->connected(be))
-#define backend_exitcode(be) ((be)->vt->exitcode(be))
-#define backend_sendok(be) ((be)->vt->sendok(be))
-#define backend_ldisc_option_state(be, opt) \
-    ((be)->vt->ldisc_option_state(be, opt))
-#define backend_provide_ldisc(be, ldisc) ((be)->vt->provide_ldisc(be, ldisc))
-#define backend_unthrottle(be, bufsize) ((be)->vt->unthrottle(be, bufsize))
-#define backend_cfg_info(be) ((be)->vt->cfg_info(be))
+static inline const char *backend_init(
+    const BackendVtable *vt, Seat *seat, Backend **out, LogContext *logctx,
+    Conf *conf, const char *host, int port, char **rhost, bool nd, bool ka)
+{ return vt->init(seat, out, logctx, conf, host, port, rhost, nd, ka); }
+static inline void backend_free(Backend *be)
+{ be->vt->free(be); }
+static inline void backend_reconfig(Backend *be, Conf *conf)
+{ be->vt->reconfig(be, conf); }
+static inline size_t backend_send(Backend *be, const char *buf, size_t len)
+{ return be->vt->send(be, buf, len); }
+static inline size_t backend_sendbuffer(Backend *be)
+{ return be->vt->sendbuffer(be); }
+static inline void backend_size(Backend *be, int width, int height)
+{ be->vt->size(be, width, height); }
+static inline void backend_special(
+    Backend *be, SessionSpecialCode code, int arg)
+{ be->vt->special(be, code, arg); }
+static inline const SessionSpecial *backend_get_specials(Backend *be)
+{ return be->vt->get_specials(be); }
+static inline bool backend_connected(Backend *be)
+{ return be->vt->connected(be); }
+static inline int backend_exitcode(Backend *be)
+{ return be->vt->exitcode(be); }
+static inline bool backend_sendok(Backend *be)
+{ return be->vt->sendok(be); }
+static inline bool backend_ldisc_option_state(Backend *be, int state)
+{ return be->vt->ldisc_option_state(be, state); }
+static inline void backend_provide_ldisc(Backend *be, Ldisc *ldisc)
+{ be->vt->provide_ldisc(be, ldisc); }
+static inline void backend_unthrottle(Backend *be, size_t bufsize)
+{ be->vt->unthrottle(be, bufsize); }
+static inline int backend_cfg_info(Backend *be)
+{ return be->vt->cfg_info(be); }
 
 extern const struct BackendVtable *const backends[];
 
@@ -915,36 +931,44 @@ struct SeatVtable {
     bool (*get_window_pixel_size)(Seat *seat, int *width, int *height);
 };
 
-#define seat_output(seat, is_stderr, data, len) \
-    ((seat)->vt->output(seat, is_stderr, data, len))
-#define seat_eof(seat) \
-    ((seat)->vt->eof(seat))
-#define seat_get_userpass_input(seat, p, input) \
-    ((seat)->vt->get_userpass_input(seat, p, input))
-#define seat_notify_remote_exit(seat) \
-    ((seat)->vt->notify_remote_exit(seat))
-#define seat_update_specials_menu(seat) \
-    ((seat)->vt->update_specials_menu(seat))
-#define seat_get_ttymode(seat, mode) \
-    ((seat)->vt->get_ttymode(seat, mode))
-#define seat_set_busy_status(seat, status) \
-    ((seat)->vt->set_busy_status(seat, status))
-#define seat_verify_ssh_host_key(seat, h, p, typ, str, fp, cb, ctx) \
-    ((seat)->vt->verify_ssh_host_key(seat, h, p, typ, str, fp, cb, ctx))
-#define seat_confirm_weak_crypto_primitive(seat, typ, alg, cb, ctx) \
-    ((seat)->vt->confirm_weak_crypto_primitive(seat, typ, alg, cb, ctx))
-#define seat_confirm_weak_cached_hostkey(seat, alg, better, cb, ctx) \
-    ((seat)->vt->confirm_weak_cached_hostkey(seat, alg, better, cb, ctx))
-#define seat_is_utf8(seat) \
-    ((seat)->vt->is_utf8(seat))
-#define seat_echoedit_update(seat, echoing, editing) \
-    ((seat)->vt->echoedit_update(seat, echoing, editing))
-#define seat_get_x_display(seat) \
-    ((seat)->vt->get_x_display(seat))
-#define seat_get_windowid(seat, out) \
-    ((seat)->vt->get_windowid(seat, out))
-#define seat_get_window_pixel_size(seat, width, height) \
-    ((seat)->vt->get_window_pixel_size(seat, width, height))
+static inline size_t seat_output(
+    Seat *seat, bool err, const void *data, size_t len)
+{ return seat->vt->output(seat, err, data, len); }
+static inline bool seat_eof(Seat *seat)
+{ return seat->vt->eof(seat); }
+static inline int seat_get_userpass_input(
+    Seat *seat, prompts_t *p, bufchain *input)
+{ return seat->vt->get_userpass_input(seat, p, input); }
+static inline void seat_notify_remote_exit(Seat *seat)
+{ seat->vt->notify_remote_exit(seat); }
+static inline void seat_update_specials_menu(Seat *seat)
+{ seat->vt->update_specials_menu(seat); }
+static inline char *seat_get_ttymode(Seat *seat, const char *mode)
+{ return seat->vt->get_ttymode(seat, mode); }
+static inline void seat_set_busy_status(Seat *seat, BusyStatus status)
+{ seat->vt->set_busy_status(seat, status); }
+static inline int seat_verify_ssh_host_key(
+    Seat *seat, const char *h, int p, const char *ktyp, char *kstr,
+    char *fp, void (*cb)(void *ctx, int result), void *ctx)
+{ return seat->vt->verify_ssh_host_key(seat, h, p, ktyp, kstr, fp, cb, ctx); }
+static inline int seat_confirm_weak_crypto_primitive(
+    Seat *seat, const char *atyp, const char *aname,
+    void (*cb)(void *ctx, int result), void *ctx)
+{ return seat->vt->confirm_weak_crypto_primitive(seat, atyp, aname, cb, ctx); }
+static inline int seat_confirm_weak_cached_hostkey(
+    Seat *seat, const char *aname, const char *better,
+    void (*cb)(void *ctx, int result), void *ctx)
+{ return seat->vt->confirm_weak_cached_hostkey(seat, aname, better, cb, ctx); }
+static inline bool seat_is_utf8(Seat *seat)
+{ return seat->vt->is_utf8(seat); }
+static inline void seat_echoedit_update(Seat *seat, bool ec, bool ed)
+{ seat->vt->echoedit_update(seat, ec, ed); }
+static inline const char *seat_get_x_display(Seat *seat)
+{ return seat->vt->get_x_display(seat); }
+static inline bool seat_get_windowid(Seat *seat, long *id_out)
+{ return seat->vt->get_windowid(seat, id_out); }
+static inline bool seat_get_window_pixel_size(Seat *seat, int *w, int *h)
+{ return seat->vt->get_window_pixel_size(seat, w, h); }
 
 /* Unlike the seat's actual method, the public entry point
  * seat_connection_fatal is a wrapper function with a printf-like API,
@@ -952,10 +976,10 @@ struct SeatVtable {
 void seat_connection_fatal(Seat *seat, const char *fmt, ...);
 
 /* Handy aliases for seat_output which set is_stderr to a fixed value. */
-#define seat_stdout(seat, data, len) \
-    seat_output(seat, false, data, len)
-#define seat_stderr(seat, data, len) \
-    seat_output(seat, true, data, len)
+static inline size_t seat_stdout(Seat *seat, const void *data, size_t len)
+{ return seat_output(seat, false, data, len); }
+static inline size_t seat_stderr(Seat *seat, const void *data, size_t len)
+{ return seat_output(seat, true, data, len); }
 
 /*
  * Stub methods for seat implementations that want to use the obvious
@@ -1083,60 +1107,66 @@ struct TermWinVtable {
     bool (*is_utf8)(TermWin *);
 };
 
-#define win_setup_draw_ctx(win) \
-    ((win)->vt->setup_draw_ctx(win))
-#define win_draw_text(win, x, y, text, len, attrs, lattrs, tc) \
-    ((win)->vt->draw_text(win, x, y, text, len, attrs, lattrs, tc))
-#define win_draw_cursor(win, x, y, text, len, attrs, lattrs, tc) \
-    ((win)->vt->draw_cursor(win, x, y, text, len, attrs, lattrs, tc))
-#define win_char_width(win, uc) \
-    ((win)->vt->char_width(win, uc))
-#define win_free_draw_ctx(win) \
-    ((win)->vt->free_draw_ctx(win))
-#define win_set_cursor_pos(win, x, y) \
-    ((win)->vt->set_cursor_pos(win, x, y))
-#define win_set_raw_mouse_mode(win, enable) \
-    ((win)->vt->set_raw_mouse_mode(win, enable))
-#define win_set_scrollbar(win, total, start, page) \
-    ((win)->vt->set_scrollbar(win, total, start, page))
-#define win_bell(win, mode) \
-    ((win)->vt->bell(win, mode))
-#define win_clip_write(win, clipboard, text, attrs, colours, len, desel) \
-    ((win)->vt->clip_write(win, clipboard, text, attrs, colours, len, desel))
-#define win_clip_request_paste(win, clipboard) \
-    ((win)->vt->clip_request_paste(win, clipboard))
-#define win_refresh(win) \
-    ((win)->vt->refresh(win))
-#define win_request_resize(win, w, h) \
-    ((win)->vt->request_resize(win, w, h))
-#define win_set_title(win, title) \
-    ((win)->vt->set_title(win, title))
-#define win_set_icon_title(win, ititle) \
-    ((win)->vt->set_icon_title(win, ititle))
-#define win_set_minimised(win, minimised) \
-    ((win)->vt->set_minimised(win, minimised))
-#define win_is_minimised(win) \
-    ((win)->vt->is_minimised(win))
-#define win_set_maximised(win, maximised) \
-    ((win)->vt->set_maximised(win, maximised))
-#define win_move(win, x, y) \
-    ((win)->vt->move(win, x, y))
-#define win_set_zorder(win, top) \
-    ((win)->vt->set_zorder(win, top))
-#define win_palette_get(win, n, r, g, b) \
-    ((win)->vt->palette_get(win, n, r, g, b))
-#define win_palette_set(win, n, r, g, b) \
-    ((win)->vt->palette_set(win, n, r, g, b))
-#define win_palette_reset(win) \
-    ((win)->vt->palette_reset(win))
-#define win_get_pos(win, x, y) \
-    ((win)->vt->get_pos(win, x, y))
-#define win_get_pixels(win, x, y) \
-    ((win)->vt->get_pixels(win, x, y))
-#define win_get_title(win, icon) \
-    ((win)->vt->get_title(win, icon))
-#define win_is_utf8(win) \
-    ((win)->vt->is_utf8(win))
+static inline bool win_setup_draw_ctx(TermWin *win)
+{ return win->vt->setup_draw_ctx(win); }
+static inline void win_draw_text(
+    TermWin *win, int x, int y, wchar_t *text, int len,
+    unsigned long attrs, int line_attrs, truecolour tc)
+{ win->vt->draw_text(win, x, y, text, len, attrs, line_attrs, tc); }
+static inline void win_draw_cursor(
+    TermWin *win, int x, int y, wchar_t *text, int len,
+    unsigned long attrs, int line_attrs, truecolour tc)
+{ win->vt->draw_cursor(win, x, y, text, len, attrs, line_attrs, tc); }
+static inline int win_char_width(TermWin *win, int uc)
+{ return win->vt->char_width(win, uc); }
+static inline void win_free_draw_ctx(TermWin *win)
+{ win->vt->free_draw_ctx(win); }
+static inline void win_set_cursor_pos(TermWin *win, int x, int y)
+{ win->vt->set_cursor_pos(win, x, y); }
+static inline void win_set_raw_mouse_mode(TermWin *win, bool enable)
+{ win->vt->set_raw_mouse_mode(win, enable); }
+static inline void win_set_scrollbar(TermWin *win, int t, int s, int p)
+{ win->vt->set_scrollbar(win, t, s, p); }
+static inline void win_bell(TermWin *win, int mode)
+{ win->vt->bell(win, mode); }
+static inline void win_clip_write(
+    TermWin *win, int clipboard, wchar_t *text, int *attrs,
+    truecolour *colours, int len, bool deselect)
+{ win->vt->clip_write(win, clipboard, text, attrs, colours, len, deselect); }
+static inline void win_clip_request_paste(TermWin *win, int clipboard)
+{ win->vt->clip_request_paste(win, clipboard); }
+static inline void win_refresh(TermWin *win)
+{ win->vt->refresh(win); }
+static inline void win_request_resize(TermWin *win, int w, int h)
+{ win->vt->request_resize(win, w, h); }
+static inline void win_set_title(TermWin *win, const char *title)
+{ win->vt->set_title(win, title); }
+static inline void win_set_icon_title(TermWin *win, const char *icontitle)
+{ win->vt->set_icon_title(win, icontitle); }
+static inline void win_set_minimised(TermWin *win, bool minimised)
+{ win->vt->set_minimised(win, minimised); }
+static inline bool win_is_minimised(TermWin *win)
+{ return win->vt->is_minimised(win); }
+static inline void win_set_maximised(TermWin *win, bool maximised)
+{ win->vt->set_maximised(win, maximised); }
+static inline void win_move(TermWin *win, int x, int y)
+{ win->vt->move(win, x, y); }
+static inline void win_set_zorder(TermWin *win, bool top)
+{ win->vt->set_zorder(win, top); }
+static inline bool win_palette_get(TermWin *win, int n, int *r, int *g, int *b)
+{ return win->vt->palette_get(win, n, r, g, b); }
+static inline void win_palette_set(TermWin *win, int n, int r, int g, int b)
+{ win->vt->palette_set(win, n, r, g, b); }
+static inline void win_palette_reset(TermWin *win)
+{ win->vt->palette_reset(win); }
+static inline void win_get_pos(TermWin *win, int *x, int *y)
+{ win->vt->get_pos(win, x, y); }
+static inline void win_get_pixels(TermWin *win, int *x, int *y)
+{ win->vt->get_pixels(win, x, y); }
+static inline const char *win_get_title(TermWin *win, bool icon)
+{ return win->vt->get_title(win, icon); }
+static inline bool win_is_utf8(TermWin *win)
+{ return win->vt->is_utf8(win); }
 
 /*
  * Global functions not specific to a connection instance.
@@ -1486,16 +1516,20 @@ void random_destroy_seed(void);
 
 /*
  * Exports from settings.c.
+ *
+ * load_settings() and do_defaults() return false if the provided
+ * session name didn't actually exist. But they still fill in the
+ * provided Conf with _something_.
  */
 const struct BackendVtable *backend_vt_from_name(const char *name);
 const struct BackendVtable *backend_vt_from_proto(int proto);
 char *get_remote_username(Conf *conf); /* dynamically allocated */
 char *save_settings(const char *section, Conf *conf);
 void save_open_settings(settings_w *sesskey, Conf *conf);
-void load_settings(const char *section, Conf *conf);
+bool load_settings(const char *section, Conf *conf);
 void load_open_settings(settings_r *sesskey, Conf *conf);
 void get_sesslist(struct sesslist *, bool allocate);
-void do_defaults(const char *, Conf *);
+bool do_defaults(const char *, Conf *);
 void registry_cleanup(void);
 
 /*
@@ -1606,9 +1640,15 @@ struct LogPolicyVtable {
 struct LogPolicy {
     const LogPolicyVtable *vt;
 };
-#define lp_eventlog(lp, event) ((lp)->vt->eventlog(lp, event))
-#define lp_askappend(lp, fn, cb, ctx) ((lp)->vt->askappend(lp, fn, cb, ctx))
-#define lp_logging_error(lp, event) ((lp)->vt->logging_error(lp, event))
+
+static inline void lp_eventlog(LogPolicy *lp, const char *event)
+{ lp->vt->eventlog(lp, event); }
+static inline int lp_askappend(
+    LogPolicy *lp, Filename *filename,
+    void (*callback)(void *ctx, int result), void *ctx)
+{ return lp->vt->askappend(lp, filename, callback, ctx); }
+static inline void lp_logging_error(LogPolicy *lp, const char *event)
+{ lp->vt->logging_error(lp, event); }
 
 LogContext *log_init(LogPolicy *lp, Conf *conf);
 void log_free(LogContext *logctx);
@@ -1899,7 +1939,7 @@ void setup_config_box(struct controlbox *b, bool midsession,
  */
 typedef struct bidi_char {
     unsigned int origwc, wc;
-    unsigned short index;
+    unsigned short index, nchars;
 } bidi_char;
 int do_bidi(bidi_char *line, int count);
 int do_shape(bidi_char *line, bidi_char *to, int count);

+ 1 - 1
source/putty/ssh.c

@@ -317,7 +317,7 @@ static void ssh_got_ssh_version(struct ssh_version_receiver *rcv,
     ssh_bpp_free(old_bpp);
 }
 
-static void ssh_check_frozen(Ssh *ssh)
+void ssh_check_frozen(Ssh *ssh)
 {
     if (!ssh->s)
         return;

+ 168 - 97
source/putty/ssh.h

@@ -306,50 +306,70 @@ struct ConnectionLayer {
     const struct ConnectionLayerVtable *vt;
 };
 
-#define ssh_rportfwd_alloc(cl, sh, sp, dh, dp, af, ld, pfr, share) \
-    ((cl)->vt->rportfwd_alloc(cl, sh, sp, dh, dp, af, ld, pfr, share))
-#define ssh_rportfwd_remove(cl, rpf) ((cl)->vt->rportfwd_remove(cl, rpf))
-#define ssh_lportfwd_open(cl, h, p, desc, pi, chan) \
-    ((cl)->vt->lportfwd_open(cl, h, p, desc, pi, chan))
-#define ssh_serverside_x11_open(cl, chan, pi) \
-    ((cl)->vt->serverside_x11_open(cl, chan, pi))
-#define ssh_serverside_agent_open(cl, chan) \
-    ((cl)->vt->serverside_agent_open(cl, chan))
-#define ssh_session_open(cl, chan) \
-    ((cl)->vt->session_open(cl, chan))
-#define ssh_add_x11_display(cl, auth, disp) \
-    ((cl)->vt->add_x11_display(cl, auth, disp))
-#define ssh_add_sharing_x11_display(cl, auth, cs, ch)   \
-    ((cl)->vt->add_sharing_x11_display(cl, auth, cs, ch))
-#define ssh_remove_sharing_x11_display(cl, fa)   \
-    ((cl)->vt->remove_sharing_x11_display(cl, fa))
-#define ssh_send_packet_from_downstream(cl, id, type, pkt, len, log)    \
-    ((cl)->vt->send_packet_from_downstream(cl, id, type, pkt, len, log))
-#define ssh_alloc_sharing_channel(cl, cs) \
-    ((cl)->vt->alloc_sharing_channel(cl, cs))
-#define ssh_delete_sharing_channel(cl, ch) \
-    ((cl)->vt->delete_sharing_channel(cl, ch))
-#define ssh_sharing_queue_global_request(cl, cs) \
-    ((cl)->vt->sharing_queue_global_request(cl, cs))
-#define ssh_sharing_no_more_downstreams(cl) \
-    ((cl)->vt->sharing_no_more_downstreams(cl))
-#define ssh_agent_forwarding_permitted(cl) \
-    ((cl)->vt->agent_forwarding_permitted(cl))
-#define ssh_terminal_size(cl, w, h) ((cl)->vt->terminal_size(cl, w, h))
-#define ssh_stdout_unthrottle(cl, bufsize) \
-    ((cl)->vt->stdout_unthrottle(cl, bufsize))
-#define ssh_stdin_backlog(cl) ((cl)->vt->stdin_backlog(cl))
-#define ssh_throttle_all_channels(cl, throttled) \
-    ((cl)->vt->throttle_all_channels(cl, throttled))
-#define ssh_ldisc_option(cl, option) ((cl)->vt->ldisc_option(cl, option))
-#define ssh_set_ldisc_option(cl, opt, val) \
-    ((cl)->vt->set_ldisc_option(cl, opt, val))
-#define ssh_enable_x_fwd(cl) ((cl)->vt->enable_x_fwd(cl))
-#define ssh_enable_agent_fwd(cl) ((cl)->vt->enable_agent_fwd(cl))
-#define ssh_set_wants_user_input(cl, wanted) \
-    ((cl)->vt->set_wants_user_input(cl, wanted))
-#define ssh_setup_server_x_forwarding(cl, conf, ap, ad, scr) \
-    ((cl)->vt->setup_server_x_forwarding(cl, conf, ap, ad, scr))
+static inline struct ssh_rportfwd *ssh_rportfwd_alloc(
+    ConnectionLayer *cl, const char *sh, int sp, const char *dh, int dp,
+    int af, const char *log, PortFwdRecord *pfr, ssh_sharing_connstate *cs)
+{ return cl->vt->rportfwd_alloc(cl, sh, sp, dh, dp, af, log, pfr, cs); }
+static inline void ssh_rportfwd_remove(
+    ConnectionLayer *cl, struct ssh_rportfwd *rpf)
+{ cl->vt->rportfwd_remove(cl, rpf); }
+static inline SshChannel *ssh_lportfwd_open(
+    ConnectionLayer *cl, const char *host, int port,
+    const char *desc, const SocketPeerInfo *pi, Channel *chan)
+{ return cl->vt->lportfwd_open(cl, host, port, desc, pi, chan); }
+static inline SshChannel *ssh_session_open(ConnectionLayer *cl, Channel *chan)
+{ return cl->vt->session_open(cl, chan); }
+static inline SshChannel *ssh_serverside_x11_open(
+    ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi)
+{ return cl->vt->serverside_x11_open(cl, chan, pi); }
+static inline SshChannel *ssh_serverside_agent_open(
+    ConnectionLayer *cl, Channel *chan)
+{ return cl->vt->serverside_agent_open(cl, chan); }
+static inline struct X11FakeAuth *ssh_add_x11_display(
+    ConnectionLayer *cl, int authtype, struct X11Display *x11disp)
+{ return cl->vt->add_x11_display(cl, authtype, x11disp); }
+static inline struct X11FakeAuth *ssh_add_sharing_x11_display(
+    ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs,
+    share_channel *share_chan)
+{ return cl->vt->add_sharing_x11_display(cl, authtype, share_cs, share_chan); }
+static inline void ssh_remove_sharing_x11_display(
+    ConnectionLayer *cl, struct X11FakeAuth *auth)
+{ cl->vt->remove_sharing_x11_display(cl, auth); }
+static inline void ssh_send_packet_from_downstream(
+    ConnectionLayer *cl, unsigned id, int type,
+    const void *pkt, int len, const char *log)
+{ cl->vt->send_packet_from_downstream(cl, id, type, pkt, len, log); }
+static inline unsigned ssh_alloc_sharing_channel(
+    ConnectionLayer *cl, ssh_sharing_connstate *connstate)
+{ return cl->vt->alloc_sharing_channel(cl, connstate); }
+static inline void ssh_delete_sharing_channel(
+    ConnectionLayer *cl, unsigned localid)
+{ cl->vt->delete_sharing_channel(cl, localid); }
+static inline void ssh_sharing_queue_global_request(
+    ConnectionLayer *cl, ssh_sharing_connstate *connstate)
+{ cl->vt->sharing_queue_global_request(cl, connstate); }
+static inline void ssh_sharing_no_more_downstreams(ConnectionLayer *cl)
+{ cl->vt->sharing_no_more_downstreams(cl); }
+static inline bool ssh_agent_forwarding_permitted(ConnectionLayer *cl)
+{ return cl->vt->agent_forwarding_permitted(cl); }
+static inline void ssh_terminal_size(ConnectionLayer *cl, int w, int h)
+{ cl->vt->terminal_size(cl, w, h); }
+static inline void ssh_stdout_unthrottle(ConnectionLayer *cl, size_t bufsize)
+{ cl->vt->stdout_unthrottle(cl, bufsize); }
+static inline size_t ssh_stdin_backlog(ConnectionLayer *cl)
+{ return cl->vt->stdin_backlog(cl); }
+static inline void ssh_throttle_all_channels(ConnectionLayer *cl, bool thr)
+{ cl->vt->throttle_all_channels(cl, thr); }
+static inline bool ssh_ldisc_option(ConnectionLayer *cl, int option)
+{ return cl->vt->ldisc_option(cl, option); }
+static inline void ssh_set_ldisc_option(ConnectionLayer *cl, int opt, bool val)
+{ cl->vt->set_ldisc_option(cl, opt, val); }
+static inline void ssh_enable_x_fwd(ConnectionLayer *cl)
+{ cl->vt->enable_x_fwd(cl); }
+static inline void ssh_enable_agent_fwd(ConnectionLayer *cl)
+{ cl->vt->enable_agent_fwd(cl); }
+static inline void ssh_set_wants_user_input(ConnectionLayer *cl, bool wanted)
+{ cl->vt->set_wants_user_input(cl, wanted); }
 
 /* Exports from portfwd.c */
 PortFwdManager *portfwdmgr_new(ConnectionLayer *cl);
@@ -380,6 +400,7 @@ void ssh_got_fallback_cmd(Ssh *ssh);
 
 /* Communications back to ssh.c from the BPP */
 void ssh_conn_processed_data(Ssh *ssh);
+void ssh_check_frozen(Ssh *ssh);
 
 /* Functions to abort the connection, for various reasons. */
 void ssh_remote_error(Ssh *ssh, const char *fmt, ...);
@@ -599,17 +620,26 @@ struct ssh_cipheralg {
     const void *extra;
 };
 
-#define ssh_cipher_new(alg) ((alg)->new(alg))
-#define ssh_cipher_free(ctx) ((ctx)->vt->free(ctx))
-#define ssh_cipher_setiv(ctx, iv) ((ctx)->vt->setiv(ctx, iv))
-#define ssh_cipher_setkey(ctx, key) ((ctx)->vt->setkey(ctx, key))
-#define ssh_cipher_encrypt(ctx, blk, len) ((ctx)->vt->encrypt(ctx, blk, len))
-#define ssh_cipher_decrypt(ctx, blk, len) ((ctx)->vt->decrypt(ctx, blk, len))
-#define ssh_cipher_encrypt_length(ctx, blk, len, seq) \
-    ((ctx)->vt->encrypt_length(ctx, blk, len, seq))
-#define ssh_cipher_decrypt_length(ctx, blk, len, seq) \
-    ((ctx)->vt->decrypt_length(ctx, blk, len, seq))
-#define ssh_cipher_alg(ctx) ((ctx)->vt)
+static inline ssh_cipher *ssh_cipher_new(const ssh_cipheralg *alg)
+{ return alg->new(alg); }
+static inline void ssh_cipher_free(ssh_cipher *c)
+{ c->vt->free(c); }
+static inline void ssh_cipher_setiv(ssh_cipher *c, const void *iv)
+{ c->vt->setiv(c, iv); }
+static inline void ssh_cipher_setkey(ssh_cipher *c, const void *key)
+{ c->vt->setkey(c, key); }
+static inline void ssh_cipher_encrypt(ssh_cipher *c, void *blk, int len)
+{ c->vt->encrypt(c, blk, len); }
+static inline void ssh_cipher_decrypt(ssh_cipher *c, void *blk, int len)
+{ c->vt->decrypt(c, blk, len); }
+static inline void ssh_cipher_encrypt_length(
+    ssh_cipher *c, void *blk, int len, unsigned long seq)
+{ return c->vt->encrypt_length(c, blk, len, seq); }
+static inline void ssh_cipher_decrypt_length(
+    ssh_cipher *c, void *blk, int len, unsigned long seq)
+{ return c->vt->decrypt_length(c, blk, len, seq); }
+static inline const struct ssh_cipheralg *ssh_cipher_alg(ssh_cipher *c)
+{ return c->vt; }
 
 struct ssh2_ciphers {
     int nciphers;
@@ -636,13 +666,21 @@ struct ssh2_macalg {
     const void *extra;
 };
 
-#define ssh2_mac_new(alg, cipher) ((alg)->new(alg, cipher))
-#define ssh2_mac_free(ctx) ((ctx)->vt->free(ctx))
-#define ssh2_mac_setkey(ctx, key) ((ctx)->vt->setkey(ctx, key))
-#define ssh2_mac_start(ctx) ((ctx)->vt->start(ctx))
-#define ssh2_mac_genresult(ctx, out) ((ctx)->vt->genresult(ctx, out))
-#define ssh2_mac_text_name(ctx) ((ctx)->vt->text_name(ctx))
-#define ssh2_mac_alg(ctx) ((ctx)->vt)
+static inline ssh2_mac *ssh2_mac_new(
+    const ssh2_macalg *alg, ssh_cipher *cipher)
+{ return alg->new(alg, cipher); }
+static inline void ssh2_mac_free(ssh2_mac *m)
+{ m->vt->free(m); }
+static inline void ssh2_mac_setkey(ssh2_mac *m, ptrlen key)
+{ m->vt->setkey(m, key); }
+static inline void ssh2_mac_start(ssh2_mac *m)
+{ m->vt->start(m); }
+static inline void ssh2_mac_genresult(ssh2_mac *m, unsigned char *out)
+{ m->vt->genresult(m, out); }
+static inline const char *ssh2_mac_text_name(ssh2_mac *m)
+{ return m->vt->text_name(m); }
+static inline const ssh2_macalg *ssh2_mac_alg(ssh2_mac *m)
+{ return m->vt; }
 
 /* Centralised 'methods' for ssh2_mac, defined in sshmac.c. These run
  * the MAC in a specifically SSH-2 style, i.e. taking account of a
@@ -672,11 +710,16 @@ struct ssh_hashalg {
     const char *text_name;    /* both combined, e.g. "SHA-n (unaccelerated)" */
 };
 
-#define ssh_hash_new(alg) ((alg)->new(alg))
-#define ssh_hash_copy(ctx) ((ctx)->vt->copy(ctx))
-#define ssh_hash_final(ctx, out) ((ctx)->vt->final(ctx, out))
-#define ssh_hash_free(ctx) ((ctx)->vt->free(ctx))
-#define ssh_hash_alg(ctx) ((ctx)->vt)
+static inline ssh_hash *ssh_hash_new(const ssh_hashalg *alg)
+{ return alg->new(alg); }
+static inline ssh_hash *ssh_hash_copy(ssh_hash *h)
+{ return h->vt->copy(h); }
+static inline void ssh_hash_final(ssh_hash *h, unsigned char *out)
+{ return h->vt->final(h, out); }
+static inline void ssh_hash_free(ssh_hash *h)
+{ return h->vt->free(h); }
+static inline const ssh_hashalg *ssh_hash_alg(ssh_hash *h)
+{ return h->vt; }
 
 /* Handy macros for defining all those text-name fields at once */
 #define HASHALG_NAMES_BARE(base) \
@@ -724,25 +767,39 @@ struct ssh_keyalg {
     const unsigned supported_flags;    /* signature-type flags we understand */
 };
 
-#define ssh_key_new_pub(alg, data) ((alg)->new_pub(alg, data))
-#define ssh_key_new_priv(alg, pub, priv) ((alg)->new_priv(alg, pub, priv))
-#define ssh_key_new_priv_openssh(alg, bs) ((alg)->new_priv_openssh(alg, bs))
-
-#define ssh_key_free(key) ((key)->vt->freekey(key))
-#define ssh_key_invalid(key, flags) ((key)->vt->invalid(key, flags))
-#define ssh_key_sign(key, data, flags, bs) \
-    ((key)->vt->sign(key, data, flags, bs))
-#define ssh_key_verify(key, sig, data) ((key)->vt->verify(key, sig, data))
-#define ssh_key_public_blob(key, bs) ((key)->vt->public_blob(key, bs))
-#define ssh_key_private_blob(key, bs) ((key)->vt->private_blob(key, bs))
-#define ssh_key_openssh_blob(key, bs) ((key)->vt->openssh_blob(key, bs))
-#define ssh_key_cache_str(key) ((key)->vt->cache_str(key))
-
-#define ssh_key_public_bits(alg, blob) ((alg)->pubkey_bits(alg, blob))
-
-#define ssh_key_alg(key) (key)->vt
-#define ssh_key_ssh_id(key) ((key)->vt->ssh_id)
-#define ssh_key_cache_id(key) ((key)->vt->cache_id)
+static inline ssh_key *ssh_key_new_pub(const ssh_keyalg *self, ptrlen pub)
+{ return self->new_pub(self, pub); }
+static inline ssh_key *ssh_key_new_priv(
+    const ssh_keyalg *self, ptrlen pub, ptrlen priv)
+{ return self->new_priv(self, pub, priv); }
+static inline ssh_key *ssh_key_new_priv_openssh(
+    const ssh_keyalg *self, BinarySource *src)
+{ return self->new_priv_openssh(self, src); }
+static inline void ssh_key_free(ssh_key *key)
+{ key->vt->freekey(key); }
+static inline char *ssh_key_invalid(ssh_key *key, unsigned flags)
+{ return key->vt->invalid(key, flags); }
+static inline void ssh_key_sign(
+    ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs)
+{ key->vt->sign(key, data, flags, bs); }
+static inline bool ssh_key_verify(ssh_key *key, ptrlen sig, ptrlen data)
+{ return key->vt->verify(key, sig, data); }
+static inline void ssh_key_public_blob(ssh_key *key, BinarySink *bs)
+{ key->vt->public_blob(key, bs); }
+static inline void ssh_key_private_blob(ssh_key *key, BinarySink *bs)
+{ key->vt->private_blob(key, bs); }
+static inline void ssh_key_openssh_blob(ssh_key *key, BinarySink *bs)
+{ key->vt->openssh_blob(key, bs); }
+static inline char *ssh_key_cache_str(ssh_key *key)
+{ return key->vt->cache_str(key); }
+static inline int ssh_key_public_bits(const ssh_keyalg *self, ptrlen blob)
+{ return self->pubkey_bits(self, blob); }
+static inline const ssh_keyalg *ssh_key_alg(ssh_key *key)
+{ return key->vt; }
+static inline const char *ssh_key_ssh_id(ssh_key *key)
+{ return key->vt->ssh_id; }
+static inline const char *ssh_key_cache_id(ssh_key *key)
+{ return key->vt->cache_id; }
 
 /*
  * Enumeration of signature flags from draft-miller-ssh-agent-02
@@ -774,16 +831,30 @@ struct ssh_compression_alg {
     const char *text_name;
 };
 
-#define ssh_compressor_new(alg) ((alg)->compress_new())
-#define ssh_compressor_free(comp) ((comp)->vt->compress_free(comp))
-#define ssh_compressor_compress(comp, in, inlen, out, outlen, minlen) \
-    ((comp)->vt->compress(comp, in, inlen, out, outlen, minlen))
-#define ssh_compressor_alg(comp) ((comp)->vt)
-#define ssh_decompressor_new(alg) ((alg)->decompress_new())
-#define ssh_decompressor_free(comp) ((comp)->vt->decompress_free(comp))
-#define ssh_decompressor_decompress(comp, in, inlen, out, outlen) \
-    ((comp)->vt->decompress(comp, in, inlen, out, outlen))
-#define ssh_decompressor_alg(comp) ((comp)->vt)
+static inline ssh_compressor *ssh_compressor_new(
+    const ssh_compression_alg *alg)
+{ return alg->compress_new(); }
+static inline ssh_decompressor *ssh_decompressor_new(
+    const ssh_compression_alg *alg)
+{ return alg->decompress_new(); }
+static inline void ssh_compressor_free(ssh_compressor *c)
+{ c->vt->compress_free(c); }
+static inline void ssh_decompressor_free(ssh_decompressor *d)
+{ d->vt->decompress_free(d); }
+static inline void ssh_compressor_compress(
+    ssh_compressor *c, const unsigned char *block, int len,
+    unsigned char **outblock, int *outlen, int minlen)
+{ c->vt->compress(c, block, len, outblock, outlen, minlen); }
+static inline bool ssh_decompressor_decompress(
+    ssh_decompressor *d, const unsigned char *block, int len,
+    unsigned char **outblock, int *outlen)
+{ return d->vt->decompress(d, block, len, outblock, outlen); }
+static inline const ssh_compression_alg *ssh_compressor_alg(
+    ssh_compressor *c)
+{ return c->vt; }
+static inline const ssh_compression_alg *ssh_decompressor_alg(
+    ssh_decompressor *d)
+{ return d->vt; }
 
 struct ssh2_userkey {
     ssh_key *key;                      /* the key itself */
@@ -1171,7 +1242,7 @@ int eddsa_generate(struct eddsa_key *key, int bits, progfn_t pfn,
 mp_int *primegen(
     int bits, int modulus, int residue, mp_int *factor,
     int phase, progfn_t pfn, void *pfnparam, unsigned firstbits);
-void invent_firstbits(unsigned *one, unsigned *two);
+void invent_firstbits(unsigned *one, unsigned *two, unsigned min_separation);
 
 /*
  * Connection-sharing API provided by platforms. This function must

+ 1 - 0
source/putty/ssh1bpp.c

@@ -124,6 +124,7 @@ void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp)
                           s->bpp.input_eof);                            \
         if (!success)                                                   \
             goto eof;                                                   \
+        ssh_check_frozen(s->bpp.ssh);                                   \
     } while (0)
 
 static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)

+ 1 - 0
source/putty/ssh2bpp-bare.c

@@ -59,6 +59,7 @@ static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp)
                           s->bpp.input_eof);                            \
         if (!success)                                                   \
             goto eof;                                                   \
+        ssh_check_frozen(s->bpp.ssh);                                   \
     } while (0)
 
 static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)

+ 1 - 0
source/putty/ssh2bpp.c

@@ -269,6 +269,7 @@ static void ssh2_bpp_enable_pending_compression(struct ssh2_bpp_state *s)
                           s->bpp.input_eof);                            \
         if (!success)                                                   \
             goto eof;                                                   \
+        ssh_check_frozen(s->bpp.ssh);                                   \
     } while (0)
 
 #define userauth_range(pkttype) ((unsigned)((pkttype) - 50) < 20)

+ 1 - 0
source/putty/ssh2transhk.c

@@ -66,6 +66,7 @@ void ssh_transient_hostkey_cache_free(ssh_transient_hostkey_cache *thc)
         sfree(ent);
     }
     freetree234(thc->cache);
+    sfree(thc);
 }
 
 void ssh_transient_hostkey_cache_add(

+ 43 - 12
source/putty/ssh2transport.c

@@ -93,6 +93,9 @@ static void ssh2_transport_gss_update(struct ssh2_transport_state *s,
 
 static bool ssh2_transport_timer_update(struct ssh2_transport_state *s,
                                         unsigned long rekey_time);
+static int ssh2_transport_confirm_weak_crypto_primitive(
+    struct ssh2_transport_state *s, const char *type, const char *name,
+    const void *alg);
 
 static const char *const kexlist_descr[NKEXLIST] = {
     "key exchange algorithm",
@@ -105,6 +108,8 @@ static const char *const kexlist_descr[NKEXLIST] = {
     "server-to-client compression method"
 };
 
+static int weak_algorithm_compare(void *av, void *bv);
+
 PacketProtocolLayer *ssh2_transport_new(
     Conf *conf, const char *host, int port, const char *fullhostname,
     const char *client_greeting, const char *server_greeting,
@@ -156,6 +161,8 @@ PacketProtocolLayer *ssh2_transport_new(
         s->in.mkkey_adjust = 1;
     }
 
+    s->weak_algorithms_consented_to = newtree234(weak_algorithm_compare);
+
     ssh2_transport_set_max_data_size(s);
 
     return &s->ppl;
@@ -217,6 +224,8 @@ static void ssh2_transport_free(PacketProtocolLayer *ppl)
     strbuf_free(s->incoming_kexinit);
     ssh_transient_hostkey_cache_free(s->thc);
 
+    freetree234(s->weak_algorithms_consented_to);
+
     expire_timer_context(s);
     sfree(s);
 }
@@ -1126,9 +1135,8 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
     }
 
     if (s->warn_kex) {
-        s->dlgret = seat_confirm_weak_crypto_primitive(
-            s->ppl.seat, "key-exchange algorithm", s->kex_alg->name,
-            ssh2_transport_dialog_callback, s);
+        s->dlgret = ssh2_transport_confirm_weak_crypto_primitive(
+            s, "key-exchange algorithm", s->kex_alg->name, s->kex_alg);
         crMaybeWaitUntilV(s->dlgret >= 0);
         if (s->dlgret == 0) {
             ssh_user_close(s->ppl.ssh, "User aborted at kex warning");
@@ -1183,9 +1191,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
         } else {
                 /* If none exist, use the more general 'weak crypto'
                  * warning prompt */
-                s->dlgret = seat_confirm_weak_crypto_primitive(
-                    s->ppl.seat, "host key type", s->hostkey_alg->ssh_id,
-                    ssh2_transport_dialog_callback, s);
+                s->dlgret = ssh2_transport_confirm_weak_crypto_primitive(
+                    s, "host key type", s->hostkey_alg->ssh_id,
+                    s->hostkey_alg);
         }
         crMaybeWaitUntilV(s->dlgret >= 0);
         if (s->dlgret == 0) {
@@ -1195,9 +1203,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
     }
 
     if (s->warn_cscipher) {
-        s->dlgret = seat_confirm_weak_crypto_primitive(
-            s->ppl.seat, "client-to-server cipher", s->out.cipher->ssh2_id,
-            ssh2_transport_dialog_callback, s);
+        s->dlgret = ssh2_transport_confirm_weak_crypto_primitive(
+            s, "client-to-server cipher", s->out.cipher->ssh2_id,
+            s->out.cipher);
         crMaybeWaitUntilV(s->dlgret >= 0);
         if (s->dlgret == 0) {
             ssh_user_close(s->ppl.ssh, "User aborted at cipher warning");
@@ -1206,9 +1214,9 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
     }
 
     if (s->warn_sccipher) {
-        s->dlgret = seat_confirm_weak_crypto_primitive(
-            s->ppl.seat, "server-to-client cipher", s->in.cipher->ssh2_id,
-            ssh2_transport_dialog_callback, s);
+        s->dlgret = ssh2_transport_confirm_weak_crypto_primitive(
+            s, "server-to-client cipher", s->in.cipher->ssh2_id,
+            s->in.cipher);
         crMaybeWaitUntilV(s->dlgret >= 0);
         if (s->dlgret == 0) {
             ssh_user_close(s->ppl.ssh, "User aborted at cipher warning");
@@ -1972,3 +1980,26 @@ static void ssh2_transport_got_user_input(PacketProtocolLayer *ppl)
     /* Just delegate this to the higher layer */
     ssh_ppl_got_user_input(s->higher_layer);
 }
+
+static int weak_algorithm_compare(void *av, void *bv)
+{
+    uintptr_t a = (uintptr_t)av, b = (uintptr_t)bv;
+    return a < b ? -1 : a > b ? +1 : 0;
+}
+
+/*
+ * Wrapper on seat_confirm_weak_crypto_primitive(), which uses the
+ * tree234 s->weak_algorithms_consented_to to ensure we ask at most
+ * once about any given crypto primitive.
+ */
+static int ssh2_transport_confirm_weak_crypto_primitive(
+    struct ssh2_transport_state *s, const char *type, const char *name,
+    const void *alg)
+{
+    if (find234(s->weak_algorithms_consented_to, (void *)alg, NULL))
+        return 1;
+    add234(s->weak_algorithms_consented_to, (void *)alg);
+
+    return seat_confirm_weak_crypto_primitive(
+        s->ppl.seat, type, name, ssh2_transport_dialog_callback, s);
+}

+ 8 - 0
source/putty/ssh2transport.h

@@ -196,6 +196,14 @@ struct ssh2_transport_state {
     bool gss_delegate;
 #endif
 
+    /* List of crypto primitives below the warning threshold that the
+     * user has already clicked OK to, so that we don't keep asking
+     * about them again during rekeys. This directly stores pointers
+     * to the algorithm vtables, compared by pointer value (which is
+     * not a determinism hazard, because we're only using it as a
+     * set). */
+    tree234 *weak_algorithms_consented_to;
+
     /*
      * List of host key algorithms for which we _don't_ have a stored
      * host key. These are indices into the main hostkey_algs[] array

+ 9 - 5
source/putty/sshbpp.h

@@ -42,11 +42,15 @@ struct BinaryPacketProtocol {
     bool expect_close;
 };
 
-#define ssh_bpp_handle_input(bpp) ((bpp)->vt->handle_input(bpp))
-#define ssh_bpp_handle_output(bpp) ((bpp)->vt->handle_output(bpp))
-#define ssh_bpp_new_pktout(bpp, type) ((bpp)->vt->new_pktout(type))
-#define ssh_bpp_queue_disconnect(bpp, msg, cat) \
-    ((bpp)->vt->queue_disconnect(bpp, msg, cat))
+static inline void ssh_bpp_handle_input(BinaryPacketProtocol *bpp)
+{ bpp->vt->handle_input(bpp); }
+static inline void ssh_bpp_handle_output(BinaryPacketProtocol *bpp)
+{ bpp->vt->handle_output(bpp); }
+static inline PktOut *ssh_bpp_new_pktout(BinaryPacketProtocol *bpp, int type)
+{ return bpp->vt->new_pktout(type); }
+static inline void ssh_bpp_queue_disconnect(BinaryPacketProtocol *bpp,
+                                            const char *msg, int category)
+{ bpp->vt->queue_disconnect(bpp, msg, category); }
 
 /* ssh_bpp_free is more than just a macro wrapper on the vtable; it
  * does centralised parts of the freeing too. */

+ 113 - 75
source/putty/sshchan.h

@@ -62,43 +62,57 @@ struct Channel {
     unsigned initial_fixed_window_size;
 };
 
-#define chan_free(ch) ((ch)->vt->free(ch))
-#define chan_open_confirmation(ch) ((ch)->vt->open_confirmation(ch))
-#define chan_open_failed(ch, err) ((ch)->vt->open_failed(ch, err))
-#define chan_send(ch, err, buf, len) ((ch)->vt->send(ch, err, buf, len))
-#define chan_send_eof(ch) ((ch)->vt->send_eof(ch))
-#define chan_set_input_wanted(ch, wanted) \
-    ((ch)->vt->set_input_wanted(ch, wanted))
-#define chan_log_close_msg(ch) ((ch)->vt->log_close_msg(ch))
-#define chan_want_close(ch, leof, reof) ((ch)->vt->want_close(ch, leof, reof))
-#define chan_rcvd_exit_status(ch, status) \
-    ((ch)->vt->rcvd_exit_status(ch, status))
-#define chan_rcvd_exit_signal(ch, sig, core, msg)   \
-    ((ch)->vt->rcvd_exit_signal(ch, sig, core, msg))
-#define chan_rcvd_exit_signal_numeric(ch, sig, core, msg)   \
-    ((ch)->vt->rcvd_exit_signal_numeric(ch, sig, core, msg))
-#define chan_run_shell(ch) \
-    ((ch)->vt->run_shell(ch))
-#define chan_run_command(ch, cmd) \
-    ((ch)->vt->run_command(ch, cmd))
-#define chan_run_subsystem(ch, subsys) \
-    ((ch)->vt->run_subsystem(ch, subsys))
-#define chan_enable_x11_forwarding(ch, oneshot, ap, ad, scr) \
-    ((ch)->vt->enable_x11_forwarding(ch, oneshot, ap, ad, scr))
-#define chan_enable_agent_forwarding(ch) \
-    ((ch)->vt->enable_agent_forwarding(ch))
-#define chan_allocate_pty(ch, termtype, w, h, pw, ph, modes) \
-    ((ch)->vt->allocate_pty(ch, termtype, w, h, pw, ph, modes))
-#define chan_set_env(ch, var, value) \
-    ((ch)->vt->set_env(ch, var, value))
-#define chan_send_break(ch, length) \
-    ((ch)->vt->send_break(ch, length))
-#define chan_send_signal(ch, signame) \
-    ((ch)->vt->send_signal(ch, signame))
-#define chan_change_window_size(ch, w, h, pw, ph) \
-    ((ch)->vt->change_window_size(ch, w, h, pw, ph))
-#define chan_request_response(ch, success)   \
-    ((ch)->vt->request_response(ch, success))
+static inline void chan_free(Channel *ch)
+{ ch->vt->free(ch); }
+static inline void chan_open_confirmation(Channel *ch)
+{ ch->vt->open_confirmation(ch); }
+static inline void chan_open_failed(Channel *ch, const char *err)
+{ ch->vt->open_failed(ch, err); }
+static inline size_t chan_send(
+    Channel *ch, bool err, const void *buf, size_t len)
+{ return ch->vt->send(ch, err, buf, len); }
+static inline void chan_send_eof(Channel *ch)
+{ ch->vt->send_eof(ch); }
+static inline void chan_set_input_wanted(Channel *ch, bool wanted)
+{ ch->vt->set_input_wanted(ch, wanted); }
+static inline char *chan_log_close_msg(Channel *ch)
+{ return ch->vt->log_close_msg(ch); }
+static inline bool chan_want_close(Channel *ch, bool leof, bool reof)
+{ return ch->vt->want_close(ch, leof, reof); }
+static inline bool chan_rcvd_exit_status(Channel *ch, int status)
+{ return ch->vt->rcvd_exit_status(ch, status); }
+static inline bool chan_rcvd_exit_signal(
+        Channel *ch, ptrlen sig, bool core, ptrlen msg)
+{ return ch->vt->rcvd_exit_signal(ch, sig, core, msg); }
+static inline bool chan_rcvd_exit_signal_numeric(
+        Channel *ch, int sig, bool core, ptrlen msg)
+{ return ch->vt->rcvd_exit_signal_numeric(ch, sig, core, msg); }
+static inline bool chan_run_shell(Channel *ch)
+{ return ch->vt->run_shell(ch); }
+static inline bool chan_run_command(Channel *ch, ptrlen cmd)
+{ return ch->vt->run_command(ch, cmd); }
+static inline bool chan_run_subsystem(Channel *ch, ptrlen subsys)
+{ return ch->vt->run_subsystem(ch, subsys); }
+static inline bool chan_enable_x11_forwarding(
+    Channel *ch, bool oneshot, ptrlen ap, ptrlen ad, unsigned scr)
+{ return ch->vt->enable_x11_forwarding(ch, oneshot, ap, ad, scr); }
+static inline bool chan_enable_agent_forwarding(Channel *ch)
+{ return ch->vt->enable_agent_forwarding(ch); }
+static inline bool chan_allocate_pty(
+    Channel *ch, ptrlen termtype, unsigned w, unsigned h,
+    unsigned pw, unsigned ph, struct ssh_ttymodes modes)
+{ return ch->vt->allocate_pty(ch, termtype, w, h, pw, ph, modes); }
+static inline bool chan_set_env(Channel *ch, ptrlen var, ptrlen value)
+{ return ch->vt->set_env(ch, var, value); }
+static inline bool chan_send_break(Channel *ch, unsigned length)
+{ return ch->vt->send_break(ch, length); }
+static inline bool chan_send_signal(Channel *ch, ptrlen signame)
+{ return ch->vt->send_signal(ch, signame); }
+static inline bool chan_change_window_size(
+    Channel *ch, unsigned w, unsigned h, unsigned pw, unsigned ph)
+{ return ch->vt->change_window_size(ch, w, h, pw, ph); }
+static inline void chan_request_response(Channel *ch, bool success)
+{ ch->vt->request_response(ch, success); }
 
 /*
  * Reusable methods you can put in vtables to give default handling of
@@ -217,44 +231,68 @@ struct SshChannel {
     ConnectionLayer *cl;
 };
 
-#define sshfwd_write(c, buf, len) ((c)->vt->write(c, false, buf, len))
-#define sshfwd_write_ext(c, stderr, buf, len) \
-    ((c)->vt->write(c, stderr, buf, len))
-#define sshfwd_write_eof(c) ((c)->vt->write_eof(c))
-#define sshfwd_initiate_close(c, err) ((c)->vt->initiate_close(c, err))
-#define sshfwd_unthrottle(c, bufsize) ((c)->vt->unthrottle(c, bufsize))
-#define sshfwd_get_conf(c) ((c)->vt->get_conf(c))
-#define sshfwd_window_override_removed(c) ((c)->vt->window_override_removed(c))
-#define sshfwd_x11_sharing_handover(c, cs, ch, pa, pp, e, pmaj, pmin, d, l) \
-    ((c)->vt->x11_sharing_handover(c, cs, ch, pa, pp, e, pmaj, pmin, d, l))
-#define sshfwd_send_exit_status(c, status) \
-    ((c)->vt->send_exit_status(c, status))
-#define sshfwd_send_exit_signal(c, sig, core, msg) \
-    ((c)->vt->send_exit_signal(c, sig, core, msg))
-#define sshfwd_send_exit_signal_numeric(c, sig, core, msg) \
-    ((c)->vt->send_exit_signal_numeric(c, sig, core, msg))
-#define sshfwd_request_x11_forwarding(c, wr, ap, ad, scr, oneshot) \
-    ((c)->vt->request_x11_forwarding(c, wr, ap, ad, scr, oneshot))
-#define sshfwd_request_agent_forwarding(c, wr) \
-    ((c)->vt->request_agent_forwarding(c, wr))
-#define sshfwd_request_pty(c, wr, conf, w, h) \
-    ((c)->vt->request_pty(c, wr, conf, w, h))
-#define sshfwd_send_env_var(c, wr, var, value) \
-    ((c)->vt->send_env_var(c, wr, var, value))
-#define sshfwd_start_shell(c, wr) \
-    ((c)->vt->start_shell(c, wr))
-#define sshfwd_start_command(c, wr, cmd) \
-    ((c)->vt->start_command(c, wr, cmd))
-#define sshfwd_start_subsystem(c, wr, subsys) \
-    ((c)->vt->start_subsystem(c, wr, subsys))
-#define sshfwd_send_serial_break(c, wr, length) \
-    ((c)->vt->send_serial_break(c, wr, length))
-#define sshfwd_send_signal(c, wr, sig) \
-    ((c)->vt->send_signal(c, wr, sig))
-#define sshfwd_send_terminal_size_change(c, w, h) \
-    ((c)->vt->send_terminal_size_change(c, w, h))
-#define sshfwd_hint_channel_is_simple(c) \
-    ((c)->vt->hint_channel_is_simple(c))
+static inline size_t sshfwd_write_ext(
+    SshChannel *c, bool is_stderr, const void *data, size_t len)
+{ return c->vt->write(c, is_stderr, data, len); }
+static inline size_t sshfwd_write(SshChannel *c, const void *data, size_t len)
+{ return sshfwd_write_ext(c, false, data, len); }
+static inline void sshfwd_write_eof(SshChannel *c)
+{ c->vt->write_eof(c); }
+static inline void sshfwd_initiate_close(SshChannel *c, const char *err)
+{ c->vt->initiate_close(c, err); }
+static inline void sshfwd_unthrottle(SshChannel *c, size_t bufsize)
+{ c->vt->unthrottle(c, bufsize); }
+static inline Conf *sshfwd_get_conf(SshChannel *c)
+{ return c->vt->get_conf(c); }
+static inline void sshfwd_window_override_removed(SshChannel *c)
+{ c->vt->window_override_removed(c); }
+static inline void sshfwd_x11_sharing_handover(
+    SshChannel *c, ssh_sharing_connstate *cs, share_channel *sch,
+    const char *addr, int port, int endian, int maj, int min,
+    const void *idata, int ilen)
+{ return c->vt->x11_sharing_handover(c, cs, sch, addr, port, endian,
+                                     maj, min, idata, ilen); }
+static inline void sshfwd_send_exit_status(SshChannel *c, int status)
+{ c->vt->send_exit_status(c, status); }
+static inline void sshfwd_send_exit_signal(
+    SshChannel *c, ptrlen signame, bool core_dumped, ptrlen msg)
+{ c->vt->send_exit_signal(c, signame, core_dumped, msg); }
+static inline void sshfwd_send_exit_signal_numeric(
+    SshChannel *c, int signum, bool core_dumped, ptrlen msg)
+{ c->vt->send_exit_signal_numeric(c, signum, core_dumped, msg); }
+static inline void sshfwd_request_x11_forwarding(
+    SshChannel *c, bool want_reply, const char *proto,
+    const char *data, int scr, bool once)
+{ c->vt->request_x11_forwarding(c, want_reply, proto, data, scr, once); }
+static inline void sshfwd_request_agent_forwarding(
+    SshChannel *c, bool want_reply)
+{ c->vt->request_agent_forwarding(c, want_reply); }
+static inline void sshfwd_request_pty(
+    SshChannel *c, bool want_reply, Conf *conf, int w, int h)
+{ c->vt->request_pty(c, want_reply, conf, w, h); }
+static inline bool sshfwd_send_env_var(
+    SshChannel *c, bool want_reply, const char *var, const char *value)
+{ return c->vt->send_env_var(c, want_reply, var, value); }
+static inline void sshfwd_start_shell(
+    SshChannel *c, bool want_reply)
+{ c->vt->start_shell(c, want_reply); }
+static inline void sshfwd_start_command(
+    SshChannel *c, bool want_reply, const char *command)
+{ c->vt->start_command(c, want_reply, command); }
+static inline bool sshfwd_start_subsystem(
+    SshChannel *c, bool want_reply, const char *subsystem)
+{ return c->vt->start_subsystem(c, want_reply, subsystem); }
+static inline bool sshfwd_send_serial_break(
+    SshChannel *c, bool want_reply, int length)
+{ return c->vt->send_serial_break(c, want_reply, length); }
+static inline bool sshfwd_send_signal(
+    SshChannel *c, bool want_reply, const char *signame)
+{ return c->vt->send_signal(c, want_reply, signame); }
+static inline void sshfwd_send_terminal_size_change(
+    SshChannel *c, int w, int h)
+{ c->vt->send_terminal_size_change(c, w, h); }
+static inline void sshfwd_hint_channel_is_simple(SshChannel *c)
+{ c->vt->hint_channel_is_simple(c); }
 
 /* ----------------------------------------------------------------------
  * The 'main' or primary channel of the SSH connection is special,

+ 14 - 8
source/putty/sshppl.h

@@ -59,14 +59,20 @@ struct PacketProtocolLayer {
     unsigned remote_bugs;
 };
 
-#define ssh_ppl_process_queue(ppl) ((ppl)->vt->process_queue(ppl))
-#define ssh_ppl_get_specials(ppl, add, ctx) \
-    ((ppl)->vt->get_specials(ppl, add, ctx))
-#define ssh_ppl_special_cmd(ppl, code, arg) \
-    ((ppl)->vt->special_cmd(ppl, code, arg))
-#define ssh_ppl_want_user_input(ppl) ((ppl)->vt->want_user_input(ppl))
-#define ssh_ppl_got_user_input(ppl) ((ppl)->vt->got_user_input(ppl))
-#define ssh_ppl_reconfigure(ppl, conf) ((ppl)->vt->reconfigure(ppl, conf))
+static inline void ssh_ppl_process_queue(PacketProtocolLayer *ppl)
+{ ppl->vt->process_queue(ppl); }
+static inline bool ssh_ppl_get_specials(
+    PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx)
+{ return ppl->vt->get_specials(ppl, add_special, ctx); }
+static inline void ssh_ppl_special_cmd(
+    PacketProtocolLayer *ppl, SessionSpecialCode code, int arg)
+{ ppl->vt->special_cmd(ppl, code, arg); }
+static inline bool ssh_ppl_want_user_input(PacketProtocolLayer *ppl)
+{ return ppl->vt->want_user_input(ppl); }
+static inline void ssh_ppl_got_user_input(PacketProtocolLayer *ppl)
+{ ppl->vt->got_user_input(ppl); }
+static inline void ssh_ppl_reconfigure(PacketProtocolLayer *ppl, Conf *conf)
+{ ppl->vt->reconfigure(ppl, conf); }
 
 /* ssh_ppl_free is more than just a macro wrapper on the vtable; it
  * does centralised parts of the freeing too. */

+ 15 - 35
source/putty/sshpubk.c

@@ -490,32 +490,19 @@ static bool read_header(FILE * fp, char *header)
 
 static char *read_body(FILE * fp)
 {
-    char *text;
-    int len;
-    int size;
-    int c;
-
-    size = 128;
-    text = snewn(size, char);
-    len = 0;
-    text[len] = '\0';
+    strbuf *buf = strbuf_new();
 
     while (1) {
-	c = fgetc(fp);
+	int c = fgetc(fp);
 	if (c == '\r' || c == '\n' || c == EOF) {
 	    if (c != EOF) {
 		c = fgetc(fp);
 		if (c != '\r' && c != '\n')
 		    ungetc(c, fp);
 	    }
-	    return text;
-	}
-	if (len + 1 >= size) {
-	    size += 128;
-	    text = sresize(text, size, char);
+	    return strbuf_to_str(buf);
 	}
-	text[len++] = c;
-	text[len] = '\0';
+	put_byte(buf, c);
     }
 }
 
@@ -847,8 +834,7 @@ bool rfc4716_loadpub(FILE *fp, char **algorithm,
     const char *error;
     char *line, *colon, *value;
     char *comment = NULL;
-    unsigned char *pubblob = NULL;
-    int pubbloblen, pubblobsize;
+    strbuf *pubblob = NULL;
     char base64in[4];
     unsigned char base64out[3];
     int base64bytes;
@@ -909,9 +895,7 @@ bool rfc4716_loadpub(FILE *fp, char **algorithm,
      * Now line contains the initial line of base64 data. Loop round
      * while it still does contain base64.
      */
-    pubblobsize = 4096;
-    pubblob = snewn(pubblobsize, unsigned char);
-    pubbloblen = 0;
+    pubblob = strbuf_new();
     base64bytes = 0;
     while (line && line[0] != '-') {
         char *p;
@@ -919,12 +903,7 @@ bool rfc4716_loadpub(FILE *fp, char **algorithm,
             base64in[base64bytes++] = *p;
             if (base64bytes == 4) {
                 int n = base64_decode_atom(base64in, base64out);
-                if (pubbloblen + n > pubblobsize) {
-                    pubblobsize = (pubbloblen + n) * 5 / 4 + 1024;
-                    pubblob = sresize(pubblob, pubblobsize, unsigned char);
-                }
-                memcpy(pubblob + pubbloblen, base64out, n);
-                pubbloblen += n;
+                put_data(pubblob, base64out, n);
                 base64bytes = 0;
             }
         }
@@ -946,29 +925,30 @@ bool rfc4716_loadpub(FILE *fp, char **algorithm,
      * return the key algorithm string too, so look for that at the
      * start of the public blob.
      */
-    if (pubbloblen < 4) {
+    if (pubblob->len < 4) {
         error = "not enough data in SSH-2 public key file";
         goto error;
     }
-    alglen = toint(GET_32BIT_MSB_FIRST(pubblob));
-    if (alglen < 0 || alglen > pubbloblen-4) {
+    alglen = toint(GET_32BIT_MSB_FIRST(pubblob->u));
+    if (alglen < 0 || alglen > pubblob->len-4) {
         error = "invalid algorithm prefix in SSH-2 public key file";
         goto error;
     }
     if (algorithm)
-        *algorithm = dupprintf("%.*s", alglen, pubblob+4);
+        *algorithm = dupprintf("%.*s", alglen, pubblob->s+4);
     if (commentptr)
         *commentptr = comment;
     else
         sfree(comment);
-    put_data(bs, pubblob, pubbloblen);
-    sfree(pubblob);
+    put_datapl(bs, ptrlen_from_strbuf(pubblob));
+    strbuf_free(pubblob);
     return true;
 
   error:
     sfree(line);
     sfree(comment);
-    sfree(pubblob);
+    if (pubblob)
+        strbuf_free(pubblob);
     if (errorstr)
         *errorstr = error;
     return false;

+ 1 - 0
source/putty/sshrsa.c

@@ -332,6 +332,7 @@ bool rsa_verify(RSAKey *key)
     mp_int *q_new = mp_min(key->p, key->q);
     mp_free(key->p);
     mp_free(key->q);
+    mp_free(key->iqmp);
     key->p = p_new;
     key->q = q_new;
     key->iqmp = mp_invert(key->q, key->p);

+ 3 - 3
source/putty/sshshare.c

@@ -138,6 +138,7 @@
 #include "putty.h"
 #include "tree234.h"
 #include "ssh.h"
+#include "sshcr.h"
 
 struct ssh_sharing_state {
     char *sockname;                  /* the socket name, kept for cleanup */
@@ -1741,10 +1742,9 @@ static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs,
 }
 
 /*
- * Coroutine macros similar to, but simplified from, those in ssh.c.
+ * An extra coroutine macro, specific to this code which is consuming
+ * 'const char *data'.
  */
-#define crBegin(v)	{ int *crLine = &v; switch(v) { case 0:;
-#define crFinishV	} *crLine = 0; return; }
 #define crGetChar(c) do                                         \
     {                                                           \
         while (len == 0) {                                      \

+ 19 - 25
source/putty/sshverstring.c

@@ -28,8 +28,7 @@ struct ssh_verstring_state {
     int remote_bugs;
     char prefix[PREFIX_MAXLEN];
     char *impl_name;
-    char *vstring;
-    size_t vslen, vstrsize;
+    strbuf *vstring;
     char *protoversion;
     const char *softwareversion;
 
@@ -93,6 +92,7 @@ BinaryPacketProtocol *ssh_verstring_new(
     s->our_protoversion = dupstr(protoversion);
     s->receiver = rcv;
     s->impl_name = dupstr(impl_name);
+    s->vstring = strbuf_new();
 
     /*
      * We send our version string early if we can. But if it includes
@@ -114,7 +114,7 @@ void ssh_verstring_free(BinaryPacketProtocol *bpp)
         container_of(bpp, struct ssh_verstring_state, bpp);
     conf_free(s->conf);
     sfree(s->impl_name);
-    sfree(s->vstring);
+    strbuf_free(s->vstring);
     sfree(s->protoversion);
     sfree(s->our_vstring);
     sfree(s->our_protoversion);
@@ -242,6 +242,7 @@ void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
         bufchain_fetch(s->bpp.in_raw, s->prefix, s->prefix_wanted.len);
         if (!memcmp(s->prefix, s->prefix_wanted.ptr, s->prefix_wanted.len)) {
             bufchain_consume(s->bpp.in_raw, s->prefix_wanted.len);
+            ssh_check_frozen(s->bpp.ssh);
             break;
         }
 
@@ -258,9 +259,11 @@ void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
             data = bufchain_prefix(s->bpp.in_raw);
             if ((nl = memchr(data.ptr, '\012', data.len)) != NULL) {
                 bufchain_consume(s->bpp.in_raw, nl - (char *)data.ptr + 1);
+                ssh_check_frozen(s->bpp.ssh);
                 break;
             } else {
                 bufchain_consume(s->bpp.in_raw, data.len);
+                ssh_check_frozen(s->bpp.ssh);
             }
         }
     }
@@ -268,12 +271,9 @@ void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
     s->found_prefix = true;
 
     /*
-     * Start a buffer to store the full greeting line.
+     * Copy the greeting line so far into vstring.
      */
-    s->vstrsize = s->prefix_wanted.len + 16;
-    s->vstring = snewn(s->vstrsize, char);
-    memcpy(s->vstring, s->prefix_wanted.ptr, s->prefix_wanted.len);
-    s->vslen = s->prefix_wanted.len;
+    put_data(s->vstring, s->prefix_wanted.ptr, s->prefix_wanted.len);
 
     /*
      * Now read the rest of the greeting line.
@@ -289,29 +289,23 @@ void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
             data.len = nl - (char *)data.ptr + 1;
         }
 
-        if (s->vslen >= s->vstrsize - 1 ||
-            data.len >= s->vstrsize - 1 - s->vslen) {
-            s->vstrsize = (s->vslen + data.len) * 5 / 4 + 32;
-            s->vstring = sresize(s->vstring, s->vstrsize, char);
-        }
-
-        memcpy(s->vstring + s->vslen, data.ptr, data.len);
-        s->vslen += data.len;
+        put_datapl(s->vstring, data);
         bufchain_consume(s->bpp.in_raw, data.len);
+        ssh_check_frozen(s->bpp.ssh);
 
-    } while (s->vstring[s->vslen-1] != '\012');
+    } while (s->vstring->s[s->vstring->len-1] != '\012');
 
     /*
      * Trim \r and \n from the version string, and replace them with
      * a NUL terminator.
      */
-    while (s->vslen > 0 &&
-           (s->vstring[s->vslen-1] == '\r' ||
-            s->vstring[s->vslen-1] == '\n'))
-        s->vslen--;
-    s->vstring[s->vslen] = '\0';
+    while (s->vstring->len > 0 &&
+           (s->vstring->s[s->vstring->len-1] == '\r' ||
+            s->vstring->s[s->vstring->len-1] == '\n'))
+        s->vstring->len--;
+    s->vstring->s[s->vstring->len] = '\0';
 
-    bpp_logevent("Remote version: %s", s->vstring);
+    bpp_logevent("Remote version: %s", s->vstring->s);
 
     /*
      * Pick out the protocol version and software version. The former
@@ -320,7 +314,7 @@ void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
      * tail of s->vstring, so it doesn't need to be allocated.
      */
     {
-        const char *pv_start = s->vstring + s->prefix_wanted.len;
+        const char *pv_start = s->vstring->s + s->prefix_wanted.len;
         int pv_len = strcspn(pv_start, "-");
         s->protoversion = dupprintf("%.*s", pv_len, pv_start);
         s->softwareversion = pv_start + pv_len;
@@ -610,7 +604,7 @@ const char *ssh_verstring_get_remote(BinaryPacketProtocol *bpp)
 {
     struct ssh_verstring_state *s =
         container_of(bpp, struct ssh_verstring_state, bpp);
-    return s->vstring;
+    return s->vstring->s;
 }
 
 const char *ssh_verstring_get_local(BinaryPacketProtocol *bpp)

+ 24 - 27
source/putty/sshzlib.c

@@ -342,8 +342,7 @@ static void lz77_compress(struct LZ77Context *ctx,
  */
 
 struct Outbuf {
-    unsigned char *outbuf;
-    int outlen, outsize;
+    strbuf *outbuf;
     unsigned long outbits;
     int noutbits;
     bool firstblock;
@@ -355,11 +354,7 @@ static void outbits(struct Outbuf *out, unsigned long bits, int nbits)
     out->outbits |= bits << out->noutbits;
     out->noutbits += nbits;
     while (out->noutbits >= 8) {
-	if (out->outlen >= out->outsize) {
-	    out->outsize = out->outlen + 64;
-	    out->outbuf = sresize(out->outbuf, out->outsize, unsigned char);
-	}
-	out->outbuf[out->outlen++] = (unsigned char) (out->outbits & 0xFF);
+        put_byte(out->outbuf, out->outbits & 0xFF);
 	out->outbits >>= 8;
 	out->noutbits -= 8;
     }
@@ -589,6 +584,7 @@ ssh_compressor *zlib_compress_init(void)
     comp->ectx.match = zlib_match;
 
     out = snew(struct Outbuf);
+    out->outbuf = NULL;
     out->outbits = out->noutbits = 0;
     out->firstblock = true;
     comp->ectx.userdata = out;
@@ -600,7 +596,10 @@ void zlib_compress_cleanup(ssh_compressor *sc)
 {
     struct ssh_zlib_compressor *comp =
         container_of(sc, struct ssh_zlib_compressor, sc);
-    sfree(comp->ectx.userdata);
+    struct Outbuf *out = (struct Outbuf *)comp->ectx.userdata;
+    if (out->outbuf)
+        strbuf_free(out->outbuf);
+    sfree(out);
     sfree(comp->ectx.ictx);
     sfree(comp);
 }
@@ -615,8 +614,8 @@ void zlib_compress_block(ssh_compressor *sc,
     struct Outbuf *out = (struct Outbuf *) comp->ectx.userdata;
     bool in_block;
 
-    out->outbuf = NULL;
-    out->outlen = out->outsize = 0;
+    assert(!out->outbuf);
+    out->outbuf = strbuf_new();
 
     /*
      * If this is the first block, output the Zlib (RFC1950) header
@@ -678,13 +677,14 @@ void zlib_compress_block(ssh_compressor *sc,
      * at least a given length, do so by emitting further empty static
      * blocks.
      */
-    while (out->outlen < minlen) {
+    while (out->outbuf->len < minlen) {
         outbits(out, 0, 7);	       /* close block */
         outbits(out, 2, 3);	       /* open new static block */
     }
 
-    *outblock = out->outbuf;
-    *outlen = out->outlen;
+    *outlen = out->outbuf->len;
+    *outblock = (unsigned char *)strbuf_to_str(out->outbuf);
+    out->outbuf = NULL;
 }
 
 /* ----------------------------------------------------------------------
@@ -855,8 +855,7 @@ struct zlib_decompress_ctx {
     int nbits;
     unsigned char window[WINSIZE];
     int winpos;
-    unsigned char *outblk;
-    int outlen, outsize;
+    strbuf *outblk;
 
     ssh_decompressor dc;
 };
@@ -878,6 +877,7 @@ ssh_decompressor *zlib_decompress_init(void)
     dctx->bits = 0;
     dctx->nbits = 0;
     dctx->winpos = 0;
+    dctx->outblk = NULL;
 
     dctx->dc.vt = &ssh_zlib;
     return &dctx->dc;
@@ -896,6 +896,8 @@ void zlib_decompress_cleanup(ssh_decompressor *dc)
 	zlib_freetable(&dctx->lenlentable);
     zlib_freetable(&dctx->staticlentable);
     zlib_freetable(&dctx->staticdisttable);
+    if (dctx->outblk)
+        strbuf_free(dctx->outblk);
     sfree(dctx);
 }
 
@@ -935,11 +937,7 @@ static void zlib_emit_char(struct zlib_decompress_ctx *dctx, int c)
 {
     dctx->window[dctx->winpos] = c;
     dctx->winpos = (dctx->winpos + 1) & (WINSIZE - 1);
-    if (dctx->outlen >= dctx->outsize) {
-	dctx->outsize = dctx->outlen + 512;
-	dctx->outblk = sresize(dctx->outblk, dctx->outsize, unsigned char);
-    }
-    dctx->outblk[dctx->outlen++] = c;
+    put_byte(dctx->outblk, c);
 }
 
 #define EATBITS(n) ( dctx->nbits -= (n), dctx->bits >>= (n) )
@@ -956,9 +954,8 @@ bool zlib_decompress_block(ssh_decompressor *dc,
 	16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
     };
 
-    dctx->outblk = snewn(256, unsigned char);
-    dctx->outsize = 256;
-    dctx->outlen = 0;
+    assert(!dctx->outblk);
+    dctx->outblk = strbuf_new();
 
     while (len > 0 || dctx->nbits > 0) {
 	while (dctx->nbits < 24 && len > 0) {
@@ -1185,13 +1182,13 @@ bool zlib_decompress_block(ssh_decompressor *dc,
     }
 
   finished:
-    *outblock = dctx->outblk;
-    *outlen = dctx->outlen;
+    *outlen = dctx->outblk->len;
+    *outblock = (unsigned char *)strbuf_to_str(dctx->outblk);
+    dctx->outblk = NULL;
     return true;
 
   decode_error:
-    sfree(dctx->outblk);
-    *outblock = dctx->outblk = NULL;
+    *outblock = NULL;
     *outlen = 0;
     return false;
 }

+ 12 - 0
source/putty/windows/winhandl.c

@@ -715,3 +715,15 @@ void *handle_get_privdata(struct handle *h)
 {
     return h->u.g.privdata;
 }
+
+static void handle_sink_write(BinarySink *bs, const void *data, size_t len)
+{
+    handle_sink *sink = BinarySink_DOWNCAST(bs, handle_sink);
+    handle_write(sink->h, data, len);
+}
+
+void handle_sink_init(handle_sink *sink, struct handle *h)
+{
+    sink->h = h;
+    BinarySink_INIT(sink, handle_sink_write);
+}

+ 7 - 2
source/putty/windows/winstore.c

@@ -113,6 +113,9 @@ settings_r *open_settings_r(const char *sessionname)
 
     strbuf_free(sb);
 
+    if (!sesskey)
+        return NULL;
+
     settings_r *toret = snew(settings_r);
     toret->sesskey = sesskey;
     return toret;
@@ -237,8 +240,10 @@ void write_setting_filename(settings_w *handle,
 
 void close_settings_r(settings_r *handle)
 {
-    RegCloseKey(handle->sesskey);
-    sfree(handle);
+    if (handle) {
+        RegCloseKey(handle->sesskey);
+        sfree(handle);
+    }
 }
 
 void del_settings(const char *sessionname)

+ 14 - 1
source/putty/windows/winstuff.h

@@ -24,6 +24,7 @@
 #endif
 
 #include "defs.h"
+#include "marshal.h"
 
 #include "tree234.h"
 
@@ -40,7 +41,11 @@
 struct Filename {
     char *path;
 };
-#define f_open(filename, mode, isprivate) ( fopen((filename)->path, (mode)) )
+static inline FILE *f_open(const Filename *filename, const char *mode,
+                           bool isprivate)
+{
+    return fopen(filename->path, mode);
+}
 
 struct FontSpec {
     char *name;
@@ -568,6 +573,8 @@ GLOBAL bool restricted_acl;
 void escape_registry_key(const char *in, strbuf *out);
 void unescape_registry_key(const char *in, strbuf *out);
 
+bool is_console_handle(HANDLE);
+
 /* A few pieces of up-to-date Windows API definition needed for older
  * compilers. */
 #ifndef LOAD_LIBRARY_SEARCH_SYSTEM32
@@ -621,6 +628,12 @@ size_t handle_backlog(struct handle *h);
 void *handle_get_privdata(struct handle *h);
 struct handle *handle_add_foreign_event(HANDLE event,
                                         void (*callback)(void *), void *ctx);
+/* Analogue of stdio_sink in marshal.h, for a Windows handle */
+struct handle_sink {
+    struct handle *h;
+    BinarySink_IMPLEMENTATION;
+};
+void handle_sink_init(handle_sink *sink, struct handle *h);
 
 /*
  * winpgntc.c needs to schedule callbacks for asynchronous agent