Colin's const-fixing Patch Of Death. Seems to build fine on Windows
[u/mdw/putty] / unix / uxnet.c
index 82b413d..77a4885 100644 (file)
@@ -26,7 +26,7 @@
 struct Socket_tag {
     struct socket_function_table *fn;
     /* the above variable absolutely *must* be the first in this structure */
-    char *error;
+    const char *error;
     int s;
     Plug plug;
     void *private_ptr;
@@ -56,7 +56,7 @@ struct Socket_tag {
 typedef struct Socket_tag *Actual_Socket;
 
 struct SockAddr_tag {
-    char *error;
+    const char *error;
     /*
      * Which address family this address belongs to. AF_INET for
      * IPv4; AF_INET6 for IPv6; AF_UNSPEC indicates that name
@@ -64,15 +64,18 @@ struct SockAddr_tag {
      * in this SockAddr structure.
      */
     int family;
-    unsigned long address;            /* Address IPv4 style. */
 #ifdef IPV6
     struct addrinfo *ai;              /* Address IPv6 style. */
+#else
+    unsigned long address;            /* Address IPv4 style. */
 #endif
     char hostname[512];                       /* Store an unresolved host name. */
 };
 
 static tree234 *sktree;
 
+static void uxsel_tell(Actual_Socket s);
+
 static int cmpfortree(void *av, void *bv)
 {
     Actual_Socket a = (Actual_Socket) av, b = (Actual_Socket) bv;
@@ -112,16 +115,21 @@ void sk_cleanup(void)
     }
 }
 
-char *error_string(int error)
+const char *error_string(int error)
 {
     return strerror(error);
 }
 
-SockAddr sk_namelookup(char *host, char **canonicalname)
+SockAddr sk_namelookup(const char *host, char **canonicalname)
 {
-    SockAddr ret = smalloc(sizeof(struct SockAddr_tag));
+    SockAddr ret = snew(struct SockAddr_tag);
+#ifdef IPV6
+    struct addrinfo hints;
+    int err;
+#else
     unsigned long a;
     struct hostent *h = NULL;
+#endif
     char realhost[8192];
 
     /* Clear the structure and default to IPv4. */
@@ -130,61 +138,49 @@ SockAddr sk_namelookup(char *host, char **canonicalname)
     *realhost = '\0';
     ret->error = NULL;
 
-    if ((a = inet_addr(host)) == (unsigned long) INADDR_NONE) {
 #ifdef IPV6
-       if (getaddrinfo(host, NULL, NULL, &ret->ai) == 0) {
-           ret->family = ret->ai->ai_family;
-       } else
-#endif
-       {
-           /*
-            * Otherwise use the IPv4-only gethostbyname... (NOTE:
-            * we don't use gethostbyname as a fallback!)
-            */
-           if (ret->family == 0) {
+    hints.ai_flags = AI_CANONNAME;
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = 0;
+    hints.ai_protocol = 0;
+    hints.ai_addrlen = 0;
+    hints.ai_addr = NULL;
+    hints.ai_canonname = NULL;
+    hints.ai_next = NULL;
+    err = getaddrinfo(host, NULL, NULL, &ret->ai);
+    if (err != 0) {
+       ret->error = gai_strerror(err);
+       return ret;
+    }
+    ret->family = ret->ai->ai_family;
+    *realhost = '\0';
+    if (ret->ai->ai_canonname != NULL)
+       strncat(realhost, ret->ai->ai_canonname, sizeof(realhost) - 1);
+    else
+       strncat(realhost, host, sizeof(realhost) - 1);
+#else
+    if ((a = inet_addr(host)) == (unsigned long) INADDR_NONE) {
+       /*
+        * Otherwise use the IPv4-only gethostbyname... (NOTE:
+        * we don't use gethostbyname as a fallback!)
+        */
+       if (ret->family == 0) {
                /*debug(("Resolving \"%s\" with gethostbyname() (IPv4 only)...\n", host)); */
                if ( (h = gethostbyname(host)) )
-                   ret->family = AF_INET;
-           }
-           if (ret->family == 0) {
+                       ret->family = AF_INET;
+       }
+       if (ret->family == 0) {
                ret->error = (h_errno == HOST_NOT_FOUND ||
-                             h_errno == NO_DATA ||
-                             h_errno == NO_ADDRESS ? "Host does not exist" :
-                             h_errno == TRY_AGAIN ?
-                             "Temporary name service failure" :
-                             "gethostbyname: unknown error");
+                   h_errno == NO_DATA ||
+                   h_errno == NO_ADDRESS ? "Host does not exist" :
+                   h_errno == TRY_AGAIN ?
+                   "Temporary name service failure" :
+                   "gethostbyname: unknown error");
                return ret;
-           }
-       }
-
-#ifdef IPV6
-       /* If we got an address info use that... */
-       if (ret->ai) {
-
-           /* 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)
-               memcpy(&a,
-                      (char *) &((struct sockaddr_in *) ret->ai->
-                                 ai_addr)->sin_addr, sizeof(a));
-
-           /* Now let's find that canonicalname... */
-           if (getnameinfo((struct sockaddr *) ret->ai->ai_addr,
-                           ret->family ==
-                           AF_INET ? sizeof(struct sockaddr_in) :
-                           sizeof(struct sockaddr_in6), realhost,
-                           sizeof(realhost), NULL, 0, 0) != 0) {
-               strncpy(realhost, host, sizeof(realhost));
-           }
-       }
-       /* We used the IPv4-only gethostbyname()... */
-       else
-#endif
-       {
-           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));
        }
+       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));
     } else {
        /*
         * This must be a numeric IPv4 address because it caused a
@@ -194,15 +190,16 @@ SockAddr sk_namelookup(char *host, char **canonicalname)
        strncpy(realhost, host, sizeof(realhost));
     }
     ret->address = ntohl(a);
+#endif
     realhost[lenof(realhost)-1] = '\0';
-    *canonicalname = smalloc(1+strlen(realhost));
+    *canonicalname = snewn(1+strlen(realhost), char);
     strcpy(*canonicalname, realhost);
     return ret;
 }
 
-SockAddr sk_nonamelookup(char *host)
+SockAddr sk_nonamelookup(const char *host)
 {
-    SockAddr ret = smalloc(sizeof(struct SockAddr_tag));
+    SockAddr ret = snew(struct SockAddr_tag);
     ret->error = NULL;
     ret->family = AF_UNSPEC;
     strncpy(ret->hostname, host, lenof(ret->hostname));
@@ -212,20 +209,24 @@ SockAddr sk_nonamelookup(char *host)
 
 void sk_getaddr(SockAddr addr, char *buf, int buflen)
 {
+
+    if (addr->family == AF_UNSPEC) {
+       strncpy(buf, addr->hostname, buflen);
+       buf[buflen-1] = '\0';
+    } else {
 #ifdef IPV6
-    if (addr->family == AF_INET6) {
-       FIXME; /* I don't know how to get a text form of an IPv6 address. */
-    } else
-#endif
-    if (addr->family == AF_INET) {
+       if (getnameinfo(addr->ai->ai_addr, addr->ai->ai_addrlen, buf, buflen,
+                       NULL, 0, NI_NUMERICHOST) != 0) {
+           buf[0] = '\0';
+           strncat(buf, "<unknown>", buflen - 1);
+       }
+#else
        struct in_addr a;
+       assert(addr->family == AF_INET);
        a.s_addr = htonl(addr->address);
        strncpy(buf, inet_ntoa(a), buflen);
        buf[buflen-1] = '\0';
-    } else {
-       assert(addr->family == AF_UNSPEC);
-       strncpy(buf, addr->hostname, buflen);
-       buf[buflen-1] = '\0';
+#endif
     }
 }
 
