diff options
Diffstat (limited to 'ksocket/ksocket.c')
-rw-r--r-- | ksocket/ksocket.c | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/ksocket/ksocket.c b/ksocket/ksocket.c new file mode 100644 index 0000000..3b6ba4d --- /dev/null +++ b/ksocket/ksocket.c @@ -0,0 +1,713 @@ +#include "ksocket.h" + +////////////////////////////////////////////////////////////////////////// +// Definitions. +////////////////////////////////////////////////////////////////////////// + +#define MEMORY_TAG ((ULONG)0x2e2e4142) + +////////////////////////////////////////////////////////////////////////// +// Structures. +////////////////////////////////////////////////////////////////////////// + +typedef struct _KSOCKET_ASYNC_CONTEXT { + KEVENT CompletionEvent; + PIRP Irp; +} KSOCKET_ASYNC_CONTEXT, *PKSOCKET_ASYNC_CONTEXT; + +typedef struct _KSOCKET { + PWSK_SOCKET WskSocket; + + union { + PVOID WskDispatch; + + PWSK_PROVIDER_CONNECTION_DISPATCH WskConnectionDispatch; + PWSK_PROVIDER_LISTEN_DISPATCH WskListenDispatch; + PWSK_PROVIDER_DATAGRAM_DISPATCH WskDatagramDispatch; +#if (NTDDI_VERSION >= NTDDI_WIN10_RS2) + PWSK_PROVIDER_STREAM_DISPATCH WskStreamDispatch; +#endif + }; + + KSOCKET_ASYNC_CONTEXT AsyncContext; +} KSOCKET, *PKSOCKET; + +////////////////////////////////////////////////////////////////////////// +// Variables. +////////////////////////////////////////////////////////////////////////// + +WSK_REGISTRATION WskRegistration; +WSK_PROVIDER_NPI WskProvider; +WSK_CLIENT_DISPATCH WskDispatch = {MAKE_WSK_VERSION(1, 0), 0, NULL}; + +////////////////////////////////////////////////////////////////////////// +// Function prototypes. +////////////////////////////////////////////////////////////////////////// + +NTSTATUS +NTAPI +KspAsyncContextAllocate(_Out_ PKSOCKET_ASYNC_CONTEXT AsyncContext); + +VOID NTAPI KspAsyncContextFree(_In_ PKSOCKET_ASYNC_CONTEXT AsyncContext); + +VOID NTAPI KspAsyncContextReset(_In_ PKSOCKET_ASYNC_CONTEXT AsyncContext); + +static NTSTATUS +KspAsyncContextCompletionRoutine(_In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, _In_ PVOID CompletionEvent); + +NTSTATUS +NTAPI +KspAsyncContextWaitForCompletion(_In_ PKSOCKET_ASYNC_CONTEXT AsyncContext, + _Inout_ PNTSTATUS Status); + +////////////////////////////////////////////////////////////////////////// +// Private functions. +////////////////////////////////////////////////////////////////////////// + +NTSTATUS +NTAPI +KspAsyncContextAllocate(_Out_ PKSOCKET_ASYNC_CONTEXT AsyncContext) { + // + // Initialize the completion event. + // + + KeInitializeEvent(&AsyncContext->CompletionEvent, SynchronizationEvent, + FALSE); + + // + // Initialize the IRP. + // + + AsyncContext->Irp = IoAllocateIrp(1, FALSE); + + if (AsyncContext->Irp == NULL) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // KspAsyncContextCompletionRoutine will set + // the CompletionEvent. + // + + IoSetCompletionRoutine(AsyncContext->Irp, &KspAsyncContextCompletionRoutine, + &AsyncContext->CompletionEvent, TRUE, TRUE, TRUE); + + return STATUS_SUCCESS; +} + +VOID NTAPI KspAsyncContextFree(_In_ PKSOCKET_ASYNC_CONTEXT AsyncContext) { + // + // Free the IRP. + // + + IoFreeIrp(AsyncContext->Irp); +} + +VOID NTAPI KspAsyncContextReset(_In_ PKSOCKET_ASYNC_CONTEXT AsyncContext) { + // + // If the WSK application allocated the IRP, or is reusing an IRP + // that it previously allocated, then it must set an IoCompletion + // routine for the IRP before calling a WSK function. In this + // situation, the WSK application must specify TRUE for the + // InvokeOnSuccess, InvokeOnError, and InvokeOnCancel parameters that + // are passed to the IoSetCompletionRoutine function to ensure that + // the IoCompletion routine is always called. Furthermore, the IoCompletion + // routine that is set for the IRP must always return + // STATUS_MORE_PROCESSING_REQUIRED to terminate the completion processing + // of the IRP. If the WSK application is done using the IRP after the + // IoCompletion routine has been called, then it should call the IoFreeIrp + // function to free the IRP before returning from the IoCompletion routine. + // If the WSK application does not free the IRP then it can reuse the IRP + // for a call to another WSK function. + // + // (ref: + // https://docs.microsoft.com/en-us/windows-hardware/drivers/network/using-irps-with-winsock-kernel-functions) + // + + // + // Reset the completion event. + // + + KeResetEvent(&AsyncContext->CompletionEvent); + + // + // Reuse the IRP. + // + + IoReuseIrp(AsyncContext->Irp, STATUS_UNSUCCESSFUL); + + IoSetCompletionRoutine(AsyncContext->Irp, &KspAsyncContextCompletionRoutine, + &AsyncContext->CompletionEvent, TRUE, TRUE, TRUE); +} + +static NTSTATUS +KspAsyncContextCompletionRoutine(_In_ PDEVICE_OBJECT DeviceObject, + _In_ PIRP Irp, _In_ PVOID CompletionEvent) { + UNREFERENCED_PARAMETER(DeviceObject); + UNREFERENCED_PARAMETER(Irp); + + KeSetEvent((PKEVENT)CompletionEvent, IO_NO_INCREMENT, FALSE); + return STATUS_MORE_PROCESSING_REQUIRED; +} + +NTSTATUS +NTAPI +KspAsyncContextWaitForCompletion(_In_ PKSOCKET_ASYNC_CONTEXT AsyncContext, + _Inout_ PNTSTATUS Status) { + if (*Status == STATUS_PENDING) { + KeWaitForSingleObject(&AsyncContext->CompletionEvent, Executive, KernelMode, + FALSE, NULL); + + *Status = AsyncContext->Irp->IoStatus.Status; + } + + return *Status; +} + +////////////////////////////////////////////////////////////////////////// +// Public functions. +////////////////////////////////////////////////////////////////////////// + +NTSTATUS +NTAPI +KsInitialize(VOID) { + OSVERSIONINFOEXW OsVersionInfo; + NTSTATUS Status; + + OsVersionInfo.dwOSVersionInfoSize = sizeof(OsVersionInfo); + Status = RtlGetVersion((POSVERSIONINFOW)&OsVersionInfo); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + if (OsVersionInfo.wProductType != VER_NT_WORKSTATION || + OsVersionInfo.dwMajorVersion != 10 || OsVersionInfo.dwMinorVersion != 0) { + return STATUS_UNSUPPORTED_WINDOWS_VERSION; + } + + // + // Register as a WSK client. + // + + WSK_CLIENT_NPI WskClient; + WskClient.ClientContext = NULL; + WskClient.Dispatch = &WskDispatch; + + Status = WskRegister(&WskClient, &WskRegistration); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Capture the provider NPI. + // + + return WskCaptureProviderNPI(&WskRegistration, WSK_INFINITE_WAIT, + &WskProvider); +} + +VOID NTAPI KsDestroy(VOID) { + // + // Release the provider NPI instance. + // + + WskReleaseProviderNPI(&WskRegistration); + + // + // Deregister as a WSK client. + // + + WskDeregister(&WskRegistration); +} + +NTSTATUS +NTAPI +KsGetAddrInfo(_In_ PUNICODE_STRING NodeName, _In_ PUNICODE_STRING ServiceName, + _In_ PADDRINFOEXW Hints, _Out_ PADDRINFOEXW *Result) { + NTSTATUS Status; + + // + // Allocate async context. + // + + KSOCKET_ASYNC_CONTEXT AsyncContext; + Status = KspAsyncContextAllocate(&AsyncContext); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Call the WSK API. + // + + Status = WskProvider.Dispatch->WskGetAddressInfo(WskProvider.Client, // Client + NodeName, // NodeName + ServiceName, // ServiceName + 0, // NameSpace + NULL, // Provider + Hints, // Hints + Result, // Result + NULL, // OwningProcess + NULL, // OwningThread + AsyncContext.Irp // Irp + ); + + KspAsyncContextWaitForCompletion(&AsyncContext, &Status); + + // + // Free the async context. + // + + KspAsyncContextFree(&AsyncContext); + + return Status; +} + +VOID NTAPI KsFreeAddrInfo(_In_ PADDRINFOEXW AddrInfo) { + WskProvider.Dispatch->WskFreeAddressInfo(WskProvider.Client, // Client + AddrInfo // AddrInfo + ); +} + +NTSTATUS +NTAPI +KsCreateSocket(_Out_ PKSOCKET *Socket, _In_ ADDRESS_FAMILY AddressFamily, + _In_ USHORT SocketType, _In_ ULONG Protocol, _In_ ULONG Flags) { + NTSTATUS Status; + + // + // Allocate memory for the socket structure. + // + + PKSOCKET NewSocket = + (PKSOCKET)ExAllocatePoolWithTag(PagedPool, sizeof(KSOCKET), MEMORY_TAG); + + if (!NewSocket) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + // + // Allocate async context for the socket. + // + + Status = KspAsyncContextAllocate(&NewSocket->AsyncContext); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Create the WSK socket. + // + + Status = WskProvider.Dispatch->WskSocket(WskProvider.Client, // Client + AddressFamily, // AddressFamily + SocketType, // SocketType + Protocol, // Protocol + Flags, // Flags + NULL, // SocketContext + NULL, // Dispatch + NULL, // OwningProcess + NULL, // OwningThread + NULL, // SecurityDescriptor + NewSocket->AsyncContext.Irp // Irp + ); + + KspAsyncContextWaitForCompletion(&NewSocket->AsyncContext, &Status); + + // + // Save the socket instance and the socket dispatch table. + // + + if (NT_SUCCESS(Status)) { + NewSocket->WskSocket = + (PWSK_SOCKET)NewSocket->AsyncContext.Irp->IoStatus.Information; + NewSocket->WskDispatch = (PVOID)NewSocket->WskSocket->Dispatch; + + *Socket = NewSocket; + } + + return Status; +} + +NTSTATUS +NTAPI +KsCreateConnectionSocket(_Out_ PKSOCKET *Socket, + _In_ ADDRESS_FAMILY AddressFamily, + _In_ USHORT SocketType, _In_ ULONG Protocol) { + return KsCreateSocket(Socket, AddressFamily, SocketType, Protocol, + WSK_FLAG_CONNECTION_SOCKET); +} + +NTSTATUS +NTAPI +KsCreateListenSocket(_Out_ PKSOCKET *Socket, _In_ ADDRESS_FAMILY AddressFamily, + _In_ USHORT SocketType, _In_ ULONG Protocol) { + return KsCreateSocket(Socket, AddressFamily, SocketType, Protocol, + WSK_FLAG_LISTEN_SOCKET); +} + +NTSTATUS +NTAPI +KsCreateDatagramSocket(_Out_ PKSOCKET *Socket, + _In_ ADDRESS_FAMILY AddressFamily, + _In_ USHORT SocketType, _In_ ULONG Protocol) { + return KsCreateSocket(Socket, AddressFamily, SocketType, Protocol, + WSK_FLAG_DATAGRAM_SOCKET); +} + +NTSTATUS +NTAPI +KsCloseSocket(_In_ PKSOCKET Socket) { + NTSTATUS Status; + + // + // Reset the async context. + // + + KspAsyncContextReset(&Socket->AsyncContext); + + // + // Close the WSK socket. + // + + Status = Socket->WskConnectionDispatch->WskCloseSocket( + Socket->WskSocket, Socket->AsyncContext.Irp); + + KspAsyncContextWaitForCompletion(&Socket->AsyncContext, &Status); + + // + // Free the async context. + // + + KspAsyncContextFree(&Socket->AsyncContext); + + // + // Free memory for the socket structure. + // + + ExFreePoolWithTag(Socket, MEMORY_TAG); + + return Status; +} + +NTSTATUS +NTAPI +KsBind(_In_ PKSOCKET Socket, _In_ PSOCKADDR LocalAddress) { + NTSTATUS Status; + + // + // Reset the async context. + // + + KspAsyncContextReset(&Socket->AsyncContext); + + // + // Bind the socket. + // + + Status = Socket->WskListenDispatch->WskBind(Socket->WskSocket, // Socket + LocalAddress, // LocalAddress + 0, // Flags (reserved) + Socket->AsyncContext.Irp // Irp + ); + + KspAsyncContextWaitForCompletion(&Socket->AsyncContext, &Status); + + return Status; +} + +NTSTATUS +NTAPI +KsAccept(_In_ PKSOCKET Socket, _Out_ PKSOCKET *NewSocket, + _Out_opt_ PSOCKADDR LocalAddress, _Out_opt_ PSOCKADDR RemoteAddress) { + NTSTATUS Status; + + // + // Reset the async context. + // + + KspAsyncContextReset(&Socket->AsyncContext); + + // + // Accept the connection. + // + + Status = + Socket->WskListenDispatch->WskAccept(Socket->WskSocket, // ListenSocket + 0, // Flags + NULL, // AcceptSocketContext + NULL, // AcceptSocketDispatch + LocalAddress, // LocalAddress + RemoteAddress, // RemoteAddress + Socket->AsyncContext.Irp // Irp + ); + + KspAsyncContextWaitForCompletion(&Socket->AsyncContext, &Status); + + // + // Save the socket instance and the socket dispatch table. + // + + if (NT_SUCCESS(Status)) { + PKSOCKET KNewSocket = + ExAllocatePoolWithTag(PagedPool, sizeof(KSOCKET), MEMORY_TAG); + + if (!KNewSocket) { + return STATUS_INSUFFICIENT_RESOURCES; + } + + KNewSocket->WskSocket = + (PWSK_SOCKET)Socket->AsyncContext.Irp->IoStatus.Information; + KNewSocket->WskDispatch = (PVOID)KNewSocket->WskSocket->Dispatch; + KspAsyncContextAllocate(&KNewSocket->AsyncContext); + + *NewSocket = KNewSocket; + } + + return Status; +} + +NTSTATUS +NTAPI +KsConnect(_In_ PKSOCKET Socket, _In_ PSOCKADDR RemoteAddress) { + NTSTATUS Status; + + // + // Reset the async context. + // + + KspAsyncContextReset(&Socket->AsyncContext); + + // + // Bind the socket to the local address. + // + + SOCKADDR_IN LocalAddress; + LocalAddress.sin_family = AF_INET; + LocalAddress.sin_addr.s_addr = INADDR_ANY; + LocalAddress.sin_port = 0; + + Status = Socket->WskConnectionDispatch->WskBind( + Socket->WskSocket, // Socket + (PSOCKADDR)&LocalAddress, // LocalAddress + 0, // Flags (reserved) + Socket->AsyncContext.Irp // Irp + ); + + KspAsyncContextWaitForCompletion(&Socket->AsyncContext, &Status); + + if (!NT_SUCCESS(Status)) { + return Status; + } + + // + // Reset the async context (again). + // + + KspAsyncContextReset(&Socket->AsyncContext); + + // + // Connect to the remote host. + // + // N.B.: Instead of calling WskSocket(), WskBind() and WskConnect(), + // it is possible to just call WskSocketConnect(). + // + + Status = + Socket->WskConnectionDispatch->WskConnect(Socket->WskSocket, // Socket + RemoteAddress, // RemoteAddress + 0, // Flags (reserved) + Socket->AsyncContext.Irp // Irp + ); + + KspAsyncContextWaitForCompletion(&Socket->AsyncContext, &Status); + + return Status; +} + +NTSTATUS +NTAPI +KsSendRecv(_In_ PKSOCKET Socket, _In_ PVOID Buffer, _Inout_ PULONG Length, + _In_ ULONG Flags, _In_ BOOLEAN Send) { + NTSTATUS Status; + + // + // Wrap the buffer into the "WSK buffer". + // + + WSK_BUF WskBuffer; + WskBuffer.Offset = 0; + WskBuffer.Length = *Length; + WskBuffer.Mdl = + IoAllocateMdl(Buffer, (ULONG)WskBuffer.Length, FALSE, FALSE, NULL); + + //__try + { MmProbeAndLockPages(WskBuffer.Mdl, KernelMode, IoWriteAccess); } +#if 0 + __except (EXCEPTION_EXECUTE_HANDLER) + { + Status = STATUS_ACCESS_VIOLATION; + goto Error; + } +#endif + + // + // Reset the async context. + // + + KspAsyncContextReset(&Socket->AsyncContext); + + // + // Send / receive the data. + // + + if (Send) { + Status = + Socket->WskConnectionDispatch->WskSend(Socket->WskSocket, // Socket + &WskBuffer, // Buffer + Flags, // Flags + Socket->AsyncContext.Irp // Irp + ); + } else { + Status = Socket->WskConnectionDispatch->WskReceive( + Socket->WskSocket, // Socket + &WskBuffer, // Buffer + Flags, // Flags + Socket->AsyncContext.Irp // Irp + ); + } + + KspAsyncContextWaitForCompletion(&Socket->AsyncContext, &Status); + + // + // Set the number of bytes sent / received. + // + + if (NT_SUCCESS(Status)) { + *Length = (ULONG)Socket->AsyncContext.Irp->IoStatus.Information; + } + + // + // Free the MDL. + // + + MmUnlockPages(WskBuffer.Mdl); + + // Error: + IoFreeMdl(WskBuffer.Mdl); + return Status; +} + +NTSTATUS +NTAPI +KsSendRecvUdp(_In_ PKSOCKET Socket, _In_ PVOID Buffer, _Inout_ PULONG Length, + _In_ ULONG Flags, _In_ PSOCKADDR RemoteAddress, + _In_ BOOLEAN Send) { + NTSTATUS Status; + + // + // Wrap the buffer into the "WSK buffer". + // + + WSK_BUF WskBuffer; + WskBuffer.Offset = 0; + WskBuffer.Length = *Length; + WskBuffer.Mdl = + IoAllocateMdl(Buffer, (ULONG)WskBuffer.Length, FALSE, FALSE, NULL); + + //__try + { MmProbeAndLockPages(WskBuffer.Mdl, KernelMode, IoWriteAccess); } +#if 0 + __except (EXCEPTION_EXECUTE_HANDLER) + { + Status = STATUS_ACCESS_VIOLATION; + goto Error; + } +#endif + + // + // Reset the async context. + // + + KspAsyncContextReset(&Socket->AsyncContext); + + // + // Send / receive the data. + // + + if (Send) { + Status = + Socket->WskDatagramDispatch->WskSendTo(Socket->WskSocket, // Socket + &WskBuffer, // Buffer + Flags, // Flags (reserved) + RemoteAddress, // RemoteAddress + 0, // ControlInfoLength + NULL, // ControlInfo + Socket->AsyncContext.Irp // Irp + ); + } else { + Status = Socket->WskDatagramDispatch->WskReceiveFrom( + Socket->WskSocket, // Socket + &WskBuffer, // Buffer + Flags, // Flags (reserved) + RemoteAddress, // RemoteAddress + NULL, // ControlInfoLength + NULL, // ControlInfo + NULL, // ControlFlags + Socket->AsyncContext.Irp // Irp + ); + } + + KspAsyncContextWaitForCompletion(&Socket->AsyncContext, &Status); + + // + // Set the number of bytes sent / received. + // + + if (NT_SUCCESS(Status)) { + *Length = (ULONG)Socket->AsyncContext.Irp->IoStatus.Information; + } + + // + // Free the MDL. + // + + MmUnlockPages(WskBuffer.Mdl); + + // Error: + IoFreeMdl(WskBuffer.Mdl); + return Status; +} + +NTSTATUS +NTAPI +KsSend(_In_ PKSOCKET Socket, _In_ PVOID Buffer, _Inout_ PULONG Length, + _In_ ULONG Flags) { + return KsSendRecv(Socket, Buffer, Length, Flags, TRUE); +} + +NTSTATUS +NTAPI +KsRecv(_In_ PKSOCKET Socket, _In_ PVOID Buffer, _Inout_ PULONG Length, + _In_ ULONG Flags) { + return KsSendRecv(Socket, Buffer, Length, Flags, FALSE); +} + +NTSTATUS +NTAPI +KsSendTo(_In_ PKSOCKET Socket, _In_ PVOID Buffer, _Inout_ PULONG Length, + _In_ ULONG Flags, _In_ PSOCKADDR RemoteAddress) { + return KsSendRecvUdp(Socket, Buffer, Length, Flags, RemoteAddress, TRUE); +} + +NTSTATUS +NTAPI +KsRecvFrom(_In_ PKSOCKET Socket, _In_ PVOID Buffer, _Inout_ PULONG Length, + _In_ ULONG Flags, _In_ PSOCKADDR RemoteAddress) { + return KsSendRecvUdp(Socket, Buffer, Length, Flags, RemoteAddress, FALSE); +} |