/* * GoodbyeDPI — Passive DPI blocker and Active DPI circumvention utility. */ #include #include #include #include #include #include #include #include #include #include "windivert.h" #include "goodbyedpi.h" #include "utils/repl_str.h" #include "service.h" #include "dnsredir.h" #include "ttltrack.h" #include "blackwhitelist.h" #include "fakepackets.h" // My mingw installation does not load inet_pton definition for some reason WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pAddr); #define GOODBYEDPI_VERSION "v0.2.2" #define die() do { sleep(20); exit(EXIT_FAILURE); } while (0) #define MAX_FILTERS 4 #define DIVERT_NO_LOCALNETSv4_DST "(" \ "(ip.DstAddr < 127.0.0.1 or ip.DstAddr > 127.255.255.255) and " \ "(ip.DstAddr < 10.0.0.0 or ip.DstAddr > 10.255.255.255) and " \ "(ip.DstAddr < 192.168.0.0 or ip.DstAddr > 192.168.255.255) and " \ "(ip.DstAddr < 172.16.0.0 or ip.DstAddr > 172.31.255.255) and " \ "(ip.DstAddr < 169.254.0.0 or ip.DstAddr > 169.254.255.255)" \ ")" #define DIVERT_NO_LOCALNETSv4_SRC "(" \ "(ip.SrcAddr < 127.0.0.1 or ip.SrcAddr > 127.255.255.255) and " \ "(ip.SrcAddr < 10.0.0.0 or ip.SrcAddr > 10.255.255.255) and " \ "(ip.SrcAddr < 192.168.0.0 or ip.SrcAddr > 192.168.255.255) and " \ "(ip.SrcAddr < 172.16.0.0 or ip.SrcAddr > 172.31.255.255) and " \ "(ip.SrcAddr < 169.254.0.0 or ip.SrcAddr > 169.254.255.255)" \ ")" #define DIVERT_NO_LOCALNETSv6_DST "(" \ "(ipv6.DstAddr > ::1) and " \ "(ipv6.DstAddr < 2001::0 or ipv6.DstAddr > 2001:1::0) and " \ "(ipv6.DstAddr < fc00::0 or ipv6.DstAddr > fe00::0) and " \ "(ipv6.DstAddr < fe80::0 or ipv6.DstAddr > fec0::0) and " \ "(ipv6.DstAddr < ff00::0 or ipv6.DstAddr > ffff::0)" \ ")" #define DIVERT_NO_LOCALNETSv6_SRC "(" \ "(ipv6.SrcAddr > ::1) and " \ "(ipv6.SrcAddr < 2001::0 or ipv6.SrcAddr > 2001:1::0) and " \ "(ipv6.SrcAddr < fc00::0 or ipv6.SrcAddr > fe00::0) and " \ "(ipv6.SrcAddr < fe80::0 or ipv6.SrcAddr > fec0::0) and " \ "(ipv6.SrcAddr < ff00::0 or ipv6.SrcAddr > ffff::0)" \ ")" /* #IPID# is a template to find&replace */ #define IPID_TEMPLATE "#IPID#" #define MAXPAYLOADSIZE_TEMPLATE "#MAXPAYLOADSIZE#" #define FILTER_STRING_TEMPLATE \ "(tcp and !impostor and !loopback " MAXPAYLOADSIZE_TEMPLATE " and " \ "((inbound and (" \ "(" \ "(" \ "(ipv6 or (ip.Id >= 0x0 and ip.Id <= 0xF) " IPID_TEMPLATE \ ") and " \ "tcp.SrcPort == 80 and tcp.Ack" \ ") or " \ "((tcp.SrcPort == 80 or tcp.SrcPort == 443) and tcp.Ack and tcp.Syn)" \ ")" \ " and (" DIVERT_NO_LOCALNETSv4_SRC " or " DIVERT_NO_LOCALNETSv6_SRC "))) or " \ "(outbound and " \ "(tcp.DstPort == 80 or tcp.DstPort == 443) and tcp.Ack and " \ "(" DIVERT_NO_LOCALNETSv4_DST " or " DIVERT_NO_LOCALNETSv6_DST "))" \ "))" #define FILTER_PASSIVE_STRING_TEMPLATE "inbound and ip and tcp and " \ "!impostor and !loopback and " \ "((ip.Id <= 0xF and ip.Id >= 0x0) " IPID_TEMPLATE ") and " \ "(tcp.SrcPort == 443 or tcp.SrcPort == 80) and tcp.Rst and " \ DIVERT_NO_LOCALNETSv4_SRC #define SET_HTTP_FRAGMENT_SIZE_OPTION(fragment_size) do { \ if (!http_fragment_size) { \ http_fragment_size = (unsigned int)fragment_size; \ } \ else if (http_fragment_size != (unsigned int)fragment_size) { \ printf( \ "WARNING: HTTP fragment size is already set to %u, not changing.\n", \ http_fragment_size \ ); \ } \ } while (0) #define TCP_HANDLE_OUTGOING_TTL_PARSE_PACKET_IF() \ if ((packet_v4 && tcp_handle_outgoing(&ppIpHdr->SrcAddr, &ppIpHdr->DstAddr, \ ppTcpHdr->SrcPort, ppTcpHdr->DstPort, \ &tcp_conn_info, 0)) \ || \ (packet_v6 && tcp_handle_outgoing(ppIpV6Hdr->SrcAddr, ppIpV6Hdr->DstAddr, \ ppTcpHdr->SrcPort, ppTcpHdr->DstPort, \ &tcp_conn_info, 1))) #define TCP_HANDLE_OUTGOING_FAKE_PACKET(func) do { \ should_send_fake = 1; \ if (do_auto_ttl || ttl_min_nhops) { \ TCP_HANDLE_OUTGOING_TTL_PARSE_PACKET_IF() { \ if (do_auto_ttl) { \ /* If Auto TTL mode */ \ ttl_of_fake_packet = tcp_get_auto_ttl(tcp_conn_info.ttl, auto_ttl_1, auto_ttl_2, \ ttl_min_nhops, auto_ttl_max); \ if (do_tcp_verb) { \ printf("Connection TTL = %d, Fake TTL = %d\n", tcp_conn_info.ttl, ttl_of_fake_packet); \ } \ } \ else if (ttl_min_nhops) { \ /* If not Auto TTL mode but --min-ttl is set */ \ if (!tcp_get_auto_ttl(tcp_conn_info.ttl, 0, 0, ttl_min_nhops, 0)) { \ /* Send only if nhops >= min_ttl */ \ should_send_fake = 0; \ } \ } \ } \ } \ if (should_send_fake) \ func(w_filter, &addr, packet, packetLen, packet_v6, \ ttl_of_fake_packet, do_wrong_chksum, do_wrong_seq); \ } while (0) static int running_from_service = 0; static int exiting = 0; static HANDLE filters[MAX_FILTERS]; static int filter_num = 0; static const char http10_redirect_302[] = "HTTP/1.0 302 "; static const char http11_redirect_302[] = "HTTP/1.1 302 "; static const char http_host_find[] = "\r\nHost: "; static const char http_host_replace[] = "\r\nhoSt: "; static const char http_useragent_find[] = "\r\nUser-Agent: "; static const char location_http[] = "\r\nLocation: http://"; static const char connection_close[] = "\r\nConnection: close"; static const char *http_methods[] = { "GET ", "HEAD ", "POST ", "PUT ", "DELETE ", "CONNECT ", "OPTIONS ", }; static struct option long_options[] = { {"port", required_argument, 0, 'z' }, {"dns-addr", required_argument, 0, 'd' }, {"dns-port", required_argument, 0, 'g' }, {"dnsv6-addr", required_argument, 0, '!' }, {"dnsv6-port", required_argument, 0, '@' }, {"dns-verb", no_argument, 0, 'v' }, {"blacklist", required_argument, 0, 'b' }, {"allow-no-sni",no_argument, 0, ']' }, {"ip-id", required_argument, 0, 'i' }, {"set-ttl", required_argument, 0, '$' }, {"min-ttl", required_argument, 0, '[' }, {"auto-ttl", optional_argument, 0, '+' }, {"wrong-chksum",no_argument, 0, '%' }, {"wrong-seq", no_argument, 0, ')' }, {"native-frag", no_argument, 0, '*' }, {"reverse-frag",no_argument, 0, '(' }, {"max-payload", optional_argument, 0, '|' }, {0, 0, 0, 0 } }; static char *filter_string = NULL; static char *filter_passive_string = NULL; static void add_filter_str(int proto, int port) { const char *udp = " or (udp and !impostor and !loopback and " \ "(udp.SrcPort == %d or udp.DstPort == %d))"; const char *tcp = " or (tcp and !impostor and !loopback " MAXPAYLOADSIZE_TEMPLATE " and " \ "(tcp.SrcPort == %d or tcp.DstPort == %d))"; char *current_filter = filter_string; size_t new_filter_size = strlen(current_filter) + (proto == IPPROTO_UDP ? strlen(udp) : strlen(tcp)) + 16; char *new_filter = malloc(new_filter_size); strcpy(new_filter, current_filter); if (proto == IPPROTO_UDP) sprintf(new_filter + strlen(new_filter), udp, port, port); else sprintf(new_filter + strlen(new_filter), tcp, port, port); filter_string = new_filter; free(current_filter); } static void add_ip_id_str(int id) { char *newstr; const char *ipid = " or ip.Id == %d"; char *addfilter = malloc(strlen(ipid) + 16); sprintf(addfilter, ipid, id); newstr = repl_str(filter_string, IPID_TEMPLATE, addfilter); free(filter_string); filter_string = newstr; newstr = repl_str(filter_passive_string, IPID_TEMPLATE, addfilter); free(filter_passive_string); filter_passive_string = newstr; } static void add_maxpayloadsize_str(unsigned short maxpayload) { char *newstr; const char *maxpayloadsize_str = "and (tcp.PayloadLength ? tcp.PayloadLength < %hu : true)"; char *addfilter = malloc(strlen(maxpayloadsize_str) + 16); sprintf(addfilter, maxpayloadsize_str, maxpayload); newstr = repl_str(filter_string, MAXPAYLOADSIZE_TEMPLATE, addfilter); free(filter_string); filter_string = newstr; } static void finalize_filter_strings() { char *newstr, *newstr2; newstr2 = repl_str(filter_string, IPID_TEMPLATE, ""); newstr = repl_str(newstr2, MAXPAYLOADSIZE_TEMPLATE, ""); free(filter_string); free(newstr2); filter_string = newstr; newstr = repl_str(filter_passive_string, IPID_TEMPLATE, ""); free(filter_passive_string); filter_passive_string = newstr; } static char* dumb_memmem(const char* haystack, unsigned int hlen, const char* needle, unsigned int nlen) { // naive implementation if (nlen > hlen) return NULL; size_t i; for (i=0; i limitValue) { puts(msg); exit(EXIT_FAILURE); } return (unsigned short int)res; } BYTE atoub(const char *str, const char *msg) { long unsigned int res = strtoul(str, NULL, 10u); enum { limitValue=0xFFu }; if(res > limitValue) { puts(msg); exit(EXIT_FAILURE); } return (BYTE)res; } static HANDLE init(char *filter, UINT64 flags) { LPTSTR errormessage = NULL; DWORD errorcode = 0; filter = WinDivertOpen(filter, WINDIVERT_LAYER_NETWORK, 0, flags); if (filter != INVALID_HANDLE_VALUE) return filter; errorcode = GetLastError(); FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorcode, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), (LPTSTR)&errormessage, 0, NULL); printf("Error opening filter: %s", errormessage); LocalFree(errormessage); if (errorcode == 577) printf("Windows Server 2016 systems must have secure boot disabled to be " "able to load WinDivert driver.\n" "Windows 7 systems must be up-to-date or at least have KB3033929 installed.\n" "https://www.microsoft.com/en-us/download/details.aspx?id=46078\n\n" "WARNING! If you see this error on Windows 7, it means your system is horribly " "outdated and SHOULD NOT BE USED TO ACCESS THE INTERNET!\n" "Most probably, you don't have security patches installed and anyone in you LAN or " "public Wi-Fi network can get full access to your computer (MS17-010 and others).\n" "You should install updates IMMEDIATELY.\n"); return NULL; } static int deinit(HANDLE handle) { if (handle) { WinDivertShutdown(handle, WINDIVERT_SHUTDOWN_BOTH); WinDivertClose(handle); return TRUE; } return FALSE; } void deinit_all() { for (int i = 0; i < filter_num; i++) { deinit(filters[i]); } } static void sigint_handler(int sig __attribute__((unused))) { exiting = 1; deinit_all(); exit(EXIT_SUCCESS); } static void mix_case(char *pktdata, unsigned int pktlen) { unsigned int i; if (pktlen <= 0) return; for (i = 0; i < pktlen; i++) { if (i % 2) { pktdata[i] = (char) toupper(pktdata[i]); } } } static int is_passivedpi_redirect(const char *pktdata, unsigned int pktlen) { /* First check if this is HTTP 302 redirect */ if (memcmp(pktdata, http11_redirect_302, sizeof(http11_redirect_302)-1) == 0 || memcmp(pktdata, http10_redirect_302, sizeof(http10_redirect_302)-1) == 0) { /* Then check if this is a redirect to new http site with Connection: close */ if (dumb_memmem(pktdata, pktlen, location_http, sizeof(location_http)-1) && dumb_memmem(pktdata, pktlen, connection_close, sizeof(connection_close)-1)) { return TRUE; } } return FALSE; } static int find_header_and_get_info(const char *pktdata, unsigned int pktlen, const char *hdrname, char **hdrnameaddr, char **hdrvalueaddr, unsigned int *hdrvaluelen) { char *data_addr_rn; char *hdr_begin; *hdrvaluelen = 0u; *hdrnameaddr = NULL; *hdrvalueaddr = NULL; /* Search for the header */ hdr_begin = dumb_memmem(pktdata, pktlen, hdrname, strlen(hdrname)); if (!hdr_begin) return FALSE; if (pktdata > hdr_begin) return FALSE; /* Set header address */ *hdrnameaddr = hdr_begin; *hdrvalueaddr = hdr_begin + strlen(hdrname); /* Search for header end (\r\n) */ data_addr_rn = dumb_memmem(*hdrvalueaddr, pktlen - (uintptr_t)(*hdrvalueaddr - pktdata), "\r\n", 2); if (data_addr_rn) { *hdrvaluelen = (uintptr_t)(data_addr_rn - *hdrvalueaddr); if (*hdrvaluelen >= 3 && *hdrvaluelen <= HOST_MAXLEN) return TRUE; } return FALSE; } /** * Very crude Server Name Indication (TLS ClientHello hostname) extractor. */ static int extract_sni(const char *pktdata, unsigned int pktlen, char **hostnameaddr, unsigned int *hostnamelen) { unsigned int ptr = 0; unsigned const char *d = (unsigned const char *)pktdata; unsigned const char *hnaddr = 0; int hnlen = 0; while (ptr + 8 < pktlen) { /* Search for specific Extensions sequence */ if (d[ptr] == '\0' && d[ptr+1] == '\0' && d[ptr+2] == '\0' && d[ptr+4] == '\0' && d[ptr+6] == '\0' && d[ptr+7] == '\0' && /* Check Extension length, Server Name list length * and Server Name length relations */ d[ptr+3] - d[ptr+5] == 2 && d[ptr+5] - d[ptr+8] == 3) { if (ptr + 8 + d[ptr+8] > pktlen) { return FALSE; } hnaddr = &d[ptr+9]; hnlen = d[ptr+8]; /* Limit hostname size up to 253 bytes */ if (hnlen < 3 || hnlen > HOST_MAXLEN) { return FALSE; } /* Validate that hostname has only ascii lowercase characters */ for (int i=0; i= '0' && hnaddr[i] <= '9') || (hnaddr[i] >= 'a' && hnaddr[i] <= 'z') || hnaddr[i] == '.' || hnaddr[i] == '-')) { return FALSE; } } *hostnameaddr = (char*)hnaddr; *hostnamelen = (unsigned int)hnlen; return TRUE; } ptr++; } return FALSE; } static inline void change_window_size(const PWINDIVERT_TCPHDR ppTcpHdr, unsigned int size) { if (size >= 1 && size <= 0xFFFFu) { ppTcpHdr->Window = htons((u_short)size); } } /* HTTP method end without trailing space */ static PVOID find_http_method_end(const char *pkt, unsigned int http_frag, int *is_fragmented) { unsigned int i; for (i = 0; i<(sizeof(http_methods) / sizeof(*http_methods)); i++) { if (memcmp(pkt, http_methods[i], strlen(http_methods[i])) == 0) { if (is_fragmented) *is_fragmented = 0; return (char*)pkt + strlen(http_methods[i]) - 1; } /* Try to find HTTP method in a second part of fragmented packet */ if ((http_frag == 1 || http_frag == 2) && memcmp(pkt, http_methods[i] + http_frag, strlen(http_methods[i]) - http_frag) == 0 ) { if (is_fragmented) *is_fragmented = 1; return (char*)pkt + strlen(http_methods[i]) - http_frag - 1; } } return NULL; } /** Fragment and send the packet. * * This function cuts off the end of the packet (step=0) or * the beginning of the packet (step=1) with fragment_size bytes. */ static void send_native_fragment(HANDLE w_filter, WINDIVERT_ADDRESS addr, char *packet, UINT packetLen, PVOID packet_data, UINT packet_dataLen, int packet_v4, int packet_v6, PWINDIVERT_IPHDR ppIpHdr, PWINDIVERT_IPV6HDR ppIpV6Hdr, PWINDIVERT_TCPHDR ppTcpHdr, unsigned int fragment_size, int step) { char packet_bak[MAX_PACKET_SIZE]; memcpy(&packet_bak, packet, packetLen); UINT orig_packetLen = packetLen; if (fragment_size >= packet_dataLen) { if (step == 1) fragment_size = 0; else return; } if (step == 0) { if (packet_v4) ppIpHdr->Length = htons( ntohs(ppIpHdr->Length) - packet_dataLen + fragment_size ); else if (packet_v6) ppIpV6Hdr->Length = htons( ntohs(ppIpV6Hdr->Length) - packet_dataLen + fragment_size ); //printf("step0 (%d:%d), pp:%d, was:%d, now:%d\n", // packet_v4, packet_v6, ntohs(ppIpHdr->Length), // packetLen, packetLen - packet_dataLen + fragment_size); packetLen = packetLen - packet_dataLen + fragment_size; } else if (step == 1) { if (packet_v4) ppIpHdr->Length = htons( ntohs(ppIpHdr->Length) - fragment_size ); else if (packet_v6) ppIpV6Hdr->Length = htons( ntohs(ppIpV6Hdr->Length) - fragment_size ); //printf("step1 (%d:%d), pp:%d, was:%d, now:%d\n", packet_v4, packet_v6, ntohs(ppIpHdr->Length), // packetLen, packetLen - fragment_size); memmove(packet_data, (char*)packet_data + fragment_size, packet_dataLen - fragment_size); packetLen -= fragment_size; ppTcpHdr->SeqNum = htonl(ntohl(ppTcpHdr->SeqNum) + fragment_size); } addr.IPChecksum = 0; addr.TCPChecksum = 0; WinDivertHelperCalcChecksums( packet, packetLen, &addr, 0 ); WinDivertSend( w_filter, packet, packetLen, NULL, &addr ); memcpy(packet, &packet_bak, orig_packetLen); //printf("Sent native fragment of %d size (step%d)\n", packetLen, step); } int main(int argc, char *argv[]) { static enum packet_type_e { unknown, ipv4_tcp, ipv4_tcp_data, ipv4_udp_data, ipv6_tcp, ipv6_tcp_data, ipv6_udp_data } packet_type; int i, should_reinject, should_recalc_checksum = 0; int sni_ok = 0; int opt; int packet_v4, packet_v6; HANDLE w_filter = NULL; WINDIVERT_ADDRESS addr; char packet[MAX_PACKET_SIZE]; PVOID packet_data; UINT packetLen; UINT packet_dataLen; PWINDIVERT_IPHDR ppIpHdr; PWINDIVERT_IPV6HDR ppIpV6Hdr; PWINDIVERT_TCPHDR ppTcpHdr; PWINDIVERT_UDPHDR ppUdpHdr; conntrack_info_t dns_conn_info; tcp_conntrack_info_t tcp_conn_info; int do_passivedpi = 0, do_fragment_http = 0, do_fragment_http_persistent = 0, do_fragment_http_persistent_nowait = 0, do_fragment_https = 0, do_host = 0, do_host_removespace = 0, do_additional_space = 0, do_http_allports = 0, do_host_mixedcase = 0, do_dnsv4_redirect = 0, do_dnsv6_redirect = 0, do_dns_verb = 0, do_tcp_verb = 0, do_blacklist = 0, do_allow_no_sni = 0, do_fake_packet = 0, do_auto_ttl = 0, do_wrong_chksum = 0, do_wrong_seq = 0, do_native_frag = 0, do_reverse_frag = 0; unsigned int http_fragment_size = 0; unsigned int https_fragment_size = 0; unsigned int current_fragment_size = 0; unsigned short max_payload_size = 0; BYTE should_send_fake = 0; BYTE ttl_of_fake_packet = 0; BYTE ttl_min_nhops = 0; BYTE auto_ttl_1 = 0; BYTE auto_ttl_2 = 0; BYTE auto_ttl_max = 0; uint32_t dnsv4_addr = 0; struct in6_addr dnsv6_addr = {0}; struct in6_addr dns_temp_addr = {0}; uint16_t dnsv4_port = htons(53); uint16_t dnsv6_port = htons(53); char *host_addr, *useragent_addr, *method_addr; unsigned int host_len, useragent_len; int http_req_fragmented; char *hdr_name_addr = NULL, *hdr_value_addr = NULL; unsigned int hdr_value_len; // Make sure to search DLLs only in safe path, not in current working dir. SetDllDirectory(""); SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT); if (!running_from_service) { running_from_service = 1; if (service_register(argc, argv)) { /* We've been called as a service. Register service * and exit this thread. main() would be called from * service.c next time. * * Note that if service_register() succeedes it does * not return until the service is stopped. * That is why we should set running_from_service * before calling service_register and unset it * afterwards. */ return 0; } running_from_service = 0; } if (filter_string == NULL) filter_string = strdup(FILTER_STRING_TEMPLATE); if (filter_passive_string == NULL) filter_passive_string = strdup(FILTER_PASSIVE_STRING_TEMPLATE); printf( "GoodbyeDPI " GOODBYEDPI_VERSION ": Passive DPI blocker and Active DPI circumvention utility\n" "https://github.com/ValdikSS/GoodbyeDPI\n\n" ); if (argc == 1) { /* enable mode -5 by default */ do_fragment_http = do_fragment_https = 1; do_reverse_frag = do_native_frag = 1; http_fragment_size = https_fragment_size = 2; do_fragment_http_persistent = do_fragment_http_persistent_nowait = 1; do_fake_packet = 1; do_auto_ttl = 1; max_payload_size = 1200; } while ((opt = getopt_long(argc, argv, "123456prsaf:e:mwk:n", long_options, NULL)) != -1) { switch (opt) { case '1': do_passivedpi = do_host = do_host_removespace \ = do_fragment_http = do_fragment_https \ = do_fragment_http_persistent \ = do_fragment_http_persistent_nowait = 1; break; case '2': do_passivedpi = do_host = do_host_removespace \ = do_fragment_http = do_fragment_https \ = do_fragment_http_persistent \ = do_fragment_http_persistent_nowait = 1; https_fragment_size = 40u; break; case '3': do_passivedpi = do_host = do_host_removespace \ = do_fragment_https = 1; https_fragment_size = 40u; break; case '4': do_passivedpi = do_host = do_host_removespace = 1; break; case '5': do_fragment_http = do_fragment_https = 1; do_reverse_frag = do_native_frag = 1; http_fragment_size = https_fragment_size = 2; do_fragment_http_persistent = do_fragment_http_persistent_nowait = 1; do_fake_packet = 1; do_auto_ttl = 1; max_payload_size = 1200; break; case '6': do_fragment_http = do_fragment_https = 1; do_reverse_frag = do_native_frag = 1; http_fragment_size = https_fragment_size = 2; do_fragment_http_persistent = do_fragment_http_persistent_nowait = 1; do_fake_packet = 1; do_wrong_seq = 1; max_payload_size = 1200; break; case 'p': do_passivedpi = 1; break; case 'r': do_host = 1; break; case 's': do_host_removespace = 1; break; case 'a': do_additional_space = 1; do_host_removespace = 1; break; case 'm': do_host_mixedcase = 1; break; case 'f': do_fragment_http = 1; SET_HTTP_FRAGMENT_SIZE_OPTION(atousi(optarg, "Fragment size should be in range [0 - 0xFFFF]\n")); break; case 'k': do_fragment_http_persistent = 1; do_native_frag = 1; SET_HTTP_FRAGMENT_SIZE_OPTION(atousi(optarg, "Fragment size should be in range [0 - 0xFFFF]\n")); break; case 'n': do_fragment_http_persistent = 1; do_fragment_http_persistent_nowait = 1; do_native_frag = 1; break; case 'e': do_fragment_https = 1; https_fragment_size = atousi(optarg, "Fragment size should be in range [0 - 65535]\n"); break; case 'w': do_http_allports = 1; break; case 'z': // --port /* i is used as a temporary variable here */ i = atoi(optarg); if (i <= 0 || i > 65535) { printf("Port parameter error!\n"); exit(EXIT_FAILURE); } if (i != 80 && i != 443) add_filter_str(IPPROTO_TCP, i); i = 0; break; case 'i': // --ip-id /* i is used as a temporary variable here */ i = atousi(optarg, "IP ID parameter error!\n"); add_ip_id_str(i); i = 0; break; case 'd': // --dns-addr if ((inet_pton(AF_INET, optarg, dns_temp_addr.s6_addr) == 1) && !do_dnsv4_redirect) { do_dnsv4_redirect = 1; if (inet_pton(AF_INET, optarg, &dnsv4_addr) != 1) { puts("DNS address parameter error!"); exit(EXIT_FAILURE); } add_filter_str(IPPROTO_UDP, 53); flush_dns_cache(); break; } puts("DNS address parameter error!"); exit(EXIT_FAILURE); break; case '!': // --dnsv6-addr if ((inet_pton(AF_INET6, optarg, dns_temp_addr.s6_addr) == 1) && !do_dnsv6_redirect) { do_dnsv6_redirect = 1; if (inet_pton(AF_INET6, optarg, dnsv6_addr.s6_addr) != 1) { puts("DNS address parameter error!"); exit(EXIT_FAILURE); } add_filter_str(IPPROTO_UDP, 53); flush_dns_cache(); break; } puts("DNS address parameter error!"); exit(EXIT_FAILURE); break; case 'g': // --dns-port if (!do_dnsv4_redirect) { puts("--dns-port should be used with --dns-addr!\n" "Make sure you use --dns-addr and pass it before " "--dns-port"); exit(EXIT_FAILURE); } dnsv4_port = atousi(optarg, "DNS port parameter error!"); if (dnsv4_port != 53) { add_filter_str(IPPROTO_UDP, dnsv4_port); } dnsv4_port = htons(dnsv4_port); break; case '@': // --dnsv6-port if (!do_dnsv6_redirect) { puts("--dnsv6-port should be used with --dnsv6-addr!\n" "Make sure you use --dnsv6-addr and pass it before " "--dnsv6-port"); exit(EXIT_FAILURE); } dnsv6_port = atousi(optarg, "DNS port parameter error!"); if (dnsv6_port != 53) { add_filter_str(IPPROTO_UDP, dnsv6_port); } dnsv6_port = htons(dnsv6_port); break; case 'v': do_dns_verb = 1; do_tcp_verb = 1; break; case 'b': // --blacklist do_blacklist = 1; if (!blackwhitelist_load_list(optarg)) { printf("Can't load blacklist from file!\n"); exit(EXIT_FAILURE); } break; case ']': // --allow-no-sni do_allow_no_sni = 1; break; case '$': // --set-ttl do_fake_packet = 1; ttl_of_fake_packet = atoub(optarg, "Set TTL parameter error!"); break; case '[': // --min-ttl do_fake_packet = 1; ttl_min_nhops = atoub(optarg, "Set Minimum TTL number of hops parameter error!"); break; case '+': // --auto-ttl do_fake_packet = 1; do_auto_ttl = 1; if (!optarg && argv[optind] && argv[optind][0] != '-') optarg = argv[optind]; if (optarg) { char *autottl_copy = strdup(optarg); if (strchr(autottl_copy, '-')) { // token "-" found, start X-Y parser char *autottl_current = strtok(autottl_copy, "-"); auto_ttl_1 = atoub(autottl_current, "Set Auto TTL parameter error!"); autottl_current = strtok(NULL, "-"); if (!autottl_current) { puts("Set Auto TTL parameter error!"); exit(EXIT_FAILURE); } auto_ttl_2 = atoub(autottl_current, "Set Auto TTL parameter error!"); autottl_current = strtok(NULL, "-"); if (!autottl_current) { puts("Set Auto TTL parameter error!"); exit(EXIT_FAILURE); } auto_ttl_max = atoub(autottl_current, "Set Auto TTL parameter error!"); } else { // single digit parser auto_ttl_2 = atoub(optarg, "Set Auto TTL parameter error!"); auto_ttl_1 = auto_ttl_2; } free(autottl_copy); } break; case '%': // --wrong-chksum do_fake_packet = 1; do_wrong_chksum = 1; break; case ')': // --wrong-seq do_fake_packet = 1; do_wrong_seq = 1; break; case '*': // --native-frag do_native_frag = 1; do_fragment_http_persistent = 1; do_fragment_http_persistent_nowait = 1; break; case '(': // --reverse-frag do_reverse_frag = 1; do_native_frag = 1; do_fragment_http_persistent = 1; do_fragment_http_persistent_nowait = 1; break; case '|': // --max-payload if (!optarg && argv[optind] && argv[optind][0] != '-') optarg = argv[optind]; if (optarg) max_payload_size = atousi(optarg, "Max payload size parameter error!"); else max_payload_size = 1200; break; default: puts("Usage: goodbyedpi.exe [OPTION...]\n" " -p block passive DPI\n" " -r replace Host with hoSt\n" " -s remove space between host header and its value\n" " -a additional space between Method and Request-URI (enables -s, may break sites)\n" " -m mix Host header case (test.com -> tEsT.cOm)\n" " -f set HTTP fragmentation to value\n" " -k enable HTTP persistent (keep-alive) fragmentation and set it to value\n" " -n do not wait for first segment ACK when -k is enabled\n" " -e set HTTPS fragmentation to value\n" " -w try to find and parse HTTP traffic on all processed ports (not only on port 80)\n" " --port additional TCP port to perform fragmentation on (and HTTP tricks with -w)\n" " --ip-id handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID).\n" " --dns-addr redirect UDPv4 DNS requests to the supplied IPv4 address (experimental)\n" " --dns-port redirect UDPv4 DNS requests to the supplied port (53 by default)\n" " --dnsv6-addr redirect UDPv6 DNS requests to the supplied IPv6 address (experimental)\n" " --dnsv6-port redirect UDPv6 DNS requests to the supplied port (53 by default)\n" " --dns-verb print verbose DNS redirection messages\n" " --blacklist perform circumvention tricks only to host names and subdomains from\n" " supplied text file (HTTP Host/TLS SNI).\n" " This option can be supplied multiple times.\n" " --allow-no-sni perform circumvention if TLS SNI can't be detected with --blacklist enabled.\n" " --set-ttl activate Fake Request Mode and send it with supplied TTL value.\n" " DANGEROUS! May break websites in unexpected ways. Use with care (or --blacklist).\n" " --auto-ttl [a1-a2-m] activate Fake Request Mode, automatically detect TTL and decrease\n" " it based on a distance. If the distance is shorter than a2, TTL is decreased\n" " by a2. If it's longer, (a1; a2) scale is used with the distance as a weight.\n" " If the resulting TTL is more than m(ax), set it to m.\n" " Default (if set): --auto-ttl 1-4-10. Also sets --min-ttl 3.\n" " DANGEROUS! May break websites in unexpected ways. Use with care (or --blacklist).\n" " --min-ttl minimum TTL distance (128/64 - TTL) for which to send Fake Request\n" " in --set-ttl and --auto-ttl modes.\n" " --wrong-chksum activate Fake Request Mode and send it with incorrect TCP checksum.\n" " May not work in a VM or with some routers, but is safer than set-ttl.\n" " Could be combined with --set-ttl\n" " --wrong-seq activate Fake Request Mode and send it with TCP SEQ/ACK in the past.\n" " --native-frag fragment (split) the packets by sending them in smaller packets, without\n" " shrinking the Window Size. Works faster (does not slow down the connection)\n" " and better.\n" " --reverse-frag fragment (split) the packets just as --native-frag, but send them in the\n" " reversed order. Works with the websites which could not handle segmented\n" " HTTPS TLS ClientHello (because they receive the TCP flow \"combined\").\n" " --max-payload [value] packets with TCP payload data more than [value] won't be processed.\n" " Use this option to reduce CPU usage by skipping huge amount of data\n" " (like file transfers) in already established sessions.\n" " May skip some huge HTTP requests from being processed.\n" " Default (if set): --max-payload 1200.\n" "\n"); puts("LEGACY modesets:\n" " -1 -p -r -s -f 2 -k 2 -n -e 2 (most compatible mode)\n" " -2 -p -r -s -f 2 -k 2 -n -e 40 (better speed for HTTPS yet still compatible)\n" " -3 -p -r -s -e 40 (better speed for HTTP and HTTPS)\n" " -4 -p -r -s (best speed)" "\n" "Modern modesets (more stable, more compatible, faster):\n" " -5 -f 2 -e 2 --auto-ttl --reverse-frag --max-payload (this is the default)\n" " -6 -f 2 -e 2 --wrong-seq --reverse-frag --max-payload\n"); exit(EXIT_FAILURE); } } if (!http_fragment_size) http_fragment_size = 2; if (!https_fragment_size) https_fragment_size = 2; if (!auto_ttl_1) auto_ttl_1 = 1; if (!auto_ttl_2) auto_ttl_2 = 4; if (do_auto_ttl) { if (!ttl_min_nhops) ttl_min_nhops = 3; if (!auto_ttl_max) auto_ttl_max = 10; } printf("Block passive: %d\n" /* 1 */ "Fragment HTTP: %u\n" /* 2 */ "Fragment persistent HTTP: %u\n" /* 3 */ "Fragment HTTPS: %u\n" /* 4 */ "Native fragmentation (splitting): %d\n" /* 5 */ "Fragments sending in reverse: %d\n" /* 6 */ "hoSt: %d\n" /* 7 */ "Host no space: %d\n" /* 8 */ "Additional space: %d\n" /* 9 */ "Mix Host: %d\n" /* 10 */ "HTTP AllPorts: %d\n" /* 11 */ "HTTP Persistent Nowait: %d\n" /* 12 */ "DNS redirect: %d\n" /* 13 */ "DNSv6 redirect: %d\n" /* 14 */ "Allow missing SNI: %d\n" /* 15 */ "Fake requests, TTL: %s (fixed: %hu, auto: %hu-%hu-%hu, min distance: %hu)\n" /* 16 */ "Fake requests, wrong checksum: %d\n" /* 17 */ "Fake requests, wrong SEQ/ACK: %d\n" /* 18 */ "Max payload size: %hu\n", /* 19 */ do_passivedpi, /* 1 */ (do_fragment_http ? http_fragment_size : 0), /* 2 */ (do_fragment_http_persistent ? http_fragment_size : 0),/* 3 */ (do_fragment_https ? https_fragment_size : 0), /* 4 */ do_native_frag, /* 5 */ do_reverse_frag, /* 6 */ do_host, /* 7 */ do_host_removespace, /* 8 */ do_additional_space, /* 9 */ do_host_mixedcase, /* 10 */ do_http_allports, /* 11 */ do_fragment_http_persistent_nowait, /* 12 */ do_dnsv4_redirect, /* 13 */ do_dnsv6_redirect, /* 14 */ do_allow_no_sni, /* 15 */ ttl_of_fake_packet ? "fixed" : (do_auto_ttl ? "auto" : "disabled"), /* 16 */ ttl_of_fake_packet, do_auto_ttl ? auto_ttl_1 : 0, do_auto_ttl ? auto_ttl_2 : 0, do_auto_ttl ? auto_ttl_max : 0, ttl_min_nhops, do_wrong_chksum, /* 17 */ do_wrong_seq, /* 18 */ max_payload_size /* 19 */ ); if (do_fragment_http && http_fragment_size > 2 && !do_native_frag) { puts("\nWARNING: HTTP fragmentation values > 2 are not fully compatible " "with other options. Please use values <= 2 or disable HTTP fragmentation " "completely."); } if (do_native_frag && !(do_fragment_http || do_fragment_https)) { puts("\nERROR: Native fragmentation is enabled but fragment sizes are not set.\n" "Fragmentation has no effect."); die(); } if (max_payload_size) add_maxpayloadsize_str(max_payload_size); finalize_filter_strings(); puts("\nOpening filter"); filter_num = 0; if (do_passivedpi) { /* IPv4 only filter for inbound RST packets with ID [0x0; 0xF] */ filters[filter_num] = init( filter_passive_string, WINDIVERT_FLAG_DROP); if (filters[filter_num] == NULL) die(); filter_num++; } /* * IPv4 & IPv6 filter for inbound HTTP redirection packets and * active DPI circumvention */ filters[filter_num] = init(filter_string, 0); w_filter = filters[filter_num]; filter_num++; for (i = 0; i < filter_num; i++) { if (filters[i] == NULL) die(); } printf("Filter activated, GoodbyeDPI is now running!\n"); signal(SIGINT, sigint_handler); while (1) { if (WinDivertRecv(w_filter, packet, sizeof(packet), &packetLen, &addr)) { debug("Got %s packet, len=%d!\n", addr.Outbound ? "outbound" : "inbound", packetLen); should_reinject = 1; should_recalc_checksum = 0; ppIpHdr = (PWINDIVERT_IPHDR)NULL; ppIpV6Hdr = (PWINDIVERT_IPV6HDR)NULL; ppTcpHdr = (PWINDIVERT_TCPHDR)NULL; ppUdpHdr = (PWINDIVERT_UDPHDR)NULL; packet_v4 = packet_v6 = 0; packet_type = unknown; // Parse network packet and set it's type if (WinDivertHelperParsePacket(packet, packetLen, &ppIpHdr, &ppIpV6Hdr, NULL, NULL, NULL, &ppTcpHdr, &ppUdpHdr, &packet_data, &packet_dataLen, NULL, NULL)) { if (ppIpHdr) { packet_v4 = 1; if (ppTcpHdr) { packet_type = ipv4_tcp; if (packet_data) { packet_type = ipv4_tcp_data; } } else if (ppUdpHdr && packet_data) { packet_type = ipv4_udp_data; } } else if (ppIpV6Hdr) { packet_v6 = 1; if (ppTcpHdr) { packet_type = ipv6_tcp; if (packet_data) { packet_type = ipv6_tcp_data; } } else if (ppUdpHdr && packet_data) { packet_type = ipv6_udp_data; } } } debug("packet_type: %d, packet_v4: %d, packet_v6: %d\n", packet_type, packet_v4, packet_v6); if (packet_type == ipv4_tcp_data || packet_type == ipv6_tcp_data) { //printf("Got parsed packet, len=%d!\n", packet_dataLen); /* Got a TCP packet WITH DATA */ /* Handle INBOUND packet with data and find HTTP REDIRECT in there */ if (!addr.Outbound && packet_dataLen > 16) { /* If INBOUND packet with DATA (tcp.Ack) */ /* Drop packets from filter with HTTP 30x Redirect */ if (do_passivedpi && is_passivedpi_redirect(packet_data, packet_dataLen)) { if (packet_v4) { //printf("Dropping HTTP Redirect packet!\n"); should_reinject = 0; } else if (packet_v6 && WINDIVERT_IPV6HDR_GET_FLOWLABEL(ppIpV6Hdr) == 0x0) { /* Contrary to IPv4 where we get only packets with IP ID 0x0-0xF, * for IPv6 we got all the incoming data packets since we can't * filter them in a driver. * * Handle only IPv6 Flow Label == 0x0 for now */ //printf("Dropping HTTP Redirect packet!\n"); should_reinject = 0; } } } /* Handle OUTBOUND packet on port 443, search for something that resembles * TLS handshake, send fake request. */ else if (addr.Outbound && ((do_fragment_https ? packet_dataLen == https_fragment_size : 0) || packet_dataLen > 16) && ppTcpHdr->DstPort != htons(80) && (do_fake_packet || do_native_frag) ) { /** * In case of Window Size fragmentation=2, we'll receive only 2 byte packet. * But if the packet is more than 2 bytes, check ClientHello byte. */ if ((packet_dataLen == 2 && memcmp(packet_data, "\x16\x03", 2) == 0) || (packet_dataLen >= 3 && memcmp(packet_data, "\x16\x03\x01", 3) == 0)) { if (do_blacklist) { sni_ok = extract_sni(packet_data, packet_dataLen, &host_addr, &host_len); } if ( (do_blacklist && sni_ok && blackwhitelist_check_hostname(host_addr, host_len) ) || (do_blacklist && !sni_ok && do_allow_no_sni) || (!do_blacklist) ) { #ifdef DEBUG char lsni[HOST_MAXLEN + 1] = {0}; extract_sni(packet_data, packet_dataLen, &host_addr, &host_len); memcpy(&lsni, host_addr, host_len); printf("Blocked HTTPS website SNI: %s\n", lsni); #endif if (do_fake_packet) { TCP_HANDLE_OUTGOING_FAKE_PACKET(send_fake_https_request); } if (do_native_frag) { // Signal for native fragmentation code handler should_recalc_checksum = 1; } } } } /* Handle OUTBOUND packet on port 80, search for Host header */ else if (addr.Outbound && packet_dataLen > 16 && (do_http_allports ? 1 : (ppTcpHdr->DstPort == htons(80))) && find_http_method_end(packet_data, (do_fragment_http ? http_fragment_size : 0u), &http_req_fragmented) && (do_host || do_host_removespace || do_host_mixedcase || do_fragment_http_persistent || do_fake_packet)) { /* Find Host header */ if (find_header_and_get_info(packet_data, packet_dataLen, http_host_find, &hdr_name_addr, &hdr_value_addr, &hdr_value_len) && hdr_value_len > 0 && hdr_value_len <= HOST_MAXLEN && (do_blacklist ? blackwhitelist_check_hostname(hdr_value_addr, hdr_value_len) : 1)) { host_addr = hdr_value_addr; host_len = hdr_value_len; #ifdef DEBUG char lhost[HOST_MAXLEN + 1] = {0}; memcpy(&lhost, host_addr, host_len); printf("Blocked HTTP website Host: %s\n", lhost); #endif if (do_native_frag) { // Signal for native fragmentation code handler should_recalc_checksum = 1; } if (do_fake_packet) { TCP_HANDLE_OUTGOING_FAKE_PACKET(send_fake_http_request); } if (do_host_mixedcase) { mix_case(host_addr, host_len); should_recalc_checksum = 1; } if (do_host) { /* Replace "Host: " with "hoSt: " */ memcpy(hdr_name_addr, http_host_replace, strlen(http_host_replace)); should_recalc_checksum = 1; //printf("Replaced Host header!\n"); } /* If removing space between host header and its value * and adding additional space between Method and Request-URI */ if (do_additional_space && do_host_removespace) { /* End of "Host:" without trailing space */ method_addr = find_http_method_end(packet_data, (do_fragment_http ? http_fragment_size : 0), NULL); if (method_addr) { memmove(method_addr + 1, method_addr, (size_t)(host_addr - method_addr - 1)); should_recalc_checksum = 1; } } /* If just removing space between host header and its value */ else if (do_host_removespace) { if (find_header_and_get_info(packet_data, packet_dataLen, http_useragent_find, &hdr_name_addr, &hdr_value_addr, &hdr_value_len)) { useragent_addr = hdr_value_addr; useragent_len = hdr_value_len; /* We move Host header value by one byte to the left and then * "insert" stolen space to the end of User-Agent value because * some web servers are not tolerant to additional space in the * end of Host header. * * Nothing is done if User-Agent header is missing. */ if (useragent_addr && useragent_len > 0) { /* useragent_addr is in the beginning of User-Agent value */ if (useragent_addr > host_addr) { /* Move one byte to the LEFT from "Host:" * to the end of User-Agent */ memmove(host_addr - 1, host_addr, (size_t)(useragent_addr + useragent_len - host_addr)); host_addr -= 1; /* Put space in the end of User-Agent header */ *(char*)((unsigned char*)useragent_addr + useragent_len - 1) = ' '; should_recalc_checksum = 1; //printf("Replaced Host header!\n"); } else { /* User-Agent goes BEFORE Host header */ /* Move one byte to the RIGHT from the end of User-Agent * to the "Host:" */ memmove(useragent_addr + useragent_len + 1, useragent_addr + useragent_len, (size_t)(host_addr - 1 - (useragent_addr + useragent_len))); /* Put space in the end of User-Agent header */ *(char*)((unsigned char*)useragent_addr + useragent_len) = ' '; should_recalc_checksum = 1; //printf("Replaced Host header!\n"); } } /* if (host_len <= HOST_MAXLEN && useragent_addr) */ } /* if (find_header_and_get_info http_useragent) */ } /* else if (do_host_removespace) */ } /* if (find_header_and_get_info http_host) */ } /* Handle OUTBOUND packet with data */ /* * should_recalc_checksum mean we have detected a packet to handle and * modified it in some way. * Handle native fragmentation here, incl. sending the packet. */ if (should_reinject && should_recalc_checksum && do_native_frag) { current_fragment_size = 0; if (do_fragment_http && ppTcpHdr->DstPort == htons(80)) { current_fragment_size = http_fragment_size; } else if (do_fragment_https && ppTcpHdr->DstPort != htons(80)) { current_fragment_size = https_fragment_size; } if (current_fragment_size) { send_native_fragment(w_filter, addr, packet, packetLen, packet_data, packet_dataLen,packet_v4, packet_v6, ppIpHdr, ppIpV6Hdr, ppTcpHdr, current_fragment_size, do_reverse_frag); send_native_fragment(w_filter, addr, packet, packetLen, packet_data, packet_dataLen,packet_v4, packet_v6, ppIpHdr, ppIpV6Hdr, ppTcpHdr, current_fragment_size, !do_reverse_frag); continue; } } } /* Handle TCP packet with data */ /* Else if we got TCP packet without data */ else if (packet_type == ipv4_tcp || packet_type == ipv6_tcp) { /* If we got INBOUND SYN+ACK packet */ if (!addr.Outbound && ppTcpHdr->Syn == 1 && ppTcpHdr->Ack == 1) { //printf("Changing Window Size!\n"); /* * Window Size is changed even if do_fragment_http_persistent * is enabled as there could be non-HTTP data on port 80 */ if (do_fake_packet && (do_auto_ttl || ttl_min_nhops)) { if (!((packet_v4 && tcp_handle_incoming(&ppIpHdr->SrcAddr, &ppIpHdr->DstAddr, ppTcpHdr->SrcPort, ppTcpHdr->DstPort, 0, ppIpHdr->TTL)) || (packet_v6 && tcp_handle_incoming((uint32_t*)&ppIpV6Hdr->SrcAddr, (uint32_t*)&ppIpV6Hdr->DstAddr, ppTcpHdr->SrcPort, ppTcpHdr->DstPort, 1, ppIpV6Hdr->HopLimit)))) { if (do_tcp_verb) puts("[TCP WARN] Can't add TCP connection record."); } } if (!do_native_frag) { if (do_fragment_http && ppTcpHdr->SrcPort == htons(80)) { change_window_size(ppTcpHdr, http_fragment_size); should_recalc_checksum = 1; } else if (do_fragment_https && ppTcpHdr->SrcPort != htons(80)) { change_window_size(ppTcpHdr, https_fragment_size); should_recalc_checksum = 1; } } } } /* Else if we got UDP packet with data */ else if ((do_dnsv4_redirect && (packet_type == ipv4_udp_data)) || (do_dnsv6_redirect && (packet_type == ipv6_udp_data))) { if (!addr.Outbound) { if ((packet_v4 && dns_handle_incoming(&ppIpHdr->DstAddr, ppUdpHdr->DstPort, packet_data, packet_dataLen, &dns_conn_info, 0)) || (packet_v6 && dns_handle_incoming(ppIpV6Hdr->DstAddr, ppUdpHdr->DstPort, packet_data, packet_dataLen, &dns_conn_info, 1))) { /* Changing source IP and port to the values * from DNS conntrack */ if (packet_v4) ppIpHdr->SrcAddr = dns_conn_info.dstip[0]; else if (packet_v6) ipv6_copy_addr(ppIpV6Hdr->SrcAddr, dns_conn_info.dstip); ppUdpHdr->DstPort = dns_conn_info.srcport; ppUdpHdr->SrcPort = dns_conn_info.dstport; should_recalc_checksum = 1; } else { if (dns_is_dns_packet(packet_data, packet_dataLen, 0)) should_reinject = 0; if (do_dns_verb && !should_reinject) { printf("[DNS] Error handling incoming packet: srcport = %hu, dstport = %hu\n", ntohs(ppUdpHdr->SrcPort), ntohs(ppUdpHdr->DstPort)); } } } else if (addr.Outbound) { if ((packet_v4 && dns_handle_outgoing(&ppIpHdr->SrcAddr, ppUdpHdr->SrcPort, &ppIpHdr->DstAddr, ppUdpHdr->DstPort, packet_data, packet_dataLen, 0)) || (packet_v6 && dns_handle_outgoing(ppIpV6Hdr->SrcAddr, ppUdpHdr->SrcPort, ppIpV6Hdr->DstAddr, ppUdpHdr->DstPort, packet_data, packet_dataLen, 1))) { /* Changing destination IP and port to the values * from configuration */ if (packet_v4) { ppIpHdr->DstAddr = dnsv4_addr; ppUdpHdr->DstPort = dnsv4_port; } else if (packet_v6) { ipv6_copy_addr(ppIpV6Hdr->DstAddr, (uint32_t*)dnsv6_addr.s6_addr); ppUdpHdr->DstPort = dnsv6_port; } should_recalc_checksum = 1; } else { if (dns_is_dns_packet(packet_data, packet_dataLen, 1)) should_reinject = 0; if (do_dns_verb && !should_reinject) { printf("[DNS] Error handling outgoing packet: srcport = %hu, dstport = %hu\n", ntohs(ppUdpHdr->SrcPort), ntohs(ppUdpHdr->DstPort)); } } } } if (should_reinject) { //printf("Re-injecting!\n"); if (should_recalc_checksum) { WinDivertHelperCalcChecksums(packet, packetLen, &addr, (UINT64)0LL); } WinDivertSend(w_filter, packet, packetLen, NULL, &addr); } } else { // error, ignore if (!exiting) printf("Error receiving packet!\n"); break; } } }