reactos/dll/win32/msafd/misc/sndrcv.c
Julio Carchi 442f5dfab5
[MSAFD] Fix non-blocking sockets support for recv() (#5575) CORE-14486
Currently ReactOS' winsock2 implementation lacks of non-blocking sockets support for recv() apicall, this causes that applications that make use of this feature can lead to unexpected behaviors, one of them is Nginx web server, which uses non-blocking sockets when serving pages through Https protocol.
CORE-14486

It also brings us significantly closer in master head to running Firefox 52, Mypal 29.3.0 and New Moon 28 browser, where the latter allows to connect to mattermost from within ReactOS.
In master head an additional reg file is needed to stop us from exporting specific NT6+ APIs, but in older releases all that should work out of the box with this brilliant patch.


Co-authored-by: Julio Carchi Ruiz <julcar@informaticos.com>
Co-authored-by: Stanislav Motylkov <x86corez@gmail.com>
2023-08-17 23:13:13 +02:00

946 lines
29 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS Ancillary Function Driver DLL
* FILE: dll/win32/msafd/misc/sndrcv.c
* PURPOSE: Send/receive routines
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
* Alex Ionescu (alex@relsoft.net)
* REVISIONS:
* CSH 01/09-2000 Created
* Alex 16/07/2004 - Complete Rewrite
*/
#include <msafd.h>
INT
WSPAPI
WSPAsyncSelect(IN SOCKET Handle,
IN HWND hWnd,
IN UINT wMsg,
IN LONG lEvent,
OUT LPINT lpErrno)
{
PSOCKET_INFORMATION Socket = NULL;
PASYNC_DATA AsyncData;
BOOLEAN BlockMode;
/* Get the Socket Structure associated to this Socket */
Socket = GetSocketStructure(Handle);
if (!Socket)
{
*lpErrno = WSAENOTSOCK;
return SOCKET_ERROR;
}
/* Allocate the Async Data Structure to pass on to the Thread later */
AsyncData = HeapAlloc(GetProcessHeap(), 0, sizeof(*AsyncData));
if (!AsyncData)
{
MsafdReturnWithErrno( STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, NULL );
return INVALID_SOCKET;
}
/* Change the Socket to Non Blocking */
BlockMode = TRUE;
SetSocketInformation(Socket, AFD_INFO_BLOCKING_MODE, &BlockMode, NULL, NULL, NULL, NULL);
Socket->SharedData->NonBlocking = TRUE;
/* Deactivate WSPEventSelect */
if (Socket->SharedData->AsyncEvents)
{
if (WSPEventSelect(Handle, NULL, 0, lpErrno) == SOCKET_ERROR)
{
HeapFree(GetProcessHeap(), 0, AsyncData);
return SOCKET_ERROR;
}
}
/* Create the Asynch Thread if Needed */
SockCreateOrReferenceAsyncThread();
/* Open a Handle to AFD's Async Helper */
SockGetAsyncSelectHelperAfdHandle();
/* Store Socket Data */
Socket->SharedData->hWnd = hWnd;
Socket->SharedData->wMsg = wMsg;
Socket->SharedData->AsyncEvents = lEvent;
Socket->SharedData->AsyncDisabledEvents = 0;
Socket->SharedData->SequenceNumber++;
/* Return if there are no more Events */
if ((Socket->SharedData->AsyncEvents & (~Socket->SharedData->AsyncDisabledEvents)) == 0)
{
HeapFree(GetProcessHeap(), 0, AsyncData);
return 0;
}
/* Set up the Async Data */
AsyncData->ParentSocket = Socket;
AsyncData->SequenceNumber = Socket->SharedData->SequenceNumber;
/* Begin Async Select by using I/O Completion */
NtSetIoCompletion(SockAsyncCompletionPort,
(PVOID)&SockProcessQueuedAsyncSelect,
AsyncData,
0,
0);
/* Return */
return ERROR_SUCCESS;
}
BOOL
WSPAPI
WSPGetOverlappedResult(
IN SOCKET Handle,
IN LPWSAOVERLAPPED lpOverlapped,
OUT LPDWORD lpdwBytes,
IN BOOL fWait,
OUT LPDWORD lpdwFlags,
OUT LPINT lpErrno)
{
PSOCKET_INFORMATION Socket;
BOOL Ret;
TRACE("Called (%x)\n", Handle);
/* Get the Socket Structure associate to this Socket*/
Socket = GetSocketStructure(Handle);
if (!Socket)
{
if(lpErrno)
*lpErrno = WSAENOTSOCK;
return FALSE;
}
if (!lpOverlapped || !lpdwBytes || !lpdwFlags)
{
if (lpErrno)
*lpErrno = WSAEFAULT;
return FALSE;
}
Ret = GetOverlappedResult((HANDLE)Handle, lpOverlapped, lpdwBytes, fWait);
if (Ret)
{
*lpdwFlags = 0;
/* Re-enable Async Event */
SockReenableAsyncSelectEvent(Socket, FD_OOB);
SockReenableAsyncSelectEvent(Socket, FD_WRITE);
SockReenableAsyncSelectEvent(Socket, FD_READ);
}
return Ret;
}
VOID
NTAPI
AfdAPC(PVOID ApcContext,
PIO_STATUS_BLOCK IoStatusBlock,
ULONG Reserved)
{
PAFDAPCCONTEXT Context = ApcContext;
/* Re-enable Async Event */
SockReenableAsyncSelectEvent(Context->lpSocket, FD_OOB);
SockReenableAsyncSelectEvent(Context->lpSocket, FD_READ);
SockReenableAsyncSelectEvent(Context->lpSocket, FD_WRITE);
Context->lpCompletionRoutine(IoStatusBlock->Status, IoStatusBlock->Information, Context->lpOverlapped, 0);
HeapFree(GlobalHeap, 0, ApcContext);
}
int
WSPAPI
WSPRecv(SOCKET Handle,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRead,
LPDWORD ReceiveFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
LPWSATHREADID lpThreadId,
LPINT lpErrno)
{
PIO_STATUS_BLOCK IOSB;
IO_STATUS_BLOCK DummyIOSB;
AFD_RECV_INFO RecvInfo;
NTSTATUS Status;
PVOID APCContext;
PIO_APC_ROUTINE APCFunction;
HANDLE Event = NULL;
HANDLE SockEvent;
PSOCKET_INFORMATION Socket;
TRACE("Called (%x)\n", Handle);
/* Get the Socket Structure associate to this Socket*/
Socket = GetSocketStructure(Handle);
if (!Socket)
{
if (lpErrno)
*lpErrno = WSAENOTSOCK;
return SOCKET_ERROR;
}
if (!lpNumberOfBytesRead && !lpOverlapped)
{
if (lpErrno)
*lpErrno = WSAEFAULT;
return SOCKET_ERROR;
}
if (Socket->SharedData->OobInline && ReceiveFlags && (*ReceiveFlags & MSG_OOB) != 0)
{
if (lpErrno)
*lpErrno = WSAEINVAL;
return SOCKET_ERROR;
}
Status = NtCreateEvent( &SockEvent, EVENT_ALL_ACCESS,
NULL, SynchronizationEvent, FALSE );
if( !NT_SUCCESS(Status) )
return -1;
/* Set up the Receive Structure */
RecvInfo.BufferArray = (PAFD_WSABUF)lpBuffers;
RecvInfo.BufferCount = dwBufferCount;
RecvInfo.TdiFlags = 0;
RecvInfo.AfdFlags = Socket->SharedData->NonBlocking ? AFD_IMMEDIATE : 0;
/* Set the TDI Flags */
if (*ReceiveFlags == 0)
{
RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL;
}
else
{
if (*ReceiveFlags & MSG_OOB)
{
RecvInfo.TdiFlags |= TDI_RECEIVE_EXPEDITED;
}
if (*ReceiveFlags & MSG_PEEK)
{
RecvInfo.TdiFlags |= TDI_RECEIVE_PEEK;
}
if (*ReceiveFlags & MSG_PARTIAL)
{
RecvInfo.TdiFlags |= TDI_RECEIVE_PARTIAL;
}
}
/* Verify if we should use APC */
if (lpOverlapped == NULL)
{
/* Not using Overlapped structure, so use normal blocking on event */
APCContext = NULL;
APCFunction = NULL;
Event = SockEvent;
IOSB = &DummyIOSB;
}
else
{
/* Overlapped request for non overlapped opened socket */
if ((Socket->SharedData->CreateFlags & SO_SYNCHRONOUS_NONALERT) != 0)
{
TRACE("Opened without flag WSA_FLAG_OVERLAPPED. Do nothing.\n");
return MsafdReturnWithErrno(0, lpErrno, 0, lpNumberOfBytesRead);
}
if (lpCompletionRoutine == NULL)
{
/* Using Overlapped Structure, but no Completion Routine, so no need for APC */
APCContext = lpOverlapped;
APCFunction = NULL;
Event = lpOverlapped->hEvent;
}
else
{
/* Using Overlapped Structure and a Completion Routine, so use an APC */
APCFunction = &AfdAPC; // should be a private io completion function inside us
APCContext = HeapAlloc(GlobalHeap, 0, sizeof(AFDAPCCONTEXT));
if (!APCContext)
{
ERR("Not enough memory for APC Context\n");
return MsafdReturnWithErrno(STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, lpNumberOfBytesRead);
}
((PAFDAPCCONTEXT)APCContext)->lpCompletionRoutine = lpCompletionRoutine;
((PAFDAPCCONTEXT)APCContext)->lpOverlapped = lpOverlapped;
((PAFDAPCCONTEXT)APCContext)->lpSocket = Socket;
RecvInfo.AfdFlags |= AFD_SKIP_FIO;
}
IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
RecvInfo.AfdFlags |= AFD_OVERLAPPED;
}
IOSB->Status = STATUS_PENDING;
/* Send IOCTL */
Status = NtDeviceIoControlFile((HANDLE)Handle,
Event,
APCFunction,
APCContext,
IOSB,
IOCTL_AFD_RECV,
&RecvInfo,
sizeof(RecvInfo),
NULL,
0);
/* Non-blocking sockets must wait until data is available */
if (Status == STATUS_PENDING && Socket->SharedData->NonBlocking)
{
if (lpErrno) *lpErrno = WSAEWOULDBLOCK;
return SOCKET_ERROR;
}
/* Wait for completion of not overlapped */
if (Status == STATUS_PENDING && lpOverlapped == NULL)
{
/* It's up to the protocol to time out recv. We must wait
* until the protocol decides it's had enough.
*/
WaitForSingleObject(SockEvent, INFINITE);
Status = IOSB->Status;
}
NtClose( SockEvent );
TRACE("Status %x Information %d\n", Status, IOSB->Information);
if (Status == STATUS_PENDING)
{
TRACE("Leaving (Pending)\n");
return MsafdReturnWithErrno(Status, lpErrno, IOSB->Information, lpNumberOfBytesRead);
}
/* Return the Flags */
*ReceiveFlags = 0;
switch (Status)
{
case STATUS_RECEIVE_EXPEDITED:
*ReceiveFlags = MSG_OOB;
break;
case STATUS_RECEIVE_PARTIAL_EXPEDITED:
*ReceiveFlags = MSG_PARTIAL | MSG_OOB;
break;
case STATUS_RECEIVE_PARTIAL:
*ReceiveFlags = MSG_PARTIAL;
break;
}
/* Re-enable Async Event */
if (*ReceiveFlags & MSG_OOB)
{
SockReenableAsyncSelectEvent(Socket, FD_OOB);
}
else
{
SockReenableAsyncSelectEvent(Socket, FD_READ);
}
if (Status == STATUS_SUCCESS && lpOverlapped && lpCompletionRoutine)
{
lpCompletionRoutine(Status, IOSB->Information, lpOverlapped, *ReceiveFlags);
HeapFree(GlobalHeap, 0, (PVOID)APCContext);
}
return MsafdReturnWithErrno ( Status, lpErrno, IOSB->Information, lpNumberOfBytesRead );
}
int
WSPAPI
WSPRecvFrom(SOCKET Handle,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesRead,
LPDWORD ReceiveFlags,
struct sockaddr *SocketAddress,
int *SocketAddressLength,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
LPWSATHREADID lpThreadId,
LPINT lpErrno )
{
PIO_STATUS_BLOCK IOSB;
IO_STATUS_BLOCK DummyIOSB;
AFD_RECV_INFO_UDP RecvInfo;
NTSTATUS Status;
PVOID APCContext;
PVOID APCFunction;
HANDLE Event = NULL;
HANDLE SockEvent;
PSOCKET_INFORMATION Socket;
/* Get the Socket Structure associate to this Socket*/
Socket = GetSocketStructure(Handle);
if (!Socket)
{
if (lpErrno)
*lpErrno = WSAENOTSOCK;
return SOCKET_ERROR;
}
if (!lpNumberOfBytesRead && !lpOverlapped)
{
if (lpErrno)
*lpErrno = WSAEFAULT;
return SOCKET_ERROR;
}
if (Socket->SharedData->OobInline && ReceiveFlags && (*ReceiveFlags & MSG_OOB) != 0)
{
if (lpErrno)
*lpErrno = WSAEINVAL;
return SOCKET_ERROR;
}
if (!(Socket->SharedData->ServiceFlags1 & XP1_CONNECTIONLESS))
{
/* Call WSPRecv for a non-datagram socket */
return WSPRecv(Handle,
lpBuffers,
dwBufferCount,
lpNumberOfBytesRead,
ReceiveFlags,
lpOverlapped,
lpCompletionRoutine,
lpThreadId,
lpErrno);
}
/* Bind us First */
if (Socket->SharedData->State == SocketOpen)
{
Socket->HelperData->WSHGetWildcardSockaddr(Socket->HelperContext,
SocketAddress,
SocketAddressLength);
/* Bind it */
if (WSPBind(Handle, SocketAddress, *SocketAddressLength, lpErrno) == SOCKET_ERROR)
return SOCKET_ERROR;
}
Status = NtCreateEvent( &SockEvent, EVENT_ALL_ACCESS,
NULL, SynchronizationEvent, FALSE );
if( !NT_SUCCESS(Status) )
return -1;
/* Set up the Receive Structure */
RecvInfo.BufferArray = (PAFD_WSABUF)lpBuffers;
RecvInfo.BufferCount = dwBufferCount;
RecvInfo.TdiFlags = 0;
RecvInfo.AfdFlags = Socket->SharedData->NonBlocking ? AFD_IMMEDIATE : 0;
RecvInfo.AddressLength = SocketAddressLength;
RecvInfo.Address = SocketAddress;
/* Set the TDI Flags */
if (*ReceiveFlags == 0)
{
RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL;
}
else
{
if (*ReceiveFlags & MSG_OOB)
{
RecvInfo.TdiFlags |= TDI_RECEIVE_EXPEDITED;
}
if (*ReceiveFlags & MSG_PEEK)
{
RecvInfo.TdiFlags |= TDI_RECEIVE_PEEK;
}
if (*ReceiveFlags & MSG_PARTIAL)
{
RecvInfo.TdiFlags |= TDI_RECEIVE_PARTIAL;
}
}
/* Verify if we should use APC */
if (lpOverlapped == NULL)
{
/* Not using Overlapped structure, so use normal blocking on event */
APCContext = NULL;
APCFunction = NULL;
Event = SockEvent;
IOSB = &DummyIOSB;
}
else
{
/* Overlapped request for non overlapped opened socket */
if ((Socket->SharedData->CreateFlags & SO_SYNCHRONOUS_NONALERT) != 0)
{
TRACE("Opened without flag WSA_FLAG_OVERLAPPED. Do nothing.\n");
return MsafdReturnWithErrno(0, lpErrno, 0, lpNumberOfBytesRead);
}
if (lpCompletionRoutine == NULL)
{
/* Using Overlapped Structure, but no Completion Routine, so no need for APC */
APCContext = lpOverlapped;
APCFunction = NULL;
Event = lpOverlapped->hEvent;
}
else
{
/* Using Overlapped Structure and a Completion Routine, so use an APC */
APCFunction = &AfdAPC; // should be a private io completion function inside us
APCContext = HeapAlloc(GlobalHeap, 0, sizeof(AFDAPCCONTEXT));
if (!APCContext)
{
ERR("Not enough memory for APC Context\n");
return MsafdReturnWithErrno(STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, lpNumberOfBytesRead);
}
((PAFDAPCCONTEXT)APCContext)->lpCompletionRoutine = lpCompletionRoutine;
((PAFDAPCCONTEXT)APCContext)->lpOverlapped = lpOverlapped;
((PAFDAPCCONTEXT)APCContext)->lpSocket = Socket;
RecvInfo.AfdFlags |= AFD_SKIP_FIO;
}
IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
RecvInfo.AfdFlags |= AFD_OVERLAPPED;
}
IOSB->Status = STATUS_PENDING;
/* Send IOCTL */
Status = NtDeviceIoControlFile((HANDLE)Handle,
Event,
APCFunction,
APCContext,
IOSB,
IOCTL_AFD_RECV_DATAGRAM,
&RecvInfo,
sizeof(RecvInfo),
NULL,
0);
/* Wait for completion of not overlapped */
if (Status == STATUS_PENDING && lpOverlapped == NULL)
{
WaitForSingleObject(SockEvent, INFINITE); // BUGBUG, shouldn wait infinitely for receive...
Status = IOSB->Status;
}
NtClose( SockEvent );
if (Status == STATUS_PENDING)
{
TRACE("Leaving (Pending)\n");
return MsafdReturnWithErrno(Status, lpErrno, IOSB->Information, lpNumberOfBytesRead);
}
/* Return the Flags */
*ReceiveFlags = 0;
switch (Status)
{
case STATUS_RECEIVE_EXPEDITED:
*ReceiveFlags = MSG_OOB;
break;
case STATUS_RECEIVE_PARTIAL_EXPEDITED:
*ReceiveFlags = MSG_PARTIAL | MSG_OOB;
break;
case STATUS_RECEIVE_PARTIAL:
*ReceiveFlags = MSG_PARTIAL;
break;
}
/* Re-enable Async Event */
if (*ReceiveFlags & MSG_OOB)
{
SockReenableAsyncSelectEvent(Socket, FD_OOB);
}
else
{
SockReenableAsyncSelectEvent(Socket, FD_READ);
}
if (Status == STATUS_SUCCESS && lpOverlapped && lpCompletionRoutine)
{
lpCompletionRoutine(Status, IOSB->Information, lpOverlapped, *ReceiveFlags);
HeapFree(GlobalHeap, 0, (PVOID)APCContext);
}
return MsafdReturnWithErrno ( Status, lpErrno, IOSB->Information, lpNumberOfBytesRead );
}
int
WSPAPI
WSPSend(SOCKET Handle,
LPWSABUF lpBuffers,
DWORD dwBufferCount,
LPDWORD lpNumberOfBytesSent,
DWORD iFlags,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
LPWSATHREADID lpThreadId,
LPINT lpErrno)
{
PIO_STATUS_BLOCK IOSB;
IO_STATUS_BLOCK DummyIOSB;
AFD_SEND_INFO SendInfo;
NTSTATUS Status;
PVOID APCContext;
PVOID APCFunction;
HANDLE Event = NULL;
HANDLE SockEvent;
PSOCKET_INFORMATION Socket;
/* Get the Socket Structure associate to this Socket*/
Socket = GetSocketStructure(Handle);
if (!Socket)
{
if (lpErrno)
*lpErrno = WSAENOTSOCK;
return SOCKET_ERROR;
}
if (!lpNumberOfBytesSent && !lpOverlapped)
{
if (lpErrno)
*lpErrno = WSAEFAULT;
return SOCKET_ERROR;
}
Status = NtCreateEvent( &SockEvent, EVENT_ALL_ACCESS,
NULL, SynchronizationEvent, FALSE );
if( !NT_SUCCESS(Status) )
return -1;
TRACE("Called\n");
/* Set up the Send Structure */
SendInfo.BufferArray = (PAFD_WSABUF)lpBuffers;
SendInfo.BufferCount = dwBufferCount;
SendInfo.TdiFlags = 0;
SendInfo.AfdFlags = Socket->SharedData->NonBlocking ? AFD_IMMEDIATE : 0;
/* Set the TDI Flags */
if (iFlags)
{
if (iFlags & MSG_OOB)
{
SendInfo.TdiFlags |= TDI_SEND_EXPEDITED;
}
if (iFlags & MSG_PARTIAL)
{
SendInfo.TdiFlags |= TDI_SEND_PARTIAL;
}
}
/* Verify if we should use APC */
if (lpOverlapped == NULL)
{
/* Not using Overlapped structure, so use normal blocking on event */
APCContext = NULL;
APCFunction = NULL;
Event = SockEvent;
IOSB = &DummyIOSB;
}
else
{
/* Overlapped request for non overlapped opened socket */
if ((Socket->SharedData->CreateFlags & SO_SYNCHRONOUS_NONALERT) != 0)
{
TRACE("Opened without flag WSA_FLAG_OVERLAPPED. Do nothing.\n");
return MsafdReturnWithErrno(0, lpErrno, 0, lpNumberOfBytesSent);
}
if (lpCompletionRoutine == NULL)
{
/* Using Overlapped Structure, but no Completion Routine, so no need for APC */
APCContext = lpOverlapped;
APCFunction = NULL;
Event = lpOverlapped->hEvent;
}
else
{
/* Using Overlapped Structure and a Completion Routine, so use an APC */
APCFunction = &AfdAPC; // should be a private io completion function inside us
APCContext = HeapAlloc(GlobalHeap, 0, sizeof(AFDAPCCONTEXT));
if (!APCContext)
{
ERR("Not enough memory for APC Context\n");
return MsafdReturnWithErrno(STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, lpNumberOfBytesSent);
}
((PAFDAPCCONTEXT)APCContext)->lpCompletionRoutine = lpCompletionRoutine;
((PAFDAPCCONTEXT)APCContext)->lpOverlapped = lpOverlapped;
((PAFDAPCCONTEXT)APCContext)->lpSocket = Socket;
SendInfo.AfdFlags |= AFD_SKIP_FIO;
}
IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
SendInfo.AfdFlags |= AFD_OVERLAPPED;
}
IOSB->Status = STATUS_PENDING;
/* Send IOCTL */
Status = NtDeviceIoControlFile((HANDLE)Handle,
Event,
APCFunction,
APCContext,
IOSB,
IOCTL_AFD_SEND,
&SendInfo,
sizeof(SendInfo),
NULL,
0);
/* Wait for completion of not overlapped */
if (Status == STATUS_PENDING && lpOverlapped == NULL)
{
WaitForSingleObject(SockEvent, INFINITE); // BUGBUG, shouldn wait infinitely for send...
Status = IOSB->Status;
}
NtClose( SockEvent );
if (Status == STATUS_PENDING)
{
TRACE("Leaving (Pending)\n");
return MsafdReturnWithErrno(Status, lpErrno, IOSB->Information, lpNumberOfBytesSent);
}
/* Re-enable Async Event */
SockReenableAsyncSelectEvent(Socket, FD_WRITE);
TRACE("Leaving (Success, %d)\n", IOSB->Information);
if (Status == STATUS_SUCCESS && lpOverlapped && lpCompletionRoutine)
{
lpCompletionRoutine(Status, IOSB->Information, lpOverlapped, 0);
HeapFree(GlobalHeap, 0, (PVOID)APCContext);
}
return MsafdReturnWithErrno( Status, lpErrno, IOSB->Information, lpNumberOfBytesSent );
}
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)
{
PIO_STATUS_BLOCK IOSB;
IO_STATUS_BLOCK DummyIOSB;
AFD_SEND_INFO_UDP SendInfo;
NTSTATUS Status;
PVOID APCContext;
PVOID APCFunction;
HANDLE Event = NULL;
PTRANSPORT_ADDRESS RemoteAddress;
PSOCKADDR BindAddress = NULL;
INT BindAddressLength;
HANDLE SockEvent;
PSOCKET_INFORMATION Socket;
/* Get the Socket Structure associate to this Socket */
Socket = GetSocketStructure(Handle);
if (!Socket)
{
if (lpErrno)
*lpErrno = WSAENOTSOCK;
return SOCKET_ERROR;
}
if (!lpNumberOfBytesSent && !lpOverlapped)
{
if (lpErrno)
*lpErrno = WSAEFAULT;
return SOCKET_ERROR;
}
if (!(Socket->SharedData->ServiceFlags1 & XP1_CONNECTIONLESS))
{
/* Use WSPSend for connection-oriented sockets */
return WSPSend(Handle,
lpBuffers,
dwBufferCount,
lpNumberOfBytesSent,
iFlags,
lpOverlapped,
lpCompletionRoutine,
lpThreadId,
lpErrno);
}
/* Bind us First */
if (Socket->SharedData->State == SocketOpen)
{
/* Get the Wildcard Address */
BindAddressLength = Socket->HelperData->MaxWSAddressLength;
BindAddress = HeapAlloc(GlobalHeap, 0, BindAddressLength);
if (!BindAddress)
{
MsafdReturnWithErrno(STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, NULL);
return INVALID_SOCKET;
}
Socket->HelperData->WSHGetWildcardSockaddr(Socket->HelperContext,
BindAddress,
&BindAddressLength);
/* Bind it */
if (WSPBind(Handle, BindAddress, BindAddressLength, lpErrno) == SOCKET_ERROR)
return SOCKET_ERROR;
}
RemoteAddress = HeapAlloc(GlobalHeap, 0, 0x6 + SocketAddressLength);
if (!RemoteAddress)
{
if (BindAddress != NULL)
{
HeapFree(GlobalHeap, 0, BindAddress);
}
return MsafdReturnWithErrno(STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, NULL);
}
Status = NtCreateEvent(&SockEvent,
EVENT_ALL_ACCESS,
NULL, SynchronizationEvent, FALSE);
if (!NT_SUCCESS(Status))
{
HeapFree(GlobalHeap, 0, RemoteAddress);
if (BindAddress != NULL)
{
HeapFree(GlobalHeap, 0, BindAddress);
}
return SOCKET_ERROR;
}
/* Set up Address in TDI Format */
RemoteAddress->TAAddressCount = 1;
RemoteAddress->Address[0].AddressLength = SocketAddressLength - sizeof(SocketAddress->sa_family);
RtlCopyMemory(&RemoteAddress->Address[0].AddressType, SocketAddress, SocketAddressLength);
/* Set up Structure */
SendInfo.BufferArray = (PAFD_WSABUF)lpBuffers;
SendInfo.AfdFlags = Socket->SharedData->NonBlocking ? AFD_IMMEDIATE : 0;
SendInfo.BufferCount = dwBufferCount;
SendInfo.TdiConnection.RemoteAddress = RemoteAddress;
SendInfo.TdiConnection.RemoteAddressLength = Socket->HelperData->MaxTDIAddressLength;
/* Verify if we should use APC */
if (lpOverlapped == NULL)
{
/* Not using Overlapped structure, so use normal blocking on event */
APCContext = NULL;
APCFunction = NULL;
Event = SockEvent;
IOSB = &DummyIOSB;
}
else
{
/* Overlapped request for non overlapped opened socket */
if ((Socket->SharedData->CreateFlags & SO_SYNCHRONOUS_NONALERT) != 0)
{
TRACE("Opened without flag WSA_FLAG_OVERLAPPED. Do nothing.\n");
return MsafdReturnWithErrno(0, lpErrno, 0, lpNumberOfBytesSent);
}
if (lpCompletionRoutine == NULL)
{
/* Using Overlapped Structure, but no Completion Routine, so no need for APC */
APCContext = lpOverlapped;
APCFunction = NULL;
Event = lpOverlapped->hEvent;
}
else
{
/* Using Overlapped Structure and a Completion Routine, so use an APC */
APCFunction = &AfdAPC; // should be a private io completion function inside us
APCContext = HeapAlloc(GlobalHeap, 0, sizeof(AFDAPCCONTEXT));
if (!APCContext)
{
ERR("Not enough memory for APC Context\n");
return MsafdReturnWithErrno(STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, lpNumberOfBytesSent);
}
((PAFDAPCCONTEXT)APCContext)->lpCompletionRoutine = lpCompletionRoutine;
((PAFDAPCCONTEXT)APCContext)->lpOverlapped = lpOverlapped;
((PAFDAPCCONTEXT)APCContext)->lpSocket = Socket;
SendInfo.AfdFlags |= AFD_SKIP_FIO;
}
IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal;
SendInfo.AfdFlags |= AFD_OVERLAPPED;
}
/* Send IOCTL */
Status = NtDeviceIoControlFile((HANDLE)Handle,
Event,
APCFunction,
APCContext,
IOSB,
IOCTL_AFD_SEND_DATAGRAM,
&SendInfo,
sizeof(SendInfo),
NULL,
0);
/* Wait for completion of not overlapped */
if (Status == STATUS_PENDING && lpOverlapped == NULL)
{
/* BUGBUG, shouldn't wait infinitely for send... */
WaitForSingleObject(SockEvent, INFINITE);
Status = IOSB->Status;
}
NtClose(SockEvent);
HeapFree(GlobalHeap, 0, RemoteAddress);
if (BindAddress != NULL)
{
HeapFree(GlobalHeap, 0, BindAddress);
}
if (Status == STATUS_PENDING)
{
TRACE("Leaving (Pending)\n");
return MsafdReturnWithErrno(Status, lpErrno, IOSB->Information, lpNumberOfBytesSent);
}
SockReenableAsyncSelectEvent(Socket, FD_WRITE);
if (Status == STATUS_SUCCESS && lpOverlapped && lpCompletionRoutine)
{
lpCompletionRoutine(Status, IOSB->Information, lpOverlapped, 0);
HeapFree(GlobalHeap, 0, (PVOID)APCContext);
}
return MsafdReturnWithErrno(Status, lpErrno, IOSB->Information, lpNumberOfBytesSent);
}
INT
WSPAPI
WSPRecvDisconnect(IN SOCKET s,
OUT LPWSABUF lpInboundDisconnectData,
OUT LPINT lpErrno)
{
UNIMPLEMENTED;
return 0;
}
INT
WSPAPI
WSPSendDisconnect(IN SOCKET s,
IN LPWSABUF lpOutboundDisconnectData,
OUT LPINT lpErrno)
{
UNIMPLEMENTED;
return 0;
}
/* EOF */