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.
1259 lines
37 KiB
1259 lines
37 KiB
/*++
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
send.c
|
|
|
|
Abstract:
|
|
|
|
This module contains support for the send( ), sendto( ),
|
|
WSASend( ), and WSASendTo( ) WinSock API.
|
|
|
|
Author:
|
|
|
|
David Treadwell (davidtr) 13-Mar-1992
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "winsockp.h"
|
|
|
|
|
|
int
|
|
WSPAPI
|
|
WSPSend (
|
|
SOCKET Handle,
|
|
LPWSABUF lpBuffers,
|
|
DWORD dwBufferCount,
|
|
LPDWORD lpNumberOfBytesSent,
|
|
DWORD iFlags,
|
|
LPWSAOVERLAPPED lpOverlapped,
|
|
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
|
|
LPWSATHREADID lpThreadId,
|
|
LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to write outgoing data from one or more buffers on a
|
|
connection-oriented socket specified by s. It may also be used, however,
|
|
on connectionless sockets which have a stipulated default peer address
|
|
established via the WSPConnect() function.
|
|
|
|
For overlapped sockets (created using WSPSocket() with flag
|
|
WSA_FLAG_OVERLAPPED) this will occur using overlapped I/O, unless both
|
|
lpOverlapped and lpCompletionRoutine are NULL in which case the socket is
|
|
treated as a non-overlapped socket. A completion indication will occur
|
|
(invocation of the completion routine or setting of an event object) when
|
|
the supplied buffer(s) have been consumed by the transport. If the
|
|
operation does not complete immediately, the final completion status is
|
|
retrieved via the completion routine or WSPGetOverlappedResult().
|
|
|
|
For non-overlapped sockets, the parameters lpOverlapped,
|
|
lpCompletionRoutine, and lpThreadId are ignored and WSPSend() adopts the
|
|
regular synchronous semantics. Data is copied from the supplied buffer(s)
|
|
into the transport's buffer. If the socket is non-blocking and stream-
|
|
oriented, and there is not sufficient space in the transport's buffer,
|
|
WSPSend() will return with only part of the supplied buffers having been
|
|
consumed. Given the same buffer situation and a blocking socket, WSPSend()
|
|
will block until all of the supplied buffer contents have been consumed.
|
|
|
|
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 these WSABUF structures
|
|
before returning from this call. This enables applications to build stack-
|
|
based WSABUF arrays.
|
|
|
|
For message-oriented sockets, care must be taken not to exceed the maximum
|
|
message size of the underlying provider, which can be obtained by getting
|
|
the value of socket option SO_MAX_MSG_SIZE. If the data is too long to
|
|
pass atomically through the underlying protocol the error WSAEMSGSIZE is
|
|
returned, and no data is transmitted.
|
|
|
|
Note that the successful completion of a WSPSend() does not indicate that
|
|
the data was successfully delivered.
|
|
|
|
dwFlags 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
|
|
dwFlags parameter. The latter is constructed by or-ing any of the
|
|
following values:
|
|
|
|
MSG_DONTROUTE - Specifies that the data should not be subject
|
|
to routing. A WinSock service provider may choose to ignore this
|
|
flag.
|
|
|
|
MSG_OOB - Send out-of-band data (stream style socket such as
|
|
SOCK_STREAM only).
|
|
|
|
MSG_PARTIAL - Specifies that lpBuffers only contains a partial
|
|
message. Note that the error code WSAEOPNOTSUPP will be returned
|
|
which do not support partial message transmissions.
|
|
|
|
If an overlapped operation completes immediately, WSPSend() returns a
|
|
value of zero and the lpNumberOfBytesSent parameter is updated with the
|
|
number of bytes sent. If the overlapped operation is successfully
|
|
initiated and will complete later, WSPSend() returns SOCKET_ERROR and
|
|
indicates error code WSA_IO_PENDING. In this case, lpNumberOfBytesSent 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
|
|
service provider guarantees to the client that posted buffers are sent
|
|
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. This array must remain valid for the duration of the
|
|
send operation.
|
|
|
|
dwBufferCount - The number of WSABUF structures in the lpBuffers array.
|
|
|
|
lpNumberOfBytesSent - A pointer to the number of bytes sent by this
|
|
call.
|
|
|
|
dwFlags - Specifies the way in which the call is made.
|
|
|
|
lpOverlapped - A pointer to a WSAOVERLAPPED structure.
|
|
|
|
lpCompletionRoutine - A pointer to the completion routine called when
|
|
the send 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 send operation has completed immediately,
|
|
WSPSend() 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 operation was initiated and no completion indication will
|
|
occur.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK localIoStatusBlock;
|
|
PIO_STATUS_BLOCK ioStatusBlock;
|
|
int err;
|
|
AFD_SEND_INFO sendInfo;
|
|
HANDLE event;
|
|
PIO_APC_ROUTINE apcRoutine;
|
|
PVOID apcContext;
|
|
|
|
WS_ENTER( "WSASend", (PVOID)Handle, (PVOID)lpBuffers, (PVOID)dwBufferCount, (PVOID)iFlags );
|
|
|
|
WS_ASSERT( lpErrno != NULL );
|
|
|
|
err = SockEnterApi( TRUE, TRUE, FALSE );
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPSelect", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up AFD_SEND_INFO.TdiFlags;
|
|
//
|
|
|
|
sendInfo.BufferArray = lpBuffers;
|
|
sendInfo.BufferCount = dwBufferCount;
|
|
sendInfo.AfdFlags = 0;
|
|
sendInfo.TdiFlags = 0;
|
|
|
|
if ( iFlags != 0 ) {
|
|
|
|
//
|
|
// The legal flags are MSG_OOB, MSG_DONTROUTE and MSG_PARTIAL.
|
|
// MSG_OOB is not legal on datagram sockets.
|
|
//
|
|
|
|
if ( ( (iFlags & ~(MSG_OOB | MSG_DONTROUTE | MSG_PARTIAL)) != 0 ) ) {
|
|
|
|
err = WSAEOPNOTSUPP;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if ( (iFlags & MSG_OOB) != 0 ) {
|
|
|
|
sendInfo.TdiFlags |= TDI_SEND_EXPEDITED;
|
|
|
|
}
|
|
|
|
if ( (iFlags & MSG_PARTIAL) != 0 ) {
|
|
|
|
sendInfo.TdiFlags |= TDI_SEND_PARTIAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
sendInfo.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.
|
|
//
|
|
|
|
sendInfo.AfdFlags |= AFD_OVERLAPPED;
|
|
|
|
}
|
|
|
|
ioStatusBlock->Status = STATUS_PENDING;
|
|
|
|
//
|
|
// Send the data over the socket.
|
|
//
|
|
|
|
status = NtDeviceIoControlFile(
|
|
(HANDLE)Handle,
|
|
event,
|
|
apcRoutine,
|
|
apcContext,
|
|
ioStatusBlock,
|
|
IOCTL_AFD_SEND,
|
|
&sendInfo,
|
|
sizeof(sendInfo),
|
|
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_SEND_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;
|
|
}
|
|
|
|
}
|
|
|
|
if ( status == STATUS_SUCCESS ) {
|
|
|
|
//
|
|
// The request completed immediately, so return the number of
|
|
// bytes sent to the user.
|
|
//
|
|
|
|
*lpNumberOfBytesSent = ioStatusBlock->Information;
|
|
|
|
} else if ( status == STATUS_PENDING ) {
|
|
|
|
err = WSA_IO_PENDING;
|
|
|
|
} else if( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// Map the NTSTATUS to a WinSock error code.
|
|
//
|
|
|
|
err = SockNtStatusToSocketError( status );
|
|
|
|
}
|
|
|
|
exit:
|
|
|
|
IF_DEBUG(SEND) {
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_PRINT(( "WSASend on socket %lx failed: %ld.\n",
|
|
Handle, err ));
|
|
|
|
} else {
|
|
|
|
WS_PRINT(( "WSASend on socket %lx succeeded, "
|
|
"bytes = %ld\n",
|
|
Handle, ioStatusBlock->Information ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If there is an async thread in this process, get a pointer to the
|
|
// socket information structure and reenable the appropriate event.
|
|
// We don't do this if there is no async thread as a performance
|
|
// optimization. Also, if we're not going to return WSAEWOULDBLOCK
|
|
// we don't need to reenable FD_WRITE events.
|
|
//
|
|
|
|
if ( SockAsyncSelectCalled && err == WSAEWOULDBLOCK ) {
|
|
|
|
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 );
|
|
SockReenableAsyncSelectEvent( socket, FD_WRITE );
|
|
SockReleaseSocketLock( socket );
|
|
|
|
SockDereferenceSocket( socket );
|
|
|
|
} else {
|
|
|
|
if ( socket == NULL ) {
|
|
|
|
WS_PRINT(( "WSASend: SockFindAndReferenceSocket failed.\n" ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPSend", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
WS_EXIT( "WSPSend", 0, TRUE );
|
|
return 0;
|
|
|
|
} // WSPSend
|
|
|
|
|
|
int
|
|
WSPAPI
|
|
WSPSendTo (
|
|
SOCKET Handle,
|
|
LPWSABUF lpBuffers,
|
|
DWORD dwBufferCount,
|
|
LPDWORD lpNumberOfBytesSent,
|
|
DWORD iFlags,
|
|
const struct sockaddr *SocketAddress,
|
|
int SocketAddressLength,
|
|
LPWSAOVERLAPPED lpOverlapped,
|
|
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
|
|
LPWSATHREADID lpThreadId,
|
|
LPINT lpErrno
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is normally used on a connectionless socket specified by s
|
|
to send a datagram contained in one or more buffers to a specific peer
|
|
socket identified by the lpTo parameter. On a connection-oriented socket,
|
|
the lpTo and iToLen parameters are ignored; in this case the WSPSendTo()
|
|
is equivalent to WSPSend().
|
|
|
|
For overlapped sockets (created using WSPSocket() with flag
|
|
WSA_FLAG_OVERLAPPED) this will occur using overlapped I/O, unless both
|
|
lpOverlapped and lpCompletionRoutine are NULL in which case the socket is
|
|
treated as a non-overlapped socket. A completion indication will occur
|
|
(invocation of the completion routine or setting of an event object) when
|
|
the supplied buffer(s) have been consumed by the transport. If the
|
|
operation does not complete immediately, the final completion status is
|
|
retrieved via the completion routine or WSPGetOverlappedResult().
|
|
|
|
For non-overlapped sockets, the parameters lpOverlapped,
|
|
lpCompletionRoutine, and lpThreadId are ignored and WSPSend() adopts the
|
|
regular synchronous semantics. Data is copied from the supplied buffer(s)
|
|
into the transport's buffer. If the socket is non-blocking and stream-
|
|
oriented, and there is not sufficient space in the transport's buffer,
|
|
WSPSend() will return with only part of the supplied buffers having been
|
|
consumed. Given the same buffer situation and a blocking socket, WSPSend()
|
|
will block until all of the supplied buffer contents have been consumed.
|
|
|
|
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 these WSABUF structures
|
|
before returning from this call. This enables applications to build stack-
|
|
based WSABUF arrays.
|
|
|
|
For message-oriented sockets, care must be taken not to exceed the maximum
|
|
message size of the underlying provider, which can be obtained by getting
|
|
the value of socket option SO_MAX_MSG_SIZE. If the data is too long to
|
|
pass atomically through the underlying protocol the error WSAEMSGSIZE is
|
|
returned, and no data is transmitted.
|
|
|
|
Note that the successful completion of a WSPSendTo() does not indicate that
|
|
the data was successfully delivered.
|
|
|
|
dwFlags 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
|
|
dwFlags parameter. The latter is constructed by or-ing any of the
|
|
following values:
|
|
|
|
MSG_DONTROUTE - Specifies that the data should not be subject
|
|
to routing. A WinSock service provider may choose to ignore this
|
|
flag.
|
|
|
|
MSG_OOB - Send out-of-band data (stream style socket such as
|
|
SOCK_STREAM only).
|
|
|
|
MSG_PARTIAL - Specifies that lpBuffers only contains a partial
|
|
message. Note that the error code WSAEOPNOTSUPP will be returned
|
|
which do not support partial message transmissions.
|
|
|
|
If an overlapped operation completes immediately, WSPSendTo() returns a
|
|
value of zero and the lpNumberOfBytesSent parameter is updated with the
|
|
number of bytes sent. If the overlapped operation is successfully
|
|
initiated and will complete later, WSPSendTo() returns SOCKET_ERROR and
|
|
indicates error code WSA_IO_PENDING. In this case, lpNumberOfBytesSent 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
|
|
service provider guarantees to the client that posted buffers are sent
|
|
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. This array must remain valid for the duration of the
|
|
send operation.
|
|
|
|
dwBufferCount - The number of WSABUF structures in the lpBuffers
|
|
array.
|
|
|
|
lpNumberOfBytesSent - A pointer to the number of bytes sent by this
|
|
call.
|
|
|
|
dwFlags - Specifies the way in which the call is made.
|
|
|
|
lpTo - An optional pointer to the address of the target socket.
|
|
|
|
iTolen - The size of the address in lpTo.
|
|
|
|
lpOverlapped - A pointer to a WSAOVERLAPPED structure.
|
|
|
|
lpCompletionRoutine - A pointer to the completion routine called
|
|
when the send 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 send operation has completed immediately,
|
|
WSPSendTo() 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 operation was initiated and no completion indication will occur.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PSOCKET_INFORMATION socket;
|
|
IO_STATUS_BLOCK localIoStatusBlock;
|
|
PIO_STATUS_BLOCK ioStatusBlock;
|
|
AFD_SEND_DATAGRAM_INFO sendInfo;
|
|
PTRANSPORT_ADDRESS tdiAddress;
|
|
ULONG tdiAddressLength;
|
|
int err;
|
|
BOOLEAN nonBlocking;
|
|
UCHAR tdiAddressBuffer[MAX_FAST_TDI_ADDRESS];
|
|
WSABUF wsaBuf;
|
|
HANDLE event;
|
|
PIO_APC_ROUTINE apcRoutine;
|
|
PVOID apcContext;
|
|
|
|
WS_ENTER( "WSASendTo", (PVOID)Handle, (PVOID)lpBuffers, (PVOID)dwBufferCount, (PVOID)iFlags );
|
|
|
|
WS_ASSERT( lpErrno != NULL );
|
|
|
|
err = SockEnterApi( TRUE, TRUE, FALSE );
|
|
|
|
if( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPSelect", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
//
|
|
// Set up locals so that we know how to clean up on exit.
|
|
//
|
|
|
|
tdiAddress = (PTRANSPORT_ADDRESS)tdiAddressBuffer;
|
|
|
|
//
|
|
// 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 not a datagram socket, just call send() to process the
|
|
// call. The address and address length parameters are not checked.
|
|
//
|
|
|
|
if ( !IS_DGRAM_SOCK(socket->SocketType) ) {
|
|
|
|
INT ret;
|
|
|
|
SockDereferenceSocket( socket );
|
|
|
|
ret = WSPSend(
|
|
Handle,
|
|
lpBuffers,
|
|
dwBufferCount,
|
|
lpNumberOfBytesSent,
|
|
iFlags,
|
|
lpOverlapped,
|
|
lpCompletionRoutine,
|
|
lpThreadId,
|
|
lpErrno
|
|
);
|
|
|
|
WS_EXIT( "WSPSendTo", ret, (BOOLEAN)(ret == SOCKET_ERROR) );
|
|
return ret;
|
|
|
|
}
|
|
|
|
IF_DEBUG(SEND) {
|
|
|
|
WS_PRINT(( "WSASendTo() on socket %lx to addr", Handle ));
|
|
WsPrintSockaddr( (PSOCKADDR)SocketAddress, &SocketAddressLength );
|
|
|
|
}
|
|
|
|
//
|
|
// 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 sending data on.
|
|
//
|
|
|
|
SockAcquireSocketLockExclusive( socket );
|
|
|
|
//
|
|
// If the socket is not connected, then the Address and AddressLength
|
|
// fields must be specified.
|
|
//
|
|
|
|
if ( socket->State != SocketStateConnected ) {
|
|
|
|
if ( SocketAddress == NULL ) {
|
|
|
|
err = WSAENOTCONN;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if ( SocketAddressLength < socket->HelperDll->MinSockaddrLength ) {
|
|
|
|
err = WSAEFAULT;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
INT ret;
|
|
|
|
//
|
|
// If the socket is connected, it is illegal to specify a
|
|
// destination address. Weird, but that is what BSD 4.3
|
|
// does.
|
|
//
|
|
|
|
if ( SocketAddress != NULL || SocketAddressLength != 0 ) {
|
|
|
|
err = WSAEISCONN;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Use WSASend() to process the call.
|
|
//
|
|
|
|
SockReleaseSocketLock( socket );
|
|
SockDereferenceSocket( socket );
|
|
|
|
ret = WSPSend(
|
|
Handle,
|
|
lpBuffers,
|
|
dwBufferCount,
|
|
lpNumberOfBytesSent,
|
|
iFlags,
|
|
lpOverlapped,
|
|
lpCompletionRoutine,
|
|
lpThreadId,
|
|
lpErrno
|
|
);
|
|
|
|
WS_EXIT( "WSASendTo", ret, (BOOLEAN)(ret == SOCKET_ERROR) );
|
|
return ret;
|
|
|
|
}
|
|
|
|
//
|
|
// The legal flags are MSG_OOB, MSG_DONTROUTE, and MSG_PARTIAL.
|
|
// MSG_OOB is not legal on datagram sockets.
|
|
//
|
|
|
|
WS_ASSERT( IS_DGRAM_SOCK( socket->SocketType ) );
|
|
|
|
if ( ( (iFlags & ~(MSG_DONTROUTE)) != 0 ) ) {
|
|
|
|
err = WSAEOPNOTSUPP;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
nonBlocking = socket->NonBlocking;
|
|
|
|
//
|
|
// If data send has been shut down, fail.
|
|
//
|
|
|
|
if ( socket->SendShutdown ) {
|
|
|
|
err = WSAESHUTDOWN;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure that the address family passed in here is the same as
|
|
// was passed in on the socket( ) call.
|
|
//
|
|
|
|
if ( (short)socket->AddressFamily != SocketAddress->sa_family ) {
|
|
|
|
err = WSAEAFNOSUPPORT;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// If this socket has not been set to allow broadcasts, check if this
|
|
// is an attempt to send to a broadcast address.
|
|
//
|
|
|
|
if ( !socket->Broadcast ) {
|
|
|
|
SOCKADDR_INFO sockaddrInfo;
|
|
|
|
err = socket->HelperDll->WSHGetSockaddrType(
|
|
(PSOCKADDR)SocketAddress,
|
|
SocketAddressLength,
|
|
&sockaddrInfo
|
|
);
|
|
|
|
if ( err != NO_ERROR) {
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// If this is an attempt to send to a broadcast address, reject
|
|
// the attempt.
|
|
//
|
|
|
|
if ( sockaddrInfo.AddressInfo == SockaddrAddressInfoBroadcast ) {
|
|
|
|
err = WSAEACCES;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// If this socket is not yet bound to an address, bind it to an
|
|
// address. We only do this if the helper DLL for the socket supports
|
|
// a get wildcard address routine--if it doesn't, the app must bind
|
|
// to an address manually.
|
|
//
|
|
|
|
if ( socket->State == SocketStateOpen &&
|
|
socket->HelperDll->WSHGetWildcardSockaddr != NULL ) {
|
|
|
|
PSOCKADDR sockaddr;
|
|
INT sockaddrLength = socket->HelperDll->MaxSockaddrLength;
|
|
int result;
|
|
|
|
sockaddr = ALLOCATE_HEAP( sockaddrLength );
|
|
|
|
if ( sockaddr == NULL ) {
|
|
|
|
err = WSAENOBUFS;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
err = socket->HelperDll->WSHGetWildcardSockaddr(
|
|
socket->HelperDllContext,
|
|
sockaddr,
|
|
&sockaddrLength
|
|
);
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
FREE_HEAP( sockaddr );
|
|
goto exit;
|
|
|
|
}
|
|
|
|
result = WSPBind(
|
|
Handle,
|
|
sockaddr,
|
|
sockaddrLength,
|
|
&err
|
|
);
|
|
|
|
FREE_HEAP( sockaddr );
|
|
|
|
if( result == SOCKET_ERROR ) {
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
} else if ( socket->State == SocketStateOpen ) {
|
|
|
|
//
|
|
// The socket is not bound and the helper DLL does not support
|
|
// a wildcard socket address. Fail, the app must bind manually.
|
|
//
|
|
|
|
err = WSAEINVAL;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
//
|
|
// Allocate enough space to hold the TDI address structure we'll pass
|
|
// to AFD. Note that is the address is small enough, we just use
|
|
// an automatic in order to improve performance.
|
|
//
|
|
|
|
tdiAddressLength = socket->HelperDll->MaxTdiAddressLength;
|
|
|
|
if ( tdiAddressLength > MAX_FAST_TDI_ADDRESS ) {
|
|
|
|
tdiAddress = ALLOCATE_HEAP( tdiAddressLength );
|
|
|
|
if ( tdiAddress == NULL ) {
|
|
|
|
err = WSAENOBUFS;
|
|
goto exit;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
WS_ASSERT( (PUCHAR)tdiAddress == tdiAddressBuffer );
|
|
|
|
}
|
|
|
|
//
|
|
// Convert the address from the sockaddr structure to the appropriate
|
|
// TDI structure.
|
|
//
|
|
|
|
SockBuildTdiAddress(
|
|
tdiAddress,
|
|
(PSOCKADDR)SocketAddress,
|
|
SocketAddressLength
|
|
);
|
|
|
|
//
|
|
// Set up the AFD_SEND_DATAGRAM_INFO structure.
|
|
//
|
|
|
|
sendInfo.BufferArray = lpBuffers;
|
|
sendInfo.BufferCount = dwBufferCount;
|
|
sendInfo.AfdFlags = 0;
|
|
|
|
//
|
|
// Set up the TDI_REQUEST structure to send the datagram.
|
|
//
|
|
|
|
sendInfo.TdiRequest.SendDatagramInformation = &sendInfo.TdiConnInfo;
|
|
|
|
sendInfo.TdiConnInfo.UserDataLength = 0;
|
|
sendInfo.TdiConnInfo.UserData = NULL;
|
|
sendInfo.TdiConnInfo.OptionsLength = 0;
|
|
sendInfo.TdiConnInfo.Options = NULL;
|
|
sendInfo.TdiConnInfo.RemoteAddressLength = tdiAddressLength;
|
|
sendInfo.TdiConnInfo.RemoteAddress = tdiAddress;
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
|
|
sendInfo.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.
|
|
//
|
|
|
|
sendInfo.AfdFlags |= AFD_OVERLAPPED;
|
|
|
|
}
|
|
|
|
ioStatusBlock->Status = STATUS_PENDING;
|
|
|
|
//
|
|
// Send the data over the socket.
|
|
//
|
|
|
|
status = NtDeviceIoControlFile(
|
|
(HANDLE)socket->Handle,
|
|
event,
|
|
apcRoutine,
|
|
apcContext,
|
|
ioStatusBlock,
|
|
IOCTL_AFD_SEND_DATAGRAM,
|
|
&sendInfo,
|
|
sizeof(sendInfo),
|
|
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_SEND_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;
|
|
}
|
|
|
|
}
|
|
|
|
if ( status == STATUS_SUCCESS ) {
|
|
|
|
//
|
|
// The request completed immediately, so return the number of
|
|
// bytes sent to the user.
|
|
//
|
|
|
|
*lpNumberOfBytesSent = ioStatusBlock->Information;
|
|
|
|
} else if ( status == STATUS_PENDING ) {
|
|
|
|
err = WSA_IO_PENDING;
|
|
|
|
} else if( !NT_SUCCESS(status) ) {
|
|
|
|
//
|
|
// Map the NTSTATUS to a WinSock error code.
|
|
//
|
|
|
|
err = SockNtStatusToSocketError( status );
|
|
|
|
}
|
|
|
|
exit:
|
|
|
|
IF_DEBUG(SEND) {
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_PRINT(( "WSASendTo on socket %lx (%lx) failed: %ld.\n",
|
|
Handle, socket, err ));
|
|
|
|
} else {
|
|
|
|
WS_PRINT(( "WSASendTo on socket %lx (%lx) succeeded, "
|
|
"bytes = %ld\n",
|
|
Handle, socket, ioStatusBlock->Information ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( socket != NULL ) {
|
|
|
|
if ( err == WSAEWOULDBLOCK ) {
|
|
|
|
SockReenableAsyncSelectEvent( socket, FD_WRITE );
|
|
|
|
}
|
|
|
|
SockReleaseSocketLock( socket );
|
|
SockDereferenceSocket( socket );
|
|
|
|
}
|
|
|
|
if ( tdiAddress != NULL &&
|
|
tdiAddress != (PTRANSPORT_ADDRESS)tdiAddressBuffer ) {
|
|
|
|
FREE_HEAP( tdiAddress );
|
|
|
|
}
|
|
|
|
if ( err != NO_ERROR ) {
|
|
|
|
WS_EXIT( "WSPSendTo", SOCKET_ERROR, TRUE );
|
|
*lpErrno = err;
|
|
return SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
WS_EXIT( "WSPSendTo", 0, FALSE );
|
|
return 0;
|
|
|
|
} // WSPSendTo
|
|
|