diff --git a/src/goodbyedpi.c b/src/goodbyedpi.c index 4c52ff46..90c385cc 100644 --- a/src/goodbyedpi.c +++ b/src/goodbyedpi.c @@ -1,3 +1,563 @@ +/* + * 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; + /* 0x47455420 is "GET ", 0x504F5354 is "POST", big endian. */ + const char *maxpayloadsize_str = "and (tcp.PayloadLength ? tcp.PayloadLength < %hu or tcp.Payload32[0] == 0x47455420 or tcp.Payload32[0] == 0x504F5354 : 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 const char *find_http_method_end(const char *pkt, unsigned int http_frag, int *is_fragmented) { + switch (*pkt) { + case 'G': + if (strncmp(pkt, "GET", 3) == 0) { + if (is_fragmented) + *is_fragmented = 0; + return pkt + 3; + } + break; + case 'P': + if (strncmp(pkt, "POST", 4) == 0) { + if (is_fragmented) + *is_fragmented = 0; + return pkt + 4; + } + break; + case 'H': + if (strncmp(pkt, "HEAD", 4) == 0) { + if (is_fragmented) + *is_fragmented = 0; + return pkt + 4; + } + break; + case 'O': + if (strncmp(pkt, "OPTIONS", 7) == 0) { + if (is_fragmented) + *is_fragmented = 0; + return pkt + 7; + } + break; + case 'D': + if (strncmp(pkt, "DELETE", 6) == 0) { + if (is_fragmented) + *is_fragmented = 0; + return pkt + 6; + } + break; + case 'T': + if (strncmp(pkt, "TRACE", 5) == 0) { + if (is_fragmented) + *is_fragmented = 0; + return pkt + 5; + } + break; + case 'C': + if (strncmp(pkt, "CONNECT", 7) == 0) { + if (is_fragmented) + *is_fragmented = 0; + return pkt + 7; + } + break; + default: + /* Try to find HTTP method in a second part of fragmented packet */ + if ((http_frag == 1 || http_frag == 2)) { + switch (*pkt) { + case 'E': + if (strncmp(pkt, "ET", http_frag) == 0) { + if (is_fragmented) + *is_fragmented = 1; + return pkt + http_frag - 1; + } + break; + case 'S': + if (strncmp(pkt, "ST", http_frag) == 0) { + if (is_fragmented) + *is_fragmented = 1; + return pkt + http_frag - 1; + } + break; + case 'A': + if (strncmp(pkt, "AD", http_frag) == 0) { + if (is_fragmented) + *is_fragmented = 1; + return pkt + http_frag - 1; + } + break; + case 'N': + if (strncmp(pkt, "NS", http_frag) == 0) { + if (is_fragmented) + *is_fragmented = 1; + return pkt + http_frag - 1; + } + break; + case 'L': + if (strncmp(pkt, "LE", http_frag) == 0) { + if (is_fragmented) + *is_fragmented = 1; + return pkt + http_frag - 1; + } + break; + case 'R': + if (strncmp(pkt, "RACE", http_frag + 1) == 0) { + if (is_fragmented) + *is_fragmented = 1; + return pkt + http_frag - 1; + } + break; + case 'O': + if (strncmp(pkt, "ONNECT", http_frag + 1) == 0) { + if (is_fragmented) + *is_fragmented = 1; + return pkt + http_frag - 1; + } + break; + default: + return NULL; + } + } + } + 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, @@ -60,3 +620,894 @@ static void send_native_fragment(HANDLE w_filter, WINDIVERT_ADDRESS addr, ); memcpy(packet, packet_bak, orig_packetLen); } + + +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_auto_ttl = auto_ttl_1 = auto_ttl_2 = auto_ttl_max = 0; + 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 */ + do_auto_ttl ? "auto" : (do_fake_packet ? "fixed" : "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; + } + } +}