diff --git a/CHANGES b/CHANGES index b61e6e2e41..30cd329ae0 100644 --- 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] diff --git a/lib/isc/win32/socket.c b/lib/isc/win32/socket.c index 2cd69abfdf..14e2e6ffe4 100644 --- a/lib/isc/win32/socket.c +++ b/lib/isc/win32/socket.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: socket.c,v 1.80 2009/09/25 23:48:11 tbox Exp $ */ +/* $Id: socket.c,v 1.81 2009/11/10 18:31:47 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); @@ -2332,7 +2404,7 @@ SocketIoThread(LPVOID ThreadContext) { /* * Did the I/O operation complete? */ - errstatus = WSAGetLastError(); + errstatus = GetLastError(); isc_result = isc__errno2resultx(errstatus, __FILE__, __LINE__); LOCK(&sock->lock); @@ -2343,8 +2415,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);