浏览代码

Merge branch 'thirdparty_dev' into dev

# Conflicts:
#	source/putty/WINDOWS/wingss.c
#	source/putty/WINDOWS/winstuff.h

#	source/putty/mpint.c
#	source/putty/putty.h
#	source/putty/ssh1login.c
#	source/putty/ssh2connection-client.c
#	source/putty/ssh2connection.c
#	source/putty/ssh2transport.c
#	source/putty/ssh2userauth.c
#	source/putty/sshcommon.c
#	source/putty/sshppl.h
#	source/putty/sshprng.c

Source commit: 24168fcf2b0bb08146700b21c2559fcc06115476
Martin Prikryl 5 年之前
父节点
当前提交
07fb20a32d

+ 30 - 3
source/putty/defs.h

@@ -15,20 +15,47 @@
 #include <stdint.h>
 #include <stdbool.h>
 
-#ifndef WINSCP
-// This is used in pageant, pscp, psftp and servers only
-#if defined _MSC_VER && _MSC_VER < 1800
+#if (!defined WINSCP) && defined _MSC_VER && _MSC_VER < 1800
 /* Work around lack of inttypes.h and strtoumax in older MSVC */
 #define PRIx32 "x"
 #define PRIu64 "I64u"
 #define PRIdMAX "I64d"
 #define PRIXMAX "I64X"
 #define SCNu64 "I64u"
+#define SIZEx "Ix"
+#define SIZEu "Iu"
 uintmax_t strtoumax(const char *nptr, char **endptr, int base);
 #else
+#ifndef WINSCP
+// Not needed by the code WinSCP uses
 #include <inttypes.h>
 #endif
+/* Because we still support older MSVC libraries which don't recognise the
+ * standard C "z" modifier for size_t-sized integers, we must use an
+ * inttypes.h-style macro for those */
+#define SIZEx "zx"
+#define SIZEu "zu"
+#endif
+
+#if defined __GNUC__ || defined __clang__
+/*
+ * On MinGW, the correct compiler format checking for vsnprintf() etc
+ * can depend on compile-time flags; these control whether you get
+ * ISO C or Microsoft's non-standard format strings.
+ * We sometimes use __attribute__ ((format)) for our own printf-like
+ * functions, which are ultimately interpreted by the toolchain-chosen
+ * printf, so we need to take that into account to get correct warnings.
+ */
+#ifdef __MINGW_PRINTF_FORMAT
+#define PRINTF_LIKE(fmt_index, ellipsis_index) \
+    __attribute__ ((format (__MINGW_PRINTF_FORMAT, fmt_index, ellipsis_index)))
+#else
+#define PRINTF_LIKE(fmt_index, ellipsis_index) \
+    __attribute__ ((format (printf, fmt_index, ellipsis_index)))
 #endif
+#else /* __GNUC__ */
+#define PRINTF_LIKE(fmt_index, ellipsis_index)
+#endif /* __GNUC__ */
 
 typedef struct conf_tag Conf;
 typedef struct terminal_tag Terminal;

+ 3 - 1
source/putty/import.c

