diff --git a/dll/windivert.c b/dll/windivert.c index 77f5d5e..e2eb116 100644 --- a/dll/windivert.c +++ b/dll/windivert.c @@ -1,6 +1,6 @@ /* * windivert.c - * (C) 2014, all rights reserved, + * (C) 2015, all rights reserved, * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -55,99 +55,6 @@ static BOOLEAN WinDivertStrCpy(wchar_t *dst, size_t dstlen, static BOOLEAN WinDivertAToI(const char *str, char **endptr, UINT32 *intptr); static BOOLEAN WinDivertAToX(const char *str, char **endptr, UINT32 *intptr); -/* - * Filter parsing. - */ -typedef enum -{ - FILTER_TOKEN_ICMP, - FILTER_TOKEN_ICMP_BODY, - FILTER_TOKEN_ICMP_CHECKSUM, - FILTER_TOKEN_ICMP_CODE, - FILTER_TOKEN_ICMP_TYPE, - FILTER_TOKEN_ICMPV6, - FILTER_TOKEN_ICMPV6_BODY, - FILTER_TOKEN_ICMPV6_CHECKSUM, - FILTER_TOKEN_ICMPV6_CODE, - FILTER_TOKEN_ICMPV6_TYPE, - FILTER_TOKEN_IP, - FILTER_TOKEN_IP_CHECKSUM, - FILTER_TOKEN_IP_DF, - FILTER_TOKEN_IP_DST_ADDR, - FILTER_TOKEN_IP_FRAG_OFF, - FILTER_TOKEN_IP_HDR_LENGTH, - FILTER_TOKEN_IP_ID, - FILTER_TOKEN_IP_LENGTH, - FILTER_TOKEN_IP_MF, - FILTER_TOKEN_IP_PROTOCOL, - FILTER_TOKEN_IP_SRC_ADDR, - FILTER_TOKEN_IP_TOS, - FILTER_TOKEN_IP_TTL, - FILTER_TOKEN_IPV6, - FILTER_TOKEN_IPV6_DST_ADDR, - FILTER_TOKEN_IPV6_FLOW_LABEL, - FILTER_TOKEN_IPV6_HOP_LIMIT, - FILTER_TOKEN_IPV6_LENGTH, - FILTER_TOKEN_IPV6_NEXT_HDR, - FILTER_TOKEN_IPV6_SRC_ADDR, - FILTER_TOKEN_IPV6_TRAFFIC_CLASS, - FILTER_TOKEN_TCP, - FILTER_TOKEN_TCP_ACK, - FILTER_TOKEN_TCP_ACK_NUM, - FILTER_TOKEN_TCP_CHECKSUM, - FILTER_TOKEN_TCP_DST_PORT, - FILTER_TOKEN_TCP_FIN, - FILTER_TOKEN_TCP_HDR_LENGTH, - FILTER_TOKEN_TCP_PAYLOAD_LENGTH, - FILTER_TOKEN_TCP_PSH, - FILTER_TOKEN_TCP_RST, - FILTER_TOKEN_TCP_SEQ_NUM, - FILTER_TOKEN_TCP_SRC_PORT, - FILTER_TOKEN_TCP_SYN, - FILTER_TOKEN_TCP_URG, - FILTER_TOKEN_TCP_URG_PTR, - FILTER_TOKEN_TCP_WINDOW, - FILTER_TOKEN_UDP, - FILTER_TOKEN_UDP_CHECKSUM, - FILTER_TOKEN_UDP_DST_PORT, - FILTER_TOKEN_UDP_LENGTH, - FILTER_TOKEN_UDP_PAYLOAD_LENGTH, - FILTER_TOKEN_UDP_SRC_PORT, - FILTER_TOKEN_TRUE, - FILTER_TOKEN_FALSE, - FILTER_TOKEN_INBOUND, - FILTER_TOKEN_OUTBOUND, - FILTER_TOKEN_IF_IDX, - FILTER_TOKEN_SUB_IF_IDX, - FILTER_TOKEN_OPEN, - FILTER_TOKEN_CLOSE, - FILTER_TOKEN_EQ, - FILTER_TOKEN_NEQ, - FILTER_TOKEN_LT, - FILTER_TOKEN_LEQ, - FILTER_TOKEN_GT, - FILTER_TOKEN_GEQ, - FILTER_TOKEN_NOT, - FILTER_TOKEN_AND, - FILTER_TOKEN_OR, - FILTER_TOKEN_NUMBER, - FILTER_TOKEN_END, -} FILTER_TOKEN_KIND; - -typedef struct -{ - FILTER_TOKEN_KIND kind; - UINT32 val[4]; -} FILTER_TOKEN; - -#define FILTER_TOKEN_MAXLEN 32 // Fits longest IPv6 - -typedef struct -{ - char *name; - FILTER_TOKEN_KIND kind; -} FILTER_TOKEN_NAME, *PFILTER_TOKEN_NAME; - /* * IPv4/IPv6 pseudo headers. */ @@ -178,10 +85,6 @@ typedef struct #ifndef UINT32_MAX #define UINT32_MAX 0xFFFFFFFF #endif -#define IPPROTO_ICMP 1 -#define IPPROTO_TCP 6 -#define IPPROTO_UDP 17 -#define IPPROTO_ICMPV6 58 /* * Prototypes. @@ -193,29 +96,17 @@ static BOOL WinDivertIoControl(HANDLE handle, DWORD code, UINT8 arg8, UINT64 arg, PVOID buf, UINT len, UINT *iolen); static BOOL WinDivertIoControlEx(HANDLE handle, DWORD code, UINT8 arg8, UINT64 arg, PVOID buf, UINT len, UINT *iolen, LPOVERLAPPED overlapped); -static BOOL WinDivertCompileFilter(const char *filter_str, - WINDIVERT_LAYER layer, windivert_ioctl_filter_t filter, UINT16 *fp); -static PFILTER_TOKEN_NAME WinDivertTokenLookup( - PFILTER_TOKEN_NAME token_names, size_t token_names_len, - const char *name); -static BOOL WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, - FILTER_TOKEN *tokens, UINT tokensmax); -static BOOL WinDivertParseFilter(FILTER_TOKEN *tokens, UINT16 *tp, - windivert_ioctl_filter_t filter, UINT16 *fp, FILTER_TOKEN_KIND op); -static void WinDivertFilterUpdate(windivert_ioctl_filter_t filter, UINT16 s, - UINT16 e, UINT16 success, UINT16 failure); static UINT8 WinDivertSkipExtHeaders(UINT8 proto, UINT8 **header, UINT *len); -static void WinDivertInitPseudoHeader(PWINDIVERT_IPHDR ip_header, - PWINDIVERT_PSEUDOHDR pseudo_header, UINT8 protocol, UINT len); -static void WinDivertInitPseudoHeaderV6(PWINDIVERT_IPV6HDR ipv6_header, - PWINDIVERT_PSEUDOV6HDR pseudov6_header, UINT8 protocol, UINT len); -static UINT16 WinDivertHelperCalcChecksum(PVOID pseudo_header, - UINT16 pseudo_header_len, PVOID data, UINT len); #ifdef WINDIVERT_DEBUG static void WinDivertFilterDump(windivert_ioctl_filter_t filter, UINT16 len); #endif +/* + * Include the helper API implementation. + */ +#include "windivert_helper.c" + /* * Thread local. */ @@ -490,8 +381,8 @@ static BOOL WinDivertIoControlEx(HANDLE handle, DWORD code, UINT8 arg8, extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, INT16 priority, UINT64 flags) { - struct windivert_ioctl_filter_s ioctl_filter[WINDIVERT_FILTER_MAXLEN]; - UINT16 filter_len; + struct windivert_ioctl_filter_s object[WINDIVERT_FILTER_MAXLEN]; + UINT obj_len; DWORD err; HANDLE handle; SC_HANDLE service; @@ -511,16 +402,15 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, return INVALID_HANDLE_VALUE; } - // Parse the filter: - if (!WinDivertCompileFilter(filter, layer, ioctl_filter, &filter_len)) + // Compile the filter: + if (!WinDivertCompileFilter(filter, layer, object, &obj_len)) { SetLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } - #ifdef WINDIVERT_DEBUG - WinDivertFilterDump(ioctl_filter, filter_len); + WinDivertFilterDump(object, obj_len); #endif // Attempt to open the WinDivert device: @@ -596,8 +486,7 @@ extern HANDLE WinDivertOpen(const char *filter, WINDIVERT_LAYER layer, // Start the filter: if (!WinDivertIoControl(handle, IOCTL_WINDIVERT_START_FILTER, 0, 0, - ioctl_filter, filter_len*sizeof(struct windivert_ioctl_filter_s), - NULL)) + object, obj_len*sizeof(struct windivert_ioctl_filter_s), NULL)) { CloseHandle(handle); return INVALID_HANDLE_VALUE; @@ -733,1220 +622,6 @@ extern BOOL WinDivertGetParam(HANDLE handle, WINDIVERT_PARAM param, 0, pValue, sizeof(UINT64), NULL); } -/* - * Compile a filter. - */ -static BOOL WinDivertCompileFilter(const char *filter_str, - WINDIVERT_LAYER layer, windivert_ioctl_filter_t filter, UINT16 *fp) -{ - FILTER_TOKEN tokens[WINDIVERT_FILTER_MAXLEN*3]; - UINT16 tp; - - if (!WinDivertTokenizeFilter(filter_str, layer, tokens, - WINDIVERT_FILTER_MAXLEN*3-1)) - { - return FALSE; - } - - tp = 0; - *fp = 0; - if (!WinDivertParseFilter(tokens, &tp, filter, fp, FILTER_TOKEN_AND)) - { - return FALSE; - } - if (tokens[tp].kind != FILTER_TOKEN_END) - { - return FALSE; - } - return TRUE; -} - -/* - * Lookup a token. - */ -static PFILTER_TOKEN_NAME WinDivertTokenLookup( - PFILTER_TOKEN_NAME token_names, size_t token_names_len, - const char *name) -{ - int lo = 0, hi = (int)token_names_len-1, mid; - int cmp; - while (hi >= lo) - { - mid = (lo + hi) / 2; - cmp = strcmp(token_names[mid].name, name); - if (cmp < 0) - { - lo = mid+1; - } - else if (cmp > 0) - { - hi = mid-1; - } - else - { - return &token_names[mid]; - } - } - return NULL; -} - -/* - * Tokenize the given filter string. - */ -static BOOL WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, - FILTER_TOKEN *tokens, UINT tokensmax) -{ - static const FILTER_TOKEN_NAME token_names[] = - { - {"and", FILTER_TOKEN_AND}, - {"false", FILTER_TOKEN_FALSE}, - {"icmp", FILTER_TOKEN_ICMP}, - {"icmp.Body", FILTER_TOKEN_ICMP_BODY}, - {"icmp.Checksum", FILTER_TOKEN_ICMP_CHECKSUM}, - {"icmp.Code", FILTER_TOKEN_ICMP_CODE}, - {"icmp.Type", FILTER_TOKEN_ICMP_TYPE}, - {"icmpv6", FILTER_TOKEN_ICMPV6}, - {"icmpv6.Body", FILTER_TOKEN_ICMPV6_BODY}, - {"icmpv6.Checksum", FILTER_TOKEN_ICMPV6_CHECKSUM}, - {"icmpv6.Code", FILTER_TOKEN_ICMPV6_CODE}, - {"icmpv6.Type", FILTER_TOKEN_ICMPV6_TYPE}, - {"ifIdx", FILTER_TOKEN_IF_IDX}, - {"inbound", FILTER_TOKEN_INBOUND}, - {"ip", FILTER_TOKEN_IP}, - {"ip.Checksum", FILTER_TOKEN_IP_CHECKSUM}, - {"ip.DF", FILTER_TOKEN_IP_DF}, - {"ip.DstAddr", FILTER_TOKEN_IP_DST_ADDR}, - {"ip.FragOff", FILTER_TOKEN_IP_FRAG_OFF}, - {"ip.HdrLength", FILTER_TOKEN_IP_HDR_LENGTH}, - {"ip.Id", FILTER_TOKEN_IP_ID}, - {"ip.Length", FILTER_TOKEN_IP_LENGTH}, - {"ip.MF", FILTER_TOKEN_IP_MF}, - {"ip.Protocol", FILTER_TOKEN_IP_PROTOCOL}, - {"ip.SrcAddr", FILTER_TOKEN_IP_SRC_ADDR}, - {"ip.TOS", FILTER_TOKEN_IP_TOS}, - {"ip.TTL", FILTER_TOKEN_IP_TTL}, - {"ipv6", FILTER_TOKEN_IPV6}, - {"ipv6.DstAddr", FILTER_TOKEN_IPV6_DST_ADDR}, - {"ipv6.FlowLabel", FILTER_TOKEN_IPV6_FLOW_LABEL}, - {"ipv6.HopLimit", FILTER_TOKEN_IPV6_HOP_LIMIT}, - {"ipv6.Length", FILTER_TOKEN_IPV6_LENGTH}, - {"ipv6.NextHdr", FILTER_TOKEN_IPV6_NEXT_HDR}, - {"ipv6.SrcAddr", FILTER_TOKEN_IPV6_SRC_ADDR}, - {"ipv6.TrafficClass", FILTER_TOKEN_IPV6_TRAFFIC_CLASS}, - {"not", FILTER_TOKEN_NOT}, - {"or", FILTER_TOKEN_OR}, - {"outbound", FILTER_TOKEN_OUTBOUND}, - {"subIfIdx", FILTER_TOKEN_SUB_IF_IDX}, - {"tcp", FILTER_TOKEN_TCP}, - {"tcp.Ack", FILTER_TOKEN_TCP_ACK}, - {"tcp.AckNum", FILTER_TOKEN_TCP_ACK_NUM}, - {"tcp.Checksum", FILTER_TOKEN_TCP_CHECKSUM}, - {"tcp.DstPort", FILTER_TOKEN_TCP_DST_PORT}, - {"tcp.Fin", FILTER_TOKEN_TCP_FIN}, - {"tcp.HdrLength", FILTER_TOKEN_TCP_HDR_LENGTH}, - {"tcp.PayloadLength", FILTER_TOKEN_TCP_PAYLOAD_LENGTH}, - {"tcp.Psh", FILTER_TOKEN_TCP_PSH}, - {"tcp.Rst", FILTER_TOKEN_TCP_RST}, - {"tcp.SeqNum", FILTER_TOKEN_TCP_SEQ_NUM}, - {"tcp.SrcPort", FILTER_TOKEN_TCP_SRC_PORT}, - {"tcp.Syn", FILTER_TOKEN_TCP_SYN}, - {"tcp.Urg", FILTER_TOKEN_TCP_URG}, - {"tcp.UrgPtr", FILTER_TOKEN_TCP_URG_PTR}, - {"tcp.Window", FILTER_TOKEN_TCP_WINDOW}, - {"true", FILTER_TOKEN_TRUE}, - {"udp", FILTER_TOKEN_UDP}, - {"udp.Checksum", FILTER_TOKEN_UDP_CHECKSUM}, - {"udp.DstPort", FILTER_TOKEN_UDP_DST_PORT}, - {"udp.Length", FILTER_TOKEN_UDP_LENGTH}, - {"udp.PayloadLength", FILTER_TOKEN_UDP_PAYLOAD_LENGTH}, - {"udp.SrcPort", FILTER_TOKEN_UDP_SRC_PORT}, - }; - FILTER_TOKEN_NAME *result; - char c; - char token[FILTER_TOKEN_MAXLEN]; - UINT i = 0, j; - UINT tp = 0; - - while (TRUE) - { - if (tp >= tokensmax-1) - { - return FALSE; - } - memset(tokens[tp].val, 0, sizeof(tokens[tp].val)); - while (isspace(filter[i])) - { - i++; - } - c = filter[i++]; - switch (c) - { - case '\0': - tokens[tp].kind = FILTER_TOKEN_END; - return TRUE; - case '(': - tokens[tp++].kind = FILTER_TOKEN_OPEN; - continue; - case ')': - tokens[tp++].kind = FILTER_TOKEN_CLOSE; - continue; - case '!': - if (filter[i] == '=') - { - i++; - tokens[tp++].kind = FILTER_TOKEN_NEQ; - } - else - { - tokens[tp++].kind = FILTER_TOKEN_NOT; - } - continue; - case '=': - if (filter[i] == '=') - { - i++; - } - tokens[tp++].kind = FILTER_TOKEN_EQ; - continue; - case '<': - if (filter[i] == '=') - { - i++; - tokens[tp++].kind = FILTER_TOKEN_LEQ; - } - else - { - tokens[tp++].kind = FILTER_TOKEN_LT; - } - continue; - case '>': - if (filter[i] == '=') - { - i++; - tokens[tp++].kind = FILTER_TOKEN_GEQ; - } - else - { - tokens[tp++].kind = FILTER_TOKEN_GT; - } - continue; - case '&': - if (filter[i++] != '&') - { - return FALSE; - } - tokens[tp++].kind = FILTER_TOKEN_AND; - continue; - case '|': - if (filter[i++] != '|') - { - return FALSE; - } - tokens[tp++].kind = FILTER_TOKEN_OR; - continue; - default: - break; - } - token[0] = c; - if (isalnum(c) || c == '.' || c == ':') - { - UINT32 num; - char *end; - for (j = 1; j < FILTER_TOKEN_MAXLEN && (isalnum(filter[i]) || - filter[i] == '.' || filter[i] == ':'); j++, i++) - { - token[j] = filter[i]; - } - if (j >= FILTER_TOKEN_MAXLEN) - { - return FALSE; - } - token[j] = '\0'; - - // Check for symbol: - result = WinDivertTokenLookup((PFILTER_TOKEN_NAME)token_names, - sizeof(token_names) / sizeof(FILTER_TOKEN_NAME), token); - if (result != NULL) - { - switch (layer) - { - case WINDIVERT_LAYER_NETWORK_FORWARD: - if (result->kind == FILTER_TOKEN_INBOUND || - result->kind == FILTER_TOKEN_OUTBOUND) - { - return FALSE; - } - break; - default: - break; - } - tokens[tp++].kind = result->kind; - continue; - } - - // Check for base 10 number: - if (WinDivertAToI(token, &end, &num) && *end == '\0') - { - tokens[tp].kind = FILTER_TOKEN_NUMBER; - tokens[tp].val[0] = num; - tp++; - continue; - } - - // Check for base 16 number: - if (WinDivertAToX(token, &end, &num) && *end == '\0') - { - tokens[tp].kind = FILTER_TOKEN_NUMBER; - tokens[tp].val[0] = num; - tp++; - continue; - } - - // Check for IPv4 address: - if (WinDivertHelperParseIPv4Address(token, tokens[tp].val)) - { - tokens[tp].kind = FILTER_TOKEN_NUMBER; - tp++; - continue; - } - - // Check for IPv6 address: - SetLastError(0); - if (WinDivertHelperParseIPv6Address(token, tokens[tp].val)) - { - // Work-around the different word orderings between the - // DLL vs SYS. - UINT32 tmp; - tmp = tokens[tp].val[0]; - tokens[tp].val[0] = tokens[tp].val[3]; - tokens[tp].val[3] = tmp; - tmp = tokens[tp].val[1]; - tokens[tp].val[1] = tokens[tp].val[2]; - tokens[tp].val[2] = tmp; - - tokens[tp].kind = FILTER_TOKEN_NUMBER; - tp++; - continue; - } - - return FALSE; - } - else - { - return FALSE; - } - } -} - -/* - * Parse the given filter. - */ -static BOOL WinDivertParseFilter(FILTER_TOKEN *tokens, UINT16 *tp, - windivert_ioctl_filter_t filter, UINT16 *fp, FILTER_TOKEN_KIND op) -{ - BOOL testop, fused, result, negate; - FILTER_TOKEN token; - UINT16 f, s; - s = *fp; - -WinDivertParseFilterNext: - - testop = TRUE; - fused = TRUE; - negate = FALSE; - token = tokens[*tp]; - - *tp = *tp + 1; - f = *fp; - if (f >= WINDIVERT_FILTER_MAXLEN) - { - return FALSE; - } - filter[f].success = WINDIVERT_FILTER_RESULT_ACCEPT; - filter[f].failure = WINDIVERT_FILTER_RESULT_REJECT; - filter[f].arg[1] = 0; - filter[f].arg[2] = 0; - filter[f].arg[3] = 0; - if (token.kind == FILTER_TOKEN_NOT) - { - negate = TRUE; - token = tokens[*tp]; - *tp = *tp + 1; - } - switch (token.kind) - { - case FILTER_TOKEN_OPEN: - result = WinDivertParseFilter(tokens, tp, filter, fp, - FILTER_TOKEN_AND); - result = (result? (tokens[*tp].kind == FILTER_TOKEN_CLOSE): FALSE); - if (!result) - { - return FALSE; - } - *tp = *tp + 1; - testop = FALSE; - fused = FALSE; - break; - case FILTER_TOKEN_TRUE: case FILTER_TOKEN_FALSE: - filter[f].field = WINDIVERT_FILTER_FIELD_ZERO; - filter[f].test = WINDIVERT_FILTER_TEST_EQ; - filter[f].arg[0] = (token.kind == FILTER_TOKEN_FALSE); - testop = FALSE; - break; - case FILTER_TOKEN_OUTBOUND: - filter[f].field = WINDIVERT_FILTER_FIELD_OUTBOUND; - break; - case FILTER_TOKEN_INBOUND: - filter[f].field = WINDIVERT_FILTER_FIELD_INBOUND; - break; - case FILTER_TOKEN_IF_IDX: - filter[f].field = WINDIVERT_FILTER_FIELD_IFIDX; - break; - case FILTER_TOKEN_SUB_IF_IDX: - filter[f].field = WINDIVERT_FILTER_FIELD_SUBIFIDX; - break; - case FILTER_TOKEN_IP: - filter[f].field = WINDIVERT_FILTER_FIELD_IP; - break; - case FILTER_TOKEN_IPV6: - filter[f].field = WINDIVERT_FILTER_FIELD_IPV6; - break; - case FILTER_TOKEN_ICMP: - filter[f].field = WINDIVERT_FILTER_FIELD_ICMP; - break; - case FILTER_TOKEN_ICMPV6: - filter[f].field = WINDIVERT_FILTER_FIELD_ICMPV6; - break; - case FILTER_TOKEN_TCP: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP; - break; - case FILTER_TOKEN_UDP: - filter[f].field = WINDIVERT_FILTER_FIELD_UDP; - break; - case FILTER_TOKEN_IP_HDR_LENGTH: - filter[f].field = WINDIVERT_FILTER_FIELD_IP_HDRLENGTH; - break; - case FILTER_TOKEN_IP_TOS: - filter[f].field = WINDIVERT_FILTER_FIELD_IP_TOS; - break; - case FILTER_TOKEN_IP_LENGTH: - filter[f].field = WINDIVERT_FILTER_FIELD_IP_LENGTH; - break; - case FILTER_TOKEN_IP_ID: - filter[f].field = WINDIVERT_FILTER_FIELD_IP_ID; - break; - case FILTER_TOKEN_IP_DF: - filter[f].field = WINDIVERT_FILTER_FIELD_IP_DF; - break; - case FILTER_TOKEN_IP_MF: - filter[f].field = WINDIVERT_FILTER_FIELD_IP_MF; - break; - case FILTER_TOKEN_IP_FRAG_OFF: - filter[f].field = WINDIVERT_FILTER_FIELD_IP_FRAGOFF; - break; - case FILTER_TOKEN_IP_TTL: - filter[f].field = WINDIVERT_FILTER_FIELD_IP_TTL; - break; - case FILTER_TOKEN_IP_PROTOCOL: - filter[f].field = WINDIVERT_FILTER_FIELD_IP_PROTOCOL; - break; - case FILTER_TOKEN_IP_CHECKSUM: - filter[f].field = WINDIVERT_FILTER_FIELD_IP_CHECKSUM; - break; - case FILTER_TOKEN_IP_SRC_ADDR: - filter[f].field = WINDIVERT_FILTER_FIELD_IP_SRCADDR; - break; - case FILTER_TOKEN_IP_DST_ADDR: - filter[f].field = WINDIVERT_FILTER_FIELD_IP_DSTADDR; - break; - case FILTER_TOKEN_IPV6_TRAFFIC_CLASS: - filter[f].field = WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS; - break; - case FILTER_TOKEN_IPV6_FLOW_LABEL: - filter[f].field = WINDIVERT_FILTER_FIELD_IPV6_FLOWLABEL; - break; - case FILTER_TOKEN_IPV6_LENGTH: - filter[f].field = WINDIVERT_FILTER_FIELD_IPV6_LENGTH; - break; - case FILTER_TOKEN_IPV6_NEXT_HDR: - filter[f].field = WINDIVERT_FILTER_FIELD_IPV6_NEXTHDR; - break; - case FILTER_TOKEN_IPV6_HOP_LIMIT: - filter[f].field = WINDIVERT_FILTER_FIELD_IPV6_HOPLIMIT; - break; - case FILTER_TOKEN_IPV6_SRC_ADDR: - filter[f].field = WINDIVERT_FILTER_FIELD_IPV6_SRCADDR; - break; - case FILTER_TOKEN_IPV6_DST_ADDR: - filter[f].field = WINDIVERT_FILTER_FIELD_IPV6_DSTADDR; - break; - case FILTER_TOKEN_ICMP_TYPE: - filter[f].field = WINDIVERT_FILTER_FIELD_ICMP_TYPE; - break; - case FILTER_TOKEN_ICMP_CODE: - filter[f].field = WINDIVERT_FILTER_FIELD_ICMP_CODE; - break; - case FILTER_TOKEN_ICMP_CHECKSUM: - filter[f].field = WINDIVERT_FILTER_FIELD_ICMP_CHECKSUM; - break; - case FILTER_TOKEN_ICMP_BODY: - filter[f].field = WINDIVERT_FILTER_FIELD_ICMP_BODY; - break; - case FILTER_TOKEN_ICMPV6_TYPE: - filter[f].field = WINDIVERT_FILTER_FIELD_ICMPV6_TYPE; - break; - case FILTER_TOKEN_ICMPV6_CODE: - filter[f].field = WINDIVERT_FILTER_FIELD_ICMPV6_CODE; - break; - case FILTER_TOKEN_ICMPV6_CHECKSUM: - filter[f].field = WINDIVERT_FILTER_FIELD_ICMPV6_CHECKSUM; - break; - case FILTER_TOKEN_ICMPV6_BODY: - filter[f].field = WINDIVERT_FILTER_FIELD_ICMPV6_BODY; - break; - case FILTER_TOKEN_TCP_SRC_PORT: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_SRCPORT; - break; - case FILTER_TOKEN_TCP_DST_PORT: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_DSTPORT; - break; - case FILTER_TOKEN_TCP_SEQ_NUM: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_SEQNUM; - break; - case FILTER_TOKEN_TCP_ACK_NUM: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_ACKNUM; - break; - case FILTER_TOKEN_TCP_HDR_LENGTH: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_HDRLENGTH; - break; - case FILTER_TOKEN_TCP_URG: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_URG; - break; - case FILTER_TOKEN_TCP_ACK: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_ACK; - break; - case FILTER_TOKEN_TCP_PSH: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_PSH; - break; - case FILTER_TOKEN_TCP_RST: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_RST; - break; - case FILTER_TOKEN_TCP_SYN: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_SYN; - break; - case FILTER_TOKEN_TCP_FIN: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_FIN; - break; - case FILTER_TOKEN_TCP_WINDOW: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_WINDOW; - break; - case FILTER_TOKEN_TCP_CHECKSUM: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_CHECKSUM; - break; - case FILTER_TOKEN_TCP_URG_PTR: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_URGPTR; - break; - case FILTER_TOKEN_TCP_PAYLOAD_LENGTH: - filter[f].field = WINDIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH; - break; - case FILTER_TOKEN_UDP_SRC_PORT: - filter[f].field = WINDIVERT_FILTER_FIELD_UDP_SRCPORT; - break; - case FILTER_TOKEN_UDP_DST_PORT: - filter[f].field = WINDIVERT_FILTER_FIELD_UDP_DSTPORT; - break; - case FILTER_TOKEN_UDP_LENGTH: - filter[f].field = WINDIVERT_FILTER_FIELD_UDP_LENGTH; - break; - case FILTER_TOKEN_UDP_CHECKSUM: - filter[f].field = WINDIVERT_FILTER_FIELD_UDP_CHECKSUM; - break; - case FILTER_TOKEN_UDP_PAYLOAD_LENGTH: - filter[f].field = WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH; - break; - default: - return FALSE; - } - - if (fused) - { - *fp = f+1; - } - - if (testop) - { - token = tokens[*tp]; - if (!negate) - { - switch (token.kind) - { - case FILTER_TOKEN_EQ: - filter[f].test = WINDIVERT_FILTER_TEST_EQ; - break; - case FILTER_TOKEN_NEQ: - filter[f].test = WINDIVERT_FILTER_TEST_NEQ; - break; - case FILTER_TOKEN_LT: - filter[f].test = WINDIVERT_FILTER_TEST_LT; - break; - case FILTER_TOKEN_LEQ: - filter[f].test = WINDIVERT_FILTER_TEST_LEQ; - break; - case FILTER_TOKEN_GT: - filter[f].test = WINDIVERT_FILTER_TEST_GT; - break; - case FILTER_TOKEN_GEQ: - filter[f].test = WINDIVERT_FILTER_TEST_GEQ; - break; - default: - filter[f].test = WINDIVERT_FILTER_TEST_NEQ; - filter[f].arg[0] = 0; - testop = FALSE; - break; - } - } - else - { - switch (token.kind) - { - case FILTER_TOKEN_EQ: - filter[f].test = WINDIVERT_FILTER_TEST_NEQ; - break; - case FILTER_TOKEN_NEQ: - filter[f].test = WINDIVERT_FILTER_TEST_EQ; - break; - case FILTER_TOKEN_LT: - filter[f].test = WINDIVERT_FILTER_TEST_GEQ; - break; - case FILTER_TOKEN_LEQ: - filter[f].test = WINDIVERT_FILTER_TEST_GT; - break; - case FILTER_TOKEN_GT: - filter[f].test = WINDIVERT_FILTER_TEST_LEQ; - break; - case FILTER_TOKEN_GEQ: - filter[f].test = WINDIVERT_FILTER_TEST_LT; - break; - default: - filter[f].test = WINDIVERT_FILTER_TEST_EQ; - filter[f].arg[0] = 0; - testop = FALSE; - break; - } - } - - if (testop) - { - *tp = *tp + 1; - token = tokens[*tp]; - *tp = *tp + 1; - if (token.kind != FILTER_TOKEN_NUMBER) - { - return FALSE; - } - filter[f].arg[0] = token.val[0]; - filter[f].arg[1] = token.val[1]; - filter[f].arg[2] = token.val[2]; - filter[f].arg[3] = token.val[3]; - } - } - - token = tokens[*tp]; - if (token.kind != FILTER_TOKEN_AND && token.kind != FILTER_TOKEN_OR) - { - return TRUE; - } - if (op < token.kind) - { - op = token.kind; - f = s; - } - *tp = *tp + 1; - switch (token.kind) - { - case FILTER_TOKEN_AND: - WinDivertFilterUpdate(filter, f, *fp, *fp, - WINDIVERT_FILTER_RESULT_REJECT); - goto WinDivertParseFilterNext; - case FILTER_TOKEN_OR: - WinDivertFilterUpdate(filter, f, *fp, - WINDIVERT_FILTER_RESULT_ACCEPT, *fp); - goto WinDivertParseFilterNext; - default: - break; - } - return TRUE; -} - -/* - * Update success. - */ -static void WinDivertFilterUpdate(windivert_ioctl_filter_t filter, UINT16 s, - UINT16 e, UINT16 success, UINT16 failure) -{ - UINT16 i; - - for (i = s; i < e; i++) - { - switch (filter[i].success) - { - case WINDIVERT_FILTER_RESULT_ACCEPT: - filter[i].success = success; - break; - case WINDIVERT_FILTER_RESULT_REJECT: - filter[i].success = failure; - break; - } - switch (filter[i].failure) - { - case WINDIVERT_FILTER_RESULT_ACCEPT: - filter[i].failure = success; - break; - case WINDIVERT_FILTER_RESULT_REJECT: - filter[i].failure = failure; - break; - } - } -} - -/****************************************************************************/ -/* WINDIVERT HELPER IMPLEMENTATION */ -/****************************************************************************/ - -/* - * Skip well-known IPv6 extension headers. - */ -static UINT8 WinDivertSkipExtHeaders(UINT8 proto, UINT8 **header, UINT *len) -{ - UINT hdrlen; - - while (TRUE) - { - if (*len <= 2) - { - return IPPROTO_NONE; - } - - hdrlen = (UINT)*(*header + 1); - switch (proto) - { - case IPPROTO_FRAGMENT: - hdrlen = 8; - break; - case IPPROTO_AH: - hdrlen += 2; - hdrlen *= 4; - break; - case IPPROTO_HOPOPTS: - case IPPROTO_DSTOPTS: - case IPPROTO_ROUTING: - hdrlen++; - hdrlen *= 8; - break; - case IPPROTO_NONE: - return proto; - default: - return proto; - } - - if (hdrlen >= *len) - { - return IPPROTO_NONE; - } - - proto = **header; - *header += hdrlen; - *len -= hdrlen; - } -} - -/* - * Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet. - */ -extern BOOL WinDivertHelperParsePacket(PVOID pPacket, UINT packetLen, - PWINDIVERT_IPHDR *ppIpHdr, PWINDIVERT_IPV6HDR *ppIpv6Hdr, - PWINDIVERT_ICMPHDR *ppIcmpHdr, PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr, - PWINDIVERT_TCPHDR *ppTcpHdr, PWINDIVERT_UDPHDR *ppUdpHdr, PVOID *ppData, - UINT *pDataLen) -{ - PWINDIVERT_IPHDR ip_header = NULL; - PWINDIVERT_IPV6HDR ipv6_header = NULL; - PWINDIVERT_ICMPHDR icmp_header = NULL; - PWINDIVERT_ICMPV6HDR icmpv6_header = NULL; - PWINDIVERT_TCPHDR tcp_header = NULL; - PWINDIVERT_UDPHDR udp_header = NULL; - UINT16 header_len; - UINT8 trans_proto; - PVOID data = NULL; - UINT data_len = 0; - BOOL success; - - if (pPacket == NULL || packetLen < sizeof(UINT8)) - { - goto WinDivertHelperParsePacketExit; - } - data = pPacket; - data_len = packetLen; - - ip_header = (PWINDIVERT_IPHDR)data; - switch (ip_header->Version) - { - case 4: - if (data_len < sizeof(WINDIVERT_IPHDR) || - ip_header->HdrLength < 5 || - data_len < ip_header->HdrLength*sizeof(UINT32) || - ntohs(ip_header->Length) != data_len) - { - ip_header = NULL; - goto WinDivertHelperParsePacketExit; - } - trans_proto = ip_header->Protocol; - header_len = ip_header->HdrLength*sizeof(UINT32); - data = (PVOID)((UINT8 *)data + header_len); - data_len -= header_len; - break; - case 6: - ip_header = NULL; - ipv6_header = (PWINDIVERT_IPV6HDR)data; - if (data_len < sizeof(WINDIVERT_IPV6HDR) || - ntohs(ipv6_header->Length) != - data_len - sizeof(WINDIVERT_IPV6HDR)) - { - ipv6_header = NULL; - goto WinDivertHelperParsePacketExit; - } - trans_proto = ipv6_header->NextHdr; - data = (PVOID)((UINT8 *)data + sizeof(WINDIVERT_IPV6HDR)); - data_len -= sizeof(WINDIVERT_IPV6HDR); - trans_proto = WinDivertSkipExtHeaders(trans_proto, (UINT8 **)&data, - &data_len); - break; - default: - ip_header = NULL; - goto WinDivertHelperParsePacketExit; - } - - switch (trans_proto) - { - case IPPROTO_TCP: - tcp_header = (PWINDIVERT_TCPHDR)data; - if (data_len < sizeof(WINDIVERT_TCPHDR) || - tcp_header->HdrLength < 5 || - data_len < tcp_header->HdrLength*sizeof(UINT32)) - { - tcp_header = NULL; - goto WinDivertHelperParsePacketExit; - } - header_len = tcp_header->HdrLength*sizeof(UINT32); - data = ((UINT8 *)data + header_len); - data_len -= header_len; - break; - case IPPROTO_UDP: - udp_header = (PWINDIVERT_UDPHDR)data; - if (data_len < sizeof(WINDIVERT_UDPHDR) || - ntohs(udp_header->Length) != data_len) - { - udp_header = NULL; - goto WinDivertHelperParsePacketExit; - } - data = ((UINT8 *)data + sizeof(WINDIVERT_UDPHDR)); - data_len -= sizeof(WINDIVERT_UDPHDR); - break; - case IPPROTO_ICMP: - icmp_header = (PWINDIVERT_ICMPHDR)data; - if (ip_header == NULL || - data_len < sizeof(WINDIVERT_ICMPHDR)) - { - icmp_header = NULL; - goto WinDivertHelperParsePacketExit; - } - data = ((UINT8 *)data + sizeof(WINDIVERT_ICMPHDR)); - data_len -= sizeof(WINDIVERT_ICMPHDR); - break; - case IPPROTO_ICMPV6: - icmpv6_header = (PWINDIVERT_ICMPV6HDR)data; - if (ipv6_header == NULL || - data_len < sizeof(WINDIVERT_ICMPV6HDR)) - { - icmpv6_header = NULL; - goto WinDivertHelperParsePacketExit; - } - data = ((UINT8 *)data + sizeof(WINDIVERT_ICMPV6HDR)); - data_len -= sizeof(WINDIVERT_ICMPV6HDR); - break; - default: - break; - } - - if (data_len == 0) - { - data = NULL; - } - -WinDivertHelperParsePacketExit: - success = TRUE; - if (ppIpHdr != NULL) - { - *ppIpHdr = ip_header; - success = success && (ip_header != NULL); - } - if (ppIpv6Hdr != NULL) - { - *ppIpv6Hdr = ipv6_header; - success = success && (ipv6_header != NULL); - } - if (ppIcmpHdr != NULL) - { - *ppIcmpHdr = icmp_header; - success = success && (icmp_header != NULL); - } - if (ppIcmpv6Hdr != NULL) - { - *ppIcmpv6Hdr = icmpv6_header; - success = success && (icmpv6_header != NULL); - } - if (ppTcpHdr != NULL) - { - *ppTcpHdr = tcp_header; - success = success && (tcp_header != NULL); - } - if (ppUdpHdr != NULL) - { - *ppUdpHdr = udp_header; - success = success && (udp_header != NULL); - } - if (ppData != NULL) - { - *ppData = data; - success = success && (data != NULL); - } - if (pDataLen != NULL) - { - *pDataLen = data_len; - } - return success; -} - -/* - * Calculate IPv4/IPv6/ICMP/ICMPv6/TCP/UDP checksums. - */ -extern UINT WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, - UINT64 flags) -{ - WINDIVERT_PSEUDOHDR pseudo_header; - WINDIVERT_PSEUDOV6HDR pseudov6_header; - PWINDIVERT_IPHDR ip_header; - PWINDIVERT_IPV6HDR ipv6_header; - PWINDIVERT_ICMPHDR icmp_header; - PWINDIVERT_ICMPV6HDR icmpv6_header; - PWINDIVERT_TCPHDR tcp_header; - PWINDIVERT_UDPHDR udp_header; - UINT payload_len, checksum_len; - UINT count = 0; - - WinDivertHelperParsePacket(pPacket, packetLen, &ip_header, &ipv6_header, - &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL, - &payload_len); - - if (ip_header != NULL && !(flags & WINDIVERT_HELPER_NO_IP_CHECKSUM)) - { - ip_header->Checksum = 0; - ip_header->Checksum = WinDivertHelperCalcChecksum(NULL, 0, - ip_header, ip_header->HdrLength*sizeof(UINT32)); - count++; - } - - if (icmp_header != NULL) - { - if (flags & WINDIVERT_HELPER_NO_ICMP_CHECKSUM) - { - return count; - } - icmp_header->Checksum = 0; - icmp_header->Checksum = WinDivertHelperCalcChecksum(NULL, 0, - icmp_header, payload_len + sizeof(WINDIVERT_ICMPHDR)); - count++; - return count; - } - - if (icmpv6_header != NULL) - { - if (flags & WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM) - { - return count; - } - checksum_len = payload_len + sizeof(WINDIVERT_ICMPV6HDR); - WinDivertInitPseudoHeaderV6(ipv6_header, &pseudov6_header, - IPPROTO_ICMPV6, checksum_len); - icmpv6_header->Checksum = 0; - icmpv6_header->Checksum = WinDivertHelperCalcChecksum(&pseudov6_header, - sizeof(pseudov6_header), icmpv6_header, checksum_len); - count++; - return count; - } - - if (tcp_header != NULL) - { - if (flags & WINDIVERT_HELPER_NO_TCP_CHECKSUM) - { - return count; - } - checksum_len = payload_len + tcp_header->HdrLength*sizeof(UINT32); - if (ip_header != NULL) - { - WinDivertInitPseudoHeader(ip_header, &pseudo_header, IPPROTO_TCP, - checksum_len); - tcp_header->Checksum = 0; - tcp_header->Checksum = WinDivertHelperCalcChecksum(&pseudo_header, - sizeof(pseudo_header), tcp_header, checksum_len); - } - else - { - WinDivertInitPseudoHeaderV6(ipv6_header, &pseudov6_header, - IPPROTO_TCP, checksum_len); - tcp_header->Checksum = 0; - tcp_header->Checksum = WinDivertHelperCalcChecksum(&pseudov6_header, - sizeof(pseudov6_header), tcp_header, checksum_len); - } - count++; - return count; - } - - if (udp_header != NULL) - { - if (flags & WINDIVERT_HELPER_NO_UDP_CHECKSUM) - { - return count; - } - checksum_len = payload_len + sizeof(WINDIVERT_UDPHDR); - if (ip_header != NULL) - { - WinDivertInitPseudoHeader(ip_header, &pseudo_header, IPPROTO_UDP, - checksum_len); - udp_header->Checksum = 0; - udp_header->Checksum = WinDivertHelperCalcChecksum(&pseudo_header, - sizeof(pseudo_header), udp_header, checksum_len); - if (udp_header->Checksum == 0) - { - udp_header->Checksum = 0xFFFF; - } - } - else - { - WinDivertInitPseudoHeaderV6(ipv6_header, &pseudov6_header, - IPPROTO_UDP, checksum_len); - udp_header->Checksum = 0; - udp_header->Checksum = WinDivertHelperCalcChecksum(&pseudov6_header, - sizeof(pseudov6_header), udp_header, checksum_len); - } - count++; - } - return count; -} - -/* - * Initialize the IP pseudo header. - */ -static void WinDivertInitPseudoHeader(PWINDIVERT_IPHDR ip_header, - PWINDIVERT_PSEUDOHDR pseudo_header, UINT8 protocol, UINT len) -{ - pseudo_header->SrcAddr = ip_header->SrcAddr; - pseudo_header->DstAddr = ip_header->DstAddr; - pseudo_header->Zero = 0; - pseudo_header->Protocol = protocol; - pseudo_header->Length = htons((UINT16)len); -} - -/* - * Initialize the IPv6 pseudo header. - */ -static void WinDivertInitPseudoHeaderV6(PWINDIVERT_IPV6HDR ipv6_header, - PWINDIVERT_PSEUDOV6HDR pseudov6_header, UINT8 protocol, UINT len) -{ - memcpy(pseudov6_header->SrcAddr, ipv6_header->SrcAddr, - sizeof(pseudov6_header->SrcAddr)); - memcpy(pseudov6_header->DstAddr, ipv6_header->DstAddr, - sizeof(pseudov6_header->DstAddr)); - pseudov6_header->Length = htonl((UINT32)len); - pseudov6_header->NextHdr = protocol; - pseudov6_header->Zero = 0; -} - -/* - * Generic checksum computation. - */ -static UINT16 WinDivertHelperCalcChecksum(PVOID pseudo_header, - UINT16 pseudo_header_len, PVOID data, UINT len) -{ - register const UINT16 *data16 = (const UINT16 *)pseudo_header; - register size_t len16 = pseudo_header_len >> 1; - register UINT32 sum = 0; - size_t i; - - // Pseudo header: - for (i = 0; i < len16; i++) - { - sum += (UINT32)data16[i]; - } - - // Main data: - data16 = (const UINT16 *)data; - len16 = len >> 1; - for (i = 0; i < len16; i++) - { - sum += (UINT32)data16[i]; - } - - if (len & 0x1) - { - const UINT8 *data8 = (const UINT8 *)data; - sum += (UINT16)data8[len-1]; - } - - sum = (sum & 0xFFFF) + (sum >> 16); - sum += (sum >> 16); - sum = ~sum; - return (UINT16)sum; -} - -/* - * Parse an IPv4 address. - */ -extern BOOL WinDivertHelperParseIPv4Address(const char *str, UINT32 *addr_ptr) -{ - UINT32 addr = 0; - UINT32 part, i; - - for (i = 0; i < 4; i++) - { - if (!WinDivertAToI(str, (char **)&str, &part) || 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. - */ -extern BOOL WinDivertHelperParseIPv6Address(const char *str, UINT32 *addr_ptr) -{ - UINT16 laddr[8]; - UINT16 raddr[8]; - BOOL left = TRUE; - UINT i, j, k, l, part; - char part_str[5]; - - memset(laddr, 0, sizeof(laddr)); - memset(raddr, 0, sizeof(raddr)); - - if (*str == ':') - { - str++; - if (*str != ':') - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - left = FALSE; - str++; - } - - for (i = 0, j = 0, k = 0; k < 8; k++) - { - if (*str == ':') - { - if (!left) - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - left = FALSE; - str++; - } - for (l = 0; l < 4 && isxdigit(*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); - if (left) - { - laddr[i++] = (UINT16)part; - } - else - { - raddr[j++] = (UINT16)part; - } - if (*str == '\0') - { - if (!left) - { - break; - } - if (k == 7) - { - break; - } - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - str++; - } - if (*str != '\0') - { - SetLastError(ERROR_INVALID_PARAMETER); - return FALSE; - } - - if (addr_ptr == NULL) - { - return TRUE; - } - - for (i = 0; i < 4; i++) - { - k = 2 * i + j; - l = k + 1; - k = (k >= 8? k - 8: k); - l = (l >= 8? l - 8: l); - addr_ptr[i] = - (UINT32)laddr[2 * i + 1] | - (UINT32)laddr[2 * i] << 16 | - (UINT32)raddr[l] | - (UINT32)raddr[k] << 16; - } - return TRUE; -} - /*****************************************************************************/ /* REPLACEMENTS */ /*****************************************************************************/ diff --git a/dll/windivert_helper.c b/dll/windivert_helper.c new file mode 100644 index 0000000..995b394 --- /dev/null +++ b/dll/windivert_helper.c @@ -0,0 +1,2408 @@ +/* + * windivert_helper.c + * (C) 2015, all rights reserved, + * + * This program 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 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_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_LENGTH, + TOKEN_UDP_SRC_PORT, + TOKEN_TRUE, + TOKEN_FALSE, + TOKEN_INBOUND, + TOKEN_OUTBOUND, + TOKEN_IF_IDX, + TOKEN_SUB_IF_IDX, + TOKEN_OPEN, + TOKEN_CLOSE, + 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 32 + +typedef struct +{ + char *name; + KIND kind; +} TOKEN_NAME, *PTOKEN_NAME; + +/* + * Filter expressions. + */ +typedef struct EXPR EXPR; +typedef struct EXPR *PEXPR; +struct EXPR +{ + union + { + UINT32 val[4]; + PEXPR arg[3]; + }; + UINT8 kind; + UINT16 succ; + UINT16 fail; +}; + +/* + * Error handling. + */ +#undef ERROR +typedef UINT64 ERROR; + +#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_OUTPUT_TOO_SHORT 7 +#define WINDIVERT_ERROR_ASSERTION_FAILED 8 + +#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) + +/* + * Compiler memory pool: + */ +typedef struct POOL +{ + unsigned offset; + ERROR error; + char memory[3 * 4096 - 32]; +} POOL, *PPOOL; + +/* + * Prototypes. + */ +static PEXPR WinDivertParseFilter(PPOOL pool, TOKEN *toks, UINT *i, INT depth, + BOOL and); +static void WinDivertInitPseudoHeader(PWINDIVERT_IPHDR ip_header, + PWINDIVERT_PSEUDOHDR pseudo_header, UINT8 protocol, UINT len); +static void WinDivertInitPseudoHeaderV6(PWINDIVERT_IPV6HDR ipv6_header, + PWINDIVERT_PSEUDOV6HDR pseudov6_header, UINT8 protocol, UINT len); +static UINT16 WinDivertHelperCalcChecksum(PVOID pseudo_header, + UINT16 pseudo_header_len, PVOID data, UINT len); + +/* + * Skip well-known IPv6 extension headers. + */ +static UINT8 WinDivertSkipExtHeaders(UINT8 proto, UINT8 **header, UINT *len) +{ + UINT hdrlen; + + while (TRUE) + { + if (*len <= 2) + { + return IPPROTO_NONE; + } + + hdrlen = (UINT)*(*header + 1); + switch (proto) + { + case IPPROTO_FRAGMENT: + hdrlen = 8; + break; + case IPPROTO_AH: + hdrlen += 2; + hdrlen *= 4; + break; + case IPPROTO_HOPOPTS: + case IPPROTO_DSTOPTS: + case IPPROTO_ROUTING: + hdrlen++; + hdrlen *= 8; + break; + case IPPROTO_NONE: + return proto; + default: + return proto; + } + + if (hdrlen >= *len) + { + return IPPROTO_NONE; + } + + proto = **header; + *header += hdrlen; + *len -= hdrlen; + } +} + +/* + * Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet. + */ +extern BOOL WinDivertHelperParsePacket(PVOID pPacket, UINT packetLen, + PWINDIVERT_IPHDR *ppIpHdr, PWINDIVERT_IPV6HDR *ppIpv6Hdr, + PWINDIVERT_ICMPHDR *ppIcmpHdr, PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr, + PWINDIVERT_TCPHDR *ppTcpHdr, PWINDIVERT_UDPHDR *ppUdpHdr, PVOID *ppData, + UINT *pDataLen) +{ + PWINDIVERT_IPHDR ip_header = NULL; + PWINDIVERT_IPV6HDR ipv6_header = NULL; + PWINDIVERT_ICMPHDR icmp_header = NULL; + PWINDIVERT_ICMPV6HDR icmpv6_header = NULL; + PWINDIVERT_TCPHDR tcp_header = NULL; + PWINDIVERT_UDPHDR udp_header = NULL; + UINT16 header_len; + UINT8 trans_proto; + PVOID data = NULL; + UINT data_len = 0; + BOOL success; + + if (pPacket == NULL || packetLen < sizeof(UINT8)) + { + goto WinDivertHelperParsePacketExit; + } + data = pPacket; + data_len = packetLen; + + ip_header = (PWINDIVERT_IPHDR)data; + switch (ip_header->Version) + { + case 4: + if (data_len < sizeof(WINDIVERT_IPHDR) || + ip_header->HdrLength < 5 || + data_len < ip_header->HdrLength*sizeof(UINT32) || + ntohs(ip_header->Length) != data_len) + { + ip_header = NULL; + goto WinDivertHelperParsePacketExit; + } + trans_proto = ip_header->Protocol; + header_len = ip_header->HdrLength*sizeof(UINT32); + data = (PVOID)((UINT8 *)data + header_len); + data_len -= header_len; + break; + case 6: + ip_header = NULL; + ipv6_header = (PWINDIVERT_IPV6HDR)data; + if (data_len < sizeof(WINDIVERT_IPV6HDR) || + ntohs(ipv6_header->Length) != + data_len - sizeof(WINDIVERT_IPV6HDR)) + { + ipv6_header = NULL; + goto WinDivertHelperParsePacketExit; + } + trans_proto = ipv6_header->NextHdr; + data = (PVOID)((UINT8 *)data + sizeof(WINDIVERT_IPV6HDR)); + data_len -= sizeof(WINDIVERT_IPV6HDR); + trans_proto = WinDivertSkipExtHeaders(trans_proto, (UINT8 **)&data, + &data_len); + break; + default: + ip_header = NULL; + goto WinDivertHelperParsePacketExit; + } + switch (trans_proto) + { + case IPPROTO_TCP: + tcp_header = (PWINDIVERT_TCPHDR)data; + if (data_len < sizeof(WINDIVERT_TCPHDR) || + tcp_header->HdrLength < 5 || + data_len < tcp_header->HdrLength*sizeof(UINT32)) + { + tcp_header = NULL; + goto WinDivertHelperParsePacketExit; + } + header_len = tcp_header->HdrLength*sizeof(UINT32); + data = ((UINT8 *)data + header_len); + data_len -= header_len; + break; + case IPPROTO_UDP: + udp_header = (PWINDIVERT_UDPHDR)data; + if (data_len < sizeof(WINDIVERT_UDPHDR) || + ntohs(udp_header->Length) != data_len) + { + udp_header = NULL; + goto WinDivertHelperParsePacketExit; + } + data = ((UINT8 *)data + sizeof(WINDIVERT_UDPHDR)); + data_len -= sizeof(WINDIVERT_UDPHDR); + break; + case IPPROTO_ICMP: + icmp_header = (PWINDIVERT_ICMPHDR)data; + if (ip_header == NULL || + data_len < sizeof(WINDIVERT_ICMPHDR)) + { + icmp_header = NULL; + goto WinDivertHelperParsePacketExit; + } + data = ((UINT8 *)data + sizeof(WINDIVERT_ICMPHDR)); + data_len -= sizeof(WINDIVERT_ICMPHDR); + break; + case IPPROTO_ICMPV6: + icmpv6_header = (PWINDIVERT_ICMPV6HDR)data; + if (ipv6_header == NULL || + data_len < sizeof(WINDIVERT_ICMPV6HDR)) + { + icmpv6_header = NULL; + goto WinDivertHelperParsePacketExit; + } + data = ((UINT8 *)data + sizeof(WINDIVERT_ICMPV6HDR)); + data_len -= sizeof(WINDIVERT_ICMPV6HDR); + break; + default: + break; + } + + if (data_len == 0) + { + data = NULL; + } + +WinDivertHelperParsePacketExit: + success = TRUE; + if (ppIpHdr != NULL) + { + *ppIpHdr = ip_header; + success = success && (ip_header != NULL); + } + if (ppIpv6Hdr != NULL) + { + *ppIpv6Hdr = ipv6_header; + success = success && (ipv6_header != NULL); + } + if (ppIcmpHdr != NULL) + { + *ppIcmpHdr = icmp_header; + success = success && (icmp_header != NULL); + } + if (ppIcmpv6Hdr != NULL) + { + *ppIcmpv6Hdr = icmpv6_header; + success = success && (icmpv6_header != NULL); + } + if (ppTcpHdr != NULL) + { + *ppTcpHdr = tcp_header; + success = success && (tcp_header != NULL); + } + if (ppUdpHdr != NULL) + { + *ppUdpHdr = udp_header; + success = success && (udp_header != NULL); + } + if (ppData != NULL) + { + *ppData = data; + success = success && (data != NULL); + } + if (pDataLen != NULL) + { + *pDataLen = data_len; + } + return success; +} + +/* + * Calculate IPv4/IPv6/ICMP/ICMPv6/TCP/UDP checksums. + */ +extern UINT WinDivertHelperCalcChecksums(PVOID pPacket, UINT packetLen, + UINT64 flags) +{ + WINDIVERT_PSEUDOHDR pseudo_header; + WINDIVERT_PSEUDOV6HDR pseudov6_header; + PWINDIVERT_IPHDR ip_header; + PWINDIVERT_IPV6HDR ipv6_header; + PWINDIVERT_ICMPHDR icmp_header; + PWINDIVERT_ICMPV6HDR icmpv6_header; + PWINDIVERT_TCPHDR tcp_header; + PWINDIVERT_UDPHDR udp_header; + UINT payload_len, checksum_len; + UINT count = 0; + + WinDivertHelperParsePacket(pPacket, packetLen, &ip_header, &ipv6_header, + &icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL, + &payload_len); + + if (ip_header != NULL && !(flags & WINDIVERT_HELPER_NO_IP_CHECKSUM)) + { + ip_header->Checksum = 0; + ip_header->Checksum = WinDivertHelperCalcChecksum(NULL, 0, + ip_header, ip_header->HdrLength*sizeof(UINT32)); + count++; + } + + if (icmp_header != NULL) + { + if (flags & WINDIVERT_HELPER_NO_ICMP_CHECKSUM) + { + return count; + } + icmp_header->Checksum = 0; + icmp_header->Checksum = WinDivertHelperCalcChecksum(NULL, 0, + icmp_header, payload_len + sizeof(WINDIVERT_ICMPHDR)); + count++; + return count; + } + + if (icmpv6_header != NULL) + { + if (flags & WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM) + { + return count; + } + checksum_len = payload_len + sizeof(WINDIVERT_ICMPV6HDR); + WinDivertInitPseudoHeaderV6(ipv6_header, &pseudov6_header, + IPPROTO_ICMPV6, checksum_len); + icmpv6_header->Checksum = 0; + icmpv6_header->Checksum = WinDivertHelperCalcChecksum(&pseudov6_header, + sizeof(pseudov6_header), icmpv6_header, checksum_len); + count++; + return count; + } + + if (tcp_header != NULL) + { + if (flags & WINDIVERT_HELPER_NO_TCP_CHECKSUM) + { + return count; + } + checksum_len = payload_len + tcp_header->HdrLength*sizeof(UINT32); + if (ip_header != NULL) + { + WinDivertInitPseudoHeader(ip_header, &pseudo_header, IPPROTO_TCP, + checksum_len); + tcp_header->Checksum = 0; + tcp_header->Checksum = WinDivertHelperCalcChecksum(&pseudo_header, + sizeof(pseudo_header), tcp_header, checksum_len); + } + else + { + WinDivertInitPseudoHeaderV6(ipv6_header, &pseudov6_header, + IPPROTO_TCP, checksum_len); + tcp_header->Checksum = 0; + tcp_header->Checksum = WinDivertHelperCalcChecksum(&pseudov6_header, + sizeof(pseudov6_header), tcp_header, checksum_len); + } + count++; + return count; + } + + if (udp_header != NULL) + { + if (flags & WINDIVERT_HELPER_NO_UDP_CHECKSUM) + { + return count; + } + checksum_len = payload_len + sizeof(WINDIVERT_UDPHDR); + if (ip_header != NULL) + { + WinDivertInitPseudoHeader(ip_header, &pseudo_header, IPPROTO_UDP, + checksum_len); + udp_header->Checksum = 0; + udp_header->Checksum = WinDivertHelperCalcChecksum(&pseudo_header, + sizeof(pseudo_header), udp_header, checksum_len); + if (udp_header->Checksum == 0) + { + udp_header->Checksum = 0xFFFF; + } + } + else + { + WinDivertInitPseudoHeaderV6(ipv6_header, &pseudov6_header, + IPPROTO_UDP, checksum_len); + udp_header->Checksum = 0; + udp_header->Checksum = WinDivertHelperCalcChecksum(&pseudov6_header, + sizeof(pseudov6_header), udp_header, checksum_len); + } + count++; + } + return count; +} + +/* + * Initialize the IP pseudo header. + */ +static void WinDivertInitPseudoHeader(PWINDIVERT_IPHDR ip_header, + PWINDIVERT_PSEUDOHDR pseudo_header, UINT8 protocol, UINT len) +{ + pseudo_header->SrcAddr = ip_header->SrcAddr; + pseudo_header->DstAddr = ip_header->DstAddr; + pseudo_header->Zero = 0; + pseudo_header->Protocol = protocol; + pseudo_header->Length = htons((UINT16)len); +} + +/* + * Initialize the IPv6 pseudo header. + */ +static void WinDivertInitPseudoHeaderV6(PWINDIVERT_IPV6HDR ipv6_header, + PWINDIVERT_PSEUDOV6HDR pseudov6_header, UINT8 protocol, UINT len) +{ + memcpy(pseudov6_header->SrcAddr, ipv6_header->SrcAddr, + sizeof(pseudov6_header->SrcAddr)); + memcpy(pseudov6_header->DstAddr, ipv6_header->DstAddr, + sizeof(pseudov6_header->DstAddr)); + pseudov6_header->Length = htonl((UINT32)len); + pseudov6_header->NextHdr = protocol; + pseudov6_header->Zero = 0; +} + +/* + * Generic checksum computation. + */ +static UINT16 WinDivertHelperCalcChecksum(PVOID pseudo_header, + UINT16 pseudo_header_len, PVOID data, UINT len) +{ + register const UINT16 *data16 = (const UINT16 *)pseudo_header; + register size_t len16 = pseudo_header_len >> 1; + register UINT32 sum = 0; + size_t i; + + // Pseudo header: + for (i = 0; i < len16; i++) + { + sum += (UINT32)data16[i]; + } + + // Main data: + data16 = (const UINT16 *)data; + len16 = len >> 1; + for (i = 0; i < len16; i++) + { + sum += (UINT32)data16[i]; + } + + if (len & 0x1) + { + const UINT8 *data8 = (const UINT8 *)data; + sum += (UINT16)data8[len-1]; + } + + sum = (sum & 0xFFFF) + (sum >> 16); + sum += (sum >> 16); + sum = ~sum; + return (UINT16)sum; +} + +/* + * Parse an IPv4 address. + */ +extern 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) || 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. + */ +extern BOOL WinDivertHelperParseIPv6Address(const char *str, UINT32 *addr_ptr) +{ + UINT16 laddr[8]; + UINT16 raddr[8]; + BOOL left = TRUE; + UINT i, j, k, l, part; + char part_str[5]; + + memset(laddr, 0, sizeof(laddr)); + memset(raddr, 0, sizeof(raddr)); + + if (*str == ':') + { + str++; + if (*str != ':') + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + left = FALSE; + str++; + } + + for (i = 0, j = 0, k = 0; k < 8; k++) + { + if (*str == ':') + { + if (!left) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + left = FALSE; + str++; + } + for (l = 0; l < 4 && isxdigit(*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); + if (left) + { + laddr[i++] = (UINT16)part; + } + else + { + raddr[j++] = (UINT16)part; + } + if (*str == '\0') + { + if (!left) + { + break; + } + if (k == 7) + { + break; + } + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + str++; + } + if (*str != '\0') + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if (addr_ptr == NULL) + { + return TRUE; + } + + for (i = 0; i < 4; i++) + { + k = 2 * i + j; + l = k + 1; + k = (k >= 8? k - 8: k); + l = (l >= 8? l - 8: l); + addr_ptr[i] = + (UINT32)laddr[2 * i + 1] | + (UINT32)laddr[2 * i] << 16 | + (UINT32)raddr[l] | + (UINT32)raddr[k] << 16; + } + return TRUE; +} + +/* + * Lookup a token. + */ +static PTOKEN_NAME WinDivertTokenLookup(PTOKEN_NAME token_names, + size_t token_names_len, const char *name) +{ + int lo = 0, hi = (int)token_names_len-1, mid; + int cmp; + while (hi >= lo) + { + mid = (lo + hi) / 2; + cmp = strcmp(token_names[mid].name, name); + if (cmp < 0) + { + lo = mid+1; + } + else if (cmp > 0) + { + hi = mid-1; + } + else + { + return &token_names[mid]; + } + } + return NULL; +} + +/* + * Tokenize the given filter string. + */ +static ERROR WinDivertTokenizeFilter(const char *filter, WINDIVERT_LAYER layer, + TOKEN *tokens, UINT tokensmax) +{ + static const TOKEN_NAME token_names[] = + { + {"and", TOKEN_AND}, + {"false", TOKEN_FALSE}, + {"icmp", TOKEN_ICMP}, + {"icmp.Body", TOKEN_ICMP_BODY}, + {"icmp.Checksum", TOKEN_ICMP_CHECKSUM}, + {"icmp.Code", TOKEN_ICMP_CODE}, + {"icmp.Type", TOKEN_ICMP_TYPE}, + {"icmpv6", TOKEN_ICMPV6}, + {"icmpv6.Body", TOKEN_ICMPV6_BODY}, + {"icmpv6.Checksum", TOKEN_ICMPV6_CHECKSUM}, + {"icmpv6.Code", TOKEN_ICMPV6_CODE}, + {"icmpv6.Type", TOKEN_ICMPV6_TYPE}, + {"ifIdx", TOKEN_IF_IDX}, + {"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}, + {"not", TOKEN_NOT}, + {"or", TOKEN_OR}, + {"outbound", TOKEN_OUTBOUND}, + {"subIfIdx", TOKEN_SUB_IF_IDX}, + {"tcp", TOKEN_TCP}, + {"tcp.Ack", TOKEN_TCP_ACK}, + {"tcp.AckNum", TOKEN_TCP_ACK_NUM}, + {"tcp.Checksum", TOKEN_TCP_CHECKSUM}, + {"tcp.DstPort", TOKEN_TCP_DST_PORT}, + {"tcp.Fin", TOKEN_TCP_FIN}, + {"tcp.HdrLength", TOKEN_TCP_HDR_LENGTH}, + {"tcp.PayloadLength", TOKEN_TCP_PAYLOAD_LENGTH}, + {"tcp.Psh", TOKEN_TCP_PSH}, + {"tcp.Rst", TOKEN_TCP_RST}, + {"tcp.SeqNum", TOKEN_TCP_SEQ_NUM}, + {"tcp.SrcPort", TOKEN_TCP_SRC_PORT}, + {"tcp.Syn", TOKEN_TCP_SYN}, + {"tcp.Urg", TOKEN_TCP_URG}, + {"tcp.UrgPtr", TOKEN_TCP_URG_PTR}, + {"tcp.Window", TOKEN_TCP_WINDOW}, + {"true", TOKEN_TRUE}, + {"udp", TOKEN_UDP}, + {"udp.Checksum", TOKEN_UDP_CHECKSUM}, + {"udp.DstPort", TOKEN_UDP_DST_PORT}, + {"udp.Length", TOKEN_UDP_LENGTH}, + {"udp.PayloadLength", TOKEN_UDP_PAYLOAD_LENGTH}, + {"udp.SrcPort", TOKEN_UDP_SRC_PORT}, + }; + TOKEN_NAME *result; + char c; + char token[TOKEN_MAXLEN]; + 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 (isspace(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 '!': + 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 (isalnum(c) || c == '.' || c == ':') + { + UINT32 num; + char *end; + for (j = 1; j < TOKEN_MAXLEN && (isalnum(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_NAME)token_names, + sizeof(token_names) / sizeof(TOKEN_NAME), token); + if (result != NULL) + { + switch (layer) + { + case WINDIVERT_LAYER_NETWORK_FORWARD: + if (result->kind == TOKEN_INBOUND || + result->kind == TOKEN_OUTBOUND) + { + return MAKE_ERROR( + WINDIVERT_ERROR_BAD_TOKEN_FOR_LAYER, i-j); + } + break; + default: + break; + } + tokens[tp++].kind = result->kind; + continue; + } + + // Check for base 10 number: + if (WinDivertAToI(token, &end, &num) && *end == '\0') + { + tokens[tp].kind = TOKEN_NUMBER; + tokens[tp].val[0] = num; + tp++; + continue; + } + + // Check for base 16 number: + if (WinDivertAToX(token, &end, &num) && *end == '\0') + { + tokens[tp].kind = TOKEN_NUMBER; + tokens[tp].val[0] = num; + tp++; + continue; + } + // Check for IPv4 address: + if (WinDivertHelperParseIPv4Address(token, tokens[tp].val)) + { + tokens[tp].kind = TOKEN_NUMBER; + tp++; + continue; + } + + // Check for IPv6 address: + SetLastError(0); + if (WinDivertHelperParseIPv6Address(token, tokens[tp].val)) + { + // Work-around the different word orderings between the + // DLL vs SYS. + UINT32 tmp; + tmp = tokens[tp].val[0]; + tokens[tp].val[0] = tokens[tp].val[3]; + tokens[tp].val[3] = tmp; + tmp = tokens[tp].val[1]; + tokens[tp].val[1] = tokens[tp].val[2]; + tokens[tp].val[2] = tmp; + + tokens[tp].kind = TOKEN_NUMBER; + tp++; + continue; + } + + return MAKE_ERROR(WINDIVERT_ERROR_BAD_TOKEN, i-j); + } + else + { + return MAKE_ERROR(WINDIVERT_ERROR_BAD_TOKEN, i); + } + } +} + +/* + * Pool allocation. + */ +static void *WinDivertAlloc(PPOOL pool, UINT size) +{ + void *ptr; + if (pool->offset + size >= sizeof(pool->memory)) + { + pool->error = MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); + return NULL; + } + ptr = pool->memory + pool->offset; + pool->offset += size; + return ptr; +}; + +/* + * Construct a variable/field. + */ +static PEXPR WinDivertMakeVar(PPOOL pool, KIND kind) +{ + // 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_TRUE}, + {{{0}}, TOKEN_FALSE}, + {{{0}}, TOKEN_INBOUND}, + {{{0}}, TOKEN_OUTBOUND}, + {{{0}}, TOKEN_IF_IDX}, + {{{0}}, TOKEN_SUB_IF_IDX} + }; + + // 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); + } + pool->error = MAKE_ERROR(WINDIVERT_ERROR_ASSERTION_FAILED, 0); + return NULL; +} + +/* + * Construct zero. + */ +static PEXPR WinDivertMakeZero(PPOOL pool) +{ + static const EXPR zero = {{{0, 0, 0, 0}}, TOKEN_NUMBER}; + return (PEXPR)&zero; +} + +/* + * Construct a number. + */ +static PEXPR WinDivertMakeNumber(PPOOL pool, TOKEN *tok) +{ + if (tok->kind != TOKEN_NUMBER) + { + pool->error = MAKE_ERROR(WINDIVERT_ERROR_ASSERTION_FAILED, 0); + return NULL; + } + PEXPR expr = (PEXPR)WinDivertAlloc(pool, sizeof(EXPR)); + if (expr == NULL) + { + return NULL; + } + memset(expr, 0, sizeof(EXPR)); + expr->kind = TOKEN_NUMBER; + expr->val[0] = tok->val[0]; + expr->val[1] = tok->val[1]; + expr->val[2] = tok->val[2]; + expr->val[3] = tok->val[3]; + return expr; +} + +/* + * Construct a binary operator. + */ +static PEXPR WinDivertMakeBinOp(PPOOL pool, KIND kind, PEXPR arg0, PEXPR arg1) +{ + if (arg0 == NULL || arg1 == NULL) + { + return NULL; + } + PEXPR expr = (PEXPR)WinDivertAlloc(pool, sizeof(EXPR)); + if (expr == NULL) + { + return NULL; + } + memset(expr, 0, sizeof(EXPR)); + expr->kind = kind; + expr->arg[0] = arg0; + expr->arg[1] = arg1; + return expr; +} + +/* + * Construct an if-then-else. + */ +static PEXPR WinDivertMakeIfThenElse(PPOOL pool, PEXPR cond, PEXPR th, + PEXPR el) +{ + PEXPR expr = (PEXPR)WinDivertAlloc(pool, sizeof(EXPR)); + if (expr == NULL) + { + return NULL; + } + memset(expr, 0, sizeof(EXPR)); + 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(PPOOL pool, TOKEN *toks, UINT *i) +{ + BOOL not = FALSE; + while (toks[*i].kind == TOKEN_NOT) + { + not = !not; + *i = *i + 1; + } + switch (toks[*i].kind) + { + case TOKEN_TRUE: + case TOKEN_FALSE: + case TOKEN_OUTBOUND: + case TOKEN_INBOUND: + case TOKEN_IF_IDX: + case TOKEN_SUB_IF_IDX: + case TOKEN_IP: + case TOKEN_IPV6: + case TOKEN_ICMP: + case TOKEN_ICMPV6: + case TOKEN_TCP: + case TOKEN_UDP: + 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: + break; + default: + pool->error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, + toks[*i].pos); + return NULL; + } + PEXPR var = WinDivertMakeVar(pool, toks[*i].kind); + *i = *i + 1; + KIND kind; + 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(pool)); + } + 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; + if (toks[*i].kind != TOKEN_NUMBER) + { + pool->error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, + toks[*i].pos); + return NULL; + } + PEXPR val = WinDivertMakeNumber(pool, toks + *i); + *i = *i + 1; + return WinDivertMakeBinOp(pool, kind, var, val); +} + +/* + * Parse a filter argument to an (and) (or) operator. + */ +static PEXPR WinDivertParseArg(PPOOL pool, TOKEN *toks, UINT *i, INT depth) +{ + if (depth-- < 0) + { + pool->error = MAKE_ERROR(WINDIVERT_ERROR_TOO_DEEP, toks[*i].pos); + return NULL; + } + PEXPR arg, th, el; + switch (toks[*i].kind) + { + case TOKEN_OPEN: + *i = *i + 1; + arg = WinDivertParseFilter(pool, toks, i, depth, FALSE); + 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); + if (th == NULL) + { + return NULL; + } + if (toks[*i].kind != TOKEN_COLON) + { + pool->error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, + toks[*i].pos); + return NULL; + } + *i = *i + 1; + el = WinDivertParseFilter(pool, toks, i, depth, FALSE); + if (el == NULL) + { + return NULL; + } + if (toks[*i].kind != TOKEN_CLOSE) + { + pool->error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, + toks[*i].pos); + return NULL; + } + *i = *i + 1; + arg = WinDivertMakeIfThenElse(pool, arg, th, el); + return arg; + } + pool->error = MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, + toks[*i].pos); + return NULL; + default: + return WinDivertParseTest(pool, toks, i); + } +} + +/* + * Parse the filter into an expression object. + */ +static PEXPR WinDivertParseFilter(PPOOL pool, TOKEN *toks, UINT *i, INT depth, + BOOL and) +{ + PEXPR expr, arg; + if (depth-- < 0) + { + pool->error = MAKE_ERROR(WINDIVERT_ERROR_TOO_DEEP, toks[*i].pos); + return NULL; + } + if (and) + expr = WinDivertParseArg(pool, toks, i, depth); + else + expr = WinDivertParseFilter(pool, toks, i, depth, TRUE); + do + { + if (expr == NULL) + { + return NULL; + } + switch (toks[*i].kind) + { + case TOKEN_AND: + *i = *i + 1; + arg = WinDivertParseArg(pool, toks, i, depth); + expr = WinDivertMakeBinOp(pool, TOKEN_AND, expr, arg); + continue; + case TOKEN_OR: + *i = *i + 1; + arg = WinDivertParseFilter(pool, toks, i, depth, TRUE); + expr = WinDivertMakeBinOp(pool, TOKEN_OR, expr, arg); + continue; + default: + return expr; + } + } + while (TRUE); +} + +/* + * Statically evaluate a test if possible. + */ +static BOOL WinDivertEvalTest(PEXPR test, BOOL *res) +{ + PEXPR var = test->arg[0]; + PEXPR val = test->arg[1]; + UINT32 val32 = val->val[0]; + BOOL big = (val->val[1] != 0 || val->val[2] != 0 || val->val[3] != 0); + UINT32 lb, ub; + switch (var->kind) + { + case TOKEN_TRUE: + lb = ub = 1; + break; + case TOKEN_FALSE: + lb = ub = 0; + break; + case TOKEN_INBOUND: + case TOKEN_OUTBOUND: + case TOKEN_IP: + case TOKEN_IPV6: + case TOKEN_ICMP: + case TOKEN_ICMPV6: + case TOKEN_TCP: + case TOKEN_UDP: + 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: + lb = 0; ub = 1; + break; + case TOKEN_IP_HDR_LENGTH: + case TOKEN_TCP_HDR_LENGTH: + lb = 0; ub = 0x0F; + break; + case TOKEN_IP_TTL: + case TOKEN_IP_PROTOCOL: + case TOKEN_IPV6_TRAFFIC_CLASS: + case TOKEN_IPV6_NEXT_HDR: + case TOKEN_IPV6_HOP_LIMIT: + case TOKEN_ICMP_TYPE: + case TOKEN_ICMP_CODE: + case TOKEN_ICMPV6_TYPE: + case TOKEN_ICMPV6_CODE: + lb = 0; ub = 0xFF; + break; + case TOKEN_IP_FRAG_OFF: + lb = 0; ub = 0x1FFF; + break; + case TOKEN_IP_TOS: + case TOKEN_IP_LENGTH: + case TOKEN_IP_ID: + case TOKEN_IP_CHECKSUM: + case TOKEN_IPV6_LENGTH: + case TOKEN_ICMP_CHECKSUM: + case TOKEN_ICMPV6_CHECKSUM: + 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_UDP_SRC_PORT: + case TOKEN_UDP_DST_PORT: + case TOKEN_UDP_LENGTH: + case TOKEN_UDP_CHECKSUM: + case TOKEN_UDP_PAYLOAD_LENGTH: + lb = 0; ub = 0xFFFF; + break; + case TOKEN_IPV6_FLOW_LABEL: + lb = 0; ub = 0x000FFFFF; + break; + case TOKEN_IPV6_SRC_ADDR: + case TOKEN_IPV6_DST_ADDR: + return FALSE; + default: + lb = 0; ub = 0xFFFFFFFF; + } + switch (test->kind) + { + case TOKEN_EQ: + if (big || val32 < lb || val32 > ub) + { + *res = FALSE; + return TRUE; + } + if (lb == ub && val32 == lb) + { + *res = TRUE; + return TRUE; + } + return FALSE; + case TOKEN_NEQ: + if (big || val32 < lb || val32 > ub) + { + *res = TRUE; + return TRUE; + } + if (lb == ub && val32 == lb) + { + *res = FALSE; + return TRUE; + } + return FALSE; + case TOKEN_LT: + if (big || val32 > ub) + { + *res = TRUE; + return TRUE; + } + if (val32 <= lb) + { + *res = FALSE; + return TRUE; + } + return FALSE; + case TOKEN_LEQ: + if (big || val32 >= ub) + { + *res = TRUE; + return TRUE; + } + if (val32 < lb) + { + *res = FALSE; + return TRUE; + } + return FALSE; + case TOKEN_GT: + if (big || val32 >= ub) + { + *res = FALSE; + return TRUE; + } + if (val32 < lb) + { + *res = TRUE; + return TRUE; + } + return FALSE; + case TOKEN_GEQ: + if (big || val32 > ub) + { + *res = FALSE; + return TRUE; + } + if (val32 <= lb) + { + *res = TRUE; + return TRUE; + } + return FALSE; + default: + return FALSE; + } +} + +/* + * 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; + BOOL res; + 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: + if (WinDivertEvalTest(expr, &res)) + { + return (res? 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; + } +} + +/* + * Emit a test. + */ +static void WinDivertEmitTest(PEXPR test, UINT16 offset, + windivert_ioctl_filter_t 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; + } + switch (var->kind) + { + case TOKEN_OUTBOUND: + object->field = WINDIVERT_FILTER_FIELD_OUTBOUND; + break; + case TOKEN_INBOUND: + object->field = WINDIVERT_FILTER_FIELD_INBOUND; + break; + case TOKEN_IF_IDX: + object->field = WINDIVERT_FILTER_FIELD_IFIDX; + break; + case TOKEN_SUB_IF_IDX: + object->field = WINDIVERT_FILTER_FIELD_SUBIFIDX; + break; + case TOKEN_IP: + object->field = WINDIVERT_FILTER_FIELD_IP; + break; + case TOKEN_IPV6: + object->field = WINDIVERT_FILTER_FIELD_IPV6; + break; + case TOKEN_ICMP: + object->field = WINDIVERT_FILTER_FIELD_ICMP; + break; + case TOKEN_ICMPV6: + object->field = WINDIVERT_FILTER_FIELD_ICMPV6; + break; + case TOKEN_TCP: + object->field = WINDIVERT_FILTER_FIELD_TCP; + break; + case TOKEN_UDP: + object->field = WINDIVERT_FILTER_FIELD_UDP; + break; + case TOKEN_IP_HDR_LENGTH: + object->field = WINDIVERT_FILTER_FIELD_IP_HDRLENGTH; + break; + case TOKEN_IP_TOS: + object->field = WINDIVERT_FILTER_FIELD_IP_TOS; + break; + case TOKEN_IP_LENGTH: + object->field = WINDIVERT_FILTER_FIELD_IP_LENGTH; + break; + case TOKEN_IP_ID: + object->field = WINDIVERT_FILTER_FIELD_IP_ID; + break; + case TOKEN_IP_DF: + object->field = WINDIVERT_FILTER_FIELD_IP_DF; + break; + case TOKEN_IP_MF: + object->field = WINDIVERT_FILTER_FIELD_IP_MF; + break; + case TOKEN_IP_FRAG_OFF: + object->field = WINDIVERT_FILTER_FIELD_IP_FRAGOFF; + break; + case TOKEN_IP_TTL: + object->field = WINDIVERT_FILTER_FIELD_IP_TTL; + break; + case TOKEN_IP_PROTOCOL: + object->field = WINDIVERT_FILTER_FIELD_IP_PROTOCOL; + break; + case TOKEN_IP_CHECKSUM: + object->field = WINDIVERT_FILTER_FIELD_IP_CHECKSUM; + break; + case TOKEN_IP_SRC_ADDR: + object->field = WINDIVERT_FILTER_FIELD_IP_SRCADDR; + break; + case TOKEN_IP_DST_ADDR: + object->field = WINDIVERT_FILTER_FIELD_IP_DSTADDR; + break; + case TOKEN_IPV6_TRAFFIC_CLASS: + object->field = WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS; + break; + case TOKEN_IPV6_FLOW_LABEL: + object->field = WINDIVERT_FILTER_FIELD_IPV6_FLOWLABEL; + break; + case TOKEN_IPV6_LENGTH: + object->field = WINDIVERT_FILTER_FIELD_IPV6_LENGTH; + break; + case TOKEN_IPV6_NEXT_HDR: + object->field = WINDIVERT_FILTER_FIELD_IPV6_NEXTHDR; + break; + case TOKEN_IPV6_HOP_LIMIT: + object->field = WINDIVERT_FILTER_FIELD_IPV6_HOPLIMIT; + break; + case TOKEN_IPV6_SRC_ADDR: + object->field = WINDIVERT_FILTER_FIELD_IPV6_SRCADDR; + break; + case TOKEN_IPV6_DST_ADDR: + object->field = WINDIVERT_FILTER_FIELD_IPV6_DSTADDR; + break; + case TOKEN_ICMP_TYPE: + object->field = WINDIVERT_FILTER_FIELD_ICMP_TYPE; + break; + case TOKEN_ICMP_CODE: + object->field = WINDIVERT_FILTER_FIELD_ICMP_CODE; + break; + case TOKEN_ICMP_CHECKSUM: + object->field = WINDIVERT_FILTER_FIELD_ICMP_CHECKSUM; + break; + case TOKEN_ICMP_BODY: + object->field = WINDIVERT_FILTER_FIELD_ICMP_BODY; + break; + case TOKEN_ICMPV6_TYPE: + object->field = WINDIVERT_FILTER_FIELD_ICMPV6_TYPE; + break; + case TOKEN_ICMPV6_CODE: + object->field = WINDIVERT_FILTER_FIELD_ICMPV6_CODE; + break; + case TOKEN_ICMPV6_CHECKSUM: + object->field = WINDIVERT_FILTER_FIELD_ICMPV6_CHECKSUM; + break; + case TOKEN_ICMPV6_BODY: + object->field = WINDIVERT_FILTER_FIELD_ICMPV6_BODY; + break; + case TOKEN_TCP_SRC_PORT: + object->field = WINDIVERT_FILTER_FIELD_TCP_SRCPORT; + break; + case TOKEN_TCP_DST_PORT: + object->field = WINDIVERT_FILTER_FIELD_TCP_DSTPORT; + break; + case TOKEN_TCP_SEQ_NUM: + object->field = WINDIVERT_FILTER_FIELD_TCP_SEQNUM; + break; + case TOKEN_TCP_ACK_NUM: + object->field = WINDIVERT_FILTER_FIELD_TCP_ACKNUM; + break; + case TOKEN_TCP_HDR_LENGTH: + object->field = WINDIVERT_FILTER_FIELD_TCP_HDRLENGTH; + break; + case TOKEN_TCP_URG: + object->field = WINDIVERT_FILTER_FIELD_TCP_URG; + break; + case TOKEN_TCP_ACK: + object->field = WINDIVERT_FILTER_FIELD_TCP_ACK; + break; + case TOKEN_TCP_PSH: + object->field = WINDIVERT_FILTER_FIELD_TCP_PSH; + break; + case TOKEN_TCP_RST: + object->field = WINDIVERT_FILTER_FIELD_TCP_RST; + break; + case TOKEN_TCP_SYN: + object->field = WINDIVERT_FILTER_FIELD_TCP_SYN; + break; + case TOKEN_TCP_FIN: + object->field = WINDIVERT_FILTER_FIELD_TCP_FIN; + break; + case TOKEN_TCP_WINDOW: + object->field = WINDIVERT_FILTER_FIELD_TCP_WINDOW; + break; + case TOKEN_TCP_CHECKSUM: + object->field = WINDIVERT_FILTER_FIELD_TCP_CHECKSUM; + break; + case TOKEN_TCP_URG_PTR: + object->field = WINDIVERT_FILTER_FIELD_TCP_URGPTR; + break; + case TOKEN_TCP_PAYLOAD_LENGTH: + object->field = WINDIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH; + break; + case TOKEN_UDP_SRC_PORT: + object->field = WINDIVERT_FILTER_FIELD_UDP_SRCPORT; + break; + case TOKEN_UDP_DST_PORT: + object->field = WINDIVERT_FILTER_FIELD_UDP_DSTPORT; + break; + case TOKEN_UDP_LENGTH: + object->field = WINDIVERT_FILTER_FIELD_UDP_LENGTH; + break; + case TOKEN_UDP_CHECKSUM: + object->field = WINDIVERT_FILTER_FIELD_UDP_CHECKSUM; + break; + case TOKEN_UDP_PAYLOAD_LENGTH: + object->field = WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH; + break; + default: + return; + } + object->arg[0] = val->val[0]; + object->arg[1] = val->val[1]; + object->arg[2] = val->val[2]; + object->arg[3] = val->val[3]; + 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, + windivert_ioctl_filter_t 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 = (label == WINDIVERT_FILTER_RESULT_ACCEPT? + WINDIVERT_FILTER_TEST_EQ: WINDIVERT_FILTER_TEST_NEQ); + object[0].arg[0] = object[0].arg[1] = object[0].arg[2] = + object[0].arg[3] = 0; + object[0].success = WINDIVERT_FILTER_RESULT_ACCEPT; + object[0].failure = WINDIVERT_FILTER_RESULT_REJECT; + *obj_len = 1; + return; + default: + break; + } + *obj_len = len + 1; + for (i = 0; i <= len; i++) + { + WinDivertEmitTest(stack[len - i], label, object + i); + } +} + +/* + * Compile a filter string into an executable filter object. + */ +static ERROR WinDivertCompileFilter(const char *filter, + WINDIVERT_LAYER layer, windivert_ioctl_filter_t object, UINT *obj_len) +{ + TOKEN tokens[WINDIVERT_FILTER_MAXLEN*3]; + PEXPR stack[WINDIVERT_FILTER_MAXLEN]; + PPOOL pool; + PEXPR expr; + UINT i, max_depth; + INT16 label; + ERROR error; + + // Tokenize the filter string: + error = WinDivertTokenizeFilter(filter, layer, tokens, + sizeof(tokens) / sizeof(tokens[0]) - 1); + if (IS_ERROR(error)) + { + return error; + } + + // Allocate memory pool for the compiler: + pool = (PPOOL)malloc(sizeof(POOL)); + if (pool == NULL) + { + return MAKE_ERROR(WINDIVERT_ERROR_NO_MEMORY, 0); + } + pool->offset = 0; + pool->error = MAKE_ERROR(WINDIVERT_ERROR_NONE, 0); + + // Parse the filter into an expression: + i = 0; + max_depth = 1024; + expr = WinDivertParseFilter(pool, tokens, &i, max_depth, FALSE); + if (expr == NULL) + { + error = pool->error; + free(pool); + return error; + } + if (tokens[i].kind != TOKEN_END) + { + free(pool); + return MAKE_ERROR(WINDIVERT_ERROR_UNEXPECTED_TOKEN, tokens[i].pos); + } + + // Construct the filter tree: + label = 0; + label = WinDivertFlattenExpr(expr, &label, WINDIVERT_FILTER_RESULT_ACCEPT, + WINDIVERT_FILTER_RESULT_REJECT, stack); + if (label < 0) + { + free(pool); + return MAKE_ERROR(WINDIVERT_ERROR_TOO_LONG, 0); + } + + // Emit the final object. + if (object != NULL) + { + WinDivertEmitFilter(stack, label, label, object, obj_len); + } + free(pool); + + 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_OUTPUT_TOO_SHORT: + return "Filter object buffer is too short"; + case WINDIVERT_ERROR_ASSERTION_FAILED: + return "Internal assertion failed"; + default: + return "Unknown error"; + } +} + +/* + * Check the given filter string. + */ +extern BOOL WinDivertHelperCheckFilter(const char *filter_str, + WINDIVERT_LAYER layer, const char **error, UINT *error_pos) +{ + ERROR err; + if (filter_str == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + err = WinDivertCompileFilter(filter_str, layer, NULL, NULL); + if (error != NULL) + { + *error = WinDivertErrorString(GET_CODE(err)); + } + if (error_pos != NULL) + { + *error_pos = GET_POS(err); + } + return !IS_ERROR(err); +} + +/* + * Evaluate the given filter with the given packet as input. + */ +extern BOOL WinDivertHelperEvalFilter(const char *filter, + WINDIVERT_LAYER layer, PVOID packet, UINT packet_len, + PWINDIVERT_ADDRESS addr) +{ + UINT16 pc; + ERROR err; + PWINDIVERT_IPHDR iphdr; + PWINDIVERT_IPV6HDR ipv6hdr; + PWINDIVERT_ICMPHDR icmphdr; + PWINDIVERT_ICMPV6HDR icmpv6hdr; + PWINDIVERT_TCPHDR tcphdr; + PWINDIVERT_UDPHDR udphdr; + UINT payload_len; + UINT32 val[4]; + BOOL pass; + struct windivert_ioctl_filter_s object[WINDIVERT_FILTER_MAXLEN]; + UINT obj_len; + + if (filter == NULL || packet == NULL || addr == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + err = WinDivertCompileFilter(filter, layer, object, &obj_len); + if (IS_ERROR(err)) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + WinDivertHelperParsePacket(packet, packet_len, &iphdr, &ipv6hdr, &icmphdr, + &icmpv6hdr, &tcphdr, &udphdr, NULL, &payload_len); + + pc = 0; + while (TRUE) + { + switch (pc) + { + case WINDIVERT_FILTER_RESULT_ACCEPT: + return TRUE; + case WINDIVERT_FILTER_RESULT_REJECT: + return FALSE; + default: + if (pc >= obj_len) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + break; + } + pass = TRUE; + switch (object[pc].field) + { + case WINDIVERT_FILTER_FIELD_IP_HDRLENGTH: + case WINDIVERT_FILTER_FIELD_IP_TOS: + case WINDIVERT_FILTER_FIELD_IP_LENGTH: + case WINDIVERT_FILTER_FIELD_IP_ID: + case WINDIVERT_FILTER_FIELD_IP_DF: + case WINDIVERT_FILTER_FIELD_IP_MF: + case WINDIVERT_FILTER_FIELD_IP_FRAGOFF: + case WINDIVERT_FILTER_FIELD_IP_TTL: + case WINDIVERT_FILTER_FIELD_IP_PROTOCOL: + case WINDIVERT_FILTER_FIELD_IP_CHECKSUM: + case WINDIVERT_FILTER_FIELD_IP_SRCADDR: + case WINDIVERT_FILTER_FIELD_IP_DSTADDR: + pass = (iphdr != NULL); + break; + case WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: + case WINDIVERT_FILTER_FIELD_IPV6_FLOWLABEL: + case WINDIVERT_FILTER_FIELD_IPV6_LENGTH: + case WINDIVERT_FILTER_FIELD_IPV6_NEXTHDR: + case WINDIVERT_FILTER_FIELD_IPV6_HOPLIMIT: + case WINDIVERT_FILTER_FIELD_IPV6_SRCADDR: + case WINDIVERT_FILTER_FIELD_IPV6_DSTADDR: + pass = (ipv6hdr != NULL); + break; + case WINDIVERT_FILTER_FIELD_ICMP_TYPE: + case WINDIVERT_FILTER_FIELD_ICMP_CODE: + case WINDIVERT_FILTER_FIELD_ICMP_CHECKSUM: + case WINDIVERT_FILTER_FIELD_ICMP_BODY: + pass = (icmphdr != NULL); + break; + case WINDIVERT_FILTER_FIELD_ICMPV6_TYPE: + case WINDIVERT_FILTER_FIELD_ICMPV6_CODE: + case WINDIVERT_FILTER_FIELD_ICMPV6_CHECKSUM: + case WINDIVERT_FILTER_FIELD_ICMPV6_BODY: + pass = (icmpv6hdr != NULL); + break; + case WINDIVERT_FILTER_FIELD_TCP_SRCPORT: + case WINDIVERT_FILTER_FIELD_TCP_DSTPORT: + case WINDIVERT_FILTER_FIELD_TCP_SEQNUM: + case WINDIVERT_FILTER_FIELD_TCP_ACKNUM: + case WINDIVERT_FILTER_FIELD_TCP_HDRLENGTH: + case WINDIVERT_FILTER_FIELD_TCP_URG: + case WINDIVERT_FILTER_FIELD_TCP_ACK: + case WINDIVERT_FILTER_FIELD_TCP_PSH: + case WINDIVERT_FILTER_FIELD_TCP_RST: + case WINDIVERT_FILTER_FIELD_TCP_SYN: + case WINDIVERT_FILTER_FIELD_TCP_FIN: + case WINDIVERT_FILTER_FIELD_TCP_WINDOW: + case WINDIVERT_FILTER_FIELD_TCP_CHECKSUM: + case WINDIVERT_FILTER_FIELD_TCP_URGPTR: + case WINDIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH: + pass = (tcphdr != NULL); + break; + case WINDIVERT_FILTER_FIELD_UDP_SRCPORT: + case WINDIVERT_FILTER_FIELD_UDP_DSTPORT: + case WINDIVERT_FILTER_FIELD_UDP_LENGTH: + case WINDIVERT_FILTER_FIELD_UDP_CHECKSUM: + case WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: + pass = (udphdr != NULL); + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + if (!pass) + { + pc = object[pc].failure; + continue; + } + val[1] = val[2] = val[3] = 0; + switch (object[pc].field) + { + case WINDIVERT_FILTER_FIELD_ZERO: + val[0] = 0; + break; + case WINDIVERT_FILTER_FIELD_INBOUND: + val[0] = (addr->Direction == WINDIVERT_DIRECTION_INBOUND); + break; + case WINDIVERT_FILTER_FIELD_OUTBOUND: + val[0] = (addr->Direction == WINDIVERT_DIRECTION_OUTBOUND); + break; + case WINDIVERT_FILTER_FIELD_IFIDX: + val[0] = addr->IfIdx; + break; + case WINDIVERT_FILTER_FIELD_SUBIFIDX: + val[0] = addr->SubIfIdx; + break; + case WINDIVERT_FILTER_FIELD_IP: + val[0] = (iphdr != NULL); + break; + case WINDIVERT_FILTER_FIELD_IPV6: + val[0] = (ipv6hdr != NULL); + break; + case WINDIVERT_FILTER_FIELD_ICMP: + val[0] = (icmphdr != NULL); + break; + case WINDIVERT_FILTER_FIELD_ICMPV6: + val[0] = (icmpv6hdr != NULL); + break; + case WINDIVERT_FILTER_FIELD_TCP: + val[0] = (tcphdr != NULL); + break; + case WINDIVERT_FILTER_FIELD_UDP: + val[0] = (udphdr != NULL); + break; + case WINDIVERT_FILTER_FIELD_IP_HDRLENGTH: + val[0] = iphdr->HdrLength; + break; + case WINDIVERT_FILTER_FIELD_IP_TOS: + val[0] = ntohs(iphdr->TOS); + break; + case WINDIVERT_FILTER_FIELD_IP_LENGTH: + val[0] = ntohs(iphdr->Length); + break; + case WINDIVERT_FILTER_FIELD_IP_ID: + val[0] = ntohs(iphdr->Id); + break; + case WINDIVERT_FILTER_FIELD_IP_DF: + val[0] = WINDIVERT_IPHDR_GET_DF(iphdr); + break; + case WINDIVERT_FILTER_FIELD_IP_MF: + val[0] = WINDIVERT_IPHDR_GET_MF(iphdr); + break; + case WINDIVERT_FILTER_FIELD_IP_FRAGOFF: + val[0] = ntohs(WINDIVERT_IPHDR_GET_FRAGOFF(iphdr)); + break; + case WINDIVERT_FILTER_FIELD_IP_TTL: + val[0] = iphdr->TTL; + break; + case WINDIVERT_FILTER_FIELD_IP_PROTOCOL: + val[0] = iphdr->Protocol; + break; + case WINDIVERT_FILTER_FIELD_IP_CHECKSUM: + val[0] = ntohs(iphdr->Checksum); + break; + case WINDIVERT_FILTER_FIELD_IP_SRCADDR: + val[0] = ntohl(iphdr->SrcAddr); + break; + case WINDIVERT_FILTER_FIELD_IP_DSTADDR: + val[0] = ntohl(iphdr->DstAddr); + break; + case WINDIVERT_FILTER_FIELD_IPV6_TRAFFICCLASS: + val[0] = WINDIVERT_IPV6HDR_GET_TRAFFICCLASS(ipv6hdr); + break; + case WINDIVERT_FILTER_FIELD_IPV6_FLOWLABEL: + val[0] = ntohl(WINDIVERT_IPV6HDR_GET_FLOWLABEL(ipv6hdr)); + break; + case WINDIVERT_FILTER_FIELD_IPV6_LENGTH: + val[0] = ntohs(ipv6hdr->Length); + break; + case WINDIVERT_FILTER_FIELD_IPV6_NEXTHDR: + val[0] = ipv6hdr->NextHdr; + break; + case WINDIVERT_FILTER_FIELD_IPV6_HOPLIMIT: + val[0] = ipv6hdr->HopLimit; + break; + case WINDIVERT_FILTER_FIELD_IPV6_SRCADDR: + val[3] = ntohl(ipv6hdr->SrcAddr[0]); + val[2] = ntohl(ipv6hdr->SrcAddr[1]); + val[1] = ntohl(ipv6hdr->SrcAddr[2]); + val[0] = ntohl(ipv6hdr->SrcAddr[3]); + break; + case WINDIVERT_FILTER_FIELD_IPV6_DSTADDR: + val[3] = ntohl(ipv6hdr->DstAddr[0]); + val[2] = ntohl(ipv6hdr->DstAddr[1]); + val[1] = ntohl(ipv6hdr->DstAddr[2]); + val[0] = ntohl(ipv6hdr->DstAddr[3]); + break; + case WINDIVERT_FILTER_FIELD_ICMP_TYPE: + val[0] = icmphdr->Type; + break; + case WINDIVERT_FILTER_FIELD_ICMP_CODE: + val[0] = icmphdr->Code; + break; + case WINDIVERT_FILTER_FIELD_ICMP_CHECKSUM: + val[0] = ntohs(icmphdr->Checksum); + break; + case WINDIVERT_FILTER_FIELD_ICMP_BODY: + val[0] = ntohs(icmphdr->Body); + break; + case WINDIVERT_FILTER_FIELD_ICMPV6_TYPE: + val[0] = icmpv6hdr->Type; + break; + case WINDIVERT_FILTER_FIELD_ICMPV6_CODE: + val[0] = icmpv6hdr->Code; + break; + case WINDIVERT_FILTER_FIELD_ICMPV6_CHECKSUM: + val[0] = ntohs(icmpv6hdr->Checksum); + break; + case WINDIVERT_FILTER_FIELD_ICMPV6_BODY: + val[0] = ntohs(icmpv6hdr->Body); + break; + case WINDIVERT_FILTER_FIELD_TCP_SRCPORT: + val[0] = ntohs(tcphdr->SrcPort); + break; + case WINDIVERT_FILTER_FIELD_TCP_DSTPORT: + val[0] = ntohs(tcphdr->DstPort); + break; + case WINDIVERT_FILTER_FIELD_TCP_SEQNUM: + val[0] = ntohl(tcphdr->SeqNum); + break; + case WINDIVERT_FILTER_FIELD_TCP_ACKNUM: + val[0] = ntohl(tcphdr->AckNum); + break; + case WINDIVERT_FILTER_FIELD_TCP_HDRLENGTH: + val[0] = tcphdr->HdrLength; + break; + case WINDIVERT_FILTER_FIELD_TCP_URG: + val[0] = tcphdr->Urg; + break; + case WINDIVERT_FILTER_FIELD_TCP_ACK: + val[0] = tcphdr->Ack; + break; + case WINDIVERT_FILTER_FIELD_TCP_PSH: + val[0] = tcphdr->Psh; + break; + case WINDIVERT_FILTER_FIELD_TCP_RST: + val[0] = tcphdr->Rst; + break; + case WINDIVERT_FILTER_FIELD_TCP_SYN: + val[0] = tcphdr->Syn; + break; + case WINDIVERT_FILTER_FIELD_TCP_FIN: + val[0] = tcphdr->Fin; + break; + case WINDIVERT_FILTER_FIELD_TCP_WINDOW: + val[0] = ntohs(tcphdr->Window); + break; + case WINDIVERT_FILTER_FIELD_TCP_CHECKSUM: + val[0] = ntohs(tcphdr->Checksum); + break; + case WINDIVERT_FILTER_FIELD_TCP_URGPTR: + val[0] = ntohs(tcphdr->UrgPtr); + break; + case WINDIVERT_FILTER_FIELD_TCP_PAYLOADLENGTH: + val[0] = payload_len; + break; + case WINDIVERT_FILTER_FIELD_UDP_SRCPORT: + val[0] = ntohs(udphdr->SrcPort); + break; + case WINDIVERT_FILTER_FIELD_UDP_DSTPORT: + val[0] = ntohs(udphdr->DstPort); + break; + case WINDIVERT_FILTER_FIELD_UDP_LENGTH: + val[0] = ntohs(udphdr->Length); + break; + case WINDIVERT_FILTER_FIELD_UDP_CHECKSUM: + val[0] = ntohs(udphdr->Checksum); + break; + case WINDIVERT_FILTER_FIELD_UDP_PAYLOADLENGTH: + val[0] = payload_len; + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + switch (object[pc].test) + { + case WINDIVERT_FILTER_TEST_EQ: + pass = (val[0] == object[pc].arg[0] && + val[1] == object[pc].arg[1] && + val[2] == object[pc].arg[2] && + val[3] == object[pc].arg[3]); + break; + case WINDIVERT_FILTER_TEST_NEQ: + pass = (val[0] != object[pc].arg[0] || + val[1] != object[pc].arg[1] || + val[2] != object[pc].arg[2] || + val[3] != object[pc].arg[3]); + break; + case WINDIVERT_FILTER_TEST_LT: + pass = (val[3] < object[pc].arg[3] || + (val[3] == object[pc].arg[3] && + (val[2] < object[pc].arg[2] || + (val[2] == object[pc].arg[2] && + (val[1] < object[pc].arg[1] || + (val[1] == object[pc].arg[1] && + val[0] < object[pc].arg[0])))))); + break; + case WINDIVERT_FILTER_TEST_LEQ: + pass = (val[3] < object[pc].arg[3] || + (val[3] == object[pc].arg[3] && + (val[2] < object[pc].arg[2] || + (val[2] == object[pc].arg[2] && + (val[1] < object[pc].arg[1] || + (val[1] == object[pc].arg[1] && + val[0] <= object[pc].arg[0])))))); + break; + case WINDIVERT_FILTER_TEST_GT: + pass = (val[3] > object[pc].arg[3] || + (val[3] == object[pc].arg[3] && + (val[2] > object[pc].arg[2] || + (val[2] == object[pc].arg[2] && + (val[1] > object[pc].arg[1] || + (val[1] == object[pc].arg[1] && + val[0] > object[pc].arg[0])))))); + break; + case WINDIVERT_FILTER_TEST_GEQ: + pass = (val[3] > object[pc].arg[3] || + (val[3] == object[pc].arg[3] && + (val[2] > object[pc].arg[2] || + (val[2] == object[pc].arg[2] && + (val[1] > object[pc].arg[1] || + (val[1] == object[pc].arg[1] && + val[0] >= object[pc].arg[0])))))); + break; + default: + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + pc = (pass? object[pc].success: object[pc].failure); + } +} + diff --git a/include/windivert.h b/include/windivert.h index 438b903..6218645 100644 --- a/include/windivert.h +++ b/include/windivert.h @@ -352,6 +352,24 @@ extern WINDIVERTEXPORT UINT WinDivertHelperCalcChecksums( __in UINT packetLen, __in UINT64 flags); +/* + * Check the given filter string. + */ +extern WINDIVERTEXPORT BOOL WinDivertHelperCheckFilter( + __in const char *filter, + __in WINDIVERT_LAYER layer, + __out_opt const char **errorStr, + __out_opt UINT *errorPos); + +/* + * Evaluate the given filter string. + */ +extern WINDIVERTEXPORT BOOL WinDivertHelperEvalFilter( + __in const char *filter, + __in WINDIVERT_LAYER layer, + __in PVOID pPacket, + __in UINT packetLen, + __in PWINDIVERT_ADDRESS pAddr); /****************************************************************************/ /* WINDIVERT LEGACY API */