/*++ Copyright (c) 1992-1996 Microsoft Corporation Module Name: accept.c Abstract: This module contains support for the accept( ) WinSock API. Author: David Treadwell (davidtr) 9-Mar-1992 Revision History: --*/ #include "winsockp.h" INT SetHelperDllContext ( IN PSOCKET_INFORMATION ParentSocket, IN PSOCKET_INFORMATION ChildSocket ); SOCKET WSPAPI WSPAccept( IN SOCKET Handle, OUT struct sockaddr *SocketAddress, OUT int *SocketAddressLength, IN LPCONDITIONPROC lpfnCondition, IN DWORD dwCallbackData, OUT LPINT lpErrno ) /*++ Routine Description: This routine extracts the first connection on the queue of pending connections on s, and checks it against the condition function, provided the condition function is specified (i.e., not NULL). The condition function must be executed in the same thread as this routine is. If the condition function returns CF_ACCEPT, this routine creates a new socket and performs any socket grouping as indicated by the result parameter g in the condition function . Newly created sockets have the same properties as s including network events registered with WSPAsyncSelect() or with WSPEventSelect(), but not including the listening socket's group ID, if any. If the condition function returns CF_REJECT, this routine rejects the connection request. If the client's accept/reject decision cannot be made immediately, the condition function will return CF_DEFER to indicate that no decision has been made, and no action about this connection request is to be taken by the service provider. When the client is ready to take action on the connection request, it will invoke WSPAccept() again and return either CF_ACCEPT or CF_REJECT as a return value from the condition function. For sockets which are in the (default) blocking mode, if no pending connections are present on the queue, WSPAccept() blocks the caller until a connection is present. For sockets in a non-blocking mode, if this function is called when no pending connections are present on the queue, WSPAccept() returns the error code WSAEWOULDBLOCK as described below. The accepted socket may not be used to accept more connections. The original socket remains open. The argument addr is a result parameter that is filled in with the address of the connecting entity, as known to the service provider. The exact format of the addr parameter is determined by the address family in which the communication is occurring. The addrlen is a value-result parameter; it will initially contain the amount of space pointed to by addr. On return, it must contain the actual length (in bytes) of the address returned by the service provider. This call is used with connection- oriented socket types such as SOCK_STREAM. If addr and/or addrlen are equal to NULL, then no information about the remote address of the accepted socket is returned. Otherwise, these two parameters shall be filled in regardless of whether the condition function is specified or what it returns. The prototype of the condition function is as follows: int CALLBACK ConditionFunc( IN LPWSABUF lpCallerId, IN LPWSABUF lpCallerData, IN OUT LPQOS lpSQOS, IN OUT LPQOS lpGQOS, IN LPWSABUF lpCalleeId, IN LPWSABUF lpCalleeData, OUT GROUP FAR * g, IN DWORD dwCallbackData ); The lpCallerId and lpCallerData are value parameters which must contain the address of the connecting entity and any user data that was sent along with the connection request, respectively. If no caller ID or caller data is available, the corresponding parameter will be NULL. lpSQOS references the flow specs for socket s specified by the caller, one for each direction, followed by any additional provider-specific parameters. The sending or receiving flow spec values will be ignored as appropriate for any unidirectional sockets. A NULL value for lpSQOS indicates no caller supplied QOS. QOS information may be returned if a QOS negotiation is to occur. lpGQOS references the flow specs for the socket group the caller is to create, one for each direction, followed by any additional provider-specific parameters. A NULL value for lpGQOS indicates no caller-supplied group QOS. QOS information may be returned if a QOS negotiation is to occur. The lpCalleeId is a value parameter which contains the local address of the connected entity. The lpCalleeData is a result parameter used by the condition function to supply user data back to the connecting entity. The storage for this data must be provided by the service provider. lpCalleeData->len initially contains the length of the buffer allocated by the service provider and pointed to by lpCalleeData->buf. A value of zero means passing user data back to the caller is not supported. The condition function will copy up to lpCalleeData->len bytes of data into lpCalleeData->buf , and then update lpCalleeData->len to indicate the actual number of bytes transferred. If no user data is to be passed back to the caller, the condition function will set lpCalleeData->len to zero. The format of all address and user data is specific to the address family to which the socket belongs. The result parameter g is assigned within the condition function to indicate the following actions: if &g is an existing socket group ID, add s to this group, provided all the requirements set by this group are met; or if &g = SG_UNCONSTRAINED_GROUP, create an unconstrained socket group and have s as the first member; or if &g = SG_CONSTRAINED_GROUP, create a constrained socket group and have s as the first member; or if &g = zero, no group operation is performed. Any set of sockets grouped together must be implemented by a single service provider. For unconstrained groups, any set of sockets may be grouped together. A constrained socket group may consist only of connection-oriented sockets, and requires that connections on all grouped sockets be to the same address on the same host. For newly created socket groups, the new group ID must be available for the WinSock SPI client to retrieve by calling WSPGetSockOpt() with option SO_GROUP_ID. A socket group and its associated ID remain valid until the last socket belonging to this socket group is closed. Socket group IDs are unique across all processes for a given service provider. dwCallbackData is supplied to the condition function exactly as supplied by the caller of WSPAccept(). Arguments: s - A descriptor identifying a socket which is listening for connections after a WSPListen(). addr - An optional pointer to a buffer which receives the address of the connecting entity, as known to the service provider. The exact format of the addr argument is determined by the address family established when the socket was created. addrlen - An optional pointer to an integer which contains the length of the address addr. lpfnCondition - The procedure instance address of an optional, WinSock 2 client- supplied condition function which will make an accept/reject decision based on the caller information passed in as parameters, and optionally create and/or join a socket group by assigning an appropriate value to the result parameter, g, of this routine. dwCallbackData - Callback data to be passed back to the WinSock 2 client as a condition function parameter. This parameter is not interpreted by the service provider. lpErrno - A pointer to the error code. Return Value: If no error occurs, WSPAccept() returns a value of type SOCKET which is a descriptor for the accepted socket. Otherwise, a value of INVALID_SOCKET is returned, and a specific error code is available in lpErrno. --*/ { NTSTATUS status; PSOCKET_INFORMATION socketInfo; IO_STATUS_BLOCK ioStatusBlock; PAFD_LISTEN_RESPONSE_INFO afdListenResponse; ULONG afdListenResponseLength; AFD_ACCEPT_INFO afdAcceptInfo; int err; SOCKET newSocketHandle; PSOCKET_INFORMATION newSocket; INT socketAddressLength; WSAPROTOCOL_INFOW protocolInfo; GROUP newGroup; BYTE afdLocalListenResponse[MAX_FAST_LISTEN_RESPONSE]; WS_ENTER( "WSPAccept", (PVOID)Handle, SocketAddress, (PVOID)SocketAddressLength, lpfnCondition ); WS_ASSERT( lpErrno != NULL ); IF_DEBUG(ACCEPT) { WS_PRINT(( "WSPAccept() on socket %lx addr %ld addrlen *%lx == %ld\n", Handle, SocketAddress, SocketAddressLength, SocketAddressLength ? *SocketAddressLength : 0 )); } err = SockEnterApi( TRUE, TRUE, FALSE ); if( err != NO_ERROR ) { WS_EXIT( "WSPAccept", INVALID_SOCKET, TRUE ); *lpErrno = err; return INVALID_SOCKET; } // // Set up local variables so that we know how to clean up on exit. // socketInfo = NULL; afdListenResponse = NULL; newSocket = NULL; newSocketHandle = INVALID_SOCKET; newGroup = 0; // // Find a pointer to the socket structure corresponding to the // passed-in handle. // socketInfo = SockFindAndReferenceSocket( Handle, TRUE ); if ( socketInfo == NULL ) { err = WSAENOTSOCK; goto exit; } // // Acquire the lock that protects sockets. We hold this lock // throughout this routine to synchronize against other callers // performing operations on the socket we're doing the accept on. // SockAcquireSocketLockExclusive( socketInfo ); // // If this is a datagram socket, fail the request. // if ( IS_DGRAM_SOCK(socketInfo->SocketType) ) { err = WSAEOPNOTSUPP; goto exit; } // // If the socket is not listening for connection attempts, fail this // request. // if ( socketInfo->State != SocketStateListening ) { err = WSAEINVAL; goto exit; } // // Make sure that the length of the address buffer is large enough // to hold a sockaddr structure for the address family of this // socket. // if ( ARGUMENT_PRESENT( SocketAddressLength ) && socketInfo->HelperDll->MinSockaddrLength > *SocketAddressLength ) { err = WSAEFAULT; goto exit; } // // Allocate space to hold the listen response structure. This // buffer must be large enough to hold a TRANSPORT_STRUCTURE for the // address family of this socket. // afdListenResponseLength = sizeof(AFD_LISTEN_RESPONSE_INFO) - sizeof(TRANSPORT_ADDRESS) + socketInfo->HelperDll->MaxTdiAddressLength; if ( afdListenResponseLength <= sizeof(afdLocalListenResponse) ) { afdListenResponse = (PAFD_LISTEN_RESPONSE_INFO)afdLocalListenResponse; } else { afdListenResponse = ALLOCATE_HEAP( afdListenResponseLength ); if ( afdListenResponse == NULL ) { err = WSAENOBUFS; goto exit; } } // // If the socket is non-blocking, determine whether data exists on // the socket. If not, fail the request. // if ( socketInfo->NonBlocking ) { struct fd_set readfds; struct timeval timeout; int returnCode; FD_ZERO( &readfds ); FD_SET( socketInfo->Handle, &readfds ); timeout.tv_sec = 0; timeout.tv_usec = 0; returnCode = WSPSelect( 1, &readfds, NULL, NULL, &timeout, lpErrno ); if ( returnCode == SOCKET_ERROR ) { err = *lpErrno; goto exit; } if ( !FD_ISSET( socketInfo->Handle, &readfds ) ) { WS_ASSERT( returnCode == 0 ); err = WSAEWOULDBLOCK; goto exit; } WS_ASSERT( returnCode == 1 ); } // // Wait for a connection attempt to arrive. // status = NtDeviceIoControlFile( (HANDLE)socketInfo->Handle, SockThreadEvent, NULL, // APC Routine NULL, // APC Context &ioStatusBlock, IOCTL_AFD_WAIT_FOR_LISTEN, NULL, 0, afdListenResponse, afdListenResponseLength ); if ( status == STATUS_PENDING ) { SockReleaseSocketLock( socketInfo ); SockWaitForSingleObject( SockThreadEvent, socketInfo->Handle, SOCK_CONDITIONALLY_CALL_BLOCKING_HOOK, SOCK_NO_TIMEOUT ); SockAcquireSocketLockExclusive( socketInfo ); status = ioStatusBlock.Status; } if ( !NT_SUCCESS(status) ) { err = SockNtStatusToSocketError( status ); goto exit; } // // If a condition function was specified, then give the user the // opportunity to accept/reject the incoming connection. // if( lpfnCondition != NULL ) { WSABUF callerId; WSABUF callerData; WSABUF calleeId; WSABUF calleeData; INT result; BYTE fastAddressBuffer[sizeof(SOCKADDR_IN)]; PBYTE addressBuffer; ULONG addressBufferLength; INT remoteAddressLength; BOOLEAN isValidGroup; PCHAR connectDataBuffer; INT connectDataBufferLength; // // Allocate space for the remote address. // addressBufferLength = socketInfo->HelperDll->MaxSockaddrLength; if( addressBufferLength <= sizeof(fastAddressBuffer) ) { addressBuffer = fastAddressBuffer; } else { addressBuffer = ALLOCATE_HEAP( addressBufferLength ); if( addressBuffer == NULL ) { err = WSAENOBUFS; goto exit; } } connectDataBufferLength = 0; connectDataBuffer = NULL; if( ( socketInfo->ServiceFlags1 & XP1_CONNECT_DATA ) != 0 ) { AFD_UNACCEPTED_CONNECT_DATA_INFO connectInfo; // // Determine the size of the incoming connect data. // connectInfo.Sequence = afdListenResponse->Sequence; connectInfo.LengthOnly = TRUE; status = NtDeviceIoControlFile( (HANDLE)socketInfo->Handle, SockThreadEvent, NULL, NULL, &ioStatusBlock, IOCTL_AFD_GET_UNACCEPTED_CONNECT_DATA, &connectInfo, sizeof(connectInfo), &connectInfo, sizeof(connectInfo) ); if( status == STATUS_PENDING ) { SockWaitForSingleObject( SockThreadEvent, socketInfo->Handle, SOCK_NEVER_CALL_BLOCKING_HOOK, SOCK_NO_TIMEOUT ); status = ioStatusBlock.Status; } if( !NT_SUCCESS(status) ) { err = SockNtStatusToSocketError( status ); goto exit; } // // If there is data on the connection, get it. // connectDataBufferLength = ioStatusBlock.Information; if( connectDataBufferLength > 0 ) { connectDataBuffer = ALLOCATE_HEAP( connectDataBufferLength ); if( connectDataBuffer == NULL ) { err = WSAENOBUFS; goto exit; } // // Retrieve the data. // connectInfo.Sequence = afdListenResponse->Sequence; connectInfo.LengthOnly = FALSE; status = NtDeviceIoControlFile( (HANDLE)socketInfo->Handle, SockThreadEvent, NULL, NULL, &ioStatusBlock, IOCTL_AFD_GET_UNACCEPTED_CONNECT_DATA, &connectInfo, sizeof(connectInfo), connectDataBuffer, connectDataBufferLength ); if( status == STATUS_PENDING ) { SockWaitForSingleObject( SockThreadEvent, socketInfo->Handle, SOCK_NEVER_CALL_BLOCKING_HOOK, SOCK_NO_TIMEOUT ); status = ioStatusBlock.Status; } if( !NT_SUCCESS(status) ) { err = SockNtStatusToSocketError( status ); FREE_HEAP( connectDataBuffer ); goto exit; } } } // // Build the addresses. // calleeId.buf = (CHAR *)socketInfo->LocalAddress; calleeId.len = (ULONG)socketInfo->LocalAddressLength; SockBuildSockaddr( (PSOCKADDR)addressBuffer, &remoteAddressLength, &afdListenResponse->RemoteAddress ); callerId.buf = (CHAR *)addressBuffer; callerId.len = (ULONG)remoteAddressLength; // // Build the caller/callee data. // // Unfortunately, since we're "faking" this deferred accept // stuff (meaning that AFD has already fully accepted the // connection before the DLL ever sees it) there's no way // to support "callee data" in the condition function. // callerData.buf = connectDataBuffer; callerData.len = connectDataBufferLength; calleeData.buf = NULL; calleeData.len = 0; result = (lpfnCondition)( &callerId, // lpCallerId callerData.buf == NULL // lpCallerData ? NULL : &callerData, NULL, // lpSQOS NULL, // lpGQOS &calleeId, // lpCalleeId calleeData.buf == NULL // lpCalleeData ? NULL : &calleeData, &newGroup, // g dwCallbackData // dwCallbackData ); // // Before we free the buffers, validate the group ID returned // by the condition function. // isValidGroup = TRUE; if( result == CF_ACCEPT && newGroup != 0 && newGroup != SG_UNCONSTRAINED_GROUP && newGroup != SG_CONSTRAINED_GROUP ) { isValidGroup = SockIsAddressConsistentWithConstrainedGroup( socketInfo, newGroup, (PSOCKADDR)addressBuffer, remoteAddressLength ); } if( addressBuffer != fastAddressBuffer ) { FREE_HEAP( addressBuffer ); } if( connectDataBuffer != NULL ) { FREE_HEAP( connectDataBuffer ); } if( result == CF_ACCEPT ) { if( !isValidGroup ) { err = WSAEINVAL; goto exit; } } else { // // If the condition function returned any value other than // CF_ACCEPT, then we'll need to tell AFD to deal with this, // and we may also need to retry the wait for listen. // AFD_DEFER_ACCEPT_INFO deferAcceptInfo; if( result != CF_DEFER && result != CF_REJECT ) { err = WSAEINVAL; goto exit; } deferAcceptInfo.Sequence = afdListenResponse->Sequence; deferAcceptInfo.Reject = ( result == CF_REJECT ); status = NtDeviceIoControlFile( (HANDLE)socketInfo->Handle, SockThreadEvent, NULL, // APC Routine NULL, // APC Context &ioStatusBlock, IOCTL_AFD_DEFER_ACCEPT, &deferAcceptInfo, sizeof(deferAcceptInfo), NULL, 0 ); // // IOCTL_AFD_DEFER_ACCEPT should never pend. // WS_ASSERT( status != STATUS_PENDING ); if ( !NT_SUCCESS(status) ) { err = SockNtStatusToSocketError( status ); goto exit; } // // The condition function returned either CF_REJECT or // CF_DEFER, so fail the WSPAccept() call with the // appropriate error code. // if( result == CF_REJECT ) { err = WSAECONNREFUSED; } else { WS_ASSERT( result == CF_DEFER ); err = WSATRY_AGAIN; } goto exit; } } // // Create a new socket to use for the connection. // RtlZeroMemory( &protocolInfo, sizeof(protocolInfo) ); protocolInfo.iAddressFamily = socketInfo->AddressFamily; protocolInfo.iSocketType = socketInfo->SocketType; protocolInfo.iProtocol = socketInfo->Protocol; protocolInfo.dwCatalogEntryId = socketInfo->CatalogEntryId; protocolInfo.dwServiceFlags1 = socketInfo->ServiceFlags1; protocolInfo.dwProviderFlags = socketInfo->ProviderFlags; newSocketHandle = WSPSocket( socketInfo->AddressFamily, socketInfo->SocketType, socketInfo->Protocol, &protocolInfo, newGroup, socketInfo->CreationFlags, &err ); if ( newSocketHandle == INVALID_SOCKET ) { WS_ASSERT( err != NO_ERROR ); goto exit; } // // Find a pointer to the new socket and reference the socket. // newSocket = SockFindAndReferenceSocket( newSocketHandle, FALSE ); if( newSocket == NULL ) { // // Cannot find the newly created socket. This usually means the // app has closed a random socket handle that just happens to // be the one we just created. // err = WSAENOTSOCK; goto exit; } // // Set up to accept the connection attempt. // afdAcceptInfo.Sequence = afdListenResponse->Sequence; afdAcceptInfo.AcceptHandle = (HANDLE)newSocketHandle; // // Do the actual accept. This associates the new socket we just // opened with the connection object that describes the VC that // was just initiated. // status = NtDeviceIoControlFile( (HANDLE)socketInfo->Handle, SockThreadEvent, NULL, // APC Routine NULL, // APC Context &ioStatusBlock, IOCTL_AFD_ACCEPT, &afdAcceptInfo, sizeof(afdAcceptInfo), NULL, 0 ); if ( status == STATUS_PENDING ) { SockReleaseSocketLock( socketInfo ); SockWaitForSingleObject( SockThreadEvent, socketInfo->Handle, SOCK_CONDITIONALLY_CALL_BLOCKING_HOOK, SOCK_NO_TIMEOUT ); SockAcquireSocketLockExclusive( socketInfo ); status = ioStatusBlock.Status; } if ( !NT_SUCCESS(status) ) { err = SockNtStatusToSocketError( status ); goto exit; } // // Notify the helper DLL that the socket has been accepted. // err = SockNotifyHelperDll( newSocket, WSH_NOTIFY_ACCEPT ); if ( err != NO_ERROR ) { goto exit; } // // Remember the address of the remote client in the new socket, // and copy the local address of the newly created socket into // the new socket. // SockBuildSockaddr( newSocket->RemoteAddress, &socketAddressLength, &afdListenResponse->RemoteAddress ); RtlCopyMemory( newSocket->LocalAddress, socketInfo->LocalAddress, socketInfo->LocalAddressLength ); newSocket->LocalAddressLength = socketInfo->LocalAddressLength; // // Copy the remote address into the caller's address buffer, and // indicate the size of the remote address. // if ( ARGUMENT_PRESENT( SocketAddress ) && ARGUMENT_PRESENT( SocketAddressLength ) ) { SockBuildSockaddr( SocketAddress, SocketAddressLength, &afdListenResponse->RemoteAddress ); } // // Do the core operations in accepting the socket. // err = SockCoreAccept( socketInfo, newSocket ); if ( err != NO_ERROR ) { goto exit; } exit: IF_DEBUG(ACCEPT) { if ( err != NO_ERROR ) { WS_PRINT(( " accept on socket %lx (%lx) failed: %ld\n", Handle, socketInfo, err )); } else { WS_PRINT(( " accept on socket %lx (%lx) returned socket " "%lx (%lx), remote", Handle, socketInfo, newSocketHandle, newSocket )); WsPrintSockaddr( newSocket->RemoteAddress, &newSocket->RemoteAddressLength ); } } if ( socketInfo != NULL ) { if ( SockAsyncThreadInitialized ) { SockReenableAsyncSelectEvent( socketInfo, FD_ACCEPT ); } SockReleaseSocketLock( socketInfo ); SockDereferenceSocket( socketInfo ); } if ( newSocket != NULL ) { SockDereferenceSocket( newSocket ); } if ( afdListenResponse != (PAFD_LISTEN_RESPONSE_INFO)afdLocalListenResponse && afdListenResponse != NULL ) { FREE_HEAP( afdListenResponse ); } if ( err != NO_ERROR ) { if ( newSocketHandle != INVALID_SOCKET ) { int errTmp; WSPCloseSocket( newSocketHandle, &errTmp ); } *lpErrno = err; return INVALID_SOCKET; } WS_EXIT( "accept", newSocketHandle, FALSE ); return newSocketHandle; } // WSPAccept INT SetHelperDllContext ( IN PSOCKET_INFORMATION ParentSocket, IN PSOCKET_INFORMATION ChildSocket ) { PVOID context; ULONG helperDllContextLength; INT error; // // Get TDI handles for the child socket. // error = SockGetTdiHandles( ChildSocket ); if ( error != NO_ERROR ) { return error; } // // Determine how much space we need for the helper DLL context // on the parent socket. // error = ParentSocket->HelperDll->WSHGetSocketInformation ( ParentSocket->HelperDllContext, ParentSocket->Handle, ParentSocket->TdiAddressHandle, ParentSocket->TdiConnectionHandle, SOL_INTERNAL, SO_CONTEXT, NULL, (PINT)&helperDllContextLength ); if ( error != NO_ERROR ) { return error; } // // Allocate a buffer to hold all context information. // context = ALLOCATE_HEAP( helperDllContextLength ); if ( context == NULL ) { return WSAENOBUFS; } // // Get the parent socket's information. // error = ParentSocket->HelperDll->WSHGetSocketInformation ( ParentSocket->HelperDllContext, ParentSocket->Handle, ParentSocket->TdiAddressHandle, ParentSocket->TdiConnectionHandle, SOL_INTERNAL, SO_CONTEXT, context, (PINT)&helperDllContextLength ); if ( error != NO_ERROR ) { FREE_HEAP( context ); return error; } // // Set the parent's context on the child socket. // error = ChildSocket->HelperDll->WSHSetSocketInformation ( ChildSocket->HelperDllContext, ChildSocket->Handle, ChildSocket->TdiAddressHandle, ChildSocket->TdiConnectionHandle, SOL_INTERNAL, SO_CONTEXT, context, helperDllContextLength ); FREE_HEAP( context ); if ( error != NO_ERROR ) { return error; } // // It all worked. // return NO_ERROR; } // SetHelperDllContext BOOL AcceptEx ( IN SOCKET sListenSocket, IN SOCKET sAcceptSocket, IN PVOID lpOutputBuffer, IN DWORD dwReceiveDataLength, IN DWORD dwLocalAddressLength, IN DWORD dwRemoteAddressLength, OUT LPDWORD lpdwBytesReceived, IN LPOVERLAPPED lpOverlapped ) /*++ Routine Description: Combines several socket functions into a single API/kernel transition. A new connection is accepted, both the local and remote addresses for the connection are returned, and a receive is done for the first chunk of data sent by the remote. By combining all these functions into a sigle call, the performance of connection acceptance is significantly improved. Unlike the normal winsock accept() call, AcceptEx() is asynchronous, which lets it fit better with servers which use thread pooling to improve scalability. As with all overlapped Win32 functions, either Win32 events or completion ports may be used as a completion notification mechanism. Another key difference between this function and the normal accept() function is that this function requires the caller to specify both the listening socket AND the socket on which to accept the connection. The sAcceptSocket must be an opened socket which is neither bound nor connected. Note that calling TransmitFile() with both the TF_DISCONNECT and TF_REUSE_SOCKET flags will result in the specified socket being returned to the "open" state, so such a socket may be passed to AcceptEx() as sAcceptSocket. Note that only a single output buffer receives the data as well as the local and remote socket addresses. Use of a single buffer improves performance, but the caller should call th GetAcceptExSockaddrs() function to locate the addresses in the output buffer. Because the addresses are written in an internal format, the caller must specify sisteen more bytes for them than the size of the SOCKADDR structure for the transport protocol in use. For example, th size of a SOCKADDR_IN, the address structure for TCP/IP, is 16 bytes, so at least 24 bytes must be specified as the buffer sizes for the local and remote addresses. The lpNumberOfBytesTransferred parameters of the GetQueuedCompletionStatus() and GetOverlappedResult() APIs indicates the number of bytes received in the request. When this operation completes successfully, sAcceptHandle may be used for only the following routines: ReadFile WriteFile send recv TransmitFile closesocket In order to use it with othr winsock routines, a setsockopt() with the SO_UPDATE_ACCEPT_CONTEXT option must be performed on the socket. This socket option initializes some user-mode state on the socket which allows other winsock routines to access the socket correctly. sAcceptSocket will not inherit the properties of sListenSocket until SO_UPDATE_ACCEPT_CONTEXT is set on the socket. When AcceptEx() returns, sAcceptSocket is in the default state for a connected socket. To use the SO_UPDATE_ACCEPT_CONTEXT option, call the setsockopt() function, specifying sAcceptSocket as the socket handle to setsockopt() and specify sListenSocket as the option value. For example, err = setsockopt( sAcceptSocket, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)&sListenSocket, sizeof(sListenSocket) ); Arguments: sListenSocket - a listening socket on which to accept a connection. The listen() socket API must have been previously called on this handle. sAcceptSocket - an open socket handle on which to accept an incoming connection. This socket must not be bound or connected. lpOutputBuffer - a pointer to a buffer which receives the first chunk of data sent on the connection and the local and remote addresses for the new connection. The receive data is written to the first part of the buffer (starting at offset 0) and the addresses are written later in the buffer. This parameter must be specified. dwReceiveDataLength - the number of bytes in the buffer that should be used for receiving data. If this parameter is specified as 0, then no receive operation is performed in conjunction with the accept--the accept completes as soon as a connection arrives without waiting for any data to arrive. dwLocalAddressLength - the number of bytes reserved for the local address information. This must be at least sixteen bytes more than the maximum sockaddr length for the transport protocol in use. dwRemoteAddressLength - the number of bytes reserved for the remote address information. This must be at least sixteen bytes more than the maximum sockaddr length for the transport protocol in use. lpdwBytesReceived - points to a DWORD which receives the count of bytes received. This is only set if the operation completes synchronously; if it returns ERROR_IO_PENDING and completes later, then this DWORD is never set and the count of bytes read may be obtained from the completion notification mechnaism. lpOverlapped - an OVERLAPPED structure to use in processing the request. This parameter MUST be specified; it may not be NULL. Return Value: TRUE if the operation completed successfully. FALSE if there was an error, in which case GetLastError() may be called to return extended error information. If GetLastError() returns ERROR_IO_PENDING then the operation was successfully initiated and is still in progress. --*/ { BYTE inputBuffer[sizeof(AFD_SUPER_ACCEPT_INFO) + 500]; DWORD inputBufferLength; PAFD_SUPER_ACCEPT_INFO superAcceptInfo; NTSTATUS status; // // The lpOverlapped parameter is required for this function. // if ( !ARGUMENT_PRESENT( lpOverlapped ) ) { SetLastError( ERROR_INVALID_PARAMETER ); return FALSE; } // // The input buffer must be large enough to hold both the super // accept info and the remote address. If the stack space isn't // large enough, allocate some heap. // inputBufferLength = sizeof(AFD_SUPER_ACCEPT_INFO) + dwRemoteAddressLength; if ( sizeof(inputBuffer) < inputBufferLength ) { superAcceptInfo = ALLOCATE_HEAP( inputBufferLength ); if ( superAcceptInfo == NULL ) { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); return FALSE; } } else { superAcceptInfo = (PAFD_SUPER_ACCEPT_INFO)inputBuffer; } // // Initialize the info we need to pass to AFD. // superAcceptInfo->AcceptHandle = (HANDLE)sAcceptSocket; superAcceptInfo->ReceiveDataLength = dwReceiveDataLength; superAcceptInfo->LocalAddressLength = dwLocalAddressLength; superAcceptInfo->RemoteAddressLength = dwRemoteAddressLength; lpOverlapped->Internal = (DWORD)STATUS_PENDING; // // Make the actual IOCTL call to AFD. // status = NtDeviceIoControlFile( (HANDLE)sListenSocket, lpOverlapped->hEvent, NULL, (DWORD)lpOverlapped->hEvent & 1 ? NULL : lpOverlapped, (PIO_STATUS_BLOCK)(&lpOverlapped->Internal), IOCTL_AFD_SUPER_ACCEPT, superAcceptInfo, inputBufferLength, lpOutputBuffer, dwReceiveDataLength + dwLocalAddressLength + dwRemoteAddressLength ); if ( (PBYTE)superAcceptInfo != inputBuffer ) { FREE_HEAP( superAcceptInfo ); } if ( NT_SUCCESS(status) && status != STATUS_PENDING) { *lpdwBytesReceived = lpOverlapped->InternalHigh; return TRUE; } else { SetLastError( SockNtStatusToSocketError(status) ); return FALSE; } } // AcceptEx VOID GetAcceptExSockaddrs ( IN PVOID lpOutputBuffer, IN DWORD dwReceiveDataLength, IN DWORD dwLocalAddressLength, IN DWORD dwRemoteAddressLength, OUT LPSOCKADDR *LocalSockaddr, OUT LPINT LocalSockaddrLength, OUT LPSOCKADDR *RemoteSockaddr, OUT LPINT RemoteSockaddrLength ) /*++ Routine Description: Processes the lpOutputBuffer parameter after a successful AcceptEx() operation. Because AcceptEx() writes address information in an internal (TDI) format, this routine is required to locate the SOCKADDR structures in the buffer. Arguments: lpOutputBuffer - the same lpOutputBuffer parameter that was passed to AcceptEx(). dwReceiveDataLength - must be equal to the dwReceiveDataLength parameter which was passed to AcceptEx(). dwLocalAddressLength - must be equal to the dwLocalAddressLength parameter which was passed to AcceptEx(). dwRemoteAddressLength - must be equal to the dwRemoteAddressLength parameter which was passed to AcceptEx(). LocalSockaddr - receives a pointer to the SOCKADDR which describes the local address of the connection (the same information as would be returned by getsockname()). This parameter must be specified. LocalSockaddrLength - receives the size of the local address. This parameter must be specified. RemoteSockaddr - receives a pointer to the SOCKADDR which describes the remote address of the connection (the same information as would be returned by getpeername()). This parameter must be specified. RemoteSockaddrLength - receives the size of the local address. This parameter must be specified. Return Value: None. --*/ { PTRANSPORT_ADDRESS tdiAddress; // // First locate the local address. There is one ULONG between the // start of the local address section of the buffer and the actual // TDI address. // tdiAddress = (PTRANSPORT_ADDRESS) ( (PCHAR)lpOutputBuffer + dwReceiveDataLength + sizeof(ULONG) ); // // Locate the local sockaddr and determine its length. // *LocalSockaddrLength = tdiAddress->Address[0].AddressLength + sizeof((*LocalSockaddr)->sa_family); *LocalSockaddr = (LPSOCKADDR)(&tdiAddress->Address[0].AddressType); // // If this is not an x86 machine, copy the sockaddr forward over the // TDI address. This is required because there are 10 bytes of TDI // overhead in front of the buffer, so the sockaddr will be unaligned // if the input buffers were aligned. // // Note that we must use RtlMoveMemory here, not RtlCopyMemory, // because the buffers are likely to overlap. // #ifndef i386 RtlMoveMemory( tdiAddress, *LocalSockaddr, *LocalSockaddrLength ); *LocalSockaddr = (LPSOCKADDR)tdiAddress; #endif // // Repeat for the remote sockaddr. // tdiAddress = (PTRANSPORT_ADDRESS) ( (PCHAR)lpOutputBuffer + dwReceiveDataLength + dwLocalAddressLength ); *RemoteSockaddrLength = tdiAddress->Address[0].AddressLength + sizeof((*RemoteSockaddr)->sa_family); *RemoteSockaddr = (LPSOCKADDR)(&tdiAddress->Address[0].AddressType); // // Again, move the sockaddr on risc machines. // #ifndef i386 RtlMoveMemory( tdiAddress, *RemoteSockaddr, *RemoteSockaddrLength ); *RemoteSockaddr = (LPSOCKADDR)tdiAddress; #endif return; } // GetAcceptExSockaddrs INT SockCoreAccept ( IN PSOCKET_INFORMATION ListenSocket, IN PSOCKET_INFORMATION AcceptSocket ) { INT err; INT result; // // Indicate that the new socket is connected. // AcceptSocket->State = SocketStateConnected; // // Clone the properties of the listening socket to the new socket. // AcceptSocket->LingerInfo = ListenSocket->LingerInfo; AcceptSocket->ReceiveBufferSize = ListenSocket->ReceiveBufferSize; AcceptSocket->SendBufferSize = ListenSocket->SendBufferSize; AcceptSocket->Broadcast = ListenSocket->Broadcast; AcceptSocket->Debug = ListenSocket->Debug; AcceptSocket->OobInline = ListenSocket->OobInline; AcceptSocket->ReuseAddresses = ListenSocket->ReuseAddresses; AcceptSocket->SendTimeout = ListenSocket->SendTimeout; AcceptSocket->ReceiveTimeout = ListenSocket->ReceiveTimeout; // // Set up the new socket to have the same blocking, inline, and // timeout characteristics as the listening socket. // if ( ListenSocket->NonBlocking ) { err = SockSetInformation( AcceptSocket, AFD_NONBLOCKING_MODE, &ListenSocket->NonBlocking, NULL, NULL ); if ( err != NO_ERROR ) { return err; } } AcceptSocket->NonBlocking = ListenSocket->NonBlocking; if ( ListenSocket->OobInline ) { err = SockSetInformation( AcceptSocket, AFD_INLINE_MODE, &ListenSocket->OobInline, NULL, NULL ); if ( err != NO_ERROR ) { return err; } } AcceptSocket->OobInline = ListenSocket->OobInline; // // If the listening socket has been called with WSAAsyncSelect, // set up WSAAsyncSelect on this socket. Otherwise, if the socket // has been called with WSAEventSelect, set up WSAEventSelect on // the socket. // if ( ListenSocket->AsyncSelectlEvent ) { result = WSPAsyncSelect( AcceptSocket->Handle, ListenSocket->AsyncSelecthWnd, ListenSocket->AsyncSelectwMsg, ListenSocket->AsyncSelectlEvent, &err ); if ( result == SOCKET_ERROR ) { return err; } } else if ( ListenSocket->EventSelectlNetworkEvents ) { result = WSPEventSelect( AcceptSocket->Handle, ListenSocket->EventSelectEventObject, ListenSocket->EventSelectlNetworkEvents, &err ); if ( result == SOCKET_ERROR ) { return err; } } // // Clone the helper DLL's context from the listening socket to the // accepted socket. // err = SetHelperDllContext( ListenSocket, AcceptSocket ); if ( err != NO_ERROR ) { err = NO_ERROR; } // // If the application has modified the send or receive buffer sizes, // then set up the buffer sizes on the socket. // err = SockUpdateWindowSizes( AcceptSocket, FALSE ); if ( err != NO_ERROR ) { return err; } // // Remember the changed state of this socket. // err = SockSetHandleContext( AcceptSocket ); if ( err != NO_ERROR ) { return err; } return NO_ERROR; } // SockCoreAccept