diff options
Diffstat (limited to 'ksocket')
-rw-r--r-- | ksocket/berkeley.c | 580 | ||||
-rw-r--r-- | ksocket/berkeley.h | 41 | ||||
-rw-r--r-- | ksocket/helper.hpp | 157 | ||||
-rw-r--r-- | ksocket/ksocket.c | 713 | ||||
-rw-r--r-- | ksocket/ksocket.h | 99 | ||||
-rw-r--r-- | ksocket/wsk.h | 460 |
6 files changed, 2050 insertions, 0 deletions
diff --git a/ksocket/berkeley.c b/ksocket/berkeley.c new file mode 100644 index 0000000..e72f92e --- /dev/null +++ b/ksocket/berkeley.c @@ -0,0 +1,580 @@ +#include "berkeley.h" +#include "ksocket.h" + +////////////////////////////////////////////////////////////////////////// +// Definitions. +////////////////////////////////////////////////////////////////////////// + +#define MEMORY_TAG ((ULONG)0x2e2e4242) +#define SOCKETFD_MAX 128 +#define TO_SOCKETFD(index) ((index % SOCKETFD_MAX) + 0) +#define FROM_SOCKETFD(sockfd) ((sockfd)-0) + +////////////////////////////////////////////////////////////////////////// +// Function prototypes. +////////////////////////////////////////////////////////////////////////// + +NTSTATUS +NTAPI +KspUtilAddrInfoToAddrInfoEx(_In_ PADDRINFOA AddrInfo, + _Out_ PADDRINFOEXW *AddrInfoEx); + +NTSTATUS +NTAPI +KspUtilAddrInfoExToAddrInfo(_In_ PADDRINFOEXW AddrInfoEx, + _Out_ PADDRINFOA *AddrInfo); + +VOID NTAPI KspUtilFreeAddrInfo(_In_ PADDRINFOA AddrInfo); + +VOID NTAPI KspUtilFreeAddrInfoEx(_In_ PADDRINFOEXW AddrInfo); + +////////////////////////////////////////////////////////////////////////// +// Variables. +////////////////////////////////////////////////////////////////////////// + +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// +// This is complete bollocks and ideally it should be replaced with +// something like RTL_AVL_TABLE. +// +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// + +PKSOCKET KsArray[SOCKETFD_MAX] = {0}; +ULONG KsIndex = 0; + +////////////////////////////////////////////////////////////////////////// +// Private functions. +////////////////////////////////////////////////////////////////////////// + +NTSTATUS +NTAPI +KspUtilAddrInfoToAddrInfoEx(_In_ PADDRINFOA AddrInfo, + _Out_ PADDRINFOEXW *AddrInfoEx) { + NTSTATUS Status; + + // + // Convert NULL input into NULL output. + // + + if (AddrInfo == NULL) { + *AddrInfoEx = NULL; + return STATUS_SUCCESS; + } + + // + // Allocate memory for the output structure. + // + + PADDRINFOEXW Result = + ExAllocatePoolWithTag(PagedPool, sizeof(ADDRINFOEXW), MEMORY_TAG); + + if (Result == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Error1; + } + + // + // Copy numeric values. + // + + RtlZeroMemory(Result, sizeof(ADDRINFOEXW)); + Result->ai_flags = AddrInfo->ai_flags; + Result->ai_family = AddrInfo->ai_family; + Result->ai_socktype = AddrInfo->ai_socktype; + Result->ai_protocol = AddrInfo->ai_protocol; + Result->ai_addrlen = AddrInfo->ai_addrlen; + + // + // Copy canonical name. + // + + ANSI_STRING CanonicalNameAnsi; + UNICODE_STRING CanonicalNameUnicode; + + if (AddrInfo->ai_canonname) { + RtlInitAnsiString(&CanonicalNameAnsi, AddrInfo->ai_canonname); + + Status = RtlAnsiStringToUnicodeString(&CanonicalNameUnicode, + &CanonicalNameAnsi, TRUE); + + if (!NT_SUCCESS(Status)) { + goto Error2; + } + + Result->ai_canonname = CanonicalNameUnicode.Buffer; + } + + // + // Copy address. + // + + Result->ai_addr = AddrInfo->ai_addr; + + // + // Copy the next structure (recursively). + // + + PADDRINFOEXW NextAddrInfo; + Status = KspUtilAddrInfoToAddrInfoEx(AddrInfo->ai_next, &NextAddrInfo); + + if (!NT_SUCCESS(Status)) { + goto Error3; + } + + Result->ai_next = NextAddrInfo; + + // + // All done! + // + + *AddrInfoEx = Result; + + return Status; + +Error3: + RtlFreeAnsiString(&CanonicalNameAnsi); + +Error2: + ExFreePoolWithTag(Result, MEMORY_TAG); + +Error1: + return Status; +} + +NTSTATUS +NTAPI +KspUtilAddrInfoExToAddrInfo(_In_ PADDRINFOEXW AddrInfoEx, + _Out_ PADDRINFOA *AddrInfo) { + NTSTATUS Status; + + // + // Convert NULL input into NULL output. + // + + if (AddrInfoEx == NULL) { + *AddrInfo = NULL; + return STATUS_SUCCESS; + } + + // + // Allocate memory for the output structure. + // + + PADDRINFOA Result = + ExAllocatePoolWithTag(PagedPool, sizeof(ADDRINFOA), MEMORY_TAG); + + if (Result == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto Error1; + } + + // + // Copy numeric values. + // + + RtlZeroMemory(Result, sizeof(ADDRINFOA)); + Result->ai_flags = AddrInfoEx->ai_flags; + Result->ai_family = AddrInfoEx->ai_family; + Result->ai_socktype = AddrInfoEx->ai_socktype; + Result->ai_protocol = AddrInfoEx->ai_protocol; + Result->ai_addrlen = AddrInfoEx->ai_addrlen; + + // + // Copy canonical name. + // + + UNICODE_STRING CanonicalNameUnicode; + ANSI_STRING CanonicalNameAnsi; + + if (AddrInfoEx->ai_canonname) { + RtlInitUnicodeString(&CanonicalNameUnicode, AddrInfoEx->ai_canonname); + Status = RtlUnicodeStringToAnsiString(&CanonicalNameAnsi, + &CanonicalNameUnicode, TRUE); + + if (!NT_SUCCESS(Status)) { + goto Error2; + } + + Result->ai_canonname = CanonicalNameAnsi.Buffer; + } + + // + // Copy address. + // + + Result->ai_addr = AddrInfoEx->ai_addr; + + // + // Copy the next structure (recursively). + // + + PADDRINFOA NextAddrInfo; + Status = KspUtilAddrInfoExToAddrInfo(AddrInfoEx->ai_next, &NextAddrInfo); + + if (!NT_SUCCESS(Status)) { + goto Error3; + } + + Result->ai_next = NextAddrInfo; + + // + // All done! + // + + *AddrInfo = Result; + + return Status; + +Error3: + RtlFreeAnsiString(&CanonicalNameAnsi); + +Error2: + ExFreePoolWithTag(Result, MEMORY_TAG); + +Error1: + return Status; +} + +VOID NTAPI KspUtilFreeAddrInfo(_In_ PADDRINFOA AddrInfo) { + // + // Free all structures recursively. + // + + if (AddrInfo->ai_next) { + KspUtilFreeAddrInfo(AddrInfo->ai_next); + } + + // + // Free the canonical name buffer. + // + + if (AddrInfo->ai_canonname) { + ANSI_STRING CanonicalName; + RtlInitAnsiString(&CanonicalName, AddrInfo->ai_canonname); + RtlFreeAnsiString(&CanonicalName); + } + + // + // Finally, free the structure itself. + // + + ExFreePoolWithTag(AddrInfo, MEMORY_TAG); +} + +VOID NTAPI KspUtilFreeAddrInfoEx(_In_ PADDRINFOEXW AddrInfo) { + // + // Free all structures recursively. + // + + if (AddrInfo->ai_next) { + KspUtilFreeAddrInfoEx(AddrInfo->ai_next); + } + + // + // Free the canonical name buffer. + // + + if (AddrInfo->ai_canonname) { + UNICODE_STRING CanonicalName; + RtlInitUnicodeString(&CanonicalName, AddrInfo->ai_canonname); + RtlFreeUnicodeString(&CanonicalName); + } + + // + // Finally, free the structure itself. + // + + ExFreePoolWithTag(AddrInfo, MEMORY_TAG); +} + +////////////////////////////////////////////////////////////////////////// +// Public functions. +////////////////////////////////////////////////////////////////////////// + +uint32_t htonl(uint32_t hostlong) { return __builtin_bswap32(hostlong); } + +uint16_t htons(uint16_t hostshort) { return __builtin_bswap16(hostshort); } + +uint32_t ntohl(uint32_t netlong) { return __builtin_bswap32(netlong); } + +uint16_t ntohs(uint16_t netshort) { return __builtin_bswap16(netshort); } + +int getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, struct addrinfo **res) { + NTSTATUS Status; + + // + // Convert node name to the UNICODE_STRING (if present). + // + + ANSI_STRING NodeNameAnsi; + UNICODE_STRING NodeNameUnicode; + PUNICODE_STRING NodeName = NULL; + + if (node) { + RtlInitAnsiString(&NodeNameAnsi, node); + Status = + RtlAnsiStringToUnicodeString(&NodeNameUnicode, &NodeNameAnsi, TRUE); + + if (!NT_SUCCESS(Status)) { + goto Error1; + } + + NodeName = &NodeNameUnicode; + } + + // + // Convert service name to the UNICODE_STRING (if present). + // + + ANSI_STRING ServiceNameAnsi; + UNICODE_STRING ServiceNameUnicode; + PUNICODE_STRING ServiceName = NULL; + + if (service) { + RtlInitAnsiString(&ServiceNameAnsi, service); + Status = RtlAnsiStringToUnicodeString(&ServiceNameUnicode, &ServiceNameAnsi, + TRUE); + + if (!NT_SUCCESS(Status)) { + goto Error2; + } + + ServiceName = &ServiceNameUnicode; + } + + // + // Convert "struct addrinfo" to the "ADDRINFOEXW". + // + + PADDRINFOEXW Hints; + Status = KspUtilAddrInfoToAddrInfoEx((PADDRINFOA)hints, &Hints); + + if (!NT_SUCCESS(Status)) { + goto Error3; + } + + // + // All data is prepared, call the underlying API. + // + + PADDRINFOEXW Result; + Status = KsGetAddrInfo(NodeName, ServiceName, Hints, &Result); + + // + // Free the memory of the converted "Hints". + // + + KspUtilFreeAddrInfoEx(Hints); + + if (!NT_SUCCESS(Status)) { + goto Error3; + } + + // + // Convert the result "ADDRINFOEXW" to the "struct addrinfo". + // + + Status = KspUtilAddrInfoExToAddrInfo(Result, res); + + // + // Free the original result. + // + + KsFreeAddrInfo(Result); + + if (!NT_SUCCESS(Status)) { + goto Error3; + } + + return STATUS_SUCCESS; + +Error3: + RtlFreeUnicodeString(&ServiceNameUnicode); + +Error2: + RtlFreeUnicodeString(&NodeNameUnicode); + +Error1: + return Status; +} + +void freeaddrinfo(struct addrinfo *res) { + // + // Call our implementation. + // + + KspUtilFreeAddrInfo(res); +} + +int socket_connection(int domain, int type, int protocol) { + NTSTATUS Status; + PKSOCKET Socket; + + Status = KsCreateConnectionSocket(&Socket, (ADDRESS_FAMILY)domain, + (USHORT)type, (ULONG)protocol); + + if (NT_SUCCESS(Status)) { + int sockfd = TO_SOCKETFD(KsIndex++); + + KsArray[FROM_SOCKETFD(sockfd)] = Socket; + + return sockfd; + } + + return -1; +} + +int socket_listen(int domain, int type, int protocol) { + NTSTATUS Status; + PKSOCKET Socket; + + // + // WskSocket() returns STATUS_PROTOCOL_UNREACHABLE (0xC000023E) + // when Protocol == 0, so coerce this value to IPPROTO_TCP here. + // + + Status = KsCreateListenSocket(&Socket, (ADDRESS_FAMILY)domain, (USHORT)type, + protocol ? (ULONG)protocol : IPPROTO_TCP); + + if (NT_SUCCESS(Status)) { + int sockfd = TO_SOCKETFD(KsIndex++); + + KsArray[FROM_SOCKETFD(sockfd)] = Socket; + + return sockfd; + } + + return -1; +} + +int socket_datagram(int domain, int type, int protocol) { + NTSTATUS Status; + PKSOCKET Socket; + + Status = KsCreateDatagramSocket(&Socket, (ADDRESS_FAMILY)domain, (USHORT)type, + (ULONG)protocol); + + if (NT_SUCCESS(Status)) { + int sockfd = TO_SOCKETFD(KsIndex++); + + KsArray[FROM_SOCKETFD(sockfd)] = Socket; + + return sockfd; + } + + return -1; +} + +int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + UNREFERENCED_PARAMETER(addrlen); + + NTSTATUS Status; + PKSOCKET Socket = KsArray[FROM_SOCKETFD(sockfd)]; + + Status = KsConnect(Socket, (PSOCKADDR)addr); + + return NT_SUCCESS(Status) ? 0 : -1; +} + +int listen(int sockfd, int backlog) { + UNREFERENCED_PARAMETER(sockfd); + UNREFERENCED_PARAMETER(backlog); + return 0; +} + +int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { + UNREFERENCED_PARAMETER(addrlen); + + NTSTATUS Status; + PKSOCKET Socket = KsArray[FROM_SOCKETFD(sockfd)]; + + Status = KsBind(Socket, (PSOCKADDR)addr); + + return NT_SUCCESS(Status) ? 0 : -1; +} + +int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { + NTSTATUS Status; + PKSOCKET Socket = KsArray[FROM_SOCKETFD(sockfd)]; + + PKSOCKET NewSocket; + Status = KsAccept(Socket, &NewSocket, NULL, (PSOCKADDR)addr); + *addrlen = sizeof(SOCKADDR); + + if (NT_SUCCESS(Status)) { + int newsockfd = TO_SOCKETFD(KsIndex++); + + KsArray[FROM_SOCKETFD(newsockfd)] = NewSocket; + + return newsockfd; + } + + return -1; +} + +int send(int sockfd, const void *buf, size_t len, int flags) { + NTSTATUS Status; + PKSOCKET Socket = KsArray[FROM_SOCKETFD(sockfd)]; + + ULONG Length = (ULONG)len; + Status = KsSend(Socket, (PVOID)buf, &Length, (ULONG)flags); + + return NT_SUCCESS(Status) ? (int)Length : -1; +} + +int sendto(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen) { + UNREFERENCED_PARAMETER(addrlen); + + NTSTATUS Status; + PKSOCKET Socket = KsArray[FROM_SOCKETFD(sockfd)]; + + ULONG Length = (ULONG)len; + Status = + KsSendTo(Socket, (PVOID)buf, &Length, (ULONG)flags, (PSOCKADDR)dest_addr); + + return NT_SUCCESS(Status) ? (int)Length : -1; +} + +int recv(int sockfd, void *buf, size_t len, int flags) { + NTSTATUS Status; + PKSOCKET Socket = KsArray[FROM_SOCKETFD(sockfd)]; + + ULONG Length = (ULONG)len; + Status = KsRecv(Socket, (PVOID)buf, &Length, (ULONG)flags); + + return NT_SUCCESS(Status) ? (int)Length : -1; +} + +int recvfrom(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen) { + UNREFERENCED_PARAMETER(addrlen); + + NTSTATUS Status; + PKSOCKET Socket = KsArray[FROM_SOCKETFD(sockfd)]; + + ULONG Length = (ULONG)len; + Status = + KsSendTo(Socket, (PVOID)buf, &Length, (ULONG)flags, (PSOCKADDR)src_addr); + *addrlen = sizeof(SOCKADDR); + + return NT_SUCCESS(Status) ? (int)Length : -1; +} + +int closesocket(int sockfd) { + NTSTATUS Status; + PKSOCKET Socket = KsArray[FROM_SOCKETFD(sockfd)]; + + Status = KsCloseSocket(Socket); + + KsArray[FROM_SOCKETFD(sockfd)] = NULL; + + return NT_SUCCESS(Status) ? 0 : -1; +} diff --git a/ksocket/berkeley.h b/ksocket/berkeley.h new file mode 100644 index 0000000..b879d3c --- /dev/null +++ b/ksocket/berkeley.h @@ -0,0 +1,41 @@ +#pragma once +#include <ntddk.h> +#include <stdint.h> +#include <ksocket/wsk.h> + +#define socket socket_connection + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int socklen_t; +typedef intptr_t ssize_t; + +uint32_t htonl(uint32_t hostlong); +uint16_t htons(uint16_t hostshort); +uint32_t ntohl(uint32_t netlong); +uint16_t ntohs(uint16_t netshort); + +int getaddrinfo(const char *node, const char *service, + const struct addrinfo *hints, struct addrinfo **res); +void freeaddrinfo(struct addrinfo *res); + +int socket_connection(int domain, int type, int protocol); +int socket_listen(int domain, int type, int protocol); +int socket_datagram(int domain, int type, int protocol); +int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +int listen(int sockfd, int backlog); +int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); +int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); +int send(int sockfd, const void *buf, size_t len, int flags); +int sendto(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, socklen_t addrlen); +int recv(int sockfd, void *buf, size_t len, int flags); +int recvfrom(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen); +int closesocket(int sockfd); + +#ifdef __cplusplus +} +#endif diff --git a/ksocket/helper.hpp b/ksocket/helper.hpp new file mode 100644 index 0000000..153e549 --- /dev/null +++ b/ksocket/helper.hpp @@ -0,0 +1,157 @@ +#ifndef HELPER_HPP +#define HELPER_HPP 1 + +#include <protobuf-c/protobuf-c.h> +#include <stdint.h> +#include <stdlib.h> + +#include <EASTL/algorithm.h> +#include <EASTL/array.h> +#include <EASTL/initializer_list.h> +#include <EASTL/string.h> +#include <EASTL/vector.h> + +#ifndef SOCKET_ERROR +#define SOCKET_ERROR -1 +#endif + +#define SEND_ALL(sock, socket_buffer, retval) \ + if (socket_buffer.GetRemainingSendSize() > 0) { \ + do { \ + retval = send(sock, socket_buffer.GetStartS(), \ + socket_buffer.GetRemainingSendSize(), 0); \ + if (retval == SOCKET_ERROR || retval == 0) \ + break; \ + if (!socket_buffer.Consume(iResult)) \ + break; \ + } while (!socket_buffer.AllConsumed()); \ + socket_buffer.Sweep(); \ + } +#define RECV_PDU_BEGIN(sock, socket_buffer, retval, pdu_type, pdu_len) \ + do { \ + if (socket_buffer.GetRemainingRecvSize() == 0) \ + break; \ + uint16_t pdu_type; \ + uint32_t pdu_len; \ + do { \ + retval = recv(sock, socket_buffer.GetStartS(), \ + socket_buffer.GetRemainingRecvSize(), 0); \ + if (retval == SOCKET_ERROR || retval == 0) \ + break; \ + } while (!socket_buffer.GetPdu(retval, pdu_type, pdu_len)); +#define RECV_PDU_END(socket_buffer, pdu_len) \ + socket_buffer.Consume(pdu_len); \ + socket_buffer.Sweep(); \ + } \ + while (0) + +class BaseSerializer { +public: + virtual uint16_t GetPduType(void) = 0; + virtual size_t GetSerializedSize(void) = 0; + virtual size_t Serialize(uint8_t *buf) = 0; +}; + +class BaseDeserializer { +public: + virtual bool Deserialize(size_t pdu_len, uint8_t *buf) = 0; + virtual void DeserializeFree(void) = 0; +}; + +template <size_t SIZE> class SocketBuffer { +public: + SocketBuffer(void) {} + ~SocketBuffer(void) {} + size_t GetRemainingSendSize(void) { return GetUsed(); } + size_t GetRemainingRecvSize(void) { return GetSize() - GetUsed(); } + uint8_t *GetStart(void) { return buffer + consumed; } + char *GetStartS(void) { return (char *)buffer + consumed; } + bool SizeCheck(size_t required_size) { return used + required_size < SIZE; }; + template <typename T> bool GetPrimitve(size_t offset, T &out) { + if (offset + sizeof(out) > used) + return false; + out = *(T *)(GetStart() + offset); + return true; + } + template <typename T> bool AddPrimitve(T value) { + if (!SizeCheck(sizeof(value))) + return false; + *(T *)(GetEnd()) = value; + used += sizeof(value); + return true; + } + bool GetPdu(size_t received_size, uint16_t &pdu_type, uint32_t &pdu_len) { + if (received_size > SIZE - used) + return false; // You did something wrong! + used += received_size; + if (used < sizeof(pdu_type) + sizeof(pdu_len)) + return false; + if (GetPrimitve<uint32_t>(0, pdu_len) == false) + return false; + if (GetPrimitve<uint16_t>(4, pdu_type) == false) + return false; + + pdu_len = ntohl(pdu_len); + pdu_type = ntohs(pdu_type); + + if (used < sizeof(pdu_type) + sizeof(pdu_len) + pdu_len) + return false; + + consumed += sizeof(pdu_type) + sizeof(pdu_len); + + return true; + } + bool AddPdu(BaseSerializer &bs) { + uint16_t pdu_type = bs.GetPduType(); + uint32_t pdu_len = bs.GetSerializedSize(); + + if (!SizeCheck(sizeof(pdu_type) + sizeof(pdu_len) + pdu_len)) + return false; + + if (!AddPrimitve<uint32_t>(htonl(pdu_len))) + return false; + if (!AddPrimitve<uint16_t>(htons(pdu_type))) + return false; + if (bs.Serialize(GetEnd()) != pdu_len) + return false; + used += pdu_len; + + return true; + } + bool AllConsumed(void) { return used == consumed; } + bool Consume(size_t consuming_size) { + if (consuming_size + consumed > used) + return false; + consumed += consuming_size; + return true; + } + void Sweep() { + if (consumed == 0) + return; + if (used != consumed) + memmove(buffer, buffer + consumed, used - consumed); + used -= consumed; + consumed = 0; + } + +private: + uint8_t buffer[SIZE]; + size_t used = 0; + size_t consumed = 0; + + size_t GetSize(void) { return SIZE; } + size_t GetUsed(void) { return used - consumed; } + uint8_t *GetEnd() { return buffer + used; } +}; + +class ProtobufCBinaryDataClass : public ProtobufCBinaryData { +public: + ProtobufCBinaryDataClass(std::initializer_list<uint8_t> bytes) { + len = bytes.size(); + data = new uint8_t[len]; + eastl::copy(bytes.begin(), bytes.end(), data); + } + ~ProtobufCBinaryDataClass(void) { delete data; } +}; + +#endif 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); +} diff --git a/ksocket/ksocket.h b/ksocket/ksocket.h new file mode 100644 index 0000000..0a6717e --- /dev/null +++ b/ksocket/ksocket.h @@ -0,0 +1,99 @@ +#pragma once +#include "wsk.h" +#include <ntddk.h> + +#define STATUS_UNSUPPORTED_WINDOWS_VERSION \ + (STATUS_SEVERITY_ERROR | (1 << 29) | (1 << 16) | 1) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _KSOCKET KSOCKET, *PKSOCKET; + +NTSTATUS +NTAPI +KsInitialize(VOID); + +VOID NTAPI KsDestroy(VOID); + +NTSTATUS +NTAPI +KsGetAddrInfo(_In_ PUNICODE_STRING NodeName, _In_ PUNICODE_STRING ServiceName, + _In_ PADDRINFOEXW Hints, _Out_ PADDRINFOEXW *Result); + +VOID NTAPI KsFreeAddrInfo(_In_ PADDRINFOEXW AddrInfo); + +NTSTATUS +NTAPI +KsCreateSocket(_Out_ PKSOCKET *Socket, _In_ ADDRESS_FAMILY AddressFamily, + _In_ USHORT SocketType, _In_ ULONG Protocol, _In_ ULONG Flags); + +NTSTATUS +NTAPI +KsCreateConnectionSocket(_Out_ PKSOCKET *Socket, + _In_ ADDRESS_FAMILY AddressFamily, + _In_ USHORT SocketType, _In_ ULONG Protocol); + +NTSTATUS +NTAPI +KsCreateListenSocket(_Out_ PKSOCKET *Socket, _In_ ADDRESS_FAMILY AddressFamily, + _In_ USHORT SocketType, _In_ ULONG Protocol); + +NTSTATUS +NTAPI +KsCreateDatagramSocket(_Out_ PKSOCKET *Socket, + _In_ ADDRESS_FAMILY AddressFamily, + _In_ USHORT SocketType, _In_ ULONG Protocol); + +NTSTATUS +NTAPI +KsCloseSocket(_In_ PKSOCKET Socket); + +NTSTATUS +NTAPI +KsBind(_In_ PKSOCKET Socket, _In_ PSOCKADDR LocalAddress); + +NTSTATUS +NTAPI +KsAccept(_In_ PKSOCKET Socket, _Out_ PKSOCKET *NewSocket, + _Out_opt_ PSOCKADDR LocalAddress, _Out_opt_ PSOCKADDR RemoteAddress); + +NTSTATUS +NTAPI +KsConnect(_In_ PKSOCKET Socket, _In_ PSOCKADDR RemoteAddress); + +NTSTATUS +NTAPI +KsSendRecv(_In_ PKSOCKET Socket, _In_ PVOID Buffer, _Inout_ PULONG Length, + _In_ ULONG Flags, _In_ BOOLEAN Send); + +NTSTATUS +NTAPI +KsSendRecvUdp(_In_ PKSOCKET Socket, _In_ PVOID Buffer, _Inout_ PULONG Length, + _In_ ULONG Flags, _In_ PSOCKADDR RemoteAddress, + _In_ BOOLEAN Send); + +NTSTATUS +NTAPI +KsSend(_In_ PKSOCKET Socket, _In_ PVOID Buffer, _Inout_ PULONG Length, + _In_ ULONG Flags); + +NTSTATUS +NTAPI +KsRecv(_In_ PKSOCKET Socket, _In_ PVOID Buffer, _Inout_ PULONG Length, + _In_ ULONG Flags); + +NTSTATUS +NTAPI +KsSendTo(_In_ PKSOCKET Socket, _In_ PVOID Buffer, _Inout_ PULONG Length, + _In_ ULONG Flags, _In_ PSOCKADDR RemoteAddress); + +NTSTATUS +NTAPI +KsRecvFrom(_In_ PKSOCKET Socket, _In_ PVOID Buffer, _Inout_ PULONG Length, + _In_ ULONG Flags, _In_ PSOCKADDR RemoteAddress); + +#ifdef __cplusplus +} +#endif diff --git a/ksocket/wsk.h b/ksocket/wsk.h new file mode 100644 index 0000000..ccea103 --- /dev/null +++ b/ksocket/wsk.h @@ -0,0 +1,460 @@ +#ifndef WSK_H +#define WSK_H 1 + +#include <ntddk.h> + +#if !defined(__MINGW64__) +#error \ + "This was designed to work **only** with Mingw-w64! For MSVC, please check https://github.com/wbenny/KSOCKET out" +#endif + +#if (NTDDI_VERSION < NTDDI_WIN10) +#error \ + "Your mingw-w64 toolchain is too old. Please use the one provided by https://github.com/utoni/mingw-w64-ddk-template" +#endif + +#if !defined(__BYTE_ORDER__) || __BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__ +#error \ + "This project requires a little endian system. Does Windows support any other?" +#endif + +// --------------------------------------------------------------- +// defines +// --------------------------------------------------------------- + +#define WSKAPI NTAPI +#define MAKE_WSK_VERSION(Mj, Mn) ((USHORT)((Mj) << 8) | (USHORT)((Mn)&0xff)) +#define WSK_NO_WAIT 0 +#define WSK_INFINITE_WAIT 0xffffffff + +#define WSK_FLAG_BASIC_SOCKET 0x00000000 +#define WSK_FLAG_LISTEN_SOCKET 0x00000001 +#define WSK_FLAG_CONNECTION_SOCKET 0x00000002 +#define WSK_FLAG_DATAGRAM_SOCKET 0x00000004 +#define WSK_FLAG_STREAM_SOCKET 0x00000008 + +#define INADDR_ANY ((ULONG)0x00000000) + +// --------------------------------------------------------------- +// forward decls / opaque structs +// --------------------------------------------------------------- + +struct _WSK_CLIENT_CONNECTION_DISPATCH; +typedef struct _WSK_CLIENT_CONNECTION_DISPATCH WSK_CLIENT_CONNECTION_DISPATCH; +typedef WSK_CLIENT_CONNECTION_DISPATCH *PWSK_CLIENT_CONNECTION_DISPATCH; +struct _WSK_CLIENT_NPI; +typedef struct _WSK_CLIENT_NPI WSK_CLIENT_NPI; +typedef struct _WSK_CLIENT_NPI *PWSK_CLIENT_NPI; +struct _WSK_PROVIDER_NPI; +typedef struct _WSK_PROVIDER_NPI WSK_PROVIDER_NPI; +typedef struct _WSK_PROVIDER_NPI *PWSK_PROVIDER_NPI; + +typedef PVOID PWSK_CLIENT; + +// --------------------------------------------------------------- +// enums +// --------------------------------------------------------------- + +typedef enum { + WskSetOption, // set socket option + WskGetOption, // get socket option + WskIoctl, // socket IOCTL + WskControlMax +} WSK_CONTROL_SOCKET_TYPE, + *PWSK_CONTROL_SOCKET_TYPE; + +enum { + AI_PASSIVE = 0x01, + AI_CANONNAME = 0x02, + AI_NUMERICHOST = 0x04, + AI_ALL = 0x0100, + AI_ADDRCONFIG = 0x0400, + AI_V4MAPPED = 0x0800, + AI_NON_AUTHORITATIVE = 0x4000, + AI_SECURE = 0x08000, + AI_RETURN_PREFERRED_NAMES = 0x10000, + AI_FQDN = 0x00020000, + AI_FILESERVER = 0x00040000 +}; + +typedef enum ADDRESS_FAMILY { + AF_UNSPEC = 0, + AF_INET = 2, + AF_INET6 = 23 +} ADDRESS_FAMILY; + +enum { + IPPROTO_ICMP = 1, + IPPROTO_IGMP = 2, + BTHPROTO_RFCOMM = 3, + IPPROTO_TCP = 6, + IPPROTO_UDP = 17, + IPPROTO_ICMPV6 = 58, + IPPROTO_RM = 113 +}; + +enum { + SOCK_STREAM = 1, + SOCK_DGRAM = 2, + SOCK_RAW = 3, + SOCK_RDM = 4, + SOCK_SEQPACKET = 5, +}; + +typedef enum { + WskInspectReject, // reject the connection request + WskInspectAccept, // proceed with accept + WskInspectPend, // delay the decision (use WskInspectComplete later) + WskInspectMax +} WSK_INSPECT_ACTION, + *PWSK_INSPECT_ACTION; + +// --------------------------------------------------------------- +// general structs +// --------------------------------------------------------------- + +typedef struct addrinfo { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char *ai_canonname; + struct sockaddr *ai_addr; + struct addrinfo *ai_next; +} ADDRINFOA, *PADDRINFOA; + +typedef struct addrinfoexA { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + char *ai_canonname; + struct sockaddr *ai_addr; + void *ai_blob; + size_t ai_bloblen; + LPGUID ai_provider; + struct addrinfoexA *ai_next; +} ADDRINFOEXA, *PADDRINFOEXA, *LPADDRINFOEXA; + +typedef struct addrinfoW { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + PWSTR ai_canonname; + struct sockaddr *ai_addr; + struct addrinfoW *ai_next; +} ADDRINFOW, *PADDRINFOW; + +typedef struct addrinfoexW { + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + size_t ai_addrlen; + PWSTR ai_canonname; + struct sockaddr *ai_addr; + void *ai_blob; + size_t ai_bloblen; + LPGUID ai_provider; + struct addrinfoexW *ai_next; +} ADDRINFOEXW, *PADDRINFOEXW, *LPADDRINFOEXW; + +typedef struct sockaddr { + ADDRESS_FAMILY sa_family; + CHAR sa_data[14]; +} SOCKADDR, *PSOCKADDR, *LPSOCKADDR; + +struct in_addr { + union { + struct { + UCHAR s_b1; + UCHAR s_b2; + UCHAR s_b3; + UCHAR s_b4; + } s_un_b; + struct { + USHORT s_w1; + USHORT s_w2; + } s_un_w; + ULONG s_addr; + }; +}; + +typedef struct sockaddr_in { + short sin_family; + USHORT sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +} SOCKADDR_IN, *PSOCKADDR_IN, *LPSOCKADDR_IN; + +typedef struct _WSK_SOCKET { + const VOID *Dispatch; +} WSK_SOCKET, *PWSK_SOCKET; + +typedef struct _WSK_BUF { + PMDL Mdl; // Locked MDL chain + ULONG Offset; // Offset into the "first" Mdl in the chain + SIZE_T Length; // Length of data starting from Offset +} WSK_BUF, *PWSK_BUF; + +typedef struct _WSK_BUF_LIST { + struct _WSK_BUF_LIST *Next; + WSK_BUF Buffer; +} WSK_BUF_LIST, *PWSK_BUF_LIST; + +typedef struct _WSK_DATA_INDICATION { + struct _WSK_DATA_INDICATION *Next; + WSK_BUF Buffer; +} WSK_DATA_INDICATION, *PWSK_DATA_INDICATION; + +typedef struct _WSK_INSPECT_ID { + ULONG_PTR Key; + ULONG SerialNumber; +} WSK_INSPECT_ID, *PWSK_INSPECT_ID; + +typedef struct _WSACMSGHDR { + SIZE_T cmsg_len; + INT cmsg_level; + INT cmsg_type; +} WSACMSGHDR, *PWSACMSGHDR, *LPWSACMSGHDR, CMSGHDR, *PCMSGHDR; + +typedef struct _WSK_DATAGRAM_INDICATION { + struct _WSK_DATAGRAM_INDICATION *Next; + WSK_BUF Buffer; + _Field_size_bytes_(ControlInfoLength) PCMSGHDR ControlInfo; + ULONG ControlInfoLength; + PSOCKADDR RemoteAddress; +} WSK_DATAGRAM_INDICATION, *PWSK_DATAGRAM_INDICATION; + +typedef struct _WSK_REGISTRATION { + ULONGLONG ReservedRegistrationState; + PVOID ReservedRegistrationContext; + KSPIN_LOCK ReservedRegistrationLock; +} WSK_REGISTRATION, *PWSK_REGISTRATION; + +// --------------------------------------------------------------- +// callback functions +// --------------------------------------------------------------- + +typedef NTSTATUS(WSKAPI *PFN_WSK_CONTROL_SOCKET)( + _In_ PWSK_SOCKET Socket, _In_ WSK_CONTROL_SOCKET_TYPE RequestType, + _In_ ULONG ControlCode, _In_ ULONG Level, _In_ SIZE_T InputSize, + _In_opt_ PVOID InputBuffer, _In_ SIZE_T OutputSize, + _Out_opt_ PVOID OutputBuffer, _Out_opt_ SIZE_T *OutputSizeReturned, + _In_ _Out_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_CLOSE_SOCKET)(_In_ PWSK_SOCKET Socket, + _Inout_ PIRP Irp); + +typedef NTSTATUS(WSKAPI *PFN_WSK_CONNECT)(_In_ PWSK_SOCKET Socket, + _In_ PSOCKADDR RemoteAddress, + ULONG Flags, _In_ _Out_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_BIND)(_In_ PWSK_SOCKET Socket, + _In_ PSOCKADDR LocalAddress, + _Reserved_ ULONG Flags, + _Inout_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_GET_LOCAL_ADDRESS)( + _In_ PWSK_SOCKET Socket, _Out_ PSOCKADDR LocalAddress, _Inout_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_GET_REMOTE_ADDRESS)( + _In_ PWSK_SOCKET Socket, _Out_ PSOCKADDR RemoteAddress, _Inout_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_SEND)(_In_ PWSK_SOCKET Socket, + _In_ PWSK_BUF Buffer, _In_ ULONG Flags, + _Inout_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_RECEIVE)(_In_ PWSK_SOCKET Socket, + _In_ PWSK_BUF Buffer, + _In_ ULONG Flags, _Inout_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_DISCONNECT)(_In_ PWSK_SOCKET Socket, + _In_opt_ PWSK_BUF Buffer, + _In_ ULONG Flags, + _Inout_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_RELEASE_DATA_INDICATION_LIST)( + _In_ PWSK_SOCKET Socket, _In_ PWSK_DATA_INDICATION DataIndication); +typedef NTSTATUS(WSKAPI *PFN_WSK_CONNECT_EX)(_In_ PWSK_SOCKET Socket, + _In_ PSOCKADDR RemoteAddress, + _In_opt_ PWSK_BUF Buffer, + _Reserved_ ULONG Flags, + _Inout_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_CONNECT_EX)(_In_ PWSK_SOCKET Socket, + _In_ PSOCKADDR RemoteAddress, + _In_opt_ PWSK_BUF Buffer, + _Reserved_ ULONG Flags, + _Inout_ PIRP Irp); +typedef void *PFN_WSK_SEND_EX; // reserved for system use +typedef void *PFN_WSK_RECEIVE_EX; // reserved for system use +typedef _At_(Irp->IoStatus.Information, __drv_allocatesMem(Mem)) + NTSTATUS(WSKAPI *PFN_WSK_ACCEPT)( + _In_ PWSK_SOCKET ListenSocket, _Reserved_ ULONG Flags, + _In_opt_ PVOID AcceptSocketContext, + _In_opt_ CONST WSK_CLIENT_CONNECTION_DISPATCH *AcceptSocketDispatch, + _Out_opt_ PSOCKADDR LocalAddress, _Out_opt_ PSOCKADDR RemoteAddress, + _Inout_ PIRP Irp); +typedef _Must_inspect_result_ NTSTATUS(WSKAPI *PFN_WSK_RECEIVE_EVENT)( + _In_opt_ PVOID SocketContext, _In_ ULONG Flags, + _In_opt_ PWSK_DATA_INDICATION DataIndication, _In_ SIZE_T BytesIndicated, + _Inout_ SIZE_T *BytesAccepted); +typedef NTSTATUS(WSKAPI *PFN_WSK_DISCONNECT_EVENT)(_In_opt_ PVOID SocketContext, + _In_ ULONG Flags); +typedef NTSTATUS(WSKAPI *PFN_WSK_SEND_BACKLOG_EVENT)( + _In_opt_ PVOID SocketContext, _In_ SIZE_T IdealBacklogSize); +typedef NTSTATUS(WSKAPI *PFN_WSK_INSPECT_COMPLETE)( + _In_ PWSK_SOCKET ListenSocket, _In_ PWSK_INSPECT_ID InspectID, + _In_ WSK_INSPECT_ACTION Action, _Inout_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_SEND_TO)( + _In_ PWSK_SOCKET Socket, _In_ PWSK_BUF Buffer, _Reserved_ ULONG Flags, + _In_opt_ PSOCKADDR RemoteAddress, _In_ ULONG ControlInfoLength, + _In_reads_bytes_opt_(ControlInfoLength) PCMSGHDR ControlInfo, + _Inout_ PIRP Irp); +typedef _Must_inspect_result_ NTSTATUS(WSKAPI *PFN_WSK_RECEIVE_FROM_EVENT)( + _In_opt_ PVOID SocketContext, _In_ ULONG Flags, + _In_opt_ PWSK_DATAGRAM_INDICATION DataIndication); +typedef NTSTATUS(WSKAPI *PFN_WSK_RECEIVE_FROM)( + _In_ PWSK_SOCKET Socket, _In_ PWSK_BUF Buffer, _Reserved_ ULONG Flags, + _Out_opt_ PSOCKADDR RemoteAddress, _Inout_ PULONG ControlLength, + _Out_writes_bytes_opt_(*ControlLength) PCMSGHDR ControlInfo, + _Out_opt_ PULONG ControlFlags, _Inout_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_RELEASE_DATAGRAM_INDICATION_LIST)( + _In_ PWSK_SOCKET Socket, _In_ PWSK_DATAGRAM_INDICATION DatagramIndication); +typedef NTSTATUS(WSKAPI *PFN_WSK_SEND_MESSAGES)( + _In_ PWSK_SOCKET Socket, _In_ PWSK_BUF_LIST BufferList, + _Reserved_ ULONG Flags, _In_opt_ PSOCKADDR RemoteAddress, + _In_ ULONG ControlInfoLength, + _In_reads_bytes_opt_(ControlInfoLength) PCMSGHDR ControlInfo, + _Inout_ PIRP Irp); +typedef _At_((void *)Irp->IoStatus.Information, __drv_allocatesMem(Mem)) + NTSTATUS(WSKAPI *PFN_WSK_SOCKET)( + _In_ PWSK_CLIENT Client, _In_ ADDRESS_FAMILY AddressFamily, + _In_ USHORT SocketType, _In_ ULONG Protocol, _In_ ULONG Flags, + _In_opt_ PVOID SocketContext, _In_opt_ CONST VOID *Dispatch, + _In_opt_ PEPROCESS OwningProcess, _In_opt_ PETHREAD OwningThread, + _In_opt_ PSECURITY_DESCRIPTOR SecurityDescriptor, _Inout_ PIRP Irp); +typedef _At_(Irp->IoStatus.Information, __drv_allocatesMem(Mem)) + NTSTATUS(WSKAPI *PFN_WSK_SOCKET_CONNECT)( + _In_ PWSK_CLIENT Client, _In_ USHORT SocketType, _In_ ULONG Protocol, + _In_ PSOCKADDR LocalAddress, _In_ PSOCKADDR RemoteAddress, + _Reserved_ ULONG Flags, _In_opt_ PVOID SocketContext, + _In_opt_ CONST WSK_CLIENT_CONNECTION_DISPATCH *Dispatch, + _In_opt_ PEPROCESS OwningProcess, _In_opt_ PETHREAD OwningThread, + _In_opt_ PSECURITY_DESCRIPTOR SecurityDescriptor, _Inout_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_CONTROL_CLIENT)( + _In_ PWSK_CLIENT Client, _In_ ULONG ControlCode, _In_ SIZE_T InputSize, + _In_reads_bytes_opt_(InputSize) PVOID InputBuffer, _In_ SIZE_T OutputSize, + _Out_writes_bytes_opt_(OutputSize) PVOID OutputBuffer, + _Out_opt_ SIZE_T *OutputSizeReturned, _Inout_opt_ PIRP Irp); +typedef _At_(*Result, __drv_allocatesMem(Mem)) + NTSTATUS(WSKAPI *PFN_WSK_GET_ADDRESS_INFO)( + _In_ PWSK_CLIENT Client, _In_opt_ PUNICODE_STRING NodeName, + _In_opt_ PUNICODE_STRING ServiceName, _In_opt_ ULONG NameSpace, + _In_opt_ GUID *Provider, _In_opt_ PADDRINFOEXW Hints, + _Outptr_ PADDRINFOEXW *Result, _In_opt_ PEPROCESS OwningProcess, + _In_opt_ PETHREAD OwningThread, _Inout_ PIRP Irp); +typedef _At_(AddrInfo, __drv_freesMem(Mem)) + VOID(WSKAPI *PFN_WSK_FREE_ADDRESS_INFO)(_In_ PWSK_CLIENT Client, + _In_ PADDRINFOEXW AddrInfo); +typedef NTSTATUS(WSKAPI *PFN_WSK_GET_NAME_INFO)( + _In_ PWSK_CLIENT Client, _In_ PSOCKADDR SockAddr, _In_ ULONG SockAddrLength, + _Out_opt_ PUNICODE_STRING NodeName, _Out_opt_ PUNICODE_STRING ServiceName, + _In_ ULONG Flags, _In_opt_ PEPROCESS OwningProcess, + _In_opt_ PETHREAD OwningThread, _Inout_ PIRP Irp); +typedef NTSTATUS(WSKAPI *PFN_WSK_CLIENT_EVENT)( + _In_opt_ PVOID ClientContext, _In_ ULONG EventType, + _In_reads_bytes_opt_(InformationLength) PVOID Information, + _In_ SIZE_T InformationLength); +_Must_inspect_result_ NTSTATUS WskRegister( + _In_ PWSK_CLIENT_NPI WskClientNpi, _Out_ PWSK_REGISTRATION WskRegistration); +_Must_inspect_result_ NTSTATUS WskCaptureProviderNPI( + _In_ PWSK_REGISTRATION WskRegistration, _In_ ULONG WaitTimeout, + _Out_ PWSK_PROVIDER_NPI WskProviderNpi); +VOID WskReleaseProviderNPI(_In_ PWSK_REGISTRATION WskRegistration); +VOID WskDeregister(_In_ PWSK_REGISTRATION WskRegistration); + +// --------------------------------------------------------------- +// function pointer structs +// --------------------------------------------------------------- + +typedef struct _WSK_CLIENT_CONNECTION_DISPATCH { + PFN_WSK_RECEIVE_EVENT WskReceiveEvent; + PFN_WSK_DISCONNECT_EVENT WskDisconnectEvent; + PFN_WSK_SEND_BACKLOG_EVENT WskSendBacklogEvent; +} WSK_CLIENT_CONNECTION_DISPATCH, *PWSK_CLIENT_CONNECTION_DISPATCH; + +typedef struct _WSK_PROVIDER_BASIC_DISPATCH { + PFN_WSK_CONTROL_SOCKET WskControlSocket; + PFN_WSK_CLOSE_SOCKET WskCloseSocket; +} WSK_PROVIDER_BASIC_DISPATCH, *PWSK_PROVIDER_BASIC_DISPATCH; + +typedef struct _WSK_PROVIDER_CONNECTION_DISPATCH { +#ifdef __cplusplus + WSK_PROVIDER_BASIC_DISPATCH Basic; +#else + WSK_PROVIDER_BASIC_DISPATCH; +#endif + PFN_WSK_BIND WskBind; + PFN_WSK_CONNECT WskConnect; + PFN_WSK_GET_LOCAL_ADDRESS WskGetLocalAddress; + PFN_WSK_GET_REMOTE_ADDRESS WskGetRemoteAddress; + PFN_WSK_SEND WskSend; + PFN_WSK_RECEIVE WskReceive; + PFN_WSK_DISCONNECT WskDisconnect; + PFN_WSK_RELEASE_DATA_INDICATION_LIST WskRelease; + PFN_WSK_CONNECT_EX WskConnectEx; + PFN_WSK_SEND_EX WskSendEx; + PFN_WSK_RECEIVE_EX WskReceiveEx; +} WSK_PROVIDER_CONNECTION_DISPATCH, *PWSK_PROVIDER_CONNECTION_DISPATCH; + +typedef struct _WSK_PROVIDER_LISTEN_DISPATCH { +#ifdef __cplusplus + WSK_PROVIDER_BASIC_DISPATCH Basic; +#else + WSK_PROVIDER_BASIC_DISPATCH; +#endif + PFN_WSK_BIND WskBind; + PFN_WSK_ACCEPT WskAccept; + PFN_WSK_INSPECT_COMPLETE WskInspectComplete; + PFN_WSK_GET_LOCAL_ADDRESS WskGetLocalAddress; +} WSK_PROVIDER_LISTEN_DISPATCH, *PWSK_PROVIDER_LISTEN_DISPATCH; + +typedef struct _WSK_PROVIDER_DATAGRAM_DISPATCH { +#ifdef __cplusplus + WSK_PROVIDER_BASIC_DISPATCH Basic; +#else + WSK_PROVIDER_BASIC_DISPATCH; +#endif + PFN_WSK_BIND WskBind; + PFN_WSK_SEND_TO WskSendTo; + PFN_WSK_RECEIVE_FROM WskReceiveFrom; + PFN_WSK_RELEASE_DATAGRAM_INDICATION_LIST WskRelease; + PFN_WSK_GET_LOCAL_ADDRESS WskGetLocalAddress; + PFN_WSK_SEND_MESSAGES WskSendMessages; +} WSK_PROVIDER_DATAGRAM_DISPATCH, *PWSK_PROVIDER_DATAGRAM_DISPATCH; + +typedef struct _WSK_PROVIDER_DISPATCH { + USHORT Version; + USHORT Reserved; + PFN_WSK_SOCKET WskSocket; + PFN_WSK_SOCKET_CONNECT WskSocketConnect; + PFN_WSK_CONTROL_CLIENT WskControlClient; + PFN_WSK_GET_ADDRESS_INFO WskGetAddressInfo; + PFN_WSK_FREE_ADDRESS_INFO WskFreeAddressInfo; + PFN_WSK_GET_NAME_INFO WskGetNameInfo; +} WSK_PROVIDER_DISPATCH, *PWSK_PROVIDER_DISPATCH; + +typedef struct _WSK_CLIENT_DISPATCH { + USHORT Version; + USHORT Reserved; + PFN_WSK_CLIENT_EVENT WskClientEvent; +} WSK_CLIENT_DISPATCH, *PWSK_CLIENT_DISPATCH; + +typedef struct _WSK_CLIENT_NPI { + PVOID ClientContext; + CONST WSK_CLIENT_DISPATCH *Dispatch; +} WSK_CLIENT_NPI, *PWSK_CLIENT_NPI; + +typedef struct _WSK_PROVIDER_NPI { + PWSK_CLIENT Client; + CONST WSK_PROVIDER_DISPATCH *Dispatch; +} WSK_PROVIDER_NPI, *PWSK_PROVIDER_NPI; + +#endif |