]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
2758. [bug] win32: Added a workaround for a windows 2008 bug
authorEvan Hunt <each@isc.org>
Tue, 10 Nov 2009 18:31:24 +0000 (18:31 +0000)
committerEvan Hunt <each@isc.org>
Tue, 10 Nov 2009 18:31:24 +0000 (18:31 +0000)
that could cause the UDP client handler to shut
down. [RT #19176]

CHANGES
lib/isc/win32/socket.c

diff --git a/CHANGES b/CHANGES
index 1f31b1e5f57470c7ecb9a0e4a12d304e5210078d..fcce57ef4c6f6978dff879d29c4a5a9a35ad0459 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,7 @@
+2758.  [bug]           win32: Added a workaround for a windows 2008 bug
+                       that could cause the UDP client handler to shut
+                       down. [RT #19176]
+
 2757.  [bug]           dig: assertion failure could occur in connect
                        timeout. [RT #20599]
 
index 7c4068fbdc2bb867edf9e028a69992e46964272e..472cb37cd731e9a3a2ba9d02b4166b69bc344506 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: socket.c,v 1.70.54.4 2009/01/29 22:40:36 jinmei Exp $ */
+/* $Id: socket.c,v 1.70.54.5 2009/11/10 18:31:24 each Exp $ */
 
 /* This code uses functions which are only available on Server 2003 and
  * higher, and Windows XP and higher.
@@ -272,6 +272,7 @@ 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)
@@ -364,6 +365,8 @@ 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
@@ -716,6 +719,7 @@ queue_receive_request(isc_socket_t *sock) {
        int total_bytes = 0;
        int Result;
        int Error;
+       isc_boolean_t need_recovering = ISC_FALSE;
        WSABUF iov[1];
        IoCompletionInfo *lpo;
        isc_result_t isc_result;
@@ -763,9 +767,39 @@ queue_receive_request(isc_socket_t *sock) {
                        sock->pending_recv++;
                        break;
 
+               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;
+                               break;
+                       } else
+                               goto fail;
+
                default:
+               fail:
                        isc_result = isc__errno2result(Error);
-                       if (isc_result == ISC_R_UNEXPECTED)
+                       if ((isc_result == ISC_R_UNEXPECTED) ||
+                           (isc_result == ISC_R_CONNECTIONRESET) ||
+                           (isc_result == ISC_R_HOSTUNREACH))
                                UNEXPECTED_ERROR(__FILE__, __LINE__,
                                        "WSARecvFrom: Windows error code: %d, isc result %d",
                                        Error, isc_result);
@@ -780,6 +814,7 @@ queue_receive_request(isc_socket_t *sock) {
                 */
                sock->pending_iocp++;
                sock->pending_recv++;
+               sock->in_recovery_cnt = 0;
        }
 
        socket_log(__LINE__, sock, NULL, IOEVENT,
@@ -789,6 +824,41 @@ queue_receive_request(isc_socket_t *sock) {
                   sock->fd, Result, Error);
 
        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);
 }
 
 static void
@@ -866,7 +936,7 @@ make_nonblock(SOCKET fd) {
 }
 
 /*
- * Windows 2000 systems incorrectly cause UDP sockets using WASRecvFrom
+ * Windows 2000 systems incorrectly cause UDP sockets using WSARecvFrom
  * to not work correctly, returning a WSACONNRESET error when a WSASendTo
  * fails with an "ICMP port unreachable" response and preventing the
  * socket from using the WSARecvFrom in subsequent operations.
@@ -1318,7 +1388,7 @@ completeio_send(isc_socket_t *sock, isc_socketevent_t *dev,
                UNEXPECTED_ERROR(__FILE__, __LINE__, "completeio_send: %s: %s",
                                 addrbuf, strbuf);
                dev->result = isc__errno2result(send_errno);
-       return (DOIO_HARD);
+               return (DOIO_HARD);
        }
 
        /*
@@ -1387,6 +1457,7 @@ startio_send(isc_socket_t *sock, isc_socketevent_t *dev, int *nbytes,
                                   "bytes, err %d/%s",
                                   sock->fd, *nbytes, *send_errno, strbuf);
                }
+               status = DOIO_HARD;
                goto done;
        }
        dev->result = ISC_R_SUCCESS;
@@ -1431,6 +1502,7 @@ 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);
 
@@ -2331,7 +2403,7 @@ SocketIoThread(LPVOID ThreadContext) {
                        /*
                         * Did the I/O operation complete?
                         */
-                       errstatus = WSAGetLastError();
+                       errstatus = GetLastError();
                        isc_result = isc__errno2resultx(errstatus, __FILE__, __LINE__);
 
                        LOCK(&sock->lock);
@@ -2342,8 +2414,32 @@ 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);
+                                       break;
+                               }
                                send_recvdone_abort(sock, isc_result);
-                               if (isc_result == ISC_R_UNEXPECTED) {
+                               if ((isc_result == ISC_R_UNEXPECTED) ||
+                                   ((isc_result == ISC_R_CONNECTIONRESET) &&
+                                    (errstatus != ERROR_OPERATION_ABORTED)) ||
+                                   (isc_result == ISC_R_HOSTUNREACH)) {
                                        UNEXPECTED_ERROR(__FILE__, __LINE__,
                                                "SOCKET_RECV: Windows error code: %d, returning ISC error %d",
                                                errstatus, isc_result);