@@ -236,18 +237,25 @@ int sk_hostname_is_local(char *name)
 
 int sk_address_is_local(SockAddr addr)
 {
+
+    if (addr->family == AF_UNSPEC)
+       return 0;                      /* we don't know; assume not */
+    else {
 #ifdef IPV6
-    if (addr->family == AF_INET6) {
-       FIXME;  /* someone who can compile for IPV6 had better do this bit */
-    } else
-#endif
-    if (addr->family == AF_INET) {
+       if (addr->family == AF_INET)
+           return ipv4_is_loopback(
+               ((struct sockaddr_in *)addr->ai->ai_addr)->sin_addr);
+       else if (addr->family == AF_INET6)
+           return IN6_IS_ADDR_LOOPBACK(
+               &((struct sockaddr_in6 *)addr->ai->ai_addr)->sin6_addr);
+       else
+           return 0;
+#else
        struct in_addr a;
+       assert(addr->family == AF_INET);
        a.s_addr = htonl(addr->address);
        return ipv4_is_loopback(a);
-    } else {
-       assert(addr->family == AF_UNSPEC);
-       return 0;                      /* we don't know; assume not */
+#endif
     }
 }
 
@@ -262,21 +270,32 @@ int sk_addrtype(SockAddr addr)
 
 void sk_addrcopy(SockAddr addr, char *buf)
 {
-    assert(addr->family != AF_UNSPEC);
+
 #ifdef IPV6
-    if (addr->family == AF_INET6) {
-       memcpy(buf, (char*) addr->ai, 16);
-    } else
+    if (addr->family == AF_INET)
+       memcpy(buf, &((struct sockaddr_in *)addr->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,
+              sizeof(struct in6_addr));
+    else
+       assert(FALSE);
+#else
+    struct in_addr a;
+
+    assert(addr->family == AF_INET);
+    a.s_addr = htonl(addr->address);
+    memcpy(buf, (char*) &a.s_addr, 4);
 #endif
-    if (addr->family == AF_INET) {
-       struct in_addr a;
-       a.s_addr = htonl(addr->address);
-       memcpy(buf, (char*) &a.s_addr, 4);
-    }
 }
 
 void sk_addr_free(SockAddr addr)
 {
+
+#ifdef IPV6
+    if (addr->ai != NULL)
+       freeaddrinfo(addr->ai);
+#endif
     sfree(addr);
 }
 
@@ -298,34 +317,34 @@ static void sk_tcp_flush(Socket s)
 }
 
 static void sk_tcp_close(Socket s);
-static int sk_tcp_write(Socket s, char *data, int len);
-static int sk_tcp_write_oob(Socket s, char *data, int len);
+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_set_frozen(Socket s, int is_frozen);
-static char *sk_tcp_socket_error(Socket s);
+static const char *sk_tcp_socket_error(Socket s);
+
+static struct socket_function_table tcp_fn_table = {
+    sk_tcp_plug,
+    sk_tcp_close,
+    sk_tcp_write,
+    sk_tcp_write_oob,
+    sk_tcp_flush,
+    sk_tcp_set_private_ptr,
+    sk_tcp_get_private_ptr,
+    sk_tcp_set_frozen,
+    sk_tcp_socket_error
+};
 
 Socket sk_register(void *sock, Plug plug)
 {
-    static struct socket_function_table fn_table = {
-       sk_tcp_plug,
-       sk_tcp_close,
-       sk_tcp_write,
-       sk_tcp_write_oob,
-       sk_tcp_flush,
-       sk_tcp_set_private_ptr,
-       sk_tcp_get_private_ptr,
-       sk_tcp_set_frozen,
-       sk_tcp_socket_error
-    };
-
     Actual_Socket ret;
 
     /*
      * Create Socket structure.
      */
-    ret = smalloc(sizeof(struct Socket_tag));
-    ret->fn = &fn_table;
+    ret = snew(struct Socket_tag);
+    ret->fn = &tcp_fn_table;
     ret->error = NULL;
     ret->plug = plug;
     bufchain_init(&ret->output_data);
@@ -347,6 +366,7 @@ Socket sk_register(void *sock, Plug plug)
 
     ret->oobinline = 0;
 
+    uxsel_tell(ret);
     add234(sktree, ret);
 
     return (Socket) ret;
@@ -355,18 +375,6 @@ Socket sk_register(void *sock, Plug plug)
 Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
              int nodelay, Plug plug)
 {
-    static struct socket_function_table fn_table = {
-       sk_tcp_plug,
-       sk_tcp_close,
-       sk_tcp_write,
-       sk_tcp_write_oob,
-       sk_tcp_flush,
-       sk_tcp_set_private_ptr,
-       sk_tcp_get_private_ptr,
-       sk_tcp_set_frozen,
-       sk_tcp_socket_error
-    };
-
     int s;
 #ifdef IPV6
     struct sockaddr_in6 a6;
@@ -375,12 +383,13 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     int err;
     Actual_Socket ret;
     short localport;
+    int fl;
 
     /*
      * Create Socket structure.
      */
-    ret = smalloc(sizeof(struct Socket_tag));
-    ret->fn = &fn_table;
+    ret = snew(struct Socket_tag);
+    ret->fn = &tcp_fn_table;
     ret->error = NULL;
     ret->plug = plug;
     bufchain_init(&ret->output_data);
@@ -431,26 +440,20 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
 
 #ifdef IPV6
        if (addr->family == AF_INET6) {
-           memset(&a6, 0, sizeof(a6));
+           /* XXX use getaddrinfo to get a local address? */
            a6.sin6_family = AF_INET6;
-/*a6.sin6_addr      = in6addr_any; *//* == 0 */
+           a6.sin6_addr = in6addr_any;
            a6.sin6_port = htons(localport);
+           retcode = bind(s, (struct sockaddr *) &a6, sizeof(a6));
        } else
 #endif
        {
+           assert(addr->family == AF_INET);
            a.sin_family = AF_INET;
            a.sin_addr.s_addr = htonl(INADDR_ANY);
            a.sin_port = htons(localport);
+           retcode = bind(s, (struct sockaddr *) &a, sizeof(a));
        }
-#ifdef IPV6
-       retcode = bind(s, (addr->family == AF_INET6 ?
-                          (struct sockaddr *) &a6 :
-                          (struct sockaddr *) &a),
-                      (addr->family ==
-                       AF_INET6 ? sizeof(a6) : sizeof(a)));
-#else
-       retcode = bind(s, (struct sockaddr *) &a, sizeof(a));
-#endif
        if (retcode >= 0) {
            err = 0;
            break;                     /* done */
@@ -476,36 +479,33 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
      * Connect to remote address.
      */
 #ifdef IPV6
-    if (addr->family == AF_INET6) {
-       memset(&a, 0, sizeof(a));
-       a6.sin6_family = AF_INET6;
-       a6.sin6_port = htons((short) port);
-       a6.sin6_addr =
-           ((struct sockaddr_in6 *) addr->ai->ai_addr)->sin6_addr;
-    } else
-#endif
-    {
-       a.sin_family = AF_INET;
-       a.sin_addr.s_addr = htonl(addr->address);
-       a.sin_port = htons((short) port);
+    /* XXX would be better to have got getaddrinfo() to fill in the port. */
+    if (addr->family == AF_INET)
+       ((struct sockaddr_in *)addr->ai->ai_addr)->sin_port =
+           htons(port);
+    else {
+       assert(addr->family == AF_INET6);
+       ((struct sockaddr_in *)addr->ai->ai_addr)->sin_port =
+           htons(port);
     }
+#else
+    a.sin_family = AF_INET;
+    a.sin_addr.s_addr = htonl(addr->address);
+    a.sin_port = htons((short) port);
+#endif
+
+    fl = fcntl(s, F_GETFL);
+    if (fl != -1)
+       fcntl(s, F_SETFL, fl | O_NONBLOCK);
 
     if ((
 #ifdef IPV6
-           connect(s, ((addr->family == AF_INET6) ?
-                       (struct sockaddr *) &a6 : (struct sockaddr *) &a),
-                   (addr->family == AF_INET6) ? sizeof(a6) : sizeof(a))
+           connect(s, addr->ai->ai_addr, addr->ai->ai_addrlen)
 #else
            connect(s, (struct sockaddr *) &a, sizeof(a))
 #endif
        ) < 0) {
-       /*
-        * FIXME: We are prepared to receive EWOULDBLOCK here,
-        * because we might want the connection to be made
-        * asynchronously; but how do we actually arrange this in
-        * Unix? I forget.
-        */
-       if ( errno != EWOULDBLOCK ) {
+       if ( errno != EINPROGRESS ) {
            ret->error = error_string(errno);
            return (Socket) ret;
        }
@@ -518,6 +518,7 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
        ret->writable = 1;
     }
 
+    uxsel_tell(ret);
     add234(sktree, ret);
 
     return (Socket) ret;
@@ -525,22 +526,14 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
 
 Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only)
 {
-    static struct socket_function_table fn_table = {
-       sk_tcp_plug,
-       sk_tcp_close,
-       sk_tcp_write,
-       sk_tcp_write_oob,
-       sk_tcp_flush,
-       sk_tcp_set_private_ptr,
-       sk_tcp_get_private_ptr,
-       sk_tcp_set_frozen,
-       sk_tcp_socket_error
-    };
-
     int s;
 #ifdef IPV6
+#if 0
     struct sockaddr_in6 a6;
 #endif
+    struct addrinfo hints, *ai;
+    char portstr[6];
+#endif
     struct sockaddr_in a;
     int err;
     Actual_Socket ret;
@@ -550,8 +543,8 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only)
     /*
      * Create Socket structure.
      */
-    ret = smalloc(sizeof(struct Socket_tag));
-    ret->fn = &fn_table;
+    ret = snew(struct Socket_tag);
+    ret->fn = &tcp_fn_table;
     ret->error = NULL;
     ret->plug = plug;
     bufchain_init(&ret->output_data);
@@ -580,11 +573,24 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only)
     setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on));
 
 #ifdef IPV6
-    if (addr->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. :-) */
+    hints.ai_flags = AI_NUMERICHOST;
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = 0;
+    hints.ai_protocol = 0;
+    hints.ai_addrlen = 0;
+    hints.ai_addr = NULL;
+    hints.ai_canonname = NULL;
+    hints.ai_next = NULL;
+    sprintf(portstr, "%d", port);
+    if (srcaddr != NULL && getaddrinfo(srcaddr, portstr, &hints, &ai) == 0)
+       retcode = bind(s, ai->ai_addr, ai->ai_addrlen);
+    else
+#if 0
+    {
+       /*
+        * FIXME: Need two listening sockets, in principle, one for v4
+        * and one for v6
+        */
        if (local_host_only)
            a6.sin6_addr = in6addr_loopback;
        else
@@ -592,6 +598,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only)
        a6.sin6_port = htons(port);
     } else
 #endif
