mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1532 lines
44 KiB
1532 lines
44 KiB
/*++
|
|
|
|
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
|