瀏覽代碼

Merge branch 'thirdparty_dev' into dev

# Conflicts:
#	source/putty/putty.h
#	source/putty/ssh.c
#	source/putty/ssh.h

Source commit: d0b35e8c439c5443b2c3face135e64348559cdd1
Martin Prikryl 6 年之前
父節點
當前提交
bca28b11c2

+ 4 - 0
source/putty/defs.h

@@ -70,6 +70,8 @@ typedef struct settings_w settings_w;
 typedef struct settings_r settings_r;
 typedef struct settings_e settings_e;
 
+typedef struct SessionSpecial SessionSpecial;
+
 /* Note indirection: for historical reasons (it used to be closer to
  * the OS socket type), the type that most code uses for a socket is
  * 'Socket', not 'Socket *'. So an implementation of Socket or Plug
@@ -90,6 +92,8 @@ typedef struct ptrlen {
 
 typedef struct logblank_t logblank_t;
 
+typedef struct BinaryPacketProtocol BinaryPacketProtocol;
+
 /* Do a compile-time type-check of 'to_check' (without evaluating it),
  * as a side effect of returning the value 'to_return'. Note that
  * although this macro double-*expands* to_return, it always

+ 2 - 11
source/putty/logging.c

@@ -219,20 +219,11 @@ void logtraffic(LogContext *ctx, unsigned char c, int logmode)
 }
 
 /*
- * Log an Event Log entry. Used in SSH packet logging mode; this is
- * also as convenient a place as any to put the output of Event Log
- * entries to stderr when a command-line tool is in verbose mode.
- * (In particular, this is a better place to put it than in the
- * front ends, because it only has to be done once for all
- * platforms. Platforms which don't have a meaningful stderr can
- * just avoid defining FLAG_STDERR.
+ * Log an Event Log entry. Used in SSH packet logging mode, to copy
+ * the Event Log entries into the same log file as the packet data.
  */
 void log_eventlog(LogContext *ctx, const char *event)
 {
-    if ((flags & FLAG_STDERR) && (flags & FLAG_VERBOSE)) {
-	fprintf(stderr, "%s\n", event);
-	fflush(stderr);
-    }
     /* If we don't have a context yet (eg winnet.c init) then skip entirely */
     if (!ctx)
 	return;

+ 62 - 34
source/putty/putty.h

@@ -172,34 +172,67 @@ struct unicode_data {
 #define LGTYP_PACKETS 3		       /* logmode: SSH data packets */
 #define LGTYP_SSHRAW 4		       /* logmode: SSH raw data */
 
+/*
+ * Enumeration of 'special commands' that can be sent during a
+ * session, separately from the byte stream of ordinary session data.
+ */
 typedef enum {
-    /* Actual special commands. Originally Telnet, but some codes have
-     * been re-used for similar specials in other protocols. */
-    TS_AYT, TS_BRK, TS_SYNCH, TS_EC, TS_EL, TS_GA, TS_NOP, TS_ABORT,
-    TS_AO, TS_IP, TS_SUSP, TS_EOR, TS_EOF, TS_LECHO, TS_RECHO, TS_PING,
-    TS_EOL,
-    /* Special command for SSH. */
-    TS_REKEY,
-    /* POSIX-style signals. (not Telnet) */
-    TS_SIGABRT, TS_SIGALRM, TS_SIGFPE,  TS_SIGHUP,  TS_SIGILL,
-    TS_SIGINT,  TS_SIGKILL, TS_SIGPIPE, TS_SIGQUIT, TS_SIGSEGV,
-    TS_SIGTERM, TS_SIGUSR1, TS_SIGUSR2,
-    /* Pseudo-specials used for constructing the specials menu. */
-    TS_SEP,	    /* Separator */
-    TS_SUBMENU,	    /* Start a new submenu with specified name */
-    TS_EXITMENU,    /* Exit current submenu or end of specials */
-    /* Starting point for protocols to invent special-action codes
-     * that can't live in this enum at all, e.g. because they change
-     * with every session.
-     *
-     * Of course, this must remain the last value in this
-     * enumeration. */
-    TS_LOCALSTART
-} Telnet_Special;
+    /*
+     * Commands that are generally useful in multiple backends.
+     */
+    SS_BRK,    /* serial-line break */
+    SS_EOF,    /* end-of-file on session input */
+    SS_NOP,    /* transmit data with no effect */
+    SS_PING,   /* try to keep the session alive (probably, but not
+                * necessarily, implemented as SS_NOP) */
+
+    /*
+     * Commands specific to Telnet.
+     */
+    SS_AYT,    /* Are You There */
+    SS_SYNCH,  /* Synch */
+    SS_EC,     /* Erase Character */
+    SS_EL,     /* Erase Line */
+    SS_GA,     /* Go Ahead */
+    SS_ABORT,  /* Abort Process */
+    SS_AO,     /* Abort Output */
+    SS_IP,     /* Interrupt Process */
+    SS_SUSP,   /* Suspend Process */
+    SS_EOR,    /* End Of Record */
+    SS_EOL,    /* Telnet end-of-line sequence (CRLF, as opposed to CR
+                * NUL that escapes a literal CR) */
+
+    /*
+     * Commands specific to SSH.
+     */
+    SS_REKEY,  /* trigger an immediate repeat key exchange */
+    SS_XCERT,  /* cross-certify another host key ('arg' indicates which) */
+
+    /*
+     * Send a POSIX-style signal. (Useful in SSH and also pterm.)
+     */
+    SS_SIGABRT, SS_SIGALRM, SS_SIGFPE,  SS_SIGHUP,  SS_SIGILL,
+    SS_SIGINT,  SS_SIGKILL, SS_SIGPIPE, SS_SIGQUIT, SS_SIGSEGV,
+    SS_SIGTERM, SS_SIGUSR1, SS_SIGUSR2,
+
+    /*
+     * These aren't really special commands, but they appear in the
+     * enumeration because the list returned from
+     * backend_get_specials() will use them to specify the structure
+     * of the GUI specials menu.
+     */
+    SS_SEP,	    /* Separator */
+    SS_SUBMENU,	    /* Start a new submenu with specified name */
+    SS_EXITMENU,    /* Exit current submenu, or end of entire specials list */
+} SessionSpecialCode;
 
-struct telnet_special {
+/*
+ * The structure type returned from backend_get_specials.
+ */
+struct SessionSpecial {
     const char *name;
-    int code;
+    SessionSpecialCode code;
+    int arg;
 };
 
 typedef enum {
@@ -457,8 +490,8 @@ struct Backend_vtable {
     /* sendbuffer() does the same thing but without attempting a send */
     int (*sendbuffer) (Backend *be);
     void (*size) (Backend *be, int width, int height);
-    void (*special) (Backend *be, Telnet_Special code);
-    const struct telnet_special *(*get_specials) (Backend *be);
+    void (*special) (Backend *be, SessionSpecialCode code, int arg);
+    const SessionSpecial *(*get_specials) (Backend *be);
     int (*connected) (Backend *be);
     int (*exitcode) (Backend *be);
     /* If back->sendok() returns FALSE, the backend doesn't currently
@@ -488,7 +521,7 @@ struct Backend_vtable {
 #define backend_send(be, buf, len) ((be)->vt->send(be, buf, len))
 #define backend_sendbuffer(be) ((be)->vt->sendbuffer(be))
 #define backend_size(be, w, h) ((be)->vt->size(be, w, h))
-#define backend_special(be, code) ((be)->vt->special(be, code))
+#define backend_special(be, code, arg) ((be)->vt->special(be, code, arg))
 #define backend_get_specials(be) ((be)->vt->get_specials(be))
 #define backend_connected(be) ((be)->vt->connected(be))
 #define backend_exitcode(be) ((be)->vt->exitcode(be))
@@ -520,10 +553,6 @@ extern const char *const appname;
  *
  * FLAG_VERBOSE is set when the user requests verbose details.
  *
- * FLAG_STDERR is set in command-line applications (which have a
- * functioning stderr that it makes sense to write to) and not in
- * GUI applications (which don't).
- *
  * FLAG_INTERACTIVE is set when a full interactive shell session is
  * being run, _either_ because no remote command has been provided
  * _or_ because the application is GUI and can't run non-
@@ -538,8 +567,7 @@ extern const char *const appname;
  * avoid collision.
  */
 #define FLAG_VERBOSE     0x0001
-#define FLAG_STDERR      0x0002
-#define FLAG_INTERACTIVE 0x0004
+#define FLAG_INTERACTIVE 0x0002
 GLOBAL int flags;
 
 /*

File diff suppressed because it is too large
+ 98 - 552
source/putty/ssh.c


+ 26 - 8
source/putty/ssh.h

@@ -62,7 +62,6 @@ typedef struct PktIn {
     int refcount;
     int type;
     unsigned long sequence; /* SSH-2 incoming sequence number */
-    long encrypted_len;	    /* for SSH-2 total-size counting */
     PacketQueueNode qnode;  /* for linking this packet on to a queue */
     BinarySource_IMPLEMENTATION;
 } PktIn;
@@ -74,7 +73,6 @@ typedef struct PktOut {
     long minlen;            /* SSH-2: ensure wire length is at least this */
     unsigned char *data;    /* allocated storage */
     long maxlen;	    /* amount of storage allocated for `data' */
-    long encrypted_len;	    /* for SSH-2 total-size counting */
 
     /* Extra metadata used in SSH packet logging mode, allowing us to
      * log in the packet header line that the packet came from a
@@ -102,10 +100,10 @@ typedef struct PktOutQueue {
     PktOut *(*get)(PacketQueueBase *, int pop);
 } PktOutQueue;
 
-void pq_base_init(PacketQueueBase *pqb);
 void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node);
 void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node);
-int pq_base_empty_on_to_front_of(PacketQueueBase *src, PacketQueueBase *dest);
+void pq_base_concatenate(PacketQueueBase *dest,
+                         PacketQueueBase *q1, PacketQueueBase *q2);
 
 void pq_in_init(PktInQueue *pq);
 void pq_out_init(PktOutQueue *pq);
@@ -120,10 +118,12 @@ void pq_out_clear(PktOutQueue *pq);
               pq_base_push_front(&(pq)->pqb, &(pkt)->qnode))
 #define pq_peek(pq) ((pq)->get(&(pq)->pqb, FALSE))
 #define pq_pop(pq) ((pq)->get(&(pq)->pqb, TRUE))
-#define pq_empty_on_to_front_of(src, dst)                               \
-    TYPECHECK((src)->get(&(src)->pqb, FALSE) ==                         \
+#define pq_concatenate(dst, q1, q2)                                      \
+    TYPECHECK((q1)->get(&(q1)->pqb, FALSE) ==                           \
+              (dst)->get(&(dst)->pqb, FALSE) &&                         \
+              (q2)->get(&(q2)->pqb, FALSE) ==                           \
               (dst)->get(&(dst)->pqb, FALSE),                           \
-              pq_base_empty_on_to_front_of(&(src)->pqb, &(dst)->pqb))
+              pq_base_concatenate(&(dst)->pqb, &(q1)->pqb, &(q2)->pqb))
 
 /*
  * Packet type contexts, so that ssh2_pkt_type can correctly decode
@@ -1345,4 +1345,22 @@ enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_LOG2_ENUM) };
 enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_REAL_ENUM) };
 #undef TMP_DECLARE_REAL_ENUM
 
-#endif // WINSCP_VS
+/* Shared function that writes tty modes into a pty request */
+void write_ttymodes_to_packet_from_conf(
+    BinarySink *bs, Frontend *frontend, Conf *conf,
+    int ssh_version, int ospeed, int ispeed);
+
+/* Shared system for allocating local SSH channel ids. Expects to be
+ * passed a tree full of structs that have a field called 'localid' of
+ * type unsigned, and will check that! */
+unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset);
+#define alloc_channel_id(tree, type) \
+    TYPECHECK(&((type *)0)->localid == (unsigned *)0, \
+              alloc_channel_id_general(tree, offsetof(type, localid)))
+
+int first_in_commasep_string(char const *needle, char const *haystack,
+                             int haylen);
+int in_commasep_string(char const *needle, char const *haystack, int haylen);
+void add_to_commasep(strbuf *buf, const char *data);
+
+#endif // WINSCP_VS

+ 18 - 7
source/putty/ssh1bpp.c

@@ -21,6 +21,7 @@ struct ssh1_bpp_state {
 
     struct crcda_ctx *crcda_ctx;
 
+    int pending_compression_request;
     ssh_compressor *compctx;
     ssh_decompressor *decompctx;
 
@@ -32,7 +33,7 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp);
 static PktOut *ssh1_bpp_new_pktout(int type);
 static void ssh1_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt);
 
-const struct BinaryPacketProtocolVtable ssh1_bpp_vtable = {
+static const struct BinaryPacketProtocolVtable ssh1_bpp_vtable = {
     ssh1_bpp_free,
     ssh1_bpp_handle_input,
     ssh1_bpp_new_pktout,
@@ -82,17 +83,13 @@ void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
     }
 }
 
-void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp)
+void ssh1_bpp_requested_compression(BinaryPacketProtocol *bpp)
 {
     struct ssh1_bpp_state *s;
     assert(bpp->vt == &ssh1_bpp_vtable);
     s = FROMFIELD(bpp, struct ssh1_bpp_state, bpp);
 
-    assert(!s->compctx);
-    assert(!s->decompctx);
-
-    s->compctx = ssh_compressor_new(&ssh_zlib);
-    s->decompctx = ssh_decompressor_new(&ssh_zlib);
+    s->pending_compression_request = TRUE;
 }
 
 static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
@@ -210,6 +207,20 @@ static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp)
 
             if (type == SSH1_MSG_DISCONNECT)
                 s->bpp.seen_disconnect = TRUE;
