[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 4\ninsert_final_newline = true\nend_of_line = lf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.yml",
    "content": "name: The program crashes, hangs, certain function does not work / Программа падает, зависает, отдельная функция не работает\ndescription: File a bug report / Сообщить об ошибке в программе\n\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        ### Carefully read all the text IN FULL. Take this seriously.\n        ### Use this form only for software bug reports! The website does not open? That's likely NOT a bug, do NOT report it here! You have a question regarding the program? That's not a bug either!\n        #### If in doubt, [use NTC.party forum](https://ntc.party/c/community-software/goodbyedpi).\n        ### Внимательно прочитайте ВЕСЬ текст ниже. Отнеситесь к этому со всей ответственностью.\n        ### Используйте эту форму только для сообщений об ошибках в программе! Неоткрывающиеся сайты таковыми не являются, вопросы по программе к ошибкам не относятся.\n        #### Если у вас есть сомнения, [воспользуйтесь форумом NTC.party](https://ntc.party/c/community-software/goodbyedpi).\n\n        GoodbyeDPI does not guarantee to work with your ISP for every blocked website or at all. If GoodbyeDPI can't unblock some or any websites, this is most likely not a software bug, and you should not report it here.\n\n        Please only report software bugs, such as:\n          * program crash\n          * incorrect network packet handling\n          * antivirus incompatibility\n          * DNS redirection problems\n          * memory leaks\n          * other software issues\n\n        Please make sure to check other opened and closed issues, it could be your bug has been reported already.\n        For questions, or if in doubt, [use NTC.party forum](https://ntc.party/c/community-software/goodbyedpi).\n\n        ### ИСПОЛЬЗУЙТЕ ЭТУ ФОРМУ ТОЛЬКО ДЛЯ БАГОВ! Веб-сайт не открывается? Это, скорее всего, не баг, не сообщайте сюда!\n        GoodbyeDPI не гарантирует ни 100% работу с вашим провайдером, ни работу с каждым заблокированным сайтом. Если GoodbyeDPI не разблокирует доступ к некоторым или всем веб-сайтам, вероятнее всего, это не программная ошибка, и не стоит о ней сообщать здесь.\n\n        Пожалуйста, сообщайте только об ошибках в программе, таких как:\n          * падение программы\n          * некорректная обработка сетевых пакетов\n          * несовместимость с антивирусами\n          * проблемы с перенаправлением DNS\n          * утечки памяти\n          * другие ошибки в программе\n\n        Также посмотрите другие открытые и закрытые баги. Возможно, ошибка уже обсуждалась или исправлена.\n        Для вопросов, а также в случае сомнений в определении бага, обращайтесь [на форум NTC.party](https://ntc.party/c/community-software/goodbyedpi).\n  - type: checkboxes\n    id: terms\n    attributes:\n      label: CAPTCHA\n      description: Confirm that you have read the text above / Подтвердите, что вы прочитали текст выше\n      options:\n        - label: I understand I could be banned from the repository if I misusing issue section not for posting bugs, but for question or 'broken website' report. / Я понимаю, что меня могут заблокировать в репозитории, если я буду использовать раздел issue не для сообщений об ошибках, а для вопросов или сообщении о «неработающем веб-сайте».\n          required: true\n    validations:\n      required: true\n  - type: input\n    id: os\n    attributes:\n      label: Operating system / операционная система\n      description: Enter your Windows version. For Windows 10 and 11 include build/update number.\n      placeholder: ex. Windows 10 20H2\n    validations:\n      required: true\n  - type: dropdown\n    id: is-svc\n    attributes:\n      label: Running as service / Запуск программы как сервис\n      description: Do you use GoodbyeDPI as a Windows Service?\n      options:\n        - I run it as a regular program / Запускаю программу обычным образом\n        - I installed it as a service / Установил как сервис Windows\n    validations:\n      required: true\n  - type: textarea\n    id: what-happened\n    attributes:\n      label: Describe the bug / Опишите ошибку программы\n      description: A clear and concise description of what the bug is / Подробно опишите, в чём заключается ошибка\n      placeholder: Attach the screenshots for clarity / При необходимости приложите скриншоты\n    validations:\n      required: true\n  - type: textarea\n    id: additional-info\n    attributes:\n      label: Additional information / Дополнительная информация\n      description: If you have a hints on why this bug happens, you can express it here / Если у вас есть предположения об источнике бага, вы можете изложить их здесь\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: The program worked yesterday but not today / Программа работала вчера, но перестала работать сегодня\n    url: https://ntc.party/c/community-software/goodbyedpi/8\n    about: Visit support community forum, people will certainly help you! / Посетите форум поддержки, где сообщество вам поможет!\n\n  - name: Сertain website is still unreachable / Отдельный веб-сайт всё ещё недоступен\n    url: https://ntc.party/c/community-software/goodbyedpi/8\n    about: That could be solved with community support! / Проблема будет решена силами сообщества!\n\n  - name: Questions about the program / Вопросы по програме\n    url: https://ntc.party/c/community-software/goodbyedpi/8\n    about: Please ask and answer questions on forum / Пожалуйста, задавайте вопросы только на форуме\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature.yml",
    "content": "name:  Feature request / Предложить новую функциональность\ndescription:  Suggest an idea or function for this project / Предложить новую идею или функциональность в программе\n\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        If you think of a great function or circumvention method which is missing from the program, go ahead and suggest it here. But first make sure to search exiting tickets.\n        Если вы придумали новую функцию или метод обхода, которого еще нет в программе, напишите о нем подробнее здесь. Но сначала убедитесь с помощью поиска, что такого запроса еще не было.\n  - type: checkboxes\n    id: ensure\n    attributes:\n      label: I've made sure there's no existing feature request / Я убедился, что такой функциональности еще никто не предлагал\n      options:\n        - label: I've made sure there's no existing feature request / Я убедился, что такой функциональности еще никто не предлагал\n          required: true\n  - type: textarea\n    id: description\n    attributes:\n      label: Describe your feature / Опишите ваше предложение\n    validations:\n      required: true\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build GoodbyeDPI\n\non:\n  push:\n    paths:\n      - 'src/**'\n  pull_request:\n    paths:\n      - 'src/**'\n  workflow_dispatch:\n\nenv:\n  WINDIVERT_URL: https://reqrypt.org/download/WinDivert-2.2.0-D.zip\n  WINDIVERT_NAME: WinDivert-2.2.0-D.zip\n  WINDIVERT_BASENAME: WinDivert-2.2.0-D\n  WINDIVERT_SHA256: 1d461cfdfa7ba88ebcfbb3603b71b703e9f72aba8aeff99a75ce293e6f89d2ba\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n\n      - name: Declare short commit variable\n        id: vars\n        run: |\n          echo \"sha_short=$(git rev-parse --short HEAD)\" >> $GITHUB_OUTPUT\n\n      - name: Install MinGW-w64\n        run: >\n          sudo rm /var/lib/man-db/auto-update &&\n          sudo DEBIAN_FRONTEND=noninteractive XZ_DEFAULTS=\"-T0\" XZ_OPT=\"-T0\" eatmydata\n          apt install -y --no-install-recommends gcc-mingw-w64\n\n      - name: Download WinDivert from cache\n        id: windivert-cache\n        uses: actions/cache@v4\n        with:\n          path: ${{ env.WINDIVERT_NAME }}\n          key: ${{ env.WINDIVERT_SHA256 }}\n\n      - name: Download WinDivert from the website\n        if: steps.windivert-cache.outputs.cache-hit != 'true'\n        run: >\n          wget ${{ env.WINDIVERT_URL }} &&\n          (echo ${{ env.WINDIVERT_SHA256 }} ${{ env.WINDIVERT_NAME }} | sha256sum -c)\n\n      - name: Unpack WinDivert\n        run: unzip ${{ env.WINDIVERT_NAME }}\n\n      - name: Compile x86_64\n        run: >\n          cd src && make clean &&\n          make CPREFIX=x86_64-w64-mingw32- BIT64=1 WINDIVERTHEADERS=../${{ env.WINDIVERT_BASENAME }}/include WINDIVERTLIBS=../${{ env.WINDIVERT_BASENAME }}/x64 -j4\n\n      - name: Prepare x86_64 directory\n        run: |\n          mkdir goodbyedpi_x86_64_${{ steps.vars.outputs.sha_short }}\n          cp src/goodbyedpi.exe ${{ env.WINDIVERT_BASENAME }}/x64/*.{dll,sys} goodbyedpi_x86_64_${{ steps.vars.outputs.sha_short }}\n\n      - name: Upload output file x86_64\n        uses: actions/upload-artifact@v4\n        with:\n          name: goodbyedpi_x86_64_${{ steps.vars.outputs.sha_short }}\n          path: goodbyedpi_x86_64_${{ steps.vars.outputs.sha_short }}\n\n      - name: Compile i686\n        run: >\n          cd src && make clean &&\n          make CPREFIX=i686-w64-mingw32- WINDIVERTHEADERS=../${{ env.WINDIVERT_BASENAME }}/include WINDIVERTLIBS=../${{ env.WINDIVERT_BASENAME }}/x86 -j4\n\n      - name: Prepare x86 directory\n        run: |\n          mkdir goodbyedpi_x86_${{ steps.vars.outputs.sha_short }}\n          cp src/goodbyedpi.exe ${{ env.WINDIVERT_BASENAME }}/x86/*.{dll,sys} goodbyedpi_x86_${{ steps.vars.outputs.sha_short }}\n\n      - name: Upload output file x86\n        uses: actions/upload-artifact@v4\n        with:\n          name: goodbyedpi_x86_${{ steps.vars.outputs.sha_short }}\n          path: goodbyedpi_x86_${{ steps.vars.outputs.sha_short }}\n"
  },
  {
    "path": ".gitignore",
    "content": "*.o\n*.exe\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "GoodbyeDPI — Deep Packet Inspection circumvention utility\n=========================\n\nThis software designed to bypass Deep Packet Inspection systems found in many Internet Service Providers which block access to certain websites.\n\nIt handles DPI connected using optical splitter or port mirroring (**Passive DPI**) which do not block any data but just replying faster than requested destination, and **Active DPI** connected in sequence.\n\n**Windows 7, 8, 8.1, 10 or 11** with administrator privileges required.\n\n# Quick start\n\n* **For Russia**: Download [latest version from Releases page](https://github.com/ValdikSS/GoodbyeDPI/releases), unpack the file and run **1_russia_blacklist_dnsredir.cmd** script.\n* For other countries: Download [latest version from Releases page](https://github.com/ValdikSS/GoodbyeDPI/releases), unpack the file and run **2_any_country_dnsredir.cmd**.\n\nThese scripts launch GoodbyeDPI in recommended mode with DNS resolver redirection to Yandex DNS on non-standard port (to prevent DNS poisoning).  \nIf it works — congratulations! You can use it as-is or configure further.\n\n# How to use\n\nDownload [latest version from Releases page](https://github.com/ValdikSS/GoodbyeDPI/releases) and run.\n\n## Supported arguments\nTo get relevant information about your version of the program, use the -h (--help) argument at startup.\n```\nUsage: goodbyedpi.exe [OPTION...]\n -p          block passive DPI\n -q          block QUIC/HTTP3\n -r          replace Host with hoSt\n -s          remove space between host header and its value\n -m          mix Host header case (test.com -> tEsT.cOm)\n -f <value>  set HTTP fragmentation to value\n -k <value>  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 <value>  set HTTPS fragmentation to value\n -a          additional space between Method and Request-URI (enables -s, may break sites)\n -w          try to find and parse HTTP traffic on all processed ports (not only on port 80)\n --port        <value>    additional TCP port to perform fragmentation on (and HTTP tricks with -w)\n --ip-id       <value>    handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID).\n                          This option can be supplied multiple times.\n --dns-addr    <value>    redirect UDP DNS requests to the supplied IP address (experimental)\n --dns-port    <value>    redirect UDP DNS requests to the supplied port (53 by default)\n --dnsv6-addr  <value>    redirect UDPv6 DNS requests to the supplied IPv6 address (experimental)\n --dnsv6-port  <value>    redirect UDPv6 DNS requests to the supplied port (53 by default)\n --dns-verb               print verbose DNS redirection messages\n --blacklist   <txtfile>  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 --frag-by-sni            if SNI is detected in TLS packet, fragment the packet right before SNI value.\n --set-ttl     <value>    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     <value>    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 --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 --fake-from-hex <value>  Load fake packets for Fake Request Mode from HEX values (like 1234abcDEF).\n                          This option can be supplied multiple times, in this case each fake packet\n                          would be sent on every request in the command line argument order.\n --fake-with-sni <value>  Generate fake packets for Fake Request Mode with given SNI domain name.\n                          The packets mimic Mozilla Firefox 130 TLS ClientHello packet\n                          (with random generated fake SessionID, key shares and ECH grease).\n                          Can be supplied multiple times for multiple fake packets.\n --fake-gen <value>       Generate random-filled fake packets for Fake Request Mode, value of them\n                          (up to 30).\n --fake-resend <value>    Send each fake packet value number of times.\n                          Default: 1 (send each packet once).\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\nLEGACY 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\nModern modesets (more stable, more compatible, faster):\n -5          -f 2 -e 2 --auto-ttl --reverse-frag --max-payload\n -6          -f 2 -e 2 --wrong-seq --reverse-frag --max-payload\n -7          -f 2 -e 2 --wrong-chksum --reverse-frag --max-payload\n -8          -f 2 -e 2 --wrong-seq --wrong-chksum --reverse-frag --max-payload\n -9          -f 2 -e 2 --wrong-seq --wrong-chksum --reverse-frag --max-payload -q (this is the default)\n\n Note: combination of --wrong-seq and --wrong-chksum generates two different fake packets.\n```\n## How to check\nTo check if your ISP's DPI could be circumvented, first make sure that your provider does not poison DNS answers by enabling \"Secure DNS (DNS over HTTPS)\" option in your browser.\n\n* **Chrome**: Settings → [Privacy and security](chrome://settings/security) → Use secure DNS → With: NextDNS\n* **Firefox**: [Settings](about:preferences) → Network Settings → Enable DNS over HTTPS → Use provider: NextDNS\n\nThen run the `goodbyedpi.exe` executable without any options. If it works — congratulations! You can use it as-is or configure further, for example by using `--blacklist` option if the list of blocked websites is known and available for your country.\n\nIf your provider intercepts DNS requests, you may want to use `--dns-addr` option to a public DNS resolver running on non-standard port (such as Yandex DNS `77.88.8.8:1253`) or configure DNS over HTTPS/TLS using third-party applications.\n\nCheck the .cmd scripts and modify it according to your preference and network conditions.\n\n# How does it work\n\n### Passive DPI\n\nMost Passive DPI send HTTP 302 Redirect if you try to access blocked website over HTTP and TCP Reset in case of HTTPS, faster than destination website. Packets sent by DPI usually have IP Identification field equal to `0x0000` or `0x0001`, as seen with Russian providers. These packets, if they redirect you to another website (censorship page), are blocked by GoodbyeDPI.\n\n### Active DPI\n\nActive DPI is more tricky to fool. Currently the software uses 7 methods to circumvent Active DPI:\n\n* TCP-level fragmentation for first data packet\n* TCP-level fragmentation for persistent (keep-alive) HTTP sessions\n* Replacing `Host` header with `hoSt`\n* Removing space between header name and value in `Host` header\n* Adding additional space between HTTP Method (GET, POST etc) and URI\n* Mixing case of Host header value\n* Sending fake HTTP/HTTPS packets with low Time-To-Live value, incorrect checksum or incorrect TCP Sequence/Acknowledgement numbers to fool DPI and prevent delivering them to the destination\n\nThese methods should not break any website as they're fully compatible with TCP and HTTP standards, yet it's sufficient to prevent DPI data classification and to circumvent censorship. Additional space may break some websites, although it's acceptable by HTTP/1.1 specification (see 19.3 Tolerant Applications).\n\nThe program loads WinDivert driver which uses Windows Filtering Platform to set filters and redirect packets to the userspace. It's running as long as console window is visible and terminates when you close the window.\n\n# How to build from source\n\nThis project can be built using **GNU Make** and [**mingw**](https://mingw-w64.org). The only dependency is [WinDivert](https://github.com/basil00/Divert).\n\nTo build x86 exe run:\n\n`make CPREFIX=i686-w64-mingw32- WINDIVERTHEADERS=/path/to/windivert/include WINDIVERTLIBS=/path/to/windivert/x86`\n\nAnd for x86_64:\n\n`make CPREFIX=x86_64-w64-mingw32- BIT64=1 WINDIVERTHEADERS=/path/to/windivert/include WINDIVERTLIBS=/path/to/windivert/amd64`\n\n# How to install as Windows Service\n\nCheck examples in `service_install_russia_blacklist.cmd`, `service_install_russia_blacklist_dnsredir.cmd` and `service_remove.cmd` scripts.\n\nModify them according to your own needs.\n\n# Known issues\n\n* Horribly outdated Windows 7 installations are not able to load WinDivert driver due to missing support for SHA256 digital signatures. Install KB3033929 [x86](https://www.microsoft.com/en-us/download/details.aspx?id=46078)/[x64](https://www.microsoft.com/en-us/download/details.aspx?id=46148), or better, update the whole system using Windows Update.\n* Intel/Qualcomm Killer network cards: `Advanced Stream Detect` in Killer Control Center is incompatible with GoodbyeDPI, [disable it](https://github.com/ValdikSS/GoodbyeDPI/issues/541#issuecomment-2296038239).\n* QUIK trading software [may interfere with GoodbyeDPI](https://github.com/ValdikSS/GoodbyeDPI/issues/677#issuecomment-2390595606). First start QUIK, then GoodbyeDPI.\n* ~~Some SSL/TLS stacks unable to process fragmented ClientHello packets, and HTTPS websites won't open. Bug: [#4](https://github.com/ValdikSS/GoodbyeDPI/issues/4), [#64](https://github.com/ValdikSS/GoodbyeDPI/issues/64).~~ Fragmentation issues are fixed in v0.1.7.\n* ~~ESET Antivirus is incompatible with WinDivert driver [#91](https://github.com/ValdikSS/GoodbyeDPI/issues/91). This is most probably antivirus bug, not WinDivert.~~\n\n\n# Similar projects\n\n- **[zapret](https://github.com/bol-van/zapret)** by @bol-van (for MacOS, Linux and Windows)\n- **[Green Tunnel](https://github.com/SadeghHayeri/GreenTunnel)** by @SadeghHayeri (for MacOS, Linux and Windows)\n- **[DPI Tunnel CLI](https://github.com/nomoresat/DPITunnel-cli)** by @zhenyolka (for Linux and routers)\n- **[DPI Tunnel for Android](https://github.com/nomoresat/DPITunnel-android)** by @zhenyolka (for Android)\n- **[PowerTunnel](https://github.com/krlvm/PowerTunnel)** by @krlvm (for Windows, MacOS and Linux)\n- **[PowerTunnel for Android](https://github.com/krlvm/PowerTunnel-Android)** by @krlvm (for Android)\n- **[SpoofDPI](https://github.com/xvzc/SpoofDPI)** by @xvzc (for macOS and Linux)\n- **[SpoofDPI-Platform](https://github.com/r3pr3ss10n/SpoofDPI-Platform)** by @r3pr3ss10n (for Android, macOS, Windows)\n- **[GhosTCP](https://github.com/macronut/ghostcp)** by @macronut (for Windows)\n- **[ByeDPI](https://github.com/hufrea/byedpi)** for Linux/Windows + **[ByeDPIAndroid](https://github.com/dovecoteescapee/ByeDPIAndroid/)** / **[ByeByeDPI](https://github.com/romanvht/ByeByeDPI/)** for Android (no root)\n- **[youtubeUnblock](https://github.com/Waujito/youtubeUnblock/)** by @Waujito (for OpenWRT/Entware routers and Linux)\n\n# Kudos\n\nThanks @basil00 for [WinDivert](https://github.com/basil00/Divert). That's the main part of this program.\n\nThanks for every [BlockCheck](https://github.com/ValdikSS/blockcheck) contributor. It would be impossible to understand DPI behaviour without this utility.\n"
  },
  {
    "path": "src/Makefile",
    "content": "ifndef MSYSTEM\n\tCPREFIX = x86_64-w64-mingw32-\nendif\n\nWINDIVERTHEADERS = ../../../include\nWINDIVERTLIBS = ../../binary\nMINGWLIB = /usr/x86_64-w64-mingw32/lib/\n\nTARGET = goodbyedpi.exe\n# Linking SSP does not work for some reason, the executable doesn't start.\n#LIBS = -L$(WINDIVERTLIBS) -Wl,-Bstatic -lssp -Wl,-Bdynamic -lWinDivert -lws2_32\nLIBS = -L$(WINDIVERTLIBS) -lWinDivert -lws2_32 -l:libssp.a\nCC = $(CPREFIX)gcc\n\nCCWINDRES = $(CPREFIX)windres\nifeq (, $(shell which $(CPREFIX)windres))\n\tCCWINDRES = windres\nendif\n\nCFLAGS = -std=c99 -pie -fPIE -pipe -I$(WINDIVERTHEADERS) -L$(WINDIVERTLIBS) \\\n         -O2 -D_FORTIFY_SOURCE=2 -fstack-protector \\\n         -Wall -Wextra -Wpedantic -Wformat=2 -Wformat-overflow=2 -Wformat-truncation=2 \\\n         -Wformat-security -Wno-format-nonliteral -Wshadow -Wstrict-aliasing=1 \\\n         -Wnull-dereference -Warray-bounds=2 -Wimplicit-fallthrough=3 \\\n         -Wstringop-overflow=4 \\\n         -Wformat-signedness -Wstrict-overflow=2 -Wcast-align=strict \\\n         -Wfloat-equal -Wcast-align -Wsign-conversion \\\n         #-fstack-protector-strong\nLDFLAGS = -fstack-protector -Wl,-O1,-pie,--dynamicbase,--nxcompat,--sort-common,--as-needed \\\n-Wl,--disable-auto-image-base\n\nifdef BIT64\n\tLDFLAGS += -Wl,--high-entropy-va -Wl,--pic-executable,-e,mainCRTStartup\nelse\n\tCFLAGS += -m32\n\tLDFLAGS += -Wl,--pic-executable,-e,_mainCRTStartup -m32\nendif\n\n.PHONY: default all clean\n\ndefault: $(TARGET)\nall: default\n\nOBJECTS = $(patsubst %.c, %.o, $(wildcard *.c utils/*.c)) goodbyedpi-rc.o\nHEADERS = $(wildcard *.h utils/*.h)\n\n%.o: %.c $(HEADERS)\n\t$(CC) $(CFLAGS) -c $< -o $@\n\ngoodbyedpi-rc.o:\n\t$(CCWINDRES) goodbyedpi-rc.rc goodbyedpi-rc.o\n\n.PRECIOUS: $(TARGET) $(OBJECTS)\n\n$(TARGET): $(OBJECTS)\n\t$(CC) $(OBJECTS) $(LDFLAGS) $(LIBS) -s -o $@\n\nclean:\n\t-rm -f *.o utils/*.o\n\t-rm -f $(TARGET)\n"
  },
  {
    "path": "src/blackwhitelist.c",
    "content": "/*\n * Blacklist for GoodbyeDPI HTTP DPI circumvention tricks\n *\n * This is a simple domain hash table.\n * Domain records are added from a text file, where every\n * domain is separated with a new line.\n */\n#include <windows.h>\n#include <stdio.h>\n#include \"goodbyedpi.h\"\n#include \"utils/uthash.h\"\n#include \"utils/getline.h\"\n\ntypedef struct blackwhitelist_record {\n    const char *host;\n    UT_hash_handle hh;   /* makes this structure hashable */\n} blackwhitelist_record_t;\n\nstatic blackwhitelist_record_t *blackwhitelist = NULL;\n\nstatic int check_get_hostname(const char *host) {\n    blackwhitelist_record_t *tmp_record = NULL;\n    if (!blackwhitelist) return FALSE;\n\n    HASH_FIND_STR(blackwhitelist, host, tmp_record);\n    if (tmp_record) {\n        debug(\"check_get_hostname found host\\n\");\n        return TRUE;\n    }\n    debug(\"check_get_hostname host not found\\n\");\n    return FALSE;\n}\n\nstatic int add_hostname(const char *host) {\n    if (!host)\n        return FALSE;\n\n    blackwhitelist_record_t *tmp_record = malloc(sizeof(blackwhitelist_record_t));\n    char *host_c = NULL;\n\n    if (!check_get_hostname(host)) {\n        host_c = strdup(host);\n        tmp_record->host = host_c;\n        HASH_ADD_KEYPTR(hh, blackwhitelist, tmp_record->host,\n                        strlen(tmp_record->host), tmp_record);\n        debug(\"Added host %s\\n\", host_c);\n        return TRUE;\n    }\n    debug(\"Not added host %s\\n\", host);\n    free(tmp_record);\n    if (host_c)\n        free(host_c);\n    return FALSE;\n}\n\nint blackwhitelist_load_list(const char *filename) {\n    char *line = malloc(HOST_MAXLEN + 1);\n    size_t linelen = HOST_MAXLEN + 1;\n    int cnt = 0;\n    ssize_t read;\n\n    FILE *fp = fopen(filename, \"r\");\n    if (!fp) return FALSE;\n\n    while ((read = getline(&line, &linelen, fp)) != -1) {\n        /* works with both \\n and \\r\\n */\n        line[strcspn(line, \"\\r\\n\")] = '\\0';\n        if (strlen(line) > HOST_MAXLEN) {\n            printf(\"WARNING: host %s exceeds maximum host length and has not been added\\n\",\n                line);\n            continue;\n        }\n        if (strlen(line) < 2) {\n            printf(\"WARNING: host %s is less than 2 characters, skipping\\n\", line);\n            continue;\n        }\n        if (add_hostname(line))\n            cnt++;\n    }\n    free(line);\n    if (!blackwhitelist) return FALSE;\n    printf(\"Loaded %d hosts from file %s\\n\", cnt, filename);\n    fclose(fp);\n    return TRUE;\n}\n\nint blackwhitelist_check_hostname(const char *host_addr, size_t host_len) {\n    char current_host[HOST_MAXLEN + 1];\n    char *tokenized_host = NULL;\n\n    if (host_len > HOST_MAXLEN) return FALSE;\n    if (host_addr && host_len) {\n        memcpy(current_host, host_addr, host_len);\n        current_host[host_len] = '\\0';\n    }\n\n    if (check_get_hostname(current_host))\n            return TRUE;\n\n    tokenized_host = strchr(current_host, '.');\n    while (tokenized_host != NULL && tokenized_host < (current_host + HOST_MAXLEN)) {\n        if (check_get_hostname(tokenized_host + 1))\n            return TRUE;\n        tokenized_host = strchr(tokenized_host + 1, '.');\n    }\n\n    debug(\"____blackwhitelist_check_hostname FALSE: host %s\\n\", current_host);\n    return FALSE;\n}\n"
  },
  {
    "path": "src/blackwhitelist.h",
    "content": "int blackwhitelist_load_list(const char *filename);\nint blackwhitelist_check_hostname(const char *host_addr, size_t host_len);\n"
  },
  {
    "path": "src/dnsredir.c",
    "content": "/*\n * DNS UDP Connection Tracker for GoodbyeDPI\n *\n * This is a simple connection tracker for DNS UDP data.\n * It's not a proper one. The caveats as follows:\n *    * Uses only source IP address and port as a hash key;\n *    * One-shot only. Removes conntrack record as soon as gets the reply;\n *    * Does not properly parse DNS request and response, only checks some bytes;\n *\n * But anyway, it works fine for DNS.\n */\n\n#include <windows.h>\n#include <time.h>\n#include <stdio.h>\n#include \"goodbyedpi.h\"\n#include \"dnsredir.h\"\n#include \"utils/uthash.h\"\n\n/* key ('4' for IPv4 or '6' for IPv6 + srcip[16] + srcport[2]) */\n#define UDP_CONNRECORD_KEY_LEN 19\n\n#define DNS_CLEANUP_INTERVAL_SEC 30\n\n/* HACK!\n * uthash uses strlen() for HASH_FIND_STR.\n * We have null bytes in our key, so we can't use strlen()\n * And since it's always UDP_CONNRECORD_KEY_LEN bytes long,\n * we don't need to use any string function to determine length.\n */\n#undef uthash_strlen\n#define uthash_strlen(s) UDP_CONNRECORD_KEY_LEN\n\ntypedef struct udp_connrecord {\n    /* key ('4' for IPv4 or '6' for IPv6 + srcip[16] + srcport[2]) */\n    char key[UDP_CONNRECORD_KEY_LEN];\n    time_t time;         /* time when this record was added */\n    uint32_t dstip[4];\n    uint16_t dstport;\n    UT_hash_handle hh;   /* makes this structure hashable */\n} udp_connrecord_t;\n\nstatic time_t last_cleanup = 0;\nstatic udp_connrecord_t *conntrack = NULL;\n\nvoid flush_dns_cache() {\n    INT_PTR WINAPI (*DnsFlushResolverCache)();\n\n    HMODULE dnsapi = LoadLibrary(\"dnsapi.dll\");\n    if (dnsapi == NULL)\n    {\n        printf(\"Can't load dnsapi.dll to flush DNS cache!\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    DnsFlushResolverCache = GetProcAddress(dnsapi, \"DnsFlushResolverCache\");\n    if (DnsFlushResolverCache == NULL || !DnsFlushResolverCache())\n        printf(\"Can't flush DNS cache!\");\n    FreeLibrary(dnsapi);\n}\n\ninline static void fill_key_data(char *key, const uint8_t is_ipv6, const uint32_t srcip[4],\n                          const uint16_t srcport)\n{\n    if (is_ipv6) {\n        *(uint8_t*)(key) = '6';\n        ipv6_copy_addr((uint32_t*)(key + sizeof(uint8_t)), srcip);\n    }\n    else {\n        *(uint8_t*)(key) = '4';\n        ipv4_copy_addr((uint32_t*)(key + sizeof(uint8_t)), srcip);\n    }\n\n    *(uint16_t*)(key + sizeof(uint8_t) + sizeof(uint32_t) * 4) = srcport;\n}\n\ninline static void fill_data_from_key(uint8_t *is_ipv6, uint32_t srcip[4], uint16_t *srcport,\n                                      const char *key)\n{\n    if (key[0] == '6') {\n        *is_ipv6 = 1;\n        ipv6_copy_addr(srcip, (uint32_t*)(key + sizeof(uint8_t)));\n    }\n    else {\n        *is_ipv6 = 0;\n        ipv4_copy_addr(srcip, (uint32_t*)(key + sizeof(uint8_t)));\n    }\n    *srcport = *(uint16_t*)(key + sizeof(uint8_t) + sizeof(uint32_t) * 4);\n}\n\ninline static void construct_key(const uint32_t srcip[4], const uint16_t srcport,\n                                 char *key, const uint8_t is_ipv6)\n{\n    debug(\"Construct key enter\\n\");\n    if (key) {\n        debug(\"Constructing key\\n\");\n        fill_key_data(key, is_ipv6, srcip, srcport);\n    }\n    debug(\"Construct key end\\n\");\n}\n\ninline static void deconstruct_key(const char *key, const udp_connrecord_t *connrecord,\n                                   conntrack_info_t *conn_info)\n{\n    debug(\"Deconstruct key enter\\n\");\n    if (key && conn_info) {\n        debug(\"Deconstructing key\\n\");\n        fill_data_from_key(&conn_info->is_ipv6, conn_info->srcip,\n                           &conn_info->srcport, key);\n\n        if (conn_info->is_ipv6)\n            ipv6_copy_addr(conn_info->dstip, connrecord->dstip);\n        else\n            ipv4_copy_addr(conn_info->dstip, connrecord->dstip);\n\n        conn_info->dstport = connrecord->dstport;\n    }\n    debug(\"Deconstruct key end\\n\");\n}\n\nstatic int check_get_udp_conntrack_key(const char *key, udp_connrecord_t **connrecord) {\n    udp_connrecord_t *tmp_connrecord = NULL;\n    if (!conntrack) return FALSE;\n\n    HASH_FIND_STR(conntrack, key, tmp_connrecord);\n    if (tmp_connrecord) {\n        if (connrecord)\n            *connrecord = tmp_connrecord;\n        debug(\"check_get_udp_conntrack_key found key\\n\");\n        return TRUE;\n    }\n    debug(\"check_get_udp_conntrack_key key not found\\n\");\n    return FALSE;\n}\n\nstatic int add_udp_conntrack(const uint32_t srcip[4], const uint16_t srcport,\n                             const uint32_t dstip[4], const uint16_t dstport,\n                             const uint8_t is_ipv6\n                            )\n{\n    if (!(srcip && srcport && dstip && dstport))\n        return FALSE;\n\n    udp_connrecord_t *tmp_connrecord = malloc(sizeof(udp_connrecord_t));\n    construct_key(srcip, srcport, tmp_connrecord->key, is_ipv6);\n\n    if (!check_get_udp_conntrack_key(tmp_connrecord->key, NULL)) {\n        tmp_connrecord->time = time(NULL);\n\n        if (is_ipv6) {\n            ipv6_copy_addr(tmp_connrecord->dstip, dstip);\n        }\n        else {\n            ipv4_copy_addr(tmp_connrecord->dstip, dstip);\n        }\n        tmp_connrecord->dstport = dstport;\n        HASH_ADD_STR(conntrack, key, tmp_connrecord);\n        debug(\"Added UDP conntrack\\n\");\n        return TRUE;\n    }\n    debug(\"Not added UDP conntrack\\n\");\n    free(tmp_connrecord);\n    return FALSE;\n}\n\nstatic void dns_cleanup() {\n    udp_connrecord_t *tmp_connrecord, *tmp_connrecord2 = NULL;\n\n    if (last_cleanup == 0) {\n        last_cleanup = time(NULL);\n        return;\n    }\n\n    if (difftime(time(NULL), last_cleanup) >= DNS_CLEANUP_INTERVAL_SEC) {\n        last_cleanup = time(NULL);\n\n        HASH_ITER(hh, conntrack, tmp_connrecord, tmp_connrecord2) {\n            if (difftime(last_cleanup, tmp_connrecord->time) >= DNS_CLEANUP_INTERVAL_SEC) {\n                HASH_DEL(conntrack, tmp_connrecord);\n                free(tmp_connrecord);\n            }\n        }\n    }\n}\n\nint dns_is_dns_packet(const char *packet_data, const UINT packet_dataLen, const int outgoing) {\n    if (packet_dataLen < 16) return FALSE;\n\n    if (outgoing && (ntohs(*(const uint16_t*)(packet_data + 2)) & 0xFA00) == 0 &&\n        (ntohs(*(const uint32_t*)(packet_data + 6))) == 0) {\n        return TRUE;\n    }\n    else if (!outgoing &&\n        (ntohs(*(const uint16_t*)(packet_data + 2)) & 0xF800) == 0x8000) {\n        return TRUE;\n    }\n    return FALSE;\n}\n\nint dns_handle_outgoing(const uint32_t srcip[4], const uint16_t srcport,\n                        const uint32_t dstip[4], const uint16_t dstport,\n                        const char *packet_data, const UINT packet_dataLen,\n                        const uint8_t is_ipv6)\n{\n    if (packet_dataLen < 16)\n        return FALSE;\n\n    dns_cleanup();\n\n    if (dns_is_dns_packet(packet_data, packet_dataLen, 1)) {\n        /* Looks like DNS request */\n        debug(\"trying to add srcport = %hu, dstport = %hu\\n\", ntohs(srcport), ntohs(dstport));\n        return add_udp_conntrack(srcip, srcport, dstip, dstport, is_ipv6);\n    }\n    debug(\"____dns_handle_outgoing FALSE: srcport = %hu, dstport = %hu\\n\", ntohs(srcport), ntohs(dstport));\n    return FALSE;\n}\n\nint dns_handle_incoming(const uint32_t srcip[4], const uint16_t srcport,\n                        const char *packet_data, const UINT packet_dataLen,\n                        conntrack_info_t *conn_info, const uint8_t is_ipv6)\n{\n    char key[UDP_CONNRECORD_KEY_LEN];\n    udp_connrecord_t *tmp_connrecord = NULL;\n\n    if (packet_dataLen < 16 || !conn_info)\n        return FALSE;\n\n    dns_cleanup();\n\n    if (dns_is_dns_packet(packet_data, packet_dataLen, 0)) {\n        /* Looks like DNS response */\n        construct_key(srcip, srcport, key, is_ipv6);\n        if (check_get_udp_conntrack_key(key, &tmp_connrecord) && tmp_connrecord) {\n            /* Connection exists in conntrack, moving on */\n            deconstruct_key(key, tmp_connrecord, conn_info);\n            HASH_DEL(conntrack, tmp_connrecord);\n            free(tmp_connrecord);\n            return TRUE;\n        }\n    }\n    debug(\"____dns_handle_incoming FALSE: srcport = %hu\\n\", ntohs(srcport));\n    return FALSE;\n}\n"
  },
  {
    "path": "src/dnsredir.h",
    "content": "#ifndef _DNSREDIR_H\n#define _DNSREDIR_H\n#include <stdint.h>\n\ntypedef struct conntrack_info {\n    uint8_t  is_ipv6;\n    uint32_t srcip[4];\n    uint16_t srcport;\n    uint32_t dstip[4];\n    uint16_t dstport;\n} conntrack_info_t;\n\ninline static void ipv4_copy_addr(uint32_t dst[4], const uint32_t src[4]) {\n    dst[0] = src[0];\n    dst[1] = 0;\n    dst[2] = 0;\n    dst[3] = 0;\n}\n\ninline static void ipv6_copy_addr(uint32_t dst[4], const uint32_t src[4]) {\n    dst[0] = src[0];\n    dst[1] = src[1];\n    dst[2] = src[2];\n    dst[3] = src[3];\n}\n\nint dns_handle_incoming(const uint32_t srcip[4], const uint16_t srcport,\n                        const char *packet_data, const UINT packet_dataLen,\n                        conntrack_info_t *conn_info, const uint8_t is_ipv6);\n\nint dns_handle_outgoing(const uint32_t srcip[4], const uint16_t srcport,\n                        const uint32_t dstip[4], const uint16_t dstport,\n                        const char *packet_data, const UINT packet_dataLen,\n                        const uint8_t is_ipv6\n                       );\n\nvoid flush_dns_cache();\nint dns_is_dns_packet(const char *packet_data, const UINT packet_dataLen, const int outgoing);\n#endif\n"
  },
  {
    "path": "src/fakepackets.c",
    "content": "#include <stdio.h>\n#define _CRT_RAND_S\n#include <stdlib.h>\n#include <stdbool.h>\n#include <ctype.h>\n#include <unistd.h>\n#include <in6addr.h>\n#include <ws2tcpip.h>\n#include \"windivert.h\"\n#include \"goodbyedpi.h\"\n\nstruct fake_t {\n    const unsigned char* data;\n    size_t size;\n};\n\nstatic struct fake_t *fakes[30] = {0};\nint fakes_count = 0;\nint fakes_resend = 1;\n\nstatic const unsigned char fake_http_request[] = \"GET / HTTP/1.1\\r\\nHost: www.w3.org\\r\\n\"\n                                                 \"User-Agent: curl/7.65.3\\r\\nAccept: */*\\r\\n\"\n                                                 \"Accept-Encoding: deflate, gzip, br\\r\\n\\r\\n\";\nstatic const unsigned char fake_https_request[] = {\n    0x16, 0x03, 0x01, 0x02, 0x00, 0x01, 0x00, 0x01, 0xfc, 0x03, 0x03, 0x9a, 0x8f, 0xa7, 0x6a, 0x5d,\n    0x57, 0xf3, 0x62, 0x19, 0xbe, 0x46, 0x82, 0x45, 0xe2, 0x59, 0x5c, 0xb4, 0x48, 0x31, 0x12, 0x15,\n    0x14, 0x79, 0x2c, 0xaa, 0xcd, 0xea, 0xda, 0xf0, 0xe1, 0xfd, 0xbb, 0x20, 0xf4, 0x83, 0x2a, 0x94,\n    0xf1, 0x48, 0x3b, 0x9d, 0xb6, 0x74, 0xba, 0x3c, 0x81, 0x63, 0xbc, 0x18, 0xcc, 0x14, 0x45, 0x57,\n    0x6c, 0x80, 0xf9, 0x25, 0xcf, 0x9c, 0x86, 0x60, 0x50, 0x31, 0x2e, 0xe9, 0x00, 0x22, 0x13, 0x01,\n    0x13, 0x03, 0x13, 0x02, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x2c, 0xc0, 0x30,\n    0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x33, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x35,\n    0x01, 0x00, 0x01, 0x91, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0d, 0x00, 0x00, 0x0a, 0x77, 0x77, 0x77,\n    0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, 0x67, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00,\n    0x00, 0x0a, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x01, 0x00,\n    0x01, 0x01, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e,\n    0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x05,\n    0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x6b, 0x00, 0x69, 0x00, 0x1d, 0x00,\n    0x20, 0xb0, 0xe4, 0xda, 0x34, 0xb4, 0x29, 0x8d, 0xd3, 0x5c, 0x70, 0xd3, 0xbe, 0xe8, 0xa7, 0x2a,\n    0x6b, 0xe4, 0x11, 0x19, 0x8b, 0x18, 0x9d, 0x83, 0x9a, 0x49, 0x7c, 0x83, 0x7f, 0xa9, 0x03, 0x8c,\n    0x3c, 0x00, 0x17, 0x00, 0x41, 0x04, 0x4c, 0x04, 0xa4, 0x71, 0x4c, 0x49, 0x75, 0x55, 0xd1, 0x18,\n    0x1e, 0x22, 0x62, 0x19, 0x53, 0x00, 0xde, 0x74, 0x2f, 0xb3, 0xde, 0x13, 0x54, 0xe6, 0x78, 0x07,\n    0x94, 0x55, 0x0e, 0xb2, 0x6c, 0xb0, 0x03, 0xee, 0x79, 0xa9, 0x96, 0x1e, 0x0e, 0x98, 0x17, 0x78,\n    0x24, 0x44, 0x0c, 0x88, 0x80, 0x06, 0x8b, 0xd4, 0x80, 0xbf, 0x67, 0x7c, 0x37, 0x6a, 0x5b, 0x46,\n    0x4c, 0xa7, 0x98, 0x6f, 0xb9, 0x22, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x03, 0x04, 0x03, 0x03, 0x03,\n    0x02, 0x03, 0x01, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x16, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08,\n    0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01, 0x00,\n    0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, 0x00, 0x02, 0x40, 0x01, 0x00, 0x15, 0x00, 0x96, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n    0x00, 0x00, 0x00, 0x00, 0x00\n};\n\n// Captured from Firefox 130.0.1\nstatic const unsigned char fake_clienthello_part0[] = { // 116 bytes\n    // TLS 1.2 ClientHello header (DD for length placeholder)\n    0x16, 0x03, 0x01, 0xDD, 0xDD, 0x01, 0x00, 0xDD, 0xDD, 0x03, 0x03,\n    // Random bytes (AA for placeholder)\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    // Random Session ID\n    0x20,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    // Cipher Suites\n    0x00, 0x22, 0x13, 0x01, 0x13, 0x03, 0x13, 0x02, 0xC0, 0x2B, 0xC0, 0x2F, 0xCC, 0xA9, 0xCC, 0xA8,\n    0xC0, 0x2C, 0xC0, 0x30, 0xC0, 0x0A, 0xC0, 0x09, 0xC0, 0x13, 0xC0, 0x14, 0x00, 0x9C, 0x00, 0x9D,\n    0x00, 0x2F, 0x00, 0x35,\n    // Compression Methods\n    0x01, 0x00,\n    // Extensions Length\n    0xDD, 0xDD,\n};\n// SNI: 00 00 L1 L1 L2 L2 00 L3 L3 (sni)\n// L1 = L+5, L2 = L+3, L3 = L // 9 + L bytes\nstatic const unsigned char fake_clienthello_part1[] = { // 523 bytes\n    // extended_master_secret\n    0x00, 0x17, 0x00, 0x00,\n    // renegotiation_info\n    0xFF, 0x01, 0x00, 0x01, 0x00,\n     // supported_groups\n    0x00, 0x0A, 0x00, 0x0E,\n    0x00, 0x0C, 0x00, 0x1D, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x01, 0x00, 0x01, 0x01,\n    // ex_point_formats\n    0x00, 0x0B, 0x00, 0x02, 0x01, 0x00,\n    // session_ticket\n    0x00, 0x23, 0x00, 0x00,\n     // ALPN\n    0x00, 0x10, 0x00, 0x0E,\n    0x00, 0x0C, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2F, 0x31, 0x2E, 0x31,\n    // status_request\n    0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00,\n    // delegated_credentials\n    0x00, 0x22, 0x00, 0x0A, 0x00, 0x08, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02, 0x03,\n    // key_share\n    0x00, 0x33, 0x00, 0x6B, 0x00, 0x69, 0x00, 0x1D, 0x00, 0x20,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0x00, 0x17, 0x00, 0x41, 0x04,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    // supported_versions\n    0x00, 0x2B, 0x00, 0x05, 0x04, 0x03, 0x04, 0x03, 0x03,\n    // signature_algorithms\n    0x00, 0x0D, 0x00, 0x18, 0x00, 0x16, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x04, 0x08, 0x05,\n    0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01,\n    // psk_key_exchange_modes\n    0x00, 0x2D, 0x00, 0x02, 0x01, 0x01,\n    // record_size_limit\n    0x00, 0x1C, 0x00, 0x02, 0x40, 0x01,\n    // encrypted_client_hello\n    0xFE, 0x0D, 0x01, 0x19, 0x00, 0x00, 0x01, 0x00, 0x01, 0xAA, 0x00, 0x20,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0x00, 0xEF,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,\n    0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA\n};\n// JA4: t13d1715h2_5b57614c22b0_5c2c66f702b0\n// JA4_r: t13d1715h2_002f,0035,009c,009d,1301,1302,1303,c009,c00a,c013,c014,c02b,c02c,c02f,c030,cca8,cca9_0005,000a,000b,000d,0017,001c,0022,0023,002b,002d,0033,fe0d,ff01_0403,0503,0603,0804,0805,0806,0401,0501,0601,0203,0201\n// JA3 Fullstring: 771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53,0-23-65281-10-11-35-16-5-34-51-43-13-45-28-65037,29-23-24-25-256-257,0\n// JA3: b5001237acdf006056b409cc433726b0\n\nstatic int send_fake_data(const HANDLE w_filter,\n                          const PWINDIVERT_ADDRESS addr,\n                          const char *pkt,\n                          const UINT packetLen,\n                          const BOOL is_ipv6,\n                          const BOOL is_https,\n                          const BYTE set_ttl,\n                          const BYTE set_checksum,\n                          const BYTE set_seq,\n                          const struct fake_t *fake_data\n                         ) {\n    char packet_fake[MAX_PACKET_SIZE];\n    WINDIVERT_ADDRESS addr_new;\n    PVOID packet_data;\n    UINT packet_dataLen;\n    UINT packetLen_new;\n    PWINDIVERT_IPHDR ppIpHdr;\n    PWINDIVERT_IPV6HDR ppIpV6Hdr;\n    PWINDIVERT_TCPHDR ppTcpHdr;\n    unsigned const char *fake_request_data = is_https ? fake_https_request : fake_http_request;\n    UINT fake_request_size = is_https ? sizeof(fake_https_request) : sizeof(fake_http_request) - 1;\n    if (fake_data) {\n        fake_request_data = fake_data->data;\n        fake_request_size = fake_data->size;\n    }\n\n    memcpy(&addr_new, addr, sizeof(WINDIVERT_ADDRESS));\n    memcpy(packet_fake, pkt, packetLen);\n\n    addr_new.TCPChecksum = 0;\n    addr_new.IPChecksum = 0;\n\n    if (!is_ipv6) {\n        // IPv4 TCP Data packet\n        if (!WinDivertHelperParsePacket(packet_fake, packetLen, &ppIpHdr,\n            NULL, NULL, NULL, NULL, &ppTcpHdr, NULL, &packet_data, &packet_dataLen,\n            NULL, NULL))\n            return 1;\n    }\n    else {\n        // IPv6 TCP Data packet\n        if (!WinDivertHelperParsePacket(packet_fake, packetLen, NULL,\n            &ppIpV6Hdr, NULL, NULL, NULL, &ppTcpHdr, NULL, &packet_data, &packet_dataLen,\n            NULL, NULL))\n            return 1;\n    }\n\n    if (packetLen + fake_request_size + 1 > MAX_PACKET_SIZE)\n        return 2;\n\n    memcpy(packet_data, fake_request_data, fake_request_size);\n    packetLen_new = packetLen - packet_dataLen + fake_request_size;\n\n    if (!is_ipv6) {\n        ppIpHdr->Length = htons(\n            ntohs(ppIpHdr->Length) -\n            packet_dataLen + fake_request_size\n        );\n\n        if (set_ttl)\n            ppIpHdr->TTL = set_ttl;\n    }\n    else {\n        ppIpV6Hdr->Length = htons(\n            ntohs(ppIpV6Hdr->Length) -\n            packet_dataLen + fake_request_size\n        );\n\n        if (set_ttl)\n            ppIpV6Hdr->HopLimit = set_ttl;\n    }\n\n    if (set_seq) {\n        // This is the smallest ACK drift Linux can't handle already, since at least v2.6.18.\n        // https://github.com/torvalds/linux/blob/v2.6.18/net/netfilter/nf_conntrack_proto_tcp.c#L395\n        ppTcpHdr->AckNum = htonl(ntohl(ppTcpHdr->AckNum) - 66000);\n        // This is just random, no specifics about this value.\n        ppTcpHdr->SeqNum = htonl(ntohl(ppTcpHdr->SeqNum) - 10000);\n    }\n\n    // Recalculate the checksum\n    WinDivertHelperCalcChecksums(packet_fake, packetLen_new, &addr_new, 0ULL);\n\n    if (set_checksum) {\n        // ...and damage it\n        ppTcpHdr->Checksum = htons(ntohs(ppTcpHdr->Checksum) - 1);\n    }\n    //printf(\"Pseudo checksum: %d\\n\", addr_new.TCPChecksum);\n\n    WinDivertSend(\n        w_filter, packet_fake,\n        packetLen_new,\n        NULL, &addr_new\n    );\n    debug(\"Fake packet: OK\");\n\n    return 0;\n}\n\nstatic int send_fake_request(const HANDLE w_filter,\n                                  const PWINDIVERT_ADDRESS addr,\n                                  const char *pkt,\n                                  const UINT packetLen,\n                                  const BOOL is_ipv6,\n                                  const BOOL is_https,\n                                  const BYTE set_ttl,\n                                  const BYTE set_checksum,\n                                  const BYTE set_seq,\n                                  const struct fake_t *fake_data\n                                 ) {\n    if (set_ttl) {\n        send_fake_data(w_filter, addr, pkt, packetLen,\n                          is_ipv6, is_https,\n                          set_ttl, FALSE, FALSE,\n                          fake_data);\n    }\n    if (set_checksum) {\n        send_fake_data(w_filter, addr, pkt, packetLen,\n                          is_ipv6, is_https,\n                          FALSE, set_checksum, FALSE,\n                          fake_data);\n    }\n    if (set_seq) {\n        send_fake_data(w_filter, addr, pkt, packetLen,\n                          is_ipv6, is_https,\n                          FALSE, FALSE, set_seq,\n                          fake_data);\n    }\n    return 0;\n}\n\nint send_fake_http_request(const HANDLE w_filter,\n                                  const PWINDIVERT_ADDRESS addr,\n                                  const char *pkt,\n                                  const UINT packetLen,\n                                  const BOOL is_ipv6,\n                                  const BYTE set_ttl,\n                                  const BYTE set_checksum,\n                                  const BYTE set_seq\n                                 ) {\n    int ret = 0;\n    for (int i=0; i<fakes_count || i == 0; i++) {\n        for (int j=0; j<fakes_resend; j++)\n            if (send_fake_request(w_filter, addr, pkt, packetLen,\n                            is_ipv6, FALSE,\n                            set_ttl, set_checksum, set_seq,\n                            fakes[i]))\n            {\n                ret++;\n            }\n    }\n    return ret;\n}\n\nint send_fake_https_request(const HANDLE w_filter,\n                                   const PWINDIVERT_ADDRESS addr,\n                                   const char *pkt,\n                                   const UINT packetLen,\n                                   const BOOL is_ipv6,\n                                   const BYTE set_ttl,\n                                   const BYTE set_checksum,\n                                   const BYTE set_seq\n                                 ) {\n    int ret = 0;\n    for (int i=0; i<fakes_count || i == 0; i++) {\n        for (int j=0; j<fakes_resend; j++)\n            if (send_fake_request(w_filter, addr, pkt, packetLen,\n                          is_ipv6, TRUE,\n                          set_ttl, set_checksum, set_seq,\n                          fakes[i]))\n            {\n                ret++;\n            }\n    }\n    return ret;\n}\n\nstatic int fake_add(const unsigned char *data, size_t size) {\n    struct fake_t *fake = malloc(sizeof(struct fake_t));\n    fake->size = size;\n    fake->data = data;\n\n    for (size_t k = 0; k <= sizeof(fakes) / sizeof(*fakes); k++) {\n        if (!fakes[k]) {\n            fakes[k] = fake;\n            fakes_count++;\n            return 0;\n        }\n    }\n    return 3;\n}\n\nint fake_load_from_hex(const char *data) {\n    size_t len = strlen(data);\n    if (len < 2 || len % 2 || len > (1420 * 2))\n        return 1;\n\n    unsigned char *finaldata = calloc((len + 2) / 2, 1);\n\n    for (size_t i = 0; i<len - 1; i+=2) {\n        char num1 = data[i];\n        char num2 = data[i+1];\n        debug(\"Current num1: %X, num2: %X\\n\", num1, num2);\n        unsigned char finalchar = 0;\n        char curchar = num1;\n\n        for (int j=0; j<=1; j++) {\n            if (curchar >= '0' && curchar <= '9')\n                curchar -= '0';\n            else if (curchar >= 'a' && curchar <= 'f')\n                curchar -= 'a' - 0xA;\n            else if (curchar >= 'A' && curchar <= 'F')\n                curchar -= 'A' - 0xA;\n            else\n                return 2; // incorrect character, not a hex data\n\n            if (!j) {\n                num1 = curchar;\n                curchar = num2;\n                continue;\n            }\n            num2 = curchar;\n        }\n        debug(\"Processed num1: %X, num2: %X\\n\", num1, num2);\n        finalchar = (num1 << 4) | num2;\n        debug(\"Final char: %X\\n\", finalchar);\n        finaldata[i/2] = finalchar;\n    }\n\n    return fake_add(finaldata, len / 2);\n}\n\nint fake_load_random(unsigned int count, unsigned int maxsize) {\n    if (count < 1 || count > sizeof(fakes) / sizeof(*fakes))\n        return 1;\n\n    unsigned int random = 0;\n\n    for (unsigned int i=0; i<count; i++) {\n        unsigned int len = 0;\n        if (rand_s(&len))\n            return 1;\n        len = 8 + (len % maxsize);\n\n        unsigned char *data = calloc(len, 1);\n        for (unsigned int j=0; j<len; j++) {\n            rand_s(&random);\n            data[j] = random % 0xFF;\n        }\n        if (fake_add(data, len))\n            return 2;\n    }\n    return 0;\n}\n\nvoid set_uint16be(unsigned char *buffer, int offset, int value) {\n    buffer[offset] = (value >> 8) & 0xFF;\n    buffer[offset + 1] = value & 0xFF;\n}\n\nint fake_load_from_sni(const char *domain_name) {\n    if (!domain_name) {\n        return 1; // just extra safeguard against NPE\n    }\n    // calculate sizes\n    const int name_size = strlen(domain_name);\n    const int part0_size = sizeof(fake_clienthello_part0);\n    const int part1_size = sizeof(fake_clienthello_part1);\n    const int sni_head_size = 9;\n    const int packet_size = part0_size + part1_size + sni_head_size + name_size;\n    // allocate memory\n    unsigned char *packet = malloc(packet_size);\n    // copy major parts of packet\n    memcpy(packet, fake_clienthello_part0, part0_size);\n    memcpy(&packet[part0_size + sni_head_size + name_size], fake_clienthello_part1, part1_size);\n    // replace placeholders with random generated values\n    unsigned int random = 0;\n    for (int i = 0; i < packet_size; i++) {\n        if (packet[i] == 0xAA) {\n            rand_s(&random);\n            packet[i] = random & 0xFF;\n        }\n    }\n    // write size fields into packet\n    set_uint16be(packet, 0x0003, packet_size - 5);\n    set_uint16be(packet, 0x0007, packet_size - 9);\n    set_uint16be(packet, 0x0072, packet_size - 116);\n    // write SNI extension\n    set_uint16be(packet, part0_size + 0, 0x0000);\n    set_uint16be(packet, part0_size + 2, name_size + 5);\n    set_uint16be(packet, part0_size + 4, name_size + 3);\n    packet[part0_size + 6] = 0x00;\n    set_uint16be(packet, part0_size + 7, name_size);\n    memcpy(&packet[part0_size + sni_head_size], domain_name, name_size);\n    // add packet to fakes\n    return fake_add(packet, packet_size);\n}\n"
  },
  {
    "path": "src/fakepackets.h",
    "content": "extern int fakes_count;\nextern int fakes_resend;\nint send_fake_http_request(const HANDLE w_filter,\n                                  const PWINDIVERT_ADDRESS addr,\n                                  const char *pkt,\n                                  const UINT packetLen,\n                                  const BOOL is_ipv6,\n                                  const BYTE set_ttl,\n                                  const BYTE set_checksum,\n                                  const BYTE set_seq\n                                 );\nint send_fake_https_request(const HANDLE w_filter,\n                                   const PWINDIVERT_ADDRESS addr,\n                                   const char *pkt,\n                                   const UINT packetLen,\n                                   const BOOL is_ipv6,\n                                   const BYTE set_ttl,\n                                   const BYTE set_checksum,\n                                   const BYTE set_seq\n                                 );\nint fake_load_from_hex(const char *data);\nint fake_load_from_sni(const char *domain_name);\nint fake_load_random(unsigned int count, unsigned int maxsize);\n"
  },
  {
    "path": "src/goodbyedpi-rc.rc",
    "content": "1 24 \"goodbyedpi.exe.manifest\" \nid ICON \"icon.ico\"\n"
  },
  {
    "path": "src/goodbyedpi.c",
    "content": "/*\n * GoodbyeDPI — Passive DPI blocker and Active DPI circumvention utility.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdbool.h>\n#include <ctype.h>\n#include <signal.h>\n#include <unistd.h>\n#include <string.h>\n#include <getopt.h>\n#include <in6addr.h>\n#include <ws2tcpip.h>\n#include \"windivert.h\"\n#include \"goodbyedpi.h\"\n#include \"utils/repl_str.h\"\n#include \"service.h\"\n#include \"dnsredir.h\"\n#include \"ttltrack.h\"\n#include \"blackwhitelist.h\"\n#include \"fakepackets.h\"\n\n// My mingw installation does not load inet_pton definition for some reason\nWINSOCK_API_LINKAGE INT WSAAPI inet_pton(INT Family, LPCSTR pStringBuf, PVOID pAddr);\n\n#define GOODBYEDPI_VERSION \"v0.2.3rc3\"\n\n#define die() do { sleep(20); exit(EXIT_FAILURE); } while (0)\n\n#define MAX_FILTERS 4\n\n#define DIVERT_NO_LOCALNETSv4_DST \"(\" \\\n                   \"(ip.DstAddr < 127.0.0.1 or ip.DstAddr > 127.255.255.255) and \" \\\n                   \"(ip.DstAddr < 10.0.0.0 or ip.DstAddr > 10.255.255.255) and \" \\\n                   \"(ip.DstAddr < 192.168.0.0 or ip.DstAddr > 192.168.255.255) and \" \\\n                   \"(ip.DstAddr < 172.16.0.0 or ip.DstAddr > 172.31.255.255) and \" \\\n                   \"(ip.DstAddr < 169.254.0.0 or ip.DstAddr > 169.254.255.255)\" \\\n                   \")\"\n#define DIVERT_NO_LOCALNETSv4_SRC \"(\" \\\n                   \"(ip.SrcAddr < 127.0.0.1 or ip.SrcAddr > 127.255.255.255) and \" \\\n                   \"(ip.SrcAddr < 10.0.0.0 or ip.SrcAddr > 10.255.255.255) and \" \\\n                   \"(ip.SrcAddr < 192.168.0.0 or ip.SrcAddr > 192.168.255.255) and \" \\\n                   \"(ip.SrcAddr < 172.16.0.0 or ip.SrcAddr > 172.31.255.255) and \" \\\n                   \"(ip.SrcAddr < 169.254.0.0 or ip.SrcAddr > 169.254.255.255)\" \\\n                   \")\"\n\n#define DIVERT_NO_LOCALNETSv6_DST \"(\" \\\n                   \"(ipv6.DstAddr > ::1) and \" \\\n                   \"(ipv6.DstAddr < 2001::0 or ipv6.DstAddr > 2001:1::0) and \" \\\n                   \"(ipv6.DstAddr < fc00::0 or ipv6.DstAddr > fe00::0) and \" \\\n                   \"(ipv6.DstAddr < fe80::0 or ipv6.DstAddr > fec0::0) and \" \\\n                   \"(ipv6.DstAddr < ff00::0 or ipv6.DstAddr > ffff::0)\" \\\n                   \")\"\n#define DIVERT_NO_LOCALNETSv6_SRC \"(\" \\\n                   \"(ipv6.SrcAddr > ::1) and \" \\\n                   \"(ipv6.SrcAddr < 2001::0 or ipv6.SrcAddr > 2001:1::0) and \" \\\n                   \"(ipv6.SrcAddr < fc00::0 or ipv6.SrcAddr > fe00::0) and \" \\\n                   \"(ipv6.SrcAddr < fe80::0 or ipv6.SrcAddr > fec0::0) and \" \\\n                   \"(ipv6.SrcAddr < ff00::0 or ipv6.SrcAddr > ffff::0)\" \\\n                   \")\"\n\n/* #IPID# is a template to find&replace */\n#define IPID_TEMPLATE \"#IPID#\"\n#define MAXPAYLOADSIZE_TEMPLATE \"#MAXPAYLOADSIZE#\"\n#define FILTER_STRING_TEMPLATE \\\n        \"(tcp and !impostor and !loopback \" MAXPAYLOADSIZE_TEMPLATE \" and \" \\\n        \"((inbound and (\" \\\n         \"(\" \\\n          \"(\" \\\n           \"(ipv6 or (ip.Id >= 0x0 and ip.Id <= 0xF) \" IPID_TEMPLATE \\\n           \") and \" \\\n           \"tcp.SrcPort == 80 and tcp.Ack\" \\\n          \") or \" \\\n          \"((tcp.SrcPort == 80 or tcp.SrcPort == 443) and tcp.Ack and tcp.Syn)\" \\\n         \")\" \\\n         \" and (\" DIVERT_NO_LOCALNETSv4_SRC \" or \" DIVERT_NO_LOCALNETSv6_SRC \"))) or \" \\\n        \"(outbound and \" \\\n         \"(tcp.DstPort == 80 or tcp.DstPort == 443) and tcp.Ack and \" \\\n         \"(\" DIVERT_NO_LOCALNETSv4_DST \" or \" DIVERT_NO_LOCALNETSv6_DST \"))\" \\\n        \"))\"\n#define FILTER_PASSIVE_BLOCK_QUIC \"outbound and !impostor and !loopback and udp \" \\\n        \"and udp.DstPort == 443 and udp.PayloadLength >= 1200 \" \\\n        \"and udp.Payload[0] >= 0xC0 and udp.Payload32[1b] == 0x01\"\n#define FILTER_PASSIVE_STRING_TEMPLATE \"inbound and ip and tcp and \" \\\n        \"!impostor and !loopback and \" \\\n        \"(true \" IPID_TEMPLATE \") and \" \\\n        \"(tcp.SrcPort == 443 or tcp.SrcPort == 80) and tcp.Rst and \" \\\n        DIVERT_NO_LOCALNETSv4_SRC\n\n#define SET_HTTP_FRAGMENT_SIZE_OPTION(fragment_size) do { \\\n    if (!http_fragment_size) { \\\n        http_fragment_size = (unsigned int)fragment_size; \\\n    } \\\n    else if (http_fragment_size != (unsigned int)fragment_size) { \\\n        printf( \\\n            \"WARNING: HTTP fragment size is already set to %u, not changing.\\n\", \\\n            http_fragment_size \\\n        ); \\\n    } \\\n} while (0)\n\n#define TCP_HANDLE_OUTGOING_TTL_PARSE_PACKET_IF() \\\n    if ((packet_v4 && tcp_handle_outgoing(&ppIpHdr->SrcAddr, &ppIpHdr->DstAddr, \\\n                        ppTcpHdr->SrcPort, ppTcpHdr->DstPort, \\\n                        &tcp_conn_info, 0)) \\\n        || \\\n        (packet_v6 && tcp_handle_outgoing(ppIpV6Hdr->SrcAddr, ppIpV6Hdr->DstAddr, \\\n                        ppTcpHdr->SrcPort, ppTcpHdr->DstPort, \\\n                        &tcp_conn_info, 1)))\n\n#define TCP_HANDLE_OUTGOING_FAKE_PACKET(func) do { \\\n    should_send_fake = 1; \\\n    if (do_auto_ttl || ttl_min_nhops) { \\\n        TCP_HANDLE_OUTGOING_TTL_PARSE_PACKET_IF() { \\\n            if (do_auto_ttl) { \\\n                /* If Auto TTL mode */ \\\n                ttl_of_fake_packet = tcp_get_auto_ttl(tcp_conn_info.ttl, auto_ttl_1, auto_ttl_2, \\\n                                                      ttl_min_nhops, auto_ttl_max); \\\n                if (do_tcp_verb) { \\\n                    printf(\"Connection TTL = %d, Fake TTL = %d\\n\", tcp_conn_info.ttl, ttl_of_fake_packet); \\\n                } \\\n            } \\\n            else if (ttl_min_nhops) { \\\n                /* If not Auto TTL mode but --min-ttl is set */ \\\n                if (!tcp_get_auto_ttl(tcp_conn_info.ttl, 0, 0, ttl_min_nhops, 0)) { \\\n                    /* Send only if nhops >= min_ttl */ \\\n                    should_send_fake = 0; \\\n                } \\\n            } \\\n        } \\\n    } \\\n    if (should_send_fake) \\\n        func(w_filter, &addr, packet, packetLen, packet_v6, \\\n             ttl_of_fake_packet, do_wrong_chksum, do_wrong_seq); \\\n} while (0)\n\nenum ERROR_CODE{\n    ERROR_DEFAULT = 1,\n    ERROR_PORT_BOUNDS,\n    ERROR_DNS_V4_ADDR,\n    ERROR_DNS_V6_ADDR,\n    ERROR_DNS_V4_PORT,\n    ERROR_DNS_V6_PORT,\n    ERROR_BLACKLIST_LOAD,\n    ERROR_AUTOTTL,\n    ERROR_ATOUSI,\n    ERROR_AUTOB\n};\n\nstatic int running_from_service = 0;\nstatic int exiting = 0;\nstatic HANDLE filters[MAX_FILTERS];\nstatic int filter_num = 0;\nstatic const char http10_redirect_302[] = \"HTTP/1.0 302 \";\nstatic const char http11_redirect_302[] = \"HTTP/1.1 302 \";\nstatic const char http_host_find[] = \"\\r\\nHost: \";\nstatic const char http_host_replace[] = \"\\r\\nhoSt: \";\nstatic const char http_useragent_find[] = \"\\r\\nUser-Agent: \";\nstatic const char location_http[] = \"\\r\\nLocation: http://\";\nstatic const char connection_close[] = \"\\r\\nConnection: close\";\nstatic const char *http_methods[] = {\n    \"GET \",\n    \"HEAD \",\n    \"POST \",\n    \"PUT \",\n    \"DELETE \",\n    \"CONNECT \",\n    \"OPTIONS \",\n};\n\nstatic struct option long_options[] = {\n    {\"port\",        required_argument, 0,  'z' },\n    {\"dns-addr\",    required_argument, 0,  'd' },\n    {\"dns-port\",    required_argument, 0,  'g' },\n    {\"dnsv6-addr\",  required_argument, 0,  '!' },\n    {\"dnsv6-port\",  required_argument, 0,  '@' },\n    {\"dns-verb\",    no_argument,       0,  'v' },\n    {\"blacklist\",   required_argument, 0,  'b' },\n    {\"allow-no-sni\",no_argument,       0,  ']' },\n    {\"frag-by-sni\", no_argument,       0,  '>' },\n    {\"ip-id\",       required_argument, 0,  'i' },\n    {\"set-ttl\",     required_argument, 0,  '$' },\n    {\"min-ttl\",     required_argument, 0,  '[' },\n    {\"auto-ttl\",    optional_argument, 0,  '+' },\n    {\"wrong-chksum\",no_argument,       0,  '%' },\n    {\"wrong-seq\",   no_argument,       0,  ')' },\n    {\"native-frag\", no_argument,       0,  '*' },\n    {\"reverse-frag\",no_argument,       0,  '(' },\n    {\"max-payload\", optional_argument, 0,  '|' },\n    {\"fake-from-hex\", required_argument, 0,  'u' },\n    {\"fake-with-sni\", required_argument, 0,  '}' },\n    {\"fake-gen\",    required_argument, 0,  'j' },\n    {\"fake-resend\", required_argument, 0,  't' },\n    {\"debug-exit\",  optional_argument, 0,  'x' },\n    {0,             0,                 0,   0  }\n};\n\nstatic char *filter_string = NULL;\nstatic char *filter_passive_string = NULL;\n\nstatic void add_filter_str(int proto, int port) {\n    const char *udp = \" or (udp and !impostor and !loopback and \" \\\n                      \"(udp.SrcPort == %d or udp.DstPort == %d))\";\n    const char *tcp = \" or (tcp and !impostor and !loopback \" MAXPAYLOADSIZE_TEMPLATE \" and \" \\\n                      \"(tcp.SrcPort == %d or tcp.DstPort == %d))\";\n\n    char *current_filter = filter_string;\n    size_t new_filter_size = strlen(current_filter) +\n            (proto == IPPROTO_UDP ? strlen(udp) : strlen(tcp)) + 16;\n    char *new_filter = malloc(new_filter_size);\n\n    strcpy(new_filter, current_filter);\n    if (proto == IPPROTO_UDP)\n        sprintf(new_filter + strlen(new_filter), udp, port, port);\n    else\n        sprintf(new_filter + strlen(new_filter), tcp, port, port);\n\n    filter_string = new_filter;\n    free(current_filter);\n}\n\nstatic void add_ip_id_str(int id) {\n    char *newstr;\n    const char *ipid = \" or ip.Id == %d\";\n    char *addfilter = malloc(strlen(ipid) + 16);\n\n    sprintf(addfilter, ipid, id);\n\n    newstr = repl_str(filter_string, IPID_TEMPLATE, addfilter);\n    free(filter_string);\n    filter_string = newstr;\n\n    newstr = repl_str(filter_passive_string, IPID_TEMPLATE, addfilter);\n    free(filter_passive_string);\n    filter_passive_string = newstr;\n}\n\nstatic void add_maxpayloadsize_str(unsigned short maxpayload) {\n    char *newstr;\n    /* 0x47455420 is \"GET \", 0x504F5354 is \"POST\", big endian. */\n    const char *maxpayloadsize_str =\n        \"and (tcp.PayloadLength ? tcp.PayloadLength < %hu \" \\\n          \"or tcp.Payload32[0] == 0x47455420 or tcp.Payload32[0] == 0x504F5354 \" \\\n          \"or (tcp.Payload[0] == 0x16 and tcp.Payload[1] == 0x03 and tcp.Payload[2] <= 0x03): true)\";\n    char *addfilter = malloc(strlen(maxpayloadsize_str) + 16);\n\n    sprintf(addfilter, maxpayloadsize_str, maxpayload);\n\n    newstr = repl_str(filter_string, MAXPAYLOADSIZE_TEMPLATE, addfilter);\n    free(filter_string);\n    filter_string = newstr;\n}\n\nstatic void finalize_filter_strings() {\n    char *newstr, *newstr2;\n\n    newstr2 = repl_str(filter_string, IPID_TEMPLATE, \"\");\n    newstr = repl_str(newstr2, MAXPAYLOADSIZE_TEMPLATE, \"\");\n    free(filter_string);\n    free(newstr2);\n    filter_string = newstr;\n\n    newstr = repl_str(filter_passive_string, IPID_TEMPLATE, \"\");\n    free(filter_passive_string);\n    filter_passive_string = newstr;\n}\n\nstatic char* dumb_memmem(const char* haystack, unsigned int hlen,\n                         const char* needle, unsigned int nlen)\n{\n    // naive implementation\n    if (nlen > hlen) return NULL;\n    size_t i;\n    for (i=0; i<hlen-nlen+1; i++) {\n        if (memcmp(haystack+i,needle,nlen)==0) {\n            return (char*)(haystack+i);\n        }\n    }\n    return NULL;\n}\n\nunsigned short int atousi(const char *str, const char *msg) {\n    long unsigned int res = strtoul(str, NULL, 10u);\n    enum {\n        limitValue=0xFFFFu\n    };\n\n    if(res > limitValue) {\n        puts(msg);\n        exit(ERROR_ATOUSI);\n    }\n    return (unsigned short int)res;\n}\n\nBYTE atoub(const char *str, const char *msg) {\n    long unsigned int res = strtoul(str, NULL, 10u);\n    enum {\n        limitValue=0xFFu\n    };\n\n    if(res > limitValue) {\n        puts(msg);\n        exit(ERROR_AUTOB);\n    }\n    return (BYTE)res;\n}\n\n\nstatic HANDLE init(char *filter, UINT64 flags) {\n    LPTSTR errormessage = NULL;\n    DWORD errorcode = 0;\n    filter = WinDivertOpen(filter, WINDIVERT_LAYER_NETWORK, 0, flags);\n    if (filter != INVALID_HANDLE_VALUE)\n        return filter;\n    errorcode = GetLastError();\n    FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |\n                  FORMAT_MESSAGE_IGNORE_INSERTS,\n                  NULL, errorcode, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),\n                  (LPTSTR)&errormessage, 0, NULL);\n    printf(\"Error opening filter: %d %s\\n\", errorcode, errormessage);\n    LocalFree(errormessage);\n    if (errorcode == 2)\n        printf(\"The driver files WinDivert32.sys or WinDivert64.sys were not found.\\n\");\n    else if (errorcode == 654)\n        printf(\"An incompatible version of the WinDivert driver is currently loaded.\\n\"\n               \"Please unload it with the following commands ran as administrator:\\n\\n\"\n               \"sc stop windivert\\n\"\n               \"sc delete windivert\\n\"\n               \"sc stop windivert14\\n\"\n               \"sc delete windivert14\\n\");\n    else if (errorcode == 1275)\n        printf(\"This error occurs for various reasons, including:\\n\"\n               \"the WinDivert driver is blocked by security software; or\\n\"\n               \"you are using a virtualization environment that does not support drivers.\\n\");\n    else if (errorcode == 1753)\n        printf(\"This error occurs when the Base Filtering Engine service has been disabled.\\n\"\n               \"Enable Base Filtering Engine service.\\n\");\n    else if (errorcode == 577)\n        printf(\"Could not load driver due to invalid digital signature.\\n\"\n               \"Windows Server 2016 systems must have secure boot disabled to be \\n\"\n               \"able to load WinDivert driver.\\n\"\n               \"Windows 7 systems must be up-to-date or at least have KB3033929 installed.\\n\"\n               \"https://www.microsoft.com/en-us/download/details.aspx?id=46078\\n\\n\"\n               \"WARNING! If you see this error on Windows 7, it means your system is horribly \"\n               \"outdated and SHOULD NOT BE USED TO ACCESS THE INTERNET!\\n\"\n               \"Most probably, you don't have security patches installed and anyone in you LAN or \"\n               \"public Wi-Fi network can get full access to your computer (MS17-010 and others).\\n\"\n               \"You should install updates IMMEDIATELY.\\n\");\n    return NULL;\n}\n\nstatic int deinit(HANDLE handle) {\n    if (handle) {\n        WinDivertShutdown(handle, WINDIVERT_SHUTDOWN_BOTH);\n        WinDivertClose(handle);\n        return TRUE;\n    }\n    return FALSE;\n}\n\nvoid deinit_all() {\n    for (int i = 0; i < filter_num; i++) {\n        deinit(filters[i]);\n    }\n}\n\nstatic void sigint_handler(int sig __attribute__((unused))) {\n    exiting = 1;\n    deinit_all();\n    exit(EXIT_SUCCESS);\n}\n\nstatic void mix_case(char *pktdata, unsigned int pktlen) {\n    unsigned int i;\n\n    if (pktlen <= 0) return;\n    for (i = 0; i < pktlen; i++) {\n        if (i % 2) {\n            pktdata[i] = (char) toupper(pktdata[i]);\n        }\n    }\n}\n\nstatic int is_passivedpi_redirect(const char *pktdata, unsigned int pktlen) {\n    /* First check if this is HTTP 302 redirect */\n    if (memcmp(pktdata, http11_redirect_302, sizeof(http11_redirect_302)-1) == 0 ||\n        memcmp(pktdata, http10_redirect_302, sizeof(http10_redirect_302)-1) == 0)\n    {\n        /* Then check if this is a redirect to new http site with Connection: close */\n        if (dumb_memmem(pktdata, pktlen, location_http, sizeof(location_http)-1) &&\n            dumb_memmem(pktdata, pktlen, connection_close, sizeof(connection_close)-1)) {\n            return TRUE;\n        }\n    }\n    return FALSE;\n}\n\nstatic int find_header_and_get_info(const char *pktdata, unsigned int pktlen,\n                const char *hdrname,\n                char **hdrnameaddr,\n                char **hdrvalueaddr, unsigned int *hdrvaluelen) {\n    char *data_addr_rn;\n    char *hdr_begin;\n\n    *hdrvaluelen = 0u;\n    *hdrnameaddr = NULL;\n    *hdrvalueaddr = NULL;\n\n    /* Search for the header */\n    hdr_begin = dumb_memmem(pktdata, pktlen,\n                hdrname, strlen(hdrname));\n    if (!hdr_begin) return FALSE;\n    if (pktdata > hdr_begin) return FALSE;\n\n    /* Set header address */\n    *hdrnameaddr = hdr_begin;\n    *hdrvalueaddr = hdr_begin + strlen(hdrname);\n\n    /* Search for header end (\\r\\n) */\n    data_addr_rn = dumb_memmem(*hdrvalueaddr,\n                        pktlen - (uintptr_t)(*hdrvalueaddr - pktdata),\n                        \"\\r\\n\", 2);\n    if (data_addr_rn) {\n        *hdrvaluelen = (uintptr_t)(data_addr_rn - *hdrvalueaddr);\n        if (*hdrvaluelen >= 3 && *hdrvaluelen <= HOST_MAXLEN)\n            return TRUE;\n    }\n    return FALSE;\n}\n\n/**\n * Very crude Server Name Indication (TLS ClientHello hostname) extractor.\n */\nstatic int extract_sni(const char *pktdata, unsigned int pktlen,\n                    char **hostnameaddr, unsigned int *hostnamelen) {\n    unsigned int ptr = 0;\n    unsigned const char *d = (unsigned const char *)pktdata;\n    unsigned const char *hnaddr = 0;\n    int hnlen = 0;\n\n    while (ptr + 8 < pktlen) {\n        /* Search for specific Extensions sequence */\n        if (d[ptr] == '\\0' && d[ptr+1] == '\\0' && d[ptr+2] == '\\0' &&\n            d[ptr+4] == '\\0' && d[ptr+6] == '\\0' && d[ptr+7] == '\\0' &&\n            /* Check Extension length, Server Name list length\n            *  and Server Name length relations\n            */\n            d[ptr+3] - d[ptr+5] == 2 && d[ptr+5] - d[ptr+8] == 3)\n            {\n                if (ptr + 8 + d[ptr+8] > pktlen) {\n                    return FALSE;\n                }\n                hnaddr = &d[ptr+9];\n                hnlen = d[ptr+8];\n                /* Limit hostname size up to 253 bytes */\n                if (hnlen < 3 || hnlen > HOST_MAXLEN) {\n                    return FALSE;\n                }\n                /* Validate that hostname has only ascii lowercase characters */\n                for (int i=0; i<hnlen; i++) {\n                    if (!( (hnaddr[i] >= '0' && hnaddr[i] <= '9') ||\n                         (hnaddr[i] >= 'a' && hnaddr[i] <= 'z') ||\n                         hnaddr[i] == '.' || hnaddr[i] == '-'))\n                    {\n                        return FALSE;\n                    }\n                }\n                *hostnameaddr = (char*)hnaddr;\n                *hostnamelen = (unsigned int)hnlen;\n                return TRUE;\n            }\n        ptr++;\n    }\n    return FALSE;\n}\n\nstatic inline void change_window_size(const PWINDIVERT_TCPHDR ppTcpHdr, unsigned int size) {\n    if (size >= 1 && size <= 0xFFFFu) {\n        ppTcpHdr->Window = htons((u_short)size);\n    }\n}\n\n/* HTTP method end without trailing space */\nstatic PVOID find_http_method_end(const char *pkt, unsigned int http_frag, int *is_fragmented) {\n    unsigned int i;\n    for (i = 0; i<(sizeof(http_methods) / sizeof(*http_methods)); i++) {\n        if (memcmp(pkt, http_methods[i], strlen(http_methods[i])) == 0) {\n            if (is_fragmented)\n                *is_fragmented = 0;\n            return (char*)pkt + strlen(http_methods[i]) - 1;\n        }\n        /* Try to find HTTP method in a second part of fragmented packet */\n        if ((http_frag == 1 || http_frag == 2) &&\n            memcmp(pkt, http_methods[i] + http_frag,\n                   strlen(http_methods[i]) - http_frag) == 0\n           )\n        {\n            if (is_fragmented)\n                *is_fragmented = 1;\n            return (char*)pkt + strlen(http_methods[i]) - http_frag - 1;\n        }\n    }\n    return NULL;\n}\n\n/** Fragment and send the packet.\n *\n * This function cuts off the end of the packet (step=0) or\n * the beginning of the packet (step=1) with fragment_size bytes.\n */\nstatic void send_native_fragment(HANDLE w_filter, WINDIVERT_ADDRESS addr,\n                        char *packet, UINT packetLen, PVOID packet_data,\n                        UINT packet_dataLen, int packet_v4, int packet_v6,\n                        PWINDIVERT_IPHDR ppIpHdr, PWINDIVERT_IPV6HDR ppIpV6Hdr,\n                        PWINDIVERT_TCPHDR ppTcpHdr,\n                        unsigned int fragment_size, int step) {\n    char packet_bak[MAX_PACKET_SIZE];\n    memcpy(packet_bak, packet, packetLen);\n    UINT orig_packetLen = packetLen;\n\n    if (fragment_size >= packet_dataLen) {\n        if (step == 1)\n            fragment_size = 0;\n        else\n            return;\n    }\n\n    if (step == 0) {\n        if (packet_v4)\n            ppIpHdr->Length = htons(\n                ntohs(ppIpHdr->Length) -\n                packet_dataLen + fragment_size\n            );\n        else if (packet_v6)\n            ppIpV6Hdr->Length = htons(\n                ntohs(ppIpV6Hdr->Length) -\n                packet_dataLen + fragment_size\n            );\n        //printf(\"step0 (%d:%d), pp:%d, was:%d, now:%d\\n\",\n        //                packet_v4, packet_v6, ntohs(ppIpHdr->Length),\n        //                packetLen, packetLen - packet_dataLen + fragment_size);\n        packetLen = packetLen - packet_dataLen + fragment_size;\n    }\n\n    else if (step == 1) {\n        if (packet_v4)\n            ppIpHdr->Length = htons(\n                ntohs(ppIpHdr->Length) - fragment_size\n            );\n        else if (packet_v6)\n            ppIpV6Hdr->Length = htons(\n                ntohs(ppIpV6Hdr->Length) - fragment_size\n            );\n        //printf(\"step1 (%d:%d), pp:%d, was:%d, now:%d\\n\", packet_v4, packet_v6, ntohs(ppIpHdr->Length),\n        //                packetLen, packetLen - fragment_size);\n        memmove(packet_data,\n                (char*)packet_data + fragment_size,\n                packet_dataLen - fragment_size);\n        packetLen -= fragment_size;\n\n        ppTcpHdr->SeqNum = htonl(ntohl(ppTcpHdr->SeqNum) + fragment_size);\n    }\n\n    addr.IPChecksum = 0;\n    addr.TCPChecksum = 0;\n\n    WinDivertHelperCalcChecksums(\n        packet, packetLen, &addr, 0\n    );\n    WinDivertSend(\n        w_filter, packet,\n        packetLen,\n        NULL, &addr\n    );\n    memcpy(packet, packet_bak, orig_packetLen);\n    //printf(\"Sent native fragment of %d size (step%d)\\n\", packetLen, step);\n}\n\nint main(int argc, char *argv[]) {\n    static enum packet_type_e {\n        unknown,\n        ipv4_tcp, ipv4_tcp_data, ipv4_udp_data,\n        ipv6_tcp, ipv6_tcp_data, ipv6_udp_data\n    } packet_type;\n    bool debug_exit = false;\n    int i, should_reinject, should_recalc_checksum = 0;\n    int sni_ok = 0;\n    int opt;\n    int packet_v4, packet_v6;\n    HANDLE w_filter = NULL;\n    WINDIVERT_ADDRESS addr;\n    char packet[MAX_PACKET_SIZE];\n    PVOID packet_data;\n    UINT packetLen;\n    UINT packet_dataLen;\n    PWINDIVERT_IPHDR ppIpHdr;\n    PWINDIVERT_IPV6HDR ppIpV6Hdr;\n    PWINDIVERT_TCPHDR ppTcpHdr;\n    PWINDIVERT_UDPHDR ppUdpHdr;\n    conntrack_info_t dns_conn_info;\n    tcp_conntrack_info_t tcp_conn_info;\n\n    int do_passivedpi = 0, do_block_quic = 0,\n        do_fragment_http = 0,\n        do_fragment_http_persistent = 0,\n        do_fragment_http_persistent_nowait = 0,\n        do_fragment_https = 0, do_host = 0,\n        do_host_removespace = 0, do_additional_space = 0,\n        do_http_allports = 0,\n        do_host_mixedcase = 0,\n        do_dnsv4_redirect = 0, do_dnsv6_redirect = 0,\n        do_dns_verb = 0, do_tcp_verb = 0, do_blacklist = 0,\n        do_allow_no_sni = 0,\n        do_fragment_by_sni = 0,\n        do_fake_packet = 0,\n        do_auto_ttl = 0,\n        do_wrong_chksum = 0,\n        do_wrong_seq = 0,\n        do_native_frag = 0, do_reverse_frag = 0;\n    unsigned int http_fragment_size = 0;\n    unsigned int https_fragment_size = 0;\n    unsigned int current_fragment_size = 0;\n    unsigned short max_payload_size = 0;\n    BYTE should_send_fake = 0;\n    BYTE ttl_of_fake_packet = 0;\n    BYTE ttl_min_nhops = 0;\n    BYTE auto_ttl_1 = 0;\n    BYTE auto_ttl_2 = 0;\n    BYTE auto_ttl_max = 0;\n    uint32_t dnsv4_addr = 0;\n    struct in6_addr dnsv6_addr = {0};\n    struct in6_addr dns_temp_addr = {0};\n    uint16_t dnsv4_port = htons(53);\n    uint16_t dnsv6_port = htons(53);\n    char *host_addr, *useragent_addr, *method_addr;\n    unsigned int host_len, useragent_len;\n    int http_req_fragmented;\n\n    char *hdr_name_addr = NULL, *hdr_value_addr = NULL;\n    unsigned int hdr_value_len;\n\n    // Make sure to search DLLs only in safe path, not in current working dir.\n    SetDllDirectory(\"\");\n    SetSearchPathMode(BASE_SEARCH_PATH_ENABLE_SAFE_SEARCHMODE | BASE_SEARCH_PATH_PERMANENT);\n\n    if (!running_from_service) {\n        running_from_service = 1;\n        if (service_register(argc, argv)) {\n            /* We've been called as a service. Register service\n             * and exit this thread. main() would be called from\n             * service.c next time.\n             *\n             * Note that if service_register() succeedes it does\n             * not return until the service is stopped.\n             * That is why we should set running_from_service\n             * before calling service_register and unset it\n             * afterwards.\n             */\n            return 0;\n        }\n        running_from_service = 0;\n    }\n\n    if (filter_string == NULL)\n        filter_string = strdup(FILTER_STRING_TEMPLATE);\n    if (filter_passive_string == NULL)\n        filter_passive_string = strdup(FILTER_PASSIVE_STRING_TEMPLATE);\n\n    printf(\n        \"GoodbyeDPI \" GOODBYEDPI_VERSION\n        \": Passive DPI blocker and Active DPI circumvention utility\\n\"\n        \"https://github.com/ValdikSS/GoodbyeDPI\\n\\n\"\n    );\n\n    if (argc == 1) {\n        /* enable mode -9 by default */\n        do_fragment_http = do_fragment_https = 1;\n        do_reverse_frag = do_native_frag = 1;\n        http_fragment_size = https_fragment_size = 2;\n        do_fragment_http_persistent = do_fragment_http_persistent_nowait = 1;\n        do_fake_packet = 1;\n        do_wrong_chksum = 1;\n        do_wrong_seq = 1;\n        do_block_quic = 1;\n        max_payload_size = 1200;\n    }\n\n    while ((opt = getopt_long(argc, argv, \"123456789pqrsaf:e:mwk:n\", long_options, NULL)) != -1) {\n        switch (opt) {\n            case '1':\n                do_passivedpi = do_host = do_host_removespace \\\n                = do_fragment_http = do_fragment_https \\\n                = do_fragment_http_persistent \\\n                = do_fragment_http_persistent_nowait = 1;\n                break;\n            case '2':\n                do_passivedpi = do_host = do_host_removespace \\\n                = do_fragment_http = do_fragment_https \\\n                = do_fragment_http_persistent \\\n                = do_fragment_http_persistent_nowait = 1;\n                https_fragment_size = 40u;\n                break;\n            case '3':\n                do_passivedpi = do_host = do_host_removespace \\\n                = do_fragment_https = 1;\n                https_fragment_size = 40u;\n                break;\n            case '4':\n                do_passivedpi = do_host = do_host_removespace = 1;\n                break;\n            case '5':\n                do_fragment_http = do_fragment_https = 1;\n                do_reverse_frag = do_native_frag = 1;\n                http_fragment_size = https_fragment_size = 2;\n                do_fragment_http_persistent = do_fragment_http_persistent_nowait = 1;\n                do_fake_packet = 1;\n                do_auto_ttl = 1;\n                max_payload_size = 1200;\n                break;\n            case '6':\n                do_fragment_http = do_fragment_https = 1;\n                do_reverse_frag = do_native_frag = 1;\n                http_fragment_size = https_fragment_size = 2;\n                do_fragment_http_persistent = do_fragment_http_persistent_nowait = 1;\n                do_fake_packet = 1;\n                do_wrong_seq = 1;\n                max_payload_size = 1200;\n                break;\n            case '9': // +7+8\n                do_block_quic = 1;\n                // fall through\n            case '8': // +7\n                do_wrong_seq = 1;\n                // fall through\n            case '7':\n                do_fragment_http = do_fragment_https = 1;\n                do_reverse_frag = do_native_frag = 1;\n                http_fragment_size = https_fragment_size = 2;\n                do_fragment_http_persistent = do_fragment_http_persistent_nowait = 1;\n                do_fake_packet = 1;\n                do_wrong_chksum = 1;\n                max_payload_size = 1200;\n                break;\n            case 'p':\n                do_passivedpi = 1;\n                break;\n            case 'q':\n                do_block_quic = 1;\n                break;\n            case 'r':\n                do_host = 1;\n                break;\n            case 's':\n                do_host_removespace = 1;\n                break;\n            case 'a':\n                do_additional_space = 1;\n                do_host_removespace = 1;\n                break;\n            case 'm':\n                do_host_mixedcase = 1;\n                break;\n            case 'f':\n                do_fragment_http = 1;\n                SET_HTTP_FRAGMENT_SIZE_OPTION(atousi(optarg, \"Fragment size should be in range [0 - 0xFFFF]\\n\"));\n                break;\n            case 'k':\n                do_fragment_http_persistent = 1;\n                do_native_frag = 1;\n                SET_HTTP_FRAGMENT_SIZE_OPTION(atousi(optarg, \"Fragment size should be in range [0 - 0xFFFF]\\n\"));\n                break;\n            case 'n':\n                do_fragment_http_persistent = 1;\n                do_fragment_http_persistent_nowait = 1;\n                do_native_frag = 1;\n                break;\n            case 'e':\n                do_fragment_https = 1;\n                https_fragment_size = atousi(optarg, \"Fragment size should be in range [0 - 65535]\\n\");\n                break;\n            case 'w':\n                do_http_allports = 1;\n                break;\n            case 'z': // --port\n                /* i is used as a temporary variable here */\n                i = atoi(optarg);\n                if (i <= 0 || i > 65535) {\n                    printf(\"Port parameter error!\\n\");\n                    exit(ERROR_PORT_BOUNDS);\n                }\n                if (i != 80 && i != 443)\n                    add_filter_str(IPPROTO_TCP, i);\n                i = 0;\n                break;\n            case 'i': // --ip-id\n                /* i is used as a temporary variable here */\n                i = atousi(optarg, \"IP ID parameter error!\\n\");\n                add_ip_id_str(i);\n                i = 0;\n                break;\n            case 'd': // --dns-addr\n                if ((inet_pton(AF_INET, optarg, dns_temp_addr.s6_addr) == 1) &&\n                    !do_dnsv4_redirect)\n                {\n                    do_dnsv4_redirect = 1;\n                    if (inet_pton(AF_INET, optarg, &dnsv4_addr) != 1) {\n                        puts(\"DNS address parameter error!\");\n                        exit(ERROR_DNS_V4_ADDR);\n                    }\n                    add_filter_str(IPPROTO_UDP, 53);\n                    flush_dns_cache();\n                    break;\n                }\n                puts(\"DNS address parameter error!\");\n                exit(ERROR_DNS_V4_ADDR);\n                break;\n            case '!': // --dnsv6-addr\n                if ((inet_pton(AF_INET6, optarg, dns_temp_addr.s6_addr) == 1) &&\n                    !do_dnsv6_redirect)\n                {\n                    do_dnsv6_redirect = 1;\n                    if (inet_pton(AF_INET6, optarg, dnsv6_addr.s6_addr) != 1) {\n                        puts(\"DNS address parameter error!\");\n                        exit(ERROR_DNS_V6_ADDR);\n                    }\n                    add_filter_str(IPPROTO_UDP, 53);\n                    flush_dns_cache();\n                    break;\n                }\n                puts(\"DNS address parameter error!\");\n                exit(ERROR_DNS_V6_ADDR);\n                break;\n            case 'g': // --dns-port\n                if (!do_dnsv4_redirect) {\n                    puts(\"--dns-port should be used with --dns-addr!\\n\"\n                        \"Make sure you use --dns-addr and pass it before \"\n                        \"--dns-port\");\n                    exit(ERROR_DNS_V4_PORT);\n                }\n                dnsv4_port = atousi(optarg, \"DNS port parameter error!\");\n                if (dnsv4_port != 53) {\n                    add_filter_str(IPPROTO_UDP, dnsv4_port);\n                }\n                dnsv4_port = htons(dnsv4_port);\n                break;\n            case '@': // --dnsv6-port\n                if (!do_dnsv6_redirect) {\n                    puts(\"--dnsv6-port should be used with --dnsv6-addr!\\n\"\n                        \"Make sure you use --dnsv6-addr and pass it before \"\n                        \"--dnsv6-port\");\n                    exit(ERROR_DNS_V6_PORT);\n                }\n                dnsv6_port = atousi(optarg, \"DNS port parameter error!\");\n                if (dnsv6_port != 53) {\n                    add_filter_str(IPPROTO_UDP, dnsv6_port);\n                }\n                dnsv6_port = htons(dnsv6_port);\n                break;\n            case 'v':\n                do_dns_verb = 1;\n                do_tcp_verb = 1;\n                break;\n            case 'b': // --blacklist\n                do_blacklist = 1;\n                if (!blackwhitelist_load_list(optarg)) {\n                    printf(\"Can't load blacklist from file!\\n\");\n                    exit(ERROR_BLACKLIST_LOAD);\n                }\n                break;\n            case ']': // --allow-no-sni\n                do_allow_no_sni = 1;\n                break;\n            case '>': // --frag-by-sni\n                do_fragment_by_sni = 1;\n                break;\n            case '$': // --set-ttl\n                do_auto_ttl = auto_ttl_1 = auto_ttl_2 = auto_ttl_max = 0;\n                do_fake_packet = 1;\n                ttl_of_fake_packet = atoub(optarg, \"Set TTL parameter error!\");\n                break;\n            case '[': // --min-ttl\n                do_fake_packet = 1;\n                ttl_min_nhops = atoub(optarg, \"Set Minimum TTL number of hops parameter error!\");\n                break;\n            case '+': // --auto-ttl\n                do_fake_packet = 1;\n                do_auto_ttl = 1;\n\n                if (!optarg && argv[optind] && argv[optind][0] != '-')\n                    optarg = argv[optind];\n\n                if (optarg) {\n                    char *autottl_copy = strdup(optarg);\n                    if (strchr(autottl_copy, '-')) {\n                        // token \"-\" found, start X-Y parser\n                        char *autottl_current = strtok(autottl_copy, \"-\");\n                        auto_ttl_1 = atoub(autottl_current, \"Set Auto TTL parameter error!\");\n                        autottl_current = strtok(NULL, \"-\");\n                        if (!autottl_current) {\n                            puts(\"Set Auto TTL parameter error!\");\n                            exit(ERROR_AUTOTTL);\n                        }\n                        auto_ttl_2 = atoub(autottl_current, \"Set Auto TTL parameter error!\");\n                        autottl_current = strtok(NULL, \"-\");\n                        if (!autottl_current) {\n                            puts(\"Set Auto TTL parameter error!\");\n                            exit(ERROR_AUTOTTL);\n                        }\n                        auto_ttl_max = atoub(autottl_current, \"Set Auto TTL parameter error!\");\n                    }\n                    else {\n                        // single digit parser\n                        auto_ttl_2 = atoub(optarg, \"Set Auto TTL parameter error!\");\n                        auto_ttl_1 = auto_ttl_2;\n                    }\n                    free(autottl_copy);\n                }\n                break;\n            case '%': // --wrong-chksum\n                do_fake_packet = 1;\n                do_wrong_chksum = 1;\n                break;\n            case ')': // --wrong-seq\n                do_fake_packet = 1;\n                do_wrong_seq = 1;\n                break;\n            case '*': // --native-frag\n                do_native_frag = 1;\n                do_fragment_http_persistent = 1;\n                do_fragment_http_persistent_nowait = 1;\n                break;\n            case '(': // --reverse-frag\n                do_reverse_frag = 1;\n                do_native_frag = 1;\n                do_fragment_http_persistent = 1;\n                do_fragment_http_persistent_nowait = 1;\n                break;\n            case '|': // --max-payload\n                if (!optarg && argv[optind] && argv[optind][0] != '-')\n                    optarg = argv[optind];\n                if (optarg)\n                    max_payload_size = atousi(optarg, \"Max payload size parameter error!\");\n                else\n                    max_payload_size = 1200;\n                break;\n            case 'u': // --fake-from-hex\n                if (fake_load_from_hex(optarg)) {\n                    printf(\"WARNING: bad fake HEX value %s\\n\", optarg);\n                }\n                break;\n            case '}': // --fake-with-sni\n                if (fake_load_from_sni(optarg)) {\n                    printf(\"WARNING: bad domain name for SNI: %s\\n\", optarg);\n                }\n                break;\n            case 'j': // --fake-gen\n                if (fake_load_random(atoub(optarg, \"Fake generator parameter error!\"), 200)) {\n                    puts(\"WARNING: fake generator has failed!\");\n                }\n                break;\n            case 't': // --fake-resend\n                fakes_resend = atoub(optarg, \"Fake resend parameter error!\");\n                if (fakes_resend == 1)\n                    puts(\"WARNING: fake-resend is 1, no resending is in place!\");\n                else if (!fakes_resend)\n                    puts(\"WARNING: fake-resend is 0, fake packet mode is disabled!\");\n                else if (fakes_resend > 100)\n                    puts(\"WARNING: fake-resend value is a little too high, don't you think?\");\n                break;\n            case 'x': // --debug-exit\n                debug_exit = true;\n                break;\n            default:\n                puts(\"Usage: goodbyedpi.exe [OPTION...]\\n\"\n                \" -p          block passive DPI\\n\"\n                \" -q          block QUIC/HTTP3\\n\"\n                \" -r          replace Host with hoSt\\n\"\n                \" -s          remove space between host header and its value\\n\"\n                \" -a          additional space between Method and Request-URI (enables -s, may break sites)\\n\"\n                \" -m          mix Host header case (test.com -> tEsT.cOm)\\n\"\n                \" -f <value>  set HTTP fragmentation to value\\n\"\n                \" -k <value>  enable HTTP persistent (keep-alive) fragmentation and set it to value\\n\"\n                \" -n          do not wait for first segment ACK when -k is enabled\\n\"\n                \" -e <value>  set HTTPS fragmentation to value\\n\"\n                \" -w          try to find and parse HTTP traffic on all processed ports (not only on port 80)\\n\"\n                \" --port        <value>    additional TCP port to perform fragmentation on (and HTTP tricks with -w)\\n\"\n                \" --ip-id       <value>    handle additional IP ID (decimal, drop redirects and TCP RSTs with this ID).\\n\"\n                \" --dns-addr    <value>    redirect UDPv4 DNS requests to the supplied IPv4 address (experimental)\\n\"\n                \" --dns-port    <value>    redirect UDPv4 DNS requests to the supplied port (53 by default)\\n\"\n                \" --dnsv6-addr  <value>    redirect UDPv6 DNS requests to the supplied IPv6 address (experimental)\\n\"\n                \" --dnsv6-port  <value>    redirect UDPv6 DNS requests to the supplied port (53 by default)\\n\"\n                \" --dns-verb               print verbose DNS redirection messages\\n\"\n                \" --blacklist   <txtfile>  perform circumvention tricks only to host names and subdomains from\\n\"\n                \"                          supplied text file (HTTP Host/TLS SNI).\\n\"\n                \"                          This option can be supplied multiple times.\\n\"\n                \" --allow-no-sni           perform circumvention if TLS SNI can't be detected with --blacklist enabled.\\n\"\n                \" --frag-by-sni            if SNI is detected in TLS packet, fragment the packet right before SNI value.\\n\"\n                \" --set-ttl     <value>    activate Fake Request Mode and send it with supplied TTL value.\\n\"\n                \"                          DANGEROUS! May break websites in unexpected ways. Use with care (or --blacklist).\\n\"\n                \" --auto-ttl    [a1-a2-m]  activate Fake Request Mode, automatically detect TTL and decrease\\n\"\n                \"                          it based on a distance. If the distance is shorter than a2, TTL is decreased\\n\"\n                \"                          by a2. If it's longer, (a1; a2) scale is used with the distance as a weight.\\n\"\n                \"                          If the resulting TTL is more than m(ax), set it to m.\\n\"\n                \"                          Default (if set): --auto-ttl 1-4-10. Also sets --min-ttl 3.\\n\"\n                \"                          DANGEROUS! May break websites in unexpected ways. Use with care (or --blacklist).\\n\"\n                \" --min-ttl     <value>    minimum TTL distance (128/64 - TTL) for which to send Fake Request\\n\"\n                \"                          in --set-ttl and --auto-ttl modes.\\n\"\n                \" --wrong-chksum           activate Fake Request Mode and send it with incorrect TCP checksum.\\n\"\n                \"                          May not work in a VM or with some routers, but is safer than set-ttl.\\n\"\n                \"                          Could be combined with --set-ttl\\n\"\n                \" --wrong-seq              activate Fake Request Mode and send it with TCP SEQ/ACK in the past.\\n\"\n                \" --native-frag            fragment (split) the packets by sending them in smaller packets, without\\n\"\n                \"                          shrinking the Window Size. Works faster (does not slow down the connection)\\n\"\n                \"                          and better.\\n\"\n                \" --reverse-frag           fragment (split) the packets just as --native-frag, but send them in the\\n\"\n                \"                          reversed order. Works with the websites which could not handle segmented\\n\"\n                \"                          HTTPS TLS ClientHello (because they receive the TCP flow \\\"combined\\\").\\n\"\n                \" --fake-from-hex <value>  Load fake packets for Fake Request Mode from HEX values (like 1234abcDEF).\\n\"\n                \"                          This option can be supplied multiple times, in this case each fake packet\\n\"\n                \"                          would be sent on every request in the command line argument order.\\n\"\n                \" --fake-with-sni <value>  Generate fake packets for Fake Request Mode with given SNI domain name.\\n\"\n                \"                          The packets mimic Mozilla Firefox 130 TLS ClientHello packet\\n\"\n                \"                          (with random generated fake SessionID, key shares and ECH grease).\\n\"\n                \"                          Can be supplied multiple times for multiple fake packets.\\n\"\n                \" --fake-gen <value>       Generate random-filled fake packets for Fake Request Mode, value of them\\n\"\n                \"                          (up to 30).\\n\"\n                \" --fake-resend <value>    Send each fake packet value number of times.\\n\"\n                \"                          Default: 1 (send each packet once).\\n\"\n                \" --max-payload [value]    packets with TCP payload data more than [value] won't be processed.\\n\"\n                \"                          Use this option to reduce CPU usage by skipping huge amount of data\\n\"\n                \"                          (like file transfers) in already established sessions.\\n\"\n                \"                          May skip some huge HTTP requests from being processed.\\n\"\n                \"                          Default (if set): --max-payload 1200.\\n\"\n                \"\\n\");\n                puts(\"LEGACY modesets:\\n\"\n                \" -1          -p -r -s -f 2 -k 2 -n -e 2 (most compatible mode)\\n\"\n                \" -2          -p -r -s -f 2 -k 2 -n -e 40 (better speed for HTTPS yet still compatible)\\n\"\n                \" -3          -p -r -s -e 40 (better speed for HTTP and HTTPS)\\n\"\n                \" -4          -p -r -s (best speed)\"\n                \"\\n\"\n                \"Modern modesets (more stable, more compatible, faster):\\n\"\n                \" -5          -f 2 -e 2 --auto-ttl --reverse-frag --max-payload\\n\"\n                \" -6          -f 2 -e 2 --wrong-seq --reverse-frag --max-payload\\n\"\n                \" -7          -f 2 -e 2 --wrong-chksum --reverse-frag --max-payload\\n\"\n                \" -8          -f 2 -e 2 --wrong-seq --wrong-chksum --reverse-frag --max-payload\\n\"\n                \" -9          -f 2 -e 2 --wrong-seq --wrong-chksum --reverse-frag --max-payload -q (this is the default)\\n\\n\"\n                \"Note: combination of --wrong-seq and --wrong-chksum generates two different fake packets.\\n\"\n                );\n                exit(ERROR_DEFAULT);\n        }\n    }\n\n    if (!http_fragment_size)\n        http_fragment_size = 2;\n    if (!https_fragment_size)\n        https_fragment_size = 2;\n    if (!auto_ttl_1)\n        auto_ttl_1 = 1;\n    if (!auto_ttl_2)\n        auto_ttl_2 = 4;\n    if (do_auto_ttl) {\n        if (!ttl_min_nhops)\n            ttl_min_nhops = 3;\n        if (!auto_ttl_max)\n            auto_ttl_max = 10;\n    }\n\n    printf(\"Block passive: %d\\n\"                    /* 1 */\n           \"Block QUIC/HTTP3: %d\\n\"                 /* 1 */\n           \"Fragment HTTP: %u\\n\"                    /* 2 */\n           \"Fragment persistent HTTP: %u\\n\"         /* 3 */\n           \"Fragment HTTPS: %u\\n\"                   /* 4 */\n           \"Fragment by SNI: %u\\n\"                  /* 5 */\n           \"Native fragmentation (splitting): %d\\n\" /* 6 */\n           \"Fragments sending in reverse: %d\\n\"     /* 7 */\n           \"hoSt: %d\\n\"                             /* 8 */\n           \"Host no space: %d\\n\"                    /* 9 */\n           \"Additional space: %d\\n\"                 /* 10 */\n           \"Mix Host: %d\\n\"                         /* 11 */\n           \"HTTP AllPorts: %d\\n\"                    /* 12 */\n           \"HTTP Persistent Nowait: %d\\n\"           /* 13 */\n           \"DNS redirect: %d\\n\"                     /* 14 */\n           \"DNSv6 redirect: %d\\n\"                   /* 15 */\n           \"Allow missing SNI: %d\\n\"                /* 16 */\n           \"Fake requests, TTL: %s (fixed: %hu, auto: %hu-%hu-%hu, min distance: %hu)\\n\"  /* 17 */\n           \"Fake requests, wrong checksum: %d\\n\"    /* 18 */\n           \"Fake requests, wrong SEQ/ACK: %d\\n\"     /* 19 */\n           \"Fake requests, custom payloads: %d\\n\"   /* 20 */\n           \"Fake requests, resend: %d\\n\"            /* 21 */\n           \"Max payload size: %hu\\n\",               /* 22 */\n           do_passivedpi, do_block_quic,                          /* 1 */\n           (do_fragment_http ? http_fragment_size : 0),           /* 2 */\n           (do_fragment_http_persistent ? http_fragment_size : 0),/* 3 */\n           (do_fragment_https ? https_fragment_size : 0),         /* 4 */\n           do_fragment_by_sni,    /* 5 */\n           do_native_frag,        /* 6 */\n           do_reverse_frag,       /* 7 */\n           do_host,               /* 8 */\n           do_host_removespace,   /* 9 */\n           do_additional_space,   /* 10 */\n           do_host_mixedcase,     /* 11 */\n           do_http_allports,      /* 12 */\n           do_fragment_http_persistent_nowait, /* 13 */\n           do_dnsv4_redirect,                  /* 14 */\n           do_dnsv6_redirect,                  /* 15 */\n           do_allow_no_sni,                    /* 16 */\n           do_auto_ttl ? \"auto\" : (do_fake_packet ? \"fixed\" : \"disabled\"),  /* 17 */\n               ttl_of_fake_packet, do_auto_ttl ? auto_ttl_1 : 0, do_auto_ttl ? auto_ttl_2 : 0,\n               do_auto_ttl ? auto_ttl_max : 0, ttl_min_nhops,\n           do_wrong_chksum, /* 18 */\n           do_wrong_seq,    /* 19 */\n           fakes_count,     /* 20 */\n           fakes_resend,    /* 21 */\n           max_payload_size /* 22 */\n          );\n\n    if (do_fragment_http && http_fragment_size > 2 && !do_native_frag) {\n        puts(\"\\nWARNING: HTTP fragmentation values > 2 are not fully compatible \"\n             \"with other options. Please use values <= 2 or disable HTTP fragmentation \"\n             \"completely.\");\n    }\n\n    if (do_native_frag && !(do_fragment_http || do_fragment_https)) {\n        puts(\"\\nERROR: Native fragmentation is enabled but fragment sizes are not set.\\n\"\n             \"Fragmentation has no effect.\");\n        die();\n    }\n\n    if (max_payload_size)\n        add_maxpayloadsize_str(max_payload_size);\n    finalize_filter_strings();\n    puts(\"\\nOpening filter\");\n    filter_num = 0;\n\n    if (do_passivedpi) {\n        /* IPv4 only filter for inbound RST packets with ID [0x0; 0xF] */\n        filters[filter_num] = init(\n            filter_passive_string,\n            WINDIVERT_FLAG_DROP);\n        if (filters[filter_num] == NULL)\n            die();\n        filter_num++;\n    }\n\n    if (do_block_quic) {\n        filters[filter_num] = init(\n            FILTER_PASSIVE_BLOCK_QUIC,\n            WINDIVERT_FLAG_DROP);\n        if (filters[filter_num] == NULL)\n            die();\n        filter_num++;\n    }\n\n    /* \n     * IPv4 & IPv6 filter for inbound HTTP redirection packets and\n     * active DPI circumvention\n     */\n    filters[filter_num] = init(filter_string, 0);\n\n    w_filter = filters[filter_num];\n    filter_num++;\n\n    for (i = 0; i < filter_num; i++) {\n        if (filters[i] == NULL)\n            die();\n    }\n    if (debug_exit) {\n        printf(\"Debug Exit\\n\");\n        exit(EXIT_SUCCESS);\n    }\n    printf(\"Filter activated, GoodbyeDPI is now running!\\n\");\n    signal(SIGINT, sigint_handler);\n\n    while (1) {\n        if (WinDivertRecv(w_filter, packet, sizeof(packet), &packetLen, &addr)) {\n            debug(\"Got %s packet, len=%d!\\n\", addr.Outbound ? \"outbound\" : \"inbound\",\n                   packetLen);\n            should_reinject = 1;\n            should_recalc_checksum = 0;\n            sni_ok = 0;\n\n            ppIpHdr = (PWINDIVERT_IPHDR)NULL;\n            ppIpV6Hdr = (PWINDIVERT_IPV6HDR)NULL;\n            ppTcpHdr = (PWINDIVERT_TCPHDR)NULL;\n            ppUdpHdr = (PWINDIVERT_UDPHDR)NULL;\n            packet_v4 = packet_v6 = 0;\n            packet_type = unknown;\n\n            // Parse network packet and set it's type\n            if (WinDivertHelperParsePacket(packet, packetLen, &ppIpHdr,\n                &ppIpV6Hdr, NULL, NULL, NULL, &ppTcpHdr, &ppUdpHdr, &packet_data, &packet_dataLen,\n                NULL, NULL))\n            {\n                if (ppIpHdr) {\n                    packet_v4 = 1;\n                    if (ppTcpHdr) {\n                        packet_type = ipv4_tcp;\n                        if (packet_data) {\n                            packet_type = ipv4_tcp_data;\n                        }\n                    }\n                    else if (ppUdpHdr && packet_data) {\n                        packet_type = ipv4_udp_data;\n                    }\n                }\n\n                else if (ppIpV6Hdr) {\n                    packet_v6 = 1;\n                    if (ppTcpHdr) {\n                        packet_type = ipv6_tcp;\n                        if (packet_data) {\n                            packet_type = ipv6_tcp_data;\n                        }\n                    }\n                    else if (ppUdpHdr && packet_data) {\n                        packet_type = ipv6_udp_data;\n                    }\n                }\n            }\n\n            debug(\"packet_type: %d, packet_v4: %d, packet_v6: %d\\n\", packet_type, packet_v4, packet_v6);\n\n            if (packet_type == ipv4_tcp_data || packet_type == ipv6_tcp_data) {\n                //printf(\"Got parsed packet, len=%d!\\n\", packet_dataLen);\n                /* Got a TCP packet WITH DATA */\n\n                /* Handle INBOUND packet with data and find HTTP REDIRECT in there */\n                if (!addr.Outbound && packet_dataLen > 16) {\n                    /* If INBOUND packet with DATA (tcp.Ack) */\n\n                    /* Drop packets from filter with HTTP 30x Redirect */\n                    if (do_passivedpi && is_passivedpi_redirect(packet_data, packet_dataLen)) {\n                        if (packet_v4) {\n                            //printf(\"Dropping HTTP Redirect packet!\\n\");\n                            should_reinject = 0;\n                        }\n                        else if (packet_v6 && WINDIVERT_IPV6HDR_GET_FLOWLABEL(ppIpV6Hdr) == 0x0) {\n                                /* Contrary to IPv4 where we get only packets with IP ID 0x0-0xF,\n                                 * for IPv6 we got all the incoming data packets since we can't\n                                 * filter them in a driver.\n                                 *\n                                 * Handle only IPv6 Flow Label == 0x0 for now\n                                 */\n                                //printf(\"Dropping HTTP Redirect packet!\\n\");\n                                should_reinject = 0;\n                        }\n                    }\n                }\n                /* Handle OUTBOUND packet on port 443, search for something that resembles\n                 * TLS handshake, send fake request.\n                 */\n                else if (addr.Outbound &&\n                        ((do_fragment_https ? packet_dataLen == https_fragment_size : 0) ||\n                         packet_dataLen > 16) &&\n                         ppTcpHdr->DstPort != htons(80) &&\n                         (do_fake_packet || do_native_frag)\n                        )\n                {\n                    /**\n                     * In case of Window Size fragmentation=2, we'll receive only 2 byte packet.\n                     * But if the packet is more than 2 bytes, check ClientHello byte.\n                    */\n                    if ((packet_dataLen == 2 && memcmp(packet_data, \"\\x16\\x03\", 2) == 0) ||\n                        (packet_dataLen >= 3 && ( memcmp(packet_data, \"\\x16\\x03\\x01\", 3) == 0 || memcmp(packet_data, \"\\x16\\x03\\x03\", 3) == 0 )))\n                    {\n                        if (do_blacklist || do_fragment_by_sni) {\n                            sni_ok = extract_sni(packet_data, packet_dataLen,\n                                        &host_addr, &host_len);\n                        }\n                        if (\n                             (do_blacklist && sni_ok &&\n                              blackwhitelist_check_hostname(host_addr, host_len)\n                             ) ||\n                             (do_blacklist && !sni_ok && do_allow_no_sni) ||\n                             (!do_blacklist)\n                           )\n                        {\n#ifdef DEBUG\n                            char lsni[HOST_MAXLEN + 1] = {0};\n                            extract_sni(packet_data, packet_dataLen,\n                                        &host_addr, &host_len);\n                            memcpy(lsni, host_addr, host_len);\n                            printf(\"Blocked HTTPS website SNI: %s\\n\", lsni);\n#endif\n                            if (do_fake_packet) {\n                                TCP_HANDLE_OUTGOING_FAKE_PACKET(send_fake_https_request);\n                            }\n                            if (do_native_frag) {\n                                // Signal for native fragmentation code handler\n                                should_recalc_checksum = 1;\n                            }\n                        }\n                    }\n                }\n                /* Handle OUTBOUND packet on port 80, search for Host header */\n                else if (addr.Outbound && \n                        packet_dataLen > 16 &&\n                        (do_http_allports ? 1 : (ppTcpHdr->DstPort == htons(80))) &&\n                        find_http_method_end(packet_data,\n                                             (do_fragment_http ? http_fragment_size : 0u),\n                                             &http_req_fragmented) &&\n                        (do_host || do_host_removespace ||\n                        do_host_mixedcase || do_fragment_http_persistent ||\n                        do_fake_packet))\n                {\n\n                    /* Find Host header */\n                    if (find_header_and_get_info(packet_data, packet_dataLen,\n                        http_host_find, &hdr_name_addr, &hdr_value_addr, &hdr_value_len) &&\n                        hdr_value_len > 0 && hdr_value_len <= HOST_MAXLEN &&\n                        (do_blacklist ? blackwhitelist_check_hostname(hdr_value_addr, hdr_value_len) : 1))\n                    {\n                        host_addr = hdr_value_addr;\n                        host_len = hdr_value_len;\n#ifdef DEBUG\n                        char lhost[HOST_MAXLEN + 1] = {0};\n                        memcpy(lhost, host_addr, host_len);\n                        printf(\"Blocked HTTP website Host: %s\\n\", lhost);\n#endif\n\n                        if (do_native_frag) {\n                            // Signal for native fragmentation code handler\n                            should_recalc_checksum = 1;\n                        }\n\n                        if (do_fake_packet) {\n                            TCP_HANDLE_OUTGOING_FAKE_PACKET(send_fake_http_request);\n                        }\n\n                        if (do_host_mixedcase) {\n                            mix_case(host_addr, host_len);\n                            should_recalc_checksum = 1;\n                        }\n\n                        if (do_host) {\n                            /* Replace \"Host: \" with \"hoSt: \" */\n                            memcpy(hdr_name_addr, http_host_replace, strlen(http_host_replace));\n                            should_recalc_checksum = 1;\n                            //printf(\"Replaced Host header!\\n\");\n                        }\n\n                        /* If removing space between host header and its value\n                         * and adding additional space between Method and Request-URI */\n                        if (do_additional_space && do_host_removespace) {\n                            /* End of \"Host:\" without trailing space */\n                            method_addr = find_http_method_end(packet_data,\n                                                            (do_fragment_http ? http_fragment_size : 0),\n                                                            NULL);\n\n                            if (method_addr) {\n                                memmove(method_addr + 1, method_addr,\n                                        (size_t)(host_addr - method_addr - 1));\n                                should_recalc_checksum = 1;\n                            }\n                        }\n                        /* If just removing space between host header and its value */\n                        else if (do_host_removespace) {\n                            if (find_header_and_get_info(packet_data, packet_dataLen,\n                                                        http_useragent_find, &hdr_name_addr,\n                                                         &hdr_value_addr, &hdr_value_len))\n                            {\n                                useragent_addr = hdr_value_addr;\n                                useragent_len = hdr_value_len;\n\n                                /* We move Host header value by one byte to the left and then\n                                 * \"insert\" stolen space to the end of User-Agent value because\n                                 * some web servers are not tolerant to additional space in the\n                                 * end of Host header.\n                                 *\n                                 * Nothing is done if User-Agent header is missing.\n                                 */\n                                if (useragent_addr && useragent_len > 0) {\n                                    /* useragent_addr is in the beginning of User-Agent value */\n\n                                    if (useragent_addr > host_addr) {\n                                        /* Move one byte to the LEFT from \"Host:\"\n                                        * to the end of User-Agent\n                                        */\n                                        memmove(host_addr - 1, host_addr,\n                                                (size_t)(useragent_addr + useragent_len - host_addr));\n                                        host_addr -= 1;\n                                        /* Put space in the end of User-Agent header */\n                                        *(char*)((unsigned char*)useragent_addr + useragent_len - 1) = ' ';\n                                        should_recalc_checksum = 1;\n                                        //printf(\"Replaced Host header!\\n\");\n                                    }\n                                    else {\n                                        /* User-Agent goes BEFORE Host header */\n\n                                        /* Move one byte to the RIGHT from the end of User-Agent\n                                        * to the \"Host:\"\n                                        */\n                                        memmove(useragent_addr + useragent_len + 1,\n                                                useragent_addr + useragent_len,\n                                                (size_t)(host_addr - 1 - (useragent_addr + useragent_len)));\n                                        /* Put space in the end of User-Agent header */\n                                        *(char*)((unsigned char*)useragent_addr + useragent_len) = ' ';\n                                        should_recalc_checksum = 1;\n                                        //printf(\"Replaced Host header!\\n\");\n                                    }\n                                } /* if (host_len <= HOST_MAXLEN && useragent_addr) */\n                            } /* if (find_header_and_get_info http_useragent) */\n                        } /* else if (do_host_removespace) */\n                    } /* if (find_header_and_get_info http_host) */\n                } /* Handle OUTBOUND packet with data */\n\n                /*\n                * should_recalc_checksum mean we have detected a packet to handle and\n                * modified it in some way.\n                * Handle native fragmentation here, incl. sending the packet.\n                */\n                if (should_reinject && should_recalc_checksum && do_native_frag)\n                {\n                    current_fragment_size = 0;\n                    if (do_fragment_http && ppTcpHdr->DstPort == htons(80)) {\n                        current_fragment_size = http_fragment_size;\n                    }\n                    else if (do_fragment_https && ppTcpHdr->DstPort != htons(80)) {\n                        if (do_fragment_by_sni && sni_ok) {\n                            current_fragment_size = (void*)host_addr - packet_data;\n                        } else {\n                            current_fragment_size = https_fragment_size;\n                        }\n                    }\n\n                    if (current_fragment_size) {\n                        send_native_fragment(w_filter, addr, packet, packetLen, packet_data,\n                                            packet_dataLen,packet_v4, packet_v6,\n                                            ppIpHdr, ppIpV6Hdr, ppTcpHdr,\n                                            current_fragment_size, do_reverse_frag);\n\n                        send_native_fragment(w_filter, addr, packet, packetLen, packet_data,\n                                            packet_dataLen,packet_v4, packet_v6,\n                                            ppIpHdr, ppIpV6Hdr, ppTcpHdr,\n                                            current_fragment_size, !do_reverse_frag);\n                        continue;\n                    }\n                }\n            } /* Handle TCP packet with data */\n\n            /* Else if we got TCP packet without data */\n            else if (packet_type == ipv4_tcp || packet_type == ipv6_tcp) {\n                /* If we got INBOUND SYN+ACK packet */\n                if (!addr.Outbound &&\n                    ppTcpHdr->Syn == 1 && ppTcpHdr->Ack == 1) {\n                    //printf(\"Changing Window Size!\\n\");\n                    /*\n                     * Window Size is changed even if do_fragment_http_persistent\n                     * is enabled as there could be non-HTTP data on port 80\n                     */\n\n                    if (do_fake_packet && (do_auto_ttl || ttl_min_nhops)) {\n                        if (!((packet_v4 && tcp_handle_incoming(&ppIpHdr->SrcAddr, &ppIpHdr->DstAddr,\n                                        ppTcpHdr->SrcPort, ppTcpHdr->DstPort,\n                                        0, ppIpHdr->TTL))\n                            ||\n                            (packet_v6 && tcp_handle_incoming((uint32_t*)&ppIpV6Hdr->SrcAddr,\n                                        (uint32_t*)&ppIpV6Hdr->DstAddr,\n                                        ppTcpHdr->SrcPort, ppTcpHdr->DstPort,\n                                        1, ppIpV6Hdr->HopLimit))))\n                        {\n                            if (do_tcp_verb)\n                                puts(\"[TCP WARN] Can't add TCP connection record.\");\n                        }\n                    }\n\n                    if (!do_native_frag) {\n                        if (do_fragment_http && ppTcpHdr->SrcPort == htons(80)) {\n                            change_window_size(ppTcpHdr, http_fragment_size);\n                            should_recalc_checksum = 1;\n                        }\n                        else if (do_fragment_https && ppTcpHdr->SrcPort != htons(80)) {\n                            change_window_size(ppTcpHdr, https_fragment_size);\n                            should_recalc_checksum = 1;\n                        }\n                    }\n                }\n            }\n\n            /* Else if we got UDP packet with data */\n            else if ((do_dnsv4_redirect && (packet_type == ipv4_udp_data)) ||\n                     (do_dnsv6_redirect && (packet_type == ipv6_udp_data)))\n            {\n                if (!addr.Outbound) {\n                    if ((packet_v4 && dns_handle_incoming(&ppIpHdr->DstAddr, ppUdpHdr->DstPort,\n                                        packet_data, packet_dataLen,\n                                        &dns_conn_info, 0))\n                        ||\n                        (packet_v6 && dns_handle_incoming(ppIpV6Hdr->DstAddr, ppUdpHdr->DstPort,\n                                        packet_data, packet_dataLen,\n                                        &dns_conn_info, 1)))\n                    {\n                        /* Changing source IP and port to the values\n                         * from DNS conntrack */\n                        if (packet_v4)\n                            ppIpHdr->SrcAddr = dns_conn_info.dstip[0];\n                        else if (packet_v6)\n                            ipv6_copy_addr(ppIpV6Hdr->SrcAddr, dns_conn_info.dstip);\n                        ppUdpHdr->DstPort = dns_conn_info.srcport;\n                        ppUdpHdr->SrcPort = dns_conn_info.dstport;\n                        should_recalc_checksum = 1;\n                    }\n                    else {\n                        if (dns_is_dns_packet(packet_data, packet_dataLen, 0))\n                            should_reinject = 0;\n\n                        if (do_dns_verb && !should_reinject) {\n                            printf(\"[DNS] Error handling incoming packet: srcport = %hu, dstport = %hu\\n\",\n                               ntohs(ppUdpHdr->SrcPort), ntohs(ppUdpHdr->DstPort));\n                        }\n                    }\n                }\n\n                else if (addr.Outbound) {\n                    if ((packet_v4 && dns_handle_outgoing(&ppIpHdr->SrcAddr, ppUdpHdr->SrcPort,\n                                        &ppIpHdr->DstAddr, ppUdpHdr->DstPort,\n                                        packet_data, packet_dataLen, 0))\n                        ||\n                        (packet_v6 && dns_handle_outgoing(ppIpV6Hdr->SrcAddr, ppUdpHdr->SrcPort,\n                                        ppIpV6Hdr->DstAddr, ppUdpHdr->DstPort,\n                                        packet_data, packet_dataLen, 1)))\n                    {\n                        /* Changing destination IP and port to the values\n                         * from configuration */\n                        if (packet_v4) {\n                            ppIpHdr->DstAddr = dnsv4_addr;\n                            ppUdpHdr->DstPort = dnsv4_port;\n                        }\n                        else if (packet_v6) {\n                            ipv6_copy_addr(ppIpV6Hdr->DstAddr, (uint32_t*)dnsv6_addr.s6_addr);\n                            ppUdpHdr->DstPort = dnsv6_port;\n                        }\n                        should_recalc_checksum = 1;\n                    }\n                    else {\n                        if (dns_is_dns_packet(packet_data, packet_dataLen, 1))\n                            should_reinject = 0;\n\n                        if (do_dns_verb && !should_reinject) {\n                            printf(\"[DNS] Error handling outgoing packet: srcport = %hu, dstport = %hu\\n\",\n                               ntohs(ppUdpHdr->SrcPort), ntohs(ppUdpHdr->DstPort));\n                        }\n                    }\n                }\n            }\n\n            if (should_reinject) {\n                //printf(\"Re-injecting!\\n\");\n                if (should_recalc_checksum) {\n                    WinDivertHelperCalcChecksums(packet, packetLen, &addr, (UINT64)0LL);\n                }\n                WinDivertSend(w_filter, packet, packetLen, NULL, &addr);\n            }\n        }\n        else {\n            // error, ignore\n            if (!exiting)\n                printf(\"Error receiving packet!\\n\");\n            break;\n        }\n    }\n}\n"
  },
  {
    "path": "src/goodbyedpi.exe.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n    <assemblyIdentity version=\"1.0.0.0\" processorArchitecture=\"X86\" name=\"GoodbyeDPI\" type=\"win32\"/>\n    <description>GoodbyeDPI</description>\n    <trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v2\">\n        <security>\n            <requestedPrivileges>\n                <requestedExecutionLevel level=\"requireAdministrator\" uiAccess=\"false\"/>\n            </requestedPrivileges>\n        </security>\n    </trustInfo>\n</assembly>\n"
  },
  {
    "path": "src/goodbyedpi.h",
    "content": "#define HOST_MAXLEN 253\n#define MAX_PACKET_SIZE 9016\n\n#ifndef DEBUG\n#define debug(...) do {} while (0)\n#else\n#define debug(...) printf(__VA_ARGS__)\n#endif\n\nint main(int argc, char *argv[]);\nvoid deinit_all();\n"
  },
  {
    "path": "src/service.c",
    "content": "#include <windows.h>\n#include <stdio.h>\n#include \"goodbyedpi.h\"\n#include \"service.h\"\n\n#define SERVICE_NAME \"GoodbyeDPI\"\n\nstatic SERVICE_STATUS ServiceStatus;\nstatic SERVICE_STATUS_HANDLE hStatus;\nstatic int service_argc = 0;\nstatic char **service_argv = NULL;\n\nint service_register(int argc, char *argv[])\n{\n    int i, ret;\n    SERVICE_TABLE_ENTRY ServiceTable[] = {\n        {SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)service_main},\n        {NULL, NULL}\n    };\n    /*\n     * Save argc & argv as service_main is called with different\n     * arguments, which are passed from \"start\" command, not\n     * from the program command line.\n     * We don't need this behaviour.\n     *\n     * Note that if StartServiceCtrlDispatcher() succeedes\n     * it does not return until the service is stopped,\n     * so we should copy all arguments first and then\n     * handle the failure.\n     */\n    if (!service_argc && !service_argv) {\n        service_argc = argc;\n        service_argv = calloc((size_t)(argc + 1), sizeof(void*));\n        for (i = 0; i < argc; i++) {\n            service_argv[i] = strdup(argv[i]);\n        }\n    }\n\n    ret = StartServiceCtrlDispatcher(ServiceTable);\n\n    if (service_argc && service_argv) {\n        for (i = 0; i < service_argc; i++) {\n            free(service_argv[i]);\n        }\n        free(service_argv);\n    }\n\n    return ret;\n}\n\nvoid service_main(int argc __attribute__((unused)),\n                  char *argv[] __attribute__((unused)))\n{\n    ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; \n    ServiceStatus.dwCurrentState = SERVICE_RUNNING;\n    ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;\n    ServiceStatus.dwWin32ExitCode = 0;\n    ServiceStatus.dwServiceSpecificExitCode = 0;\n    ServiceStatus.dwCheckPoint = 1;\n    ServiceStatus.dwWaitHint = 0;\n\n    hStatus = RegisterServiceCtrlHandler(\n        SERVICE_NAME,\n        (LPHANDLER_FUNCTION)service_controlhandler);\n    if (hStatus == (SERVICE_STATUS_HANDLE)0)\n    {\n        // Registering Control Handler failed\n        return;\n    }\n\n    SetServiceStatus(hStatus, &ServiceStatus);\n\n    // Calling main with saved argc & argv\n    ServiceStatus.dwWin32ExitCode = (DWORD)main(service_argc, service_argv);\n    ServiceStatus.dwCurrentState  = SERVICE_STOPPED;\n    SetServiceStatus(hStatus, &ServiceStatus);\n    return;\n}\n\n// Control handler function\nvoid service_controlhandler(DWORD request)\n{\n    switch(request)\n    {\n        case SERVICE_CONTROL_STOP:\n        case SERVICE_CONTROL_SHUTDOWN:\n            deinit_all();\n            ServiceStatus.dwWin32ExitCode = 0;\n            ServiceStatus.dwCurrentState  = SERVICE_STOPPED;\n        default:\n            break;\n    }\n    // Report current status\n    SetServiceStatus(hStatus, &ServiceStatus);\n    return;\n}\n"
  },
  {
    "path": "src/service.h",
    "content": "int service_register();\nvoid service_main(int argc, char *argv[]);\nvoid service_controlhandler(DWORD request);\n"
  },
  {
    "path": "src/ttltrack.c",
    "content": "/**\n * TCP (TTL) Connection Tracker for GoodbyeDPI\n *\n * Monitors SYN/ACK only, to extract the TTL value of the remote server.\n *\n */\n\n#include <windows.h>\n#include <time.h>\n#include <stdio.h>\n#include <math.h>\n#include \"goodbyedpi.h\"\n#include \"ttltrack.h\"\n#include \"utils/uthash.h\"\n\n\n/* key ('4' for IPv4 or '6' for IPv6 + srcip[16] + dstip[16] + srcport[2] + dstport[2]) */\n#define TCP_CONNRECORD_KEY_LEN 37\n\n#define TCP_CLEANUP_INTERVAL_SEC 30\n\n/* HACK!\n * uthash uses strlen() for HASH_FIND_STR.\n * We have null bytes in our key, so we can't use strlen()\n * And since it's always TCP_CONNRECORD_KEY_LEN bytes long,\n * we don't need to use any string function to determine length.\n */\n#undef uthash_strlen\n#define uthash_strlen(s) TCP_CONNRECORD_KEY_LEN\n\ntypedef struct tcp_connrecord {\n    /* key ('4' for IPv4 or '6' for IPv6 + srcip[16] + dstip[16] + srcport[2] + dstport[2]) */\n    char key[TCP_CONNRECORD_KEY_LEN];\n    time_t time;         /* time when this record was added */\n    uint16_t ttl;\n    UT_hash_handle hh;   /* makes this structure hashable */\n} tcp_connrecord_t;\n\nstatic time_t last_cleanup = 0;\nstatic tcp_connrecord_t *conntrack = NULL;\n\ninline static void fill_key_data(char *key, const uint8_t is_ipv6, const uint32_t srcip[4],\n                    const uint32_t dstip[4], const uint16_t srcport, const uint16_t dstport)\n{\n    unsigned int offset = 0;\n\n    if (is_ipv6) {\n        *(uint8_t*)(key) = '6';\n        offset += sizeof(uint8_t);\n        ipv6_copy_addr((uint32_t*)(key + offset), srcip);\n        offset += sizeof(uint32_t) * 4;\n        ipv6_copy_addr((uint32_t*)(key + offset), dstip);\n        offset += sizeof(uint32_t) * 4;\n    }\n    else {\n        *(uint8_t*)(key) = '4';\n        offset += sizeof(uint8_t);\n        ipv4_copy_addr((uint32_t*)(key + offset), srcip);\n        offset += sizeof(uint32_t) * 4;\n        ipv4_copy_addr((uint32_t*)(key + offset), dstip);\n        offset += sizeof(uint32_t) * 4;\n    }\n\n    *(uint16_t*)(key + offset) = srcport;\n    offset += sizeof(srcport);\n    *(uint16_t*)(key + offset) = dstport;\n    offset += sizeof(dstport);\n}\n\ninline static void fill_data_from_key(uint8_t *is_ipv6, uint32_t srcip[4], uint32_t dstip[4],\n                                     uint16_t *srcport, uint16_t *dstport, const char *key)\n{\n    unsigned int offset = 0;\n\n    if (key[0] == '6') {\n        *is_ipv6 = 1;\n        offset += sizeof(uint8_t);\n        ipv6_copy_addr(srcip, (uint32_t*)(key + offset));\n        offset += sizeof(uint32_t) * 4;\n        ipv6_copy_addr(dstip, (uint32_t*)(key + offset));\n        offset += sizeof(uint32_t) * 4;\n    }\n    else {\n        *is_ipv6 = 0;\n        offset += sizeof(uint8_t);\n        ipv4_copy_addr(srcip, (uint32_t*)(key + offset));\n        offset += sizeof(uint32_t) * 4;\n        ipv4_copy_addr(dstip, (uint32_t*)(key + offset));\n        offset += sizeof(uint32_t) * 4;\n    }\n    *srcport = *(uint16_t*)(key + offset);\n    offset += sizeof(*srcport);\n    *dstport = *(uint16_t*)(key + offset);\n    offset += sizeof(*dstport);\n}\n\ninline static void construct_key(const uint32_t srcip[4], const uint32_t dstip[4],\n                                 const uint16_t srcport, const uint16_t dstport,\n                                 char *key, const uint8_t is_ipv6)\n{\n    debug(\"Construct key enter\\n\");\n    if (key) {\n        debug(\"Constructing key\\n\");\n        fill_key_data(key, is_ipv6, srcip, dstip, srcport, dstport);\n    }\n    debug(\"Construct key end\\n\");\n}\n\ninline static void deconstruct_key(const char *key, const tcp_connrecord_t *connrecord,\n                                   tcp_conntrack_info_t *conn_info)\n{\n    debug(\"Deconstruct key enter\\n\");\n    if (key && conn_info) {\n        debug(\"Deconstructing key\\n\");\n        fill_data_from_key(&conn_info->is_ipv6,\n                           conn_info->srcip, conn_info->dstip,\n                           &conn_info->srcport, &conn_info->dstport,\n                           key);\n\n        conn_info->ttl = connrecord->ttl;\n    }\n    debug(\"Deconstruct key end\\n\");\n}\n\nstatic int check_get_tcp_conntrack_key(const char *key, tcp_connrecord_t **connrecord) {\n    tcp_connrecord_t *tmp_connrecord = NULL;\n    if (!conntrack) return FALSE;\n\n    HASH_FIND_STR(conntrack, key, tmp_connrecord);\n    if (tmp_connrecord) {\n        if (connrecord)\n            *connrecord = tmp_connrecord;\n        debug(\"check_get_tcp_conntrack_key found key\\n\");\n        return TRUE;\n    }\n    debug(\"check_get_tcp_conntrack_key key not found\\n\");\n    return FALSE;\n}\n\nstatic int add_tcp_conntrack(const uint32_t srcip[4], const uint32_t dstip[4],\n                             const uint16_t srcport, const uint16_t dstport,\n                             const uint8_t is_ipv6, const uint8_t ttl\n                            )\n{\n    if (!(srcip && srcport && dstip && dstport))\n        return FALSE;\n\n    tcp_connrecord_t *tmp_connrecord = malloc(sizeof(tcp_connrecord_t));\n    construct_key(srcip, dstip, srcport, dstport, tmp_connrecord->key, is_ipv6);\n\n    if (!check_get_tcp_conntrack_key(tmp_connrecord->key, NULL)) {\n        tmp_connrecord->time = time(NULL);\n        tmp_connrecord->ttl = ttl;\n        HASH_ADD_STR(conntrack, key, tmp_connrecord);\n        debug(\"Added TCP conntrack %u:%hu - %u:%hu\\n\", srcip[0], ntohs(srcport), dstip[0], ntohs(dstport));\n        return TRUE;\n    }\n    debug(\"Not added TCP conntrack %u:%hu - %u:%hu\\n\", srcip[0], ntohs(srcport), dstip[0], ntohs(dstport));\n    free(tmp_connrecord);\n    return FALSE;\n}\n\nstatic void tcp_cleanup() {\n    tcp_connrecord_t *tmp_connrecord, *tmp_connrecord2 = NULL;\n\n    if (last_cleanup == 0) {\n        last_cleanup = time(NULL);\n        return;\n    }\n\n    if (difftime(time(NULL), last_cleanup) >= TCP_CLEANUP_INTERVAL_SEC) {\n        last_cleanup = time(NULL);\n\n        HASH_ITER(hh, conntrack, tmp_connrecord, tmp_connrecord2) {\n            if (difftime(last_cleanup, tmp_connrecord->time) >= TCP_CLEANUP_INTERVAL_SEC) {\n                HASH_DEL(conntrack, tmp_connrecord);\n                free(tmp_connrecord);\n            }\n        }\n    }\n}\n\nint tcp_handle_incoming(uint32_t srcip[4], uint32_t dstip[4],\n                        uint16_t srcport, uint16_t dstport,\n                        uint8_t is_ipv6, uint8_t ttl)\n{\n    tcp_cleanup();\n\n    debug(\"trying to add TCP srcport = %hu, dstport = %hu\\n\", ntohs(srcport), ntohs(dstport));\n    return add_tcp_conntrack(srcip, dstip, srcport, dstport, is_ipv6, ttl);\n\n    debug(\"____tcp_handle_incoming FALSE: srcport = %hu, dstport = %hu\\n\", ntohs(srcport), ntohs(dstport));\n    return FALSE;\n}\n\nint tcp_handle_outgoing(uint32_t srcip[4], uint32_t dstip[4],\n                        uint16_t srcport, uint16_t dstport,\n                        tcp_conntrack_info_t *conn_info,\n                        uint8_t is_ipv6)\n{\n    char key[TCP_CONNRECORD_KEY_LEN];\n    tcp_connrecord_t *tmp_connrecord = NULL;\n\n    if (!conn_info)\n        return FALSE;\n\n    tcp_cleanup();\n    construct_key(dstip, srcip, dstport, srcport, key, is_ipv6);\n    if (check_get_tcp_conntrack_key(key, &tmp_connrecord) && tmp_connrecord) {\n        /* Connection exists in conntrack, moving on */\n        deconstruct_key(key, tmp_connrecord, conn_info);\n        HASH_DEL(conntrack, tmp_connrecord);\n        free(tmp_connrecord);\n        debug(\"____tcp_handle_outgoing TRUE: srcport = %hu\\n\", ntohs(srcport));\n        return TRUE;\n    }\n\n    debug(\"____tcp_handle_outgoing FALSE: srcport = %hu\\n\", ntohs(srcport));\n    return FALSE;\n}\n\nint tcp_get_auto_ttl(const uint8_t ttl, const uint8_t autottl1,\n                     const uint8_t autottl2, const uint8_t minhops,\n                     const uint8_t maxttl) {\n    uint8_t nhops = 0;\n    uint8_t ttl_of_fake_packet = 0;\n\n    if (ttl > 98 && ttl < 128) {\n        nhops = 128 - ttl;\n    }\n    else if (ttl > 34 && ttl < 64) {\n        nhops = 64 - ttl;\n    }\n    else {\n        return 0;\n    }\n\n    if (nhops <= autottl1 || nhops < minhops) {\n        return 0;\n    }\n\n    ttl_of_fake_packet = nhops - autottl2;\n    if (ttl_of_fake_packet < autottl2 && nhops <= 9) {\n        ttl_of_fake_packet = nhops - autottl1 - trunc((autottl2 - autottl1) * ((float)nhops/10));\n    }\n\n    if (maxttl && ttl_of_fake_packet > maxttl) {\n        ttl_of_fake_packet = maxttl;\n    }\n\n    return ttl_of_fake_packet;\n}"
  },
  {
    "path": "src/ttltrack.h",
    "content": "#ifndef _TTLTRACK_H\n#define _TTLTRACK_H\n#include <stdint.h>\n#include \"dnsredir.h\"\n\ntypedef struct tcp_conntrack_info {\n    uint8_t  is_ipv6;\n    uint8_t  ttl;\n    uint32_t srcip[4];\n    uint16_t srcport;\n    uint32_t dstip[4];\n    uint16_t dstport;\n} tcp_conntrack_info_t;\n\nint tcp_handle_incoming(uint32_t srcip[4], uint32_t dstip[4],\n                        uint16_t srcport, uint16_t dstport,\n                        uint8_t is_ipv6, uint8_t ttl);\n\nint tcp_handle_outgoing(uint32_t srcip[4], uint32_t dstip[4],\n                        uint16_t srcport, uint16_t dstport,\n                        tcp_conntrack_info_t *conn_info,\n                        uint8_t is_ipv6);\n\nint tcp_get_auto_ttl(const uint8_t ttl, const uint8_t autottl1,\n                     const uint8_t autottl2, const uint8_t minhops,\n                     const uint8_t maxttl);\n#endif\n"
  },
  {
    "path": "src/utils/getline.c",
    "content": "/*\t$NetBSD: getdelim.c,v 1.2 2015/12/25 20:12:46 joerg Exp $\t*/\n/*\tNetBSD-src: getline.c,v 1.2 2014/09/16 17:23:50 christos Exp \t*/\n\n/*-\n * Copyright (c) 2011 The NetBSD Foundation, Inc.\n * All rights reserved.\n *\n * This code is derived from software contributed to The NetBSD Foundation\n * by Christos Zoulas.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS\n * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\n * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\n * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS\n * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\n * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\n * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\n * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\n * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n * POSSIBILITY OF SUCH DAMAGE.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include \"getline.h\"\n\n#if !HAVE_GETDELIM\n\nssize_t\ngetdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)\n{\n\tchar *ptr, *eptr;\n\n\n\tif (*buf == NULL || *bufsiz == 0) {\n\t\t*bufsiz = BUFSIZ;\n\t\tif ((*buf = malloc(*bufsiz)) == NULL)\n\t\t\treturn -1;\n\t}\n\n\tfor (ptr = *buf, eptr = *buf + *bufsiz;;) {\n\t\tint c = fgetc(fp);\n\t\tif (c == -1) {\n\t\t\tif (feof(fp)) {\n\t\t\t\tssize_t diff = (ssize_t)(ptr - *buf);\n\t\t\t\tif (diff != 0) {\n\t\t\t\t\t*ptr = '\\0';\n\t\t\t\t\treturn diff;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn -1;\n\t\t}\n\t\t*ptr++ = c;\n\t\tif (c == delimiter) {\n\t\t\t*ptr = '\\0';\n\t\t\treturn ptr - *buf;\n\t\t}\n\t\tif (ptr + 2 >= eptr) {\n\t\t\tchar *nbuf;\n\t\t\tsize_t nbufsiz = *bufsiz * 2;\n\t\t\tssize_t d = ptr - *buf;\n\t\t\tif ((nbuf = realloc(*buf, nbufsiz)) == NULL)\n\t\t\t\treturn -1;\n\t\t\t*buf = nbuf;\n\t\t\t*bufsiz = nbufsiz;\n\t\t\teptr = nbuf + nbufsiz;\n\t\t\tptr = nbuf + d;\n\t\t}\n\t}\n}\n\n#endif\n\n#if !HAVE_GETLINE\n\nssize_t\ngetline(char **buf, size_t *bufsiz, FILE *fp)\n{\n\treturn getdelim(buf, bufsiz, '\\n', fp);\n}\n\n#endif\n"
  },
  {
    "path": "src/utils/getline.h",
    "content": "#if !HAVE_GETDELIM\nssize_t\tgetdelim(char **, size_t *, int, FILE *);\n#endif\n\n#if !HAVE_GETLINE\nssize_t\tgetline(char **, size_t *, FILE *);\n#endif\n"
  },
  {
    "path": "src/utils/repl_str.c",
    "content": "#include <string.h>\n#include <stdlib.h>\n#include <stddef.h>\n\n#if (__STDC_VERSION__ >= 199901L)\n#include <stdint.h>\n#endif\n\nchar *repl_str(const char *str, const char *from, const char *to) {\n\n\t/* Adjust each of the below values to suit your needs. */\n\n\t/* Increment positions cache size initially by this number. */\n\tsize_t cache_sz_inc = 16;\n\t/* Thereafter, each time capacity needs to be increased,\n\t * multiply the increment by this factor. */\n\tconst size_t cache_sz_inc_factor = 3;\n\t/* But never increment capacity by more than this number. */\n\tconst size_t cache_sz_inc_max = 1048576;\n\n\tchar *pret, *ret = NULL;\n\tconst char *pstr2, *pstr = str;\n\tsize_t i, count = 0;\n\t#if (__STDC_VERSION__ >= 199901L)\n\tuintptr_t *pos_cache_tmp, *pos_cache = NULL;\n\t#else\n\tptrdiff_t *pos_cache_tmp, *pos_cache = NULL;\n\t#endif\n\tsize_t cache_sz = 0;\n\tsize_t cpylen, orglen, retlen, tolen, fromlen = strlen(from);\n\n\t/* Find all matches and cache their positions. */\n\twhile ((pstr2 = strstr(pstr, from)) != NULL) {\n\t\tcount++;\n\n\t\t/* Increase the cache size when necessary. */\n\t\tif (cache_sz < count) {\n\t\t\tcache_sz += cache_sz_inc;\n\t\t\tpos_cache_tmp = realloc(pos_cache, sizeof(*pos_cache) * cache_sz);\n\t\t\tif (pos_cache_tmp == NULL) {\n\t\t\t\tgoto end_repl_str;\n\t\t\t} else pos_cache = pos_cache_tmp;\n\t\t\tcache_sz_inc *= cache_sz_inc_factor;\n\t\t\tif (cache_sz_inc > cache_sz_inc_max) {\n\t\t\t\tcache_sz_inc = cache_sz_inc_max;\n\t\t\t}\n\t\t}\n\n\t\tpos_cache[count-1] = (uintptr_t)(pstr2 - str);\n\t\tpstr = pstr2 + fromlen;\n\t}\n\n\torglen = (size_t)(pstr - str) + strlen(pstr);\n\n\t/* Allocate memory for the post-replacement string. */\n\tif (count > 0) {\n\t\ttolen = strlen(to);\n\t\tretlen = orglen + (tolen - fromlen) * count;\n\t} else\tretlen = orglen;\n\tret = malloc(retlen + 1);\n\tif (ret == NULL) {\n\t\tgoto end_repl_str;\n\t}\n\n\tif (count == 0) {\n\t\t/* If no matches, then just duplicate the string. */\n\t\tstrcpy(ret, str);\n\t} else {\n\t\t/* Otherwise, duplicate the string whilst performing\n\t\t * the replacements using the position cache. */\n\t\tpret = ret;\n\t\tmemcpy(pret, str, pos_cache[0]);\n\t\tpret += pos_cache[0];\n\t\tfor (i = 0; i < count; i++) {\n\t\t\tmemcpy(pret, to, tolen);\n\t\t\tpret += tolen;\n\t\t\tpstr = str + pos_cache[i] + fromlen;\n\t\t\tcpylen = (i == count-1 ? orglen : pos_cache[i+1]) - pos_cache[i] - fromlen;\n\t\t\tmemcpy(pret, pstr, cpylen);\n\t\t\tpret += cpylen;\n\t\t}\n\t\tret[retlen] = '\\0';\n\t}\n\nend_repl_str:\n\t/* Free the cache and return the post-replacement string,\n\t * which will be NULL in the event of an error. */\n\tfree(pos_cache);\n\treturn ret;\n}\n"
  },
  {
    "path": "src/utils/repl_str.h",
    "content": "char *repl_str(const char *str, const char *from, const char *to);\n"
  },
  {
    "path": "src/utils/uthash.h",
    "content": "/*\nCopyright (c) 2003-2021, Troy D. Hanson     http://troydhanson.github.io/uthash/\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n    * Redistributions of source code must retain the above copyright\n      notice, this list of conditions and the following disclaimer.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS\nIS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED\nTO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\nPARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER\nOR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\nEXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\nPROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\nPROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\nLIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\nNEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\nSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n*/\n\n#ifndef UTHASH_H\n#define UTHASH_H\n\n#define UTHASH_VERSION 2.3.0\n\n#include <string.h>   /* memcmp, memset, strlen */\n#include <stddef.h>   /* ptrdiff_t */\n#include <stdlib.h>   /* exit */\n\n#if defined(HASH_DEFINE_OWN_STDINT) && HASH_DEFINE_OWN_STDINT\n/* This codepath is provided for backward compatibility, but I plan to remove it. */\n#warning \"HASH_DEFINE_OWN_STDINT is deprecated; please use HASH_NO_STDINT instead\"\ntypedef unsigned int uint32_t;\ntypedef unsigned char uint8_t;\n#elif defined(HASH_NO_STDINT) && HASH_NO_STDINT\n#else\n#include <stdint.h>   /* uint8_t, uint32_t */\n#endif\n\n/* These macros use decltype or the earlier __typeof GNU extension.\n   As decltype is only available in newer compilers (VS2010 or gcc 4.3+\n   when compiling c++ source) this code uses whatever method is needed\n   or, for VS2008 where neither is available, uses casting workarounds. */\n#if !defined(DECLTYPE) && !defined(NO_DECLTYPE)\n#if defined(_MSC_VER)   /* MS compiler */\n#if _MSC_VER >= 1600 && defined(__cplusplus)  /* VS2010 or newer in C++ mode */\n#define DECLTYPE(x) (decltype(x))\n#else                   /* VS2008 or older (or VS2010 in C mode) */\n#define NO_DECLTYPE\n#endif\n#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__)\n#define NO_DECLTYPE\n#else                   /* GNU, Sun and other compilers */\n#define DECLTYPE(x) (__typeof(x))\n#endif\n#endif\n\n#ifdef NO_DECLTYPE\n#define DECLTYPE(x)\n#define DECLTYPE_ASSIGN(dst,src)                                                 \\\ndo {                                                                             \\\n  char **_da_dst = (char**)(&(dst));                                             \\\n  *_da_dst = (char*)(src);                                                       \\\n} while (0)\n#else\n#define DECLTYPE_ASSIGN(dst,src)                                                 \\\ndo {                                                                             \\\n  (dst) = DECLTYPE(dst)(src);                                                    \\\n} while (0)\n#endif\n\n#ifndef uthash_malloc\n#define uthash_malloc(sz) malloc(sz)      /* malloc fcn                      */\n#endif\n#ifndef uthash_free\n#define uthash_free(ptr,sz) free(ptr)     /* free fcn                        */\n#endif\n#ifndef uthash_bzero\n#define uthash_bzero(a,n) memset(a,'\\0',n)\n#endif\n#ifndef uthash_strlen\n#define uthash_strlen(s) strlen(s)\n#endif\n\n#ifndef HASH_FUNCTION\n#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv)\n#endif\n\n#ifndef HASH_KEYCMP\n#define HASH_KEYCMP(a,b,n) memcmp(a,b,n)\n#endif\n\n#ifndef uthash_noexpand_fyi\n#define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */\n#endif\n#ifndef uthash_expand_fyi\n#define uthash_expand_fyi(tbl)            /* can be defined to log expands   */\n#endif\n\n#ifndef HASH_NONFATAL_OOM\n#define HASH_NONFATAL_OOM 0\n#endif\n\n#if HASH_NONFATAL_OOM\n/* malloc failures can be recovered from */\n\n#ifndef uthash_nonfatal_oom\n#define uthash_nonfatal_oom(obj) do {} while (0)    /* non-fatal OOM error */\n#endif\n\n#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0)\n#define IF_HASH_NONFATAL_OOM(x) x\n\n#else\n/* malloc failures result in lost memory, hash tables are unusable */\n\n#ifndef uthash_fatal\n#define uthash_fatal(msg) exit(-1)        /* fatal OOM error */\n#endif\n\n#define HASH_RECORD_OOM(oomed) uthash_fatal(\"out of memory\")\n#define IF_HASH_NONFATAL_OOM(x)\n\n#endif\n\n/* initial number of buckets */\n#define HASH_INITIAL_NUM_BUCKETS 32U     /* initial number of buckets        */\n#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */\n#define HASH_BKT_CAPACITY_THRESH 10U     /* expand when bucket count reaches */\n\n/* calculate the element whose hash handle address is hhp */\n#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))\n/* calculate the hash handle from element address elp */\n#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho)))\n\n#define HASH_ROLLBACK_BKT(hh, head, itemptrhh)                                   \\\ndo {                                                                             \\\n  struct UT_hash_handle *_hd_hh_item = (itemptrhh);                              \\\n  unsigned _hd_bkt;                                                              \\\n  HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);         \\\n  (head)->hh.tbl->buckets[_hd_bkt].count++;                                      \\\n  _hd_hh_item->hh_next = NULL;                                                   \\\n  _hd_hh_item->hh_prev = NULL;                                                   \\\n} while (0)\n\n#define HASH_VALUE(keyptr,keylen,hashv)                                          \\\ndo {                                                                             \\\n  HASH_FUNCTION(keyptr, keylen, hashv);                                          \\\n} while (0)\n\n#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out)                 \\\ndo {                                                                             \\\n  (out) = NULL;                                                                  \\\n  if (head) {                                                                    \\\n    unsigned _hf_bkt;                                                            \\\n    HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt);                  \\\n    if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) {                         \\\n      HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \\\n    }                                                                            \\\n  }                                                                              \\\n} while (0)\n\n#define HASH_FIND(hh,head,keyptr,keylen,out)                                     \\\ndo {                                                                             \\\n  (out) = NULL;                                                                  \\\n  if (head) {                                                                    \\\n    unsigned _hf_hashv;                                                          \\\n    HASH_VALUE(keyptr, keylen, _hf_hashv);                                       \\\n    HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out);             \\\n  }                                                                              \\\n} while (0)\n\n#ifdef HASH_BLOOM\n#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)\n#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)\n#define HASH_BLOOM_MAKE(tbl,oomed)                                               \\\ndo {                                                                             \\\n  (tbl)->bloom_nbits = HASH_BLOOM;                                               \\\n  (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN);                 \\\n  if (!(tbl)->bloom_bv) {                                                        \\\n    HASH_RECORD_OOM(oomed);                                                      \\\n  } else {                                                                       \\\n    uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                           \\\n    (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE;                                     \\\n  }                                                                              \\\n} while (0)\n\n#define HASH_BLOOM_FREE(tbl)                                                     \\\ndo {                                                                             \\\n  uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN);                              \\\n} while (0)\n\n#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))\n#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))\n\n#define HASH_BLOOM_ADD(tbl,hashv)                                                \\\n  HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))\n\n#define HASH_BLOOM_TEST(tbl,hashv)                                               \\\n  HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))\n\n#else\n#define HASH_BLOOM_MAKE(tbl,oomed)\n#define HASH_BLOOM_FREE(tbl)\n#define HASH_BLOOM_ADD(tbl,hashv)\n#define HASH_BLOOM_TEST(tbl,hashv) (1)\n#define HASH_BLOOM_BYTELEN 0U\n#endif\n\n#define HASH_MAKE_TABLE(hh,head,oomed)                                           \\\ndo {                                                                             \\\n  (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table));         \\\n  if (!(head)->hh.tbl) {                                                         \\\n    HASH_RECORD_OOM(oomed);                                                      \\\n  } else {                                                                       \\\n    uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table));                         \\\n    (head)->hh.tbl->tail = &((head)->hh);                                        \\\n    (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;                      \\\n    (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2;            \\\n    (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head);                  \\\n    (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc(                    \\\n        HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket));               \\\n    (head)->hh.tbl->signature = HASH_SIGNATURE;                                  \\\n    if (!(head)->hh.tbl->buckets) {                                              \\\n      HASH_RECORD_OOM(oomed);                                                    \\\n      uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                        \\\n    } else {                                                                     \\\n      uthash_bzero((head)->hh.tbl->buckets,                                      \\\n          HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket));             \\\n      HASH_BLOOM_MAKE((head)->hh.tbl, oomed);                                    \\\n      IF_HASH_NONFATAL_OOM(                                                      \\\n        if (oomed) {                                                             \\\n          uthash_free((head)->hh.tbl->buckets,                                   \\\n              HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));           \\\n          uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                    \\\n        }                                                                        \\\n      )                                                                          \\\n    }                                                                            \\\n  }                                                                              \\\n} while (0)\n\n#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \\\ndo {                                                                             \\\n  (replaced) = NULL;                                                             \\\n  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \\\n  if (replaced) {                                                                \\\n    HASH_DELETE(hh, head, replaced);                                             \\\n  }                                                                              \\\n  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \\\n} while (0)\n\n#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \\\ndo {                                                                             \\\n  (replaced) = NULL;                                                             \\\n  HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \\\n  if (replaced) {                                                                \\\n    HASH_DELETE(hh, head, replaced);                                             \\\n  }                                                                              \\\n  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \\\n} while (0)\n\n#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced)                   \\\ndo {                                                                             \\\n  unsigned _hr_hashv;                                                            \\\n  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \\\n  HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \\\n} while (0)\n\n#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn)    \\\ndo {                                                                             \\\n  unsigned _hr_hashv;                                                            \\\n  HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv);                         \\\n  HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \\\n} while (0)\n\n#define HASH_APPEND_LIST(hh, head, add)                                          \\\ndo {                                                                             \\\n  (add)->hh.next = NULL;                                                         \\\n  (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);           \\\n  (head)->hh.tbl->tail->next = (add);                                            \\\n  (head)->hh.tbl->tail = &((add)->hh);                                           \\\n} while (0)\n\n#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn)                                 \\\ndo {                                                                             \\\n  do {                                                                           \\\n    if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) {                             \\\n      break;                                                                     \\\n    }                                                                            \\\n  } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next));           \\\n} while (0)\n\n#ifdef NO_DECLTYPE\n#undef HASH_AKBI_INNER_LOOP\n#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn)                                 \\\ndo {                                                                             \\\n  char *_hs_saved_head = (char*)(head);                                          \\\n  do {                                                                           \\\n    DECLTYPE_ASSIGN(head, _hs_iter);                                             \\\n    if (cmpfcn(head, add) > 0) {                                                 \\\n      DECLTYPE_ASSIGN(head, _hs_saved_head);                                     \\\n      break;                                                                     \\\n    }                                                                            \\\n    DECLTYPE_ASSIGN(head, _hs_saved_head);                                       \\\n  } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next));           \\\n} while (0)\n#endif\n\n#if HASH_NONFATAL_OOM\n\n#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed)            \\\ndo {                                                                             \\\n  if (!(oomed)) {                                                                \\\n    unsigned _ha_bkt;                                                            \\\n    (head)->hh.tbl->num_items++;                                                 \\\n    HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                  \\\n    HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed);    \\\n    if (oomed) {                                                                 \\\n      HASH_ROLLBACK_BKT(hh, head, &(add)->hh);                                   \\\n      HASH_DELETE_HH(hh, head, &(add)->hh);                                      \\\n      (add)->hh.tbl = NULL;                                                      \\\n      uthash_nonfatal_oom(add);                                                  \\\n    } else {                                                                     \\\n      HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                   \\\n      HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                \\\n    }                                                                            \\\n  } else {                                                                       \\\n    (add)->hh.tbl = NULL;                                                        \\\n    uthash_nonfatal_oom(add);                                                    \\\n  }                                                                              \\\n} while (0)\n\n#else\n\n#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed)            \\\ndo {                                                                             \\\n  unsigned _ha_bkt;                                                              \\\n  (head)->hh.tbl->num_items++;                                                   \\\n  HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt);                    \\\n  HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed);      \\\n  HASH_BLOOM_ADD((head)->hh.tbl, hashval);                                       \\\n  HASH_EMIT_KEY(hh, head, keyptr, keylen_in);                                    \\\n} while (0)\n\n#endif\n\n\n#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \\\ndo {                                                                             \\\n  IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; )                                     \\\n  (add)->hh.hashv = (hashval);                                                   \\\n  (add)->hh.key = (char*) (keyptr);                                              \\\n  (add)->hh.keylen = (unsigned) (keylen_in);                                     \\\n  if (!(head)) {                                                                 \\\n    (add)->hh.next = NULL;                                                       \\\n    (add)->hh.prev = NULL;                                                       \\\n    HASH_MAKE_TABLE(hh, add, _ha_oomed);                                         \\\n    IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { )                                    \\\n      (head) = (add);                                                            \\\n    IF_HASH_NONFATAL_OOM( } )                                                    \\\n  } else {                                                                       \\\n    void *_hs_iter = (head);                                                     \\\n    (add)->hh.tbl = (head)->hh.tbl;                                              \\\n    HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn);                                 \\\n    if (_hs_iter) {                                                              \\\n      (add)->hh.next = _hs_iter;                                                 \\\n      if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) {     \\\n        HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add);              \\\n      } else {                                                                   \\\n        (head) = (add);                                                          \\\n      }                                                                          \\\n      HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add);                      \\\n    } else {                                                                     \\\n      HASH_APPEND_LIST(hh, head, add);                                           \\\n    }                                                                            \\\n  }                                                                              \\\n  HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed);       \\\n  HASH_FSCK(hh, head, \"HASH_ADD_KEYPTR_BYHASHVALUE_INORDER\");                    \\\n} while (0)\n\n#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn)             \\\ndo {                                                                             \\\n  unsigned _hs_hashv;                                                            \\\n  HASH_VALUE(keyptr, keylen_in, _hs_hashv);                                      \\\n  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \\\n} while (0)\n\n#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \\\n  HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)\n\n#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn)                 \\\n  HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)\n\n#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add)        \\\ndo {                                                                             \\\n  IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; )                                     \\\n  (add)->hh.hashv = (hashval);                                                   \\\n  (add)->hh.key = (const void*) (keyptr);                                        \\\n  (add)->hh.keylen = (unsigned) (keylen_in);                                     \\\n  if (!(head)) {                                                                 \\\n    (add)->hh.next = NULL;                                                       \\\n    (add)->hh.prev = NULL;                                                       \\\n    HASH_MAKE_TABLE(hh, add, _ha_oomed);                                         \\\n    IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { )                                    \\\n      (head) = (add);                                                            \\\n    IF_HASH_NONFATAL_OOM( } )                                                    \\\n  } else {                                                                       \\\n    (add)->hh.tbl = (head)->hh.tbl;                                              \\\n    HASH_APPEND_LIST(hh, head, add);                                             \\\n  }                                                                              \\\n  HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed);       \\\n  HASH_FSCK(hh, head, \"HASH_ADD_KEYPTR_BYHASHVALUE\");                            \\\n} while (0)\n\n#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \\\ndo {                                                                             \\\n  unsigned _ha_hashv;                                                            \\\n  HASH_VALUE(keyptr, keylen_in, _ha_hashv);                                      \\\n  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add);      \\\n} while (0)\n\n#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add)            \\\n  HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)\n\n#define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \\\n  HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)\n\n#define HASH_TO_BKT(hashv,num_bkts,bkt)                                          \\\ndo {                                                                             \\\n  bkt = ((hashv) & ((num_bkts) - 1U));                                           \\\n} while (0)\n\n/* delete \"delptr\" from the hash table.\n * \"the usual\" patch-up process for the app-order doubly-linked-list.\n * The use of _hd_hh_del below deserves special explanation.\n * These used to be expressed using (delptr) but that led to a bug\n * if someone used the same symbol for the head and deletee, like\n *  HASH_DELETE(hh,users,users);\n * We want that to work, but by changing the head (users) below\n * we were forfeiting our ability to further refer to the deletee (users)\n * in the patch-up process. Solution: use scratch space to\n * copy the deletee pointer, then the latter references are via that\n * scratch pointer rather than through the repointed (users) symbol.\n */\n#define HASH_DELETE(hh,head,delptr)                                              \\\n    HASH_DELETE_HH(hh, head, &(delptr)->hh)\n\n#define HASH_DELETE_HH(hh,head,delptrhh)                                         \\\ndo {                                                                             \\\n  struct UT_hash_handle *_hd_hh_del = (delptrhh);                                \\\n  if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) {                \\\n    HASH_BLOOM_FREE((head)->hh.tbl);                                             \\\n    uthash_free((head)->hh.tbl->buckets,                                         \\\n                (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket));    \\\n    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \\\n    (head) = NULL;                                                               \\\n  } else {                                                                       \\\n    unsigned _hd_bkt;                                                            \\\n    if (_hd_hh_del == (head)->hh.tbl->tail) {                                    \\\n      (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev);     \\\n    }                                                                            \\\n    if (_hd_hh_del->prev != NULL) {                                              \\\n      HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next;   \\\n    } else {                                                                     \\\n      DECLTYPE_ASSIGN(head, _hd_hh_del->next);                                   \\\n    }                                                                            \\\n    if (_hd_hh_del->next != NULL) {                                              \\\n      HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev;   \\\n    }                                                                            \\\n    HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);        \\\n    HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del);               \\\n    (head)->hh.tbl->num_items--;                                                 \\\n  }                                                                              \\\n  HASH_FSCK(hh, head, \"HASH_DELETE_HH\");                                         \\\n} while (0)\n\n/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */\n#define HASH_FIND_STR(head,findstr,out)                                          \\\ndo {                                                                             \\\n    unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr);            \\\n    HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out);                     \\\n} while (0)\n#define HASH_ADD_STR(head,strfield,add)                                          \\\ndo {                                                                             \\\n    unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield);    \\\n    HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add);                  \\\n} while (0)\n#define HASH_REPLACE_STR(head,strfield,add,replaced)                             \\\ndo {                                                                             \\\n    unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield);    \\\n    HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced);    \\\n} while (0)\n#define HASH_FIND_INT(head,findint,out)                                          \\\n    HASH_FIND(hh,head,findint,sizeof(int),out)\n#define HASH_ADD_INT(head,intfield,add)                                          \\\n    HASH_ADD(hh,head,intfield,sizeof(int),add)\n#define HASH_REPLACE_INT(head,intfield,add,replaced)                             \\\n    HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)\n#define HASH_FIND_PTR(head,findptr,out)                                          \\\n    HASH_FIND(hh,head,findptr,sizeof(void *),out)\n#define HASH_ADD_PTR(head,ptrfield,add)                                          \\\n    HASH_ADD(hh,head,ptrfield,sizeof(void *),add)\n#define HASH_REPLACE_PTR(head,ptrfield,add,replaced)                             \\\n    HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)\n#define HASH_DEL(head,delptr)                                                    \\\n    HASH_DELETE(hh,head,delptr)\n\n/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.\n * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.\n */\n#ifdef HASH_DEBUG\n#include <stdio.h>   /* fprintf, stderr */\n#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0)\n#define HASH_FSCK(hh,head,where)                                                 \\\ndo {                                                                             \\\n  struct UT_hash_handle *_thh;                                                   \\\n  if (head) {                                                                    \\\n    unsigned _bkt_i;                                                             \\\n    unsigned _count = 0;                                                         \\\n    char *_prev;                                                                 \\\n    for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) {           \\\n      unsigned _bkt_count = 0;                                                   \\\n      _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head;                            \\\n      _prev = NULL;                                                              \\\n      while (_thh) {                                                             \\\n        if (_prev != (char*)(_thh->hh_prev)) {                                   \\\n          HASH_OOPS(\"%s: invalid hh_prev %p, actual %p\\n\",                       \\\n              (where), (void*)_thh->hh_prev, (void*)_prev);                      \\\n        }                                                                        \\\n        _bkt_count++;                                                            \\\n        _prev = (char*)(_thh);                                                   \\\n        _thh = _thh->hh_next;                                                    \\\n      }                                                                          \\\n      _count += _bkt_count;                                                      \\\n      if ((head)->hh.tbl->buckets[_bkt_i].count !=  _bkt_count) {                \\\n        HASH_OOPS(\"%s: invalid bucket count %u, actual %u\\n\",                    \\\n            (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count);         \\\n      }                                                                          \\\n    }                                                                            \\\n    if (_count != (head)->hh.tbl->num_items) {                                   \\\n      HASH_OOPS(\"%s: invalid hh item count %u, actual %u\\n\",                     \\\n          (where), (head)->hh.tbl->num_items, _count);                           \\\n    }                                                                            \\\n    _count = 0;                                                                  \\\n    _prev = NULL;                                                                \\\n    _thh =  &(head)->hh;                                                         \\\n    while (_thh) {                                                               \\\n      _count++;                                                                  \\\n      if (_prev != (char*)_thh->prev) {                                          \\\n        HASH_OOPS(\"%s: invalid prev %p, actual %p\\n\",                            \\\n            (where), (void*)_thh->prev, (void*)_prev);                           \\\n      }                                                                          \\\n      _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh);                         \\\n      _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL);     \\\n    }                                                                            \\\n    if (_count != (head)->hh.tbl->num_items) {                                   \\\n      HASH_OOPS(\"%s: invalid app item count %u, actual %u\\n\",                    \\\n          (where), (head)->hh.tbl->num_items, _count);                           \\\n    }                                                                            \\\n  }                                                                              \\\n} while (0)\n#else\n#define HASH_FSCK(hh,head,where)\n#endif\n\n/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to\n * the descriptor to which this macro is defined for tuning the hash function.\n * The app can #include <unistd.h> to get the prototype for write(2). */\n#ifdef HASH_EMIT_KEYS\n#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                                   \\\ndo {                                                                             \\\n  unsigned _klen = fieldlen;                                                     \\\n  write(HASH_EMIT_KEYS, &_klen, sizeof(_klen));                                  \\\n  write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen);                        \\\n} while (0)\n#else\n#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)\n#endif\n\n/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */\n#define HASH_BER(key,keylen,hashv)                                               \\\ndo {                                                                             \\\n  unsigned _hb_keylen = (unsigned)keylen;                                        \\\n  const unsigned char *_hb_key = (const unsigned char*)(key);                    \\\n  (hashv) = 0;                                                                   \\\n  while (_hb_keylen-- != 0U) {                                                   \\\n    (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++;                           \\\n  }                                                                              \\\n} while (0)\n\n\n/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at\n * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */\n#define HASH_SAX(key,keylen,hashv)                                               \\\ndo {                                                                             \\\n  unsigned _sx_i;                                                                \\\n  const unsigned char *_hs_key = (const unsigned char*)(key);                    \\\n  hashv = 0;                                                                     \\\n  for (_sx_i=0; _sx_i < keylen; _sx_i++) {                                       \\\n    hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i];                       \\\n  }                                                                              \\\n} while (0)\n/* FNV-1a variation */\n#define HASH_FNV(key,keylen,hashv)                                               \\\ndo {                                                                             \\\n  unsigned _fn_i;                                                                \\\n  const unsigned char *_hf_key = (const unsigned char*)(key);                    \\\n  (hashv) = 2166136261U;                                                         \\\n  for (_fn_i=0; _fn_i < keylen; _fn_i++) {                                       \\\n    hashv = hashv ^ _hf_key[_fn_i];                                              \\\n    hashv = hashv * 16777619U;                                                   \\\n  }                                                                              \\\n} while (0)\n\n#define HASH_OAT(key,keylen,hashv)                                               \\\ndo {                                                                             \\\n  unsigned _ho_i;                                                                \\\n  const unsigned char *_ho_key=(const unsigned char*)(key);                      \\\n  hashv = 0;                                                                     \\\n  for(_ho_i=0; _ho_i < keylen; _ho_i++) {                                        \\\n      hashv += _ho_key[_ho_i];                                                   \\\n      hashv += (hashv << 10);                                                    \\\n      hashv ^= (hashv >> 6);                                                     \\\n  }                                                                              \\\n  hashv += (hashv << 3);                                                         \\\n  hashv ^= (hashv >> 11);                                                        \\\n  hashv += (hashv << 15);                                                        \\\n} while (0)\n\n#define HASH_JEN_MIX(a,b,c)                                                      \\\ndo {                                                                             \\\n  a -= b; a -= c; a ^= ( c >> 13 );                                              \\\n  b -= c; b -= a; b ^= ( a << 8 );                                               \\\n  c -= a; c -= b; c ^= ( b >> 13 );                                              \\\n  a -= b; a -= c; a ^= ( c >> 12 );                                              \\\n  b -= c; b -= a; b ^= ( a << 16 );                                              \\\n  c -= a; c -= b; c ^= ( b >> 5 );                                               \\\n  a -= b; a -= c; a ^= ( c >> 3 );                                               \\\n  b -= c; b -= a; b ^= ( a << 10 );                                              \\\n  c -= a; c -= b; c ^= ( b >> 15 );                                              \\\n} while (0)\n\n#define HASH_JEN(key,keylen,hashv)                                               \\\ndo {                                                                             \\\n  unsigned _hj_i,_hj_j,_hj_k;                                                    \\\n  unsigned const char *_hj_key=(unsigned const char*)(key);                      \\\n  hashv = 0xfeedbeefu;                                                           \\\n  _hj_i = _hj_j = 0x9e3779b9u;                                                   \\\n  _hj_k = (unsigned)(keylen);                                                    \\\n  while (_hj_k >= 12U) {                                                         \\\n    _hj_i +=    (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 )                      \\\n        + ( (unsigned)_hj_key[2] << 16 )                                         \\\n        + ( (unsigned)_hj_key[3] << 24 ) );                                      \\\n    _hj_j +=    (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 )                      \\\n        + ( (unsigned)_hj_key[6] << 16 )                                         \\\n        + ( (unsigned)_hj_key[7] << 24 ) );                                      \\\n    hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 )                         \\\n        + ( (unsigned)_hj_key[10] << 16 )                                        \\\n        + ( (unsigned)_hj_key[11] << 24 ) );                                     \\\n                                                                                 \\\n     HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                          \\\n                                                                                 \\\n     _hj_key += 12;                                                              \\\n     _hj_k -= 12U;                                                               \\\n  }                                                                              \\\n  hashv += (unsigned)(keylen);                                                   \\\n  switch ( _hj_k ) {                                                             \\\n    case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */         \\\n    case 10: hashv += ( (unsigned)_hj_key[9] << 16 );  /* FALLTHROUGH */         \\\n    case 9:  hashv += ( (unsigned)_hj_key[8] << 8 );   /* FALLTHROUGH */         \\\n    case 8:  _hj_j += ( (unsigned)_hj_key[7] << 24 );  /* FALLTHROUGH */         \\\n    case 7:  _hj_j += ( (unsigned)_hj_key[6] << 16 );  /* FALLTHROUGH */         \\\n    case 6:  _hj_j += ( (unsigned)_hj_key[5] << 8 );   /* FALLTHROUGH */         \\\n    case 5:  _hj_j += _hj_key[4];                      /* FALLTHROUGH */         \\\n    case 4:  _hj_i += ( (unsigned)_hj_key[3] << 24 );  /* FALLTHROUGH */         \\\n    case 3:  _hj_i += ( (unsigned)_hj_key[2] << 16 );  /* FALLTHROUGH */         \\\n    case 2:  _hj_i += ( (unsigned)_hj_key[1] << 8 );   /* FALLTHROUGH */         \\\n    case 1:  _hj_i += _hj_key[0];                      /* FALLTHROUGH */         \\\n    default: ;                                                                   \\\n  }                                                                              \\\n  HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                             \\\n} while (0)\n\n/* The Paul Hsieh hash function */\n#undef get16bits\n#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)             \\\n  || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)\n#define get16bits(d) (*((const uint16_t *) (d)))\n#endif\n\n#if !defined (get16bits)\n#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)             \\\n                       +(uint32_t)(((const uint8_t *)(d))[0]) )\n#endif\n#define HASH_SFH(key,keylen,hashv)                                               \\\ndo {                                                                             \\\n  unsigned const char *_sfh_key=(unsigned const char*)(key);                     \\\n  uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen;                                \\\n                                                                                 \\\n  unsigned _sfh_rem = _sfh_len & 3U;                                             \\\n  _sfh_len >>= 2;                                                                \\\n  hashv = 0xcafebabeu;                                                           \\\n                                                                                 \\\n  /* Main loop */                                                                \\\n  for (;_sfh_len > 0U; _sfh_len--) {                                             \\\n    hashv    += get16bits (_sfh_key);                                            \\\n    _sfh_tmp  = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv;              \\\n    hashv     = (hashv << 16) ^ _sfh_tmp;                                        \\\n    _sfh_key += 2U*sizeof (uint16_t);                                            \\\n    hashv    += hashv >> 11;                                                     \\\n  }                                                                              \\\n                                                                                 \\\n  /* Handle end cases */                                                         \\\n  switch (_sfh_rem) {                                                            \\\n    case 3: hashv += get16bits (_sfh_key);                                       \\\n            hashv ^= hashv << 16;                                                \\\n            hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18;              \\\n            hashv += hashv >> 11;                                                \\\n            break;                                                               \\\n    case 2: hashv += get16bits (_sfh_key);                                       \\\n            hashv ^= hashv << 11;                                                \\\n            hashv += hashv >> 17;                                                \\\n            break;                                                               \\\n    case 1: hashv += *_sfh_key;                                                  \\\n            hashv ^= hashv << 10;                                                \\\n            hashv += hashv >> 1;                                                 \\\n            break;                                                               \\\n    default: ;                                                                   \\\n  }                                                                              \\\n                                                                                 \\\n  /* Force \"avalanching\" of final 127 bits */                                    \\\n  hashv ^= hashv << 3;                                                           \\\n  hashv += hashv >> 5;                                                           \\\n  hashv ^= hashv << 4;                                                           \\\n  hashv += hashv >> 17;                                                          \\\n  hashv ^= hashv << 25;                                                          \\\n  hashv += hashv >> 6;                                                           \\\n} while (0)\n\n/* iterate over items in a known bucket to find desired item */\n#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out)               \\\ndo {                                                                             \\\n  if ((head).hh_head != NULL) {                                                  \\\n    DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head));                     \\\n  } else {                                                                       \\\n    (out) = NULL;                                                                \\\n  }                                                                              \\\n  while ((out) != NULL) {                                                        \\\n    if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) {       \\\n      if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) {                  \\\n        break;                                                                   \\\n      }                                                                          \\\n    }                                                                            \\\n    if ((out)->hh.hh_next != NULL) {                                             \\\n      DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next));                \\\n    } else {                                                                     \\\n      (out) = NULL;                                                              \\\n    }                                                                            \\\n  }                                                                              \\\n} while (0)\n\n/* add an item to a bucket  */\n#define HASH_ADD_TO_BKT(head,hh,addhh,oomed)                                     \\\ndo {                                                                             \\\n  UT_hash_bucket *_ha_head = &(head);                                            \\\n  _ha_head->count++;                                                             \\\n  (addhh)->hh_next = _ha_head->hh_head;                                          \\\n  (addhh)->hh_prev = NULL;                                                       \\\n  if (_ha_head->hh_head != NULL) {                                               \\\n    _ha_head->hh_head->hh_prev = (addhh);                                        \\\n  }                                                                              \\\n  _ha_head->hh_head = (addhh);                                                   \\\n  if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \\\n      && !(addhh)->tbl->noexpand) {                                              \\\n    HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed);                              \\\n    IF_HASH_NONFATAL_OOM(                                                        \\\n      if (oomed) {                                                               \\\n        HASH_DEL_IN_BKT(head,addhh);                                             \\\n      }                                                                          \\\n    )                                                                            \\\n  }                                                                              \\\n} while (0)\n\n/* remove an item from a given bucket */\n#define HASH_DEL_IN_BKT(head,delhh)                                              \\\ndo {                                                                             \\\n  UT_hash_bucket *_hd_head = &(head);                                            \\\n  _hd_head->count--;                                                             \\\n  if (_hd_head->hh_head == (delhh)) {                                            \\\n    _hd_head->hh_head = (delhh)->hh_next;                                        \\\n  }                                                                              \\\n  if ((delhh)->hh_prev) {                                                        \\\n    (delhh)->hh_prev->hh_next = (delhh)->hh_next;                                \\\n  }                                                                              \\\n  if ((delhh)->hh_next) {                                                        \\\n    (delhh)->hh_next->hh_prev = (delhh)->hh_prev;                                \\\n  }                                                                              \\\n} while (0)\n\n/* Bucket expansion has the effect of doubling the number of buckets\n * and redistributing the items into the new buckets. Ideally the\n * items will distribute more or less evenly into the new buckets\n * (the extent to which this is true is a measure of the quality of\n * the hash function as it applies to the key domain).\n *\n * With the items distributed into more buckets, the chain length\n * (item count) in each bucket is reduced. Thus by expanding buckets\n * the hash keeps a bound on the chain length. This bounded chain\n * length is the essence of how a hash provides constant time lookup.\n *\n * The calculation of tbl->ideal_chain_maxlen below deserves some\n * explanation. First, keep in mind that we're calculating the ideal\n * maximum chain length based on the *new* (doubled) bucket count.\n * In fractions this is just n/b (n=number of items,b=new num buckets).\n * Since the ideal chain length is an integer, we want to calculate\n * ceil(n/b). We don't depend on floating point arithmetic in this\n * hash, so to calculate ceil(n/b) with integers we could write\n *\n *      ceil(n/b) = (n/b) + ((n%b)?1:0)\n *\n * and in fact a previous version of this hash did just that.\n * But now we have improved things a bit by recognizing that b is\n * always a power of two. We keep its base 2 log handy (call it lb),\n * so now we can write this with a bit shift and logical AND:\n *\n *      ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)\n *\n */\n#define HASH_EXPAND_BUCKETS(hh,tbl,oomed)                                        \\\ndo {                                                                             \\\n  unsigned _he_bkt;                                                              \\\n  unsigned _he_bkt_i;                                                            \\\n  struct UT_hash_handle *_he_thh, *_he_hh_nxt;                                   \\\n  UT_hash_bucket *_he_new_buckets, *_he_newbkt;                                  \\\n  _he_new_buckets = (UT_hash_bucket*)uthash_malloc(                              \\\n           sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U);             \\\n  if (!_he_new_buckets) {                                                        \\\n    HASH_RECORD_OOM(oomed);                                                      \\\n  } else {                                                                       \\\n    uthash_bzero(_he_new_buckets,                                                \\\n        sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U);                \\\n    (tbl)->ideal_chain_maxlen =                                                  \\\n       ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) +                      \\\n       ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U);    \\\n    (tbl)->nonideal_items = 0;                                                   \\\n    for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) {           \\\n      _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head;                             \\\n      while (_he_thh != NULL) {                                                  \\\n        _he_hh_nxt = _he_thh->hh_next;                                           \\\n        HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt);           \\\n        _he_newbkt = &(_he_new_buckets[_he_bkt]);                                \\\n        if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) {                 \\\n          (tbl)->nonideal_items++;                                               \\\n          if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \\\n            _he_newbkt->expand_mult++;                                           \\\n          }                                                                      \\\n        }                                                                        \\\n        _he_thh->hh_prev = NULL;                                                 \\\n        _he_thh->hh_next = _he_newbkt->hh_head;                                  \\\n        if (_he_newbkt->hh_head != NULL) {                                       \\\n          _he_newbkt->hh_head->hh_prev = _he_thh;                                \\\n        }                                                                        \\\n        _he_newbkt->hh_head = _he_thh;                                           \\\n        _he_thh = _he_hh_nxt;                                                    \\\n      }                                                                          \\\n    }                                                                            \\\n    uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \\\n    (tbl)->num_buckets *= 2U;                                                    \\\n    (tbl)->log2_num_buckets++;                                                   \\\n    (tbl)->buckets = _he_new_buckets;                                            \\\n    (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ?   \\\n        ((tbl)->ineff_expands+1U) : 0U;                                          \\\n    if ((tbl)->ineff_expands > 1U) {                                             \\\n      (tbl)->noexpand = 1;                                                       \\\n      uthash_noexpand_fyi(tbl);                                                  \\\n    }                                                                            \\\n    uthash_expand_fyi(tbl);                                                      \\\n  }                                                                              \\\n} while (0)\n\n\n/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */\n/* Note that HASH_SORT assumes the hash handle name to be hh.\n * HASH_SRT was added to allow the hash handle name to be passed in. */\n#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)\n#define HASH_SRT(hh,head,cmpfcn)                                                 \\\ndo {                                                                             \\\n  unsigned _hs_i;                                                                \\\n  unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize;               \\\n  struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail;            \\\n  if (head != NULL) {                                                            \\\n    _hs_insize = 1;                                                              \\\n    _hs_looping = 1;                                                             \\\n    _hs_list = &((head)->hh);                                                    \\\n    while (_hs_looping != 0U) {                                                  \\\n      _hs_p = _hs_list;                                                          \\\n      _hs_list = NULL;                                                           \\\n      _hs_tail = NULL;                                                           \\\n      _hs_nmerges = 0;                                                           \\\n      while (_hs_p != NULL) {                                                    \\\n        _hs_nmerges++;                                                           \\\n        _hs_q = _hs_p;                                                           \\\n        _hs_psize = 0;                                                           \\\n        for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) {                           \\\n          _hs_psize++;                                                           \\\n          _hs_q = ((_hs_q->next != NULL) ?                                       \\\n            HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL);                   \\\n          if (_hs_q == NULL) {                                                   \\\n            break;                                                               \\\n          }                                                                      \\\n        }                                                                        \\\n        _hs_qsize = _hs_insize;                                                  \\\n        while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) {    \\\n          if (_hs_psize == 0U) {                                                 \\\n            _hs_e = _hs_q;                                                       \\\n            _hs_q = ((_hs_q->next != NULL) ?                                     \\\n              HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL);                 \\\n            _hs_qsize--;                                                         \\\n          } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) {                     \\\n            _hs_e = _hs_p;                                                       \\\n            if (_hs_p != NULL) {                                                 \\\n              _hs_p = ((_hs_p->next != NULL) ?                                   \\\n                HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL);               \\\n            }                                                                    \\\n            _hs_psize--;                                                         \\\n          } else if ((cmpfcn(                                                    \\\n                DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)),             \\\n                DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q))              \\\n                )) <= 0) {                                                       \\\n            _hs_e = _hs_p;                                                       \\\n            if (_hs_p != NULL) {                                                 \\\n              _hs_p = ((_hs_p->next != NULL) ?                                   \\\n                HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL);               \\\n            }                                                                    \\\n            _hs_psize--;                                                         \\\n          } else {                                                               \\\n            _hs_e = _hs_q;                                                       \\\n            _hs_q = ((_hs_q->next != NULL) ?                                     \\\n              HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL);                 \\\n            _hs_qsize--;                                                         \\\n          }                                                                      \\\n          if ( _hs_tail != NULL ) {                                              \\\n            _hs_tail->next = ((_hs_e != NULL) ?                                  \\\n              ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL);                       \\\n          } else {                                                               \\\n            _hs_list = _hs_e;                                                    \\\n          }                                                                      \\\n          if (_hs_e != NULL) {                                                   \\\n            _hs_e->prev = ((_hs_tail != NULL) ?                                  \\\n              ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL);                    \\\n          }                                                                      \\\n          _hs_tail = _hs_e;                                                      \\\n        }                                                                        \\\n        _hs_p = _hs_q;                                                           \\\n      }                                                                          \\\n      if (_hs_tail != NULL) {                                                    \\\n        _hs_tail->next = NULL;                                                   \\\n      }                                                                          \\\n      if (_hs_nmerges <= 1U) {                                                   \\\n        _hs_looping = 0;                                                         \\\n        (head)->hh.tbl->tail = _hs_tail;                                         \\\n        DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list));           \\\n      }                                                                          \\\n      _hs_insize *= 2U;                                                          \\\n    }                                                                            \\\n    HASH_FSCK(hh, head, \"HASH_SRT\");                                             \\\n  }                                                                              \\\n} while (0)\n\n/* This function selects items from one hash into another hash.\n * The end result is that the selected items have dual presence\n * in both hashes. There is no copy of the items made; rather\n * they are added into the new hash through a secondary hash\n * hash handle that must be present in the structure. */\n#define HASH_SELECT(hh_dst, dst, hh_src, src, cond)                              \\\ndo {                                                                             \\\n  unsigned _src_bkt, _dst_bkt;                                                   \\\n  void *_last_elt = NULL, *_elt;                                                 \\\n  UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL;                         \\\n  ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst));                 \\\n  if ((src) != NULL) {                                                           \\\n    for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) {    \\\n      for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head;               \\\n        _src_hh != NULL;                                                         \\\n        _src_hh = _src_hh->hh_next) {                                            \\\n        _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh);                         \\\n        if (cond(_elt)) {                                                        \\\n          IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; )                             \\\n          _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho);          \\\n          _dst_hh->key = _src_hh->key;                                           \\\n          _dst_hh->keylen = _src_hh->keylen;                                     \\\n          _dst_hh->hashv = _src_hh->hashv;                                       \\\n          _dst_hh->prev = _last_elt;                                             \\\n          _dst_hh->next = NULL;                                                  \\\n          if (_last_elt_hh != NULL) {                                            \\\n            _last_elt_hh->next = _elt;                                           \\\n          }                                                                      \\\n          if ((dst) == NULL) {                                                   \\\n            DECLTYPE_ASSIGN(dst, _elt);                                          \\\n            HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed);                             \\\n            IF_HASH_NONFATAL_OOM(                                                \\\n              if (_hs_oomed) {                                                   \\\n                uthash_nonfatal_oom(_elt);                                       \\\n                (dst) = NULL;                                                    \\\n                continue;                                                        \\\n              }                                                                  \\\n            )                                                                    \\\n          } else {                                                               \\\n            _dst_hh->tbl = (dst)->hh_dst.tbl;                                    \\\n          }                                                                      \\\n          HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt);      \\\n          HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \\\n          (dst)->hh_dst.tbl->num_items++;                                        \\\n          IF_HASH_NONFATAL_OOM(                                                  \\\n            if (_hs_oomed) {                                                     \\\n              HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh);                           \\\n              HASH_DELETE_HH(hh_dst, dst, _dst_hh);                              \\\n              _dst_hh->tbl = NULL;                                               \\\n              uthash_nonfatal_oom(_elt);                                         \\\n              continue;                                                          \\\n            }                                                                    \\\n          )                                                                      \\\n          HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv);                          \\\n          _last_elt = _elt;                                                      \\\n          _last_elt_hh = _dst_hh;                                                \\\n        }                                                                        \\\n      }                                                                          \\\n    }                                                                            \\\n  }                                                                              \\\n  HASH_FSCK(hh_dst, dst, \"HASH_SELECT\");                                         \\\n} while (0)\n\n#define HASH_CLEAR(hh,head)                                                      \\\ndo {                                                                             \\\n  if ((head) != NULL) {                                                          \\\n    HASH_BLOOM_FREE((head)->hh.tbl);                                             \\\n    uthash_free((head)->hh.tbl->buckets,                                         \\\n                (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket));      \\\n    uthash_free((head)->hh.tbl, sizeof(UT_hash_table));                          \\\n    (head) = NULL;                                                               \\\n  }                                                                              \\\n} while (0)\n\n#define HASH_OVERHEAD(hh,head)                                                   \\\n (((head) != NULL) ? (                                                           \\\n (size_t)(((head)->hh.tbl->num_items   * sizeof(UT_hash_handle))   +             \\\n          ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket))   +             \\\n           sizeof(UT_hash_table)                                   +             \\\n           (HASH_BLOOM_BYTELEN))) : 0U)\n\n#ifdef NO_DECLTYPE\n#define HASH_ITER(hh,head,el,tmp)                                                \\\nfor(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \\\n  (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))\n#else\n#define HASH_ITER(hh,head,el,tmp)                                                \\\nfor(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL));      \\\n  (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))\n#endif\n\n/* obtain a count of items in the hash */\n#define HASH_COUNT(head) HASH_CNT(hh,head)\n#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)\n\ntypedef struct UT_hash_bucket {\n   struct UT_hash_handle *hh_head;\n   unsigned count;\n\n   /* expand_mult is normally set to 0. In this situation, the max chain length\n    * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If\n    * the bucket's chain exceeds this length, bucket expansion is triggered).\n    * However, setting expand_mult to a non-zero value delays bucket expansion\n    * (that would be triggered by additions to this particular bucket)\n    * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.\n    * (The multiplier is simply expand_mult+1). The whole idea of this\n    * multiplier is to reduce bucket expansions, since they are expensive, in\n    * situations where we know that a particular bucket tends to be overused.\n    * It is better to let its chain length grow to a longer yet-still-bounded\n    * value, than to do an O(n) bucket expansion too often.\n    */\n   unsigned expand_mult;\n\n} UT_hash_bucket;\n\n/* random signature used only to find hash tables in external analysis */\n#define HASH_SIGNATURE 0xa0111fe1u\n#define HASH_BLOOM_SIGNATURE 0xb12220f2u\n\ntypedef struct UT_hash_table {\n   UT_hash_bucket *buckets;\n   unsigned num_buckets, log2_num_buckets;\n   unsigned num_items;\n   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */\n   ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */\n\n   /* in an ideal situation (all buckets used equally), no bucket would have\n    * more than ceil(#items/#buckets) items. that's the ideal chain length. */\n   unsigned ideal_chain_maxlen;\n\n   /* nonideal_items is the number of items in the hash whose chain position\n    * exceeds the ideal chain maxlen. these items pay the penalty for an uneven\n    * hash distribution; reaching them in a chain traversal takes >ideal steps */\n   unsigned nonideal_items;\n\n   /* ineffective expands occur when a bucket doubling was performed, but\n    * afterward, more than half the items in the hash had nonideal chain\n    * positions. If this happens on two consecutive expansions we inhibit any\n    * further expansion, as it's not helping; this happens when the hash\n    * function isn't a good fit for the key domain. When expansion is inhibited\n    * the hash will still work, albeit no longer in constant time. */\n   unsigned ineff_expands, noexpand;\n\n   uint32_t signature; /* used only to find hash tables in external analysis */\n#ifdef HASH_BLOOM\n   uint32_t bloom_sig; /* used only to test bloom exists in external analysis */\n   uint8_t *bloom_bv;\n   uint8_t bloom_nbits;\n#endif\n\n} UT_hash_table;\n\ntypedef struct UT_hash_handle {\n   struct UT_hash_table *tbl;\n   void *prev;                       /* prev element in app order      */\n   void *next;                       /* next element in app order      */\n   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */\n   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */\n   const void *key;                  /* ptr to enclosing struct's key  */\n   unsigned keylen;                  /* enclosing struct's key len     */\n   unsigned hashv;                   /* result of hash-fcn(key)        */\n} UT_hash_handle;\n\n#endif /* UTHASH_H */\n"
  }
]