diff --git a/LICENSE b/LICENSE index 5325b0b..8489a8e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -WinDivert is dual-licensed, and is available under the GNU Lesser General -Public License (LGPL) Version 3 or the GNU General Public License (GPL) -Version 2. Copies of the LGPLv3, GPLv3 and GPLv2 are provided below. +WinDivert is dual-licensed under your choice of the GNU Lesser General Public +License (LGPL) Version 3 or the GNU General Public License (GPL) Version 2. +Copies of the LGPLv3, GPLv3 and GPLv2 are provided below. ============================================================================== diff --git a/README b/README index 046ef7d..6798e09 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -WinDivert 1.4: Windows Packet Divert +WinDivert 2.0: Windows Packet Divert ==================================== 1. Introduction diff --git a/VERSION b/VERSION index 1c99cf0..227cea2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.4 +2.0.0 diff --git a/dll/windivert.c b/dll/windivert.c index 3e773c7..72e8bfa 100644 --- a/dll/windivert.c +++ b/dll/windivert.c @@ -51,19 +51,6 @@ #define WINDIVERT_DRIVER32_SYS L"\\" WINDIVERT_DRIVER_NAME L"32.sys" #define WINDIVERT_DRIVER64_SYS L"\\" WINDIVERT_DRIVER_NAME L"64.sys" -/* - * Definitions to remove (some) external dependencies: - */ -#define BYTESWAP16(x) \ - ((((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00)) -#define BYTESWAP32(x) \ - ((((x) >> 24) & 0x000000FF) | (((x) >> 8) & 0x0000FF00) | \ - (((x) << 8) & 0x00FF0000) | (((x) << 24) & 0xFF000000)) -#define ntohs(x) BYTESWAP16(x) -#define htons(x) BYTESWAP16(x) -#define ntohl(x) BYTESWAP32(x) -#define htonl(x) BYTESWAP32(x) - static BOOLEAN WinDivertIsXDigit(char c); static BOOLEAN WinDivertIsSpace(char c); static BOOLEAN WinDivertIsAlNum(char c); @@ -76,27 +63,6 @@ static int WinDivertStrCmp(const char *s, const char *t); static BOOLEAN WinDivertAToI(const char *str, char **endptr, UINT32 *intptr); static BOOLEAN WinDivertAToX(const char *str, char **endptr, UINT32 *intptr); -/* - * IPv4/IPv6 pseudo headers. - */ -typedef struct -{ - UINT32 SrcAddr; - UINT32 DstAddr; - UINT8 Zero; - UINT8 Protocol; - UINT16 Length; -} WINDIVERT_PSEUDOHDR, *PWINDIVERT_PSEUDOHDR; - -typedef struct -{ - UINT32 SrcAddr[4]; - UINT32 DstAddr[4]; - UINT32 Length; - UINT32 Zero:24; - UINT32 NextHdr:8; -} WINDIVERT_PSEUDOV6HDR, *PWINDIVERT_PSEUDOV6HDR; - /* * Misc. */ @@ -113,19 +79,16 @@ typedef struct 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); -#ifdef WINDIVERT_DEBUG -static void WinDivertFilterDump(windivert_ioctl_filter_t filter, UINT16 len); -#endif - /* * Include the helper API implementation. */ +#include "windivert_shared.c" #include "windivert_helper.c" /* @@ -337,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; @@ -357,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 || @@ -376,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) { - struct windivert_ioctl_s ioctl; + 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) @@ -402,39 +363,48 @@ static BOOL WinDivertIoControlEx(HANDLE handle, DWORD code, UINT8 arg8, extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, INT16 priority, UINT64 flags) { - struct windivert_ioctl_filter_s object[WINDIVERT_FILTER_MAXLEN]; + WINDIVERT_FILTER object[WINDIVERT_FILTER_MAXLEN]; UINT obj_len; ERROR comp_err; DWORD err; HANDLE handle; SC_HANDLE service; - UINT32 priority32; - + UINT64 priority64, filter_flags; + // Parameter checking. - if (!WINDIVERT_FLAGS_VALID(flags) || layer > WINDIVERT_LAYER_MAX) + switch (layer) { - SetLastError(ERROR_INVALID_PARAMETER); - return INVALID_HANDLE_VALUE; + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + case WINDIVERT_LAYER_FLOW: + case WINDIVERT_LAYER_SOCKET: + case WINDIVERT_LAYER_REFLECT: + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; } - priority32 = WINDIVERT_PRIORITY(priority); - if (priority32 < WINDIVERT_PRIORITY_MIN || - priority32 > WINDIVERT_PRIORITY_MAX) + if (!WINDIVERT_FLAGS_VALID(flags)) { SetLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } - // Compile the filter: + if (priority < WINDIVERT_PRIORITY_MIN || + priority > WINDIVERT_PRIORITY_MAX) + { + SetLastError(ERROR_INVALID_PARAMETER); + return INVALID_HANDLE_VALUE; + } + + // Compile & analyze the filter: comp_err = WinDivertCompileFilter(filter, layer, object, &obj_len); if (IS_ERROR(comp_err)) { SetLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } - -#ifdef WINDIVERT_DEBUG - WinDivertFilterDump(object, obj_len); -#endif + filter_flags = WinDivertAnalyzeFilter(layer, object, obj_len); // Attempt to open the WinDivert device: handle = CreateFile(L"\\\\.\\" WINDIVERT_DEVICE_NAME, @@ -449,6 +419,11 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, } // Open failed because the device isn't installed; install it now. + if ((flags & WINDIVERT_FLAG_NO_INSTALL) != 0) + { + SetLastError(ERROR_SERVICE_DOES_NOT_EXIST); + return INVALID_HANDLE_VALUE; + } SetLastError(0); service = WinDivertDriverInstall(); if (service == NULL) @@ -477,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; @@ -488,8 +463,8 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, // Set the flags: if (flags != 0) { - if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_SET_FLAGS, 0, - (UINT64)flags, NULL, 0, NULL)) + if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_SET_FLAGS, flags, 0, + NULL, 0, NULL)) { CloseHandle(handle); return INVALID_HANDLE_VALUE; @@ -497,10 +472,12 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, } // Set the priority: - if (priority32 != WINDIVERT_PRIORITY_DEFAULT) + if (priority != WINDIVERT_PRIORITY_DEFAULT) { - if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_SET_PRIORITY, 0, - (UINT64)priority32, NULL, 0, NULL)) + // Make positive: + priority64 = (UINT64)((INT64)priority + WINDIVERT_PRIORITY_MAX); + if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_SET_PRIORITY, + priority64, 0, NULL, 0, NULL)) { CloseHandle(handle); return INVALID_HANDLE_VALUE; @@ -508,8 +485,8 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, } // Start the filter: - if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_START_FILTER, 0, 0, - object, obj_len*sizeof(struct windivert_ioctl_filter_s), NULL)) + if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_START_FILTER, + filter_flags, 0, object, obj_len * sizeof(WINDIVERT_FILTER), NULL)) { CloseHandle(handle); return INVALID_HANDLE_VALUE; @@ -519,56 +496,21 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, return handle; } -/* - * Workaround for #134 - */ -static void WinDivertFixChecksums(PVOID pPacket, UINT packetLen, - PWINDIVERT_ADDRESS addr) -{ - UINT64 flags = - WINDIVERT_HELPER_NO_IP_CHECKSUM | - WINDIVERT_HELPER_NO_TCP_CHECKSUM | - WINDIVERT_HELPER_NO_UDP_CHECKSUM; - BOOL calc = FALSE; - if (addr->PseudoIPChecksum != 0) - { - addr->PseudoIPChecksum = 0; - flags &= ~WINDIVERT_HELPER_NO_IP_CHECKSUM; - calc = TRUE; - } - if (addr->PseudoTCPChecksum != 0) - { - addr->PseudoTCPChecksum = 0; - flags &= ~WINDIVERT_HELPER_NO_TCP_CHECKSUM; - calc = TRUE; - } - if (addr->PseudoUDPChecksum != 0) - { - addr->PseudoUDPChecksum = 0; - flags &= ~WINDIVERT_HELPER_NO_UDP_CHECKSUM; - calc = TRUE; - } - if (calc) - { - WinDivertHelperCalcChecksums(pPacket, packetLen, addr, flags); - } -} - /* * Receive a WinDivert packet. */ 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) @@ -578,57 +520,62 @@ 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); } } /* * Send a WinDivert packet. */ -extern BOOL WinDivertSend(HANDLE handle, PVOID pPacket, UINT packetLen, - PWINDIVERT_ADDRESS addr, UINT *writelen) +extern BOOL WinDivertSend(HANDLE handle, const VOID *pPacket, UINT packetLen, + const WINDIVERT_ADDRESS *addr, UINT *writelen) { - if (addr == NULL) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - WinDivertFixChecksums(pPacket, packetLen, addr); - return WinDivertIoControl(handle, IOCTL_WINDIVERT_SEND, 0, (UINT64)addr, - pPacket, packetLen, writelen); + return WinDivertIoControl(handle, IOCTL_WINDIVERT_SEND, (UINT64)addr, + sizeof(WINDIVERT_ADDRESS), (PVOID)pPacket, packetLen, writelen); } /* * Send a WinDivert packet. */ -extern BOOL WinDivertSendEx(HANDLE handle, PVOID pPacket, UINT packetLen, - UINT64 flags, PWINDIVERT_ADDRESS addr, UINT *writelen, +extern BOOL WinDivertSendEx(HANDLE handle, const VOID *pPacket, UINT packetLen, + UINT *writeLen, UINT64 flags, const WINDIVERT_ADDRESS *addr, UINT addrLen, LPOVERLAPPED overlapped) { - if (flags != 0 || addr == NULL) + if (flags != 0) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } - WinDivertFixChecksums(pPacket, packetLen, addr); 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, (PVOID)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, (PVOID)pPacket, packetLen, writeLen, + overlapped); } } +/* + * Shutdown a WinDivert handle. + */ +extern BOOL WinDivertShutdown(HANDLE handle, WINDIVERT_SHUTDOWN how) +{ + return WinDivertIoControl(handle, IOCTL_WINDIVERT_SHUTDOWN, + (UINT64)how, 0, NULL, 0, NULL); +} + /* * Close a WinDivert handle. */ @@ -841,253 +788,3 @@ static BOOLEAN WinDivertAToX(const char *str, char **endptr, UINT32 *intptr) return TRUE; } -/***************************************************************************/ -/* DEBUGGING */ -/***************************************************************************/ - -#ifdef WINDIVERT_DEBUG -/* - * Print a filter (debugging). - */ -static void WinDivertFilterDump(windivert_ioctl_filter_t filter, UINT16 len) -{ - UINT16 i; - - for (i = 0; i < len; i++) - { - printf("label_%u:\n\tif (", i); - switch (filter[i].field) - { - case WINDIVERT_FILTER_FIELD_ZERO: - printf("zero "); - break; - case WINDIVERT_FILTER_FIELD_INBOUND: - printf("inbound "); - break; - case WINDIVERT_FILTER_FIELD_OUTBOUND: - printf("outbound "); - break; - case WINDIVERT_FILTER_FIELD_IFIDX: - printf("ifIdx "); - break; - case WINDIVERT_FILTER_FIELD_SUBIFIDX: - printf("subIfIdx "); - break; - case WINDIVERT_FILTER_FIELD_IP: - printf("ip "); - break; - case WINDIVERT_FILTER_FIELD_IPV6: - printf("ipv6 "); - break; - case WINDIVERT_FILTER_FIELD_ICMP: - printf("icmp "); - break; - case WINDIVERT_FILTER_FIELD_ICMPV6: - printf("icmpv6 "); - break; - case WINDIVERT_FILTER_FIELD_TCP: - printf("tcp "); - break; - case WINDIVERT_FILTER_FIELD_UDP: - printf("udp "); - break; - case WINDIVERT_FILTER_FIELD_IP_HDRLENGTH: - printf("ip.HdrLength "); - break; - case WINDIVERT_FILTER_FIELD_IP_TOS: - printf("ip.TOS "); - break; - case WINDIVERT_FILTER_FIELD_IP_LENGTH: - printf("ip.Length "); - break; - case WINDIVERT_FILTER_FIELD_IP_ID: - printf("ip.Id "); - break; - case WINDIVERT_FILTER_FIELD_IP_DF: - printf("ip.DF "); - break; - case WINDIVERT_FILTER_FIELD_IP_MF: - printf("ip.MF "); - break; - case WINDIVERT_FILTER_FIELD_IP_FRAGOFF: - printf("ip.FragOff "); - break; - case WINDIVERT_FILTER_FIELD_IP_TTL: - printf("ip.TTL "); - break; - case WINDIVERT_FILTER_FIELD_IP_PROTOCOL: - printf("ip.Protocol "); - break; - case WINDIVERT_FILTER_FIELD_IP_CHECKSUM: - printf("ip.Checksum "); - break; - case WINDIVERT_FILTER_FIELD_IP_SRCADDR: - printf("ip.SrcAddr "); - break; - case WINDIVERT_FILTER_FIELD_IP_DSTADDR: - printf("ip.DstAddr "); - break; - case WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: - printf("ipv6.TrafficClass "); - break; - case WINDIVERT_FILTER_FIELD_IPV6_FLOWLABEL: - printf("ipv6.FlowLabel "); - break; - case WINDIVERT_FILTER_FIELD_IPV6_LENGTH: - printf("ipv6.Length "); - break; - case WINDIVERT_FILTER_FIELD_IPV6_NEXTHDR: - printf("ipv6.NextHdr "); - break; - case WINDIVERT_FILTER_FIELD_IPV6_HOPLIMIT: - printf("ipv6.HopLimit "); - break; - case WINDIVERT_FILTER_FIELD_IPV6_SRCADDR: - printf("ipv6.SrcAddr "); - break; - case WINDIVERT_FILTER_FIELD_IPV6_DSTADDR: - printf("ipv6.DstAddr "); - break; - case WINDIVERT_FILTER_FIELD_ICMP_TYPE: - printf("icmp.Type "); - break; - case WINDIVERT_FILTER_FIELD_ICMP_CODE: - printf("icmp.Code "); - break; - case WINDIVERT_FILTER_FIELD_ICMP_CHECKSUM: - printf("icmp.Checksum "); - break; - case WINDIVERT_FILTER_FIELD_ICMP_BODY: - printf("icmp.Body "); - break; - case WINDIVERT_FILTER_FIELD_ICMPV6_TYPE: - printf("icmpv6.Type "); - break; - case WINDIVERT_FILTER_FIELD_ICMPV6_CODE: - printf("icmpv6.Code "); - break; - case WINDIVERT_FILTER_FIELD_ICMPV6_CHECKSUM: - printf("icmpv6.Checksum "); - break; - case WINDIVERT_FILTER_FIELD_ICMPV6_BODY: - printf("icmpv6.Body "); - break; - case WINDIVERT_FILTER_FIELD_TCP_SRCPORT: - printf("tcp.SrcPort "); - break; - case WINDIVERT_FILTER_FIELD_TCP_DSTPORT: - printf("tcp.DstPort "); - break; - case WINDIVERT_FILTER_FIELD_TCP_SEQNUM: - printf("tcp.SeqNum "); - break; - case WINDIVERT_FILTER_FIELD_TCP_ACKNUM: - printf("tcp.AckNum "); - break; - case WINDIVERT_FILTER_FIELD_TCP_HDRLENGTH: - printf("tcp.HdrLength "); - break; - case WINDIVERT_FILTER_FIELD_TCP_URG: - printf("tcp.Urg "); - break; - case WINDIVERT_FILTER_FIELD_TCP_ACK: - printf("tcp.Ack "); - break; - case WINDIVERT_FILTER_FIELD_TCP_PSH: - printf("tcp.Psh "); - break; - case WINDIVERT_FILTER_FIELD_TCP_RST: - printf("tcp.Rst "); - break; - case WINDIVERT_FILTER_FIELD_TCP_SYN: - printf("tcp.Syn "); - break; - case WINDIVERT_FILTER_FIELD_TCP_FIN: - printf("tcp.Fin "); - break; - case WINDIVERT_FILTER_FIELD_TCP_WINDOW: - printf("tcp.Window "); - break; - case WINDIVERT_FILTER_FIELD_TCP_CHECKSUM: - printf("tcp.Checksum "); - break; - case WINDIVERT_FILTER_FIELD_TCP_URGPTR: - printf("tcp.UrgPtr "); - break; - case WINDIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH: - printf("tcp.PayloadLength " ); - break; - case WINDIVERT_FILTER_FIELD_UDP_SRCPORT: - printf("udp.SrcPort "); - break; - case WINDIVERT_FILTER_FIELD_UDP_DSTPORT: - printf("udp.DstPort "); - break; - case WINDIVERT_FILTER_FIELD_UDP_LENGTH: - printf("udp.Length "); - break; - case WINDIVERT_FILTER_FIELD_UDP_CHECKSUM: - printf("udp.Checksum "); - break; - case WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: - printf("udp.PayloadLength "); - break; - default: - printf("unknown.Field "); - break; - } - switch (filter[i].test) - { - case WINDIVERT_FILTER_TEST_EQ: - printf("== "); - break; - case WINDIVERT_FILTER_TEST_NEQ: - printf("!= "); - break; - case WINDIVERT_FILTER_TEST_LT: - printf("< "); - break; - case WINDIVERT_FILTER_TEST_LEQ: - printf("<= "); - break; - case WINDIVERT_FILTER_TEST_GT: - printf("> "); - break; - case WINDIVERT_FILTER_TEST_GEQ: - printf(">= "); - break; - default: - printf("?? "); - break; - } - printf("%u)\n", filter[i].arg[0]); - switch (filter[i].success) - { - case WINDIVERT_FILTER_RESULT_ACCEPT: - printf("\t\treturn ACCEPT;\n"); - break; - case WINDIVERT_FILTER_RESULT_REJECT: - printf("\t\treturn REJECT;\n"); - break; - default: - printf("\t\tgoto label_%u;\n", filter[i].success); - break; - } - printf("\telse\n"); - switch (filter[i].failure) - { - case WINDIVERT_FILTER_RESULT_ACCEPT: - printf("\t\treturn ACCEPT;\n"); - break; - case WINDIVERT_FILTER_RESULT_REJECT: - printf("\t\treturn REJECT;\n"); - break; - default: - printf("\t\tgoto label_%u;\n", filter[i].failure); - break; - } - } -} - -#endif /* WINDIVERT_DEBUG */ - diff --git a/dll/windivert.def b/dll/windivert.def index 2c20d03..aa30104 100644 --- a/dll/windivert.def +++ b/dll/windivert.def @@ -6,12 +6,25 @@ EXPORTS WinDivertRecvEx WinDivertSend WinDivertSendEx + WinDivertShutdown WinDivertClose WinDivertSetParam WinDivertGetParam WinDivertHelperCalcChecksums + WinDivertHelperHashPacket WinDivertHelperParsePacket WinDivertHelperParseIPv4Address WinDivertHelperParseIPv6Address - WinDivertHelperCheckFilter + WinDivertHelperFormatIPv4Address + WinDivertHelperFormatIPv6Address + WinDivertHelperCompileFilter WinDivertHelperEvalFilter + WinDivertHelperFormatFilter + WinDivertHelperNtohs + WinDivertHelperHtons + WinDivertHelperNtohl + WinDivertHelperHtonl + WinDivertHelperNtohll + WinDivertHelperHtonll + WinDivertHelperNtohIpv6Address + WinDivertHelperHtonIpv6Address diff --git a/dll/windivert_hash.c b/dll/windivert_hash.c new file mode 100644 index 0000000..2dcbc94 --- /dev/null +++ b/dll/windivert_hash.c @@ -0,0 +1,201 @@ +/* + * windivert_hash.c + * (C) 2018, all rights reserved, + * + * This file is part of WinDivert. + * + * WinDivert is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * WinDivert is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * xxHash - Fast Hash algorithm + * Copyright (C) 2012-2016, Yann Collet + * + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This is a modified version of the 64bit xxHash algorithm: + * - The algorithm is seeded with packet data rather than the single 64bit + * "seed" value. + * - The input sized is fixed to 32bytes (excluding the seed), so there is + * only ever a single round. As such, the algorithm has been specialized. + */ + +#define WINDIVERT_ROTL(x, r) (((x) << (r)) | ((x) >> (64 - (r)))) + +static const UINT64 WINDIVERT_PRIME64_1 = 11400714785074694791ull; +static const UINT64 WINDIVERT_PRIME64_2 = 14029467366897019727ull; +static const UINT64 WINDIVERT_PRIME64_3 = 1609587929392839161ull; +static const UINT64 WINDIVERT_PRIME64_4 = 9650029242287828579ull; + +static UINT64 WinDivertXXH64Round(UINT64 acc, UINT64 input) +{ + acc += input * WINDIVERT_PRIME64_2; + acc = WINDIVERT_ROTL(acc, 31); + acc *= WINDIVERT_PRIME64_1; + return acc; +} + +static UINT64 WinDivertXXH64MergeRound(UINT64 acc, UINT64 val) +{ + val = WinDivertXXH64Round(0, val); + acc ^= val; + acc = acc * WINDIVERT_PRIME64_1 + WINDIVERT_PRIME64_4; + return acc; +} + +static UINT64 WinDivertXXH64Avalanche(UINT64 h64) +{ + h64 ^= h64 >> 33; + h64 *= WINDIVERT_PRIME64_2; + h64 ^= h64 >> 29; + h64 *= WINDIVERT_PRIME64_3; + h64 ^= h64 >> 32; + return h64; +} + +/* + * WinDivert packet hash function. + */ +static UINT64 WinDivertHashPacket(UINT64 seed, PWINDIVERT_IPHDR ip_header, + PWINDIVERT_IPV6HDR ipv6_header, PWINDIVERT_ICMPHDR icmp_header, + PWINDIVERT_ICMPV6HDR icmpv6_header, PWINDIVERT_TCPHDR tcp_header, + PWINDIVERT_UDPHDR udp_header) +{ + UINT64 h64, v1, v2, v3, v4, v[4], *data64; + UINT32 *data32; + UINT i; + static const UINT64 padding64[] = // SHA2 IV + { + 0x428A2F9871374491ull, 0xB5C0FBCFE9B5DBA5ull, 0x3956C25B59F111F1ull, + 0x923F82A4AB1C5ED5ull, 0xD807AA9812835B01ull, 0x243185BE550C7DC3ull, + 0x72BE5D7480DEB1FEull, 0x9BDC06A7C19BF174ull, 0xE49B69C1EFBE4786ull, + }; + + // Set-up seed & data + v1 = seed ^ padding64[0]; + if (ip_header != NULL) + { + data64 = (UINT64 *)ip_header; + v2 = data64[0] ^ padding64[1]; + v3 = data64[1] ^ padding64[2]; + data32 = (UINT32 *)ip_header; + v4 = (UINT64)data32[4] ^ padding64[3]; + i = 0; + } + else if (ipv6_header != NULL) + { + data64 = (UINT64 *)ipv6_header; + v2 = data64[0] ^ padding64[1]; + v3 = data64[1] ^ padding64[2]; + v4 = data64[2] ^ padding64[3]; + v[0] = data64[3] ^ padding64[4]; + v[1] = data64[4] ^ padding64[5]; + i = 2; + } + else + return 0; + + if (tcp_header != NULL) + { + data64 = (UINT64 *)tcp_header; + v[i] = data64[0] ^ padding64[i+4]; i++; + v[i] = data64[1] ^ padding64[i+4]; i++; + data32 = (UINT32 *)tcp_header; + if (i <= 3) + { + v[i] = (UINT64)data32[4] ^ padding64[i+4]; i++; + } + else + { + v2 ^= ((UINT64)data32[4] << 32); + } + } + else + { + if (udp_header != NULL) + { + data64 = (UINT64 *)udp_header; + v[i] = data64[0] ^ padding64[i+4]; i++; + } + else if (icmp_header != NULL) + { + data64 = (UINT64 *)icmp_header; + v[i] = data64[0] ^ padding64[i+4]; i++; + } + else if (icmpv6_header != NULL) + { + data64 = (UINT64 *)icmpv6_header; + v[i] = data64[0] ^ padding64[i+4]; i++; + } + } + + while (i <= 3) + { + v[i] = seed ^ padding64[i+4]; i++; + } + + // Hash + v1 = WinDivertXXH64Round(v[0], v1); + v2 = WinDivertXXH64Round(v[1], v2); + v3 = WinDivertXXH64Round(v[2], v3); + v4 = WinDivertXXH64Round(v[3], v4); + h64 = WINDIVERT_ROTL(v1, 1) + WINDIVERT_ROTL(v2, 7) + + WINDIVERT_ROTL(v3, 12) + WINDIVERT_ROTL(v4, 18); + h64 = WinDivertXXH64MergeRound(h64, v1); + h64 = WinDivertXXH64MergeRound(h64, v2); + h64 = WinDivertXXH64MergeRound(h64, v3); + h64 = WinDivertXXH64MergeRound(h64, v4); + h64 += 32; // "length" + h64 = WinDivertXXH64Avalanche(h64); + + return h64; +} + diff --git a/dll/windivert_helper.c b/dll/windivert_helper.c index 917c803..678a15d 100644 --- a/dll/windivert_helper.c +++ b/dll/windivert_helper.c @@ -93,6 +93,9 @@ typedef enum TOKEN_TCP_DST_PORT, TOKEN_TCP_FIN, TOKEN_TCP_HDR_LENGTH, + TOKEN_TCP_PAYLOAD, + TOKEN_TCP_PAYLOAD16, + TOKEN_TCP_PAYLOAD32, TOKEN_TCP_PAYLOAD_LENGTH, TOKEN_TCP_PSH, TOKEN_TCP_RST, @@ -106,8 +109,19 @@ typedef enum TOKEN_UDP_CHECKSUM, TOKEN_UDP_DST_PORT, TOKEN_UDP_LENGTH, + TOKEN_UDP_PAYLOAD, + TOKEN_UDP_PAYLOAD16, + TOKEN_UDP_PAYLOAD32, TOKEN_UDP_PAYLOAD_LENGTH, TOKEN_UDP_SRC_PORT, + TOKEN_ZERO, + TOKEN_EVENT, + TOKEN_RANDOM8, + TOKEN_RANDOM16, + TOKEN_RANDOM32, + TOKEN_PACKET, + TOKEN_PACKET16, + TOKEN_PACKET32, TOKEN_TRUE, TOKEN_FALSE, TOKEN_INBOUND, @@ -116,8 +130,33 @@ typedef enum TOKEN_SUB_IF_IDX, TOKEN_LOOPBACK, TOKEN_IMPOSTOR, + TOKEN_PROCESS_ID, + TOKEN_LOCAL_ADDR, + TOKEN_REMOTE_ADDR, + TOKEN_LOCAL_PORT, + TOKEN_REMOTE_PORT, + TOKEN_PROTOCOL, + TOKEN_LAYER, + TOKEN_FLOW, + TOKEN_SOCKET, + TOKEN_NETWORK, + TOKEN_NETWORK_FORWARD, + TOKEN_REFLECT, + TOKEN_EVENT_PACKET, + TOKEN_EVENT_ESTABLISHED, + TOKEN_EVENT_DELETED, + TOKEN_EVENT_BIND, + TOKEN_EVENT_CONNECT, + TOKEN_EVENT_LISTEN, + TOKEN_EVENT_ACCEPT, + TOKEN_EVENT_OPEN, + TOKEN_EVENT_CLOSE, TOKEN_OPEN, TOKEN_CLOSE, + TOKEN_SQUARE_OPEN, + TOKEN_SQUARE_CLOSE, + TOKEN_MINUS, + TOKEN_BYTES, TOKEN_EQ, TOKEN_NEQ, TOKEN_LT, @@ -139,13 +178,51 @@ typedef struct UINT pos; UINT32 val[4]; } TOKEN; -#define TOKEN_MAXLEN 32 +#define TOKEN_MAXLEN 32 typedef struct { char *name; KIND kind; -} TOKEN_NAME, *PTOKEN_NAME; + UINT8 flags; +} TOKEN_INFO, *PTOKEN_INFO; + +#define WINDIVERT_LAYER_FLAG_NETWORK (1 << WINDIVERT_LAYER_NETWORK) +#define WINDIVERT_LAYER_FLAG_NETWORK_FORWARD \ + (1 << WINDIVERT_LAYER_NETWORK_FORWARD) +#define WINDIVERT_LAYER_FLAG_FLOW (1 << WINDIVERT_LAYER_FLOW) +#define WINDIVERT_LAYER_FLAG_SOCKET (1 << WINDIVERT_LAYER_SOCKET) +#define WINDIVERT_LAYER_FLAG_REFLECT (1 << WINDIVERT_LAYER_REFLECT) + +/* + * Layer flags shorthand. + */ +#define LNMFSR (WINDIVERT_LAYER_FLAG_NETWORK | \ + WINDIVERT_LAYER_FLAG_NETWORK_FORWARD | \ + WINDIVERT_LAYER_FLAG_FLOW | \ + WINDIVERT_LAYER_FLAG_SOCKET | \ + WINDIVERT_LAYER_FLAG_REFLECT) +#define LNMFS_ (WINDIVERT_LAYER_FLAG_NETWORK | \ + WINDIVERT_LAYER_FLAG_NETWORK_FORWARD | \ + WINDIVERT_LAYER_FLAG_FLOW | \ + WINDIVERT_LAYER_FLAG_SOCKET) +#define LN_F__ (WINDIVERT_LAYER_FLAG_NETWORK | \ + WINDIVERT_LAYER_FLAG_FLOW) +#define L__F_R (WINDIVERT_LAYER_FLAG_FLOW | \ + WINDIVERT_LAYER_FLAG_REFLECT) +#define LN_FS_ (WINDIVERT_LAYER_FLAG_NETWORK | \ + WINDIVERT_LAYER_FLAG_FLOW | \ + WINDIVERT_LAYER_FLAG_SOCKET) +#define L__FSR (WINDIVERT_LAYER_FLAG_FLOW | \ + WINDIVERT_LAYER_FLAG_SOCKET | \ + WINDIVERT_LAYER_FLAG_REFLECT) +#define LNM___ (WINDIVERT_LAYER_FLAG_NETWORK | \ + WINDIVERT_LAYER_FLAG_NETWORK_FORWARD) +#define L__F__ WINDIVERT_LAYER_FLAG_FLOW +#define L___S_ WINDIVERT_LAYER_FLAG_SOCKET +#define L____R WINDIVERT_LAYER_FLAG_REFLECT + + /* * Filter expressions. @@ -160,6 +237,7 @@ struct EXPR PEXPR arg[3]; }; UINT8 kind; + UINT8 count; UINT16 succ; UINT16 fail; }; @@ -168,7 +246,7 @@ struct EXPR * Error handling. */ #undef ERROR -typedef UINT64 ERROR; +typedef UINT64 ERROR, *PERROR; #define WINDIVERT_ERROR_NONE 0 #define WINDIVERT_ERROR_NO_MEMORY 1 @@ -177,8 +255,10 @@ typedef UINT64 ERROR; #define WINDIVERT_ERROR_BAD_TOKEN 4 #define WINDIVERT_ERROR_BAD_TOKEN_FOR_LAYER 5 #define WINDIVERT_ERROR_UNEXPECTED_TOKEN 6 -#define WINDIVERT_ERROR_OUTPUT_TOO_SHORT 7 -#define WINDIVERT_ERROR_ASSERTION_FAILED 8 +#define WINDIVERT_ERROR_INDEX_OOB 7 +#define WINDIVERT_ERROR_OUTPUT_TOO_SHORT 8 +#define WINDIVERT_ERROR_BAD_OBJECT 9 +#define WINDIVERT_ERROR_ASSERTION_FAILED 10 #define MAKE_ERROR(code, pos) \ (((ERROR)(code) << 32) | (ERROR)(pos)); @@ -190,427 +270,29 @@ typedef UINT64 ERROR; #define IS_ERROR(err) \ (GET_CODE(err) != WINDIVERT_ERROR_NONE) -#define MAX(a, b) ((a) > (b)? (a): (b)) - -/* - * Compiler memory pool: - */ -typedef struct POOL -{ - unsigned offset; - ERROR error; - char memory[3 * 4096 - 32]; -} POOL, *PPOOL; - /* * Prototypes. */ -static PEXPR WinDivertParseFilter(PPOOL pool, TOKEN *toks, UINT *i, INT depth, - BOOL and); -static UINT16 WinDivertInitPseudoHeader(PWINDIVERT_IPHDR ip_header, - PWINDIVERT_IPV6HDR ipv6_header, UINT8 protocol, UINT len, - void *pseudo_header); -static UINT16 WinDivertHelperCalcChecksum(PVOID pseudo_header, - UINT16 pseudo_header_len, PVOID data, UINT len); - -/* - * Skip well-known IPv6 extension headers. - */ -static UINT8 WinDivertSkipExtHeaders(UINT8 proto, UINT8 **header, UINT *len) -{ - UINT hdrlen; - - while (TRUE) - { - if (*len <= 2) - { - return IPPROTO_NONE; - } - - hdrlen = (UINT)*(*header + 1); - switch (proto) - { - case IPPROTO_FRAGMENT: - hdrlen = 8; - break; - case IPPROTO_AH: - hdrlen += 2; - hdrlen *= 4; - break; - case IPPROTO_HOPOPTS: - case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: - hdrlen++; - hdrlen *= 8; - break; - case IPPROTO_NONE: - return proto; - default: - return proto; - } - - if (hdrlen >= *len) - { - return IPPROTO_NONE; - } - - proto = **header; - *header += hdrlen; - *len -= hdrlen; - } -} +static PEXPR WinDivertParseFilter(HANDLE pool, TOKEN *toks, UINT *i, + INT depth, BOOL and, PERROR error); +static BOOL WinDivertCondExecFilter(PWINDIVERT_FILTER filter, UINT length, + UINT8 field, UINT32 arg); +static BOOL WinDivertDeserializeFilter(PWINDIVERT_STREAM stream, + PWINDIVERT_FILTER filter, UINT *length); +static void WinDivertFormatExpr(PWINDIVERT_STREAM stream, PEXPR expr, + WINDIVERT_LAYER layer, BOOL top_level, BOOL and); /* * Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet. */ -extern BOOL WinDivertHelperParsePacket(PVOID pPacket, UINT packetLen, +extern BOOL WinDivertHelperParsePacket(const VOID *pPacket, UINT packetLen, PWINDIVERT_IPHDR *ppIpHdr, PWINDIVERT_IPV6HDR *ppIpv6Hdr, PWINDIVERT_ICMPHDR *ppIcmpHdr, PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr, PWINDIVERT_TCPHDR *ppTcpHdr, PWINDIVERT_UDPHDR *ppUdpHdr, PVOID *ppData, UINT *pDataLen) { - PWINDIVERT_IPHDR ip_header = NULL; - PWINDIVERT_IPV6HDR ipv6_header = NULL; - PWINDIVERT_ICMPHDR icmp_header = NULL; - PWINDIVERT_ICMPV6HDR icmpv6_header = NULL; - PWINDIVERT_TCPHDR tcp_header = NULL; - PWINDIVERT_UDPHDR udp_header = NULL; - UINT16 header_len; - UINT8 trans_proto; - PVOID data = NULL; - UINT data_len = 0; - BOOL success; - - if (pPacket == NULL || packetLen < sizeof(UINT8)) - { - goto WinDivertHelperParsePacketExit; - } - data = pPacket; - data_len = packetLen; - - ip_header = (PWINDIVERT_IPHDR)data; - switch (ip_header->Version) - { - case 4: - if (data_len < sizeof(WINDIVERT_IPHDR) || - ip_header->HdrLength < 5 || - data_len < ip_header->HdrLength*sizeof(UINT32) || - ntohs(ip_header->Length) != data_len) - { - ip_header = NULL; - goto WinDivertHelperParsePacketExit; - } - trans_proto = ip_header->Protocol; - header_len = ip_header->HdrLength*sizeof(UINT32); - data = (PVOID)((UINT8 *)data + header_len); - data_len -= header_len; - break; - case 6: - ip_header = NULL; - ipv6_header = (PWINDIVERT_IPV6HDR)data; - if (data_len < sizeof(WINDIVERT_IPV6HDR) || - ntohs(ipv6_header->Length) != - data_len - sizeof(WINDIVERT_IPV6HDR)) - { - ipv6_header = NULL; - goto WinDivertHelperParsePacketExit; - } - trans_proto = ipv6_header->NextHdr; - data = (PVOID)((UINT8 *)data + sizeof(WINDIVERT_IPV6HDR)); - data_len -= sizeof(WINDIVERT_IPV6HDR); - trans_proto = WinDivertSkipExtHeaders(trans_proto, (UINT8 **)&data, - &data_len); - break; - default: - ip_header = NULL; - goto WinDivertHelperParsePacketExit; - } - switch (trans_proto) - { - case IPPROTO_TCP: - tcp_header = (PWINDIVERT_TCPHDR)data; - if (data_len < sizeof(WINDIVERT_TCPHDR) || - tcp_header->HdrLength < 5 || - data_len < tcp_header->HdrLength*sizeof(UINT32)) - { - tcp_header = NULL; - goto WinDivertHelperParsePacketExit; - } - header_len = tcp_header->HdrLength*sizeof(UINT32); - data = ((UINT8 *)data + header_len); - data_len -= header_len; - break; - case IPPROTO_UDP: - udp_header = (PWINDIVERT_UDPHDR)data; - if (data_len < sizeof(WINDIVERT_UDPHDR) || - ntohs(udp_header->Length) != data_len) - { - udp_header = NULL; - goto WinDivertHelperParsePacketExit; - } - data = ((UINT8 *)data + sizeof(WINDIVERT_UDPHDR)); - data_len -= sizeof(WINDIVERT_UDPHDR); - break; - case IPPROTO_ICMP: - icmp_header = (PWINDIVERT_ICMPHDR)data; - if (ip_header == NULL || - data_len < sizeof(WINDIVERT_ICMPHDR)) - { - icmp_header = NULL; - goto WinDivertHelperParsePacketExit; - } - data = ((UINT8 *)data + sizeof(WINDIVERT_ICMPHDR)); - data_len -= sizeof(WINDIVERT_ICMPHDR); - break; - case IPPROTO_ICMPV6: - icmpv6_header = (PWINDIVERT_ICMPV6HDR)data; - if (ipv6_header == NULL || - data_len < sizeof(WINDIVERT_ICMPV6HDR)) - { - icmpv6_header = NULL; - goto WinDivertHelperParsePacketExit; - } - data = ((UINT8 *)data + sizeof(WINDIVERT_ICMPV6HDR)); - data_len -= sizeof(WINDIVERT_ICMPV6HDR); - break; - default: - break; - } - - if (data_len == 0) - { - data = NULL; - } - -WinDivertHelperParsePacketExit: - success = TRUE; - if (ppIpHdr != NULL) - { - *ppIpHdr = ip_header; - success = success && (ip_header != NULL); - } - if (ppIpv6Hdr != NULL) - { - *ppIpv6Hdr = ipv6_header; - success = success && (ipv6_header != NULL); - } - if (ppIcmpHdr != NULL) - { - *ppIcmpHdr = icmp_header; - success = success && (icmp_header != NULL); - } - if (ppIcmpv6Hdr != NULL) - { - *ppIcmpv6Hdr = icmpv6_header; - success = success && (icmpv6_header != NULL); - } - if (ppTcpHdr != NULL) - { - *ppTcpHdr = tcp_header; - success = success && (tcp_header != NULL); - } - if (ppUdpHdr != NULL) - { - *ppUdpHdr = udp_header; - success = success && (udp_header != NULL); - } - if (ppData != NULL) - { - *ppData = data; - success = success && (data != NULL); - } - if (pDataLen != NULL) - { - *pDataLen = data_len; - } - return success; -} - -/* - * Calculate IPv4/IPv6/ICMP/ICMPv6/TCP/UDP checksums. - */ -extern UINT WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, - PWINDIVERT_ADDRESS pAddr, UINT64 flags) -{ - UINT8 pseudo_header[ - MAX(sizeof(WINDIVERT_PSEUDOHDR), sizeof(WINDIVERT_PSEUDOV6HDR))]; - UINT16 pseudo_header_len; - PWINDIVERT_IPHDR ip_header; - PWINDIVERT_IPV6HDR ipv6_header; - PWINDIVERT_ICMPHDR icmp_header; - PWINDIVERT_ICMPV6HDR icmpv6_header; - PWINDIVERT_TCPHDR tcp_header; - PWINDIVERT_UDPHDR udp_header; - UINT payload_len, checksum_len; - UINT count = 0; - - WinDivertHelperParsePacket(pPacket, packetLen, &ip_header, &ipv6_header, - &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL, - &payload_len); - - if (ip_header != NULL && !(flags & WINDIVERT_HELPER_NO_IP_CHECKSUM)) - { - ip_header->Checksum = 0; - if (pAddr == NULL || pAddr->PseudoIPChecksum == 0) - { - ip_header->Checksum = WinDivertHelperCalcChecksum(NULL, 0, - ip_header, ip_header->HdrLength*sizeof(UINT32)); - } - count++; - } - - if (icmp_header != NULL) - { - if ((flags & WINDIVERT_HELPER_NO_ICMP_CHECKSUM) != 0) - { - return count; - } - icmp_header->Checksum = 0; - icmp_header->Checksum = WinDivertHelperCalcChecksum(NULL, 0, - icmp_header, payload_len + sizeof(WINDIVERT_ICMPHDR)); - count++; - return count; - } - - if (icmpv6_header != NULL) - { - if ((flags & WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM) != 0) - { - return count; - } - checksum_len = payload_len + sizeof(WINDIVERT_ICMPV6HDR); - pseudo_header_len = WinDivertInitPseudoHeader(NULL, ipv6_header, - IPPROTO_ICMPV6, checksum_len, pseudo_header); - icmpv6_header->Checksum = 0; - icmpv6_header->Checksum = WinDivertHelperCalcChecksum(pseudo_header, - pseudo_header_len, icmpv6_header, checksum_len); - count++; - return count; - } - - if (tcp_header != NULL) - { - if ((flags & WINDIVERT_HELPER_NO_TCP_CHECKSUM) != 0) - { - return count; - } - if (pAddr == NULL || pAddr->PseudoTCPChecksum == 0) - { - // Full TCP checksum - checksum_len = payload_len + tcp_header->HdrLength*sizeof(UINT32); - pseudo_header_len = WinDivertInitPseudoHeader(ip_header, - ipv6_header, IPPROTO_TCP, checksum_len, pseudo_header); - tcp_header->Checksum = 0; - tcp_header->Checksum = WinDivertHelperCalcChecksum( - pseudo_header, pseudo_header_len, tcp_header, checksum_len); - } - else if (pAddr->Direction == WINDIVERT_DIRECTION_OUTBOUND) - { - // Pseudo TCP checksum - tcp_header->Checksum = 0; - } - count++; - return count; - } - - if (udp_header != NULL) - { - if ((flags & WINDIVERT_HELPER_NO_UDP_CHECKSUM) != 0) - { - return count; - } - if (pAddr == NULL || pAddr->PseudoUDPChecksum == 0) - { - // Full UDP checksum - checksum_len = payload_len + sizeof(WINDIVERT_UDPHDR); - pseudo_header_len = WinDivertInitPseudoHeader(ip_header, - ipv6_header, IPPROTO_UDP, checksum_len, pseudo_header); - udp_header->Checksum = 0; - udp_header->Checksum = WinDivertHelperCalcChecksum( - pseudo_header, pseudo_header_len, udp_header, checksum_len); - if (udp_header->Checksum == 0) - { - udp_header->Checksum = 0xFFFF; - } - } - else if (pAddr->Direction == WINDIVERT_DIRECTION_OUTBOUND) - { - // Pseudo UDP checksum - udp_header->Checksum = 0; - } - count++; - } - return count; -} - -/* - * Initialize the IP/IPv6 pseudo header. - */ -static UINT16 WinDivertInitPseudoHeader(PWINDIVERT_IPHDR ip_header, - PWINDIVERT_IPV6HDR ipv6_header, UINT8 protocol, UINT len, - void *pseudo_header) -{ - if (ip_header != NULL) - { - PWINDIVERT_PSEUDOHDR pseudo_header_v4 = - (PWINDIVERT_PSEUDOHDR)pseudo_header; - pseudo_header_v4->SrcAddr = ip_header->SrcAddr; - pseudo_header_v4->DstAddr = ip_header->DstAddr; - pseudo_header_v4->Zero = 0; - pseudo_header_v4->Protocol = protocol; - pseudo_header_v4->Length = htons((UINT16)len); - return sizeof(WINDIVERT_PSEUDOHDR); - } - else - { - PWINDIVERT_PSEUDOV6HDR pseudo_header_v6 = - (PWINDIVERT_PSEUDOV6HDR)pseudo_header; - memcpy(pseudo_header_v6->SrcAddr, ipv6_header->SrcAddr, - sizeof(pseudo_header_v6->SrcAddr)); - memcpy(pseudo_header_v6->DstAddr, ipv6_header->DstAddr, - sizeof(pseudo_header_v6->DstAddr)); - pseudo_header_v6->Length = htonl((UINT32)len); - pseudo_header_v6->NextHdr = protocol; - pseudo_header_v6->Zero = 0; - return sizeof(WINDIVERT_PSEUDOV6HDR); - } -} - -/* - * Generic checksum computation. - */ -static UINT16 WinDivertHelperCalcChecksum(PVOID pseudo_header, - UINT16 pseudo_header_len, PVOID data, UINT len) -{ - register const UINT16 *data16 = (const UINT16 *)pseudo_header; - register size_t len16 = pseudo_header_len >> 1; - register UINT32 sum = 0; - size_t i; - - // Pseudo header: - for (i = 0; i < len16; i++) - { - sum += (UINT32)data16[i]; - } - - // Main data: - data16 = (const UINT16 *)data; - len16 = len >> 1; - for (i = 0; i < len16; i++) - { - sum += (UINT32)data16[i]; - } - - if (len & 0x1) - { - const UINT8 *data8 = (const UINT8 *)data; - sum += (UINT16)data8[len-1]; - } - - sum = (sum & 0xFFFF) + (sum >> 16); - sum += (sum >> 16); - sum = ~sum; - return (UINT16)sum; + return WinDivertParsePacket((PVOID)pPacket, packetLen, ppIpHdr, ppIpv6Hdr, + ppIcmpHdr, ppIcmpv6Hdr, ppTcpHdr, ppUdpHdr, NULL, ppData, pDataLen); } /* @@ -658,15 +340,15 @@ extern BOOL WinDivertHelperParseIPv4Address(const char *str, UINT32 *addr_ptr) */ extern BOOL WinDivertHelperParseIPv6Address(const char *str, UINT32 *addr_ptr) { - UINT16 laddr[8]; - UINT16 raddr[8]; - BOOL left = TRUE; + UINT16 laddr[8] = {0}; + UINT16 raddr[8] = {0}; + UINT32 addr[4]; + BOOL left = TRUE, ipv4 = FALSE; + UINT32 ipv4_addr; UINT i, j, k, l, part; char part_str[5]; - memset(laddr, 0, sizeof(laddr)); - memset(raddr, 0, sizeof(raddr)); - + j = 0; if (*str == ':') { str++; @@ -677,9 +359,13 @@ extern BOOL WinDivertHelperParseIPv6Address(const char *str, UINT32 *addr_ptr) } left = FALSE; str++; + if (*str == '\0') + { + goto WinDivertHelperParseIPv6AddressSuccess; + } } - for (i = 0, j = 0, k = 0; k < 8; k++) + for (i = 0, k = 0; k < 8; k++) { if (*str == ':') { @@ -690,6 +376,18 @@ extern BOOL WinDivertHelperParseIPv6Address(const char *str, UINT32 *addr_ptr) } left = FALSE; str++; + if (*str == '\0') + { + break; + } + } + + if (i < 6 && WinDivertHelperParseIPv4Address(str, &ipv4_addr)) + { + // Tail is IPv4 address: + ipv4 = TRUE; + j += 2; + goto WinDivertHelperParseIPv6AddressSuccess; } for (l = 0; l < 4 && WinDivertIsXDigit(*str); l++) { @@ -718,11 +416,7 @@ extern BOOL WinDivertHelperParseIPv6Address(const char *str, UINT32 *addr_ptr) } if (*str == '\0') { - if (!left) - { - break; - } - if (k == 7) + if (!left || k == 7) { break; } @@ -731,44 +425,55 @@ extern BOOL WinDivertHelperParseIPv6Address(const char *str, UINT32 *addr_ptr) } str++; } - if (*str != '\0') - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - if (addr_ptr == NULL) + +WinDivertHelperParseIPv6AddressSuccess: + + if (!ipv4 && addr_ptr == NULL) { return TRUE; } - for (i = 0; i < 4; i++) { k = 2 * i + j; l = k + 1; k = (k >= 8? k - 8: k); l = (l >= 8? l - 8: l); - addr_ptr[i] = + addr[3 - i] = (UINT32)laddr[2 * i + 1] | (UINT32)laddr[2 * i] << 16 | (UINT32)raddr[l] | (UINT32)raddr[k] << 16; } + if (ipv4) + { + // Validate IPv4 address + if (addr[3] != 0 || addr[2] != 0 || addr[0] != 0 || + (addr[1] != 0x0000FFFF && addr[1] != 0)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + addr[0] = ipv4_addr; + } + if (addr_ptr != NULL) + { + memcpy(addr_ptr, addr, sizeof(addr)); + } return TRUE; } /* * Lookup a token. */ -static PTOKEN_NAME WinDivertTokenLookup(PTOKEN_NAME token_names, - size_t token_names_len, const char *name) +static PTOKEN_INFO WinDivertTokenLookup(PTOKEN_INFO token_info, + size_t token_info_len, const char *name) { - int lo = 0, hi = (int)token_names_len-1, mid; + int lo = 0, hi = (int)token_info_len-1, mid; int cmp; while (hi >= lo) { mid = (lo + hi) / 2; - cmp = WinDivertStrCmp(token_names[mid].name, name); + cmp = WinDivertStrCmp(token_info[mid].name, name); if (cmp < 0) { lo = mid+1; @@ -779,86 +484,176 @@ static PTOKEN_NAME WinDivertTokenLookup(PTOKEN_NAME token_names, } else { - return &token_names[mid]; + return &token_info[mid]; } } return NULL; } +/* + * Expand a "macro" value. + */ +static BOOL WinDivertExpandMacro(KIND kind, WINDIVERT_LAYER layer, + UINT32 *val) +{ + switch (kind) + { + case TOKEN_NETWORK: + *val = WINDIVERT_LAYER_NETWORK; + return TRUE; + case TOKEN_NETWORK_FORWARD: + *val = WINDIVERT_LAYER_NETWORK_FORWARD; + return TRUE; + case TOKEN_FLOW: + *val = WINDIVERT_LAYER_FLOW; + return TRUE; + case TOKEN_SOCKET: + *val = WINDIVERT_LAYER_SOCKET; + return TRUE; + case TOKEN_REFLECT: + *val = WINDIVERT_LAYER_REFLECT; + return TRUE; + case TOKEN_EVENT_PACKET: + *val = WINDIVERT_EVENT_NETWORK_PACKET; + return (layer == WINDIVERT_LAYER_NETWORK); + case TOKEN_EVENT_ESTABLISHED: + *val = WINDIVERT_EVENT_FLOW_ESTABLISHED; + return (layer == WINDIVERT_LAYER_FLOW); + case TOKEN_EVENT_DELETED: + *val = WINDIVERT_EVENT_FLOW_DELETED; + return (layer == WINDIVERT_LAYER_FLOW); + case TOKEN_EVENT_BIND: + *val = WINDIVERT_EVENT_SOCKET_BIND; + return (layer == WINDIVERT_LAYER_SOCKET); + case TOKEN_EVENT_CONNECT: + *val = WINDIVERT_EVENT_SOCKET_CONNECT; + return (layer == WINDIVERT_LAYER_SOCKET); + case TOKEN_EVENT_LISTEN: + *val = WINDIVERT_EVENT_SOCKET_LISTEN; + return (layer == WINDIVERT_LAYER_SOCKET); + case TOKEN_EVENT_ACCEPT: + *val = WINDIVERT_EVENT_SOCKET_ACCEPT; + return (layer == WINDIVERT_LAYER_SOCKET); + case TOKEN_EVENT_OPEN: + *val = WINDIVERT_EVENT_REFLECT_OPEN; + return (layer == WINDIVERT_LAYER_REFLECT); + case TOKEN_EVENT_CLOSE: + *val = WINDIVERT_EVENT_REFLECT_CLOSE; + return (layer == WINDIVERT_LAYER_REFLECT); + default: + return FALSE; + } +} + /* * Tokenize the given filter string. */ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, TOKEN *tokens, UINT tokensmax) { - static const TOKEN_NAME token_names[] = + static const TOKEN_INFO token_info[] = { - {"and", TOKEN_AND}, - {"false", TOKEN_FALSE}, - {"icmp", TOKEN_ICMP}, - {"icmp.Body", TOKEN_ICMP_BODY}, - {"icmp.Checksum", TOKEN_ICMP_CHECKSUM}, - {"icmp.Code", TOKEN_ICMP_CODE}, - {"icmp.Type", TOKEN_ICMP_TYPE}, - {"icmpv6", TOKEN_ICMPV6}, - {"icmpv6.Body", TOKEN_ICMPV6_BODY}, - {"icmpv6.Checksum", TOKEN_ICMPV6_CHECKSUM}, - {"icmpv6.Code", TOKEN_ICMPV6_CODE}, - {"icmpv6.Type", TOKEN_ICMPV6_TYPE}, - {"ifIdx", TOKEN_IF_IDX}, - {"impostor", TOKEN_IMPOSTOR}, - {"inbound", TOKEN_INBOUND}, - {"ip", TOKEN_IP}, - {"ip.Checksum", TOKEN_IP_CHECKSUM}, - {"ip.DF", TOKEN_IP_DF}, - {"ip.DstAddr", TOKEN_IP_DST_ADDR}, - {"ip.FragOff", TOKEN_IP_FRAG_OFF}, - {"ip.HdrLength", TOKEN_IP_HDR_LENGTH}, - {"ip.Id", TOKEN_IP_ID}, - {"ip.Length", TOKEN_IP_LENGTH}, - {"ip.MF", TOKEN_IP_MF}, - {"ip.Protocol", TOKEN_IP_PROTOCOL}, - {"ip.SrcAddr", TOKEN_IP_SRC_ADDR}, - {"ip.TOS", TOKEN_IP_TOS}, - {"ip.TTL", TOKEN_IP_TTL}, - {"ipv6", TOKEN_IPV6}, - {"ipv6.DstAddr", TOKEN_IPV6_DST_ADDR}, - {"ipv6.FlowLabel", TOKEN_IPV6_FLOW_LABEL}, - {"ipv6.HopLimit", TOKEN_IPV6_HOP_LIMIT}, - {"ipv6.Length", TOKEN_IPV6_LENGTH}, - {"ipv6.NextHdr", TOKEN_IPV6_NEXT_HDR}, - {"ipv6.SrcAddr", TOKEN_IPV6_SRC_ADDR}, - {"ipv6.TrafficClass", TOKEN_IPV6_TRAFFIC_CLASS}, - {"loopback", TOKEN_LOOPBACK}, - {"not", TOKEN_NOT}, - {"or", TOKEN_OR}, - {"outbound", TOKEN_OUTBOUND}, - {"subIfIdx", TOKEN_SUB_IF_IDX}, - {"tcp", TOKEN_TCP}, - {"tcp.Ack", TOKEN_TCP_ACK}, - {"tcp.AckNum", TOKEN_TCP_ACK_NUM}, - {"tcp.Checksum", TOKEN_TCP_CHECKSUM}, - {"tcp.DstPort", TOKEN_TCP_DST_PORT}, - {"tcp.Fin", TOKEN_TCP_FIN}, - {"tcp.HdrLength", TOKEN_TCP_HDR_LENGTH}, - {"tcp.PayloadLength", TOKEN_TCP_PAYLOAD_LENGTH}, - {"tcp.Psh", TOKEN_TCP_PSH}, - {"tcp.Rst", TOKEN_TCP_RST}, - {"tcp.SeqNum", TOKEN_TCP_SEQ_NUM}, - {"tcp.SrcPort", TOKEN_TCP_SRC_PORT}, - {"tcp.Syn", TOKEN_TCP_SYN}, - {"tcp.Urg", TOKEN_TCP_URG}, - {"tcp.UrgPtr", TOKEN_TCP_URG_PTR}, - {"tcp.Window", TOKEN_TCP_WINDOW}, - {"true", TOKEN_TRUE}, - {"udp", TOKEN_UDP}, - {"udp.Checksum", TOKEN_UDP_CHECKSUM}, - {"udp.DstPort", TOKEN_UDP_DST_PORT}, - {"udp.Length", TOKEN_UDP_LENGTH}, - {"udp.PayloadLength", TOKEN_UDP_PAYLOAD_LENGTH}, - {"udp.SrcPort", TOKEN_UDP_SRC_PORT}, + {"ACCEPT", TOKEN_EVENT_ACCEPT, L___S_}, + {"BIND", TOKEN_EVENT_BIND, L___S_}, + {"CLOSE", TOKEN_EVENT_CLOSE, L____R}, + {"CONNECT", TOKEN_EVENT_CONNECT, L___S_}, + {"DELETED", TOKEN_EVENT_DELETED, L__F__}, + {"ESTABLISHED", TOKEN_EVENT_ESTABLISHED, L__F_R}, + {"FLOW", TOKEN_FLOW, L____R}, + {"LISTEN", TOKEN_EVENT_LISTEN, L___S_}, + {"NETWORK", TOKEN_NETWORK, L____R}, + {"NETWORK_FORWARD", TOKEN_NETWORK_FORWARD, L____R}, + {"OPEN", TOKEN_EVENT_OPEN, L____R}, + {"PACKET", TOKEN_EVENT_PACKET, LNM___}, + {"REFLECT", TOKEN_REFLECT, L____R}, + {"SOCKET", TOKEN_SOCKET, L____R}, + {"and", TOKEN_AND, LNMFSR}, + {"event", TOKEN_EVENT, LNMFSR}, + {"false", TOKEN_FALSE, LNMFSR}, + {"icmp", TOKEN_ICMP, LNMFS_}, + {"icmp.Body", TOKEN_ICMP_BODY, LNM___}, + {"icmp.Checksum", TOKEN_ICMP_CHECKSUM, LNM___}, + {"icmp.Code", TOKEN_ICMP_CODE, LNM___}, + {"icmp.Type", TOKEN_ICMP_TYPE, LNM___}, + {"icmpv6", TOKEN_ICMPV6, LNMFS_}, + {"icmpv6.Body", TOKEN_ICMPV6_BODY, LNM___}, + {"icmpv6.Checksum", TOKEN_ICMPV6_CHECKSUM, LNM___}, + {"icmpv6.Code", TOKEN_ICMPV6_CODE, LNM___}, + {"icmpv6.Type", TOKEN_ICMPV6_TYPE, LNM___}, + {"ifIdx", TOKEN_IF_IDX, LNM___}, + {"impostor", TOKEN_IMPOSTOR, LNM___}, + {"inbound", TOKEN_INBOUND, LN_F__}, + {"ip", TOKEN_IP, LNMFS_}, + {"ip.Checksum", TOKEN_IP_CHECKSUM, LNM___}, + {"ip.DF", TOKEN_IP_DF, LNM___}, + {"ip.DstAddr", TOKEN_IP_DST_ADDR, LNM___}, + {"ip.FragOff", TOKEN_IP_FRAG_OFF, LNM___}, + {"ip.HdrLength", TOKEN_IP_HDR_LENGTH, LNM___}, + {"ip.Id", TOKEN_IP_ID, LNM___}, + {"ip.Length", TOKEN_IP_LENGTH, LNM___}, + {"ip.MF", TOKEN_IP_MF, LNM___}, + {"ip.Protocol", TOKEN_IP_PROTOCOL, LNM___}, + {"ip.SrcAddr", TOKEN_IP_SRC_ADDR, LNM___}, + {"ip.TOS", TOKEN_IP_TOS, LNM___}, + {"ip.TTL", TOKEN_IP_TTL, LNM___}, + {"ipv6", TOKEN_IPV6, LNMFS_}, + {"ipv6.DstAddr", TOKEN_IPV6_DST_ADDR, LNM___}, + {"ipv6.FlowLabel", TOKEN_IPV6_FLOW_LABEL, LNM___}, + {"ipv6.HopLimit", TOKEN_IPV6_HOP_LIMIT, LNM___}, + {"ipv6.Length", TOKEN_IPV6_LENGTH, LNM___}, + {"ipv6.NextHdr", TOKEN_IPV6_NEXT_HDR, LNM___}, + {"ipv6.SrcAddr", TOKEN_IPV6_SRC_ADDR, LNM___}, + {"ipv6.TrafficClass", TOKEN_IPV6_TRAFFIC_CLASS, LNM___}, + {"layer", TOKEN_LAYER, L____R}, + {"localAddr", TOKEN_LOCAL_ADDR, LN_FS_}, + {"localPort", TOKEN_LOCAL_PORT, LN_FS_}, + {"loopback", TOKEN_LOOPBACK, LN_FS_}, + {"not", TOKEN_NOT, LNMFSR}, + {"or", TOKEN_OR, LNMFSR}, + {"outbound", TOKEN_OUTBOUND, LN_F__}, + {"packet", TOKEN_PACKET, LNM___}, + {"packet16", TOKEN_PACKET16, LNM___}, + {"packet32", TOKEN_PACKET32, LNM___}, + {"processId", TOKEN_PROCESS_ID, L__FSR}, + {"protocol", TOKEN_PROTOCOL, LN_FS_}, + {"random16", TOKEN_RANDOM16, LNM___}, + {"random32", TOKEN_RANDOM32, LNM___}, + {"random8", TOKEN_RANDOM8, LNM___}, + {"remoteAddr", TOKEN_REMOTE_ADDR, LN_FS_}, + {"remotePort", TOKEN_REMOTE_PORT, LN_FS_}, + {"subIfIdx", TOKEN_SUB_IF_IDX, LNM___}, + {"tcp", TOKEN_TCP, LNMFS_}, + {"tcp.Ack", TOKEN_TCP_ACK, LNM___}, + {"tcp.AckNum", TOKEN_TCP_ACK_NUM, LNM___}, + {"tcp.Checksum", TOKEN_TCP_CHECKSUM, LNM___}, + {"tcp.DstPort", TOKEN_TCP_DST_PORT, LNM___}, + {"tcp.Fin", TOKEN_TCP_FIN, LNM___}, + {"tcp.HdrLength", TOKEN_TCP_HDR_LENGTH, LNM___}, + {"tcp.Payload", TOKEN_TCP_PAYLOAD, LNM___}, + {"tcp.Payload16", TOKEN_TCP_PAYLOAD16, LNM___}, + {"tcp.Payload32", TOKEN_TCP_PAYLOAD32, LNM___}, + {"tcp.PayloadLength", TOKEN_TCP_PAYLOAD_LENGTH, LNM___}, + {"tcp.Psh", TOKEN_TCP_PSH, LNM___}, + {"tcp.Rst", TOKEN_TCP_RST, LNM___}, + {"tcp.SeqNum", TOKEN_TCP_SEQ_NUM, LNM___}, + {"tcp.SrcPort", TOKEN_TCP_SRC_PORT, LNM___}, + {"tcp.Syn", TOKEN_TCP_SYN, LNM___}, + {"tcp.Urg", TOKEN_TCP_URG, LNM___}, + {"tcp.UrgPtr", TOKEN_TCP_URG_PTR, LNM___}, + {"tcp.Window", TOKEN_TCP_WINDOW, LNM___}, + {"true", TOKEN_TRUE, LNMFSR}, + {"udp", TOKEN_UDP, LNMFS_}, + {"udp.Checksum", TOKEN_UDP_CHECKSUM, LNM___}, + {"udp.DstPort", TOKEN_UDP_DST_PORT, LNM___}, + {"udp.Length", TOKEN_UDP_LENGTH, LNM___}, + {"udp.Payload", TOKEN_UDP_PAYLOAD, LNM___}, + {"udp.Payload16", TOKEN_UDP_PAYLOAD16, LNM___}, + {"udp.Payload32", TOKEN_UDP_PAYLOAD32, LNM___}, + {"udp.PayloadLength", TOKEN_UDP_PAYLOAD_LENGTH, LNM___}, + {"udp.SrcPort", TOKEN_UDP_SRC_PORT, LNM___}, + {"zero", TOKEN_ZERO, LNMFSR}, }; - TOKEN_NAME *result; + TOKEN_INFO *result; char c; char token[TOKEN_MAXLEN]; UINT i = 0, j; @@ -888,6 +683,15 @@ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, case ')': tokens[tp++].kind = TOKEN_CLOSE; continue; + case '[': + tokens[tp++].kind = TOKEN_SQUARE_OPEN; + continue; + case ']': + tokens[tp++].kind = TOKEN_SQUARE_CLOSE; + continue; + case '-': + tokens[tp++].kind = TOKEN_MINUS; + continue; case '!': if (filter[i] == '=') { @@ -956,12 +760,13 @@ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, break; } token[0] = c; - if (WinDivertIsAlNum(c) || c == '.' || c == ':') + if (WinDivertIsAlNum(c) || c == '.' || c == ':' || c == '_') { UINT32 num; char *end; for (j = 1; j < TOKEN_MAXLEN && (WinDivertIsAlNum(filter[i]) || - filter[i] == '.' || filter[i] == ':'); j++, i++) + filter[i] == '.' || filter[i] == ':' || filter[i] == '_'); + j++, i++) { token[j] = filter[i]; } @@ -982,38 +787,48 @@ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, } // Check for symbol: - result = WinDivertTokenLookup((PTOKEN_NAME)token_names, - sizeof(token_names) / sizeof(TOKEN_NAME), token); + result = WinDivertTokenLookup((PTOKEN_INFO)token_info, + sizeof(token_info) / sizeof(TOKEN_INFO), token); if (result != NULL) { - switch (layer) + if ((result->flags & (1 << layer)) == 0) { - case WINDIVERT_LAYER_NETWORK_FORWARD: - if (result->kind == TOKEN_INBOUND || - result->kind == TOKEN_OUTBOUND) - { - return MAKE_ERROR( - WINDIVERT_ERROR_BAD_TOKEN_FOR_LAYER, i-j); - } - break; - default: - break; + return MAKE_ERROR(WINDIVERT_ERROR_BAD_TOKEN_FOR_LAYER, i-j); + } + if (WinDivertExpandMacro(result->kind, layer, + &tokens[tp].val[0])) + { + tokens[tp].kind = TOKEN_NUMBER; + } + else + { + tokens[tp].kind = result->kind; } - tokens[tp++].kind = result->kind; - continue; - } - - // Check for base 10 number: - if (WinDivertAToI(token, &end, &num) && *end == '\0') - { - tokens[tp].kind = TOKEN_NUMBER; - tokens[tp].val[0] = num; tp++; continue; } + // Check for base 10 number: + if (WinDivertAToI(token, &end, &num)) + { + BOOL b = (*end == 'b' && *(end+1) == '\0'); + if (*end == '\0' || b) + { + tokens[tp].kind = TOKEN_NUMBER; + tokens[tp].val[0] = num; + tp++; + if (b) + { + tokens[tp].kind = TOKEN_BYTES; + tp++; + } + continue; + } + } + // Check for base 16 number: - if (WinDivertAToX(token, &end, &num) && *end == '\0') + if (token[0] == '0' && token[1] == 'x' && + WinDivertAToX(token, &end, &num) && *end == '\0') { tokens[tp].kind = TOKEN_NUMBER; tokens[tp].val[0] = num; @@ -1023,6 +838,7 @@ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, // Check for IPv4 address: if (WinDivertHelperParseIPv4Address(token, tokens[tp].val)) { + tokens[tp].val[1] = 0x0000FFFF; tokens[tp].kind = TOKEN_NUMBER; tp++; continue; @@ -1032,16 +848,6 @@ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, SetLastError(0); if (WinDivertHelperParseIPv6Address(token, tokens[tp].val)) { - // Work-around the different word orderings between the - // DLL vs SYS. - UINT32 tmp; - tmp = tokens[tp].val[0]; - tokens[tp].val[0] = tokens[tp].val[3]; - tokens[tp].val[3] = tmp; - tmp = tokens[tp].val[1]; - tokens[tp].val[1] = tokens[tp].val[2]; - tokens[tp].val[2] = tmp; - tokens[tp].kind = TOKEN_NUMBER; tp++; continue; @@ -1056,26 +862,10 @@ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, } } -/* - * Pool allocation. - */ -static void *WinDivertAlloc(PPOOL pool, UINT size) -{ - void *ptr; - if (pool->offset + size >= sizeof(pool->memory)) - { - pool->error = MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); - return NULL; - } - ptr = pool->memory + pool->offset; - pool->offset += size; - return ptr; -}; - /* * Construct a variable/field. */ -static PEXPR WinDivertMakeVar(PPOOL pool, KIND kind) +static PEXPR WinDivertMakeVar(KIND kind, PERROR error) { // NOTE: must be in order of kind. static const EXPR vars[] = @@ -1133,6 +923,11 @@ static PEXPR WinDivertMakeVar(PPOOL pool, KIND kind) {{{0}}, TOKEN_UDP_LENGTH}, {{{0}}, TOKEN_UDP_PAYLOAD_LENGTH}, {{{0}}, TOKEN_UDP_SRC_PORT}, + {{{0}}, TOKEN_ZERO}, + {{{0}}, TOKEN_EVENT}, + {{{0}}, TOKEN_RANDOM8}, + {{{0}}, TOKEN_RANDOM16}, + {{{0}}, TOKEN_RANDOM32}, {{{0}}, TOKEN_TRUE}, {{{0}}, TOKEN_FALSE}, {{{0}}, TOKEN_INBOUND}, @@ -1140,7 +935,14 @@ static PEXPR WinDivertMakeVar(PPOOL pool, KIND kind) {{{0}}, TOKEN_IF_IDX}, {{{0}}, TOKEN_SUB_IF_IDX}, {{{0}}, TOKEN_LOOPBACK}, - {{{0}}, TOKEN_IMPOSTOR} + {{{0}}, TOKEN_IMPOSTOR}, + {{{0}}, TOKEN_PROCESS_ID}, + {{{0}}, TOKEN_LOCAL_ADDR}, + {{{0}}, TOKEN_REMOTE_ADDR}, + {{{0}}, TOKEN_LOCAL_PORT}, + {{{0}}, TOKEN_REMOTE_PORT}, + {{{0}}, TOKEN_PROTOCOL}, + {{{0}}, TOKEN_LAYER}, }; // Binary search: @@ -1160,14 +962,31 @@ static PEXPR WinDivertMakeVar(PPOOL pool, KIND kind) } return (PEXPR)(vars + mid); } - pool->error = MAKE_ERROR(WINDIVERT_ERROR_ASSERTION_FAILED, 0); + *error = MAKE_ERROR(WINDIVERT_ERROR_ASSERTION_FAILED, 0); return NULL; } +/* + * Construct array varable. + */ +static PEXPR WinDivertMakeArrayVar(HANDLE pool, KIND kind, INT idx, + PERROR error) +{ + PEXPR var = (PEXPR)HeapAlloc(pool, HEAP_ZERO_MEMORY, sizeof(EXPR)); + if (var == NULL) + { + *error = MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); + return NULL; + } + var->kind = kind; + var->val[0] = (UINT32)idx; + return var; +} + /* * Construct zero. */ -static PEXPR WinDivertMakeZero(PPOOL pool) +static PEXPR WinDivertMakeZero(void) { static const EXPR zero = {{{0, 0, 0, 0}}, TOKEN_NUMBER}; return (PEXPR)&zero; @@ -1176,44 +995,43 @@ static PEXPR WinDivertMakeZero(PPOOL pool) /* * Construct a number. */ -static PEXPR WinDivertMakeNumber(PPOOL pool, TOKEN *tok) +static PEXPR WinDivertMakeNumber(HANDLE pool, UINT32 *val, PERROR error) { - PEXPR expr; - if (tok->kind != TOKEN_NUMBER) + if (val[0] == 0 && val[1] == 0 && val[2] == 0 && val[3] == 0) { - pool->error = MAKE_ERROR(WINDIVERT_ERROR_ASSERTION_FAILED, 0); - return NULL; + return WinDivertMakeZero(); } - expr = (PEXPR)WinDivertAlloc(pool, sizeof(EXPR)); + PEXPR expr = (PEXPR)HeapAlloc(pool, HEAP_ZERO_MEMORY, sizeof(EXPR)); if (expr == NULL) { + *error = MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); return NULL; } - memset(expr, 0, sizeof(EXPR)); expr->kind = TOKEN_NUMBER; - expr->val[0] = tok->val[0]; - expr->val[1] = tok->val[1]; - expr->val[2] = tok->val[2]; - expr->val[3] = tok->val[3]; + expr->val[0] = val[0]; + expr->val[1] = val[1]; + expr->val[2] = val[2]; + expr->val[3] = val[3]; return expr; } /* * Construct a binary operator. */ -static PEXPR WinDivertMakeBinOp(PPOOL pool, KIND kind, PEXPR arg0, PEXPR arg1) +static PEXPR WinDivertMakeBinOp(HANDLE pool, KIND kind, PEXPR arg0, PEXPR arg1, + PERROR error) { PEXPR expr; if (arg0 == NULL || arg1 == NULL) { return NULL; } - expr = (PEXPR)WinDivertAlloc(pool, sizeof(EXPR)); + expr = (PEXPR)HeapAlloc(pool, HEAP_ZERO_MEMORY, sizeof(EXPR)); if (expr == NULL) { + *error = MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); return NULL; } - memset(expr, 0, sizeof(EXPR)); expr->kind = kind; expr->arg[0] = arg0; expr->arg[1] = arg1; @@ -1223,15 +1041,15 @@ static PEXPR WinDivertMakeBinOp(PPOOL pool, KIND kind, PEXPR arg0, PEXPR arg1) /* * Construct an if-then-else. */ -static PEXPR WinDivertMakeIfThenElse(PPOOL pool, PEXPR cond, PEXPR th, - PEXPR el) +static PEXPR WinDivertMakeIfThenElse(HANDLE pool, PEXPR cond, PEXPR th, + PEXPR el, PERROR error) { - PEXPR expr = (PEXPR)WinDivertAlloc(pool, sizeof(EXPR)); + PEXPR expr = (PEXPR)HeapAlloc(pool, HEAP_ZERO_MEMORY, sizeof(EXPR)); if (expr == NULL) { + *error = MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); return NULL; } - memset(expr, 0, sizeof(EXPR)); expr->kind = TOKEN_QUESTION; expr->arg[0] = cond; expr->arg[1] = th; @@ -1242,11 +1060,12 @@ static PEXPR WinDivertMakeIfThenElse(PPOOL pool, PEXPR cond, PEXPR th, /* * Parse a filter test. */ -static PEXPR WinDivertParseTest(PPOOL pool, TOKEN *toks, UINT *i) +static PEXPR WinDivertParseTest(HANDLE pool, TOKEN *toks, UINT *i, PERROR error) { PEXPR var, val; KIND kind; - BOOL not = FALSE; + BOOL not = FALSE, neg; + UINT idx, size; while (toks[*i].kind == TOKEN_NOT) { not = !not; @@ -1254,6 +1073,11 @@ static PEXPR WinDivertParseTest(PPOOL pool, TOKEN *toks, UINT *i) } switch (toks[*i].kind) { + case TOKEN_ZERO: + case TOKEN_EVENT: + case TOKEN_RANDOM8: + case TOKEN_RANDOM16: + case TOKEN_RANDOM32: case TOKEN_TRUE: case TOKEN_FALSE: case TOKEN_OUTBOUND: @@ -1268,6 +1092,13 @@ static PEXPR WinDivertParseTest(PPOOL pool, TOKEN *toks, UINT *i) case TOKEN_ICMPV6: case TOKEN_TCP: case TOKEN_UDP: + case TOKEN_PROCESS_ID: + case TOKEN_LOCAL_ADDR: + case TOKEN_REMOTE_ADDR: + case TOKEN_LOCAL_PORT: + case TOKEN_REMOTE_PORT: + case TOKEN_PROTOCOL: + case TOKEN_LAYER: case TOKEN_IP_HDR_LENGTH: case TOKEN_IP_TOS: case TOKEN_IP_LENGTH: @@ -1315,14 +1146,80 @@ static PEXPR WinDivertParseTest(PPOOL pool, TOKEN *toks, UINT *i) case TOKEN_UDP_LENGTH: case TOKEN_UDP_CHECKSUM: case TOKEN_UDP_PAYLOAD_LENGTH: + var = WinDivertMakeVar(toks[*i].kind, error); + *i = *i + 1; + break; + case TOKEN_PACKET: + case TOKEN_TCP_PAYLOAD: + case TOKEN_UDP_PAYLOAD: + size = sizeof(UINT8); + goto array; + case TOKEN_PACKET16: + case TOKEN_TCP_PAYLOAD16: + case TOKEN_UDP_PAYLOAD16: + size = sizeof(UINT16); + goto array; + case TOKEN_PACKET32: + case TOKEN_TCP_PAYLOAD32: + case TOKEN_UDP_PAYLOAD32: + size = sizeof(UINT32); + array: + kind = toks[*i].kind; + *i = *i + 1; + if (toks[*i].kind != TOKEN_SQUARE_OPEN) + { + goto unexpected_token; + } + *i = *i + 1; + neg = FALSE; + if (toks[*i].kind == TOKEN_MINUS) + { + neg = TRUE; + *i = *i + 1; + } + if (toks[*i].kind != TOKEN_NUMBER) + { + goto unexpected_token; + } + if (toks[*i].val[3] != 0 || toks[*i].val[2] != 0 || + toks[*i].val[1] != 0 || toks[*i].val[0] > UINT16_MAX) + { + *error = MAKE_ERROR(WINDIVERT_ERROR_INDEX_OOB, toks[*i].pos); + return NULL; + } + idx = toks[*i].val[0]; + *i = *i + 1; + if (toks[*i].kind == TOKEN_BYTES) + { + *i = *i + 1; + } + else + { + idx *= size; + } + if ((!neg && idx > UINT16_MAX - size) || + (neg && idx > UINT16_MAX) || (neg && idx < size)) + { + *error = MAKE_ERROR(WINDIVERT_ERROR_INDEX_OOB, toks[*i].pos); + return NULL; + } + var = WinDivertMakeArrayVar(pool, kind, (neg? -(INT)idx: (INT)idx), + error); + if (var == NULL) + { + return NULL; + } + if (toks[*i].kind != TOKEN_SQUARE_CLOSE) + { + goto unexpected_token; + } + *i = *i + 1; break; default: - pool->error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, - toks[*i].pos); + unexpected_token: + *error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, toks[*i].pos); return NULL; } - var = WinDivertMakeVar(pool, toks[*i].kind); - *i = *i + 1; switch (toks[*i].kind) { case TOKEN_EQ: @@ -1335,7 +1232,7 @@ static PEXPR WinDivertParseTest(PPOOL pool, TOKEN *toks, UINT *i) break; default: return WinDivertMakeBinOp(pool, (not? TOKEN_EQ: TOKEN_NEQ), var, - WinDivertMakeZero(pool)); + WinDivertMakeZero(), error); } if (not) { @@ -1366,31 +1263,31 @@ static PEXPR WinDivertParseTest(PPOOL pool, TOKEN *toks, UINT *i) *i = *i + 1; if (toks[*i].kind != TOKEN_NUMBER) { - pool->error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, - toks[*i].pos); + *error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, toks[*i].pos); return NULL; } - val = WinDivertMakeNumber(pool, toks + *i); + val = WinDivertMakeNumber(pool, toks[*i].val, error); *i = *i + 1; - return WinDivertMakeBinOp(pool, kind, var, val); + return WinDivertMakeBinOp(pool, kind, var, val, error); } /* * Parse a filter argument to an (and) (or) operator. */ -static PEXPR WinDivertParseArg(PPOOL pool, TOKEN *toks, UINT *i, INT depth) +static PEXPR WinDivertParseArg(HANDLE pool, TOKEN *toks, UINT *i, INT depth, + PERROR error) { PEXPR arg, th, el; if (depth-- < 0) { - pool->error = MAKE_ERROR(WINDIVERT_ERROR_TOO_DEEP, toks[*i].pos); + *error = MAKE_ERROR(WINDIVERT_ERROR_TOO_DEEP, toks[*i].pos); return NULL; } switch (toks[*i].kind) { case TOKEN_OPEN: *i = *i + 1; - arg = WinDivertParseFilter(pool, toks, i, depth, FALSE); + arg = WinDivertParseFilter(pool, toks, i, depth, FALSE, error); if (toks[*i].kind == TOKEN_CLOSE) { *i = *i + 1; @@ -1399,57 +1296,56 @@ static PEXPR WinDivertParseArg(PPOOL pool, TOKEN *toks, UINT *i, INT depth) if (toks[*i].kind == TOKEN_QUESTION) { *i = *i + 1; - th = WinDivertParseFilter(pool, toks, i, depth, FALSE); + th = WinDivertParseFilter(pool, toks, i, depth, FALSE, error); if (th == NULL) { return NULL; } if (toks[*i].kind != TOKEN_COLON) { - pool->error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, + *error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, toks[*i].pos); return NULL; } *i = *i + 1; - el = WinDivertParseFilter(pool, toks, i, depth, FALSE); + el = WinDivertParseFilter(pool, toks, i, depth, FALSE, error); if (el == NULL) { return NULL; } if (toks[*i].kind != TOKEN_CLOSE) { - pool->error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, + *error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, toks[*i].pos); return NULL; } *i = *i + 1; - arg = WinDivertMakeIfThenElse(pool, arg, th, el); + arg = WinDivertMakeIfThenElse(pool, arg, th, el, error); return arg; } - pool->error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, - toks[*i].pos); + *error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, toks[*i].pos); return NULL; default: - return WinDivertParseTest(pool, toks, i); + return WinDivertParseTest(pool, toks, i, error); } } /* * Parse the filter into an expression object. */ -static PEXPR WinDivertParseFilter(PPOOL pool, TOKEN *toks, UINT *i, INT depth, - BOOL and) +static PEXPR WinDivertParseFilter(HANDLE pool, TOKEN *toks, UINT *i, INT depth, + BOOL and, PERROR error) { PEXPR expr, arg; if (depth-- < 0) { - pool->error = MAKE_ERROR(WINDIVERT_ERROR_TOO_DEEP, toks[*i].pos); + *error = MAKE_ERROR(WINDIVERT_ERROR_TOO_DEEP, toks[*i].pos); return NULL; } if (and) - expr = WinDivertParseArg(pool, toks, i, depth); + expr = WinDivertParseArg(pool, toks, i, depth, error); else - expr = WinDivertParseFilter(pool, toks, i, depth, TRUE); + expr = WinDivertParseFilter(pool, toks, i, depth, TRUE, error); do { if (expr == NULL) @@ -1460,13 +1356,13 @@ static PEXPR WinDivertParseFilter(PPOOL pool, TOKEN *toks, UINT *i, INT depth, { case TOKEN_AND: *i = *i + 1; - arg = WinDivertParseArg(pool, toks, i, depth); - expr = WinDivertMakeBinOp(pool, TOKEN_AND, expr, arg); + arg = WinDivertParseArg(pool, toks, i, depth, error); + expr = WinDivertMakeBinOp(pool, TOKEN_AND, expr, arg, error); continue; case TOKEN_OR: *i = *i + 1; - arg = WinDivertParseFilter(pool, toks, i, depth, TRUE); - expr = WinDivertMakeBinOp(pool, TOKEN_OR, expr, arg); + arg = WinDivertParseFilter(pool, toks, i, depth, TRUE, error); + expr = WinDivertMakeBinOp(pool, TOKEN_OR, expr, arg, error); continue; default: return expr; @@ -1487,12 +1383,21 @@ static BOOL WinDivertEvalTest(PEXPR test, BOOL *res) UINT32 lb, ub; switch (var->kind) { + case TOKEN_ZERO: + lb = ub = 0; + break; case TOKEN_TRUE: lb = ub = 1; break; case TOKEN_FALSE: lb = ub = 0; break; + case TOKEN_LAYER: + lb = 0; ub = WINDIVERT_LAYER_MAX; + break; + case TOKEN_EVENT: + lb = 0; ub = WINDIVERT_EVENT_MAX; + break; case TOKEN_INBOUND: case TOKEN_OUTBOUND: case TOKEN_IP: @@ -1524,6 +1429,11 @@ static BOOL WinDivertEvalTest(PEXPR test, BOOL *res) case TOKEN_ICMP_CODE: case TOKEN_ICMPV6_TYPE: case TOKEN_ICMPV6_CODE: + case TOKEN_PROTOCOL: + case TOKEN_PACKET: + case TOKEN_TCP_PAYLOAD: + case TOKEN_UDP_PAYLOAD: + case TOKEN_RANDOM8: lb = 0; ub = 0xFF; break; case TOKEN_IP_FRAG_OFF: @@ -1547,13 +1457,23 @@ static BOOL WinDivertEvalTest(PEXPR test, BOOL *res) case TOKEN_UDP_LENGTH: case TOKEN_UDP_CHECKSUM: case TOKEN_UDP_PAYLOAD_LENGTH: + case TOKEN_LOCAL_PORT: + case TOKEN_REMOTE_PORT: + case TOKEN_PACKET16: + case TOKEN_TCP_PAYLOAD16: + case TOKEN_UDP_PAYLOAD16: + case TOKEN_RANDOM16: lb = 0; ub = 0xFFFF; break; case TOKEN_IPV6_FLOW_LABEL: lb = 0; ub = 0x000FFFFF; break; + case TOKEN_IP_SRC_ADDR: + case TOKEN_IP_DST_ADDR: case TOKEN_IPV6_SRC_ADDR: case TOKEN_IPV6_DST_ADDR: + case TOKEN_LOCAL_ADDR: + case TOKEN_REMOTE_ADDR: return FALSE; default: lb = 0; ub = 0xFFFFFFFF; @@ -1689,8 +1609,9 @@ static INT16 WinDivertFlattenExpr(PEXPR expr, INT16 *label, INT16 succ, * Emit a test. */ static void WinDivertEmitTest(PEXPR test, UINT16 offset, - windivert_ioctl_filter_t object) + PWINDIVERT_FILTER object) { + BOOL big; PEXPR var = test->arg[0], val = test->arg[1]; switch (test->kind) { @@ -1715,8 +1636,61 @@ static void WinDivertEmitTest(PEXPR test, UINT16 offset, default: return; } + big = FALSE; + object->arg[1] = object->arg[2] = object->arg[3] = 0; switch (var->kind) { + case TOKEN_ZERO: + object->field = WINDIVERT_FILTER_FIELD_ZERO; + break; + case TOKEN_EVENT: + object->field = WINDIVERT_FILTER_FIELD_EVENT; + break; + case TOKEN_RANDOM8: + object->field = WINDIVERT_FILTER_FIELD_RANDOM8; + break; + case TOKEN_RANDOM16: + object->field = WINDIVERT_FILTER_FIELD_RANDOM16; + break; + case TOKEN_RANDOM32: + object->field = WINDIVERT_FILTER_FIELD_RANDOM32; + break; + case TOKEN_PACKET: + object->field = WINDIVERT_FILTER_FIELD_PACKET; + object->arg[1] = var->val[0]; + break; + case TOKEN_PACKET16: + object->field = WINDIVERT_FILTER_FIELD_PACKET16; + object->arg[1] = var->val[0]; + break; + case TOKEN_PACKET32: + object->field = WINDIVERT_FILTER_FIELD_PACKET32; + object->arg[1] = var->val[0]; + break; + case TOKEN_TCP_PAYLOAD: + object->field = WINDIVERT_FILTER_FIELD_TCP_PAYLOAD; + object->arg[1] = var->val[0]; + break; + case TOKEN_TCP_PAYLOAD16: + object->field = WINDIVERT_FILTER_FIELD_TCP_PAYLOAD16; + object->arg[1] = var->val[0]; + break; + case TOKEN_TCP_PAYLOAD32: + object->field = WINDIVERT_FILTER_FIELD_TCP_PAYLOAD32; + object->arg[1] = var->val[0]; + break; + case TOKEN_UDP_PAYLOAD: + object->field = WINDIVERT_FILTER_FIELD_UDP_PAYLOAD; + object->arg[1] = var->val[0]; + break; + case TOKEN_UDP_PAYLOAD16: + object->field = WINDIVERT_FILTER_FIELD_UDP_PAYLOAD16; + object->arg[1] = var->val[0]; + break; + case TOKEN_UDP_PAYLOAD32: + object->field = WINDIVERT_FILTER_FIELD_UDP_PAYLOAD32; + object->arg[1] = var->val[0]; + break; case TOKEN_OUTBOUND: object->field = WINDIVERT_FILTER_FIELD_OUTBOUND; break; @@ -1735,6 +1709,29 @@ static void WinDivertEmitTest(PEXPR test, UINT16 offset, case TOKEN_IMPOSTOR: object->field = WINDIVERT_FILTER_FIELD_IMPOSTOR; break; + case TOKEN_PROCESS_ID: + object->field = WINDIVERT_FILTER_FIELD_PROCESSID; + break; + case TOKEN_LOCAL_ADDR: + object->field = WINDIVERT_FILTER_FIELD_LOCALADDR; + big = TRUE; + break; + case TOKEN_REMOTE_ADDR: + object->field = WINDIVERT_FILTER_FIELD_REMOTEADDR; + big = TRUE; + break; + case TOKEN_LOCAL_PORT: + object->field = WINDIVERT_FILTER_FIELD_LOCALPORT; + break; + case TOKEN_REMOTE_PORT: + object->field = WINDIVERT_FILTER_FIELD_REMOTEPORT; + break; + case TOKEN_PROTOCOL: + object->field = WINDIVERT_FILTER_FIELD_PROTOCOL; + break; + case TOKEN_LAYER: + object->field = WINDIVERT_FILTER_FIELD_LAYER; + break; case TOKEN_IP: object->field = WINDIVERT_FILTER_FIELD_IP; break; @@ -1785,9 +1782,11 @@ static void WinDivertEmitTest(PEXPR test, UINT16 offset, break; case TOKEN_IP_SRC_ADDR: object->field = WINDIVERT_FILTER_FIELD_IP_SRCADDR; + big = TRUE; break; case TOKEN_IP_DST_ADDR: object->field = WINDIVERT_FILTER_FIELD_IP_DSTADDR; + big = TRUE; break; case TOKEN_IPV6_TRAFFIC_CLASS: object->field = WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS; @@ -1806,9 +1805,11 @@ static void WinDivertEmitTest(PEXPR test, UINT16 offset, break; case TOKEN_IPV6_SRC_ADDR: object->field = WINDIVERT_FILTER_FIELD_IPV6_SRCADDR; + big = TRUE; break; case TOKEN_IPV6_DST_ADDR: object->field = WINDIVERT_FILTER_FIELD_IPV6_DSTADDR; + big = TRUE; break; case TOKEN_ICMP_TYPE: object->field = WINDIVERT_FILTER_FIELD_ICMP_TYPE; @@ -1898,9 +1899,12 @@ static void WinDivertEmitTest(PEXPR test, UINT16 offset, return; } object->arg[0] = val->val[0]; - object->arg[1] = val->val[1]; - object->arg[2] = val->val[2]; - object->arg[3] = val->val[3]; + if (big) + { + object->arg[1] = val->val[1]; + object->arg[2] = val->val[2]; + object->arg[3] = val->val[3]; + } switch (test->succ) { case WINDIVERT_FILTER_RESULT_ACCEPT: @@ -1928,7 +1932,7 @@ static void WinDivertEmitTest(PEXPR test, UINT16 offset, * Emit a filter object. */ static void WinDivertEmitFilter(PEXPR *stack, UINT len, UINT16 label, - windivert_ioctl_filter_t object, UINT *obj_len) + PWINDIVERT_FILTER object, UINT *obj_len) { UINT i; switch (label) @@ -1936,12 +1940,11 @@ static void WinDivertEmitFilter(PEXPR *stack, UINT len, UINT16 label, case WINDIVERT_FILTER_RESULT_ACCEPT: case WINDIVERT_FILTER_RESULT_REJECT: object[0].field = WINDIVERT_FILTER_FIELD_ZERO; - object[0].test = (label == WINDIVERT_FILTER_RESULT_ACCEPT? - WINDIVERT_FILTER_TEST_EQ: WINDIVERT_FILTER_TEST_NEQ); + object[0].test = WINDIVERT_FILTER_TEST_EQ; object[0].arg[0] = object[0].arg[1] = object[0].arg[2] = object[0].arg[3] = 0; - object[0].success = WINDIVERT_FILTER_RESULT_ACCEPT; - object[0].failure = WINDIVERT_FILTER_RESULT_REJECT; + object[0].success = label; + object[0].failure = label; *obj_len = 1; return; default: @@ -1954,50 +1957,261 @@ static void WinDivertEmitFilter(PEXPR *stack, UINT len, UINT16 label, } } +/* + * Analyze a filter object. + */ +static UINT64 WinDivertAnalyzeFilter(WINDIVERT_LAYER layer, + PWINDIVERT_FILTER filter, UINT length) +{ + BOOL result; + UINT64 flags = 0; + + // False filter? + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_ZERO, 0); + if (!result) + { + return 0; + } + + if (layer == WINDIVERT_LAYER_NETWORK || + layer == WINDIVERT_LAYER_NETWORK_FORWARD) + { + // Inbound? + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_INBOUND, 1); + if (result) + { + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_OUTBOUND, 0); + } + flags |= (result? WINDIVERT_FILTER_FLAG_INBOUND: 0); + + // Outbound? + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_OUTBOUND, 1); + if (result) + { + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_INBOUND, 0); + } + flags |= (result? WINDIVERT_FILTER_FLAG_OUTBOUND: 0); + } + + if (layer != WINDIVERT_LAYER_REFLECT) + { + // IPv4? + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_IP, 1); + if (result) + { + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_IPV6, 0); + } + flags |= (result? WINDIVERT_FILTER_FLAG_IP: 0); + + // Ipv6? + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_IPV6, 1); + if (result) + { + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_IP, 0); + } + flags |= (result? WINDIVERT_FILTER_FLAG_IPV6: 0); + } + + // Events: + switch (layer) + { + case WINDIVERT_LAYER_FLOW: + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_EVENT, WINDIVERT_EVENT_FLOW_DELETED); + flags |= (result? WINDIVERT_FILTER_FLAG_EVENT_FLOW_DELETED: 0); + break; + + case WINDIVERT_LAYER_SOCKET: + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_EVENT, WINDIVERT_EVENT_SOCKET_BIND); + flags |= (result? WINDIVERT_FILTER_FLAG_EVENT_SOCKET_BIND: 0); + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_EVENT, WINDIVERT_EVENT_SOCKET_CONNECT); + flags |= (result? WINDIVERT_FILTER_FLAG_EVENT_SOCKET_CONNECT: 0); + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_EVENT, WINDIVERT_EVENT_SOCKET_LISTEN); + flags |= (result? WINDIVERT_FILTER_FLAG_EVENT_SOCKET_LISTEN: 0); + result = WinDivertCondExecFilter(filter, length, + WINDIVERT_FILTER_FIELD_EVENT, WINDIVERT_EVENT_SOCKET_ACCEPT); + flags |= (result? WINDIVERT_FILTER_FLAG_EVENT_SOCKET_ACCEPT: 0); + break; + + default: + break; + } + + return flags; +} + +/* + * Execute a filter object with respect to an assumption/condition. + * FALSE = definite reject; TRUE = maybe accept. + */ +static BOOL WinDivertCondExecFilter(PWINDIVERT_FILTER filter, UINT length, + UINT8 field, UINT32 arg) +{ + INT16 ip; + UINT8 succ, fail; + BOOL result[WINDIVERT_FILTER_MAXLEN]; + BOOL result_succ, result_fail, result_test; + + if (length == 0) + { + return TRUE; + } + + for (ip = (INT16)(length-1); ip >= 0; ip--) + { + succ = filter[ip].success; + if (succ == WINDIVERT_FILTER_RESULT_ACCEPT || succ <= ip || + succ >= length) + { + result_succ = TRUE; + } + else if (succ == WINDIVERT_FILTER_RESULT_REJECT) + { + result_succ = FALSE; + } + else + { + result_succ = result[succ]; + } + + fail = filter[ip].failure; + if (fail == WINDIVERT_FILTER_RESULT_ACCEPT || fail <= ip || + fail >= length) + { + result_fail = TRUE; + } + else if (fail == WINDIVERT_FILTER_RESULT_REJECT) + { + result_fail = FALSE; + } + else + { + result_fail = result[fail]; + } + + if (result_succ && result_fail) + { + result[ip] = TRUE; + } + else if (!result_succ && !result_fail) + { + result[ip] = FALSE; + } + else if (filter[ip].field == field) + { + switch (filter[ip].test) + { + case WINDIVERT_FILTER_TEST_EQ: + result_test = (arg == filter[ip].arg[0]); + break; + case WINDIVERT_FILTER_TEST_NEQ: + result_test = (arg != filter[ip].arg[0]); + break; + case WINDIVERT_FILTER_TEST_LT: + result_test = (arg < filter[ip].arg[0]); + break; + case WINDIVERT_FILTER_TEST_LEQ: + result_test = (arg <= filter[ip].arg[0]); + break; + case WINDIVERT_FILTER_TEST_GT: + result_test = (arg > filter[ip].arg[0]); + break; + case WINDIVERT_FILTER_TEST_GEQ: + result_test = (arg >= filter[ip].arg[0]); + break; + default: + return TRUE; // abort. + } + result[ip] = (result_test? result_succ: result_fail); + } + else + { + result[ip] = TRUE; + } + } + + return result[0]; +} + /* * Compile a filter string into an executable filter object. */ static ERROR WinDivertCompileFilter(const char *filter, - WINDIVERT_LAYER layer, windivert_ioctl_filter_t object, UINT *obj_len) + WINDIVERT_LAYER layer, PWINDIVERT_FILTER object, UINT *obj_len) { - TOKEN tokens[WINDIVERT_FILTER_MAXLEN*3]; - PEXPR stack[WINDIVERT_FILTER_MAXLEN]; - PPOOL pool; + TOKEN *tokens; + PEXPR *stack; + HANDLE pool; PEXPR expr; UINT i, max_depth; INT16 label; + const SIZE_T min_pool_size = 8192; + const SIZE_T tokens_size = 5 * WINDIVERT_FILTER_MAXLEN; ERROR error; - // Tokenize the filter string: - error = WinDivertTokenizeFilter(filter, layer, tokens, - sizeof(tokens) / sizeof(tokens[0]) - 1); - if (IS_ERROR(error)) + // Check for pre-compiled filter object: + if (filter[0] == '@') { - return error; + WINDIVERT_STREAM stream; + stream.data = (char *)filter; + stream.pos = 0; + stream.max = UINT_MAX; + stream.overflow = FALSE; + + if (!WinDivertDeserializeFilter(&stream, object, obj_len)) + { + return MAKE_ERROR(WINDIVERT_ERROR_BAD_OBJECT, 0); + } + return MAKE_ERROR(WINDIVERT_ERROR_NONE, 0); } - // Allocate memory pool for the compiler: - pool = (PPOOL)HeapAlloc(GetProcessHeap(), 0, sizeof(POOL)); + // Allocate memory for the compiler: + pool = HeapCreate(HEAP_NO_SERIALIZE, min_pool_size, 16 * min_pool_size); if (pool == NULL) { return MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); } - pool->offset = 0; - pool->error = MAKE_ERROR(WINDIVERT_ERROR_NONE, 0); + tokens = (TOKEN *)HeapAlloc(pool, 0, tokens_size * sizeof(TOKEN)); + stack = (PEXPR *)HeapAlloc(pool, 0, + WINDIVERT_FILTER_MAXLEN * sizeof(PEXPR)); + if (tokens == NULL || stack == NULL) + { + HeapDestroy(pool); + return MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); + } + + // Tokenize the filter string: + error = WinDivertTokenizeFilter(filter, layer, tokens, tokens_size-1); + if (IS_ERROR(error)) + { + HeapDestroy(pool); + return error; + } // Parse the filter into an expression: i = 0; max_depth = 1024; - expr = WinDivertParseFilter(pool, tokens, &i, max_depth, FALSE); + expr = WinDivertParseFilter(pool, tokens, &i, max_depth, FALSE, &error); if (expr == NULL) { - error = pool->error; - HeapFree(GetProcessHeap(), 0, pool); + HeapDestroy(pool); return error; } if (tokens[i].kind != TOKEN_END) { - HeapFree(GetProcessHeap(), 0, pool); + HeapDestroy(pool); return MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, tokens[i].pos); } @@ -2007,7 +2221,7 @@ static ERROR WinDivertCompileFilter(const char *filter, WINDIVERT_FILTER_RESULT_REJECT, stack); if (label < 0) { - HeapFree(GetProcessHeap(), 0, pool); + HeapDestroy(pool); return MAKE_ERROR(WINDIVERT_ERROR_TOO_LONG, 0); } @@ -2016,7 +2230,7 @@ static ERROR WinDivertCompileFilter(const char *filter, { WinDivertEmitFilter(stack, label, label, object, obj_len); } - HeapFree(GetProcessHeap(), 0, pool); + HeapDestroy(pool); return MAKE_ERROR(WINDIVERT_ERROR_NONE, 0); } @@ -2042,8 +2256,12 @@ static const char *WinDivertErrorString(UINT code) return "Filter expression contains a bad token for layer"; case WINDIVERT_ERROR_UNEXPECTED_TOKEN: return "Filter expression parse error"; + case WINDIVERT_ERROR_INDEX_OOB: + return "Filter expression array index is out-of-bounds"; case WINDIVERT_ERROR_OUTPUT_TOO_SHORT: return "Filter object buffer is too short"; + case WINDIVERT_ERROR_BAD_OBJECT: + return "Filter object is invalid"; case WINDIVERT_ERROR_ASSERTION_FAILED: return "Internal assertion failed"; default: @@ -2052,10 +2270,11 @@ static const char *WinDivertErrorString(UINT code) } /* - * Check the given filter string. + * Compile the given filter string. */ -extern BOOL WinDivertHelperCheckFilter(const char *filter_str, - WINDIVERT_LAYER layer, const char **error, UINT *error_pos) +extern BOOL WinDivertHelperCompileFilter(const char *filter_str, + WINDIVERT_LAYER layer, char *object, UINT obj_len, const char **error, + UINT *error_pos) { ERROR err; if (filter_str == NULL) @@ -2063,7 +2282,33 @@ extern BOOL WinDivertHelperCheckFilter(const char *filter_str, SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } - err = WinDivertCompileFilter(filter_str, layer, NULL, NULL); + + SetLastError(ERROR_SUCCESS); + if (object == NULL) + { + err = WinDivertCompileFilter(filter_str, layer, NULL, NULL); + } + else + { + WINDIVERT_FILTER object0[WINDIVERT_FILTER_MAXLEN]; + UINT obj0_len; + err = WinDivertCompileFilter(filter_str, layer, object0, &obj0_len); + if (!IS_ERROR(err)) + { + WINDIVERT_STREAM stream; + stream.data = object; + stream.pos = 0; + stream.max = obj_len; + stream.overflow = FALSE; + + WinDivertSerializeFilter(&stream, object0, obj0_len); + if (stream.overflow) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + err = MAKE_ERROR(WINDIVERT_ERROR_OUTPUT_TOO_SHORT, 0); + } + } + } if (error != NULL) { *error = WinDivertErrorString(GET_CODE(err)); @@ -2078,31 +2323,34 @@ extern BOOL WinDivertHelperCheckFilter(const char *filter_str, /* * Big number comparison. */ -static int WinDivertBigNumCompare(const UINT32 *a, const UINT32 *b) +static int WinDivertBigNumCompare(const UINT32 *a, const UINT32 *b, BOOL big) { - if (a[3] < b[3]) + if (big) { - return -1; - } - if (a[3] > b[3]) - { - return 1; - } - if (a[2] < b[2]) - { - return -1; - } - if (a[2] > b[2]) - { - return 1; - } - if (a[1] < b[1]) - { - return -1; - } - if (a[1] > b[1]) - { - return 1; + if (a[3] < b[3]) + { + return -1; + } + if (a[3] > b[3]) + { + return 1; + } + if (a[2] < b[2]) + { + return -1; + } + if (a[2] > b[2]) + { + return 1; + } + if (a[1] < b[1]) + { + return -1; + } + if (a[1] > b[1]) + { + return 1; + } } if (a[0] < b[0]) { @@ -2115,44 +2363,101 @@ static int WinDivertBigNumCompare(const UINT32 *a, const UINT32 *b) return 0; } +/* + * Get packet/payload data. + */ +static BOOL WinDivertGetData(const VOID *packet, UINT packet_len, UINT offset, + INT idx, UINT size, PVOID data) +{ + if (idx < 0) + { + idx += (INT)packet_len; + } + else + { + idx += (INT)offset; + } + if (idx < (INT)offset || idx > (INT)(packet_len - size)) + { + return FALSE; + } + memcpy(data, (UINT8 *)packet + idx, size); + return TRUE; +} + /* * Evaluate the given filter with the given packet as input. */ -extern BOOL WinDivertHelperEvalFilter(const char *filter, - WINDIVERT_LAYER layer, PVOID packet, UINT packet_len, - PWINDIVERT_ADDRESS addr) +extern BOOL WinDivertHelperEvalFilter(const char *filter, const VOID *packet, + UINT packet_len, const WINDIVERT_ADDRESS *addr) { UINT16 pc; ERROR err; - PWINDIVERT_IPHDR iphdr; - PWINDIVERT_IPV6HDR ipv6hdr; - PWINDIVERT_ICMPHDR icmphdr; - PWINDIVERT_ICMPV6HDR icmpv6hdr; - PWINDIVERT_TCPHDR tcphdr; - PWINDIVERT_UDPHDR udphdr; - UINT payload_len; + PWINDIVERT_IPHDR iphdr = NULL; + PWINDIVERT_IPV6HDR ipv6hdr = NULL; + PWINDIVERT_ICMPHDR icmphdr = NULL; + PWINDIVERT_ICMPV6HDR icmpv6hdr = NULL; + PWINDIVERT_TCPHDR tcphdr = NULL; + PWINDIVERT_UDPHDR udphdr = NULL; + UINT8 protocol = 0; + UINT header_len = 0, payload_len = 0; + UINT64 random64 = 0; UINT32 val[4]; - BOOL pass; + UINT8 data8; + UINT16 data16; + UINT32 data32; + BOOL pass, big; int cmp; - struct windivert_ioctl_filter_s object[WINDIVERT_FILTER_MAXLEN]; + WINDIVERT_FILTER object[WINDIVERT_FILTER_MAXLEN]; UINT obj_len; - if (filter == NULL || packet == NULL || addr == NULL) + if (filter == NULL || addr == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } + switch (addr->Layer) + { + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + if (packet == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + WinDivertParsePacket((PVOID)packet, packet_len, &iphdr, &ipv6hdr, + &icmphdr, &icmpv6hdr, &tcphdr, &udphdr, &protocol, NULL, + &payload_len); + header_len = packet_len - payload_len; + if ((addr->IPv6 && ipv6hdr == NULL) || + (!addr->IPv6 && iphdr == NULL)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + break; + case WINDIVERT_LAYER_FLOW: + case WINDIVERT_LAYER_SOCKET: + if (packet != NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + break; + case WINDIVERT_LAYER_REFLECT: + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } - err = WinDivertCompileFilter(filter, layer, object, &obj_len); + err = WinDivertCompileFilter(filter, addr->Layer, object, &obj_len); if (IS_ERROR(err)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } - WinDivertHelperParsePacket(packet, packet_len, &iphdr, &ipv6hdr, &icmphdr, - &icmpv6hdr, &tcphdr, &udphdr, NULL, &payload_len); - pc = 0; while (TRUE) { @@ -2171,8 +2476,65 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, break; } pass = TRUE; + big = FALSE; switch (object[pc].field) { + case WINDIVERT_FILTER_FIELD_ZERO: + case WINDIVERT_FILTER_FIELD_EVENT: + pass = TRUE; + break; + case WINDIVERT_FILTER_FIELD_INBOUND: + case WINDIVERT_FILTER_FIELD_OUTBOUND: + pass = (addr->Layer != WINDIVERT_LAYER_NETWORK_FORWARD && + addr->Layer != WINDIVERT_LAYER_REFLECT); + break; + case WINDIVERT_FILTER_FIELD_LOOPBACK: + case WINDIVERT_FILTER_FIELD_IMPOSTOR: + case WINDIVERT_FILTER_FIELD_IP: + case WINDIVERT_FILTER_FIELD_IPV6: + case WINDIVERT_FILTER_FIELD_ICMP: + case WINDIVERT_FILTER_FIELD_ICMPV6: + case WINDIVERT_FILTER_FIELD_TCP: + case WINDIVERT_FILTER_FIELD_UDP: + pass = (addr->Layer != WINDIVERT_LAYER_REFLECT); + break; + case WINDIVERT_FILTER_FIELD_RANDOM8: + case WINDIVERT_FILTER_FIELD_RANDOM16: + case WINDIVERT_FILTER_FIELD_RANDOM32: + pass = (addr->Layer == WINDIVERT_LAYER_NETWORK || + addr->Layer == WINDIVERT_LAYER_NETWORK_FORWARD); + if (pass && random64 == 0) + { + random64 = WinDivertHashPacket((UINT64)addr->Timestamp, + iphdr, ipv6hdr, icmphdr, icmpv6hdr, tcphdr, udphdr); + random64 |= 0xFF00000000000000ull; + } + break; + case WINDIVERT_FILTER_FIELD_IFIDX: + case WINDIVERT_FILTER_FIELD_SUBIFIDX: + case WINDIVERT_FILTER_FIELD_PACKET: + case WINDIVERT_FILTER_FIELD_PACKET16: + case WINDIVERT_FILTER_FIELD_PACKET32: + pass = (addr->Layer == WINDIVERT_LAYER_NETWORK || + addr->Layer == WINDIVERT_LAYER_NETWORK_FORWARD); + break; + case WINDIVERT_FILTER_FIELD_LOCALADDR: + case WINDIVERT_FILTER_FIELD_REMOTEADDR: + case WINDIVERT_FILTER_FIELD_LOCALPORT: + case WINDIVERT_FILTER_FIELD_REMOTEPORT: + case WINDIVERT_FILTER_FIELD_PROTOCOL: + pass = (addr->Layer == WINDIVERT_LAYER_NETWORK || + addr->Layer == WINDIVERT_LAYER_FLOW || + addr->Layer == WINDIVERT_LAYER_SOCKET); + break; + case WINDIVERT_FILTER_FIELD_PROCESSID: + pass = (addr->Layer == WINDIVERT_LAYER_FLOW || + addr->Layer == WINDIVERT_LAYER_SOCKET || + addr->Layer == WINDIVERT_LAYER_REFLECT); + break; + case WINDIVERT_FILTER_FIELD_LAYER: + pass = (addr->Layer == WINDIVERT_LAYER_REFLECT); + break; case WINDIVERT_FILTER_FIELD_IP_HDRLENGTH: case WINDIVERT_FILTER_FIELD_IP_TOS: case WINDIVERT_FILTER_FIELD_IP_LENGTH: @@ -2185,7 +2547,9 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, case WINDIVERT_FILTER_FIELD_IP_CHECKSUM: case WINDIVERT_FILTER_FIELD_IP_SRCADDR: case WINDIVERT_FILTER_FIELD_IP_DSTADDR: - pass = (iphdr != NULL); + pass = (addr->Layer == WINDIVERT_LAYER_NETWORK || + addr->Layer == WINDIVERT_LAYER_NETWORK_FORWARD); + pass = pass && (iphdr != NULL); break; case WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: case WINDIVERT_FILTER_FIELD_IPV6_FLOWLABEL: @@ -2194,19 +2558,25 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, case WINDIVERT_FILTER_FIELD_IPV6_HOPLIMIT: case WINDIVERT_FILTER_FIELD_IPV6_SRCADDR: case WINDIVERT_FILTER_FIELD_IPV6_DSTADDR: - pass = (ipv6hdr != NULL); + pass = (addr->Layer == WINDIVERT_LAYER_NETWORK || + addr->Layer == WINDIVERT_LAYER_NETWORK_FORWARD); + pass = pass && (ipv6hdr != NULL); break; case WINDIVERT_FILTER_FIELD_ICMP_TYPE: case WINDIVERT_FILTER_FIELD_ICMP_CODE: case WINDIVERT_FILTER_FIELD_ICMP_CHECKSUM: case WINDIVERT_FILTER_FIELD_ICMP_BODY: - pass = (icmphdr != NULL); + pass = (addr->Layer == WINDIVERT_LAYER_NETWORK || + addr->Layer == WINDIVERT_LAYER_NETWORK_FORWARD); + pass = pass && (icmphdr != NULL); break; case WINDIVERT_FILTER_FIELD_ICMPV6_TYPE: case WINDIVERT_FILTER_FIELD_ICMPV6_CODE: case WINDIVERT_FILTER_FIELD_ICMPV6_CHECKSUM: case WINDIVERT_FILTER_FIELD_ICMPV6_BODY: - pass = (icmpv6hdr != NULL); + pass = (addr->Layer == WINDIVERT_LAYER_NETWORK || + addr->Layer == WINDIVERT_LAYER_NETWORK_FORWARD); + pass = pass && (icmpv6hdr != NULL); break; case WINDIVERT_FILTER_FIELD_TCP_SRCPORT: case WINDIVERT_FILTER_FIELD_TCP_DSTPORT: @@ -2222,42 +2592,96 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, case WINDIVERT_FILTER_FIELD_TCP_WINDOW: case WINDIVERT_FILTER_FIELD_TCP_CHECKSUM: case WINDIVERT_FILTER_FIELD_TCP_URGPTR: + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD: + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD16: + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD32: case WINDIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH: - pass = (tcphdr != NULL); + pass = (addr->Layer == WINDIVERT_LAYER_NETWORK || + addr->Layer == WINDIVERT_LAYER_NETWORK_FORWARD); + pass = pass && (tcphdr != NULL); break; case WINDIVERT_FILTER_FIELD_UDP_SRCPORT: case WINDIVERT_FILTER_FIELD_UDP_DSTPORT: case WINDIVERT_FILTER_FIELD_UDP_LENGTH: case WINDIVERT_FILTER_FIELD_UDP_CHECKSUM: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD16: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD32: case WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: - pass = (udphdr != NULL); + pass = (addr->Layer == WINDIVERT_LAYER_NETWORK || + addr->Layer == WINDIVERT_LAYER_NETWORK_FORWARD); + pass = pass && (udphdr != NULL); break; default: - pass = TRUE; - break; + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; } if (!pass) { pc = object[pc].failure; continue; } - val[1] = val[2] = val[3] = 0; switch (object[pc].field) { case WINDIVERT_FILTER_FIELD_ZERO: val[0] = 0; break; + case WINDIVERT_FILTER_FIELD_EVENT: + val[0] = addr->Event; + break; + case WINDIVERT_FILTER_FIELD_RANDOM8: + val[0] = (UINT32)((random64 >> 48) & 0xFF); + break; + case WINDIVERT_FILTER_FIELD_RANDOM16: + val[0] = (UINT32)((random64 >> 32) & 0xFFFF); + break; + case WINDIVERT_FILTER_FIELD_RANDOM32: + val[0] = (UINT32)random64; + break; + case WINDIVERT_FILTER_FIELD_PACKET: + pass = WinDivertGetData(packet, packet_len, /*offset=*/0, + object[pc].arg[1], sizeof(data8), &data8); + val[0] = data8; + break; + case WINDIVERT_FILTER_FIELD_PACKET16: + pass = WinDivertGetData(packet, packet_len, /*offset=*/0, + object[pc].arg[1], sizeof(data16), &data16); + val[0] = ntohs(data16); + break; + case WINDIVERT_FILTER_FIELD_PACKET32: + pass = WinDivertGetData(packet, packet_len, /*offset=*/0, + object[pc].arg[1], sizeof(data32), &data32); + val[0] = ntohl(data32); + break; + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD: + pass = WinDivertGetData(packet, packet_len, header_len, + object[pc].arg[1], sizeof(data8), &data8); + val[0] = data8; + break; + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD16: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD16: + pass = WinDivertGetData(packet, packet_len, header_len, + object[pc].arg[1], sizeof(data16), &data16); + val[0] = ntohs(data16); + break; + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD32: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD32: + pass = WinDivertGetData(packet, packet_len, header_len, + object[pc].arg[1], sizeof(data32), &data32); + val[0] = ntohl(data32); + break; case WINDIVERT_FILTER_FIELD_INBOUND: - val[0] = (addr->Direction == WINDIVERT_DIRECTION_INBOUND); + val[0] = !addr->Outbound; break; case WINDIVERT_FILTER_FIELD_OUTBOUND: - val[0] = (addr->Direction == WINDIVERT_DIRECTION_OUTBOUND); + val[0] = addr->Outbound; break; case WINDIVERT_FILTER_FIELD_IFIDX: - val[0] = addr->IfIdx; + val[0] = addr->Network.IfIdx; break; case WINDIVERT_FILTER_FIELD_SUBIFIDX: - val[0] = addr->SubIfIdx; + val[0] = addr->Network.SubIfIdx; break; case WINDIVERT_FILTER_FIELD_LOOPBACK: val[0] = addr->Loopback; @@ -2266,22 +2690,86 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, val[0] = addr->Impostor; break; case WINDIVERT_FILTER_FIELD_IP: - val[0] = (iphdr != NULL); + val[0] = !addr->IPv6; break; case WINDIVERT_FILTER_FIELD_IPV6: - val[0] = (ipv6hdr != NULL); + val[0] = addr->IPv6; break; case WINDIVERT_FILTER_FIELD_ICMP: - val[0] = (icmphdr != NULL); + switch (addr->Layer) + { + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + val[0] = (UINT32)(icmphdr != NULL); + break; + case WINDIVERT_LAYER_SOCKET: + val[0] = (UINT32)(!addr->IPv6 && + addr->Socket.Protocol == IPPROTO_ICMP); + break; + case WINDIVERT_LAYER_FLOW: + val[0] = (UINT32)(!addr->IPv6 && + addr->Flow.Protocol == IPPROTO_ICMP); + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } break; case WINDIVERT_FILTER_FIELD_ICMPV6: - val[0] = (icmpv6hdr != NULL); + switch (addr->Layer) + { + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + val[0] = (UINT32)(icmpv6hdr != NULL); + break; + case WINDIVERT_LAYER_SOCKET: + val[0] = (UINT32)(addr->IPv6 && + addr->Socket.Protocol == IPPROTO_ICMPV6); + break; + case WINDIVERT_LAYER_FLOW: + val[0] = (UINT32)(addr->IPv6 && + addr->Flow.Protocol == IPPROTO_ICMPV6); + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } break; case WINDIVERT_FILTER_FIELD_TCP: - val[0] = (tcphdr != NULL); + switch (addr->Layer) + { + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + val[0] = (UINT32)(tcphdr != NULL); + break; + case WINDIVERT_LAYER_SOCKET: + val[0] = (UINT32)(addr->Socket.Protocol == IPPROTO_TCP); + break; + case WINDIVERT_LAYER_FLOW: + val[0] = (UINT32)(addr->Flow.Protocol == IPPROTO_TCP); + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } break; case WINDIVERT_FILTER_FIELD_UDP: - val[0] = (udphdr != NULL); + switch (addr->Layer) + { + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + val[0] = (UINT32)(udphdr != NULL); + break; + case WINDIVERT_LAYER_SOCKET: + val[0] = (UINT32)(addr->Socket.Protocol == IPPROTO_UDP); + break; + case WINDIVERT_LAYER_FLOW: + val[0] = (UINT32)(addr->Flow.Protocol == IPPROTO_UDP); + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } break; case WINDIVERT_FILTER_FIELD_IP_HDRLENGTH: val[0] = iphdr->HdrLength; @@ -2314,9 +2802,11 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, val[0] = ntohs(iphdr->Checksum); break; case WINDIVERT_FILTER_FIELD_IP_SRCADDR: + val[1] = 0x0000FFFF; val[0] = ntohl(iphdr->SrcAddr); break; case WINDIVERT_FILTER_FIELD_IP_DSTADDR: + val[1] = 0x0000FFFF; val[0] = ntohl(iphdr->DstAddr); break; case WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: @@ -2335,12 +2825,14 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, val[0] = ipv6hdr->HopLimit; break; case WINDIVERT_FILTER_FIELD_IPV6_SRCADDR: + big = TRUE; val[3] = ntohl(ipv6hdr->SrcAddr[0]); val[2] = ntohl(ipv6hdr->SrcAddr[1]); val[1] = ntohl(ipv6hdr->SrcAddr[2]); val[0] = ntohl(ipv6hdr->SrcAddr[3]); break; case WINDIVERT_FILTER_FIELD_IPV6_DSTADDR: + big = TRUE; val[3] = ntohl(ipv6hdr->DstAddr[0]); val[2] = ntohl(ipv6hdr->DstAddr[1]); val[1] = ntohl(ipv6hdr->DstAddr[2]); @@ -2430,11 +2922,196 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, case WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: val[0] = payload_len; break; + case WINDIVERT_FILTER_FIELD_LOCALADDR: + big = TRUE; + switch (addr->Layer) + { + case WINDIVERT_LAYER_NETWORK: + if (!addr->IPv6) + { + val[1] = 0x0000FFFF; + val[0] = ntohl((addr->Outbound? iphdr->SrcAddr: + iphdr->DstAddr)); + } + else if (addr->Outbound) + { + val[3] = ntohl(ipv6hdr->SrcAddr[0]); + val[2] = ntohl(ipv6hdr->SrcAddr[1]); + val[1] = ntohl(ipv6hdr->SrcAddr[2]); + val[0] = ntohl(ipv6hdr->SrcAddr[3]); + } + else + { + val[3] = ntohl(ipv6hdr->DstAddr[0]); + val[2] = ntohl(ipv6hdr->DstAddr[1]); + val[1] = ntohl(ipv6hdr->DstAddr[2]); + val[0] = ntohl(ipv6hdr->DstAddr[3]); + } + break; + case WINDIVERT_LAYER_FLOW: + val[0] = addr->Flow.LocalAddr[0]; + val[1] = addr->Flow.LocalAddr[1]; + val[2] = addr->Flow.LocalAddr[2]; + val[3] = addr->Flow.LocalAddr[3]; + break; + case WINDIVERT_LAYER_SOCKET: + val[0] = addr->Socket.LocalAddr[0]; + val[1] = addr->Socket.LocalAddr[1]; + val[2] = addr->Socket.LocalAddr[2]; + val[3] = addr->Socket.LocalAddr[3]; + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + break; + case WINDIVERT_FILTER_FIELD_REMOTEADDR: + big = TRUE; + switch (addr->Layer) + { + case WINDIVERT_LAYER_NETWORK: + if (!addr->IPv6) + { + val[1] = 0x0000FFFF; + val[0] = ntohl((!addr->Outbound? iphdr->SrcAddr: + iphdr->DstAddr)); + } + else if (!addr->Outbound) + { + val[3] = ntohl(ipv6hdr->SrcAddr[0]); + val[2] = ntohl(ipv6hdr->SrcAddr[1]); + val[1] = ntohl(ipv6hdr->SrcAddr[2]); + val[0] = ntohl(ipv6hdr->SrcAddr[3]); + } + else + { + val[3] = ntohl(ipv6hdr->DstAddr[0]); + val[2] = ntohl(ipv6hdr->DstAddr[1]); + val[1] = ntohl(ipv6hdr->DstAddr[2]); + val[0] = ntohl(ipv6hdr->DstAddr[3]); + } + break; + case WINDIVERT_LAYER_FLOW: + val[0] = addr->Flow.RemoteAddr[0]; + val[1] = addr->Flow.RemoteAddr[1]; + val[2] = addr->Flow.RemoteAddr[2]; + val[3] = addr->Flow.RemoteAddr[3]; + break; + case WINDIVERT_LAYER_SOCKET: + val[0] = addr->Socket.RemoteAddr[0]; + val[1] = addr->Socket.RemoteAddr[1]; + val[2] = addr->Socket.RemoteAddr[2]; + val[3] = addr->Socket.RemoteAddr[3]; + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + break; + case WINDIVERT_FILTER_FIELD_LOCALPORT: + switch (addr->Layer) + { + case WINDIVERT_LAYER_NETWORK: + if (tcphdr != NULL) + { + val[0] = ntohs((addr->Outbound? tcphdr->SrcPort: + tcphdr->DstPort)); + } + else if (udphdr != NULL) + { + val[0] = ntohs((addr->Outbound? udphdr->SrcPort: + udphdr->DstPort)); + } + else + { + val[0] = 0; + } + break; + case WINDIVERT_LAYER_FLOW: + val[0] = addr->Flow.LocalPort; + break; + case WINDIVERT_LAYER_SOCKET: + val[0] = addr->Socket.LocalPort; + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + break; + case WINDIVERT_FILTER_FIELD_REMOTEPORT: + switch (addr->Layer) + { + case WINDIVERT_LAYER_NETWORK: + if (tcphdr != NULL) + { + val[0] = ntohs((!addr->Outbound? tcphdr->SrcPort: + tcphdr->DstPort)); + } + else if (udphdr != NULL) + { + val[0] = ntohs((!addr->Outbound? udphdr->SrcPort: + udphdr->DstPort)); + } + else + { + val[0] = 0; + } + break; + case WINDIVERT_LAYER_FLOW: + val[0] = addr->Flow.RemotePort; + break; + case WINDIVERT_LAYER_SOCKET: + val[0] = addr->Socket.RemotePort; + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + break; + case WINDIVERT_FILTER_FIELD_PROTOCOL: + switch (addr->Layer) + { + case WINDIVERT_LAYER_NETWORK: + val[0] = protocol; + break; + case WINDIVERT_LAYER_FLOW: + val[0] = addr->Flow.Protocol; + break; + case WINDIVERT_LAYER_SOCKET: + val[0] = addr->Socket.Protocol; + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + break; + case WINDIVERT_FILTER_FIELD_PROCESSID: + switch (addr->Layer) + { + case WINDIVERT_LAYER_FLOW: + val[0] = addr->Flow.ProcessId; + break; + case WINDIVERT_LAYER_SOCKET: + val[0] = addr->Socket.ProcessId; + break; + case WINDIVERT_LAYER_REFLECT: + val[0] = addr->Reflect.ProcessId; + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + break; default: SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } - cmp = WinDivertBigNumCompare(val, object[pc].arg); + if (!pass) + { + pc = object[pc].failure; + continue; + } + cmp = WinDivertBigNumCompare(val, object[pc].arg, big); switch (object[pc].test) { case WINDIVERT_FILTER_TEST_EQ: @@ -2463,3 +3140,1438 @@ extern BOOL WinDivertHelperEvalFilter(const char *filter, } } +/* + * Get a char from a stream. + */ +static char WinDivertGetChar(PWINDIVERT_STREAM stream) +{ + char c; + if (stream->pos >= stream->max) + { + stream->overflow = TRUE; + return EOF; + } + c = stream->data[stream->pos]; + stream->pos++; + return c; +} + +/* + * Deserialize a number. + */ +static BOOL WinDivertDeserializeNumber(PWINDIVERT_STREAM stream, UINT max_len, + UINT32 *result) +{ + UINT32 i, val = 0; + char c; + for (i = 0; i < max_len; i++) + { + if ((val & 0xF8000000) != 0) + { + return FALSE; // Overflow + } + val <<= 5; + c = WinDivertGetChar(stream); + if (c >= '!' && c <= '!' + 31) + { + val += (UINT32)(c - '!'); + } + else if (c >= '!' + 32 && c <= '!' + 64) + { + val += (UINT32)(c - '!' - 32); + *result = val; + return TRUE; + } + else + { + return FALSE; + } + } + return FALSE; +} + +/* + * Deserialize a test. + */ +static BOOL WinDivertDeserializeTest(PWINDIVERT_STREAM stream, + PWINDIVERT_FILTER filter) +{ + UINT32 val; + UINT i; + + if (WinDivertGetChar(stream) != '_') + { + return FALSE; + } + + if (!WinDivertDeserializeNumber(stream, 2, &val) || + val > WINDIVERT_FILTER_FIELD_MAX) + { + return FALSE; + } + filter->field = (UINT8)val; + + if (!WinDivertDeserializeNumber(stream, 2, &val) || + val > WINDIVERT_FILTER_TEST_MAX) + { + return FALSE; + } + filter->test = (UINT8)val; + + if (!WinDivertDeserializeNumber(stream, 7, &filter->arg[0])) + { + return FALSE; + } + + switch (filter->field) + { + case WINDIVERT_FILTER_FIELD_IPV6_SRCADDR: + case WINDIVERT_FILTER_FIELD_IPV6_DSTADDR: + case WINDIVERT_FILTER_FIELD_LOCALADDR: + case WINDIVERT_FILTER_FIELD_REMOTEADDR: + for (i = 1; i < 4; i++) + { + if (!WinDivertDeserializeNumber(stream, 7, &filter->arg[i])) + { + return FALSE; + } + } + break; + case WINDIVERT_FILTER_FIELD_IP_SRCADDR: + case WINDIVERT_FILTER_FIELD_IP_DSTADDR: + filter->arg[1] = 0x0000FFFF; + filter->arg[2] = filter->arg[3] = 0; + break; + case WINDIVERT_FILTER_FIELD_PACKET: + case WINDIVERT_FILTER_FIELD_PACKET16: + case WINDIVERT_FILTER_FIELD_PACKET32: + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD: + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD16: + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD32: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD16: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD32: + if (!WinDivertDeserializeNumber(stream, 7, &val)) + { + return FALSE; + } + filter->arg[1] = (UINT32)((INT)val - UINT16_MAX); + filter->arg[2] = filter->arg[3] = 0; + break; + default: + filter->arg[1] = filter->arg[2] = filter->arg[3] = 0; + break; + } + + if (!WinDivertDeserializeNumber(stream, 2, &val) || val > UINT8_MAX) + { + return FALSE; + } + filter->success = (UINT8)val - 2; + + if (!WinDivertDeserializeNumber(stream, 2, &val) || val > UINT8_MAX) + { + return FALSE; + } + filter->failure = (UINT8)val - 2; + + return TRUE; +} + +/* + * Deserialize a filter header. + */ +static BOOL WinDivertDeserializeFilterHeader(PWINDIVERT_STREAM stream, + UINT *length) +{ + UINT32 version, length32; + + if (WinDivertGetChar(stream) != '@' || + WinDivertGetChar(stream) != 'W' || + WinDivertGetChar(stream) != 'i' || + WinDivertGetChar(stream) != 'n' || + WinDivertGetChar(stream) != 'D' || + WinDivertGetChar(stream) != 'i' || + WinDivertGetChar(stream) != 'v' || + WinDivertGetChar(stream) != '_') + { + return FALSE; + } + + if (!WinDivertDeserializeNumber(stream, 4, &version) || (version != 0)) + { + return FALSE; + } + + if (!WinDivertDeserializeNumber(stream, 2, &length32) || + length32 == 0 || length32 > WINDIVERT_FILTER_MAXLEN) + { + return FALSE; + } + *length = length32; + + return TRUE; +} + +/* + * Deserialize a filter. + */ +static BOOL WinDivertDeserializeFilter(PWINDIVERT_STREAM stream, + PWINDIVERT_FILTER filter, UINT *length) +{ + UINT i; + + if (!WinDivertDeserializeFilterHeader(stream, length)) + { + return FALSE; + } + + for (i = 0; i < *length; i++) + { + if (!WinDivertDeserializeTest(stream, filter + i)) + { + return FALSE; + } + } + + if (WinDivertGetChar(stream) != '\0') + { + return FALSE; + } + + return TRUE; +} + +/* + * Decompile a test into an expression. + */ +static PEXPR WinDivertDecompileTest(HANDLE pool, PWINDIVERT_FILTER test) +{ + KIND kind; + PEXPR var, val, expr; + UINT32 tmp[4]; + ERROR error; + + switch (test->field) + { + case WINDIVERT_FILTER_FIELD_ZERO: + kind = TOKEN_ZERO; break; + case WINDIVERT_FILTER_FIELD_EVENT: + kind = TOKEN_EVENT; break; + case WINDIVERT_FILTER_FIELD_RANDOM8: + kind = TOKEN_RANDOM8; break; + case WINDIVERT_FILTER_FIELD_RANDOM16: + kind = TOKEN_RANDOM16; break; + case WINDIVERT_FILTER_FIELD_RANDOM32: + kind = TOKEN_RANDOM32; break; + case WINDIVERT_FILTER_FIELD_PACKET: + kind = TOKEN_PACKET; break; + case WINDIVERT_FILTER_FIELD_PACKET16: + kind = TOKEN_PACKET16; break; + case WINDIVERT_FILTER_FIELD_PACKET32: + kind = TOKEN_PACKET32; break; + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD: + kind = TOKEN_TCP_PAYLOAD; break; + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD16: + kind = TOKEN_TCP_PAYLOAD16; break; + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD32: + kind = TOKEN_TCP_PAYLOAD32; break; + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD: + kind = TOKEN_UDP_PAYLOAD; break; + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD16: + kind = TOKEN_UDP_PAYLOAD16; break; + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD32: + kind = TOKEN_UDP_PAYLOAD32; break; + case WINDIVERT_FILTER_FIELD_INBOUND: + kind = TOKEN_INBOUND; break; + case WINDIVERT_FILTER_FIELD_OUTBOUND: + kind = TOKEN_OUTBOUND; break; + case WINDIVERT_FILTER_FIELD_IFIDX: + kind = TOKEN_IF_IDX; break; + case WINDIVERT_FILTER_FIELD_SUBIFIDX: + kind = TOKEN_SUB_IF_IDX; break; + case WINDIVERT_FILTER_FIELD_IP: + kind = TOKEN_IP; break; + case WINDIVERT_FILTER_FIELD_IPV6: + kind = TOKEN_IPV6; break; + case WINDIVERT_FILTER_FIELD_ICMP: + kind = TOKEN_ICMP; break; + case WINDIVERT_FILTER_FIELD_TCP: + kind = TOKEN_TCP; break; + case WINDIVERT_FILTER_FIELD_UDP: + kind = TOKEN_UDP; break; + case WINDIVERT_FILTER_FIELD_ICMPV6: + kind = TOKEN_ICMPV6; break; + case WINDIVERT_FILTER_FIELD_IP_HDRLENGTH: + kind = TOKEN_IP_HDR_LENGTH; break; + case WINDIVERT_FILTER_FIELD_IP_TOS: + kind = TOKEN_IP_TOS; break; + case WINDIVERT_FILTER_FIELD_IP_LENGTH: + kind = TOKEN_IP_LENGTH; break; + case WINDIVERT_FILTER_FIELD_IP_ID: + kind = TOKEN_IP_ID; break; + case WINDIVERT_FILTER_FIELD_IP_DF: + kind = TOKEN_IP_DF; break; + case WINDIVERT_FILTER_FIELD_IP_MF: + kind = TOKEN_IP_MF; break; + case WINDIVERT_FILTER_FIELD_IP_FRAGOFF: + kind = TOKEN_IP_FRAG_OFF; break; + case WINDIVERT_FILTER_FIELD_IP_TTL: + kind = TOKEN_IP_TTL; break; + case WINDIVERT_FILTER_FIELD_IP_PROTOCOL: + kind = TOKEN_IP_PROTOCOL; break; + case WINDIVERT_FILTER_FIELD_IP_CHECKSUM: + kind = TOKEN_IP_CHECKSUM; break; + case WINDIVERT_FILTER_FIELD_IP_SRCADDR: + kind = TOKEN_IP_SRC_ADDR; break; + case WINDIVERT_FILTER_FIELD_IP_DSTADDR: + kind = TOKEN_IP_DST_ADDR; break; + case WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: + kind = TOKEN_IPV6_TRAFFIC_CLASS; break; + case WINDIVERT_FILTER_FIELD_IPV6_FLOWLABEL: + kind = TOKEN_IPV6_FLOW_LABEL; break; + case WINDIVERT_FILTER_FIELD_IPV6_LENGTH: + kind = TOKEN_IPV6_LENGTH; break; + case WINDIVERT_FILTER_FIELD_IPV6_NEXTHDR: + kind = TOKEN_IPV6_NEXT_HDR; break; + case WINDIVERT_FILTER_FIELD_IPV6_HOPLIMIT: + kind = TOKEN_IPV6_HOP_LIMIT; break; + case WINDIVERT_FILTER_FIELD_IPV6_SRCADDR: + kind = TOKEN_IPV6_SRC_ADDR; break; + case WINDIVERT_FILTER_FIELD_IPV6_DSTADDR: + kind = TOKEN_IPV6_DST_ADDR; break; + case WINDIVERT_FILTER_FIELD_ICMP_TYPE: + kind = TOKEN_ICMP_TYPE; break; + case WINDIVERT_FILTER_FIELD_ICMP_CODE: + kind = TOKEN_ICMP_CODE; break; + case WINDIVERT_FILTER_FIELD_ICMP_CHECKSUM: + kind = TOKEN_ICMP_CHECKSUM; break; + case WINDIVERT_FILTER_FIELD_ICMP_BODY: + kind = TOKEN_ICMP_BODY; break; + case WINDIVERT_FILTER_FIELD_ICMPV6_TYPE: + kind = TOKEN_ICMPV6_TYPE; break; + case WINDIVERT_FILTER_FIELD_ICMPV6_CODE: + kind = TOKEN_ICMPV6_CODE; break; + case WINDIVERT_FILTER_FIELD_ICMPV6_CHECKSUM: + kind = TOKEN_ICMPV6_CHECKSUM; break; + case WINDIVERT_FILTER_FIELD_ICMPV6_BODY: + kind = TOKEN_ICMPV6_BODY; break; + case WINDIVERT_FILTER_FIELD_TCP_SRCPORT: + kind = TOKEN_TCP_SRC_PORT; break; + case WINDIVERT_FILTER_FIELD_TCP_DSTPORT: + kind = TOKEN_TCP_DST_PORT; break; + case WINDIVERT_FILTER_FIELD_TCP_SEQNUM: + kind = TOKEN_TCP_SEQ_NUM; break; + case WINDIVERT_FILTER_FIELD_TCP_ACKNUM: + kind = TOKEN_TCP_ACK_NUM; break; + case WINDIVERT_FILTER_FIELD_TCP_HDRLENGTH: + kind = TOKEN_TCP_HDR_LENGTH; break; + case WINDIVERT_FILTER_FIELD_TCP_URG: + kind = TOKEN_TCP_URG; break; + case WINDIVERT_FILTER_FIELD_TCP_ACK: + kind = TOKEN_TCP_ACK; break; + case WINDIVERT_FILTER_FIELD_TCP_PSH: + kind = TOKEN_TCP_PSH; break; + case WINDIVERT_FILTER_FIELD_TCP_RST: + kind = TOKEN_TCP_RST; break; + case WINDIVERT_FILTER_FIELD_TCP_SYN: + kind = TOKEN_TCP_SYN; break; + case WINDIVERT_FILTER_FIELD_TCP_FIN: + kind = TOKEN_TCP_FIN; break; + case WINDIVERT_FILTER_FIELD_TCP_WINDOW: + kind = TOKEN_TCP_WINDOW; break; + case WINDIVERT_FILTER_FIELD_TCP_CHECKSUM: + kind = TOKEN_TCP_CHECKSUM; break; + case WINDIVERT_FILTER_FIELD_TCP_URGPTR: + kind = TOKEN_TCP_URG_PTR; break; + case WINDIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH: + kind = TOKEN_TCP_PAYLOAD_LENGTH; break; + case WINDIVERT_FILTER_FIELD_UDP_SRCPORT: + kind = TOKEN_UDP_SRC_PORT; break; + case WINDIVERT_FILTER_FIELD_UDP_DSTPORT: + kind = TOKEN_UDP_DST_PORT; break; + case WINDIVERT_FILTER_FIELD_UDP_LENGTH: + kind = TOKEN_UDP_LENGTH; break; + case WINDIVERT_FILTER_FIELD_UDP_CHECKSUM: + kind = TOKEN_UDP_CHECKSUM; break; + case WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: + kind = TOKEN_UDP_PAYLOAD_LENGTH; break; + case WINDIVERT_FILTER_FIELD_LOOPBACK: + kind = TOKEN_LOOPBACK; break; + case WINDIVERT_FILTER_FIELD_IMPOSTOR: + kind = TOKEN_IMPOSTOR; break; + case WINDIVERT_FILTER_FIELD_PROCESSID: + kind = TOKEN_PROCESS_ID; break; + case WINDIVERT_FILTER_FIELD_LOCALADDR: + kind = TOKEN_LOCAL_ADDR; break; + case WINDIVERT_FILTER_FIELD_REMOTEADDR: + kind = TOKEN_REMOTE_ADDR; break; + case WINDIVERT_FILTER_FIELD_LOCALPORT: + kind = TOKEN_LOCAL_PORT; break; + case WINDIVERT_FILTER_FIELD_REMOTEPORT: + kind = TOKEN_REMOTE_PORT; break; + case WINDIVERT_FILTER_FIELD_PROTOCOL: + kind = TOKEN_PROTOCOL; break; + case WINDIVERT_FILTER_FIELD_LAYER: + kind = TOKEN_LAYER; break; + default: + return NULL; + } + + switch (kind) + { + case TOKEN_PACKET: + case TOKEN_PACKET16: + case TOKEN_PACKET32: + case TOKEN_TCP_PAYLOAD: + case TOKEN_TCP_PAYLOAD16: + case TOKEN_TCP_PAYLOAD32: + case TOKEN_UDP_PAYLOAD: + case TOKEN_UDP_PAYLOAD16: + case TOKEN_UDP_PAYLOAD32: + var = WinDivertMakeArrayVar(pool, kind, test->arg[1], &error); + if (var == NULL) + { + return NULL; + } + tmp[0] = test->arg[0]; + tmp[1] = tmp[2] = tmp[3] = 0; + val = WinDivertMakeNumber(pool, tmp, &error); + if (val == NULL) + { + return NULL; + } + break; + default: + var = WinDivertMakeVar(kind, &error); + if (var == NULL) + { + return NULL; + } + val = WinDivertMakeNumber(pool, test->arg, &error); + if (val == NULL) + { + return NULL; + } + break; + } + + switch (test->test) + { + case WINDIVERT_FILTER_TEST_EQ: + kind = TOKEN_EQ; break; + case WINDIVERT_FILTER_TEST_NEQ: + kind = TOKEN_NEQ; break; + case WINDIVERT_FILTER_TEST_LT: + kind = TOKEN_LT; break; + case WINDIVERT_FILTER_TEST_LEQ: + kind = TOKEN_LEQ; break; + case WINDIVERT_FILTER_TEST_GT: + kind = TOKEN_GT; break; + case WINDIVERT_FILTER_TEST_GEQ: + kind = TOKEN_GEQ; break; + default: + return NULL; + } + + expr = WinDivertMakeBinOp(pool, kind, var, val, &error); + if (expr == NULL) + { + return NULL; + } + expr->succ = test->success; + expr->fail = test->failure; + return expr; +} + +/* + * Dereference an expression. + */ +static void WinDivertDerefExpr(PEXPR *exprs, UINT8 i) +{ + switch (i) + { + case WINDIVERT_FILTER_RESULT_ACCEPT: + case WINDIVERT_FILTER_RESULT_REJECT: + return; + default: + exprs[i]->count--; + if (exprs[i]->count == 0) + { + exprs[i] = NULL; + } + return; + } +} + +/* + * Apply an and/or simplification for WinDivertCoalesceAndOr(). + */ +static PEXPR WinDivertSimplifyAndOr(HANDLE pool, PEXPR *exprs, PEXPR expr, + BOOL and, UINT8 next, UINT8 other) +{ + PEXPR next_expr = exprs[next], new_expr; + ERROR error; + + new_expr = WinDivertMakeBinOp(pool, (and? TOKEN_AND: TOKEN_OR), expr, + next_expr, &error); + if (new_expr == NULL) + { + return NULL; + } + new_expr->succ = next_expr->succ; + new_expr->fail = next_expr->fail; + new_expr->count = expr->count; + WinDivertDerefExpr(exprs, next); + WinDivertDerefExpr(exprs, other); + return new_expr; +} + +/* + * Detect and coalesce and/or (& (?:)) expression patterns. + */ +static PEXPR WinDivertCoalesceAndOr(HANDLE pool, PEXPR *exprs, UINT8 i, + ERROR *error) +{ + PEXPR expr, next_expr, new_expr; + BOOL singleton; + static const EXPR true_expr = {{{0}}, TOKEN_TRUE}; + + expr = exprs[i]; + while (TRUE) + { + if (expr == NULL || expr->count == 0) + { + return NULL; + } + + singleton = FALSE; + switch (expr->succ) + { + case WINDIVERT_FILTER_RESULT_ACCEPT: + case WINDIVERT_FILTER_RESULT_REJECT: + break; + default: + next_expr = exprs[expr->succ]; + if (next_expr->count != 1) + { + break; + } + singleton = TRUE; + if (next_expr->fail == expr->fail) + { + expr = WinDivertSimplifyAndOr(pool, exprs, expr, + /*and=*/TRUE, expr->succ, expr->fail); + continue; + } + else if (next_expr->succ == expr->fail) + { + new_expr = (PEXPR)HeapAlloc(pool, HEAP_ZERO_MEMORY, + sizeof(EXPR)); + if (new_expr == NULL) + { + return NULL; + } + new_expr->kind = TOKEN_QUESTION; + new_expr->arg[0] = expr; + new_expr->arg[1] = next_expr; + new_expr->arg[2] = (PEXPR)&true_expr; + new_expr->succ = next_expr->succ; + new_expr->fail = next_expr->fail; + new_expr->count = expr->count; + WinDivertDerefExpr(exprs, expr->succ); + WinDivertDerefExpr(exprs, expr->fail); + expr = new_expr; + continue; + } + break; + } + switch (expr->fail) + { + case WINDIVERT_FILTER_RESULT_ACCEPT: + case WINDIVERT_FILTER_RESULT_REJECT: + singleton = FALSE; + break; + default: + next_expr = exprs[expr->fail]; + if (next_expr->count != 1) + { + singleton = FALSE; + break; + } + if (next_expr->succ == expr->succ) + { + expr = WinDivertSimplifyAndOr(pool, exprs, expr, + /*and=*/FALSE, expr->fail, expr->succ); + continue; + } + else if (next_expr->fail == expr->succ) + { + expr = WinDivertSimplifyAndOr(pool, exprs, expr, + /*and=*/TRUE, expr->fail, expr->succ); + continue; + } + break; + } + + if (singleton) + { + // Both branches have count==1; simplify into a (?:) expression: + PEXPR succ_expr, fail_expr; + succ_expr = exprs[expr->succ]; + fail_expr = exprs[expr->fail]; + if (succ_expr->succ != fail_expr->succ || + succ_expr->fail != fail_expr->fail) + { + break; + } + new_expr = (PEXPR)HeapAlloc(pool, HEAP_ZERO_MEMORY, sizeof(EXPR)); + if (new_expr == NULL) + { + return NULL; + } + new_expr->kind = TOKEN_QUESTION; + new_expr->arg[0] = expr; + new_expr->arg[1] = succ_expr; + new_expr->arg[2] = fail_expr; + new_expr->succ = succ_expr->succ; + new_expr->fail = fail_expr->fail; + new_expr->count = expr->count; + WinDivertDerefExpr(exprs, expr->succ); + WinDivertDerefExpr(exprs, expr->fail); + WinDivertDerefExpr(exprs, new_expr->succ); + WinDivertDerefExpr(exprs, new_expr->fail); + expr = new_expr; + continue; + } + + // No simplifications, so we are done. + break; + } + + exprs[i] = expr; + return expr; +} + +/* + * Coalesce all remaining expressions. + */ +static PEXPR WinDivertCoalesceExpr(HANDLE pool, PEXPR *exprs, UINT8 i) +{ + PEXPR expr, succ_expr, fail_expr, new_expr; + static const EXPR true_expr = {{{0}}, TOKEN_TRUE}; + static const EXPR false_expr = {{{0}}, TOKEN_FALSE}; + + switch (i) + { + case WINDIVERT_FILTER_RESULT_ACCEPT: + return (PEXPR)&true_expr; + case WINDIVERT_FILTER_RESULT_REJECT: + return (PEXPR)&false_expr; + default: + break; + } + + expr = exprs[i]; + if (expr == NULL) + { + return NULL; + } + + if (expr->succ == expr->fail) + { + return WinDivertCoalesceExpr(pool, exprs, expr->succ); + } + + succ_expr = WinDivertCoalesceExpr(pool, exprs, expr->succ); + fail_expr = WinDivertCoalesceExpr(pool, exprs, expr->fail); + if (succ_expr == NULL || fail_expr == NULL) + { + return NULL; + } + if (succ_expr->kind == TOKEN_TRUE && fail_expr->kind == TOKEN_FALSE) + { + return expr; + } + + new_expr = (PEXPR)HeapAlloc(pool, HEAP_ZERO_MEMORY, sizeof(EXPR)); + if (new_expr == NULL) + { + return NULL; + } + + new_expr->kind = TOKEN_QUESTION; + new_expr->arg[0] = expr; + new_expr->arg[1] = succ_expr; + new_expr->arg[2] = fail_expr; + return new_expr; +} + +/* + * Format a decimal number. + */ +static void WinDivertFormatNumber(PWINDIVERT_STREAM stream, UINT32 val) +{ + UINT64 r = 1000000000, dig; + BOOL zeroes = FALSE; + + while (r != 0) + { + dig = val / r; + val = val % r; + r = r / 10; + if (dig == 0 && !zeroes && r != 0) + { + continue; + } + WinDivertPutChar(stream, '0' + dig); + zeroes = TRUE; + } +} + +/* + * Format a hexidecimal number. + */ +static void WinDivertFormatHexNumber(PWINDIVERT_STREAM stream, UINT32 val) +{ + INT s = 28; + UINT32 dig; + BOOL zeroes = FALSE; + + while (s >= 0) + { + dig = (val & ((UINT32)0xF << s)) >> s; + s -= 4; + if (dig == 0 && !zeroes && s >= 0) + { + continue; + } + WinDivertPutChar(stream, (dig <= 9? '0' + dig: 'a' + (dig - 10))); + zeroes = TRUE; + } +} + +/* + * Format an IPv4 address. + */ +static void WinDivertFormatIPv4Addr(PWINDIVERT_STREAM stream, UINT32 addr) +{ + WinDivertFormatNumber(stream, (addr & 0xFF000000) >> 24); + WinDivertPutChar(stream, '.'); + WinDivertFormatNumber(stream, (addr & 0x00FF0000) >> 16); + WinDivertPutChar(stream, '.'); + WinDivertFormatNumber(stream, (addr & 0x0000FF00) >> 8); + WinDivertPutChar(stream, '.'); + WinDivertFormatNumber(stream, (addr & 0x000000FF) >> 0); +} + +/* + * Format an IPv6 address. + */ +static void WinDivertFormatIPv6Addr(PWINDIVERT_STREAM stream, + const UINT32 *addr32) +{ + INT i, z_curr, z_count, z_start, z_max; + UINT16 addr[8]; + + // IPv4 special case: + if (addr32[3] == 0 && addr32[2] == 0 && addr32[1] == 0x0000FFFF) + { + WinDivertFormatIPv4Addr(stream, addr32[0]); + return; + } + + // Find zeroes: + memcpy(addr, addr32, sizeof(addr)); + z_curr = 7; + z_count = 0; + z_start = z_max = -1; + for (i = 7; i >= 0; i--) + { + if (addr[i] == 0) + { + z_count++; + z_start = (z_count > z_max? z_curr: z_start); + z_max = (z_count > z_max? z_count: z_max); + } + else + { + z_curr = i-1; + z_count = 0; + } + } + + // Format address: + for (i = 7; i >= 0; i--) + { + if (i == z_start) + { + WinDivertPutString(stream, (i == 7? "::": ":")); + i -= (z_max-1); + continue; + } + WinDivertFormatHexNumber(stream, addr[i]); + WinDivertPutString(stream, (i != 0? ":": "")); + } +} + +/* + * Format an IPv4 address. + */ +extern BOOL WinDivertHelperFormatIPv4Address(UINT32 addr, char *buffer, + UINT bufLen) +{ + WINDIVERT_STREAM stream; + stream.data = buffer; + stream.pos = 0; + stream.max = bufLen; + stream.overflow = FALSE; + WinDivertFormatIPv4Addr(&stream, addr); + WinDivertPutNul(&stream); + if (stream.overflow) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + return TRUE; +} + +/* + * Format an IPv6 address. + */ +extern BOOL WinDivertHelperFormatIPv6Address(const UINT32 *addr, char *buffer, + UINT bufLen) +{ + WINDIVERT_STREAM stream; + stream.data = buffer; + stream.pos = 0; + stream.max = bufLen; + stream.overflow = FALSE; + WinDivertFormatIPv6Addr(&stream, addr); + WinDivertPutNul(&stream); + if (stream.overflow) + { + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; + } + return TRUE; +} + +/* + * Format a test expression. + */ +static void WinDivertFormatTestExpr(PWINDIVERT_STREAM stream, PEXPR expr, + WINDIVERT_LAYER layer) +{ + PEXPR field = expr->arg[0], val = expr->arg[1]; + BOOL is_ipv4_addr = FALSE, is_ipv6_addr = FALSE, is_layer = FALSE, + is_event = FALSE, is_hex = FALSE; + + switch (field->kind) + { + case TOKEN_ZERO: + case TOKEN_INBOUND: + case TOKEN_OUTBOUND: + case TOKEN_IP: + case TOKEN_IPV6: + case TOKEN_ICMP: + case TOKEN_TCP: + case TOKEN_UDP: + case TOKEN_ICMPV6: + case TOKEN_IP_DF: + case TOKEN_IP_MF: + case TOKEN_TCP_URG: + case TOKEN_TCP_ACK: + case TOKEN_TCP_PSH: + case TOKEN_TCP_RST: + case TOKEN_TCP_SYN: + case TOKEN_TCP_FIN: + case TOKEN_LOOPBACK: + case TOKEN_IMPOSTOR: + if (val->val[1] != 0 || val->val[2] != 0 || val->val[3] != 0 || + val->val[0] > 1) + { + break; + } + switch (expr->kind) + { + case TOKEN_EQ: + WinDivertPutString(stream, (val->val[0] == 0? "not ": "")); + WinDivertFormatExpr(stream, field, layer, + /*top_level=*/FALSE, /*and=*/FALSE); + return; + case TOKEN_NEQ: + WinDivertPutString(stream, (val->val[0] != 0? "not ": "")); + WinDivertFormatExpr(stream, field, layer, + /*top_level=*/FALSE, /*and=*/FALSE); + return; + default: + break; + } + break; + case TOKEN_IP_SRC_ADDR: + case TOKEN_IP_DST_ADDR: + is_ipv4_addr = TRUE; + break; + case TOKEN_IPV6_SRC_ADDR: + case TOKEN_IPV6_DST_ADDR: + case TOKEN_LOCAL_ADDR: + case TOKEN_REMOTE_ADDR: + is_ipv6_addr = TRUE; + break; + case TOKEN_LAYER: + is_layer = TRUE; + break; + case TOKEN_EVENT: + is_event = TRUE; + break; + case TOKEN_PACKET: + case TOKEN_PACKET16: + case TOKEN_PACKET32: + case TOKEN_IP_ID: + case TOKEN_IP_CHECKSUM: + case TOKEN_TCP_CHECKSUM: + case TOKEN_TCP_PAYLOAD: + case TOKEN_TCP_PAYLOAD16: + case TOKEN_TCP_PAYLOAD32: + case TOKEN_UDP_CHECKSUM: + case TOKEN_UDP_PAYLOAD: + case TOKEN_UDP_PAYLOAD16: + case TOKEN_UDP_PAYLOAD32: + case TOKEN_ICMP_CHECKSUM: + case TOKEN_ICMPV6_CHECKSUM: + is_hex = TRUE; + break; + default: + break; + } + + WinDivertFormatExpr(stream, field, layer, /*top_level=*/FALSE, + /*and=*/FALSE); + switch (expr->kind) + { + case TOKEN_EQ: + WinDivertPutString(stream, " = "); break; + case TOKEN_NEQ: + WinDivertPutString(stream, " != "); break; + case TOKEN_LT: + WinDivertPutString(stream, " < "); break; + case TOKEN_LEQ: + WinDivertPutString(stream, " <= "); break; + case TOKEN_GT: + WinDivertPutString(stream, " > "); break; + case TOKEN_GEQ: + WinDivertPutString(stream, " >= "); break; + } + if (is_ipv4_addr) + { + WinDivertFormatIPv4Addr(stream, val->val[0]); + } + else if (is_ipv6_addr) + { + WinDivertFormatIPv6Addr(stream, val->val); + } + else if (is_layer) + { + switch (val->val[0]) + { + case WINDIVERT_LAYER_NETWORK: + WinDivertPutString(stream, "NETWORK"); break; + case WINDIVERT_LAYER_NETWORK_FORWARD: + WinDivertPutString(stream, "NETWORK_FORWARD"); break; + case WINDIVERT_LAYER_FLOW: + WinDivertPutString(stream, "FLOW"); break; + case WINDIVERT_LAYER_SOCKET: + WinDivertPutString(stream, "SOCKET"); break; + case WINDIVERT_LAYER_REFLECT: + WinDivertPutString(stream, "REFLECT"); break; + default: + WinDivertFormatNumber(stream, val->val[0]); break; + } + } + else if (is_event) + { + switch (layer) + { + case WINDIVERT_LAYER_NETWORK: + case WINDIVERT_LAYER_NETWORK_FORWARD: + if (val->val[0] == WINDIVERT_EVENT_NETWORK_PACKET) + { + WinDivertPutString(stream, "PACKET"); + } + else + { + WinDivertFormatNumber(stream, val->val[0]); + } + break; + case WINDIVERT_LAYER_FLOW: + switch (val->val[0]) + { + case WINDIVERT_EVENT_FLOW_ESTABLISHED: + WinDivertPutString(stream, "ESTABLISHED"); break; + case WINDIVERT_EVENT_FLOW_DELETED: + WinDivertPutString(stream, "DELETED"); break; + default: + WinDivertFormatNumber(stream, val->val[0]); break; + } + break; + case WINDIVERT_LAYER_SOCKET: + switch (val->val[0]) + { + case WINDIVERT_EVENT_SOCKET_BIND: + WinDivertPutString(stream, "BIND"); break; + case WINDIVERT_EVENT_SOCKET_CONNECT: + WinDivertPutString(stream, "CONNECT"); break; + case WINDIVERT_EVENT_SOCKET_LISTEN: + WinDivertPutString(stream, "LISTEN"); break; + case WINDIVERT_EVENT_SOCKET_ACCEPT: + WinDivertPutString(stream, "ACCEPT"); break; + default: + WinDivertFormatNumber(stream, val->val[0]); break; + } + break; + case WINDIVERT_LAYER_REFLECT: + switch (val->val[0]) + { + case WINDIVERT_EVENT_REFLECT_OPEN: + WinDivertPutString(stream, "OPEN"); break; + case WINDIVERT_EVENT_REFLECT_CLOSE: + WinDivertPutString(stream, "CLOSE"); break; + default: + WinDivertFormatNumber(stream, val->val[0]); break; + } + break; + default: + WinDivertFormatNumber(stream, val->val[0]); break; + } + } + else if (is_hex) + { + WinDivertPutString(stream, "0x"); + WinDivertFormatHexNumber(stream, val->val[0]); + } + else + { + WinDivertFormatNumber(stream, val->val[0]); + } +} + +/* + * Format an expression. + */ +static void WinDivertFormatExpr(PWINDIVERT_STREAM stream, PEXPR expr, + WINDIVERT_LAYER layer, BOOL top_level, BOOL and) +{ + INT idx; + + if (stream->pos >= stream->max) + { + return; + } + + switch (expr->kind) + { + case TOKEN_AND: + if (!top_level && !and) + { + WinDivertPutChar(stream, '('); + } + WinDivertFormatExpr(stream, expr->arg[0], layer, + /*top_level=*/FALSE, /*and=*/TRUE); + WinDivertPutString(stream, " and "); + WinDivertFormatExpr(stream, expr->arg[1], layer, + /*top_level=*/FALSE, /*and=*/TRUE); + if (!top_level && !and) + { + WinDivertPutChar(stream, ')'); + } + return; + case TOKEN_OR: + if (!top_level && and) + { + WinDivertPutChar(stream, '('); + } + WinDivertFormatExpr(stream, expr->arg[0], layer, + /*top_level=*/FALSE, /*and=*/FALSE); + WinDivertPutString(stream, " or "); + WinDivertFormatExpr(stream, expr->arg[1], layer, + /*top_level=*/FALSE, /*and=*/FALSE); + if (!top_level && and) + { + WinDivertPutChar(stream, ')'); + } + return; + case TOKEN_QUESTION: + WinDivertPutChar(stream, '('); + WinDivertFormatExpr(stream, expr->arg[0], layer, + /*top_level=*/TRUE, /*and=*/FALSE); + WinDivertPutString(stream, "? "); + WinDivertFormatExpr(stream, expr->arg[1], layer, + /*top_level=*/TRUE, /*and=*/FALSE); + WinDivertPutString(stream, ": "); + WinDivertFormatExpr(stream, expr->arg[2], layer, + /*top_level=*/TRUE, /*and=*/FALSE); + WinDivertPutChar(stream, ')'); + return; + case TOKEN_TRUE: + WinDivertPutString(stream, "true"); + return; + case TOKEN_FALSE: + WinDivertPutString(stream, "false"); + return; + case TOKEN_EQ: + case TOKEN_NEQ: + case TOKEN_LT: + case TOKEN_LEQ: + case TOKEN_GT: + case TOKEN_GEQ: + WinDivertFormatTestExpr(stream, expr, layer); + return; + case TOKEN_ZERO: + WinDivertPutString(stream, "zero"); return; + case TOKEN_EVENT: + WinDivertPutString(stream, "event"); return; + case TOKEN_RANDOM8: + WinDivertPutString(stream, "random8"); return; + case TOKEN_RANDOM16: + WinDivertPutString(stream, "random16"); return; + case TOKEN_RANDOM32: + WinDivertPutString(stream, "random32"); return; + case TOKEN_PACKET: + WinDivertPutString(stream, "packet"); break; + case TOKEN_PACKET16: + WinDivertPutString(stream, "packet16"); break; + case TOKEN_PACKET32: + WinDivertPutString(stream, "packet32"); break; + case TOKEN_TCP_PAYLOAD: + WinDivertPutString(stream, "tcp.Payload"); break; + case TOKEN_TCP_PAYLOAD16: + WinDivertPutString(stream, "tcp.Payload16"); break; + case TOKEN_TCP_PAYLOAD32: + WinDivertPutString(stream, "tcp.Payload32"); break; + case TOKEN_UDP_PAYLOAD: + WinDivertPutString(stream, "udp.Payload"); break; + case TOKEN_UDP_PAYLOAD16: + WinDivertPutString(stream, "udp.Payload16"); break; + case TOKEN_UDP_PAYLOAD32: + WinDivertPutString(stream, "udp.Payload32"); break; + case TOKEN_INBOUND: + WinDivertPutString(stream, "inbound"); return; + case TOKEN_OUTBOUND: + WinDivertPutString(stream, "outbound"); return; + case TOKEN_IF_IDX: + WinDivertPutString(stream, "ifIdx"); return; + case TOKEN_SUB_IF_IDX: + WinDivertPutString(stream, "subIfIdx"); return; + case TOKEN_IP: + WinDivertPutString(stream, "ip"); return; + case TOKEN_IPV6: + WinDivertPutString(stream, "ipv6"); return; + case TOKEN_ICMP: + WinDivertPutString(stream, "icmp"); return; + case TOKEN_TCP: + WinDivertPutString(stream, "tcp"); return; + case TOKEN_UDP: + WinDivertPutString(stream, "udp"); return; + case TOKEN_ICMPV6: + WinDivertPutString(stream, "icmpv6"); return; + case TOKEN_IP_HDR_LENGTH: + WinDivertPutString(stream, "ip.HdrLength"); return; + case TOKEN_IP_TOS: + WinDivertPutString(stream, "ip.TOS"); return; + case TOKEN_IP_LENGTH: + WinDivertPutString(stream, "ip.Length"); return; + case TOKEN_IP_ID: + WinDivertPutString(stream, "ip.Id"); return; + case TOKEN_IP_DF: + WinDivertPutString(stream, "ip.DF"); return; + case TOKEN_IP_MF: + WinDivertPutString(stream, "ip.MF"); return; + case TOKEN_IP_FRAG_OFF: + WinDivertPutString(stream, "ip.FragOff"); return; + case TOKEN_IP_TTL: + WinDivertPutString(stream, "ip.TTL"); return; + case TOKEN_IP_PROTOCOL: + WinDivertPutString(stream, "ip.Protocol"); return; + case TOKEN_IP_CHECKSUM: + WinDivertPutString(stream, "ip.Checksum"); return; + case TOKEN_IP_SRC_ADDR: + WinDivertPutString(stream, "ip.SrcAddr"); return; + case TOKEN_IP_DST_ADDR: + WinDivertPutString(stream, "ip.DstAddr"); return; + case TOKEN_IPV6_TRAFFIC_CLASS: + WinDivertPutString(stream, "ipv6.TrafficClass"); return; + case TOKEN_IPV6_FLOW_LABEL: + WinDivertPutString(stream, "ipv6.FlowLabel"); return; + case TOKEN_IPV6_LENGTH: + WinDivertPutString(stream, "ipv6.Length"); return; + case TOKEN_IPV6_NEXT_HDR: + WinDivertPutString(stream, "ipv6.NextHdr"); return; + case TOKEN_IPV6_HOP_LIMIT: + WinDivertPutString(stream, "ipv6.HopLimit"); return; + case TOKEN_IPV6_SRC_ADDR: + WinDivertPutString(stream, "ipv6.SrcAddr"); return; + case TOKEN_IPV6_DST_ADDR: + WinDivertPutString(stream, "ipv6.DstAddr"); return; + case TOKEN_ICMP_TYPE: + WinDivertPutString(stream, "icmp.Type"); return; + case TOKEN_ICMP_CODE: + WinDivertPutString(stream, "icmp.Code"); return; + case TOKEN_ICMP_CHECKSUM: + WinDivertPutString(stream, "icmp.Checksum"); return; + case TOKEN_ICMP_BODY: + WinDivertPutString(stream, "icmp.Body"); return; + case TOKEN_ICMPV6_TYPE: + WinDivertPutString(stream, "icmpv6.Type"); return; + case TOKEN_ICMPV6_CODE: + WinDivertPutString(stream, "icmpv6.Code"); return; + case TOKEN_ICMPV6_CHECKSUM: + WinDivertPutString(stream, "icmpv6.Checksum"); return; + case TOKEN_ICMPV6_BODY: + WinDivertPutString(stream, "icmpv6.Body"); return; + case TOKEN_TCP_SRC_PORT: + WinDivertPutString(stream, "tcp.SrcPort"); return; + case TOKEN_TCP_DST_PORT: + WinDivertPutString(stream, "tcp.DstPort"); return; + case TOKEN_TCP_SEQ_NUM: + WinDivertPutString(stream, "tcp.SeqNum"); return; + case TOKEN_TCP_ACK_NUM: + WinDivertPutString(stream, "tcp.AckNum"); return; + case TOKEN_TCP_HDR_LENGTH: + WinDivertPutString(stream, "tcp.HdrLength"); return; + case TOKEN_TCP_URG: + WinDivertPutString(stream, "tcp.Urg"); return; + case TOKEN_TCP_ACK: + WinDivertPutString(stream, "tcp.Ack"); return; + case TOKEN_TCP_PSH: + WinDivertPutString(stream, "tcp.Psh"); return; + case TOKEN_TCP_RST: + WinDivertPutString(stream, "tcp.Rst"); return; + case TOKEN_TCP_SYN: + WinDivertPutString(stream, "tcp.Syn"); return; + case TOKEN_TCP_FIN: + WinDivertPutString(stream, "tcp.Fin"); return; + case TOKEN_TCP_WINDOW: + WinDivertPutString(stream, "tcp.Window"); return; + case TOKEN_TCP_CHECKSUM: + WinDivertPutString(stream, "tcp.Checksum"); return; + case TOKEN_TCP_URG_PTR: + WinDivertPutString(stream, "tcp.UrgPtr"); return; + case TOKEN_TCP_PAYLOAD_LENGTH: + WinDivertPutString(stream, "tcp.PayloadLength"); return; + case TOKEN_UDP_SRC_PORT: + WinDivertPutString(stream, "udp.SrcPort"); return; + case TOKEN_UDP_DST_PORT: + WinDivertPutString(stream, "udp.DstPort"); return; + case TOKEN_UDP_LENGTH: + WinDivertPutString(stream, "udp.Length"); return; + case TOKEN_UDP_CHECKSUM: + WinDivertPutString(stream, "udp.Checksum"); return; + case TOKEN_UDP_PAYLOAD_LENGTH: + WinDivertPutString(stream, "udp.PayloadLength"); return; + case TOKEN_LOOPBACK: + WinDivertPutString(stream, "loopback"); return; + case TOKEN_IMPOSTOR: + WinDivertPutString(stream, "impostor"); return; + case TOKEN_PROCESS_ID: + WinDivertPutString(stream, "processId"); return; + case TOKEN_LOCAL_ADDR: + WinDivertPutString(stream, "localAddr"); return; + case TOKEN_REMOTE_ADDR: + WinDivertPutString(stream, "remoteAddr"); return; + case TOKEN_LOCAL_PORT: + WinDivertPutString(stream, "localPort"); return; + case TOKEN_REMOTE_PORT: + WinDivertPutString(stream, "remotePort"); return; + case TOKEN_PROTOCOL: + WinDivertPutString(stream, "protocol"); return; + case TOKEN_LAYER: + WinDivertPutString(stream, "layer"); return; + case TOKEN_NUMBER: + WinDivertFormatNumber(stream, expr->val[0]); return; + } + + WinDivertPutChar(stream, '['); + idx = (INT)expr->val[0]; + if (idx < 0) + { + WinDivertPutChar(stream, '-'); + idx = -idx; + } + WinDivertFormatNumber(stream, (UINT32)idx); + WinDivertPutString(stream, "b]"); +} + +/* + * Format a filter string. + */ +BOOL WinDivertHelperFormatFilter(const char *filter, WINDIVERT_LAYER layer, + char *buffer, UINT buflen) +{ + PEXPR exprs[WINDIVERT_FILTER_MAXLEN], expr; + ERROR err; + WINDIVERT_FILTER object[WINDIVERT_FILTER_MAXLEN]; + UINT obj_len; + INT i; + HANDLE pool; + WINDIVERT_STREAM stream; + ERROR error; + const SIZE_T min_pool_size = 8192; + + if (filter == NULL || buffer == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + err = WinDivertCompileFilter(filter, layer, object, &obj_len); + if (IS_ERROR(err)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + pool = HeapCreate(HEAP_NO_SERIALIZE, min_pool_size, 16 * min_pool_size); + if (pool == NULL) + { + return FALSE; + } + + // Decompile all tests: + for (i = (INT)obj_len-1; i >= 0; i--) + { + expr = WinDivertDecompileTest(pool, object + i); + if (expr == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + exprs[i] = expr; + switch (expr->succ) + { + case WINDIVERT_FILTER_RESULT_ACCEPT: + case WINDIVERT_FILTER_RESULT_REJECT: + break; + default: + exprs[expr->succ]->count++; + break; + } + switch (expr->fail) + { + case WINDIVERT_FILTER_RESULT_ACCEPT: + case WINDIVERT_FILTER_RESULT_REJECT: + break; + default: + exprs[expr->fail]->count++; + break; + } + } + exprs[0]->count++; + + // Coalesce (unflatten) tests into and/or expressions: + for (i = (INT)obj_len-1; i >= 0; i--) + { + error = MAKE_ERROR(WINDIVERT_ERROR_NONE, 0); + (PVOID)WinDivertCoalesceAndOr(pool, exprs, i, &error); + if (IS_ERROR(error)) + { + HeapDestroy(pool); + return FALSE; + } + } + + // Coalesce remaining expressions: + expr = WinDivertCoalesceExpr(pool, exprs, 0); + if (expr == NULL) + { + HeapDestroy(pool); + return FALSE; + } + + // Format the final expression: + stream.data = buffer; + stream.pos = 0; + stream.max = buflen; + stream.overflow = FALSE; + WinDivertFormatExpr(&stream, expr, layer, /*top_level=*/TRUE, + /*and=*/FALSE); + WinDivertPutNul(&stream); + + // Clean-up: + HeapDestroy(pool); + if (!stream.overflow) + { + return TRUE; + } + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return FALSE; +} + +/* + * WinDivert packet hash function. + */ +extern UINT64 WinDivertHelperHashPacket(const VOID *pPacket, UINT packetLen, + UINT64 seed) +{ + PWINDIVERT_IPHDR ip_header = NULL; + PWINDIVERT_IPV6HDR ipv6_header = NULL; + PWINDIVERT_ICMPHDR icmp_header = NULL; + PWINDIVERT_ICMPV6HDR icmpv6_header = NULL; + PWINDIVERT_TCPHDR tcp_header = NULL; + PWINDIVERT_UDPHDR udp_header = NULL; + + WinDivertParsePacket((PVOID)pPacket, packetLen, &ip_header, &ipv6_header, + &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL, + NULL, NULL); + return WinDivertHashPacket(seed, ip_header, ipv6_header, icmp_header, + icmpv6_header, tcp_header, udp_header); +} + +/* + * Byte ordering. + */ +extern UINT16 WinDivertHelperNtohs(UINT16 x) +{ + return BYTESWAP16(x); +} +extern UINT16 WinDivertHelperHtons(UINT16 x) +{ + return BYTESWAP16(x); +} +extern UINT32 WinDivertHelperNtohl(UINT32 x) +{ + return BYTESWAP32(x); +} +extern UINT32 WinDivertHelperHtonl(UINT32 x) +{ + return BYTESWAP32(x); +} +extern UINT64 WinDivertHelperNtohll(UINT64 x) +{ + return BYTESWAP64(x); +} +extern UINT64 WinDivertHelperHtonll(UINT64 x) +{ + return BYTESWAP64(x); +} +static void WinDivertByteSwap128(const UINT *inAddr, UINT *outAddr) +{ + UINT32 tmp[4], i; // tmp[] allows overlapping + for (i = 0; i < 4; i++) + { + tmp[3-i] = BYTESWAP32(inAddr[0]); + } + for (i = 0; i < 4; i++) + { + outAddr[i] = tmp[i]; + } +} +extern void WinDivertHelperNtohIpv6Address(const UINT *inAddr, UINT *outAddr) +{ + WinDivertByteSwap128(inAddr, outAddr); +} +extern void WinDivertHelperHtonIpv6Address(const UINT *inAddr, UINT *outAddr) +{ + WinDivertByteSwap128(inAddr, outAddr); +} + diff --git a/dll/windivert_shared.c b/dll/windivert_shared.c new file mode 100644 index 0000000..dfe2dd0 --- /dev/null +++ b/dll/windivert_shared.c @@ -0,0 +1,639 @@ +/* + * windivert_shared.c + * (C) 2018, all rights reserved, + * + * This file is part of WinDivert. + * + * WinDivert is free software: you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + * + * WinDivert is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#define WINDIVERT_OBJECT_MAXLEN \ + (8 + 4 + 2 + WINDIVERT_FILTER_MAXLEN * (1 + 2 + 2 + 4*7 + 2 + 2) + 1) + +#define MAX(a, b) ((a) > (b)? (a): (b)) + +/* + * Definitions to remove (some) external dependencies: + */ +#define BYTESWAP16(x) \ + ((((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00)) +#define BYTESWAP32(x) \ + ((((x) >> 24) & 0x000000FF) | (((x) >> 8) & 0x0000FF00) | \ + (((x) << 8) & 0x00FF0000) | (((x) << 24) & 0xFF000000)) +#define BYTESWAP64(x) \ + ((((x) >> 56) & 0x00000000000000FFull) | \ + (((x) >> 40) & 0x000000000000FF00ull) | \ + (((x) >> 24) & 0x0000000000FF0000ull) | \ + (((x) >> 8) & 0x00000000FF000000ull) | \ + (((x) << 8) & 0x000000FF00000000ull) | \ + (((x) << 24) & 0x0000FF0000000000ull) | \ + (((x) << 40) & 0x00FF000000000000ull) | \ + (((x) << 56) & 0xFF00000000000000ull)) +#define ntohs(x) BYTESWAP16(x) +#define htons(x) BYTESWAP16(x) +#define ntohl(x) BYTESWAP32(x) +#define htonl(x) BYTESWAP32(x) + +#include "windivert_hash.c" + +/* + * IPv4/IPv6 pseudo headers. + */ +typedef struct +{ + UINT32 SrcAddr; + UINT32 DstAddr; + UINT8 Zero; + UINT8 Protocol; + UINT16 Length; +} WINDIVERT_PSEUDOHDR, *PWINDIVERT_PSEUDOHDR; + +typedef struct +{ + UINT32 SrcAddr[4]; + UINT32 DstAddr[4]; + UINT32 Length; + UINT32 Zero:24; + UINT32 NextHdr:8; +} WINDIVERT_PSEUDOV6HDR, *PWINDIVERT_PSEUDOV6HDR; + +/* + * Streams. + */ +typedef struct +{ + char *data; + UINT pos; + UINT max; + BOOL overflow; +} WINDIVERT_STREAM, *PWINDIVERT_STREAM; + +/* + * Prototypes. + */ +static UINT16 WinDivertInitPseudoHeader(PWINDIVERT_IPHDR ip_header, + PWINDIVERT_IPV6HDR ipv6_header, UINT8 protocol, UINT len, + void *pseudo_header); +static UINT16 WinDivertCalcChecksum(PVOID pseudo_header, + UINT16 pseudo_header_len, PVOID data, UINT len); + +/* + * Put a char into a stream. + */ +static void WinDivertPutChar(PWINDIVERT_STREAM stream, char c) +{ + if (stream->pos >= stream->max) + { + stream->overflow = TRUE; + return; + } + stream->data[stream->pos] = c; + stream->pos++; +} + +/* + * Put a string into a stream. + */ +static void WinDivertPutString(PWINDIVERT_STREAM stream, const char *str) +{ + while (*str) + { + WinDivertPutChar(stream, *str); + str++; + } +} + +/* + * Put a NUL character into a stream. + */ +static void WinDivertPutNul(PWINDIVERT_STREAM stream) +{ + if (stream->pos >= stream->max && stream->max > 0) + { + stream->data[stream->max-1] = '\0'; // Truncate + } + else + { + WinDivertPutChar(stream, '\0'); + } +} + +/* + * Serialize a number. + */ +static void WinDivertSerializeNumber(PWINDIVERT_STREAM stream, UINT32 val) +{ + UINT64 mask = 0x00000007C0000000ull; + UINT dig = 6; + UINT64 val64 = (UINT64)val; + BOOL final; + + while ((mask & val64) == 0 && dig != 0) + { + mask >>= 5; + dig--; + } + while (TRUE) + { + final = (dig == 0); + WinDivertPutChar(stream, '!' + (char)((mask & val64) >> (5 * dig)) + + (final? 32: 0)); + if (final) + { + break; + } + mask >>= 5; + dig--; + } +} + +/* + * Serialize a test. + */ +static void WinDivertSerializeTest(PWINDIVERT_STREAM stream, + const WINDIVERT_FILTER *filter) +{ + INT idx; + UINT i; + + WinDivertPutChar(stream, '_'); + WinDivertSerializeNumber(stream, filter->field); + WinDivertSerializeNumber(stream, filter->test); + WinDivertSerializeNumber(stream, filter->arg[0]); + switch (filter->field) + { + case WINDIVERT_FILTER_FIELD_IPV6_SRCADDR: + case WINDIVERT_FILTER_FIELD_IPV6_DSTADDR: + case WINDIVERT_FILTER_FIELD_LOCALADDR: + case WINDIVERT_FILTER_FIELD_REMOTEADDR: + for (i = 1; i < 4; i++) + { + WinDivertSerializeNumber(stream, filter->arg[i]); + } + break; + case WINDIVERT_FILTER_FIELD_PACKET: + case WINDIVERT_FILTER_FIELD_PACKET16: + case WINDIVERT_FILTER_FIELD_PACKET32: + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD: + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD16: + case WINDIVERT_FILTER_FIELD_TCP_PAYLOAD32: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD16: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOAD32: + idx = (INT)filter->arg[1]; + idx += UINT16_MAX; + WinDivertSerializeNumber(stream, (UINT32)idx); + break; + default: + break; + } + WinDivertSerializeNumber(stream, (UINT8)(filter->success + 2)); + WinDivertSerializeNumber(stream, (UINT8)(filter->failure + 2)); +} + +/* + * Serialize a test. + */ +static void WinDivertSerializeFilter(PWINDIVERT_STREAM stream, + const WINDIVERT_FILTER *filter, UINT8 length) +{ + UINT8 i; + WinDivertPutString(stream, "@WinDiv_"); // Magic + WinDivertSerializeNumber(stream, 0); // Version + WinDivertSerializeNumber(stream, length); // Length + for (i = 0; i < length; i++) + { + WinDivertSerializeTest(stream, filter + i); + } + WinDivertPutNul(stream); +} + +/* + * Skip well-known IPv6 extension headers. + */ +static UINT8 WinDivertSkipExtHeaders(UINT8 proto, UINT8 **header, UINT *len) +{ + UINT hdrlen; + + while (TRUE) + { + if (*len <= 2) + { + return IPPROTO_NONE; + } + + hdrlen = (UINT)*(*header + 1); + switch (proto) + { + case IPPROTO_FRAGMENT: + hdrlen = 8; + break; + case IPPROTO_AH: + hdrlen += 2; + hdrlen *= 4; + break; + case IPPROTO_HOPOPTS: + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + hdrlen++; + hdrlen *= 8; + break; + case IPPROTO_NONE: + return proto; + default: + return proto; + } + + if (hdrlen >= *len) + { + return IPPROTO_NONE; + } + + proto = **header; + *header += hdrlen; + *len -= hdrlen; + } +} + +/* + * Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet. + */ +static BOOL WinDivertParsePacket(PVOID pPacket, UINT packetLen, + PWINDIVERT_IPHDR *ppIpHdr, PWINDIVERT_IPV6HDR *ppIpv6Hdr, + PWINDIVERT_ICMPHDR *ppIcmpHdr, PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr, + PWINDIVERT_TCPHDR *ppTcpHdr, PWINDIVERT_UDPHDR *ppUdpHdr, UINT8 *pProto, + PVOID *ppData, UINT *pDataLen) +{ + PWINDIVERT_IPHDR ip_header = NULL; + PWINDIVERT_IPV6HDR ipv6_header = NULL; + PWINDIVERT_ICMPHDR icmp_header = NULL; + PWINDIVERT_ICMPV6HDR icmpv6_header = NULL; + PWINDIVERT_TCPHDR tcp_header = NULL; + PWINDIVERT_UDPHDR udp_header = NULL; + UINT16 header_len; + UINT8 trans_proto = 0; + PVOID data = NULL; + UINT data_len = 0; + BOOL success; + + if (pPacket == NULL || packetLen < sizeof(UINT8)) + { + goto WinDivertHelperParsePacketExit; + } + data = pPacket; + data_len = packetLen; + + ip_header = (PWINDIVERT_IPHDR)data; + switch (ip_header->Version) + { + case 4: + if (data_len < sizeof(WINDIVERT_IPHDR) || + ip_header->HdrLength < 5 || + data_len < ip_header->HdrLength*sizeof(UINT32) || + ntohs(ip_header->Length) != data_len) + { + ip_header = NULL; + goto WinDivertHelperParsePacketExit; + } + trans_proto = ip_header->Protocol; + header_len = ip_header->HdrLength*sizeof(UINT32); + data = (PVOID)((UINT8 *)data + header_len); + data_len -= header_len; + break; + case 6: + ip_header = NULL; + ipv6_header = (PWINDIVERT_IPV6HDR)data; + if (data_len < sizeof(WINDIVERT_IPV6HDR) || + ntohs(ipv6_header->Length) != + data_len - sizeof(WINDIVERT_IPV6HDR)) + { + ipv6_header = NULL; + goto WinDivertHelperParsePacketExit; + } + trans_proto = ipv6_header->NextHdr; + data = (PVOID)((UINT8 *)data + sizeof(WINDIVERT_IPV6HDR)); + data_len -= sizeof(WINDIVERT_IPV6HDR); + trans_proto = WinDivertSkipExtHeaders(trans_proto, (UINT8 **)&data, + &data_len); + break; + default: + ip_header = NULL; + goto WinDivertHelperParsePacketExit; + } + switch (trans_proto) + { + case IPPROTO_TCP: + tcp_header = (PWINDIVERT_TCPHDR)data; + if (data_len < sizeof(WINDIVERT_TCPHDR) || + tcp_header->HdrLength < 5 || + data_len < tcp_header->HdrLength*sizeof(UINT32)) + { + tcp_header = NULL; + goto WinDivertHelperParsePacketExit; + } + header_len = tcp_header->HdrLength*sizeof(UINT32); + data = ((UINT8 *)data + header_len); + data_len -= header_len; + break; + case IPPROTO_UDP: + udp_header = (PWINDIVERT_UDPHDR)data; + if (data_len < sizeof(WINDIVERT_UDPHDR) || + ntohs(udp_header->Length) != data_len) + { + udp_header = NULL; + goto WinDivertHelperParsePacketExit; + } + data = ((UINT8 *)data + sizeof(WINDIVERT_UDPHDR)); + data_len -= sizeof(WINDIVERT_UDPHDR); + break; + case IPPROTO_ICMP: + icmp_header = (PWINDIVERT_ICMPHDR)data; + if (ip_header == NULL || + data_len < sizeof(WINDIVERT_ICMPHDR)) + { + icmp_header = NULL; + goto WinDivertHelperParsePacketExit; + } + data = ((UINT8 *)data + sizeof(WINDIVERT_ICMPHDR)); + data_len -= sizeof(WINDIVERT_ICMPHDR); + break; + case IPPROTO_ICMPV6: + icmpv6_header = (PWINDIVERT_ICMPV6HDR)data; + if (ipv6_header == NULL || + data_len < sizeof(WINDIVERT_ICMPV6HDR)) + { + icmpv6_header = NULL; + goto WinDivertHelperParsePacketExit; + } + data = ((UINT8 *)data + sizeof(WINDIVERT_ICMPV6HDR)); + data_len -= sizeof(WINDIVERT_ICMPV6HDR); + break; + default: + break; + } + + if (data_len == 0) + { + data = NULL; + } + +WinDivertHelperParsePacketExit: + success = TRUE; + if (ppIpHdr != NULL) + { + *ppIpHdr = ip_header; + success = success && (ip_header != NULL); + } + if (ppIpv6Hdr != NULL) + { + *ppIpv6Hdr = ipv6_header; + success = success && (ipv6_header != NULL); + } + if (ppIcmpHdr != NULL) + { + *ppIcmpHdr = icmp_header; + success = success && (icmp_header != NULL); + } + if (ppIcmpv6Hdr != NULL) + { + *ppIcmpv6Hdr = icmpv6_header; + success = success && (icmpv6_header != NULL); + } + if (ppTcpHdr != NULL) + { + *ppTcpHdr = tcp_header; + success = success && (tcp_header != NULL); + } + if (ppUdpHdr != NULL) + { + *ppUdpHdr = udp_header; + success = success && (udp_header != NULL); + } + if (pProto != NULL) + { + *pProto = trans_proto; + } + if (ppData != NULL) + { + *ppData = data; + success = success && (data != NULL); + } + if (pDataLen != NULL) + { + *pDataLen = data_len; + } + return success; +} + +/* + * Calculate IPv4/IPv6/ICMP/ICMPv6/TCP/UDP checksums. + */ +extern UINT WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, + const WINDIVERT_ADDRESS *pAddr, UINT64 flags) +{ + UINT8 pseudo_header[ + MAX(sizeof(WINDIVERT_PSEUDOHDR), sizeof(WINDIVERT_PSEUDOV6HDR))]; + UINT16 pseudo_header_len; + PWINDIVERT_IPHDR ip_header; + PWINDIVERT_IPV6HDR ipv6_header; + PWINDIVERT_ICMPHDR icmp_header; + PWINDIVERT_ICMPV6HDR icmpv6_header; + PWINDIVERT_TCPHDR tcp_header; + PWINDIVERT_UDPHDR udp_header; + UINT payload_len, checksum_len; + UINT count = 0; + + WinDivertParsePacket(pPacket, packetLen, &ip_header, &ipv6_header, + &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL, NULL, + &payload_len); + + if (ip_header != NULL && !(flags & WINDIVERT_HELPER_NO_IP_CHECKSUM)) + { + ip_header->Checksum = 0; + if (pAddr == NULL || pAddr->PseudoIPChecksum == 0) + { + ip_header->Checksum = WinDivertCalcChecksum(NULL, 0, + ip_header, ip_header->HdrLength*sizeof(UINT32)); + } + count++; + } + + if (icmp_header != NULL) + { + if ((flags & WINDIVERT_HELPER_NO_ICMP_CHECKSUM) != 0) + { + return count; + } + icmp_header->Checksum = 0; + icmp_header->Checksum = WinDivertCalcChecksum(NULL, 0, + icmp_header, payload_len + sizeof(WINDIVERT_ICMPHDR)); + count++; + return count; + } + + if (icmpv6_header != NULL) + { + if ((flags & WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM) != 0) + { + return count; + } + checksum_len = payload_len + sizeof(WINDIVERT_ICMPV6HDR); + pseudo_header_len = WinDivertInitPseudoHeader(NULL, ipv6_header, + IPPROTO_ICMPV6, checksum_len, pseudo_header); + icmpv6_header->Checksum = 0; + icmpv6_header->Checksum = WinDivertCalcChecksum(pseudo_header, + pseudo_header_len, icmpv6_header, checksum_len); + count++; + return count; + } + + if (tcp_header != NULL) + { + if ((flags & WINDIVERT_HELPER_NO_TCP_CHECKSUM) != 0) + { + return count; + } + if (pAddr == NULL || pAddr->PseudoTCPChecksum == 0) + { + // Full TCP checksum + checksum_len = payload_len + tcp_header->HdrLength*sizeof(UINT32); + pseudo_header_len = WinDivertInitPseudoHeader(ip_header, + ipv6_header, IPPROTO_TCP, checksum_len, pseudo_header); + tcp_header->Checksum = 0; + tcp_header->Checksum = WinDivertCalcChecksum( + pseudo_header, pseudo_header_len, tcp_header, checksum_len); + } + else if (pAddr->Outbound) + { + // Pseudo TCP checksum + tcp_header->Checksum = 0; + } + count++; + return count; + } + + if (udp_header != NULL) + { + if ((flags & WINDIVERT_HELPER_NO_UDP_CHECKSUM) != 0) + { + return count; + } + if (pAddr == NULL || pAddr->PseudoUDPChecksum == 0) + { + // Full UDP checksum + checksum_len = payload_len + sizeof(WINDIVERT_UDPHDR); + pseudo_header_len = WinDivertInitPseudoHeader(ip_header, + ipv6_header, IPPROTO_UDP, checksum_len, pseudo_header); + udp_header->Checksum = 0; + udp_header->Checksum = WinDivertCalcChecksum( + pseudo_header, pseudo_header_len, udp_header, checksum_len); + if (udp_header->Checksum == 0) + { + udp_header->Checksum = 0xFFFF; + } + } + else if (pAddr->Outbound) + { + // Pseudo UDP checksum + udp_header->Checksum = 0; + } + count++; + } + return count; +} + +/* + * Initialize the IP/IPv6 pseudo header. + */ +static UINT16 WinDivertInitPseudoHeader(PWINDIVERT_IPHDR ip_header, + PWINDIVERT_IPV6HDR ipv6_header, UINT8 protocol, UINT len, + void *pseudo_header) +{ + if (ip_header != NULL) + { + PWINDIVERT_PSEUDOHDR pseudo_header_v4 = + (PWINDIVERT_PSEUDOHDR)pseudo_header; + pseudo_header_v4->SrcAddr = ip_header->SrcAddr; + pseudo_header_v4->DstAddr = ip_header->DstAddr; + pseudo_header_v4->Zero = 0; + pseudo_header_v4->Protocol = protocol; + pseudo_header_v4->Length = htons((UINT16)len); + return sizeof(WINDIVERT_PSEUDOHDR); + } + else + { + PWINDIVERT_PSEUDOV6HDR pseudo_header_v6 = + (PWINDIVERT_PSEUDOV6HDR)pseudo_header; + memcpy(pseudo_header_v6->SrcAddr, ipv6_header->SrcAddr, + sizeof(pseudo_header_v6->SrcAddr)); + memcpy(pseudo_header_v6->DstAddr, ipv6_header->DstAddr, + sizeof(pseudo_header_v6->DstAddr)); + pseudo_header_v6->Length = htonl((UINT32)len); + pseudo_header_v6->NextHdr = protocol; + pseudo_header_v6->Zero = 0; + return sizeof(WINDIVERT_PSEUDOV6HDR); + } +} + +/* + * Generic checksum computation. + */ +static UINT16 WinDivertCalcChecksum(PVOID pseudo_header, + UINT16 pseudo_header_len, PVOID data, UINT len) +{ + register const UINT16 *data16 = (const UINT16 *)pseudo_header; + register size_t len16 = pseudo_header_len >> 1; + register UINT32 sum = 0; + size_t i; + + // Pseudo header: + for (i = 0; i < len16; i++) + { + sum += (UINT32)data16[i]; + } + + // Main data: + data16 = (const UINT16 *)data; + len16 = len >> 1; + for (i = 0; i < len16; i++) + { + sum += (UINT32)data16[i]; + } + + if (len & 0x1) + { + const UINT8 *data8 = (const UINT8 *)data; + sum += (UINT16)data8[len-1]; + } + + sum = (sum & 0xFFFF) + (sum >> 16); + sum += (sum >> 16); + sum = ~sum; + return (UINT16)sum; +} + diff --git a/doc/windivert.html b/doc/windivert.html index 0f7b1e0..4f08cf2 100644 --- a/doc/windivert.html +++ b/doc/windivert.html @@ -1,10 +1,10 @@ - WinDivert 1.4 Documentation + WinDivert 2.0 Documentation -

WinDivert 1.4: Windows Packet Divert

+

WinDivert 2.0: Windows Packet Divert

Table of Contents