diff --git a/dll/windivert.c b/dll/windivert.c index cf4d399..0a7d63c 100644 --- a/dll/windivert.c +++ b/dll/windivert.c @@ -79,10 +79,10 @@ static BOOLEAN WinDivertAToX(const char *str, char **endptr, UINT32 *intptr); static BOOLEAN WinDivertUse32Bit(void); static BOOLEAN WinDivertGetDriverFileName(LPWSTR sys_str); static SC_HANDLE WinDivertDriverInstall(VOID); -static BOOL WinDivertIoControl(HANDLE handle, DWORD code, UINT8 arg8, - UINT64 arg, PVOID buf, UINT len, UINT *iolen); -static BOOL WinDivertIoControlEx(HANDLE handle, DWORD code, UINT8 arg8, - UINT64 arg, PVOID buf, UINT len, UINT *iolen, LPOVERLAPPED overlapped); +static BOOL WinDivertIoControl(HANDLE handle, DWORD code, UINT64 arg1, + UINT64 arg2, PVOID buf, UINT len, UINT *iolen); +static BOOL WinDivertIoControlEx(HANDLE handle, DWORD code, UINT64 arg1, + UINT64 arg2, PVOID buf, UINT len, UINT *iolen, LPOVERLAPPED overlapped); static UINT8 WinDivertSkipExtHeaders(UINT8 proto, UINT8 **header, UINT *len); /* @@ -300,8 +300,8 @@ WinDivertDriverInstallExit: /* * Perform a DeviceIoControl. */ -static BOOL WinDivertIoControl(HANDLE handle, DWORD code, UINT8 arg8, - UINT64 arg, PVOID buf, UINT len, UINT *iolen) +static BOOL WinDivertIoControl(HANDLE handle, DWORD code, UINT64 arg1, + UINT64 arg2, PVOID buf, UINT len, UINT *iolen) { OVERLAPPED overlapped; DWORD iolen0; @@ -320,7 +320,7 @@ static BOOL WinDivertIoControl(HANDLE handle, DWORD code, UINT8 arg8, memset(&overlapped, 0, sizeof(overlapped)); overlapped.hEvent = event; - if (!WinDivertIoControlEx(handle, code, arg8, arg, buf, len, iolen, + if (!WinDivertIoControlEx(handle, code, arg1, arg2, buf, len, iolen, &overlapped)) { if (GetLastError() != ERROR_IO_PENDING || @@ -339,17 +339,15 @@ static BOOL WinDivertIoControl(HANDLE handle, DWORD code, UINT8 arg8, /* * Perform an (overlapped) DeviceIoControl. */ -static BOOL WinDivertIoControlEx(HANDLE handle, DWORD code, UINT8 arg8, - UINT64 arg, PVOID buf, UINT len, UINT *iolen, LPOVERLAPPED overlapped) +static BOOL WinDivertIoControlEx(HANDLE handle, DWORD code, UINT64 arg1, + UINT64 arg2, PVOID buf, UINT len, UINT *iolen, LPOVERLAPPED overlapped) { WINDIVERT_IOCTL ioctl; BOOL result; DWORD iolen0; - ioctl.version = WINDIVERT_IOCTL_VERSION; - ioctl.magic = WINDIVERT_IOCTL_MAGIC; - ioctl.arg8 = arg8; - ioctl.arg = arg; + ioctl.arg1 = arg1; + ioctl.arg2 = arg2; result = DeviceIoControl(handle, code, &ioctl, sizeof(ioctl), buf, (DWORD)len, &iolen0, overlapped); if (result && iolen != NULL) @@ -454,8 +452,8 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, // Set the layer: if (layer != WINDIVERT_LAYER_DEFAULT) { - if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_SET_LAYER, 0, - (UINT64)layer, NULL, 0, NULL)) + if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_SET_LAYER, + (UINT64)layer, 0, NULL, 0, NULL)) { CloseHandle(handle); return INVALID_HANDLE_VALUE; @@ -465,7 +463,7 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, // Set the flags: if (flags != 0) { - if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_SET_FLAGS, 0, flags, + if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_SET_FLAGS, flags, 0, NULL, 0, NULL)) { CloseHandle(handle); @@ -478,8 +476,8 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, { // Make positive: priority64 = (UINT64)((INT64)priority + WINDIVERT_PRIORITY_MAX); - if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_SET_PRIORITY, 0, - priority64, NULL, 0, NULL)) + if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_SET_PRIORITY, + priority64, 0, NULL, 0, NULL)) { CloseHandle(handle); return INVALID_HANDLE_VALUE; @@ -487,8 +485,8 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, } // Start the filter: - if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_START_FILTER, 0, - filter_flags, object, obj_len * sizeof(WINDIVERT_FILTER), NULL)) + if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_START_FILTER, + filter_flags, 0, object, obj_len * sizeof(WINDIVERT_FILTER), NULL)) { CloseHandle(handle); return INVALID_HANDLE_VALUE; @@ -504,15 +502,15 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, extern BOOL WinDivertRecv(HANDLE handle, PVOID pPacket, UINT packetLen, PWINDIVERT_ADDRESS addr, UINT *readlen) { - return WinDivertIoControl(handle, IOCTL_WINDIVERT_RECV, 0, (UINT64)addr, - pPacket, packetLen, readlen); + return WinDivertIoControl(handle, IOCTL_WINDIVERT_RECV, (UINT64)addr, + (UINT64)NULL, pPacket, packetLen, readlen); } /* * Receive a WinDivert packet. */ extern BOOL WinDivertRecvEx(HANDLE handle, PVOID pPacket, UINT packetLen, - UINT64 flags, PWINDIVERT_ADDRESS addr, UINT *readlen, + UINT *readLen, UINT64 flags, PWINDIVERT_ADDRESS addr, UINT *pAddrLen, LPOVERLAPPED overlapped) { if (flags != 0) @@ -522,13 +520,14 @@ extern BOOL WinDivertRecvEx(HANDLE handle, PVOID pPacket, UINT packetLen, } if (overlapped == NULL) { - return WinDivertIoControl(handle, IOCTL_WINDIVERT_RECV, 0, - (UINT64)addr, pPacket, packetLen, readlen); + return WinDivertIoControl(handle, IOCTL_WINDIVERT_RECV, + (UINT64)addr, (UINT64)pAddrLen, pPacket, packetLen, readLen); } else { - return WinDivertIoControlEx(handle, IOCTL_WINDIVERT_RECV, 0, - (UINT64)addr, pPacket, packetLen, readlen, overlapped); + return WinDivertIoControlEx(handle, IOCTL_WINDIVERT_RECV, + (UINT64)addr, (UINT64)pAddrLen, pPacket, packetLen, readLen, + overlapped); } } @@ -538,36 +537,32 @@ extern BOOL WinDivertRecvEx(HANDLE handle, PVOID pPacket, UINT packetLen, extern BOOL WinDivertSend(HANDLE handle, PVOID pPacket, UINT packetLen, PWINDIVERT_ADDRESS addr, UINT *writelen) { - if (addr == NULL) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - return WinDivertIoControl(handle, IOCTL_WINDIVERT_SEND, 0, (UINT64)addr, - pPacket, packetLen, writelen); + return WinDivertIoControl(handle, IOCTL_WINDIVERT_SEND, (UINT64)addr, + sizeof(WINDIVERT_ADDRESS), pPacket, packetLen, writelen); } /* * Send a WinDivert packet. */ extern BOOL WinDivertSendEx(HANDLE handle, PVOID pPacket, UINT packetLen, - UINT64 flags, PWINDIVERT_ADDRESS addr, UINT *writelen, + UINT *writeLen, UINT64 flags, PWINDIVERT_ADDRESS addr, UINT addrLen, LPOVERLAPPED overlapped) { - if (flags != 0 || addr == NULL) + if (flags != 0) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (overlapped == NULL) { - return WinDivertIoControl(handle, IOCTL_WINDIVERT_SEND, 0, - (UINT64)addr, pPacket, packetLen, writelen); + return WinDivertIoControl(handle, IOCTL_WINDIVERT_SEND, + (UINT64)addr, (UINT64)addrLen, pPacket, packetLen, writeLen); } else { - return WinDivertIoControlEx(handle, IOCTL_WINDIVERT_SEND, 0, - (UINT64)addr, pPacket, packetLen, writelen, overlapped); + return WinDivertIoControlEx(handle, IOCTL_WINDIVERT_SEND, + (UINT64)addr, (UINT64)addrLen, pPacket, packetLen, writeLen, + overlapped); } } diff --git a/examples/passthru/passthru.c b/examples/passthru/passthru.c index 3ffd053..6491635 100644 --- a/examples/passthru/passthru.c +++ b/examples/passthru/passthru.c @@ -47,7 +47,10 @@ #include "windivert.h" -#define MAXBUF 0xFFFF +#define MAXBUF 0xFFFF +#define MAXBATCH 0xFF + +static int batch = 1; static DWORD passthru(LPVOID arg); @@ -59,9 +62,10 @@ int __cdecl main(int argc, char **argv) int num_threads, priority = 0, i; HANDLE handle, thread; - if (argc != 3 && argc != 4) + if (argc < 3 || argc > 5) { - fprintf(stderr, "usage: %s filter num-threads [priority]\n", argv[0]); + fprintf(stderr, "usage: %s filter num-threads [batch] [priority]\n", + argv[0]); exit(EXIT_FAILURE); } num_threads = atoi(argv[2]); @@ -70,9 +74,18 @@ int __cdecl main(int argc, char **argv) fprintf(stderr, "error: invalid number of threads\n"); exit(EXIT_FAILURE); } - if (argc == 4) + if (argc >= 4) { - priority = atoi(argv[3]); + batch = atoi(argv[3]); + } + if (batch <= 0 || batch > MAXBATCH) + { + fprintf(stderr, "error: invalid batch size\n"); + exit(EXIT_FAILURE); + } + if (argc >= 5) + { + priority = atoi(argv[4]); } // Divert traffic matching the filter: @@ -112,16 +125,18 @@ int __cdecl main(int argc, char **argv) // Passthru thread. static DWORD passthru(LPVOID arg) { - unsigned char packet[MAXBUF]; - UINT packet_len; - WINDIVERT_ADDRESS addr; + UINT8 packet[MAXBUF]; + UINT packet_len, addr_len; + WINDIVERT_ADDRESS addr[MAXBATCH]; HANDLE handle = (HANDLE)arg; // Main loop: while (TRUE) { // Read a matching packet. - if (!WinDivertRecv(handle, packet, sizeof(packet), &addr, &packet_len)) + addr_len = batch * sizeof(WINDIVERT_ADDRESS); + if (!WinDivertRecvEx(handle, packet, sizeof(packet), &packet_len, 0, + addr, &addr_len, NULL)) { fprintf(stderr, "warning: failed to read packet (%d)\n", GetLastError()); @@ -129,7 +144,8 @@ static DWORD passthru(LPVOID arg) } // Re-inject the matching packet. - if (!WinDivertSend(handle, packet, packet_len, &addr, NULL)) + if (!WinDivertSendEx(handle, packet, packet_len, NULL, 0, addr, + addr_len, NULL)) { fprintf(stderr, "warning: failed to reinject packet (%d)\n", GetLastError()); diff --git a/examples/streamdump/streamdump.c b/examples/streamdump/streamdump.c index 48315fa..b1010c3 100644 --- a/examples/streamdump/streamdump.c +++ b/examples/streamdump/streamdump.c @@ -227,8 +227,8 @@ int __cdecl main(int argc, char **argv) memset(&overlapped, 0, sizeof(overlapped)); ResetEvent(event); overlapped.hEvent = event; - if (!WinDivertRecvEx(handle, packet, sizeof(packet), 0, &addr, - &packet_len, &overlapped)) + if (!WinDivertRecvEx(handle, packet, sizeof(packet), &packet_len, 0, + &addr, NULL, &overlapped)) { if (GetLastError() != ERROR_IO_PENDING) { @@ -299,8 +299,8 @@ read_failed: error("failed to allocate memory"); } memset(poverlapped, 0, sizeof(OVERLAPPED)); - if (WinDivertSendEx(handle, packet, packet_len, 0, &addr, NULL, - poverlapped)) + if (WinDivertSendEx(handle, packet, packet_len, NULL, 0, &addr, + sizeof(WINDIVERT_ADDRESS), poverlapped)) { continue; } diff --git a/examples/windivertctl/windivertctl.c b/examples/windivertctl/windivertctl.c index b2abceb..08705cb 100644 --- a/examples/windivertctl/windivertctl.c +++ b/examples/windivertctl/windivertctl.c @@ -360,14 +360,9 @@ usage: printf("%sSEND_ONLY", (or? "|": "")); or = TRUE; } - if ((addr.Reflect.Flags & WINDIVERT_FLAG_DEBUG) != 0) + if ((addr.Reflect.Flags & WINDIVERT_FLAG_RECV_PARTIAL) != 0) { - printf("%sDEBUG", (or? "|": "")); - or = TRUE; - } - if ((addr.Reflect.Flags & WINDIVERT_FLAG_PARTIAL) != 0) - { - printf("%sPARTIAL", (or? "|": "")); + printf("%sRECV_PARTIAL", (or? "|": "")); or = TRUE; } if ((addr.Reflect.Flags & WINDIVERT_FLAG_NO_INSTALL) != 0) diff --git a/include/windivert.h b/include/windivert.h index b3baab0..354b4e0 100644 --- a/include/windivert.h +++ b/include/windivert.h @@ -184,15 +184,14 @@ typedef enum /* * WinDivert flags. */ -#define WINDIVERT_FLAG_SNIFF 0x01 -#define WINDIVERT_FLAG_DROP 0x02 -#define WINDIVERT_FLAG_RECV_ONLY 0x04 +#define WINDIVERT_FLAG_SNIFF 0x0001 +#define WINDIVERT_FLAG_DROP 0x0002 +#define WINDIVERT_FLAG_RECV_ONLY 0x0004 #define WINDIVERT_FLAG_READ_ONLY WINDIVERT_FLAG_RECV_ONLY -#define WINDIVERT_FLAG_SEND_ONLY 0x08 +#define WINDIVERT_FLAG_SEND_ONLY 0x0008 #define WINDIVERT_FLAG_WRITE_ONLY WINDIVERT_FLAG_SEND_ONLY -#define WINDIVERT_FLAG_DEBUG 0x10 -#define WINDIVERT_FLAG_PARTIAL 0x20 -#define WINDIVERT_FLAG_NO_INSTALL 0x40 +#define WINDIVERT_FLAG_RECV_PARTIAL 0x0010 +#define WINDIVERT_FLAG_NO_INSTALL 0x0020 /* * WinDivert parameters. @@ -201,7 +200,7 @@ typedef enum { WINDIVERT_PARAM_QUEUE_LEN = 0, /* Packet queue length. */ WINDIVERT_PARAM_QUEUE_TIME = 1, /* Packet queue time. */ - WINDIVERT_PARAM_QUEUE_SIZE = 2 /* Packet queue size. */ + WINDIVERT_PARAM_QUEUE_SIZE = 2, /* Packet queue size. */ } WINDIVERT_PARAM, *PWINDIVERT_PARAM; #define WINDIVERT_PARAM_MAX WINDIVERT_PARAM_QUEUE_SIZE @@ -224,7 +223,7 @@ extern WINDIVERTEXPORT BOOL WinDivertRecv( __out PVOID pPacket, __in UINT packetLen, __out_opt PWINDIVERT_ADDRESS pAddr, - __out_opt UINT *readLen); + __out_opt UINT *pReadLen); /* * Receive (read) a packet from a WinDivert handle. @@ -233,9 +232,10 @@ extern WINDIVERTEXPORT BOOL WinDivertRecvEx( __in HANDLE handle, __out PVOID pPacket, __in UINT packetLen, + __out_opt UINT *pReadLen, __in UINT64 flags, - __out_opt PWINDIVERT_ADDRESS pAddr, - __out_opt UINT *readLen, + __out PWINDIVERT_ADDRESS pAddr, + __inout_opt UINT *pAddrLen, __inout_opt LPOVERLAPPED lpOverlapped); /* @@ -246,7 +246,7 @@ extern WINDIVERTEXPORT BOOL WinDivertSend( __in PVOID pPacket, __in UINT packetLen, __in PWINDIVERT_ADDRESS pAddr, - __out_opt UINT *writeLen); + __out_opt UINT *pWriteLen); /* * Send (write/inject) a packet to a WinDivert handle. @@ -255,9 +255,10 @@ extern WINDIVERTEXPORT BOOL WinDivertSendEx( __in HANDLE handle, __in PVOID pPacket, __in UINT packetLen, + __out_opt UINT *pWriteLen, __in UINT64 flags, __in PWINDIVERT_ADDRESS pAddr, - __out_opt UINT *writeLen, + __in UINT addrLen, __inout_opt LPOVERLAPPED lpOverlapped); /* diff --git a/include/windivert_device.h b/include/windivert_device.h index 1444da8..65e194f 100644 --- a/include/windivert_device.h +++ b/include/windivert_device.h @@ -59,9 +59,6 @@ #define WINDIVERT_DEVICE_NAME \ L"WinDivert" WINDIVERT_VERSION_LSTR -#define WINDIVERT_IOCTL_VERSION 7 -#define WINDIVERT_IOCTL_MAGIC 0xC7C9 - #define WINDIVERT_FILTER_FIELD_ZERO 0 #define WINDIVERT_FILTER_FIELD_INBOUND 1 #define WINDIVERT_FILTER_FIELD_OUTBOUND 2 @@ -162,8 +159,8 @@ */ #define WINDIVERT_FLAGS_ALL \ (WINDIVERT_FLAG_SNIFF | WINDIVERT_FLAG_DROP | WINDIVERT_FLAG_RECV_ONLY |\ - WINDIVERT_FLAG_SEND_ONLY | WINDIVERT_FLAG_DEBUG | \ - WINDIVERT_FLAG_PARTIAL | WINDIVERT_FLAG_NO_INSTALL) + WINDIVERT_FLAG_SEND_ONLY | WINDIVERT_FLAG_RECV_PARTIAL | \ + WINDIVERT_FLAG_NO_INSTALL) #define WINDIVERT_FLAGS_EXCLUDE(flags, flag1, flag2) \ (((flags) & ((flag1) | (flag2))) != ((flag1) | (flag2))) #define WINDIVERT_FLAGS_VALID(flags) \ @@ -172,7 +169,7 @@ WINDIVERT_FLAG_DROP) && \ WINDIVERT_FLAGS_EXCLUDE(flags, WINDIVERT_FLAG_RECV_ONLY, \ WINDIVERT_FLAG_SEND_ONLY) && \ - WINDIVERT_FLAGS_EXCLUDE(flags, WINDIVERT_FLAG_PARTIAL, \ + WINDIVERT_FLAGS_EXCLUDE(flags, WINDIVERT_FLAG_RECV_PARTIAL, \ WINDIVERT_FLAG_SEND_ONLY)) /* @@ -207,16 +204,19 @@ #define WINDIVERT_PARAM_QUEUE_SIZE_MAX 33554432 // 32MB #define WINDIVERT_PARAM_QUEUE_SIZE_DEFAULT 4194304 // 4MB +/* + * WinDivert batch limits. + */ +#define WINDIVERT_BATCH_MAX 0xFF + /* * WinDivert message definitions. */ #pragma pack(push, 1) typedef struct { - UINT16 magic; // WINDIVERT_IOCTL_MAGIC - UINT8 version; // WINDIVERT_IOCTL_VERSION - UINT8 arg8; // 8-bit argument - UINT64 arg; // 64-bit argument + UINT64 arg1; // argument #1 + UINT64 arg2; // argument #2 } WINDIVERT_IOCTL, *PWINDIVERT_IOCTL; /* diff --git a/sys/windivert.c b/sys/windivert.c index 3f52a18..0b092c0 100644 --- a/sys/windivert.c +++ b/sys/windivert.c @@ -159,7 +159,7 @@ struct context_s WDFWORKITEM workers[WINDIVERT_CONTEXT_MAXWORKERS]; // Read workers. UINT8 worker_curr; // Current read worker. - UINT8 layer; // Context's layer. + WINDIVERT_LAYER layer; // Context's layer. UINT64 flags; // Context's flags. UINT32 priority; // Context (internal) priority. INT16 priority16; // Context (user) priority. @@ -215,6 +215,8 @@ typedef struct layer_s *layer_t; struct req_context_s { PWINDIVERT_ADDRESS addr; // Pointer to address structure. + UINT *addr_len_ptr; // Pointer to address length. + UINT addr_len; // Address length (in bytes). }; typedef struct req_context_s req_context_s; typedef struct req_context_s *req_context_t; @@ -347,7 +349,7 @@ extern VOID windivert_cleanup(IN WDFFILEOBJECT object); extern VOID windivert_close(IN WDFFILEOBJECT object); extern VOID windivert_destroy(IN WDFOBJECT object); extern NTSTATUS windivert_write(context_t context, WDFREQUEST request, - PWINDIVERT_ADDRESS addr); + req_context_t req_context); extern void NTAPI windivert_inject_complete(VOID *context, NET_BUFFER_LIST *packets, BOOLEAN dispatch_level); extern void NTAPI windivert_reinject_complete(VOID *context, @@ -1919,20 +1921,33 @@ static NTSTATUS windivert_read(context_t context, WDFREQUEST request) /* * WinDivert service a single read request. */ -static void windivert_read_service_request(packet_t packet, BOOL partial, - WDFREQUEST request) +static void windivert_read_service_request(context_t context, packet_t packet, + BOOL partial, LONGLONG timestamp, WDFREQUEST request) { + KLOCK_QUEUE_HANDLE lock_handle; + PLIST_ENTRY entry; PMDL dst_mdl; UINT8 *layer_data, *src, *dst; - ULONG dst_len, src_len; + ULONG dst_len, src_len, read_len; + BOOL timeout; + packet_t new_packet; req_context_t req_context; PWINDIVERT_ADDRESS addr; + UINT i, addr_len, addr_len_max; + UINT *addr_len_ptr; NTSTATUS status; + if (request == NULL) + { + // This occurs if the packet timed out. + windivert_free_packet(packet); + return; + } + DEBUG("SERVICE: servicing read request (request=%p, packet=%p)", request, packet); - - layer_data = (PVOID)packet->data; + + // Get the packet and address buffers: switch (packet->layer) { case WINDIVERT_LAYER_NETWORK: @@ -1953,29 +1968,14 @@ static void windivert_read_service_request(packet_t packet, BOOL partial, DEBUG_ERROR("failed to get address of output MDL", status); goto windivert_read_service_request_exit; } - - if (packet->layer != WINDIVERT_LAYER_REFLECT) - { - src = WINDIVERT_PACKET_DATA_PTR(WINDIVERT_DATA_NETWORK, packet); - } - else - { - src = WINDIVERT_PACKET_DATA_PTR(WINDIVERT_DATA_REFLECT, packet); - } - src_len = packet->packet_len; dst_len = MmGetMdlByteCount(dst_mdl); - if (!partial && src_len > dst_len) - { - status = STATUS_BUFFER_TOO_SMALL; - } - dst_len = (src_len < dst_len? src_len: dst_len); - RtlCopyMemory(dst, src, dst_len); break; case WINDIVERT_LAYER_FLOW: case WINDIVERT_LAYER_SOCKET: status = STATUS_SUCCESS; + dst = NULL; dst_len = 0; break; @@ -1985,54 +1985,144 @@ static void windivert_read_service_request(packet_t packet, BOOL partial, goto windivert_read_service_request_exit; } - // Write the address information. - req_context = windivert_req_context_get(request); - addr = req_context->addr; - if (addr != NULL) + req_context = windivert_req_context_get(request); + addr = req_context->addr; + addr_len = 0; + addr_len_max = (UINT)req_context->addr_len; + addr_len_ptr = req_context->addr_len_ptr; + read_len = 0; + i = 0; + while (TRUE) { - addr->Timestamp = (INT64)packet->timestamp; - addr->Layer = packet->layer; - addr->Event = packet->event; - addr->Outbound = packet->outbound; - addr->Loopback = packet->loopback; - addr->Impostor = packet->impostor; - addr->IPv6 = packet->ipv6; - addr->PseudoIPChecksum = packet->pseudo_ip_checksum; - addr->PseudoTCPChecksum = packet->pseudo_tcp_checksum; - addr->PseudoUDPChecksum = packet->pseudo_udp_checksum; - addr->Final = packet->final; - addr->Reserved = 0; + // Copy the packet data: switch (packet->layer) { case WINDIVERT_LAYER_NETWORK: case WINDIVERT_LAYER_NETWORK_FORWARD: - RtlCopyMemory(&addr->Network, layer_data, + case WINDIVERT_LAYER_REFLECT: + + if (packet->layer != WINDIVERT_LAYER_REFLECT) + { + src = WINDIVERT_PACKET_DATA_PTR(WINDIVERT_DATA_NETWORK, + packet); + } + else + { + src = WINDIVERT_PACKET_DATA_PTR(WINDIVERT_DATA_REFLECT, + packet); + } + src_len = packet->packet_len; + if (!partial && src_len > dst_len) + { + status = STATUS_BUFFER_TOO_SMALL; + } + src_len = (src_len < dst_len? src_len: dst_len); + RtlCopyMemory(dst, src, src_len); + dst += src_len; + dst_len -= src_len; + read_len += src_len; + break; + + default: + break; + } + + // Copy the address data: + addr[i].Timestamp = (INT64)packet->timestamp; + addr[i].Layer = packet->layer; + addr[i].Event = packet->event; + addr[i].Outbound = packet->outbound; + addr[i].Loopback = packet->loopback; + addr[i].Impostor = packet->impostor; + addr[i].IPv6 = packet->ipv6; + addr[i].PseudoIPChecksum = packet->pseudo_ip_checksum; + addr[i].PseudoTCPChecksum = packet->pseudo_tcp_checksum; + addr[i].PseudoUDPChecksum = packet->pseudo_udp_checksum; + addr[i].Final = packet->final; + addr[i].Reserved = 0; + layer_data = (PVOID)packet->data; + switch (packet->layer) + { + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + RtlCopyMemory(&addr[i].Network, layer_data, sizeof(WINDIVERT_DATA_NETWORK)); break; case WINDIVERT_LAYER_FLOW: - RtlCopyMemory(&addr->Flow, layer_data, + RtlCopyMemory(&addr[i].Flow, layer_data, sizeof(WINDIVERT_DATA_FLOW)); break; case WINDIVERT_LAYER_SOCKET: - RtlCopyMemory(&addr->Socket, layer_data, + RtlCopyMemory(&addr[i].Socket, layer_data, sizeof(WINDIVERT_DATA_SOCKET)); break; case WINDIVERT_LAYER_REFLECT: - RtlCopyMemory(&addr->Reflect, layer_data, + RtlCopyMemory(&addr[i].Reflect, layer_data, sizeof(WINDIVERT_DATA_REFLECT)); break; default: break; } + + i++; + addr_len += sizeof(WINDIVERT_ADDRESS); + if (addr_len >= addr_len_max || i >= WINDIVERT_BATCH_MAX) + { + // addr[] is full: + break; + } + if (dst_len < sizeof(WINDIVERT_IPHDR) + sizeof(WINDIVERT_TCPHDR)) + { + // Remaining space too small: + break; + } + + // Attempt to fill the buffer with more packets: + new_packet = NULL; + KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); + if (context->state == WINDIVERT_CONTEXT_STATE_OPEN && + !IsListEmpty(&context->packet_queue)) + { + entry = RemoveHeadList(&context->packet_queue); + new_packet = CONTAINING_RECORD(entry, struct packet_s, entry); + timeout = WINDIVERT_TIMEOUT(context, new_packet->timestamp, + timestamp); + if (new_packet->packet_len > dst_len || timeout) + { + // Note: timeouts to be handled elsewhere. + InsertHeadList(&context->packet_queue, entry); + new_packet = NULL; + } + else + { + context->packet_queue_length--; + context->packet_queue_size -= new_packet->packet_len; + } + } + KeReleaseInStackQueuedSpinLock(&lock_handle); + if (new_packet == NULL) + { + // No suitable packet: + break; + } + + windivert_free_packet(packet); + packet = new_packet; + } + + if (addr_len_ptr != NULL) + { + *addr_len_ptr = addr_len; } windivert_read_service_request_exit: - WdfRequestCompleteWithInformation(request, status, dst_len); + windivert_free_packet(packet); + WdfRequestCompleteWithInformation(request, status, read_len); } /* @@ -2043,12 +2133,8 @@ static void windivert_read_service(context_t context) KLOCK_QUEUE_HANDLE lock_handle; WDFREQUEST request; PLIST_ENTRY entry; - PMDL dst_mdl; - PVOID dst, src; - ULONG dst_len, src_len; LONGLONG timestamp; - BOOL partial; - BOOL timeout; + BOOL partial, timeout; NTSTATUS status; packet_t packet; req_context_t req_context; @@ -2056,7 +2142,7 @@ static void windivert_read_service(context_t context) timestamp = KeQueryPerformanceCounter(NULL).QuadPart; KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); - partial = ((context->flags & WINDIVERT_FLAG_PARTIAL) != 0); + partial = ((context->flags & WINDIVERT_FLAG_RECV_PARTIAL) != 0); while (context->state == WINDIVERT_CONTEXT_STATE_OPEN && !IsListEmpty(&context->packet_queue)) { @@ -2078,12 +2164,9 @@ static void windivert_read_service(context_t context) context->packet_queue_size -= packet->packet_len; KeReleaseInStackQueuedSpinLock(&lock_handle); - if (!timeout) - { - windivert_read_service_request(packet, partial, request); - } + windivert_read_service_request(context, packet, partial, timestamp, + request); - windivert_free_packet(packet); timestamp = KeQueryPerformanceCounter(NULL).QuadPart; KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); } @@ -2094,12 +2177,12 @@ static void windivert_read_service(context_t context) * WinDivert write routine. */ static NTSTATUS windivert_write(context_t context, WDFREQUEST request, - PWINDIVERT_ADDRESS addr) + req_context_t req_context) { KLOCK_QUEUE_HANDLE lock_handle; PMDL mdl = NULL, mdl_copy = NULL; PVOID data, data_copy = NULL; - UINT data_len; + UINT data_len, packet_len, inject_len; PWINDIVERT_IPHDR ip_header; PWINDIVERT_IPV6HDR ipv6_header; BOOL ipv4; @@ -2108,7 +2191,9 @@ static NTSTATUS windivert_write(context_t context, WDFREQUEST request, UINT64 flags, checksums; HANDLE handle, compl_handle; PNET_BUFFER_LIST buffers = NULL; - NTSTATUS status = STATUS_SUCCESS; + PWINDIVERT_ADDRESS addr; + UINT i, addr_len, addr_len_max; + NTSTATUS status = STATUS_SUCCESS, status_soft_error = STATUS_SUCCESS; DEBUG("WRITE: writing/injecting a packet (context=%p, request=%p)", context, request); @@ -2118,7 +2203,7 @@ static NTSTATUS windivert_write(context_t context, WDFREQUEST request, { KeReleaseInStackQueuedSpinLock(&lock_handle); status = STATUS_INVALID_DEVICE_STATE; - goto windivert_write_exit; + goto windivert_write_hard_error; } layer = context->layer; priority = context->priority; @@ -2129,7 +2214,7 @@ static NTSTATUS windivert_write(context_t context, WDFREQUEST request, { status = STATUS_INVALID_PARAMETER; DEBUG_ERROR("failed to inject; recv-only flag is set", status); - goto windivert_write_exit; + goto windivert_write_hard_error; } switch (layer) @@ -2139,7 +2224,7 @@ static NTSTATUS windivert_write(context_t context, WDFREQUEST request, case WINDIVERT_LAYER_REFLECT: status = STATUS_INVALID_PARAMETER; DEBUG_ERROR("failed to inject at layer", status); - goto windivert_write_exit; + goto windivert_write_hard_error; default: break; } @@ -2148,7 +2233,7 @@ static NTSTATUS windivert_write(context_t context, WDFREQUEST request, if (!NT_SUCCESS(status)) { DEBUG_ERROR("failed to retrieve input MDL", status); - goto windivert_write_exit; + goto windivert_write_hard_error; } data = MmGetSystemAddressForMdlSafe(mdl, @@ -2157,176 +2242,181 @@ static NTSTATUS windivert_write(context_t context, WDFREQUEST request, { status = STATUS_INSUFFICIENT_RESOURCES; DEBUG_ERROR("failed to get MDL address", status); - goto windivert_write_exit; + goto windivert_write_hard_error; } - data_len = MmGetMdlByteCount(mdl); - if (data_len > UINT16_MAX || data_len < sizeof(WINDIVERT_IPHDR)) - { -windivert_write_bad_packet: - status = STATUS_INVALID_PARAMETER; - DEBUG_ERROR("failed to inject a bad packet", status); - goto windivert_write_exit; - } + data_len = MmGetMdlByteCount(mdl); + inject_len = 0; + addr = req_context->addr; + addr_len_max = (ULONG)req_context->addr_len; + addr_len = 0; - // Copy packet data: - data_copy = windivert_malloc(data_len, FALSE); - if (data_copy == NULL) + for (i = 0; addr_len < addr_len_max && i < WINDIVERT_BATCH_MAX; i++, + addr_len += sizeof(WINDIVERT_ADDRESS)) { - status = STATUS_INSUFFICIENT_RESOURCES; - DEBUG_ERROR("failed to allocate memory for injected packet data", - status); - goto windivert_write_exit; - } - RtlCopyMemory(data_copy, data, sizeof(WINDIVERT_IPHDR)); - ip_header = (PWINDIVERT_IPHDR)data_copy; - switch (ip_header->Version) - { - case 4: - if (data_len != RtlUshortByteSwap(ip_header->Length)) - { - goto windivert_write_bad_packet; - } - ipv4 = TRUE; - break; - case 6: - if (data_len < sizeof(WINDIVERT_IPV6HDR)) - { - goto windivert_write_bad_packet; - } - ipv6_header = (PWINDIVERT_IPV6HDR)data_copy; - if (data_len != RtlUshortByteSwap(ipv6_header->Length) + - sizeof(WINDIVERT_IPV6HDR)) - { - goto windivert_write_bad_packet; - } - ipv4 = FALSE; - break; - default: - goto windivert_write_bad_packet; - } - if (data_len > sizeof(WINDIVERT_IPHDR)) - { - RtlCopyMemory((char *)data_copy + sizeof(WINDIVERT_IPHDR), - (char *)data + sizeof(WINDIVERT_IPHDR), - data_len - sizeof(WINDIVERT_IPHDR)); - } - - // Fix checksums: - if (addr->PseudoIPChecksum != 0 || addr->PseudoTCPChecksum != 0 || - addr->PseudoUDPChecksum != 0) - { - checksums = - (addr->PseudoIPChecksum? 0: WINDIVERT_HELPER_NO_IP_CHECKSUM) | - (addr->PseudoTCPChecksum? 0: WINDIVERT_HELPER_NO_TCP_CHECKSUM) | - (addr->PseudoUDPChecksum? 0: WINDIVERT_HELPER_NO_UDP_CHECKSUM); - WinDivertHelperCalcChecksums(data_copy, data_len, NULL, checksums); - } - - // Decrement TTL for impostor packets: - if (addr->Impostor && !windivert_decrement_ttl(data_copy, ipv4)) - { - status = STATUS_HOPLIMIT_EXCEEDED; - goto windivert_write_exit; - } - - // Allocate packet: - mdl_copy = IoAllocateMdl(data_copy, data_len, FALSE, FALSE, NULL); - if (mdl_copy == NULL) - { - status = STATUS_INSUFFICIENT_RESOURCES; - DEBUG_ERROR("failed to allocate MDL for injected packet", status); - goto windivert_write_exit; - } - MmBuildMdlForNonPagedPool(mdl_copy); - status = FwpsAllocateNetBufferAndNetBufferList0(nbl_pool_handle, 0, 0, - mdl_copy, 0, data_len, &buffers); - if (!NT_SUCCESS(status)) - { - DEBUG_ERROR("failed to create NET_BUFFER_LIST for injected packet", - status); - goto windivert_write_exit; - } - - // Inject packet: - handle = (ipv4? inject_handle: injectv6_handle); - compl_handle = ((flags & WINDIVERT_FLAG_DEBUG) != 0? (HANDLE)request: NULL); - if (layer == WINDIVERT_LAYER_NETWORK_FORWARD) - { - status = FwpsInjectForwardAsync0(handle, (HANDLE)priority, 0, - (ipv4? AF_INET: AF_INET6), UNSPECIFIED_COMPARTMENT_ID, - addr->Network.IfIdx, buffers, windivert_inject_complete, - compl_handle); - } - else if (addr->Outbound != 0) - { - status = FwpsInjectNetworkSendAsync0(handle, (HANDLE)priority, 0, - UNSPECIFIED_COMPARTMENT_ID, buffers, windivert_inject_complete, - compl_handle); - } - else - { - status = FwpsInjectNetworkReceiveAsync0(handle, (HANDLE)priority, 0, - UNSPECIFIED_COMPARTMENT_ID, addr->Network.IfIdx, - addr->Network.SubIfIdx, buffers, windivert_inject_complete, - compl_handle); - } - -windivert_write_exit: - - if (NT_SUCCESS(status)) - { - if ((flags & WINDIVERT_FLAG_DEBUG) == 0) + buffers = NULL; + mdl_copy = NULL; + data_copy = NULL; + + // Get the packet length: + if (data_len < sizeof(WINDIVERT_IPHDR)) { - WdfRequestCompleteWithInformation(request, status, data_len); +windivert_write_too_small_packet: + status = STATUS_BUFFER_TOO_SMALL; + DEBUG_ERROR("failed to inject partial packet", status); + goto windivert_write_hard_error; } - } - else - { - // Request completed in windivert_ioctl() - if (buffers != NULL) + ip_header = (PWINDIVERT_IPHDR)data; + switch (ip_header->Version) { + case 4: + packet_len = RtlUshortByteSwap(ip_header->Length); + ipv4 = TRUE; + break; + case 6: + if (data_len < sizeof(WINDIVERT_IPV6HDR)) + { + goto windivert_write_too_small_packet; + } + ipv6_header = (PWINDIVERT_IPV6HDR)data; + packet_len = RtlUshortByteSwap(ipv6_header->Length) + + sizeof(WINDIVERT_IPV6HDR); + ipv4 = FALSE; + break; + default: + status = STATUS_INVALID_PARAMETER; + DEBUG_ERROR("failed to inject non-IP packet", status); + goto windivert_write_hard_error; + } + if (data_len < packet_len) + { + goto windivert_write_too_small_packet; + } + + // Copy packet data: + data_copy = windivert_malloc(packet_len, FALSE); + if (data_copy == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + DEBUG_ERROR("failed to allocate memory for injected packet data", + status); + goto windivert_write_hard_error; + } + RtlCopyMemory(data_copy, data, packet_len); + + // Fix checksums: + if (addr[i].PseudoIPChecksum != 0 || addr[i].PseudoTCPChecksum != 0 || + addr[i].PseudoUDPChecksum != 0) + { + checksums = + (addr[i].PseudoIPChecksum? 0: + WINDIVERT_HELPER_NO_IP_CHECKSUM) | + (addr[i].PseudoTCPChecksum? 0: + WINDIVERT_HELPER_NO_TCP_CHECKSUM) | + (addr[i].PseudoUDPChecksum? 0: + WINDIVERT_HELPER_NO_UDP_CHECKSUM); + WinDivertHelperCalcChecksums(data_copy, packet_len, NULL, + checksums); + } + + // Decrement TTL for impostor packets: + if (addr[i].Impostor && !windivert_decrement_ttl(data_copy, ipv4)) + { + status_soft_error = STATUS_HOPLIMIT_EXCEEDED; + windivert_free(data_copy); + goto windivert_write_loop; + } + + // Allocate packet: + mdl_copy = IoAllocateMdl(data_copy, packet_len, FALSE, FALSE, NULL); + if (mdl_copy == NULL) + { + status = STATUS_INSUFFICIENT_RESOURCES; + DEBUG_ERROR("failed to allocate MDL for injected packet", status); + goto windivert_write_hard_error; + } + MmBuildMdlForNonPagedPool(mdl_copy); + status = FwpsAllocateNetBufferAndNetBufferList0(nbl_pool_handle, 0, 0, + mdl_copy, 0, packet_len, &buffers); + if (!NT_SUCCESS(status)) + { + DEBUG_ERROR("failed to create NET_BUFFER_LIST for injected packet", + status); + goto windivert_write_hard_error; + } + + // Inject packet: + handle = (ipv4? inject_handle: injectv6_handle); + if (layer == WINDIVERT_LAYER_NETWORK_FORWARD) + { + status = FwpsInjectForwardAsync0(handle, (HANDLE)priority, 0, + (ipv4? AF_INET: AF_INET6), UNSPECIFIED_COMPARTMENT_ID, + addr[i].Network.IfIdx, buffers, windivert_inject_complete, + data_copy); + } + else if (addr[i].Outbound != 0) + { + status = FwpsInjectNetworkSendAsync0(handle, (HANDLE)priority, 0, + UNSPECIFIED_COMPARTMENT_ID, buffers, windivert_inject_complete, + data_copy); + } + else + { + status = FwpsInjectNetworkReceiveAsync0(handle, (HANDLE)priority, 0, + UNSPECIFIED_COMPARTMENT_ID, addr[i].Network.IfIdx, + addr[i].Network.SubIfIdx, buffers, windivert_inject_complete, + data_copy); + } + + if (!NT_SUCCESS(status)) + { + status_soft_error = status; FwpsFreeNetBufferList0(buffers); - } - if (mdl_copy != NULL) - { IoFreeMdl(mdl_copy); + windivert_free(data_copy); } - windivert_free(data_copy); + +windivert_write_loop: + + // Reset state: + inject_len += packet_len; + data = (PVOID)((UINT8 *)data + packet_len); + data_len -= packet_len; } + // Note: status_soft_error is for "soft" errors that do not prevent other + // batched packets from being injected. + WdfRequestCompleteWithInformation(request, status_soft_error, inject_len); + return STATUS_SUCCESS; + +windivert_write_hard_error: + + // Request to be completed in windivert_ioctl() + if (buffers != NULL) + { + FwpsFreeNetBufferList0(buffers); + } + if (mdl_copy != NULL) + { + IoFreeMdl(mdl_copy); + } + windivert_free(data_copy); + return status; } /* * WinDivert inject complete routine. */ -static void NTAPI windivert_inject_complete(VOID *context, +static void NTAPI windivert_inject_complete(VOID *data, NET_BUFFER_LIST *buffers, BOOLEAN dispatch_level) { PMDL mdl; - PVOID data; PNET_BUFFER buffer; - size_t length; - WDFREQUEST request; - NTSTATUS status; UNREFERENCED_PARAMETER(dispatch_level); buffer = NET_BUFFER_LIST_FIRST_NB(buffers); - request = (WDFREQUEST)context; - if (request != NULL) - { - status = NET_BUFFER_LIST_STATUS(buffers); - length = 0; - if (NT_SUCCESS(status)) - { - length = NET_BUFFER_DATA_LENGTH(buffer); - } - WdfRequestCompleteWithInformation(request, status, length); - } mdl = NET_BUFFER_FIRST_MDL(buffer); - data = MmGetSystemAddressForMdlSafe(mdl, - NormalPagePriority | no_exec_flag); windivert_free(data); IoFreeMdl(mdl); FwpsFreeNetBufferList0(buffers); @@ -2340,7 +2430,6 @@ static void NTAPI windivert_reinject_complete(VOID *context, { PMDL mdl; PNET_BUFFER buffer; - size_t length; packet_t packet; UNREFERENCED_PARAMETER(dispatch_level); @@ -2362,6 +2451,8 @@ VOID windivert_caller_context(IN WDFDEVICE device, IN WDFREQUEST request) WDF_REQUEST_PARAMETERS params; WDFMEMORY memobj; PWINDIVERT_ADDRESS addr = NULL; + UINT *addr_len_ptr = NULL; + UINT64 addr_len = 0; PWINDIVERT_IOCTL ioctl; WDF_OBJECT_ATTRIBUTES attributes; req_context_t req_context = NULL; @@ -2390,16 +2481,6 @@ VOID windivert_caller_context(IN WDFDEVICE device, IN WDFREQUEST request) goto windivert_caller_context_error; } - ioctl = (PWINDIVERT_IOCTL)inbuf; - if (ioctl->version != WINDIVERT_IOCTL_VERSION || - ioctl->magic != WINDIVERT_IOCTL_MAGIC) - { - status = STATUS_INVALID_PARAMETER; - DEBUG_ERROR("input buffer contained a bad ioctl message header", - status); - goto windivert_caller_context_error; - } - // Probe and lock user buffers here (if required). WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, req_context_s); status = WdfObjectAllocateContext(request, &attributes, &req_context); @@ -2411,34 +2492,71 @@ VOID windivert_caller_context(IN WDFDEVICE device, IN WDFREQUEST request) switch (params.Parameters.DeviceIoControl.IoControlCode) { case IOCTL_WINDIVERT_RECV: - if ((PVOID)ioctl->arg == NULL) + ioctl = (PWINDIVERT_IOCTL)inbuf; + addr = (PWINDIVERT_ADDRESS)ioctl->arg1; + addr_len_ptr = (UINT *)ioctl->arg2; + addr_len = sizeof(WINDIVERT_ADDRESS); + if (addr_len_ptr != NULL) + { + status = WdfRequestProbeAndLockUserBufferForWrite(request, + addr_len_ptr, sizeof(UINT), &memobj); + if (!NT_SUCCESS(status)) + { + status = STATUS_INVALID_PARAMETER; + DEBUG_ERROR("invalid address length pointer for RECV ioctl", + status); + goto windivert_caller_context_error; + } + addr_len_ptr = (UINT *)WdfMemoryGetBuffer(memobj, NULL); + addr_len = *addr_len_ptr; + if (addr_len < sizeof(WINDIVERT_ADDRESS) || + addr_len > WINDIVERT_BATCH_MAX * sizeof(WINDIVERT_ADDRESS)) + { + status = STATUS_INVALID_PARAMETER; + DEBUG_ERROR("out-of-range address length for RECV ioctl", + status); + goto windivert_caller_context_error; + } + } + if (addr == NULL) { status = STATUS_INVALID_PARAMETER; - DEBUG_ERROR("null arg pointer for RECV ioctl", status); + DEBUG_ERROR("null address for RECV ioctl", status); goto windivert_caller_context_error; } - status = WdfRequestProbeAndLockUserBufferForWrite(request, - (PVOID)ioctl->arg, sizeof(WINDIVERT_ADDRESS), &memobj); + status = WdfRequestProbeAndLockUserBufferForWrite(request, addr, + addr_len, &memobj); if (!NT_SUCCESS(status)) { - DEBUG_ERROR("invalid arg pointer for RECV ioctl", status); + DEBUG_ERROR("invalid address for RECV ioctl", status); goto windivert_caller_context_error; } addr = (PWINDIVERT_ADDRESS)WdfMemoryGetBuffer(memobj, NULL); break; case IOCTL_WINDIVERT_SEND: - if ((PVOID)ioctl->arg == NULL) + ioctl = (PWINDIVERT_IOCTL)inbuf; + addr = (PWINDIVERT_ADDRESS)ioctl->arg1; + addr_len = ioctl->arg2; + if (addr_len < sizeof(WINDIVERT_ADDRESS) || + addr_len > WINDIVERT_BATCH_MAX * sizeof(WINDIVERT_ADDRESS)) { status = STATUS_INVALID_PARAMETER; - DEBUG_ERROR("null arg pointer for SEND ioctl", status); + DEBUG_ERROR("out-of-range address length for RECV ioctl", + status); goto windivert_caller_context_error; } - status = WdfRequestProbeAndLockUserBufferForRead(request, - (PVOID)ioctl->arg, sizeof(WINDIVERT_ADDRESS), &memobj); + if (addr == NULL) + { + status = STATUS_INVALID_PARAMETER; + DEBUG_ERROR("null address for SEND ioctl", status); + goto windivert_caller_context_error; + } + status = WdfRequestProbeAndLockUserBufferForRead(request, addr, + addr_len, &memobj); if (!NT_SUCCESS(status)) { - DEBUG_ERROR("invalid arg pointer for SEND ioctl", status); + DEBUG_ERROR("invalid address for SEND ioctl", status); goto windivert_caller_context_error; } addr = (PWINDIVERT_ADDRESS)WdfMemoryGetBuffer(memobj, NULL); @@ -2459,7 +2577,9 @@ VOID windivert_caller_context(IN WDFDEVICE device, IN WDFREQUEST request) goto windivert_caller_context_error; } - req_context->addr = addr; + req_context->addr = addr; + req_context->addr_len = (UINT)addr_len; + req_context->addr_len_ptr = addr_len_ptr; windivert_caller_context_exit: @@ -2488,10 +2608,7 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, PWINDIVERT_FILTER filter; UINT8 layer; INT16 priority; - UINT32 priority32; - INT64 priority64; UINT64 flags; - PWINDIVERT_ADDRESS addr; req_context_t req_context; NTSTATUS status = STATUS_SUCCESS; context_t context = @@ -2539,8 +2656,7 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, case IOCTL_WINDIVERT_SEND: req_context = windivert_req_context_get(request); - addr = req_context->addr; - status = windivert_write(context, request, addr); + status = windivert_write(context, request, req_context); if (NT_SUCCESS(status)) { return; @@ -2552,11 +2668,13 @@ extern VOID windivert_ioctl(IN WDFQUEUE queue, IN WDFREQUEST request, BOOL inbound, outbound, ipv4, ipv6; PIRP irp; LONGLONG timestamp; + UINT64 filter_flags; UINT32 process_id; UINT8 filter_len; ioctl = (PWINDIVERT_IOCTL)inbuf; - if ((ioctl->arg & ~WINDIVERT_FILTER_FLAGS_ALL) != 0) + filter_flags = ioctl->arg1; + if ((filter_flags & ~WINDIVERT_FILTER_FLAGS_ALL) != 0) { status = STATUS_INVALID_PARAMETER; DEBUG_ERROR("failed to start filter; invalid flags", status); @@ -2630,15 +2748,18 @@ windivert_ioctl_bad_start_state: windivert_reflect_open_event(context); - flags = ioctl->arg; - status = windivert_install_callouts(context, layer, flags); + status = windivert_install_callouts(context, layer, filter_flags); break; } case IOCTL_WINDIVERT_SET_LAYER: + { + UINT64 layer; + ioctl = (PWINDIVERT_IOCTL)inbuf; - switch (ioctl->arg) + layer = ioctl->arg1; + switch (layer) { case WINDIVERT_LAYER_NETWORK: case WINDIVERT_LAYER_NETWORK_FORWARD: @@ -2651,7 +2772,6 @@ windivert_ioctl_bad_start_state: DEBUG_ERROR("failed to set layer; invalid value", status); goto windivert_ioctl_exit; } - layer = (UINT8)ioctl->arg; KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); if (context->state != WINDIVERT_CONTEXT_STATE_OPENING) { @@ -2659,13 +2779,18 @@ windivert_ioctl_bad_start_state: status = STATUS_INVALID_DEVICE_STATE; goto windivert_ioctl_exit; } - context->layer = layer; + context->layer = (WINDIVERT_LAYER)layer; KeReleaseInStackQueuedSpinLock(&lock_handle); break; + } case IOCTL_WINDIVERT_SET_PRIORITY: + { + UINT32 priority32; + INT64 priority64; + ioctl = (PWINDIVERT_IOCTL)inbuf; - priority64 = (INT64)ioctl->arg - WINDIVERT_PRIORITY_MAX; + priority64 = (INT64)ioctl->arg1 - WINDIVERT_PRIORITY_MAX; if (priority64 < WINDIVERT_PRIORITY_MIN || priority64 > WINDIVERT_PRIORITY_MAX) { @@ -2686,17 +2811,21 @@ windivert_ioctl_bad_start_state: context->priority = priority32; KeReleaseInStackQueuedSpinLock(&lock_handle); break; + } case IOCTL_WINDIVERT_SET_FLAGS: + { + UINT64 flags; + ioctl = (PWINDIVERT_IOCTL)inbuf; - if (!WINDIVERT_FLAGS_VALID(ioctl->arg)) + flags = ioctl->arg1; + if (!WINDIVERT_FLAGS_VALID(flags)) { status = STATUS_INVALID_PARAMETER; DEBUG_ERROR("failed to set flags; invalid flags value", status); goto windivert_ioctl_exit; } - flags = ioctl->arg; KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); if (context->state != WINDIVERT_CONTEXT_STATE_OPENING) { @@ -2707,10 +2836,15 @@ windivert_ioctl_bad_start_state: context->flags = flags; KeReleaseInStackQueuedSpinLock(&lock_handle); break; + } case IOCTL_WINDIVERT_SET_PARAM: + { + UINT64 param, value; + ioctl = (PWINDIVERT_IOCTL)inbuf; - value = ioctl->arg; + param = ioctl->arg1; + value = ioctl->arg2; KeAcquireInStackQueuedSpinLock(&context->lock, &lock_handle); if (context->state != WINDIVERT_CONTEXT_STATE_OPEN) { @@ -2718,7 +2852,7 @@ windivert_ioctl_bad_start_state: status = STATUS_INVALID_DEVICE_STATE; goto windivert_ioctl_exit; } - switch ((WINDIVERT_PARAM)ioctl->arg8) + switch (param) { case WINDIVERT_PARAM_QUEUE_LEN: if (value < WINDIVERT_PARAM_QUEUE_LEN_MIN || @@ -2770,9 +2904,14 @@ windivert_ioctl_bad_start_state: } KeReleaseInStackQueuedSpinLock(&lock_handle); break; + } case IOCTL_WINDIVERT_GET_PARAM: + { + UINT64 param; + ioctl = (PWINDIVERT_IOCTL)inbuf; + param = ioctl->arg1; if (outbuflen != sizeof(UINT64)) { status = STATUS_INVALID_PARAMETER; @@ -2788,7 +2927,7 @@ windivert_ioctl_bad_start_state: status = STATUS_INVALID_DEVICE_STATE; goto windivert_ioctl_exit; } - switch ((WINDIVERT_PARAM)ioctl->arg8) + switch (param) { case WINDIVERT_PARAM_QUEUE_LEN: *valptr = context->packet_queue_maxlength; @@ -2808,6 +2947,7 @@ windivert_ioctl_bad_start_state: } KeReleaseInStackQueuedSpinLock(&lock_handle); break; + } default: status = STATUS_INVALID_DEVICE_REQUEST; diff --git a/test/test.c b/test/test.c index aab5ed0..ed8b590 100644 --- a/test/test.c +++ b/test/test.c @@ -319,7 +319,7 @@ int main(void) // (1) stop normal traffic from interacting with the tests; and // (2) stop test packets escaping to the Internet or TCP/IP stack. upper_handle = WinDivertOpen("true", WINDIVERT_LAYER_NETWORK, -510, - WINDIVERT_FLAG_DROP | WINDIVERT_FLAG_DEBUG); + WINDIVERT_FLAG_DROP); lower_handle = WinDivertOpen("true", WINDIVERT_LAYER_NETWORK, 510, WINDIVERT_FLAG_DROP); if (upper_handle == INVALID_HANDLE_VALUE || @@ -468,7 +468,7 @@ static BOOL run_test(HANDLE inject_handle, const char *filter, goto failed; } overlapped.hEvent = event; - if (!WinDivertRecvEx(handle, buf, sizeof(buf), 0, &addr, &buf_len, + if (!WinDivertRecvEx(handle, buf, sizeof(buf), &buf_len, 0, &addr, NULL, &overlapped)) { if (GetLastError() != ERROR_IO_PENDING)