@@ -538,8 +538,10 @@ static ssh2_userkey *openssh_pem_read(
     strbuf *blob = strbuf_new_nm();
     int privptr = 0, publen;
 
-    if (!key)
+    if (!key) {
+        strbuf_free(blob);
         return NULL;
+    }
 
     if (key->encrypted) {
         unsigned char keybuf[32];

+ 5 - 4
source/putty/logging.c

@@ -58,7 +58,7 @@ static void logwrite(LogContext *ctx, ptrlen data)
  * Convenience wrapper on logwrite() which printf-formats the
  * string.
  */
-static void logprintf(LogContext *ctx, const char *fmt, ...)
+static PRINTF_LIKE(2, 3) void logprintf(LogContext *ctx, const char *fmt, ...)
 {
     va_list ap;
     char *data;
@@ -344,7 +344,7 @@ void log_packet(LogContext *ctx, int direction, int type,
         /* If we're about to stop omitting, it's time to say how
          * much we omitted. */
         if ((blktype != PKTLOG_OMIT) && omitted) {
-            logprintf(ctx, "  (%d byte%s omitted)\r\n",
+            logprintf(ctx, "  (%"SIZEu" byte%s omitted)\r\n",
                       omitted, (omitted==1?"":"s"));
             omitted = 0;
         }
@@ -352,7 +352,8 @@ void log_packet(LogContext *ctx, int direction, int type,
         /* (Re-)initialise dumpdata as necessary
          * (start of row, or if we've just stopped omitting) */
         if (!output_pos && !omitted)
-            sprintf(dumpdata, "  %08zx%*s\r\n", p-(p%16), 1+3*16+2+16, "");
+            sprintf(dumpdata, "  %08"SIZEx"%*s\r\n",
+                    p-(p%16), 1+3*16+2+16, "");
 
         /* Deal with the current byte. */
         if (blktype == PKTLOG_OMIT) {
@@ -387,7 +388,7 @@ void log_packet(LogContext *ctx, int direction, int type,
 
     /* Tidy up */
     if (omitted)
-        logprintf(ctx, "  (%d byte%s omitted)\r\n",
+        logprintf(ctx, "  (%"SIZEu" byte%s omitted)\r\n",
                   omitted, (omitted==1?"":"s"));
     logflush(ctx);
 }

+ 11 - 0
source/putty/marshal.c

@@ -235,6 +235,17 @@ ptrlen BinarySource_get_pstring(BinarySource *src)
     return make_ptrlen(consume(len), len);
 }
 
+void BinarySource_REWIND_TO__(BinarySource *src, size_t pos)
+{
+    if (pos <= src->len) {
+        src->pos = pos;
+        src->err = BSE_NO_ERROR;    /* clear any existing error */
+    } else {
+        src->pos = src->len;
+        src->err = BSE_OUT_OF_DATA; /* new error if we rewind out of range */
+    }
+}
+
 static void stdio_sink_write(BinarySink *bs, const void *data, size_t len)
 {
     stdio_sink *sink = BinarySink_DOWNCAST(bs, stdio_sink);

+ 8 - 0
source/putty/marshal.h

@@ -255,6 +255,10 @@ static inline void BinarySource_INIT__(BinarySource *src, ptrlen data)
               (object)->binarysource_)
 #define BinarySource_COPIED(obj) \
     ((obj)->binarysource_->binarysource_ = (obj)->binarysource_)
+#define BinarySource_REWIND_TO(src, pos) \
+    BinarySource_REWIND_TO__((src)->binarysource_, pos)
+#define BinarySource_REWIND(src) \
+    BinarySource_REWIND_TO__((src)->binarysource_, 0)
 
 #define get_data(src, len) \
     BinarySource_get_data(BinarySource_UPCAST(src), len)
@@ -282,6 +286,8 @@ static inline void BinarySource_INIT__(BinarySource *src, ptrlen data)
     BinarySource_get_rsa_ssh1_pub(BinarySource_UPCAST(src), rsa, order)
 #define get_rsa_ssh1_priv(src, rsa) \
     BinarySource_get_rsa_ssh1_priv(BinarySource_UPCAST(src), rsa)
+#define get_rsa_ssh1_priv_agent(src) \
+    BinarySource_get_rsa_ssh1_priv_agent(BinarySource_UPCAST(src))
 
 #define get_err(src) (BinarySource_UPCAST(src)->err)
 #define get_avail(src) (BinarySource_UPCAST(src)->len - \
@@ -303,6 +309,8 @@ ptrlen BinarySource_get_pstring(BinarySource *);
 mp_int *BinarySource_get_mp_ssh1(BinarySource *src);
 mp_int *BinarySource_get_mp_ssh2(BinarySource *src);
 
+void BinarySource_REWIND_TO__(BinarySource *src, size_t pos);
+
 /*
  * A couple of useful standard BinarySink implementations, which live
  * as sensibly here as anywhere else: one that makes a BinarySink

+ 5 - 3
source/putty/memory.c

@@ -130,9 +130,11 @@ void *safegrowarray(void *ptr, size_t *allocated, size_t eltsize,
     void *toret;
     if (secret) {
         toret = safemalloc(newsize, eltsize, 0);
-        memcpy(toret, ptr, oldsize * eltsize);
-        smemclr(ptr, oldsize * eltsize);
-        sfree(ptr);
+        if (oldsize) {
+            memcpy(toret, ptr, oldsize * eltsize);
+            smemclr(ptr, oldsize * eltsize);
+            sfree(ptr);
+        }
     } else {
         toret = saferealloc(ptr, newsize, eltsize);
     }

+ 19 - 29
source/putty/misc.c

@@ -51,43 +51,29 @@ void add_prompt(prompts_t *p, char *promptstr, bool echo)
     prompt_t *pr = snew(prompt_t);
     pr->prompt = promptstr;
     pr->echo = echo;
-    pr->result = NULL;
-    pr->resultsize = 0;
+    pr->result = strbuf_new_nm();
     sgrowarray(p->prompts, p->prompts_size, p->n_prompts);
     p->prompts[p->n_prompts++] = pr;
 }
-void prompt_ensure_result_size(prompt_t *pr, int newlen)
+void prompt_set_result(prompt_t *pr, const char *newstr)
 {
-    if ((int)pr->resultsize < newlen) {
-        char *newbuf;
-        newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */
-
-        /*
-         * We don't use sresize / realloc here, because we will be
-         * storing sensitive stuff like passwords in here, and we want
-         * to make sure that the data doesn't get copied around in
-         * memory without the old copy being destroyed.
-         */
-        newbuf = snewn(newlen, char);
-        memcpy(newbuf, pr->result, pr->resultsize);
-        smemclr(pr->result, pr->resultsize);
-        sfree(pr->result);
-        pr->result = newbuf;
-        pr->resultsize = newlen;
-    }
+    strbuf_clear(pr->result);
+    put_datapl(pr->result, ptrlen_from_asciz(newstr));
 }
-void prompt_set_result(prompt_t *pr, const char *newstr)
+const char *prompt_get_result_ref(prompt_t *pr)
+{
+    return pr->result->s;
+}
+char *prompt_get_result(prompt_t *pr)
 {
-    prompt_ensure_result_size(pr, strlen(newstr) + 1);
-    strcpy(pr->result, newstr);
+    return dupstr(pr->result->s);
 }
 void free_prompts(prompts_t *p)
 {
     size_t i;
     for (i=0; i < p->n_prompts; i++) {
         prompt_t *pr = p->prompts[i];
-        smemclr(pr->result, pr->resultsize); /* burn the evidence */
-        sfree(pr->result);
+        strbuf_free(pr->result);
         sfree(pr->prompt);
         sfree(pr);
     }
@@ -238,22 +224,26 @@ char *buildinfo(const char *newline)
 #else
     strbuf_catf(buf, ", emulating ");
 #endif
-    strbuf_catf(buf, "Visual Studio", newline);
+    strbuf_catf(buf, "Visual Studio");
 
 #if 0
     /*
      * List of _MSC_VER values and their translations taken from
      * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
-     * except for 1920, which is not yet listed on that page as of
-     * 2019-03-22, and was determined experimentally by Sean Kain.
      *
      * The pointless #if 0 branch containing this comment is there so
      * that every real clause can start with #elif and there's no
      * anomalous first clause. That way the patch looks nicer when you
      * add extra ones.
      */
+#elif _MSC_VER == 1923
+    strbuf_catf(buf, " 2019 (16.3)");
+#elif _MSC_VER == 1922
+    strbuf_catf(buf, " 2019 (16.2)");
+#elif _MSC_VER == 1921
+    strbuf_catf(buf, " 2019 (16.1)");
 #elif _MSC_VER == 1920
-    strbuf_catf(buf, " 2019 (16.x)");
+    strbuf_catf(buf, " 2019 (16.0)");
 #elif _MSC_VER == 1916
     strbuf_catf(buf, " 2017 version 15.9");
 #elif _MSC_VER == 1915

+ 9 - 24
source/putty/misc.h

@@ -24,29 +24,10 @@ char *host_strchr(const char *s, int c);
 char *host_strrchr(const char *s, int c);
 char *host_strduptrim(const char *s);
 
-#ifdef __GNUC__
-/*
- * On MinGW, the correct compiler format checking for vsnprintf() etc
- * can depend on compile-time flags; these control whether you get
- * ISO C or Microsoft's non-standard format strings.
- * We sometimes use __attribute__ ((format)) for our own printf-like
- * functions, which are ultimately interpreted by the toolchain-chosen
- * printf, so we need to take that into account to get correct warnings.
- */
-#ifdef __MINGW_PRINTF_FORMAT
-#define PUTTY_PRINTF_ARCHETYPE __MINGW_PRINTF_FORMAT
-#else
-#define PUTTY_PRINTF_ARCHETYPE printf
-#endif
-#endif /* __GNUC__ */
-
 char *dupstr(const char *s);
-char *dupcat(const char *s1, ...);
-char *dupprintf(const char *fmt, ...)
-#ifdef __GNUC__
-    __attribute__ ((format (PUTTY_PRINTF_ARCHETYPE, 1, 2)))
-#endif
-    ;
+char *dupcat_fn(const char *s1, ...);
+#define dupcat(...) dupcat_fn(__VA_ARGS__, (const char *)NULL)
+char *dupprintf(const char *fmt, ...) PRINTF_LIKE(1, 2);
 char *dupvprintf(const char *fmt, va_list ap);
 void burnstr(char *string);
 
@@ -72,9 +53,13 @@ strbuf *strbuf_new_nm(void);
 
 void strbuf_free(strbuf *buf);
 void *strbuf_append(strbuf *buf, size_t len);
+void strbuf_shrink_to(strbuf *buf, size_t new_len);
+void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove);
 char *strbuf_to_str(strbuf *buf); /* does free buf, but you must free result */
-void strbuf_catf(strbuf *buf, const char *fmt, ...);
+void strbuf_catf(strbuf *buf, const char *fmt, ...) PRINTF_LIKE(2, 3);
 void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap);
+static inline void strbuf_clear(strbuf *buf) { strbuf_shrink_to(buf, 0); }
+bool strbuf_chomp(strbuf *buf, char char_to_remove);
 
 strbuf *strbuf_new_for_agent_query(void);
 void strbuf_finalise_agent_query(strbuf *buf);
@@ -263,7 +248,7 @@ static inline NORETURN void unreachable_internal(void) {
  */
 
 #ifdef DEBUG
-void debug_printf(const char *fmt, ...);
+void debug_printf(const char *fmt, ...) PRINTF_LIKE(1, 2);
 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))

+ 51 - 20
source/putty/mpint.c

@@ -35,6 +35,35 @@ static inline BignumInt mp_word(mp_int *x, size_t i)
     return i < x->nw ? x->w[i] : 0;
 }
 
+/*
+ * Shift an ordinary C integer by BIGNUM_INT_BITS, in a way that
+ * avoids writing a shift operator whose RHS is greater or equal to
+ * the size of the type, because that's undefined behaviour in C.
+ *
+ * In fact we must avoid even writing it in a definitely-untaken
+ * branch of an if, because compilers will sometimes warn about
+ * that. So you can't just write 'shift too big ? 0 : n >> shift',
+ * because even if 'shift too big' is a constant-expression
+ * evaluating to false, you can still get complaints about the
+ * else clause of the ?:.
+ *
+ * So we have to re-check _inside_ that clause, so that the shift
+ * count is reset to something nonsensical but safe in the case
+ * where the clause wasn't going to be taken anyway.
+ */
+static uintmax_t shift_right_by_one_word(uintmax_t n)
+{
+    bool shift_too_big = BIGNUM_INT_BYTES >= sizeof(n);
+    return shift_too_big ? 0 :
+        n >> (shift_too_big ? 0 : BIGNUM_INT_BITS);
+}
+static uintmax_t shift_left_by_one_word(uintmax_t n)
+{
+    bool shift_too_big = BIGNUM_INT_BYTES >= sizeof(n);
+    return shift_too_big ? 0 :
+        n << (shift_too_big ? 0 : BIGNUM_INT_BITS);
+}
+
 static mp_int *mp_make_sized(size_t nw)
 {
     mp_int *x = snew_plus(mp_int, nw * sizeof(BignumInt));
@@ -276,13 +305,9 @@ unsigned mp_get_bit(mp_int *x, size_t bit)
 uintmax_t mp_get_integer(mp_int *x)
 {
     uintmax_t toret = 0;
-    size_t i; // WINSCP
-    for (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];
-    }
+    size_t i; // WINSCP 
+    for (i = x->nw; i-- > 0 ;)
+        toret = shift_left_by_one_word(toret) | x->w[i];
     return toret;
 }
 
@@ -803,13 +828,15 @@ static BignumCarry mp_add_masked_integer_into(
     size_t i; // WINSCP
     for (i = 0; i < rw; i++) {
         BignumInt aword = mp_word(a, i);
-        size_t shift = i * BIGNUM_INT_BITS;
-        BignumInt bword = shift < BIGNUM_INT_BYTES ? b >> shift : 0;
+        BignumInt bword = b;
+        b = shift_right_by_one_word(b);
+        { // WINSCP
         BignumInt out;
         bword = (bword ^ b_xor) & b_and;
         BignumADC(out, carry, aword, bword, carry);
         if (w_out)
             w_out[i] = out;
+        } // WINSCP
     }
     return carry;
 }
@@ -847,7 +874,7 @@ static void mp_add_integer_into_shifted_by_words(
          * leave n alone. */
         { // WINSCP
         BignumInt bword = n & -(BignumInt)indicator;
-        uintmax_t new_n = (BIGNUM_INT_BITS < 64 ? n >> BIGNUM_INT_BITS : 0);
+        uintmax_t new_n = shift_right_by_one_word(n);
         n ^= (n ^ new_n) & -(uintmax_t)indicator;
 
         { // WINSCP
@@ -896,13 +923,16 @@ unsigned mp_cmp_hs(mp_int *a, mp_int *b)
 unsigned mp_hs_integer(mp_int *x, uintmax_t n)
 {
     BignumInt carry = 1;
-    size_t i; // WINSCP
-    for (i = 0; i < x->nw; i++) {
-        size_t shift = i * BIGNUM_INT_BITS;
-        BignumInt nword = shift < CHAR_BIT*sizeof(n) ? n >> shift : 0;
+    size_t nwords = sizeof(n)/BIGNUM_INT_BYTES;
+    size_t i, e; // WINSCP
+    for (i = 0, e = size_t_max(x->nw, nwords); i < e; i++) {
+        BignumInt nword = n;
+        n = shift_right_by_one_word(n);
+        { // WINSCP
         BignumInt dummy_out;
-        BignumADC(dummy_out, carry, x->w[i], ~nword, carry);
+        BignumADC(dummy_out, carry, mp_word(x, i), ~nword, carry);
         (void)dummy_out;
+        } // WINSCP
     }
     return carry;
 }
@@ -924,11 +954,12 @@ unsigned mp_cmp_eq(mp_int *a, mp_int *b)
 unsigned mp_eq_integer(mp_int *x, uintmax_t n)
 {
     BignumInt diff = 0;
-    size_t i; // WINSCP
-    for (i = 0; i < x->nw; i++) {
-        size_t shift = i * BIGNUM_INT_BITS;
-        BignumInt nword = shift < CHAR_BIT*sizeof(n) ? n >> shift : 0;
-        diff |= x->w[i] ^ nword;
+    size_t nwords = sizeof(n)/BIGNUM_INT_BYTES;
+    size_t i, e; // WINSCP
+    for (i = 0, e = size_t_max(x->nw, nwords); i < e; i++) {
+        BignumInt nword = n;
+        n = shift_right_by_one_word(n);
+        diff |= mp_word(x, i) ^ nword;
     }
     return 1 ^ normalise_to_1(diff);   /* return 1 if diff _is_ zero */
 }

+ 5 - 1
source/putty/mpint_i.h

@@ -59,7 +59,11 @@
 
 /* You can lower the BignumInt size by defining BIGNUM_OVERRIDE on the
  * command line to be your chosen max value of BIGNUM_INT_BITS_BITS */
-#define BB_OK(b) (!defined BIGNUM_OVERRIDE || BIGNUM_OVERRIDE >= b)
+#if defined BIGNUM_OVERRIDE
+#define BB_OK(b) ((b) <= BIGNUM_OVERRIDE)
+#else
+#define BB_OK(b) (1)
+#endif
 
 #if defined __SIZEOF_INT128__ && BB_OK(6)
 

+ 2 - 1
source/putty/network.h

@@ -276,7 +276,8 @@ char *get_hostname(void);
  * Trivial socket implementation which just stores an error. Found in
  * errsock.c.
  */
-Socket *new_error_socket_fmt(Plug *plug, const char *fmt, ...);
+Socket *new_error_socket_fmt(Plug *plug, const char *fmt, ...)
+    PRINTF_LIKE(2, 3);
 
 /*
  * Trivial plug that does absolutely nothing. Found in nullplug.c.

+ 1 - 1
source/putty/proxy.c

@@ -1188,7 +1188,7 @@ int proxy_socks5_negotiate (ProxySocket *p, int change)
             switch (data[3]) {
               case 1: len += 4; break; /* IPv4 address */
               case 4: len += 16; break;/* IPv6 address */
-              case 3: len += (unsigned char)data[4]; break; /* domain name */
+              case 3: len += 1+(unsigned char)data[4]; break; /* domain name */
               default:
                 plug_closing(p->plug, "Proxy error: SOCKS proxy returned "
                              "unrecognised address format",

+ 12 - 22
source/putty/putty.h

@@ -636,19 +636,7 @@ GLOBAL char *cmdline_session_name;
 typedef struct {
     char *prompt;
     bool echo;
-    /*
-     * 'result' must be a dynamically allocated array of exactly
-     * 'resultsize' chars. The code for actually reading input may
-     * realloc it bigger (and adjust resultsize accordingly) if it has
-     * to. The caller should free it again when finished with it.
-     *
-     * If resultsize==0, then result may be NULL. When setting up a
-     * prompt_t, it's therefore easiest to initialise them this way,
-     * which means all actual allocation is done by the callee. This
-     * is what add_prompt does.
-     */
-    char *result;
-    size_t resultsize;
+    strbuf *result;
 } prompt_t;
 typedef struct {
     /*
@@ -679,11 +667,11 @@ typedef struct {
     void *data;         /* slot for housekeeping data, managed by
                          * seat_get_userpass_input(); initially NULL */
 } prompts_t;
-prompts_t *new_prompts(void); // WINSCP (void)
+prompts_t *new_prompts(void);
 void add_prompt(prompts_t *p, char *promptstr, bool echo);
 void prompt_set_result(prompt_t *pr, const char *newstr);
-void prompt_ensure_result_size(prompt_t *pr, int len);
-/* Burn the evidence. (Assumes _all_ strings want free()ing.) */
+char *prompt_get_result(prompt_t *pr);
+const char *prompt_get_result_ref(prompt_t *pr);
 void free_prompts(prompts_t *p);
 
 /*
@@ -1015,7 +1003,7 @@ static inline bool seat_set_trust_status(Seat *seat, bool trusted)
 /* Unlike the seat's actual method, the public entry point
  * seat_connection_fatal is a wrapper function with a printf-like API,
  * defined in misc.c. */
-void seat_connection_fatal(Seat *seat, const char *fmt, ...);
+void seat_connection_fatal(Seat *seat, const char *fmt, ...) PRINTF_LIKE(2, 3);
 
 /* Handy aliases for seat_output which set is_stderr to a fixed value. */
 static inline size_t seat_stdout(Seat *seat, const void *data, size_t len)
@@ -1230,8 +1218,8 @@ static inline bool win_is_utf8(TermWin *win)
 /*
  * Global functions not specific to a connection instance.
  */
-void nonfatal(const char *, ...);
-NORETURN void modalfatalbox(const char *, ...);
+void nonfatal(const char *, ...) PRINTF_LIKE(1, 2);
+NORETURN void modalfatalbox(const char *, ...) PRINTF_LIKE(1, 2);
 NORETURN void cleanup_exit(int);
 
 /*
@@ -1268,6 +1256,7 @@ NORETURN void cleanup_exit(int);
     X(BOOL, NONE, compression) \
     X(INT, INT, ssh_kexlist) \
     X(INT, INT, ssh_hklist) \
+    X(BOOL, NONE, ssh_prefer_known_hostkeys) \
     X(INT, NONE, ssh_rekey_time) /* in minutes */ \
     X(STR, NONE, ssh_rekey_data) /* string encoding e.g. "100K", "2M", "1G" */ \
     X(BOOL, NONE, tryagent) \
@@ -1722,7 +1711,7 @@ void logfclose(LogContext *logctx);
 void logtraffic(LogContext *logctx, unsigned char c, int logmode);
 void logflush(LogContext *logctx);
 void logevent(LogContext *logctx, const char *event);
-void logeventf(LogContext *logctx, const char *fmt, ...);
+void logeventf(LogContext *logctx, const char *fmt, ...) PRINTF_LIKE(2, 3);
 void logeventvf(LogContext *logctx, const char *fmt, va_list ap);
 
 /*
@@ -1937,7 +1926,8 @@ bool is_interactive(void);
 void console_print_error_msg(const char *prefix, const char *msg);
 void console_print_error_msg_fmt_v(
     const char *prefix, const char *fmt, va_list ap);
-void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...);
+void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...)
+    PRINTF_LIKE(2, 3);
 
 /*
  * Exports from printing.c.
@@ -1975,7 +1965,7 @@ bool cmdline_host_ok(Conf *);
 #define TOOLTYPE_PORT_ARG 64
 extern int cmdline_tooltype;
 
-void cmdline_error(const char *, ...);
+void cmdline_error(const char *, ...) PRINTF_LIKE(1, 2);
 
 /*
  * Exports from config.c.

+ 3 - 2
source/putty/ssh.c

@@ -566,7 +566,7 @@ void ssh_deferred_abort_callback(void *vctx)
     Ssh *ssh = (Ssh *)vctx;
     char *msg = ssh->deferred_abort_message;
     ssh->deferred_abort_message = NULL;
-    ssh_sw_abort(ssh, msg);
+    ssh_sw_abort(ssh, "%s", msg);
     sfree(msg);
 }
 
@@ -1006,7 +1006,8 @@ static size_t ssh_sendbuffer(Backend *be)
 
     backlog = ssh_stdin_backlog(ssh->cl);
 
-    /* FIXME: also include sizes of pqs */
+    if (ssh->base_layer)
+        backlog += ssh_ppl_queued_data_size(ssh->base_layer);
 
     /*
      * If the SSH socket itself has backed up, add the total backup

+ 9 - 6
source/putty/ssh.h

@@ -57,6 +57,7 @@ struct ssh_channel;
 typedef struct PacketQueueNode PacketQueueNode;
 struct PacketQueueNode {
     PacketQueueNode *next, *prev;
+    size_t formal_size;    /* contribution to PacketQueueBase's total_size */
     bool on_free_queue;     /* is this packet scheduled for freeing? */
 };
 
@@ -89,6 +90,7 @@ typedef struct PktOut {
 
 typedef struct PacketQueueBase {
     PacketQueueNode end;
+    size_t total_size;    /* sum of all formal_size fields on the queue */
     struct IdempotentCallback *ic;
     Seat * seat; // WINSCP
 } PacketQueueBase;
@@ -409,12 +411,12 @@ 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, ...);
-void ssh_remote_eof(Ssh *ssh, const char *fmt, ...);
-void ssh_proto_error(Ssh *ssh, const char *fmt, ...);
-void ssh_sw_abort(Ssh *ssh, const char *fmt, ...);
-void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...);
-void ssh_user_close(Ssh *ssh, const char *fmt, ...);
+void ssh_remote_error(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
+void ssh_remote_eof(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
+void ssh_proto_error(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
+void ssh_sw_abort(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
+void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
+void ssh_user_close(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3);
 
 /* Bit positions in the SSH-1 cipher protocol word */
 #define SSH1_CIPHER_IDEA        1
@@ -545,6 +547,7 @@ void BinarySource_get_rsa_ssh1_pub(
     BinarySource *src, RSAKey *result, RsaSsh1Order order);
 void BinarySource_get_rsa_ssh1_priv(
     BinarySource *src, RSAKey *rsa);
+RSAKey *BinarySource_get_rsa_ssh1_priv_agent(BinarySource *src);
 bool rsa_ssh1_encrypt(unsigned char *data, int length, RSAKey *key);
 mp_int *rsa_ssh1_decrypt(mp_int *input, RSAKey *key);
 bool rsa_ssh1_decrypt_pkcs1(mp_int *input, RSAKey *key, strbuf *outbuf);

+ 1 - 0
source/putty/ssh1bpp.c

@@ -236,6 +236,7 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
                        NULL, 0, NULL);
         }
 
+        s->pktin->qnode.formal_size = get_avail(s->pktin);
         pq_push(&s->bpp.in_pq, s->pktin);
 
         {

+ 5 - 2
source/putty/ssh1connection.c

@@ -47,6 +47,7 @@ static const struct PacketProtocolLayerVtable ssh1_connection_vtable = {
     ssh1_connection_want_user_input,
     ssh1_connection_got_user_input,
     ssh1_connection_reconfigure,
+    ssh_ppl_default_queued_data_size,
     NULL /* no layer names in SSH-1 */,
     ssh1_connection_winscp_query,
 };
@@ -525,10 +526,12 @@ static void ssh1_channel_close_local(struct ssh1_channel *c,
 {
     struct ssh1_connection_state *s = c->connlayer;
     PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
-    const char *msg = chan_log_close_msg(c->chan);
+    char *msg = chan_log_close_msg(c->chan);
 
-    if (msg != NULL)
+    if (msg != NULL) {
         ppl_logevent("%s%s%s", msg, reason ? " " : "", reason ? reason : "");
+        sfree(msg);
+    }
 
     chan_free(c->chan);
     c->chan = zombiechan_new();

+ 188 - 116
source/putty/ssh1login.c

@@ -12,6 +12,12 @@
 #include "sshppl.h"
 #include "sshcr.h"
 
+typedef struct agent_key {
+    RSAKey key;
+    strbuf *comment;
+    ptrlen blob; /* only used during initial parsing of agent response */
+} agent_key;
+
 struct ssh1_login_state {
     int crState;
 
@@ -47,11 +53,11 @@ struct ssh1_login_state {
     void *agent_response_to_free;
     ptrlen agent_response;
     BinarySource asrc[1];          /* response from SSH agent */
-    int keyi, nkeys;
+    size_t agent_keys_len;
+    agent_key *agent_keys;
+    size_t agent_key_index, agent_key_limit;
     bool authed;
     RSAKey key;
-    mp_int *challenge;
-    strbuf *agent_comment;
     int dlgret;
     Filename *keyfile;
     RSAKey servkey, hostkey;
@@ -81,6 +87,7 @@ static const struct PacketProtocolLayerVtable ssh1_login_vtable = {
     ssh1_login_want_user_input,
     ssh1_login_got_user_input,
     ssh1_login_reconfigure,
+    ssh_ppl_default_queued_data_size,
     NULL /* no layer names in SSH-1 */,
     ssh1_login_winscp_query,
 };
@@ -100,7 +107,6 @@ PacketProtocolLayer *ssh1_login_new(
     s->savedhost = dupstr(host);
     s->savedport = port;
     s->successor_layer = successor_layer;
-    s->agent_comment = strbuf_new();
     return &s->ppl;
 }
 
@@ -119,9 +125,16 @@ static void ssh1_login_free(PacketProtocolLayer *ppl)
     if (s->publickey_blob)
         strbuf_free(s->publickey_blob);
     sfree(s->publickey_comment);
-    strbuf_free(s->agent_comment);
     if (s->cur_prompt)
         free_prompts(s->cur_prompt);
+    if (s->agent_keys) {
+        size_t i; // WINSCP
+        for (i = 0; i < s->agent_keys_len; i++) {
+            freersakey(&s->agent_keys[i].key);
+            strbuf_free(s->agent_keys[i].comment);
+        }
+        sfree(s->agent_keys);
+    }
     sfree(s->agent_response_to_free);
     if (s->auth_agent_query)
         agent_cancel_query(s->auth_agent_query);
@@ -418,7 +431,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
             ssh_user_close(s->ppl.ssh, "No username provided");
             return;
         }
-        s->username = dupstr(s->cur_prompt->prompts[0]->result);
+        s->username = prompt_get_result(s->cur_prompt->prompts[0]);
         free_prompts(s->cur_prompt);
         s->cur_prompt = NULL;
     }
@@ -505,122 +518,177 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
 
             get_uint32(s->asrc); /* skip length field */
             if (get_byte(s->asrc) == SSH1_AGENT_RSA_IDENTITIES_ANSWER) {
-                s->nkeys = toint(get_uint32(s->asrc));
-                if (s->nkeys < 0) {
-                    ppl_logevent("Pageant reported negative key count %d",
-                                 s->nkeys);
-                    s->nkeys = 0;
-                }
-                ppl_logevent("Pageant has %d SSH-1 keys", s->nkeys);
-                for (s->keyi = 0; s->keyi < s->nkeys; s->keyi++) {
-                    size_t start, end;
-                    start = s->asrc->pos;
-                    get_rsa_ssh1_pub(s->asrc, &s->key,
-                                     RSA_SSH1_EXPONENT_FIRST);
-                    end = s->asrc->pos;
-                    s->agent_comment->len = 0;
-                    put_datapl(s->agent_comment, get_string(s->asrc));
+                size_t nkeys = get_uint32(s->asrc);
+                size_t origpos = s->asrc->pos;
+
+                /*
+                 * Check that the agent response is well formed.
+                 */
+                { // WINSCP
+                size_t i; // WINSCP
+                for (i = 0; i < nkeys; i++) {
+                    get_rsa_ssh1_pub(s->asrc, NULL, RSA_SSH1_EXPONENT_FIRST);
+                    get_string(s->asrc); /* comment */
                     if (get_err(s->asrc)) {
-                        ppl_logevent("Pageant key list packet was truncated");
-                        break;
+                        ppl_logevent("Pageant's response was truncated");
+                        goto parsed_agent_query;
                     }
-                    if (s->publickey_blob) {
-                        ptrlen keystr = make_ptrlen(
-                            (const char *)s->asrc->data + start, end - start);
-
-                        if (keystr.len == s->publickey_blob->len &&
-                            !memcmp(keystr.ptr, s->publickey_blob->s,
-                                    s->publickey_blob->len)) {
-                            ppl_logevent("Pageant key #%d matches "
-                                         "configured key file", s->keyi);
-                            s->tried_publickey = true;
-                        } else
-                            /* Skip non-configured key */
-                            continue;
+                }
+                } // WINSCP
+
+                /*
+                 * Copy the list of public-key blobs out of the Pageant
+                 * response.
+                 */
+                BinarySource_REWIND_TO(s->asrc, origpos);
+                s->agent_keys_len = nkeys;
+                s->agent_keys = snewn(s->agent_keys_len, agent_key);
+                { // WINSCP
+                size_t i; // WINSCP
+                for (i = 0; i < nkeys; i++) {
+                    memset(&s->agent_keys[i].key, 0,
+                           sizeof(s->agent_keys[i].key));
+
+                    { // WINSCP
+                    const char *blobstart = get_ptr(s->asrc);
+                    get_rsa_ssh1_pub(s->asrc, &s->agent_keys[i].key,
+                                     RSA_SSH1_EXPONENT_FIRST);
+                    { // WINSCP
+                    const char *blobend = get_ptr(s->asrc);
+
+                    s->agent_keys[i].comment = strbuf_new();
+                    put_datapl(s->agent_keys[i].comment, get_string(s->asrc));
+
+                    s->agent_keys[i].blob = make_ptrlen(
+                        blobstart, blobend - blobstart);
+                    } // WINSCP
+                    } // WINSCP
+                }
+                } // WINSCP
+
+                ppl_logevent("Pageant has %"SIZEu" SSH-1 keys", nkeys);
+
+                if (s->publickey_blob) {
+                    /*
+                     * If we've been given a specific public key blob,
+                     * filter the list of keys to try from the agent
+                     * down to only that one, or none if it's not
+                     * there.
+                     */
+                    ptrlen our_blob = ptrlen_from_strbuf(s->publickey_blob);
+                    size_t i;
+
+                    for (i = 0; i < nkeys; i++) {
+                        if (ptrlen_eq_ptrlen(our_blob, s->agent_keys[i].blob))
+                            break;
                     }
-                    ppl_logevent("Trying Pageant key #%d", s->keyi);
-                    pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_RSA);
-                    put_mp_ssh1(pkt, s->key.modulus);
-                    pq_push(s->ppl.out_pq, pkt);
-                    crMaybeWaitUntilV((pktin = ssh1_login_pop(s))
-                                      != NULL);
-                    if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
-                        ppl_logevent("Key refused");
-                        continue;
+
+                    if (i < nkeys) {
+                        ppl_logevent("Pageant key #%"SIZEu" matches "
+                                     "configured key file", i);
+                        s->agent_key_index = i;
+                        s->agent_key_limit = i+1;
+                    } else {
+                        ppl_logevent("Configured key file not in Pageant");
+                        s->agent_key_index = 0;
+                        s->agent_key_limit = 0;
                     }
-                    ppl_logevent("Received RSA challenge");
-                    s->challenge = get_mp_ssh1(pktin);
+                } else {
+                    /*
+                     * Otherwise, try them all.
+                     */
+                    s->agent_key_index = 0;
+                    s->agent_key_limit = nkeys;
+                }
+            } else {
+                ppl_logevent("Failed to get reply from Pageant");
+            }
+          parsed_agent_query:;
+
+            for (; s->agent_key_index < s->agent_key_limit;
+                 s->agent_key_index++) {
+                ppl_logevent("Trying Pageant key #%"SIZEu, s->agent_key_index);
+                pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_RSA);
+                put_mp_ssh1(pkt,
+                            s->agent_keys[s->agent_key_index].key.modulus);
+                pq_push(s->ppl.out_pq, pkt);
+                crMaybeWaitUntilV((pktin = ssh1_login_pop(s))
+                                  != NULL);
+                if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) {
+                    ppl_logevent("Key refused");
+                    continue;
+                }
+                ppl_logevent("Received RSA challenge");
+
+                {
+                    mp_int *challenge = get_mp_ssh1(pktin);
                     if (get_err(pktin)) {
-                        mp_free(s->challenge);
+                        mp_free(challenge);
                         ssh_proto_error(s->ppl.ssh, "Server's RSA challenge "
                                         "was badly formatted");
                         return;
                     }
 
-                    {
-                        strbuf *agentreq;
-                        const char *ret;
-
-                        agentreq = strbuf_new_for_agent_query();
-                        put_byte(agentreq, SSH1_AGENTC_RSA_CHALLENGE);
-                        put_uint32(agentreq, mp_get_nbits(s->key.modulus));
-                        put_mp_ssh1(agentreq, s->key.exponent);
-                        put_mp_ssh1(agentreq, s->key.modulus);
-                        put_mp_ssh1(agentreq, s->challenge);
-                        put_data(agentreq, s->session_id, 16);
-                        put_uint32(agentreq, 1);    /* response format */
-                        ssh1_login_agent_query(s, agentreq);
-                        strbuf_free(agentreq);
-                        crMaybeWaitUntilV(!s->auth_agent_query);
-
-                        ret = s->agent_response.ptr;
-                        if (ret) {
-                            if (s->agent_response.len >= 5+16 &&
-                                ret[4] == SSH1_AGENT_RSA_RESPONSE) {
-                                ppl_logevent("Sending Pageant's response");
-                                pkt = ssh_bpp_new_pktout(
-                                    s->ppl.bpp, SSH1_CMSG_AUTH_RSA_RESPONSE);
-                                put_data(pkt, ret + 5, 16);
-                                pq_push(s->ppl.out_pq, pkt);
-                                crMaybeWaitUntilV(
-                                    (pktin = ssh1_login_pop(s))
-                                    != NULL);
-                                if (pktin->type == SSH1_SMSG_SUCCESS) {
-                                    ppl_logevent("Pageant's response "
-                                                 "accepted");
-                                    if (flags & FLAG_VERBOSE) {
-                                        ptrlen comment = ptrlen_from_strbuf(
-                                            s->agent_comment);
-                                        ppl_printf("Authenticated using RSA "
-                                                   "key \"%.*s\" from "
-                                                   "agent\r\n",
-                                                   PTRLEN_PRINTF(comment));
-                                    }
-                                    s->authed = true;
-                                } else
-                                    ppl_logevent("Pageant's response not "
-                                                 "accepted");
-                            } else {
-                                ppl_logevent("Pageant failed to answer "
-                                             "challenge");
-                                sfree((char *)ret);
-                            }
+                    { // WINSCP
+                    strbuf *agentreq = strbuf_new_for_agent_query();
+                    put_byte(agentreq, SSH1_AGENTC_RSA_CHALLENGE);
+
+                    rsa_ssh1_public_blob(
+                        BinarySink_UPCAST(agentreq),
+                        &s->agent_keys[s->agent_key_index].key,
+                        RSA_SSH1_EXPONENT_FIRST);
+
+                    put_mp_ssh1(agentreq, challenge);
+                    mp_free(challenge);
+
+                    put_data(agentreq, s->session_id, 16);
+                    put_uint32(agentreq, 1);    /* response format */
+                    ssh1_login_agent_query(s, agentreq);
+                    strbuf_free(agentreq);
+                    crMaybeWaitUntilV(!s->auth_agent_query);
+                    } // WINSCP
+                }
+
+                {
+                    const unsigned char *ret = s->agent_response.ptr;
+                    if (ret) {
+                        if (s->agent_response.len >= 5+16 &&
+                            ret[4] == SSH1_AGENT_RSA_RESPONSE) {
+                            ppl_logevent("Sending Pageant's response");
+                            pkt = ssh_bpp_new_pktout(
+                                s->ppl.bpp, SSH1_CMSG_AUTH_RSA_RESPONSE);
+                            put_data(pkt, ret + 5, 16);
+                            pq_push(s->ppl.out_pq, pkt);
+                            crMaybeWaitUntilV(
+                                (pktin = ssh1_login_pop(s))
+                                != NULL);
+                            if (pktin->type == SSH1_SMSG_SUCCESS) {
+                                ppl_logevent("Pageant's response "
+                                             "accepted");
+                                if (flags & FLAG_VERBOSE) {
+                                    ptrlen comment = ptrlen_from_strbuf(
+                                        s->agent_keys[s->agent_key_index].
+                                        comment);
+                                    ppl_printf("Authenticated using RSA "
+                                               "key \"%.*s\" from "
+                                               "agent\r\n",
+                                               PTRLEN_PRINTF(comment));
+                                }
+                                s->authed = true;
+                            } else
+                                ppl_logevent("Pageant's response not "
+                                             "accepted");
                         } else {
-                            ppl_logevent("No reply received from Pageant");
+                            ppl_logevent("Pageant failed to answer "
+                                         "challenge");
+                            sfree((char *)ret);
                         }
+                    } else {
+                        ppl_logevent("No reply received from Pageant");
                     }
-                    mp_free(s->key.exponent);
-                    mp_free(s->key.modulus);
-                    mp_free(s->challenge);
-                    if (s->authed)
-                        break;
                 }
-                sfree(s->agent_response_to_free);
-                s->agent_response_to_free = NULL;
-                if (s->publickey_blob && !s->tried_publickey)
-                    ppl_logevent("Configured key file not in Pageant");
-            } else {
-                ppl_logevent("Failed to get reply from Pageant");
+                if (s->authed)
+                    break;
             }
             if (s->authed)
                 break;
@@ -650,7 +718,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                         ppl_printf("No passphrase required.\r\n");
                     passphrase = NULL;
                 } else {
-                    s->cur_prompt = new_prompts(); // WINSCP removed s->ppl.seat
+                    s->cur_prompt = new_prompts();
                     s->cur_prompt->to_server = false;
                     s->cur_prompt->from_server = false;
                     s->cur_prompt->name = dupstr("SSH key passphrase");
@@ -678,7 +746,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                                        "User aborted at passphrase prompt");
                         return;
                     }
-                    passphrase = dupstr(s->cur_prompt->prompts[0]->result);
+                    passphrase = prompt_get_result(s->cur_prompt->prompts[0]);
                     free_prompts(s->cur_prompt);
                     s->cur_prompt = NULL;
                 }
@@ -789,7 +857,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
         /*
          * Otherwise, try various forms of password-like authentication.
          */
-        s->cur_prompt = new_prompts(); // WINSCP removed s->ppl.seat
+        s->cur_prompt = new_prompts();
 
         if (conf_get_bool(s->conf, CONF_try_tis_auth) &&
             (s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) &&
@@ -994,8 +1062,10 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                  * we can use the primary defence.
                  */
                 int bottom, top, pwlen, i;
+                const char *pw = prompt_get_result_ref(
+                    s->cur_prompt->prompts[0]);
 
-                pwlen = strlen(s->cur_prompt->prompts[0]->result);
+                pwlen = strlen(pw);
                 if (pwlen < 16) {
                     bottom = 0;    /* zero length passwords are OK! :-) */
                     top = 15;
@@ -1009,7 +1079,7 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                 for (i = bottom; i <= top; i++) {
                     if (i == pwlen) {
                         pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type);
-                        put_stringz(pkt, s->cur_prompt->prompts[0]->result);
+                        put_stringz(pkt, pw);
                         pq_push(s->ppl.out_pq, pkt);
                     } else {
                         strbuf *random_data = strbuf_new_nm();
@@ -1032,7 +1102,8 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
 
                 ppl_logevent("Sending length-padded password");
                 pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type);
-                put_asciz(padded_pw, s->cur_prompt->prompts[0]->result);
+                put_asciz(padded_pw, prompt_get_result_ref(
+                              s->cur_prompt->prompts[0]));
                 { // WINSCP
                 size_t pad = 63 & -(ssize_t)padded_pw->len; // WINSCP
                 random_read(strbuf_append(padded_pw, pad), pad);
@@ -1046,12 +1117,13 @@ static void ssh1_login_process_queue(PacketProtocolLayer *ppl)
                  */
                 ppl_logevent("Sending unpadded password");
                 pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type);
-                put_stringz(pkt, s->cur_prompt->prompts[0]->result);
+                put_stringz(pkt, prompt_get_result_ref(
+                                s->cur_prompt->prompts[0]));
                 pq_push(s->ppl.out_pq, pkt);
             }
         } else {
             pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type);
-            put_stringz(pkt, s->cur_prompt->prompts[0]->result);
+            put_stringz(pkt, prompt_get_result_ref(s->cur_prompt->prompts[0]));
             pq_push(s->ppl.out_pq, pkt);
         }
         ppl_logevent("Sent password");

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

@@ -129,6 +129,7 @@ static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
             continue;
         }
 
+        s->pktin->qnode.formal_size = get_avail(s->pktin);
         pq_push(&s->bpp.in_pq, s->pktin);
         s->pktin = NULL;
     }

+ 1 - 0
source/putty/ssh2bpp.c

@@ -589,6 +589,7 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
             continue;
         }
 
+        s->pktin->qnode.formal_size = get_avail(s->pktin);
         pq_push(&s->bpp.in_pq, s->pktin);
 
         {

+ 3 - 0
source/putty/ssh2connection-client.c

@@ -319,6 +319,9 @@ SshChannel *ssh2_serverside_agent_open(ConnectionLayer *cl, Channel *chan)
 static void ssh2_channel_response(
     struct ssh2_channel *c, PktIn *pkt, void *ctx)
 {
+    /* If pkt==NULL (because this handler has been called in response
+     * to CHANNEL_CLOSE arriving while the request was still
+     * outstanding), we treat that the same as CHANNEL_FAILURE. */
     chan_request_response(c->chan,
                           pkt && pkt->type == SSH2_MSG_CHANNEL_SUCCESS);
 }

+ 3 - 1
source/putty/ssh2connection.c

@@ -33,6 +33,7 @@ static const struct PacketProtocolLayerVtable ssh2_connection_vtable = {
     ssh2_connection_want_user_input,
     ssh2_connection_got_user_input,
     ssh2_connection_reconfigure,
+    ssh_ppl_default_queued_data_size,
     "ssh-connection",
     ssh2_connection_winscp_query,
 };
@@ -758,7 +759,8 @@ static bool ssh2_connection_filter_queue(struct ssh2_connection_state *s)
                         "Received %s for channel %d with no outstanding "
                         "channel request",
                         ssh2_pkt_type(s->ppl.bpp->pls->kctx,
-                                      s->ppl.bpp->pls->actx, pktin->type));
+                                      s->ppl.bpp->pls->actx, pktin->type),
+                        c->localid);
                     return true;
                 }
                 ocr->handler(c, pktin, ocr->ctx);

+ 27 - 11
source/putty/ssh2transport.c

@@ -73,6 +73,7 @@ static void ssh2_transport_special_cmd(PacketProtocolLayer *ppl,
 static bool ssh2_transport_want_user_input(PacketProtocolLayer *ppl);
 static void ssh2_transport_got_user_input(PacketProtocolLayer *ppl);
 static void ssh2_transport_reconfigure(PacketProtocolLayer *ppl, Conf *conf);
+static size_t ssh2_transport_queued_data_size(PacketProtocolLayer *ppl);
 
 static void ssh2_transport_set_max_data_size(struct ssh2_transport_state *s);
 static unsigned long sanitise_rekey_time(int rekey_time, unsigned long def);
@@ -87,6 +88,7 @@ static const struct PacketProtocolLayerVtable ssh2_transport_vtable = {
     ssh2_transport_want_user_input,
     ssh2_transport_got_user_input,
     ssh2_transport_reconfigure,
+    ssh2_transport_queued_data_size,
     NULL, /* no protocol name for this layer */
     ssh2_transport_winscp_query,
 };
@@ -270,7 +272,7 @@ static void ssh2_mkkey(
      */
     keylen_padded = ((keylen + hlen - 1) / hlen) * hlen;
 
-    out->len = 0;
+    strbuf_clear(out);
     key = strbuf_append(out, keylen_padded);
 
     /* First hlen bytes. */
@@ -576,9 +578,10 @@ static void ssh2_write_kexinit_lists(
         }
     } else if (first_time) {
         /*
-         * In the first key exchange, we list all the algorithms
-         * we're prepared to cope with, but prefer those algorithms
-         * for which we have a host key for this host.
+         * In the first key exchange, we list all the algorithms we're
+         * prepared to cope with, but (if configured to) we prefer
+         * those algorithms for which we have a host key for this
+         * host.
          *
          * If the host key algorithm is below the warning
          * threshold, we warn even if we did already have a key
@@ -594,7 +597,8 @@ static void ssh2_write_kexinit_lists(
             for (j = 0; j < lenof(ssh2_hostkey_algs); j++) {
                 if (ssh2_hostkey_algs[j].id != preferred_hk[i])
                     continue;
-                if (have_ssh_host_key(seat, hk_host, hk_port, // WINSCP
+                if (conf_get_bool(conf, CONF_ssh_prefer_known_hostkeys) &&
+                    have_ssh_host_key(seat, hk_host, hk_port, // WINSCP
                                       ssh2_hostkey_algs[j].alg->cache_id)) {
                     alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY],
                                               ssh2_hostkey_algs[j].alg->ssh_id);
@@ -1097,7 +1101,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
      * Construct our KEXINIT packet, in a strbuf so we can refer to it
      * later.
      */
-    s->client_kexinit->len = 0;
+    strbuf_clear(s->client_kexinit);
     put_byte(s->outgoing_kexinit, SSH2_MSG_KEXINIT);
     random_read(strbuf_append(s->outgoing_kexinit, 16), 16);
     ssh2_write_kexinit_lists(
@@ -1135,7 +1139,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
                                       s->ppl.bpp->pls->actx, pktin->type));
         return;
     }
-    s->incoming_kexinit->len = 0;
+    strbuf_clear(s->incoming_kexinit);
     put_byte(s->incoming_kexinit, SSH2_MSG_KEXINIT);
     put_data(s->incoming_kexinit, get_ptr(pktin), get_avail(pktin));
 
@@ -1214,9 +1218,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl)
             if (better) {
                 if (betteralgs) {
                     char *old_ba = betteralgs;
-                    betteralgs = dupcat(betteralgs, ",",
-                                        hktype->alg->ssh_id,
-                                        (const char *)NULL);
+                    betteralgs = dupcat(betteralgs, ",", hktype->alg->ssh_id);
                     sfree(old_ba);
                 } else {
                     betteralgs = dupstr(hktype->alg->ssh_id);
@@ -2045,7 +2047,16 @@ static int ssh2_transport_confirm_weak_crypto_primitive(
 
     return seat_confirm_weak_crypto_primitive(
         s->ppl.seat, type, name, ssh2_transport_dialog_callback, s);
-}        
+}
+
+static size_t ssh2_transport_queued_data_size(PacketProtocolLayer *ppl)
+{
+    struct ssh2_transport_state *s =
+        container_of(ppl, struct ssh2_transport_state, ppl);
+
+    return (ssh_ppl_default_queued_data_size(ppl) +
+            ssh_ppl_queued_data_size(s->higher_layer));
+}
 
 #include "puttyexp.h"
 
@@ -2068,6 +2079,11 @@ static unsigned int ssh2_transport_winscp_query(PacketProtocolLayer *ppl, int qu
     }
 }
 
+void call_ssh_timer(Backend * be)
+{
+    // TODO
+}
+
 // WINSCP
 void get_hostkey_algs(int * count, cp_ssh_keyalg * SignKeys)
 {

+ 135 - 92
source/putty/ssh2userauth.c

@@ -18,6 +18,11 @@
 
 #define BANNER_LIMIT 131072
 
+typedef struct agent_key {
+    strbuf *blob, *comment;
+    ptrlen algorithm;
+} agent_key;
+
 struct ssh2_userauth_state {
     int crState;
 
@@ -71,9 +76,9 @@ struct ssh2_userauth_state {
     void *agent_response_to_free;
     ptrlen agent_response;
     BinarySource asrc[1];          /* for reading SSH agent response */
-    size_t pkblob_pos_in_agent;
-    int keyi, nkeys;
-    ptrlen pk, alg, comment;
+    size_t agent_keys_len;
+    agent_key *agent_keys;
+    size_t agent_key_index, agent_key_limit;
     int len;
     PktOut *pktout;
     bool want_user_input;
@@ -122,6 +127,7 @@ static const struct PacketProtocolLayerVtable ssh2_userauth_vtable = {
     ssh2_userauth_want_user_input,
     ssh2_userauth_got_user_input,
     ssh2_userauth_reconfigure,
+    ssh_ppl_default_queued_data_size,
     "ssh-userauth",
 };
 
@@ -177,6 +183,14 @@ static void ssh2_userauth_free(PacketProtocolLayer *ppl)
     if (s->successor_layer)
         ssh_ppl_free(s->successor_layer);
 
+    if (s->agent_keys) {
+        size_t i; // WINSCP
+        for (i = 0; i < s->agent_keys_len; i++) {
+            strbuf_free(s->agent_keys[i].blob);
+            strbuf_free(s->agent_keys[i].comment);
+        }
+        sfree(s->agent_keys);
+    }
     sfree(s->agent_response_to_free);
     if (s->auth_agent_query)
         agent_cancel_query(s->auth_agent_query);
@@ -305,8 +319,6 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
      * Find out about any keys Pageant has (but if there's a public
      * key configured, filter out all others).
      */
-    s->nkeys = 0;
-    s->pkblob_pos_in_agent = 0;
     if (s->tryagent && agent_exists()) {
         ppl_logevent("Pageant is running. Requesting keys.");
 
@@ -322,48 +334,83 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
 
         get_uint32(s->asrc); /* skip length field */
         if (get_byte(s->asrc) == SSH2_AGENT_IDENTITIES_ANSWER) {
-            int keyi;
+            size_t nkeys = get_uint32(s->asrc);
+            size_t origpos = s->asrc->pos;
 
-            s->nkeys = toint(get_uint32(s->asrc));
+            /*
+             * Check that the agent response is well formed.
+             */
+            { // WINSCP
+            size_t i; // WINSCP
+            for (i = 0; i < nkeys; i++) {
+                get_string(s->asrc);   /* blob */
+                get_string(s->asrc);   /* comment */
+                if (get_err(s->asrc)) {
+                    ppl_logevent("Pageant's response was truncated");
+                    goto done_agent_query;
+                }
+            }
+            } // WINSCP
 
             /*
-             * Vet the Pageant response to ensure that the key count
-             * and blob lengths make sense.
+             * Copy the list of public-key blobs out of the Pageant
+             * response.
              */
-            if (s->nkeys < 0) {
-                ppl_logevent("Pageant response contained a negative"
-                             " key count %d", s->nkeys);
-                s->nkeys = 0;
-                goto done_agent_query;
-            } else {
-                ppl_logevent("Pageant has %d SSH-2 keys", s->nkeys);
-
-                /* See if configured key is in agent. */
-                for (keyi = 0; keyi < s->nkeys; keyi++) {
-                    size_t pos = s->asrc->pos;
-                    ptrlen blob = get_string(s->asrc);
-                    get_string(s->asrc); /* skip comment */
-                    if (get_err(s->asrc)) {
-                        ppl_logevent("Pageant response was truncated");
-                        s->nkeys = 0;
-                        goto done_agent_query;
-                    }
+            BinarySource_REWIND_TO(s->asrc, origpos);
+            s->agent_keys_len = nkeys;
+            s->agent_keys = snewn(s->agent_keys_len, agent_key);
+            { // WINSCP
+            size_t i; // WINSCP
+            for (i = 0; i < nkeys; i++) {
+                s->agent_keys[i].blob = strbuf_new();
+                put_datapl(s->agent_keys[i].blob, get_string(s->asrc));
+                s->agent_keys[i].comment = strbuf_new();
+                put_datapl(s->agent_keys[i].comment, get_string(s->asrc));
 
-                    if (s->publickey_blob &&
-                        blob.len == s->publickey_blob->len &&
-                        !memcmp(blob.ptr, s->publickey_blob->s,
-                                s->publickey_blob->len)) {
-                        ppl_logevent("Pageant key #%d matches "
-                                     "configured key file", keyi);
-                        s->keyi = keyi;
-                        s->pkblob_pos_in_agent = pos;
+                { // WINSCP
+                /* Also, extract the algorithm string from the start
+                 * of the public-key blob. */
+                BinarySource src[1];
+                BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(
+                    s->agent_keys[i].blob));
+                s->agent_keys[i].algorithm = get_string(src);
+                } // WINSCP
+            }
+            } // WINSCP
+
+            ppl_logevent("Pageant has %"SIZEu" SSH-2 keys", nkeys);
+
+            if (s->publickey_blob) {
+                /*
+                 * If we've been given a specific public key blob,
+                 * filter the list of keys to try from the agent down
+                 * to only that one, or none if it's not there.
+                 */
+                ptrlen our_blob = ptrlen_from_strbuf(s->publickey_blob);
+                size_t i;
+
+                for (i = 0; i < nkeys; i++) {
+                    if (ptrlen_eq_ptrlen(our_blob, ptrlen_from_strbuf(
+                                             s->agent_keys[i].blob)))
                         break;
-                    }
                 }
-                if (s->publickey_blob && !s->pkblob_pos_in_agent) {
+
+                if (i < nkeys) {
+                    ppl_logevent("Pageant key #%"SIZEu" matches "
+                                 "configured key file", i);
+                    s->agent_key_index = i;
+                    s->agent_key_limit = i+1;
+                } else {
                     ppl_logevent("Configured key file not in Pageant");
-                    s->nkeys = 0;
+                    s->agent_key_index = 0;
+                    s->agent_key_limit = 0;
                 }
+            } else {
+                /*
+                 * Otherwise, try them all.
+                 */
+                s->agent_key_index = 0;
+                s->agent_key_limit = nkeys;
             }
         } else {
             ppl_logevent("Failed to get reply from Pageant");
@@ -438,7 +485,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
             }
             sfree(s->locally_allocated_username); /* for change_username */
             s->username = s->locally_allocated_username =
-                dupstr(s->cur_prompt->prompts[0]->result);
+                prompt_get_result(s->cur_prompt->prompts[0]);
             free_prompts(s->cur_prompt);
         } else {
             if ((flags & FLAG_VERBOSE) || (flags & FLAG_INTERACTIVE))
@@ -462,17 +509,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
 
         s->tried_pubkey_config = false;
         s->kbd_inter_refused = false;
-
-        /* Reset agent request state. */
         s->done_agent = false;
-        if (s->agent_response.ptr) {
-            if (s->pkblob_pos_in_agent) {
-                s->asrc->pos = s->pkblob_pos_in_agent;
-            } else {
-                s->asrc->pos = 9;      /* skip length + type + key count */
-                s->keyi = 0;
-            }
-        }
 
         while (1) {
             /*
@@ -517,9 +554,9 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                     ptrlen data = bufchain_prefix(&s->banner);
                     seat_stderr_pl(s->ppl.seat, data);
                     display_banner(s->ppl.seat, data.ptr, data.len); // WINSCP
-                    bufchain_consume(&s->banner, data.len);
                     mid_line =
                         (((const char *)data.ptr)[data.len-1] != '\n');
+                    bufchain_consume(&s->banner, data.len);
                 }
                 bufchain_clear(&s->banner);
 
@@ -629,7 +666,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                 /*
                  * Save the methods string for use in error messages.
                  */
-                s->last_methods_string->len = 0;
+                strbuf_clear(s->last_methods_string);
                 put_datapl(s->last_methods_string, methods);
 #ifdef WINSCP
                 ppl_logevent("Server offered these authentication methods: %s", s->last_methods_string->s);
@@ -702,7 +739,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
             } else
 #endif /* NO_GSSAPI */
 
-            if (s->can_pubkey && !s->done_agent && s->nkeys) {
+            if (s->can_pubkey && !s->done_agent &&
+                s->agent_key_index < s->agent_key_limit) {
 
                 /*
                  * Attempt public-key authentication using a key from Pageant.
@@ -710,16 +748,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
 
                 s->ppl.bpp->pls->actx = SSH2_PKTCTX_PUBLICKEY;
 
-                ppl_logevent("Trying Pageant key #%d", s->keyi);
-
-                /* Unpack key from agent response */
-                s->pk = get_string(s->asrc);
-                s->comment = get_string(s->asrc);
-                {
-                    BinarySource src[1];
-                    BinarySource_BARE_INIT_PL(src, s->pk);
-                    s->alg = get_string(src);
-                }
+                ppl_logevent("Trying Pageant key #%"SIZEu, s->agent_key_index);
 
                 /* See if server will accept it */
                 s->pktout = ssh_bpp_new_pktout(
@@ -729,8 +758,10 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                 put_stringz(s->pktout, "publickey");
                                                     /* method */
                 put_bool(s->pktout, false); /* no signature included */
-                put_stringpl(s->pktout, s->alg);
-                put_stringpl(s->pktout, s->pk);
+                put_stringpl(s->pktout,
+                             s->agent_keys[s->agent_key_index].algorithm);
+                put_stringpl(s->pktout, ptrlen_from_strbuf(
+                            s->agent_keys[s->agent_key_index].blob));
                 pq_push(s->ppl.out_pq, s->pktout);
                 s->type = AUTH_TYPE_PUBLICKEY_OFFER_QUIET;
 
@@ -743,11 +774,13 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
 
                 } else {
                     strbuf *agentreq, *sigdata;
+                    ptrlen comment = ptrlen_from_strbuf(
+                        s->agent_keys[s->agent_key_index].comment);
 
                     if (flags & FLAG_VERBOSE)
                         ppl_printf("Authenticating with public key "
                                    "\"%.*s\" from agent\r\n",
-                                   PTRLEN_PRINTF(s->comment));
+                                   PTRLEN_PRINTF(comment));
 
                     /*
                      * Server is willing to accept the key.
@@ -760,13 +793,16 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                     put_stringz(s->pktout, "publickey");
                                                         /* method */
                     put_bool(s->pktout, true);  /* signature included */
-                    put_stringpl(s->pktout, s->alg);
-                    put_stringpl(s->pktout, s->pk);
+                    put_stringpl(s->pktout,
+                                 s->agent_keys[s->agent_key_index].algorithm);
+                    put_stringpl(s->pktout, ptrlen_from_strbuf(
+                            s->agent_keys[s->agent_key_index].blob));
 
                     /* Ask agent for signature. */
                     agentreq = strbuf_new_for_agent_query();
                     put_byte(agentreq, SSH2_AGENTC_SIGN_REQUEST);
-                    put_stringpl(agentreq, s->pk);
+                    put_stringpl(agentreq, ptrlen_from_strbuf(
+                            s->agent_keys[s->agent_key_index].blob));
                     /* Now the data to be signed... */
                     sigdata = strbuf_new();
                     ssh2_userauth_add_session_id(s, sigdata);
@@ -788,8 +824,11 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                         if (get_byte(src) == SSH2_AGENT_SIGN_RESPONSE &&
                             (sigblob = get_string(src), !get_err(src))) {
                             ppl_logevent("Sending Pageant's response");
-                            ssh2_userauth_add_sigblob(s, s->pktout,
-                                                      s->pk, sigblob);
+                            ssh2_userauth_add_sigblob(
+                                s, s->pktout,
+                                ptrlen_from_strbuf(
+                                    s->agent_keys[s->agent_key_index].blob),
+                                sigblob);
                             pq_push(s->ppl.out_pq, s->pktout);
                             s->type = AUTH_TYPE_PUBLICKEY;
                         } else {
@@ -797,19 +836,21 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                             ppl_printf("Pageant failed to "
                                        "provide a signature\r\n");
                             s->suppress_wait_for_response_packet = true;
+                            ssh_free_pktout(s->pktout);
                         }
+                    } else {
+                        ppl_logevent("Pageant failed to respond to "
+                                     "signing request");
+                        ppl_printf("Pageant failed to "
+                                   "respond to signing request\r\n");
+                        s->suppress_wait_for_response_packet = true;
+                        ssh_free_pktout(s->pktout);
                     }
                 }
 
                 /* Do we have any keys left to try? */
-                if (s->pkblob_pos_in_agent) {
+                if (++s->agent_key_index >= s->agent_key_limit)
                     s->done_agent = true;
-                    s->tried_pubkey_config = true;
-                } else {
-                    s->keyi++;
-                    if (s->keyi >= s->nkeys)
-                        s->done_agent = true;
-                }
 
             } else if (s->can_pubkey && s->publickey_blob &&
                        s->privatekey_available && !s->tried_pubkey_config) {
@@ -899,7 +940,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                             return;
                         }
                         passphrase =
-                            dupstr(s->cur_prompt->prompts[0]->result);
+                            prompt_get_result(s->cur_prompt->prompts[0]);
                         free_prompts(s->cur_prompt);
                     } else {
                         passphrase = NULL; /* no passphrase needed */
@@ -1360,6 +1401,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                     }
                     if (sb->len)
                         s->cur_prompt->instruction = strbuf_to_str(sb);
+                    else
+                        strbuf_free(sb);
 
                     /*
                      * Our prompts_t is fully constructed now. Get the
@@ -1400,10 +1443,10 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                         s->ppl.bpp, SSH2_MSG_USERAUTH_INFO_RESPONSE);
                     put_uint32(s->pktout, s->num_prompts);
                     { // WINSCP
-                    uint32_t i;
-                    for (i=0; i < s->num_prompts; i++) {
-                        put_stringz(s->pktout,
-                                    s->cur_prompt->prompts[i]->result);
+                    uint32_t i; // WINSCP
+                    for (i = 0; i < s->num_prompts; i++) {
+                        put_stringz(s->pktout, prompt_get_result_ref(
+                                        s->cur_prompt->prompts[i]));
                     }
                     } // WINSCP
                     s->pktout->minlen = 256;
@@ -1496,7 +1539,7 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                  * Squirrel away the password. (We may need it later if
                  * asked to change it.)
                  */
-                s->password = dupstr(s->cur_prompt->prompts[0]->result);
+                s->password = prompt_get_result(s->cur_prompt->prompts[0]);
                 free_prompts(s->cur_prompt);
 
                 /*
@@ -1627,20 +1670,20 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                          * (A side effect is that the user doesn't have to
                          * re-enter it if they louse up the new password.)
                          */
-                        if (s->cur_prompt->prompts[0]->result[0]) {
+                        if (s->cur_prompt->prompts[0]->result->s[0]) {
                             smemclr(s->password, strlen(s->password));
                                 /* burn the evidence */
                             sfree(s->password);
-                            s->password =
-                                dupstr(s->cur_prompt->prompts[0]->result);
+                            s->password = prompt_get_result(
+                                s->cur_prompt->prompts[0]);
                         }
 
                         /*
                          * Check the two new passwords match.
                          */
-                        got_new = (strcmp(s->cur_prompt->prompts[1]->result,
-                                          s->cur_prompt->prompts[2]->result)
-                                   == 0);
+                        got_new = !strcmp(
+                            prompt_get_result_ref(s->cur_prompt->prompts[1]),
+                            prompt_get_result_ref(s->cur_prompt->prompts[2]));
                         if (!got_new)
                             /* They don't. Silly user. */
                             ppl_printf("Passwords do not match\r\n");
@@ -1658,8 +1701,8 @@ static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl)
                     put_stringz(s->pktout, "password");
                     put_bool(s->pktout, true);
                     put_stringz(s->pktout, s->password);
-                    put_stringz(s->pktout,
-                                       s->cur_prompt->prompts[1]->result);
+                    put_stringz(s->pktout, prompt_get_result_ref(
+                                    s->cur_prompt->prompts[1]));
                     free_prompts(s->cur_prompt);
                     s->pktout->minlen = 256;
                     pq_push(s->ppl.out_pq, s->pktout);
@@ -1821,7 +1864,7 @@ static void ssh2_userauth_add_sigblob(
         /* debug("modulus length is %d\n", len); */
         /* debug("signature length is %d\n", siglen); */
 
-        if (mod_mp.len != sig_mp.len) {
+        if (mod_mp.len > sig_mp.len) {
             strbuf *substr = strbuf_new();
             put_data(substr, sigblob.ptr, sig_prefix_len);
             put_uint32(substr, mod_mp.len);

+ 38 - 7
source/putty/sshcommon.c

@@ -35,6 +35,7 @@ void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node)
     node->prev = pqb->end.prev;
     node->next->prev = node;
     node->prev->next = node;
+    pqb->total_size += node->formal_size;
 
     if (pqb->ic)
         queue_idempotent_callback(pqb->ic);
@@ -47,6 +48,7 @@ void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node)
     node->next = pqb->end.next;
     node->next->prev = node;
     node->prev->next = node;
+    pqb->total_size += node->formal_size;
 
     if (pqb->ic)
         queue_idempotent_callback(pqb->ic);
@@ -77,6 +79,23 @@ static IdempotentCallback ic_pktin_free = {
 };
 #endif
 
+static inline void pq_unlink_common(PacketQueueBase *pqb,
+                                    PacketQueueNode *node)
+{
+    node->next->prev = node->prev;
+    node->prev->next = node->next;
+
+    /* Check total_size doesn't drift out of sync downwards, by
+     * ensuring it doesn't underflow when we do this subtraction */
+    assert(pqb->total_size >= node->formal_size);
+    pqb->total_size -= node->formal_size;
+
+    /* Check total_size doesn't drift out of sync upwards, by checking
+     * that it's returned to exactly zero whenever a queue is
+     * emptied */
+    assert(pqb->end.next != &pqb->end || pqb->total_size == 0);
+}
+
 static PktIn *pq_in_after(PacketQueueBase *pqb,
                           PacketQueueNode *prev, bool pop)
 {
@@ -91,8 +110,8 @@ static PktIn *pq_in_after(PacketQueueBase *pqb,
         if (set->ic_pktin_free == NULL)
         {
             set->pktin_freeq_head = snew(PacketQueueNode);
-            set->pktin_freeq_head->next = set->pktin_freeq_head;
-            set->pktin_freeq_head->prev = set->pktin_freeq_head;
+            set->pktin_freeq_head->next = &set->pktin_freeq_head;
+            set->pktin_freeq_head->prev = &set->pktin_freeq_head;
             set->pktin_freeq_head->on_free_queue = TRUE;
 
             set->ic_pktin_free = snew(IdempotentCallback);
@@ -103,8 +122,7 @@ static PktIn *pq_in_after(PacketQueueBase *pqb,
         }
         #endif
 
-        node->next->prev = node->prev;
-        node->prev->next = node->next;
+        pq_unlink_common(pqb, node);
 
         node->prev = set->pktin_freeq_head->prev; // WINSCP
         node->next = set->pktin_freeq_head; // WINSCP
@@ -112,7 +130,7 @@ static PktIn *pq_in_after(PacketQueueBase *pqb,
         node->prev->next = node;
         node->on_free_queue = true;
 
-        queue_idempotent_callback(set->ic_pktin_free); // WINSCP
+        queue_idempotent_callback(&set->ic_pktin_free); // WINSCP
     }
 
     return container_of(node, PktIn, qnode);
@@ -126,8 +144,8 @@ static PktOut *pq_out_after(PacketQueueBase *pqb,
         return NULL;
 
     if (pop) {
-        node->next->prev = node->prev;
-        node->prev->next = node->next;
+        pq_unlink_common(pqb, node);
+
         node->prev = node->next = NULL;
     }
 
@@ -140,6 +158,7 @@ void pq_in_init(PktInQueue *pq, Seat * seat) // WINSCP
     pq->pqb.seat = seat;
     pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
     pq->after = pq_in_after;
+    pq->pqb.total_size = 0;
 }
 
 void pq_out_init(PktOutQueue *pq, Seat * seat) // WINSCP
@@ -148,6 +167,7 @@ void pq_out_init(PktOutQueue *pq, Seat * seat) // WINSCP
     pq->pqb.seat = seat;
     pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end;
     pq->after = pq_out_after;
+    pq->pqb.total_size = 0;
 }
 
 void pq_in_clear(PktInQueue *pq)
@@ -179,6 +199,8 @@ void pq_base_concatenate(PacketQueueBase *qdest,
 {
     struct PacketQueueNode *head1, *tail1, *head2, *tail2;
 
+    size_t total_size = q1->total_size + q2->total_size;
+
     /*
      * Extract the contents from both input queues, and empty them.
      */
@@ -190,6 +212,7 @@ void pq_base_concatenate(PacketQueueBase *qdest,
 
     q1->end.next = q1->end.prev = &q1->end;
     q2->end.next = q2->end.prev = &q2->end;
+    q1->total_size = q2->total_size = 0;
 
     /*
      * Link the two lists together, handling the case where one or
@@ -232,6 +255,8 @@ void pq_base_concatenate(PacketQueueBase *qdest,
         if (qdest->ic)
             queue_idempotent_callback(qdest->ic);
     }
+
+    qdest->total_size = total_size;
 }
 
 /* ----------------------------------------------------------------------
@@ -261,6 +286,7 @@ static void ssh_pkt_adddata(PktOut *pkt, const void *data, int len)
     sgrowarrayn_nm(pkt->data, pkt->maxlen, pkt->length, len);
     memcpy(pkt->data + pkt->length, data, len);
     pkt->length += len;
+    pkt->qnode.formal_size = pkt->length;
 }
 
 static void ssh_pkt_BinarySink_write(BinarySink *bs,
@@ -836,6 +862,11 @@ void ssh_ppl_user_output_string_and_free(PacketProtocolLayer *ppl, char *text)
     sfree(text);
 }
 
+size_t ssh_ppl_default_queued_data_size(PacketProtocolLayer *ppl)
+{
+    return ppl->out_pq->pqb.total_size;
+}
+
 /* ----------------------------------------------------------------------
  * Common helper functions for clients and implementations of
  * BinaryPacketProtocol.

+ 3 - 1
source/putty/sshdss.c

@@ -74,8 +74,10 @@ static char *dss_cache_str(ssh_key *key)
     struct dss_key *dss = container_of(key, struct dss_key, sshk);
     strbuf *sb = strbuf_new();
 
-    if (!dss->p)
+    if (!dss->p) {
+        strbuf_free(sb);
         return NULL;
+    }
 
     append_hex_to_strbuf(sb, dss->p);
     append_hex_to_strbuf(sb, dss->q);

+ 1 - 1
source/putty/sshecc.c

@@ -1764,7 +1764,7 @@ bool ec_ed_alg_and_curve_by_bits(
     int bits, const struct ec_curve **curve, const ssh_keyalg **alg)
 {
     switch (bits) {
-      case 256: *alg = &ssh_ecdsa_ed25519; break;
+      case 255: case 256: *alg = &ssh_ecdsa_ed25519; break;
       default: return false;
     }
     *curve = ((struct ecsign_extra *)(*alg)->extra)->curve();

+ 1 - 1
source/putty/sshgssc.c

@@ -25,7 +25,7 @@ static Ssh_gss_stat ssh_gssapi_import_name(struct ssh_gss_library *lib,
     gss_buffer_desc host_buf;
     char *pStr;
 
-    pStr = dupcat("host@", host, NULL);
+    pStr = dupcat("host@", host);
 
     host_buf.value = pStr;
     host_buf.length = strlen(pStr);

+ 3 - 3
source/putty/sshhmac.c

@@ -44,7 +44,7 @@ static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher)
     ctx->digest = snewn(ctx->hashalg->hlen, uint8_t);
 
     ctx->text_name = strbuf_new();
-    strbuf_catf(ctx->text_name, "HMAC-%s",
+    strbuf_catf(ctx->text_name, "HMAC-%s%s",
                 ctx->hashalg->text_basename, extra->suffix);
     if (extra->annotation || ctx->hashalg->annotation) {
         strbuf_catf(ctx->text_name, " (");
@@ -229,7 +229,7 @@ const ssh2_macalg ssh_hmac_sha1_96 = {
 };
 
 const struct hmac_extra ssh_hmac_sha1_buggy_extra = {
-    &ssh_sha1, " (bug-compatible)"
+    &ssh_sha1, "", "bug-compatible"
 };
 
 const ssh2_macalg ssh_hmac_sha1_buggy = {
@@ -240,7 +240,7 @@ const ssh2_macalg ssh_hmac_sha1_buggy = {
 };
 
 const struct hmac_extra ssh_hmac_sha1_96_buggy_extra = {
-    &ssh_sha1, "-96 (bug-compatible)"
+    &ssh_sha1, "-96", "bug-compatible"
 };
 
 const ssh2_macalg ssh_hmac_sha1_96_buggy = {

+ 8 - 0
source/putty/sshppl.h

@@ -19,6 +19,7 @@ struct PacketProtocolLayerVtable {
     bool (*want_user_input)(PacketProtocolLayer *ppl);
     void (*got_user_input)(PacketProtocolLayer *ppl);
     void (*reconfigure)(PacketProtocolLayer *ppl, Conf *conf);
+    size_t (*queued_data_size)(PacketProtocolLayer *ppl);
 
     /* Protocol-level name of this layer. */
     const char *name;
@@ -75,6 +76,8 @@ 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); }
+static inline size_t ssh_ppl_queued_data_size(PacketProtocolLayer *ppl)
+{ return ppl->vt->queued_data_size(ppl); }
 static inline unsigned int ssh_ppl_winscp_query(PacketProtocolLayer *ppl, int query)
 { return ppl->vt->winscp_query(ppl, query); }
 
@@ -94,6 +97,11 @@ void ssh_ppl_setup_queues(PacketProtocolLayer *ppl,
  * avoid dereferencing itself on return from this function! */
 void ssh_ppl_replace(PacketProtocolLayer *old, PacketProtocolLayer *new);
 
+/* Default implementation of queued_data_size, which just adds up the
+ * sizes of all the packets in pq_out. A layer can override this if it
+ * has other things to take into account as well. */
+size_t ssh_ppl_default_queued_data_size(PacketProtocolLayer *ppl);
+
 PacketProtocolLayer *ssh1_login_new(
     Conf *conf, const char *host, int port,
     PacketProtocolLayer *successor_layer);

+ 4 - 4
source/putty/sshprng.c

@@ -183,7 +183,7 @@ static void prng_seed_BinarySink_write(
     prng *pr = BinarySink_DOWNCAST(bs, prng);
     prng_impl *pi = container_of(pr, prng_impl, Prng);
     assert(pi->keymaker);
-    prngdebug("prng: got %zu bytes of seed\n", len);
+    prngdebug("prng: got %"SIZEu" bytes of seed\n", len);
     put_data(pi->keymaker, data, len);
 }
 
@@ -234,7 +234,7 @@ void prng_read(prng *pr, void *vout, size_t size)
 
     assert(!pi->keymaker);
 
-    prngdebug("prng_read %zu\n", size);
+    prngdebug("prng_read %"SIZEu"\n", size);
 
     { // WINSCP
     uint8_t *out = (uint8_t *)vout;
@@ -264,7 +264,7 @@ void prng_add_entropy(prng *pr, unsigned source_id, ptrlen data)
         index++;
     }
 
-    prngdebug("prng_add_entropy source=%u size=%zu -> collector %zi\n",
+    prngdebug("prng_add_entropy source=%u size=%"SIZEu" -> collector %zi\n",
               source_id, data.len, index);
 
     put_datapl(pi->collectors[index], data);
@@ -283,7 +283,7 @@ void prng_add_entropy(prng *pr, unsigned source_id, ptrlen data)
         { // WINSCP
         size_t i; // WINSCP
         for (i = 0; i < NCOLLECTORS; i++) {
-            prngdebug("emptying collector %zu\n", i);
+            prngdebug("emptying collector %"SIZEu"\n", i);
             ssh_hash_final(pi->collectors[i], pi->pending_output);
             put_data(&pi->Prng, pi->pending_output, pi->hashalg->hlen);
             pi->collectors[i] = ssh_hash_new(pi->hashalg);

+ 4 - 4
source/putty/sshpubk.c

@@ -402,8 +402,8 @@ bool rsa_ssh1_savekey(const Filename *filename, RSAKey *key,
 /*
  * PuTTY's own format for SSH-2 keys is as follows:
  *
- * The file is text. Lines are terminated by CRLF, although CR-only
- * and LF-only are tolerated on input.
+ * The file is text. Lines are terminated by LF by preference,
+ * although CRLF and CR-only are tolerated on input.
  *
  * The first line says "PuTTY-User-Key-File-2: " plus the name of the
  * algorithm ("ssh-dss", "ssh-rsa" etc).
@@ -1396,8 +1396,8 @@ char *ssh1_pubkey_str(RSAKey *key)
 
     dec1 = mp_get_decimal(key->exponent);
     dec2 = mp_get_decimal(key->modulus);
-    buffer = dupprintf("%zd %s %s%s%s", mp_get_nbits(key->modulus), dec1, dec2,
-                       key->comment ? " " : "",
+    buffer = dupprintf("%"SIZEu" %s %s%s%s", mp_get_nbits(key->modulus),
+                       dec1, dec2, key->comment ? " " : "",
                        key->comment ? key->comment : "");
     sfree(dec1);
     sfree(dec2);

+ 21 - 3
source/putty/sshrsa.c

@@ -43,6 +43,24 @@ void BinarySource_get_rsa_ssh1_priv(
     rsa->private_exponent = get_mp_ssh1(src);
 }
 
+RSAKey *BinarySource_get_rsa_ssh1_priv_agent(BinarySource *src)
+{
+    RSAKey *rsa = snew(RSAKey);
+    memset(rsa, 0, sizeof(RSAKey));
+
+    get_rsa_ssh1_pub(src, rsa, RSA_SSH1_MODULUS_FIRST);
+    get_rsa_ssh1_priv(src, rsa);
+
+    /* SSH-1 names p and q the other way round, i.e. we have the
+     * inverse of p mod q and not of q mod p. We swap the names,
+     * because our internal RSA wants iqmp. */
+    rsa->iqmp = get_mp_ssh1(src);
+    rsa->q = get_mp_ssh1(src);
+    rsa->p = get_mp_ssh1(src);
+
+    return rsa;
+}
+
 bool rsa_ssh1_encrypt(unsigned char *data, int length, RSAKey *key)
 {
     mp_int *b1, *b2;
@@ -294,7 +312,7 @@ char *rsa_ssh1_fingerprint(RSAKey *key)
     ssh_hash_final(hash, digest);
 
     out = strbuf_new();
-    strbuf_catf(out, "%d ", mp_get_nbits(key->modulus));
+    strbuf_catf(out, "%"SIZEu" ", mp_get_nbits(key->modulus));
     for (i = 0; i < 16; i++)
         strbuf_catf(out, "%s%02x", i ? ":" : "", digest[i]);
     if (key->comment)
@@ -788,7 +806,7 @@ char *rsa2_invalid(ssh_key *key, unsigned flags)
     const ssh_hashalg *halg = rsa2_hash_alg_for_flags(flags, &sign_alg_name);
     if (nbytes < rsa_pkcs1_length_of_fixed_parts(halg)) {
         return dupprintf(
-            "%zu-bit RSA key is too short to generate %s signatures",
+            "%"SIZEu"-bit RSA key is too short to generate %s signatures",
             bits, sign_alg_name);
     }
 
@@ -1009,7 +1027,7 @@ mp_int *ssh_rsakex_decrypt(
         if (out[i] == 1) {
             i++;  /* skip over the 1 byte */
             break;
-        } else if (out[i] != 1) {
+        } else if (out[i] != 0) {
             sfree(out);
             return NULL;
         }

+ 9 - 6
source/putty/sshshare.c

@@ -705,8 +705,8 @@ static void share_remove_forwarding(struct ssh_sharing_connstate *cs,
     sfree(fwd);
 }
 
-static void log_downstream(struct ssh_sharing_connstate *cs,
-                           const char *logfmt, ...)
+static PRINTF_LIKE(2, 3) void log_downstream(struct ssh_sharing_connstate *cs,
+                                             const char *logfmt, ...)
 {
     va_list ap;
     char *buf;
@@ -719,8 +719,8 @@ static void log_downstream(struct ssh_sharing_connstate *cs,
     sfree(buf);
 }
 
-static void log_general(struct ssh_sharing_state *sharestate,
-                        const char *logfmt, ...)
+static PRINTF_LIKE(2, 3) void log_general(struct ssh_sharing_state *sharestate,
+                                          const char *logfmt, ...)
 {
     va_list ap;
     char *buf;
@@ -1794,8 +1794,11 @@ static void share_receive(Plug *plug, int urgent, const char *data, size_t len)
     }
     if (cs->recvlen > 0 && cs->recvbuf[cs->recvlen-1] == '\015')
         cs->recvlen--;                 /* trim off \r before \n */
+    { // WINSCP
+    ptrlen verstring = make_ptrlen(cs->recvbuf, cs->recvlen);
     log_downstream(cs, "Downstream version string: %.*s",
-                   cs->recvlen, cs->recvbuf);
+                   PTRLEN_PRINTF(verstring));
+    } // WINSCP
     cs->got_verstring = true;
 
     /*
@@ -1858,7 +1861,7 @@ static void share_listen_closing(Plug *plug, const char *error_msg,
 static void share_send_verstring(ssh_sharing_connstate *cs)
 {
     char *fullstring = dupcat("[email protected]",
-                              cs->parent->server_verstring, "\015\012", NULL);
+                              cs->parent->server_verstring, "\015\012");
     sk_write(cs->sock, fullstring, strlen(fullstring));
     sfree(fullstring);
 

+ 1 - 2
source/putty/sshverstring.c

@@ -303,8 +303,7 @@ void ssh_verstring_handle_input(BinaryPacketProtocol *bpp)
     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';
+        strbuf_shrink_by(s->vstring, 1);
 
     bpp_logevent("Remote version: %s", s->vstring->s);
 

+ 1 - 1
source/putty/tree234.c

@@ -1072,7 +1072,7 @@ int n_errors = 0;
 /*
  * Error reporting function.
  */
-void error(char *fmt, ...)
+PRINTF_LIKE(1, 2) void error(char *fmt, ...)
 {
     va_list ap;
     printf("ERROR: ");

+ 38 - 4
source/putty/utils.c

@@ -182,10 +182,21 @@ int main(void)
     printf("passed %d failed %d total %d\n", passes, fails, passes+fails);
     return fails != 0 ? 1 : 0;
 }
+
 /* Stubs to stop the rest of this module causing compile failures. */
-void modalfatalbox(const char *fmt, ...) {}
-int conf_get_int(Conf *conf, int primary) { return 0; }
-char *conf_get_str(Conf *conf, int primary) { return NULL; }
+static NORETURN void fatal_error(const char *p, ...)
+{
+    va_list ap;
+    fprintf(stderr, "host_string_test: ");
+    va_start(ap, p);
+    vfprintf(stderr, p, ap);
+    va_end(ap);
+    fputc('\n', stderr);
+    exit(1);
+}
+
+void out_of_memory(void) { fatal_error("out of memory"); }
+
 #endif /* TEST_HOST_STRFOO */
 
 /*
@@ -249,7 +260,7 @@ char *dupstr(const char *s)
 }
 
 /* Allocate the concatenation of N strings. Terminate arg list with NULL. */
-char *dupcat(const char *s1, ...)
+char *dupcat_fn(const char *s1, ...)
 {
     int len;
     char *p, *q, *sn;
@@ -429,6 +440,29 @@ void *strbuf_append(strbuf *buf_o, size_t len)
     return toret;
 }
 
+void strbuf_shrink_to(strbuf *buf, size_t new_len)
+{
+    assert(new_len <= buf->len);
+    buf->len = new_len;
+    buf->s[buf->len] = '\0';
+}
+
+void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove)
+{
+    assert(amount_to_remove <= buf->len);
+    buf->len -= amount_to_remove;
+    buf->s[buf->len] = '\0';
+}
+
+bool strbuf_chomp(strbuf *buf, char char_to_remove)
+{
+    if (buf->len > 0 && buf->s[buf->len-1] == char_to_remove) {
+        strbuf_shrink_by(buf, 1);
+        return true;
+    }
+    return false;
+}
+
 static void strbuf_BinarySink_write(
     BinarySink *bs, const void *data, size_t len)
 {

+ 5 - 5
source/putty/version.h

@@ -1,6 +1,6 @@
 /* Generated by automated build script */
-#define RELEASE 0.73
-#define TEXTVER "Release 0.73"
-#define SSHVER "-Release-0.73"
-#define BINARY_VERSION 0,73,0,0
-#define SOURCE_COMMIT "745ed3ad3beaf52fc623827e770b3a068b238dd5"
+#define RELEASE 0.74
+#define TEXTVER "Release 0.74"
+#define SSHVER "-Release-0.74"
+#define BINARY_VERSION 0,74,0,0
+#define SOURCE_COMMIT "014d4fb151369f255b3debed7d15a154fd9036f5"

+ 1 - 1
source/putty/windows/wingss.c

@@ -319,7 +319,7 @@ static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib,
     if (host == NULL) return SSH_GSS_FAILURE;
 
     /* copy it into form host/FQDN */
-    pStr = dupcat("host/", host, NULL);
+    pStr = dupcat("host/", host);
 
     *srv_name = (Ssh_gss_name) pStr;
 

+ 1 - 1
source/putty/windows/winmisc.c

@@ -281,7 +281,7 @@ HMODULE load_system32_dll(const char *libname)
             sgrowarray(sysdir, sysdirsize, len);
     }
 
-    fullpath = dupcat(sysdir, "\\", libname, NULL);
+    fullpath = dupcat(sysdir, "\\", libname);
     ret = LoadLibrary(fullpath);
     sfree(fullpath);
     return ret;

+ 1 - 2
source/putty/windows/winnet.c

@@ -1946,8 +1946,7 @@ char *get_hostname(void)
     return dupstr(hostbuf);
 }
 
-SockAddr *platform_get_x11_unix_address(const char *display, int displaynum,
-                                       char **canonicalname)
+SockAddr *platform_get_x11_unix_address(const char *display, int displaynum)
 {
     SockAddr *ret = snew(SockAddr);
     memset(ret, 0, sizeof(SockAddr));

+ 10 - 14
source/putty/windows/winstore.c

@@ -193,7 +193,7 @@ FontSpec *read_setting_fontspec(settings_r *handle, const char *name)
     if (!fontname)
         return NULL;
 
-    settingname = dupcat(name, "IsBold", NULL);
+    settingname = dupcat(name, "IsBold");
     isbold = read_setting_i(handle, settingname, -1);
     sfree(settingname);
     if (isbold == -1) {
@@ -201,7 +201,7 @@ FontSpec *read_setting_fontspec(settings_r *handle, const char *name)
         return NULL;
     }
 
-    settingname = dupcat(name, "CharSet", NULL);
+    settingname = dupcat(name, "CharSet");
     charset = read_setting_i(handle, settingname, -1);
     sfree(settingname);
     if (charset == -1) {
@@ -209,7 +209,7 @@ FontSpec *read_setting_fontspec(settings_r *handle, const char *name)
         return NULL;
     }
 
-    settingname = dupcat(name, "Height", NULL);
+    settingname = dupcat(name, "Height");
     height = read_setting_i(handle, settingname, INT_MIN);
     sfree(settingname);
     if (height == INT_MIN) {
@@ -228,13 +228,13 @@ void write_setting_fontspec(settings_w *handle,
     char *settingname;
 
     write_setting_s(handle, name, font->name);
-    settingname = dupcat(name, "IsBold", NULL);
+    settingname = dupcat(name, "IsBold");
     write_setting_i(handle, settingname, font->isbold);
     sfree(settingname);
-    settingname = dupcat(name, "CharSet", NULL);
+    settingname = dupcat(name, "CharSet");
     write_setting_i(handle, settingname, font->charset);
     sfree(settingname);
-    settingname = dupcat(name, "Height", NULL);
+    settingname = dupcat(name, "Height");
     write_setting_i(handle, settingname, font->height);
     sfree(settingname);
 }
@@ -593,15 +593,13 @@ static HANDLE access_random_seed(int action)
         char profile[MAX_PATH + 1];
         if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA,
                                          NULL, SHGFP_TYPE_CURRENT, profile)) &&
-            try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND",
-                                            (const char *)NULL),
+            try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"),
                                      action, &rethandle))
             return rethandle;
 
         if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_APPDATA,
                                          NULL, SHGFP_TYPE_CURRENT, profile)) &&
-            try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND",
-                                            (const char *)NULL),
+            try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"),
                                      action, &rethandle))
             return rethandle;
     }
@@ -624,8 +622,7 @@ static HANDLE access_random_seed(int action)
 
         if (drvlen < lenof(drv) && pathlen < lenof(path) && pathlen > 0 &&
             try_random_seed_and_free(
-                dupcat(drv, path, "\\PUTTY.RND", (const char *)NULL),
-                action, &rethandle))
+                dupcat(drv, path, "\\PUTTY.RND"), action, &rethandle))
             return rethandle;
     }
 
@@ -637,8 +634,7 @@ static HANDLE access_random_seed(int action)
         DWORD len = GetWindowsDirectory(windir, sizeof(windir));
         if (len < lenof(windir) &&
             try_random_seed_and_free(
-                dupcat(windir, "\\PUTTY.RND", (const char *)NULL),
-                action, &rethandle))
+                dupcat(windir, "\\PUTTY.RND"), action, &rethandle))
             return rethandle;
     }
 

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

@@ -357,7 +357,7 @@ DECL_WINDOWS_FUNCTION(GLOBAL, int, select,
  * Provided by each client of winnet.c, and called by winnet.c to turn
  * on or off WSA*Select for a given socket.
  */
-char *do_select(Plug * plug, SOCKET skt, bool startup); // WINSCP
+char *do_select(Plug * plug, SOCKET skt, bool enable); // WINSCP
 
 /*
  * Network-subsystem-related functions provided in other Windows modules.