/* * windivert_helper.c * (C) 2021, 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. */ /****************************************************************************/ /* WINDIVERT HELPER IMPLEMENTATION */ /****************************************************************************/ /* * Protocols. */ #define IPPROTO_HOPOPTS 0 #define IPPROTO_ICMP 1 #define IPPROTO_TCP 6 #define IPPROTO_UDP 17 #define IPPROTO_ROUTING 43 #define IPPROTO_FRAGMENT 44 #define IPPROTO_AH 51 #define IPPROTO_ICMPV6 58 #define IPPROTO_NONE 59 #define IPPROTO_DSTOPTS 60 /* * Filter tokens. */ typedef enum { TOKEN_ICMP, TOKEN_ICMP_BODY, TOKEN_ICMP_CHECKSUM, TOKEN_ICMP_CODE, TOKEN_ICMP_TYPE, TOKEN_ICMPV6, TOKEN_ICMPV6_BODY, TOKEN_ICMPV6_CHECKSUM, TOKEN_ICMPV6_CODE, TOKEN_ICMPV6_TYPE, TOKEN_IP, TOKEN_IP_CHECKSUM, TOKEN_IP_DF, TOKEN_IP_DST_ADDR, TOKEN_IP_FRAG_OFF, TOKEN_IP_HDR_LENGTH, TOKEN_IP_ID, TOKEN_IP_LENGTH, TOKEN_IP_MF, TOKEN_IP_PROTOCOL, TOKEN_IP_SRC_ADDR, TOKEN_IP_TOS, TOKEN_IP_TTL, TOKEN_IPV6, TOKEN_IPV6_DST_ADDR, TOKEN_IPV6_FLOW_LABEL, TOKEN_IPV6_HOP_LIMIT, TOKEN_IPV6_LENGTH, TOKEN_IPV6_NEXT_HDR, TOKEN_IPV6_SRC_ADDR, TOKEN_IPV6_TRAFFIC_CLASS, TOKEN_TCP, TOKEN_TCP_ACK, TOKEN_TCP_ACK_NUM, TOKEN_TCP_CHECKSUM, 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, TOKEN_TCP_SEQ_NUM, TOKEN_TCP_SRC_PORT, TOKEN_TCP_SYN, TOKEN_TCP_URG, TOKEN_TCP_URG_PTR, TOKEN_TCP_WINDOW, TOKEN_UDP, 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_LENGTH, TOKEN_TIMESTAMP, TOKEN_TRUE, TOKEN_FALSE, TOKEN_INBOUND, TOKEN_OUTBOUND, TOKEN_FRAGMENT, TOKEN_IF_IDX, 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_ENDPOINT_ID, TOKEN_PARENT_ENDPOINT_ID, TOKEN_LAYER, TOKEN_PRIORITY, 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_MACRO_TRUE, TOKEN_MACRO_FALSE, TOKEN_MACRO_TCP, TOKEN_MACRO_UDP, TOKEN_MACRO_ICMP, TOKEN_MACRO_ICMPV6, TOKEN_OPEN, TOKEN_CLOSE, TOKEN_SQUARE_OPEN, TOKEN_SQUARE_CLOSE, TOKEN_MINUS, TOKEN_BYTES, TOKEN_EQ, TOKEN_NEQ, TOKEN_LT, TOKEN_LEQ, TOKEN_GT, TOKEN_GEQ, TOKEN_NOT, TOKEN_AND, TOKEN_OR, TOKEN_COLON, TOKEN_QUESTION, TOKEN_NUMBER, TOKEN_END, } KIND; typedef struct { KIND kind; UINT pos; UINT32 val[4]; } TOKEN; #define TOKEN_MAXLEN 40 typedef struct { char *name; KIND kind; UINT8 flags; } TOKEN_INFO, *PTOKEN_INFO; /* * Filter expressions. */ typedef struct EXPR EXPR; typedef struct EXPR *PEXPR; struct EXPR { union { UINT32 val[4]; PEXPR arg[3]; }; UINT8 kind; UINT8 count; BOOLEAN neg; UINT16 succ; UINT16 fail; }; /* * Error handling. */ #undef ERROR typedef UINT64 ERROR, *PERROR; #define WINDIVERT_ERROR_NONE 0 #define WINDIVERT_ERROR_NO_MEMORY 1 #define WINDIVERT_ERROR_TOO_DEEP 2 #define WINDIVERT_ERROR_TOO_LONG 3 #define WINDIVERT_ERROR_BAD_TOKEN 4 #define WINDIVERT_ERROR_BAD_TOKEN_FOR_LAYER 5 #define WINDIVERT_ERROR_UNEXPECTED_TOKEN 6 #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 WINDIVERT_MIN_POOL_SIZE 12288 #define WINDIVERT_MAX_POOL_SIZE 131072 #define MAKE_ERROR(code, pos) \ (((ERROR)(code) << 32) | (ERROR)(pos)); #define GET_CODE(err) \ ((UINT)((err) >> 32)) #define GET_POS(err) \ ((UINT)((err) & 0xFFFFFFFF)) #undef IS_ERROR #define IS_ERROR(err) \ (GET_CODE(err) != WINDIVERT_ERROR_NONE) /* * Prototypes. */ static UINT32 WinDivertKindToField(KIND kind); 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 int WinDivertCompare128(BOOL neg_a, const UINT32 *a, BOOL neg_b, const UINT32 *b, BOOL big); 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 an IPv4 address. */ BOOL WinDivertHelperParseIPv4Address(const char *str, UINT32 *addr_ptr) { UINT32 addr = 0; UINT32 part, i; if (str == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } for (i = 0; i < 4; i++) { if (!WinDivertAToI(str, (char **)&str, &part, 1) || part > UINT8_MAX) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (i != 3 && *str++ != '.') { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } addr |= part << (8*(3-i)); } if (*str != '\0') { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if (addr_ptr != NULL) { *addr_ptr = addr; } return TRUE; } /* * Parse an IPv6 address. */ BOOL WinDivertHelperParseIPv6Address(const char *str, UINT32 *addr_ptr) { 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]; j = 0; if (*str == ':') { str++; if (*str != ':') { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } left = FALSE; str++; if (*str == '\0') { goto WinDivertHelperParseIPv6AddressSuccess; } } for (i = 0, k = 0; k < 8; k++) { if (*str == ':') { if (!left) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } 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++) { part_str[l] = *str; str++; } if (l == 0) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } part_str[l] = '\0'; if (*str != ':' && *str != '\0') { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } WinDivertAToX(part_str, NULL, &part, /*size=*/1, /*prefix=*/FALSE); if (left) { laddr[i++] = (UINT16)part; } else { raddr[j++] = (UINT16)part; } if (*str == '\0') { if (!left || k == 7) { break; } SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } str++; } 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[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_INFO WinDivertTokenLookup(PTOKEN_INFO token_info, size_t token_info_len, const char *name) { int lo = 0, hi = (int)token_info_len-1, mid; int cmp; while (hi >= lo) { mid = (lo + hi) / 2; cmp = WinDivertStrCmp(token_info[mid].name, name); if (cmp < 0) { lo = mid+1; } else if (cmp > 0) { hi = mid-1; } else { return &token_info[mid]; } } return NULL; } /* * Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet. */ BOOL WinDivertHelperParsePacket(const VOID *pPacket, UINT packetLen, PWINDIVERT_IPHDR *ppIPHeader, PWINDIVERT_IPV6HDR *ppIPv6Header, UINT8 *pProtocol, PWINDIVERT_ICMPHDR *ppICMPHeader, PWINDIVERT_ICMPV6HDR *ppICMPv6Header, PWINDIVERT_TCPHDR *ppTCPHeader, PWINDIVERT_UDPHDR *ppUDPHeader, PVOID *ppData, UINT *pDataLen, PVOID *ppNext, UINT *pNextLen) { WINDIVERT_PACKET info; if (!WinDivertHelperParsePacketEx(pPacket, packetLen, &info)) { return FALSE; } if (info.Truncated) { return FALSE; } if (pProtocol != NULL) { *pProtocol = info.Protocol; } if (ppIPHeader != NULL) { *ppIPHeader = info.IPHeader; } if (ppIPv6Header != NULL) { *ppIPv6Header = info.IPv6Header; } if (ppICMPHeader != NULL) { *ppICMPHeader = info.ICMPHeader; } if (ppICMPv6Header != NULL) { *ppICMPv6Header = info.ICMPv6Header; } if (ppTCPHeader != NULL) { *ppTCPHeader = info.TCPHeader; } if (ppUDPHeader != NULL) { *ppUDPHeader = info.UDPHeader; } if (ppData != NULL) { *ppData = info.Payload; } if (pDataLen != NULL) { *pDataLen = info.PayloadLength; } if (ppNext != NULL) { *ppNext = (info.Extended? (PVOID)((UINT8 *)pPacket + (info.HeaderLength + info.PayloadLength)): NULL); } if (pNextLen != NULL) { *pNextLen = (info.Extended? packetLen - (info.HeaderLength + info.PayloadLength): 0); } return TRUE; } /* * 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 || layer == WINDIVERT_LAYER_NETWORK_FORWARD); 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: switch (layer) { case WINDIVERT_LAYER_SOCKET: *val = WINDIVERT_EVENT_SOCKET_CLOSE; return TRUE; case WINDIVERT_LAYER_REFLECT: *val = WINDIVERT_EVENT_REFLECT_CLOSE; return TRUE; default: return FALSE; } case TOKEN_MACRO_TRUE: *val = 1; return TRUE; case TOKEN_MACRO_FALSE: *val = 0; return TRUE; case TOKEN_MACRO_TCP: *val = IPPROTO_TCP; return TRUE; case TOKEN_MACRO_UDP: *val = IPPROTO_UDP; return TRUE; case TOKEN_MACRO_ICMP: *val = IPPROTO_ICMP; return TRUE; case TOKEN_MACRO_ICMPV6: *val = IPPROTO_ICMPV6; return TRUE; default: return FALSE; } } /* * Tokenize the given filter string. */ static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, TOKEN *tokens, UINT tokensmax) { static const TOKEN_INFO token_info[] = { {"ACCEPT", TOKEN_EVENT_ACCEPT }, {"BIND", TOKEN_EVENT_BIND }, {"CLOSE", TOKEN_EVENT_CLOSE }, {"CONNECT", TOKEN_EVENT_CONNECT }, {"DELETED", TOKEN_EVENT_DELETED }, {"ESTABLISHED", TOKEN_EVENT_ESTABLISHED }, {"FALSE", TOKEN_MACRO_FALSE }, {"FLOW", TOKEN_FLOW }, {"ICMP", TOKEN_MACRO_ICMP }, {"ICMPV6", TOKEN_MACRO_ICMPV6 }, {"LISTEN", TOKEN_EVENT_LISTEN }, {"NETWORK", TOKEN_NETWORK }, {"NETWORK_FORWARD", TOKEN_NETWORK_FORWARD }, {"OPEN", TOKEN_EVENT_OPEN }, {"PACKET", TOKEN_EVENT_PACKET }, {"REFLECT", TOKEN_REFLECT }, {"SOCKET", TOKEN_SOCKET }, {"TCP", TOKEN_MACRO_TCP }, {"TRUE", TOKEN_MACRO_TRUE }, {"UDP", TOKEN_MACRO_UDP }, {"and", TOKEN_AND }, {"endpointId", TOKEN_ENDPOINT_ID }, {"event", TOKEN_EVENT }, {"false", TOKEN_FALSE }, {"fragment", TOKEN_FRAGMENT }, {"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}, {"layer", TOKEN_LAYER }, {"length", TOKEN_LENGTH }, {"localAddr", TOKEN_LOCAL_ADDR }, {"localPort", TOKEN_LOCAL_PORT }, {"loopback", TOKEN_LOOPBACK }, {"not", TOKEN_NOT }, {"or", TOKEN_OR }, {"outbound", TOKEN_OUTBOUND }, {"packet", TOKEN_PACKET }, {"packet16", TOKEN_PACKET16 }, {"packet32", TOKEN_PACKET32 }, {"parentEndpointId", TOKEN_PARENT_ENDPOINT_ID}, {"priority", TOKEN_PRIORITY }, {"processId", TOKEN_PROCESS_ID }, {"protocol", TOKEN_PROTOCOL }, {"random16", TOKEN_RANDOM16 }, {"random32", TOKEN_RANDOM32 }, {"random8", TOKEN_RANDOM8 }, {"remoteAddr", TOKEN_REMOTE_ADDR }, {"remotePort", TOKEN_REMOTE_PORT }, {"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.Payload", TOKEN_TCP_PAYLOAD }, {"tcp.Payload16", TOKEN_TCP_PAYLOAD16 }, {"tcp.Payload32", TOKEN_TCP_PAYLOAD32 }, {"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 }, {"timestamp", TOKEN_TIMESTAMP }, {"true", TOKEN_TRUE }, {"udp", TOKEN_UDP }, {"udp.Checksum", TOKEN_UDP_CHECKSUM }, {"udp.DstPort", TOKEN_UDP_DST_PORT }, {"udp.Length", TOKEN_UDP_LENGTH }, {"udp.Payload", TOKEN_UDP_PAYLOAD }, {"udp.Payload16", TOKEN_UDP_PAYLOAD16 }, {"udp.Payload32", TOKEN_UDP_PAYLOAD32 }, {"udp.PayloadLength", TOKEN_UDP_PAYLOAD_LENGTH}, {"udp.SrcPort", TOKEN_UDP_SRC_PORT }, {"zero", TOKEN_ZERO }, }; TOKEN_INFO *result; char c; char token[TOKEN_MAXLEN]; UINT32 field; UINT i = 0, j; UINT tp = 0; while (TRUE) { if (tp >= tokensmax-1) { return MAKE_ERROR(WINDIVERT_ERROR_TOO_LONG, i); } memset(tokens[tp].val, 0, sizeof(tokens[tp].val)); while (WinDivertIsSpace(filter[i])) { i++; } tokens[tp].pos = i; c = filter[i++]; switch (c) { case '\0': tokens[tp].kind = TOKEN_END; return MAKE_ERROR(WINDIVERT_ERROR_NONE, 0); case '(': tokens[tp++].kind = TOKEN_OPEN; continue; 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] == '=') { i++; tokens[tp++].kind = TOKEN_NEQ; } else { tokens[tp++].kind = TOKEN_NOT; } continue; case '=': if (filter[i] == '=') { i++; } tokens[tp++].kind = TOKEN_EQ; continue; case '<': if (filter[i] == '=') { i++; tokens[tp++].kind = TOKEN_LEQ; } else { tokens[tp++].kind = TOKEN_LT; } continue; case '>': if (filter[i] == '=') { i++; tokens[tp++].kind = TOKEN_GEQ; } else { tokens[tp++].kind = TOKEN_GT; } continue; case ':': if (filter[i] == ':') { break; // Probably ipv6 address, e.g. ::1. } tokens[tp++].kind = TOKEN_COLON; continue; case '?': tokens[tp++].kind = TOKEN_QUESTION; continue; case '&': if (filter[i++] != '&') { return MAKE_ERROR(WINDIVERT_ERROR_BAD_TOKEN, i-1); } tokens[tp++].kind = TOKEN_AND; continue; case '|': if (filter[i++] != '|') { return MAKE_ERROR(WINDIVERT_ERROR_BAD_TOKEN, i-1); } tokens[tp++].kind = TOKEN_OR; continue; default: break; } token[0] = c; if (WinDivertIsAlNum(c) || c == '.' || c == ':' || c == '_') { UINT32 num[4]; char *end; for (j = 1; j < TOKEN_MAXLEN && (WinDivertIsAlNum(filter[i]) || filter[i] == '.' || filter[i] == ':' || filter[i] == '_'); j++, i++) { token[j] = filter[i]; } if (j >= TOKEN_MAXLEN) { return MAKE_ERROR(WINDIVERT_ERROR_BAD_TOKEN, i-j); } token[j] = '\0'; // Handle trailing colons: if (j >= 1 && token[j-1] == ':') { if (j == 1 || token[j-2] != ':') { token[j-1] = '\0'; i--; } } // Check for symbol: result = WinDivertTokenLookup((PTOKEN_INFO)token_info, sizeof(token_info) / sizeof(TOKEN_INFO), token); if (result != NULL) { field = WinDivertKindToField(result->kind); if (field <= WINDIVERT_FILTER_FIELD_MAX && !WinDivertValidateField(layer, field)) { 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; } tp++; continue; } // Check for 'b': if (token[0] == 'b' && token[1] == '\0') { tokens[tp].kind = TOKEN_BYTES; continue; } // Check for base 10 number: if (WinDivertAToI(token, &end, num, sizeof(num)/sizeof(num[0]))) { BOOL b = (*end == 'b' && *(end+1) == '\0'); if (*end == '\0' || b) { tokens[tp].kind = TOKEN_NUMBER; memcpy(tokens[tp].val, num, sizeof(tokens[tp].val)); tp++; if (b) { memset(tokens[tp].val, 0, sizeof(tokens[tp].val)); tokens[tp].kind = TOKEN_BYTES; tp++; } continue; } } // Check for base 16 number: if (token[0] == '0' && token[1] == 'x' && WinDivertAToX(token, &end, num, sizeof(num)/sizeof(num[0]), /*prefix=*/TRUE) && *end == '\0') { tokens[tp].kind = TOKEN_NUMBER; memcpy(tokens[tp].val, num, sizeof(tokens[tp].val)); tp++; continue; } // Check for IPv4 address: if (WinDivertHelperParseIPv4Address(token, tokens[tp].val)) { tokens[tp].val[1] = 0x0000FFFF; tokens[tp].kind = TOKEN_NUMBER; tp++; continue; } // Check for IPv6 address: SetLastError(0); if (WinDivertHelperParseIPv6Address(token, tokens[tp].val)) { tokens[tp].kind = TOKEN_NUMBER; tp++; continue; } return MAKE_ERROR(WINDIVERT_ERROR_BAD_TOKEN, i-j); } else { return MAKE_ERROR(WINDIVERT_ERROR_BAD_TOKEN, i); } } } /* * Construct a variable/field. */ static PEXPR WinDivertMakeVar(KIND kind, PERROR error) { // NOTE: must be in order of kind. static const EXPR vars[] = { {{{0}}, TOKEN_ICMP}, {{{0}}, TOKEN_ICMP_BODY}, {{{0}}, TOKEN_ICMP_CHECKSUM}, {{{0}}, TOKEN_ICMP_CODE}, {{{0}}, TOKEN_ICMP_TYPE}, {{{0}}, TOKEN_ICMPV6}, {{{0}}, TOKEN_ICMPV6_BODY}, {{{0}}, TOKEN_ICMPV6_CHECKSUM}, {{{0}}, TOKEN_ICMPV6_CODE}, {{{0}}, TOKEN_ICMPV6_TYPE}, {{{0}}, TOKEN_IP}, {{{0}}, TOKEN_IP_CHECKSUM}, {{{0}}, TOKEN_IP_DF}, {{{0}}, TOKEN_IP_DST_ADDR}, {{{0}}, TOKEN_IP_FRAG_OFF}, {{{0}}, TOKEN_IP_HDR_LENGTH}, {{{0}}, TOKEN_IP_ID}, {{{0}}, TOKEN_IP_LENGTH}, {{{0}}, TOKEN_IP_MF}, {{{0}}, TOKEN_IP_PROTOCOL}, {{{0}}, TOKEN_IP_SRC_ADDR}, {{{0}}, TOKEN_IP_TOS}, {{{0}}, TOKEN_IP_TTL}, {{{0}}, TOKEN_IPV6}, {{{0}}, TOKEN_IPV6_DST_ADDR}, {{{0}}, TOKEN_IPV6_FLOW_LABEL}, {{{0}}, TOKEN_IPV6_HOP_LIMIT}, {{{0}}, TOKEN_IPV6_LENGTH}, {{{0}}, TOKEN_IPV6_NEXT_HDR}, {{{0}}, TOKEN_IPV6_SRC_ADDR}, {{{0}}, TOKEN_IPV6_TRAFFIC_CLASS}, {{{0}}, TOKEN_TCP}, {{{0}}, TOKEN_TCP_ACK}, {{{0}}, TOKEN_TCP_ACK_NUM}, {{{0}}, TOKEN_TCP_CHECKSUM}, {{{0}}, TOKEN_TCP_DST_PORT}, {{{0}}, TOKEN_TCP_FIN}, {{{0}}, TOKEN_TCP_HDR_LENGTH}, {{{0}}, TOKEN_TCP_PAYLOAD_LENGTH}, {{{0}}, TOKEN_TCP_PSH}, {{{0}}, TOKEN_TCP_RST}, {{{0}}, TOKEN_TCP_SEQ_NUM}, {{{0}}, TOKEN_TCP_SRC_PORT}, {{{0}}, TOKEN_TCP_SYN}, {{{0}}, TOKEN_TCP_URG}, {{{0}}, TOKEN_TCP_URG_PTR}, {{{0}}, TOKEN_TCP_WINDOW}, {{{0}}, TOKEN_UDP}, {{{0}}, TOKEN_UDP_CHECKSUM}, {{{0}}, TOKEN_UDP_DST_PORT}, {{{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_LENGTH}, {{{0}}, TOKEN_TIMESTAMP}, {{{0}}, TOKEN_TRUE}, {{{0}}, TOKEN_FALSE}, {{{0}}, TOKEN_INBOUND}, {{{0}}, TOKEN_OUTBOUND}, {{{0}}, TOKEN_FRAGMENT}, {{{0}}, TOKEN_IF_IDX}, {{{0}}, TOKEN_SUB_IF_IDX}, {{{0}}, TOKEN_LOOPBACK}, {{{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_ENDPOINT_ID}, {{{0}}, TOKEN_PARENT_ENDPOINT_ID}, {{{0}}, TOKEN_LAYER}, {{{0}}, TOKEN_PRIORITY}, }; // Binary search: UINT lo = 0, hi = sizeof(vars) / sizeof(vars[0]) - 1, mid; while (lo <= hi) { mid = (hi + lo) / 2; if (vars[mid].kind < kind) { lo = mid + 1; continue; } if (vars[mid].kind > kind) { hi = mid - 1; continue; } return (PEXPR)(vars + mid); } if (error != NULL) { *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(void) { static const EXPR zero = {{{0, 0, 0, 0}}, TOKEN_NUMBER}; return (PEXPR)&zero; } /* * Construct one. */ static PEXPR WinDivertMakeOne(void) { static const EXPR one = {{{1, 0, 0, 0}}, TOKEN_NUMBER}; return (PEXPR)&one; } /* * Construct a number. */ static PEXPR WinDivertMakeNumber(HANDLE pool, UINT32 *val, PERROR error) { PEXPR expr = (PEXPR)HeapAlloc(pool, HEAP_ZERO_MEMORY, sizeof(EXPR)); if (expr == NULL) { *error = MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); return NULL; } expr->kind = TOKEN_NUMBER; 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(HANDLE pool, KIND kind, PEXPR arg0, PEXPR arg1, PERROR error) { PEXPR expr; if (arg0 == NULL || arg1 == NULL) { return NULL; } expr = (PEXPR)HeapAlloc(pool, HEAP_ZERO_MEMORY, sizeof(EXPR)); if (expr == NULL) { *error = MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); return NULL; } expr->kind = kind; expr->arg[0] = arg0; expr->arg[1] = arg1; return expr; } /* * Construct an if-then-else. */ static PEXPR WinDivertMakeIfThenElse(HANDLE pool, PEXPR cond, PEXPR th, PEXPR el, PERROR error) { PEXPR expr = (PEXPR)HeapAlloc(pool, HEAP_ZERO_MEMORY, sizeof(EXPR)); if (expr == NULL) { *error = MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); return NULL; } expr->kind = TOKEN_QUESTION; expr->arg[0] = cond; expr->arg[1] = th; expr->arg[2] = el; return expr; } /* * Parse a filter test. */ static PEXPR WinDivertParseTest(HANDLE pool, TOKEN *toks, UINT *i, PERROR error) { PEXPR var, val; KIND kind; BOOL not = FALSE, neg; UINT idx, size; while (toks[*i].kind == TOKEN_NOT) { not = !not; *i = *i + 1; } switch (toks[*i].kind) { case TOKEN_TIMESTAMP: case TOKEN_PRIORITY: case TOKEN_ZERO: case TOKEN_EVENT: case TOKEN_RANDOM8: case TOKEN_RANDOM16: case TOKEN_RANDOM32: case TOKEN_TRUE: case TOKEN_FALSE: case TOKEN_OUTBOUND: case TOKEN_INBOUND: case TOKEN_FRAGMENT: case TOKEN_IF_IDX: case TOKEN_SUB_IF_IDX: case TOKEN_LOOPBACK: case TOKEN_IMPOSTOR: case TOKEN_IP: case TOKEN_IPV6: case TOKEN_ICMP: 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_ENDPOINT_ID: case TOKEN_PARENT_ENDPOINT_ID: case TOKEN_LENGTH: case TOKEN_LAYER: case TOKEN_IP_HDR_LENGTH: case TOKEN_IP_TOS: case TOKEN_IP_LENGTH: case TOKEN_IP_ID: case TOKEN_IP_DF: case TOKEN_IP_MF: case TOKEN_IP_FRAG_OFF: case TOKEN_IP_TTL: case TOKEN_IP_PROTOCOL: case TOKEN_IP_CHECKSUM: case TOKEN_IP_SRC_ADDR: case TOKEN_IP_DST_ADDR: case TOKEN_IPV6_TRAFFIC_CLASS: case TOKEN_IPV6_FLOW_LABEL: case TOKEN_IPV6_LENGTH: case TOKEN_IPV6_NEXT_HDR: case TOKEN_IPV6_HOP_LIMIT: case TOKEN_IPV6_SRC_ADDR: case TOKEN_IPV6_DST_ADDR: case TOKEN_ICMP_TYPE: case TOKEN_ICMP_CODE: case TOKEN_ICMP_CHECKSUM: case TOKEN_ICMP_BODY: case TOKEN_ICMPV6_TYPE: case TOKEN_ICMPV6_CODE: case TOKEN_ICMPV6_CHECKSUM: case TOKEN_ICMPV6_BODY: case TOKEN_TCP_SRC_PORT: case TOKEN_TCP_DST_PORT: case TOKEN_TCP_SEQ_NUM: case TOKEN_TCP_ACK_NUM: case TOKEN_TCP_HDR_LENGTH: 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_TCP_WINDOW: case TOKEN_TCP_CHECKSUM: case TOKEN_TCP_URG_PTR: case TOKEN_TCP_PAYLOAD_LENGTH: case TOKEN_UDP_SRC_PORT: case TOKEN_UDP_DST_PORT: 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] > WINDIVERT_MTU_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: unexpected_token: *error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, toks[*i].pos); return NULL; } switch (toks[*i].kind) { case TOKEN_EQ: case TOKEN_NEQ: case TOKEN_LT: case TOKEN_LEQ: case TOKEN_GT: case TOKEN_GEQ: kind = toks[*i].kind; break; default: return WinDivertMakeBinOp(pool, (not? TOKEN_EQ: TOKEN_NEQ), var, WinDivertMakeZero(), error); } if (not) { switch (kind) { case TOKEN_EQ: kind = TOKEN_NEQ; break; case TOKEN_NEQ: kind = TOKEN_EQ; break; case TOKEN_LT: kind = TOKEN_GEQ; break; case TOKEN_LEQ: kind = TOKEN_GT; break; case TOKEN_GT: kind = TOKEN_LEQ; break; case TOKEN_GEQ: kind = TOKEN_LT; break; default: break; } } *i = *i + 1; neg = FALSE; if (toks[*i].kind == TOKEN_MINUS) { neg = TRUE; *i = *i + 1; } if (toks[*i].kind != TOKEN_NUMBER) { *error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, toks[*i].pos); return NULL; } val = WinDivertMakeNumber(pool, toks[*i].val, error); val->neg = neg; *i = *i + 1; return WinDivertMakeBinOp(pool, kind, var, val, error); } /* * Parse a filter argument to an (and) (or) operator. */ static PEXPR WinDivertParseAndOrArg(HANDLE pool, TOKEN *toks, UINT *i, INT depth, PERROR error) { PEXPR arg, th, el; if (depth-- < 0) { *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, error); if (toks[*i].kind == TOKEN_CLOSE) { *i = *i + 1; return arg; } if (toks[*i].kind == TOKEN_QUESTION) { *i = *i + 1; th = WinDivertParseFilter(pool, toks, i, depth, FALSE, error); if (th == NULL) { return NULL; } if (toks[*i].kind != TOKEN_COLON) { *error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, toks[*i].pos); return NULL; } *i = *i + 1; el = WinDivertParseFilter(pool, toks, i, depth, FALSE, error); if (el == NULL) { return NULL; } if (toks[*i].kind != TOKEN_CLOSE) { *error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, toks[*i].pos); return NULL; } *i = *i + 1; arg = WinDivertMakeIfThenElse(pool, arg, th, el, error); return arg; } *error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, toks[*i].pos); return NULL; default: return WinDivertParseTest(pool, toks, i, error); } } /* * Parse the filter into an expression object. */ static PEXPR WinDivertParseFilter(HANDLE pool, TOKEN *toks, UINT *i, INT depth, BOOL and, PERROR error) { PEXPR expr, arg; if (depth-- < 0) { *error = MAKE_ERROR(WINDIVERT_ERROR_TOO_DEEP, toks[*i].pos); return NULL; } if (and) expr = WinDivertParseAndOrArg(pool, toks, i, depth, error); else expr = WinDivertParseFilter(pool, toks, i, depth, TRUE, error); do { if (expr == NULL) { return NULL; } switch (toks[*i].kind) { case TOKEN_AND: *i = *i + 1; arg = WinDivertParseAndOrArg(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, error); expr = WinDivertMakeBinOp(pool, TOKEN_OR, expr, arg, error); continue; default: return expr; } } while (TRUE); } /* * Simplify a test if possible. */ static void WinDivertSimplifyTest(PEXPR test) { PEXPR var = test->arg[0]; PEXPR val = test->arg[1]; BOOL neg_lb = FALSE, neg_ub = FALSE, neg; UINT32 lb[4] = {0}, ub[4] = {0}; int result_lb, result_ub; BOOL eq = FALSE, result = FALSE; KIND type = TOKEN_TRUE; switch (var->kind) { case TOKEN_ZERO: case TOKEN_FALSE: eq = TRUE; lb[0] = ub[0] = 0; break; case TOKEN_TRUE: eq = TRUE; lb[0] = ub[0] = 1; break; case TOKEN_LAYER: lb[0] = 0; ub[0] = WINDIVERT_LAYER_MAX; break; case TOKEN_PRIORITY: neg_lb = TRUE; lb[0] = ub[0] = WINDIVERT_PRIORITY_MAX; break; case TOKEN_EVENT: lb[0] = 0; ub[0] = WINDIVERT_EVENT_MAX; break; case TOKEN_IP_DF: case TOKEN_IP_MF: type = TOKEN_IP; lb[0] = 0; ub[0] = 1; break; case TOKEN_TCP_URG: case TOKEN_TCP_ACK: case TOKEN_TCP_PSH: case TOKEN_TCP_RST: case TOKEN_TCP_SYN: case TOKEN_TCP_FIN: type = TOKEN_TCP; lb[0] = 0; ub[0] = 1; break; case TOKEN_INBOUND: case TOKEN_OUTBOUND: case TOKEN_FRAGMENT: case TOKEN_IP: case TOKEN_IPV6: case TOKEN_ICMP: case TOKEN_ICMPV6: case TOKEN_TCP: case TOKEN_UDP: lb[0] = 0; ub[0] = 1; break; case TOKEN_IP_HDR_LENGTH: type = TOKEN_IP; lb[0] = 0; ub[0] = 0x0F; break; case TOKEN_TCP_HDR_LENGTH: type = TOKEN_TCP; lb[0] = 0; ub[0] = 0x0F; break; case TOKEN_IP_TTL: case TOKEN_IP_PROTOCOL: type = TOKEN_IP; lb[0] = 0; ub[0] = 0xFF; break; case TOKEN_IPV6_TRAFFIC_CLASS: case TOKEN_IPV6_NEXT_HDR: case TOKEN_IPV6_HOP_LIMIT: type = TOKEN_IPV6; lb[0] = 0; ub[0] = 0xFF; break; case TOKEN_ICMP_TYPE: case TOKEN_ICMP_CODE: type = TOKEN_ICMP; lb[0] = 0; ub[0] = 0xFF; break; case TOKEN_ICMPV6_TYPE: case TOKEN_ICMPV6_CODE: type = TOKEN_ICMPV6; lb[0] = 0; ub[0] = 0xFF; break; case TOKEN_TCP_PAYLOAD: type = TOKEN_TCP; lb[0] = 0; ub[0] = 0xFF; break; case TOKEN_UDP_PAYLOAD: type = TOKEN_UDP; lb[0] = 0; ub[0] = 0xFF; break; case TOKEN_PROTOCOL: case TOKEN_PACKET: case TOKEN_RANDOM8: lb[0] = 0; ub[0] = 0xFF; break; case TOKEN_IP_FRAG_OFF: type = TOKEN_IP; lb[0] = 0; ub[0] = 0x1FFF; break; case TOKEN_IP_TOS: case TOKEN_IP_LENGTH: case TOKEN_IP_ID: case TOKEN_IP_CHECKSUM: type = TOKEN_IP; lb[0] = 0; ub[0] = 0xFFFF; break; case TOKEN_IPV6_LENGTH: type = TOKEN_IPV6; lb[0] = 0; ub[0] = 0xFFFF; break; case TOKEN_ICMP_CHECKSUM: type = TOKEN_ICMP; lb[0] = 0; ub[0] = 0xFFFF; break; case TOKEN_ICMPV6_CHECKSUM: type = TOKEN_ICMPV6; lb[0] = 0; ub[0] = 0xFFFF; break; case TOKEN_TCP_SRC_PORT: case TOKEN_TCP_DST_PORT: case TOKEN_TCP_WINDOW: case TOKEN_TCP_CHECKSUM: case TOKEN_TCP_URG_PTR: case TOKEN_TCP_PAYLOAD_LENGTH: case TOKEN_TCP_PAYLOAD16: type = TOKEN_TCP; lb[0] = 0; ub[0] = 0xFFFF; break; case TOKEN_UDP_SRC_PORT: case TOKEN_UDP_DST_PORT: case TOKEN_UDP_LENGTH: case TOKEN_UDP_CHECKSUM: case TOKEN_UDP_PAYLOAD_LENGTH: case TOKEN_UDP_PAYLOAD16: type = TOKEN_UDP; lb[0] = 0; ub[0] = 0xFFFF; break; case TOKEN_LOCAL_PORT: case TOKEN_REMOTE_PORT: case TOKEN_PACKET16: case TOKEN_RANDOM16: lb[0] = 0; ub[0] = 0xFFFF; break; case TOKEN_LENGTH: lb[0] = sizeof(WINDIVERT_IPHDR); ub[0] = WINDIVERT_MTU_MAX; break; case TOKEN_IPV6_FLOW_LABEL: type = TOKEN_IPV6; lb[0] = 0; ub[0] = 0x000FFFFF; break; case TOKEN_IP_SRC_ADDR: case TOKEN_IP_DST_ADDR: type = TOKEN_IP; lb[0] = 0; lb[1] = 0xFFFF; ub[0] = 0xFFFFFFFF; ub[1] = 0xFFFF; break; case TOKEN_IPV6_SRC_ADDR: case TOKEN_IPV6_DST_ADDR: type = TOKEN_IPV6; // Fallthrough case TOKEN_LOCAL_ADDR: case TOKEN_REMOTE_ADDR: lb[0] = lb[1] = lb[2] = lb[3] = 0; ub[0] = ub[1] = ub[2] = ub[3] = 0xFFFFFFFF; break; case TOKEN_TIMESTAMP: lb[0] = 0; lb[1] = 0x80000000; ub[0] = 0xFFFFFFFF; ub[1] = 0x7FFFFFFF; neg_lb = TRUE; break; case TOKEN_TCP_PAYLOAD32: type = TOKEN_TCP; lb[0] = 0; ub[0] = 0xFFFFFFFF; break; case TOKEN_UDP_PAYLOAD32: type = TOKEN_UDP; lb[0] = 0; ub[0] = 0xFFFFFFFF; break; case TOKEN_IF_IDX: case TOKEN_SUB_IF_IDX: case TOKEN_RANDOM32: case TOKEN_PROCESS_ID: lb[0] = 0; ub[0] = 0xFFFFFFFF; break; case TOKEN_ENDPOINT_ID: case TOKEN_PARENT_ENDPOINT_ID: lb[0] = lb[1] = 0; ub[0] = ub[1] = 0xFFFFFFFF; break; default: return; } neg = (val->neg? TRUE: FALSE); result_lb = WinDivertCompare128(neg, val->val, neg_lb, lb, /*big=*/TRUE); result_ub = WinDivertCompare128(neg, val->val, neg_ub, ub, /*big=*/TRUE); switch (test->kind) { case TOKEN_EQ: if (result_lb < 0 || result_ub > 0) { result = FALSE; break; } if (eq && result_lb == 0) { result = TRUE; break; } return; case TOKEN_NEQ: if (result_lb < 0 || result_ub > 0) { result = TRUE; break; } if (eq && result_lb == 0) { result = FALSE; break; } return; case TOKEN_LT: if (result_ub > 0) { result = TRUE; break; } if (result_lb <= 0) { result = FALSE; break; } return; case TOKEN_LEQ: if (result_ub >= 0) { result = TRUE; break; } if (result_lb < 0) { result = FALSE; break; } return; case TOKEN_GT: if (result_ub >= 0) { result = FALSE; break; } if (result_lb < 0) { result = TRUE; break; } return; case TOKEN_GEQ: if (result_ub > 0) { result = FALSE; break; } if (result_lb <= 0) { result = TRUE; break; } return; default: return; } test->arg[0] = WinDivertMakeVar(type, NULL); test->arg[1] = (result? WinDivertMakeOne(): WinDivertMakeZero()); test->kind = TOKEN_EQ; } /* * Flatten an expression into a sequence of tests and jumps. */ static INT16 WinDivertFlattenExpr(PEXPR expr, INT16 *label, INT16 succ, INT16 fail, PEXPR *stack) { INT16 succ1, fail1; if (succ < 0 || fail < 0) { return -1; } switch (expr->kind) { case TOKEN_AND: succ = WinDivertFlattenExpr(expr->arg[1], label, succ, fail, stack); succ = WinDivertFlattenExpr(expr->arg[0], label, succ, fail, stack); return succ; case TOKEN_OR: fail = WinDivertFlattenExpr(expr->arg[1], label, succ, fail, stack); fail = WinDivertFlattenExpr(expr->arg[0], label, succ, fail, stack); return fail; case TOKEN_QUESTION: fail1 = WinDivertFlattenExpr(expr->arg[2], label, succ, fail, stack); succ1 = WinDivertFlattenExpr(expr->arg[1], label, succ, fail, stack); succ = WinDivertFlattenExpr(expr->arg[0], label, succ1, fail1, stack); return succ; default: WinDivertSimplifyTest(expr); if (expr->kind == TOKEN_EQ && expr->arg[0]->kind == TOKEN_TRUE) { return (expr->arg[1]->val[0] != 0? succ: fail); } if (*label >= WINDIVERT_FILTER_MAXLEN) { return -1; } stack[*label] = expr; expr->succ = succ; expr->fail = fail; succ = *label; *label = *label + 1; return succ; } } /* * Convert a kind to a field. */ static UINT32 WinDivertKindToField(KIND kind) { switch (kind) { case TOKEN_ZERO: return WINDIVERT_FILTER_FIELD_ZERO; case TOKEN_EVENT: return WINDIVERT_FILTER_FIELD_EVENT; case TOKEN_RANDOM8: return WINDIVERT_FILTER_FIELD_RANDOM8; case TOKEN_RANDOM16: return WINDIVERT_FILTER_FIELD_RANDOM16; case TOKEN_RANDOM32: return WINDIVERT_FILTER_FIELD_RANDOM32; case TOKEN_PACKET: return WINDIVERT_FILTER_FIELD_PACKET; case TOKEN_PACKET16: return WINDIVERT_FILTER_FIELD_PACKET16; case TOKEN_PACKET32: return WINDIVERT_FILTER_FIELD_PACKET32; case TOKEN_LENGTH: return WINDIVERT_FILTER_FIELD_LENGTH; case TOKEN_TIMESTAMP: return WINDIVERT_FILTER_FIELD_TIMESTAMP; case TOKEN_TCP_PAYLOAD: return WINDIVERT_FILTER_FIELD_TCP_PAYLOAD; case TOKEN_TCP_PAYLOAD16: return WINDIVERT_FILTER_FIELD_TCP_PAYLOAD16; case TOKEN_TCP_PAYLOAD32: return WINDIVERT_FILTER_FIELD_TCP_PAYLOAD32; case TOKEN_UDP_PAYLOAD: return WINDIVERT_FILTER_FIELD_UDP_PAYLOAD; case TOKEN_UDP_PAYLOAD16: return WINDIVERT_FILTER_FIELD_UDP_PAYLOAD16; case TOKEN_UDP_PAYLOAD32: return WINDIVERT_FILTER_FIELD_UDP_PAYLOAD32; case TOKEN_OUTBOUND: return WINDIVERT_FILTER_FIELD_OUTBOUND; case TOKEN_INBOUND: return WINDIVERT_FILTER_FIELD_INBOUND; case TOKEN_FRAGMENT: return WINDIVERT_FILTER_FIELD_FRAGMENT; case TOKEN_IF_IDX: return WINDIVERT_FILTER_FIELD_IFIDX; case TOKEN_SUB_IF_IDX: return WINDIVERT_FILTER_FIELD_SUBIFIDX; case TOKEN_LOOPBACK: return WINDIVERT_FILTER_FIELD_LOOPBACK; case TOKEN_IMPOSTOR: return WINDIVERT_FILTER_FIELD_IMPOSTOR; case TOKEN_PROCESS_ID: return WINDIVERT_FILTER_FIELD_PROCESSID; case TOKEN_LOCAL_ADDR: return WINDIVERT_FILTER_FIELD_LOCALADDR; case TOKEN_REMOTE_ADDR: return WINDIVERT_FILTER_FIELD_REMOTEADDR; case TOKEN_LOCAL_PORT: return WINDIVERT_FILTER_FIELD_LOCALPORT; case TOKEN_REMOTE_PORT: return WINDIVERT_FILTER_FIELD_REMOTEPORT; case TOKEN_PROTOCOL: return WINDIVERT_FILTER_FIELD_PROTOCOL; case TOKEN_ENDPOINT_ID: return WINDIVERT_FILTER_FIELD_ENDPOINTID; case TOKEN_PARENT_ENDPOINT_ID: return WINDIVERT_FILTER_FIELD_PARENTENDPOINTID; case TOKEN_LAYER: return WINDIVERT_FILTER_FIELD_LAYER; case TOKEN_PRIORITY: return WINDIVERT_FILTER_FIELD_PRIORITY; case TOKEN_IP: return WINDIVERT_FILTER_FIELD_IP; case TOKEN_IPV6: return WINDIVERT_FILTER_FIELD_IPV6; case TOKEN_ICMP: return WINDIVERT_FILTER_FIELD_ICMP; case TOKEN_ICMPV6: return WINDIVERT_FILTER_FIELD_ICMPV6; case TOKEN_TCP: return WINDIVERT_FILTER_FIELD_TCP; case TOKEN_UDP: return WINDIVERT_FILTER_FIELD_UDP; case TOKEN_IP_HDR_LENGTH: return WINDIVERT_FILTER_FIELD_IP_HDRLENGTH; case TOKEN_IP_TOS: return WINDIVERT_FILTER_FIELD_IP_TOS; case TOKEN_IP_LENGTH: return WINDIVERT_FILTER_FIELD_IP_LENGTH; case TOKEN_IP_ID: return WINDIVERT_FILTER_FIELD_IP_ID; case TOKEN_IP_DF: return WINDIVERT_FILTER_FIELD_IP_DF; case TOKEN_IP_MF: return WINDIVERT_FILTER_FIELD_IP_MF; case TOKEN_IP_FRAG_OFF: return WINDIVERT_FILTER_FIELD_IP_FRAGOFF; case TOKEN_IP_TTL: return WINDIVERT_FILTER_FIELD_IP_TTL; case TOKEN_IP_PROTOCOL: return WINDIVERT_FILTER_FIELD_IP_PROTOCOL; case TOKEN_IP_CHECKSUM: return WINDIVERT_FILTER_FIELD_IP_CHECKSUM; case TOKEN_IP_SRC_ADDR: return WINDIVERT_FILTER_FIELD_IP_SRCADDR; case TOKEN_IP_DST_ADDR: return WINDIVERT_FILTER_FIELD_IP_DSTADDR; case TOKEN_IPV6_TRAFFIC_CLASS: return WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS; case TOKEN_IPV6_FLOW_LABEL: return WINDIVERT_FILTER_FIELD_IPV6_FLOWLABEL; case TOKEN_IPV6_LENGTH: return WINDIVERT_FILTER_FIELD_IPV6_LENGTH; case TOKEN_IPV6_NEXT_HDR: return WINDIVERT_FILTER_FIELD_IPV6_NEXTHDR; case TOKEN_IPV6_HOP_LIMIT: return WINDIVERT_FILTER_FIELD_IPV6_HOPLIMIT; case TOKEN_IPV6_SRC_ADDR: return WINDIVERT_FILTER_FIELD_IPV6_SRCADDR; case TOKEN_IPV6_DST_ADDR: return WINDIVERT_FILTER_FIELD_IPV6_DSTADDR; case TOKEN_ICMP_TYPE: return WINDIVERT_FILTER_FIELD_ICMP_TYPE; case TOKEN_ICMP_CODE: return WINDIVERT_FILTER_FIELD_ICMP_CODE; case TOKEN_ICMP_CHECKSUM: return WINDIVERT_FILTER_FIELD_ICMP_CHECKSUM; case TOKEN_ICMP_BODY: return WINDIVERT_FILTER_FIELD_ICMP_BODY; case TOKEN_ICMPV6_TYPE: return WINDIVERT_FILTER_FIELD_ICMPV6_TYPE; case TOKEN_ICMPV6_CODE: return WINDIVERT_FILTER_FIELD_ICMPV6_CODE; case TOKEN_ICMPV6_CHECKSUM: return WINDIVERT_FILTER_FIELD_ICMPV6_CHECKSUM; case TOKEN_ICMPV6_BODY: return WINDIVERT_FILTER_FIELD_ICMPV6_BODY; case TOKEN_TCP_SRC_PORT: return WINDIVERT_FILTER_FIELD_TCP_SRCPORT; case TOKEN_TCP_DST_PORT: return WINDIVERT_FILTER_FIELD_TCP_DSTPORT; case TOKEN_TCP_SEQ_NUM: return WINDIVERT_FILTER_FIELD_TCP_SEQNUM; case TOKEN_TCP_ACK_NUM: return WINDIVERT_FILTER_FIELD_TCP_ACKNUM; case TOKEN_TCP_HDR_LENGTH: return WINDIVERT_FILTER_FIELD_TCP_HDRLENGTH; case TOKEN_TCP_URG: return WINDIVERT_FILTER_FIELD_TCP_URG; case TOKEN_TCP_ACK: return WINDIVERT_FILTER_FIELD_TCP_ACK; case TOKEN_TCP_PSH: return WINDIVERT_FILTER_FIELD_TCP_PSH; case TOKEN_TCP_RST: return WINDIVERT_FILTER_FIELD_TCP_RST; case TOKEN_TCP_SYN: return WINDIVERT_FILTER_FIELD_TCP_SYN; case TOKEN_TCP_FIN: return WINDIVERT_FILTER_FIELD_TCP_FIN; case TOKEN_TCP_WINDOW: return WINDIVERT_FILTER_FIELD_TCP_WINDOW; case TOKEN_TCP_CHECKSUM: return WINDIVERT_FILTER_FIELD_TCP_CHECKSUM; case TOKEN_TCP_URG_PTR: return WINDIVERT_FILTER_FIELD_TCP_URGPTR; case TOKEN_TCP_PAYLOAD_LENGTH: return WINDIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH; case TOKEN_UDP_SRC_PORT: return WINDIVERT_FILTER_FIELD_UDP_SRCPORT; case TOKEN_UDP_DST_PORT: return WINDIVERT_FILTER_FIELD_UDP_DSTPORT; case TOKEN_UDP_LENGTH: return WINDIVERT_FILTER_FIELD_UDP_LENGTH; case TOKEN_UDP_CHECKSUM: return WINDIVERT_FILTER_FIELD_UDP_CHECKSUM; case TOKEN_UDP_PAYLOAD_LENGTH: return WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH; default: return UINT32_MAX; } } /* * Emit a test. */ static void WinDivertEmitTest(PEXPR test, UINT16 offset, PWINDIVERT_FILTER object) { PEXPR var = test->arg[0], val = test->arg[1]; switch (test->kind) { case TOKEN_EQ: object->test = WINDIVERT_FILTER_TEST_EQ; break; case TOKEN_NEQ: object->test = WINDIVERT_FILTER_TEST_NEQ; break; case TOKEN_LT: object->test = WINDIVERT_FILTER_TEST_LT; break; case TOKEN_LEQ: object->test = WINDIVERT_FILTER_TEST_LEQ; break; case TOKEN_GT: object->test = WINDIVERT_FILTER_TEST_GT; break; case TOKEN_GEQ: object->test = WINDIVERT_FILTER_TEST_GEQ; break; default: return; } object->field = WinDivertKindToField(var->kind); object->neg = (val->neg? 1: 0); object->arg[0] = val->val[0]; object->arg[1] = val->val[1]; object->arg[2] = val->val[2]; object->arg[3] = val->val[3]; switch (var->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: object->arg[1] = var->val[0]; break; default: break; } switch (test->succ) { case WINDIVERT_FILTER_RESULT_ACCEPT: case WINDIVERT_FILTER_RESULT_REJECT: object->success = test->succ; break; default: object->success = offset - test->succ; break; } switch (test->fail) { case WINDIVERT_FILTER_RESULT_ACCEPT: case WINDIVERT_FILTER_RESULT_REJECT: object->failure = test->fail; break; default: object->failure = offset - test->fail; break; } return; } /* * Emit a filter object. */ static void WinDivertEmitFilter(PEXPR *stack, UINT len, UINT16 label, PWINDIVERT_FILTER object, UINT *obj_len) { UINT i; switch (label) { case WINDIVERT_FILTER_RESULT_ACCEPT: case WINDIVERT_FILTER_RESULT_REJECT: object[0].field = WINDIVERT_FILTER_FIELD_ZERO; object[0].test = WINDIVERT_FILTER_TEST_EQ; object[0].neg = 0; object[0].arg[0] = 0; object[0].arg[1] = 0; object[0].arg[2] = 0; object[0].arg[3] = 0; object[0].success = label; object[0].failure = label; *obj_len = 1; return; default: break; } *obj_len = len + 1; for (i = 0; i <= len; i++) { WinDivertEmitTest(stack[len - i], label, object + i); } } /* * 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_CLOSE); flags |= (result? WINDIVERT_FILTER_FLAG_EVENT_SOCKET_CLOSE: 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; UINT16 succ, fail; BOOLEAN result[WINDIVERT_FILTER_MAXLEN]; BOOLEAN result_succ, result_fail, result_test; if (length == 0) { return TRUE; } for (ip = (INT16)(length-1); ip >= 0; ip--) { succ = filter[ip].success; switch (succ) { case WINDIVERT_FILTER_RESULT_ACCEPT: result_succ = TRUE; break; case WINDIVERT_FILTER_RESULT_REJECT: result_succ = FALSE; break; default: result_succ = (succ > ip && succ < length? result[succ]: TRUE); break; } fail = filter[ip].failure; switch (fail) { case WINDIVERT_FILTER_RESULT_ACCEPT: result_fail = TRUE; break; case WINDIVERT_FILTER_RESULT_REJECT: result_fail = FALSE; break; default: result_fail = (fail > ip && fail < length? result[fail]: TRUE); break; } if (result_succ && result_fail) { result[ip] = TRUE; } else if (!result_succ && !result_fail) { result[ip] = FALSE; } else if (filter[ip].field == field) { if (filter[ip].neg || filter[ip].arg[1] != 0 || filter[ip].arg[2] != 0 || filter[ip].arg[3] != 0) { result[ip] = TRUE; } else { 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: result[ip] = TRUE; continue; } 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, HANDLE pool, WINDIVERT_LAYER layer, PWINDIVERT_FILTER object, UINT *obj_len) { TOKEN *tokens; PEXPR *stack; PEXPR expr; UINT i, max_depth, pos; INT16 label; const SIZE_T tokens_size = 5 * WINDIVERT_FILTER_MAXLEN; ERROR error; // Check for pre-compiled filter object: if (filter[0] == '@') { WINDIVERT_STREAM stream; stream.data = (char *)filter; stream.pos = 0; stream.max = UINT_MAX; stream.overflow = FALSE; if (!WinDivertDeserializeFilter(&stream, object, obj_len)) { SetLastError(ERROR_INVALID_PARAMETER); return MAKE_ERROR(WINDIVERT_ERROR_BAD_OBJECT, 0); } return 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) { return MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); } // Tokenize the filter string: error = WinDivertTokenizeFilter(filter, layer, tokens, tokens_size-1); if (IS_ERROR(error)) { return error; } // Parse the filter into an expression: i = 0; max_depth = 1024; expr = WinDivertParseFilter(pool, tokens, &i, max_depth, FALSE, &error); if (expr == NULL) { return error; } if (tokens[i].kind != TOKEN_END) { pos = tokens[i].pos; return MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, pos); } // Construct the filter tree: label = 0; label = WinDivertFlattenExpr(expr, &label, WINDIVERT_FILTER_RESULT_ACCEPT, WINDIVERT_FILTER_RESULT_REJECT, stack); if (label < 0) { return MAKE_ERROR(WINDIVERT_ERROR_TOO_LONG, 0); } // Emit the final object. if (object != NULL) { WinDivertEmitFilter(stack, label, label, object, obj_len); } return MAKE_ERROR(WINDIVERT_ERROR_NONE, 0); } /* * Convert a error code into a user readable string. */ static const char *WinDivertErrorString(UINT code) { switch (code) { case WINDIVERT_ERROR_NONE: return "No error"; case WINDIVERT_ERROR_NO_MEMORY: return "Out of memory"; case WINDIVERT_ERROR_TOO_DEEP: return "Filter expression too deep"; case WINDIVERT_ERROR_TOO_LONG: return "Filter expression too long"; case WINDIVERT_ERROR_BAD_TOKEN: return "Filter expression contains a bad token"; case WINDIVERT_ERROR_BAD_TOKEN_FOR_LAYER: 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: return "Unknown error"; } } /* * Compile the given filter string. */ BOOL WinDivertHelperCompileFilter(const char *filter_str, WINDIVERT_LAYER layer, char *object, UINT obj_len, const char **error, UINT *error_pos) { HANDLE pool; ERROR err; if (filter_str == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } pool = HeapCreate(HEAP_NO_SERIALIZE, WINDIVERT_MIN_POOL_SIZE, WINDIVERT_MAX_POOL_SIZE); if (pool == NULL) { return FALSE; } SetLastError(ERROR_SUCCESS); if (object == NULL) { err = WinDivertCompileFilter(filter_str, pool, layer, NULL, NULL); } else { WINDIVERT_FILTER *filter_obj = HeapAlloc(pool, 0, WINDIVERT_FILTER_MAXLEN * sizeof(WINDIVERT_FILTER)); UINT filter_obj_len; err = WINDIVERT_ERROR_NO_MEMORY; if (filter_obj != NULL) { err = WinDivertCompileFilter(filter_str, pool, layer, filter_obj, &filter_obj_len); if (!IS_ERROR(err)) { WINDIVERT_STREAM stream; stream.data = object; stream.pos = 0; stream.max = obj_len; stream.overflow = FALSE; WinDivertSerializeFilter(&stream, filter_obj, filter_obj_len); if (stream.overflow) { SetLastError(ERROR_INSUFFICIENT_BUFFER); err = MAKE_ERROR(WINDIVERT_ERROR_OUTPUT_TOO_SHORT, 0); } } } } HeapDestroy(pool); if (error != NULL) { *error = WinDivertErrorString(GET_CODE(err)); } if (error_pos != NULL) { *error_pos = GET_POS(err); } return !IS_ERROR(err); } /* * Get packet/payload data. */ static BOOL WinDivertGetData(const VOID *packet, UINT packet_len, INT min, INT max, INT idx, PVOID data, UINT size) { idx += (idx < 0? max: min); if (idx < min || idx > (max - (INT)size)) { return FALSE; } memcpy(data, (UINT8 *)packet + idx, size); return TRUE; } /* * Evaluate the given filter with the given packet as input. */ BOOL WinDivertHelperEvalFilter(const char *filter, const VOID *packet, UINT packet_len, const WINDIVERT_ADDRESS *addr) { ERROR err; DWORD error; WINDIVERT_PACKET info; 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; const WINDIVERT_DATA_NETWORK *network_data = NULL; const WINDIVERT_DATA_FLOW *flow_data = NULL; const WINDIVERT_DATA_SOCKET *socket_data = NULL; const WINDIVERT_DATA_REFLECT *reflect_data = NULL; BOOL fragment = FALSE; UINT8 protocol = 0; UINT header_len = 0, payload_len = 0; int result; HANDLE pool; WINDIVERT_FILTER *object; UINT obj_len; 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; } if (!WinDivertHelperParsePacketEx((PVOID)packet, packet_len, &info)) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } protocol = info.Protocol; ip_header = info.IPHeader; ipv6_header = info.IPv6Header; icmp_header = info.ICMPHeader; icmpv6_header = info.ICMPv6Header; tcp_header = info.TCPHeader; udp_header = info.UDPHeader; payload_len = info.PayloadLength; header_len = info.HeaderLength; fragment = info.Fragment; if ((addr->IPv6 && ipv6_header == NULL) || (!addr->IPv6 && ip_header == 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; } switch (addr->Layer) { case WINDIVERT_LAYER_NETWORK: case WINDIVERT_LAYER_NETWORK_FORWARD: network_data = &addr->Network; break; case WINDIVERT_LAYER_FLOW: flow_data = &addr->Flow; break; case WINDIVERT_LAYER_SOCKET: socket_data = &addr->Socket; break; case WINDIVERT_LAYER_REFLECT: reflect_data = &addr->Reflect; break; default: SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } pool = HeapCreate(HEAP_NO_SERIALIZE, WINDIVERT_MIN_POOL_SIZE, WINDIVERT_MAX_POOL_SIZE); if (pool == NULL) { return FALSE; } object = HeapAlloc(pool, 0, WINDIVERT_FILTER_MAXLEN * sizeof(WINDIVERT_FILTER)); if (object == NULL) { goto WinDivertHelperEvalFilterError; } err = WinDivertCompileFilter(filter, pool, addr->Layer, object, &obj_len); if (IS_ERROR(err)) { SetLastError(ERROR_INVALID_PARAMETER); goto WinDivertHelperEvalFilterError; } result = WinDivertExecuteFilter( object, addr->Layer, addr->Timestamp, addr->Event, (addr->IPv6 != 0? FALSE: TRUE), (addr->Outbound != 0? TRUE: FALSE), (addr->Loopback != 0? TRUE: FALSE), (addr->Impostor != 0? TRUE: FALSE), fragment, network_data, flow_data, socket_data, reflect_data, ip_header, ipv6_header, icmp_header, icmpv6_header, tcp_header, udp_header, protocol, packet, packet_len, header_len, payload_len); HeapDestroy(pool); if (result < 0) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } else if (result == 0) { SetLastError(0); return FALSE; } else { return TRUE; } WinDivertHelperEvalFilterError: error = GetLastError(); HeapDestroy(pool); SetLastError(error); return FALSE; } /* * 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; } /* * Decode a digit. */ static BOOL WinDivertDecodeDigit(char c, UINT8 *digit, BOOL *final) { if (c >= '0' && c <= '9') { *digit = c - '0'; *final = FALSE; return TRUE; } if (c >= 'A' && c <= 'V') { *digit = c - 'A' + 10; *final = FALSE; return TRUE; } if (c >= 'W' && c <= 'Z') { *digit = c - 'W'; *final = TRUE; return TRUE; } if (c >= 'a' && c <= 'z') { *digit = c - 'a' + 4; *final = TRUE; return TRUE; } if (c == '+') { *digit = 30; *final = TRUE; return TRUE; } if (c == '=') { *digit = 31; *final = TRUE; return TRUE; } return FALSE; } /* * Deserialize a number. */ static BOOL WinDivertDeserializeNumber(PWINDIVERT_STREAM stream, UINT max_len, UINT32 *result) { UINT32 i, val = 0; UINT8 digit; BOOL final; char c; for (i = 0; i < max_len; i++) { if ((val & 0xF8000000) != 0) { return FALSE; // Overflow } val <<= 5; c = WinDivertGetChar(stream); if (!WinDivertDecodeDigit(c, &digit, &final)) { return FALSE; } val += digit; if (final) { *result = val; return TRUE; } } return FALSE; } /* * Deserialize a label. */ static BOOL WinDivertDeserializeLabel(PWINDIVERT_STREAM stream, UINT16 *label) { UINT32 val; switch (WinDivertGetChar(stream)) { case 'A': *label = WINDIVERT_FILTER_RESULT_ACCEPT; return TRUE; case 'X': *label = WINDIVERT_FILTER_RESULT_REJECT; return TRUE; case 'L': if (!WinDivertDeserializeNumber(stream, 2, &val) || val > WINDIVERT_FILTER_MAXLEN) { return FALSE; } *label = (UINT16)val; return TRUE; default: return FALSE; } } /* * Deserialize a test. */ static BOOL WinDivertDeserializeTest(PWINDIVERT_STREAM stream, PWINDIVERT_FILTER filter) { UINT32 val; UINT16 success, failure; UINT i; if (WinDivertGetChar(stream) != '_') { return FALSE; } if (!WinDivertDeserializeNumber(stream, 2, &val) || val > WINDIVERT_FILTER_FIELD_MAX) { return FALSE; } filter->field = (UINT16)val; if (!WinDivertDeserializeNumber(stream, 2, &val) || val > WINDIVERT_FILTER_TEST_MAX) { return FALSE; } filter->test = (UINT16)val; if (!WinDivertDeserializeNumber(stream, 1, &val) || val > 1) { return FALSE; } filter->neg = (UINT16)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_ENDPOINTID: case WINDIVERT_FILTER_FIELD_PARENTENDPOINTID: case WINDIVERT_FILTER_FIELD_TIMESTAMP: if (!WinDivertDeserializeNumber(stream, 7, &filter->arg[1])) { return FALSE; } filter->arg[2] = filter->arg[3] = 0; 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 (!WinDivertDeserializeLabel(stream, &success) || !WinDivertDeserializeLabel(stream, &failure)) { return FALSE; } filter->success = success; filter->failure = failure; 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; } switch (filter[i].success) { case WINDIVERT_FILTER_RESULT_ACCEPT: case WINDIVERT_FILTER_RESULT_REJECT: break; default: if (filter[i].success <= i || filter[i].success >= *length) { return FALSE; } } switch (filter[i].failure) { case WINDIVERT_FILTER_RESULT_ACCEPT: case WINDIVERT_FILTER_RESULT_REJECT: break; default: if (filter[i].failure <= i || filter[i].failure >= *length) { 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_LENGTH: kind = TOKEN_LENGTH; break; case WINDIVERT_FILTER_FIELD_TIMESTAMP: kind = TOKEN_TIMESTAMP; 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_FRAGMENT: kind = TOKEN_FRAGMENT; 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_ENDPOINTID: kind = TOKEN_ENDPOINT_ID; break; case WINDIVERT_FILTER_FIELD_PARENTENDPOINTID: kind = TOKEN_PARENT_ENDPOINT_ID; break; case WINDIVERT_FILTER_FIELD_LAYER: kind = TOKEN_LAYER; break; case WINDIVERT_FILTER_FIELD_PRIORITY: kind = TOKEN_PRIORITY; 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; } if (test->neg) { val->neg = TRUE; } 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, UINT16 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, UINT16 next, UINT16 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, UINT16 i, ERROR *error) { PEXPR expr, next_expr, new_expr; BOOL singleton; static const EXPR true_expr = {{{0}}, TOKEN_TRUE}; static const EXPR false_expr = {{{0}}, TOKEN_FALSE}; 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) { 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] = (PEXPR)&false_expr; new_expr->arg[2] = next_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; } 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, UINT16 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 32bit decimal number. */ static void WinDivertFormatDecNumber32(PWINDIVERT_STREAM stream, UINT32 val) { UINT32 r = 1000000000ul, 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 128bit decimal number. */ static void WinDivertFormatDecNumber(PWINDIVERT_STREAM stream, const UINT32 *val0) { UINT32 val[4]; char buf[40]; UINT i, j; if (val0[0] == 0 && val0[1] == 0 && val0[2] == 0 && val0[3] == 0) { WinDivertPutChar(stream, '0'); return; } val[0] = val0[0]; val[1] = val0[1]; val[2] = val0[2]; val[3] = val0[3]; for (i = 0; i < sizeof(buf) && (val[0] != 0 || val[1] != 0 || val[2] != 0 || val[3] != 0); i++) { buf[i] = '0' + WinDivertDivTen128(val); } for (j = 0; j < i; j++) { WinDivertPutChar(stream, buf[i - j - 1]); } } /* * Format a 128bit hexidecimal number. */ static void WinDivertFormatHexNumber(PWINDIVERT_STREAM stream, const UINT32 *val) { INT i, s; UINT32 dig; BOOL zeroes = FALSE; for (i = 3; val[i] == 0 && i >= 1; i--) ; for (; i >= 0; i--) { s = 28; while (s >= 0) { dig = (val[i] & ((UINT32)0xF << s)) >> s; s -= 4; if (dig == 0 && !zeroes) { continue; } WinDivertPutChar(stream, (dig <= 9? '0' + dig: 'a' + (dig - 10))); zeroes = TRUE; } } if (!zeroes) { WinDivertPutChar(stream, '0'); } } /* * Format an IPv4 address. */ static void WinDivertFormatIPv4Addr(PWINDIVERT_STREAM stream, UINT32 addr) { WinDivertFormatDecNumber32(stream, (addr & 0xFF000000) >> 24); WinDivertPutChar(stream, '.'); WinDivertFormatDecNumber32(stream, (addr & 0x00FF0000) >> 16); WinDivertPutChar(stream, '.'); WinDivertFormatDecNumber32(stream, (addr & 0x0000FF00) >> 8); WinDivertPutChar(stream, '.'); WinDivertFormatDecNumber32(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]; UINT32 part[4] = {0}; // 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; } part[0] = (UINT32)addr[i]; WinDivertFormatHexNumber(stream, part); WinDivertPutString(stream, (i != 0? ":": "")); } } /* * Format an IPv4 address. */ 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. */ 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_FRAGMENT: 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 (val->neg) { WinDivertPutChar(stream, '-'); } 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: WinDivertFormatDecNumber32(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 { WinDivertFormatDecNumber32(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: WinDivertFormatDecNumber32(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; case WINDIVERT_EVENT_SOCKET_CLOSE: WinDivertPutString(stream, "CLOSE"); break; default: WinDivertFormatDecNumber32(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: WinDivertFormatDecNumber32(stream, val->val[0]); break; } break; default: WinDivertFormatDecNumber32(stream, val->val[0]); break; } } else if (is_hex) { WinDivertPutString(stream, "0x"); WinDivertFormatHexNumber(stream, val->val); } else { WinDivertFormatDecNumber(stream, val->val); } } /* * 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_LENGTH: WinDivertPutString(stream, "length"); return; case TOKEN_TIMESTAMP: WinDivertPutString(stream, "timestamp"); return; 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_FRAGMENT: WinDivertPutString(stream, "fragment"); 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_ENDPOINT_ID: WinDivertPutString(stream, "endpointId"); return; case TOKEN_PARENT_ENDPOINT_ID: WinDivertPutString(stream, "parentEndpointId"); return; case TOKEN_LAYER: WinDivertPutString(stream, "layer"); return; case TOKEN_PRIORITY: WinDivertPutString(stream, "priority"); return; case TOKEN_NUMBER: WinDivertFormatDecNumber(stream, expr->val); return; } WinDivertPutChar(stream, '['); idx = (INT)expr->val[0]; if (idx < 0) { WinDivertPutChar(stream, '-'); idx = -idx; } WinDivertFormatDecNumber32(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; DWORD error; WINDIVERT_FILTER *object; UINT obj_len; INT i; HANDLE pool; WINDIVERT_STREAM stream; if (filter == NULL || buffer == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } pool = HeapCreate(HEAP_NO_SERIALIZE, WINDIVERT_MIN_POOL_SIZE, WINDIVERT_MAX_POOL_SIZE); if (pool == NULL) { return FALSE; } object = HeapAlloc(pool, 0, WINDIVERT_FILTER_MAXLEN * sizeof(WINDIVERT_FILTER)); if (object == NULL) { goto WinDivertHelperFormatFilterError; } err = WinDivertCompileFilter(filter, pool, layer, object, &obj_len); if (IS_ERROR(err)) { SetLastError(ERROR_INVALID_PARAMETER); goto WinDivertHelperFormatFilterError; } // Decompile all tests: for (i = (INT)obj_len-1; i >= 0; i--) { expr = WinDivertDecompileTest(pool, object + i); if (expr == NULL) { SetLastError(ERROR_INVALID_PARAMETER); goto WinDivertHelperFormatFilterError; } 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--) { err = MAKE_ERROR(WINDIVERT_ERROR_NONE, 0); (PVOID)WinDivertCoalesceAndOr(pool, exprs, i, &err); if (IS_ERROR(err)) { goto WinDivertHelperFormatFilterError; } } // Coalesce remaining expressions: expr = WinDivertCoalesceExpr(pool, exprs, 0); if (expr == NULL) { goto WinDivertHelperFormatFilterError; } // 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; WinDivertHelperFormatFilterError: error = GetLastError(); HeapDestroy(pool); SetLastError(error); return FALSE; } /* * WinDivert packet hash function. */ 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; if (!WinDivertHelperParsePacket((PVOID)pPacket, packetLen, &ip_header, &ipv6_header, NULL, &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL, NULL, NULL, NULL)) { return 0; } return WinDivertHashPacket(seed, ip_header, ipv6_header, icmp_header, icmpv6_header, tcp_header, udp_header); } /* * Byte ordering. */ UINT16 WinDivertHelperNtohs(UINT16 x) { return BYTESWAP16(x); } UINT16 WinDivertHelperHtons(UINT16 x) { return BYTESWAP16(x); } UINT32 WinDivertHelperNtohl(UINT32 x) { return BYTESWAP32(x); } UINT32 WinDivertHelperHtonl(UINT32 x) { return BYTESWAP32(x); } UINT64 WinDivertHelperNtohll(UINT64 x) { return BYTESWAP64(x); } UINT64 WinDivertHelperHtonll(UINT64 x) { return BYTESWAP64(x); } static void WinDivertByteSwap128(const UINT *inAddr, UINT *outAddr) { UINT32 tmp[4], i; // tmp[] allows overlapping inAddr/outAddr for (i = 0; i < 4; i++) { tmp[3-i] = BYTESWAP32(inAddr[i]); } for (i = 0; i < 4; i++) { outAddr[i] = tmp[i]; } } void WinDivertHelperNtohIPv6Address(const UINT *inAddr, UINT *outAddr) { WinDivertByteSwap128(inAddr, outAddr); } void WinDivertHelperHtonIPv6Address(const UINT *inAddr, UINT *outAddr) { WinDivertByteSwap128(inAddr, outAddr); } // Old names to be removed in next version void WinDivertHelperNtohIpv6Address(const UINT *inAddr, UINT *outAddr) { WinDivertByteSwap128(inAddr, outAddr); } void WinDivertHelperHtonIpv6Address(const UINT *inAddr, UINT *outAddr) { WinDivertByteSwap128(inAddr, outAddr); }