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.
615 lines
18 KiB
615 lines
18 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
startup.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the startup code for the Winsock 2 to
|
|
Winsock 1.1 Mapper Service Provider.
|
|
|
|
The following routines are exported by this module:
|
|
|
|
WSPSocket()
|
|
WSPCloseSocket()
|
|
|
|
Author:
|
|
|
|
Keith Moore (keithmo) 29-May-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
SOCKET
|
|
WSPAPI
|
|
WSPSocket(
|
|
IN int af,
|
|
IN int type,
|
|
IN int protocol,
|
|
IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
|
|
IN GROUP g,
|
|
IN DWORD dwFlags,
|
|
OUT LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
WSPSocket() causes a socket descriptor and any related resources to be
|
|
allocated. By default, the created socket will not have the overlapped
|
|
attribute. WinSock providers are encouraged to be realized as Windows
|
|
installable file systems, and supply system file handles as socket
|
|
descriptors. These providers must call WPUModifyIFSHandle() prior to
|
|
returning from this routine For non-file-system WinSock providers,
|
|
WPUCreateSocketHandle() must be used to acquire a unique socket descriptor
|
|
from the WinSock 2 DLL prior to returning from this routine.
|
|
|
|
The values for af, type and protocol are those supplied by the application
|
|
in the corresponding API functions socket() or WSASocket(). A service
|
|
provider is free to ignore or pay attention to any or all of these values
|
|
as is appropriate for the particular protocol. However, the provider must
|
|
be willing to accept the value of zero for af and type, since the
|
|
WinSock 2 DLL considers these to be wild card values. Also the value of
|
|
manifest constant FROM_PROTOCOL_INFOW must be accepted for any of af, type
|
|
and protocol. This value indicates that the WinSock 2 application wishes to
|
|
use the corresponding values from the indicated WSAPROTOCOL_INFOW struct:
|
|
|
|
iAddressFamily
|
|
iSocketType
|
|
iProtocol
|
|
|
|
Parameter g is used to indicate the appropriate actions on socket groups:
|
|
|
|
If g is an existing socket group ID, join the new socket to this
|
|
group, provided all the requirements set by this group are met.
|
|
|
|
If g = SG_UNCONSTRAINED_GROUP, create an unconstrained socket
|
|
group and have the new socket be the first member.
|
|
|
|
If g = SG_CONSTRAINED_GROUP, create a constrained socket group and
|
|
have the new socket be the first member.
|
|
|
|
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.
|
|
|
|
The dwFlags parameter may be used to specify the attributes of the socket
|
|
by OR-ing any of the following Flags:
|
|
|
|
WSA_FLAG_OVERLAPPED - This flag causes an overlapped socket to
|
|
be created. Overlapped sockets may utilize WSPSend(),
|
|
WSPSendTo(), WSPRecv(), WSPRecvFrom() and WSPIoctl() for
|
|
overlapped I/O operations, which allows multiple operations
|
|
to be initiated and in progress simultaneously.
|
|
|
|
WSA_FLAG_MULTIPOINT_C_ROOT - Indicates that the socket created
|
|
will be a c_root in a multipoint session. Only allowed if a
|
|
rooted control plane is indicated in the protocol's
|
|
WSAPROTOCOL_INFOW struct.
|
|
|
|
WSA_FLAG_MULTIPOINT_C_LEAF - Indicates that the socket created
|
|
will be a c_leaf in a multicast session. Only allowed if
|
|
XP1_SUPPORT_MULTIPOINT is indicated in the protocol's
|
|
WSAPROTOCOL_INFOW struct.
|
|
|
|
WSA_FLAG_MULTIPOINT_D_ROOT - Indicates that the socket created
|
|
will be a d_root in a multipoint session. Only allowed if a
|
|
rooted data plane is indicated in the protocol's
|
|
WSAPROTOCOL_INFOW struct.
|
|
|
|
WSA_FLAG_MULTIPOINT_D_LEAF - Indicates that the socket created
|
|
will be a d_leaf in a multipoint session. Only allowed if
|
|
XP1_SUPPORT_MULTIPOINT is indicated in the protocol's
|
|
WSAPROTOCOL_INFOW struct.
|
|
|
|
N.B For multipoint sockets, exactly one of WSA_FLAG_MULTIPOINT_C_ROOT
|
|
or WSA_FLAG_MULTIPOINT_C_LEAF must be specified, and exactly one of
|
|
WSA_FLAG_MULTIPOINT_D_ROOT or WSA_FLAG_MULTIPOINT_D_LEAF must be
|
|
specified.
|
|
|
|
Connection-oriented sockets such as SOCK_STREAM provide full-duplex
|
|
connections, and must be in a connected state before any data may be sent
|
|
or received on them. A connection to another socket is created with a
|
|
WSPConnect() call. Once connected, data may be transferred using WSPSend()
|
|
and WSPRecv() calls. When a session has been completed, a WSPCloseSocket()
|
|
must be performed.
|
|
|
|
The communications protocols used to implement a reliable, connection-
|
|
oriented socket ensure that data is not lost or duplicated. If data for
|
|
which the peer protocol has buffer space cannot be successfully
|
|
transmitted within a reasonable length of time, the connection is
|
|
considered broken and subsequent calls will fail with the error code set
|
|
to WSAETIMEDOUT.
|
|
|
|
Connectionless, message-oriented sockets allow sending and receiving of
|
|
datagrams to and from arbitrary peers using WSPSendTo() and WSPRecvFrom().
|
|
If such a socket is WSPConnect()ed to a specific peer, datagrams may be
|
|
sent to that peer using WSPSend() and may be received from (only) this
|
|
peer using WSPRecv().
|
|
|
|
Support for sockets with type SOCK_RAW is not required but service
|
|
providers are encouraged to support raw sockets whenever it makes sense
|
|
to do so.
|
|
|
|
When a special WSAPROTOCOL_INFOW struct (obtained via the
|
|
WSPDuplicateSocket() function and used to create additional descriptors
|
|
for a shared socket) is passed as an input parameter to WSPSocket(),
|
|
the g and dwFlags parameters are ignored.
|
|
|
|
Arguments:
|
|
|
|
af- An address family specification.
|
|
|
|
type - A type specification for the new socket.
|
|
|
|
protocol - A particular protocol to be used with the socket which is
|
|
specific to the indicated address family.
|
|
|
|
lpProtocolInfo - A pointer to a WSAPROTOCOL_INFOW struct that defines
|
|
the characteristics of the socket to be created.
|
|
|
|
g - The identifier of the socket group which the new socket is to join.
|
|
|
|
dwFlags - The socket attribute specification.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
If no error occurs, WSPSocket() returns a descriptor referencing the
|
|
new socket. Otherwise, a value of INVALID_SOCKET is returned, and a
|
|
specific error code is available in lpErrno.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSOCKET_INFORMATION socketInfo;
|
|
PHOOKER_INFORMATION hookerInfo;
|
|
SOCKET ws1Handle;
|
|
SOCKET ws2Handle;
|
|
INT err;
|
|
|
|
SOCK_ENTER( "WSPSocket", (PVOID)af, (PVOID)type, (PVOID)protocol, lpProtocolInfo );
|
|
|
|
SOCK_ASSERT( lpProtocolInfo != NULL );
|
|
SOCK_ASSERT( lpErrno != NULL );
|
|
|
|
err = SockEnterApi( TRUE, FALSE );
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
SOCK_EXIT( "WSPSocket", INVALID_SOCKET, TRUE );
|
|
*lpErrno = err;
|
|
return INVALID_SOCKET;
|
|
|
|
}
|
|
|
|
//
|
|
// Setup locals so we know how to cleanup on exit.
|
|
//
|
|
|
|
socketInfo = NULL;
|
|
hookerInfo = NULL;
|
|
ws1Handle = INVALID_SOCKET;
|
|
ws2Handle = INVALID_SOCKET;
|
|
|
|
//
|
|
// Snag the socket attributes from the protocol structure.
|
|
//
|
|
|
|
if( af == 0 || af == FROM_PROTOCOL_INFO ) {
|
|
|
|
af = lpProtocolInfo->iAddressFamily;
|
|
|
|
}
|
|
|
|
if( type == 0 || type == FROM_PROTOCOL_INFO ) {
|
|
|
|
type = lpProtocolInfo->iSocketType;
|
|
|
|
}
|
|
|
|
if( protocol == 0 || protocol == FROM_PROTOCOL_INFO ) {
|
|
|
|
protocol = lpProtocolInfo->iProtocol;
|
|
|
|
}
|
|
|
|
//
|
|
// Grab the global lock. We must hold this until the socket is
|
|
// created an put on the global list.
|
|
//
|
|
|
|
//
|
|
// Find a hooker for this socket. If this socket is successfully
|
|
// created, the the hooker won't be dereferenced until the socket
|
|
// is closed.
|
|
//
|
|
|
|
hookerInfo = SockFindAndReferenceHooker( &lpProtocolInfo->ProviderId );
|
|
|
|
if( hookerInfo == NULL ) {
|
|
|
|
//
|
|
// WSAEINPROGRESS is a distinguished error code that will cause
|
|
// WS2_32.DLL to continue its protocol catalog enumeration
|
|
// and try another provider that supports this socket triple.
|
|
//
|
|
|
|
err = WSAEINPROGRESS;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Create the WS1 socket.
|
|
//
|
|
|
|
SockPreApiCallout();
|
|
|
|
ws1Handle = hookerInfo->socket( af, type, protocol );
|
|
|
|
if( ws1Handle == INVALID_SOCKET ) {
|
|
|
|
err = hookerInfo->WSAGetLastError();
|
|
SOCK_ASSERT( err != NO_ERROR );
|
|
SockPostApiCallout();
|
|
goto exit;
|
|
|
|
}
|
|
|
|
SockPostApiCallout();
|
|
|
|
//
|
|
// Create our internal structure and the "visible" WS2 socket.
|
|
//
|
|
|
|
socketInfo = SockCreateSocket(
|
|
hookerInfo,
|
|
af,
|
|
type,
|
|
protocol,
|
|
dwFlags,
|
|
lpProtocolInfo->dwCatalogEntryId,
|
|
ws1Handle,
|
|
&err
|
|
);
|
|
|
|
if( socketInfo == NULL ) {
|
|
|
|
SOCK_ASSERT( err != NO_ERROR );
|
|
goto exit;
|
|
|
|
}
|
|
|
|
SOCK_ASSERT( err == NO_ERROR );
|
|
SOCK_ASSERT( ws1Handle == socketInfo->WS1Handle );
|
|
|
|
ws2Handle = socketInfo->WS2Handle;
|
|
SOCK_ASSERT( ws2Handle != INVALID_SOCKET );
|
|
|
|
exit:
|
|
|
|
if( err == NO_ERROR ) {
|
|
|
|
IF_DEBUG(SOCKET) {
|
|
|
|
SOCK_PRINT((
|
|
"Opened socket %lx:%lx (%08lx) from hooker %08lx\n",
|
|
socketInfo->WS2Handle,
|
|
ws1Handle,
|
|
socketInfo,
|
|
hookerInfo
|
|
));
|
|
|
|
}
|
|
|
|
SockDereferenceSocket( socketInfo );
|
|
|
|
} else {
|
|
|
|
if( ws1Handle != INVALID_SOCKET ) {
|
|
|
|
SockPreApiCallout();
|
|
hookerInfo->closesocket( ws1Handle );
|
|
SockPostApiCallout();
|
|
|
|
}
|
|
|
|
*lpErrno = err;
|
|
ws2Handle = INVALID_SOCKET;
|
|
|
|
}
|
|
|
|
if( hookerInfo != NULL ) {
|
|
|
|
SockDereferenceHooker( hookerInfo );
|
|
|
|
}
|
|
|
|
SOCK_EXIT( "WSPSocket", ws2Handle, (BOOL)( ws2Handle == INVALID_SOCKET ) );
|
|
return ws2Handle;
|
|
|
|
} // WSPSocket
|
|
|
|
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPCloseSocket(
|
|
IN SOCKET s,
|
|
OUT LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine closes a socket. More precisely, it releases the socket
|
|
descriptor s, so that further references to s should fail with the error
|
|
WSAENOTSOCK. If this is the last reference to an underlying socket, the
|
|
associated naming information and queued data are discarded. Any blocking,
|
|
asynchronous or overlapped calls pending on the socket (issued by any
|
|
thread in this process) are canceled without posting any notification
|
|
messages, signaling any event objects or invoking any completion routines.
|
|
In this case, the pending overlapped operations fail with the error status
|
|
WSA_OPERATION_ABORTED. FD_CLOSE will not be posted after WSPCloseSocket()
|
|
is called.
|
|
|
|
WSPClosesocket() behavior is summarized as follows:
|
|
|
|
If SO_DONTLINGER enabled (the default setting) WSPCloseSocket()
|
|
returns immediately - connection is gracefully closed "in
|
|
the background".
|
|
|
|
If SO_LINGER enabled with a zero timeout, WSPCloseSocket()
|
|
returns immediately - connection is reset/aborted.
|
|
|
|
If SO_LINGER enabled with non-zero timeout:
|
|
|
|
- With a blocking socket, WSPCloseSocket() blocks
|
|
until all data sent or timeout expires.
|
|
|
|
- With a non-blocking socket, WSPCloseSocket()
|
|
returns immediately indicating failure.
|
|
|
|
The semantics of WSPCloseSocket() are affected by the socket options
|
|
SO_LINGER and SO_DONTLINGER as follows:
|
|
|
|
Option Interval Type of close Wait for close?
|
|
~~~~~~ ~~~~~~~~ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
|
|
SO_DONTLINGER Don't care Graceful No
|
|
SO_LINGER Zero Hard No
|
|
SO_LINGER Non-zero Graceful Yes
|
|
|
|
If SO_LINGER is set (i.e. the l_onoff field of the linger structure is
|
|
non-zero) and the timeout interval, l_linger, is zero, WSPClosesocket()
|
|
is not blocked even if queued data has not yet been sent or acknowledged.
|
|
This is called a "hard" or "abortive" close, because the socket's virtual
|
|
circuit is reset immediately, and any unsent data is lost. Any WSPRecv()
|
|
call on the remote side of the circuit will fail with WSAECONNRESET.
|
|
|
|
If SO_LINGER is set with a non-zero timeout interval on a blocking socket,
|
|
the WSPClosesocket() call blocks on a blocking socket until the remaining
|
|
data has been sent or until the timeout expires. This is called a graceful
|
|
disconnect. If the timeout expires before all data has been sent, the
|
|
service provider should abort the connection before WSPClosesocket()
|
|
returns.
|
|
|
|
Enabling SO_LINGER with a non-zero timeout interval on a non-blocking
|
|
socket is not recommended. In this case, the call to WSPClosesocket() will
|
|
fail with an error of WSAEWOULDBLOCK if the close operation cannot be
|
|
completed immediately. If WSPClosesocket() fails with WSAEWOULDBLOCK the
|
|
socket handle is still valid, and a disconnect is not initiated. The
|
|
WinSock SPI client must call WSPClosesocket() again to close the socket,
|
|
although WSPClosesocket() may continue to fail unless the WinSock SPI
|
|
client disables SO_DONTLINGER, enables SO_LINGER with a zero timeout, or
|
|
calls WSPShutdown() to initiate closure.
|
|
|
|
If SO_DONTLINGER is set on a stream socket (i.e. the l_onoff field of the
|
|
linger structure is zero), the WSPClosesocket() call will return
|
|
immediately. However, any data queued for transmission will be sent if
|
|
possible before the underlying socket is closed. This is called a graceful
|
|
disconnect and is the default behavior. Note that in this case the WinSock
|
|
provider is allowed to retain any resources associated with the socket
|
|
until such time as the graceful disconnect has completed or the provider
|
|
aborts the connection due to an inability to complete the operation in a
|
|
provider-determined amount of time. This may affect Winsock clients which
|
|
expect to use all available sockets.
|
|
|
|
Arguments:
|
|
|
|
s - A descriptor identifying a socket.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
If no error occurs, WSPCloseSocket() returns 0. Otherwise, a value of
|
|
SOCKET_ERROR is returned, and a specific error code is available
|
|
in lpErrno.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSOCKET_INFORMATION socketInfo;
|
|
INT err;
|
|
INT dummy;
|
|
INT result;
|
|
SOCKET_STATE previousState;
|
|
SOCKET ws1Handle;
|
|
SOCKET ws2Handle;
|
|
|
|
SOCK_ENTER( "WSPCloseSocket", (PVOID)s, NULL, NULL, NULL );
|
|
|
|
SOCK_ASSERT( lpErrno != NULL );
|
|
|
|
err = SockEnterApi( FALSE, FALSE );
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
SOCK_EXIT( "WSPCloseSocket", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// Attempt to find the socket in our lookup table.
|
|
//
|
|
|
|
socketInfo = SockFindAndReferenceWS2Socket( s );
|
|
|
|
if( socketInfo == NULL || socketInfo->State == SocketStateClosing ) {
|
|
|
|
IF_DEBUG(SOCKET) {
|
|
|
|
SOCK_PRINT((
|
|
"WSPCloseSocket failed on %s handle: %lx\n",
|
|
socketInfo == NULL ? "unknown" : "closed",
|
|
s
|
|
));
|
|
|
|
}
|
|
|
|
if( socketInfo != NULL ) {
|
|
|
|
SockDereferenceSocket( socketInfo );
|
|
|
|
}
|
|
|
|
SOCK_EXIT( "WSPCloseSocket", SOCKET_ERROR, TRUE );
|
|
*lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// Set the state of the socket to closing so that future closes will
|
|
// fail. Capture the current state first, in case the close fails.
|
|
//
|
|
|
|
previousState = socketInfo->State;
|
|
socketInfo->State = SocketStateClosing;
|
|
|
|
//
|
|
// Snag the handles from the structure.
|
|
//
|
|
|
|
ws1Handle = socketInfo->WS1Handle;
|
|
SOCK_ASSERT( ws1Handle != INVALID_SOCKET );
|
|
|
|
ws2Handle = socketInfo->WS2Handle;
|
|
SOCK_ASSERT( ws2Handle != INVALID_SOCKET );
|
|
|
|
//
|
|
// Let the hooker do its thang.
|
|
//
|
|
|
|
SockPreApiCallout();
|
|
|
|
result = socketInfo->Hooker->closesocket( ws1Handle );
|
|
|
|
if( result == SOCKET_ERROR ) {
|
|
err = socketInfo->Hooker->WSAGetLastError();
|
|
SOCK_ASSERT( err != NO_ERROR );
|
|
SockPostApiCallout();
|
|
socketInfo->State = previousState;
|
|
goto exit;
|
|
}
|
|
|
|
SockPostApiCallout();
|
|
|
|
//
|
|
// The WS1 handle is closed, now close the WS2 handle. If this fails,
|
|
// there's not much we can do as we've already closed the hooker's
|
|
// handle.
|
|
//
|
|
|
|
result = SockUpcallTable.lpWPUCloseSocketHandle(
|
|
ws2Handle,
|
|
&dummy
|
|
);
|
|
|
|
if( result == SOCKET_ERROR ) {
|
|
|
|
SOCK_PRINT((
|
|
"WSPCloseSocket: WPUCloseSocketHandle( %lx ) failed, error %d\n",
|
|
ws2Handle,
|
|
dummy
|
|
));
|
|
|
|
}
|
|
|
|
//
|
|
// Manually dereference the socket. The dereference accounts for
|
|
// the "active" reference of the socket and will cause the socket to
|
|
// be deleted when the actual reference count goes to zero.
|
|
//
|
|
|
|
SOCK_ASSERT( socketInfo->ReferenceCount >= 2 );
|
|
|
|
socketInfo->ReferenceCount--;
|
|
|
|
exit:
|
|
|
|
SockDereferenceSocket( socketInfo );
|
|
|
|
if( err == NO_ERROR ) {
|
|
|
|
IF_DEBUG(SOCKET) {
|
|
|
|
SOCK_PRINT((
|
|
"WSPCloseSocket: closed %lx:%lx (%08lx)\n",
|
|
ws2Handle,
|
|
ws1Handle,
|
|
socketInfo
|
|
));
|
|
|
|
}
|
|
|
|
SOCK_EXIT( "WSPCloseSocket", NO_ERROR, FALSE );
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
IF_DEBUG(SOCKET) {
|
|
|
|
SOCK_PRINT((
|
|
"WSPCloseSocket: %lx:%lx (%08lx) failed, error %d\n",
|
|
ws2Handle,
|
|
ws1Handle,
|
|
socketInfo,
|
|
err
|
|
));
|
|
|
|
}
|
|
|
|
SOCK_EXIT( "WSPCloseSocket", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
} // WSPCloseSocket
|
|
|