+#endif
     {
        int got_addr = 0;
        a.sin_family = AF_INET;
@@ -620,16 +627,9 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only)
        }
 
        a.sin_port = htons((short)port);
+       retcode = bind(s, (struct sockaddr *) &a, sizeof(a));
     }
-#ifdef IPV6
-    retcode = bind(s, (addr->family == AF_INET6 ?
-                      (struct sockaddr *) &a6 :
-                      (struct sockaddr *) &a),
-                  (addr->family ==
-                   AF_INET6 ? sizeof(a6) : sizeof(a)));
-#else
-    retcode = bind(s, (struct sockaddr *) &a, sizeof(a));
-#endif
+
     if (retcode >= 0) {
        err = 0;
     } else {
@@ -648,6 +648,7 @@ Socket sk_newlistener(char *srcaddr, int port, Plug plug, int local_host_only)
        return (Socket) ret;
     }
 
+    uxsel_tell(ret);
     add234(sktree, ret);
 
     return (Socket) ret;
@@ -657,11 +658,41 @@ static void sk_tcp_close(Socket sock)
 {
     Actual_Socket s = (Actual_Socket) sock;
 
+    uxsel_del(s->s);
     del234(sktree, s);
     close(s->s);
     sfree(s);
 }
 
+int sk_getxdmdata(void *sock, unsigned long *ip, int *port)
+{
+    Actual_Socket s = (Actual_Socket) sock;
+    struct sockaddr_in addr;
+    socklen_t addrlen;
+
+    /*
+     * We must check that this socket really _is_ an Actual_Socket.
+     */
+    if (s->fn != &tcp_fn_table)
+       return 0;                      /* failure */
+
+    /*
+     * If we ever implement connecting to a local X server through
+     * a Unix socket, we return 0xFFFFFFFF for the IP address and
+     * our current pid for the port. Bizarre, but such is life.
+     */
+
+    addrlen = sizeof(addr);
+    if (getsockname(s->s, (struct sockaddr *)&addr, &addrlen) < 0 ||
+       addr.sin_family != AF_INET)
+       return 0;
+
+    *ip = ntohl(addr.sin_addr.s_addr);
+    *port = ntohs(addr.sin_port);
+
+    return 1;
+}
+
 /*
  * The function which tries to send on a socket once it's deemed
  * writable.
@@ -725,9 +756,10 @@ void try_send(Actual_Socket s)
            }
        }
     }
+    uxsel_tell(s);
 }
 
-static int sk_tcp_write(Socket sock, char *buf, int len)
+static int sk_tcp_write(Socket sock, const char *buf, int len)
 {
     Actual_Socket s = (Actual_Socket) sock;
 
@@ -742,10 +774,16 @@ static int sk_tcp_write(Socket sock, char *buf, int len)
     if (s->writable)
        try_send(s);
 
+    /*
+     * Update the select() status to correctly reflect whether or
+     * not we should be selecting for write.
+     */
+    uxsel_tell(s);
+
     return bufchain_size(&s->output_data);
 }
 
