2444 lines
71 KiB
C
2444 lines
71 KiB
C
/*
|
|
* windivert_helper.c
|
|
* (C) 2016, 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/****************************************************************************/
|
|
/* 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;
|
|
UINT64 flags_all =
|
|
(WINDIVERT_HELPER_NO_IP_CHECKSUM |
|
|
WINDIVERT_HELPER_NO_ICMP_CHECKSUM |
|
|
WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM |
|
|
WINDIVERT_HELPER_NO_TCP_CHECKSUM |
|
|
WINDIVERT_HELPER_NO_UDP_CHECKSUM);
|
|
|
|
if ((flags & flags_all) == flags_all)
|
|
{
|
|
return 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) &&
|
|
(!(flags & WINDIVERT_HELPER_NO_REPLACE) || ip_header->Checksum == 0))
|
|
{
|
|
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) ||
|
|
((flags & WINDIVERT_HELPER_NO_REPLACE) &&
|
|
icmp_header->Checksum != 0))
|
|
{
|
|
return count;
|
|
}
|
|
icmp_header->Checksum = 0;
|
|
icmp_header->Checksum = WinDivertHelperCalcChecksum(NULL, 0,
|
|
icmp_header, payload_len + sizeof(WINDIVERT_ICMPHDR));
|
|
count++;
|
|
return count;
|
|
}
|
|
|
|
if (icmpv6_header != NULL)
|
|
{
|
|
if ((flags & WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM) ||
|
|
((flags & WINDIVERT_HELPER_NO_REPLACE) &&
|
|
icmpv6_header->Checksum != 0))
|
|
{
|
|
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) ||
|
|
((flags & WINDIVERT_HELPER_NO_REPLACE) &&
|
|
tcp_header->Checksum != 0))
|
|
{
|
|
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) ||
|
|
((flags & WINDIVERT_HELPER_NO_REPLACE) &&
|
|
udp_header->Checksum != 0))
|
|
{
|
|
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)
|
|
{
|
|
PEXPR expr;
|
|
if (tok->kind != TOKEN_NUMBER)
|
|
{
|
|
pool->error = MAKE_ERROR(WINDIVERT_ERROR_ASSERTION_FAILED, 0);
|
|
return NULL;
|
|
}
|
|
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)
|
|
{
|
|
PEXPR expr;
|
|
if (arg0 == NULL || arg1 == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
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)
|
|
{
|
|
PEXPR var, val;
|
|
KIND kind;
|
|
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;
|
|
}
|
|
var = WinDivertMakeVar(pool, toks[*i].kind);
|
|
*i = *i + 1;
|
|
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;
|
|
}
|
|
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)
|
|
{
|
|
PEXPR arg, th, el;
|
|
if (depth-- < 0)
|
|
{
|
|
pool->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);
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Big number comparison.
|
|
*/
|
|
static int WinDivertBigNumCompare(const UINT32 *a, const UINT32 *b)
|
|
{
|
|
if (a[3] < b[3])
|
|
{
|
|
return -1;
|
|
}
|
|
if (a[3] > b[3])
|
|
{
|
|
return 1;
|
|
}
|
|
if (a[2] < b[2])
|
|
{
|
|
return -1;
|
|
}
|
|
if (a[2] > b[2])
|
|
{
|
|
return 1;
|
|
}
|
|
if (a[1] < b[1])
|
|
{
|
|
return -1;
|
|
}
|
|
if (a[1] > b[1])
|
|
{
|
|
return 1;
|
|
}
|
|
if (a[0] < b[0])
|
|
{
|
|
return -1;
|
|
}
|
|
if (a[0] > b[0])
|
|
{
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
int cmp;
|
|
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:
|
|
pass = TRUE;
|
|
break;
|
|
}
|
|
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] = 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] = ntohl(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] = ntohl(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;
|
|
}
|
|
cmp = WinDivertBigNumCompare(val, object[pc].arg);
|
|
switch (object[pc].test)
|
|
{
|
|
case WINDIVERT_FILTER_TEST_EQ:
|
|
pass = (cmp == 0);
|
|
break;
|
|
case WINDIVERT_FILTER_TEST_NEQ:
|
|
pass = (cmp != 0);
|
|
break;
|
|
case WINDIVERT_FILTER_TEST_LT:
|
|
pass = (cmp < 0);
|
|
break;
|
|
case WINDIVERT_FILTER_TEST_LEQ:
|
|
pass = (cmp <= 0);
|
|
break;
|
|
case WINDIVERT_FILTER_TEST_GT:
|
|
pass = (cmp > 0);
|
|
break;
|
|
case WINDIVERT_FILTER_TEST_GEQ:
|
|
pass = (cmp >= 0);
|
|
break;
|
|
default:
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
pc = (pass? object[pc].success: object[pc].failure);
|
|
}
|
|
}
|
|
|