in server/mpm/winnt/child.c [363:736]
static unsigned int __stdcall winnt_accept(void *lr_)
{
ap_listen_rec *lr = (ap_listen_rec *)lr_;
apr_os_sock_info_t sockinfo;
winnt_conn_ctx_t *context = NULL;
DWORD BytesRead = 0;
SOCKET nlsd;
LPFN_ACCEPTEX lpfnAcceptEx = NULL;
LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockaddrs = NULL;
GUID GuidAcceptEx = WSAID_ACCEPTEX;
GUID GuidGetAcceptExSockaddrs = WSAID_GETACCEPTEXSOCKADDRS;
apr_status_t rv;
accept_filter_e accf;
int err_count = 0;
HANDLE events[3];
#if APR_HAVE_IPV6
SOCKADDR_STORAGE ss_listen;
int namelen = sizeof(ss_listen);
#endif
u_long zero = 0;
apr_os_sock_get(&nlsd, lr->sd);
#if APR_HAVE_IPV6
if (getsockname(nlsd, (struct sockaddr *)&ss_listen, &namelen) == SOCKET_ERROR) {
ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(),
ap_server_conf, APLOGNO(00332)
"winnt_accept: getsockname error on listening socket, "
"is IPv6 available?");
return 1;
}
#endif
accf = get_accept_filter(lr->protocol);
if (accf == ACCEPT_FILTER_CONNECT)
{
if (WSAIoctl(nlsd, SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidAcceptEx, sizeof GuidAcceptEx,
&lpfnAcceptEx, sizeof lpfnAcceptEx,
&BytesRead, NULL, NULL) == SOCKET_ERROR) {
ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(),
ap_server_conf, APLOGNO(02322)
"winnt_accept: failed to retrieve AcceptEx, try 'AcceptFilter none'");
return 1;
}
if (WSAIoctl(nlsd, SIO_GET_EXTENSION_FUNCTION_POINTER,
&GuidGetAcceptExSockaddrs, sizeof GuidGetAcceptExSockaddrs,
&lpfnGetAcceptExSockaddrs, sizeof lpfnGetAcceptExSockaddrs,
&BytesRead, NULL, NULL) == SOCKET_ERROR) {
ap_log_error(APLOG_MARK, APLOG_ERR, apr_get_netos_error(),
ap_server_conf, APLOGNO(02323)
"winnt_accept: failed to retrieve GetAcceptExSockaddrs, try 'AcceptFilter none'");
return 1;
}
/* first, high priority event is an already accepted connection */
events[1] = listener_shutdown_event;
events[2] = max_requests_per_child_event;
}
else /* accf == ACCEPT_FILTER_NONE */
{
reinit: /* target of connect upon too many AcceptEx failures */
/* last, low priority event is a not yet accepted connection */
events[0] = listener_shutdown_event;
events[1] = max_requests_per_child_event;
events[2] = CreateEvent(NULL, FALSE, FALSE, NULL);
/* The event needs to be removed from the accepted socket,
* if not removed from the listen socket prior to accept(),
*/
rv = WSAEventSelect(nlsd, events[2], FD_ACCEPT);
if (rv) {
ap_log_error(APLOG_MARK, APLOG_ERR,
apr_get_netos_error(), ap_server_conf, APLOGNO(00333)
"WSAEventSelect() failed.");
CloseHandle(events[2]);
return 1;
}
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00334)
"Child: Accept thread listening on %pI using AcceptFilter %s",
lr->bind_addr, accept_filter_to_string(accf));
while (1) {
if (!context) {
rv = mpm_get_completion_context(&context);
if (rv) {
/* We have an irrecoverable error, tell the child to die */
SetEvent(exit_event);
break;
}
else if (rv == APR_SUCCESS && !context) {
/* Normal exit */
break;
}
}
if (accf == ACCEPT_FILTER_CONNECT)
{
char *buf;
/* Create and initialize the accept socket */
#if APR_HAVE_IPV6
if (context->accept_socket == INVALID_SOCKET) {
context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM,
IPPROTO_TCP);
context->socket_family = ss_listen.ss_family;
}
else if (context->socket_family != ss_listen.ss_family) {
closesocket(context->accept_socket);
context->accept_socket = socket(ss_listen.ss_family, SOCK_STREAM,
IPPROTO_TCP);
context->socket_family = ss_listen.ss_family;
}
#else
if (context->accept_socket == INVALID_SOCKET)
context->accept_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
#endif
if (context->accept_socket == INVALID_SOCKET) {
ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(),
ap_server_conf, APLOGNO(00336)
"winnt_accept: Failed to allocate an accept socket. "
"Temporary resource constraint? Try again.");
Sleep(100);
continue;
}
buf = context->buff;
/* AcceptEx on the completion context. The completion context will be
* signaled when a connection is accepted.
*/
if (!lpfnAcceptEx(nlsd, context->accept_socket, buf, 0,
PADDED_ADDR_SIZE, PADDED_ADDR_SIZE, &BytesRead,
&context->overlapped)) {
rv = apr_get_netos_error();
if ((rv == APR_FROM_OS_ERROR(WSAECONNRESET)) ||
(rv == APR_FROM_OS_ERROR(WSAEACCES))) {
/* We can get here when:
* 1) the client disconnects early
* 2) handshake was incomplete
*/
closesocket(context->accept_socket);
context->accept_socket = INVALID_SOCKET;
continue;
}
else if ((rv == APR_FROM_OS_ERROR(WSAEINVAL)) ||
(rv == APR_FROM_OS_ERROR(WSAENOTSOCK))) {
/* We can get here when:
* 1) TransmitFile does not properly recycle the accept socket (typically
* because the client disconnected)
* 2) there is VPN or Firewall software installed with
* buggy WSAAccept or WSADuplicateSocket implementation
* 3) the dynamic address / adapter has changed
* Give five chances, then fall back on AcceptFilter 'none'
*/
closesocket(context->accept_socket);
context->accept_socket = INVALID_SOCKET;
++err_count;
if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00337)
"Child: Encountered too many AcceptEx "
"faults accepting client connections. "
"Possible causes: dynamic address renewal, "
"or incompatible VPN or firewall software. ");
ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf, APLOGNO(00338)
"winnt_mpm: falling back to "
"'AcceptFilter none'.");
err_count = 0;
accf = ACCEPT_FILTER_NONE;
}
continue;
}
else if ((rv != APR_FROM_OS_ERROR(ERROR_IO_PENDING)) &&
(rv != APR_FROM_OS_ERROR(WSA_IO_PENDING))) {
closesocket(context->accept_socket);
context->accept_socket = INVALID_SOCKET;
++err_count;
if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00339)
"Child: Encountered too many AcceptEx "
"faults accepting client connections.");
ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, ap_server_conf, APLOGNO(00340)
"winnt_mpm: falling back to "
"'AcceptFilter none'.");
err_count = 0;
accf = ACCEPT_FILTER_NONE;
goto reinit;
}
continue;
}
err_count = 0;
events[0] = context->overlapped.hEvent;
do {
rv = WaitForMultipleObjectsEx(3, events, FALSE, INFINITE, TRUE);
} while (rv == WAIT_IO_COMPLETION);
if (rv == WAIT_OBJECT_0) {
if ((context->accept_socket != INVALID_SOCKET) &&
!GetOverlappedResult((HANDLE)context->accept_socket,
&context->overlapped,
&BytesRead, FALSE)) {
ap_log_error(APLOG_MARK, APLOG_WARNING,
apr_get_os_error(), ap_server_conf, APLOGNO(00341)
"winnt_accept: Asynchronous AcceptEx failed.");
closesocket(context->accept_socket);
context->accept_socket = INVALID_SOCKET;
}
}
else {
/* listener_shutdown_event triggered or event handle was closed */
closesocket(context->accept_socket);
context->accept_socket = INVALID_SOCKET;
break;
}
if (context->accept_socket == INVALID_SOCKET) {
continue;
}
}
err_count = 0;
/* Potential optimization; consider handing off to the worker */
/* Inherit the listen socket settings. Required for
* shutdown() to work
*/
if (setsockopt(context->accept_socket, SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT, (char *)&nlsd,
sizeof(nlsd))) {
ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(),
ap_server_conf, APLOGNO(00342)
"setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed.");
/* Not a failure condition. Keep running. */
}
/* Get the local & remote address
* TODO; error check
*/
lpfnGetAcceptExSockaddrs(buf, 0, PADDED_ADDR_SIZE, PADDED_ADDR_SIZE,
&context->sa_server, &context->sa_server_len,
&context->sa_client, &context->sa_client_len);
}
else /* accf == ACCEPT_FILTER_NONE */
{
/* There is no socket reuse without AcceptEx() */
if (context->accept_socket != INVALID_SOCKET)
closesocket(context->accept_socket);
/* This could be a persistent event per-listener rather than
* per-accept. However, the event needs to be removed from
* the target socket if not removed from the listen socket
* prior to accept(), or the event select is inherited.
* and must be removed from the accepted socket.
*/
do {
rv = WaitForMultipleObjectsEx(3, events, FALSE, INFINITE, TRUE);
} while (rv == WAIT_IO_COMPLETION);
if (rv != WAIT_OBJECT_0 + 2) {
/* not FD_ACCEPT;
* listener_shutdown_event triggered or event handle was closed
*/
break;
}
context->sa_server = (void *) context->buff;
context->sa_server_len = sizeof(context->buff) / 2;
context->sa_client_len = context->sa_server_len;
context->sa_client = (void *) (context->buff
+ context->sa_server_len);
context->accept_socket = accept(nlsd, context->sa_server,
&context->sa_server_len);
if (context->accept_socket == INVALID_SOCKET) {
rv = apr_get_netos_error();
if ( rv == APR_FROM_OS_ERROR(WSAECONNRESET)
|| rv == APR_FROM_OS_ERROR(WSAEINPROGRESS)
|| rv == APR_FROM_OS_ERROR(WSAEWOULDBLOCK) ) {
ap_log_error(APLOG_MARK, APLOG_DEBUG,
rv, ap_server_conf, APLOGNO(00343)
"accept() failed, retrying.");
continue;
}
/* A more serious error than 'retry', log it */
ap_log_error(APLOG_MARK, APLOG_WARNING,
rv, ap_server_conf, APLOGNO(00344)
"accept() failed.");
if ( rv == APR_FROM_OS_ERROR(WSAEMFILE)
|| rv == APR_FROM_OS_ERROR(WSAENOBUFS) ) {
/* Hopefully a temporary condition in the provider? */
Sleep(100);
++err_count;
if (err_count > MAX_ACCEPTEX_ERR_COUNT) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00345)
"Child: Encountered too many accept() "
"resource faults, aborting.");
/* We have an irrecoverable error, tell the child to die */
SetEvent(exit_event);
break;
}
continue;
}
/* We have an irrecoverable error, tell the child to die */
SetEvent(exit_event);
break;
}
/* Per MSDN, cancel the inherited association of this socket
* to the WSAEventSelect API, and restore the state corresponding
* to apr_os_sock_make's default assumptions (really, a flaw within
* os_sock_make and os_sock_put that it does not query).
*/
WSAEventSelect(context->accept_socket, 0, 0);
err_count = 0;
context->sa_server_len = sizeof(context->buff) / 2;
if (getsockname(context->accept_socket, context->sa_server,
&context->sa_server_len) == SOCKET_ERROR) {
ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, APLOGNO(00346)
"getsockname failed");
continue;
}
if ((getpeername(context->accept_socket, context->sa_client,
&context->sa_client_len)) == SOCKET_ERROR) {
ap_log_error(APLOG_MARK, APLOG_WARNING, apr_get_netos_error(), ap_server_conf, APLOGNO(00347)
"getpeername failed");
memset(&context->sa_client, '\0', sizeof(context->sa_client));
}
}
sockinfo.os_sock = &context->accept_socket;
sockinfo.local = context->sa_server;
sockinfo.remote = context->sa_client;
sockinfo.family = context->sa_server->sa_family;
sockinfo.type = SOCK_STREAM;
sockinfo.protocol = IPPROTO_TCP;
/* Restore the state corresponding to apr_os_sock_make's default
* assumption of timeout -1 (really, a flaw of os_sock_make and
* os_sock_put that it does not query to determine ->timeout).
* XXX: Upon a fix to APR, these three statements should disappear.
*/
ioctlsocket(context->accept_socket, FIONBIO, &zero);
setsockopt(context->accept_socket, SOL_SOCKET, SO_RCVTIMEO,
(char *) &zero, sizeof(zero));
setsockopt(context->accept_socket, SOL_SOCKET, SO_SNDTIMEO,
(char *) &zero, sizeof(zero));
apr_os_sock_make(&context->sock, &sockinfo, context->ptrans);
/* When a connection is received, send an io completion notification
* to the ThreadDispatchIOCP.
*/
PostQueuedCompletionStatus(ThreadDispatchIOCP, BytesRead,
IOCP_CONNECTION_ACCEPTED,
&context->overlapped);
context = NULL;
}
if (accf == ACCEPT_FILTER_NONE)
CloseHandle(events[2]);
ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, ap_server_conf, APLOGNO(00348)
"Child: Accept thread exiting.");
return 0;
}