static int deinit(HANDLE handle) { if (handle) { WinDivertClose(handle); return 1; } return 0; }
int __cdecl main(int argc, char **argv) { WINDIVERT_ADDRESS addr; UINT buf_len, b, writelen; HANDLE filter, console; UINT64 flags = WINDIVERT_FLAG_SNIFF; INT16 priority = 0; unsigned char cap_buffer[65535]; if (argc >= 3) { if (argc >= 4) { if (strcmp(argv[3], "sniff") == 0) { flags = WINDIVERT_FLAG_SNIFF; } else if (strcmp(argv[3], "grab") == 0) { flags = 0; } else { printf("error: unknown flag \"%s\"\n", argv[3]); exit(EXIT_FAILURE); } } if (argc >= 5) { priority = (UINT)atoi(argv[4]); } filter = WinDivertOpenLayer7SubFilterEx(argv[1], argv[2], WINDIVERT_LAYER_NETWORK, priority, flags); if (filter == INVALID_HANDLE_VALUE) { if (GetLastError() == ERROR_INVALID_PARAMETER) { fprintf(stderr, "error: filter syntax error\n"); exit(EXIT_FAILURE); } fprintf(stderr, "error: failed to open the WinDivert device (%d)\n", GetLastError()); exit(EXIT_FAILURE); } signal(SIGINT, sigint_watchdog); signal(SIGTERM, sigint_watchdog); while (!can_exit) { if (!WinDivertRecv(filter, cap_buffer, sizeof(cap_buffer), &addr, &buf_len)) { Sleep(10); continue; } for (b = 0; b < buf_len; b++) { if ((b % 40) == 0) { printf("\n\t"); } if (isprint(cap_buffer[b])) { printf("%c", cap_buffer[b]); } else { printf("."); } } printf("\n"); if (flags == 0) { WinDivertSend(filter, cap_buffer, buf_len, &addr, &writelen); } Sleep(10); } WinDivertClose(filter); } else { printf("usage: l7f.exe windivert-filter layer7-filtering sniff|grab [priority]\n" " l7f \"outbound and true\" \"\\\"GET*HTTP/1*\\\" or \\\"POST*HTTP/1\\\""); } return 0; }
// periodically try to consume packets to keep the network responsive and not blocked by recv static DWORD divertClockLoop(LPVOID arg) { DWORD startTick, stepTick, waitResult; int ix; UNREFERENCED_PARAMETER(arg); for(;;) { // use acquire as wait for yielding thread startTick = GetTickCount(); waitResult = WaitForSingleObject(mutex, CLOCK_WAITMS); switch(waitResult) { case WAIT_OBJECT_0: /***************** enter critical region ************************/ divertConsumeStep(); /***************** leave critical region ************************/ if (!ReleaseMutex(mutex)) { InterlockedIncrement16(&stopLooping); LOG("Fatal: Failed to release mutex (%lu)", GetLastError()); ABORT(); } // if didn't spent enough time, we sleep on it stepTick = GetTickCount() - startTick; if (stepTick < CLOCK_WAITMS) { Sleep(CLOCK_WAITMS - stepTick); } break; case WAIT_TIMEOUT: // read loop is processing, so we can skip this run LOG("!!! Skipping one run"); Sleep(CLOCK_WAITMS); break; case WAIT_ABANDONED: LOG("Acquired abandoned mutex"); InterlockedIncrement16(&stopLooping); break; case WAIT_FAILED: LOG("Acquire failed (%lu)", GetLastError()); InterlockedIncrement16(&stopLooping); break; } // need to get the lock here if (stopLooping) { int lastSendCount = 0; BOOL closed; waitResult = WaitForSingleObject(mutex, INFINITE); switch (waitResult) { case WAIT_ABANDONED: case WAIT_FAILED: LOG("Acquire failed/abandoned mutex (%lu), will still try closing and return", GetLastError()); case WAIT_OBJECT_0: /***************** enter critical region ************************/ LOG("Read stopLooping, stopping..."); // clean up by closing all modules for (ix = 0; ix < MODULE_CNT; ++ix) { Module *module = modules[ix]; if (*(module->enabledFlag)) { module->closeDown(head, tail); } } LOG("Send all packets upon closing"); lastSendCount = sendAllListPackets(); LOG("Lastly sent %d packets. Closing...", lastSendCount); // terminate recv loop by closing handler. handle related error in recv loop to quit closed = WinDivertClose(divertHandle); assert(closed); // release to let read loop exit properly /***************** leave critical region ************************/ if (!ReleaseMutex(mutex)) { LOG("Fatal: Failed to release mutex (%lu)", GetLastError()); ABORT(); } return 0; break; } } } }
/* * 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; const char *err_str; UINT err_pos; HANDLE handle = INVALID_HANDLE_VALUE, handle0 = INVALID_HANDLE_VALUE, event = NULL; // (0) Verify the test data: if (!WinDivertHelperCheckFilter(filter, WINDIVERT_LAYER_NETWORK, &err_str, &err_pos)) { fprintf(stderr, "error: filter string \"%s\" is invalid with error " "\"%s\" (position=%u)\n", filter, err_str, err_pos); goto failed; } memset(&addr, 0, sizeof(addr)); addr.Direction = WINDIVERT_DIRECTION_OUTBOUND; if (WinDivertHelperEvalFilter(filter, WINDIVERT_LAYER_NETWORK, (PVOID)packet, packet_len, &addr) != match) { fprintf(stderr, "error: filter \"%s\" does not match the given " "packet\n", filter); goto failed; } // (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: if (!WinDivertSend(inject_handle, (PVOID)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 250ms. 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, 250)) { 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; } if (addr.Direction == WINDIVERT_DIRECTION_OUTBOUND) { WinDivertHelperCalcChecksums(buf, buf_len, 0); } // (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; }
/* * 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; }