+
+            if (type == SSH1_SMSG_SUCCESS && s->pending_compression_request) {
+                assert(!s->compctx);
+                assert(!s->decompctx);
+
+                s->compctx = ssh_compressor_new(&ssh_zlib);
+                s->decompctx = ssh_decompressor_new(&ssh_zlib);
+
+                s->pending_compression_request = FALSE;
+            }
+
+            if (type == SSH1_SMSG_FAILURE && s->pending_compression_request) {
+                s->pending_compression_request = FALSE;
+            }
         }
     }
     crFinishV;

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

@@ -25,7 +25,7 @@ static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp);
 static PktOut *ssh2_bare_bpp_new_pktout(int type);
 static void ssh2_bare_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *);
 
-const struct BinaryPacketProtocolVtable ssh2_bare_bpp_vtable = {
+static const struct BinaryPacketProtocolVtable ssh2_bare_bpp_vtable = {
     ssh2_bare_bpp_free,
     ssh2_bare_bpp_handle_input,
     ssh2_bare_bpp_new_pktout,
@@ -79,8 +79,6 @@ static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
         s->pktin->refcount = 1;
         s->data = snew_plus_get_aux(s->pktin);
 
-        s->pktin->encrypted_len = s->packetlen;
-
         s->pktin->sequence = s->incoming_sequence++;
 
         /*

+ 8 - 4
source/putty/ssh2bpp.c

@@ -24,6 +24,7 @@ struct ssh2_bpp_state {
     unsigned char *data;
     unsigned cipherblk;
     PktIn *pktin;
+    struct DataTransferStats *stats;
 
     struct ssh2_bpp_direction in, out;
     /* comp and decomp logically belong in the per-direction
@@ -41,18 +42,19 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp);
 static PktOut *ssh2_bpp_new_pktout(int type);
 static void ssh2_bpp_format_packet(BinaryPacketProtocol *bpp, PktOut *pkt);
 
-const struct BinaryPacketProtocolVtable ssh2_bpp_vtable = {
+static const struct BinaryPacketProtocolVtable ssh2_bpp_vtable = {
     ssh2_bpp_free,
     ssh2_bpp_handle_input,
     ssh2_bpp_new_pktout,
     ssh2_bpp_format_packet,
 };
 
-BinaryPacketProtocol *ssh2_bpp_new(void)
+BinaryPacketProtocol *ssh2_bpp_new(struct DataTransferStats *stats)
 {
     struct ssh2_bpp_state *s = snew(struct ssh2_bpp_state);
     memset(s, 0, sizeof(*s));
     s->bpp.vt = &ssh2_bpp_vtable;
+    s->stats = stats;
     return &s->bpp;
 }
 
@@ -407,7 +409,8 @@ static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp)
         s->payload = s->len - s->pad - 1;
 
         s->length = s->payload + 5;
-        s->pktin->encrypted_len = s->packetlen;
+
+        DTS_CONSUME(s->stats, in, s->packetlen);
 
         s->pktin->sequence = s->in.sequence++;
 
@@ -601,7 +604,8 @@ static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt)
     }
 
     s->out.sequence++;       /* whether or not we MACed */
-    pkt->encrypted_len = origlen + padding;
+
+    DTS_CONSUME(s->stats, out, origlen + padding);
 
 }
 

+ 2 - 2
source/putty/sshaes.c

@@ -50,8 +50,6 @@
 #    define INLINE
 #endif
 
-typedef struct AESContext AESContext;
-
 struct AESContext {
     word32 keysched_buf[(MAX_NR + 1) * NB + 3];
     word32 invkeysched_buf[(MAX_NR + 1) * NB + 3];
@@ -1250,6 +1248,7 @@ INLINE static int supports_aes_ni()
  * Wrapper of SHUFPD instruction for MSVC
  */
 #ifdef _MSC_VER
+FUNC_ISA
 INLINE static __m128i mm_shuffle_pd_i0(__m128i a, __m128i b)
 {
     union {
@@ -1262,6 +1261,7 @@ INLINE static __m128i mm_shuffle_pd_i0(__m128i a, __m128i b)
     return ru.i;
 }
 
+FUNC_ISA
 INLINE static __m128i mm_shuffle_pd_i1(__m128i a, __m128i b)
 {
     union {

+ 31 - 2
source/putty/sshbpp.h

@@ -34,9 +34,38 @@ BinaryPacketProtocol *ssh1_bpp_new(void);
 void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
                          const struct ssh1_cipheralg *cipher,
                          const void *session_key);
-void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp);
+/* requested_compression() notifies the SSH-1 BPP that we've just sent
+ * a request to enable compression, which means that on receiving the
+ * next SSH1_SMSG_SUCCESS or SSH1_SMSG_FAILURE message, it should set
+ * up zlib compression if it was SUCCESS. */
+void ssh1_bpp_requested_compression(BinaryPacketProtocol *bpp);
 
-BinaryPacketProtocol *ssh2_bpp_new(void);
+/*
+ * Structure that tracks how much data is sent and received, for
+ * purposes of triggering an SSH-2 rekey when either one gets over a
+ * configured limit. In each direction, the flag 'running' indicates
+ * that we haven't hit the limit yet, and 'remaining' tracks how much
+ * longer until we do. The macro DTS_CONSUME subtracts a given amount
+ * from the counter in a particular direction, and evaluates to a
+ * boolean indicating whether the limit has been hit.
+ *
+ * The limit is sticky: once 'running' has flipped to false,
+ * 'remaining' is no longer decremented, so it shouldn't dangerously
+ * wrap round.
+ */
+struct DataTransferStats {
+    struct {
+        int running;
+        unsigned long remaining;
+    } in, out;
+};
+#define DTS_CONSUME(stats, direction, size)             \
+    ((stats)->direction.running &&                      \
+     (stats)->direction.remaining <= (size) ?           \
+     ((stats)->direction.running = FALSE, TRUE) :       \
+     ((stats)->direction.remaining -= (size), FALSE))
+
+BinaryPacketProtocol *ssh2_bpp_new(struct DataTransferStats *stats);
 void ssh2_bpp_new_outgoing_crypto(
     BinaryPacketProtocol *bpp,
     const struct ssh2_cipheralg *cipher, const void *ckey, const void *iv,

+ 12 - 0
source/putty/sshchan.h

@@ -56,6 +56,18 @@ void chan_remotely_opened_failure(Channel *chan, const char *errtext);
  * closing until both directions have had an EOF */
 int chan_no_eager_close(Channel *, int, int);
 
+/*
+ * Constructor for a trivial do-nothing implementation of
+ * ChannelVtable. Used for 'zombie' channels, i.e. channels whose
+ * proper local source of data has been shut down or otherwise stopped
+ * existing, but the SSH side is still there and needs some kind of a
+ * Channel implementation to talk to. In particular, the want_close
+ * method for this channel always returns 'yes, please close this
+ * channel asap', regardless of whether local and/or remote EOF have
+ * been sent - indeed, even if _neither_ has.
+ */
+Channel *zombiechan_new(void);
+
 /* ----------------------------------------------------------------------
  * This structure is owned by an SSH connection layer, and identifies
  * the connection layer's end of the channel, for the Channel

+ 522 - 12
source/putty/sshcommon.c

@@ -4,7 +4,9 @@
  */
 
 #include <assert.h>
+#include <stdlib.h>
 
+#include "putty.h"
 #include "ssh.h"
 #include "sshchan.h"
 
@@ -88,22 +90,66 @@ void pq_out_clear(PktOutQueue *pq)
         ssh_free_pktout(pkt);
 }
 
-int pq_base_empty_on_to_front_of(PacketQueueBase *src, PacketQueueBase *dest)
+/*
+ * Concatenate the contents of the two queues q1 and q2, and leave the
+ * result in qdest. qdest must be either empty, or one of the input
+ * queues.
+ */
+void pq_base_concatenate(PacketQueueBase *qdest,
+                         PacketQueueBase *q1, PacketQueueBase *q2)
 {
-    struct PacketQueueNode *srcfirst, *srclast;
+    struct PacketQueueNode *head1, *tail1, *head2, *tail2;
 
-    if (src->end.next == &src->end)
-        return FALSE;
+    /*
+     * Extract the contents from both input queues, and empty them.
+     */
 
-    srcfirst = src->end.next;
-    srclast = src->end.prev;
-    srcfirst->prev = &dest->end;
-    srclast->next = dest->end.next;
-    srcfirst->prev->next = srcfirst;
-    srclast->next->prev = srclast;
-    src->end.next = src->end.prev = &src->end;
+    head1 = (q1->end.next == &q1->end ? NULL : q1->end.next);
+    tail1 = (q1->end.prev == &q1->end ? NULL : q1->end.prev);
+    head2 = (q2->end.next == &q2->end ? NULL : q2->end.next);
+    tail2 = (q2->end.prev == &q2->end ? NULL : q2->end.prev);
 
-    return TRUE;
+    q1->end.next = q1->end.prev = &q1->end;
+    q2->end.next = q2->end.prev = &q2->end;
+
+    /*
+     * Link the two lists together, handling the case where one or
+     * both is empty.
+     */
+
+    if (tail1)
+        tail1->next = head2;
+    else
+        head1 = head2;
+
+    if (head2)
+        head2->prev = tail1;
+    else
+        tail2 = tail1;
+
+    /*
+     * Check the destination queue is currently empty. (If it was one
+     * of the input queues, then it will be, because we emptied both
+     * of those just a moment ago.)
+     */
+
+    assert(qdest->end.next == &qdest->end);
+    assert(qdest->end.prev == &qdest->end);
+
+    /*
+     * If our concatenated list has anything in it, then put it in
+     * dest.
+     */
+
+    if (!head1) {
+        assert(!tail2);
+    } else {
+        assert(tail2);
+        qdest->end.next = head1;
+        qdest->end.prev = tail2;
+        head1->prev = &qdest->end;
+        tail2->next = &qdest->end;
+    }
 }
 
 /* ----------------------------------------------------------------------
@@ -159,3 +205,467 @@ void ssh_free_pktout(PktOut *pkt)
     sfree(pkt->data);
     sfree(pkt);
 }
+/* ----------------------------------------------------------------------
+ * Implement zombiechan_new() and its trivial vtable.
+ */
+
+static void zombiechan_free(Channel *chan);
+static int zombiechan_send(Channel *chan, int is_stderr, const void *, int);
+static void zombiechan_set_input_wanted(Channel *chan, int wanted);
+static void zombiechan_do_nothing(Channel *chan);
+static void zombiechan_open_failure(Channel *chan, const char *);
+static int zombiechan_want_close(Channel *chan, int sent_eof, int rcvd_eof);
+static char *zombiechan_log_close_msg(Channel *chan) { return NULL; }
+
+static const struct ChannelVtable zombiechan_channelvt = {
+    zombiechan_free,
+    zombiechan_do_nothing,             /* open_confirmation */
+    zombiechan_open_failure,
+    zombiechan_send,
+    zombiechan_do_nothing,             /* send_eof */
+    zombiechan_set_input_wanted,
+    zombiechan_log_close_msg,
+    zombiechan_want_close,
+};
+
+Channel *zombiechan_new(void)
+{
+    Channel *chan = snew(Channel);
+    chan->vt = &zombiechan_channelvt;
+    chan->initial_fixed_window_size = 0;
+    return chan;
+}
+
+static void zombiechan_free(Channel *chan)
+{
+    assert(chan->vt == &zombiechan_channelvt);
+    sfree(chan);
+}
+
+static void zombiechan_do_nothing(Channel *chan)
+{
+    assert(chan->vt == &zombiechan_channelvt);
+}
+
+static void zombiechan_open_failure(Channel *chan, const char *errtext)
+{
+    assert(chan->vt == &zombiechan_channelvt);
+}
+
+static int zombiechan_send(Channel *chan, int is_stderr,
+                           const void *data, int length)
+{
+    assert(chan->vt == &zombiechan_channelvt);
+    return 0;
+}
+
+static void zombiechan_set_input_wanted(Channel *chan, int enable)
+{
+    assert(chan->vt == &zombiechan_channelvt);
+}
+
+static int zombiechan_want_close(Channel *chan, int sent_eof, int rcvd_eof)
+{
+    return TRUE;
+}
+
+/* ----------------------------------------------------------------------
+ * Centralised standard methods for other channel implementations to
+ * borrow.
+ */
+
+void chan_remotely_opened_confirmation(Channel *chan)
+{
+    assert(0 && "this channel type should never receive OPEN_CONFIRMATION");
+}
+
+void chan_remotely_opened_failure(Channel *chan, const char *errtext)
+{
+    assert(0 && "this channel type should never receive OPEN_FAILURE");
+}
+
+int chan_no_eager_close(Channel *chan, int sent_local_eof, int rcvd_remote_eof)
+{
+    return FALSE;     /* default: never proactively ask for a close */
+}
+
+/* ----------------------------------------------------------------------
+ * Common routine to marshal tty modes into an SSH packet.
+ */
+
+void write_ttymodes_to_packet_from_conf(
+    BinarySink *bs, Frontend *frontend, Conf *conf,
+    int ssh_version, int ospeed, int ispeed)
+{
+    int i;
+
+    /*
+     * Codes for terminal modes.
+     * Most of these are the same in SSH-1 and SSH-2.
+     * This list is derived from RFC 4254 and
+     * SSH-1 RFC-1.2.31.
+     */
+    static const struct ssh_ttymode {
+        const char *mode;
+        int opcode;
+        enum { TTY_OP_CHAR, TTY_OP_BOOL } type;
+    } ssh_ttymodes[] = {
+        /* "V" prefix discarded for special characters relative to SSH specs */
+        { "INTR",      1, TTY_OP_CHAR },
+        { "QUIT",      2, TTY_OP_CHAR },
+        { "ERASE",     3, TTY_OP_CHAR },
+        { "KILL",      4, TTY_OP_CHAR },
+        { "EOF",       5, TTY_OP_CHAR },
+        { "EOL",       6, TTY_OP_CHAR },
+        { "EOL2",      7, TTY_OP_CHAR },
+        { "START",     8, TTY_OP_CHAR },
+        { "STOP",      9, TTY_OP_CHAR },
+        { "SUSP",     10, TTY_OP_CHAR },
+        { "DSUSP",    11, TTY_OP_CHAR },
+        { "REPRINT",  12, TTY_OP_CHAR },
+        { "WERASE",   13, TTY_OP_CHAR },
+        { "LNEXT",    14, TTY_OP_CHAR },
+        { "FLUSH",    15, TTY_OP_CHAR },
+        { "SWTCH",    16, TTY_OP_CHAR },
+        { "STATUS",   17, TTY_OP_CHAR },
+        { "DISCARD",  18, TTY_OP_CHAR },
+        { "IGNPAR",   30, TTY_OP_BOOL },
+        { "PARMRK",   31, TTY_OP_BOOL },
+        { "INPCK",    32, TTY_OP_BOOL },
+        { "ISTRIP",   33, TTY_OP_BOOL },
+        { "INLCR",    34, TTY_OP_BOOL },
+        { "IGNCR",    35, TTY_OP_BOOL },
+        { "ICRNL",    36, TTY_OP_BOOL },
+        { "IUCLC",    37, TTY_OP_BOOL },
+        { "IXON",     38, TTY_OP_BOOL },
+        { "IXANY",    39, TTY_OP_BOOL },
+        { "IXOFF",    40, TTY_OP_BOOL },
+        { "IMAXBEL",  41, TTY_OP_BOOL },
+        { "IUTF8",    42, TTY_OP_BOOL },
+        { "ISIG",     50, TTY_OP_BOOL },
+        { "ICANON",   51, TTY_OP_BOOL },
+        { "XCASE",    52, TTY_OP_BOOL },
+        { "ECHO",     53, TTY_OP_BOOL },
+        { "ECHOE",    54, TTY_OP_BOOL },
+        { "ECHOK",    55, TTY_OP_BOOL },
+        { "ECHONL",   56, TTY_OP_BOOL },
+        { "NOFLSH",   57, TTY_OP_BOOL },
+        { "TOSTOP",   58, TTY_OP_BOOL },
+        { "IEXTEN",   59, TTY_OP_BOOL },
+        { "ECHOCTL",  60, TTY_OP_BOOL },
+        { "ECHOKE",   61, TTY_OP_BOOL },
+        { "PENDIN",   62, TTY_OP_BOOL }, /* XXX is this a real mode? */
+        { "OPOST",    70, TTY_OP_BOOL },
+        { "OLCUC",    71, TTY_OP_BOOL },
+        { "ONLCR",    72, TTY_OP_BOOL },
+        { "OCRNL",    73, TTY_OP_BOOL },
+        { "ONOCR",    74, TTY_OP_BOOL },
+        { "ONLRET",   75, TTY_OP_BOOL },
+        { "CS7",      90, TTY_OP_BOOL },
+        { "CS8",      91, TTY_OP_BOOL },
+        { "PARENB",   92, TTY_OP_BOOL },
+        { "PARODD",   93, TTY_OP_BOOL }
+    };
+
+    /* Miscellaneous other tty-related constants. */
+    enum {
+        /* The opcodes for ISPEED/OSPEED differ between SSH-1 and SSH-2. */
+        SSH1_TTY_OP_ISPEED = 192,
+        SSH1_TTY_OP_OSPEED = 193,
+        SSH2_TTY_OP_ISPEED = 128,
+        SSH2_TTY_OP_OSPEED = 129,
+
+        SSH_TTY_OP_END = 0
+    };
+
+    for (i = 0; i < lenof(ssh_ttymodes); i++) {
+        const struct ssh_ttymode *mode = ssh_ttymodes + i;
+        const char *sval = conf_get_str_str(conf, CONF_ttymodes, mode->mode);
+        char *to_free = NULL;
+
+        /* Every mode known to the current version of the code should be
+         * mentioned; this was ensured when settings were loaded. */
+
+        /*
+         * sval[0] can be
+         *  - 'V', indicating that an explicit value follows it;
+         *  - 'A', indicating that we should pass the value through from
+         *    the local environment via get_ttymode; or
+         *  - 'N', indicating that we should explicitly not send this
+         *    mode.
+         */
+        if (sval[0] == 'A') {
+            sval = to_free = get_ttymode(frontend, mode->mode);
+        } else if (sval[0] == 'V') {
+            sval++;                    /* skip the 'V' */
+        } else {
+            /* else 'N', or something from the future we don't understand */
+            continue;
+        }
+
+        if (sval) {
+            /*
+             * Parse the string representation of the tty mode
+             * into the integer value it will take on the wire.
+             */
+            unsigned ival = 0;
+
+            switch (mode->type) {
+              case TTY_OP_CHAR:
+                if (*sval) {
+                    char *next = NULL;
+                    /* We know ctrlparse won't write to the string, so
+                     * casting away const is ugly but allowable. */
+                    ival = ctrlparse((char *)sval, &next);
+                    if (!next)
+                        ival = sval[0];
+                } else {
+                    ival = 255; /* special value meaning "don't set" */
+                }
+                break;
+              case TTY_OP_BOOL:
+                if (stricmp(sval, "yes") == 0 ||
+                    stricmp(sval, "on") == 0 ||
+                    stricmp(sval, "true") == 0 ||
+                    stricmp(sval, "+") == 0)
+                    ival = 1;      /* true */
+                else if (stricmp(sval, "no") == 0 ||
+                         stricmp(sval, "off") == 0 ||
+                         stricmp(sval, "false") == 0 ||
+                         stricmp(sval, "-") == 0)
+                    ival = 0;      /* false */
+                else
+                    ival = (atoi(sval) != 0);
+                break;
+              default:
+                assert(0 && "Bad mode->type");
+            }
+
+            /*
+             * And write it into the output packet. The parameter
+             * value is formatted as a byte in SSH-1, but a uint32
+             * in SSH-2.
+             */
+            put_byte(bs, mode->opcode);
+            if (ssh_version == 1)
+                put_byte(bs, ival);
+            else
+                put_uint32(bs, ival);
+        }
+
+        sfree(to_free);
+    }
+
+    /*
+     * Finish off with the terminal speeds (which are formatted as
+     * uint32 in both protocol versions) and the end marker.
+     */
+    put_byte(bs, ssh_version == 1 ? SSH1_TTY_OP_ISPEED : SSH2_TTY_OP_ISPEED);
+    put_uint32(bs, ispeed);
+    put_byte(bs, ssh_version == 1 ? SSH1_TTY_OP_OSPEED : SSH2_TTY_OP_OSPEED);
+    put_uint32(bs, ospeed);
+    put_byte(bs, SSH_TTY_OP_END);
+}
+
+/* ----------------------------------------------------------------------
+ * Routine for allocating a new channel ID, given a means of finding
+ * the index field in a given channel structure.
+ */
+
+unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset)
+{
+    const unsigned CHANNEL_NUMBER_OFFSET = 256;
+    search234_state ss;
+
+    /*
+     * First-fit allocation of channel numbers: we always pick the
+     * lowest unused one.
+     *
+     * Every channel before that, and no channel after it, has an ID
+     * exactly equal to its tree index plus CHANNEL_NUMBER_OFFSET. So
+     * we can use the search234 system to identify the length of that
+     * initial sequence, in a single log-time pass down the channels
+     * tree.
+     */
+    search234_start(&ss, channels);
+    while (ss.element) {
+        unsigned localid = *(unsigned *)((char *)ss.element + localid_offset);
+        if (localid == ss.index + CHANNEL_NUMBER_OFFSET)
+            search234_step(&ss, +1);
+        else
+            search234_step(&ss, -1);
+    }
+
+    /*
+     * Now ss.index gives exactly the number of channels in that
+     * initial sequence. So adding CHANNEL_NUMBER_OFFSET to it must
+     * give precisely the lowest unused channel number.
+     */
+    return ss.index + CHANNEL_NUMBER_OFFSET;
+}
+
+/* ----------------------------------------------------------------------
+ * Functions for handling the comma-separated strings used to store
+ * lists of protocol identifiers in SSH-2.
+ */
+
+int first_in_commasep_string(char const *needle, char const *haystack,
+                             int haylen)
+{
+    int needlen;
+    if (!needle || !haystack)          /* protect against null pointers */
+        return 0;
+    needlen = strlen(needle);
+
+    if (haylen >= needlen &&       /* haystack is long enough */
+        !memcmp(needle, haystack, needlen) &&   /* initial match */
+        (haylen == needlen || haystack[needlen] == ',')
+        /* either , or EOS follows */
+        )
+        return 1;
+    return 0;
+}
+
+int in_commasep_string(char const *needle, char const *haystack, int haylen)
+{
+    char *p;
+
+    if (!needle || !haystack)          /* protect against null pointers */
+        return FALSE;
+    /*
+     * Is it at the start of the string?
+     */
+    if (first_in_commasep_string(needle, haystack, haylen))
+        return TRUE;
+    /*
+     * If not, search for the next comma and resume after that.
+     * If no comma found, terminate.
+     */
+    p = memchr(haystack, ',', haylen);
+    if (!p)
+        return FALSE;
+    /* + 1 to skip over comma */
+    return in_commasep_string(needle, p + 1, haylen - (p + 1 - haystack));
+}
+
+void add_to_commasep(strbuf *buf, const char *data)
+{
+    if (buf->len > 0)
+        put_byte(buf, ',');
+    put_data(buf, data, strlen(data));
+}
+
+/* ----------------------------------------------------------------------
+ * Functions for translating SSH packet type codes into their symbolic
+ * string names.
+ */
+
+#define translate(x) if (type == x) return #x
+#define translatek(x,ctx) if (type == x && (pkt_kctx == ctx)) return #x
+#define translatea(x,ctx) if (type == x && (pkt_actx == ctx)) return #x
+const char *ssh1_pkt_type(int type)
+{
+    translate(SSH1_MSG_DISCONNECT);
+    translate(SSH1_SMSG_PUBLIC_KEY);
+    translate(SSH1_CMSG_SESSION_KEY);
+    translate(SSH1_CMSG_USER);
+    translate(SSH1_CMSG_AUTH_RSA);
+    translate(SSH1_SMSG_AUTH_RSA_CHALLENGE);
+    translate(SSH1_CMSG_AUTH_RSA_RESPONSE);
+    translate(SSH1_CMSG_AUTH_PASSWORD);
+    translate(SSH1_CMSG_REQUEST_PTY);
+    translate(SSH1_CMSG_WINDOW_SIZE);
+    translate(SSH1_CMSG_EXEC_SHELL);
+    translate(SSH1_CMSG_EXEC_CMD);
+    translate(SSH1_SMSG_SUCCESS);
+    translate(SSH1_SMSG_FAILURE);
+    translate(SSH1_CMSG_STDIN_DATA);
+    translate(SSH1_SMSG_STDOUT_DATA);
+    translate(SSH1_SMSG_STDERR_DATA);
+    translate(SSH1_CMSG_EOF);
+    translate(SSH1_SMSG_EXIT_STATUS);
+    translate(SSH1_MSG_CHANNEL_OPEN_CONFIRMATION);
+    translate(SSH1_MSG_CHANNEL_OPEN_FAILURE);
+    translate(SSH1_MSG_CHANNEL_DATA);
+    translate(SSH1_MSG_CHANNEL_CLOSE);
+    translate(SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION);
+    translate(SSH1_SMSG_X11_OPEN);
+    translate(SSH1_CMSG_PORT_FORWARD_REQUEST);
+    translate(SSH1_MSG_PORT_OPEN);
+    translate(SSH1_CMSG_AGENT_REQUEST_FORWARDING);
+    translate(SSH1_SMSG_AGENT_OPEN);
+    translate(SSH1_MSG_IGNORE);
+    translate(SSH1_CMSG_EXIT_CONFIRMATION);
+    translate(SSH1_CMSG_X11_REQUEST_FORWARDING);
+    translate(SSH1_CMSG_AUTH_RHOSTS_RSA);
+    translate(SSH1_MSG_DEBUG);
+    translate(SSH1_CMSG_REQUEST_COMPRESSION);
+    translate(SSH1_CMSG_AUTH_TIS);
+    translate(SSH1_SMSG_AUTH_TIS_CHALLENGE);
+    translate(SSH1_CMSG_AUTH_TIS_RESPONSE);
+    translate(SSH1_CMSG_AUTH_CCARD);
+    translate(SSH1_SMSG_AUTH_CCARD_CHALLENGE);
+    translate(SSH1_CMSG_AUTH_CCARD_RESPONSE);
+    return "unknown";
+}
+const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type)
+{
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE,SSH2_PKTCTX_GSSAPI);
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,SSH2_PKTCTX_GSSAPI);
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,SSH2_PKTCTX_GSSAPI);
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_ERROR,SSH2_PKTCTX_GSSAPI);
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK,SSH2_PKTCTX_GSSAPI);
+    translatea(SSH2_MSG_USERAUTH_GSSAPI_MIC, SSH2_PKTCTX_GSSAPI);
+    translate(SSH2_MSG_DISCONNECT);
+    translate(SSH2_MSG_IGNORE);
+    translate(SSH2_MSG_UNIMPLEMENTED);
+    translate(SSH2_MSG_DEBUG);
+    translate(SSH2_MSG_SERVICE_REQUEST);
+    translate(SSH2_MSG_SERVICE_ACCEPT);
+    translate(SSH2_MSG_KEXINIT);
+    translate(SSH2_MSG_NEWKEYS);
+    translatek(SSH2_MSG_KEXDH_INIT, SSH2_PKTCTX_DHGROUP);
+    translatek(SSH2_MSG_KEXDH_REPLY, SSH2_PKTCTX_DHGROUP);
+    translatek(SSH2_MSG_KEX_DH_GEX_REQUEST_OLD, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEX_DH_GEX_REQUEST, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEX_DH_GEX_GROUP, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEX_DH_GEX_INIT, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEX_DH_GEX_REPLY, SSH2_PKTCTX_DHGEX);
+    translatek(SSH2_MSG_KEXRSA_PUBKEY, SSH2_PKTCTX_RSAKEX);
+    translatek(SSH2_MSG_KEXRSA_SECRET, SSH2_PKTCTX_RSAKEX);
+    translatek(SSH2_MSG_KEXRSA_DONE, SSH2_PKTCTX_RSAKEX);
+    translatek(SSH2_MSG_KEX_ECDH_INIT, SSH2_PKTCTX_ECDHKEX);
+    translatek(SSH2_MSG_KEX_ECDH_REPLY, SSH2_PKTCTX_ECDHKEX);
+    translatek(SSH2_MSG_KEXGSS_INIT, SSH2_PKTCTX_GSSKEX);
+    translatek(SSH2_MSG_KEXGSS_CONTINUE, SSH2_PKTCTX_GSSKEX);
+    translatek(SSH2_MSG_KEXGSS_COMPLETE, SSH2_PKTCTX_GSSKEX);
+    translatek(SSH2_MSG_KEXGSS_HOSTKEY, SSH2_PKTCTX_GSSKEX);
+    translatek(SSH2_MSG_KEXGSS_ERROR, SSH2_PKTCTX_GSSKEX);
+    translatek(SSH2_MSG_KEXGSS_GROUPREQ, SSH2_PKTCTX_GSSKEX);
+    translatek(SSH2_MSG_KEXGSS_GROUP, SSH2_PKTCTX_GSSKEX);
+    translate(SSH2_MSG_USERAUTH_REQUEST);
+    translate(SSH2_MSG_USERAUTH_FAILURE);
+    translate(SSH2_MSG_USERAUTH_SUCCESS);
+    translate(SSH2_MSG_USERAUTH_BANNER);
+    translatea(SSH2_MSG_USERAUTH_PK_OK, SSH2_PKTCTX_PUBLICKEY);
+    translatea(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, SSH2_PKTCTX_PASSWORD);
+    translatea(SSH2_MSG_USERAUTH_INFO_REQUEST, SSH2_PKTCTX_KBDINTER);
+    translatea(SSH2_MSG_USERAUTH_INFO_RESPONSE, SSH2_PKTCTX_KBDINTER);
+    translate(SSH2_MSG_GLOBAL_REQUEST);
+    translate(SSH2_MSG_REQUEST_SUCCESS);
+    translate(SSH2_MSG_REQUEST_FAILURE);
+    translate(SSH2_MSG_CHANNEL_OPEN);
+    translate(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION);
+    translate(SSH2_MSG_CHANNEL_OPEN_FAILURE);
+    translate(SSH2_MSG_CHANNEL_WINDOW_ADJUST);
+    translate(SSH2_MSG_CHANNEL_DATA);
+    translate(SSH2_MSG_CHANNEL_EXTENDED_DATA);
+    translate(SSH2_MSG_CHANNEL_EOF);
+    translate(SSH2_MSG_CHANNEL_CLOSE);
+    translate(SSH2_MSG_CHANNEL_REQUEST);
+    translate(SSH2_MSG_CHANNEL_SUCCESS);
+    translate(SSH2_MSG_CHANNEL_FAILURE);
+    return "unknown";
+}
+#undef translate
+#undef translatec

