Patches to prevent a couple of silly crashes
[u/mdw/putty] / winnet.c
index 86ae41c..f15b136 100644 (file)
--- a/winnet.c
+++ b/winnet.c
@@ -62,7 +62,8 @@ struct Socket_tag {
     void *private_ptr;
     struct buffer *head, *tail;
     int writable;
-    int in_oob, sending_oob;
+    int sending_oob;
+    int oobinline;
 };
 
 struct SockAddr_tag {
@@ -288,7 +289,8 @@ void sk_addr_free(SockAddr addr) {
     sfree(addr);
 }
 
-Socket sk_new(SockAddr addr, int port, sk_receiver_t receiver) {
+Socket sk_new(SockAddr addr, int port, int privport, int oobinline,
+              sk_receiver_t receiver) {
     SOCKET s;
 #ifdef IPV6
     SOCKADDR_IN6 a6;
@@ -298,6 +300,7 @@ Socket sk_new(SockAddr addr, int port, sk_receiver_t receiver) {
     char *errstr;
     Socket ret;
     extern char *do_select(SOCKET skt, int startup);
+    short localport;
 
     /*
      * Create Socket structure.
@@ -307,7 +310,6 @@ Socket sk_new(SockAddr addr, int port, sk_receiver_t receiver) {
     ret->receiver = receiver;
     ret->head = ret->tail = NULL;
     ret->writable = 1;                /* to start with */
-    ret->in_oob = FALSE;
     ret->sending_oob = 0;
 
     /*
@@ -322,31 +324,65 @@ Socket sk_new(SockAddr addr, int port, sk_receiver_t receiver) {
        return ret;
     }
 
+    ret->oobinline = oobinline;
+    if (oobinline) {
+       BOOL b = TRUE;
+       setsockopt (s, SOL_SOCKET, SO_OOBINLINE, (void *)&b, sizeof(b));
+    }
+
     /*
      * Bind to local address.
      */
-#ifdef IPV6
-    if (addr->family == AF_INET6)
-    {
-       memset(&a6,0,sizeof(a6));
-       a6.sin6_family  = AF_INET6;
-       /*a6.sin6_addr  = in6addr_any;*/                        /* == 0 */
-       a6.sin6_port    = htons(0);
-    }
+    if (privport)
+        localport = 1023;              /* count from 1023 downwards */
     else
-    {
+        localport = 0;                 /* just use port 0 (ie winsock picks) */
+
+    /* Loop round trying to bind */
+    while (1) {
+        int retcode;
+
+#ifdef IPV6
+        if (addr->family == AF_INET6)
+        {
+            memset(&a6,0,sizeof(a6));
+            a6.sin6_family     = AF_INET6;
+            /*a6.sin6_addr     = in6addr_any;*/  /* == 0 */
+            a6.sin6_port       = htons(localport);
+        }
+        else
+        {
 #endif
-       a.sin_family = AF_INET;
-       a.sin_addr.s_addr = htonl(INADDR_ANY);
-       a.sin_port = htons(0);
+            a.sin_family = AF_INET;
+            a.sin_addr.s_addr = htonl(INADDR_ANY);
+            a.sin_port = htons(localport);
 #ifdef IPV6
-    }
-    if (bind (s, (addr->family == AF_INET6) ? (struct sockaddr *)&a6 : (struct sockaddr *)&a, (addr->family == AF_INET6) ? sizeof(a6) : sizeof(a)) == SOCKET_ERROR)
+        }
+        retcode = bind (s, (addr->family == AF_INET6 ?
+                            (struct sockaddr *)&a6 :
+                            (struct sockaddr *)&a),
+                        (addr->family == AF_INET6 ? sizeof(a6) : sizeof(a)));
 #else
-       if (bind (s, (struct sockaddr *)&a, sizeof(a)) == SOCKET_ERROR)
+        retcode = bind (s, (struct sockaddr *)&a, sizeof(a));
 #endif
+        if (retcode != SOCKET_ERROR) {
+            err = 0;
+            break;                     /* done */
+        } else {
+            err = WSAGetLastError();
+            if (err != WSAEADDRINUSE)  /* failed, for a bad reason */
+                break;
+        }
+
+        if (localport == 0)
+            break;                     /* we're only looping once */
+        localport--;
+        if (localport == 0)
+            break;                     /* we might have got to the end */
+    }
+
+    if (err)
     {
-       err = WSAGetLastError();
        ret->error = winsock_error_string(err);
        return ret;
     }
@@ -394,6 +430,8 @@ Socket sk_new(SockAddr addr, int port, sk_receiver_t receiver) {
 }
 
 void sk_close(Socket s) {
+    extern char *do_select(SOCKET skt, int startup);
+
     del234(sktree, s);
     do_select(s->s, 0);
     closesocket(s->s);
@@ -526,7 +564,7 @@ void sk_write_oob(Socket s, char *buf, int len) {
 }
 
 int select_result(WPARAM wParam, LPARAM lParam) {
-    int ret;
+    int ret, open;
     DWORD err;
     char buf[BUFFER_GRANULE];
     Socket s;
@@ -538,14 +576,38 @@ int select_result(WPARAM wParam, LPARAM lParam) {
        return 1;                      /* boggle */
 
     if ((err = WSAGETSELECTERROR(lParam)) != 0) {
-       fatalbox(winsock_error_string(err));
+        /*
+         * An error has occurred on this socket. Pass it to the
+         * receiver function.
+         */
+        return s->receiver(s, 3, winsock_error_string(err), err);
     }
 
     noise_ultralight(lParam);
 
     switch (WSAGETSELECTEVENT(lParam)) {
       case FD_READ:
+        /*
+         * We have received data on the socket. For an oobinline
+         * socket, this might be data _before_ an urgent pointer,
+         * in which case we send it to the back end with type==1
+         * (data prior to urgent).
+         */
+        if (s->oobinline) {
+            atmark = 1;
+            ioctlsocket(s->s, SIOCATMARK, &atmark);
+            /*
+             * Avoid checking the return value from ioctlsocket(),
+             * on the grounds that some WinSock wrappers don't
+             * support it. If it does nothing, we get atmark==1,
+             * which is equivalent to `no OOB pending', so the
+             * effect will be to non-OOB-ify any OOB data.
+             */
+        } else
+            atmark = 1;
+
        ret = recv(s->s, buf, sizeof(buf), 0);
+        noise_ultralight(ret);
        if (ret < 0) {
            err = WSAGetLastError();
            if (err == WSAEWOULDBLOCK) {
@@ -553,32 +615,25 @@ int select_result(WPARAM wParam, LPARAM lParam) {
            }
        }
        if (ret < 0) {
-           fatalbox(winsock_error_string(err));
+           return s->receiver(s, 3, winsock_error_string(err), err);
        } else {
-            int type = s->in_oob ? 2 : 0;
-            s->in_oob = FALSE;
-           return s->receiver(s, type, buf, ret);
+           return s->receiver(s, atmark ? 0 : 1, buf, ret);
        }
        break;
       case FD_OOB:
-       /*
-        * Read all data up to the OOB marker, and send it to the
-        * receiver with urgent==1 (OOB pending).
-        */
-        atmark = 1;
-        s->in_oob = TRUE;
-        /* Some WinSock wrappers don't support this call, so we
-         * deliberately don't check the return value. If the call
-         * fails and does nothing, we will get back atmark==1,
-         * which is good enough to keep going at least. */
-        ioctlsocket(s->s, SIOCATMARK, &atmark);
+        /*
+         * This will only happen on a non-oobinline socket. It
+         * indicates that we can immediately perform an OOB read
+         * and get back OOB data, which we will send to the back
+         * end with type==2 (urgent data).
+         */
         ret = recv(s->s, buf, sizeof(buf), MSG_OOB);
         noise_ultralight(ret);
         if (ret <= 0) {
             fatalbox(ret == 0 ? "Internal networking trouble" :
                      winsock_error_string(WSAGetLastError()));
         } else {
-            return s->receiver(s, atmark ? 2 : 1, buf, ret);
+            return s->receiver(s, 2, buf, ret);
         }
         break;
       case FD_WRITE:
@@ -586,9 +641,19 @@ int select_result(WPARAM wParam, LPARAM lParam) {
        try_send(s);
        break;
       case FD_CLOSE:
-       /* Signal a close on the socket. */
-       return s->receiver(s, 0, NULL, 0);
-       break;
+       /* Signal a close on the socket. First read any outstanding data. */
+        open = 1;
+        do {
+            ret = recv(s->s, buf, sizeof(buf), 0);
+            if (ret < 0) {
+                err = WSAGetLastError();
+                if (err == WSAEWOULDBLOCK)
+                    break;
+                return s->receiver(s, 3, winsock_error_string(err), err);
+            } else
+                open &= s->receiver(s, 0, buf, ret);
+       } while (ret > 0);
+        return open;
     }
 
     return 1;