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.
1630 lines
48 KiB
1630 lines
48 KiB
/*++
|
|
|
|
Copyright (c) 1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
stubs.c
|
|
|
|
Abstract:
|
|
|
|
This module contains select routines for the Winsock 2 to
|
|
Winsock 1.1 Mapper Service Provider.
|
|
|
|
The following routines are exported by this module:
|
|
|
|
WSPAsyncSelect()
|
|
WSPEnumNetworkEvents()
|
|
WSPEventSelect()
|
|
WSPSelect()
|
|
SockDestroyAsyncWindow()
|
|
|
|
Author:
|
|
|
|
Keith Moore (keithmo) 29-May-1996
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
//
|
|
// The message we'll actually pass down to the WS1 DLL.
|
|
//
|
|
|
|
#define WM_MY_ASYNC_SELECT_MESSAGE 0x1234
|
|
|
|
|
|
//
|
|
// Window managment globals.
|
|
//
|
|
|
|
CHAR SockAsyncWindowClassName[] = "WinSock2MapperAsyncHelperWindow";
|
|
HWND SockAsyncWindowHandle;
|
|
|
|
|
|
//
|
|
// Forward procedure references.
|
|
//
|
|
|
|
LRESULT
|
|
CALLBACK
|
|
SockAsyncWindowProc(
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
VOID
|
|
SockProcessAsyncSelectMessage(
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
);
|
|
|
|
BOOL
|
|
SockCreateAsyncWindow(
|
|
VOID
|
|
);
|
|
|
|
INT
|
|
SockMapWS2FdSetToWS1(
|
|
PFD_SET WS2FdSet,
|
|
PFD_SET * WS1FdSet,
|
|
PULONG * TargetBuffer,
|
|
PSOCKET_INFORMATION * SocketInfo
|
|
);
|
|
|
|
INT
|
|
SockMapWS1FdSetToWS2(
|
|
PFD_SET WS1FdSet,
|
|
PFD_SET WS2FdSet
|
|
);
|
|
|
|
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPAsyncSelect(
|
|
IN SOCKET s,
|
|
IN HWND hWnd,
|
|
IN unsigned int wMsg,
|
|
IN long lEvent,
|
|
OUT LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to request that the service provider send a Window
|
|
message to the client's window hWnd whenever it detects any of the
|
|
network events specified by the lEvent parameter. The service provider
|
|
should use the WPUPostMessage() function to post the message. The message
|
|
to be sent is specified by the wMsg parameter. The socket for which
|
|
notification is required is identified by s.
|
|
|
|
This routine automatically sets socket s to non-blocking mode, regardless
|
|
of the value of lEvent. See WSPIoctl() about how to set the socket back to
|
|
blocking mode.
|
|
|
|
The lEvent parameter is constructed by or'ing any of the values specified
|
|
in the following list.
|
|
|
|
Value Meaning
|
|
~~~~~ ~~~~~~~
|
|
FD_READ Issue notification of readiness for reading.
|
|
FD_WRITE Issue notification of readiness for writing.
|
|
FD_OOB Issue notification of the arrival of out-of-band data.
|
|
FD_ACCEPT Issue notification of incoming connections.
|
|
FD_CONNECT Issue notification of completed connection.
|
|
FD_CLOSE Issue notification of socket closure.
|
|
FD_QOS Issue notification of socket Quality of Service (QOS)
|
|
changes.
|
|
FD_GROUP_QOS Issue notification of socket group Quality of Service
|
|
(QOS) changes.
|
|
|
|
Invoking WSPAsyncSelect() for a socket cancels any previous
|
|
WSPAsyncSelect() or WSPEventSelect() for the same socket. For example,
|
|
to receive notification for both reading and writing, the WinSock SPI
|
|
client must call WSPAsyncSelect() with both FD_READ and FD_WRITE, as
|
|
follows:
|
|
|
|
rc = WSPAsyncSelect(s, hWnd, wMsg, FD_READ | FD_WRITE, &error);
|
|
|
|
It is not possible to specify different messages for different events. The
|
|
following code will not work; the second call will cancel the effects of
|
|
the first, and only FD_WRITE events will be reported with message wMsg2:
|
|
|
|
rc = WSPAsyncSelect(s, hWnd, wMsg1, FD_READ, &error);
|
|
rc = WSPAsyncSelect(s, hWnd, wMsg2, FD_WRITE, &error); // bad
|
|
|
|
To cancel all notification - i.e., to indicate that the service provider
|
|
should send no further messages related to network events on the socket -
|
|
lEvent will be set to zero.
|
|
|
|
rc = WSPAsyncSelect(s, hWnd, 0, 0, &error);
|
|
|
|
Since a WSPAccept()'ed socket has the same properties as the listening
|
|
socket used to accept it, any WSPAsyncSelect() events set for the
|
|
listening socket apply to the accepted socket. For example, if a listening
|
|
socket has WSPAsyncSelect() events FD_ACCEPT, FD_READ, and FD_WRITE, then
|
|
any socket accepted on that listening socket will also have FD_ACCEPT,
|
|
FD_READ, and FD_WRITE events with the same wMsg value used for messages.
|
|
If a different wMsg or events are desired, the WinSock SPI client must
|
|
call WSPAsyncSelect(), passing the accepted socket and the desired new
|
|
information.
|
|
|
|
When one of the nominated network events occurs on the specified socket s,
|
|
the service provider uses WPUPostMessage() to send message wMsg to the
|
|
WinSock SPI client's window hWnd. The wParam argument identifies the
|
|
socket on which a network event has occurred. The low word of lParam
|
|
specifies the network event that has occurred. The high word of lParam
|
|
contains any error code. The error code be any error as defined in
|
|
ws2spi.h.
|
|
|
|
The possible network event codes which may be indicated are as follows:
|
|
|
|
Value Meaning
|
|
~~~~~ ~~~~~~~
|
|
FD_READ Socket s ready for reading.
|
|
FD_WRITE Socket s ready for writing.
|
|
FD_OOB Out-of-band data ready for reading on socket s.
|
|
FD_ACCEPT Socket s ready for accepting a new incoming
|
|
connection.
|
|
FD_CONNECT Connection initiated on socket s completed.
|
|
FD_CLOSE Connection identified by socket s has been closed.
|
|
FD_QOS Quality of Service associated with socket s has
|
|
changed.
|
|
FD_GROUP_QOS Quality of Service associated with the socket group
|
|
to which s belongs has changed.
|
|
|
|
Although WSPAsyncSelect() can be called with interest in multiple events,
|
|
the service provider issues the same Windows message for each event.
|
|
|
|
A WinSock 2 provider shall not continually flood a WinSock SPI client
|
|
with messages for a particular network event. Having successfully posted
|
|
notification of a particular event to a WinSock SPI client window, no
|
|
further message(s) for that network event will be posted to the WinSock
|
|
SPI client window until the WinSock SPI client makes the function call
|
|
which implicitly reenables notification of that network event.
|
|
|
|
Event Re-enabling functions
|
|
~~~~~ ~~~~~~~~~~~~~~~~~~~~~
|
|
FD_READ WSPRecv() or WSPRecvFrom().
|
|
FD_WRITE WSPSend() or WSPSendTo().
|
|
FD_OOB WSPRecv() or WSPRecvFrom().
|
|
FD_ACCEPT WSPAccept() unless the error code returned is
|
|
WSATRY_AGAIN indicating that the condition
|
|
function returned CF_DEFER.
|
|
FD_CONNECT NONE
|
|
FD_CLOSE NONE
|
|
FD_QOS WSPIoctl() with SIO_GET_QOS
|
|
FD_GROUP_QOS WSPIoctl() with SIO_GET_GROUP_QOS
|
|
|
|
Any call to the reenabling routine, even one which fails, results in
|
|
reenabling of message posting for the relevant event.
|
|
|
|
For FD_READ, FD_OOB, and FD_ACCEPT events, message posting is "level-
|
|
triggered." This means that if the reenabling routine is called and the
|
|
relevant condition is still met after the call, a WSPAsyncSelect()
|
|
message is posted to the WinSock SPI client.
|
|
|
|
The FD_QOS and FD_GROUP_QOS events are considered edge triggered. A
|
|
message will be posted exactly once when a QOS change occurs. Further
|
|
messages will not be forthcoming until either the provider detects a
|
|
further change in QOS or the WinSock SPI client renegotiates the QOS
|
|
for the socket.
|
|
|
|
If any event has already happened when the WinSock SPI client calls
|
|
WSPAsyncSelect() or when the reenabling function is called, then a
|
|
message is posted as appropriate. For example, consider the following
|
|
sequence:
|
|
|
|
1. A WinSock SPI client calls WSPListen().
|
|
2. A connect request is received but not yet accepted.
|
|
3. The WinSock SPI client calls WSPAsyncSelect() specifying
|
|
that it wants to receive FD_ACCEPT messages for the socket.
|
|
Due to the persistence of events, the WinSock service provider
|
|
posts an FD_ACCEPT message immediately.
|
|
|
|
The FD_WRITE event is handled slightly differently. An FD_WRITE message
|
|
is posted when a socket is first connected with WSPConnect() (after
|
|
FD_CONNECT, if also registered) or accepted with WSPAccept(), and then
|
|
after a WSPSend() or WSPSendTo() fails with WSAEWOULDBLOCK and buffer
|
|
space becomes available. Therefore, a WinSock SPI client can assume that
|
|
sends are possible starting from the first FD_WRITE message and lasting
|
|
until a send returns WSAEWOULDBLOCK. After such a failure the WinSock SPI
|
|
client will be notified that sends are again possible with an FD_WRITE
|
|
message.
|
|
|
|
The FD_OOB event is used only when a socket is configured to receive
|
|
out-of-band data separately. If the socket is configured to receive
|
|
out-of-band data in-line, the out-of-band (expedited) data is treated as
|
|
normal data and the WinSock SPI client must register an interest in
|
|
FD_READ events, not FD_OOB events.
|
|
|
|
The error code in an FD_CLOSE message indicates whether the socket close
|
|
was graceful or abortive. If the error code is 0, then the close was
|
|
graceful; if the error code is WSAECONNRESET, then the socket's virtual
|
|
circuit was reset. This only applies to connection-oriented sockets such
|
|
as SOCK_STREAM.
|
|
|
|
The FD_CLOSE message is posted when a close indication is received for
|
|
the virtual circuit corresponding to the socket. In TCP terms, this means
|
|
that the FD_CLOSE is posted when the connection goes into the TIME WAIT
|
|
or CLOSE WAIT states. This results from the remote end performing a
|
|
WSPShutdown() on the send side or a WSPCloseSocket(). FD_CLOSE shall only
|
|
be posted after all data is read from a socket.
|
|
|
|
In the case of a graceful close, the service provider shall only send an
|
|
FD_CLOSE message to indicate virtual circuit closure after all the
|
|
received data has been read. It shall NOT send an FD_READ message to
|
|
indicate this condition.
|
|
|
|
The FD_QOS or FD_GROUP_QOS message is posted when any field in the flow
|
|
spec associated with socket s or the socket group that s belongs to has
|
|
changed, respectively. The service provider must update the QOS
|
|
information available to the client via WSPIoctl() with SIO_GET_QOS
|
|
and/or SIO_GET_GROUP_QOS.
|
|
|
|
Here is a summary of events and conditions for each asynchronous
|
|
notification message:
|
|
|
|
FD_READ
|
|
~~~~~~~
|
|
1. When WSPAsyncSelect() called, if there is data currently
|
|
available to receive.
|
|
2. When data arrives, if FD_READ not already posted.
|
|
3. after WSPRecv() or WSPRecvfrom() called (with or without
|
|
MSG_PEEK), if data is still available to receive.
|
|
|
|
N.B. When WSPSetSockOpt() SO_OOBINLINE is enabled "data" includes
|
|
both normal data and out-of-band (OOB) data in the instances noted
|
|
above.
|
|
|
|
FD_WRITE
|
|
~~~~~~~~
|
|
1. When WSPAsyncSelect() called, if a WSPSend() or WSPSendTo() is
|
|
possible.
|
|
2. After WSPConnect() or WSPAccept() called, when connection
|
|
established.
|
|
3. After WSPSend() or WSPSendTo() fail with WSAEWOULDBLOCK, when
|
|
WSPSend() or WSPSendTo() are likely to succeed.
|
|
4. After WSPBind() on a datagram socket.
|
|
|
|
FD_OOB
|
|
~~~~~~
|
|
Only valid when WSPSetSockOpt() SO_OOBINLINE is disabled (default).
|
|
1. When WSPAsyncSelect() called, if there is OOB data currently
|
|
available to receive with the MSG_OOB flag.
|
|
2. When OOB data arrives, if FD_OOB not already posted.
|
|
3. After WSPRecv() or WSPRecvfrom() called with or without MSG_OOB
|
|
flag, if OOB data is still available to receive.
|
|
|
|
FD_ACCEPT
|
|
~~~~~~~~~
|
|
1. When WSPAsyncSelect() called, if there is currently a connection
|
|
request available to accept.
|
|
2. When a connection request arrives, if FD_ACCEPT not already
|
|
posted.
|
|
3. After WSPAccept() called, if there is another connection request
|
|
available to accept.
|
|
|
|
FD_CONNECT
|
|
~~~~~~~~~~
|
|
1. When WSPAsyncSelect() called, if there is currently a connection
|
|
established.
|
|
2. After WSPConnect() called, when connection is established (even
|
|
when WSPConnect() succeeds immediately, as is typical with a
|
|
datagram socket)
|
|
|
|
FD_CLOSE
|
|
~~~~~~~~
|
|
Only valid on connection-oriented sockets (e.g. SOCK_STREAM)
|
|
1. When WSPAsyncSelect() called, if socket connection has been
|
|
closed.
|
|
2. After remote system initiated graceful close, when no data
|
|
currently available to receive (note: if data has been received
|
|
and is waiting to be read when the remote system initiates a
|
|
graceful close, the FD_CLOSE is not delivered until all pending
|
|
data has been read).
|
|
3. After local system initiates graceful close with WSPShutdown()
|
|
and remote system has responded with "End of Data" notification
|
|
(e.g. TCP FIN), when no data currently available to receive.
|
|
4. When remote system aborts connection (e.g. sent TCP RST), and
|
|
lParam will contain WSAECONNRESET error value.
|
|
|
|
N.B. FD_CLOSE is not posted after WSPClosesocket() is called.
|
|
|
|
FD_QOS
|
|
~~~~~~
|
|
1. When WSPAsyncSelect() called, if the QOS associated with the
|
|
socket has been changed.
|
|
2. After WSPIoctl() with SIO_GET_QOS called, when the QOS is
|
|
changed.
|
|
|
|
FD_GROUP_QOS
|
|
~~~~~~~~~~~~
|
|
1. When WSPAsyncSelect() called, if the group QOS associated with
|
|
the socket has been changed.
|
|
2. After WSPIoctl() with SIO_GET_GROUP_QOS called, when the group
|
|
QOS is changed.
|
|
|
|
Arguments:
|
|
|
|
s - A descriptor identifying the socket for which event notification is
|
|
required.
|
|
|
|
hWnd - A handle identifying the window which should receive a message
|
|
when a network event occurs.
|
|
|
|
wMsg - The message to be sent when a network event occurs.
|
|
|
|
lEvent - A bitmask which specifies a combination of network events in
|
|
which the WinSock SPI client is interested.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
The return value is 0 if the WinSock SPI client's declaration of
|
|
interest in the network event set was successful. Otherwise the
|
|
value SOCKET_ERROR is returned, and a specific error code is
|
|
available in lpErrno.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PSOCKET_INFORMATION socketInfo;
|
|
INT err;
|
|
INT result;
|
|
HWND capturedWindowHandle;
|
|
UINT capturedWindowMessage;
|
|
|
|
SOCK_ENTER( "WSPAsyncSelect", (PVOID)s, (PVOID)hWnd, (PVOID)wMsg, (PVOID)lEvent );
|
|
|
|
SOCK_ASSERT( lpErrno != NULL );
|
|
|
|
err = SockEnterApi( TRUE, FALSE );
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
SOCK_EXIT( "WSPAsyncSelect", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// Setup locals so we know how to cleanup on exit.
|
|
//
|
|
|
|
socketInfo = NULL;
|
|
|
|
//
|
|
// Attempt to find the socket in our lookup table.
|
|
//
|
|
|
|
socketInfo = SockFindAndReferenceWS2Socket( s );
|
|
|
|
if( socketInfo == NULL || socketInfo->State == SocketStateClosing ) {
|
|
|
|
IF_DEBUG(SELECT) {
|
|
|
|
SOCK_PRINT((
|
|
"WSPAsyncSelect failed on %s handle: %lx\n",
|
|
socketInfo == NULL ? "unknown" : "closed",
|
|
s
|
|
));
|
|
|
|
}
|
|
|
|
if( socketInfo != NULL ) {
|
|
|
|
SockDereferenceSocket( socketInfo );
|
|
|
|
}
|
|
|
|
SOCK_EXIT( "WSPAsyncSelect", SOCKET_ERROR, TRUE );
|
|
*lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// BUGBUG: Check for outstanding WSPEventSelect!
|
|
//
|
|
|
|
//
|
|
// Initialize the async window if necessary.
|
|
//
|
|
|
|
if( !SockCreateAsyncWindow() ) {
|
|
|
|
err = WSAENOBUFS; // SWAG
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Before we call the hooker, save the async select state in the
|
|
// socket.
|
|
//
|
|
|
|
capturedWindowHandle = socketInfo->WindowHandle;
|
|
capturedWindowMessage = socketInfo->WindowMessage;
|
|
|
|
socketInfo->WindowHandle = hWnd;
|
|
socketInfo->WindowMessage = wMsg;
|
|
|
|
//
|
|
// Let the hooker do its thang.
|
|
//
|
|
|
|
SockPreApiCallout();
|
|
|
|
result = socketInfo->Hooker->WSAAsyncSelect(
|
|
socketInfo->WS1Handle,
|
|
SockAsyncWindowHandle,
|
|
WM_MY_ASYNC_SELECT_MESSAGE,
|
|
lEvent
|
|
);
|
|
|
|
if( result == SOCKET_ERROR ) {
|
|
|
|
err = socketInfo->Hooker->WSAGetLastError();
|
|
SOCK_ASSERT( err != NO_ERROR );
|
|
SockPostApiCallout();
|
|
goto exit;
|
|
|
|
}
|
|
|
|
SockPostApiCallout();
|
|
|
|
//
|
|
// Success!
|
|
//
|
|
|
|
SOCK_ASSERT( err == NO_ERROR );
|
|
SOCK_ASSERT( result != SOCKET_ERROR );
|
|
|
|
exit:
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
socketInfo->WindowHandle = capturedWindowHandle;
|
|
socketInfo->WindowMessage = capturedWindowMessage;
|
|
*lpErrno = err;
|
|
|
|
}
|
|
|
|
SockDereferenceSocket( socketInfo );
|
|
|
|
SOCK_EXIT( "WSPAsyncSelect", result, (BOOL)( result == SOCKET_ERROR ) );
|
|
return result;
|
|
|
|
} // WSPAsyncSelect
|
|
|
|
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPEnumNetworkEvents(
|
|
IN SOCKET s,
|
|
IN WSAEVENT hEventObject,
|
|
OUT LPWSANETWORKEVENTS lpNetworkEvents,
|
|
OUT LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to report which network events have occurred for the
|
|
indicated socket since the last invocation of this routine. It is intended
|
|
for use in conjunction with WSPEventSelect(), which associates an event
|
|
object with one or more network events. Recording of network events
|
|
commences when WSPEventSelect() is called with a non-zero lNetworkEvents
|
|
parameter and remains in effect until another call is made to
|
|
WSPEventSelect() with the lNetworkEvents parameter set to zero, or until a
|
|
call is made to WSPAsyncSelect().
|
|
|
|
The socket's internal record of network events is copied to the structure
|
|
referenced by lpNetworkEvents, whereafter the internal network events
|
|
record is cleared. If hEventObject is non-null, the indicated event object
|
|
is also reset. The WinSock provider guarantees that the operations of
|
|
copying the network event record, clearing it and resetting any associated
|
|
event object are atomic, such that the next occurrence of a nominated
|
|
network event will cause the event object to become set. In the case of
|
|
this function returning SOCKET_ERROR, the associated event object is not
|
|
reset and the record of network events is not cleared.
|
|
|
|
The WSANETWORKEVENTS structure is defined as follows:
|
|
|
|
typedef struct _WSANETWORKEVENTS {
|
|
long lNetworkEvents;
|
|
int iErrorCodes[FD_MAX_EVENTS];
|
|
} WSANETWORKEVENTS, FAR * LPWSANETWORKEVENTS;
|
|
|
|
The lNetworkEvent field of the structure indicates which of the FD_XXX
|
|
network events have occurred. The iErrorCodes array is used to contain any
|
|
associated error codes, with array index corresponding to the position of
|
|
event bits in lNetworkEvents. The identifiers FD_READ_BIT, FD_WRITE_BIT,
|
|
etc. may be used to index the iErrorCodes array.
|
|
|
|
Arguments:
|
|
|
|
s - A descriptor identifying the socket.
|
|
|
|
hEventObject - An optional handle identifying an associated event
|
|
object to be reset.
|
|
|
|
lpNetworkEvents - A pointer to a WSANETWORKEVENTS struct which is
|
|
filled with a record of occurred network events and any associated
|
|
error codes.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
The return value is 0 if the operation was successful. Otherwise the value
|
|
SOCKET_ERROR is returned, and a specific error number is available in
|
|
lpErrno.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return SOCKET_ERROR;
|
|
|
|
} // WSPEnumNetworkEvents
|
|
|
|
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPEventSelect(
|
|
IN SOCKET s,
|
|
IN WSAEVENT hEventObject,
|
|
IN long lNetworkEvents,
|
|
OUT LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to specify an event object, hEventObject, to be
|
|
associated with the selected network events, lNetworkEvents. The socket
|
|
for which an event object is specified is identified by s. The event
|
|
object is set when any of the nominated network events occur.
|
|
|
|
WSPEventSelect() operates very similarly to WSPAsyncSelect(), the
|
|
difference being in the actions taken when a nominated network event
|
|
occurs. Whereas WSPAsyncSelect() causes a WinSock SPI client-specified
|
|
Windows message to be posted, WSPEventSelect() sets the associated event
|
|
object and records the occurrence of this event in an internal network
|
|
event record. A WinSock SPI client can use WSPEnumNetworkEvents() to
|
|
retrieve the contents of the internal network event record and thus
|
|
determine which of the nominated network events have occurred.
|
|
|
|
This routine automatically sets socket s to non-blocking mode, regardless
|
|
of the value of lNetworkEvents.
|
|
|
|
The lNetworkEvents parameter is constructed by OR'ing any of the values
|
|
specified in the following list.
|
|
|
|
Value Meaning
|
|
~~~~~ ~~~~~~~
|
|
FD_READ Issue notification of readiness for reading.
|
|
FD_WRITE Issue notification of readiness for writing.
|
|
FD_OOB Issue notification of the arrival of out-of-band data.
|
|
FD_ACCEPT Issue notification of incoming connections.
|
|
FD_CONNECT Issue notification of completed connection.
|
|
FD_CLOSE Issue notification of socket closure.
|
|
FD_QOS Issue notification of socket Quality of Service (QOS)
|
|
changes.
|
|
FD_GROUP_QOS Issue notification of socket group Quality of Service
|
|
(QOS) changes.
|
|
|
|
Issuing a WSPEventSelect() for a socket cancels any previous
|
|
WSPAsyncSelect() or WSPEventSelect() for the same socket and clears the
|
|
internal network event record. For example, to associate an event object
|
|
with both reading and writing network events, the WinSock SPI client must
|
|
call WSPEventSelect() with both FD_READ and FD_WRITE, as follows:
|
|
|
|
rc = WSPEventSelect(s, hEventObject, FD_READ | FD_WRITE);
|
|
|
|
It is not possible to specify different event objects for different
|
|
network events. The following code will not work; the second call will
|
|
cancel the effects of the first, and only FD_WRITE network event will be
|
|
associated with hEventObject2:
|
|
|
|
rc = WSPEventSelect(s, hEventObject1, FD_READ);
|
|
rc = WSPEventSelect(s, hEventObject2, FD_WRITE); //bad
|
|
|
|
To cancel the association and selection of network events on a socket,
|
|
lNetworkEvents should be set to zero, in which case the hEventObject
|
|
parameter will be ignored.
|
|
|
|
rc = WSPEventSelect(s, hEventObject, 0);
|
|
|
|
Closing a socket with WSPCloseSocket() also cancels the association and
|
|
selection of network events specified in WSPEventSelect() for the socket.
|
|
The WinSock SPI client, however, still must call WSACloseEvent() to
|
|
explicitly close the event object and free any resources.
|
|
|
|
Since a WSPAccept()'ed socket has the same properties as the listening
|
|
socket used to accept it, any WSPEventSelect() association and network
|
|
events selection set for the listening socket apply to the accepted socket.
|
|
For example, if a listening socket has WSPEventSelect() association of
|
|
hEventOject with FD_ACCEPT, FD_READ, and FD_WRITE, then any socket
|
|
accepted on that listening socket will also have FD_ACCEPT, FD_READ, and
|
|
FD_WRITE network events associated with the same hEventObject. If a
|
|
different hEventObject or network events are desired, the WinSock SPI
|
|
client should call WSPEventSelect(), passing the accepted socket and the
|
|
desired new information.
|
|
|
|
Having successfully recorded the occurrence of the network event and
|
|
signaled the associated event object, no further actions are taken for
|
|
that network event until the WinSock SPI client makes the function call
|
|
which implicitly reenables the setting of that network event and signaling
|
|
of the associated event object.
|
|
|
|
Event Re-enabling functions
|
|
~~~~~ ~~~~~~~~~~~~~~~~~~~~~
|
|
FD_READ WSPRecv() or WSPRecvFrom().
|
|
FD_WRITE WSPSend() or WSPSendTo().
|
|
FD_OOB WSPRecv() or WSPRecvFrom().
|
|
FD_ACCEPT WSPAccept() unless the error code returned is
|
|
WSATRY_AGAIN indicating that the condition
|
|
function returned CF_DEFER.
|
|
FD_CONNECT NONE
|
|
FD_CLOSE NONE
|
|
FD_QOS WSPIoctl() with SIO_GET_QOS
|
|
FD_GROUP_QOS WSPIoctl() with SIO_GET_GROUP_QOS
|
|
|
|
Any call to the reenabling routine, even one which fails, results in
|
|
reenabling of recording and signaling for the relevant network event and
|
|
event object, respectively.
|
|
|
|
For FD_READ, FD_OOB, and FD_ACCEPT network events, network event recording
|
|
and event object signaling are "level-triggered." This means that if the
|
|
reenabling routine is called and the relevant network condition is still
|
|
valid after the call, the network event is recorded and the associated
|
|
event object is signaled . This allows a WinSock SPI client to be event-
|
|
driven and not be concerned with the amount of data that arrives at any one
|
|
time. Consider the following sequence:
|
|
|
|
1. The service provider receives 100 bytes of data on socket s,
|
|
records the FD_READ network event and signals the associated
|
|
event object.
|
|
2. The WinSock SPI client issues WSPRecv( s, buffptr, 50, 0) to
|
|
read 50 bytes.
|
|
3. The service provider records the FD_READ network event and
|
|
signals the associated event object again since there is still
|
|
data to be read.
|
|
|
|
With these semantics, a WinSock SPI client need not read all available data
|
|
in response to an FD_READ network event --a single WSPRecv() in response to
|
|
each FD_READ network event is appropriate.
|
|
|
|
The FD_QOS and FD_GROUP_QOS events are considered edge triggered. A message
|
|
will be posted exactly once when a QOS change occurs. Further indications
|
|
will not be issued until either the service provider detects a further
|
|
change in QOS or the WinSock SPI client renegotiates the QOS for the
|
|
socket.
|
|
|
|
If a network event has already happened when the WinSock SPI client calls
|
|
WSPEventSelect() or when the reenabling function is called, then a network
|
|
event is recorded and the associated event object is signaled as
|
|
appropriate. For example, consider the following sequence:
|
|
|
|
1. A WinSock SPI client calls WSPListen().
|
|
2. A connect request is received but not yet accepted.
|
|
3. The WinSock SPI client calls WSPEventSelect() specifying that
|
|
it is interested in the FD_ACCEPT network event for the socket.
|
|
The service provider records the FD_ACCEPT network event and
|
|
signals the associated event object immediately.
|
|
|
|
The FD_WRITE network event is handled slightly differently. An FD_WRITE
|
|
network event is recorded when a socket is first connected with
|
|
WSPConnect() or accepted with WSPAccept(), and then after a WSPSend() or
|
|
WSPSendTo() fails with WSAEWOULDBLOCK and buffer space becomes available.
|
|
Therefore, a WinSock SPI client can assume that sends are possible
|
|
starting from the first FD_WRITE network event setting and lasting until
|
|
a send returns WSAEWOULDBLOCK. After such a failure the WinSock SPI client
|
|
will find out that sends are again possible when an FD_WRITE network event
|
|
is recorded and the associated event object is signaled.
|
|
|
|
The FD_OOB network event is used only when a socket is configured to
|
|
receive out-of-band data separately. If the socket is configured to receive
|
|
out-of-band data in-line, the out-of-band (expedited) data is treated as
|
|
normal data and the WinSock SPI client should register an interest in, and
|
|
will get, FD_READ network event, not FD_OOB network event. A WinSock SPI
|
|
client may set or inspect the way in which out-of-band data is to be
|
|
handled by using WSPSetSockOpt() or WSPGetSockOpt() for the SO_OOBINLINE
|
|
option.
|
|
|
|
The error code in an FD_CLOSE network event indicates whether the socket
|
|
close was graceful or abortive. If the error code is 0, then the close was
|
|
graceful; if the error code is WSAECONNRESET, then the socket's virtual
|
|
circuit was reset. This only applies to connection-oriented sockets such
|
|
as SOCK_STREAM.
|
|
|
|
The FD_CLOSE network event is recorded when a close indication is received
|
|
for the virtual circuit corresponding to the socket. In TCP terms, this
|
|
means that the FD_CLOSE is recorded when the connection goes into the
|
|
FIN WAIT or CLOSE WAIT states. This results from the remote end performing
|
|
a WSPShutdown() on the send side or a WSPCloseSocket().
|
|
|
|
Service providers shall record ONLY an FD_CLOSE network event to indicate
|
|
closure of a virtual circuit, they shall NOT record an FD_READ network
|
|
event to indicate this condition.
|
|
|
|
The FD_QOS or FD_GROUP_QOS network event is recorded when any field in the
|
|
flow spec associated with socket s or the socket group that s belongs to
|
|
has changed, respectively. This change must be made available to WinSock
|
|
SPI clients via the WSPIoctl() function with SIO_GET_QOS and/or
|
|
SIO_GET_GROUP_QOS to retrieve the current QOS for socket s or for the
|
|
socket group s belongs to, respectively.
|
|
|
|
Arguments:
|
|
|
|
s - A descriptor identifying the socket.
|
|
|
|
hEventObject - A handle identifying the event object to be associated
|
|
with the supplied set of network events.
|
|
|
|
lNetworkEvents - A bitmask which specifies the combination of network
|
|
events in which the WinSock SPI client has interest.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
The return value is 0 if the WinSock SPI client's specification of the
|
|
network events and the associated event object was successful.
|
|
Otherwise the value SOCKET_ERROR is returned, and a specific error
|
|
number is available in lpErrno.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
return SOCKET_ERROR;
|
|
|
|
} // WSPEventSelect
|
|
|
|
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPSelect(
|
|
IN int nfds,
|
|
IN OUT fd_set FAR * readfds,
|
|
IN OUT fd_set FAR * writefds,
|
|
IN OUT fd_set FAR * exceptfds,
|
|
IN const struct timeval FAR * timeout,
|
|
OUT LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to determine the status of one or more sockets. For
|
|
each socket, the caller may request information on read, write or error
|
|
status. The set of sockets for which a given status is requested is
|
|
indicated by an fd_set structure. All entries in an fd_set correspond to
|
|
sockets created by the service provider. Upon return, the structures are
|
|
updated to reflect the subset of these sockets which meet the specified
|
|
condition, and WSPSelect() returns the total number of sockets meeting the
|
|
conditions. A set of macros is provided for manipulating an fd_set. These
|
|
macros are compatible with those used in the Berkeley software, but the
|
|
underlying representation is completely different.
|
|
|
|
The parameter readfds identifies those sockets which are to be checked for
|
|
readability. If the socket is currently WSPListen()ing, it will be marked
|
|
as readable if an incoming connection request has been received, so that a
|
|
WSPAccept() is guaranteed to complete without blocking. For other sockets,
|
|
readability means that queued data is available for reading so that a
|
|
WSPRecv() or WSPRecvfrom() is guaranteed not to block.
|
|
|
|
For connection-oriented sockets, readability may also indicate that a close
|
|
request has been received from the peer. If the virtual circuit was
|
|
closed gracefully, then a WSPRecv() will return immediately with 0 bytes
|
|
read. If the virtual circuit was reset, then a WSPRecv() will complete
|
|
immediately with an error code, such as WSAECONNRESET. The presence of
|
|
out-of-band data will be checked if the socket option SO_OOBINLINE has
|
|
been enabled (see WSPSetSockOpt()).
|
|
|
|
The parameter writefds identifies those sockets which are to be checked for
|
|
writability. If a socket is WSPConnect()ing, writability means that the
|
|
connection establishment successfully completed. If the socket is not in
|
|
the process of WSPConnect()ing, writability means that a WSPSend() or
|
|
WSPSendTo() are guaranteed to succeed. However, they may block on a
|
|
blocking socket if the len exceeds the amount of outgoing system buffer
|
|
space available.. (It is not specified how long these guarantees can be
|
|
assumed to be valid, particularly in a multithreaded environment.)
|
|
|
|
The parameter exceptfds identifies those sockets which are to be checked
|
|
for the presence of out-of-band data or any exceptional error conditions.
|
|
Note that out-of-band data will only be reported in this way if the option
|
|
SO_OOBINLINE is FALSE. If a socket is WSPConnect()ing (non-blocking),
|
|
failure of the connect attempt is indicated in exceptfds.
|
|
|
|
Any two of readfds, writefds, or exceptfds may be given as NULL if no
|
|
descriptors are to be checked for the condition of interest. At least one
|
|
must be non-NULL, and any non- NULL descriptor set must contain at least
|
|
one socket descriptor.
|
|
|
|
A socket will be identified in a particular set when WSPSelect() returns
|
|
if:
|
|
|
|
readfds
|
|
~~~~~~~
|
|
|
|
If WSPListen()ing, a connection is pending, WSPAccept()
|
|
will succeed.
|
|
|
|
Data is available for reading (includes OOB data if
|
|
SO_OOBINLINE is enabled).
|
|
|
|
Connection has been closed/reset/aborted.
|
|
|
|
writefds
|
|
~~~~~~~~
|
|
|
|
If WSPConnect()ing (non-blocking), connection has succeeded.
|
|
|
|
Data may be sent.
|
|
|
|
|
|
exceptfds
|
|
~~~~~~~~~
|
|
|
|
If WSPConnect()ing (non-blocking), connection attempt failed.
|
|
|
|
OOB data is available for reading (only if SO_OOBINLINE is
|
|
disabled).
|
|
|
|
Three macros and one upcall function are defined in the header file
|
|
ws2spi.h for manipulating and checking the descriptor sets. The variable
|
|
FD_SETSIZE determines the maximum number of descriptors in a set. (The
|
|
default value of FD_SETSIZE is 64, which may be modified by #defining
|
|
FD_SETSIZE to another value before #including ws2spi.h.) Internally,
|
|
socket handles in a fd_set are not represented as bit flags as in Berkeley
|
|
Unix. Their data representation is opaque. Use of these macros will
|
|
maintain software portability between different socket environments. The
|
|
macros to manipulate and check fd_set contents are:
|
|
|
|
FD_CLR(s, *set) Removes the descriptor s from set.
|
|
|
|
FD_SET(s, *set) Adds descriptor s to set.
|
|
|
|
FD_ZERO(*set) Initializes the set to the NULL set.
|
|
|
|
The upcall function used to check the membership is:
|
|
|
|
int WPUFDIsSet ( SOCKET s, FD_SET FAR * set );
|
|
|
|
which will return nonzero if s is a member of the set, or zero otherwise.
|
|
|
|
The parameter timeout controls how long the WSPSelect() may take to
|
|
complete. If timeout is a null pointer, WSPSelect() will block indefinitely
|
|
until at least one descriptor meets the specified criteria. Otherwise,
|
|
timeout points to a struct timeval which specifies the maximum time that
|
|
WSPSelect() should wait before returning. When WSPSelect() returns, the
|
|
contents of the struct timeval are not altered. If the timeval is
|
|
initialized to {0, 0}, WSPSelect() will return immediately; this is used
|
|
to "poll" the state of the selected sockets. If this is the case, then the
|
|
WSPSelect() call is considered nonblocking and the standard assumptions for
|
|
nonblocking calls apply. For example, the blocking hook will not be called,
|
|
and the WinSock provider will not yield.
|
|
|
|
WSPSelect() has no effect on the persistence of socket events registered
|
|
with WSPAsyncSelect() or WSPEventSelect().
|
|
|
|
Arguments:
|
|
|
|
nfds - This argument is ignored and included only for the sake of
|
|
compatibility.
|
|
|
|
readfds - An optional pointer to a set of sockets to be checked for
|
|
readability.
|
|
|
|
writefds - An optional pointer to a set of sockets to be checked for
|
|
writability
|
|
|
|
exceptfds - An optional pointer to a set of sockets to be checked for
|
|
errors.
|
|
|
|
timeout - The maximum time for WSPSelect() to wait, or NULL for a
|
|
blocking operation.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
WSPSelect() returns the total number of descriptors which are ready and
|
|
contained in the fd_set structures, or SOCKET_ERROR if an error
|
|
occurred. If the return value is SOCKET_ERROR, a specific error code
|
|
is available in lpErrno.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
|
|
PSOCKET_INFORMATION socketInfo;
|
|
INT err;
|
|
INT result;
|
|
UINT fdsetCount;
|
|
UINT socketCount;
|
|
UINT i;
|
|
PFD_SET ws1ReadFds;
|
|
PFD_SET ws1WriteFds;
|
|
PFD_SET ws1ExceptFds;
|
|
PVOID fdsetBuffer;
|
|
PULONG fdsetBufferScan;
|
|
ULONG fdsetBufferLength;
|
|
|
|
SOCK_ENTER( "WSPSelect", (PVOID)nfds, (PVOID)readfds, (PVOID)writefds, (PVOID)exceptfds );
|
|
|
|
SOCK_ASSERT( lpErrno != NULL );
|
|
|
|
err = SockEnterApi( TRUE, FALSE );
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
SOCK_EXIT( "WSPSelect", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// Setup locals so we know how to cleanup on exit.
|
|
//
|
|
|
|
fdsetCount = 0;
|
|
socketCount = 0;
|
|
ws1ReadFds = NULL;
|
|
ws1WriteFds = NULL;
|
|
ws1ExceptFds = NULL;
|
|
fdsetBuffer = NULL;
|
|
socketInfo = NULL;
|
|
|
|
//
|
|
// Count the number of FD_SETs and sockets in those sets.
|
|
//
|
|
|
|
if( readfds != NULL ) {
|
|
|
|
fdsetCount++;
|
|
socketCount += readfds->fd_count;
|
|
|
|
}
|
|
|
|
if( writefds != NULL ) {
|
|
|
|
fdsetCount++;
|
|
socketCount += writefds->fd_count;
|
|
|
|
}
|
|
|
|
if( exceptfds != NULL ) {
|
|
|
|
fdsetCount++;
|
|
socketCount += exceptfds->fd_count;
|
|
|
|
}
|
|
|
|
//
|
|
// Bail if there's no sets or no sockets.
|
|
//
|
|
|
|
if( fdsetCount == 0 || socketCount == 0 ) {
|
|
|
|
SOCK_EXIT( "WSPSelect", 0, FALSE );
|
|
return 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer to hold *all* of the mapped FD_SETs.
|
|
//
|
|
|
|
fdsetBufferLength = ( fdsetCount * sizeof(UINT) ) +
|
|
( socketCount * sizeof(SOCKET) );
|
|
|
|
fdsetBuffer = SOCK_ALLOCATE_HEAP( fdsetBufferLength );
|
|
|
|
if( fdsetBuffer == NULL ) {
|
|
|
|
SOCK_EXIT( "WSPSelect", SOCKET_ERROR, TRUE );
|
|
*lpErrno = WSAENOBUFS;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
fdsetBuffer,
|
|
fdsetBufferLength
|
|
);
|
|
|
|
//
|
|
// Acquire the global lock. Any failure from this point must release
|
|
// the lock before returning.
|
|
//
|
|
|
|
//
|
|
// Build the mapped FD_SETs.
|
|
//
|
|
|
|
fdsetBufferScan = fdsetBuffer;
|
|
|
|
if( readfds != NULL ) {
|
|
|
|
err = SockMapWS2FdSetToWS1(
|
|
readfds,
|
|
&ws1ReadFds,
|
|
&fdsetBufferScan,
|
|
&socketInfo
|
|
);
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( writefds != NULL ) {
|
|
|
|
err = SockMapWS2FdSetToWS1(
|
|
writefds,
|
|
&ws1WriteFds,
|
|
&fdsetBufferScan,
|
|
&socketInfo
|
|
);
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( exceptfds != NULL ) {
|
|
|
|
err = SockMapWS2FdSetToWS1(
|
|
exceptfds,
|
|
&ws1ExceptFds,
|
|
&fdsetBufferScan,
|
|
&socketInfo
|
|
);
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SOCK_ASSERT( socketInfo != NULL );
|
|
|
|
//
|
|
// Now that we've got the sets mapped, we can let the hooker do
|
|
// its thang.
|
|
//
|
|
|
|
SockPrepareForBlockingHook( socketInfo );
|
|
SockPreApiCallout();
|
|
|
|
result = socketInfo->Hooker->select(
|
|
fdsetCount,
|
|
ws1ReadFds,
|
|
ws1WriteFds,
|
|
ws1ExceptFds,
|
|
timeout
|
|
);
|
|
|
|
if( result == SOCKET_ERROR ) {
|
|
|
|
err = socketInfo->Hooker->WSAGetLastError();
|
|
SOCK_ASSERT( err != NO_ERROR );
|
|
SockPostApiCallout();
|
|
goto exit;
|
|
|
|
}
|
|
|
|
SockPostApiCallout();
|
|
|
|
//
|
|
// Now, map the WS1 FD_SETs back to WS2 FD_Sets.
|
|
//
|
|
|
|
result = 0;
|
|
|
|
if( ws1ReadFds != NULL ) {
|
|
|
|
SOCK_ASSERT( readfds != NULL );
|
|
|
|
result += SockMapWS1FdSetToWS2(
|
|
ws1ReadFds,
|
|
readfds
|
|
);
|
|
|
|
}
|
|
|
|
if( ws1WriteFds != NULL ) {
|
|
|
|
SOCK_ASSERT( writefds != NULL );
|
|
|
|
result += SockMapWS1FdSetToWS2(
|
|
ws1WriteFds,
|
|
writefds
|
|
);
|
|
|
|
}
|
|
|
|
if( ws1ExceptFds != NULL ) {
|
|
|
|
SOCK_ASSERT( exceptfds != NULL );
|
|
|
|
result += SockMapWS1FdSetToWS2(
|
|
ws1ExceptFds,
|
|
exceptfds
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Success!
|
|
//
|
|
|
|
exit:
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
*lpErrno = err;
|
|
result = SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
if( fdsetBuffer != NULL ) {
|
|
|
|
SOCK_FREE_HEAP( fdsetBuffer );
|
|
|
|
}
|
|
|
|
if( socketInfo != NULL ) {
|
|
|
|
SockDereferenceSocket( socketInfo );
|
|
|
|
}
|
|
|
|
SOCK_EXIT( "WSPSelect", result, (BOOL)( result == SOCKET_ERROR ) );
|
|
return result;
|
|
|
|
} // WSPSelect
|
|
|
|
|
|
|
|
BOOL
|
|
SockCreateAsyncWindow(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates the async window.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
BOOL - TRUE if the window was created successfully, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
WNDCLASS WndClass;
|
|
|
|
//
|
|
// First ensure that the window hasn't already been created.
|
|
//
|
|
|
|
if( SockAsyncWindowHandle != NULL ) {
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
SockAcquireGlobalLock();
|
|
|
|
if( SockAsyncWindowHandle != NULL ) {
|
|
|
|
SockReleaseGlobalLock();
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
//
|
|
// Register the window class.
|
|
//
|
|
|
|
WndClass.style = 0;
|
|
WndClass.lpfnWndProc = SockAsyncWindowProc;
|
|
WndClass.cbClsExtra = 0;
|
|
WndClass.cbWndExtra = 0;
|
|
WndClass.hInstance = NULL;
|
|
WndClass.hIcon = NULL;
|
|
WndClass.hCursor = NULL;
|
|
WndClass.hbrBackground = NULL;
|
|
WndClass.lpszMenuName = NULL;
|
|
WndClass.lpszClassName = SockAsyncWindowClassName;
|
|
|
|
if( !RegisterClass( &WndClass ) ) {
|
|
|
|
SOCK_PRINT((
|
|
"SockCreateAsyncWindow: cannot register class\n"
|
|
));
|
|
|
|
SockReleaseGlobalLock();
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// Create the window.
|
|
//
|
|
|
|
SockAsyncWindowHandle = CreateWindow(
|
|
SockAsyncWindowClassName,
|
|
"",
|
|
WS_OVERLAPPEDWINDOW,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
CW_USEDEFAULT,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if( SockAsyncWindowHandle == NULL ) {
|
|
|
|
SOCK_PRINT((
|
|
"SockCreateAsyncWindow: cannot create window\n"
|
|
));
|
|
|
|
SockReleaseGlobalLock();
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
IF_DEBUG(SELECT) {
|
|
|
|
SOCK_PRINT((
|
|
"SockCreateAsyncWindow: created %08lX\n",
|
|
SockAsyncWindowHandle
|
|
));
|
|
}
|
|
|
|
ShowWindow( SockAsyncWindowHandle, SW_HIDE );
|
|
UpdateWindow( SockAsyncWindowHandle );
|
|
|
|
//
|
|
// Success!
|
|
//
|
|
|
|
SockReleaseGlobalLock();
|
|
return TRUE;
|
|
|
|
} // SockCreateAsyncWindow
|
|
|
|
|
|
VOID
|
|
SockDestroyAsyncWindow(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine destroys the async window.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
SockAcquireGlobalLock();
|
|
|
|
if( SockAsyncWindowHandle != NULL ) {
|
|
|
|
IF_DEBUG(SELECT) {
|
|
|
|
SOCK_PRINT((
|
|
"SockDestroyAsyncWindow: destroying %08lX\n",
|
|
SockAsyncWindowHandle
|
|
));
|
|
|
|
}
|
|
|
|
SOCK_REQUIRE( DestroyWindow( SockAsyncWindowHandle ) );
|
|
SockAsyncWindowHandle = NULL;
|
|
|
|
}
|
|
|
|
SockReleaseGlobalLock();
|
|
|
|
} // SockDestroyAsyncWindow
|
|
|
|
|
|
LRESULT
|
|
CALLBACK
|
|
SockAsyncWindowProc(
|
|
HWND hwnd,
|
|
UINT msg,
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
LRESULT Result;
|
|
|
|
Result = 0;
|
|
|
|
switch( msg ) {
|
|
|
|
case WM_DESTROY :
|
|
PostQuitMessage( 0 );
|
|
break;
|
|
|
|
case WM_MY_ASYNC_SELECT_MESSAGE :
|
|
SockProcessAsyncSelectMessage(
|
|
wParam,
|
|
lParam
|
|
);
|
|
|
|
default :
|
|
Result = DefWindowProc( hwnd, msg, wParam, lParam );
|
|
break;
|
|
|
|
}
|
|
|
|
return Result;
|
|
|
|
} // SockAsyncWindowProc
|
|
|
|
|
|
VOID
|
|
SockProcessAsyncSelectMessage(
|
|
WPARAM wParam,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
|
|
PSOCKET_INFORMATION socketInfo;
|
|
|
|
//
|
|
// See if we can map the WS1 handle to our socket info structure.
|
|
// If we can't, it's probably due to a race condition between the
|
|
// application and the WS1 DLL, so we'll just eat the message.
|
|
//
|
|
|
|
socketInfo = SockFindAndReferenceWS1Socket( (SOCKET)wParam );
|
|
|
|
if( socketInfo != NULL ) {
|
|
|
|
//
|
|
// Post the message to the application.
|
|
//
|
|
|
|
PostMessage(
|
|
socketInfo->WindowHandle,
|
|
socketInfo->WindowMessage,
|
|
(WPARAM)socketInfo->WS2Handle,
|
|
lParam
|
|
);
|
|
|
|
SockDereferenceSocket( socketInfo );
|
|
|
|
}
|
|
|
|
} // SockProcessAsyncSelectMessage
|
|
|
|
|
|
INT
|
|
SockMapWS2FdSetToWS1(
|
|
PFD_SET WS2FdSet,
|
|
PFD_SET * WS1FdSet,
|
|
PULONG * TargetBuffer,
|
|
PSOCKET_INFORMATION * SocketInfo
|
|
)
|
|
{
|
|
|
|
PSOCKET_INFORMATION socketInfo;
|
|
PSOCKET_INFORMATION returnedSocketInfo;
|
|
PFD_SET fdSet;
|
|
PULONG bufferScan;
|
|
UINT i;
|
|
INT err;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
SOCK_ASSERT( WS2FdSet != NULL );
|
|
SOCK_ASSERT( WS1FdSet != NULL );
|
|
SOCK_ASSERT( TargetBuffer != NULL );
|
|
SOCK_ASSERT( SocketInfo != NULL );
|
|
|
|
//
|
|
// Grab the starting location for the mapped FD_SET.
|
|
//
|
|
|
|
bufferScan = *TargetBuffer;
|
|
fdSet = (PFD_SET)bufferScan;
|
|
returnedSocketInfo = *SocketInfo;
|
|
|
|
//
|
|
// Set the mapped count.
|
|
//
|
|
|
|
*bufferScan++ = WS2FdSet->fd_count;
|
|
|
|
//
|
|
// Scan the incoming array and map the WS2 handles to WS1 handles.
|
|
//
|
|
|
|
for( i = 0 ; i < WS2FdSet->fd_count ; i++ ) {
|
|
|
|
socketInfo = SockFindAndReferenceWS2Socket( WS2FdSet->fd_array[i] );
|
|
|
|
if( socketInfo == NULL || socketInfo->State == SocketStateClosing ) {
|
|
|
|
IF_DEBUG(SELECT) {
|
|
|
|
SOCK_PRINT((
|
|
"SockMapWS2FdSetToWS1: failed on %s handle: %lx\n",
|
|
socketInfo == NULL ? "unknown" : "closed",
|
|
fdSet->fd_array[i]
|
|
));
|
|
|
|
}
|
|
|
|
if( socketInfo != NULL ) {
|
|
|
|
SockDereferenceSocket( socketInfo );
|
|
|
|
}
|
|
|
|
return WSAENOTSOCK;
|
|
|
|
}
|
|
|
|
//
|
|
// Save the WS1 handle in the mapped FD_SET.
|
|
//
|
|
|
|
*bufferScan++ = socketInfo->WS1Handle;
|
|
|
|
//
|
|
// This is kind of a hack. The caller (WSPSelect) needs the
|
|
// PSOCKET_INFORMATION structure for any one socket in any of
|
|
// the FD_SETs. If we have not yet returned a PSOCKET_INFORMATION
|
|
// structure to the caller, we'll pass it the current one and
|
|
// *not* dereference it. Otherwise (we've already passed one back)
|
|
// we'll dereference it ourselves.
|
|
//
|
|
|
|
if( returnedSocketInfo == NULL ) {
|
|
|
|
returnedSocketInfo = socketInfo;
|
|
|
|
} else {
|
|
|
|
SockDereferenceSocket( socketInfo );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Return the pointers back to the caller.
|
|
//
|
|
|
|
*WS1FdSet = fdSet;
|
|
*TargetBuffer = bufferScan;
|
|
*SocketInfo = returnedSocketInfo;
|
|
|
|
return NO_ERROR;
|
|
|
|
} // SockMapWS2FdSetToWS1
|
|
|
|
|
|
INT
|
|
SockMapWS1FdSetToWS2(
|
|
PFD_SET WS1FdSet,
|
|
PFD_SET WS2FdSet
|
|
)
|
|
{
|
|
|
|
PSOCKET_INFORMATION socketInfo;
|
|
UINT i, j;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
SOCK_ASSERT( WS2FdSet != NULL );
|
|
SOCK_ASSERT( WS1FdSet != NULL );
|
|
|
|
//
|
|
// Move the count over.
|
|
//
|
|
|
|
WS2FdSet->fd_count = WS1FdSet->fd_count;
|
|
|
|
//
|
|
// Map the sockets over.
|
|
//
|
|
|
|
for( i = 0, j = 0 ; i < WS1FdSet->fd_count ; i++ ) {
|
|
|
|
//
|
|
// Try to find the info for this WS1 socket. If we can't,
|
|
// just remove it from the list.
|
|
//
|
|
|
|
socketInfo = SockFindAndReferenceWS1Socket( WS1FdSet->fd_array[i] );
|
|
|
|
if( socketInfo == NULL || socketInfo->State == SocketStateClosing ) {
|
|
|
|
WS2FdSet->fd_count--;
|
|
|
|
if( socketInfo != NULL ) {
|
|
|
|
SockDereferenceSocket( socketInfo );
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
WS2FdSet->fd_array[j++] = socketInfo->WS2Handle;
|
|
SockDereferenceSocket( socketInfo );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return j;
|
|
|
|
} // SockMapWS1FdSetToWS2
|
|
|