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.
1269 lines
40 KiB
1269 lines
40 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
recv.c
|
|
|
|
Abstract:
|
|
|
|
This module contains support for the recv( ), recvfrom( ), WSARecv( ),
|
|
and WSARecvFrom( ) WinSock APIs.
|
|
|
|
Author:
|
|
|
|
David Treadwell (davidtr) 13-Mar-1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "winsockp.h"
|
|
|
|
#define WSAEMSGPARTIAL (WSABASEERR+100)
|
|
|
|
|
|
int
|
|
WSPAPI
|
|
WSPRecv(
|
|
SOCKET Handle,
|
|
LPWSABUF lpBuffers,
|
|
DWORD dwBufferCount,
|
|
LPDWORD lpNumberOfBytesRead,
|
|
LPDWORD ReceiveFlags,
|
|
LPWSAOVERLAPPED lpOverlapped,
|
|
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
|
|
LPWSATHREADID lpThreadId,
|
|
LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used on connected sockets or bound connectionless sockets
|
|
specified by the s parameter and is used to read incoming data.
|
|
|
|
For overlapped sockets WSPRecv() is used to post one or more buffers into
|
|
which incoming data will be placed as it becomes available, after which the
|
|
WinSock SPI client-specified completion indication (invocation of the
|
|
completion routine or setting of an event object) occurs. If the operation
|
|
does not complete immediately, the final completion status is retrieved
|
|
via the completion routine or WSPGetOverlappedResult().
|
|
|
|
If both lpOverlapped and lpCompletionRoutine are NULL, the socket in this
|
|
routine will be treated as a non-overlapped socket.
|
|
|
|
For non-overlapped sockets, the lpOverlapped, lpCompletionRoutine, and
|
|
lpThreadId parameters are ignored. Any data which has already been received
|
|
and buffered by the transport will be copied into the supplied user
|
|
buffers. For the case of a blocking socket with no data currently having
|
|
been received and buffered by the transport, the call will block until data
|
|
is received.
|
|
|
|
The supplied buffers are filled in the order in which they appear in the
|
|
array pointed to by lpBuffers, and the buffers are packed so that no holes
|
|
are created.
|
|
|
|
The array of WSABUF structures pointed to by the lpBuffers parameter is
|
|
transient. If this operation completes in an overlapped manner, it is the
|
|
service provider's responsibility to capture this array of pointers to
|
|
WSABUF structures before returning from this call. This enables WinSock SPI
|
|
clients to build stack-based WSABUF arrays.
|
|
|
|
For byte stream style sockets (e.g., type SOCK_STREAM), incoming data is
|
|
placed into the buffers until the buffers are filled, the connection is
|
|
closed, or internally buffered data is exhausted. Regardless of whether or
|
|
not the incoming data fills all the buffers, the completion indication
|
|
occurs for overlapped sockets. For message-oriented sockets (e.g., type
|
|
SOCK_DGRAM), an incoming message is placed into the supplied buffers, up
|
|
to the total size of the buffers supplied, and the completion indication
|
|
occurs for overlapped sockets. If the message is larger than the buffers
|
|
supplied, the buffers are filled with the first part of the message. If the
|
|
MSG_PARTIAL feature is supported by the service provider, the MSG_PARTIAL
|
|
flag is set in lpFlags and subsequent receive operation(s) may be used to
|
|
retrieve the rest of the message. If MSG_PARTIAL is not supported but the
|
|
protocol is reliable, WSPRecv() generates the error WSAEMSGSIZE and a
|
|
subsequent receive operation with a larger buffer can be used to retrieve
|
|
the entire message. Otherwise (i.e. the protocol is unreliable and does not
|
|
support MSG_PARTIAL), the excess data is lost, and WSPRecv() generates the
|
|
error WSAEMSGSIZE.
|
|
|
|
For connection-oriented sockets, WSPRecv() can indicate the graceful
|
|
termination of the virtual circuit in one of two ways, depending on whether
|
|
the socket is a byte stream or message-oriented. For byte streams, zero
|
|
bytes having been read indicates graceful closure and that no more bytes
|
|
will ever be read. For message-oriented sockets, where a zero byte message
|
|
is often allowable, a return error code of WSAEDISCON is used to indicate
|
|
graceful closure. In any case a return error code of WSAECONNRESET
|
|
indicates an abortive close has occurred.
|
|
|
|
lpFlags may be used to influence the behavior of the function invocation
|
|
beyond the options specified for the associated socket. That is, the
|
|
semantics of this routine are determined by the socket options and the
|
|
lpFlags parameter. The latter is constructed by or-ing any of the
|
|
following values:
|
|
|
|
MSG_PEEK - Peek at the incoming data. The data is copied into the
|
|
buffer but is not removed from the input queue. This flag is valid
|
|
only for non-overlapped sockets.
|
|
|
|
MSG_OOB - Process out-of-band data.
|
|
|
|
MSG_PARTIAL - This flag is for message-oriented sockets only. On
|
|
output, indicates that the data supplied is a portion of the message
|
|
transmitted by the sender. Remaining portions of the message will be
|
|
supplied in subsequent receive operations. A subsequent receive
|
|
operation with MSG_PARTIAL flag cleared indicates end of sender's
|
|
message.
|
|
|
|
As an input parameter, MSG_PARTIAL indicates that the receive
|
|
operation should complete even if only part of a message has been
|
|
received by the service provider.
|
|
|
|
If an overlapped operation completes immediately, WSPRecv() returns a
|
|
value of zero and the lpNumberOfBytesRecvd parameter is updated with the
|
|
number of bytes received. If the overlapped operation is successfully
|
|
initiated and will complete later, WSPRecv() returns SOCKET_ERROR and
|
|
indicates error code WSA_IO_PENDING. In this case, lpNumberOfBytesRecvd is
|
|
not updated. When the overlapped operation completes the amount of data
|
|
transferred is indicated either via the cbTransferred parameter in the
|
|
completion routine (if specified), or via the lpcbTransfer parameter in
|
|
WSPGetOverlappedResult().
|
|
|
|
Providers must allow this routine to be called from within the completion
|
|
routine of a previous WSPRecv(), WSPRecvFrom(), WSPSend() or WSPSendTo()
|
|
function. However, for a given socket, I/O completion routines may not be
|
|
nested. This permits time-sensitive data transmissions to occur entirely
|
|
within a preemptive context.
|
|
|
|
The lpOverlapped parameter must be valid for the duration of the
|
|
overlapped operation. If multiple I/O operations are simultaneously
|
|
outstanding, each must reference a separate overlapped structure. The
|
|
WSAOVERLAPPED structure has the following form:
|
|
|
|
typedef struct _WSAOVERLAPPED {
|
|
DWORD Internal; // reserved
|
|
DWORD InternalHigh; // reserved
|
|
DWORD Offset; // reserved
|
|
DWORD OffsetHigh; // reserved
|
|
WSAEVENT hEvent;
|
|
} WSAOVERLAPPED, FAR * LPWSAOVERLAPPED;
|
|
|
|
If the lpCompletionRoutine parameter is NULL, the service provider signals
|
|
the hEvent field of lpOverlapped when the overlapped operation completes
|
|
if it contains a valid event object handle. The WinSock SPI client can use
|
|
WSPGetOverlappedResult() to wait or poll on the event object.
|
|
|
|
If lpCompletionRoutine is not NULL, the hEvent field is ignored and can be
|
|
used by the WinSock SPI client to pass context information to the
|
|
completion routine. It is the service provider's responsibility to arrange
|
|
for invocation of the client-specified completion routine when the
|
|
overlapped operation completes. Since the completion routine must be
|
|
executed in the context of the same thread that initiated the overlapped
|
|
operation, it cannot be invoked directly from the service provider. The
|
|
WinSock DLL offers an asynchronous procedure call (APC) mechanism to
|
|
facilitate invocation of completion routines.
|
|
|
|
A service provider arranges for a function to be executed in the proper
|
|
thread by calling WPUQueueApc(). Note that this routine must be invoked
|
|
while in the context of the same process (but not necessarily the same
|
|
thread) that was used to initiate the overlapped operation. It is the
|
|
service provider's responsibility to arrange for this process context to
|
|
be active prior to calling WPUQueueApc().
|
|
|
|
WPUQueueApc() takes as input parameters a pointer to a WSATHREADID
|
|
structure (supplied to the provider via the lpThreadId input parameter),
|
|
a pointer to an APC function to be invoked, and a 32 bit context value
|
|
that is subsequently passed to the APC function. Because only a single
|
|
32-bit context value is available, the APC function cannot itself be the
|
|
client-specified completion routine. The service provider must instead
|
|
supply a pointer to its own APC function which uses the supplied context
|
|
value to access the needed result information for the overlapped operation,
|
|
and then invokes the client-specified completion routine.
|
|
|
|
The prototype for the client-supplied completion routine is as follows:
|
|
|
|
void
|
|
CALLBACK
|
|
CompletionRoutine(
|
|
IN DWORD dwError,
|
|
IN DWORD cbTransferred,
|
|
IN LPWSAOVERLAPPED lpOverlapped,
|
|
IN DWORD dwFlags
|
|
);
|
|
|
|
CompletionRoutine is a placeholder for a client supplied function
|
|
name.
|
|
|
|
dwError specifies the completion status for the overlapped
|
|
operation as indicated by lpOverlapped.
|
|
|
|
cbTransferred specifies the number of bytes sent.
|
|
|
|
No flag values are currently defined and the dwFlags value will
|
|
be zero.
|
|
|
|
This routine does not return a value.
|
|
|
|
The completion routines may be called in any order, not necessarily in
|
|
the same order the overlapped operations are completed. However, the
|
|
posted buffers are guaranteed to be filled in the same order they are
|
|
supplied.
|
|
|
|
Arguments:
|
|
|
|
s - A descriptor identifying a connected socket.
|
|
|
|
lpBuffers - A pointer to an array of WSABUF structures. Each WSABUF
|
|
structure contains a pointer to a buffer and the length of the
|
|
buffer.
|
|
|
|
dwBufferCount - The number of WSABUF structures in the lpBuffers
|
|
array.
|
|
|
|
lpNumberOfBytesRecvd - A pointer to the number of bytes received by
|
|
this call.
|
|
|
|
lpFlags - A pointer to flags.
|
|
|
|
lpOverlapped - A pointer to a WSAOVERLAPPED structure.
|
|
|
|
lpCompletionRoutine - A pointer to the completion routine called when
|
|
the receive operation has been completed.
|
|
|
|
lpThreadId - A pointer to a thread ID structure to be used by the
|
|
provider in a subsequent call to WPUQueueApc(). The provider should
|
|
store the referenced WSATHREADID structure (not the pointer to same)
|
|
until after the WPUQueueApc() function returns.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
If no error occurs and the receive operation has completed immediately,
|
|
WSPRecv() returns 0. Note that in this case the completion routine,
|
|
if specified, will have already been queued. Otherwise, a value of
|
|
SOCKET_ERROR is returned, and a specific error code is available in
|
|
lpErrno. The error code WSA_IO_PENDING indicates that the overlapped
|
|
operation has been successfully initiated and that completion will be
|
|
indicated at a later time. Any other error code indicates that no
|
|
overlapped operations was initiated and no completion indication will
|
|
occur.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK localIoStatusBlock;
|
|
PIO_STATUS_BLOCK ioStatusBlock;
|
|
int err;
|
|
AFD_RECV_INFO recvInfo;
|
|
HANDLE event;
|
|
PIO_APC_ROUTINE apcRoutine;
|
|
PVOID apcContext;
|
|
|
|
WS_ENTER( "WSPRecv", (PVOID)Handle, lpBuffers, (PVOID)dwBufferCount, (PVOID)ReceiveFlags );
|
|
|
|
WS_ASSERT( lpErrno != NULL );
|
|
|
|
err = SockEnterApi( TRUE, TRUE, FALSE );
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPRecv", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up the AFD_RECV_INFO structure.
|
|
//
|
|
|
|
recvInfo.BufferArray = lpBuffers;
|
|
recvInfo.BufferCount = dwBufferCount;
|
|
recvInfo.AfdFlags = 0;
|
|
recvInfo.TdiFlags = 0;
|
|
|
|
if ( *ReceiveFlags == 0 ) {
|
|
|
|
recvInfo.TdiFlags |= TDI_RECEIVE_NORMAL;
|
|
|
|
} else if( *ReceiveFlags != 0 ) {
|
|
|
|
//
|
|
// The legal flags are MSG_OOB and MSG_PEEK. MSG_OOB is not
|
|
// legal on datagram sockets.
|
|
//
|
|
|
|
if ( (*ReceiveFlags & ~(MSG_OOB | MSG_PEEK)) != 0 ) {
|
|
|
|
err = WSAEOPNOTSUPP;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if ( (*ReceiveFlags & MSG_OOB) != 0 ) {
|
|
|
|
recvInfo.TdiFlags |= TDI_RECEIVE_EXPEDITED;
|
|
|
|
} else {
|
|
|
|
recvInfo.TdiFlags |= TDI_RECEIVE_NORMAL;
|
|
|
|
}
|
|
|
|
if ( (*ReceiveFlags & MSG_PEEK) != 0 ) {
|
|
|
|
recvInfo.TdiFlags |= TDI_RECEIVE_PEEK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Determine the appropriate APC routine & context, event handle,
|
|
// and IO status block to use for the request.
|
|
//
|
|
|
|
if( lpOverlapped == NULL ) {
|
|
|
|
//
|
|
// This a synchronous request, use per-thread event object.
|
|
//
|
|
|
|
apcRoutine = NULL;
|
|
apcContext = NULL;
|
|
|
|
event = SockThreadEvent;
|
|
|
|
ioStatusBlock = &localIoStatusBlock;
|
|
|
|
} else {
|
|
|
|
if( lpCompletionRoutine == NULL ) {
|
|
|
|
//
|
|
// No APC, use event object from OVERLAPPED structure.
|
|
//
|
|
|
|
event = lpOverlapped->hEvent;
|
|
|
|
apcRoutine = NULL;
|
|
apcContext = ( (DWORD)event & 1 ) ? NULL : lpOverlapped;
|
|
|
|
} else {
|
|
|
|
//
|
|
// APC, ignore event object.
|
|
//
|
|
|
|
event = NULL;
|
|
|
|
apcRoutine = SockIoCompletion;
|
|
apcContext = lpCompletionRoutine;
|
|
|
|
//
|
|
// Tell AFD to skip fast IO on this request.
|
|
//
|
|
|
|
recvInfo.AfdFlags |= AFD_NO_FAST_IO;
|
|
|
|
}
|
|
|
|
//
|
|
// Use part of the OVERLAPPED structure as our IO_STATUS_BLOCK.
|
|
//
|
|
|
|
ioStatusBlock = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
|
|
|
|
//
|
|
// Tell AFD this is an overlapped operation.
|
|
//
|
|
|
|
recvInfo.AfdFlags |= AFD_OVERLAPPED;
|
|
|
|
}
|
|
|
|
ioStatusBlock->Status = STATUS_PENDING;
|
|
|
|
//
|
|
// Receive the data on the socket.
|
|
//
|
|
|
|
status = NtDeviceIoControlFile(
|
|
(HANDLE)Handle,
|
|
event,
|
|
apcRoutine,
|
|
apcContext,
|
|
ioStatusBlock,
|
|
IOCTL_AFD_RECEIVE,
|
|
&recvInfo,
|
|
sizeof(recvInfo),
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
//
|
|
// If this request has no overlapped structure, then wait for
|
|
// the operation to complete.
|
|
//
|
|
|
|
if ( status == STATUS_PENDING &&
|
|
lpOverlapped == NULL ) {
|
|
|
|
BOOLEAN success;
|
|
|
|
success = SockWaitForSingleObject(
|
|
event,
|
|
Handle,
|
|
SOCK_CONDITIONALLY_CALL_BLOCKING_HOOK,
|
|
SOCK_RECEIVE_TIMEOUT
|
|
);
|
|
|
|
//
|
|
// If the wait completed successfully, look in the IO status
|
|
// block to determine the real status code of the request. If
|
|
// the wait timed out, then cancel the IO and set up for an
|
|
// error return.
|
|
//
|
|
|
|
if ( success ) {
|
|
|
|
status = ioStatusBlock->Status;
|
|
|
|
} else {
|
|
|
|
SockCancelIo( Handle );
|
|
status = STATUS_IO_TIMEOUT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Set up the ReceiveFlags output parameter based on the type
|
|
// of receive.
|
|
//
|
|
|
|
switch ( status ) {
|
|
|
|
case STATUS_BUFFER_OVERFLOW:
|
|
|
|
//
|
|
// Translate the status to STATUS_RECEIVE_PARTIAL and fall through
|
|
// to that case.
|
|
//
|
|
|
|
status = STATUS_RECEIVE_PARTIAL;
|
|
|
|
case STATUS_RECEIVE_PARTIAL:
|
|
*ReceiveFlags = MSG_PARTIAL;
|
|
break;
|
|
|
|
case STATUS_RECEIVE_EXPEDITED:
|
|
*ReceiveFlags = MSG_OOB;
|
|
break;
|
|
|
|
case STATUS_RECEIVE_PARTIAL_EXPEDITED:
|
|
*ReceiveFlags = MSG_PARTIAL | MSG_OOB;
|
|
break;
|
|
|
|
default:
|
|
*ReceiveFlags = 0;
|
|
break;
|
|
|
|
}
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
|
|
err = SockNtStatusToSocketError( status );
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Determine the completion status.
|
|
//
|
|
// If the receive was a partial message (won't happen on a streams
|
|
// transport like TCP) set the last error to WSAEMSGSIZE and
|
|
// negate ths number of bytes received. This allows the app to know
|
|
// that the receive was partial and also how many bytes were
|
|
// received.
|
|
//
|
|
|
|
switch( status ) {
|
|
|
|
case STATUS_RECEIVE_PARTIAL_EXPEDITED :
|
|
case STATUS_RECEIVE_PARTIAL :
|
|
err = WSAEMSGSIZE;
|
|
ioStatusBlock->Information = -1 * ioStatusBlock->Information;
|
|
break;
|
|
|
|
case STATUS_PENDING :
|
|
err = WSA_IO_PENDING;
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// Return the number of bytes transferred.
|
|
//
|
|
|
|
*lpNumberOfBytesRead = ioStatusBlock->Information;
|
|
|
|
exit:
|
|
|
|
IF_DEBUG(RECEIVE) {
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_PRINT(( "WSPRecv on socket %lx failed: %ld (status %X).\n",
|
|
Handle, err, status ));
|
|
|
|
} else {
|
|
|
|
WS_PRINT(( "WSPRecv on socket %lx succeeded, "
|
|
"bytes = %ld\n",
|
|
Handle, ioStatusBlock->Information ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If there async select has been called in this process, get a
|
|
// pointer to the socket information structure and reenable the
|
|
// appropriate event. We don't do this if no async thread as a
|
|
// performance optimazation.
|
|
//
|
|
|
|
if ( SockAsyncSelectCalled ) {
|
|
|
|
PSOCKET_INFORMATION socket;
|
|
|
|
socket = SockFindAndReferenceSocket( Handle, TRUE );
|
|
|
|
//
|
|
// If the socket was found, reenable the right event. If it
|
|
// was not found, then presumably the socket handle was
|
|
// invalid.
|
|
//
|
|
|
|
if ( socket != NULL ) {
|
|
|
|
SockAcquireSocketLockExclusive( socket );
|
|
|
|
if ( (*ReceiveFlags & MSG_OOB) != 0 ) {
|
|
|
|
SockReenableAsyncSelectEvent( socket, FD_OOB );
|
|
|
|
} else {
|
|
|
|
SockReenableAsyncSelectEvent( socket, FD_READ );
|
|
|
|
}
|
|
|
|
SockReleaseSocketLock( socket );
|
|
SockDereferenceSocket( socket );
|
|
|
|
} else {
|
|
|
|
WS_PRINT(( "WSPRecv: SockFindAndReferenceSocket failed.\n" ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPRecv", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
WS_EXIT( "WSPRecv", 0, FALSE );
|
|
return 0;
|
|
|
|
} // WSPRecv
|
|
|
|
|
|
int
|
|
WSPAPI
|
|
WSPRecvFrom(
|
|
SOCKET Handle,
|
|
LPWSABUF lpBuffers,
|
|
DWORD dwBufferCount,
|
|
LPDWORD lpNumberOfBytesRead,
|
|
LPDWORD ReceiveFlags,
|
|
OUT struct sockaddr *SocketAddress,
|
|
OUT int *SocketAddressLength,
|
|
LPWSAOVERLAPPED lpOverlapped,
|
|
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
|
|
LPWSATHREADID lpThreadId,
|
|
LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used primarily on a connectionless socket specified by s.
|
|
|
|
For overlapped sockets WSPRecv() is used to post one or more buffers into
|
|
which incoming data will be placed as it becomes available, after which the
|
|
WinSock SPI client-specified completion indication (invocation of the
|
|
completion routine or setting of an event object) occurs. If the operation
|
|
does not complete immediately, the final completion status is retrieved
|
|
via the completion routine or WSPGetOverlappedResult(). Also note that the
|
|
values pointed to by lpFrom and lpFromlen are not updated until completion
|
|
is indicated. Applications must not use or disturb these values until they
|
|
have been updated, therefore the client must not use automatic (i.e stack-
|
|
based) variables for these parameters.
|
|
|
|
If both lpOverlapped and lpCompletionRoutine are NULL, the socket in this
|
|
routine will be treated as a non-overlapped socket.
|
|
|
|
For non-overlapped sockets, the lpOverlapped, lpCompletionRoutine, and
|
|
lpThreadId parameters are ignored. Any data which has already been received
|
|
and buffered by the transport will be copied into the supplied user
|
|
buffers. For the case of a blocking socket with no data currently having
|
|
been received and buffered by the transport, the call will block until data
|
|
is received.
|
|
|
|
The supplied buffers are filled in the order in which they appear in the
|
|
array pointed to by lpBuffers, and the buffers are packed so that no holes
|
|
are created.
|
|
|
|
The array of WSABUF structures pointed to by the lpBuffers parameter is
|
|
transient. If this operation completes in an overlapped manner, it is the
|
|
service provider's responsibility to capture this array of pointers to
|
|
WSABUF structures before returning from this call. This enables WinSock SPI
|
|
clients to build stack-based WSABUF arrays.
|
|
|
|
For connectionless socket types, the address from which the data originated
|
|
is copied to the buffer pointed by lpFrom. On input, the value pointed to
|
|
by lpFromlen is initialized to the size of this buffer, and is modified on
|
|
completion to indicate the actual size of the address stored there. As
|
|
noted previously for overlapped sockets, the lpFrom and lpFromlen
|
|
parameters are not updated until after the overlapped I/O has completed.
|
|
The memory pointed to by these parameters must, therefore, remain available
|
|
to the service provider and cannot be allocated on the WinSock SPI client's
|
|
stack frame. The lpFrom and lpFromlen parameters are ignored for
|
|
connection-oriented sockets.
|
|
|
|
For byte stream style sockets (e.g., type SOCK_STREAM), incoming data is
|
|
placed into the buffers until the buffers are filled, the connection is
|
|
closed, or internally buffered data is exhausted. Regardless of whether or
|
|
not the incoming data fills all the buffers, the completion indication
|
|
occurs for overlapped sockets. For message-oriented sockets (e.g., type
|
|
SOCK_DGRAM), an incoming message is placed into the supplied buffers, up
|
|
to the total size of the buffers supplied, and the completion indication
|
|
occurs for overlapped sockets. If the message is larger than the buffers
|
|
supplied, the buffers are filled with the first part of the message. If the
|
|
MSG_PARTIAL feature is supported by the service provider, the MSG_PARTIAL
|
|
flag is set in lpFlags and subsequent receive operation(s) may be used to
|
|
retrieve the rest of the message. If MSG_PARTIAL is not supported but the
|
|
protocol is reliable, WSPRecvFrom() generates the error WSAEMSGSIZE and a
|
|
subsequent receive operation with a larger buffer can be used to retrieve
|
|
the entire message. Otherwise (i.e. the protocol is unreliable and does not
|
|
support MSG_PARTIAL), the excess data is lost, and WSPRecvFrom() generates
|
|
the error WSAEMSGSIZE.
|
|
|
|
For connection-oriented sockets, WSPRecvFrom() can indicate the graceful
|
|
termination of the virtual circuit in one of two ways, depending on whether
|
|
the socket is a byte stream or message-oriented. For byte streams, zero
|
|
bytes having been read indicates graceful closure and that no more bytes
|
|
will ever be read. For message-oriented sockets, where a zero byte message
|
|
is often allowable, a return error code of WSAEDISCON is used to indicate
|
|
graceful closure. In any case a return error code of WSAECONNRESET
|
|
indicates an abortive close has occurred.
|
|
|
|
lpFlags may be used to influence the behavior of the function invocation
|
|
beyond the options specified for the associated socket. That is, the
|
|
semantics of this routine are determined by the socket options and the
|
|
lpFlags parameter. The latter is constructed by or-ing any of the
|
|
following values:
|
|
|
|
MSG_PEEK - Peek at the incoming data. The data is copied into the
|
|
buffer but is not removed from the input queue. This flag is valid
|
|
only for non-overlapped sockets.
|
|
|
|
MSG_OOB - Process out-of-band data.
|
|
|
|
MSG_PARTIAL - This flag is for message-oriented sockets only. On
|
|
output, indicates that the data supplied is a portion of the message
|
|
transmitted by the sender. Remaining portions of the message will be
|
|
supplied in subsequent receive operations. A subsequent receive
|
|
operation with MSG_PARTIAL flag cleared indicates end of sender's
|
|
message.
|
|
|
|
As an input parameter, MSG_PARTIAL indicates that the receive
|
|
operation should complete even if only part of a message has been
|
|
received by the service provider.
|
|
|
|
For message-oriented sockets, the MSG_PARTIAL bit is set in the lpFlags
|
|
parameter if a partial message is received. If a complete message is
|
|
received, MSG_PARTIAL is cleared in lpFlags. In the case of delayed
|
|
completion, the value pointed to by lpFlags is not updated. When
|
|
completion has been indicated the WinSock SPI client should call
|
|
WSPGetOverlappedResult() and examine the flags pointed to by the
|
|
lpdwFlags parameter.
|
|
|
|
If an overlapped operation completes immediately, WSPRecvFrom() returns a
|
|
value of zero and the lpNumberOfBytesRecvd parameter is updated with the
|
|
number of bytes received. If the overlapped operation is successfully
|
|
initiated and will complete later, WSPRecvFrom() returns SOCKET_ERROR and
|
|
indicates error code WSA_IO_PENDING. In this case, lpNumberOfBytesRecvd is
|
|
not updated. When the overlapped operation completes the amount of data
|
|
transferred is indicated either via the cbTransferred parameter in the
|
|
completion routine (if specified), or via the lpcbTransfer parameter in
|
|
WSPGetOverlappedResult().
|
|
|
|
Providers must allow this routine to be called from within the completion
|
|
routine of a previous WSPRecv(), WSPRecvFrom(), WSPSend() or WSPSendTo()
|
|
function. However, for a given socket, I/O completion routines may not be
|
|
nested. This permits time-sensitive data transmissions to occur entirely
|
|
within a preemptive context.
|
|
|
|
The lpOverlapped parameter must be valid for the duration of the
|
|
overlapped operation. If multiple I/O operations are simultaneously
|
|
outstanding, each must reference a separate overlapped structure. The
|
|
WSAOVERLAPPED structure has the following form:
|
|
|
|
typedef struct _WSAOVERLAPPED {
|
|
DWORD Internal; // reserved
|
|
DWORD InternalHigh; // reserved
|
|
DWORD Offset; // reserved
|
|
DWORD OffsetHigh; // reserved
|
|
WSAEVENT hEvent;
|
|
} WSAOVERLAPPED, FAR * LPWSAOVERLAPPED;
|
|
|
|
If the lpCompletionRoutine parameter is NULL, the service provider signals
|
|
the hEvent field of lpOverlapped when the overlapped operation completes
|
|
if it contains a valid event object handle. The WinSock SPI client can use
|
|
WSPGetOverlappedResult() to wait or poll on the event object.
|
|
|
|
If lpCompletionRoutine is not NULL, the hEvent field is ignored and can be
|
|
used by the WinSock SPI client to pass context information to the
|
|
completion routine. It is the service provider's responsibility to arrange
|
|
for invocation of the client-specified completion routine when the
|
|
overlapped operation completes. Since the completion routine must be
|
|
executed in the context of the same thread that initiated the overlapped
|
|
operation, it cannot be invoked directly from the service provider. The
|
|
WinSock DLL offers an asynchronous procedure call (APC) mechanism to
|
|
facilitate invocation of completion routines.
|
|
|
|
A service provider arranges for a function to be executed in the proper
|
|
thread by calling WPUQueueApc(). Note that this routine must be invoked
|
|
while in the context of the same process (but not necessarily the same
|
|
thread) that was used to initiate the overlapped operation. It is the
|
|
service provider's responsibility to arrange for this process context to
|
|
be active prior to calling WPUQueueApc().
|
|
|
|
WPUQueueApc() takes as input parameters a pointer to a WSATHREADID
|
|
structure (supplied to the provider via the lpThreadId input parameter),
|
|
a pointer to an APC function to be invoked, and a 32 bit context value
|
|
that is subsequently passed to the APC function. Because only a single
|
|
32-bit context value is available, the APC function cannot itself be the
|
|
client-specified completion routine. The service provider must instead
|
|
supply a pointer to its own APC function which uses the supplied context
|
|
value to access the needed result information for the overlapped operation,
|
|
and then invokes the client-specified completion routine.
|
|
|
|
The prototype for the client-supplied completion routine is as follows:
|
|
|
|
void
|
|
CALLBACK
|
|
CompletionRoutine(
|
|
IN DWORD dwError,
|
|
IN DWORD cbTransferred,
|
|
IN LPWSAOVERLAPPED lpOverlapped,
|
|
IN DWORD dwFlags
|
|
);
|
|
|
|
CompletionRoutine is a placeholder for a client supplied function
|
|
name.
|
|
|
|
dwError specifies the completion status for the overlapped
|
|
operation as indicated by lpOverlapped.
|
|
|
|
cbTransferred specifies the number of bytes sent.
|
|
|
|
No flag values are currently defined and the dwFlags value will
|
|
be zero.
|
|
|
|
This routine does not return a value.
|
|
|
|
The completion routines may be called in any order, not necessarily in
|
|
the same order the overlapped operations are completed. However, the
|
|
posted buffers are guaranteed to be filled in the same order they are
|
|
supplied.
|
|
|
|
Arguments:
|
|
|
|
s - A descriptor identifying a socket.
|
|
|
|
lpBuffers - A pointer to an array of WSABUF structures. Each WSABUF
|
|
structure contains a pointer to a buffer and the length of the
|
|
buffer.
|
|
|
|
dwBufferCount - The number of WSABUF structures in the lpBuffers array.
|
|
|
|
lpNumberOfBytesRecvd - A pointer to the number of bytes received by
|
|
this call.
|
|
|
|
lpFlags - A pointer to flags.
|
|
|
|
lpFrom - An optional pointer to a buffer which will hold the source
|
|
address upon the completion of the overlapped operation.
|
|
|
|
lpFromlen - A pointer to the size of the from buffer, required only if
|
|
lpFrom is specified.
|
|
|
|
lpOverlapped - A pointer to a WSAOVERLAPPED structure.
|
|
|
|
lpCompletionRoutine - A pointer to the completion routine called when
|
|
the receive operation has been completed.
|
|
|
|
lpThreadId - A pointer to a thread ID structure to be used by the
|
|
provider in a subsequent call to WPUQueueApc().The provider should
|
|
store the referenced WSATHREADID structure (not the pointer to same)
|
|
until after the WPUQueueApc() function returns.
|
|
|
|
lpErrno - A pointer to the error code.
|
|
|
|
Return Value:
|
|
|
|
If no error occurs and the receive operation has completed immediately,
|
|
WSPRecvFrom() returns 0. Note that in this case the completion routine,
|
|
if specified will have already been queued. Otherwise, a value of
|
|
SOCKET_ERROR is returned, and a specific error code is available in
|
|
lpErrno. The error code WSA_IO_PENDING indicates that the overlapped
|
|
operation has been successfully initiated and that completion will be
|
|
indicated at a later time. Any other error code indicates that no
|
|
overlapped operations was initiated and no completion indication will
|
|
occur.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PSOCKET_INFORMATION socket;
|
|
IO_STATUS_BLOCK localIoStatusBlock;
|
|
PIO_STATUS_BLOCK ioStatusBlock;
|
|
ULONG receiveControlBufferLength;
|
|
int err;
|
|
BOOLEAN nonBlocking;
|
|
AFD_RECV_DATAGRAM_INFO recvInfo;
|
|
HANDLE event;
|
|
PIO_APC_ROUTINE apcRoutine;
|
|
PVOID apcContext;
|
|
|
|
WS_ENTER( "WSPRecvFrom", (PVOID)Handle, lpBuffers, (PVOID)dwBufferCount, (PVOID)ReceiveFlags );
|
|
|
|
WS_ASSERT( lpErrno != NULL );
|
|
|
|
err = SockEnterApi( TRUE, TRUE, FALSE );
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPRecvFrom", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// Find a pointer to the socket structure corresponding to the
|
|
// passed-in handle.
|
|
//
|
|
|
|
socket = SockFindAndReferenceSocket( Handle, TRUE );
|
|
|
|
if ( socket == NULL ) {
|
|
|
|
err = WSAENOTSOCK;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// If this is a connected datagram socket, then it is not legal to
|
|
// specify a destination address.
|
|
//
|
|
|
|
if ( IS_DGRAM_SOCK(socket->SocketType) &&
|
|
socket->State == SocketStateConnected &&
|
|
(SocketAddress != NULL || SocketAddressLength != NULL) ) {
|
|
|
|
SockAcquireSocketLockExclusive( socket );
|
|
err = WSAEISCONN;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// If this is not a datagram socket or if the socket is connected,
|
|
// just call WSPRecv() to process the call.
|
|
//
|
|
|
|
if ( !IS_DGRAM_SOCK(socket->SocketType) ||
|
|
socket->State == SocketStateConnected ) {
|
|
|
|
INT ret;
|
|
|
|
SockDereferenceSocket( socket );
|
|
|
|
ret = WSPRecv(
|
|
Handle,
|
|
lpBuffers,
|
|
dwBufferCount,
|
|
lpNumberOfBytesRead,
|
|
ReceiveFlags,
|
|
lpOverlapped,
|
|
lpCompletionRoutine,
|
|
lpThreadId,
|
|
lpErrno
|
|
);
|
|
|
|
WS_EXIT( "WSPRecvFrom", ret, (BOOLEAN)(ret == SOCKET_ERROR) );
|
|
return ret;
|
|
|
|
}
|
|
|
|
//
|
|
// Acquire the lock that protect this sockets. We hold this lock
|
|
// throughout this routine to synchronize against other callers
|
|
// performing operations on the socket we're receiving data on.
|
|
//
|
|
|
|
SockAcquireSocketLockExclusive( socket );
|
|
|
|
nonBlocking = socket->NonBlocking;
|
|
|
|
//
|
|
// This is only legal on bound sockets.
|
|
//
|
|
|
|
if ( socket->State == SocketStateOpen ) {
|
|
|
|
err = WSAEINVAL;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Only MSG_PEEK is legal on WSPRecvFrom() with a datagram socket.
|
|
//
|
|
|
|
if ( (*ReceiveFlags & ~MSG_PEEK) != 0 ) {
|
|
|
|
err = WSAEOPNOTSUPP;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// If data receive has been shut down, fail.
|
|
//
|
|
|
|
if ( socket->ReceiveShutdown ) {
|
|
|
|
err = WSAESHUTDOWN;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Verify that we either got both components of an address, or
|
|
// we got neither.
|
|
//
|
|
|
|
if( (SocketAddress == NULL) ^
|
|
(SocketAddressLength == NULL || *SocketAddressLength == 0) ) {
|
|
|
|
err = WSAEFAULT;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that the address structure passed in is legitimate. Since
|
|
// it is an output parameter, all we really care about is that the
|
|
// length of the buffer is sufficient.
|
|
//
|
|
|
|
if ( SocketAddressLength != NULL &&
|
|
(LONG)*SocketAddressLength < (LONG)socket->HelperDll->MinSockaddrLength ) {
|
|
|
|
err = WSAEFAULT;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up the AFD_RECV_DATAGRAM_INFO structure.
|
|
//
|
|
|
|
recvInfo.BufferArray = lpBuffers;
|
|
recvInfo.BufferCount = dwBufferCount;
|
|
recvInfo.AfdFlags = 0;
|
|
recvInfo.TdiFlags = TDI_RECEIVE_NORMAL;
|
|
recvInfo.Address = SocketAddress;
|
|
recvInfo.AddressLength = SocketAddressLength;
|
|
|
|
if ( (*ReceiveFlags & MSG_PEEK) != 0 ) {
|
|
|
|
recvInfo.TdiFlags |= TDI_RECEIVE_PEEK;
|
|
|
|
}
|
|
|
|
//
|
|
// Determine the appropriate APC routine & context, event handle,
|
|
// and IO status block to use for the request.
|
|
//
|
|
|
|
if( lpOverlapped == NULL ) {
|
|
|
|
//
|
|
// This a synchronous request, use per-thread event object.
|
|
//
|
|
|
|
apcRoutine = NULL;
|
|
apcContext = NULL;
|
|
|
|
event = SockThreadEvent;
|
|
|
|
ioStatusBlock = &localIoStatusBlock;
|
|
|
|
} else {
|
|
|
|
if( lpCompletionRoutine == NULL ) {
|
|
|
|
//
|
|
// No APC, use event object from OVERLAPPED structure.
|
|
//
|
|
|
|
event = lpOverlapped->hEvent;
|
|
|
|
apcRoutine = NULL;
|
|
apcContext = ( (DWORD)event & 1 ) ? NULL : lpOverlapped;
|
|
|
|
} else {
|
|
|
|
//
|
|
// APC, ignore event object.
|
|
//
|
|
|
|
event = NULL;
|
|
|
|
apcRoutine = SockIoCompletion;
|
|
apcContext = lpCompletionRoutine;
|
|
|
|
//
|
|
// Tell AFD to skip fast IO on this request.
|
|
//
|
|
|
|
recvInfo.AfdFlags |= AFD_NO_FAST_IO;
|
|
|
|
}
|
|
|
|
//
|
|
// Use part of the OVERLAPPED structure as our IO_STATUS_BLOCK.
|
|
//
|
|
|
|
ioStatusBlock = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
|
|
|
|
//
|
|
// Tell AFD this is an overlapped operation.
|
|
//
|
|
|
|
recvInfo.AfdFlags |= AFD_OVERLAPPED;
|
|
|
|
}
|
|
|
|
ioStatusBlock->Status = STATUS_PENDING;
|
|
|
|
//
|
|
// Receive the data on the socket.
|
|
//
|
|
|
|
status = NtDeviceIoControlFile(
|
|
(HANDLE)socket->Handle,
|
|
event,
|
|
apcRoutine,
|
|
apcContext,
|
|
ioStatusBlock,
|
|
IOCTL_AFD_RECEIVE_DATAGRAM,
|
|
&recvInfo,
|
|
sizeof(recvInfo),
|
|
NULL,
|
|
0
|
|
);
|
|
|
|
//
|
|
// If this request has no overlapped structure, then wait for
|
|
// the operation to complete.
|
|
//
|
|
|
|
if ( status == STATUS_PENDING &&
|
|
lpOverlapped == NULL ) {
|
|
|
|
BOOLEAN success;
|
|
|
|
SockReleaseSocketLock( socket );
|
|
|
|
success = SockWaitForSingleObject(
|
|
event,
|
|
Handle,
|
|
SOCK_CONDITIONALLY_CALL_BLOCKING_HOOK,
|
|
SOCK_RECEIVE_TIMEOUT
|
|
);
|
|
|
|
SockAcquireSocketLockExclusive( socket );
|
|
|
|
//
|
|
// If the wait completed successfully, look in the IO status
|
|
// block to determine the real status code of the request. If
|
|
// the wait timed out, then cancel the IO and set up for an
|
|
// error return.
|
|
//
|
|
|
|
if ( success ) {
|
|
|
|
status = ioStatusBlock->Status;
|
|
|
|
} else {
|
|
|
|
SockCancelIo( Handle );
|
|
status = STATUS_IO_TIMEOUT;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Set up the ReceiveFlags output parameter based on the type
|
|
// of receive.
|
|
//
|
|
|
|
switch ( status ) {
|
|
|
|
case STATUS_BUFFER_OVERFLOW:
|
|
|
|
//
|
|
// Translate the status to STATUS_RECEIVE_PARTIAL and fall through
|
|
// to that case.
|
|
//
|
|
|
|
status = STATUS_RECEIVE_PARTIAL;
|
|
|
|
case STATUS_RECEIVE_PARTIAL:
|
|
*ReceiveFlags = MSG_PARTIAL;
|
|
break;
|
|
|
|
case STATUS_RECEIVE_EXPEDITED:
|
|
*ReceiveFlags = MSG_OOB;
|
|
break;
|
|
|
|
case STATUS_RECEIVE_PARTIAL_EXPEDITED:
|
|
*ReceiveFlags = MSG_PARTIAL | MSG_OOB;
|
|
break;
|
|
|
|
default:
|
|
*ReceiveFlags = 0;
|
|
break;
|
|
|
|
}
|
|
|
|
if( !NT_SUCCESS(status) ) {
|
|
|
|
err = SockNtStatusToSocketError( status );
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Determine the completion status.
|
|
//
|
|
// If the receive was a partial message (won't happen on a streams
|
|
// transport like TCP) set the last error to WSAEMSGSIZE and
|
|
// negate ths number of bytes received. This allows the app to know
|
|
// that the receive was partial and also how many bytes were
|
|
// received.
|
|
//
|
|
|
|
switch( status ) {
|
|
|
|
case STATUS_RECEIVE_PARTIAL_EXPEDITED :
|
|
case STATUS_RECEIVE_PARTIAL :
|
|
err = WSAEMSGSIZE;
|
|
ioStatusBlock->Information = -1 * ioStatusBlock->Information;
|
|
break;
|
|
|
|
case STATUS_PENDING :
|
|
err = WSA_IO_PENDING;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Return the number of bytes transferred.
|
|
//
|
|
|
|
*lpNumberOfBytesRead = ioStatusBlock->Information;
|
|
|
|
exit:
|
|
|
|
IF_DEBUG(RECEIVE) {
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_PRINT(( "WSPRecvFrom on socket %lx (%lx) failed: %ld.\n",
|
|
Handle, socket, err ));
|
|
|
|
} else {
|
|
|
|
WS_PRINT(( "WSPRecvFrom on socket %lx (%lx) succeeded, "
|
|
"bytes %ld",
|
|
Handle, socket, ioStatusBlock->Information ));
|
|
WsPrintSockaddr( SocketAddress, SocketAddressLength );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( socket != NULL ) {
|
|
|
|
if ( (*ReceiveFlags & MSG_OOB) != 0 ) {
|
|
|
|
SockReenableAsyncSelectEvent( socket, FD_OOB );
|
|
|
|
} else {
|
|
|
|
SockReenableAsyncSelectEvent( socket, FD_READ );
|
|
|
|
}
|
|
|
|
SockReleaseSocketLock( socket );
|
|
SockDereferenceSocket( socket );
|
|
|
|
}
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPRecvFrom", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
WS_EXIT( "WSPRecvFrom", 0, FALSE );
|
|
return 0;
|
|
|
|
} // WSPRecvFrom
|
|
|