Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1391 lines
34 KiB

/*++
Copyright (c) 1992-1996 Microsoft Corporation
Module Name:
connect.c
Abstract:
This module contains support for the connect( ) WinSock API.
Author:
David Treadwell (davidtr) 28-Feb-1992
Revision History:
--*/
#include "winsockp.h"
VOID
AsyncConnectCompletionApc (
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved
);
BOOLEAN
IsSockaddrEqualToZero (
IN const struct sockaddr * SocketAddress,
IN int SocketAddressLength
);
INT
SockBeginAsyncConnect (
IN PSOCKET_INFORMATION Socket,
IN struct sockaddr * SocketAddress,
IN int SocketAddressLength,
LPWSABUF lpCalleeData
);
INT
SockDoConnect (
IN SOCKET Handle,
IN const struct sockaddr * SocketAddress,
IN int SocketAddressLength,
IN BOOLEAN InThread,
IN PSOCK_ASYNC_CONNECT_CONTEXT ConnectContext,
LPWSABUF lpCallerData,
LPWSABUF lpCalleeData
);
INT
PostProcessConnect (
IN PSOCKET_INFORMATION Socket
);
INT
UnconnectDatagramSocket (
IN PSOCKET_INFORMATION Socket
);
INT
WSPAPI
WSPConnect(
IN SOCKET Handle,
IN const struct sockaddr * SocketAddress,
IN int SocketAddressLength,
LPWSABUF lpCallerData,
LPWSABUF lpCalleeData,
LPQOS lpSQOS,
LPQOS lpGQOS,
LPINT lpErrno
)
/*++
Routine Description:
This routine is used to create a connection to the specified destination,
and to perform a number of other ancillary operations that occur at
connect time as well. If the socket, s, is unbound, unique values are
assigned to the local association by the system, and the socket is marked
as bound.
For connection-oriented sockets (e.g., type SOCK_STREAM), an active
connection is initiated to the specified host using name (an address in
the name space of the socket; for a detailed description, please see
WSPBind()). When this call completes successfully, the socket is ready to
send/receive data. If the address field of the name structure is all
zeroes, WSPConnect() will return the error WSAEADDRNOTAVAIL. Any attempt
to re-connect an active connection will fail with the error code
WSAEISCONN.
For a connectionless socket (e.g., type SOCK_DGRAM), the operation
performed by WSPConnect() is to establish a default destination address
so that the socket may be used with subsequent connection-oriented send
and receive operations (WSPSend(),WSPRecv()). Any datagrams received from
an address other than the destination address specified will be discarded.
If the address field of the name structure is all zeroes, the socket will
be "dis-connected" - the default remote address will be indeterminate,
so WSPSend() and WSPRecv() calls will return the error code WSAENOTCONN,
although WSPSendTo() and WSPRecvFrom() may still be used. The default
destination may be changed by simply calling WSPConnect() again, even if
the socket is already "connected". Any datagrams queued for receipt are
discarded if name is different from the previous WSPConnect().
For connectionless sockets, name may indicate any valid address, including
a broadcast address. However, to connect to a broadcast address, a socket
must have WSPSetSockOpt() SO_BROADCAST enabled, otherwise WSPConnect()
will fail with the error code WSAEACCES.
On connectionless sockets, exchange of user to user data is not possible
and the corresponding parameters will be silently ignored.
The WinSock SPI client is responsible for allocating any memory space
pointed to directly or indirectly by any of the parameters it specifies.
The lpCallerData is a value parameter which contains any user data that
is to be sent along with the connection request. If lpCallerData is NULL,
no user data will be passed to the peer. The lpCalleeData is a result
parameter which will reference any user data passed back from the peer as
part of the connection establishment. lpCalleeData->len initially
contains the length of the buffer allocated by the WinSock SPI client
and pointed to by lpCalleeData->buf. lpCalleeData->len will be set to 0
if no user data has been passed back. The lpCalleeData information will
be valid when the connection operation is complete. For blocking sockets,
this will be when the WSPConnect() function returns. For non-blocking
sockets, this will be after the FD_CONNECT notification has occurred. If
lpCalleeData is NULL, no user data will be passed back. The exact format
of the user data is specific to the address family to which the socket
belongs and/or the applications involved.
At connect time, a WinSock SPI client may use the lpSQOS and/or lpGQOS
parameters to override any previous QOS specification made for the socket
via WSPIoctl() with either the SIO_SET_QOS or SIO_SET_GROUP_QOS opcodes.
lpSQOS specifies the flow specs for socket s, one for each direction,
followed by any additional provider-specific parameters. If either the
associated transport provider in general or the specific type of socket
in particular cannot honor the QOS request, an error will be returned as
indicated below. The sending or receiving flow spec values will be ignored,
respectively, for any unidirectional sockets. If no provider-specific
parameters are supplied, the buf and len fields of lpSQOS->ProviderSpecific
should be set to NULL and 0, respectively. A NULL value for lpSQOS
indicates no application supplied QOS.
lpGQOS specifies the flow specs for the socket group (if applicable), one
for each direction, followed by any additional provider-specific
parameters. If no provider- specific parameters are supplied, the buf and
len fields of lpSQOS->ProviderSpecific should be set to NULL and 0,
respectively. A NULL value for lpGQOS indicates no application-supplied
group QOS. This parameter will be ignored if s is not the creator of the
socket group.
When connected sockets break (i.e. become closed for whatever reason),
they should be discarded and recreated. It is safest to assume that when
things go awry for any reason on a connected socket, the WinSock SPI
client must discard and recreate the needed sockets in order to return
to a stable point.
Arguments:
s - A descriptor identifying an unconnected socket.
name- The name of the peer to which the socket is to be connected.
namelen - The length of the name.
lpCallerData - A pointer to the user data that is to be transferred
to the peer during connection establishment.
lpCalleeData - A pointer to a buffer into which may be copied any user
data received from the peer during connection establishment.
lpSQOS - A pointer to the flow specs for socket s, one for each
direction.
lpGQOS - A pointer to the flow specs for the socket group (if
applicable).
lpErrno - A pointer to the error code.
Return Value:
If no error occurs, WSPConnect() returns 0. Otherwise, it returns
SOCKET_ERROR, and a specific error code is available in lpErrno.
--*/
{
char szAddr[18];
int err;
WS_ENTER( "WSPConnect", (PVOID)Handle, (PVOID)SocketAddress, (PVOID)SocketAddressLength, lpCallerData );
IF_DEBUG(CONNECT) {
WS_PRINT(( "connect()ing socket %lx to remote addr ", Handle ));
WsPrintSockaddr( (PSOCKADDR)SocketAddress, &SocketAddressLength );
}
WS_ASSERT( lpErrno != NULL );
err = SockDoConnect(
Handle,
SocketAddress,
SocketAddressLength,
FALSE,
NULL,
lpCallerData,
lpCalleeData
);
if( err == NO_ERROR ) {
WS_EXIT( "WSPConnect", 0, FALSE );
return 0;
}
WS_EXIT( "WSPConnect", SOCKET_ERROR, TRUE );
*lpErrno = err;
return SOCKET_ERROR;
} // WSPConnect
int
SockDoConnect (
IN SOCKET Handle,
IN const struct sockaddr * SocketAddress,
IN int SocketAddressLength,
IN BOOLEAN InThread,
IN PSOCK_ASYNC_CONNECT_CONTEXT ConnectContext,
IN LPWSABUF lpCallerData,
IN LPWSABUF lpCalleeData
)
{
NTSTATUS status;
PSOCKET_INFORMATION socket;
PTRANSPORT_ADDRESS tdiAddress;
ULONG tdiAddressLength;
int err;
PTDI_REQUEST_CONNECT tdiRequest;
ULONG tdiRequestLength;
SOCKADDR_INFO sockaddrInfo;
IO_STATUS_BLOCK ioStatusBlock;
BOOLEAN posted;
err = SockEnterApi( TRUE, TRUE, FALSE );
if( err != NO_ERROR ) {
return err;
}
//
// Set up local variables so that we know how to clean up on exit.
//
socket = NULL;
tdiRequest = 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;
}
//
// 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 connecting.
//
SockAcquireSocketLockExclusive( socket );
//
// The only valid socket states for this API are Open (only socket(
// ) was called) and Bound (socket( ) and bind( ) were called).
// Note that it is legal to reconnect a datagram socket.
//
if ( socket->State == SocketStateConnected &&
!IS_DGRAM_SOCK(socket->SocketType) ) {
err = WSAEISCONN;
goto exit;
}
if( socket->ConnectOutstanding && !InThread ) {
err = WSAEALREADY;
goto exit;
}
if ( socket->State != SocketStateOpen &&
socket->State != SocketStateBound &&
socket->State != SocketStateConnected ) {
err = WSAEINVAL;
goto exit;
}
//
// If this is a non-blocking connect on a non-overlapped socket,
// then fail the request.
//
if( socket->NonBlocking &&
( socket->CreationFlags & WSA_FLAG_OVERLAPPED ) == 0 ) {
err = WSAEINVAL;
goto exit;
}
//
// Make sure that the address structure passed in is legitimate.
//
if ( SocketAddressLength == 0 ) {
err = WSAEDESTADDRREQ;
goto exit;
}
//
// If this is a connected datagram socket and the caller has
// specified an address equal to all zeros, then this is a request
// to "unconnect" the socket.
//
if ( socket->State == SocketStateConnected &&
IS_DGRAM_SOCK(socket->SocketType) &&
IsSockaddrEqualToZero( SocketAddress, SocketAddressLength ) ) {
err = UnconnectDatagramSocket( socket );
goto exit;
}
//
// Determine the type of the sockaddr.
//
err = socket->HelperDll->WSHGetSockaddrType(
(PSOCKADDR)SocketAddress,
SocketAddressLength,
&sockaddrInfo
);
if ( err != NO_ERROR ) {
goto exit;
}
//
// Make sure that the address family passed in here is the same as
// was passed in on the socket( ) call.
//
if ( socket->AddressFamily != SocketAddress->sa_family ) {
err = WSAEINVAL;
goto exit;
}
//
// If the socket address is too short, fail.
//
if ( SocketAddressLength < socket->HelperDll->MinSockaddrLength ) {
err = WSAEFAULT;
goto exit;
}
//
// If the socket address is too long, truncate to the max possible
// length. If we didn't do this, the allocation below for the max
// TDI address length would be insufficient and SockBuildSockaddr()
// would overrun the allocated buffer.
//
if ( SocketAddressLength > socket->HelperDll->MaxSockaddrLength ) {
SocketAddressLength = socket->HelperDll->MaxSockaddrLength;
}
//
// If this socket belongs to a constrained group, then search all
// open sockets for one belonging to this same group and verify
// the target addresses match.
//
if( socket->GroupType == GroupTypeConstrained &&
!SockIsAddressConsistentWithConstrainedGroup(
socket,
socket->GroupID,
(PSOCKADDR)SocketAddress,
SocketAddressLength
) ) {
err = WSAEINVAL;
goto exit;
}
//
// If this socket is not yet bound to an address, bind it to an
// address. We only do this if the helper DLL for the socket supports
// a get wildcard address routine--if it doesn't, the app must bind
// to an address manually.
//
if ( socket->State == SocketStateOpen &&
socket->HelperDll->WSHGetWildcardSockaddr != NULL ) {
PSOCKADDR sockaddr;
INT sockaddrLength = socket->HelperDll->MaxSockaddrLength;
int result;
sockaddr = ALLOCATE_HEAP( sockaddrLength );
if ( sockaddr == NULL ) {
err = WSAENOBUFS;
goto exit;
}
err = socket->HelperDll->WSHGetWildcardSockaddr(
socket->HelperDllContext,
sockaddr,
&sockaddrLength
);
if ( err != NO_ERROR ) {
FREE_HEAP( sockaddr );
goto exit;
}
result = WSPBind(
Handle,
sockaddr,
sockaddrLength,
&err
);
FREE_HEAP( sockaddr );
if( result == SOCKET_ERROR ) {
goto exit;
}
} else if ( socket->State == SocketStateOpen ) {
//
// The socket is not bound and the helper DLL does not support
// a wildcard socket address. Fail, the app must bind manually.
//
err = WSAEINVAL;
goto exit;
}
//
// Make sure that the address family passed in here is the same as
// was passed in on the socket( ) call.
//
if ( socket->AddressFamily != SocketAddress->sa_family ) {
err = WSAEAFNOSUPPORT;
goto exit;
}
//
// If we have outbound connect data, set it on the socket.
//
if( lpCallerData != NULL &&
lpCallerData->buf != NULL &&
lpCallerData->len > 0 ) {
INT bufferLength;
//
// Set the connect data on the socket.
//
bufferLength = (INT)lpCallerData->len;
err = SockPassConnectData(
socket,
IOCTL_AFD_SET_CONNECT_DATA,
(PCHAR)lpCallerData->buf,
bufferLength,
&bufferLength
);
if( err != NO_ERROR ) {
goto exit;
}
}
//
// Disable the FD_CONNECT async select event before actually starting
// the connect attempt--otherwise, the following could occur:
//
// - call IOCTL to begin connect.
// - connect completes, async thread sets FD_CONNECT as disabled.
// - we reenable FD_CONNECT just before leaving this routine,
// but it shouldn't be enabled yet.
//
// Also disable FD_WRITE so that we don't get any FD_WRITE events
// until after the socket is connected.
//
if ( (socket->AsyncSelectlEvent & FD_CONNECT) != 0 ) {
socket->DisabledAsyncSelectEvents |= FD_CONNECT | FD_WRITE;
}
//
// If this is a nonblocking socket, perform the connect asynchronously.
// Datagram connects are done in this thread, since they are fast
// and don't require network activity.
//
if ( socket->NonBlocking && !InThread &&
!IS_DGRAM_SOCK(socket->SocketType) ) {
socket->ConnectOutstanding = TRUE;
err = SockBeginAsyncConnect(
socket,
(PSOCKADDR)SocketAddress,
SocketAddressLength,
lpCalleeData
);
WS_ASSERT( err != NO_ERROR );
goto exit;
}
//
// Determine how long the address will be in TDI format.
//
tdiAddressLength = socket->HelperDll->MaxTdiAddressLength;
//
// Allocate and initialize the TDI request structure.
//
tdiRequestLength = sizeof(*tdiRequest) +
sizeof(*tdiRequest->RequestConnectionInformation) +
sizeof(*tdiRequest->ReturnConnectionInformation) +
tdiAddressLength;
tdiRequest = ALLOCATE_HEAP( tdiRequestLength );
if ( tdiRequest == NULL ) {
err = WSAENOBUFS;
goto exit;
}
tdiRequest->RequestConnectionInformation =
(PTDI_CONNECTION_INFORMATION)(tdiRequest + 1);
tdiRequest->RequestConnectionInformation->UserDataLength = 0;
tdiRequest->RequestConnectionInformation->UserData = NULL;
tdiRequest->RequestConnectionInformation->OptionsLength = 0;
tdiRequest->RequestConnectionInformation->Options = NULL;
tdiRequest->RequestConnectionInformation->RemoteAddressLength = tdiAddressLength;
tdiRequest->RequestConnectionInformation->RemoteAddress =
tdiRequest->RequestConnectionInformation + 2;
tdiRequest->Timeout = RtlConvertLongToLargeInteger( ~0 );
tdiRequest->ReturnConnectionInformation =
tdiRequest->RequestConnectionInformation + 1;
tdiRequest->ReturnConnectionInformation->UserDataLength = 0;
tdiRequest->ReturnConnectionInformation->UserData = NULL;
tdiRequest->ReturnConnectionInformation->OptionsLength = 0;
tdiRequest->ReturnConnectionInformation->Options = NULL;
tdiRequest->ReturnConnectionInformation->RemoteAddressLength = 0;
tdiRequest->ReturnConnectionInformation->RemoteAddress = NULL;
tdiRequest->Timeout = RtlConvertLongToLargeInteger( ~0 );
//
// Convert the address from the sockaddr structure to the appropriate
// TDI structure.
//
tdiAddress = (PTRANSPORT_ADDRESS)
tdiRequest->RequestConnectionInformation->RemoteAddress;
SockBuildTdiAddress(
tdiAddress,
(PSOCKADDR)SocketAddress,
SocketAddressLength
);
//
// Save the name of the server we're connecting to.
//
RtlCopyMemory(
socket->RemoteAddress,
SocketAddress,
SocketAddressLength
);
socket->RemoteAddressLength = SocketAddressLength;
//
// If the user is expecting connect data back, then set a default
// connect data buffer in AFD. It would be preferrable if we could
// allocate that buffer on demand, but TDI does not allow for it.
//
if( lpCalleeData != NULL &&
lpCalleeData->buf != NULL &&
lpCalleeData->len > 0 ) {
ULONG length;
length = (ULONG)lpCalleeData->len;
err = SockPassConnectData(
socket,
IOCTL_AFD_SIZE_CONNECT_DATA,
(PCHAR)&length,
sizeof(length),
NULL
);
if( err != NO_ERROR ) {
goto exit;
}
}
//
// If we're doing this connect in the async thread, remember the
// information that the completion APC will need.
//
if ( InThread ) {
ConnectContext->TdiRequest = tdiRequest;
ConnectContext->TdiRequestLength = tdiRequestLength;
ConnectContext->Socket = socket;
//
// Now, submit the connect request to AFD, specifying an APC
// completion routine that will handle finishing up the request.
//
status = NtDeviceIoControlFile(
(HANDLE)socket->Handle,
NULL,
(PVOID)AsyncConnectCompletionApc,
ConnectContext,
&ConnectContext->IoStatusBlock,
IOCTL_AFD_CONNECT,
tdiRequest,
tdiRequestLength,
tdiRequest,
tdiRequestLength
);
//
// Release the socket lock--we don't need it any longer.
//
SockReleaseSocketLock( socket );
//
// If the request failed immediately, call the completion
// routine directly to perform cleanup.
//
if ( NT_ERROR(status) ) {
ConnectContext->IoStatusBlock.Status = status;
AsyncConnectCompletionApc(
ConnectContext,
&ConnectContext->IoStatusBlock,
0
);
}
//
// Now just return. The completion APC will handle all cleanup.
//
return SOCKET_ERROR;
}
//
// Call AFD to perform the actual connect operation.
//
socket->ConnectInProgress = TRUE;
do {
status = NtDeviceIoControlFile(
(HANDLE)socket->Handle,
SockThreadEvent,
NULL,
NULL,
&ioStatusBlock,
IOCTL_AFD_CONNECT,
tdiRequest,
tdiRequestLength,
tdiRequest,
tdiRequestLength
);
if ( status == STATUS_PENDING ) {
//
// Wait for the connect to complete.
//
SockReleaseSocketLock( socket );
SockWaitForSingleObject(
SockThreadEvent,
socket->Handle,
SOCK_CONDITIONALLY_CALL_BLOCKING_HOOK,
SOCK_NO_TIMEOUT
);
status = ioStatusBlock.Status;
SockAcquireSocketLockExclusive( socket );
}
//
// See if socket in process of being closed
//
if ( socket->State == SocketStateClosing ) {
err = WSAENOTSOCK;
goto exit;
}
//
// If the connect attempt failed, notify the helper DLL as
// appropriate. This allows the helper DLL to do a dialin if a
// RAS-style link is appropriate.
//
if ( !NT_SUCCESS(status) && socket->State != SocketStateClosing ) {
err = SockNotifyHelperDll( socket, WSH_NOTIFY_CONNECT_ERROR );
}
} while ( err == WSATRY_AGAIN );
if ( !NT_SUCCESS(status) ) {
err = SockNtStatusToSocketError( status );
goto exit;
}
WS_ASSERT( status != STATUS_TIMEOUT );
//
// Finish up processing the connect.
//
err = PostProcessConnect( socket );
if( err != NO_ERROR ) {
goto exit;
}
//
// If we have a buffer for inbound connect data, retrieve it
// from the socket.
//
if( lpCalleeData != NULL &&
lpCalleeData->buf != NULL &&
lpCalleeData->len > 0 ) {
//
// Get the connect data from the socket.
//
err = SockPassConnectData(
socket,
IOCTL_AFD_GET_CONNECT_DATA,
(PCHAR)lpCalleeData->buf,
(INT)lpCalleeData->len,
(PINT)&lpCalleeData->len
);
if( err == NO_ERROR ) {
if( lpCalleeData->len == 0 ) {
lpCalleeData->buf = NULL;
}
} else {
//
// We'll cheat a bit here and pretend we got no callee data.
//
lpCalleeData->len = 0;
lpCalleeData->buf = NULL;
err = NO_ERROR;
}
}
exit:
IF_DEBUG(CONNECT) {
if ( err != NO_ERROR ) {
WS_PRINT(( "connect on socket %lx (%lx) failed: %ld\n",
Handle, socket, err ));
} else {
WS_PRINT(( "connect on socket %lx (%lx) succeeded.\n",
Handle, socket ));
}
}
//
// Perform cleanup--dereference the socket if it was referenced,
// free allocated resources.
//
if ( socket != NULL ) {
//
// Indicate there there is no longer a connect in progress on the
// socket.
//
socket->ConnectInProgress = FALSE;
//
// If the socket is waiting for FD_WRITE messages, reenable them
// now. They were disabled in WSAAsyncSelect() because we don't
// want to post FD_WRITE messages before FD_CONNECT messages.
//
if ( (socket->AsyncSelectlEvent & FD_WRITE) != 0 ) {
SockReenableAsyncSelectEvent( socket, FD_WRITE );
}
SockReleaseSocketLock( socket );
SockDereferenceSocket( socket );
}
if ( tdiRequest != NULL ) {
FREE_HEAP( tdiRequest );
}
if ( InThread ) {
FREE_HEAP( ConnectContext );
}
//
// Return an error if appropriate.
//
return err;
} // SockDoConnect
BOOLEAN
IsSockaddrEqualToZero (
IN const struct sockaddr * SocketAddress,
IN int SocketAddressLength
)
{
int i;
for ( i = 0; i < SocketAddressLength; i++ ) {
if ( *((PCHAR)SocketAddress + i) != 0 ) {
return FALSE;
}
}
return TRUE;
} // IsSockaddrEqualToZero
INT
UnconnectDatagramSocket (
IN PSOCKET_INFORMATION Socket
)
{
AFD_PARTIAL_DISCONNECT_INFO disconnectInfo;
IO_STATUS_BLOCK ioStatusBlock;
ULONG error;
NTSTATUS status;
//
// *** This routine assumes that it is called with the socket
// referenced and the socket's lock held exclusively!
//
disconnectInfo.Timeout = RtlConvertLongToLargeInteger( -1 );
disconnectInfo.DisconnectMode = AFD_UNCONNECT_DATAGRAM;
IF_DEBUG(CONNECT) {
WS_PRINT(( "unconnecting datagram socket %lx(%lx)\n",
Socket->Handle, Socket ));
}
//
// Send the IOCTL to AFD for processing.
//
status = NtDeviceIoControlFile(
(HANDLE)Socket->Handle,
SockThreadEvent,
NULL, // APC Routine
NULL, // APC Context
&ioStatusBlock,
IOCTL_AFD_PARTIAL_DISCONNECT,
&disconnectInfo,
sizeof(disconnectInfo),
NULL, // OutputBuffer
0L // OutputBufferLength
);
if ( status == STATUS_PENDING ) {
SockReleaseSocketLock( Socket );
SockWaitForSingleObject(
SockThreadEvent,
Socket->Handle,
SOCK_NEVER_CALL_BLOCKING_HOOK,
SOCK_NO_TIMEOUT
);
SockAcquireSocketLockExclusive( Socket );
status = ioStatusBlock.Status;
}
if ( !NT_SUCCESS(status) ) {
error = SockNtStatusToSocketError( status );
} else {
//
// The socket is now unconnected.
//
// !!! do we need to call SockSetHandleContext() for this socket?
error = NO_ERROR;
Socket->State = SocketStateBound;
}
return error;
} // UnconnectDatagramSocket
INT
SockBeginAsyncConnect (
IN PSOCKET_INFORMATION Socket,
IN struct sockaddr * SocketAddress,
IN int SocketAddressLength,
IN LPWSABUF lpCalleeData
)
{
PSOCK_ASYNC_CONNECT_CONTEXT connectContext;
PWINSOCK_CONTEXT_BLOCK contextBlock;
//
// Initialize the async thread if it hasn't already been started.
//
if ( !SockCheckAndInitAsyncThread( ) ) {
return WSAENOBUFS;
}
//
// Allocate connect-specific context we'll pass to the thread we
// create for the async connect.
//
connectContext = ALLOCATE_HEAP( sizeof(*connectContext) + SocketAddressLength );
if ( connectContext == NULL ) {
return WSAENOBUFS;
}
//
// Initialize the context structure.
//
connectContext->SocketHandle = Socket->Handle;
connectContext->SocketAddressLength = SocketAddressLength;
connectContext->CalleeData = lpCalleeData;
RtlCopyMemory(
connectContext + 1,
SocketAddress,
SocketAddressLength
);
//
// Get an async context block.
//
contextBlock = SockAllocateContextBlock( );
if ( contextBlock == NULL ) {
FREE_HEAP( connectContext );
return WSAENOBUFS;
}
//
// Initialize the context block with the information needed for
// an async connect.
//
contextBlock->OpCode = WS_OPCODE_ASYNC_CONNECT;
contextBlock->Overlay.AsyncConnect = connectContext;
//
// Queue the request to the async thread.
//
SockQueueRequestToAsyncThread( contextBlock );
//
// Return indicating that the connect is in progress.
//
return WSAEWOULDBLOCK;
} // SockBeginAsyncConnect
DWORD
SockDoAsyncConnect (
IN PSOCK_ASYNC_CONNECT_CONTEXT ConnectContext
)
{
INT error;
//
// Perform the actual connect.
//
error = SockDoConnect(
ConnectContext->SocketHandle,
(PSOCKADDR)(ConnectContext + 1),
ConnectContext->SocketAddressLength,
TRUE,
ConnectContext,
NULL,
ConnectContext->CalleeData
);
return error;
} // SockDoAsyncConnect
VOID
AsyncConnectCompletionApc (
IN PVOID ApcContext,
IN PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG Reserved
)
{
PSOCK_ASYNC_CONNECT_CONTEXT connectContext;
PSOCKET_INFORMATION socket;
NTSTATUS status;
INT err;
BOOL posted;
UNREFERENCED_PARAMETER( Reserved );
//
// Initialize locals.
//
connectContext = ApcContext;
socket = connectContext->Socket;
status = connectContext->IoStatusBlock.Status;
//
// Acquire the lock to synchronize access to the socket.
//
SockAcquireSocketLockExclusive( socket );
//
// If the connect attempt failed, notify the helper DLL as
// appropriate. This allows the helper DLL to do a dialin if a
// RAS-style link is appropriate.
//
if ( !NT_SUCCESS(status) && socket->State != SocketStateClosing ) {
err = SockNotifyHelperDll( socket, WSH_NOTIFY_CONNECT_ERROR );
//
// If requested by the helper DLL, resubmit the connect request
// to AFD.
//
if ( err == WSATRY_AGAIN ) {
status = NtDeviceIoControlFile(
(HANDLE)socket->Handle,
NULL,
AsyncConnectCompletionApc,
connectContext,
&connectContext->IoStatusBlock,
IOCTL_AFD_CONNECT,
connectContext->TdiRequest,
connectContext->TdiRequestLength,
connectContext->TdiRequest,
connectContext->TdiRequestLength
);
SockReleaseSocketLock( socket );
//
// If the request failed immediately, call the completion
// routine directly to perform cleanup.
//
if ( NT_ERROR(status) ) {
connectContext->IoStatusBlock.Status = status;
AsyncConnectCompletionApc(
connectContext,
&connectContext->IoStatusBlock,
0
);
}
return;
}
}
//
// If the connect failed, bail now.
//
if( socket->State == SocketStateClosing ) {
err = WSAENOTSOCK;
goto exit;
}
if ( !NT_SUCCESS(status) ) {
err = SockNtStatusToSocketError( status );
goto exit;
}
WS_ASSERT( status != STATUS_TIMEOUT );
//
// Finish up processing the connect.
//
err = PostProcessConnect( socket );
exit:
IF_DEBUG(CONNECT) {
if ( err != NO_ERROR ) {
WS_PRINT(( "connect on socket %lx (%lx) failed: %ld\n",
socket->Handle, socket, err ));
} else {
WS_PRINT(( "connect on socket %lx (%lx) succeeded.\n",
socket->Handle, socket ));
}
}
//
// Perform cleanup--dereference the socket if it was referenced,
// free allocated resources.
//
if ( socket != NULL ) {
socket->ConnectOutstanding = FALSE;
socket->LastError = err;
//
// Indicate there there is no longer a connect in progress on the
// socket.
//
socket->ConnectInProgress = FALSE;
//
// If the app has requested FD_CONNECT events, post an
// appropriate FD_CONNECT message.
//
if ( (socket->AsyncSelectlEvent & FD_CONNECT) != 0 ) {
posted = (SockUpcallTable->lpWPUPostMessage)(
socket->AsyncSelecthWnd,
socket->AsyncSelectwMsg,
(WPARAM)socket->Handle,
WSAMAKESELECTREPLY( FD_CONNECT, err )
);
//WS_ASSERT( posted );
IF_DEBUG(POST) {
WS_PRINT(( "POSTED wMsg %lx hWnd %lx socket %lx event %s err %ld\n",
socket->AsyncSelectwMsg,
socket->AsyncSelecthWnd,
socket->Handle, "FD_CONNECT", err ));
}
}
//
// If the socket is waiting for FD_WRITE messages, reenable them
// now. They were disabled in WSAAsyncSelect() because we don't
// want to post FD_WRITE messages before FD_CONNECT messages.
//
if ( (socket->AsyncSelectlEvent & FD_WRITE) != 0 ) {
SockReenableAsyncSelectEvent( socket, FD_WRITE );
}
SockReleaseSocketLock( socket );
SockDereferenceSocket( socket );
}
FREE_HEAP( connectContext->TdiRequest );
FREE_HEAP( connectContext );
return;
} // AsyncConnectCompletionApc
INT
PostProcessConnect (
IN PSOCKET_INFORMATION Socket
)
{
INT err;
//
// Notify the helper DLL that the socket is now connected.
//
err = SockNotifyHelperDll( Socket, WSH_NOTIFY_CONNECT );
//
// If the connect succeeded, remember this fact in the socket info
// structure.
//
if ( err == NO_ERROR ) {
Socket->State = SocketStateConnected;
//
// Remember the changed state of this socket.
//
err = SockSetHandleContext( Socket );
if ( err != NO_ERROR ) {
return err;
}
}
//
// If the application has modified the send or receive buffer sizes,
// then set up the buffer sizes on the socket.
//
err = SockUpdateWindowSizes( Socket, FALSE );
if ( err != NO_ERROR ) {
return err;
}
return NO_ERROR;
} // PostProcessConnect