+ 2 - 2
source/putty/sshdes.c

@@ -821,13 +821,13 @@ static void des3_ssh1_sesskey(ssh1_cipher *cipher, const void *key)
 
 static void des3_ssh1_encrypt_blk(ssh1_cipher *cipher, void *blk, int len)
 {
-    struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+    struct des3_ssh1_ctx *ctx = FROMFIELD(cipher, struct des3_ssh1_ctx, vt);
     des_3cbc_encrypt(blk, len, ctx->contexts);
 }
 
 static void des3_ssh1_decrypt_blk(ssh1_cipher *cipher, void *blk, int len)
 {
-    struct des_ssh1_ctx *ctx = FROMFIELD(cipher, struct des_ssh1_ctx, vt);
+    struct des3_ssh1_ctx *ctx = FROMFIELD(cipher, struct des3_ssh1_ctx, vt);
     des_3cbc_decrypt(blk, len, ctx->contexts+3);
 }
 

+ 1 - 1
source/putty/sshverstring.c

@@ -44,7 +44,7 @@ static void ssh_verstring_handle_input(BinaryPacketProtocol *bpp);
 static PktOut *ssh_verstring_new_pktout(int type);
 static void ssh_verstring_format_packet(BinaryPacketProtocol *bpp, PktOut *);
 
