/ src / goodbyedpi.c
goodbyedpi.c
   1  /*
   2   * GoodbyeDPI — Passive DPI blocker and Active DPI circumvention utility.
   3   */
   4  
   5  #include <stdio.h>
   6  #include <stdlib.h>
   7  #include <ctype.h>
   8  #include <signal.h>
   9  #include <unistd.h>
  10  #include <string.h>
  11  #include <getopt.h>
  12  #include <in6addr.h>
  13  #include <ws2tcpip.h>
  14  #include "windivert.h"
  15  #include "goodbyedpi.h"
  16  #include "utils/repl_str.h"
  17  #include "service.h"
  18  #include "dnsredir.h"
  19  #include "ttltrack.h"
  20  #include "blackwhitelist.h"
  21  #include "fakepackets.h"
  22  
  23  // My mingw installation does not load inet_pton definition for some reason
  24  WINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pAddr);
  25  
  26  #define GOODBYEDPI_VERSION "v0.2.2"
  27  
  28  #define die() do { sleep(20); exit(EXIT_FAILURE); } while (0)
  29  
  30  #define MAX_FILTERS 4
  31  
  32  #define DIVERT_NO_LOCALNETSv4_DST "(" \
  33                     "(ip.DstAddr < 127.0.0.1 or ip.DstAddr > 127.255.255.255) and " \
  34                     "(ip.DstAddr < 10.0.0.0 or ip.DstAddr > 10.255.255.255) and " \
  35                     "(ip.DstAddr < 192.168.0.0 or ip.DstAddr > 192.168.255.255) and " \
  36                     "(ip.DstAddr < 172.16.0.0 or ip.DstAddr > 172.31.255.255) and " \
  37                     "(ip.DstAddr < 169.254.0.0 or ip.DstAddr > 169.254.255.255)" \
  38                     ")"
  39  #define DIVERT_NO_LOCALNETSv4_SRC "(" \
  40                     "(ip.SrcAddr < 127.0.0.1 or ip.SrcAddr > 127.255.255.255) and " \
  41                     "(ip.SrcAddr < 10.0.0.0 or ip.SrcAddr > 10.255.255.255) and " \
  42                     "(ip.SrcAddr < 192.168.0.0 or ip.SrcAddr > 192.168.255.255) and " \
  43                     "(ip.SrcAddr < 172.16.0.0 or ip.SrcAddr > 172.31.255.255) and " \
  44                     "(ip.SrcAddr < 169.254.0.0 or ip.SrcAddr > 169.254.255.255)" \
  45                     ")"
  46  
  47  #define DIVERT_NO_LOCALNETSv6_DST "(" \
  48                     "(ipv6.DstAddr > ::1) and " \
  49                     "(ipv6.DstAddr < 2001::0 or ipv6.DstAddr > 2001:1::0) and " \
  50                     "(ipv6.DstAddr < fc00::0 or ipv6.DstAddr > fe00::0) and " \
  51                     "(ipv6.DstAddr < fe80::0 or ipv6.DstAddr > fec0::0) and " \
  52                     "(ipv6.DstAddr < ff00::0 or ipv6.DstAddr > ffff::0)" \
  53                     ")"
  54  #define DIVERT_NO_LOCALNETSv6_SRC "(" \
  55                     "(ipv6.SrcAddr > ::1) and " \
  56                     "(ipv6.SrcAddr < 2001::0 or ipv6.SrcAddr > 2001:1::0) and " \
  57                     "(ipv6.SrcAddr < fc00::0 or ipv6.SrcAddr > fe00::0) and " \
  58                     "(ipv6.SrcAddr < fe80::0 or ipv6.SrcAddr > fec0::0) and " \
  59                     "(ipv6.SrcAddr < ff00::0 or ipv6.SrcAddr > ffff::0)" \
  60                     ")"
  61  
  62  /* #IPID# is a template to find&replace */
  63  #define IPID_TEMPLATE "#IPID#"
  64  #define MAXPAYLOADSIZE_TEMPLATE "#MAXPAYLOADSIZE#"
  65  #define FILTER_STRING_TEMPLATE \
  66          "(tcp and !impostor and !loopback " MAXPAYLOADSIZE_TEMPLATE " and " \
  67          "((inbound and (" \
  68           "(" \
  69            "(" \
  70             "(ipv6 or (ip.Id >= 0x0 and ip.Id <= 0xF) " IPID_TEMPLATE \
  71             ") and " \
  72             "tcp.SrcPort == 80 and tcp.Ack" \
  73            ") or " \
  74            "((tcp.SrcPort == 80 or tcp.SrcPort == 443) and tcp.Ack and tcp.Syn)" \
  75           ")" \
  76           " and (" DIVERT_NO_LOCALNETSv4_SRC " or " DIVERT_NO_LOCALNETSv6_SRC "))) or " \
  77          "(outbound and " \
  78           "(tcp.DstPort == 80 or tcp.DstPort == 443) and tcp.Ack and " \
  79           "(" DIVERT_NO_LOCALNETSv4_DST " or " DIVERT_NO_LOCALNETSv6_DST "))" \
  80          "))"
  81  #define FILTER_PASSIVE_STRING_TEMPLATE "inbound and ip and tcp and " \
  82          "!impostor and !loopback and " \
  83          "((ip.Id <= 0xF and ip.Id >= 0x0) " IPID_TEMPLATE ") and " \
  84          "(tcp.SrcPort == 443 or tcp.SrcPort == 80) and tcp.Rst and " \
  85          DIVERT_NO_LOCALNETSv4_SRC
  86  
  87  #define SET_HTTP_FRAGMENT_SIZE_OPTION(fragment_size) do { \
  88      if (!http_fragment_size) { \
  89          http_fragment_size = (unsigned int)fragment_size; \
  90      } \
  91      else if (http_fragment_size != (unsigned int)fragment_size) { \
  92          printf( \
  93              "WARNING: HTTP fragment size is already set to %u, not changing.\n", \
  94              http_fragment_size \
  95          ); \
  96      } \
  97  } while (0)
  98  
  99  #define TCP_HANDLE_OUTGOING_TTL_PARSE_PACKET_IF() \
 100      if ((packet_v4 && tcp_handle_outgoing(&ppIpHdr->SrcAddr, &ppIpHdr->DstAddr, \
 101                          ppTcpHdr->SrcPort, ppTcpHdr->DstPort, \
 102                          &tcp_conn_info, 0)) \
 103          || \
 104          (packet_v6 && tcp_handle_outgoing(ppIpV6Hdr->SrcAddr, ppIpV6Hdr->DstAddr, \
 105                          ppTcpHdr->SrcPort, ppTcpHdr->DstPort, \
 106                          &tcp_conn_info, 1)))
 107  
 108  #define TCP_HANDLE_OUTGOING_FAKE_PACKET(func) do { \
 109      should_send_fake = 1; \
 110      if (do_auto_ttl || ttl_min_nhops) { \
 111          TCP_HANDLE_OUTGOING_TTL_PARSE_PACKET_IF() { \
 112              if (do_auto_ttl) { \
 113                  /* If Auto TTL mode */ \
 114                  ttl_of_fake_packet = tcp_get_auto_ttl(tcp_conn_info.ttl, auto_ttl_1, auto_ttl_2, \
 115                                                        ttl_min_nhops, auto_ttl_max); \
 116                  if (do_tcp_verb) { \
 117                      printf("Connection TTL = %d, Fake TTL = %d\n", tcp_conn_info.ttl, ttl_of_fake_packet); \
 118                  } \
 119              } \
 120              else if (ttl_min_nhops) { \
 121                  /* If not Auto TTL mode but --min-ttl is set */ \
 122                  if (!tcp_get_auto_ttl(tcp_conn_info.ttl, 0, 0, ttl_min_nhops, 0)) { \
 123                      /* Send only if nhops >= min_ttl */ \
 124                      should_send_fake = 0; \
 125                  } \
 126              } \
 127          } \
 128      } \
 129      if (should_send_fake) \
 130          func(w_filter, &addr, packet, packetLen, packet_v6, \
 131               ttl_of_fake_packet, do_wrong_chksum, do_wrong_seq); \
 132  } while (0)
 133  
 134  
 135  static int running_from_service = 0;
 136  static int exiting = 0;
 137  static HANDLE filters[MAX_FILTERS];
 138  static int filter_num = 0;
 139  static const char http10_redirect_302[] = "HTTP/1.0 302 ";
 140  static const char http11_redirect_302[] = "HTTP/1.1 302 ";
 141  static const char http_host_find[] = "\r\nHost: ";
 142  static const char http_host_replace[] = "\r\nhoSt: ";
 143  static const char http_useragent_find[] = "\r\nUser-Agent: ";
 144  static const char location_http[] = "\r\nLocation: http://";
 145  static const char connection_close[] = "\r\nConnection: close";
 146  static const char *http_methods[] = {
 147      "GET ",
 148      "HEAD ",
 149      "POST ",
 150      "PUT ",
 151      "DELETE ",
 152      "CONNECT ",
 153      "OPTIONS ",
 154  };
 155  
 156  static struct option long_options[] = {
 157      {"port",        required_argument, 0,  'z' },
 158      {"dns-addr",    required_argument, 0,  'd' },
 159      {"dns-port",    required_argument, 0,  'g' },
 160      {"dnsv6-addr",  required_argument, 0,  '!' },
 161      {"dnsv6-port",  required_argument, 0,  '@' },
 162      {"dns-verb",    no_argument,       0,  'v' },
 163      {"blacklist",   required_argument, 0,  'b' },
 164      {"allow-no-sni",no_argument,       0,  ']' },
 165      {"frag-by-sni", no_argument,       0,  '>' },
 166      {"ip-id",       required_argument, 0,  'i' },
 167      {"set-ttl",     required_argument, 0,  '$' },
 168      {"min-ttl",     required_argument, 0,  '[' },
 169      {"auto-ttl",    optional_argument, 0,  '+' },
 170      {"wrong-chksum",no_argument,       0,  '%' },
 171      {"wrong-seq",   no_argument,       0,  ')' },
 172      {"native-frag", no_argument,       0,  '*' },
 173      {"reverse-frag",no_argument,       0,  '(' },
 174      {"max-payload", optional_argument, 0,  '|' },
 175      {0,             0,                 0,   0  }
 176  };
 177  
 178  static char *filter_string = NULL;
 179  static char *filter_passive_string = NULL;
 180  
 181  static void add_filter_str(int proto, int port) {
 182      const char *udp = " or (udp and !impostor and !loopback and " \
 183                        "(udp.SrcPort == %d or udp.DstPort == %d))";
 184      const char *tcp = " or (tcp and !impostor and !loopback " MAXPAYLOADSIZE_TEMPLATE " and " \
 185                        "(tcp.SrcPort == %d or tcp.DstPort == %d))";
 186  
 187      char *current_filter = filter_string;
 188      size_t new_filter_size = strlen(current_filter) +
 189              (proto == IPPROTO_UDP ? strlen(udp) : strlen(tcp)) + 16;
 190      char *new_filter = malloc(new_filter_size);
 191  
 192      strcpy(new_filter, current_filter);
 193      if (proto == IPPROTO_UDP)
 194          sprintf(new_filter + strlen(new_filter), udp, port, port);
 195      else
 196          sprintf(new_filter + strlen(new_filter), tcp, port, port);
 197  
 198      filter_string = new_filter;
 199      free(current_filter);
 200  }
 201  
 202  static void add_ip_id_str(int id) {
 203      char *newstr;
 204      const char *ipid = " or ip.Id == %d";
 205      char *addfilter = malloc(strlen(ipid) + 16);
 206  
 207      sprintf(addfilter, ipid, id);
 208  
 209      newstr = repl_str(filter_string, IPID_TEMPLATE, addfilter);
 210      free(filter_string);
 211      filter_string = newstr;
 212  
 213      newstr = repl_str(filter_passive_string, IPID_TEMPLATE, addfilter);
 214      free(filter_passive_string);
 215      filter_passive_string = newstr;
 216  }
 217  
 218  static void add_maxpayloadsize_str(unsigned short maxpayload) {
 219      char *newstr;
 220      /* 0x47455420 is "GET ", 0x504F5354 is "POST", big endian. */
 221      const char *maxpayloadsize_str = "and (tcp.PayloadLength ? tcp.PayloadLength < %hu or tcp.Payload32[0] == 0x47455420 or tcp.Payload32[0] == 0x504F5354 : true)";
 222      char *addfilter = malloc(strlen(maxpayloadsize_str) + 16);
 223  
 224      sprintf(addfilter, maxpayloadsize_str, maxpayload);
 225  
 226      newstr = repl_str(filter_string, MAXPAYLOADSIZE_TEMPLATE, addfilter);
 227      free(filter_string);
 228      filter_string = newstr;
 229  }
 230  
 231  static void finalize_filter_strings() {
 232      char *newstr, *newstr2;
 233  
 234      newstr2 = repl_str(filter_string, IPID_TEMPLATE, "");
 235      newstr = repl_str(newstr2, MAXPAYLOADSIZE_TEMPLATE, "");
 236      free(filter_string);
 237      free(newstr2);
 238      filter_string = newstr;
 239  
 240      newstr = repl_str(filter_passive_string, IPID_TEMPLATE, "");
 241      free(filter_passive_string);
 242      filter_passive_string = newstr;
 243  }
 244  
 245  static char* dumb_memmem(const char* haystack, unsigned int hlen,
 246                           const char* needle, unsigned int nlen)
 247  {
 248      // naive implementation
 249      if (nlen > hlen) return NULL;
 250      size_t i;
 251      for (i=0; i<hlen-nlen+1; i++) {
 252          if (memcmp(haystack+i,needle,nlen)==0) {
 253              return (char*)(haystack+i);
 254          }
 255      }
 256      return NULL;
 257  }
 258  
 259  unsigned short int atousi(const char *str, const char *msg) {
 260      long unsigned int res = strtoul(str, NULL, 10u);
 261      enum {
 262          limitValue=0xFFFFu
 263      };
 264  
 265      if(res > limitValue) {
 266          puts(msg);
 267          exit(EXIT_FAILURE);
 268      }
 269      return (unsigned short int)res;
 270  }
 271  
 272  BYTE atoub(const char *str, const char *msg) {
 273      long unsigned int res = strtoul(str, NULL, 10u);
 274      enum {
 275          limitValue=0xFFu
 276      };
 277  
 278      if(res > limitValue) {
 279          puts(msg);
 280          exit(EXIT_FAILURE);
 281      }
 282      return (BYTE)res;
 283  }
 284  
 285  
 286  static HANDLE init(char *filter, UINT64 flags) {
 287      LPTSTR errormessage = NULL;
 288      DWORD errorcode = 0;
 289      filter = WinDivertOpen(filter, WINDIVERT_LAYER_NETWORK, 0, flags);
 290      if (filter != INVALID_HANDLE_VALUE)
 291          return filter;
 292      errorcode = GetLastError();
 293      FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
 294                    FORMAT_MESSAGE_IGNORE_INSERTS,
 295                    NULL, errorcode, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
 296                    (LPTSTR)&errormessage, 0, NULL);
 297      printf("Error opening filter: %d %s\n", errorcode, errormessage);
 298      LocalFree(errormessage);
 299      if (errorcode == 2)
 300          printf("The driver files WinDivert32.sys or WinDivert64.sys were not found.\n");
 301      else if (errorcode == 654)
 302          printf("An incompatible version of the WinDivert driver is currently loaded.\n"
 303                 "Please unload it with the following commands ran as administrator:\n\n"
 304                 "sc stop windivert\n"
 305                 "sc delete windivert\n"
 306                 "sc stop windivert14"
 307                 "sc delete windivert14\n");
 308      else if (errorcode == 1275)
 309          printf("This error occurs for various reasons, including:\n"
 310                 "the WinDivert driver is blocked by security software; or\n"
 311                 "you are using a virtualization environment that does not support drivers.\n");
 312      else if (errorcode == 1753)
 313          printf("This error occurs when the Base Filtering Engine service has been disabled.\n"
 314                 "Enable Base Filtering Engine service.\n");
 315      else if (errorcode == 577)
 316          printf("Could not load driver due to invalid digital signature.\n"
 317                 "Windows Server 2016 systems must have secure boot disabled to be \n"
 318                 "able to load WinDivert driver.\n"
 319                 "Windows 7 systems must be up-to-date or at least have KB3033929 installed.\n"
 320                 "https://www.microsoft.com/en-us/download/details.aspx?id=46078\n\n"
 321                 "WARNING! If you see this error on Windows 7, it means your system is horribly "
 322                 "outdated and SHOULD NOT BE USED TO ACCESS THE INTERNET!\n"
 323                 "Most probably, you don't have security patches installed and anyone in you LAN or "
 324                 "public Wi-Fi network can get full access to your computer (MS17-010 and others).\n"
 325                 "You should install updates IMMEDIATELY.\n");
 326      return NULL;
 327  }
 328  
 329  static int deinit(HANDLE handle) {
 330      if (handle) {
 331          WinDivertShutdown(handle, WINDIVERT_SHUTDOWN_BOTH);
 332          WinDivertClose(handle);
 333          return TRUE;
 334      }
 335      return FALSE;
 336  }
 337  
 338  void deinit_all() {
 339      for (int i = 0; i < filter_num; i++) {
 340          deinit(filters[i]);
 341      }
 342  }
 343  
 344  static void sigint_handler(int sig __attribute__((unused))) {
 345      exiting = 1;
 346      deinit_all();
 347      exit(EXIT_SUCCESS);
 348  }
 349  
 350  static void mix_case(char *pktdata, unsigned int pktlen) {
 351      unsigned int i;
 352  
 353      if (pktlen <= 0) return;
 354      for (i = 0; i < pktlen; i++) {
 355          if (i % 2) {
 356              pktdata[i] = (char) toupper(pktdata[i]);
 357          }
 358      }
 359  }
 360  
 361  static int is_passivedpi_redirect(const char *pktdata, unsigned int pktlen) {
 362      /* First check if this is HTTP 302 redirect */
 363      if (memcmp(pktdata, http11_redirect_302, sizeof(http11_redirect_302)-1) == 0 ||
 364          memcmp(pktdata, http10_redirect_302, sizeof(http10_redirect_302)-1) == 0)
 365      {
 366          /* Then check if this is a redirect to new http site with Connection: close */
 367          if (dumb_memmem(pktdata, pktlen, location_http, sizeof(location_http)-1) &&
 368              dumb_memmem(pktdata, pktlen, connection_close, sizeof(connection_close)-1)) {
 369              return TRUE;
 370          }
 371      }
 372      return FALSE;
 373  }
 374  
 375  static int find_header_and_get_info(const char *pktdata, unsigned int pktlen,
 376                  const char *hdrname,
 377                  char **hdrnameaddr,
 378                  char **hdrvalueaddr, unsigned int *hdrvaluelen) {
 379      char *data_addr_rn;
 380      char *hdr_begin;
 381  
 382      *hdrvaluelen = 0u;
 383      *hdrnameaddr = NULL;
 384      *hdrvalueaddr = NULL;
 385  
 386      /* Search for the header */
 387      hdr_begin = dumb_memmem(pktdata, pktlen,
 388                  hdrname, strlen(hdrname));
 389      if (!hdr_begin) return FALSE;
 390      if (pktdata > hdr_begin) return FALSE;
 391  
 392      /* Set header address */
 393      *hdrnameaddr = hdr_begin;
 394      *hdrvalueaddr = hdr_begin + strlen(hdrname);
 395  
 396      /* Search for header end (\r\n) */
 397      data_addr_rn = dumb_memmem(*hdrvalueaddr,
 398                          pktlen - (uintptr_t)(*hdrvalueaddr - pktdata),
 399                          "\r\n", 2);
 400      if (data_addr_rn) {
 401          *hdrvaluelen = (uintptr_t)(data_addr_rn - *hdrvalueaddr);
 402          if (*hdrvaluelen >= 3 && *hdrvaluelen <= HOST_MAXLEN)
 403              return TRUE;
 404      }
 405      return FALSE;
 406  }
 407  
 408  /**
 409   * Very crude Server Name Indication (TLS ClientHello hostname) extractor.
 410   */
 411  static int extract_sni(const char *pktdata, unsigned int pktlen,
 412                      char **hostnameaddr, unsigned int *hostnamelen) {
 413      unsigned int ptr = 0;
 414      unsigned const char *d = (unsigned const char *)pktdata;
 415      unsigned const char *hnaddr = 0;
 416      int hnlen = 0;
 417  
 418      while (ptr + 8 < pktlen) {
 419          /* Search for specific Extensions sequence */
 420          if (d[ptr] == '\0' && d[ptr+1] == '\0' && d[ptr+2] == '\0' &&
 421              d[ptr+4] == '\0' && d[ptr+6] == '\0' && d[ptr+7] == '\0' &&
 422              /* Check Extension length, Server Name list length
 423              *  and Server Name length relations
 424              */
 425              d[ptr+3] - d[ptr+5] == 2 && d[ptr+5] - d[ptr+8] == 3)
 426              {
 427                  if (ptr + 8 + d[ptr+8] > pktlen) {
 428                      return FALSE;
 429                  }
 430                  hnaddr = &d[ptr+9];
 431                  hnlen = d[ptr+8];
 432                  /* Limit hostname size up to 253 bytes */
 433                  if (hnlen < 3 || hnlen > HOST_MAXLEN) {
 434                      return FALSE;
 435                  }
 436                  /* Validate that hostname has only ascii lowercase characters */
 437                  for (int i=0; i<hnlen; i++) {
 438                      if (!( (hnaddr[i] >= '0' && hnaddr[i] <= '9') ||
 439                           (hnaddr[i] >= 'a' && hnaddr[i] <= 'z') ||
 440                           hnaddr[i] == '.' || hnaddr[i] == '-'))
 441                      {
 442                          return FALSE;
 443                      }
 444                  }
 445                  *hostnameaddr = (char*)hnaddr;
 446                  *hostnamelen = (unsigned int)hnlen;
 447                  return TRUE;
 448              }
 449          ptr++;
 450      }
 451      return FALSE;
 452  }
 453  
 454  static inline void change_window_size(const PWINDIVERT_TCPHDR ppTcpHdr, unsigned int size) {
 455      if (size >= 1 && size <= 0xFFFFu) {
 456          ppTcpHdr->Window = htons((u_short)size);
 457      }
 458  }
 459  
 460  /* HTTP method end without trailing space */
 461  static PVOID find_http_method_end(const char *pkt, unsigned int http_frag, int *is_fragmented) {
 462      unsigned int i;
 463      for (i = 0; i<(sizeof(http_methods) / sizeof(*http_methods)); i++) {
 464          if (memcmp(pkt, http_methods[i], strlen(http_methods[i])) == 0) {
 465              if (is_fragmented)
 466                  *is_fragmented = 0;
 467              return (char*)pkt + strlen(http_methods[i]) - 1;
 468          }
 469          /* Try to find HTTP method in a second part of fragmented packet */
 470          if ((http_frag == 1 || http_frag == 2) &&
 471              memcmp(pkt, http_methods[i] + http_frag,
 472                     strlen(http_methods[i]) - http_frag) == 0
 473             )
 474          {
 475              if (is_fragmented)
 476                  *is_fragmented = 1;
 477              return (char*)pkt + strlen(http_methods[i]) - http_frag - 1;
 478          }
 479      }
 480      return NULL;
 481  }
 482  
 483  /** Fragment and send the packet.
 484   *
 485   * This function cuts off the end of the packet (step=0) or
 486   * the beginning of the packet (step=1) with fragment_size bytes.
 487   */
 488  static void send_native_fragment(HANDLE w_filter, WINDIVERT_ADDRESS addr,
 489                          char *packet, UINT packetLen, PVOID packet_data,
 490                          UINT packet_dataLen, int packet_v4, int packet_v6,
 491                          PWINDIVERT_IPHDR ppIpHdr, PWINDIVERT_IPV6HDR ppIpV6Hdr,
 492                          PWINDIVERT_TCPHDR ppTcpHdr,
 493                          unsigned int fragment_size, int step) {
 494      char packet_bak[MAX_PACKET_SIZE];
 495      memcpy(packet_bak, packet, packetLen);
 496      UINT orig_packetLen = packetLen;
 497  
 498      if (fragment_size >= packet_dataLen) {
 499          if (step == 1)
 500              fragment_size = 0;
 501          else
 502              return;
 503      }
 504  
 505      if (step == 0) {
 506          if (packet_v4)
 507              ppIpHdr->Length = htons(
 508                  ntohs(ppIpHdr->Length) -
 509                  packet_dataLen + fragment_size
 510              );
 511          else if (packet_v6)
 512              ppIpV6Hdr->Length = htons(
 513                  ntohs(ppIpV6Hdr->Length) -
 514                  packet_dataLen + fragment_size
 515              );
 516          //printf("step0 (%d:%d), pp:%d, was:%d, now:%d\n",
 517          //                packet_v4, packet_v6, ntohs(ppIpHdr->Length),
 518          //                packetLen, packetLen - packet_dataLen + fragment_size);
 519          packetLen = packetLen - packet_dataLen + fragment_size;
 520      }
 521  
 522      else if (step == 1) {
 523          if (packet_v4)
 524              ppIpHdr->Length = htons(
 525                  ntohs(ppIpHdr->Length) - fragment_size
 526              );
 527          else if (packet_v6)
 528              ppIpV6Hdr->Length = htons(
 529                  ntohs(ppIpV6Hdr->Length) - fragment_size
 530              );
 531          //printf("step1 (%d:%d), pp:%d, was:%d, now:%d\n", packet_v4, packet_v6, ntohs(ppIpHdr->Length),
 532          //                packetLen, packetLen - fragment_size);
 533          memmove(packet_data,
 534                  (char*)packet_data + fragment_size,
 535                  packet_dataLen - fragment_size);
 536          packetLen -= fragment_size;
 537  
 538          ppTcpHdr->SeqNum = htonl(ntohl(ppTcpHdr->SeqNum) + fragment_size);
 539      }
 540  
 541      addr.IPChecksum = 0;
 542      addr.TCPChecksum = 0;
 543  
 544      WinDivertHelperCalcChecksums(
 545          packet, packetLen, &addr, 0
 546      );
 547      WinDivertSend(
 548          w_filter, packet,
 549          packetLen,
 550          NULL, &addr
 551      );
 552      memcpy(packet, packet_bak, orig_packetLen);
 553      //printf("Sent native fragment of %d size (step%d)\n", packetLen, step);
 554  }
 555  
 556  int main(int argc, char *argv[]) {
 557      static enum packet_type_e {
 558          unknown,
 559          ipv4_tcp, ipv4_tcp_data, ipv4_udp_data,
 560          ipv6_tcp, ipv6_tcp_data, ipv6_udp_data
 561      } packet_type;
 562      int i, should_reinject, should_recalc_checksum = 0;
 563      int sni_ok = 0;
 564      int opt;
 565      int packet_v4, packet_v6;
 566      HANDLE w_filter = NULL;
 567      WINDIVERT_ADDRESS addr;
 568      char packet[MAX_PACKET_SIZE];
 569      PVOID packet_data;
 570      UINT packetLen;
 571      UINT packet_dataLen;
 572      PWINDIVERT_IPHDR ppIpHdr;
 573      PWINDIVERT_IPV6HDR ppIpV6Hdr;
 574      PWINDIVERT_TCPHDR ppTcpHdr;
 575      PWINDIVERT_UDPHDR ppUdpHdr;
 576      conntrack_info_t dns_conn_info;
 577      tcp_conntrack_info_t tcp_conn_info;
 578  
 579      int do_passivedpi = 0, do_fragment_http = 0,
 580          do_fragment_http_persistent = 0,
 581          do_fragment_http_persistent_nowait = 0,
 582          do_fragment_https = 0, do_host = 0,
 583          do_host_removespace = 0, do_additional_space = 0,
 584          do_http_allports = 0,
 585          do_host_mixedcase = 0,
 586          do_dnsv4_redirect = 0, do_dnsv6_redirect = 0,
 587          do_dns_verb = 0, do_tcp_verb = 0, do_blacklist = 0,
 588          do_allow_no_sni = 0,
 589          do_fragment_by_sni = 0,
 590          do_fake_packet = 0,
 591          do_auto_ttl = 0,
 592          do_wrong_chksum = 0,
 593          do_wrong_seq = 0,
 594          do_native_frag = 0, do_reverse_frag = 0;
 595      unsigned int http_fragment_size = 0;
 596      unsigned int https_fragment_size = 0;
 597      unsigned int current_fragment_size = 0;
 598      unsigned short max_payload_size = 0;
 599      BYTE should_send_fake = 0;
 600      BYTE ttl_of_fake_packet = 0;
 601      BYTE ttl_min_nhops = 0;
 602      BYTE auto_ttl_1 = 0;
 603      BYTE auto_ttl_2 = 0;
 604      BYTE auto_ttl_max = 0;
 605      uint32_t dnsv4_addr = 0;
 606      struct in6_addr dnsv6_addr = {0};
 607      struct in6_addr dns_temp_addr = {0};
 608      uint16_t dnsv4_port = htons(53);
 609      uint16_t dnsv6_port = htons(53);
 610      char *host_addr, *useragent_addr, *method_addr;
 611      unsigned int host_len, useragent_len;
 612      int http_req_fragmented;
 613  
 614      char *hdr_name_addr = NULL, *hdr_value_addr = NULL;
 615      unsigned int hdr_value_len;
 616  
 617      // Make sure to search DLLs only in safe path, not in current working dir.
 618      SetDllDirectory("");
 619      SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT);
 620  
 621      if (!running_from_service) {
 622          running_from_service = 1;
 623          if (service_register(argc, argv)) {
 624              /* We've been called as a service. Register service
 625               * and exit this thread. main() would be called from
 626               * service.c next time.
 627               *
 628               * Note that if service_register() succeedes it does
 629               * not return until the service is stopped.
 630               * That is why we should set running_from_service
 631               * before calling service_register and unset it
 632               * afterwards.
 633               */
 634              return 0;
 635          }
 636          running_from_service = 0;
 637      }
 638  
 639      if (filter_string == NULL)
 640          filter_string = strdup(FILTER_STRING_TEMPLATE);
 641      if (filter_passive_string == NULL)
 642          filter_passive_string = strdup(FILTER_PASSIVE_STRING_TEMPLATE);
 643  
 644      printf(
 645          "GoodbyeDPI " GOODBYEDPI_VERSION
 646          ": Passive DPI blocker and Active DPI circumvention utility\n"
 647          "https://github.com/ValdikSS/GoodbyeDPI\n\n"
 648      );
 649  
 650      if (argc == 1) {
 651          /* enable mode -5 by default */
 652          do_fragment_http = do_fragment_https = 1;
 653          do_reverse_frag = do_native_frag = 1;
 654          http_fragment_size = https_fragment_size = 2;
 655          do_fragment_http_persistent = do_fragment_http_persistent_nowait = 1;
 656          do_fake_packet = 1;
 657          do_auto_ttl = 1;
 658          max_payload_size = 1200;
 659      }
 660  
 661      while ((opt = getopt_long(argc, argv, "123456prsaf:e:mwk:n", long_options, NULL)) != -1) {
 662          switch (opt) {
 663              case '1':
 664                  do_passivedpi = do_host = do_host_removespace \
 665                  = do_fragment_http = do_fragment_https \
 666                  = do_fragment_http_persistent \
 667                  = do_fragment_http_persistent_nowait = 1;
 668                  break;
 669              case '2':
 670                  do_passivedpi = do_host = do_host_removespace \
 671                  = do_fragment_http = do_fragment_https \
 672                  = do_fragment_http_persistent \
 673                  = do_fragment_http_persistent_nowait = 1;
 674                  https_fragment_size = 40u;
 675                  break;
 676              case '3':
 677                  do_passivedpi = do_host = do_host_removespace \
 678                  = do_fragment_https = 1;
 679                  https_fragment_size = 40u;
 680                  break;
 681              case '4':
 682                  do_passivedpi = do_host = do_host_removespace = 1;
 683                  break;
 684              case '5':
 685                  do_fragment_http = do_fragment_https = 1;
 686                  do_reverse_frag = do_native_frag = 1;
 687                  http_fragment_size = https_fragment_size = 2;
 688                  do_fragment_http_persistent = do_fragment_http_persistent_nowait = 1;
 689                  do_fake_packet = 1;
 690                  do_auto_ttl = 1;
 691                  max_payload_size = 1200;
 692                  break;
 693              case '6':
 694                  do_fragment_http = do_fragment_https = 1;
 695                  do_reverse_frag = do_native_frag = 1;
 696                  http_fragment_size = https_fragment_size = 2;
 697                  do_fragment_http_persistent = do_fragment_http_persistent_nowait = 1;
 698                  do_fake_packet = 1;
 699                  do_wrong_seq = 1;
 700                  max_payload_size = 1200;
 701                  break;
 702              case 'p':
 703                  do_passivedpi = 1;
 704                  break;
 705              case 'r':
 706                  do_host = 1;
 707                  break;
 708              case 's':
 709                  do_host_removespace = 1;
 710                  break;
 711              case 'a':
 712                  do_additional_space = 1;
 713                  do_host_removespace = 1;
 714                  break;
 715              case 'm':
 716                  do_host_mixedcase = 1;
 717                  break;
 718              case 'f':
 719                  do_fragment_http = 1;
 720                  SET_HTTP_FRAGMENT_SIZE_OPTION(atousi(optarg, "Fragment size should be in range [0 - 0xFFFF]\n"));
 721                  break;
 722              case 'k':
 723                  do_fragment_http_persistent = 1;
 724                  do_native_frag = 1;
 725                  SET_HTTP_FRAGMENT_SIZE_OPTION(atousi(optarg, "Fragment size should be in range [0 - 0xFFFF]\n"));
 726                  break;
 727              case 'n':
 728                  do_fragment_http_persistent = 1;
 729                  do_fragment_http_persistent_nowait = 1;
 730                  do_native_frag = 1;
 731                  break;
 732              case 'e':
 733                  do_fragment_https = 1;
 734                  https_fragment_size = atousi(optarg, "Fragment size should be in range [0 - 65535]\n");
 735                  break;
 736              case 'w':
 737                  do_http_allports = 1;
 738                  break;
 739              case 'z': // --port
 740                  /* i is used as a temporary variable here */
 741                  i = atoi(optarg);
 742                  if (i <= 0 || i > 65535) {
 743                      printf("Port parameter error!\n");
 744                      exit(EXIT_FAILURE);
 745                  }
 746                  if (i != 80 && i != 443)
 747                      add_filter_str(IPPROTO_TCP, i);
 748                  i = 0;
 749                  break;
 750              case 'i': // --ip-id
 751                  /* i is used as a temporary variable here */
 752                  i = atousi(optarg, "IP ID parameter error!\n");
 753                  add_ip_id_str(i);
 754                  i = 0;
 755                  break;
 756              case 'd': // --dns-addr
 757                  if ((inet_pton(AF_INET, optarg, dns_temp_addr.s6_addr) == 1) &&
 758                      !do_dnsv4_redirect)
 759                  {
 760                      do_dnsv4_redirect = 1;
 761                      if (inet_pton(AF_INET, optarg, &dnsv4_addr) != 1) {
 762                          puts("DNS address parameter error!");
 763                          exit(EXIT_FAILURE);
 764                      }
 765                      add_filter_str(IPPROTO_UDP, 53);
 766                      flush_dns_cache();
 767                      break;
 768                  }
 769                  puts("DNS address parameter error!");
 770                  exit(EXIT_FAILURE);
 771                  break;
 772              case '!': // --dnsv6-addr
 773                  if ((inet_pton(AF_INET6, optarg, dns_temp_addr.s6_addr) == 1) &&
 774                      !do_dnsv6_redirect)
 775                  {
 776                      do_dnsv6_redirect = 1;
 777                      if (inet_pton(AF_INET6, optarg, dnsv6_addr.s6_addr) != 1) {
 778                          puts("DNS address parameter error!");
 779                          exit(EXIT_FAILURE);
 780                      }
 781                      add_filter_str(IPPROTO_UDP, 53);
 782                      flush_dns_cache();
 783                      break;
 784                  }
 785                  puts("DNS address parameter error!");
 786                  exit(EXIT_FAILURE);
 787                  break;
 788              case 'g': // --dns-port
 789                  if (!do_dnsv4_redirect) {
 790                      puts("--dns-port should be used with --dns-addr!\n"
 791                          "Make sure you use --dns-addr and pass it before "
 792                          "--dns-port");
 793                      exit(EXIT_FAILURE);
 794                  }
 795                  dnsv4_port = atousi(optarg, "DNS port parameter error!");
 796                  if (dnsv4_port != 53) {
 797                      add_filter_str(IPPROTO_UDP, dnsv4_port);
 798                  }
 799                  dnsv4_port = htons(dnsv4_port);
 800                  break;
 801              case '@': // --dnsv6-port
 802                  if (!do_dnsv6_redirect) {
 803                      puts("--dnsv6-port should be used with --dnsv6-addr!\n"
 804                          "Make sure you use --dnsv6-addr and pass it before "
 805                          "--dnsv6-port");
 806                      exit(EXIT_FAILURE);
 807                  }
 808                  dnsv6_port = atousi(optarg, "DNS port parameter error!");
 809                  if (dnsv6_port != 53) {
 810                      add_filter_str(IPPROTO_UDP, dnsv6_port);
 811                  }
 812                  dnsv6_port = htons(dnsv6_port);
 813                  break;
 814              case 'v':
 815                  do_dns_verb = 1;
 816                  do_tcp_verb = 1;
 817                  break;
 818              case 'b': // --blacklist
 819                  do_blacklist = 1;
 820                  if (!blackwhitelist_load_list(optarg)) {
 821                      printf("Can't load blacklist from file!\n");
 822                      exit(EXIT_FAILURE);
 823                  }
 824                  break;
 825              case ']': // --allow-no-sni
 826                  do_allow_no_sni = 1;
 827                  break;
 828              case '>': // --frag-by-sni
 829                  do_fragment_by_sni = 1;
 830                  break;
 831              case '$': // --set-ttl
 832                  do_auto_ttl = auto_ttl_1 = auto_ttl_2 = auto_ttl_max = 0;
 833                  do_fake_packet = 1;
 834                  ttl_of_fake_packet = atoub(optarg, "Set TTL parameter error!");
 835                  break;
 836              case '[': // --min-ttl
 837                  do_fake_packet = 1;
 838                  ttl_min_nhops = atoub(optarg, "Set Minimum TTL number of hops parameter error!");
 839                  break;
 840              case '+': // --auto-ttl
 841                  do_fake_packet = 1;
 842                  do_auto_ttl = 1;
 843  
 844                  if (!optarg && argv[optind] && argv[optind][0] != '-')
 845                      optarg = argv[optind];
 846  
 847                  if (optarg) {
 848                      char *autottl_copy = strdup(optarg);
 849                      if (strchr(autottl_copy, '-')) {
 850                          // token "-" found, start X-Y parser
 851                          char *autottl_current = strtok(autottl_copy, "-");
 852                          auto_ttl_1 = atoub(autottl_current, "Set Auto TTL parameter error!");
 853                          autottl_current = strtok(NULL, "-");
 854                          if (!autottl_current) {
 855                              puts("Set Auto TTL parameter error!");
 856                              exit(EXIT_FAILURE);
 857                          }
 858                          auto_ttl_2 = atoub(autottl_current, "Set Auto TTL parameter error!");
 859                          autottl_current = strtok(NULL, "-");
 860                          if (!autottl_current) {
 861                              puts("Set Auto TTL parameter error!");
 862                              exit(EXIT_FAILURE);
 863                          }
 864                          auto_ttl_max = atoub(autottl_current, "Set Auto TTL parameter error!");
 865                      }
 866                      else {
 867                          // single digit parser
 868                          auto_ttl_2 = atoub(optarg, "Set Auto TTL parameter error!");
 869                          auto_ttl_1 = auto_ttl_2;
 870                      }
 871                      free(autottl_copy);
 872                  }
 873                  break;
 874              case '%': // --wrong-chksum
 875                  do_fake_packet = 1;
 876                  do_wrong_chksum = 1;
 877                  break;
 878              case ')': // --wrong-seq
 879                  do_fake_packet = 1;
 880                  do_wrong_seq = 1;
 881                  break;
 882              case '*': // --native-frag
 883                  do_native_frag = 1;
 884                  do_fragment_http_persistent = 1;
 885                  do_fragment_http_persistent_nowait = 1;
 886                  break;
 887              case '(': // --reverse-frag
 888                  do_reverse_frag = 1;
 889                  do_native_frag = 1;
 890                  do_fragment_http_persistent = 1;
 891                  do_fragment_http_persistent_nowait = 1;
 892                  break;
 893              case '|': // --max-payload
 894                  if (!optarg && argv[optind] && argv[optind][0] != '-')
 895                      optarg = argv[optind];
 896                  if (optarg)
 897                      max_payload_size = atousi(optarg, "Max payload size parameter error!");
 898                  else
 899                      max_payload_size = 1200;
 900                  break;
 901              default:
 902                  puts("Usage: goodbyedpi.exe [OPTION...]\n"
 903                  " -p          block passive DPI\n"
 904                  " -r          replace Host with hoSt\n"
 905                  " -s          remove space between host header and its value\n"
 906                  " -a          additional space between Method and Request-URI (enables -s, may break sites)\n"
 907                  " -m          mix Host header case (test.com -> tEsT.cOm)\n"
 908                  " -f <value>  set HTTP fragmentation to value\n"
 909                  " -k <value>  enable HTTP persistent (keep-alive) fragmentation and set it to value\n"
 910                  " -n          do not wait for first segment ACK when -k is enabled\n"
 911                  " -e <value>  set HTTPS fragmentation to value\n"
 912                  " -w          try to find and parse HTTP traffic on all processed ports (not only on port 80)\n"
 913                  " --port        <value>    additional TCP port to perform fragmentation on (and HTTP tricks with -w)\n"
 914                  " --ip-id       <value>    handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID).\n"
 915                  " --dns-addr    <value>    redirect UDPv4 DNS requests to the supplied IPv4 address (experimental)\n"
 916                  " --dns-port    <value>    redirect UDPv4 DNS requests to the supplied port (53 by default)\n"
 917                  " --dnsv6-addr  <value>    redirect UDPv6 DNS requests to the supplied IPv6 address (experimental)\n"
 918                  " --dnsv6-port  <value>    redirect UDPv6 DNS requests to the supplied port (53 by default)\n"
 919                  " --dns-verb               print verbose DNS redirection messages\n"
 920                  " --blacklist   <txtfile>  perform circumvention tricks only to host names and subdomains from\n"
 921                  "                          supplied text file (HTTP Host/TLS SNI).\n"
 922                  "                          This option can be supplied multiple times.\n"
 923                  " --allow-no-sni           perform circumvention if TLS SNI can't be detected with --blacklist enabled.\n"
 924                  " --frag-by-sni            if SNI is detected in TLS packet, fragment the packet right before SNI value.\n"
 925                  " --set-ttl     <value>    activate Fake Request Mode and send it with supplied TTL value.\n"
 926                  "                          DANGEROUS! May break websites in unexpected ways. Use with care (or --blacklist).\n"
 927                  " --auto-ttl    [a1-a2-m]  activate Fake Request Mode, automatically detect TTL and decrease\n"
 928                  "                          it based on a distance. If the distance is shorter than a2, TTL is decreased\n"
 929                  "                          by a2. If it's longer, (a1; a2) scale is used with the distance as a weight.\n"
 930                  "                          If the resulting TTL is more than m(ax), set it to m.\n"
 931                  "                          Default (if set): --auto-ttl 1-4-10. Also sets --min-ttl 3.\n"
 932                  "                          DANGEROUS! May break websites in unexpected ways. Use with care (or --blacklist).\n"
 933                  " --min-ttl     <value>    minimum TTL distance (128/64 - TTL) for which to send Fake Request\n"
 934                  "                          in --set-ttl and --auto-ttl modes.\n"
 935                  " --wrong-chksum           activate Fake Request Mode and send it with incorrect TCP checksum.\n"
 936                  "                          May not work in a VM or with some routers, but is safer than set-ttl.\n"
 937                  "                          Could be combined with --set-ttl\n"
 938                  " --wrong-seq              activate Fake Request Mode and send it with TCP SEQ/ACK in the past.\n"
 939                  " --native-frag            fragment (split) the packets by sending them in smaller packets, without\n"
 940                  "                          shrinking the Window Size. Works faster (does not slow down the connection)\n"
 941                  "                          and better.\n"
 942                  " --reverse-frag           fragment (split) the packets just as --native-frag, but send them in the\n"
 943                  "                          reversed order. Works with the websites which could not handle segmented\n"
 944                  "                          HTTPS TLS ClientHello (because they receive the TCP flow \"combined\").\n"
 945                  " --max-payload [value]    packets with TCP payload data more than [value] won't be processed.\n"
 946                  "                          Use this option to reduce CPU usage by skipping huge amount of data\n"
 947                  "                          (like file transfers) in already established sessions.\n"
 948                  "                          May skip some huge HTTP requests from being processed.\n"
 949                  "                          Default (if set): --max-payload 1200.\n"
 950                  "\n");
 951                  puts("LEGACY modesets:\n"
 952                  " -1          -p -r -s -f 2 -k 2 -n -e 2 (most compatible mode)\n"
 953                  " -2          -p -r -s -f 2 -k 2 -n -e 40 (better speed for HTTPS yet still compatible)\n"
 954                  " -3          -p -r -s -e 40 (better speed for HTTP and HTTPS)\n"
 955                  " -4          -p -r -s (best speed)"
 956                  "\n"
 957                  "Modern modesets (more stable, more compatible, faster):\n"
 958                  " -5          -f 2 -e 2 --auto-ttl --reverse-frag --max-payload (this is the default)\n"
 959                  " -6          -f 2 -e 2 --wrong-seq --reverse-frag --max-payload\n");
 960                  exit(EXIT_FAILURE);
 961          }
 962      }
 963  
 964      if (!http_fragment_size)
 965          http_fragment_size = 2;
 966      if (!https_fragment_size)
 967          https_fragment_size = 2;
 968      if (!auto_ttl_1)
 969          auto_ttl_1 = 1;
 970      if (!auto_ttl_2)
 971          auto_ttl_2 = 4;
 972      if (do_auto_ttl) {
 973          if (!ttl_min_nhops)
 974              ttl_min_nhops = 3;
 975          if (!auto_ttl_max)
 976              auto_ttl_max = 10;
 977      }
 978  
 979      printf("Block passive: %d\n"                    /* 1 */
 980             "Fragment HTTP: %u\n"                    /* 2 */
 981             "Fragment persistent HTTP: %u\n"         /* 3 */
 982             "Fragment HTTPS: %u\n"                   /* 4 */
 983             "Fragment by SNI: %u\n"                  /* 5 */
 984             "Native fragmentation (splitting): %d\n" /* 6 */
 985             "Fragments sending in reverse: %d\n"     /* 7 */
 986             "hoSt: %d\n"                             /* 8 */
 987             "Host no space: %d\n"                    /* 9 */
 988             "Additional space: %d\n"                 /* 10 */
 989             "Mix Host: %d\n"                         /* 11 */
 990             "HTTP AllPorts: %d\n"                    /* 12 */
 991             "HTTP Persistent Nowait: %d\n"           /* 13 */
 992             "DNS redirect: %d\n"                     /* 14 */
 993             "DNSv6 redirect: %d\n"                   /* 15 */
 994             "Allow missing SNI: %d\n"                /* 16 */
 995             "Fake requests, TTL: %s (fixed: %hu, auto: %hu-%hu-%hu, min distance: %hu)\n"  /* 17 */
 996             "Fake requests, wrong checksum: %d\n"    /* 18 */
 997             "Fake requests, wrong SEQ/ACK: %d\n"     /* 19 */
 998             "Max payload size: %hu\n",               /* 20 */
 999             do_passivedpi,                                         /* 1 */
1000             (do_fragment_http ? http_fragment_size : 0),           /* 2 */
1001             (do_fragment_http_persistent ? http_fragment_size : 0),/* 3 */
1002             (do_fragment_https ? https_fragment_size : 0),         /* 4 */
1003             do_fragment_by_sni,    /* 5 */
1004             do_native_frag,        /* 6 */
1005             do_reverse_frag,       /* 7 */
1006             do_host,               /* 8 */
1007             do_host_removespace,   /* 9 */
1008             do_additional_space,   /* 10 */
1009             do_host_mixedcase,     /* 11 */
1010             do_http_allports,      /* 12 */
1011             do_fragment_http_persistent_nowait, /* 13 */
1012             do_dnsv4_redirect,                  /* 14 */
1013             do_dnsv6_redirect,                  /* 15 */
1014             do_allow_no_sni,                    /* 16 */
1015             do_auto_ttl ? "auto" : (do_fake_packet ? "fixed" : "disabled"),  /* 17 */
1016                 ttl_of_fake_packet, do_auto_ttl ? auto_ttl_1 : 0, do_auto_ttl ? auto_ttl_2 : 0,
1017                 do_auto_ttl ? auto_ttl_max : 0, ttl_min_nhops,
1018             do_wrong_chksum, /* 18 */
1019             do_wrong_seq,    /* 19 */
1020             max_payload_size /* 20 */
1021            );
1022  
1023      if (do_fragment_http && http_fragment_size > 2 && !do_native_frag) {
1024          puts("\nWARNING: HTTP fragmentation values > 2 are not fully compatible "
1025               "with other options. Please use values <= 2 or disable HTTP fragmentation "
1026               "completely.");
1027      }
1028  
1029      if (do_native_frag && !(do_fragment_http || do_fragment_https)) {
1030          puts("\nERROR: Native fragmentation is enabled but fragment sizes are not set.\n"
1031               "Fragmentation has no effect.");
1032          die();
1033      }
1034  
1035      if (max_payload_size)
1036          add_maxpayloadsize_str(max_payload_size);
1037      finalize_filter_strings();
1038      puts("\nOpening filter");
1039      filter_num = 0;
1040  
1041      if (do_passivedpi) {
1042          /* IPv4 only filter for inbound RST packets with ID [0x0; 0xF] */
1043          filters[filter_num] = init(
1044              filter_passive_string,
1045              WINDIVERT_FLAG_DROP);
1046          if (filters[filter_num] == NULL)
1047              die();
1048          filter_num++;
1049      }
1050  
1051      /* 
1052       * IPv4 & IPv6 filter for inbound HTTP redirection packets and
1053       * active DPI circumvention
1054       */
1055      filters[filter_num] = init(filter_string, 0);
1056  
1057      w_filter = filters[filter_num];
1058      filter_num++;
1059  
1060      for (i = 0; i < filter_num; i++) {
1061          if (filters[i] == NULL)
1062              die();
1063      }
1064  
1065      printf("Filter activated, GoodbyeDPI is now running!\n");
1066      signal(SIGINT, sigint_handler);
1067  
1068      while (1) {
1069          if (WinDivertRecv(w_filter, packet, sizeof(packet), &packetLen, &addr)) {
1070              debug("Got %s packet, len=%d!\n", addr.Outbound ? "outbound" : "inbound",
1071                     packetLen);
1072              should_reinject = 1;
1073              should_recalc_checksum = 0;
1074              sni_ok = 0;
1075  
1076              ppIpHdr = (PWINDIVERT_IPHDR)NULL;
1077              ppIpV6Hdr = (PWINDIVERT_IPV6HDR)NULL;
1078              ppTcpHdr = (PWINDIVERT_TCPHDR)NULL;
1079              ppUdpHdr = (PWINDIVERT_UDPHDR)NULL;
1080              packet_v4 = packet_v6 = 0;
1081              packet_type = unknown;
1082  
1083              // Parse network packet and set it's type
1084              if (WinDivertHelperParsePacket(packet, packetLen, &ppIpHdr,
1085                  &ppIpV6Hdr, NULL, NULL, NULL, &ppTcpHdr, &ppUdpHdr, &packet_data, &packet_dataLen,
1086                  NULL, NULL))
1087              {
1088                  if (ppIpHdr) {
1089                      packet_v4 = 1;
1090                      if (ppTcpHdr) {
1091                          packet_type = ipv4_tcp;
1092                          if (packet_data) {
1093                              packet_type = ipv4_tcp_data;
1094                          }
1095                      }
1096                      else if (ppUdpHdr && packet_data) {
1097                          packet_type = ipv4_udp_data;
1098                      }
1099                  }
1100  
1101                  else if (ppIpV6Hdr) {
1102                      packet_v6 = 1;
1103                      if (ppTcpHdr) {
1104                          packet_type = ipv6_tcp;
1105                          if (packet_data) {
1106                              packet_type = ipv6_tcp_data;
1107                          }
1108                      }
1109                      else if (ppUdpHdr && packet_data) {
1110                          packet_type = ipv6_udp_data;
1111                      }
1112                  }
1113              }
1114  
1115              debug("packet_type: %d, packet_v4: %d, packet_v6: %d\n", packet_type, packet_v4, packet_v6);
1116  
1117              if (packet_type == ipv4_tcp_data || packet_type == ipv6_tcp_data) {
1118                  //printf("Got parsed packet, len=%d!\n", packet_dataLen);
1119                  /* Got a TCP packet WITH DATA */
1120  
1121                  /* Handle INBOUND packet with data and find HTTP REDIRECT in there */
1122                  if (!addr.Outbound && packet_dataLen > 16) {
1123                      /* If INBOUND packet with DATA (tcp.Ack) */
1124  
1125                      /* Drop packets from filter with HTTP 30x Redirect */
1126                      if (do_passivedpi && is_passivedpi_redirect(packet_data, packet_dataLen)) {
1127                          if (packet_v4) {
1128                              //printf("Dropping HTTP Redirect packet!\n");
1129                              should_reinject = 0;
1130                          }
1131                          else if (packet_v6 && WINDIVERT_IPV6HDR_GET_FLOWLABEL(ppIpV6Hdr) == 0x0) {
1132                                  /* Contrary to IPv4 where we get only packets with IP ID 0x0-0xF,
1133                                   * for IPv6 we got all the incoming data packets since we can't
1134                                   * filter them in a driver.
1135                                   *
1136                                   * Handle only IPv6 Flow Label == 0x0 for now
1137                                   */
1138                                  //printf("Dropping HTTP Redirect packet!\n");
1139                                  should_reinject = 0;
1140                          }
1141                      }
1142                  }
1143                  /* Handle OUTBOUND packet on port 443, search for something that resembles
1144                   * TLS handshake, send fake request.
1145                   */
1146                  else if (addr.Outbound &&
1147                          ((do_fragment_https ? packet_dataLen == https_fragment_size : 0) ||
1148                           packet_dataLen > 16) &&
1149                           ppTcpHdr->DstPort != htons(80) &&
1150                           (do_fake_packet || do_native_frag)
1151                          )
1152                  {
1153                      /**
1154                       * In case of Window Size fragmentation=2, we'll receive only 2 byte packet.
1155                       * But if the packet is more than 2 bytes, check ClientHello byte.
1156                      */
1157                      if ((packet_dataLen == 2 && memcmp(packet_data, "\x16\x03", 2) == 0) ||
1158                          (packet_dataLen >= 3 && ( memcmp(packet_data, "\x16\x03\x01", 3) == 0 || memcmp(packet_data, "\x16\x03\x03", 3) == 0 )))
1159                      {
1160                          if (do_blacklist || do_fragment_by_sni) {
1161                              sni_ok = extract_sni(packet_data, packet_dataLen,
1162                                          &host_addr, &host_len);
1163                          }
1164                          if (
1165                               (do_blacklist && sni_ok &&
1166                                blackwhitelist_check_hostname(host_addr, host_len)
1167                               ) ||
1168                               (do_blacklist && !sni_ok && do_allow_no_sni) ||
1169                               (!do_blacklist)
1170                             )
1171                          {
1172  #ifdef DEBUG
1173                              char lsni[HOST_MAXLEN + 1] = {0};
1174                              extract_sni(packet_data, packet_dataLen,
1175                                          &host_addr, &host_len);
1176                              memcpy(lsni, host_addr, host_len);
1177                              printf("Blocked HTTPS website SNI: %s\n", lsni);
1178  #endif
1179                              if (do_fake_packet) {
1180                                  TCP_HANDLE_OUTGOING_FAKE_PACKET(send_fake_https_request);
1181                              }
1182                              if (do_native_frag) {
1183                                  // Signal for native fragmentation code handler
1184                                  should_recalc_checksum = 1;
1185                              }
1186                          }
1187                      }
1188                  }
1189                  /* Handle OUTBOUND packet on port 80, search for Host header */
1190                  else if (addr.Outbound && 
1191                          packet_dataLen > 16 &&
1192                          (do_http_allports ? 1 : (ppTcpHdr->DstPort == htons(80))) &&
1193                          find_http_method_end(packet_data,
1194                                               (do_fragment_http ? http_fragment_size : 0u),
1195                                               &http_req_fragmented) &&
1196                          (do_host || do_host_removespace ||
1197                          do_host_mixedcase || do_fragment_http_persistent ||
1198                          do_fake_packet))
1199                  {
1200  
1201                      /* Find Host header */
1202                      if (find_header_and_get_info(packet_data, packet_dataLen,
1203                          http_host_find, &hdr_name_addr, &hdr_value_addr, &hdr_value_len) &&
1204                          hdr_value_len > 0 && hdr_value_len <= HOST_MAXLEN &&
1205                          (do_blacklist ? blackwhitelist_check_hostname(hdr_value_addr, hdr_value_len) : 1))
1206                      {
1207                          host_addr = hdr_value_addr;
1208                          host_len = hdr_value_len;
1209  #ifdef DEBUG
1210                          char lhost[HOST_MAXLEN + 1] = {0};
1211                          memcpy(lhost, host_addr, host_len);
1212                          printf("Blocked HTTP website Host: %s\n", lhost);
1213  #endif
1214  
1215                          if (do_native_frag) {
1216                              // Signal for native fragmentation code handler
1217                              should_recalc_checksum = 1;
1218                          }
1219  
1220                          if (do_fake_packet) {
1221                              TCP_HANDLE_OUTGOING_FAKE_PACKET(send_fake_http_request);
1222                          }
1223  
1224                          if (do_host_mixedcase) {
1225                              mix_case(host_addr, host_len);
1226                              should_recalc_checksum = 1;
1227                          }
1228  
1229                          if (do_host) {
1230                              /* Replace "Host: " with "hoSt: " */
1231                              memcpy(hdr_name_addr, http_host_replace, strlen(http_host_replace));
1232                              should_recalc_checksum = 1;
1233                              //printf("Replaced Host header!\n");
1234                          }
1235  
1236                          /* If removing space between host header and its value
1237                           * and adding additional space between Method and Request-URI */
1238                          if (do_additional_space && do_host_removespace) {
1239                              /* End of "Host:" without trailing space */
1240                              method_addr = find_http_method_end(packet_data,
1241                                                              (do_fragment_http ? http_fragment_size : 0),
1242                                                              NULL);
1243  
1244                              if (method_addr) {
1245                                  memmove(method_addr + 1, method_addr,
1246                                          (size_t)(host_addr - method_addr - 1));
1247                                  should_recalc_checksum = 1;
1248                              }
1249                          }
1250                          /* If just removing space between host header and its value */
1251                          else if (do_host_removespace) {
1252                              if (find_header_and_get_info(packet_data, packet_dataLen,
1253                                                          http_useragent_find, &hdr_name_addr,
1254                                                           &hdr_value_addr, &hdr_value_len))
1255                              {
1256                                  useragent_addr = hdr_value_addr;
1257                                  useragent_len = hdr_value_len;
1258  
1259                                  /* We move Host header value by one byte to the left and then
1260                                   * "insert" stolen space to the end of User-Agent value because
1261                                   * some web servers are not tolerant to additional space in the
1262                                   * end of Host header.
1263                                   *
1264                                   * Nothing is done if User-Agent header is missing.
1265                                   */
1266                                  if (useragent_addr && useragent_len > 0) {
1267                                      /* useragent_addr is in the beginning of User-Agent value */
1268  
1269                                      if (useragent_addr > host_addr) {
1270                                          /* Move one byte to the LEFT from "Host:"
1271                                          * to the end of User-Agent
1272                                          */
1273                                          memmove(host_addr - 1, host_addr,
1274                                                  (size_t)(useragent_addr + useragent_len - host_addr));
1275                                          host_addr -= 1;
1276                                          /* Put space in the end of User-Agent header */
1277                                          *(char*)((unsigned char*)useragent_addr + useragent_len - 1) = ' ';
1278                                          should_recalc_checksum = 1;
1279                                          //printf("Replaced Host header!\n");
1280                                      }
1281                                      else {
1282                                          /* User-Agent goes BEFORE Host header */
1283  
1284                                          /* Move one byte to the RIGHT from the end of User-Agent
1285                                          * to the "Host:"
1286                                          */
1287                                          memmove(useragent_addr + useragent_len + 1,
1288                                                  useragent_addr + useragent_len,
1289                                                  (size_t)(host_addr - 1 - (useragent_addr + useragent_len)));
1290                                          /* Put space in the end of User-Agent header */
1291                                          *(char*)((unsigned char*)useragent_addr + useragent_len) = ' ';
1292                                          should_recalc_checksum = 1;
1293                                          //printf("Replaced Host header!\n");
1294                                      }
1295                                  } /* if (host_len <= HOST_MAXLEN && useragent_addr) */
1296                              } /* if (find_header_and_get_info http_useragent) */
1297                          } /* else if (do_host_removespace) */
1298                      } /* if (find_header_and_get_info http_host) */
1299                  } /* Handle OUTBOUND packet with data */
1300  
1301                  /*
1302                  * should_recalc_checksum mean we have detected a packet to handle and
1303                  * modified it in some way.
1304                  * Handle native fragmentation here, incl. sending the packet.
1305                  */
1306                  if (should_reinject && should_recalc_checksum && do_native_frag)
1307                  {
1308                      current_fragment_size = 0;
1309                      if (do_fragment_http && ppTcpHdr->DstPort == htons(80)) {
1310                          current_fragment_size = http_fragment_size;
1311                      }
1312                      else if (do_fragment_https && ppTcpHdr->DstPort != htons(80)) {
1313                          if (do_fragment_by_sni && sni_ok) {
1314                              current_fragment_size = (void*)host_addr - packet_data;
1315                          } else {
1316                              current_fragment_size = https_fragment_size;
1317                          }
1318                      }
1319  
1320                      if (current_fragment_size) {
1321                          send_native_fragment(w_filter, addr, packet, packetLen, packet_data,
1322                                              packet_dataLen,packet_v4, packet_v6,
1323                                              ppIpHdr, ppIpV6Hdr, ppTcpHdr,
1324                                              current_fragment_size, do_reverse_frag);
1325  
1326                          send_native_fragment(w_filter, addr, packet, packetLen, packet_data,
1327                                              packet_dataLen,packet_v4, packet_v6,
1328                                              ppIpHdr, ppIpV6Hdr, ppTcpHdr,
1329                                              current_fragment_size, !do_reverse_frag);
1330                          continue;
1331                      }
1332                  }
1333              } /* Handle TCP packet with data */
1334  
1335              /* Else if we got TCP packet without data */
1336              else if (packet_type == ipv4_tcp || packet_type == ipv6_tcp) {
1337                  /* If we got INBOUND SYN+ACK packet */
1338                  if (!addr.Outbound &&
1339                      ppTcpHdr->Syn == 1 && ppTcpHdr->Ack == 1) {
1340                      //printf("Changing Window Size!\n");
1341                      /*
1342                       * Window Size is changed even if do_fragment_http_persistent
1343                       * is enabled as there could be non-HTTP data on port 80
1344                       */
1345  
1346                      if (do_fake_packet && (do_auto_ttl || ttl_min_nhops)) {
1347                          if (!((packet_v4 && tcp_handle_incoming(&ppIpHdr->SrcAddr, &ppIpHdr->DstAddr,
1348                                          ppTcpHdr->SrcPort, ppTcpHdr->DstPort,
1349                                          0, ppIpHdr->TTL))
1350                              ||
1351                              (packet_v6 && tcp_handle_incoming((uint32_t*)&ppIpV6Hdr->SrcAddr,
1352                                          (uint32_t*)&ppIpV6Hdr->DstAddr,
1353                                          ppTcpHdr->SrcPort, ppTcpHdr->DstPort,
1354                                          1, ppIpV6Hdr->HopLimit))))
1355                          {
1356                              if (do_tcp_verb)
1357                                  puts("[TCP WARN] Can't add TCP connection record.");
1358                          }
1359                      }
1360  
1361                      if (!do_native_frag) {
1362                          if (do_fragment_http && ppTcpHdr->SrcPort == htons(80)) {
1363                              change_window_size(ppTcpHdr, http_fragment_size);
1364                              should_recalc_checksum = 1;
1365                          }
1366                          else if (do_fragment_https && ppTcpHdr->SrcPort != htons(80)) {
1367                              change_window_size(ppTcpHdr, https_fragment_size);
1368                              should_recalc_checksum = 1;
1369                          }
1370                      }
1371                  }
1372              }
1373  
1374              /* Else if we got UDP packet with data */
1375              else if ((do_dnsv4_redirect && (packet_type == ipv4_udp_data)) ||
1376                       (do_dnsv6_redirect && (packet_type == ipv6_udp_data)))
1377              {
1378                  if (!addr.Outbound) {
1379                      if ((packet_v4 && dns_handle_incoming(&ppIpHdr->DstAddr, ppUdpHdr->DstPort,
1380                                          packet_data, packet_dataLen,
1381                                          &dns_conn_info, 0))
1382                          ||
1383                          (packet_v6 && dns_handle_incoming(ppIpV6Hdr->DstAddr, ppUdpHdr->DstPort,
1384                                          packet_data, packet_dataLen,
1385                                          &dns_conn_info, 1)))
1386                      {
1387                          /* Changing source IP and port to the values
1388                           * from DNS conntrack */
1389                          if (packet_v4)
1390                              ppIpHdr->SrcAddr = dns_conn_info.dstip[0];
1391                          else if (packet_v6)
1392                              ipv6_copy_addr(ppIpV6Hdr->SrcAddr, dns_conn_info.dstip);
1393                          ppUdpHdr->DstPort = dns_conn_info.srcport;
1394                          ppUdpHdr->SrcPort = dns_conn_info.dstport;
1395                          should_recalc_checksum = 1;
1396                      }
1397                      else {
1398                          if (dns_is_dns_packet(packet_data, packet_dataLen, 0))
1399                              should_reinject = 0;
1400  
1401                          if (do_dns_verb && !should_reinject) {
1402                              printf("[DNS] Error handling incoming packet: srcport = %hu, dstport = %hu\n",
1403                                 ntohs(ppUdpHdr->SrcPort), ntohs(ppUdpHdr->DstPort));
1404                          }
1405                      }
1406                  }
1407  
1408                  else if (addr.Outbound) {
1409                      if ((packet_v4 && dns_handle_outgoing(&ppIpHdr->SrcAddr, ppUdpHdr->SrcPort,
1410                                          &ppIpHdr->DstAddr, ppUdpHdr->DstPort,
1411                                          packet_data, packet_dataLen, 0))
1412                          ||
1413                          (packet_v6 && dns_handle_outgoing(ppIpV6Hdr->SrcAddr, ppUdpHdr->SrcPort,
1414                                          ppIpV6Hdr->DstAddr, ppUdpHdr->DstPort,
1415                                          packet_data, packet_dataLen, 1)))
1416                      {
1417                          /* Changing destination IP and port to the values
1418                           * from configuration */
1419                          if (packet_v4) {
1420                              ppIpHdr->DstAddr = dnsv4_addr;
1421                              ppUdpHdr->DstPort = dnsv4_port;
1422                          }
1423                          else if (packet_v6) {
1424                              ipv6_copy_addr(ppIpV6Hdr->DstAddr, (uint32_t*)dnsv6_addr.s6_addr);
1425                              ppUdpHdr->DstPort = dnsv6_port;
1426                          }
1427                          should_recalc_checksum = 1;
1428                      }
1429                      else {
1430                          if (dns_is_dns_packet(packet_data, packet_dataLen, 1))
1431                              should_reinject = 0;
1432  
1433                          if (do_dns_verb && !should_reinject) {
1434                              printf("[DNS] Error handling outgoing packet: srcport = %hu, dstport = %hu\n",
1435                                 ntohs(ppUdpHdr->SrcPort), ntohs(ppUdpHdr->DstPort));
1436                          }
1437                      }
1438                  }
1439              }
1440  
1441              if (should_reinject) {
1442                  //printf("Re-injecting!\n");
1443                  if (should_recalc_checksum) {
1444                      WinDivertHelperCalcChecksums(packet, packetLen, &addr, (UINT64)0LL);
1445                  }
1446                  WinDivertSend(w_filter, packet, packetLen, NULL, &addr);
1447              }
1448          }
1449          else {
1450              // error, ignore
1451              if (!exiting)
1452                  printf("Error receiving packet!\n");
1453              break;
1454          }
1455      }
1456  }