]> asedeno.scripts.mit.edu Git - PuTTY.git/blobdiff - windows/winnet.c
Make Windows sockets non-inheritable
[PuTTY.git] / windows / winnet.c
index 1c574e31a52a70d4fd0ccdb53627aeddbda8920f..9875323744cda7ffbdf2377061231176debc848a 100644 (file)
@@ -5,11 +5,15 @@
  * unfix.org.
  */
 
+#include <winsock2.h> /* need to put this first, for winelib builds */
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <assert.h>
 
 #define DEFINE_PLUG_METHOD_MACROS
+#define NEED_DECLARATION_OF_SELECT     /* in order to initialise it */
+
 #include "putty.h"
 #include "network.h"
 #include "tree234.h"
 #include <ws2tcpip.h>
 
 #ifndef NO_IPV6
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-braces"
+#endif
 const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT;
 const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
 #endif
 
 #define ipv4_is_loopback(addr) \
@@ -34,13 +45,25 @@ const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT;
  */
 typedef struct Socket_tag *Actual_Socket;
 
+/*
+ * Mutable state that goes with a SockAddr: stores information
+ * about where in the list of candidate IP(v*) addresses we've
+ * currently got to.
+ */
+typedef struct SockAddrStep_tag SockAddrStep;
+struct SockAddrStep_tag {
+#ifndef NO_IPV6
+    struct addrinfo *ai;              /* steps along addr->ais */
+#endif
+    int curraddr;
+};
+
 struct Socket_tag {
     const struct socket_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
-    char *error;
+    const char *error;
     SOCKET s;
     Plug plug;
-    void *private_ptr;
     bufchain output_data;
     int connected;
     int writable;
@@ -51,7 +74,9 @@ struct Socket_tag {
     char oobdata[1];
     int sending_oob;
     int oobinline, nodelay, keepalive, privport;
+    enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
     SockAddr addr;
+    SockAddrStep step;
     int port;
     int pending_error;                /* in case send() returns error */
     /*
@@ -64,28 +89,46 @@ struct Socket_tag {
 };
 
 struct SockAddr_tag {
+    int refcount;
     char *error;
-    /* 
-     * Which address family this address belongs to. AF_INET for
-     * IPv4; AF_INET6 for IPv6; AF_UNSPEC indicates that name
-     * resolution has not been done and a simple host name is held
-     * in this SockAddr structure.
-     * The hostname field is also used when the hostname has both
-     * an IPv6 and IPv4 address and the IPv6 connection attempt
-     * fails. We then try the IPv4 address.
-     * This 'family' should become an option in the GUI and
-     * on the commandline for selecting a default protocol.
-     */
-    int family;
+    int resolved;
+    int namedpipe; /* indicates that this SockAddr is phony, holding a Windows
+                    * named pipe pathname instead of a network address */
 #ifndef NO_IPV6
     struct addrinfo *ais;             /* Addresses IPv6 style. */
-    struct addrinfo *ai;              /* steps along the linked list */
 #endif
     unsigned long *addresses;         /* Addresses IPv4 style. */
-    int naddresses, curraddr;
+    int naddresses;
     char hostname[512];                       /* Store an unresolved host name. */
 };
 
+/*
+ * Which address family this address belongs to. AF_INET for IPv4;
+ * AF_INET6 for IPv6; AF_UNSPEC indicates that name resolution has
+ * not been done and a simple host name is held in this SockAddr
+ * structure.
+ */
+#ifndef NO_IPV6
+#define SOCKADDR_FAMILY(addr, step) \
+    (!(addr)->resolved ? AF_UNSPEC : \
+     (step).ai ? (step).ai->ai_family : AF_INET)
+#else
+#define SOCKADDR_FAMILY(addr, step) \
+    (!(addr)->resolved ? AF_UNSPEC : AF_INET)
+#endif
+
+/*
+ * Start a SockAddrStep structure to step through multiple
+ * addresses.
+ */
+#ifndef NO_IPV6
+#define START_STEP(addr, step) \
+    ((step).ai = (addr)->ais, (step).curraddr = 0)
+#else
+#define START_STEP(addr, step) \
+    ((step).curraddr = 0)
+#endif
+
 static tree234 *sktree;
 
 static int cmpfortree(void *av, void *bv)
@@ -96,13 +139,17 @@ static int cmpfortree(void *av, void *bv)
        return -1;
     if (as > bs)
        return +1;
+    if (a < b)
+       return -1;
+    if (a > b)
+       return +1;
     return 0;
 }
 
 static int cmpforsearch(void *av, void *bv)
 {
     Actual_Socket b = (Actual_Socket) bv;
-    unsigned long as = (unsigned long) av, bs = (unsigned long) b->s;
+    uintptr_t as = (uintptr_t) av, bs = (uintptr_t) b->s;
     if (as < bs)
        return -1;
     if (as > bs)
@@ -110,66 +157,56 @@ static int cmpforsearch(void *av, void *bv)
     return 0;
 }
 
