]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
2972. [bug] win32: address windows socket errors. [RT #21906]
authorMark Andrews <marka@isc.org>
Thu, 18 Nov 2010 00:40:44 +0000 (00:40 +0000)
committerMark Andrews <marka@isc.org>
Thu, 18 Nov 2010 00:40:44 +0000 (00:40 +0000)
CHANGES
lib/isc/win32/socket.c

diff --git a/CHANGES b/CHANGES
index 82a57c1b8ab4e5899e66d7f8e821adf2b1c87060..fa2e5fcf1ea55804ebdfe0d2780f7de35a6892e1 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,5 @@
+2972.  [bug]           win32: address windows socket errors. [RT #21906]
+
 2971.  [bug]           Fixed a bug that caused journal files not to be
                        compacted on Windows systems as a result of
                        non-POSIX-compliant rename() semantics. [RT #22434]
index 472cb37cd731e9a3a2ba9d02b4166b69bc344506..fe279fb56d94146c835534e181347ecde535d142 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: socket.c,v 1.70.54.5 2009/11/10 18:31:24 each Exp $ */
+/* $Id: socket.c,v 1.70.54.5.10.1 2010/11/18 00:40:44 marka Exp $ */
 
 /* This code uses functions which are only available on Server 2003 and
  * higher, and Windows XP and higher.
@@ -272,7 +272,6 @@ struct isc_socket {
        unsigned int            pending_accept; /* Number of outstanding accept() calls. */
        unsigned int            state; /* Socket state. Debugging and consistency checking. */
        int                     state_lineno;  /* line which last touched state */
-       int                     in_recovery_cnt; /* avoid recovery loop. */
 };
 
 #define _set_state(sock, _state) do { (sock)->state = (_state); (sock)->state_lineno = __LINE__; } while (0)
@@ -365,8 +364,6 @@ static void send_connectdone_event(isc_socket_t *sock, isc_socket_connev_t **cde
 static void send_recvdone_abort(isc_socket_t *sock, isc_result_t result);
 static void queue_receive_event(isc_socket_t *sock, isc_task_t *task, isc_socketevent_t *dev);
 static void queue_receive_request(isc_socket_t *sock);
-static void hard_recover_receive_request(isc_socket_t *sock);
-static void recover_receive_request(isc_socket_t *sock, void **lplpo);
 
 /*
  * This is used to dump the contents of the sock structure
@@ -719,22 +716,31 @@ queue_receive_request(isc_socket_t *sock) {
        int total_bytes = 0;
        int Result;
        int Error;
-       isc_boolean_t need_recovering = ISC_FALSE;
+       int need_retry;
        WSABUF iov[1];
-       IoCompletionInfo *lpo;
+       IoCompletionInfo *lpo = NULL;
        isc_result_t isc_result;
 
+ retry:
+       need_retry = ISC_FALSE;
+
        /*
         * If we already have a receive pending, do nothing.
         */
-       if (sock->pending_recv > 0)
+       if (sock->pending_recv > 0) {
+               if (lpo != NULL)
+                       HeapFree(hHeapHandle, 0, lpo);
                return;
+       }
 
        /*
         * If no one is waiting, do nothing.
         */
-       if (ISC_LIST_EMPTY(sock->recv_list))
+       if (ISC_LIST_EMPTY(sock->recv_list)) {
+               if (lpo != NULL)
+                       HeapFree(hHeapHandle, 0, lpo);
                return;
+       }
 
        INSIST(sock->recvbuf.remaining == 0);
        INSIST(sock->fd != INVALID_SOCKET);
@@ -742,10 +748,13 @@ queue_receive_request(isc_socket_t *sock) {
        iov[0].len = sock->recvbuf.len;
        iov[0].buf = sock->recvbuf.base;
 
-       lpo = (IoCompletionInfo *)HeapAlloc(hHeapHandle,
-                                           HEAP_ZERO_MEMORY,
-                                           sizeof(IoCompletionInfo));
-       RUNTIME_CHECK(lpo != NULL);
+       if (lpo == NULL) {
+               lpo = (IoCompletionInfo *)HeapAlloc(hHeapHandle,
+                                                   HEAP_ZERO_MEMORY,
+                                                   sizeof(IoCompletionInfo));
+               RUNTIME_CHECK(lpo != NULL);
+       } else
+               ZeroMemory(lpo, sizeof(IoCompletionInfo));
        lpo->request_type = SOCKET_RECV;
 
        sock->recvbuf.from_addr_len = sizeof(sock->recvbuf.from_addr);
@@ -767,43 +776,26 @@ queue_receive_request(isc_socket_t *sock) {
                        sock->pending_recv++;
                        break;
 
+               /* direct error: no completion event */
                case ERROR_HOST_UNREACHABLE:
-                       if (sock->type == isc_sockettype_udp) {
-                               UNEXPECTED_ERROR(__FILE__, __LINE__,
-                                        "WSARecvFrom ERROR_HOST_UNREACHABLE: trying to recover");
-                               need_recovering = ISC_TRUE;
-                               break;
-                       } else
-                               goto fail;
-
                case WSAENETRESET:
-                       if (sock->type == isc_sockettype_udp) {
-                               UNEXPECTED_ERROR(__FILE__, __LINE__,
-                                        "WSARecvFrom WSAENETRESET: trying to recover");
-                               need_recovering = ISC_TRUE;
-                               break;
-                       } else
-                               goto fail;
-
                case WSAECONNRESET:
-                       if (sock->type == isc_sockettype_udp) {
-                               UNEXPECTED_ERROR(__FILE__, __LINE__,
-                                        "WSARecvFrom WSAECONNRESET: trying to recover");
-                               need_recovering = ISC_TRUE;
+                       if (!sock->connected) {
+                               /* soft error */
+                               need_retry = ISC_TRUE;
                                break;
-                       } else
-                               goto fail;
+                       }
+                       /* FALLTHROUGH */
 
                default:
-               fail:
                        isc_result = isc__errno2result(Error);
-                       if ((isc_result == ISC_R_UNEXPECTED) ||
-                           (isc_result == ISC_R_CONNECTIONRESET) ||
-                           (isc_result == ISC_R_HOSTUNREACH))
+                       if (isc_result == ISC_R_UNEXPECTED)
                                UNEXPECTED_ERROR(__FILE__, __LINE__,
                                        "WSARecvFrom: Windows error code: %d, isc result %d",
                                        Error, isc_result);
                        send_recvdone_abort(sock, isc_result);
+                       HeapFree(hHeapHandle, 0, lpo);
+                       lpo = NULL;
                        break;
                }
        } else {
@@ -814,7 +806,6 @@ queue_receive_request(isc_socket_t *sock) {
                 */
                sock->pending_iocp++;
                sock->pending_recv++;
-               sock->in_recovery_cnt = 0;
        }
 
        socket_log(__LINE__, sock, NULL, IOEVENT,
@@ -825,40 +816,8 @@ queue_receive_request(isc_socket_t *sock) {
 
        CONSISTENT(sock);
 
-       if (need_recovering)
-               recover_receive_request(sock, &lpo);
-}
-
-/*
- * (placeholder) Hard recovery, doing nothing useful today
- * (other than to avoid unlimited recursion).
- */
-static void
-hard_recover_receive_request(isc_socket_t *sock)
-{
-       UNEXPECTED_ERROR(__FILE__, __LINE__,
-                        "can't recover fd %d sock %p",
-                        sock->fd, sock);
-       send_recvdone_abort(sock, ISC_R_UNEXPECTED);
-}
-
-/*
- * Recovery from a Windows 2008 Server bug
- * (WSARecvFrom() getting an ERROR_HOST_UNREACHABLE).
- * Free the overlapped pointer and requeue a receive request.
- */
-static void
-recover_receive_request(isc_socket_t *sock, void **lplpo)
-{
-       if (*lplpo != NULL)
-               HeapFree(hHeapHandle, 0, *lplpo);
-       *lplpo = NULL;
-
-       /* limit recursion to 20 */
-       if (sock->in_recovery_cnt++ < 20)
-               queue_receive_request(sock);
-       else
-               hard_recover_receive_request(sock);
+       if (need_retry)
+               goto retry;
 }
 
 static void
@@ -1502,7 +1461,6 @@ allocate_socket(isc_socketmgr_t *manager, isc_sockettype_t type,
        sock->connected = 0;
        sock->pending_connect = 0;
        sock->bound = 0;
-       sock->in_recovery_cnt = 0;
        memset(sock->name, 0, sizeof(sock->name));      // zero the name field
        _set_state(sock, SOCK_INITIALIZED);
 
@@ -2345,6 +2303,63 @@ connectdone_is_active(isc_socket_t *sock, isc_socket_connev_t *dev)
        return (sock->connect_ev == dev ? ISC_TRUE : ISC_FALSE);
 }
 
+//
+// The Windows network stack seems to have two very distinct paths depending
+// on what is installed.  Specifically, if something is looking at network
+// connections (like an anti-virus or anti-malware application, such as
+// McAfee products) Windows may return additional error conditions which
+// were not previously returned.
+//
+// One specific one is when a TCP SYN scan is used.  In this situation,
+// Windows responds with the SYN-ACK, but the scanner never responds with
+// the 3rd packet, the ACK.  Windows consiers this a partially open connection.
+// Most Unix networking stacks, and Windows without McAfee installed, will
+// not return this to the caller.  However, with this product installed,
+// Windows returns this as a failed status on the Accept() call.  Here, we
+// will just re-issue the ISCAcceptEx() call as if nothing had happened.
+//
+// This code should only be called when the listening socket has received
+// such an error.  Additionally, the "parent" socket must be locked.
+// Additionally, the lpo argument is re-used here, and must not be freed
+// by the caller.
+//
+static isc_result_t
+restart_accept(isc_socket_t *parent, IoCompletionInfo *lpo)
+{
+       isc_socket_t *nsock = lpo->adev->newsocket;
+       SOCKET new_fd;
+               
+       /*
+        * AcceptEx() requires we pass in a socket.  Note that we carefully
+        * do not close the previous socket in case of an error message returned by
+        * our new socket() call.  If we return an error here, our caller will
+        * clean up.
+        */
+       new_fd = socket(parent->pf, SOCK_STREAM, IPPROTO_TCP);
+       if (nsock->fd == INVALID_SOCKET) {
+               return (ISC_R_FAILURE); // parent will ask windows for error message
+       }
+       closesocket(nsock->fd);
+       nsock->fd = new_fd;
+
+       memset(&lpo->overlapped, 0, sizeof(lpo->overlapped));
+               
+       ISCAcceptEx(parent->fd,
+                   nsock->fd,                          /* Accepted Socket */
+                   lpo->acceptbuffer,                  /* Buffer for initial Recv */
+                   0,                                  /* Length of Buffer */
+                   sizeof(SOCKADDR_STORAGE) + 16,      /* Local address length + 16 */
+                   sizeof(SOCKADDR_STORAGE) + 16,      /* Remote address lengh + 16 */
+                   (LPDWORD)&lpo->received_bytes,      /* Bytes Recved */
+                   (LPOVERLAPPED)lpo                   /* Overlapped structure */
+                   );
+
+       InterlockedDecrement(&nsock->manager->iocp_total);
+       iocompletionport_update(nsock);
+       
+       return (ISC_R_SUCCESS);
+}
+
 /*
  * This is the I/O Completion Port Worker Function. It loops forever
  * waiting for I/O to complete and then forwards them for further
@@ -2385,6 +2400,7 @@ SocketIoThread(LPVOID ThreadContext) {
         * Loop forever waiting on I/O Completions and then processing them
         */
        while (TRUE) {
+               wait_again:
                bSuccess = GetQueuedCompletionStatus(manager->hIoCompletionPort,
                                                     &nbytes, (LPDWORD)&sock,
                                                     (LPWSAOVERLAPPED *)&lpo,
@@ -2414,32 +2430,16 @@ SocketIoThread(LPVOID ThreadContext) {
                                sock->pending_iocp--;
                                INSIST(sock->pending_recv > 0);
                                sock->pending_recv--;
-                               if ((sock->type == isc_sockettype_udp) &&
-                                   (errstatus == ERROR_HOST_UNREACHABLE)) {
-                                       UNEXPECTED_ERROR(__FILE__, __LINE__,
-                                                        "SOCKET_RECV ERROR_HOST_UNREACHABLE: trying to recover");
-                                       recover_receive_request(sock, &lpo);
-                                       break;
-                               }
-                               if ((sock->type == isc_sockettype_udp) &&
-                                   (errstatus == WSAENETRESET)) {
-                                       UNEXPECTED_ERROR(__FILE__, __LINE__,
-                                                        "SOCKET_RECV WSAENETRESET: trying to recover");
-                                       recover_receive_request(sock, &lpo);
-                                       break;
-                               }
-                               if ((sock->type == isc_sockettype_udp) &&
-                                   (errstatus == WSAECONNRESET)) {
-                                       UNEXPECTED_ERROR(__FILE__, __LINE__,
-                                                        "SOCKET_RECV WSAECONNRESET: trying to recover");
-                                       recover_receive_request(sock, &lpo);
+                               if (!sock->connected &&
+                                   ((errstatus == ERROR_HOST_UNREACHABLE) ||
+                                    (errstatus == WSAENETRESET) ||
+                                    (errstatus == WSAECONNRESET))) {
+                                       /* ignore soft errors */
+                                       queue_receive_request(sock);
                                        break;
                                }
                                send_recvdone_abort(sock, isc_result);
-                               if ((isc_result == ISC_R_UNEXPECTED) ||
-                                   ((isc_result == ISC_R_CONNECTIONRESET) &&
-                                    (errstatus != ERROR_OPERATION_ABORTED)) ||
-                                   (isc_result == ISC_R_HOSTUNREACH)) {
+                               if (isc_result == ISC_R_UNEXPECTED) {
                                        UNEXPECTED_ERROR(__FILE__, __LINE__,
                                                "SOCKET_RECV: Windows error code: %d, returning ISC error %d",
                                                errstatus, isc_result);
@@ -2461,8 +2461,25 @@ SocketIoThread(LPVOID ThreadContext) {
 
                        case SOCKET_ACCEPT:
                                INSIST(sock->pending_iocp > 0);
-                               sock->pending_iocp--;
                                INSIST(sock->pending_accept > 0);
+
+                               socket_log(__LINE__, sock, NULL, EVENT, NULL, 0, 0,
+                                       "Accept: errstatus=%d isc_result=%d", errstatus, isc_result);
+
+                               if (acceptdone_is_active(sock, lpo->adev)) {
+                                       if (restart_accept(sock, lpo) == ISC_R_SUCCESS) {
+                                               UNLOCK(&sock->lock);
+                                               goto wait_again;
+                                       } else {
+                                               errstatus = GetLastError();
+                                               isc_result = isc__errno2resultx(errstatus, __FILE__, __LINE__);
+                                               socket_log(__LINE__, sock, NULL, EVENT, NULL, 0, 0,
+                                                       "restart_accept() failed: errstatus=%d isc_result=%d",
+                                                       errstatus, isc_result);
+                                       }
+                               }
+
+                               sock->pending_iocp--;
                                sock->pending_accept--;
                                if (acceptdone_is_active(sock, lpo->adev)) {
                                        closesocket(lpo->adev->newsocket->fd);