mirror of
https://github.com/reactos/reactos.git
synced 2024-11-04 05:43:30 +00:00
4077 lines
130 KiB
C
4077 lines
130 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS Ancillary Function Driver DLL
|
|
* FILE: dll/win32/msafd/misc/dllmain.c
|
|
* PURPOSE: DLL entry point
|
|
* PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
|
|
* Alex Ionescu (alex@relsoft.net)
|
|
* Pierre Schweitzer (pierre@reactos.org)
|
|
* REVISIONS:
|
|
* CSH 01/09-2000 Created
|
|
* Alex 16/07/2004 - Complete Rewrite
|
|
*/
|
|
|
|
#include <msafd.h>
|
|
|
|
#include <winuser.h>
|
|
#include <wchar.h>
|
|
|
|
HANDLE GlobalHeap;
|
|
WSPUPCALLTABLE Upcalls;
|
|
DWORD CatalogEntryId; /* CatalogEntryId for upcalls */
|
|
LPWPUCOMPLETEOVERLAPPEDREQUEST lpWPUCompleteOverlappedRequest;
|
|
PSOCKET_INFORMATION SocketListHead = NULL;
|
|
CRITICAL_SECTION SocketListLock;
|
|
LIST_ENTRY SockHelpersListHead = { NULL, NULL };
|
|
ULONG SockAsyncThreadRefCount;
|
|
HANDLE SockAsyncHelperAfdHandle;
|
|
HANDLE SockAsyncCompletionPort = NULL;
|
|
BOOLEAN SockAsyncSelectCalled;
|
|
|
|
|
|
|
|
/*
|
|
* FUNCTION: Creates a new socket
|
|
* ARGUMENTS:
|
|
* af = Address family
|
|
* type = Socket type
|
|
* protocol = Protocol type
|
|
* lpProtocolInfo = Pointer to protocol information
|
|
* g = Reserved
|
|
* dwFlags = Socket flags
|
|
* lpErrno = Address of buffer for error information
|
|
* RETURNS:
|
|
* Created socket, or INVALID_SOCKET if it could not be created
|
|
*/
|
|
SOCKET
|
|
WSPAPI
|
|
WSPSocket(int AddressFamily,
|
|
int SocketType,
|
|
int Protocol,
|
|
LPWSAPROTOCOL_INFOW lpProtocolInfo,
|
|
GROUP g,
|
|
DWORD dwFlags,
|
|
LPINT lpErrno)
|
|
{
|
|
OBJECT_ATTRIBUTES Object;
|
|
IO_STATUS_BLOCK IOSB;
|
|
USHORT SizeOfPacket;
|
|
ULONG SizeOfEA;
|
|
PAFD_CREATE_PACKET AfdPacket;
|
|
HANDLE Sock;
|
|
PSOCKET_INFORMATION Socket = NULL;
|
|
PFILE_FULL_EA_INFORMATION EABuffer = NULL;
|
|
PHELPER_DATA HelperData;
|
|
PVOID HelperDLLContext;
|
|
DWORD HelperEvents;
|
|
UNICODE_STRING TransportName;
|
|
UNICODE_STRING DevName;
|
|
LARGE_INTEGER GroupData;
|
|
INT Status;
|
|
PSOCK_SHARED_INFO SharedData = NULL;
|
|
|
|
TRACE("Creating Socket, getting TDI Name - AddressFamily (%d) SocketType (%d) Protocol (%d).\n",
|
|
AddressFamily, SocketType, Protocol);
|
|
|
|
if (lpProtocolInfo && lpProtocolInfo->dwServiceFlags3 != 0 && lpProtocolInfo->dwServiceFlags4 != 0)
|
|
{
|
|
/* Duplpicating socket from different process */
|
|
if (UlongToPtr(lpProtocolInfo->dwServiceFlags3) == INVALID_HANDLE_VALUE)
|
|
{
|
|
Status = WSAEINVAL;
|
|
goto error;
|
|
}
|
|
if (UlongToPtr(lpProtocolInfo->dwServiceFlags4) == INVALID_HANDLE_VALUE)
|
|
{
|
|
Status = WSAEINVAL;
|
|
goto error;
|
|
}
|
|
SharedData = MapViewOfFile(UlongToPtr(lpProtocolInfo->dwServiceFlags3),
|
|
FILE_MAP_ALL_ACCESS,
|
|
0,
|
|
0,
|
|
sizeof(SOCK_SHARED_INFO));
|
|
if (!SharedData)
|
|
{
|
|
Status = WSAEINVAL;
|
|
goto error;
|
|
}
|
|
InterlockedIncrement(&SharedData->RefCount);
|
|
AddressFamily = SharedData->AddressFamily;
|
|
SocketType = SharedData->SocketType;
|
|
Protocol = SharedData->Protocol;
|
|
}
|
|
|
|
if (AddressFamily == AF_UNSPEC && SocketType == 0 && Protocol == 0)
|
|
{
|
|
Status = WSAEINVAL;
|
|
goto error;
|
|
}
|
|
|
|
/* Set the defaults */
|
|
if (AddressFamily == AF_UNSPEC)
|
|
AddressFamily = AF_INET;
|
|
|
|
if (SocketType == 0)
|
|
{
|
|
switch (Protocol)
|
|
{
|
|
case IPPROTO_TCP:
|
|
SocketType = SOCK_STREAM;
|
|
break;
|
|
case IPPROTO_UDP:
|
|
SocketType = SOCK_DGRAM;
|
|
break;
|
|
case IPPROTO_RAW:
|
|
SocketType = SOCK_RAW;
|
|
break;
|
|
default:
|
|
TRACE("Unknown Protocol (%d). We will try SOCK_STREAM.\n", Protocol);
|
|
SocketType = SOCK_STREAM;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Protocol == 0)
|
|
{
|
|
switch (SocketType)
|
|
{
|
|
case SOCK_STREAM:
|
|
Protocol = IPPROTO_TCP;
|
|
break;
|
|
case SOCK_DGRAM:
|
|
Protocol = IPPROTO_UDP;
|
|
break;
|
|
case SOCK_RAW:
|
|
Protocol = IPPROTO_RAW;
|
|
break;
|
|
default:
|
|
TRACE("Unknown SocketType (%d). We will try IPPROTO_TCP.\n", SocketType);
|
|
Protocol = IPPROTO_TCP;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Get Helper Data and Transport */
|
|
Status = SockGetTdiName (&AddressFamily,
|
|
&SocketType,
|
|
&Protocol,
|
|
g,
|
|
dwFlags,
|
|
&TransportName,
|
|
&HelperDLLContext,
|
|
&HelperData,
|
|
&HelperEvents);
|
|
|
|
/* Check for error */
|
|
if (Status != NO_ERROR)
|
|
{
|
|
ERR("SockGetTdiName: Status %x\n", Status);
|
|
goto error;
|
|
}
|
|
|
|
/* AFD Device Name */
|
|
RtlInitUnicodeString(&DevName, L"\\Device\\Afd\\Endpoint");
|
|
|
|
/* Set Socket Data */
|
|
Socket = HeapAlloc(GlobalHeap, 0, sizeof(*Socket));
|
|
if (!Socket)
|
|
{
|
|
Status = WSAENOBUFS;
|
|
goto error;
|
|
}
|
|
RtlZeroMemory(Socket, sizeof(*Socket));
|
|
if (SharedData)
|
|
{
|
|
Socket->SharedData = SharedData;
|
|
Socket->SharedDataHandle = UlongToHandle(lpProtocolInfo->dwServiceFlags3);
|
|
Sock = UlongToHandle(lpProtocolInfo->dwServiceFlags4);
|
|
Socket->Handle = (SOCKET)lpProtocolInfo->dwServiceFlags4;
|
|
}
|
|
else
|
|
{
|
|
Socket->SharedDataHandle = INVALID_HANDLE_VALUE;
|
|
Socket->SharedData = HeapAlloc(GlobalHeap, 0, sizeof(*Socket->SharedData));
|
|
if (!Socket->SharedData)
|
|
{
|
|
Status = WSAENOBUFS;
|
|
goto error;
|
|
}
|
|
RtlZeroMemory(Socket->SharedData, sizeof(*Socket->SharedData));
|
|
Socket->SharedData->State = SocketOpen;
|
|
Socket->SharedData->RefCount = 1L;
|
|
Socket->SharedData->Listening = FALSE;
|
|
Socket->SharedData->AddressFamily = AddressFamily;
|
|
Socket->SharedData->SocketType = SocketType;
|
|
Socket->SharedData->Protocol = Protocol;
|
|
Socket->SharedData->SizeOfLocalAddress = HelperData->MaxWSAddressLength;
|
|
Socket->SharedData->SizeOfRemoteAddress = HelperData->MaxWSAddressLength;
|
|
Socket->SharedData->UseDelayedAcceptance = HelperData->UseDelayedAcceptance;
|
|
Socket->SharedData->CreateFlags = dwFlags;
|
|
Socket->SharedData->ServiceFlags1 = lpProtocolInfo->dwServiceFlags1;
|
|
Socket->SharedData->ProviderFlags = lpProtocolInfo->dwProviderFlags;
|
|
Socket->SharedData->UseSAN = FALSE;
|
|
Socket->SharedData->NonBlocking = FALSE; /* Sockets start blocking */
|
|
Socket->SharedData->RecvTimeout = INFINITE;
|
|
Socket->SharedData->SendTimeout = INFINITE;
|
|
Socket->SharedData->OobInline = FALSE;
|
|
|
|
/* Ask alex about this */
|
|
if( Socket->SharedData->SocketType == SOCK_DGRAM ||
|
|
Socket->SharedData->SocketType == SOCK_RAW )
|
|
{
|
|
TRACE("Connectionless socket\n");
|
|
Socket->SharedData->ServiceFlags1 |= XP1_CONNECTIONLESS;
|
|
}
|
|
Socket->Handle = INVALID_SOCKET;
|
|
}
|
|
|
|
Socket->HelperContext = HelperDLLContext;
|
|
Socket->HelperData = HelperData;
|
|
Socket->HelperEvents = HelperEvents;
|
|
Socket->LocalAddress = &Socket->SharedData->WSLocalAddress;
|
|
Socket->RemoteAddress = &Socket->SharedData->WSRemoteAddress;
|
|
Socket->SanData = NULL;
|
|
RtlCopyMemory(&Socket->ProtocolInfo, lpProtocolInfo, sizeof(Socket->ProtocolInfo));
|
|
if (SharedData)
|
|
goto ok;
|
|
|
|
/* Packet Size */
|
|
SizeOfPacket = TransportName.Length + sizeof(AFD_CREATE_PACKET) + sizeof(WCHAR);
|
|
|
|
/* EA Size */
|
|
SizeOfEA = SizeOfPacket + sizeof(FILE_FULL_EA_INFORMATION) + AFD_PACKET_COMMAND_LENGTH;
|
|
|
|
/* Set up EA Buffer */
|
|
EABuffer = HeapAlloc(GlobalHeap, 0, SizeOfEA);
|
|
if (!EABuffer)
|
|
{
|
|
Status = WSAENOBUFS;
|
|
goto error;
|
|
}
|
|
|
|
RtlZeroMemory(EABuffer, SizeOfEA);
|
|
EABuffer->NextEntryOffset = 0;
|
|
EABuffer->Flags = 0;
|
|
EABuffer->EaNameLength = AFD_PACKET_COMMAND_LENGTH;
|
|
RtlCopyMemory (EABuffer->EaName,
|
|
AfdCommand,
|
|
AFD_PACKET_COMMAND_LENGTH + 1);
|
|
EABuffer->EaValueLength = SizeOfPacket;
|
|
|
|
/* Set up AFD Packet */
|
|
AfdPacket = (PAFD_CREATE_PACKET)(EABuffer->EaName + EABuffer->EaNameLength + 1);
|
|
AfdPacket->SizeOfTransportName = TransportName.Length;
|
|
RtlCopyMemory (AfdPacket->TransportName,
|
|
TransportName.Buffer,
|
|
TransportName.Length + sizeof(WCHAR));
|
|
AfdPacket->GroupID = g;
|
|
|
|
/* Set up Endpoint Flags */
|
|
if ((Socket->SharedData->ServiceFlags1 & XP1_CONNECTIONLESS) != 0)
|
|
{
|
|
if ((SocketType != SOCK_DGRAM) && (SocketType != SOCK_RAW))
|
|
{
|
|
/* Only RAW or UDP can be Connectionless */
|
|
Status = WSAEINVAL;
|
|
goto error;
|
|
}
|
|
AfdPacket->EndpointFlags |= AFD_ENDPOINT_CONNECTIONLESS;
|
|
}
|
|
|
|
if ((Socket->SharedData->ServiceFlags1 & XP1_MESSAGE_ORIENTED) != 0)
|
|
{
|
|
if (SocketType == SOCK_STREAM)
|
|
{
|
|
if ((Socket->SharedData->ServiceFlags1 & XP1_PSEUDO_STREAM) == 0)
|
|
{
|
|
/* The Provider doesn't actually support Message Oriented Streams */
|
|
Status = WSAEINVAL;
|
|
goto error;
|
|
}
|
|
}
|
|
AfdPacket->EndpointFlags |= AFD_ENDPOINT_MESSAGE_ORIENTED;
|
|
}
|
|
|
|
if (SocketType == SOCK_RAW) AfdPacket->EndpointFlags |= AFD_ENDPOINT_RAW;
|
|
|
|
if (dwFlags & (WSA_FLAG_MULTIPOINT_C_ROOT |
|
|
WSA_FLAG_MULTIPOINT_C_LEAF |
|
|
WSA_FLAG_MULTIPOINT_D_ROOT |
|
|
WSA_FLAG_MULTIPOINT_D_LEAF))
|
|
{
|
|
if ((Socket->SharedData->ServiceFlags1 & XP1_SUPPORT_MULTIPOINT) == 0)
|
|
{
|
|
/* The Provider doesn't actually support Multipoint */
|
|
Status = WSAEINVAL;
|
|
goto error;
|
|
}
|
|
AfdPacket->EndpointFlags |= AFD_ENDPOINT_MULTIPOINT;
|
|
|
|
if (dwFlags & WSA_FLAG_MULTIPOINT_C_ROOT)
|
|
{
|
|
if (((Socket->SharedData->ServiceFlags1 & XP1_MULTIPOINT_CONTROL_PLANE) == 0)
|
|
|| ((dwFlags & WSA_FLAG_MULTIPOINT_C_LEAF) != 0))
|
|
{
|
|
/* The Provider doesn't support Control Planes, or you already gave a leaf */
|
|
Status = WSAEINVAL;
|
|
goto error;
|
|
}
|
|
AfdPacket->EndpointFlags |= AFD_ENDPOINT_C_ROOT;
|
|
}
|
|
|
|
if (dwFlags & WSA_FLAG_MULTIPOINT_D_ROOT)
|
|
{
|
|
if (((Socket->SharedData->ServiceFlags1 & XP1_MULTIPOINT_DATA_PLANE) == 0)
|
|
|| ((dwFlags & WSA_FLAG_MULTIPOINT_D_LEAF) != 0))
|
|
{
|
|
/* The Provider doesn't support Data Planes, or you already gave a leaf */
|
|
Status = WSAEINVAL;
|
|
goto error;
|
|
}
|
|
AfdPacket->EndpointFlags |= AFD_ENDPOINT_D_ROOT;
|
|
}
|
|
}
|
|
|
|
/* Set up Object Attributes */
|
|
InitializeObjectAttributes (&Object,
|
|
&DevName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
|
|
0,
|
|
0);
|
|
|
|
/* Create the Socket as asynchronous. That means we have to block
|
|
ourselves after every call to NtDeviceIoControlFile. This is
|
|
because the kernel doesn't support overlapping synchronous I/O
|
|
requests (made from multiple threads) at this time (Sep 2005) */
|
|
Status = NtCreateFile(&Sock,
|
|
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
|
&Object,
|
|
&IOSB,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN_IF,
|
|
0,
|
|
EABuffer,
|
|
SizeOfEA);
|
|
|
|
HeapFree(GlobalHeap, 0, EABuffer);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to open socket. Status 0x%08x\n", Status);
|
|
Status = TranslateNtStatusError(Status);
|
|
goto error;
|
|
}
|
|
|
|
/* Save Handle */
|
|
Socket->Handle = (SOCKET)Sock;
|
|
|
|
/* Save Group Info */
|
|
if (g != 0)
|
|
{
|
|
GetSocketInformation(Socket,
|
|
AFD_INFO_GROUP_ID_TYPE,
|
|
NULL,
|
|
NULL,
|
|
&GroupData,
|
|
NULL,
|
|
NULL);
|
|
Socket->SharedData->GroupID = GroupData.u.LowPart;
|
|
Socket->SharedData->GroupType = GroupData.u.HighPart;
|
|
}
|
|
|
|
/* Get Window Sizes and Save them */
|
|
GetSocketInformation (Socket,
|
|
AFD_INFO_SEND_WINDOW_SIZE,
|
|
NULL,
|
|
&Socket->SharedData->SizeOfSendBuffer,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
GetSocketInformation (Socket,
|
|
AFD_INFO_RECEIVE_WINDOW_SIZE,
|
|
NULL,
|
|
&Socket->SharedData->SizeOfRecvBuffer,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
ok:
|
|
|
|
/* Save in Process Sockets List */
|
|
EnterCriticalSection(&SocketListLock);
|
|
Socket->NextSocket = SocketListHead;
|
|
SocketListHead = Socket;
|
|
LeaveCriticalSection(&SocketListLock);
|
|
|
|
/* Create the Socket Context */
|
|
CreateContext(Socket);
|
|
|
|
/* Notify Winsock */
|
|
Upcalls.lpWPUModifyIFSHandle(Socket->ProtocolInfo.dwCatalogEntryId, (SOCKET)Sock, lpErrno);
|
|
|
|
/* Return Socket Handle */
|
|
TRACE("Success %x\n", Sock);
|
|
|
|
return (SOCKET)Sock;
|
|
|
|
error:
|
|
ERR("Ending %x\n", Status);
|
|
|
|
if( SharedData )
|
|
{
|
|
UnmapViewOfFile(SharedData);
|
|
NtClose(UlongToHandle(lpProtocolInfo->dwServiceFlags3));
|
|
}
|
|
else
|
|
{
|
|
if( Socket && Socket->SharedData )
|
|
HeapFree(GlobalHeap, 0, Socket->SharedData);
|
|
}
|
|
|
|
if( Socket )
|
|
HeapFree(GlobalHeap, 0, Socket);
|
|
|
|
if( EABuffer )
|
|
HeapFree(GlobalHeap, 0, EABuffer);
|
|
|
|
if( lpErrno )
|
|
*lpErrno = Status;
|
|
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPDuplicateSocket(
|
|
IN SOCKET Handle,
|
|
IN DWORD dwProcessId,
|
|
OUT LPWSAPROTOCOL_INFOW lpProtocolInfo,
|
|
OUT LPINT lpErrno)
|
|
{
|
|
HANDLE hProcess, hDuplicatedSharedData, hDuplicatedHandle;
|
|
PSOCKET_INFORMATION Socket;
|
|
PSOCK_SHARED_INFO pSharedData, pOldSharedData;
|
|
BOOL bDuplicated;
|
|
|
|
if (Handle == INVALID_SOCKET)
|
|
return MsafdReturnWithErrno(STATUS_INVALID_PARAMETER, lpErrno, 0, NULL);
|
|
Socket = GetSocketStructure(Handle);
|
|
if( !Socket )
|
|
{
|
|
if( lpErrno )
|
|
*lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
if ( !(hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, dwProcessId)) )
|
|
return MsafdReturnWithErrno(STATUS_INVALID_PARAMETER, lpErrno, 0, NULL);
|
|
|
|
/* It is a not yet duplicated socket, so map the memory, copy the SharedData and free heap */
|
|
if( Socket->SharedDataHandle == INVALID_HANDLE_VALUE )
|
|
{
|
|
Socket->SharedDataHandle = CreateFileMapping(INVALID_HANDLE_VALUE,
|
|
NULL,
|
|
PAGE_READWRITE | SEC_COMMIT,
|
|
0,
|
|
(sizeof(SOCK_SHARED_INFO) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1),
|
|
NULL);
|
|
if( Socket->SharedDataHandle == INVALID_HANDLE_VALUE )
|
|
return MsafdReturnWithErrno(STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, NULL);
|
|
pSharedData = MapViewOfFile(Socket->SharedDataHandle,
|
|
FILE_MAP_ALL_ACCESS,
|
|
0,
|
|
0,
|
|
sizeof(SOCK_SHARED_INFO));
|
|
|
|
RtlCopyMemory(pSharedData, Socket->SharedData, sizeof(SOCK_SHARED_INFO));
|
|
pOldSharedData = Socket->SharedData;
|
|
Socket->SharedData = pSharedData;
|
|
HeapFree(GlobalHeap, 0, pOldSharedData);
|
|
}
|
|
/* Duplicate the handles for the new process */
|
|
bDuplicated = DuplicateHandle(GetCurrentProcess(),
|
|
Socket->SharedDataHandle,
|
|
hProcess,
|
|
(LPHANDLE)&hDuplicatedSharedData,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS);
|
|
if (!bDuplicated)
|
|
{
|
|
NtClose(hProcess);
|
|
return MsafdReturnWithErrno(STATUS_ACCESS_DENIED, lpErrno, 0, NULL);
|
|
}
|
|
bDuplicated = DuplicateHandle(GetCurrentProcess(),
|
|
(HANDLE)Socket->Handle,
|
|
hProcess,
|
|
(LPHANDLE)&hDuplicatedHandle,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS);
|
|
NtClose(hProcess);
|
|
if( !bDuplicated )
|
|
return MsafdReturnWithErrno(STATUS_ACCESS_DENIED, lpErrno, 0, NULL);
|
|
|
|
|
|
if (!lpProtocolInfo)
|
|
return MsafdReturnWithErrno(STATUS_ACCESS_VIOLATION, lpErrno, 0, NULL);
|
|
|
|
RtlCopyMemory(lpProtocolInfo, &Socket->ProtocolInfo, sizeof(*lpProtocolInfo));
|
|
|
|
lpProtocolInfo->iAddressFamily = Socket->SharedData->AddressFamily;
|
|
lpProtocolInfo->iProtocol = Socket->SharedData->Protocol;
|
|
lpProtocolInfo->iSocketType = Socket->SharedData->SocketType;
|
|
lpProtocolInfo->dwServiceFlags3 = HandleToUlong(hDuplicatedSharedData);
|
|
lpProtocolInfo->dwServiceFlags4 = HandleToUlong(hDuplicatedHandle);
|
|
|
|
if( lpErrno )
|
|
*lpErrno = NO_ERROR;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
INT
|
|
TranslateNtStatusError(NTSTATUS Status)
|
|
{
|
|
switch (Status)
|
|
{
|
|
case STATUS_CANT_WAIT:
|
|
return WSAEWOULDBLOCK;
|
|
|
|
case STATUS_TIMEOUT:
|
|
return WSAETIMEDOUT;
|
|
|
|
case STATUS_SUCCESS:
|
|
return NO_ERROR;
|
|
|
|
case STATUS_FILE_CLOSED:
|
|
return WSAECONNRESET;
|
|
|
|
case STATUS_END_OF_FILE:
|
|
return WSAESHUTDOWN;
|
|
|
|
case STATUS_PENDING:
|
|
return WSA_IO_PENDING;
|
|
|
|
case STATUS_BUFFER_TOO_SMALL:
|
|
case STATUS_BUFFER_OVERFLOW:
|
|
return WSAEMSGSIZE;
|
|
|
|
case STATUS_NO_MEMORY:
|
|
case STATUS_INSUFFICIENT_RESOURCES:
|
|
return WSAENOBUFS;
|
|
|
|
case STATUS_INVALID_CONNECTION:
|
|
return WSAENOTCONN;
|
|
|
|
case STATUS_PROTOCOL_NOT_SUPPORTED:
|
|
return WSAEAFNOSUPPORT;
|
|
|
|
case STATUS_INVALID_ADDRESS:
|
|
return WSAEADDRNOTAVAIL;
|
|
|
|
case STATUS_REMOTE_NOT_LISTENING:
|
|
case STATUS_REMOTE_DISCONNECT:
|
|
return WSAECONNREFUSED;
|
|
|
|
case STATUS_NETWORK_UNREACHABLE:
|
|
return WSAENETUNREACH;
|
|
|
|
case STATUS_HOST_UNREACHABLE:
|
|
return WSAEHOSTUNREACH;
|
|
|
|
case STATUS_INVALID_PARAMETER:
|
|
return WSAEINVAL;
|
|
|
|
case STATUS_CANCELLED:
|
|
return WSA_OPERATION_ABORTED;
|
|
|
|
case STATUS_ADDRESS_ALREADY_EXISTS:
|
|
return WSAEADDRINUSE;
|
|
|
|
case STATUS_LOCAL_DISCONNECT:
|
|
return WSAECONNABORTED;
|
|
|
|
case STATUS_ACCESS_VIOLATION:
|
|
return WSAEFAULT;
|
|
|
|
case STATUS_ACCESS_DENIED:
|
|
return WSAEACCES;
|
|
|
|
case STATUS_NOT_IMPLEMENTED:
|
|
return WSAEOPNOTSUPP;
|
|
|
|
default:
|
|
ERR("MSAFD: Unhandled NTSTATUS value: 0x%x\n", Status);
|
|
return WSAENETDOWN;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: Closes an open socket
|
|
* ARGUMENTS:
|
|
* s = Socket descriptor
|
|
* lpErrno = Address of buffer for error information
|
|
* RETURNS:
|
|
* NO_ERROR, or SOCKET_ERROR if the socket could not be closed
|
|
*/
|
|
INT
|
|
WSPAPI
|
|
WSPCloseSocket(IN SOCKET Handle,
|
|
OUT LPINT lpErrno)
|
|
{
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PSOCKET_INFORMATION Socket = NULL, CurrentSocket;
|
|
NTSTATUS Status;
|
|
HANDLE SockEvent;
|
|
AFD_DISCONNECT_INFO DisconnectInfo;
|
|
SOCKET_STATE OldState;
|
|
LONG LingerWait = -1;
|
|
DWORD References;
|
|
|
|
/* Get the Socket Structure associate to this Socket*/
|
|
Socket = GetSocketStructure(Handle);
|
|
if (!Socket)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
/* Create the Wait Event */
|
|
Status = NtCreateEvent(&SockEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
ERR("NtCreateEvent failed: 0x%08x", Status);
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
if (Socket->HelperEvents & WSH_NOTIFY_CLOSE)
|
|
{
|
|
Status = Socket->HelperData->WSHNotify(Socket->HelperContext,
|
|
Socket->Handle,
|
|
Socket->TdiAddressHandle,
|
|
Socket->TdiConnectionHandle,
|
|
WSH_NOTIFY_CLOSE);
|
|
|
|
if (Status)
|
|
{
|
|
if (lpErrno) *lpErrno = Status;
|
|
ERR("WSHNotify failed. Error 0x%#x", Status);
|
|
NtClose(SockEvent);
|
|
return SOCKET_ERROR;
|
|
}
|
|
}
|
|
|
|
/* If a Close is already in Process, give up */
|
|
if (Socket->SharedData->State == SocketClosed)
|
|
{
|
|
WARN("Socket is closing.\n");
|
|
NtClose(SockEvent);
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
/* Decrement reference count on SharedData */
|
|
References = InterlockedDecrement(&Socket->SharedData->RefCount);
|
|
if (References)
|
|
goto ok;
|
|
|
|
/* Set the state to close */
|
|
OldState = Socket->SharedData->State;
|
|
Socket->SharedData->State = SocketClosed;
|
|
|
|
/* If SO_LINGER is ON and the Socket is connected, we need to disconnect */
|
|
/* FIXME: Should we do this on Datagram Sockets too? */
|
|
if ((OldState == SocketConnected) && (Socket->SharedData->LingerData.l_onoff))
|
|
{
|
|
ULONG SendsInProgress;
|
|
ULONG SleepWait;
|
|
|
|
/* We need to respect the timeout */
|
|
SleepWait = 100;
|
|
LingerWait = Socket->SharedData->LingerData.l_linger * 1000;
|
|
|
|
/* Loop until no more sends are pending, within the timeout */
|
|
while (LingerWait)
|
|
{
|
|
/* Find out how many Sends are in Progress */
|
|
if (GetSocketInformation(Socket,
|
|
AFD_INFO_SENDS_IN_PROGRESS,
|
|
NULL,
|
|
&SendsInProgress,
|
|
NULL,
|
|
NULL,
|
|
NULL))
|
|
{
|
|
/* Bail out if anything but NO_ERROR */
|
|
LingerWait = 0;
|
|
break;
|
|
}
|
|
|
|
/* Bail out if no more sends are pending */
|
|
if (!SendsInProgress)
|
|
{
|
|
LingerWait = -1;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* We have to execute a sleep, so it's kind of like
|
|
* a block. If the socket is Nonblock, we cannot
|
|
* go on since asynchronous operation is expected
|
|
* and we cannot offer it
|
|
*/
|
|
if (Socket->SharedData->NonBlocking)
|
|
{
|
|
WARN("Would block!\n");
|
|
NtClose(SockEvent);
|
|
Socket->SharedData->State = OldState;
|
|
if (lpErrno) *lpErrno = WSAEWOULDBLOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
/* Now we can sleep, and decrement the linger wait */
|
|
/*
|
|
* FIXME: It seems Windows does some funky acceleration
|
|
* since the waiting seems to be longer and longer. I
|
|
* don't think this improves performance so much, so we
|
|
* wait a fixed time instead.
|
|
*/
|
|
Sleep(SleepWait);
|
|
LingerWait -= SleepWait;
|
|
}
|
|
}
|
|
|
|
if (OldState == SocketConnected)
|
|
{
|
|
if (LingerWait <= 0)
|
|
{
|
|
DisconnectInfo.Timeout = RtlConvertLongToLargeInteger(0);
|
|
DisconnectInfo.DisconnectType = LingerWait < 0 ? AFD_DISCONNECT_SEND : AFD_DISCONNECT_ABORT;
|
|
|
|
if (((DisconnectInfo.DisconnectType & AFD_DISCONNECT_SEND) && (!Socket->SharedData->SendShutdown)) ||
|
|
((DisconnectInfo.DisconnectType & AFD_DISCONNECT_ABORT) && (!Socket->SharedData->ReceiveShutdown)))
|
|
{
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IoStatusBlock,
|
|
IOCTL_AFD_DISCONNECT,
|
|
&DisconnectInfo,
|
|
sizeof(DisconnectInfo),
|
|
NULL,
|
|
0);
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IoStatusBlock.Status;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Cleanup Time! */
|
|
Socket->HelperContext = NULL;
|
|
Socket->SharedData->AsyncDisabledEvents = -1;
|
|
NtClose(Socket->TdiAddressHandle);
|
|
Socket->TdiAddressHandle = NULL;
|
|
NtClose(Socket->TdiConnectionHandle);
|
|
Socket->TdiConnectionHandle = NULL;
|
|
ok:
|
|
EnterCriticalSection(&SocketListLock);
|
|
if (SocketListHead == Socket)
|
|
{
|
|
SocketListHead = SocketListHead->NextSocket;
|
|
}
|
|
else
|
|
{
|
|
CurrentSocket = SocketListHead;
|
|
while (CurrentSocket->NextSocket)
|
|
{
|
|
if (CurrentSocket->NextSocket == Socket)
|
|
{
|
|
CurrentSocket->NextSocket = CurrentSocket->NextSocket->NextSocket;
|
|
break;
|
|
}
|
|
|
|
CurrentSocket = CurrentSocket->NextSocket;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&SocketListLock);
|
|
|
|
/* Close the handle */
|
|
NtClose((HANDLE)Handle);
|
|
NtClose(SockEvent);
|
|
|
|
if( Socket->SharedDataHandle != INVALID_HANDLE_VALUE )
|
|
{
|
|
/* It is a duplicated socket, so unmap the memory */
|
|
UnmapViewOfFile(Socket->SharedData);
|
|
NtClose(Socket->SharedDataHandle);
|
|
Socket->SharedData = NULL;
|
|
}
|
|
if( !References && Socket->SharedData )
|
|
{
|
|
HeapFree(GlobalHeap, 0, Socket->SharedData);
|
|
}
|
|
HeapFree(GlobalHeap, 0, Socket);
|
|
return MsafdReturnWithErrno(Status, lpErrno, 0, NULL);
|
|
}
|
|
|
|
|
|
/*
|
|
* FUNCTION: Associates a local address with a socket
|
|
* ARGUMENTS:
|
|
* s = Socket descriptor
|
|
* name = Pointer to local address
|
|
* namelen = Length of name
|
|
* lpErrno = Address of buffer for error information
|
|
* RETURNS:
|
|
* 0, or SOCKET_ERROR if the socket could not be bound
|
|
*/
|
|
INT
|
|
WSPAPI
|
|
WSPBind(SOCKET Handle,
|
|
const struct sockaddr *SocketAddress,
|
|
int SocketAddressLength,
|
|
LPINT lpErrno)
|
|
{
|
|
IO_STATUS_BLOCK IOSB;
|
|
PAFD_BIND_DATA BindData;
|
|
PSOCKET_INFORMATION Socket = NULL;
|
|
NTSTATUS Status;
|
|
SOCKADDR_INFO SocketInfo;
|
|
HANDLE SockEvent;
|
|
|
|
/* Get the Socket Structure associate to this Socket*/
|
|
Socket = GetSocketStructure(Handle);
|
|
if (!Socket)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
if (Socket->SharedData->State != SocketOpen)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
if (!SocketAddress || SocketAddressLength < Socket->SharedData->SizeOfLocalAddress)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
/* Get Address Information */
|
|
Socket->HelperData->WSHGetSockaddrType ((PSOCKADDR)SocketAddress,
|
|
SocketAddressLength,
|
|
&SocketInfo);
|
|
|
|
if (SocketInfo.AddressInfo == SockaddrAddressInfoBroadcast && !Socket->SharedData->Broadcast)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEADDRNOTAVAIL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
Status = NtCreateEvent(&SockEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
/* See below */
|
|
BindData = HeapAlloc(GlobalHeap, 0, 0xA + SocketAddressLength);
|
|
if (!BindData)
|
|
{
|
|
return MsafdReturnWithErrno(STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, NULL);
|
|
}
|
|
|
|
/* Set up Address in TDI Format */
|
|
BindData->Address.TAAddressCount = 1;
|
|
BindData->Address.Address[0].AddressLength = (USHORT)(SocketAddressLength - sizeof(SocketAddress->sa_family));
|
|
BindData->Address.Address[0].AddressType = SocketAddress->sa_family;
|
|
RtlCopyMemory (BindData->Address.Address[0].Address,
|
|
SocketAddress->sa_data,
|
|
SocketAddressLength - sizeof(SocketAddress->sa_family));
|
|
|
|
/* Set the Share Type */
|
|
if (Socket->SharedData->ExclusiveAddressUse)
|
|
{
|
|
BindData->ShareType = AFD_SHARE_EXCLUSIVE;
|
|
}
|
|
else if (SocketInfo.EndpointInfo == SockaddrEndpointInfoWildcard)
|
|
{
|
|
BindData->ShareType = AFD_SHARE_WILDCARD;
|
|
}
|
|
else if (Socket->SharedData->ReuseAddresses)
|
|
{
|
|
BindData->ShareType = AFD_SHARE_REUSE;
|
|
}
|
|
else
|
|
{
|
|
BindData->ShareType = AFD_SHARE_UNIQUE;
|
|
}
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Socket->Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_BIND,
|
|
BindData,
|
|
0xA + Socket->SharedData->SizeOfLocalAddress, /* Can't figure out a way to calculate this in C*/
|
|
BindData,
|
|
0xA + Socket->SharedData->SizeOfLocalAddress); /* Can't figure out a way to calculate this C */
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
NtClose( SockEvent );
|
|
HeapFree(GlobalHeap, 0, BindData);
|
|
|
|
Socket->SharedData->SocketLastError = TranslateNtStatusError(Status);
|
|
if (Status != STATUS_SUCCESS)
|
|
return MsafdReturnWithErrno ( Status, lpErrno, 0, NULL );
|
|
|
|
/* Set up Socket Data */
|
|
Socket->SharedData->State = SocketBound;
|
|
Socket->TdiAddressHandle = (HANDLE)IOSB.Information;
|
|
|
|
if (Socket->HelperEvents & WSH_NOTIFY_BIND)
|
|
{
|
|
Status = Socket->HelperData->WSHNotify(Socket->HelperContext,
|
|
Socket->Handle,
|
|
Socket->TdiAddressHandle,
|
|
Socket->TdiConnectionHandle,
|
|
WSH_NOTIFY_BIND);
|
|
|
|
if (Status)
|
|
{
|
|
if (lpErrno) *lpErrno = Status;
|
|
return SOCKET_ERROR;
|
|
}
|
|
}
|
|
|
|
return MsafdReturnWithErrno ( Status, lpErrno, 0, NULL );
|
|
}
|
|
|
|
int
|
|
WSPAPI
|
|
WSPListen(SOCKET Handle,
|
|
int Backlog,
|
|
LPINT lpErrno)
|
|
{
|
|
IO_STATUS_BLOCK IOSB;
|
|
AFD_LISTEN_DATA ListenData;
|
|
PSOCKET_INFORMATION Socket = NULL;
|
|
HANDLE SockEvent;
|
|
NTSTATUS Status;
|
|
|
|
/* Get the Socket Structure associate to this Socket*/
|
|
Socket = GetSocketStructure(Handle);
|
|
if (!Socket)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
if (Socket->SharedData->Listening)
|
|
return NO_ERROR;
|
|
|
|
Status = NtCreateEvent(&SockEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if( !NT_SUCCESS(Status) )
|
|
return SOCKET_ERROR;
|
|
|
|
/* Set Up Listen Structure */
|
|
ListenData.UseSAN = FALSE;
|
|
ListenData.UseDelayedAcceptance = Socket->SharedData->UseDelayedAcceptance;
|
|
ListenData.Backlog = Backlog;
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Socket->Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_START_LISTEN,
|
|
&ListenData,
|
|
sizeof(ListenData),
|
|
NULL,
|
|
0);
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
NtClose( SockEvent );
|
|
|
|
Socket->SharedData->SocketLastError = TranslateNtStatusError(Status);
|
|
if (Status != STATUS_SUCCESS)
|
|
return MsafdReturnWithErrno ( Status, lpErrno, 0, NULL );
|
|
|
|
/* Set to Listening */
|
|
Socket->SharedData->Listening = TRUE;
|
|
|
|
if (Socket->HelperEvents & WSH_NOTIFY_LISTEN)
|
|
{
|
|
Status = Socket->HelperData->WSHNotify(Socket->HelperContext,
|
|
Socket->Handle,
|
|
Socket->TdiAddressHandle,
|
|
Socket->TdiConnectionHandle,
|
|
WSH_NOTIFY_LISTEN);
|
|
|
|
if (Status)
|
|
{
|
|
if (lpErrno) *lpErrno = Status;
|
|
return SOCKET_ERROR;
|
|
}
|
|
}
|
|
|
|
return MsafdReturnWithErrno ( Status, lpErrno, 0, NULL );
|
|
}
|
|
|
|
|
|
int
|
|
WSPAPI
|
|
WSPSelect(IN int nfds,
|
|
IN OUT fd_set *readfds OPTIONAL,
|
|
IN OUT fd_set *writefds OPTIONAL,
|
|
IN OUT fd_set *exceptfds OPTIONAL,
|
|
IN const struct timeval *timeout OPTIONAL,
|
|
OUT LPINT lpErrno)
|
|
{
|
|
IO_STATUS_BLOCK IOSB;
|
|
PAFD_POLL_INFO PollInfo;
|
|
NTSTATUS Status;
|
|
ULONG HandleCount;
|
|
ULONG PollBufferSize;
|
|
PVOID PollBuffer;
|
|
ULONG i, j = 0, x;
|
|
HANDLE SockEvent;
|
|
LARGE_INTEGER Timeout;
|
|
PSOCKET_INFORMATION Socket;
|
|
SOCKET Handle;
|
|
ULONG Events;
|
|
fd_set selectfds;
|
|
|
|
/* Find out how many sockets we have, and how large the buffer needs
|
|
* to be */
|
|
FD_ZERO(&selectfds);
|
|
if (readfds != NULL)
|
|
{
|
|
for (i = 0; i < readfds->fd_count; i++)
|
|
{
|
|
FD_SET(readfds->fd_array[i], &selectfds);
|
|
}
|
|
}
|
|
if (writefds != NULL)
|
|
{
|
|
for (i = 0; i < writefds->fd_count; i++)
|
|
{
|
|
FD_SET(writefds->fd_array[i], &selectfds);
|
|
}
|
|
}
|
|
if (exceptfds != NULL)
|
|
{
|
|
for (i = 0; i < exceptfds->fd_count; i++)
|
|
{
|
|
FD_SET(exceptfds->fd_array[i], &selectfds);
|
|
}
|
|
}
|
|
|
|
HandleCount = selectfds.fd_count;
|
|
|
|
if ( HandleCount == 0 )
|
|
{
|
|
WARN("No handles! Returning SOCKET_ERROR\n", HandleCount);
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
PollBufferSize = sizeof(*PollInfo) + ((HandleCount - 1) * sizeof(AFD_HANDLE));
|
|
|
|
TRACE("HandleCount: %u BufferSize: %u\n", HandleCount, PollBufferSize);
|
|
|
|
/* Convert Timeout to NT Format */
|
|
if (timeout == NULL)
|
|
{
|
|
Timeout.u.LowPart = -1;
|
|
Timeout.u.HighPart = 0x7FFFFFFF;
|
|
TRACE("Infinite timeout\n");
|
|
}
|
|
else
|
|
{
|
|
Timeout = RtlEnlargedIntegerMultiply
|
|
((timeout->tv_sec * 1000) + (timeout->tv_usec / 1000), -10000);
|
|
/* Negative timeouts are illegal. Since the kernel represents an
|
|
* incremental timeout as a negative number, we check for a positive
|
|
* result.
|
|
*/
|
|
if (Timeout.QuadPart > 0)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
TRACE("Timeout: Orig %d.%06d kernel %d\n",
|
|
timeout->tv_sec, timeout->tv_usec,
|
|
Timeout.u.LowPart);
|
|
}
|
|
|
|
Status = NtCreateEvent(&SockEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if(!NT_SUCCESS(Status))
|
|
{
|
|
if (lpErrno)
|
|
*lpErrno = WSAEFAULT;
|
|
|
|
ERR("NtCreateEvent failed, 0x%08x\n", Status);
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
/* Allocate */
|
|
PollBuffer = HeapAlloc(GlobalHeap, 0, PollBufferSize);
|
|
|
|
if (!PollBuffer)
|
|
{
|
|
if (lpErrno)
|
|
*lpErrno = WSAEFAULT;
|
|
NtClose(SockEvent);
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
PollInfo = (PAFD_POLL_INFO)PollBuffer;
|
|
|
|
RtlZeroMemory( PollInfo, PollBufferSize );
|
|
|
|
/* Number of handles for AFD to Check */
|
|
PollInfo->Exclusive = FALSE;
|
|
PollInfo->Timeout = Timeout;
|
|
|
|
for (i = 0; i < selectfds.fd_count; i++)
|
|
{
|
|
PollInfo->Handles[i].Handle = selectfds.fd_array[i];
|
|
}
|
|
if (readfds != NULL) {
|
|
for (i = 0; i < readfds->fd_count; i++)
|
|
{
|
|
for (j = 0; j < HandleCount; j++)
|
|
{
|
|
if (PollInfo->Handles[j].Handle == readfds->fd_array[i])
|
|
break;
|
|
}
|
|
if (j >= HandleCount)
|
|
{
|
|
ERR("Error while counting readfds %ld > %ld\n", j, HandleCount);
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
HeapFree(GlobalHeap, 0, PollBuffer);
|
|
NtClose(SockEvent);
|
|
return SOCKET_ERROR;
|
|
}
|
|
Socket = GetSocketStructure(readfds->fd_array[i]);
|
|
if (!Socket)
|
|
{
|
|
ERR("Invalid socket handle provided in readfds %d\n", readfds->fd_array[i]);
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
HeapFree(GlobalHeap, 0, PollBuffer);
|
|
NtClose(SockEvent);
|
|
return SOCKET_ERROR;
|
|
}
|
|
PollInfo->Handles[j].Events |= AFD_EVENT_RECEIVE |
|
|
AFD_EVENT_DISCONNECT |
|
|
AFD_EVENT_ABORT |
|
|
AFD_EVENT_CLOSE |
|
|
AFD_EVENT_ACCEPT;
|
|
//if (Socket->SharedData->OobInline != 0)
|
|
// PollInfo->Handles[j].Events |= AFD_EVENT_OOB_RECEIVE;
|
|
}
|
|
}
|
|
if (writefds != NULL)
|
|
{
|
|
for (i = 0; i < writefds->fd_count; i++)
|
|
{
|
|
for (j = 0; j < HandleCount; j++)
|
|
{
|
|
if (PollInfo->Handles[j].Handle == writefds->fd_array[i])
|
|
break;
|
|
}
|
|
if (j >= HandleCount)
|
|
{
|
|
ERR("Error while counting writefds %ld > %ld\n", j, HandleCount);
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
HeapFree(GlobalHeap, 0, PollBuffer);
|
|
NtClose(SockEvent);
|
|
return SOCKET_ERROR;
|
|
}
|
|
Socket = GetSocketStructure(writefds->fd_array[i]);
|
|
if (!Socket)
|
|
{
|
|
ERR("Invalid socket handle provided in writefds %d\n", writefds->fd_array[i]);
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
HeapFree(GlobalHeap, 0, PollBuffer);
|
|
NtClose(SockEvent);
|
|
return SOCKET_ERROR;
|
|
}
|
|
PollInfo->Handles[j].Handle = writefds->fd_array[i];
|
|
PollInfo->Handles[j].Events |= AFD_EVENT_SEND;
|
|
if (Socket->SharedData->NonBlocking != 0)
|
|
PollInfo->Handles[j].Events |= AFD_EVENT_CONNECT;
|
|
}
|
|
}
|
|
if (exceptfds != NULL)
|
|
{
|
|
for (i = 0; i < exceptfds->fd_count; i++)
|
|
{
|
|
for (j = 0; j < HandleCount; j++)
|
|
{
|
|
if (PollInfo->Handles[j].Handle == exceptfds->fd_array[i])
|
|
break;
|
|
}
|
|
if (j > HandleCount)
|
|
{
|
|
ERR("Error while counting exceptfds %ld > %ld\n", j, HandleCount);
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
HeapFree(GlobalHeap, 0, PollBuffer);
|
|
NtClose(SockEvent);
|
|
return SOCKET_ERROR;
|
|
}
|
|
Socket = GetSocketStructure(exceptfds->fd_array[i]);
|
|
if (!Socket)
|
|
{
|
|
TRACE("Invalid socket handle provided in exceptfds %d\n", exceptfds->fd_array[i]);
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
HeapFree(GlobalHeap, 0, PollBuffer);
|
|
NtClose(SockEvent);
|
|
return SOCKET_ERROR;
|
|
}
|
|
PollInfo->Handles[j].Handle = exceptfds->fd_array[i];
|
|
if (Socket->SharedData->OobInline == 0)
|
|
PollInfo->Handles[j].Events |= AFD_EVENT_OOB_RECEIVE;
|
|
if (Socket->SharedData->NonBlocking != 0)
|
|
PollInfo->Handles[j].Events |= AFD_EVENT_CONNECT_FAIL;
|
|
}
|
|
}
|
|
|
|
PollInfo->HandleCount = HandleCount;
|
|
PollBufferSize = FIELD_OFFSET(AFD_POLL_INFO, Handles) + PollInfo->HandleCount * sizeof(AFD_HANDLE);
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)PollInfo->Handles[0].Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_SELECT,
|
|
PollInfo,
|
|
PollBufferSize,
|
|
PollInfo,
|
|
PollBufferSize);
|
|
|
|
TRACE("DeviceIoControlFile => %x\n", Status);
|
|
|
|
/* Wait for Completion */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
/* Clear the Structures */
|
|
if( readfds )
|
|
FD_ZERO(readfds);
|
|
if( writefds )
|
|
FD_ZERO(writefds);
|
|
if( exceptfds )
|
|
FD_ZERO(exceptfds);
|
|
|
|
/* Loop through return structure */
|
|
HandleCount = PollInfo->HandleCount;
|
|
|
|
/* Return in FDSET Format */
|
|
for (i = 0; i < HandleCount; i++)
|
|
{
|
|
Events = PollInfo->Handles[i].Events;
|
|
Handle = PollInfo->Handles[i].Handle;
|
|
for(x = 1; x; x<<=1)
|
|
{
|
|
Socket = GetSocketStructure(Handle);
|
|
if (!Socket)
|
|
{
|
|
TRACE("Invalid socket handle found %d\n", Handle);
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
HeapFree(GlobalHeap, 0, PollBuffer);
|
|
NtClose(SockEvent);
|
|
return SOCKET_ERROR;
|
|
}
|
|
switch (Events & x)
|
|
{
|
|
case AFD_EVENT_RECEIVE:
|
|
case AFD_EVENT_DISCONNECT:
|
|
case AFD_EVENT_ABORT:
|
|
case AFD_EVENT_ACCEPT:
|
|
case AFD_EVENT_CLOSE:
|
|
TRACE("Event %x on handle %x\n",
|
|
Events,
|
|
Handle);
|
|
if ((Events & x) == AFD_EVENT_DISCONNECT || (Events & x) == AFD_EVENT_CLOSE)
|
|
Socket->SharedData->SocketLastError = WSAECONNRESET;
|
|
if ((Events & x) == AFD_EVENT_ABORT)
|
|
Socket->SharedData->SocketLastError = WSAECONNABORTED;
|
|
if( readfds )
|
|
FD_SET(Handle, readfds);
|
|
break;
|
|
case AFD_EVENT_SEND:
|
|
TRACE("Event %x on handle %x\n",
|
|
Events,
|
|
Handle);
|
|
if (writefds)
|
|
FD_SET(Handle, writefds);
|
|
break;
|
|
case AFD_EVENT_CONNECT:
|
|
TRACE("Event %x on handle %x\n",
|
|
Events,
|
|
Handle);
|
|
if( writefds && Socket->SharedData->NonBlocking != 0 )
|
|
FD_SET(Handle, writefds);
|
|
break;
|
|
case AFD_EVENT_OOB_RECEIVE:
|
|
TRACE("Event %x on handle %x\n",
|
|
Events,
|
|
Handle);
|
|
if( readfds && Socket->SharedData->OobInline != 0 )
|
|
FD_SET(Handle, readfds);
|
|
if( exceptfds && Socket->SharedData->OobInline == 0 )
|
|
FD_SET(Handle, exceptfds);
|
|
break;
|
|
case AFD_EVENT_CONNECT_FAIL:
|
|
TRACE("Event %x on handle %x\n",
|
|
Events,
|
|
Handle);
|
|
if( exceptfds && Socket->SharedData->NonBlocking != 0 )
|
|
FD_SET(Handle, exceptfds);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
HeapFree( GlobalHeap, 0, PollBuffer );
|
|
NtClose( SockEvent );
|
|
|
|
if( lpErrno )
|
|
{
|
|
switch( IOSB.Status )
|
|
{
|
|
case STATUS_SUCCESS:
|
|
case STATUS_TIMEOUT:
|
|
*lpErrno = 0;
|
|
break;
|
|
default:
|
|
*lpErrno = WSAEINVAL;
|
|
break;
|
|
}
|
|
TRACE("*lpErrno = %x\n", *lpErrno);
|
|
}
|
|
|
|
HandleCount = (readfds ? readfds->fd_count : 0) +
|
|
(writefds && writefds != readfds ? writefds->fd_count : 0) +
|
|
(exceptfds && exceptfds != readfds && exceptfds != writefds ? exceptfds->fd_count : 0);
|
|
|
|
TRACE("%d events\n", HandleCount);
|
|
|
|
return HandleCount;
|
|
}
|
|
|
|
DWORD
|
|
GetCurrentTimeInSeconds(VOID)
|
|
{
|
|
SYSTEMTIME st1970 = { 1970, 1, 0, 1, 0, 0, 0, 0 };
|
|
union
|
|
{
|
|
FILETIME ft;
|
|
ULONGLONG ll;
|
|
} u1970, Time;
|
|
|
|
GetSystemTimeAsFileTime(&Time.ft);
|
|
SystemTimeToFileTime(&st1970, &u1970.ft);
|
|
return (DWORD)((Time.ll - u1970.ll) / 10000000ULL);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
SOCKET
|
|
WSPAPI
|
|
WSPAccept(
|
|
_In_ SOCKET Handle,
|
|
_Out_writes_bytes_to_opt_(*addrlen, *addrlen) struct sockaddr FAR *SocketAddress,
|
|
_Inout_opt_ LPINT SocketAddressLength,
|
|
_In_opt_ LPCONDITIONPROC lpfnCondition,
|
|
_In_opt_ DWORD_PTR dwCallbackData,
|
|
_Out_ LPINT lpErrno)
|
|
{
|
|
IO_STATUS_BLOCK IOSB;
|
|
PAFD_RECEIVED_ACCEPT_DATA ListenReceiveData;
|
|
AFD_ACCEPT_DATA AcceptData;
|
|
AFD_DEFER_ACCEPT_DATA DeferData;
|
|
AFD_PENDING_ACCEPT_DATA PendingAcceptData;
|
|
PSOCKET_INFORMATION Socket = NULL;
|
|
NTSTATUS Status;
|
|
struct fd_set ReadSet;
|
|
struct timeval Timeout;
|
|
PVOID PendingData = NULL;
|
|
ULONG PendingDataLength = 0;
|
|
PVOID CalleeDataBuffer;
|
|
WSABUF CallerData, CalleeID, CallerID, CalleeData;
|
|
PSOCKADDR RemoteAddress = NULL;
|
|
GROUP GroupID = 0;
|
|
ULONG CallBack;
|
|
SOCKET AcceptSocket;
|
|
PSOCKET_INFORMATION AcceptSocketInfo;
|
|
UCHAR ReceiveBuffer[0x1A];
|
|
HANDLE SockEvent;
|
|
|
|
/* Get the Socket Structure associate to this Socket*/
|
|
Socket = GetSocketStructure(Handle);
|
|
if (!Socket)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
if (!Socket->SharedData->Listening)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
if ((SocketAddress && !SocketAddressLength) ||
|
|
(SocketAddressLength && !SocketAddress) ||
|
|
(SocketAddressLength && *SocketAddressLength < sizeof(SOCKADDR)))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return INVALID_SOCKET;
|
|
}
|
|
|
|
Status = NtCreateEvent(&SockEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if( !NT_SUCCESS(Status) )
|
|
{
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
/* Dynamic Structure...ugh */
|
|
ListenReceiveData = (PAFD_RECEIVED_ACCEPT_DATA)ReceiveBuffer;
|
|
|
|
/* If this is non-blocking, make sure there's something for us to accept */
|
|
FD_ZERO(&ReadSet);
|
|
FD_SET(Socket->Handle, &ReadSet);
|
|
Timeout.tv_sec=0;
|
|
Timeout.tv_usec=0;
|
|
|
|
if (WSPSelect(0, &ReadSet, NULL, NULL, &Timeout, lpErrno) == SOCKET_ERROR)
|
|
{
|
|
NtClose(SockEvent);
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
if (ReadSet.fd_array[0] != Socket->Handle)
|
|
{
|
|
NtClose(SockEvent);
|
|
if (lpErrno) *lpErrno = WSAEWOULDBLOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Socket->Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_WAIT_FOR_LISTEN,
|
|
NULL,
|
|
0,
|
|
ListenReceiveData,
|
|
0xA + sizeof(*ListenReceiveData));
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
NtClose( SockEvent );
|
|
return MsafdReturnWithErrno( Status, lpErrno, 0, NULL );
|
|
}
|
|
|
|
if (lpfnCondition != NULL)
|
|
{
|
|
if ((Socket->SharedData->ServiceFlags1 & XP1_CONNECT_DATA) != 0)
|
|
{
|
|
/* Find out how much data is pending */
|
|
PendingAcceptData.SequenceNumber = ListenReceiveData->SequenceNumber;
|
|
PendingAcceptData.ReturnSize = TRUE;
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Socket->Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_GET_PENDING_CONNECT_DATA,
|
|
&PendingAcceptData,
|
|
sizeof(PendingAcceptData),
|
|
&PendingAcceptData,
|
|
sizeof(PendingAcceptData));
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
NtClose( SockEvent );
|
|
return MsafdReturnWithErrno( Status, lpErrno, 0, NULL );
|
|
}
|
|
|
|
/* How much data to allocate */
|
|
PendingDataLength = IOSB.Information;
|
|
|
|
if (PendingDataLength)
|
|
{
|
|
/* Allocate needed space */
|
|
PendingData = HeapAlloc(GlobalHeap, 0, PendingDataLength);
|
|
if (!PendingData)
|
|
{
|
|
return MsafdReturnWithErrno( STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, NULL );
|
|
}
|
|
|
|
/* We want the data now */
|
|
PendingAcceptData.ReturnSize = FALSE;
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Socket->Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_GET_PENDING_CONNECT_DATA,
|
|
&PendingAcceptData,
|
|
sizeof(PendingAcceptData),
|
|
PendingData,
|
|
PendingDataLength);
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
NtClose( SockEvent );
|
|
return MsafdReturnWithErrno( Status, lpErrno, 0, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((Socket->SharedData->ServiceFlags1 & XP1_QOS_SUPPORTED) != 0)
|
|
{
|
|
/* I don't support this yet */
|
|
}
|
|
|
|
/* Build Callee ID */
|
|
CalleeID.buf = (PVOID)Socket->LocalAddress;
|
|
CalleeID.len = Socket->SharedData->SizeOfLocalAddress;
|
|
|
|
RemoteAddress = HeapAlloc(GlobalHeap, 0, sizeof(*RemoteAddress));
|
|
if (!RemoteAddress)
|
|
{
|
|
return MsafdReturnWithErrno(STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, NULL);
|
|
}
|
|
|
|
/* Set up Address in SOCKADDR Format */
|
|
RtlCopyMemory (RemoteAddress,
|
|
&ListenReceiveData->Address.Address[0].AddressType,
|
|
sizeof(*RemoteAddress));
|
|
|
|
/* Build Caller ID */
|
|
CallerID.buf = (PVOID)RemoteAddress;
|
|
CallerID.len = sizeof(*RemoteAddress);
|
|
|
|
/* Build Caller Data */
|
|
CallerData.buf = PendingData;
|
|
CallerData.len = PendingDataLength;
|
|
|
|
/* Check if socket supports Conditional Accept */
|
|
if (Socket->SharedData->UseDelayedAcceptance != 0)
|
|
{
|
|
/* Allocate Buffer for Callee Data */
|
|
CalleeDataBuffer = HeapAlloc(GlobalHeap, 0, 4096);
|
|
if (!CalleeDataBuffer) {
|
|
return MsafdReturnWithErrno( STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, NULL );
|
|
}
|
|
CalleeData.buf = CalleeDataBuffer;
|
|
CalleeData.len = 4096;
|
|
}
|
|
else
|
|
{
|
|
/* Nothing */
|
|
CalleeData.buf = 0;
|
|
CalleeData.len = 0;
|
|
}
|
|
|
|
/* Call the Condition Function */
|
|
CallBack = (lpfnCondition)(&CallerID,
|
|
CallerData.buf == NULL ? NULL : &CallerData,
|
|
NULL,
|
|
NULL,
|
|
&CalleeID,
|
|
CalleeData.buf == NULL ? NULL : &CalleeData,
|
|
&GroupID,
|
|
dwCallbackData);
|
|
|
|
if (((CallBack == CF_ACCEPT) && GroupID) != 0)
|
|
{
|
|
/* TBD: Check for Validity */
|
|
}
|
|
|
|
if (CallBack == CF_ACCEPT)
|
|
{
|
|
if ((Socket->SharedData->ServiceFlags1 & XP1_QOS_SUPPORTED) != 0)
|
|
{
|
|
/* I don't support this yet */
|
|
}
|
|
if (CalleeData.buf)
|
|
{
|
|
// SockSetConnectData Sockets(SocketID), IOCTL_AFD_SET_CONNECT_DATA, CalleeData.Buffer, CalleeData.BuffSize, 0
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Callback rejected. Build Defer Structure */
|
|
DeferData.SequenceNumber = ListenReceiveData->SequenceNumber;
|
|
DeferData.RejectConnection = (CallBack == CF_REJECT);
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Socket->Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_DEFER_ACCEPT,
|
|
&DeferData,
|
|
sizeof(DeferData),
|
|
NULL,
|
|
0);
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
NtClose( SockEvent );
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
return MsafdReturnWithErrno( Status, lpErrno, 0, NULL );
|
|
}
|
|
|
|
if (CallBack == CF_REJECT )
|
|
{
|
|
if (lpErrno) *lpErrno = WSAECONNREFUSED;
|
|
return SOCKET_ERROR;
|
|
}
|
|
else
|
|
{
|
|
if (lpErrno) *lpErrno = WSAECONNREFUSED;
|
|
return SOCKET_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Create a new Socket */
|
|
AcceptSocket = WSPSocket (Socket->SharedData->AddressFamily,
|
|
Socket->SharedData->SocketType,
|
|
Socket->SharedData->Protocol,
|
|
&Socket->ProtocolInfo,
|
|
GroupID,
|
|
Socket->SharedData->CreateFlags,
|
|
lpErrno);
|
|
if (AcceptSocket == INVALID_SOCKET)
|
|
return SOCKET_ERROR;
|
|
|
|
/* Set up the Accept Structure */
|
|
AcceptData.ListenHandle = (HANDLE)AcceptSocket;
|
|
AcceptData.SequenceNumber = ListenReceiveData->SequenceNumber;
|
|
|
|
/* Send IOCTL to Accept */
|
|
Status = NtDeviceIoControlFile((HANDLE)Socket->Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_ACCEPT,
|
|
&AcceptData,
|
|
sizeof(AcceptData),
|
|
NULL,
|
|
0);
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
Socket->SharedData->SocketLastError = TranslateNtStatusError(Status);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
NtClose(SockEvent);
|
|
WSPCloseSocket( AcceptSocket, lpErrno );
|
|
return MsafdReturnWithErrno( Status, lpErrno, 0, NULL );
|
|
}
|
|
|
|
AcceptSocketInfo = GetSocketStructure(AcceptSocket);
|
|
if (!AcceptSocketInfo)
|
|
{
|
|
NtClose(SockEvent);
|
|
WSPCloseSocket( AcceptSocket, lpErrno );
|
|
return MsafdReturnWithErrno( STATUS_PROTOCOL_NOT_SUPPORTED, lpErrno, 0, NULL );
|
|
}
|
|
|
|
AcceptSocketInfo->SharedData->State = SocketConnected;
|
|
AcceptSocketInfo->SharedData->ConnectTime = GetCurrentTimeInSeconds();
|
|
|
|
/* Return Address in SOCKADDR FORMAT */
|
|
if( SocketAddress )
|
|
{
|
|
RtlCopyMemory (SocketAddress,
|
|
&ListenReceiveData->Address.Address[0].AddressType,
|
|
sizeof(*RemoteAddress));
|
|
if( SocketAddressLength )
|
|
*SocketAddressLength = sizeof(*RemoteAddress);
|
|
}
|
|
|
|
NtClose( SockEvent );
|
|
|
|
/* Re-enable Async Event */
|
|
SockReenableAsyncSelectEvent(Socket, FD_ACCEPT);
|
|
|
|
TRACE("Socket %x\n", AcceptSocket);
|
|
|
|
if (Status == STATUS_SUCCESS && (Socket->HelperEvents & WSH_NOTIFY_ACCEPT))
|
|
{
|
|
Status = Socket->HelperData->WSHNotify(Socket->HelperContext,
|
|
Socket->Handle,
|
|
Socket->TdiAddressHandle,
|
|
Socket->TdiConnectionHandle,
|
|
WSH_NOTIFY_ACCEPT);
|
|
|
|
if (Status)
|
|
{
|
|
if (lpErrno) *lpErrno = Status;
|
|
return SOCKET_ERROR;
|
|
}
|
|
}
|
|
|
|
if (lpErrno) *lpErrno = NO_ERROR;
|
|
|
|
/* Return Socket */
|
|
return AcceptSocket;
|
|
}
|
|
|
|
int
|
|
WSPAPI
|
|
WSPConnect(SOCKET Handle,
|
|
const struct sockaddr * SocketAddress,
|
|
int SocketAddressLength,
|
|
LPWSABUF lpCallerData,
|
|
LPWSABUF lpCalleeData,
|
|
LPQOS lpSQOS,
|
|
LPQOS lpGQOS,
|
|
LPINT lpErrno)
|
|
{
|
|
IO_STATUS_BLOCK IOSB;
|
|
PAFD_CONNECT_INFO ConnectInfo = NULL;
|
|
PSOCKET_INFORMATION Socket;
|
|
NTSTATUS Status;
|
|
INT Errno;
|
|
ULONG ConnectDataLength;
|
|
ULONG InConnectDataLength;
|
|
INT BindAddressLength;
|
|
PSOCKADDR BindAddress;
|
|
HANDLE SockEvent;
|
|
int SocketDataLength;
|
|
|
|
TRACE("Called (%lx) %lx:%d\n", Handle, ((const struct sockaddr_in *)SocketAddress)->sin_addr, ((const struct sockaddr_in *)SocketAddress)->sin_port);
|
|
|
|
/* Get the Socket Structure associate to this Socket*/
|
|
Socket = GetSocketStructure(Handle);
|
|
if (!Socket)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
Status = NtCreateEvent(&SockEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
return SOCKET_ERROR;
|
|
|
|
/* Bind us First */
|
|
if (Socket->SharedData->State == SocketOpen)
|
|
{
|
|
/* Get the Wildcard Address */
|
|
BindAddressLength = Socket->HelperData->MaxWSAddressLength;
|
|
BindAddress = HeapAlloc(GetProcessHeap(), 0, BindAddressLength);
|
|
if (!BindAddress)
|
|
{
|
|
NtClose(SockEvent);
|
|
return MsafdReturnWithErrno(STATUS_INSUFFICIENT_RESOURCES, lpErrno, 0, NULL);
|
|
}
|
|
Socket->HelperData->WSHGetWildcardSockaddr (Socket->HelperContext,
|
|
BindAddress,
|
|
&BindAddressLength);
|
|
/* Bind it */
|
|
if (WSPBind(Handle, BindAddress, BindAddressLength, lpErrno) == SOCKET_ERROR)
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
/* Set the Connect Data */
|
|
if (lpCallerData != NULL)
|
|
{
|
|
ConnectDataLength = lpCallerData->len;
|
|
Status = NtDeviceIoControlFile((HANDLE)Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_SET_CONNECT_DATA,
|
|
lpCallerData->buf,
|
|
ConnectDataLength,
|
|
NULL,
|
|
0);
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS)
|
|
goto notify;
|
|
}
|
|
|
|
/* Calculate the size of SocketAddress->sa_data */
|
|
SocketDataLength = SocketAddressLength - FIELD_OFFSET(struct sockaddr, sa_data);
|
|
|
|
/* Allocate a connection info buffer with SocketDataLength bytes of payload */
|
|
ConnectInfo = HeapAlloc(GetProcessHeap(), 0,
|
|
FIELD_OFFSET(AFD_CONNECT_INFO,
|
|
RemoteAddress.Address[0].Address[SocketDataLength]));
|
|
if (!ConnectInfo)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto notify;
|
|
}
|
|
|
|
/* Set up Address in TDI Format */
|
|
ConnectInfo->RemoteAddress.TAAddressCount = 1;
|
|
ConnectInfo->RemoteAddress.Address[0].AddressLength = SocketDataLength;
|
|
ConnectInfo->RemoteAddress.Address[0].AddressType = SocketAddress->sa_family;
|
|
RtlCopyMemory(ConnectInfo->RemoteAddress.Address[0].Address,
|
|
SocketAddress->sa_data,
|
|
SocketDataLength);
|
|
|
|
/*
|
|
* Disable FD_WRITE and FD_CONNECT
|
|
* The latter fixes a race condition where the FD_CONNECT is re-enabled
|
|
* at the end of this function right after the Async Thread disables it.
|
|
* This should only happen at the *next* WSPConnect
|
|
*/
|
|
if (Socket->SharedData->AsyncEvents & FD_CONNECT)
|
|
{
|
|
Socket->SharedData->AsyncDisabledEvents |= FD_CONNECT | FD_WRITE;
|
|
}
|
|
|
|
/* Tell AFD that we want Connection Data back, have it allocate a buffer */
|
|
if (lpCalleeData != NULL)
|
|
{
|
|
InConnectDataLength = lpCalleeData->len;
|
|
Status = NtDeviceIoControlFile((HANDLE)Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_SET_CONNECT_DATA_SIZE,
|
|
&InConnectDataLength,
|
|
sizeof(InConnectDataLength),
|
|
NULL,
|
|
0);
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS)
|
|
goto notify;
|
|
}
|
|
|
|
/* AFD doesn't seem to care if these are invalid, but let's 0 them anyways */
|
|
ConnectInfo->Root = 0;
|
|
ConnectInfo->UseSAN = FALSE;
|
|
ConnectInfo->Unknown = 0;
|
|
|
|
/* FIXME: Handle Async Connect */
|
|
if (Socket->SharedData->NonBlocking)
|
|
{
|
|
ERR("Async Connect UNIMPLEMENTED!\n");
|
|
}
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_CONNECT,
|
|
ConnectInfo,
|
|
0x22,
|
|
NULL,
|
|
0);
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
Socket->SharedData->SocketLastError = TranslateNtStatusError(Status);
|
|
if (Status != STATUS_SUCCESS)
|
|
goto notify;
|
|
|
|
Socket->SharedData->State = SocketConnected;
|
|
Socket->TdiConnectionHandle = (HANDLE)IOSB.Information;
|
|
Socket->SharedData->ConnectTime = GetCurrentTimeInSeconds();
|
|
|
|
/* Get any pending connect data */
|
|
if (lpCalleeData != NULL)
|
|
{
|
|
Status = NtDeviceIoControlFile((HANDLE)Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_GET_CONNECT_DATA,
|
|
NULL,
|
|
0,
|
|
lpCalleeData->buf,
|
|
lpCalleeData->len);
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
}
|
|
|
|
TRACE("Ending %lx\n", IOSB.Status);
|
|
|
|
notify:
|
|
if (ConnectInfo) HeapFree(GetProcessHeap(), 0, ConnectInfo);
|
|
|
|
/* Re-enable Async Event */
|
|
SockReenableAsyncSelectEvent(Socket, FD_WRITE);
|
|
|
|
/* FIXME: THIS IS NOT RIGHT!!! HACK HACK HACK! */
|
|
SockReenableAsyncSelectEvent(Socket, FD_CONNECT);
|
|
|
|
NtClose(SockEvent);
|
|
|
|
if (Status == STATUS_SUCCESS && (Socket->HelperEvents & WSH_NOTIFY_CONNECT))
|
|
{
|
|
Errno = Socket->HelperData->WSHNotify(Socket->HelperContext,
|
|
Socket->Handle,
|
|
Socket->TdiAddressHandle,
|
|
Socket->TdiConnectionHandle,
|
|
WSH_NOTIFY_CONNECT);
|
|
|
|
if (Errno)
|
|
{
|
|
if (lpErrno) *lpErrno = Errno;
|
|
return SOCKET_ERROR;
|
|
}
|
|
}
|
|
else if (Status != STATUS_SUCCESS && (Socket->HelperEvents & WSH_NOTIFY_CONNECT_ERROR))
|
|
{
|
|
Errno = Socket->HelperData->WSHNotify(Socket->HelperContext,
|
|
Socket->Handle,
|
|
Socket->TdiAddressHandle,
|
|
Socket->TdiConnectionHandle,
|
|
WSH_NOTIFY_CONNECT_ERROR);
|
|
|
|
if (Errno)
|
|
{
|
|
if (lpErrno) *lpErrno = Errno;
|
|
return SOCKET_ERROR;
|
|
}
|
|
}
|
|
|
|
return MsafdReturnWithErrno(Status, lpErrno, 0, NULL);
|
|
}
|
|
int
|
|
WSPAPI
|
|
WSPShutdown(SOCKET Handle,
|
|
int HowTo,
|
|
LPINT lpErrno)
|
|
|
|
{
|
|
IO_STATUS_BLOCK IOSB;
|
|
AFD_DISCONNECT_INFO DisconnectInfo;
|
|
PSOCKET_INFORMATION Socket = NULL;
|
|
NTSTATUS Status;
|
|
HANDLE SockEvent;
|
|
|
|
TRACE("Called\n");
|
|
|
|
/* Get the Socket Structure associate to this Socket*/
|
|
Socket = GetSocketStructure(Handle);
|
|
if (!Socket)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
Status = NtCreateEvent(&SockEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if( !NT_SUCCESS(Status) )
|
|
return SOCKET_ERROR;
|
|
|
|
/* Set AFD Disconnect Type */
|
|
switch (HowTo)
|
|
{
|
|
case SD_RECEIVE:
|
|
DisconnectInfo.DisconnectType = AFD_DISCONNECT_RECV;
|
|
Socket->SharedData->ReceiveShutdown = TRUE;
|
|
break;
|
|
case SD_SEND:
|
|
DisconnectInfo.DisconnectType= AFD_DISCONNECT_SEND;
|
|
Socket->SharedData->SendShutdown = TRUE;
|
|
break;
|
|
case SD_BOTH:
|
|
DisconnectInfo.DisconnectType = AFD_DISCONNECT_RECV | AFD_DISCONNECT_SEND;
|
|
Socket->SharedData->ReceiveShutdown = TRUE;
|
|
Socket->SharedData->SendShutdown = TRUE;
|
|
break;
|
|
}
|
|
|
|
DisconnectInfo.Timeout = RtlConvertLongToLargeInteger(-1000000);
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_DISCONNECT,
|
|
&DisconnectInfo,
|
|
sizeof(DisconnectInfo),
|
|
NULL,
|
|
0);
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
TRACE("Ending\n");
|
|
|
|
NtClose( SockEvent );
|
|
|
|
Socket->SharedData->SocketLastError = TranslateNtStatusError(Status);
|
|
return MsafdReturnWithErrno( Status, lpErrno, 0, NULL );
|
|
}
|
|
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPGetSockName(IN SOCKET Handle,
|
|
OUT LPSOCKADDR Name,
|
|
IN OUT LPINT NameLength,
|
|
OUT LPINT lpErrno)
|
|
{
|
|
IO_STATUS_BLOCK IOSB;
|
|
ULONG TdiAddressSize;
|
|
PTDI_ADDRESS_INFO TdiAddress;
|
|
PTRANSPORT_ADDRESS SocketAddress;
|
|
PSOCKET_INFORMATION Socket = NULL;
|
|
NTSTATUS Status;
|
|
HANDLE SockEvent;
|
|
|
|
/* Get the Socket Structure associate to this Socket*/
|
|
Socket = GetSocketStructure(Handle);
|
|
if (!Socket)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
if (!Name || !NameLength)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
Status = NtCreateEvent(&SockEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if( !NT_SUCCESS(Status) )
|
|
return SOCKET_ERROR;
|
|
|
|
/* Allocate a buffer for the address */
|
|
TdiAddressSize =
|
|
sizeof(TRANSPORT_ADDRESS) + Socket->SharedData->SizeOfLocalAddress;
|
|
TdiAddress = HeapAlloc(GlobalHeap, 0, TdiAddressSize);
|
|
|
|
if ( TdiAddress == NULL )
|
|
{
|
|
NtClose( SockEvent );
|
|
if (lpErrno) *lpErrno = WSAENOBUFS;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
SocketAddress = &TdiAddress->Address;
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Socket->Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_GET_SOCK_NAME,
|
|
NULL,
|
|
0,
|
|
TdiAddress,
|
|
TdiAddressSize);
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
NtClose( SockEvent );
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (*NameLength >= Socket->SharedData->SizeOfLocalAddress)
|
|
{
|
|
Name->sa_family = SocketAddress->Address[0].AddressType;
|
|
RtlCopyMemory (Name->sa_data,
|
|
SocketAddress->Address[0].Address,
|
|
SocketAddress->Address[0].AddressLength);
|
|
*NameLength = Socket->SharedData->SizeOfLocalAddress;
|
|
TRACE("NameLength %d Address: %x Port %x\n",
|
|
*NameLength, ((struct sockaddr_in *)Name)->sin_addr.s_addr,
|
|
((struct sockaddr_in *)Name)->sin_port);
|
|
HeapFree(GlobalHeap, 0, TdiAddress);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
HeapFree(GlobalHeap, 0, TdiAddress);
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
}
|
|
|
|
HeapFree(GlobalHeap, 0, TdiAddress);
|
|
|
|
return MsafdReturnWithErrno ( Status, lpErrno, 0, NULL );
|
|
}
|
|
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPGetPeerName(IN SOCKET s,
|
|
OUT LPSOCKADDR Name,
|
|
IN OUT LPINT NameLength,
|
|
OUT LPINT lpErrno)
|
|
{
|
|
IO_STATUS_BLOCK IOSB;
|
|
ULONG TdiAddressSize;
|
|
PTRANSPORT_ADDRESS SocketAddress;
|
|
PSOCKET_INFORMATION Socket = NULL;
|
|
NTSTATUS Status;
|
|
HANDLE SockEvent;
|
|
|
|
/* Get the Socket Structure associate to this Socket*/
|
|
Socket = GetSocketStructure(s);
|
|
if (!Socket)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
if (Socket->SharedData->State != SocketConnected)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAENOTCONN;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
if (!Name || !NameLength)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
Status = NtCreateEvent(&SockEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if( !NT_SUCCESS(Status) )
|
|
return SOCKET_ERROR;
|
|
|
|
/* Allocate a buffer for the address */
|
|
TdiAddressSize = sizeof(TRANSPORT_ADDRESS) + Socket->SharedData->SizeOfRemoteAddress;
|
|
SocketAddress = HeapAlloc(GlobalHeap, 0, TdiAddressSize);
|
|
|
|
if ( SocketAddress == NULL )
|
|
{
|
|
NtClose( SockEvent );
|
|
if (lpErrno) *lpErrno = WSAENOBUFS;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Socket->Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_GET_PEER_NAME,
|
|
NULL,
|
|
0,
|
|
SocketAddress,
|
|
TdiAddressSize);
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
NtClose( SockEvent );
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (*NameLength >= Socket->SharedData->SizeOfRemoteAddress)
|
|
{
|
|
Name->sa_family = SocketAddress->Address[0].AddressType;
|
|
RtlCopyMemory (Name->sa_data,
|
|
SocketAddress->Address[0].Address,
|
|
SocketAddress->Address[0].AddressLength);
|
|
*NameLength = Socket->SharedData->SizeOfRemoteAddress;
|
|
TRACE("NameLength %d Address: %x Port %x\n",
|
|
*NameLength, ((struct sockaddr_in *)Name)->sin_addr.s_addr,
|
|
((struct sockaddr_in *)Name)->sin_port);
|
|
HeapFree(GlobalHeap, 0, SocketAddress);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
HeapFree(GlobalHeap, 0, SocketAddress);
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
}
|
|
|
|
HeapFree(GlobalHeap, 0, SocketAddress);
|
|
|
|
return MsafdReturnWithErrno ( Status, lpErrno, 0, NULL );
|
|
}
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPIoctl(IN SOCKET Handle,
|
|
IN DWORD dwIoControlCode,
|
|
IN LPVOID lpvInBuffer,
|
|
IN DWORD cbInBuffer,
|
|
OUT LPVOID lpvOutBuffer,
|
|
IN DWORD cbOutBuffer,
|
|
OUT LPDWORD lpcbBytesReturned,
|
|
IN LPWSAOVERLAPPED lpOverlapped,
|
|
IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine,
|
|
IN LPWSATHREADID lpThreadId,
|
|
OUT LPINT lpErrno)
|
|
{
|
|
PSOCKET_INFORMATION Socket = NULL;
|
|
BOOL NeedsCompletion = lpOverlapped != NULL;
|
|
BOOLEAN NonBlocking;
|
|
INT Errno = NO_ERROR, Ret = SOCKET_ERROR;
|
|
DWORD cbRet = 0;
|
|
|
|
/* Get the Socket Structure associate to this Socket*/
|
|
Socket = GetSocketStructure(Handle);
|
|
if (!Socket)
|
|
{
|
|
if(lpErrno)
|
|
*lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
if (!lpcbBytesReturned && !lpOverlapped)
|
|
{
|
|
if(lpErrno)
|
|
*lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
switch( dwIoControlCode )
|
|
{
|
|
case FIONBIO:
|
|
if( cbInBuffer < sizeof(INT) || IS_INTRESOURCE(lpvInBuffer) )
|
|
{
|
|
Errno = WSAEFAULT;
|
|
break;
|
|
}
|
|
NonBlocking = *((PULONG)lpvInBuffer) ? TRUE : FALSE;
|
|
/* Don't allow to go in blocking mode if WSPAsyncSelect or WSPEventSelect is pending */
|
|
if (!NonBlocking)
|
|
{
|
|
/* If there is an WSPAsyncSelect pending, fail with WSAEINVAL */
|
|
if (Socket->SharedData->AsyncEvents & (~Socket->SharedData->AsyncDisabledEvents))
|
|
{
|
|
Errno = WSAEINVAL;
|
|
break;
|
|
}
|
|
/* If there is an WSPEventSelect pending, fail with WSAEINVAL */
|
|
if (Socket->NetworkEvents)
|
|
{
|
|
Errno = WSAEINVAL;
|
|
break;
|
|
}
|
|
}
|
|
Socket->SharedData->NonBlocking = NonBlocking ? 1 : 0;
|
|
NeedsCompletion = FALSE;
|
|
Errno = SetSocketInformation(Socket, AFD_INFO_BLOCKING_MODE, &NonBlocking, NULL, NULL, lpOverlapped, lpCompletionRoutine);
|
|
if (Errno == NO_ERROR)
|
|
Ret = NO_ERROR;
|
|
break;
|
|
case FIONREAD:
|
|
if (IS_INTRESOURCE(lpvOutBuffer) || cbOutBuffer == 0)
|
|
{
|
|
cbRet = sizeof(ULONG);
|
|
Errno = WSAEFAULT;
|
|
break;
|
|
}
|
|
if (cbOutBuffer < sizeof(ULONG))
|
|
{
|
|
Errno = WSAEINVAL;
|
|
break;
|
|
}
|
|
NeedsCompletion = FALSE;
|
|
Errno = GetSocketInformation(Socket, AFD_INFO_RECEIVE_CONTENT_SIZE, NULL, (PULONG)lpvOutBuffer, NULL, lpOverlapped, lpCompletionRoutine);
|
|
if (Errno == NO_ERROR)
|
|
{
|
|
cbRet = sizeof(ULONG);
|
|
Ret = NO_ERROR;
|
|
}
|
|
break;
|
|
case SIOCATMARK:
|
|
if (IS_INTRESOURCE(lpvOutBuffer) || cbOutBuffer == 0)
|
|
{
|
|
cbRet = sizeof(BOOL);
|
|
Errno = WSAEFAULT;
|
|
break;
|
|
}
|
|
if (cbOutBuffer < sizeof(BOOL))
|
|
{
|
|
Errno = WSAEINVAL;
|
|
break;
|
|
}
|
|
if (Socket->SharedData->SocketType != SOCK_STREAM)
|
|
{
|
|
Errno = WSAEINVAL;
|
|
break;
|
|
}
|
|
|
|
/* FIXME: Return false if OOBINLINE is true for now
|
|
We should MSG_PEEK|MSG_OOB check with driver
|
|
*/
|
|
*(BOOL*)lpvOutBuffer = !Socket->SharedData->OobInline;
|
|
|
|
cbRet = sizeof(BOOL);
|
|
Errno = NO_ERROR;
|
|
Ret = NO_ERROR;
|
|
break;
|
|
case SIO_GET_EXTENSION_FUNCTION_POINTER:
|
|
if (cbOutBuffer == 0)
|
|
{
|
|
cbRet = sizeof(PVOID);
|
|
Errno = WSAEFAULT;
|
|
break;
|
|
}
|
|
|
|
if (cbInBuffer < sizeof(GUID) ||
|
|
cbOutBuffer < sizeof(PVOID))
|
|
{
|
|
Errno = WSAEINVAL;
|
|
break;
|
|
}
|
|
|
|
{
|
|
GUID AcceptExGUID = WSAID_ACCEPTEX;
|
|
GUID ConnectExGUID = WSAID_CONNECTEX;
|
|
GUID DisconnectExGUID = WSAID_DISCONNECTEX;
|
|
GUID GetAcceptExSockaddrsGUID = WSAID_GETACCEPTEXSOCKADDRS;
|
|
|
|
if (IsEqualGUID(&AcceptExGUID, lpvInBuffer))
|
|
{
|
|
*((PVOID *)lpvOutBuffer) = WSPAcceptEx;
|
|
cbRet = sizeof(PVOID);
|
|
Errno = NO_ERROR;
|
|
Ret = NO_ERROR;
|
|
}
|
|
else if (IsEqualGUID(&ConnectExGUID, lpvInBuffer))
|
|
{
|
|
*((PVOID *)lpvOutBuffer) = WSPConnectEx;
|
|
cbRet = sizeof(PVOID);
|
|
Errno = NO_ERROR;
|
|
Ret = NO_ERROR;
|
|
}
|
|
else if (IsEqualGUID(&DisconnectExGUID, lpvInBuffer))
|
|
{
|
|
*((PVOID *)lpvOutBuffer) = WSPDisconnectEx;
|
|
cbRet = sizeof(PVOID);
|
|
Errno = NO_ERROR;
|
|
Ret = NO_ERROR;
|
|
}
|
|
else if (IsEqualGUID(&GetAcceptExSockaddrsGUID, lpvInBuffer))
|
|
{
|
|
*((PVOID *)lpvOutBuffer) = WSPGetAcceptExSockaddrs;
|
|
cbRet = sizeof(PVOID);
|
|
Errno = NO_ERROR;
|
|
Ret = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ERR("Querying unknown extension function: %x\n", ((GUID*)lpvInBuffer)->Data1);
|
|
Errno = WSAEOPNOTSUPP;
|
|
}
|
|
}
|
|
|
|
break;
|
|
case SIO_ADDRESS_LIST_QUERY:
|
|
if (IS_INTRESOURCE(lpvOutBuffer) || cbOutBuffer == 0)
|
|
{
|
|
cbRet = sizeof(SOCKET_ADDRESS_LIST) + sizeof(Socket->SharedData->WSLocalAddress);
|
|
Errno = WSAEFAULT;
|
|
break;
|
|
}
|
|
if (cbOutBuffer < sizeof(INT))
|
|
{
|
|
Errno = WSAEINVAL;
|
|
break;
|
|
}
|
|
|
|
cbRet = sizeof(SOCKET_ADDRESS_LIST) + sizeof(Socket->SharedData->WSLocalAddress);
|
|
|
|
((SOCKET_ADDRESS_LIST*)lpvOutBuffer)->iAddressCount = 1;
|
|
|
|
if (cbOutBuffer < (sizeof(SOCKET_ADDRESS_LIST) + sizeof(Socket->SharedData->WSLocalAddress)))
|
|
{
|
|
Errno = WSAEFAULT;
|
|
break;
|
|
}
|
|
|
|
((SOCKET_ADDRESS_LIST*)lpvOutBuffer)->Address[0].iSockaddrLength = sizeof(Socket->SharedData->WSLocalAddress);
|
|
((SOCKET_ADDRESS_LIST*)lpvOutBuffer)->Address[0].lpSockaddr = &Socket->SharedData->WSLocalAddress;
|
|
|
|
Errno = NO_ERROR;
|
|
Ret = NO_ERROR;
|
|
break;
|
|
default:
|
|
Errno = Socket->HelperData->WSHIoctl(Socket->HelperContext,
|
|
Handle,
|
|
Socket->TdiAddressHandle,
|
|
Socket->TdiConnectionHandle,
|
|
dwIoControlCode,
|
|
lpvInBuffer,
|
|
cbInBuffer,
|
|
lpvOutBuffer,
|
|
cbOutBuffer,
|
|
&cbRet,
|
|
lpOverlapped,
|
|
lpCompletionRoutine,
|
|
&NeedsCompletion);
|
|
|
|
if (Errno == NO_ERROR)
|
|
Ret = NO_ERROR;
|
|
break;
|
|
}
|
|
if (lpOverlapped && NeedsCompletion)
|
|
{
|
|
lpOverlapped->Internal = Errno;
|
|
lpOverlapped->InternalHigh = cbRet;
|
|
if (lpCompletionRoutine != NULL)
|
|
{
|
|
lpCompletionRoutine(Errno, cbRet, lpOverlapped, 0);
|
|
}
|
|
if (lpOverlapped->hEvent)
|
|
SetEvent(lpOverlapped->hEvent);
|
|
if (!PostQueuedCompletionStatus((HANDLE)Handle, cbRet, 0, lpOverlapped))
|
|
{
|
|
ERR("PostQueuedCompletionStatus failed %d\n", GetLastError());
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
if (lpErrno)
|
|
*lpErrno = Errno;
|
|
if (lpcbBytesReturned)
|
|
*lpcbBytesReturned = cbRet;
|
|
return Ret;
|
|
}
|
|
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPGetSockOpt(IN SOCKET Handle,
|
|
IN INT Level,
|
|
IN INT OptionName,
|
|
OUT CHAR FAR* OptionValue,
|
|
IN OUT LPINT OptionLength,
|
|
OUT LPINT lpErrno)
|
|
{
|
|
PSOCKET_INFORMATION Socket = NULL;
|
|
PVOID Buffer;
|
|
INT BufferSize;
|
|
BOOL BoolBuffer;
|
|
DWORD DwordBuffer;
|
|
INT Errno;
|
|
|
|
TRACE("Called\n");
|
|
|
|
/* Get the Socket Structure associate to this Socket*/
|
|
Socket = GetSocketStructure(Handle);
|
|
if (Socket == NULL)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
if (!OptionLength || !OptionValue)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
switch (Level)
|
|
{
|
|
case SOL_SOCKET:
|
|
switch (OptionName)
|
|
{
|
|
case SO_TYPE:
|
|
Buffer = &Socket->SharedData->SocketType;
|
|
BufferSize = sizeof(INT);
|
|
break;
|
|
|
|
case SO_RCVBUF:
|
|
Buffer = &Socket->SharedData->SizeOfRecvBuffer;
|
|
BufferSize = sizeof(ULONG);
|
|
break;
|
|
|
|
case SO_SNDBUF:
|
|
Buffer = &Socket->SharedData->SizeOfSendBuffer;
|
|
BufferSize = sizeof(ULONG);
|
|
break;
|
|
|
|
case SO_ACCEPTCONN:
|
|
BoolBuffer = Socket->SharedData->Listening;
|
|
Buffer = &BoolBuffer;
|
|
BufferSize = sizeof(BOOL);
|
|
break;
|
|
|
|
case SO_BROADCAST:
|
|
BoolBuffer = Socket->SharedData->Broadcast;
|
|
Buffer = &BoolBuffer;
|
|
BufferSize = sizeof(BOOL);
|
|
break;
|
|
|
|
case SO_DEBUG:
|
|
BoolBuffer = Socket->SharedData->Debug;
|
|
Buffer = &BoolBuffer;
|
|
BufferSize = sizeof(BOOL);
|
|
break;
|
|
|
|
case SO_DONTLINGER:
|
|
BoolBuffer = (Socket->SharedData->LingerData.l_onoff == 0);
|
|
Buffer = &BoolBuffer;
|
|
BufferSize = sizeof(BOOL);
|
|
break;
|
|
|
|
case SO_LINGER:
|
|
if (Socket->SharedData->SocketType == SOCK_DGRAM)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAENOPROTOOPT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
Buffer = &Socket->SharedData->LingerData;
|
|
BufferSize = sizeof(struct linger);
|
|
break;
|
|
|
|
case SO_OOBINLINE:
|
|
BoolBuffer = (Socket->SharedData->OobInline != 0);
|
|
Buffer = &BoolBuffer;
|
|
BufferSize = sizeof(BOOL);
|
|
break;
|
|
|
|
case SO_KEEPALIVE:
|
|
case SO_DONTROUTE:
|
|
/* These guys go directly to the helper */
|
|
goto SendToHelper;
|
|
|
|
case SO_CONDITIONAL_ACCEPT:
|
|
BoolBuffer = (Socket->SharedData->UseDelayedAcceptance != 0);
|
|
Buffer = &BoolBuffer;
|
|
BufferSize = sizeof(BOOL);
|
|
break;
|
|
|
|
case SO_REUSEADDR:
|
|
BoolBuffer = (Socket->SharedData->ReuseAddresses != 0);
|
|
Buffer = &BoolBuffer;
|
|
BufferSize = sizeof(BOOL);
|
|
break;
|
|
|
|
case SO_EXCLUSIVEADDRUSE:
|
|
BoolBuffer = (Socket->SharedData->ExclusiveAddressUse != 0);
|
|
Buffer = &BoolBuffer;
|
|
BufferSize = sizeof(BOOL);
|
|
break;
|
|
|
|
case SO_ERROR:
|
|
Buffer = &Socket->SharedData->SocketLastError;
|
|
BufferSize = sizeof(INT);
|
|
break;
|
|
|
|
case SO_CONNECT_TIME:
|
|
DwordBuffer = GetCurrentTimeInSeconds() - Socket->SharedData->ConnectTime;
|
|
Buffer = &DwordBuffer;
|
|
BufferSize = sizeof(DWORD);
|
|
break;
|
|
|
|
case SO_SNDTIMEO:
|
|
Buffer = &Socket->SharedData->SendTimeout;
|
|
BufferSize = sizeof(DWORD);
|
|
break;
|
|
case SO_RCVTIMEO:
|
|
Buffer = &Socket->SharedData->RecvTimeout;
|
|
BufferSize = sizeof(DWORD);
|
|
break;
|
|
case SO_PROTOCOL_INFOW:
|
|
Buffer = &Socket->ProtocolInfo;
|
|
BufferSize = sizeof(Socket->ProtocolInfo);
|
|
break;
|
|
|
|
case SO_GROUP_ID:
|
|
case SO_GROUP_PRIORITY:
|
|
case SO_MAX_MSG_SIZE:
|
|
|
|
default:
|
|
DbgPrint("MSAFD: Get unknown optname %x\n", OptionName);
|
|
if (lpErrno) *lpErrno = WSAENOPROTOOPT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
if (*OptionLength < BufferSize)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
*OptionLength = BufferSize;
|
|
return SOCKET_ERROR;
|
|
}
|
|
RtlCopyMemory(OptionValue, Buffer, BufferSize);
|
|
|
|
return 0;
|
|
|
|
default:
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
SendToHelper:
|
|
Errno = Socket->HelperData->WSHGetSocketInformation(Socket->HelperContext,
|
|
Handle,
|
|
Socket->TdiAddressHandle,
|
|
Socket->TdiConnectionHandle,
|
|
Level,
|
|
OptionName,
|
|
OptionValue,
|
|
(LPINT)OptionLength);
|
|
if (lpErrno) *lpErrno = Errno;
|
|
return (Errno == NO_ERROR) ? NO_ERROR : SOCKET_ERROR;
|
|
}
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPSetSockOpt(
|
|
IN SOCKET s,
|
|
IN INT level,
|
|
IN INT optname,
|
|
IN CONST CHAR FAR* optval,
|
|
IN INT optlen,
|
|
OUT LPINT lpErrno)
|
|
{
|
|
PSOCKET_INFORMATION Socket;
|
|
INT Errno;
|
|
|
|
/* Get the Socket Structure associate to this Socket*/
|
|
Socket = GetSocketStructure(s);
|
|
if (Socket == NULL)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAENOTSOCK;
|
|
return SOCKET_ERROR;
|
|
}
|
|
if (!optval)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
|
|
/* FIXME: We should handle some more cases here */
|
|
if (level == SOL_SOCKET)
|
|
{
|
|
switch (optname)
|
|
{
|
|
case SO_BROADCAST:
|
|
if (optlen < sizeof(BOOL))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
Socket->SharedData->Broadcast = (*optval != 0) ? 1 : 0;
|
|
return NO_ERROR;
|
|
|
|
case SO_OOBINLINE:
|
|
if (optlen < sizeof(BOOL))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
Socket->SharedData->OobInline = (*optval != 0) ? 1 : 0;
|
|
return NO_ERROR;
|
|
|
|
case SO_DONTLINGER:
|
|
if (optlen < sizeof(BOOL))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
Socket->SharedData->LingerData.l_onoff = (*optval != 0) ? 0 : 1;
|
|
return NO_ERROR;
|
|
|
|
case SO_REUSEADDR:
|
|
if (optlen < sizeof(BOOL))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
Socket->SharedData->ReuseAddresses = (*optval != 0) ? 1 : 0;
|
|
return NO_ERROR;
|
|
|
|
case SO_EXCLUSIVEADDRUSE:
|
|
if (optlen < sizeof(BOOL))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
Socket->SharedData->ExclusiveAddressUse = (*optval != 0) ? 1 : 0;
|
|
return NO_ERROR;
|
|
|
|
case SO_LINGER:
|
|
if (optlen < sizeof(struct linger))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
RtlCopyMemory(&Socket->SharedData->LingerData,
|
|
optval,
|
|
sizeof(struct linger));
|
|
return NO_ERROR;
|
|
|
|
case SO_SNDBUF:
|
|
if (optlen < sizeof(ULONG))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
SetSocketInformation(Socket,
|
|
AFD_INFO_SEND_WINDOW_SIZE,
|
|
NULL,
|
|
(PULONG)optval,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
GetSocketInformation(Socket,
|
|
AFD_INFO_SEND_WINDOW_SIZE,
|
|
NULL,
|
|
&Socket->SharedData->SizeOfSendBuffer,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
return NO_ERROR;
|
|
|
|
case SO_RCVBUF:
|
|
if (optlen < sizeof(ULONG))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
SetSocketInformation(Socket,
|
|
AFD_INFO_RECEIVE_WINDOW_SIZE,
|
|
NULL,
|
|
(PULONG)optval,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
GetSocketInformation(Socket,
|
|
AFD_INFO_RECEIVE_WINDOW_SIZE,
|
|
NULL,
|
|
&Socket->SharedData->SizeOfRecvBuffer,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
return NO_ERROR;
|
|
|
|
case SO_ERROR:
|
|
if (optlen < sizeof(INT))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
RtlCopyMemory(&Socket->SharedData->SocketLastError,
|
|
optval,
|
|
sizeof(INT));
|
|
return NO_ERROR;
|
|
|
|
case SO_SNDTIMEO:
|
|
if (optlen < sizeof(DWORD))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
RtlCopyMemory(&Socket->SharedData->SendTimeout,
|
|
optval,
|
|
sizeof(DWORD));
|
|
return NO_ERROR;
|
|
|
|
case SO_RCVTIMEO:
|
|
if (optlen < sizeof(DWORD))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
RtlCopyMemory(&Socket->SharedData->RecvTimeout,
|
|
optval,
|
|
sizeof(DWORD));
|
|
return NO_ERROR;
|
|
|
|
case SO_KEEPALIVE:
|
|
case SO_DONTROUTE:
|
|
/* These go directly to the helper dll */
|
|
goto SendToHelper;
|
|
|
|
default:
|
|
/* Obviously this is a hack */
|
|
ERR("MSAFD: Set unknown optname %x\n", optname);
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
|
|
SendToHelper:
|
|
Errno = Socket->HelperData->WSHSetSocketInformation(Socket->HelperContext,
|
|
s,
|
|
Socket->TdiAddressHandle,
|
|
Socket->TdiConnectionHandle,
|
|
level,
|
|
optname,
|
|
(PCHAR)optval,
|
|
optlen);
|
|
if (lpErrno) *lpErrno = Errno;
|
|
return (Errno == NO_ERROR) ? NO_ERROR : SOCKET_ERROR;
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: Initialize service provider for a client
|
|
* ARGUMENTS:
|
|
* wVersionRequested = Highest WinSock SPI version that the caller can use
|
|
* lpWSPData = Address of WSPDATA structure to initialize
|
|
* lpProtocolInfo = Pointer to structure that defines the desired protocol
|
|
* UpcallTable = Pointer to upcall table of the WinSock DLL
|
|
* lpProcTable = Address of procedure table to initialize
|
|
* RETURNS:
|
|
* Status of operation
|
|
*/
|
|
_Must_inspect_result_
|
|
int
|
|
WSPAPI
|
|
WSPStartup(
|
|
_In_ WORD wVersionRequested,
|
|
_In_ LPWSPDATA lpWSPData,
|
|
_In_ LPWSAPROTOCOL_INFOW lpProtocolInfo,
|
|
_In_ WSPUPCALLTABLE UpcallTable,
|
|
_Out_ LPWSPPROC_TABLE lpProcTable)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
if (((LOBYTE(wVersionRequested) == 2) && (HIBYTE(wVersionRequested) < 2)) ||
|
|
(LOBYTE(wVersionRequested) < 2))
|
|
{
|
|
ERR("WSPStartup NOT SUPPORTED for version 0x%X\n", wVersionRequested);
|
|
return WSAVERNOTSUPPORTED;
|
|
}
|
|
else
|
|
Status = NO_ERROR;
|
|
/* FIXME: Enable all cases of WSPStartup status */
|
|
Upcalls = UpcallTable;
|
|
|
|
if (Status == NO_ERROR)
|
|
{
|
|
lpProcTable->lpWSPAccept = WSPAccept;
|
|
lpProcTable->lpWSPAddressToString = WSPAddressToString;
|
|
lpProcTable->lpWSPAsyncSelect = WSPAsyncSelect;
|
|
lpProcTable->lpWSPBind = WSPBind;
|
|
lpProcTable->lpWSPCancelBlockingCall = WSPCancelBlockingCall;
|
|
lpProcTable->lpWSPCleanup = WSPCleanup;
|
|
lpProcTable->lpWSPCloseSocket = WSPCloseSocket;
|
|
lpProcTable->lpWSPConnect = WSPConnect;
|
|
lpProcTable->lpWSPDuplicateSocket = WSPDuplicateSocket;
|
|
lpProcTable->lpWSPEnumNetworkEvents = WSPEnumNetworkEvents;
|
|
lpProcTable->lpWSPEventSelect = WSPEventSelect;
|
|
lpProcTable->lpWSPGetOverlappedResult = WSPGetOverlappedResult;
|
|
lpProcTable->lpWSPGetPeerName = WSPGetPeerName;
|
|
lpProcTable->lpWSPGetSockName = WSPGetSockName;
|
|
lpProcTable->lpWSPGetSockOpt = WSPGetSockOpt;
|
|
lpProcTable->lpWSPGetQOSByName = WSPGetQOSByName;
|
|
lpProcTable->lpWSPIoctl = WSPIoctl;
|
|
lpProcTable->lpWSPJoinLeaf = WSPJoinLeaf;
|
|
lpProcTable->lpWSPListen = WSPListen;
|
|
lpProcTable->lpWSPRecv = WSPRecv;
|
|
lpProcTable->lpWSPRecvDisconnect = WSPRecvDisconnect;
|
|
lpProcTable->lpWSPRecvFrom = WSPRecvFrom;
|
|
lpProcTable->lpWSPSelect = WSPSelect;
|
|
lpProcTable->lpWSPSend = WSPSend;
|
|
lpProcTable->lpWSPSendDisconnect = WSPSendDisconnect;
|
|
lpProcTable->lpWSPSendTo = WSPSendTo;
|
|
lpProcTable->lpWSPSetSockOpt = WSPSetSockOpt;
|
|
lpProcTable->lpWSPShutdown = WSPShutdown;
|
|
lpProcTable->lpWSPSocket = WSPSocket;
|
|
lpProcTable->lpWSPStringToAddress = WSPStringToAddress;
|
|
lpWSPData->wVersion = MAKEWORD(2, 2);
|
|
lpWSPData->wHighVersion = MAKEWORD(2, 2);
|
|
/* Save CatalogEntryId for all upcalls */
|
|
CatalogEntryId = lpProtocolInfo->dwCatalogEntryId;
|
|
}
|
|
|
|
TRACE("Status (%d).\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPAddressToString(IN LPSOCKADDR lpsaAddress,
|
|
IN DWORD dwAddressLength,
|
|
IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
|
|
OUT LPWSTR lpszAddressString,
|
|
IN OUT LPDWORD lpdwAddressStringLength,
|
|
OUT LPINT lpErrno)
|
|
{
|
|
SIZE_T size;
|
|
WCHAR buffer[54]; /* 32 digits + 7':' + '[' + '%" + 5 digits + ']:' + 5 digits + '\0' */
|
|
WCHAR *p;
|
|
|
|
if (!lpsaAddress || !lpszAddressString || !lpdwAddressStringLength)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
switch (lpsaAddress->sa_family)
|
|
{
|
|
case AF_INET:
|
|
if (dwAddressLength < sizeof(SOCKADDR_IN))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
swprintf(buffer,
|
|
L"%u.%u.%u.%u:%u",
|
|
(unsigned int)(ntohl(((SOCKADDR_IN *)lpsaAddress)->sin_addr.s_addr) >> 24 & 0xff),
|
|
(unsigned int)(ntohl(((SOCKADDR_IN *)lpsaAddress)->sin_addr.s_addr) >> 16 & 0xff),
|
|
(unsigned int)(ntohl(((SOCKADDR_IN *)lpsaAddress)->sin_addr.s_addr) >> 8 & 0xff),
|
|
(unsigned int)(ntohl(((SOCKADDR_IN *)lpsaAddress)->sin_addr.s_addr) & 0xff),
|
|
ntohs(((SOCKADDR_IN *)lpsaAddress)->sin_port));
|
|
|
|
p = wcschr(buffer, L':');
|
|
if (!((SOCKADDR_IN *)lpsaAddress)->sin_port)
|
|
{
|
|
*p = 0;
|
|
}
|
|
break;
|
|
default:
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
size = wcslen(buffer) + 1;
|
|
|
|
if (*lpdwAddressStringLength < size)
|
|
{
|
|
*lpdwAddressStringLength = size;
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
*lpdwAddressStringLength = size;
|
|
wcscpy(lpszAddressString, buffer);
|
|
return 0;
|
|
}
|
|
|
|
INT
|
|
WSPAPI
|
|
WSPStringToAddress(IN LPWSTR AddressString,
|
|
IN INT AddressFamily,
|
|
IN LPWSAPROTOCOL_INFOW lpProtocolInfo,
|
|
OUT LPSOCKADDR lpAddress,
|
|
IN OUT LPINT lpAddressLength,
|
|
OUT LPINT lpErrno)
|
|
{
|
|
int numdots = 0;
|
|
USHORT port;
|
|
LONG inetaddr = 0, ip_part;
|
|
LPWSTR *bp = NULL;
|
|
SOCKADDR_IN *sockaddr;
|
|
|
|
if (!lpAddressLength || !lpAddress || !AddressString)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
sockaddr = (SOCKADDR_IN *)lpAddress;
|
|
|
|
/* Set right address family */
|
|
if (lpProtocolInfo != NULL)
|
|
{
|
|
sockaddr->sin_family = lpProtocolInfo->iAddressFamily;
|
|
}
|
|
else
|
|
{
|
|
sockaddr->sin_family = AddressFamily;
|
|
}
|
|
|
|
/* Report size */
|
|
if (AddressFamily == AF_INET)
|
|
{
|
|
if (*lpAddressLength < (INT)sizeof(SOCKADDR_IN))
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEFAULT;
|
|
}
|
|
else
|
|
{
|
|
// translate ip string to ip
|
|
|
|
/* Get ip number */
|
|
bp = &AddressString;
|
|
inetaddr = 0;
|
|
|
|
while (*bp < &AddressString[wcslen(AddressString)])
|
|
{
|
|
ip_part = wcstol(*bp, bp, 10);
|
|
/* ip part number should be in range 0-255 */
|
|
if (ip_part < 0 || ip_part > 255)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
inetaddr = (inetaddr << 8) + ip_part;
|
|
/* we end on string end or port separator */
|
|
if ((*bp)[0] == 0 || (*bp)[0] == L':')
|
|
break;
|
|
/* ip parts are dot separated. verify it */
|
|
if ((*bp)[0] != L'.')
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
/* count the dots */
|
|
numdots++;
|
|
/* move over the dot to next ip part */
|
|
(*bp)++;
|
|
}
|
|
|
|
/* check dots count */
|
|
if (numdots != 3)
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
/* Get port number */
|
|
if ((*bp)[0] == L':')
|
|
{
|
|
/* move over the column to port part */
|
|
(*bp)++;
|
|
/* next char should be numeric */
|
|
if ((*bp)[0] < L'0' || (*bp)[0] > L'9')
|
|
{
|
|
if (lpErrno) *lpErrno = WSAEINVAL;
|
|
return SOCKET_ERROR;
|
|
}
|
|
port = wcstol(*bp, bp, 10);
|
|
}
|
|
else
|
|
{
|
|
port = 0;
|
|
}
|
|
|
|
if (lpErrno) *lpErrno = NO_ERROR;
|
|
/* rest sockaddr.sin_addr.s_addr
|
|
for we need to be sure it is zero when we come to while */
|
|
*lpAddressLength = sizeof(*sockaddr);
|
|
memset(lpAddress, 0, sizeof(*sockaddr));
|
|
sockaddr->sin_family = AF_INET;
|
|
sockaddr->sin_addr.s_addr = inetaddr;
|
|
sockaddr->sin_port = port;
|
|
}
|
|
}
|
|
|
|
if (lpErrno && !*lpErrno)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
return SOCKET_ERROR;
|
|
}
|
|
|
|
/*
|
|
* FUNCTION: Cleans up service provider for a client
|
|
* ARGUMENTS:
|
|
* lpErrno = Address of buffer for error information
|
|
* RETURNS:
|
|
* 0 if successful, or SOCKET_ERROR if not
|
|
*/
|
|
INT
|
|
WSPAPI
|
|
WSPCleanup(OUT LPINT lpErrno)
|
|
|
|
{
|
|
TRACE("Leaving.\n");
|
|
|
|
if (lpErrno) *lpErrno = NO_ERROR;
|
|
|
|
return 0;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
AfdInfoAPC(PVOID ApcContext,
|
|
PIO_STATUS_BLOCK IoStatusBlock,
|
|
ULONG Reserved)
|
|
{
|
|
PAFDAPCCONTEXT Context = ApcContext;
|
|
|
|
Context->lpCompletionRoutine(IoStatusBlock->Status, IoStatusBlock->Information, Context->lpOverlapped, 0);
|
|
HeapFree(GlobalHeap, 0, ApcContext);
|
|
}
|
|
|
|
int
|
|
GetSocketInformation(PSOCKET_INFORMATION Socket,
|
|
ULONG AfdInformationClass,
|
|
PBOOLEAN Boolean OPTIONAL,
|
|
PULONG Ulong OPTIONAL,
|
|
PLARGE_INTEGER LargeInteger OPTIONAL,
|
|
LPWSAOVERLAPPED Overlapped OPTIONAL,
|
|
LPWSAOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine OPTIONAL)
|
|
{
|
|
PIO_STATUS_BLOCK IOSB;
|
|
IO_STATUS_BLOCK DummyIOSB;
|
|
AFD_INFO InfoData;
|
|
NTSTATUS Status;
|
|
PAFDAPCCONTEXT APCContext;
|
|
PIO_APC_ROUTINE APCFunction;
|
|
HANDLE Event = NULL;
|
|
HANDLE SockEvent;
|
|
|
|
Status = NtCreateEvent(&SockEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if( !NT_SUCCESS(Status) )
|
|
return SOCKET_ERROR;
|
|
|
|
/* Set Info Class */
|
|
InfoData.InformationClass = AfdInformationClass;
|
|
|
|
/* Verify if we should use APC */
|
|
if (Overlapped == 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");
|
|
NtClose( SockEvent );
|
|
return 0;
|
|
}
|
|
if (CompletionRoutine == NULL)
|
|
{
|
|
/* Using Overlapped Structure, but no Completition Routine, so no need for APC */
|
|
APCContext = (PAFDAPCCONTEXT)Overlapped;
|
|
APCFunction = NULL;
|
|
Event = Overlapped->hEvent;
|
|
}
|
|
else
|
|
{
|
|
/* Using Overlapped Structure and a Completition Routine, so use an APC */
|
|
APCFunction = &AfdInfoAPC; // should be a private io completition function inside us
|
|
APCContext = HeapAlloc(GlobalHeap, 0, sizeof(AFDAPCCONTEXT));
|
|
if (!APCContext)
|
|
{
|
|
ERR("Not enough memory for APC Context\n");
|
|
NtClose( SockEvent );
|
|
return WSAEFAULT;
|
|
}
|
|
APCContext->lpCompletionRoutine = CompletionRoutine;
|
|
APCContext->lpOverlapped = Overlapped;
|
|
APCContext->lpSocket = Socket;
|
|
}
|
|
|
|
IOSB = (PIO_STATUS_BLOCK)&Overlapped->Internal;
|
|
}
|
|
|
|
IOSB->Status = STATUS_PENDING;
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Socket->Handle,
|
|
Event,
|
|
APCFunction,
|
|
APCContext,
|
|
IOSB,
|
|
IOCTL_AFD_GET_INFO,
|
|
&InfoData,
|
|
sizeof(InfoData),
|
|
&InfoData,
|
|
sizeof(InfoData));
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING && Overlapped == NULL)
|
|
{
|
|
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 WSA_IO_PENDING;
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS)
|
|
return SOCKET_ERROR;
|
|
|
|
/* Return Information */
|
|
if (Ulong != NULL)
|
|
{
|
|
*Ulong = InfoData.Information.Ulong;
|
|
}
|
|
if (LargeInteger != NULL)
|
|
{
|
|
*LargeInteger = InfoData.Information.LargeInteger;
|
|
}
|
|
if (Boolean != NULL)
|
|
{
|
|
*Boolean = InfoData.Information.Boolean;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
int
|
|
SetSocketInformation(PSOCKET_INFORMATION Socket,
|
|
ULONG AfdInformationClass,
|
|
PBOOLEAN Boolean OPTIONAL,
|
|
PULONG Ulong OPTIONAL,
|
|
PLARGE_INTEGER LargeInteger OPTIONAL,
|
|
LPWSAOVERLAPPED Overlapped OPTIONAL,
|
|
LPWSAOVERLAPPED_COMPLETION_ROUTINE CompletionRoutine OPTIONAL)
|
|
{
|
|
PIO_STATUS_BLOCK IOSB;
|
|
IO_STATUS_BLOCK DummyIOSB;
|
|
AFD_INFO InfoData;
|
|
NTSTATUS Status;
|
|
PAFDAPCCONTEXT APCContext;
|
|
PIO_APC_ROUTINE APCFunction;
|
|
HANDLE Event = NULL;
|
|
HANDLE SockEvent;
|
|
|
|
Status = NtCreateEvent(&SockEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if( !NT_SUCCESS(Status) )
|
|
return SOCKET_ERROR;
|
|
|
|
/* Set Info Class */
|
|
InfoData.InformationClass = AfdInformationClass;
|
|
|
|
/* Set Information */
|
|
if (Ulong != NULL)
|
|
{
|
|
InfoData.Information.Ulong = *Ulong;
|
|
}
|
|
if (LargeInteger != NULL)
|
|
{
|
|
InfoData.Information.LargeInteger = *LargeInteger;
|
|
}
|
|
if (Boolean != NULL)
|
|
{
|
|
InfoData.Information.Boolean = *Boolean;
|
|
}
|
|
|
|
/* Verify if we should use APC */
|
|
if (Overlapped == 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");
|
|
NtClose( SockEvent );
|
|
return 0;
|
|
}
|
|
if (CompletionRoutine == NULL)
|
|
{
|
|
/* Using Overlapped Structure, but no Completition Routine, so no need for APC */
|
|
APCContext = (PAFDAPCCONTEXT)Overlapped;
|
|
APCFunction = NULL;
|
|
Event = Overlapped->hEvent;
|
|
}
|
|
else
|
|
{
|
|
/* Using Overlapped Structure and a Completition Routine, so use an APC */
|
|
APCFunction = &AfdInfoAPC; // should be a private io completition function inside us
|
|
APCContext = HeapAlloc(GlobalHeap, 0, sizeof(AFDAPCCONTEXT));
|
|
if (!APCContext)
|
|
{
|
|
ERR("Not enough memory for APC Context\n");
|
|
NtClose( SockEvent );
|
|
return WSAEFAULT;
|
|
}
|
|
APCContext->lpCompletionRoutine = CompletionRoutine;
|
|
APCContext->lpOverlapped = Overlapped;
|
|
APCContext->lpSocket = Socket;
|
|
}
|
|
|
|
IOSB = (PIO_STATUS_BLOCK)&Overlapped->Internal;
|
|
}
|
|
|
|
IOSB->Status = STATUS_PENDING;
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Socket->Handle,
|
|
Event,
|
|
APCFunction,
|
|
APCContext,
|
|
IOSB,
|
|
IOCTL_AFD_SET_INFO,
|
|
&InfoData,
|
|
sizeof(InfoData),
|
|
NULL,
|
|
0);
|
|
|
|
/* Wait for return */
|
|
if (Status == STATUS_PENDING && Overlapped == NULL)
|
|
{
|
|
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 WSA_IO_PENDING;
|
|
}
|
|
|
|
return Status == STATUS_SUCCESS ? NO_ERROR : SOCKET_ERROR;
|
|
|
|
}
|
|
|
|
PSOCKET_INFORMATION
|
|
GetSocketStructure(SOCKET Handle)
|
|
{
|
|
PSOCKET_INFORMATION CurrentSocket;
|
|
|
|
EnterCriticalSection(&SocketListLock);
|
|
|
|
CurrentSocket = SocketListHead;
|
|
while (CurrentSocket)
|
|
{
|
|
if (CurrentSocket->Handle == Handle)
|
|
{
|
|
LeaveCriticalSection(&SocketListLock);
|
|
return CurrentSocket;
|
|
}
|
|
|
|
CurrentSocket = CurrentSocket->NextSocket;
|
|
}
|
|
|
|
LeaveCriticalSection(&SocketListLock);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int CreateContext(PSOCKET_INFORMATION Socket)
|
|
{
|
|
IO_STATUS_BLOCK IOSB;
|
|
SOCKET_CONTEXT ContextData;
|
|
NTSTATUS Status;
|
|
HANDLE SockEvent;
|
|
|
|
Status = NtCreateEvent(&SockEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
SynchronizationEvent,
|
|
FALSE);
|
|
|
|
if( !NT_SUCCESS(Status) )
|
|
return SOCKET_ERROR;
|
|
|
|
/* Create Context */
|
|
ContextData.SharedData = *Socket->SharedData;
|
|
ContextData.SizeOfHelperData = 0;
|
|
RtlCopyMemory (&ContextData.LocalAddress,
|
|
Socket->LocalAddress,
|
|
Socket->SharedData->SizeOfLocalAddress);
|
|
RtlCopyMemory (&ContextData.RemoteAddress,
|
|
Socket->RemoteAddress,
|
|
Socket->SharedData->SizeOfRemoteAddress);
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile((HANDLE)Socket->Handle,
|
|
SockEvent,
|
|
NULL,
|
|
NULL,
|
|
&IOSB,
|
|
IOCTL_AFD_SET_CONTEXT,
|
|
&ContextData,
|
|
sizeof(ContextData),
|
|
NULL,
|
|
0);
|
|
|
|
/* Wait for Completion */
|
|
if (Status == STATUS_PENDING)
|
|
{
|
|
WaitForSingleObject(SockEvent, INFINITE);
|
|
Status = IOSB.Status;
|
|
}
|
|
|
|
NtClose( SockEvent );
|
|
|
|
return Status == STATUS_SUCCESS ? NO_ERROR : SOCKET_ERROR;
|
|
}
|
|
|
|
BOOLEAN SockCreateOrReferenceAsyncThread(VOID)
|
|
{
|
|
HANDLE hAsyncThread;
|
|
DWORD AsyncThreadId;
|
|
HANDLE AsyncEvent;
|
|
OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleFlags;
|
|
NTSTATUS Status;
|
|
|
|
/* Check if the Thread Already Exists */
|
|
if (SockAsyncThreadRefCount)
|
|
{
|
|
ASSERT(SockAsyncCompletionPort);
|
|
return TRUE;
|
|
}
|
|
|
|
/* Create the Completion Port */
|
|
if (!SockAsyncCompletionPort)
|
|
{
|
|
Status = NtCreateIoCompletion(&SockAsyncCompletionPort,
|
|
IO_COMPLETION_ALL_ACCESS,
|
|
NULL,
|
|
2); // Allow 2 threads only
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ERR("Failed to create completion port: 0x%08x\n", Status);
|
|
return FALSE;
|
|
}
|
|
/* Protect Handle */
|
|
HandleFlags.ProtectFromClose = TRUE;
|
|
HandleFlags.Inherit = FALSE;
|
|
Status = NtSetInformationObject(SockAsyncCompletionPort,
|
|
ObjectHandleFlagInformation,
|
|
&HandleFlags,
|
|
sizeof(HandleFlags));
|
|
}
|
|
|
|
/* Create the Async Event */
|
|
Status = NtCreateEvent(&AsyncEvent,
|
|
EVENT_ALL_ACCESS,
|
|
NULL,
|
|
NotificationEvent,
|
|
FALSE);
|
|
|
|
/* Create the Async Thread */
|
|
hAsyncThread = CreateThread(NULL,
|
|
0,
|
|
SockAsyncThread,
|
|
NULL,
|
|
0,
|
|
&AsyncThreadId);
|
|
|
|
/* Close the Handle */
|
|
NtClose(hAsyncThread);
|
|
|
|
/* Increase the Reference Count */
|
|
SockAsyncThreadRefCount++;
|
|
return TRUE;
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
SockAsyncThread(PVOID ThreadParam)
|
|
{
|
|
PVOID AsyncContext;
|
|
PASYNC_COMPLETION_ROUTINE AsyncCompletionRoutine;
|
|
IO_STATUS_BLOCK IOSB;
|
|
NTSTATUS Status;
|
|
|
|
/* Make the Thread Higher Priority */
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
|
|
|
|
/* Do a KQUEUE/WorkItem Style Loop, thanks to IoCompletion Ports */
|
|
do
|
|
{
|
|
Status = NtRemoveIoCompletion (SockAsyncCompletionPort,
|
|
(PVOID*)&AsyncCompletionRoutine,
|
|
&AsyncContext,
|
|
&IOSB,
|
|
NULL);
|
|
/* Call the Async Function */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
(*AsyncCompletionRoutine)(AsyncContext, &IOSB);
|
|
}
|
|
else
|
|
{
|
|
/* It Failed, sleep for a second */
|
|
Sleep(1000);
|
|
}
|
|
} while ((Status != STATUS_TIMEOUT));
|
|
|
|
/* The Thread has Ended */
|
|
return 0;
|
|
}
|
|
|
|
BOOLEAN SockGetAsyncSelectHelperAfdHandle(VOID)
|
|
{
|
|
UNICODE_STRING AfdHelper;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoSb;
|
|
FILE_COMPLETION_INFORMATION CompletionInfo;
|
|
OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleFlags;
|
|
|
|
/* First, make sure we're not already initialized */
|
|
if (SockAsyncHelperAfdHandle)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
/* Set up Handle Name and Object */
|
|
RtlInitUnicodeString(&AfdHelper, L"\\Device\\Afd\\AsyncSelectHlp" );
|
|
InitializeObjectAttributes(&ObjectAttributes,
|
|
&AfdHelper,
|
|
OBJ_INHERIT | OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
/* Open the Handle to AFD */
|
|
NtCreateFile(&SockAsyncHelperAfdHandle,
|
|
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoSb,
|
|
NULL,
|
|
0,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
FILE_OPEN_IF,
|
|
0,
|
|
NULL,
|
|
0);
|
|
|
|
/*
|
|
* Now Set up the Completion Port Information
|
|
* This means that whenever a Poll is finished, the routine will be executed
|
|
*/
|
|
CompletionInfo.Port = SockAsyncCompletionPort;
|
|
CompletionInfo.Key = SockAsyncSelectCompletionRoutine;
|
|
NtSetInformationFile(SockAsyncHelperAfdHandle,
|
|
&IoSb,
|
|
&CompletionInfo,
|
|
sizeof(CompletionInfo),
|
|
FileCompletionInformation);
|
|
|
|
|
|
/* Protect the Handle */
|
|
HandleFlags.ProtectFromClose = TRUE;
|
|
HandleFlags.Inherit = FALSE;
|
|
NtSetInformationObject(SockAsyncCompletionPort,
|
|
ObjectHandleFlagInformation,
|
|
&HandleFlags,
|
|
sizeof(HandleFlags));
|
|
|
|
|
|
/* Set this variable to true so that Send/Recv/Accept will know wether to renable disabled events */
|
|
SockAsyncSelectCalled = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
VOID SockAsyncSelectCompletionRoutine(PVOID Context, PIO_STATUS_BLOCK IoStatusBlock)
|
|
{
|
|
|
|
PASYNC_DATA AsyncData = Context;
|
|
PSOCKET_INFORMATION Socket;
|
|
ULONG x;
|
|
|
|
/* Get the Socket */
|
|
Socket = AsyncData->ParentSocket;
|
|
|
|
/* Check if the Sequence Number Changed behind our back */
|
|
if (AsyncData->SequenceNumber != Socket->SharedData->SequenceNumber )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Check we were manually called b/c of a failure */
|
|
if (!NT_SUCCESS(IoStatusBlock->Status))
|
|
{
|
|
/* FIXME: Perform Upcall */
|
|
return;
|
|
}
|
|
|
|
for (x = 1; x; x<<=1)
|
|
{
|
|
switch (AsyncData->AsyncSelectInfo.Handles[0].Events & x)
|
|
{
|
|
case AFD_EVENT_RECEIVE:
|
|
if (0 != (Socket->SharedData->AsyncEvents & FD_READ) &&
|
|
0 == (Socket->SharedData->AsyncDisabledEvents & FD_READ))
|
|
{
|
|
/* Make the Notification */
|
|
(Upcalls.lpWPUPostMessage)(Socket->SharedData->hWnd,
|
|
Socket->SharedData->wMsg,
|
|
Socket->Handle,
|
|
WSAMAKESELECTREPLY(FD_READ, 0));
|
|
/* Disable this event until the next read(); */
|
|
Socket->SharedData->AsyncDisabledEvents |= FD_READ;
|
|
}
|
|
break;
|
|
|
|
case AFD_EVENT_OOB_RECEIVE:
|
|
if (0 != (Socket->SharedData->AsyncEvents & FD_OOB) &&
|
|
0 == (Socket->SharedData->AsyncDisabledEvents & FD_OOB))
|
|
{
|
|
/* Make the Notification */
|
|
(Upcalls.lpWPUPostMessage)(Socket->SharedData->hWnd,
|
|
Socket->SharedData->wMsg,
|
|
Socket->Handle,
|
|
WSAMAKESELECTREPLY(FD_OOB, 0));
|
|
/* Disable this event until the next read(); */
|
|
Socket->SharedData->AsyncDisabledEvents |= FD_OOB;
|
|
}
|
|
break;
|
|
|
|
case AFD_EVENT_SEND:
|
|
if (0 != (Socket->SharedData->AsyncEvents & FD_WRITE) &&
|
|
0 == (Socket->SharedData->AsyncDisabledEvents & FD_WRITE))
|
|
{
|
|
/* Make the Notification */
|
|
(Upcalls.lpWPUPostMessage)(Socket->SharedData->hWnd,
|
|
Socket->SharedData->wMsg,
|
|
Socket->Handle,
|
|
WSAMAKESELECTREPLY(FD_WRITE, 0));
|
|
/* Disable this event until the next write(); */
|
|
Socket->SharedData->AsyncDisabledEvents |= FD_WRITE;
|
|
}
|
|
break;
|
|
|
|
/* FIXME: THIS IS NOT RIGHT!!! HACK HACK HACK! */
|
|
case AFD_EVENT_CONNECT:
|
|
case AFD_EVENT_CONNECT_FAIL:
|
|
if (0 != (Socket->SharedData->AsyncEvents & FD_CONNECT) &&
|
|
0 == (Socket->SharedData->AsyncDisabledEvents & FD_CONNECT))
|
|
{
|
|
/* Make the Notification */
|
|
(Upcalls.lpWPUPostMessage)(Socket->SharedData->hWnd,
|
|
Socket->SharedData->wMsg,
|
|
Socket->Handle,
|
|
WSAMAKESELECTREPLY(FD_CONNECT, 0));
|
|
/* Disable this event forever; */
|
|
Socket->SharedData->AsyncDisabledEvents |= FD_CONNECT;
|
|
}
|
|
break;
|
|
|
|
case AFD_EVENT_ACCEPT:
|
|
if (0 != (Socket->SharedData->AsyncEvents & FD_ACCEPT) &&
|
|
0 == (Socket->SharedData->AsyncDisabledEvents & FD_ACCEPT))
|
|
{
|
|
/* Make the Notification */
|
|
(Upcalls.lpWPUPostMessage)(Socket->SharedData->hWnd,
|
|
Socket->SharedData->wMsg,
|
|
Socket->Handle,
|
|
WSAMAKESELECTREPLY(FD_ACCEPT, 0));
|
|
/* Disable this event until the next accept(); */
|
|
Socket->SharedData->AsyncDisabledEvents |= FD_ACCEPT;
|
|
}
|
|
break;
|
|
|
|
case AFD_EVENT_DISCONNECT:
|
|
case AFD_EVENT_ABORT:
|
|
case AFD_EVENT_CLOSE:
|
|
if (0 != (Socket->SharedData->AsyncEvents & FD_CLOSE) &&
|
|
0 == (Socket->SharedData->AsyncDisabledEvents & FD_CLOSE))
|
|
{
|
|
/* Make the Notification */
|
|
(Upcalls.lpWPUPostMessage)(Socket->SharedData->hWnd,
|
|
Socket->SharedData->wMsg,
|
|
Socket->Handle,
|
|
WSAMAKESELECTREPLY(FD_CLOSE, 0));
|
|
/* Disable this event forever; */
|
|
Socket->SharedData->AsyncDisabledEvents |= FD_CLOSE;
|
|
}
|
|
break;
|
|
/* FIXME: Support QOS */
|
|
}
|
|
}
|
|
|
|
/* Check if there are any events left for us to check */
|
|
if ((Socket->SharedData->AsyncEvents & (~Socket->SharedData->AsyncDisabledEvents)) == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Keep Polling */
|
|
SockProcessAsyncSelect(Socket, AsyncData);
|
|
return;
|
|
}
|
|
|
|
VOID SockProcessAsyncSelect(PSOCKET_INFORMATION Socket, PASYNC_DATA AsyncData)
|
|
{
|
|
|
|
ULONG lNetworkEvents;
|
|
NTSTATUS Status;
|
|
|
|
/* Set up the Async Data Event Info */
|
|
AsyncData->AsyncSelectInfo.Timeout.HighPart = 0x7FFFFFFF;
|
|
AsyncData->AsyncSelectInfo.Timeout.LowPart = 0xFFFFFFFF;
|
|
AsyncData->AsyncSelectInfo.HandleCount = 1;
|
|
AsyncData->AsyncSelectInfo.Exclusive = TRUE;
|
|
AsyncData->AsyncSelectInfo.Handles[0].Handle = Socket->Handle;
|
|
AsyncData->AsyncSelectInfo.Handles[0].Events = 0;
|
|
|
|
/* Remove unwanted events */
|
|
lNetworkEvents = Socket->SharedData->AsyncEvents & (~Socket->SharedData->AsyncDisabledEvents);
|
|
|
|
/* Set Events to wait for */
|
|
if (lNetworkEvents & FD_READ)
|
|
{
|
|
AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_RECEIVE;
|
|
}
|
|
|
|
if (lNetworkEvents & FD_WRITE)
|
|
{
|
|
AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_SEND;
|
|
}
|
|
|
|
if (lNetworkEvents & FD_OOB)
|
|
{
|
|
AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_OOB_RECEIVE;
|
|
}
|
|
|
|
if (lNetworkEvents & FD_ACCEPT)
|
|
{
|
|
AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_ACCEPT;
|
|
}
|
|
|
|
/* FIXME: THIS IS NOT RIGHT!!! HACK HACK HACK! */
|
|
if (lNetworkEvents & FD_CONNECT)
|
|
{
|
|
AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_CONNECT | AFD_EVENT_CONNECT_FAIL;
|
|
}
|
|
|
|
if (lNetworkEvents & FD_CLOSE)
|
|
{
|
|
AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_DISCONNECT | AFD_EVENT_ABORT | AFD_EVENT_CLOSE;
|
|
}
|
|
|
|
if (lNetworkEvents & FD_QOS)
|
|
{
|
|
AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_QOS;
|
|
}
|
|
|
|
if (lNetworkEvents & FD_GROUP_QOS)
|
|
{
|
|
AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_GROUP_QOS;
|
|
}
|
|
|
|
/* Send IOCTL */
|
|
Status = NtDeviceIoControlFile (SockAsyncHelperAfdHandle,
|
|
NULL,
|
|
NULL,
|
|
AsyncData,
|
|
&AsyncData->IoStatusBlock,
|
|
IOCTL_AFD_SELECT,
|
|
&AsyncData->AsyncSelectInfo,
|
|
sizeof(AsyncData->AsyncSelectInfo),
|
|
&AsyncData->AsyncSelectInfo,
|
|
sizeof(AsyncData->AsyncSelectInfo));
|
|
|
|
/* I/O Manager Won't call the completion routine, let's do it manually */
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
AsyncData->IoStatusBlock.Status = Status;
|
|
SockAsyncSelectCompletionRoutine(AsyncData, &AsyncData->IoStatusBlock);
|
|
}
|
|
}
|
|
|
|
VOID SockProcessQueuedAsyncSelect(PVOID Context, PIO_STATUS_BLOCK IoStatusBlock)
|
|
{
|
|
PASYNC_DATA AsyncData = Context;
|
|
BOOL FreeContext = TRUE;
|
|
PSOCKET_INFORMATION Socket;
|
|
|
|
/* Get the Socket */
|
|
Socket = AsyncData->ParentSocket;
|
|
|
|
/* If someone closed it, stop the function */
|
|
if (Socket->SharedData->State != SocketClosed)
|
|
{
|
|
/* Check if the Sequence Number changed by now, in which case quit */
|
|
if (AsyncData->SequenceNumber == Socket->SharedData->SequenceNumber)
|
|
{
|
|
/* Do the actual select, if needed */
|
|
if ((Socket->SharedData->AsyncEvents & (~Socket->SharedData->AsyncDisabledEvents)))
|
|
{
|
|
SockProcessAsyncSelect(Socket, AsyncData);
|
|
FreeContext = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Free the Context */
|
|
if (FreeContext)
|
|
{
|
|
HeapFree(GetProcessHeap(), 0, AsyncData);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
SockReenableAsyncSelectEvent (IN PSOCKET_INFORMATION Socket,
|
|
IN ULONG Event)
|
|
{
|
|
PASYNC_DATA AsyncData;
|
|
|
|
/* Make sure the event is actually disabled */
|
|
if (!(Socket->SharedData->AsyncDisabledEvents & Event))
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Re-enable it */
|
|
Socket->SharedData->AsyncDisabledEvents &= ~Event;
|
|
|
|
/* Return if no more events are being polled */
|
|
if ((Socket->SharedData->AsyncEvents & (~Socket->SharedData->AsyncDisabledEvents)) == 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Wait on new events */
|
|
AsyncData = HeapAlloc(GetProcessHeap(), 0, sizeof(ASYNC_DATA));
|
|
if (!AsyncData) return;
|
|
|
|
/* Create the Asynch Thread if Needed */
|
|
SockCreateOrReferenceAsyncThread();
|
|
|
|
/* Increase the sequence number to stop anything else */
|
|
Socket->SharedData->SequenceNumber++;
|
|
|
|
/* 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);
|
|
|
|
/* All done */
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
DllMain(HANDLE hInstDll,
|
|
ULONG dwReason,
|
|
PVOID Reserved)
|
|
{
|
|
|
|
switch (dwReason)
|
|
{
|
|
case DLL_PROCESS_ATTACH:
|
|
|
|
TRACE("Loading MSAFD.DLL \n");
|
|
|
|
/* Don't need thread attach notifications
|
|
so disable them to improve performance */
|
|
DisableThreadLibraryCalls(hInstDll);
|
|
|
|
/* List of DLL Helpers */
|
|
InitializeListHead(&SockHelpersListHead);
|
|
|
|
/* Heap to use when allocating */
|
|
GlobalHeap = GetProcessHeap();
|
|
|
|
/* Initialize the lock that protects our socket list */
|
|
InitializeCriticalSection(&SocketListLock);
|
|
|
|
TRACE("MSAFD.DLL has been loaded\n");
|
|
|
|
break;
|
|
|
|
case DLL_THREAD_ATTACH:
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
|
|
/* Delete the socket list lock */
|
|
DeleteCriticalSection(&SocketListLock);
|
|
|
|
break;
|
|
}
|
|
|
|
TRACE("DllMain of msafd.dll (leaving)\n");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* EOF */
|