Robert de Bath's asynchronous-connect patch. Helps a lot in port
[u/mdw/putty] / winnet.c
index 40f416d..c5e9eb7 100644 (file)
--- a/winnet.c
+++ b/winnet.c
@@ -63,6 +63,7 @@ struct Socket_tag {
     Plug plug;
     void *private_ptr;
     bufchain output_data;
+    int connected;
     int writable;
     int frozen; /* this causes readability notifications to be ignored */
     int frozen_readable; /* this means we missed at least one readability
@@ -338,6 +339,21 @@ SockAddr sk_namelookup(char *host, char **canonicalname)
     return ret;
 }
 
+void sk_getaddr(SockAddr addr, char *buf, int buflen)
+{
+#ifdef IPV6
+    if (addr->family == AF_INET) {
+#endif
+       struct in_addr a;
+       a.s_addr = htonl(addr->address);
+       strncpy(buf, inet_ntoa(a), buflen);
+#ifdef IPV6
+    } else {
+       FIXME; /* I don't know how to get a text form of an IPv6 address. */
+    }
+#endif
+}
+
 void sk_addr_free(SockAddr addr)
 {
     sfree(addr);
@@ -448,7 +464,8 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
     ret->error = NULL;
     ret->plug = plug;
     bufchain_init(&ret->output_data);
-    ret->writable = 1;                /* to start with */
+    ret->connected = 0;                       /* to start with */
+    ret->writable = 0;                /* to start with */
     ret->sending_oob = 0;
     ret->frozen = 0;
     ret->frozen_readable = 0;
@@ -543,6 +560,15 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
        a.sin_addr.s_addr = htonl(addr->address);
        a.sin_port = htons((short) port);
     }
+
+    /* Set up a select mechanism. This could be an AsyncSelect on a
+     * window, or an EventSelect on an event object. */
+    errstr = do_select(s, 1);
+    if (errstr) {
+       ret->error = errstr;
+       return (Socket) ret;
+    }
+
     if ((
 #ifdef IPV6
            connect(s, ((addr->family == AF_INET6) ?
@@ -553,16 +579,22 @@ Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
 #endif
        ) == SOCKET_ERROR) {
        err = WSAGetLastError();
-       ret->error = winsock_error_string(err);
-       return (Socket) ret;
-    }
-
-    /* Set up a select mechanism. This could be an AsyncSelect on a
-     * window, or an EventSelect on an event object. */
-    errstr = do_select(s, 1);
-    if (errstr) {
-       ret->error = errstr;
-       return (Socket) ret;
+       /*
+        * We expect a potential EWOULDBLOCK here, because the
+        * chances are the front end has done a select for
+        * FD_CONNECT, so that connect() will complete
+        * asynchronously.
+        */
+       if ( err != WSAEWOULDBLOCK ) {
+           ret->error = winsock_error_string(err);
+           return (Socket) ret;
+       }
+    } else {
+       /*
+        * If we _don't_ get EWOULDBLOCK, the connect has completed
+        * and we should set the socket as writable.
+        */
+       ret->writable = 1;
     }
 
     add234(sktree, ret);
@@ -718,14 +750,15 @@ void try_send(Actual_Socket s)
        noise_ultralight(nsent);
        if (nsent <= 0) {
            err = (nsent < 0 ? WSAGetLastError() : 0);
-           if ((err == 0 && nsent < 0) || err == WSAEWOULDBLOCK) {
+           if ((err < WSABASEERR && nsent < 0) || err == WSAEWOULDBLOCK) {
                /*
                 * Perfectly normal: we've sent all we can for the moment.
                 * 
-                * (Apparently some WinSocks can return <0 but
-                * leave no error indication - WSAGetLastError() is
-                * called but returns zero - so we check that case
-                * and treat it just like WSAEWOULDBLOCK.)
+                * (Some WinSock send() implementations can return
+                * <0 but leave no sensible error indication -
+                * WSAGetLastError() is called but returns zero or
+                * a small number - so we check that case and treat
+                * it just like WSAEWOULDBLOCK.)
                 */
                s->writable = FALSE;
                return;
@@ -824,6 +857,9 @@ int select_result(WPARAM wParam, LPARAM lParam)
     noise_ultralight(lParam);
 
     switch (WSAGETSELECTEVENT(lParam)) {
+      case FD_CONNECT:
+       s->connected = s->writable = 1;
+       break;
       case FD_READ:
        /* In the case the socket is still frozen, we don't even bother */
        if (s->frozen) {