-#define NOTHING
-#define DECL_WINSOCK_FUNCTION(linkage, rettype, name, params) \
-    typedef rettype (WINAPI *t_##name) params; \
-    linkage t_##name p_##name
-#define GET_WINSOCK_FUNCTION(module, name) \
-    p_##name = module ? (t_##name) GetProcAddress(module, #name) : NULL
-
-DECL_WINSOCK_FUNCTION(NOTHING, int, WSAAsyncSelect,
-                     (SOCKET, HWND, u_int, long));
-DECL_WINSOCK_FUNCTION(NOTHING, int, WSAEventSelect, (SOCKET, WSAEVENT, long));
-DECL_WINSOCK_FUNCTION(NOTHING, int, select,
-                     (int, fd_set FAR *, fd_set FAR *,
-                      fd_set FAR *, const struct timeval FAR *));
-DECL_WINSOCK_FUNCTION(NOTHING, int, WSAGetLastError, (void));
-DECL_WINSOCK_FUNCTION(NOTHING, int, WSAEnumNetworkEvents,
-                     (SOCKET, WSAEVENT, LPWSANETWORKEVENTS));
-DECL_WINSOCK_FUNCTION(static, int, WSAStartup, (WORD, LPWSADATA));
-DECL_WINSOCK_FUNCTION(static, int, WSACleanup, (void));
-DECL_WINSOCK_FUNCTION(static, int, closesocket, (SOCKET));
-DECL_WINSOCK_FUNCTION(static, u_long, ntohl, (u_long));
-DECL_WINSOCK_FUNCTION(static, u_long, htonl, (u_long));
-DECL_WINSOCK_FUNCTION(static, u_short, htons, (u_short));
-DECL_WINSOCK_FUNCTION(static, u_short, ntohs, (u_short));
-DECL_WINSOCK_FUNCTION(static, struct hostent FAR *, gethostbyname,
+DECL_WINDOWS_FUNCTION(static, int, WSAStartup, (WORD, LPWSADATA));
+DECL_WINDOWS_FUNCTION(static, int, WSACleanup, (void));
+DECL_WINDOWS_FUNCTION(static, int, closesocket, (SOCKET));
+DECL_WINDOWS_FUNCTION(static, u_long, ntohl, (u_long));
+DECL_WINDOWS_FUNCTION(static, u_long, htonl, (u_long));
+DECL_WINDOWS_FUNCTION(static, u_short, htons, (u_short));
+DECL_WINDOWS_FUNCTION(static, u_short, ntohs, (u_short));
+DECL_WINDOWS_FUNCTION(static, int, gethostname, (char *, int));
+DECL_WINDOWS_FUNCTION(static, struct hostent FAR *, gethostbyname,
                      (const char FAR *));
-DECL_WINSOCK_FUNCTION(static, struct servent FAR *, getservbyname,
+DECL_WINDOWS_FUNCTION(static, struct servent FAR *, getservbyname,
                      (const char FAR *, const char FAR *));
-DECL_WINSOCK_FUNCTION(static, unsigned long, inet_addr, (const char FAR *));
-DECL_WINSOCK_FUNCTION(static, char FAR *, inet_ntoa, (struct in_addr));
-DECL_WINSOCK_FUNCTION(static, int, connect,
+DECL_WINDOWS_FUNCTION(static, unsigned long, inet_addr, (const char FAR *));
+DECL_WINDOWS_FUNCTION(static, char FAR *, inet_ntoa, (struct in_addr));
+DECL_WINDOWS_FUNCTION(static, const char FAR *, inet_ntop,
+                      (int, void FAR *, char *, size_t));
+DECL_WINDOWS_FUNCTION(static, int, connect,
                      (SOCKET, const struct sockaddr FAR *, int));
-DECL_WINSOCK_FUNCTION(static, int, bind,
+DECL_WINDOWS_FUNCTION(static, int, bind,
                      (SOCKET, const struct sockaddr FAR *, int));
-DECL_WINSOCK_FUNCTION(static, int, setsockopt,
+DECL_WINDOWS_FUNCTION(static, int, setsockopt,
                      (SOCKET, int, int, const char FAR *, int));
-DECL_WINSOCK_FUNCTION(static, SOCKET, socket, (int, int, int));
-DECL_WINSOCK_FUNCTION(static, int, listen, (SOCKET, int));
-DECL_WINSOCK_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int));
-DECL_WINSOCK_FUNCTION(static, int, ioctlsocket,
+DECL_WINDOWS_FUNCTION(static, SOCKET, socket, (int, int, int));
+DECL_WINDOWS_FUNCTION(static, int, listen, (SOCKET, int));
+DECL_WINDOWS_FUNCTION(static, int, send, (SOCKET, const char FAR *, int, int));
+DECL_WINDOWS_FUNCTION(static, int, shutdown, (SOCKET, int));
+DECL_WINDOWS_FUNCTION(static, int, ioctlsocket,
                      (SOCKET, long, u_long FAR *));
-DECL_WINSOCK_FUNCTION(static, SOCKET, accept,
+DECL_WINDOWS_FUNCTION(static, SOCKET, accept,
                      (SOCKET, struct sockaddr FAR *, int FAR *));
-DECL_WINSOCK_FUNCTION(static, int, recv, (SOCKET, char FAR *, int, int));
-DECL_WINSOCK_FUNCTION(static, int, WSAIoctl,
+DECL_WINDOWS_FUNCTION(static, int, getpeername,
+                     (SOCKET, struct sockaddr FAR *, int FAR *));
+DECL_WINDOWS_FUNCTION(static, int, recv, (SOCKET, char FAR *, int, int));
+DECL_WINDOWS_FUNCTION(static, int, WSAIoctl,
                      (SOCKET, DWORD, LPVOID, DWORD, LPVOID, DWORD,
                       LPDWORD, LPWSAOVERLAPPED,
                       LPWSAOVERLAPPED_COMPLETION_ROUTINE));
 #ifndef NO_IPV6
-DECL_WINSOCK_FUNCTION(static, int, getaddrinfo,
+DECL_WINDOWS_FUNCTION(static, int, getaddrinfo,
                      (const char *nodename, const char *servname,
                       const struct addrinfo *hints, struct addrinfo **res));
-DECL_WINSOCK_FUNCTION(static, void, freeaddrinfo, (struct addrinfo *res));
-DECL_WINSOCK_FUNCTION(static, int, getnameinfo,
+DECL_WINDOWS_FUNCTION(static, void, freeaddrinfo, (struct addrinfo *res));
+DECL_WINDOWS_FUNCTION(static, int, getnameinfo,
                      (const struct sockaddr FAR * sa, socklen_t salen,
                       char FAR * host, size_t hostlen, char FAR * serv,
                       size_t servlen, int flags));
-DECL_WINSOCK_FUNCTION(static, char *, gai_strerror, (int ecode));
-DECL_WINSOCK_FUNCTION(static, int, WSAAddressToStringA,
+DECL_WINDOWS_FUNCTION(static, char *, gai_strerror, (int ecode));
+DECL_WINDOWS_FUNCTION(static, int, WSAAddressToStringA,
                      (LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFO,
-                      LPTSTR, LPDWORD));
+                      LPSTR, LPDWORD));
 #endif
 
 static HMODULE winsock_module = NULL;
@@ -203,11 +240,21 @@ int sk_startup(int hi, int lo)
     return TRUE;
 }
 
+/* Actually define this function pointer, which won't have been
+ * defined alongside all the others by PUTTY_DO_GLOBALS because of the
+ * annoying winelib header-ordering issue. (See comment in winstuff.h.) */
+DECL_WINDOWS_FUNCTION(/* empty */, int, select,
+                     (int, fd_set FAR *, fd_set FAR *,
+                      fd_set FAR *, const struct timeval FAR *));
+
 void sk_init(void)
 {
-    winsock2_module = winsock_module = LoadLibrary("WS2_32.DLL");
+#ifndef NO_IPV6
+    winsock2_module =
+#endif
+        winsock_module = load_system32_dll("ws2_32.dll");
     if (!winsock_module) {
-       winsock_module = LoadLibrary("WSOCK32.DLL");
+       winsock_module = load_system32_dll("wsock32.dll");
     }
     if (!winsock_module)
        fatalbox("Unable to load any WinSock library");
@@ -218,60 +265,64 @@ void sk_init(void)
 #ifdef NET_SETUP_DIAGNOSTICS
        logevent(NULL, "Native WinSock IPv6 support detected");
 #endif
-       GET_WINSOCK_FUNCTION(winsock_module, getaddrinfo);
-       GET_WINSOCK_FUNCTION(winsock_module, freeaddrinfo);
-       GET_WINSOCK_FUNCTION(winsock_module, getnameinfo);
-       GET_WINSOCK_FUNCTION(winsock_module, gai_strerror);
+       GET_WINDOWS_FUNCTION(winsock_module, getaddrinfo);
+       GET_WINDOWS_FUNCTION(winsock_module, freeaddrinfo);
+       GET_WINDOWS_FUNCTION(winsock_module, getnameinfo);
+       GET_WINDOWS_FUNCTION(winsock_module, gai_strerror);
     } else {
        /* Fall back to wship6.dll for Windows 2000 */
-       wship6_module = LoadLibrary("wship6.dll");
+       wship6_module = load_system32_dll("wship6.dll");
        if (wship6_module) {
 #ifdef NET_SETUP_DIAGNOSTICS
            logevent(NULL, "WSH IPv6 support detected");
 #endif
-           GET_WINSOCK_FUNCTION(wship6_module, getaddrinfo);
-           GET_WINSOCK_FUNCTION(wship6_module, freeaddrinfo);
-           GET_WINSOCK_FUNCTION(wship6_module, getnameinfo);
-           GET_WINSOCK_FUNCTION(wship6_module, gai_strerror);
+           GET_WINDOWS_FUNCTION(wship6_module, getaddrinfo);
+           GET_WINDOWS_FUNCTION(wship6_module, freeaddrinfo);
+           GET_WINDOWS_FUNCTION(wship6_module, getnameinfo);
+           GET_WINDOWS_FUNCTION(wship6_module, gai_strerror);
        } else {
 #ifdef NET_SETUP_DIAGNOSTICS
            logevent(NULL, "No IPv6 support detected");
 #endif
        }
     }
-    GET_WINSOCK_FUNCTION(winsock2_module, WSAAddressToStringA);
+    GET_WINDOWS_FUNCTION(winsock2_module, WSAAddressToStringA);
 #else
 #ifdef NET_SETUP_DIAGNOSTICS
     logevent(NULL, "PuTTY was built without IPv6 support");
 #endif
 #endif
 
-    GET_WINSOCK_FUNCTION(winsock_module, WSAAsyncSelect);
-    GET_WINSOCK_FUNCTION(winsock_module, WSAEventSelect);
-    GET_WINSOCK_FUNCTION(winsock_module, select);
-    GET_WINSOCK_FUNCTION(winsock_module, WSAGetLastError);
-    GET_WINSOCK_FUNCTION(winsock_module, WSAEnumNetworkEvents);
-    GET_WINSOCK_FUNCTION(winsock_module, WSAStartup);
-    GET_WINSOCK_FUNCTION(winsock_module, WSACleanup);
-    GET_WINSOCK_FUNCTION(winsock_module, closesocket);
-    GET_WINSOCK_FUNCTION(winsock_module, ntohl);
-    GET_WINSOCK_FUNCTION(winsock_module, htonl);
-    GET_WINSOCK_FUNCTION(winsock_module, htons);
-    GET_WINSOCK_FUNCTION(winsock_module, ntohs);
-    GET_WINSOCK_FUNCTION(winsock_module, gethostbyname);
-    GET_WINSOCK_FUNCTION(winsock_module, getservbyname);
-    GET_WINSOCK_FUNCTION(winsock_module, inet_addr);
-    GET_WINSOCK_FUNCTION(winsock_module, inet_ntoa);
-    GET_WINSOCK_FUNCTION(winsock_module, connect);
-    GET_WINSOCK_FUNCTION(winsock_module, bind);
-    GET_WINSOCK_FUNCTION(winsock_module, setsockopt);
-    GET_WINSOCK_FUNCTION(winsock_module, socket);
-    GET_WINSOCK_FUNCTION(winsock_module, listen);
-    GET_WINSOCK_FUNCTION(winsock_module, send);
-    GET_WINSOCK_FUNCTION(winsock_module, ioctlsocket);
-    GET_WINSOCK_FUNCTION(winsock_module, accept);
-    GET_WINSOCK_FUNCTION(winsock_module, recv);
-    GET_WINSOCK_FUNCTION(winsock_module, WSAIoctl);
+    GET_WINDOWS_FUNCTION(winsock_module, WSAAsyncSelect);
+    GET_WINDOWS_FUNCTION(winsock_module, WSAEventSelect);
+    GET_WINDOWS_FUNCTION(winsock_module, select);
+    GET_WINDOWS_FUNCTION(winsock_module, WSAGetLastError);
+    GET_WINDOWS_FUNCTION(winsock_module, WSAEnumNetworkEvents);
+    GET_WINDOWS_FUNCTION(winsock_module, WSAStartup);
+    GET_WINDOWS_FUNCTION(winsock_module, WSACleanup);
+    GET_WINDOWS_FUNCTION(winsock_module, closesocket);
+    GET_WINDOWS_FUNCTION(winsock_module, ntohl);
+    GET_WINDOWS_FUNCTION(winsock_module, htonl);
+    GET_WINDOWS_FUNCTION(winsock_module, htons);
+    GET_WINDOWS_FUNCTION(winsock_module, ntohs);
+    GET_WINDOWS_FUNCTION(winsock_module, gethostname);
+    GET_WINDOWS_FUNCTION(winsock_module, gethostbyname);
+    GET_WINDOWS_FUNCTION(winsock_module, getservbyname);
+    GET_WINDOWS_FUNCTION(winsock_module, inet_addr);
+    GET_WINDOWS_FUNCTION(winsock_module, inet_ntoa);
+    GET_WINDOWS_FUNCTION(winsock_module, inet_ntop);
+    GET_WINDOWS_FUNCTION(winsock_module, connect);
+    GET_WINDOWS_FUNCTION(winsock_module, bind);
+    GET_WINDOWS_FUNCTION(winsock_module, setsockopt);
+    GET_WINDOWS_FUNCTION(winsock_module, socket);
+    GET_WINDOWS_FUNCTION(winsock_module, listen);
+    GET_WINDOWS_FUNCTION(winsock_module, send);
+    GET_WINDOWS_FUNCTION(winsock_module, shutdown);
+    GET_WINDOWS_FUNCTION(winsock_module, ioctlsocket);
+    GET_WINDOWS_FUNCTION(winsock_module, accept);
+    GET_WINDOWS_FUNCTION(winsock_module, getpeername);
+    GET_WINDOWS_FUNCTION(winsock_module, recv);
+    GET_WINDOWS_FUNCTION(winsock_module, WSAIoctl);
 
     /* Try to get the best WinSock version we can get */
     if (!sk_startup(2,2) &&
@@ -296,7 +347,8 @@ void sk_cleanup(void)
        sktree = NULL;
     }
 
-    p_WSACleanup();
+    if (p_WSACleanup)
+       p_WSACleanup();
     if (winsock_module)
        FreeLibrary(winsock_module);
 #ifndef NO_IPV6
@@ -305,8 +357,38 @@ void sk_cleanup(void)
 #endif
 }
 
-char *winsock_error_string(int error)
+struct errstring {
+    int error;
+    char *text;
+};
+
+static int errstring_find(void *av, void *bv)
 {
+    int *a = (int *)av;
+    struct errstring *b = (struct errstring *)bv;
+    if (*a < b->error)
+        return -1;
+    if (*a > b->error)
+        return +1;
+    return 0;
+}
+static int errstring_compare(void *av, void *bv)
+{
+    struct errstring *a = (struct errstring *)av;
+    return errstring_find(&a->error, bv);
+}
+
+static tree234 *errstrings = NULL;
+
+const char *winsock_error_string(int error)
+{
+    const char prefix[] = "Network error: ";
+    struct errstring *es;
+
+    /*
+     * Error codes we know about and have historically had reasonably
+     * sensible error messages for.
+     */
     switch (error) {
       case WSAEACCES:
        return "Network error: Permission denied";
@@ -379,9 +461,53 @@ char *winsock_error_string(int error)
        return "Network error: Resource temporarily unavailable";
       case WSAEDISCON:
        return "Network error: Graceful shutdown in progress";
-      default:
-       return "Unknown network error";
     }
+
+    /*
+     * Generic code to handle any other error.
+     *
+     * Slightly nasty hack here: we want to return a static string
+     * which the caller will never have to worry about freeing, but on
+     * the other hand if we call FormatMessage to get it then it will
+     * want to either allocate a buffer or write into one we own.
+     *
+     * So what we do is to maintain a tree234 of error strings we've
+     * already used. New ones are allocated from the heap, but then
+     * put in this tree and kept forever.
+     */
+
+    if (!errstrings)
+        errstrings = newtree234(errstring_compare);
+
+    es = find234(errstrings, &error, errstring_find);
+
+    if (!es) {
+        int bufsize, bufused;
+
+        es = snew(struct errstring);
+        es->error = error;
+        /* maximum size for FormatMessage is 64K */
+        bufsize = 65535 + sizeof(prefix);
+        es->text = snewn(bufsize, char);
+        strcpy(es->text, prefix);
+        bufused = strlen(es->text);
+        if (!FormatMessage((FORMAT_MESSAGE_FROM_SYSTEM |
+                            FORMAT_MESSAGE_IGNORE_INSERTS), NULL, error,
+                           MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                           es->text + bufused, bufsize - bufused, NULL)) {
+            sprintf(es->text + bufused,
+                    "Windows error code %d (and FormatMessage returned %u)",
+                    error, (unsigned int)GetLastError());
+        } else {
+            int len = strlen(es->text);
+            if (len > 0 && es->text[len-1] == '\n')
+                es->text[len-1] = '\0';
+        }
+        es->text = sresize(es->text, strlen(es->text) + 1, char);
+        add234(errstrings, es);
+    }
+
+    return es->text;
 }
 
 SockAddr sk_namelookup(const char *host, char **canonicalname,
@@ -389,26 +515,30 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
 {
     SockAddr ret = snew(struct SockAddr_tag);
     unsigned long a;
-    struct hostent *h = NULL;
     char realhost[8192];
-    int ret_family;
-    int err;
+    int hint_family;
 
-    /* Clear the structure and default to IPv4. */
-    memset(ret, 0, sizeof(struct SockAddr_tag));
-    ret->family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
+    /* Default to IPv4. */
+    hint_family = (address_family == ADDRTYPE_IPV4 ? AF_INET :
 #ifndef NO_IPV6
                   address_family == ADDRTYPE_IPV6 ? AF_INET6 :
 #endif
                   AF_UNSPEC);
+
+    /* Clear the structure and default to IPv4. */
+    memset(ret, 0, sizeof(struct SockAddr_tag));
 #ifndef NO_IPV6
-    ret->ai = ret->ais = NULL;
+    ret->ais = NULL;
 #endif
+    ret->namedpipe = FALSE;
     ret->addresses = NULL;
-    ret_family = AF_UNSPEC;
+    ret->resolved = FALSE;
+    ret->refcount = 1;
     *realhost = '\0';
 
     if ((a = p_inet_addr(host)) == (unsigned long) INADDR_NONE) {
+       struct hostent *h = NULL;
+       int err;
 #ifndef NO_IPV6
        /*
         * Use getaddrinfo when it's available
@@ -419,11 +549,16 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
            logevent(NULL, "Using getaddrinfo() for resolving");
 #endif
            memset(&hints, 0, sizeof(hints));
-           hints.ai_family = ret->family;
+           hints.ai_family = hint_family;
            hints.ai_flags = AI_CANONNAME;
-           if ((err = p_getaddrinfo(host, NULL, &hints, &ret->ais)) == 0)
-               ret_family = ret->ais->ai_family;
-           ret->ai = ret->ais;
+            {
+                /* strip [] on IPv6 address literals */
+                char *trimmed_host = host_strduptrim(host);
+                err = p_getaddrinfo(trimmed_host, NULL, &hints, &ret->ais);
+                sfree(trimmed_host);
+            }
+           if (err == 0)
+               ret->resolved = TRUE;
        } else
 #endif
        {
@@ -435,12 +570,12 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
             * (NOTE: we don't use gethostbyname as a fallback!)
             */
            if ( (h = p_gethostbyname(host)) )
-               ret_family = AF_INET;
+               ret->resolved = TRUE;
            else
                err = p_WSAGetLastError();
        }
 
-       if (ret_family == AF_UNSPEC) {
+       if (!ret->resolved) {
            ret->error = (err == WSAENETDOWN ? "Network is down" :
                          err == WSAHOST_NOT_FOUND ? "Host does not exist" :
                          err == WSATRY_AGAIN ? "Host not found" :
@@ -450,20 +585,19 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
                          "gethostbyname: unknown error");
        } else {
            ret->error = NULL;
-           ret->family = ret_family;
 
 #ifndef NO_IPV6
            /* If we got an address info use that... */
-           if (ret->ai) {
+           if (ret->ais) {
                /* Are we in IPv4 fallback mode? */
                /* We put the IPv4 address into the a variable so we can further-on use the IPv4 code... */
-               if (ret->family == AF_INET)
+               if (ret->ais->ai_family == AF_INET)
                    memcpy(&a,
-                          (char *) &((SOCKADDR_IN *) ret->ai->
+                          (char *) &((SOCKADDR_IN *) ret->ais->
                                      ai_addr)->sin_addr, sizeof(a));
 
-               if (ret->ai->ai_canonname)
-                   strncpy(realhost, ret->ai->ai_canonname, lenof(realhost));
+               if (ret->ais->ai_canonname)
+                   strncpy(realhost, ret->ais->ai_canonname, lenof(realhost));
                else
                    strncpy(realhost, host, lenof(realhost));
            }
@@ -479,7 +613,6 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
                    memcpy(&a, h->h_addr_list[n], sizeof(a));
                    ret->addresses[n] = p_ntohl(a);
                }
-               ret->curraddr = 0;
                memcpy(&a, h->h_addr, sizeof(a));
                /* This way we are always sure the h->h_name is valid :) */
                strncpy(realhost, h->h_name, sizeof(realhost));
@@ -492,9 +625,8 @@ SockAddr sk_namelookup(const char *host, char **canonicalname,
         */
        ret->addresses = snewn(1, unsigned long);
        ret->naddresses = 1;
-       ret->curraddr = 0;
        ret->addresses[0] = p_ntohl(a);
-       ret->family = AF_INET;
+       ret->resolved = TRUE;
        strncpy(realhost, host, sizeof(realhost));
     }
     realhost[lenof(realhost)-1] = '\0';
@@ -507,31 +639,49 @@ SockAddr sk_nonamelookup(const char *host)
 {
     SockAddr ret = snew(struct SockAddr_tag);
     ret->error = NULL;
-    ret->family = AF_UNSPEC;
+    ret->resolved = FALSE;
 #ifndef NO_IPV6
-    ret->ai = ret->ais = NULL;
+    ret->ais = NULL;
 #endif
+    ret->namedpipe = FALSE;
     ret->addresses = NULL;
     ret->naddresses = 0;
+    ret->refcount = 1;
     strncpy(ret->hostname, host, lenof(ret->hostname));
     ret->hostname[lenof(ret->hostname)-1] = '\0';
     return ret;
 }
 
-int sk_nextaddr(SockAddr addr)
+SockAddr sk_namedpipe_addr(const char *pipename)
 {
+    SockAddr ret = snew(struct SockAddr_tag);
+    ret->error = NULL;
+    ret->resolved = FALSE;
 #ifndef NO_IPV6
-    if (addr->ai) {
-       if (addr->ai->ai_next) {
-           addr->ai = addr->ai->ai_next;
-           addr->family = addr->ai->ai_family;
+    ret->ais = NULL;
+#endif
+    ret->namedpipe = TRUE;
+    ret->addresses = NULL;
+    ret->naddresses = 0;
+    ret->refcount = 1;
+    strncpy(ret->hostname, pipename, lenof(ret->hostname));
+    ret->hostname[lenof(ret->hostname)-1] = '\0';
+    return ret;
+}
+
+int sk_nextaddr(SockAddr addr, SockAddrStep *step)
+{
+#ifndef NO_IPV6
+    if (step->ai) {
+       if (step->ai->ai_next) {
+           step->ai = step->ai->ai_next;
            return TRUE;
        } else
            return FALSE;
     }
 #endif
-    if (addr->curraddr+1 < addr->naddresses) {
-       addr->curraddr++;
+    if (step->curraddr+1 < addr->naddresses) {
+       step->curraddr++;
        return TRUE;
     } else {
        return FALSE;
@@ -540,19 +690,30 @@ int sk_nextaddr(SockAddr addr)
 
 void sk_getaddr(SockAddr addr, char *buf, int buflen)
 {
+    SockAddrStep step;
+    START_STEP(addr, step);
+
 #ifndef NO_IPV6
-    if (addr->ai) {
+    if (step.ai) {
+       int err = 0;
        if (p_WSAAddressToStringA) {
-           p_WSAAddressToStringA(addr->ai->ai_addr, addr->ai->ai_addrlen,
-                                 NULL, buf, &buflen);
+           DWORD dwbuflen = buflen;
+           err = p_WSAAddressToStringA(step.ai->ai_addr, step.ai->ai_addrlen,
+                                       NULL, buf, &dwbuflen);
        } else
-           strncpy(buf, "IPv6", buflen);
+           err = -1;
+       if (err) {
+           strncpy(buf, addr->hostname, buflen);
+           if (!buf[0])
+               strncpy(buf, "<unknown>", buflen);
+           buf[buflen-1] = '\0';
+       }
     } else
 #endif
-    if (addr->family == AF_INET) {
+    if (SOCKADDR_FAMILY(addr, step) == AF_INET) {
        struct in_addr a;
-       assert(addr->addresses && addr->curraddr < addr->naddresses);
-       a.s_addr = p_htonl(addr->addresses[addr->curraddr]);
+       assert(addr->addresses && step.curraddr < addr->naddresses);
+       a.s_addr = p_htonl(addr->addresses[step.curraddr]);
        strncpy(buf, p_inet_ntoa(a), buflen);
        buf[buflen-1] = '\0';
     } else {
@@ -561,9 +722,45 @@ void sk_getaddr(SockAddr addr, char *buf, int buflen)
     }
 }
 
-int sk_hostname_is_local(char *name)
+/*
+ * This constructs a SockAddr that points at one specific sub-address
+ * of a parent SockAddr. The returned SockAddr does not own all its
+ * own memory: it points into the old one's data structures, so it
+ * MUST NOT be used after the old one is freed, and it MUST NOT be
+ * passed to sk_addr_free. (The latter is why it's returned by value
+ * rather than dynamically allocated - that should clue in anyone
+ * writing a call to it that something is weird about it.)
+ */
+static struct SockAddr_tag sk_extractaddr_tmp(
+    SockAddr addr, const SockAddrStep *step)
 {
-    return !strcmp(name, "localhost");
+    struct SockAddr_tag toret;
+    toret = *addr;                    /* structure copy */
+    toret.refcount = 1;
+
+#ifndef NO_IPV6
+    toret.ais = step->ai;
+#endif
+    if (SOCKADDR_FAMILY(addr, *step) == AF_INET
+#ifndef NO_IPV6
+        && !toret.ais
+#endif
+        )
+        toret.addresses += step->curraddr;
+
+    return toret;
+}
+
+int sk_addr_needs_port(SockAddr addr)
+{
+    return addr->namedpipe ? FALSE : TRUE;
+}
+
+int sk_hostname_is_local(const char *name)
+{
+    return !strcmp(name, "localhost") ||
+          !strcmp(name, "::1") ||
+          !strncmp(name, "127.", 4);
 }
 
 static INTERFACE_INFO local_interfaces[16];
@@ -577,6 +774,8 @@ static int ipv4_is_local_addr(struct in_addr addr)
        SOCKET s = p_socket(AF_INET, SOCK_DGRAM, 0);
        DWORD retbytes;
 
+       SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0);
+
        if (p_WSAIoctl &&
            p_WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0,
                       local_interfaces, sizeof(local_interfaces),
@@ -599,64 +798,86 @@ static int ipv4_is_local_addr(struct in_addr addr)
 
 int sk_address_is_local(SockAddr addr)
 {
+    SockAddrStep step;
+    int family;
+    START_STEP(addr, step);
+    family = SOCKADDR_FAMILY(addr, step);
+
 #ifndef NO_IPV6
-    if (addr->family == AF_INET6) {
-       return IN6_IS_ADDR_LOOPBACK((const struct in6_addr *)addr->ai->ai_addr);
+    if (family == AF_INET6) {
+       return IN6_IS_ADDR_LOOPBACK(&((const struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr);
     } else
 #endif
-    if (addr->family == AF_INET) {
+    if (family == AF_INET) {
 #ifndef NO_IPV6
-       if (addr->ai) {
-           return ipv4_is_local_addr(((struct sockaddr_in *)addr->ai->ai_addr)
+       if (step.ai) {
+           return ipv4_is_local_addr(((struct sockaddr_in *)step.ai->ai_addr)
                                      ->sin_addr);
        } else
 #endif
        {
            struct in_addr a;
-           assert(addr->addresses && addr->curraddr < addr->naddresses);
-           a.s_addr = p_htonl(addr->addresses[addr->curraddr]);
+           assert(addr->addresses && step.curraddr < addr->naddresses);
+           a.s_addr = p_htonl(addr->addresses[step.curraddr]);
            return ipv4_is_local_addr(a);
        }
     } else {
-       assert(addr->family == AF_UNSPEC);
+       assert(family == AF_UNSPEC);
        return 0;                      /* we don't know; assume not */
     }
 }
 
+int sk_address_is_special_local(SockAddr addr)
+{
+    return 0;                /* no Unix-domain socket analogue here */
+}
+
 int sk_addrtype(SockAddr addr)
 {
-    return (addr->family == AF_INET ? ADDRTYPE_IPV4 :
+    SockAddrStep step;
+    int family;
+    START_STEP(addr, step);
+    family = SOCKADDR_FAMILY(addr, step);
+
+    return (family == AF_INET ? ADDRTYPE_IPV4 :
 #ifndef NO_IPV6
-           addr->family == AF_INET6 ? ADDRTYPE_IPV6 :
+           family == AF_INET6 ? ADDRTYPE_IPV6 :
 #endif
            ADDRTYPE_NAME);
 }
 
 void sk_addrcopy(SockAddr addr, char *buf)
 {
-    assert(addr->family != AF_UNSPEC);
+    SockAddrStep step;
+    int family;
+    START_STEP(addr, step);
+    family = SOCKADDR_FAMILY(addr, step);
+
+    assert(family != AF_UNSPEC);
 #ifndef NO_IPV6
-    if (addr->ai) {
-       if (addr->family == AF_INET)
-           memcpy(buf, &((struct sockaddr_in *)addr->ai->ai_addr)->sin_addr,
+    if (step.ai) {
+       if (family == AF_INET)
+           memcpy(buf, &((struct sockaddr_in *)step.ai->ai_addr)->sin_addr,
                   sizeof(struct in_addr));
-       else if (addr->family == AF_INET6)
-           memcpy(buf, &((struct sockaddr_in6 *)addr->ai->ai_addr)->sin6_addr,
+       else if (family == AF_INET6)
+           memcpy(buf, &((struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr,
                   sizeof(struct in6_addr));
        else
            assert(FALSE);
     } else
 #endif
-    if (addr->family == AF_INET) {
+    if (family == AF_INET) {
        struct in_addr a;
-       assert(addr->addresses && addr->curraddr < addr->naddresses);
-       a.s_addr = p_htonl(addr->addresses[addr->curraddr]);
+       assert(addr->addresses && step.curraddr < addr->naddresses);
+       a.s_addr = p_htonl(addr->addresses[step.curraddr]);
        memcpy(buf, (char*) &a.s_addr, 4);
     }
 }
 
 void sk_addr_free(SockAddr addr)
 {
+    if (--addr->refcount > 0)
+       return;
 #ifndef NO_IPV6
     if (addr->ais && p_freeaddrinfo)
        p_freeaddrinfo(addr->ais);
@@ -666,6 +887,12 @@ void sk_addr_free(SockAddr addr)
     sfree(addr);
 }
 
+SockAddr sk_addr_dup(SockAddr addr)
+{
+    addr->refcount++;
+    return addr;
+}
+
 static Plug sk_tcp_plug(Socket sock, Plug p)
 {
     Actual_Socket s = (Actual_Socket) sock;
@@ -686,25 +913,25 @@ static void sk_tcp_flush(Socket s)
 static void sk_tcp_close(Socket s);
 static int sk_tcp_write(Socket s, const char *data, int len);
 static int sk_tcp_write_oob(Socket s, const char *data, int len);
-static void sk_tcp_set_private_ptr(Socket s, void *ptr);
-static void *sk_tcp_get_private_ptr(Socket s);
+static void sk_tcp_write_eof(Socket s);
 static void sk_tcp_set_frozen(Socket s, int is_frozen);
 static const char *sk_tcp_socket_error(Socket s);
+static char *sk_tcp_peer_info(Socket s);
 
 extern char *do_select(SOCKET skt, int startup);
 
-Socket sk_register(void *sock, Plug plug)
+static Socket sk_tcp_accept(accept_ctx_t ctx, Plug plug)
 {
     static const struct socket_function_table fn_table = {
        sk_tcp_plug,
        sk_tcp_close,
        sk_tcp_write,
        sk_tcp_write_oob,
+       sk_tcp_write_eof,
        sk_tcp_flush,
-       sk_tcp_set_private_ptr,
-       sk_tcp_get_private_ptr,
        sk_tcp_set_frozen,
-       sk_tcp_socket_error
+       sk_tcp_socket_error,
+       sk_tcp_peer_info,
     };
 
     DWORD err;
@@ -721,6 +948,7 @@ Socket sk_register(void *sock, Plug plug)
     bufchain_init(&ret->output_data);
     ret->writable = 1;                /* to start with */
     ret->sending_oob = 0;
+    ret->outgoingeof = EOF_NO;
     ret->frozen = 1;
     ret->frozen_readable = 0;
     ret->localhost_only = 0;          /* unused, but best init anyway */
@@ -728,7 +956,7 @@ Socket sk_register(void *sock, Plug plug)
     ret->parent = ret->child = NULL;
     ret->addr = NULL;
 
-    ret->s = (SOCKET)sock;
+    ret->s = (SOCKET)ctx.p;
 
     if (ret->s == INVALID_SOCKET) {
        err = p_WSAGetLastError();
@@ -768,22 +996,24 @@ static DWORD try_connect(Actual_Socket sock)
         p_closesocket(sock->s);
     }
 
-    plug_log(sock->plug, 0, sock->addr, sock->port, NULL, 0);
+    {
+        struct SockAddr_tag thisaddr = sk_extractaddr_tmp(
+            sock->addr, &sock->step);
+        plug_log(sock->plug, 0, &thisaddr, sock->port, NULL, 0);
+    }
 
     /*
      * Open socket.
      */
-#ifndef NO_IPV6
-    /* Let's default to IPv6, this shouldn't hurt anybody
-     * If the stack supports IPv6 it will also allow IPv4 connections. */
-    if (sock->addr->ai) {
-       family = sock->addr->ai->ai_family;
-    } else
-#endif
-    {
-       /* Default to IPv4 */
-       family = AF_INET;
-    }
+    family = SOCKADDR_FAMILY(sock->addr, sock->step);
+
+    /*
+     * Remove the socket from the tree before we overwrite its
+     * internal socket id, because that forms part of the tree's
+     * sorting criterion. We'll add it back before exiting this
+     * function, whether we changed anything or not.
+     */
+    del234(sktree, sock);
 
     s = p_socket(family, SOCK_STREAM, 0);
     sock->s = s;
@@ -794,6 +1024,8 @@ static DWORD try_connect(Actual_Socket sock)
        goto ret;
     }
 
+       SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0);
+
     if (sock->oobinline) {
        BOOL b = TRUE;
        p_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b));
@@ -835,11 +1067,10 @@ static DWORD try_connect(Actual_Socket sock)
            a.sin_port = p_htons(localport);
        }
 #ifndef NO_IPV6
-       sockcode = p_bind(s, (sock->addr->family == AF_INET6 ?
-                          (struct sockaddr *) &a6 :
-                          (struct sockaddr *) &a),
-                      (sock->addr->family ==
-                       AF_INET6 ? sizeof(a6) : sizeof(a)));
+       sockcode = p_bind(s, (family == AF_INET6 ?
+                             (struct sockaddr *) &a6 :
+                             (struct sockaddr *) &a),
+                         (family == AF_INET6 ? sizeof(a6) : sizeof(a)));
 #else
        sockcode = p_bind(s, (struct sockaddr *) &a, sizeof(a));
 #endif
@@ -868,26 +1099,26 @@ static DWORD try_connect(Actual_Socket sock)
      * Connect to remote address.
      */
 #ifndef NO_IPV6
-    if (sock->addr->ai) {
+    if (sock->step.ai) {
        if (family == AF_INET6) {
            a6.sin6_family = AF_INET6;
            a6.sin6_port = p_htons((short) sock->port);
            a6.sin6_addr =
-               ((struct sockaddr_in6 *) sock->addr->ai->ai_addr)->sin6_addr;
-           a6.sin6_flowinfo = ((struct sockaddr_in6 *) sock->addr->ai->ai_addr)->sin6_flowinfo;
-           a6.sin6_scope_id = ((struct sockaddr_in6 *) sock->addr->ai->ai_addr)->sin6_scope_id;
+               ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_addr;
+           a6.sin6_flowinfo = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_flowinfo;
+           a6.sin6_scope_id = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_scope_id;
        } else {
            a.sin_family = AF_INET;
            a.sin_addr =
-               ((struct sockaddr_in *) sock->addr->ai->ai_addr)->sin_addr;
+               ((struct sockaddr_in *) sock->step.ai->ai_addr)->sin_addr;
            a.sin_port = p_htons((short) sock->port);
        }
     } else
 #endif
     {
-       assert(sock->addr->addresses && sock->addr->curraddr < sock->addr->naddresses);
+       assert(sock->addr->addresses && sock->step.curraddr < sock->addr->naddresses);
        a.sin_family = AF_INET;
-       a.sin_addr.s_addr = p_htonl(sock->addr->addresses[sock->addr->curraddr]);
+       a.sin_addr.s_addr = p_htonl(sock->addr->addresses[sock->step.curraddr]);
        a.sin_port = p_htons((short) sock->port);
     }
 
@@ -929,13 +1160,20 @@ static DWORD try_connect(Actual_Socket sock)
        sock->writable = 1;
     }
 
-    add234(sktree, sock);
-
     err = 0;
 
     ret:
-    if (err)
-       plug_log(sock->plug, 1, sock->addr, sock->port, sock->error, err);
+
+    /*
+     * No matter what happened, put the socket back in the tree.
+     */
+    add234(sktree, sock);
+
+    if (err) {
+        struct SockAddr_tag thisaddr = sk_extractaddr_tmp(
+            sock->addr, &sock->step);
+       plug_log(sock->plug, 1, &thisaddr, sock->port, sock->error, err);
+    }
     return err;
 }
 
@@ -947,11 +1185,11 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
        sk_tcp_close,
        sk_tcp_write,
        sk_tcp_write_oob,
+       sk_tcp_write_eof,
        sk_tcp_flush,
-       sk_tcp_set_private_ptr,
-       sk_tcp_get_private_ptr,
        sk_tcp_set_frozen,
-       sk_tcp_socket_error
+       sk_tcp_socket_error,
+       sk_tcp_peer_info,
     };
 
     Actual_Socket ret;
@@ -968,6 +1206,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     ret->connected = 0;                       /* to start with */
     ret->writable = 0;                /* to start with */
     ret->sending_oob = 0;
+    ret->outgoingeof = EOF_NO;
     ret->frozen = 0;
     ret->frozen_readable = 0;
     ret->localhost_only = 0;          /* unused, but best init anyway */
@@ -979,29 +1218,30 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     ret->privport = privport;
     ret->port = port;
     ret->addr = addr;
+    START_STEP(ret->addr, ret->step);
     ret->s = INVALID_SOCKET;
 
     err = 0;
     do {
         err = try_connect(ret);
-    } while (err && sk_nextaddr(ret->addr));
+    } while (err && sk_nextaddr(ret->addr, &ret->step));
 
     return (Socket) ret;
 }
 
-Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
-                     int orig_address_family)
+Socket sk_newlistener(const char *srcaddr, int port, Plug plug,
+                      int local_host_only, int orig_address_family)
 {
     static const struct socket_function_table fn_table = {
        sk_tcp_plug,
        sk_tcp_close,
        sk_tcp_write,
        sk_tcp_write_oob,
+       sk_tcp_write_eof,
        sk_tcp_flush,
-       sk_tcp_set_private_ptr,
-       sk_tcp_get_private_ptr,
        sk_tcp_set_frozen,
-       sk_tcp_socket_error
+       sk_tcp_socket_error,
+       sk_tcp_peer_info,
     };
 
     SOCKET s;
@@ -1028,6 +1268,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
     bufchain_init(&ret->output_data);
     ret->writable = 0;                /* to start with */
     ret->sending_oob = 0;
+    ret->outgoingeof = EOF_NO;
     ret->frozen = 0;
     ret->frozen_readable = 0;
     ret->localhost_only = local_host_only;
@@ -1066,6 +1307,8 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
        return (Socket) ret;
     }
 
+       SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0);
+
     ret->oobinline = 0;
 
     p_setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
@@ -1074,15 +1317,29 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
        if (address_family == AF_INET6) {
            memset(&a6, 0, sizeof(a6));
            a6.sin6_family = AF_INET6;
-           /* FIXME: srcaddr is ignored for IPv6, because I (SGT) don't
-            * know how to do it. :-)
-            * (jeroen:) saddr is specified as an address.. eg 2001:db8::1
-            * Thus we need either a parser that understands [2001:db8::1]:80
-            * style addresses and/or enhance this to understand hostnames too. */
            if (local_host_only)
                a6.sin6_addr = in6addr_loopback;
            else
                a6.sin6_addr = in6addr_any;
+            if (srcaddr != NULL && p_getaddrinfo) {
+                struct addrinfo hints;
+                struct addrinfo *ai;
+                int err;
+
+                memset(&hints, 0, sizeof(hints));
+                hints.ai_family = AF_INET6;
+                hints.ai_flags = 0;
+                {
+                    /* strip [] on IPv6 address literals */
+                    char *trimmed_addr = host_strduptrim(srcaddr);
+                    err = p_getaddrinfo(trimmed_addr, NULL, &hints, &ai);
+                    sfree(trimmed_addr);
+                }
+                if (err == 0 && ai->ai_family == AF_INET6) {
+                    a6.sin6_addr =
+                        ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
+                }
+            }
            a6.sin6_port = p_htons(port);
        } else
 #endif
@@ -1139,7 +1396,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only,
 
     if (p_listen(s, SOMAXCONN) == SOCKET_ERROR) {
         p_closesocket(s);
-       ret->error = winsock_error_string(err);
+       ret->error = winsock_error_string(p_WSAGetLastError());
        return (Socket) ret;
     }
 
@@ -1195,6 +1452,27 @@ static void sk_tcp_close(Socket sock)
     sfree(s);
 }
 
+/*
+ * Deal with socket errors detected in try_send().
+ */
+static void socket_error_callback(void *vs)
+{
+    Actual_Socket s = (Actual_Socket)vs;
+
+    /*
+     * Just in case other socket work has caused this socket to vanish
+     * or become somehow non-erroneous before this callback arrived...
+     */
+    if (!find234(sktree, s, NULL) || !s->pending_error)
+        return;
+
+    /*
+     * An error has occurred on this socket. Pass it to the plug.
+     */
+    plug_closing(s->plug, winsock_error_string(s->pending_error),
+                 s->pending_error, 0);
+}
+
 /*
  * The function which tries to send on a socket once it's deemed
  * writable.
@@ -1244,6 +1522,7 @@ void try_send(Actual_Socket s)
                 * plug_closing()) at some suitable future moment.
                 */
                s->pending_error = err;
+                queue_toplevel_callback(socket_error_callback, s);
                return;
            } else {
                /* We're inside the Windows frontend here, so we know
@@ -1264,12 +1543,23 @@ void try_send(Actual_Socket s)
            }
        }
     }
+
+    /*
+     * If we reach here, we've finished sending everything we might
+     * have needed to send. Send EOF, if we need to.
+     */
+    if (s->outgoingeof == EOF_PENDING) {
+        p_shutdown(s->s, SD_SEND);
+        s->outgoingeof = EOF_SENT;
+    }
 }
 
 static int sk_tcp_write(Socket sock, const char *buf, int len)
 {
     Actual_Socket s = (Actual_Socket) sock;
 
+    assert(s->outgoingeof == EOF_NO);
+
     /*
      * Add the data to the buffer list on the socket.
      */
@@ -1288,6 +1578,8 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len)
 {
     Actual_Socket s = (Actual_Socket) sock;
 
+    assert(s->outgoingeof == EOF_NO);
+
     /*
      * Replace the buffer list on the socket with the data.
      */
@@ -1305,6 +1597,24 @@ static int sk_tcp_write_oob(Socket sock, const char *buf, int len)
     return s->sending_oob;
 }
 
+static void sk_tcp_write_eof(Socket sock)
+{
+    Actual_Socket s = (Actual_Socket) sock;
+
+    assert(s->outgoingeof == EOF_NO);
+
+    /*
+     * Mark the socket as pending outgoing EOF.
+     */
+    s->outgoingeof = EOF_PENDING;
+
+    /*
+     * Now try sending from the start of the buffer list.
+     */
+    if (s->writable)
+       try_send(s);
+}
+
 int select_result(WPARAM wParam, LPARAM lParam)
 {
     int ret, open;
@@ -1328,9 +1638,11 @@ int select_result(WPARAM wParam, LPARAM lParam)
         * plug.
         */
        if (s->addr) {
-           plug_log(s->plug, 1, s->addr, s->port,
+            struct SockAddr_tag thisaddr = sk_extractaddr_tmp(
+                s->addr, &s->step);
+           plug_log(s->plug, 1, &thisaddr, s->port,
                     winsock_error_string(err), err);
-           while (s->addr && sk_nextaddr(s->addr)) {
+           while (err && s->addr && sk_nextaddr(s->addr, &s->step)) {
                err = try_connect(s);
            }
        }
@@ -1408,7 +1720,7 @@ int select_result(WPARAM wParam, LPARAM lParam)
        ret = p_recv(s->s, buf, sizeof(buf), MSG_OOB);
        noise_ultralight(ret);
        if (ret <= 0) {
-           char *str = (ret == 0 ? "Internal networking trouble" :
+           const char *str = (ret == 0 ? "Internal networking trouble" :
                         winsock_error_string(p_WSAGetLastError()));
            /* We're inside the Windows frontend here, so we know
             * that the frontend handle is unnecessary. */
@@ -1457,6 +1769,7 @@ int select_result(WPARAM wParam, LPARAM lParam)
 #endif
            int addrlen = sizeof(isa);
            SOCKET t;  /* socket of connection */
+            accept_ctx_t actx;
 
            memset(&isa, 0, sizeof(isa));
            err = 0;
@@ -1467,15 +1780,19 @@ int select_result(WPARAM wParam, LPARAM lParam)
                if (err == WSATRY_AGAIN)
                    break;
            }
+
+            actx.p = (void *)t;
+
 #ifndef NO_IPV6
             if (isa.ss_family == AF_INET &&
                 s->localhost_only &&
-                !ipv4_is_local_addr(((struct sockaddr_in *)&isa)->sin_addr)) {
+                !ipv4_is_local_addr(((struct sockaddr_in *)&isa)->sin_addr))
 #else
-           if (s->localhost_only && !ipv4_is_local_addr(isa.sin_addr)) {
+           if (s->localhost_only && !ipv4_is_local_addr(isa.sin_addr))
 #endif
+           {
                p_closesocket(t);      /* dodgy WinSock let nonlocal through */
-           } else if (plug_accepting(s->plug, (void*)t)) {
+           } else if (plug_accepting(s->plug, sk_tcp_accept, actx)) {
                p_closesocket(t);      /* denied or error */
            }
        }
@@ -1484,60 +1801,6 @@ int select_result(WPARAM wParam, LPARAM lParam)
     return 1;
 }
 
-/*
- * Deal with socket errors detected in try_send().
- */
-void net_pending_errors(void)
-{
-    int i;
-    Actual_Socket s;
-
-    /*
-     * This might be a fiddly business, because it's just possible
-     * that handling a pending error on one socket might cause
-     * others to be closed. (I can't think of any reason this might
-     * happen in current SSH implementation, but to maintain
-     * generality of this network layer I'll assume the worst.)
-     * 
-     * So what we'll do is search the socket list for _one_ socket
-     * with a pending error, and then handle it, and then search
-     * the list again _from the beginning_. Repeat until we make a
-     * pass with no socket errors present. That way we are
-     * protected against the socket list changing under our feet.
-     */
-
-    do {
-       for (i = 0; (s = index234(sktree, i)) != NULL; i++) {
-           if (s->pending_error) {
-               /*
-                * An error has occurred on this socket. Pass it to the
-                * plug.
-                */
-               plug_closing(s->plug,
-                            winsock_error_string(s->pending_error),
-                            s->pending_error, 0);
-               break;
-           }
-       }
-    } while (s);
-}
-
-/*
- * Each socket abstraction contains a `void *' private field in
- * which the client can keep state.
- */
-static void sk_tcp_set_private_ptr(Socket sock, void *ptr)
-{
-    Actual_Socket s = (Actual_Socket) sock;
-    s->private_ptr = ptr;
-}
-
-static void *sk_tcp_get_private_ptr(Socket sock)
-{
-    Actual_Socket s = (Actual_Socket) sock;
-    return s->private_ptr;
-}
-
 /*
  * Special error values are returned from sk_namelookup and sk_new
  * if there's a problem. These functions extract an error message,
@@ -1553,6 +1816,38 @@ static const char *sk_tcp_socket_error(Socket sock)
     return s->error;
 }
 
+static char *sk_tcp_peer_info(Socket sock)
+{
+    Actual_Socket s = (Actual_Socket) sock;
+#ifdef NO_IPV6
+    struct sockaddr_in addr;
+#else
+    struct sockaddr_storage addr;
+    char buf[INET6_ADDRSTRLEN];
+#endif
+    int addrlen = sizeof(addr);
+
+    if (p_getpeername(s->s, (struct sockaddr *)&addr, &addrlen) < 0)
+        return NULL;
+
+    if (((struct sockaddr *)&addr)->sa_family == AF_INET) {
+        return dupprintf
+            ("%s:%d",
+             p_inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr),
+             (int)p_ntohs(((struct sockaddr_in *)&addr)->sin_port));
+#ifndef NO_IPV6
+    } else if (((struct sockaddr *)&addr)->sa_family == AF_INET6) {
+        return dupprintf
+            ("[%s]:%d",
+             p_inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr,
+                         buf, sizeof(buf)),
+             (int)p_ntohs(((struct sockaddr_in6 *)&addr)->sin6_port));
+#endif
+    } else {
+        return NULL;
+    }
+}
+
 static void sk_tcp_set_frozen(Socket sock, int is_frozen)
 {
     Actual_Socket s = (Actual_Socket) sock;
@@ -1569,6 +1864,17 @@ static void sk_tcp_set_frozen(Socket sock, int is_frozen)
     s->frozen_readable = 0;
 }
 
+void socket_reselect_all(void)
+{
+    Actual_Socket s;
+    int i;
+
+    for (i = 0; (s = index234(sktree, i)) != NULL; i++) {
+       if (!s->frozen)
+           do_select(s->s, 1);
+    }
+}
+
 /*
  * For Plink: enumerate all sockets currently active.
  */
@@ -1606,10 +1912,28 @@ int net_service_lookup(char *service)
        return 0;
 }
 
-SockAddr platform_get_x11_unix_address(int displaynum, char **canonicalname)
+char *get_hostname(void)
+{
+    int len = 128;
+    char *hostname = NULL;
+    do {
+       len *= 2;
+       hostname = sresize(hostname, len, char);
+       if (p_gethostname(hostname, len) < 0) {
+           sfree(hostname);
+           hostname = NULL;
+           break;
+       }
+    } while (strlen(hostname) >= (size_t)(len-1));
+    return hostname;
+}
+
+SockAddr platform_get_x11_unix_address(const char *display, int displaynum,
+                                      char **canonicalname)
 {
     SockAddr ret = snew(struct SockAddr_tag);
     memset(ret, 0, sizeof(struct SockAddr_tag));
     ret->error = "unix sockets not supported on this platform";
+    ret->refcount = 1;
     return ret;
 }