diff --git a/test/build.sh b/test/build.sh new file mode 100755 index 0000000..a45b276 --- /dev/null +++ b/test/build.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# +# build.sh +# (C) 2014, 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 . + +# Script for MinGW/Linux cross compilation. +# NOTE: run wddk-build.bat before this script. + +CC=x86_64-w64-mingw32-gcc + +$CC -s -O2 -I../include/ test.c -o test.exe -lWinDivert \ + -L"../install/MINGW/amd64/" + diff --git a/test/test.c b/test/test.c new file mode 100644 index 0000000..727ec6e --- /dev/null +++ b/test/test.c @@ -0,0 +1,338 @@ +/* + * test.c + * (C) 2014, 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 testing framework. + */ + +#include +#include +#include + +#include "windivert.h" + +#define MAX_PACKET 2048 + +/* + * Packet data. + */ +#include "test_data.c" + +/* + * Test entry. + */ +struct packet +{ + char *packet; + size_t packet_len; + char *name; +}; + +struct test +{ + char *filter; + struct packet *packet; + BOOL match; +}; + +/* + * Prototypes. + */ +static BOOL run_test(HANDLE inject_handle, const char *filter, + const char *packet, const size_t packet_len, BOOL match); + +/* + * Test data. + */ +struct packet pkt_echo_request = +{ + echo_request, + sizeof(echo_request), + "ipv4_icmp_echo_req" +}; +struct packet pkt_http_request = +{ + http_request, + sizeof(http_request), + "ipv4_tcp_http_req" +}; +struct packet pkt_ipv6_exthdrs_udp = +{ + ipv6_exthdrs_udp, + sizeof(ipv6_exthdrs_udp), + "ipv6_exthdrs_udp" +}; +struct test tests[] = +{ + {"outbound and icmp", &pkt_echo_request, TRUE}, + {"outbound", &pkt_echo_request, TRUE}, + {"icmp", &pkt_echo_request, TRUE}, + {"not icmp", &pkt_echo_request, FALSE}, + {"inbound", &pkt_echo_request, FALSE}, + {"tcp", &pkt_echo_request, FALSE}, + {"icmp.Type == 8", &pkt_echo_request, TRUE}, + {"icmp.Type == 9", &pkt_echo_request, FALSE}, + {"tcp", &pkt_http_request, TRUE}, + {"outbound and tcp and tcp.DstPort == 80", &pkt_http_request, TRUE}, + {"outbound and tcp and tcp.DstPort == 81", &pkt_http_request, FALSE}, + {"outbound and tcp and tcp.DstPort != 80", &pkt_http_request, FALSE}, + {"inbound and tcp and tcp.DstPort == 80", &pkt_http_request, FALSE}, + {"tcp.PayloadLength == 469", &pkt_http_request, TRUE}, + {"tcp.PayloadLength != 469", &pkt_http_request, FALSE}, + {"tcp.PayloadLength >= 469", &pkt_http_request, TRUE}, + {"tcp.PayloadLength <= 469", &pkt_http_request, TRUE}, + {"tcp.PayloadLength > 469", &pkt_http_request, FALSE}, + {"tcp.PayloadLength < 469", &pkt_http_request, FALSE}, + {"true", &pkt_ipv6_exthdrs_udp, TRUE}, + {"udp", &pkt_ipv6_exthdrs_udp, TRUE}, + {"tcp", &pkt_ipv6_exthdrs_udp, FALSE}, + {"ipv6.SrcAddr == ::1", &pkt_ipv6_exthdrs_udp, TRUE}, + {"ipv6.SrcAddr == ::2", &pkt_ipv6_exthdrs_udp, FALSE}, + {"udp.SrcPort == 4660 and udp.DstPort == 43690", + &pkt_ipv6_exthdrs_udp, TRUE}, + {"udp.SrcPort == 4660 and udp.DstPort == 12345", + &pkt_ipv6_exthdrs_udp, FALSE}, +}; + +/* + * Main. + */ +int main(void) +{ + HANDLE upper_handle, lower_handle; + HANDLE console; + size_t i; + + // Open handles to: + // (1) stop normal traffic from interacting with the tests; and + // (2) stop test packets escaping to the Internet or TCP/IP stack. + upper_handle = WinDivertOpen("true", WINDIVERT_LAYER_NETWORK, -510, + WINDIVERT_FLAG_DROP); + lower_handle = WinDivertOpen("true", WINDIVERT_LAYER_NETWORK, 510, + WINDIVERT_FLAG_DROP); + if (upper_handle == INVALID_HANDLE_VALUE || + lower_handle == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "error: failed to open WinDivert handle (err = %d)", + GetLastError()); + exit(EXIT_FAILURE); + } + + console = GetStdHandle(STD_OUTPUT_HANDLE); + + // Wait for existing packets to flush: + Sleep(100); + + // Run tests: + size_t num_tests = sizeof(tests) / sizeof(struct test); + for (i = 0; i < num_tests; i++) + { + char *filter = tests[i].filter; + char *packet = tests[i].packet->packet; + size_t packet_len = tests[i].packet->packet_len; + char *name = tests[i].packet->name; + BOOL match = tests[i].match; + + // Ensure the correct checksum: + WinDivertHelperCalcChecksums(packet, packet_len, 0); + + // Run the test: + BOOL res = run_test(upper_handle, filter, packet, packet_len, match); + + printf("%.2u ", i); + if (res) + { + SetConsoleTextAttribute(console, FOREGROUND_GREEN); + printf("PASSED"); + } + else + { + SetConsoleTextAttribute(console, FOREGROUND_RED); + printf("FAILED"); + } + SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + printf(" p=["); + SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN); + printf("%s", name); + SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + printf("] f=["); + SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN); + printf("%s", filter); + SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN | + FOREGROUND_BLUE); + printf("]\n"); + } + + WinDivertClose(upper_handle); + WinDivertClose(lower_handle); + + return 0; +} + +/* + * Run a test case. + */ +static BOOL run_test(HANDLE inject_handle, const char *filter, + const char *packet, const size_t packet_len, BOOL match) +{ + char buf[MAX_PACKET]; + UINT buf_len, i; + DWORD iolen; + WINDIVERT_ADDRESS addr; + OVERLAPPED overlapped; + HANDLE handle = INVALID_HANDLE_VALUE, handle0 = INVALID_HANDLE_VALUE, + event = NULL; + + // (1) Open a WinDivert handle to the given filter: + handle = WinDivertOpen(filter, WINDIVERT_LAYER_NETWORK, 0, 0); + if (handle == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "error: failed to open WinDivert handle for filter " + "\"%s\" (err = %d)\n", filter, GetLastError()); + goto failed; + } + + if (!match) + { + // Catch non-matching packets: + handle0 = handle; + handle = WinDivertOpen("true", WINDIVERT_LAYER_NETWORK, 33, 0); + if (handle == INVALID_HANDLE_VALUE) + { + fprintf(stderr, "error: failed to open WinDivert handle " + "(err = %d)\n", GetLastError()); + goto failed; + } + } + + // (2) Inject the packet: + memset(&addr, 0, sizeof(addr)); + addr.Direction = WINDIVERT_DIRECTION_OUTBOUND; + if (!WinDivertSend(inject_handle, packet, packet_len, &addr, NULL)) + { + fprintf(stderr, "error: failed to inject test packet (err = %d)\n", + GetLastError()); + goto failed; + } + + // (3) Wait for the packet to arrive. + // NOTE: This may fail, so set a generous time-out of 1 second. + memset(&overlapped, 0, sizeof(overlapped)); + event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (event == NULL) + { + fprintf(stderr, "error: failed to create event (err = %d)\n", + GetLastError()); + goto failed; + } + overlapped.hEvent = event; + if (!WinDivertRecvEx(handle, buf, sizeof(buf), 0, &addr, &buf_len, + &overlapped)) + { + if (GetLastError() != ERROR_IO_PENDING) + { +read_failed: + fprintf(stderr, "error: failed to read packet from WinDivert " + "handle (err = %d)\n", GetLastError()); + goto failed; + } + + switch (WaitForSingleObject(event, 1000)) + { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + fprintf(stderr, "error: failed to read packet from WinDivert " + "handle (timeout)\n", GetLastError()); + goto failed; + default: + goto read_failed; + } + + if (!GetOverlappedResult(handle, &overlapped, &iolen, TRUE)) + { + fprintf(stderr, "error: failed to get the overlapped result from " + "WinDivert handle (err = %d)\n", GetLastError()); + goto failed; + } + buf_len = (UINT)iolen; + } + + // (4) Verify that the packet is the same. + if (buf_len != packet_len) + { + fprintf(stderr, "error: packet length mis-match, expected (%u), got " + "(%u)\n", packet_len, buf_len); + goto failed; + } + for (i = 0; i < packet_len; i++) + { + if (packet[i] != buf[i]) + { + fprintf(stderr, "error: packet data mis-match, expected byte #%u " + "to be (0x%.2X), got (0x%.2X)\n", i, (unsigned char)packet[i], + (unsigned char)buf[i]); + for (i = 0; i < packet_len; i++) + { + printf("%c", (packet[i] == buf[i]? '.': 'X')); + } + putchar('\n'); + goto failed; + } + } + + // (5) Clean-up: + if (!WinDivertClose(handle)) + { + handle = INVALID_HANDLE_VALUE; + fprintf(stderr, "error: failed to close WinDivert handle (err = %d)\n", + GetLastError()); + goto failed; + } + if (handle0 != INVALID_HANDLE_VALUE) + { + if (!WinDivertClose(handle0)) + { + handle0 = INVALID_HANDLE_VALUE; + fprintf(stderr, "error: failed to close WinDivert handle " + "(err = %d)\n", GetLastError()); + goto failed; + } + } + CloseHandle(event); + + return TRUE; + +failed: + if (handle0 != INVALID_HANDLE_VALUE) + { + WinDivertClose(handle0); + } + if (handle != INVALID_HANDLE_VALUE) + { + WinDivertClose(handle); + } + if (event != NULL) + { + CloseHandle(event); + } + return FALSE; +} + diff --git a/test/test_data.c b/test/test_data.c new file mode 100644 index 0000000..1258020 --- /dev/null +++ b/test/test_data.c @@ -0,0 +1,121 @@ +/* + * test_data.c + * (C) 2014, 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 . + */ + +// IPV4 ICMP ECHO REQUEST +static unsigned char echo_request[] = +{ + 0x45, 0x00, 0x00, 0x54, 0x12, 0x34, 0x40, 0x00, + 0x40, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x01, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, + 0x0d, 0x56, 0x00, 0x01, 0x8b, 0xa6, 0x60, 0x54, + 0x00, 0x00, 0x00, 0x00, 0xf9, 0x08, 0x0a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, + 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37 +}; + +// IPV4 TCP HTTP GET REQUEST +static unsigned char http_request[] = +{ + 0x45, 0x00, 0x02, 0x09, 0x48, 0x2d, 0x40, 0x00, + 0x40, 0x06, 0x00, 0x00, 0x0a, 0x0a, 0x0a, 0x0a, + 0x5d, 0xb8, 0xd8, 0x77, 0xa3, 0x1a, 0x00, 0x50, + 0x53, 0x38, 0xcc, 0xc2, 0x56, 0x37, 0xb3, 0x55, + 0x80, 0x18, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x01, 0x08, 0x0a, 0x00, 0x2c, 0x85, 0x1b, + 0x1b, 0x7f, 0x3a, 0x71, 0x47, 0x45, 0x54, 0x20, + 0x2f, 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, + 0x2e, 0x31, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, + 0x3a, 0x20, 0x77, 0x77, 0x77, 0x2e, 0x65, 0x78, + 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x20, 0x6b, + 0x65, 0x65, 0x70, 0x2d, 0x61, 0x6c, 0x69, 0x76, + 0x65, 0x0d, 0x0a, 0x43, 0x61, 0x63, 0x68, 0x65, + 0x2d, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + 0x3a, 0x20, 0x6d, 0x61, 0x78, 0x2d, 0x61, 0x67, + 0x65, 0x3d, 0x30, 0x0d, 0x0a, 0x41, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x3a, 0x20, 0x74, 0x65, 0x78, + 0x74, 0x2f, 0x68, 0x74, 0x6d, 0x6c, 0x2c, 0x61, + 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x2f, 0x78, 0x68, 0x74, 0x6d, 0x6c, + 0x2b, 0x78, 0x6d, 0x6c, 0x2c, 0x61, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2f, 0x78, 0x6d, 0x6c, 0x3b, 0x71, 0x3d, 0x30, + 0x2e, 0x39, 0x2c, 0x69, 0x6d, 0x61, 0x67, 0x65, + 0x2f, 0x77, 0x65, 0x62, 0x70, 0x2c, 0x2a, 0x2f, + 0x2a, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x38, 0x0d, + 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, 0x41, 0x67, + 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x0d, 0x0a, 0x41, 0x63, 0x63, 0x65, + 0x70, 0x74, 0x2d, 0x45, 0x6e, 0x63, 0x6f, 0x64, + 0x69, 0x6e, 0x67, 0x3a, 0x20, 0x67, 0x7a, 0x69, + 0x70, 0x2c, 0x64, 0x65, 0x66, 0x6c, 0x61, 0x74, + 0x65, 0x2c, 0x73, 0x64, 0x63, 0x68, 0x0d, 0x0a, + 0x41, 0x63, 0x63, 0x65, 0x70, 0x74, 0x2d, 0x4c, + 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x3a, + 0x20, 0x65, 0x6e, 0x2d, 0x55, 0x53, 0x2c, 0x65, + 0x6e, 0x3b, 0x71, 0x3d, 0x30, 0x2e, 0x38, 0x0d, + 0x0a, 0x49, 0x66, 0x2d, 0x4e, 0x6f, 0x6e, 0x65, + 0x2d, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x3a, 0x20, + 0x22, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, + 0x33, 0x33, 0x22, 0x0d, 0x0a, 0x49, 0x66, 0x2d, + 0x4d, 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, + 0x2d, 0x53, 0x69, 0x6e, 0x63, 0x65, 0x3a, 0x20, + 0x46, 0x72, 0x69, 0x2c, 0x20, 0x30, 0x33, 0x20, + 0x41, 0x75, 0x67, 0x20, 0x32, 0x30, 0x31, 0x34, + 0x20, 0x31, 0x33, 0x3a, 0x33, 0x33, 0x3a, 0x33, + 0x33, 0x20, 0x47, 0x4d, 0x54, 0x0d, 0x0a, 0x0d, + 0x0a +}; + +// IPV6 EXTENSION HEADERS UDP +static unsigned char ipv6_exthdrs_udp[] = +{ + 0x60, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x34, 0xaa, 0xaa, 0x00, 0x15, 0x00, 0x00, + 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, + 0x72, 0x6c, 0x64, 0x21, 0x01 +}; +