-static int sk_tcp_write_oob(Socket sock, char *buf, int len)
+static int sk_tcp_write_oob(Socket sock, const char *buf, int len)
 {
     Actual_Socket s = (Actual_Socket) sock;
 
@@ -763,10 +801,16 @@ static int sk_tcp_write_oob(Socket sock, char *buf, int len)
     if (s->writable)
        try_send(s);
 
+    /*
+     * Update the select() status to correctly reflect whether or
+     * not we should be selecting for write.
+     */
+    uxsel_tell(s);
+
     return s->sending_oob;
 }
 
-int select_result(int fd, int event)
+static int net_select_result(int fd, int event)
 {
     int ret;
     int err;
@@ -782,11 +826,6 @@ int select_result(int fd, int event)
     noise_ultralight(event);
 
     switch (event) {
-#ifdef FIXME_NONBLOCKING_CONNECTIONS
-      case FIXME:                     /* connected */
-       s->connected = s->writable = 1;
-       break;
-#endif
       case 4:                         /* exceptional */
        if (!s->oobinline) {
            /*
@@ -798,8 +837,8 @@ int select_result(int fd, int event)
            ret = recv(s->s, buf, sizeof(buf), MSG_OOB);
            noise_ultralight(ret);
            if (ret <= 0) {
-               char *str = (ret == 0 ? "Internal networking trouble" :
-                            error_string(errno));
+               const char *str = (ret == 0 ? "Internal networking trouble" :
+                                  error_string(errno));
                /* We're inside the Unix frontend here, so we know
                 * that the frontend handle is unnecessary. */
                logevent(NULL, str);
@@ -883,7 +922,15 @@ int select_result(int fd, int event)
        }
        break;
       case 2:                         /* writable */
-       {
+       if (!s->connected) {
+           /*
+            * select() reports a socket as _writable_ when an
+            * asynchronous connection is completed.
+            */
+           s->connected = s->writable = 1;
+           uxsel_tell(s);
+           break;
+       } else {
            int bufsize_before, bufsize_after;
            s->writable = 1;
            bufsize_before = s->sending_oob + bufchain_size(&s->output_data);
@@ -956,11 +1003,11 @@ static void *sk_tcp_get_private_ptr(Socket sock)
  * if there's a problem. These functions extract an error message,
  * or return NULL if there's no problem.
  */
-char *sk_addr_error(SockAddr addr)
+const char *sk_addr_error(SockAddr addr)
 {
     return addr->error;
 }
-static char *sk_tcp_socket_error(Socket sock)
+static const char *sk_tcp_socket_error(Socket sock)
 {
     Actual_Socket s = (Actual_Socket) sock;
     return s->error;
@@ -977,42 +1024,21 @@ static void sk_tcp_set_frozen(Socket sock, int is_frozen)
        recv(s->s, &c, 1, MSG_PEEK);
     }
     s->frozen_readable = 0;
+    uxsel_tell(s);
 }
 
-/*
- * For Unix select()-based frontends: enumerate all sockets
- * currently active, and state whether we currently wish to receive
- * select events on them for reading, writing and exceptional
- * status.
- */
-static void set_rwx(Actual_Socket s, int *rwx)
+static void uxsel_tell(Actual_Socket s)
 {
-    int val = 0;
+    int rwx = 0;
+    if (!s->connected)
+       rwx |= 2;                      /* write == connect */
     if (s->connected && !s->frozen)
-       val |= 1 | 4;                  /* read, except */
+       rwx |= 1 | 4;                  /* read, except */
     if (bufchain_size(&s->output_data))
-       val |= 2;                      /* write */
+       rwx |= 2;                      /* write */
     if (s->listener)
-       val |= 1;                      /* read == accept */
-    *rwx = val;
-}
-
-int first_socket(int *state, int *rwx)
-{
-    Actual_Socket s;
-    *state = 0;
-    s = index234(sktree, (*state)++);
-    if (s)
-       set_rwx(s, rwx);
-    return s ? s->s : -1;
-}
-
-int next_socket(int *state, int *rwx)
-{
-    Actual_Socket s = index234(sktree, (*state)++);
-    if (s)
-       set_rwx(s, rwx);
-    return s ? s->s : -1;
+       rwx |= 1;                      /* read == accept */
+    uxsel_set(s->s, rwx, net_select_result);
 }
 
 int net_service_lookup(char *service)