-const struct BinaryPacketProtocolVtable ssh_verstring_vtable = {
+static const struct BinaryPacketProtocolVtable ssh_verstring_vtable = {
     ssh_verstring_free,
     ssh_verstring_handle_input,
     ssh_verstring_new_pktout,

+ 9 - 17
source/putty/windows/winproxy.c

@@ -65,23 +65,15 @@ Socket platform_new_connection(SockAddr addr, const char *hostname,
 	return ret;
     }
 
-    if (flags & FLAG_STDERR) {
-        /* If we have a sensible stderr, the proxy command can send
-         * its own standard error there, so we won't interfere. */
-        us_from_cmd_err = cmd_err_to_us = NULL;
-    } else {
-        /* If we don't have a sensible stderr, we should catch the
-         * proxy command's standard error to put in our event log. */
-        if (!CreatePipe(&us_from_cmd_err, &cmd_err_to_us, &sa, 0)) {
-            Socket ret = new_error_socket
-                ("Unable to create pipes for proxy command", plug);
-            sfree(cmd);
-            CloseHandle(us_from_cmd);
-            CloseHandle(cmd_to_us);
-            CloseHandle(us_to_cmd);
-            CloseHandle(cmd_from_us);
-            return ret;
-        }
+    if (!CreatePipe(&us_from_cmd_err, &cmd_err_to_us, &sa, 0)) {
+        Socket ret = new_error_socket
+            ("Unable to create pipes for proxy command", plug);
+        sfree(cmd);
+        CloseHandle(us_from_cmd);
+        CloseHandle(cmd_to_us);
+        CloseHandle(us_to_cmd);
+        CloseHandle(cmd_from_us);
+        return ret;
     }
 
     SetHandleInformation(us_to_cmd, HANDLE_FLAG_INHERIT, 0);

Some files were not shown because too many files changed in this diff