/*++ Copyright (c) 1992 Microsoft Corporation Module Name: eventsel.c Abstract: This module contains support for the WSAEventSelect() and WSAEnumNetworkEvents() WinSock APIs. Author: Keith Moore (keithmo) 5-Aug-1995 Revision History: --*/ #include "winsockp.h" // // Data to make the mapping of poll events to FD_* events simpler // in WSAEnumNetworkEvents(). Note that FD_CONNECT and FD_CLOSE // are not in this list. They must be handled specially. // typedef struct _POLL_MAPPING { ULONG PollEventBit; LONG NetworkEventBit; } POLL_MAPPING, *PPOLL_MAPPING; POLL_MAPPING PollEventMapping[] = { { AFD_POLL_RECEIVE_BIT, FD_READ_BIT }, { AFD_POLL_SEND_BIT, FD_WRITE_BIT }, { AFD_POLL_RECEIVE_EXPEDITED_BIT, FD_OOB_BIT }, { AFD_POLL_ACCEPT_BIT, FD_ACCEPT_BIT }, { AFD_POLL_QOS_BIT, FD_QOS_BIT }, { AFD_POLL_GROUP_QOS_BIT, FD_GROUP_QOS_BIT } }; #define NUM_POLL_MAPPINGS (sizeof(PollEventMapping) / sizeof(PollEventMapping[0])) int WSPAPI WSPEventSelect ( SOCKET Handle, WSAEVENT hEventObject, long lNetworkEvents, LPINT lpErrno ) /*++ Routine Description: Does that event select thang. CKMBUGBUG Arguments: Handle - A descriptor identifying the socket for which event notification is required. hEventObject - A handle identifying the event object which should be signalled when a network event occurs. lEvent - A bitmask which specifies a combination of network events in which the application is interested. Return Value: The return value is 0 if the application's declaration of interest in the network event set was successful. Otherwise the value SOCKET_ERROR is returned, and a specific error number may be retrieved by calling WSAGetLastError(). --*/ { PSOCKET_INFORMATION socket; int err; BOOLEAN blocking; WS_ENTER( "WSAEventSelect", (PVOID)Handle, (PVOID)hEventObject, (PVOID)lNetworkEvents, NULL ); WS_ASSERT( lpErrno != NULL ); err = SockEnterApi( TRUE, TRUE, FALSE ); if( err != NO_ERROR ) { WS_EXIT( "WSPEventSelect", SOCKET_ERROR, TRUE ); *lpErrno = err; return SOCKET_ERROR; } // // Initialize locals so that we know how to clean up on exit. // socket = NULL; // // Find a pointer to the socket structure corresponding to the // passed-in handle. // socket = SockFindAndReferenceSocket( Handle, TRUE ); if ( socket == NULL ) { err = WSAENOTSOCK; goto exit; } // // Set the socket to nonblocking. // blocking = TRUE; err = SockSetInformation( socket, AFD_NONBLOCKING_MODE, &blocking, NULL, NULL ); if ( err != NO_ERROR ) { goto exit; } socket->NonBlocking = TRUE; // // If there's a WSAAsyncSelect active on this socket, deactivate it. // if( socket->AsyncSelectlEvent ) { err = SockAsyncSelectHelper( socket, NULL, 0, 0 ); if( err != NO_ERROR ) { goto exit; } } // // Make sure that only valid bits are specified in lEvent. // // !!! should we also make sure that the bits make sense for the // state of the socket, i.e. don't allow FD_ACCEPT on a // connected socket? // if ( (lNetworkEvents & ~FD_ALL_EVENTS) != 0 ) { err = WSAEINVAL; goto exit; } // // Let the helper do the dirty work. // err = SockEventSelectHelper( socket, hEventObject, lNetworkEvents ); exit: if ( socket != NULL ) { SockDereferenceSocket( socket ); } if ( err != NO_ERROR) { IF_DEBUG(EVENT_SELECT) { WS_PRINT(( "WSAEventSelect failed: %ld\n", err )); } WS_EXIT( "WSAEventSelect", SOCKET_ERROR, TRUE ); *lpErrno = err; return SOCKET_ERROR; } IF_DEBUG(EVENT_SELECT) { WS_PRINT(( "WSAEventSelect successfully posted request, " "socket = %lx\n", socket )); } WS_EXIT( "WSAEventSelect", NO_ERROR, FALSE ); return NO_ERROR; } // WSPEventSelect int SockEventSelectHelper( PSOCKET_INFORMATION Socket, WSAEVENT hEventObject, long lNetworkEvents ) /*++ Routine Description: x Arguments: x Return Value: x --*/ { AFD_EVENT_SELECT_INFO eventInfo; IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; // // Acquire the lock that protects this socket. We hold this lock // throughout this routine to synchronize against other callers // performing operations on the socket we're using. // SockAcquireSocketLockExclusive( Socket ); // // Initialize the AFD_EVENT_SELECT_INFO structure. // eventInfo.Event = hEventObject; eventInfo.PollEvents = 0; if( lNetworkEvents & FD_READ ) { eventInfo.PollEvents |= AFD_POLL_RECEIVE; } if( lNetworkEvents & FD_WRITE ) { eventInfo.PollEvents |= AFD_POLL_SEND; } if( lNetworkEvents & FD_OOB ) { eventInfo.PollEvents |= AFD_POLL_RECEIVE_EXPEDITED; } if( lNetworkEvents & FD_ACCEPT ) { eventInfo.PollEvents |= AFD_POLL_ACCEPT; } if( lNetworkEvents & FD_CONNECT ) { eventInfo.PollEvents |= AFD_POLL_CONNECT | AFD_POLL_CONNECT_FAIL; } if( lNetworkEvents & FD_CLOSE ) { eventInfo.PollEvents |= AFD_POLL_DISCONNECT | AFD_POLL_ABORT; } if( lNetworkEvents & FD_QOS ) { eventInfo.PollEvents |= AFD_POLL_QOS; } if( lNetworkEvents & FD_GROUP_QOS ) { eventInfo.PollEvents |= AFD_POLL_GROUP_QOS; } // // Send the IOCTL to AFD. AFD will reference the event object and // store the poll events in its internal endpoint structure. // status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, SockThreadEvent, NULL, // APC Routine NULL, // APC Context &ioStatusBlock, IOCTL_AFD_EVENT_SELECT, &eventInfo, sizeof(eventInfo), NULL, 0 ); if( status == STATUS_PENDING ) { SockWaitForSingleObject( SockThreadEvent, Socket->Handle, SOCK_NEVER_CALL_BLOCKING_HOOK, SOCK_NO_TIMEOUT ); status = ioStatusBlock.Status; } if( !NT_SUCCESS(status) ) { SockReleaseSocketLock( Socket ); return SockNtStatusToSocketError( status ); } // // Store the event object handle and network event mask in the socket. // Socket->EventSelectEventObject = hEventObject; Socket->EventSelectlNetworkEvents = lNetworkEvents; // // Release the socket lock & return. // SockReleaseSocketLock( Socket ); return NO_ERROR; } // SockEventSelectHelper int WSPAPI WSPEnumNetworkEvents ( SOCKET Handle, WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents, LPINT lpErrno ) /*++ Routine Description: Does that enum network events thang. CKMBUGBUG Arguments: Handle - A descriptor identifying the socket. hEventObject - An optional handle identifying an associated event object to be reset. lpNetworkEvents - Points to a WSANETWORKEVENTS structure which records an occurred network event and the associated error code. Return Value: The return value is 0 if the application's declaration of interest in the network event set was successful. Otherwise the value SOCKET_ERROR is returned, and a specific error number may be retrieved by calling WSAGetLastError(). --*/ { PSOCKET_INFORMATION socket; INT err; BOOLEAN blocking; AFD_ENUM_NETWORK_EVENTS_INFO eventInfo; IO_STATUS_BLOCK ioStatusBlock; NTSTATUS status; NTSTATUS eventStatus; INT i; PPOLL_MAPPING pollMapping; WS_ENTER( "WSAEnumNetworkEvents", (PVOID)Handle, (PVOID)hEventObject, (PVOID)lpNetworkEvents, NULL ); WS_ASSERT( lpErrno != NULL ); err = SockEnterApi( TRUE, TRUE, FALSE ); if( err != NO_ERROR ) { WS_EXIT( "WSPEnumNetworkEvents", SOCKET_ERROR, TRUE ); *lpErrno = err; return SOCKET_ERROR; } // // Initialize locals so that we know how to clean up on exit. // socket = NULL; // // Validate the parameters. // if( lpNetworkEvents == NULL ) { err = WSAEINVAL; goto exit; } // // Zero the WSANETWORKEVENTS structure. // RtlZeroMemory( lpNetworkEvents, sizeof(*lpNetworkEvents) ); // // Find a pointer to the socket structure corresponding to the // passed-in handle. // socket = SockFindAndReferenceSocket( Handle, TRUE ); if ( socket == NULL ) { err = WSAENOTSOCK; goto exit; } // // Acquire the lock that protects this socket. We hold this lock // throughout this routine to synchronize against other callers // performing operations on the socket we're using. // SockAcquireSocketLockExclusive( socket ); // // Initialize the AFD_ENUM_NETWORK_EVENTS_INFO structure. // eventInfo.Event = hEventObject; // // Send the IOCTL to AFD. AFD will update the structure with // information on network events that have occurred. AFD will // then reset the event object (if specified). // status = NtDeviceIoControlFile( (HANDLE)Handle, SockThreadEvent, NULL, // APC Routine NULL, // APC Context &ioStatusBlock, IOCTL_AFD_ENUM_NETWORK_EVENTS, &eventInfo, sizeof(eventInfo), &eventInfo, sizeof(eventInfo) ); if( status == STATUS_PENDING ) { SockWaitForSingleObject( SockThreadEvent, socket->Handle, SOCK_NEVER_CALL_BLOCKING_HOOK, SOCK_NO_TIMEOUT ); status = ioStatusBlock.Status; } if( !NT_SUCCESS(status) ) { err = SockNtStatusToSocketError( status ); goto exit; } // // Interpret the results. // pollMapping = PollEventMapping; for( i = 0 ; i < NUM_POLL_MAPPINGS ; i++ ) { if( eventInfo.PollEvents & ( 1 << pollMapping->PollEventBit ) ) { lpNetworkEvents->lNetworkEvents |= ( 1 << pollMapping->NetworkEventBit ); eventStatus = eventInfo.EventStatus[pollMapping->PollEventBit]; if( !NT_SUCCESS(eventStatus) ) { lpNetworkEvents->iErrorCode[pollMapping->NetworkEventBit] = SockNtStatusToSocketError( eventStatus ); } } pollMapping++; } if( eventInfo.PollEvents & AFD_POLL_CONNECT ) { lpNetworkEvents->lNetworkEvents |= FD_CONNECT; eventStatus = eventInfo.EventStatus[AFD_POLL_CONNECT_BIT]; if( !NT_SUCCESS(eventStatus ) ) { lpNetworkEvents->iErrorCode[FD_CONNECT_BIT] = SockNtStatusToSocketError( eventStatus ); } } else if (eventInfo.PollEvents & AFD_POLL_CONNECT_FAIL ) { lpNetworkEvents->lNetworkEvents |= FD_CONNECT; eventStatus = eventInfo.EventStatus[AFD_POLL_CONNECT_FAIL_BIT]; if( !NT_SUCCESS(eventStatus ) ) { lpNetworkEvents->iErrorCode[FD_CONNECT_BIT] = SockNtStatusToSocketError( eventStatus ); } } if( eventInfo.PollEvents & AFD_POLL_ABORT ) { lpNetworkEvents->lNetworkEvents |= FD_CLOSE; eventStatus = eventInfo.EventStatus[AFD_POLL_ABORT_BIT]; if( !NT_SUCCESS(eventStatus ) ) { lpNetworkEvents->iErrorCode[FD_CLOSE_BIT] = SockNtStatusToSocketError( eventStatus ); } } else if( eventInfo.PollEvents & AFD_POLL_DISCONNECT ) { lpNetworkEvents->lNetworkEvents |= FD_CLOSE; eventStatus = eventInfo.EventStatus[AFD_POLL_DISCONNECT_BIT]; if( !NT_SUCCESS(eventStatus ) ) { lpNetworkEvents->iErrorCode[FD_CLOSE_BIT] = SockNtStatusToSocketError( eventStatus ); } } exit: if ( socket != NULL ) { SockReleaseSocketLock( socket ); SockDereferenceSocket( socket ); } if ( err != NO_ERROR) { IF_DEBUG(EVENT_SELECT) { WS_PRINT(( "WSAEnumNetworkEvents failed: %ld\n", err )); } WS_EXIT( "WSAEnumNetworkEvents", SOCKET_ERROR, TRUE ); *lpErrno = err; return SOCKET_ERROR; } IF_DEBUG(EVENT_SELECT) { WS_PRINT(( "WSAEnumNetworkEvents successfully posted request, " "socket = %lx\n", socket )); } WS_EXIT( "WSAEnumNetworkEvents", NO_ERROR, FALSE ); return NO_ERROR; } // WSPEnumNetworkEvents