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.
572 lines
22 KiB
572 lines
22 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
wspmisc.c
|
|
|
|
Abstract:
|
|
|
|
This module contains miscellaneous routines for the Winsock 2 to
|
|
Winsock 1.1 Mapper Service Provider.
|
|
|
|
The following routines are exported by this module:
|
|
|
|
WSPCancelBlockingCall()
|
|
WSPDuplicateSocket()
|
|
WSPGetOverlappedResult()
|
|
WSPGetQOSByName()
|
|
WSPJoinLeaf()
|
|
|
|
Author:
|
|
|
|
Keith Moore (keithmo) 29-May-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPCancelBlockingCall(
|
|
OUT LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cancels any outstanding blocking operation for this thread.
|
|
It is normally used in two situations:
|
|
|
|
1. A WinSock SPI client is processing a message which has been
|
|
received while a service provider is implementing pseudo
|
|
blocking. In this case, WSAIsBlocking() will be true.
|
|
|
|
2. A blocking call is in progress, and the WinSock service
|
|
provider has called back to the WinSock SPI client's "blocking
|
|
hook" function (via the callback function retrieved from
|
|
WPUQueryBlockingCallback()), which in turn is invoking this
|
|
function. Such a situation might arise, for instance, in
|
|
implementing a Cancel option for an operation which require an
|
|
extended time to complete.
|
|
|
|
In each case, the original blocking call will terminate as soon as
|
|
possible with the error WSAEINTR. (In (1), the termination will not take
|
|
place until Windows message scheduling has caused control to revert back
|
|
to the pseudo blocking routine in WinSock. In (2), the blocking call
|
|
will be terminated as soon as the blocking hook function completes.)
|
|
|
|
In the case of a blocking WSPConnect() operation, WinSock will terminate
|
|
the blocking call as soon as possible, but it may not be possible for
|
|
the socket resources to be released until the connection has completed
|
|
(and then been reset) or timed out. This is likely to be noticeable only
|
|
if the WinSock SPI client immediately tries to open a new socket (if no
|
|
sockets are available), or to WSPConnect() to the same peer.
|
|
|
|
Canceling an WSPAccept() or a WSPSelect() call does not adversely impact
|
|
the sockets passed to these calls. Only the particular call fails; any
|
|
operation that was legal before the cancel is legal after the cancel,
|
|
and the state of the socket is not affected in any way.
|
|
|
|
Canceling any operation other than WSPAccept() and WSPSelect() can leave
|
|
the socket in an indeterminate state. If a WinSock SPI client cancels a
|
|
blocking operation on a socket, the only operation that the WinSock SPI
|
|
client can depend on being able to perform on the socket is a call to
|
|
WSPCloseSocket(), although other operations may work on some WinSock
|
|
service providers. If a WinSock SPI client desires maximum portability,
|
|
it must be careful not to depend on performing operations after a cancel.
|
|
A WinSock SPI client may reset the connection by setting the timeout on
|
|
SO_LINGER to 0 and calling WSPCloseSocket().
|
|
|
|
If a cancel operation compromised the integrity of a SOCK_STREAM's data
|
|
stream in any way, the WinSock provider will reset the connection and
|
|
fail all future operations other than WSPCloseSocket() with
|
|
WSAECONNABORTED.
|
|
|
|
Note it is acceptable for WSPCancelBlockingCall() to return successfully
|
|
if the blocking network operation completes prior to being canceled. In
|
|
this case, the blocking operation will return successfully as if
|
|
WSPCancelBlockingCall() had never been called. The only way for the
|
|
WinSock SPI client to know with certainty that an operation was actually
|
|
canceled is to check for a return code of WSAEINTR from the blocking call.
|
|
|
|
Arguments:
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
The value returned by WSPCancelBlockingCall() is 0 if the operation was
|
|
successfully canceled. Otherwise the value SOCKET_ERROR is returned,
|
|
and a specific error code is available in lpErrno.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
INT err;
|
|
INT result;
|
|
PSOCK_TLS_DATA tlsData;
|
|
|
|
SOCK_ENTER( "WSPCancelBlockingCall", lpErrno, NULL, NULL, NULL );
|
|
|
|
SOCK_ASSERT( lpErrno != NULL );
|
|
|
|
err = SockEnterApi( TRUE, TRUE );
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
SOCK_EXIT( "WSPCancelBlockingCall", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
tlsData = SOCK_GET_THREAD_DATA();
|
|
SOCK_ASSERT( tlsData != NULL );
|
|
|
|
//
|
|
// This call is only valid when we are in a blocking call.
|
|
//
|
|
|
|
if( !tlsData->IsBlocking ) {
|
|
|
|
SOCK_EXIT( "WSPCancelBlockingCall", SOCKET_ERROR, TRUE );
|
|
*lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// The IO should not have been cancelled yet.
|
|
//
|
|
|
|
SOCK_ASSERT( tlsData->ReentrancyFlag );
|
|
SOCK_ASSERT( !tlsData->IoCancelled );
|
|
SOCK_ASSERT( tlsData->BlockingSocketInfo != NULL );
|
|
|
|
//
|
|
// Cancel it.
|
|
//
|
|
|
|
result = tlsData->BlockingSocketInfo->Hooker->WSACancelBlockingCall();
|
|
|
|
if( result == SOCKET_ERROR ) {
|
|
|
|
*lpErrno = tlsData->BlockingSocketInfo->Hooker->WSAGetLastError();
|
|
SOCK_EXIT( "WSPCancelBlockingCall", SOCKET_ERROR, TRUE );
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// Remember that we've cancelled it.
|
|
//
|
|
|
|
tlsData->IoCancelled = TRUE;
|
|
|
|
SOCK_EXIT( "WSPCancelBlockingCall", NO_ERROR, FALSE );
|
|
return NO_ERROR;
|
|
|
|
} // WSPCancelBlockingCall
|
|
|
|
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPDuplicateSocket(
|
|
IN SOCKET s,
|
|
IN DWORD dwProcessId,
|
|
OUT LPWSAPROTOCOL_INFOW lpProtocolInfo,
|
|
OUT LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
A source process calls WSPDuplicateSocket() to obtain a special
|
|
WSAPROTOCOL_INFOW structure. It uses some interprocess communications
|
|
(IPC) mechanism to pass the contents of this structure to a target
|
|
process, which in turn uses it in a call to WSPSocket() to obtain a
|
|
descriptor for the duplicated socket. Note that the special
|
|
WSAPROTOCOL_INFOW structure may only be used once by the target process.
|
|
|
|
It is the service provider's responsibility to perform whatever operations
|
|
are needed in the source process context and to create a WSAPROTOCOL_INFOW
|
|
structure that will be recognized when it subsequently appears as a
|
|
parameter to WSPSocket() in the target processes' context. The provider
|
|
must then return a socket descriptor that references a common underlying
|
|
socket. The dwProviderReserved field of the WSAPROTOCOL_INFOW struct is
|
|
available for the service provider's use, and may be used to store any
|
|
useful context information, including a duplicated handle.
|
|
|
|
When new socket descriptors are allocated IFS providers must call
|
|
WPUModifyIFSHandle() and non-IFS providers must call
|
|
WPUCreateSocketHandle().
|
|
|
|
The descriptors that reference a shared socket may be used independently
|
|
as far as I/O is concerned. However, the WinSock interface does not
|
|
implement any type of access control, so it is up to the processes
|
|
involved to coordinate their operations on a shared socket. A typical use
|
|
for shared sockets is to have one process that is responsible for
|
|
creating sockets and establishing connections, hand off sockets to
|
|
other processes which are responsible for information exchange.
|
|
|
|
Since what is duplicated are the socket descriptors and not the underlying
|
|
socket, all of the state associated with a socket is held in common across
|
|
all the descriptors. For example a WSPSetSockOpt() operation performed
|
|
using one descriptor is subsequently visible using a WSPGetSockOpt() from
|
|
any or all descriptors. A process may call WSPClosesocket() on a
|
|
duplicated socket and the descriptor will become deallocated. The
|
|
underlying socket, however, will remain open until WSPClosesocket() is
|
|
called by the last remaining descriptor.
|
|
|
|
Notification on shared sockets is subject to the usual constraints of
|
|
WSPAsyncSelect() and WSPEventSelect(). Issuing either of these calls
|
|
using any of the shared descriptors cancels any previous event
|
|
registration for the socket, regardless of which descriptor was used to
|
|
make that registration. Thus, for example, a shared socket cannot deliver
|
|
FD_READ events to process A and FD_WRITE events to process B. For
|
|
situations when such tight coordination is required, it is suggested that
|
|
developers use threads instead of separate processes.
|
|
|
|
Arguments:
|
|
|
|
s - Specifies the local socket descriptor.
|
|
|
|
dwProcessId - Specifies the ID of the target process for which the
|
|
shared socket will be used.
|
|
|
|
lpProtocolInfo - A pointer to a buffer allocated by the client that
|
|
is large enough to contain a WSAPROTOCOL_INFOW struct. The service
|
|
provider copies the protocol info struct contents to this buffer.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
If no error occurs, WSPDuplicateSocket() returns zero. Otherwise, the
|
|
value of SOCKET_ERROR is returned, and a specific error number is
|
|
available in lpErrno.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
*lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
|
|
} // WSPDuplicateSocket
|
|
|
|
|
|
|
|
BOOL
|
|
WSPAPI
|
|
WSPGetOverlappedResult(
|
|
IN SOCKET s,
|
|
IN LPWSAOVERLAPPED lpOverlapped,
|
|
OUT LPDWORD lpcbTransfer,
|
|
IN BOOL fWait,
|
|
OUT LPDWORD lpdwFlags,
|
|
OUT LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The results reported by the WSPGetOverlappedResult() function are those
|
|
of the specified socket's last overlapped operation to which the specified
|
|
WSAOVERLAPPED structure was provided, and for which the operation's results
|
|
were pending. A pending operation is indicated when the function that
|
|
started the operation returns FALSE, and the lpErrno is WSA_IO_PENDING.
|
|
When an I/O operation is pending, the function that started the operation
|
|
resets the hEvent member of the WSAOVERLAPPED structure to the nonsignaled
|
|
state. Then when the pending operation has been completed, the system sets
|
|
the event object to the signaled state.
|
|
|
|
If the fWait parameter is TRUE, WSPGetOverlappedResult() determines whether
|
|
the pending operation has been completed by blocking and waiting for the
|
|
event object to be in the signaled state.
|
|
|
|
Arguments:
|
|
|
|
s - Identifies the socket. This is the same socket that was specified
|
|
when the overlapped operation was started by a call to WSPRecv(),
|
|
WSPRecvFrom(), WSPSend(), WSPSendTo(), or WSPIoctl().
|
|
|
|
lpOverlapped - Points to a WSAOVERLAPPED structure that was specified
|
|
when the overlapped operation was started.
|
|
|
|
lpcbTransfer - Points to a 32-bit variable that receives the number of
|
|
bytes that were actually transferred by a send or receive operation,
|
|
or by WSPIoctl().
|
|
|
|
fWait - Specifies whether the function should wait for the pending
|
|
overlapped operation to complete. If TRUE, the function does not
|
|
return until the operation has been completed. If FALSE and the
|
|
operation is still pending, the function returns FALSE and lpErrno
|
|
is WSA_IO_INCOMPLETE.
|
|
|
|
lpdwFlags - Points to a 32-bit variable that will receive one or more
|
|
flags that supplement the completion status. If the overlapped
|
|
operation was initiated via WSPRecv() or WSPRecvFrom(), this
|
|
parameter will contain the results value for lpFlags parameter.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
If WSPGetOverlappedResult() succeeds, the return value is TRUE. This
|
|
means that the overlapped operation has completed successfully
|
|
and that the value pointed to by lpcbTransfer has been updated.
|
|
If WSPGetOverlappedResult() returns FALSE, this means that either
|
|
the overlapped operation has not completed or the overlapped
|
|
operation completed but with errors, or that completion status
|
|
could not be determined due to errors in one or more parameters
|
|
to WSPGetOverlappedResult(). On failure, the value pointed to by
|
|
lpcbTransfer will not be updated. lpErrno indicates the cause of
|
|
the failure (either of WSPGetOverlappedResult() or of the
|
|
associated overlapped operation).
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
*lpErrno = WSAEINVAL;
|
|
return FALSE;
|
|
|
|
} // WSPGetOverlappedResult
|
|
|
|
|
|
|
|
BOOL
|
|
WSPAPI
|
|
WSPGetQOSByName(
|
|
IN SOCKET s,
|
|
IN LPWSABUF lpQOSName,
|
|
OUT LPQOS lpQOS,
|
|
OUT LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clients may use this routine to initialize a QOS structure to a set of
|
|
known values appropriate for a particular service class or media type.
|
|
These values are stored in a template which is referenced by a well-known
|
|
name
|
|
|
|
Arguments:
|
|
|
|
s - A descriptor identifying a socket.
|
|
|
|
lpQOSName - Specifies the QOS template name.
|
|
|
|
lpQOS - A pointer to the QOS structure to be filled.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
If no error occurs, WSPGetQOSByName() returns TRUE. Otherwise, a value of
|
|
FALSE is returned, and a specific error code is available in
|
|
lpErrno.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
*lpErrno = WSAEINVAL;
|
|
return FALSE;
|
|
|
|
} // WSPGetQOSByName
|
|
|
|
|
|
|
|
SOCKET
|
|
WSPAPI
|
|
WSPJoinLeaf(
|
|
IN SOCKET s,
|
|
IN const struct sockaddr FAR * name,
|
|
IN int namelen,
|
|
IN LPWSABUF lpCallerData,
|
|
OUT LPWSABUF lpCalleeData,
|
|
IN LPQOS lpSQOS,
|
|
IN LPQOS lpGQOS,
|
|
IN DWORD dwFlags,
|
|
OUT LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to join a leaf node to a multipoint session, and to
|
|
perform a number of other ancillary operations that occur at session join
|
|
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.
|
|
|
|
WSPJoinLeaf() has the same parameters and semantics as WSPConnect() except
|
|
that it returns a socket descriptor (as in WSPAccept()), and it has an
|
|
additional dwFlags parameter. Only multipoint sockets created using
|
|
WSPSocket() with appropriate multipoint flags set may be used for input
|
|
parameter s in this routine. If the socket is in the non-blocking mode,
|
|
the returned socket descriptor will not be useable until after a
|
|
corresponding FD_CONNECT indication has been received, except that
|
|
closesocket() may be invoked on this socket descriptor to cancel a pending
|
|
join operation. A root node in a multipoint session may call WSPJoinLeaf()
|
|
one or more times in order to add a number of leaf nodes, however at most
|
|
one multipoint connection request may be outstanding at a time. Refer to
|
|
section 3.14. Protocol-Independent Multicast and Multipoint for additional
|
|
information.
|
|
|
|
The socket descriptor returned by WSPJoinLeaf() is different depending on
|
|
whether the input socket descriptor, s, is a c_root or a c_leaf. When used
|
|
with a c_root socket, the name parameter designates a particular leaf node
|
|
to be added and the returned socket descriptor is a c_leaf socket
|
|
corresponding to the newly added leaf node. The newly created socket has
|
|
the same properties as s including asynchronous events registered with
|
|
WSPAsyncSelect() or with WSPEventSelect(), but not including the c_root
|
|
socket's group ID, if any. It is not intended to be used for exchange of
|
|
multipoint data, but rather is used to receive network event indications
|
|
(e.g. FD_CLOSE) for the connection that exists to the particular c_leaf.
|
|
Some multipoint implementations may also allow this socket to be used for
|
|
"side chats" between the root and an individual leaf node. An FD_CLOSE
|
|
indication will be received for this socket if the corresponding leaf node
|
|
calls WSPCloseSocket() to drop out of the multipoint session.
|
|
Symmetrically, invoking WSPCloseSocket() on the c_leaf socket returned from
|
|
WSPJoinLeaf() will cause the socket in the corresponding leaf node to get
|
|
FD_CLOSE notification.
|
|
|
|
When WSPJoinLeaf() is invoked with a c_leaf socket, the name parameter
|
|
contains the address of the root node (for a rooted control scheme) or an
|
|
existing multipoint session (non-rooted control scheme), and the returned
|
|
socket descriptor is the same as the input socket descriptor. In other
|
|
words, a new socket descriptor is not allocated. In a rooted control
|
|
scheme, the root application would put its c_root socket in the listening
|
|
mode by calling WSPListen(). The standard FD_ACCEPT notification will be
|
|
delivered when the leaf node requests to join itself to the multipoint
|
|
session. The root application uses the usual WSPAccept() functions to
|
|
admit the new leaf node. The value returned from WSPAccept() is also a
|
|
c_leaf socket descriptor just like those returned from WSPJoinLeaf(). To
|
|
accommodate multipoint schemes that allow both root-initiated and leaf-
|
|
initiated joins, it is acceptable for a c_root socket that is already in
|
|
listening mode to be used as an input to WSPJoinLeaf().
|
|
|
|
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 multipoint session join request. If lpCallerData
|
|
is NULL, no user data will be passed to the peer. The lpCalleeData is a
|
|
result parameter which will contain any user data passed back from the peer
|
|
as part of the multipoint session 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 multipoint join operation is complete. For blocking
|
|
sockets, this will be when the WSPJoinLeaf() 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 multipoint session establishment 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.
|
|
|
|
The dwFlags parameter is used to indicate whether the socket will be acting
|
|
only as a sender (JL_SENDER_ONLY), only as a receiver (JL_RECEIVER_ONLY),
|
|
or both (JL_BOTH).
|
|
|
|
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 a multipoint socket.
|
|
|
|
name - The name of the peer to which the socket is to be joined.
|
|
|
|
namelen - The length of the name.
|
|
|
|
lpCallerData - A pointer to the user data that is to be transferred to
|
|
the peer during multipoint session establishment.
|
|
|
|
lpCalleeData - A pointer to the user data that is to be transferred back
|
|
from the peer during multipoint session 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).
|
|
|
|
dwFlags - Flags to indicate that the socket is acting as a sender,
|
|
receiver, or both.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
If no error occurs, WSPJoinLeaf() returns a value of type SOCKET which is
|
|
a descriptor for the newly created multipoint socket. Otherwise, a
|
|
value of INVALID_SOCKET is returned, and a specific error code is
|
|
available in lpErrno.
|
|
|
|
On a blocking socket, the return value indicates success or failure of
|
|
the join operation.
|
|
|
|
With a non-blocking socket, successful initiation of a join operation
|
|
is indicated by a return value of a valid socket descriptor.
|
|
Subsequently, an FD_CONNECT indication is given when the join
|
|
operation completes, either successfully or otherwise.
|
|
|
|
Also, until the multipoint session join attempt completes all
|
|
subsequent calls to WSPJoinLeaf() on the same socket will fail with
|
|
the error code WSAEALREADY.
|
|
|
|
If the return error code indicates the multipoint session join attempt
|
|
failed (i.e. WSAECONNREFUSED, WSAENETUNREACH, WSAETIMEDOUT) the
|
|
WinSock SPI client may call WSPJoinLeaf() again for the same socket.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
*lpErrno = WSAEINVAL;
|
|
return INVALID_SOCKET;
|
|
|
|
} // WSPJoinLeaf
|
|
|