[
  {
    "path": ".gitattributes",
    "content": ".gitattributes export-ignore\n.gitignore export-ignore\n"
  },
  {
    "path": ".gitignore",
    "content": "cscope.out\n*.o\n*.d\n*.lib\n*.dll\n*.gch\n*.dwo\nsrc/wg\nsrc/wg.exe\n*.swp\n*.id0\n*.id1\n*.id2\n*.nam\n*.til\n*.pro.user\nmaint/\n"
  },
  {
    "path": "COPYING",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 2, June 1991\n\n Copyright (C) 1989, 1991 Free Software Foundation, Inc.,\n 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The licenses for most software are designed to take away your\nfreedom to share and change it.  By contrast, the GNU General Public\nLicense is intended to guarantee your freedom to share and change free\nsoftware--to make sure the software is free for all its users.  This\nGeneral Public License applies to most of the Free Software\nFoundation's software and to any other program whose authors commit to\nusing it.  (Some other Free Software Foundation software is covered by\nthe GNU Lesser General Public License instead.)  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthis service if you wish), that you receive source code or can get it\nif you want it, that you can change the software or use pieces of it\nin new free programs; and that you know you can do these things.\n\n  To protect your rights, we need to make restrictions that forbid\nanyone to deny you these rights or to ask you to surrender the rights.\nThese restrictions translate to certain responsibilities for you if you\ndistribute copies of the software, or if you modify it.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must give the recipients all the rights that\nyou have.  You must make sure that they, too, receive or can get the\nsource code.  And you must show them these terms so they know their\nrights.\n\n  We protect your rights with two steps: (1) copyright the software, and\n(2) offer you this license which gives you legal permission to copy,\ndistribute and/or modify the software.\n\n  Also, for each author's protection and ours, we want to make certain\nthat everyone understands that there is no warranty for this free\nsoftware.  If the software is modified by someone else and passed on, we\nwant its recipients to know that what they have is not the original, so\nthat any problems introduced by others will not reflect on the original\nauthors' reputations.\n\n  Finally, any free program is threatened constantly by software\npatents.  We wish to avoid the danger that redistributors of a free\nprogram will individually obtain patent licenses, in effect making the\nprogram proprietary.  To prevent this, we have made it clear that any\npatent must be licensed for everyone's free use or not licensed at all.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                    GNU GENERAL PUBLIC LICENSE\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n\n  0. This License applies to any program or other work which contains\na notice placed by the copyright holder saying it may be distributed\nunder the terms of this General Public License.  The \"Program\", below,\nrefers to any such program or work, and a \"work based on the Program\"\nmeans either the Program or any derivative work under copyright law:\nthat is to say, a work containing the Program or a portion of it,\neither verbatim or with modifications and/or translated into another\nlanguage.  (Hereinafter, translation is included without limitation in\nthe term \"modification\".)  Each licensee is addressed as \"you\".\n\nActivities other than copying, distribution and modification are not\ncovered by this License; they are outside its scope.  The act of\nrunning the Program is not restricted, and the output from the Program\nis covered only if its contents constitute a work based on the\nProgram (independent of having been made by running the Program).\nWhether that is true depends on what the Program does.\n\n  1. You may copy and distribute verbatim copies of the Program's\nsource code as you receive it, in any medium, provided that you\nconspicuously and appropriately publish on each copy an appropriate\ncopyright notice and disclaimer of warranty; keep intact all the\nnotices that refer to this License and to the absence of any warranty;\nand give any other recipients of the Program a copy of this License\nalong with the Program.\n\nYou may charge a fee for the physical act of transferring a copy, and\nyou may at your option offer warranty protection in exchange for a fee.\n\n  2. You may modify your copy or copies of the Program or any portion\nof it, thus forming a work based on the Program, and copy and\ndistribute such modifications or work under the terms of Section 1\nabove, provided that you also meet all of these conditions:\n\n    a) You must cause the modified files to carry prominent notices\n    stating that you changed the files and the date of any change.\n\n    b) You must cause any work that you distribute or publish, that in\n    whole or in part contains or is derived from the Program or any\n    part thereof, to be licensed as a whole at no charge to all third\n    parties under the terms of this License.\n\n    c) If the modified program normally reads commands interactively\n    when run, you must cause it, when started running for such\n    interactive use in the most ordinary way, to print or display an\n    announcement including an appropriate copyright notice and a\n    notice that there is no warranty (or else, saying that you provide\n    a warranty) and that users may redistribute the program under\n    these conditions, and telling the user how to view a copy of this\n    License.  (Exception: if the Program itself is interactive but\n    does not normally print such an announcement, your work based on\n    the Program is not required to print an announcement.)\n\nThese requirements apply to the modified work as a whole.  If\nidentifiable sections of that work are not derived from the Program,\nand can be reasonably considered independent and separate works in\nthemselves, then this License, and its terms, do not apply to those\nsections when you distribute them as separate works.  But when you\ndistribute the same sections as part of a whole which is a work based\non the Program, the distribution of the whole must be on the terms of\nthis License, whose permissions for other licensees extend to the\nentire whole, and thus to each and every part regardless of who wrote it.\n\nThus, it is not the intent of this section to claim rights or contest\nyour rights to work written entirely by you; rather, the intent is to\nexercise the right to control the distribution of derivative or\ncollective works based on the Program.\n\nIn addition, mere aggregation of another work not based on the Program\nwith the Program (or with a work based on the Program) on a volume of\na storage or distribution medium does not bring the other work under\nthe scope of this License.\n\n  3. You may copy and distribute the Program (or a work based on it,\nunder Section 2) in object code or executable form under the terms of\nSections 1 and 2 above provided that you also do one of the following:\n\n    a) Accompany it with the complete corresponding machine-readable\n    source code, which must be distributed under the terms of Sections\n    1 and 2 above on a medium customarily used for software interchange; or,\n\n    b) Accompany it with a written offer, valid for at least three\n    years, to give any third party, for a charge no more than your\n    cost of physically performing source distribution, a complete\n    machine-readable copy of the corresponding source code, to be\n    distributed under the terms of Sections 1 and 2 above on a medium\n    customarily used for software interchange; or,\n\n    c) Accompany it with the information you received as to the offer\n    to distribute corresponding source code.  (This alternative is\n    allowed only for noncommercial distribution and only if you\n    received the program in object code or executable form with such\n    an offer, in accord with Subsection b above.)\n\nThe source code for a work means the preferred form of the work for\nmaking modifications to it.  For an executable work, complete source\ncode means all the source code for all modules it contains, plus any\nassociated interface definition files, plus the scripts used to\ncontrol compilation and installation of the executable.  However, as a\nspecial exception, the source code distributed need not include\nanything that is normally distributed (in either source or binary\nform) with the major components (compiler, kernel, and so on) of the\noperating system on which the executable runs, unless that component\nitself accompanies the executable.\n\nIf distribution of executable or object code is made by offering\naccess to copy from a designated place, then offering equivalent\naccess to copy the source code from the same place counts as\ndistribution of the source code, even though third parties are not\ncompelled to copy the source along with the object code.\n\n  4. You may not copy, modify, sublicense, or distribute the Program\nexcept as expressly provided under this License.  Any attempt\notherwise to copy, modify, sublicense or distribute the Program is\nvoid, and will automatically terminate your rights under this License.\nHowever, parties who have received copies, or rights, from you under\nthis License will not have their licenses terminated so long as such\nparties remain in full compliance.\n\n  5. You are not required to accept this License, since you have not\nsigned it.  However, nothing else grants you permission to modify or\ndistribute the Program or its derivative works.  These actions are\nprohibited by law if you do not accept this License.  Therefore, by\nmodifying or distributing the Program (or any work based on the\nProgram), you indicate your acceptance of this License to do so, and\nall its terms and conditions for copying, distributing or modifying\nthe Program or works based on it.\n\n  6. Each time you redistribute the Program (or any work based on the\nProgram), the recipient automatically receives a license from the\noriginal licensor to copy, distribute or modify the Program subject to\nthese terms and conditions.  You may not impose any further\nrestrictions on the recipients' exercise of the rights granted herein.\nYou are not responsible for enforcing compliance by third parties to\nthis License.\n\n  7. If, as a consequence of a court judgment or allegation of patent\ninfringement or for any other reason (not limited to patent issues),\nconditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot\ndistribute so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you\nmay not distribute the Program at all.  For example, if a patent\nlicense would not permit royalty-free redistribution of the Program by\nall those who receive copies directly or indirectly through you, then\nthe only way you could satisfy both it and this License would be to\nrefrain entirely from distribution of the Program.\n\nIf any portion of this section is held invalid or unenforceable under\nany particular circumstance, the balance of the section is intended to\napply and the section as a whole is intended to apply in other\ncircumstances.\n\nIt is not the purpose of this section to induce you to infringe any\npatents or other property right claims or to contest validity of any\nsuch claims; this section has the sole purpose of protecting the\nintegrity of the free software distribution system, which is\nimplemented by public license practices.  Many people have made\ngenerous contributions to the wide range of software distributed\nthrough that system in reliance on consistent application of that\nsystem; it is up to the author/donor to decide if he or she is willing\nto distribute software through any other system and a licensee cannot\nimpose that choice.\n\nThis section is intended to make thoroughly clear what is believed to\nbe a consequence of the rest of this License.\n\n  8. If the distribution and/or use of the Program is restricted in\ncertain countries either by patents or by copyrighted interfaces, the\noriginal copyright holder who places the Program under this License\nmay add an explicit geographical distribution limitation excluding\nthose countries, so that distribution is permitted only in or among\ncountries not thus excluded.  In such case, this License incorporates\nthe limitation as if written in the body of this License.\n\n  9. The Free Software Foundation may publish revised and/or new versions\nof the General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\nEach version is given a distinguishing version number.  If the Program\nspecifies a version number of this License which applies to it and \"any\nlater version\", you have the option of following the terms and conditions\neither of that version or of any later version published by the Free\nSoftware Foundation.  If the Program does not specify a version number of\nthis License, you may choose any version ever published by the Free Software\nFoundation.\n\n  10. If you wish to incorporate parts of the Program into other free\nprograms whose distribution conditions are different, write to the author\nto ask for permission.  For software which is copyrighted by the Free\nSoftware Foundation, write to the Free Software Foundation; we sometimes\nmake exceptions for this.  Our decision will be guided by the two goals\nof preserving the free status of all derivatives of our free software and\nof promoting the sharing and reuse of software generally.\n\n                            NO WARRANTY\n\n  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\nFOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\nOTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\nPROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\nOR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\nMERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\nTO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\nPROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\nREPAIR OR CORRECTION.\n\n  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\nREDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\nOUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\nTO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\nYOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\nPROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGES.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nconvey the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software; you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation; either version 2 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License along\n    with this program; if not, write to the Free Software Foundation, Inc.,\n    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program is interactive, make it output a short notice like this\nwhen it starts in an interactive mode:\n\n    Gnomovision version 69, Copyright (C) year name of author\n    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, the commands you use may\nbe called something other than `show w' and `show c'; they could even be\nmouse-clicks or menu items--whatever suits your program.\n\nYou should also get your employer (if you work as a programmer) or your\nschool, if any, to sign a \"copyright disclaimer\" for the program, if\nnecessary.  Here is a sample; alter the names:\n\n  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\n  `Gnomovision' (which makes passes at compilers) written by James Hacker.\n\n  <signature of Ty Coon>, 1 April 1989\n  Ty Coon, President of Vice\n\nThis General Public License does not permit incorporating your program into\nproprietary programs.  If your program is a subroutine library, you may\nconsider it more useful to permit linking proprietary applications with the\nlibrary.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.\n"
  },
  {
    "path": "README.md",
    "content": "# [wireguard-tools](https://git.zx2c4.com/wireguard-tools/about/) &mdash; tools for configuring [WireGuard](https://www.wireguard.com/)\n\nThis supplies the main userspace tooling for using and configuring WireGuard\ntunnels, including the\n[`wg(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8) and\n[`wg-quick(8)`](https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8)\nutilities. This project supports Linux, OpenBSD, FreeBSD, macOS, Windows, and\nAndroid.\n\n**More information may be found at [WireGuard.com](https://www.wireguard.com/).**\n\n## Building\n\n    $ cd src\n    $ make\n\nThere are no dependencies other than a good C compiler and a sane libc.\n\n## Installing\n\n    # make install\n\nThis command takes into account several environment variables:\n\n  * `PREFIX`               default: `/usr`\n  * `DESTDIR`              default:\n  * `BINDIR`               default: `$(PREFIX)/bin`\n  * `LIBDIR`               default: `$(PREFIX)/lib`\n  * `MANDIR`               default: `$(PREFIX)/share/man`\n  * `BASHCOMPDIR`          default: `$(PREFIX)/share/bash-completion/completions`\n  * `RUNSTATEDIR`          default: `/var/run`\n  * `PKG_CONFIG`           default: `pkg-config`\n\n  * `WITH_BASHCOMPLETION`  default: [auto-detect]\n  * `WITH_WGQUICK`         default: [auto-detect]\n  * `WITH_SYSTEMDUNITS`    default: [auto-detect]\n  * `DEBUG`                default:\n\nThe first section is rather standard. The second section is not:\n\n  * `WITH_BASHCOMPLETION` decides whether or not bash completion files for the\n    tools are installed. This is just a nice thing for people who have bash.\n    If you don't have bash, or don't want this, set the environment variable\n    to `no`. If you'd like to force its use, even if bash-completion isn't\n    detected in `DESTDIR`, then set it to `yes`.\n\n  * `WITH_WGQUICK` decides whether or not the wg-quick(8) script is installed.\n    This is a very quick and dirty bash script for reading a few extra\n    variables from wg(8)-style configuration files, and automatically\n    configures the interface. If you don't have bash, you probably don't want\n    this at all. Likewise, if you already have a working network management\n    tool or configuration, you probably want to integrate wg(8) or the direct\n    WireGuard API into your network manager, rather than using wg-quick(8).\n    But for folks who like simple quick and dirty scripts, this is nice. If you'd\n    like to force its use, even if bash isn't detected in DESTDIR, then set it\n    to `yes`.\n\n  * `WITH_SYSTEMDUNITS` decides whether or not systemd units are installed for\n    wg-quick(8). If you don't use systemd, you certainly don't want this, and\n    should set it to `no`. If systemd isn't auto-detected, but you still would\n    like to install it, set this to `yes`.\n\n  * `DEBUG` decides whether to build with `-g`, when set to `yes`.\n\nIf you're a simple `make && make install` kind of user, you can get away with\nnot setting these variables and relying on the auto-detection. However, if\nyou're writing a package for a distro, you'll want to explicitly set these,\ndepending on what you want.\n\n## `contrib/`\n\nThe `contrib/` subdirectory contains various scripts and examples. Most of these\nare not immediately useful for production use, but should provide inspiration for\ncreating fully-featured tools. See the `README` in each directory.\n\n## License\n\nThis project is released under the [GPLv2](COPYING).\n"
  },
  {
    "path": "contrib/dns-hatchet/README",
    "content": "The DNS Hatchet\n===============\n\nThis is a workaround for distributions without resolvconf or any proper\nmechanism of setting the DNS. Running 'apply.sh` in this directory will\ninsert 'hatchet.bash` into the right place in 'wg-quick.bash`. It is\nrecommended that distributions without any resolvconf available run this\nbefore calling 'make install` in their packaging scripts.\n"
  },
  {
    "path": "contrib/dns-hatchet/apply.sh",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n\nME=\"$(readlink -f \"$(dirname \"$(readlink -f \"$0\")\")\")\"\nTOOLS=\"$ME/../../src\"\n\nsed -i \"/~~ function override insertion point ~~/r $ME/hatchet.bash\" \"$TOOLS/wg-quick/linux.bash\"\n"
  },
  {
    "path": "contrib/dns-hatchet/hatchet.bash",
    "content": "set_dns() {\n\t[[ ${#DNS[@]} -gt 0 ]] || return 0\n\n\tif [[ $(resolvconf --version 2>/dev/null) == openresolv\\ * ]]; then\n\t\t{ printf 'nameserver %s\\n' \"${DNS[@]}\"\n\t\t  [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\\n' \"${DNS_SEARCH[*]}\"\n\t\t} | cmd resolvconf -a \"$INTERFACE\" -m 0 -x\n\telse\n\t\techo \"[#] mount \\`${DNS[*]}' /etc/resolv.conf\" >&2\n\t\t[[ -e /etc/resolv.conf ]] || touch /etc/resolv.conf\n\t\t{ cat <<-_EOF\n\t\t\t# This file was generated by wg-quick(8) for use with\n\t\t\t# the WireGuard interface $INTERFACE. It cannot be\n\t\t\t# removed or altered directly. You may remove this file\n\t\t\t# by running \\`wg-quick down $INTERFACE', or if that\n\t\t\t# poses problems, run \\`umount /etc/resolv.conf'.\n\n\t\t_EOF\n\t\tprintf 'nameserver %s\\n' \"${DNS[@]}\"\n\t\t[[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\\n' \"${DNS_SEARCH[*]}\"\n\t\t} | unshare -m --propagation shared bash -c \"$(cat <<-_EOF\n\t\t\tset -e\n\t\t\tcontext=\"\\$(stat -c %C /etc/resolv.conf 2>/dev/null)\" || unset context\n\t\t\tmount --make-private /dev/shm\n\t\t\tmount -t tmpfs none /dev/shm\n\t\t\tcat > /dev/shm/resolv.conf\n\t\t\t[[ -z \\$context || \\$context == \"?\" ]] || chcon \"\\$context\" /dev/shm/resolv.conf 2>/dev/null || true\n\t\t\tmount -o remount,ro /dev/shm\n\t\t\tmount -o bind,ro /dev/shm/resolv.conf /etc/resolv.conf\n\t\t_EOF\n\t\t)\"\n\tfi\n\tHAVE_SET_DNS=1\n}\n\nunset_dns() {\n\t[[ ${#DNS[@]} -gt 0 ]] || return 0\n\n\tif [[ $(resolvconf --version 2>/dev/null) == openresolv\\ * ]]; then\n\t\tcmd resolvconf -d \"$INTERFACE\"\n\telse\n\t\tcmd umount /etc/resolv.conf\n\tfi\n}\n"
  },
  {
    "path": "contrib/embeddable-wg-library/.gitignore",
    "content": "test\n"
  },
  {
    "path": "contrib/embeddable-wg-library/Makefile",
    "content": "CFLAGS += -Wall\n\ntest: test.c wireguard.c wireguard.h\n\nclean:\n\trm -f test\n.PHONY: clean\n"
  },
  {
    "path": "contrib/embeddable-wg-library/README",
    "content": "Embeddable WireGuard C Library\n==============================\n\nThis is a mini single-file library, meant to be embedded directly into the\nsource code of your program. It is *not* meant to be built as a shared\nlibrary.\n\n\nUsage\n-----\n\nCopy wireguard.c and wireguard.h into your project. They should build with\nany C89 compiler. There are no dependencies except libc.\n\nPlease see the set of simple functions in wireguard.h for information on\nhow to use, as well as the example code in test.c.\n\n\nLicense\n-------\n\nBecause this uses code from libmnl, wireguard.c and wireguard.h are licensed\nunder the LGPL-2.1+.\n"
  },
  {
    "path": "contrib/embeddable-wg-library/test.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1+\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include \"wireguard.h\"\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n\nvoid list_devices(void)\n{\n\tchar *device_names, *device_name;\n\tsize_t len;\n\n\tdevice_names = wg_list_device_names();\n\tif (!device_names) {\n\t\tperror(\"Unable to get device names\");\n\t\texit(1);\n\t}\n\twg_for_each_device_name(device_names, device_name, len) {\n\t\twg_device *device;\n\t\twg_peer *peer;\n\t\twg_key_b64_string key;\n\n\t\tif (wg_get_device(&device, device_name) < 0) {\n\t\t\tperror(\"Unable to get device\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (device->flags & WGDEVICE_HAS_PUBLIC_KEY) {\n\t\t\twg_key_to_base64(key, device->public_key);\n\t\t\tprintf(\"%s has public key %s\\n\", device_name, key);\n\t\t} else\n\t\t\tprintf(\"%s has no public key\\n\", device_name);\n\t\twg_for_each_peer(device, peer) {\n\t\t\twg_key_to_base64(key, peer->public_key);\n\t\t\tprintf(\" - peer %s\\n\", key);\n\t\t}\n\t\twg_free_device(device);\n\t}\n\tfree(device_names);\n}\n\nint main(int argc, char *argv[])\n{\n\twg_peer new_peer = {\n\t\t.flags = WGPEER_HAS_PUBLIC_KEY | WGPEER_REPLACE_ALLOWEDIPS\n\t};\n\twg_device new_device = {\n\t\t.name = \"wgtest0\",\n\t\t.listen_port = 1234,\n\t\t.flags = WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_LISTEN_PORT,\n\t\t.first_peer = &new_peer,\n\t\t.last_peer = &new_peer\n\t};\n\twg_key temp_private_key;\n\n\twg_generate_private_key(temp_private_key);\n\twg_generate_public_key(new_peer.public_key, temp_private_key);\n\twg_generate_private_key(new_device.private_key);\n\n\tif (wg_add_device(new_device.name) < 0) {\n\t\tperror(\"Unable to add device\");\n\t\texit(1);\n\t}\n\n\tif (wg_set_device(&new_device) < 0) {\n\t\tperror(\"Unable to set device\");\n\t\texit(1);\n\t}\n\n\tlist_devices();\n\n\tif (wg_del_device(new_device.name) < 0) {\n\t\tperror(\"Unable to delete device\");\n\t\texit(1);\n\t}\n\n\treturn 0;\n}\n"
  },
  {
    "path": "contrib/embeddable-wg-library/wireguard.c",
    "content": "// SPDX-License-Identifier: LGPL-2.1+\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n * Copyright (C) 2008-2012 Pablo Neira Ayuso <pablo@netfilter.org>.\n */\n\n#define _GNU_SOURCE\n\n#include <errno.h>\n#include <linux/genetlink.h>\n#include <linux/if_link.h>\n#include <linux/netlink.h>\n#include <linux/rtnetlink.h>\n#include <netinet/in.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <time.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <assert.h>\n\n#include \"wireguard.h\"\n\n/* wireguard.h netlink uapi: */\n\n#define WG_GENL_NAME \"wireguard\"\n#define WG_GENL_VERSION 1\n\nenum wg_cmd {\n\tWG_CMD_GET_DEVICE,\n\tWG_CMD_SET_DEVICE,\n\t__WG_CMD_MAX\n};\n\nenum wgdevice_flag {\n\tWGDEVICE_F_REPLACE_PEERS = 1U << 0\n};\nenum wgdevice_attribute {\n\tWGDEVICE_A_UNSPEC,\n\tWGDEVICE_A_IFINDEX,\n\tWGDEVICE_A_IFNAME,\n\tWGDEVICE_A_PRIVATE_KEY,\n\tWGDEVICE_A_PUBLIC_KEY,\n\tWGDEVICE_A_FLAGS,\n\tWGDEVICE_A_LISTEN_PORT,\n\tWGDEVICE_A_FWMARK,\n\tWGDEVICE_A_PEERS,\n\t__WGDEVICE_A_LAST\n};\n\nenum wgpeer_flag {\n\tWGPEER_F_REMOVE_ME = 1U << 0,\n\tWGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1\n};\nenum wgpeer_attribute {\n\tWGPEER_A_UNSPEC,\n\tWGPEER_A_PUBLIC_KEY,\n\tWGPEER_A_PRESHARED_KEY,\n\tWGPEER_A_FLAGS,\n\tWGPEER_A_ENDPOINT,\n\tWGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,\n\tWGPEER_A_LAST_HANDSHAKE_TIME,\n\tWGPEER_A_RX_BYTES,\n\tWGPEER_A_TX_BYTES,\n\tWGPEER_A_ALLOWEDIPS,\n\tWGPEER_A_PROTOCOL_VERSION,\n\t__WGPEER_A_LAST\n};\n\nenum wgallowedip_attribute {\n\tWGALLOWEDIP_A_UNSPEC,\n\tWGALLOWEDIP_A_FAMILY,\n\tWGALLOWEDIP_A_IPADDR,\n\tWGALLOWEDIP_A_CIDR_MASK,\n\t__WGALLOWEDIP_A_LAST\n};\n\n/* libmnl mini library: */\n\n#define MNL_SOCKET_AUTOPID 0\n#define MNL_ALIGNTO 4\n#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1))\n#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr))\n#define MNL_ATTR_HDRLEN MNL_ALIGN(sizeof(struct nlattr))\n\nenum mnl_attr_data_type {\n\tMNL_TYPE_UNSPEC,\n\tMNL_TYPE_U8,\n\tMNL_TYPE_U16,\n\tMNL_TYPE_U32,\n\tMNL_TYPE_U64,\n\tMNL_TYPE_STRING,\n\tMNL_TYPE_FLAG,\n\tMNL_TYPE_MSECS,\n\tMNL_TYPE_NESTED,\n\tMNL_TYPE_NESTED_COMPAT,\n\tMNL_TYPE_NUL_STRING,\n\tMNL_TYPE_BINARY,\n\tMNL_TYPE_MAX,\n};\n\n#define mnl_attr_for_each(attr, nlh, offset) \\\n\tfor ((attr) = mnl_nlmsg_get_payload_offset((nlh), (offset)); \\\n\t     mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \\\n\t     (attr) = mnl_attr_next(attr))\n\n#define mnl_attr_for_each_nested(attr, nest) \\\n\tfor ((attr) = mnl_attr_get_payload(nest); \\\n\t     mnl_attr_ok((attr), (char *)mnl_attr_get_payload(nest) + mnl_attr_get_payload_len(nest) - (char *)(attr)); \\\n\t     (attr) = mnl_attr_next(attr))\n\n#define mnl_attr_for_each_payload(payload, payload_size) \\\n\tfor ((attr) = (payload); \\\n\t     mnl_attr_ok((attr), (char *)(payload) + payload_size - (char *)(attr)); \\\n\t     (attr) = mnl_attr_next(attr))\n\n#define MNL_CB_ERROR\t-1\n#define MNL_CB_STOP\t0\n#define MNL_CB_OK\t1\n\ntypedef int (*mnl_attr_cb_t)(const struct nlattr *attr, void *data);\ntypedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data);\n\n#ifndef MNL_ARRAY_SIZE\n#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))\n#endif\n\nstatic size_t mnl_ideal_socket_buffer_size(void)\n{\n\tstatic size_t size = 0;\n\n\tif (size)\n\t\treturn size;\n\tsize = (size_t)sysconf(_SC_PAGESIZE);\n\tif (size > 8192)\n\t\tsize = 8192;\n\treturn size;\n}\n\nstatic size_t mnl_nlmsg_size(size_t len)\n{\n\treturn len + MNL_NLMSG_HDRLEN;\n}\n\nstatic struct nlmsghdr *mnl_nlmsg_put_header(void *buf)\n{\n\tint len = MNL_ALIGN(sizeof(struct nlmsghdr));\n\tstruct nlmsghdr *nlh = buf;\n\n\tmemset(buf, 0, len);\n\tnlh->nlmsg_len = len;\n\treturn nlh;\n}\n\nstatic void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, size_t size)\n{\n\tchar *ptr = (char *)nlh + nlh->nlmsg_len;\n\tsize_t len = MNL_ALIGN(size);\n\tnlh->nlmsg_len += len;\n\tmemset(ptr, 0, len);\n\treturn ptr;\n}\n\nstatic void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh)\n{\n\treturn (void *)nlh + MNL_NLMSG_HDRLEN;\n}\n\nstatic void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t offset)\n{\n\treturn (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);\n}\n\nstatic bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)\n{\n\treturn len >= (int)sizeof(struct nlmsghdr) &&\n\t       nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&\n\t       (int)nlh->nlmsg_len <= len;\n}\n\nstatic struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len)\n{\n\t*len -= MNL_ALIGN(nlh->nlmsg_len);\n\treturn (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len));\n}\n\nstatic void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh)\n{\n\treturn (void *)nlh + MNL_ALIGN(nlh->nlmsg_len);\n}\n\nstatic bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq)\n{\n\treturn nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true;\n}\n\nstatic bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, unsigned int portid)\n{\n\treturn nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true;\n}\n\nstatic uint16_t mnl_attr_get_type(const struct nlattr *attr)\n{\n\treturn attr->nla_type & NLA_TYPE_MASK;\n}\n\nstatic uint16_t mnl_attr_get_payload_len(const struct nlattr *attr)\n{\n\treturn attr->nla_len - MNL_ATTR_HDRLEN;\n}\n\nstatic void *mnl_attr_get_payload(const struct nlattr *attr)\n{\n\treturn (void *)attr + MNL_ATTR_HDRLEN;\n}\n\nstatic bool mnl_attr_ok(const struct nlattr *attr, int len)\n{\n\treturn len >= (int)sizeof(struct nlattr) &&\n\t       attr->nla_len >= sizeof(struct nlattr) &&\n\t       (int)attr->nla_len <= len;\n}\n\nstatic struct nlattr *mnl_attr_next(const struct nlattr *attr)\n{\n\treturn (struct nlattr *)((void *)attr + MNL_ALIGN(attr->nla_len));\n}\n\nstatic int mnl_attr_type_valid(const struct nlattr *attr, uint16_t max)\n{\n\tif (mnl_attr_get_type(attr) > max) {\n\t\terrno = EOPNOTSUPP;\n\t\treturn -1;\n\t}\n\treturn 1;\n}\n\nstatic int __mnl_attr_validate(const struct nlattr *attr,\n\t\t\t       enum mnl_attr_data_type type, size_t exp_len)\n{\n\tuint16_t attr_len = mnl_attr_get_payload_len(attr);\n\tconst char *attr_data = mnl_attr_get_payload(attr);\n\n\tif (attr_len < exp_len) {\n\t\terrno = ERANGE;\n\t\treturn -1;\n\t}\n\tswitch(type) {\n\tcase MNL_TYPE_FLAG:\n\t\tif (attr_len > 0) {\n\t\t\terrno = ERANGE;\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase MNL_TYPE_NUL_STRING:\n\t\tif (attr_len == 0) {\n\t\t\terrno = ERANGE;\n\t\t\treturn -1;\n\t\t}\n\t\tif (attr_data[attr_len-1] != '\\0') {\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase MNL_TYPE_STRING:\n\t\tif (attr_len == 0) {\n\t\t\terrno = ERANGE;\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase MNL_TYPE_NESTED:\n\n\t\tif (attr_len == 0)\n\t\t\tbreak;\n\n\t\tif (attr_len < MNL_ATTR_HDRLEN) {\n\t\t\terrno = ERANGE;\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tdefault:\n\n\t\tbreak;\n\t}\n\tif (exp_len && attr_len > exp_len) {\n\t\terrno = ERANGE;\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\nstatic const size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = {\n\t[MNL_TYPE_U8]\t\t= sizeof(uint8_t),\n\t[MNL_TYPE_U16]\t\t= sizeof(uint16_t),\n\t[MNL_TYPE_U32]\t\t= sizeof(uint32_t),\n\t[MNL_TYPE_U64]\t\t= sizeof(uint64_t),\n\t[MNL_TYPE_MSECS]\t= sizeof(uint64_t),\n};\n\nstatic int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type)\n{\n\tint exp_len;\n\n\tif (type >= MNL_TYPE_MAX) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\texp_len = mnl_attr_data_type_len[type];\n\treturn __mnl_attr_validate(attr, type, exp_len);\n}\n\nstatic int mnl_attr_parse(const struct nlmsghdr *nlh, unsigned int offset,\n\t\t\t  mnl_attr_cb_t cb, void *data)\n{\n\tint ret = MNL_CB_OK;\n\tconst struct nlattr *attr;\n\n\tmnl_attr_for_each(attr, nlh, offset)\n\t\tif ((ret = cb(attr, data)) <= MNL_CB_STOP)\n\t\t\treturn ret;\n\treturn ret;\n}\n\nstatic int mnl_attr_parse_nested(const struct nlattr *nested, mnl_attr_cb_t cb,\n\t\t\t\t void *data)\n{\n\tint ret = MNL_CB_OK;\n\tconst struct nlattr *attr;\n\n\tmnl_attr_for_each_nested(attr, nested)\n\t\tif ((ret = cb(attr, data)) <= MNL_CB_STOP)\n\t\t\treturn ret;\n\treturn ret;\n}\n\nstatic uint8_t mnl_attr_get_u8(const struct nlattr *attr)\n{\n\treturn *((uint8_t *)mnl_attr_get_payload(attr));\n}\n\nstatic uint16_t mnl_attr_get_u16(const struct nlattr *attr)\n{\n\treturn *((uint16_t *)mnl_attr_get_payload(attr));\n}\n\nstatic uint32_t mnl_attr_get_u32(const struct nlattr *attr)\n{\n\treturn *((uint32_t *)mnl_attr_get_payload(attr));\n}\n\nstatic uint64_t mnl_attr_get_u64(const struct nlattr *attr)\n{\n\tuint64_t tmp;\n\tmemcpy(&tmp, mnl_attr_get_payload(attr), sizeof(tmp));\n\treturn tmp;\n}\n\nstatic const char *mnl_attr_get_str(const struct nlattr *attr)\n{\n\treturn mnl_attr_get_payload(attr);\n}\n\nstatic void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, size_t len,\n\t\t\t const void *data)\n{\n\tstruct nlattr *attr = mnl_nlmsg_get_payload_tail(nlh);\n\tuint16_t payload_len = MNL_ALIGN(sizeof(struct nlattr)) + len;\n\tint pad;\n\n\tattr->nla_type = type;\n\tattr->nla_len = payload_len;\n\tmemcpy(mnl_attr_get_payload(attr), data, len);\n\tnlh->nlmsg_len += MNL_ALIGN(payload_len);\n\tpad = MNL_ALIGN(len) - len;\n\tif (pad > 0)\n\t\tmemset(mnl_attr_get_payload(attr) + len, 0, pad);\n}\n\nstatic void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, uint16_t data)\n{\n\tmnl_attr_put(nlh, type, sizeof(uint16_t), &data);\n}\n\nstatic void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t data)\n{\n\tmnl_attr_put(nlh, type, sizeof(uint32_t), &data);\n}\n\nstatic void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, const char *data)\n{\n\tmnl_attr_put(nlh, type, strlen(data)+1, data);\n}\n\nstatic struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type)\n{\n\tstruct nlattr *start = mnl_nlmsg_get_payload_tail(nlh);\n\n\tstart->nla_type = NLA_F_NESTED | type;\n\tnlh->nlmsg_len += MNL_ALIGN(sizeof(struct nlattr));\n\treturn start;\n}\n\nstatic bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen,\n\t\t\t       uint16_t type, size_t len, const void *data)\n{\n\tif (nlh->nlmsg_len + MNL_ATTR_HDRLEN + MNL_ALIGN(len) > buflen)\n\t\treturn false;\n\tmnl_attr_put(nlh, type, len, data);\n\treturn true;\n}\n\nstatic bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen,\n\t\t\t\t  uint16_t type, uint8_t data)\n{\n\treturn mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data);\n}\n\nstatic bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen,\n\t\t\t\t   uint16_t type, uint16_t data)\n{\n\treturn mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data);\n}\n\nstatic bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen,\n\t\t\t\t   uint16_t type, uint32_t data)\n{\n\treturn mnl_attr_put_check(nlh, buflen, type, sizeof(uint32_t), &data);\n}\n\nstatic struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, size_t buflen,\n\t\t\t\t\t\tuint16_t type)\n{\n\tif (nlh->nlmsg_len + MNL_ATTR_HDRLEN > buflen)\n\t\treturn NULL;\n\treturn mnl_attr_nest_start(nlh, type);\n}\n\nstatic void mnl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *start)\n{\n\tstart->nla_len = mnl_nlmsg_get_payload_tail(nlh) - (void *)start;\n}\n\nstatic void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start)\n{\n\tnlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start;\n}\n\nstatic int mnl_cb_noop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)\n{\n\treturn MNL_CB_OK;\n}\n\nstatic int mnl_cb_error(const struct nlmsghdr *nlh, __attribute__((unused)) void *data)\n{\n\tconst struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);\n\n\tif (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {\n\t\terrno = EBADMSG;\n\t\treturn MNL_CB_ERROR;\n\t}\n\n\tif (err->error < 0)\n\t\terrno = -err->error;\n\telse\n\t\terrno = err->error;\n\n\treturn err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;\n}\n\nstatic int mnl_cb_stop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)\n{\n\treturn MNL_CB_STOP;\n}\n\nstatic const mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = {\n\t[NLMSG_NOOP]\t= mnl_cb_noop,\n\t[NLMSG_ERROR]\t= mnl_cb_error,\n\t[NLMSG_DONE]\t= mnl_cb_stop,\n\t[NLMSG_OVERRUN]\t= mnl_cb_noop,\n};\n\nstatic int __mnl_cb_run(const void *buf, size_t numbytes,\n\t\t\tunsigned int seq, unsigned int portid,\n\t\t\tmnl_cb_t cb_data, void *data,\n\t\t\tconst mnl_cb_t *cb_ctl_array,\n\t\t\tunsigned int cb_ctl_array_len)\n{\n\tint ret = MNL_CB_OK, len = numbytes;\n\tconst struct nlmsghdr *nlh = buf;\n\n\twhile (mnl_nlmsg_ok(nlh, len)) {\n\n\t\tif (!mnl_nlmsg_portid_ok(nlh, portid)) {\n\t\t\terrno = ESRCH;\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (!mnl_nlmsg_seq_ok(nlh, seq)) {\n\t\t\terrno = EPROTO;\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {\n\t\t\terrno = EINTR;\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {\n\t\t\tif (cb_data){\n\t\t\t\tret = cb_data(nlh, data);\n\t\t\t\tif (ret <= MNL_CB_STOP)\n\t\t\t\t\tgoto out;\n\t\t\t}\n\t\t} else if (nlh->nlmsg_type < cb_ctl_array_len) {\n\t\t\tif (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) {\n\t\t\t\tret = cb_ctl_array[nlh->nlmsg_type](nlh, data);\n\t\t\t\tif (ret <= MNL_CB_STOP)\n\t\t\t\t\tgoto out;\n\t\t\t}\n\t\t} else if (default_cb_array[nlh->nlmsg_type]) {\n\t\t\tret = default_cb_array[nlh->nlmsg_type](nlh, data);\n\t\t\tif (ret <= MNL_CB_STOP)\n\t\t\t\tgoto out;\n\t\t}\n\t\tnlh = mnl_nlmsg_next(nlh, &len);\n\t}\nout:\n\treturn ret;\n}\n\nstatic int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq,\n\t\t       unsigned int portid, mnl_cb_t cb_data, void *data,\n\t\t       const mnl_cb_t *cb_ctl_array, unsigned int cb_ctl_array_len)\n{\n\treturn __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data,\n\t\t\t    cb_ctl_array, cb_ctl_array_len);\n}\n\nstatic int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,\n\t\t      unsigned int portid, mnl_cb_t cb_data, void *data)\n{\n\treturn __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, NULL, 0);\n}\n\nstruct mnl_socket {\n\tint \t\t\tfd;\n\tstruct sockaddr_nl\taddr;\n};\n\nstatic unsigned int mnl_socket_get_portid(const struct mnl_socket *nl)\n{\n\treturn nl->addr.nl_pid;\n}\n\nstatic struct mnl_socket *__mnl_socket_open(int bus, int flags)\n{\n\tstruct mnl_socket *nl;\n\n\tnl = calloc(1, sizeof(struct mnl_socket));\n\tif (nl == NULL)\n\t\treturn NULL;\n\n\tnl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus);\n\tif (nl->fd == -1) {\n\t\tfree(nl);\n\t\treturn NULL;\n\t}\n\n\treturn nl;\n}\n\nstatic struct mnl_socket *mnl_socket_open(int bus)\n{\n\treturn __mnl_socket_open(bus, 0);\n}\n\nstatic int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid)\n{\n\tint ret;\n\tsocklen_t addr_len;\n\n\tnl->addr.nl_family = AF_NETLINK;\n\tnl->addr.nl_groups = groups;\n\tnl->addr.nl_pid = pid;\n\n\tret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr));\n\tif (ret < 0)\n\t\treturn ret;\n\n\taddr_len = sizeof(nl->addr);\n\tret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len);\n\tif (ret < 0)\n\t\treturn ret;\n\n\tif (addr_len != sizeof(nl->addr)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tif (nl->addr.nl_family != AF_NETLINK) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\nstatic ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *buf,\n\t\t\t\t size_t len)\n{\n\tstatic const struct sockaddr_nl snl = {\n\t\t.nl_family = AF_NETLINK\n\t};\n\treturn sendto(nl->fd, buf, len, 0,\n\t\t      (struct sockaddr *) &snl, sizeof(snl));\n}\n\nstatic ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf,\n\t\t\t\t   size_t bufsiz)\n{\n\tssize_t ret;\n\tstruct sockaddr_nl addr;\n\tstruct iovec iov = {\n\t\t.iov_base\t= buf,\n\t\t.iov_len\t= bufsiz,\n\t};\n\tstruct msghdr msg = {\n\t\t.msg_name\t= &addr,\n\t\t.msg_namelen\t= sizeof(struct sockaddr_nl),\n\t\t.msg_iov\t= &iov,\n\t\t.msg_iovlen\t= 1,\n\t\t.msg_control\t= NULL,\n\t\t.msg_controllen\t= 0,\n\t\t.msg_flags\t= 0,\n\t};\n\tret = recvmsg(nl->fd, &msg, 0);\n\tif (ret == -1)\n\t\treturn ret;\n\n\tif (msg.msg_flags & MSG_TRUNC) {\n\t\terrno = ENOSPC;\n\t\treturn -1;\n\t}\n\tif (msg.msg_namelen != sizeof(struct sockaddr_nl)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\treturn ret;\n}\n\nstatic int mnl_socket_close(struct mnl_socket *nl)\n{\n\tint ret = close(nl->fd);\n\tfree(nl);\n\treturn ret;\n}\n\n/* mnlg mini library: */\n\nstruct mnlg_socket {\n\tstruct mnl_socket *nl;\n\tchar *buf;\n\tuint16_t id;\n\tuint8_t version;\n\tunsigned int seq;\n\tunsigned int portid;\n};\n\nstatic struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,\n\t\t\t\t\t   uint16_t flags, uint16_t id,\n\t\t\t\t\t   uint8_t version)\n{\n\tstruct nlmsghdr *nlh;\n\tstruct genlmsghdr *genl;\n\n\tnlh = mnl_nlmsg_put_header(nlg->buf);\n\tnlh->nlmsg_type\t= id;\n\tnlh->nlmsg_flags = flags;\n\tnlg->seq = time(NULL);\n\tnlh->nlmsg_seq = nlg->seq;\n\n\tgenl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));\n\tgenl->cmd = cmd;\n\tgenl->version = version;\n\n\treturn nlh;\n}\n\nstatic struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,\n\t\t\t\t\t uint16_t flags)\n{\n\treturn __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version);\n}\n\nstatic int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh)\n{\n\treturn mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);\n}\n\nstatic int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data)\n{\n\t(void)nlh;\n\t(void)data;\n\treturn MNL_CB_OK;\n}\n\nstatic int mnlg_cb_error(const struct nlmsghdr *nlh, void *data)\n{\n\tconst struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);\n\t(void)data;\n\n\tif (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {\n\t\terrno = EBADMSG;\n\t\treturn MNL_CB_ERROR;\n\t}\n\t/* Netlink subsystems returns the errno value with different signess */\n\tif (err->error < 0)\n\t\terrno = -err->error;\n\telse\n\t\terrno = err->error;\n\n\treturn err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;\n}\n\nstatic int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data)\n{\n\t(void)data;\n\tif (nlh->nlmsg_flags & NLM_F_MULTI && nlh->nlmsg_len == mnl_nlmsg_size(sizeof(int))) {\n\t\tint error = *(int *)mnl_nlmsg_get_payload(nlh);\n\t\t/* Netlink subsystems returns the errno value with different signess */\n\t\tif (error < 0)\n\t\t\terrno = -error;\n\t\telse\n\t\t\terrno = error;\n\n\t\treturn error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;\n\t}\n\treturn MNL_CB_STOP;\n}\n\nstatic const mnl_cb_t mnlg_cb_array[] = {\n\t[NLMSG_NOOP]\t= mnlg_cb_noop,\n\t[NLMSG_ERROR]\t= mnlg_cb_error,\n\t[NLMSG_DONE]\t= mnlg_cb_stop,\n\t[NLMSG_OVERRUN]\t= mnlg_cb_noop,\n};\n\nstatic int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)\n{\n\tint err;\n\n\tdo {\n\t\terr = mnl_socket_recvfrom(nlg->nl, nlg->buf,\n\t\t\t\t\t  mnl_ideal_socket_buffer_size());\n\t\tif (err <= 0)\n\t\t\tbreak;\n\t\terr = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid,\n\t\t\t\t  data_cb, data, mnlg_cb_array, MNL_ARRAY_SIZE(mnlg_cb_array));\n\t} while (err > 0);\n\n\treturn err;\n}\n\nstatic int get_family_id_attr_cb(const struct nlattr *attr, void *data)\n{\n\tconst struct nlattr **tb = data;\n\tint type = mnl_attr_get_type(attr);\n\n\tif (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)\n\t\treturn MNL_CB_ERROR;\n\n\tif (type == CTRL_ATTR_FAMILY_ID &&\n\t    mnl_attr_validate(attr, MNL_TYPE_U16) < 0)\n\t\treturn MNL_CB_ERROR;\n\ttb[type] = attr;\n\treturn MNL_CB_OK;\n}\n\nstatic int get_family_id_cb(const struct nlmsghdr *nlh, void *data)\n{\n\tuint16_t *p_id = data;\n\tstruct nlattr *tb[CTRL_ATTR_MAX + 1] = { 0 };\n\n\tmnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_family_id_attr_cb, tb);\n\tif (!tb[CTRL_ATTR_FAMILY_ID])\n\t\treturn MNL_CB_ERROR;\n\t*p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);\n\treturn MNL_CB_OK;\n}\n\nstatic struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)\n{\n\tstruct mnlg_socket *nlg;\n\tstruct nlmsghdr *nlh;\n\tint err;\n\n\tnlg = malloc(sizeof(*nlg));\n\tif (!nlg)\n\t\treturn NULL;\n\tnlg->id = 0;\n\n\terr = -ENOMEM;\n\tnlg->buf = malloc(mnl_ideal_socket_buffer_size());\n\tif (!nlg->buf)\n\t\tgoto err_buf_alloc;\n\n\tnlg->nl = mnl_socket_open(NETLINK_GENERIC);\n\tif (!nlg->nl) {\n\t\terr = -errno;\n\t\tgoto err_mnl_socket_open;\n\t}\n\n\tif (mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID) < 0) {\n\t\terr = -errno;\n\t\tgoto err_mnl_socket_bind;\n\t}\n\n\tnlg->portid = mnl_socket_get_portid(nlg->nl);\n\n\tnlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,\n\t\t\t\t NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);\n\tmnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);\n\n\tif (mnlg_socket_send(nlg, nlh) < 0) {\n\t\terr = -errno;\n\t\tgoto err_mnlg_socket_send;\n\t}\n\n\terrno = 0;\n\tif (mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id) < 0) {\n\t\terrno = errno == ENOENT ? EPROTONOSUPPORT : errno;\n\t\terr = errno ? -errno : -ENOSYS;\n\t\tgoto err_mnlg_socket_recv_run;\n\t}\n\n\tnlg->version = version;\n\terrno = 0;\n\treturn nlg;\n\nerr_mnlg_socket_recv_run:\nerr_mnlg_socket_send:\nerr_mnl_socket_bind:\n\tmnl_socket_close(nlg->nl);\nerr_mnl_socket_open:\n\tfree(nlg->buf);\nerr_buf_alloc:\n\tfree(nlg);\n\terrno = -err;\n\treturn NULL;\n}\n\nstatic void mnlg_socket_close(struct mnlg_socket *nlg)\n{\n\tmnl_socket_close(nlg->nl);\n\tfree(nlg->buf);\n\tfree(nlg);\n}\n\n/* wireguard-specific parts: */\n\nstruct string_list {\n\tchar *buffer;\n\tsize_t len;\n\tsize_t cap;\n};\n\nstatic int string_list_add(struct string_list *list, const char *str)\n{\n\tsize_t len = strlen(str) + 1;\n\n\tif (len == 1)\n\t\treturn 0;\n\n\tif (len >= list->cap - list->len) {\n\t\tchar *new_buffer;\n\t\tsize_t new_cap = list->cap * 2;\n\n\t\tif (new_cap <  list->len +len + 1)\n\t\t\tnew_cap = list->len + len + 1;\n\t\tnew_buffer = realloc(list->buffer, new_cap);\n\t\tif (!new_buffer)\n\t\t\treturn -errno;\n\t\tlist->buffer = new_buffer;\n\t\tlist->cap = new_cap;\n\t}\n\tmemcpy(list->buffer + list->len, str, len);\n\tlist->len += len;\n\tlist->buffer[list->len] = '\\0';\n\treturn 0;\n}\n\nstruct interface {\n\tconst char *name;\n\tbool is_wireguard;\n};\n\nstatic int parse_linkinfo(const struct nlattr *attr, void *data)\n{\n\tstruct interface *interface = data;\n\n\tif (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp(WG_GENL_NAME, mnl_attr_get_str(attr)))\n\t\tinterface->is_wireguard = true;\n\treturn MNL_CB_OK;\n}\n\nstatic int parse_infomsg(const struct nlattr *attr, void *data)\n{\n\tstruct interface *interface = data;\n\n\tif (mnl_attr_get_type(attr) == IFLA_LINKINFO)\n\t\treturn mnl_attr_parse_nested(attr, parse_linkinfo, data);\n\telse if (mnl_attr_get_type(attr) == IFLA_IFNAME)\n\t\tinterface->name = mnl_attr_get_str(attr);\n\treturn MNL_CB_OK;\n}\n\nstatic int read_devices_cb(const struct nlmsghdr *nlh, void *data)\n{\n\tstruct string_list *list = data;\n\tstruct interface interface = { 0 };\n\tint ret;\n\n\tret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface);\n\tif (ret != MNL_CB_OK)\n\t\treturn ret;\n\tif (interface.name && interface.is_wireguard)\n\t\tret = string_list_add(list, interface.name);\n\tif (ret < 0)\n\t\treturn ret;\n\tif (nlh->nlmsg_type != NLMSG_DONE)\n\t\treturn MNL_CB_OK + 1;\n\treturn MNL_CB_OK;\n}\n\nstatic int fetch_device_names(struct string_list *list)\n{\n\tstruct mnl_socket *nl = NULL;\n\tchar *rtnl_buffer = NULL;\n\tsize_t message_len;\n\tunsigned int portid, seq;\n\tssize_t len;\n\tint ret = 0;\n\tstruct nlmsghdr *nlh;\n\tstruct ifinfomsg *ifm;\n\n\tret = -ENOMEM;\n\trtnl_buffer = calloc(mnl_ideal_socket_buffer_size(), 1);\n\tif (!rtnl_buffer)\n\t\tgoto cleanup;\n\n\tnl = mnl_socket_open(NETLINK_ROUTE);\n\tif (!nl) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\n\tif (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\n\tseq = time(NULL);\n\tportid = mnl_socket_get_portid(nl);\n\tnlh = mnl_nlmsg_put_header(rtnl_buffer);\n\tnlh->nlmsg_type = RTM_GETLINK;\n\tnlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;\n\tnlh->nlmsg_seq = seq;\n\tifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));\n\tifm->ifi_family = AF_UNSPEC;\n\tmessage_len = nlh->nlmsg_len;\n\n\tif (mnl_socket_sendto(nl, rtnl_buffer, message_len) < 0) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\nanother:\n\tif ((len = mnl_socket_recvfrom(nl, rtnl_buffer, mnl_ideal_socket_buffer_size())) < 0) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\tif ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, list)) < 0) {\n\t\t/* Netlink returns NLM_F_DUMP_INTR if the set of all tunnels changed\n\t\t * during the dump. That's unfortunate, but is pretty common on busy\n\t\t * systems that are adding and removing tunnels all the time. Rather\n\t\t * than retrying, potentially indefinitely, we just work with the\n\t\t * partial results. */\n\t\tif (errno != EINTR) {\n\t\t\tret = -errno;\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\tif (len == MNL_CB_OK + 1)\n\t\tgoto another;\n\tret = 0;\n\ncleanup:\n\tfree(rtnl_buffer);\n\tif (nl)\n\t\tmnl_socket_close(nl);\n\treturn ret;\n}\n\nstatic int add_del_iface(const char *ifname, bool add)\n{\n\tstruct mnl_socket *nl = NULL;\n\tchar *rtnl_buffer;\n\tssize_t len;\n\tint ret;\n\tstruct nlmsghdr *nlh;\n\tstruct ifinfomsg *ifm;\n\tstruct nlattr *nest;\n\n\trtnl_buffer = calloc(mnl_ideal_socket_buffer_size(), 1);\n\tif (!rtnl_buffer) {\n\t\tret = -ENOMEM;\n\t\tgoto cleanup;\n\t}\n\n\tnl = mnl_socket_open(NETLINK_ROUTE);\n\tif (!nl) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\n\tif (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\n\tnlh = mnl_nlmsg_put_header(rtnl_buffer);\n\tnlh->nlmsg_type = add ? RTM_NEWLINK : RTM_DELLINK;\n\tnlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | (add ? NLM_F_CREATE | NLM_F_EXCL : 0);\n\tnlh->nlmsg_seq = time(NULL);\n\tifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));\n\tifm->ifi_family = AF_UNSPEC;\n\tmnl_attr_put_strz(nlh, IFLA_IFNAME, ifname);\n\tnest = mnl_attr_nest_start(nlh, IFLA_LINKINFO);\n\tmnl_attr_put_strz(nlh, IFLA_INFO_KIND, WG_GENL_NAME);\n\tmnl_attr_nest_end(nlh, nest);\n\n\tif (mnl_socket_sendto(nl, rtnl_buffer, nlh->nlmsg_len) < 0) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\tif ((len = mnl_socket_recvfrom(nl, rtnl_buffer, mnl_ideal_socket_buffer_size())) < 0) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\tif (mnl_cb_run(rtnl_buffer, len, nlh->nlmsg_seq, mnl_socket_get_portid(nl), NULL, NULL) < 0) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\tret = 0;\n\ncleanup:\n\tfree(rtnl_buffer);\n\tif (nl)\n\t\tmnl_socket_close(nl);\n\treturn ret;\n}\n\nint wg_set_device(wg_device *dev)\n{\n\tint ret = 0;\n\twg_peer *peer = NULL;\n\twg_allowedip *allowedip = NULL;\n\tstruct nlattr *peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest;\n\tstruct nlmsghdr *nlh;\n\tstruct mnlg_socket *nlg;\n\n\tnlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);\n\tif (!nlg)\n\t\treturn -errno;\n\nagain:\n\tnlh = mnlg_msg_prepare(nlg, WG_CMD_SET_DEVICE, NLM_F_REQUEST | NLM_F_ACK);\n\tmnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, dev->name);\n\n\tif (!peer) {\n\t\tuint32_t flags = 0;\n\n\t\tif (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)\n\t\t\tmnl_attr_put(nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key);\n\t\tif (dev->flags & WGDEVICE_HAS_LISTEN_PORT)\n\t\t\tmnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port);\n\t\tif (dev->flags & WGDEVICE_HAS_FWMARK)\n\t\t\tmnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark);\n\t\tif (dev->flags & WGDEVICE_REPLACE_PEERS)\n\t\t\tflags |= WGDEVICE_F_REPLACE_PEERS;\n\t\tif (flags)\n\t\t\tmnl_attr_put_u32(nlh, WGDEVICE_A_FLAGS, flags);\n\t}\n\tif (!dev->first_peer)\n\t\tgoto send;\n\tpeers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL;\n\tpeers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS);\n\tfor (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) {\n\t\tuint32_t flags = 0;\n\n\t\tpeer_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), 0);\n\t\tif (!peer_nest)\n\t\t\tgoto toobig_peers;\n\t\tif (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key))\n\t\t\tgoto toobig_peers;\n\t\tif (peer->flags & WGPEER_REMOVE_ME)\n\t\t\tflags |= WGPEER_F_REMOVE_ME;\n\t\tif (!allowedip) {\n\t\t\tif (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)\n\t\t\t\tflags |= WGPEER_F_REPLACE_ALLOWEDIPS;\n\t\t\tif (peer->flags & WGPEER_HAS_PRESHARED_KEY) {\n\t\t\t\tif (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key))\n\t\t\t\t\tgoto toobig_peers;\n\t\t\t}\n\t\t\tif (peer->endpoint.addr.sa_family == AF_INET) {\n\t\t\t\tif (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4))\n\t\t\t\t\tgoto toobig_peers;\n\t\t\t} else if (peer->endpoint.addr.sa_family == AF_INET6) {\n\t\t\t\tif (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6))\n\t\t\t\t\tgoto toobig_peers;\n\t\t\t}\n\t\t\tif (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {\n\t\t\t\tif (!mnl_attr_put_u16_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval))\n\t\t\t\t\tgoto toobig_peers;\n\t\t\t}\n\t\t}\n\t\tif (flags) {\n\t\t\tif (!mnl_attr_put_u32_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_FLAGS, flags))\n\t\t\t\tgoto toobig_peers;\n\t\t}\n\t\tif (peer->first_allowedip) {\n\t\t\tif (!allowedip)\n\t\t\t\tallowedip = peer->first_allowedip;\n\t\t\tallowedips_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), WGPEER_A_ALLOWEDIPS);\n\t\t\tif (!allowedips_nest)\n\t\t\t\tgoto toobig_allowedips;\n\t\t\tfor (; allowedip; allowedip = allowedip->next_allowedip) {\n\t\t\t\tallowedip_nest = mnl_attr_nest_start_check(nlh, mnl_ideal_socket_buffer_size(), 0);\n\t\t\t\tif (!allowedip_nest)\n\t\t\t\t\tgoto toobig_allowedips;\n\t\t\t\tif (!mnl_attr_put_u16_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_FAMILY, allowedip->family))\n\t\t\t\t\tgoto toobig_allowedips;\n\t\t\t\tif (allowedip->family == AF_INET) {\n\t\t\t\t\tif (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4))\n\t\t\t\t\t\tgoto toobig_allowedips;\n\t\t\t\t} else if (allowedip->family == AF_INET6) {\n\t\t\t\t\tif (!mnl_attr_put_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6))\n\t\t\t\t\t\tgoto toobig_allowedips;\n\t\t\t\t}\n\t\t\t\tif (!mnl_attr_put_u8_check(nlh, mnl_ideal_socket_buffer_size(), WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr))\n\t\t\t\t\tgoto toobig_allowedips;\n\t\t\t\tmnl_attr_nest_end(nlh, allowedip_nest);\n\t\t\t\tallowedip_nest = NULL;\n\t\t\t}\n\t\t\tmnl_attr_nest_end(nlh, allowedips_nest);\n\t\t\tallowedips_nest = NULL;\n\t\t}\n\n\t\tmnl_attr_nest_end(nlh, peer_nest);\n\t\tpeer_nest = NULL;\n\t}\n\tmnl_attr_nest_end(nlh, peers_nest);\n\tpeers_nest = NULL;\n\tgoto send;\ntoobig_allowedips:\n\tif (allowedip_nest)\n\t\tmnl_attr_nest_cancel(nlh, allowedip_nest);\n\tif (allowedips_nest)\n\t\tmnl_attr_nest_end(nlh, allowedips_nest);\n\tmnl_attr_nest_end(nlh, peer_nest);\n\tmnl_attr_nest_end(nlh, peers_nest);\n\tgoto send;\ntoobig_peers:\n\tif (peer_nest)\n\t\tmnl_attr_nest_cancel(nlh, peer_nest);\n\tmnl_attr_nest_end(nlh, peers_nest);\n\tgoto send;\nsend:\n\tif (mnlg_socket_send(nlg, nlh) < 0) {\n\t\tret = -errno;\n\t\tgoto out;\n\t}\n\terrno = 0;\n\tif (mnlg_socket_recv_run(nlg, NULL, NULL) < 0) {\n\t\tret = errno ? -errno : -EINVAL;\n\t\tgoto out;\n\t}\n\tif (peer)\n\t\tgoto again;\n\nout:\n\tmnlg_socket_close(nlg);\n\terrno = -ret;\n\treturn ret;\n}\n\nstatic int parse_allowedip(const struct nlattr *attr, void *data)\n{\n\twg_allowedip *allowedip = data;\n\n\tswitch (mnl_attr_get_type(attr)) {\n\tcase WGALLOWEDIP_A_UNSPEC:\n\t\tbreak;\n\tcase WGALLOWEDIP_A_FAMILY:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U16))\n\t\t\tallowedip->family = mnl_attr_get_u16(attr);\n\t\tbreak;\n\tcase WGALLOWEDIP_A_IPADDR:\n\t\tif (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip4))\n\t\t\tmemcpy(&allowedip->ip4, mnl_attr_get_payload(attr), sizeof(allowedip->ip4));\n\t\telse if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip6))\n\t\t\tmemcpy(&allowedip->ip6, mnl_attr_get_payload(attr), sizeof(allowedip->ip6));\n\t\tbreak;\n\tcase WGALLOWEDIP_A_CIDR_MASK:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U8))\n\t\t\tallowedip->cidr = mnl_attr_get_u8(attr);\n\t\tbreak;\n\t}\n\n\treturn MNL_CB_OK;\n}\n\nstatic int parse_allowedips(const struct nlattr *attr, void *data)\n{\n\twg_peer *peer = data;\n\twg_allowedip *new_allowedip = calloc(1, sizeof(wg_allowedip));\n\tint ret;\n\n\tif (!new_allowedip)\n\t\treturn MNL_CB_ERROR;\n\tif (!peer->first_allowedip)\n\t\tpeer->first_allowedip = peer->last_allowedip = new_allowedip;\n\telse {\n\t\tpeer->last_allowedip->next_allowedip = new_allowedip;\n\t\tpeer->last_allowedip = new_allowedip;\n\t}\n\tret = mnl_attr_parse_nested(attr, parse_allowedip, new_allowedip);\n\tif (!ret)\n\t\treturn ret;\n\tif (!((new_allowedip->family == AF_INET && new_allowedip->cidr <= 32) || (new_allowedip->family == AF_INET6 && new_allowedip->cidr <= 128))) {\n\t\terrno = EAFNOSUPPORT;\n\t\treturn MNL_CB_ERROR;\n\t}\n\treturn MNL_CB_OK;\n}\n\nbool wg_key_is_zero(const wg_key key)\n{\n\tvolatile uint8_t acc = 0;\n\tunsigned int i;\n\n\tfor (i = 0; i < sizeof(wg_key); ++i) {\n\t\tacc |= key[i];\n\t\t__asm__ (\"\" : \"=r\" (acc) : \"0\" (acc));\n\t}\n\treturn 1 & ((acc - 1) >> 8);\n}\n\nstatic int parse_peer(const struct nlattr *attr, void *data)\n{\n\twg_peer *peer = data;\n\n\tswitch (mnl_attr_get_type(attr)) {\n\tcase WGPEER_A_UNSPEC:\n\t\tbreak;\n\tcase WGPEER_A_PUBLIC_KEY:\n\t\tif (mnl_attr_get_payload_len(attr) == sizeof(peer->public_key)) {\n\t\t\tmemcpy(peer->public_key, mnl_attr_get_payload(attr), sizeof(peer->public_key));\n\t\t\tpeer->flags |= WGPEER_HAS_PUBLIC_KEY;\n\t\t}\n\t\tbreak;\n\tcase WGPEER_A_PRESHARED_KEY:\n\t\tif (mnl_attr_get_payload_len(attr) == sizeof(peer->preshared_key)) {\n\t\t\tmemcpy(peer->preshared_key, mnl_attr_get_payload(attr), sizeof(peer->preshared_key));\n\t\t\tif (!wg_key_is_zero(peer->preshared_key))\n\t\t\t\tpeer->flags |= WGPEER_HAS_PRESHARED_KEY;\n\t\t}\n\t\tbreak;\n\tcase WGPEER_A_ENDPOINT: {\n\t\tstruct sockaddr *addr;\n\n\t\tif (mnl_attr_get_payload_len(attr) < sizeof(*addr))\n\t\t\tbreak;\n\t\taddr = mnl_attr_get_payload(attr);\n\t\tif (addr->sa_family == AF_INET && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr4))\n\t\t\tmemcpy(&peer->endpoint.addr4, addr, sizeof(peer->endpoint.addr4));\n\t\telse if (addr->sa_family == AF_INET6 && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr6))\n\t\t\tmemcpy(&peer->endpoint.addr6, addr, sizeof(peer->endpoint.addr6));\n\t\tbreak;\n\t}\n\tcase WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U16))\n\t\t\tpeer->persistent_keepalive_interval = mnl_attr_get_u16(attr);\n\t\tbreak;\n\tcase WGPEER_A_LAST_HANDSHAKE_TIME:\n\t\tif (mnl_attr_get_payload_len(attr) == sizeof(peer->last_handshake_time))\n\t\t\tmemcpy(&peer->last_handshake_time, mnl_attr_get_payload(attr), sizeof(peer->last_handshake_time));\n\t\tbreak;\n\tcase WGPEER_A_RX_BYTES:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U64))\n\t\t\tpeer->rx_bytes = mnl_attr_get_u64(attr);\n\t\tbreak;\n\tcase WGPEER_A_TX_BYTES:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U64))\n\t\t\tpeer->tx_bytes = mnl_attr_get_u64(attr);\n\t\tbreak;\n\tcase WGPEER_A_ALLOWEDIPS:\n\t\treturn mnl_attr_parse_nested(attr, parse_allowedips, peer);\n\t}\n\n\treturn MNL_CB_OK;\n}\n\nstatic int parse_peers(const struct nlattr *attr, void *data)\n{\n\twg_device *device = data;\n\twg_peer *new_peer = calloc(1, sizeof(wg_peer));\n\tint ret;\n\n\tif (!new_peer)\n\t\treturn MNL_CB_ERROR;\n\tif (!device->first_peer)\n\t\tdevice->first_peer = device->last_peer = new_peer;\n\telse {\n\t\tdevice->last_peer->next_peer = new_peer;\n\t\tdevice->last_peer = new_peer;\n\t}\n\tret = mnl_attr_parse_nested(attr, parse_peer, new_peer);\n\tif (!ret)\n\t\treturn ret;\n\tif (!(new_peer->flags & WGPEER_HAS_PUBLIC_KEY)) {\n\t\terrno = ENXIO;\n\t\treturn MNL_CB_ERROR;\n\t}\n\treturn MNL_CB_OK;\n}\n\nstatic int parse_device(const struct nlattr *attr, void *data)\n{\n\twg_device *device = data;\n\n\tswitch (mnl_attr_get_type(attr)) {\n\tcase WGDEVICE_A_UNSPEC:\n\t\tbreak;\n\tcase WGDEVICE_A_IFINDEX:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U32))\n\t\t\tdevice->ifindex = mnl_attr_get_u32(attr);\n\t\tbreak;\n\tcase WGDEVICE_A_IFNAME:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_STRING)) {\n\t\t\tstrncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1);\n\t\t\tdevice->name[sizeof(device->name) - 1] = '\\0';\n\t\t}\n\t\tbreak;\n\tcase WGDEVICE_A_PRIVATE_KEY:\n\t\tif (mnl_attr_get_payload_len(attr) == sizeof(device->private_key)) {\n\t\t\tmemcpy(device->private_key, mnl_attr_get_payload(attr), sizeof(device->private_key));\n\t\t\tdevice->flags |= WGDEVICE_HAS_PRIVATE_KEY;\n\t\t}\n\t\tbreak;\n\tcase WGDEVICE_A_PUBLIC_KEY:\n\t\tif (mnl_attr_get_payload_len(attr) == sizeof(device->public_key)) {\n\t\t\tmemcpy(device->public_key, mnl_attr_get_payload(attr), sizeof(device->public_key));\n\t\t\tdevice->flags |= WGDEVICE_HAS_PUBLIC_KEY;\n\t\t}\n\t\tbreak;\n\tcase WGDEVICE_A_LISTEN_PORT:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U16))\n\t\t\tdevice->listen_port = mnl_attr_get_u16(attr);\n\t\tbreak;\n\tcase WGDEVICE_A_FWMARK:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U32))\n\t\t\tdevice->fwmark = mnl_attr_get_u32(attr);\n\t\tbreak;\n\tcase WGDEVICE_A_PEERS:\n\t\treturn mnl_attr_parse_nested(attr, parse_peers, device);\n\t}\n\n\treturn MNL_CB_OK;\n}\n\nstatic int read_device_cb(const struct nlmsghdr *nlh, void *data)\n{\n\treturn mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_device, data);\n}\n\nstatic void coalesce_peers(wg_device *device)\n{\n\twg_peer *old_next_peer, *peer = device->first_peer;\n\n\twhile (peer && peer->next_peer) {\n\t\tif (memcmp(peer->public_key, peer->next_peer->public_key, sizeof(wg_key))) {\n\t\t\tpeer = peer->next_peer;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!peer->first_allowedip) {\n\t\t\tpeer->first_allowedip = peer->next_peer->first_allowedip;\n\t\t\tpeer->last_allowedip = peer->next_peer->last_allowedip;\n\t\t} else {\n\t\t\tpeer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip;\n\t\t\tpeer->last_allowedip = peer->next_peer->last_allowedip;\n\t\t}\n\t\told_next_peer = peer->next_peer;\n\t\tpeer->next_peer = old_next_peer->next_peer;\n\t\tfree(old_next_peer);\n\t}\n}\n\nint wg_get_device(wg_device **device, const char *device_name)\n{\n\tint ret = 0;\n\tstruct nlmsghdr *nlh;\n\tstruct mnlg_socket *nlg;\n\ntry_again:\n\t*device = calloc(1, sizeof(wg_device));\n\tif (!*device)\n\t\treturn -errno;\n\n\tnlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);\n\tif (!nlg) {\n\t\twg_free_device(*device);\n\t\t*device = NULL;\n\t\treturn -errno;\n\t}\n\n\tnlh = mnlg_msg_prepare(nlg, WG_CMD_GET_DEVICE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);\n\tmnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, device_name);\n\tif (mnlg_socket_send(nlg, nlh) < 0) {\n\t\tret = -errno;\n\t\tgoto out;\n\t}\n\terrno = 0;\n\tif (mnlg_socket_recv_run(nlg, read_device_cb, *device) < 0) {\n\t\tret = errno ? -errno : -EINVAL;\n\t\tgoto out;\n\t}\n\tcoalesce_peers(*device);\n\nout:\n\tif (nlg)\n\t\tmnlg_socket_close(nlg);\n\tif (ret) {\n\t\twg_free_device(*device);\n\t\tif (ret == -EINTR)\n\t\t\tgoto try_again;\n\t\t*device = NULL;\n\t}\n\terrno = -ret;\n\treturn ret;\n}\n\n/* first\\0second\\0third\\0forth\\0last\\0\\0 */\nchar *wg_list_device_names(void)\n{\n\tstruct string_list list = { 0 };\n\tint ret = fetch_device_names(&list);\n\n\terrno = -ret;\n\tif (errno) {\n\t\tfree(list.buffer);\n\t\treturn NULL;\n\t}\n\treturn list.buffer ?: strdup(\"\\0\");\n}\n\nint wg_add_device(const char *device_name)\n{\n\treturn add_del_iface(device_name, true);\n}\n\nint wg_del_device(const char *device_name)\n{\n\treturn add_del_iface(device_name, false);\n}\n\nvoid wg_free_device(wg_device *dev)\n{\n\twg_peer *peer, *np;\n\twg_allowedip *allowedip, *na;\n\n\tif (!dev)\n\t\treturn;\n\tfor (peer = dev->first_peer, np = peer ? peer->next_peer : NULL; peer; peer = np, np = peer ? peer->next_peer : NULL) {\n\t\tfor (allowedip = peer->first_allowedip, na = allowedip ? allowedip->next_allowedip : NULL; allowedip; allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL)\n\t\t\tfree(allowedip);\n\t\tfree(peer);\n\t}\n\tfree(dev);\n}\n\nstatic void encode_base64(char dest[static 4], const uint8_t src[static 3])\n{\n\tconst uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 };\n\tunsigned int i;\n\n\tfor (i = 0; i < 4; ++i)\n\t\tdest[i] = input[i] + 'A'\n\t\t\t  + (((25 - input[i]) >> 8) & 6)\n\t\t\t  - (((51 - input[i]) >> 8) & 75)\n\t\t\t  - (((61 - input[i]) >> 8) & 15)\n\t\t\t  + (((62 - input[i]) >> 8) & 3);\n\n}\n\nvoid wg_key_to_base64(wg_key_b64_string base64, const wg_key key)\n{\n\tunsigned int i;\n\n\tfor (i = 0; i < 32 / 3; ++i)\n\t\tencode_base64(&base64[i * 4], &key[i * 3]);\n\tencode_base64(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 });\n\tbase64[sizeof(wg_key_b64_string) - 2] = '=';\n\tbase64[sizeof(wg_key_b64_string) - 1] = '\\0';\n}\n\nstatic int decode_base64(const char src[static 4])\n{\n\tint val = 0;\n\tunsigned int i;\n\n\tfor (i = 0; i < 4; ++i)\n\t\tval |= (-1\n\t\t\t    + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64))\n\t\t\t    + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70))\n\t\t\t    + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5))\n\t\t\t    + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63)\n\t\t\t    + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64)\n\t\t\t) << (18 - 6 * i);\n\treturn val;\n}\n\nint wg_key_from_base64(wg_key key, const wg_key_b64_string base64)\n{\n\tunsigned int i;\n\tint val;\n\tvolatile uint8_t ret = 0;\n\n\tif (strlen(base64) != sizeof(wg_key_b64_string) - 1 || base64[sizeof(wg_key_b64_string) - 2] != '=') {\n\t\terrno = EINVAL;\n\t\tgoto out;\n\t}\n\n\tfor (i = 0; i < 32 / 3; ++i) {\n\t\tval = decode_base64(&base64[i * 4]);\n\t\tret |= (uint32_t)val >> 31;\n\t\tkey[i * 3 + 0] = (val >> 16) & 0xff;\n\t\tkey[i * 3 + 1] = (val >> 8) & 0xff;\n\t\tkey[i * 3 + 2] = val & 0xff;\n\t}\n\tval = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' });\n\tret |= ((uint32_t)val >> 31) | (val & 0xff);\n\tkey[i * 3 + 0] = (val >> 16) & 0xff;\n\tkey[i * 3 + 1] = (val >> 8) & 0xff;\n\terrno = EINVAL & ~((ret - 1) >> 8);\nout:\n\treturn -errno;\n}\n\ntypedef int64_t fe[16];\n\nstatic __attribute__((noinline)) void memzero_explicit(void *s, size_t count)\n{\n\tmemset(s, 0, count);\n\t__asm__ __volatile__(\"\": :\"r\"(s) :\"memory\");\n}\n\nstatic void carry(fe o)\n{\n\tint i;\n\n\tfor (i = 0; i < 16; ++i) {\n\t\to[(i + 1) % 16] += (i == 15 ? 38 : 1) * (o[i] >> 16);\n\t\to[i] &= 0xffff;\n\t}\n}\n\nstatic void cswap(fe p, fe q, int b)\n{\n\tint i;\n\tint64_t t, c = ~(b - 1);\n\n\tfor (i = 0; i < 16; ++i) {\n\t\tt = c & (p[i] ^ q[i]);\n\t\tp[i] ^= t;\n\t\tq[i] ^= t;\n\t}\n\n\tmemzero_explicit(&t, sizeof(t));\n\tmemzero_explicit(&c, sizeof(c));\n\tmemzero_explicit(&b, sizeof(b));\n}\n\nstatic void pack(uint8_t *o, const fe n)\n{\n\tint i, j, b;\n\tfe m, t;\n\n\tmemcpy(t, n, sizeof(t));\n\tcarry(t);\n\tcarry(t);\n\tcarry(t);\n\tfor (j = 0; j < 2; ++j) {\n\t\tm[0] = t[0] - 0xffed;\n\t\tfor (i = 1; i < 15; ++i) {\n\t\t\tm[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);\n\t\t\tm[i - 1] &= 0xffff;\n\t\t}\n\t\tm[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);\n\t\tb = (m[15] >> 16) & 1;\n\t\tm[14] &= 0xffff;\n\t\tcswap(t, m, 1 - b);\n\t}\n\tfor (i = 0; i < 16; ++i) {\n\t\to[2 * i] = t[i] & 0xff;\n\t\to[2 * i + 1] = t[i] >> 8;\n\t}\n\n\tmemzero_explicit(m, sizeof(m));\n\tmemzero_explicit(t, sizeof(t));\n\tmemzero_explicit(&b, sizeof(b));\n}\n\nstatic void add(fe o, const fe a, const fe b)\n{\n\tint i;\n\n\tfor (i = 0; i < 16; ++i)\n\t\to[i] = a[i] + b[i];\n}\n\nstatic void subtract(fe o, const fe a, const fe b)\n{\n\tint i;\n\n\tfor (i = 0; i < 16; ++i)\n\t\to[i] = a[i] - b[i];\n}\n\nstatic void multmod(fe o, const fe a, const fe b)\n{\n\tint i, j;\n\tint64_t t[31] = { 0 };\n\n\tfor (i = 0; i < 16; ++i) {\n\t\tfor (j = 0; j < 16; ++j)\n\t\t\tt[i + j] += a[i] * b[j];\n\t}\n\tfor (i = 0; i < 15; ++i)\n\t\tt[i] += 38 * t[i + 16];\n\tmemcpy(o, t, sizeof(fe));\n\tcarry(o);\n\tcarry(o);\n\n\tmemzero_explicit(t, sizeof(t));\n}\n\nstatic void invert(fe o, const fe i)\n{\n\tfe c;\n\tint a;\n\n\tmemcpy(c, i, sizeof(c));\n\tfor (a = 253; a >= 0; --a) {\n\t\tmultmod(c, c, c);\n\t\tif (a != 2 && a != 4)\n\t\t\tmultmod(c, c, i);\n\t}\n\tmemcpy(o, c, sizeof(fe));\n\n\tmemzero_explicit(c, sizeof(c));\n}\n\nstatic void clamp_key(uint8_t *z)\n{\n\tz[31] = (z[31] & 127) | 64;\n\tz[0] &= 248;\n}\n\nvoid wg_generate_public_key(wg_key public_key, const wg_key private_key)\n{\n\tint i, r;\n\tuint8_t z[32];\n\tfe a = { 1 }, b = { 9 }, c = { 0 }, d = { 1 }, e, f;\n\n\tmemcpy(z, private_key, sizeof(z));\n\tclamp_key(z);\n\n\tfor (i = 254; i >= 0; --i) {\n\t\tr = (z[i >> 3] >> (i & 7)) & 1;\n\t\tcswap(a, b, r);\n\t\tcswap(c, d, r);\n\t\tadd(e, a, c);\n\t\tsubtract(a, a, c);\n\t\tadd(c, b, d);\n\t\tsubtract(b, b, d);\n\t\tmultmod(d, e, e);\n\t\tmultmod(f, a, a);\n\t\tmultmod(a, c, a);\n\t\tmultmod(c, b, e);\n\t\tadd(e, a, c);\n\t\tsubtract(a, a, c);\n\t\tmultmod(b, a, a);\n\t\tsubtract(c, d, f);\n\t\tmultmod(a, c, (const fe){ 0xdb41, 1 });\n\t\tadd(a, a, d);\n\t\tmultmod(c, c, a);\n\t\tmultmod(a, d, f);\n\t\tmultmod(d, b, (const fe){ 9 });\n\t\tmultmod(b, e, e);\n\t\tcswap(a, b, r);\n\t\tcswap(c, d, r);\n\t}\n\tinvert(c, c);\n\tmultmod(a, a, c);\n\tpack(public_key, a);\n\n\tmemzero_explicit(&r, sizeof(r));\n\tmemzero_explicit(z, sizeof(z));\n\tmemzero_explicit(a, sizeof(a));\n\tmemzero_explicit(b, sizeof(b));\n\tmemzero_explicit(c, sizeof(c));\n\tmemzero_explicit(d, sizeof(d));\n\tmemzero_explicit(e, sizeof(e));\n\tmemzero_explicit(f, sizeof(f));\n}\n\nvoid wg_generate_private_key(wg_key private_key)\n{\n\twg_generate_preshared_key(private_key);\n\tclamp_key(private_key);\n}\n\nvoid wg_generate_preshared_key(wg_key preshared_key)\n{\n\tssize_t ret;\n\tsize_t i;\n\tint fd;\n#if defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)))\n\tif (!getentropy(preshared_key, sizeof(wg_key)))\n\t\treturn;\n#endif\n#if defined(__NR_getrandom) && defined(__linux__)\n\tif (syscall(__NR_getrandom, preshared_key, sizeof(wg_key), 0) == sizeof(wg_key))\n\t\treturn;\n#endif\n\tfd = open(\"/dev/urandom\", O_RDONLY);\n\tassert(fd >= 0);\n\tfor (i = 0; i < sizeof(wg_key); i += ret) {\n\t\tret = read(fd, preshared_key + i, sizeof(wg_key) - i);\n\t\tassert(ret > 0);\n\t}\n\tclose(fd);\n}\n"
  },
  {
    "path": "contrib/embeddable-wg-library/wireguard.h",
    "content": "/* SPDX-License-Identifier: LGPL-2.1+ */\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#ifndef WIREGUARD_H\n#define WIREGUARD_H\n\n#include <net/if.h>\n#include <netinet/in.h>\n#include <sys/socket.h>\n#include <time.h>\n#include <stdint.h>\n#include <stdbool.h>\n\ntypedef uint8_t wg_key[32];\ntypedef char wg_key_b64_string[((sizeof(wg_key) + 2) / 3) * 4 + 1];\n\n/* Cross platform __kernel_timespec */\nstruct timespec64 {\n\tint64_t tv_sec;\n\tint64_t tv_nsec;\n};\n\ntypedef struct wg_allowedip {\n\tuint16_t family;\n\tunion {\n\t\tstruct in_addr ip4;\n\t\tstruct in6_addr ip6;\n\t};\n\tuint8_t cidr;\n\tstruct wg_allowedip *next_allowedip;\n} wg_allowedip;\n\nenum wg_peer_flags {\n\tWGPEER_REMOVE_ME = 1U << 0,\n\tWGPEER_REPLACE_ALLOWEDIPS = 1U << 1,\n\tWGPEER_HAS_PUBLIC_KEY = 1U << 2,\n\tWGPEER_HAS_PRESHARED_KEY = 1U << 3,\n\tWGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4\n};\n\ntypedef union wg_endpoint {\n\tstruct sockaddr addr;\n\tstruct sockaddr_in addr4;\n\tstruct sockaddr_in6 addr6;\n} wg_endpoint;\n\ntypedef struct wg_peer {\n\tenum wg_peer_flags flags;\n\n\twg_key public_key;\n\twg_key preshared_key;\n\n\twg_endpoint endpoint;\n\n\tstruct timespec64 last_handshake_time;\n\tuint64_t rx_bytes, tx_bytes;\n\tuint16_t persistent_keepalive_interval;\n\n\tstruct wg_allowedip *first_allowedip, *last_allowedip;\n\tstruct wg_peer *next_peer;\n} wg_peer;\n\nenum wg_device_flags {\n\tWGDEVICE_REPLACE_PEERS = 1U << 0,\n\tWGDEVICE_HAS_PRIVATE_KEY = 1U << 1,\n\tWGDEVICE_HAS_PUBLIC_KEY = 1U << 2,\n\tWGDEVICE_HAS_LISTEN_PORT = 1U << 3,\n\tWGDEVICE_HAS_FWMARK = 1U << 4\n};\n\ntypedef struct wg_device {\n\tchar name[IFNAMSIZ];\n\tuint32_t ifindex;\n\n\tenum wg_device_flags flags;\n\n\twg_key public_key;\n\twg_key private_key;\n\n\tuint32_t fwmark;\n\tuint16_t listen_port;\n\n\tstruct wg_peer *first_peer, *last_peer;\n} wg_device;\n\n#define wg_for_each_device_name(__names, __name, __len) for ((__name) = (__names), (__len) = 0; ((__len) = strlen(__name)); (__name) += (__len) + 1)\n#define wg_for_each_peer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer)\n#define wg_for_each_allowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip)\n\nint wg_set_device(wg_device *dev);\nint wg_get_device(wg_device **dev, const char *device_name);\nint wg_add_device(const char *device_name);\nint wg_del_device(const char *device_name);\nvoid wg_free_device(wg_device *dev);\nchar *wg_list_device_names(void); /* first\\0second\\0third\\0forth\\0last\\0\\0 */\nvoid wg_key_to_base64(wg_key_b64_string base64, const wg_key key);\nint wg_key_from_base64(wg_key key, const wg_key_b64_string base64);\nbool wg_key_is_zero(const wg_key key);\nvoid wg_generate_public_key(wg_key public_key, const wg_key private_key);\nvoid wg_generate_private_key(wg_key private_key);\nvoid wg_generate_preshared_key(wg_key preshared_key);\n\n#endif\n"
  },
  {
    "path": "contrib/external-tests/haskell/Setup.hs",
    "content": "import Distribution.Simple\nmain = defaultMain\n"
  },
  {
    "path": "contrib/external-tests/haskell/package.yaml",
    "content": "name: cacophony-wg\nversion: 0.1.0\nlicense: PublicDomain\nmaintainer: John Galt <jgalt@centromere.net>\ncategory: Cryptography\nghc-options: -Wall\n\nexecutables:\n  cacophony-wg:\n    main: Main.hs\n    source-dirs: src\n\n    dependencies:\n      - base\n      - base16-bytestring\n      - base64-bytestring\n      - blake2\n      - bytestring\n      - cacophony >= 0.10\n      - cereal\n      - cryptonite\n      - memory\n      - network\n      - time\n\n    ghc-options:\n      - -O2\n      - -rtsopts\n      - -threaded\n      - -with-rtsopts=-N\n\n    other-modules:\n      - Data.Time.TAI64\n\n    default-extensions:\n      - OverloadedStrings\n"
  },
  {
    "path": "contrib/external-tests/haskell/src/Data/Time/TAI64.hs",
    "content": "module Data.Time.TAI64 (\n    TAI64(..)\n  , TAI64N(..)\n  , TAI64NA(..)\n  , posixToTAI64\n  , posixToTAI64N\n  , posixToTAI64NA\n  , getCurrentTAI64\n  , getCurrentTAI64N\n  , getCurrentTAI64NA\n  , tAI64ToPosix\n  , tAI64NToPosix\n  , tAI64NAToPosix\n) where\n\nimport Data.Serialize\nimport Control.Monad\nimport Data.Word\n\nimport Data.Time.Clock\nimport Data.Time.Clock.POSIX\n\nimport Numeric\n\ndata TAI64 = TAI64\n  {-# UNPACK #-} !Word64\n  deriving (Eq, Ord)\n\ndata TAI64N = TAI64N\n  {-# UNPACK #-} !TAI64\n  {-# UNPACK #-} !Word32\n  deriving (Eq, Ord, Show)\n\ndata TAI64NA = TAI64NA\n  {-# UNPACK #-} !TAI64N\n  {-# UNPACK #-} !Word32\n  deriving (Eq, Ord, Show)\n\ninstance Show TAI64   where\n  show (TAI64 t) = \"TAI64 0x\" ++ showHex t \"\"\n\ninstance Serialize TAI64 where\n  put (TAI64 t) = putWord64be t\n  get = liftM TAI64 get\n\ninstance Serialize TAI64N where\n  put (TAI64N  t' nt) = put t' >> putWord32be nt\n  get = liftM2 TAI64N  get get\n\ninstance Serialize TAI64NA where\n  put (TAI64NA t' at) = put t' >> putWord32be at\n  get = liftM2 TAI64NA get get\n\n\nposixToTAI64 :: POSIXTime -> TAI64\nposixToTAI64 = TAI64 . (2^62 +) . truncate . realToFrac\n\nposixToTAI64N :: POSIXTime -> TAI64N\nposixToTAI64N pt = TAI64N t' ns where\n  t' = posixToTAI64 pt\n  ns = (`mod` 10^9) $ truncate (pts * 10**9)\n  pts = realToFrac pt\n\nposixToTAI64NA :: POSIXTime -> TAI64NA -- | PICOsecond precision\nposixToTAI64NA pt = TAI64NA t' as where\n  t' = posixToTAI64N pt\n  as = (`mod` 10^9) $ truncate (pts * 10**18)\n  pts = realToFrac pt\n\ngetCurrentTAI64   :: IO TAI64\ngetCurrentTAI64N  :: IO TAI64N\ngetCurrentTAI64NA :: IO TAI64NA\ngetCurrentTAI64   = liftM posixToTAI64   getPOSIXTime\ngetCurrentTAI64N  = liftM posixToTAI64N  getPOSIXTime\ngetCurrentTAI64NA = liftM posixToTAI64NA getPOSIXTime\n\ntAI64ToPosix :: TAI64 -> POSIXTime\ntAI64ToPosix (TAI64 s) = fromRational . fromIntegral $ s - 2^62\n\ntAI64NToPosix :: TAI64N -> POSIXTime\ntAI64NToPosix (TAI64N t' n) = tAI64ToPosix t' + nanopart where\n  nanopart = fromRational $ (toRational $ 10**(-9)) * toRational n -- TODO: optimize?\n\ntAI64NAToPosix :: TAI64NA -> POSIXTime\ntAI64NAToPosix (TAI64NA t' a) = tAI64NToPosix t' + attopart where\n  attopart = fromRational $ (toRational $ 10**(-18)) * toRational a\n"
  },
  {
    "path": "contrib/external-tests/haskell/src/Main.hs",
    "content": "module Main where\n\nimport           Control.Monad              (void)\nimport           Crypto.Hash.BLAKE2.BLAKE2s (hash)\nimport           Data.ByteArray             (ScrubbedBytes, convert)\nimport           Data.ByteString            (ByteString, replicate, take, drop)\nimport qualified Data.ByteString.Base16     as B16\nimport qualified Data.ByteString.Base64     as B64\nimport           Data.Maybe                 (fromMaybe)\nimport           Data.Monoid                ((<>))\nimport qualified Data.Serialize             as S\nimport           Network.Socket\nimport qualified Network.Socket.ByteString  as NBS\nimport           Prelude hiding             (replicate, take, drop)\n\nimport Crypto.Noise\nimport Crypto.Noise.Cipher\nimport Crypto.Noise.Cipher.ChaChaPoly1305\nimport Crypto.Noise.DH\nimport Crypto.Noise.DH.Curve25519\nimport Crypto.Noise.HandshakePatterns (noiseIKpsk2)\nimport Crypto.Noise.Hash hiding (hash)\nimport Crypto.Noise.Hash.BLAKE2s\n\nimport Data.Time.TAI64\n\nsampleICMPRequest :: ByteString\nsampleICMPRequest = fst . B16.decode $\n  \"450000250000000014018f5b0abd81020abd810108001bfa039901b6576972654775617264\"\n\nvalidateICMPResponse :: ByteString\n                     -> Bool\nvalidateICMPResponse r =\n  -- Strip off part of IPv4 header because this is only a demo.\n  drop 12 sample == drop 12 r\n  where\n    sample = fst . B16.decode $ \"45000025e3030000400180570abd81010abd8102000023fa039901b65769726547756172640000000000000000000000\"\n\nunsafeMessage :: (Cipher c, DH d, Hash h)\n              => Bool\n              -> Maybe ScrubbedBytes\n              -> ScrubbedBytes\n              -> NoiseState c d h\n              -> (ScrubbedBytes, NoiseState c d h)\nunsafeMessage write mpsk msg ns = case operation msg ns of\n  NoiseResultMessage ct ns' -> (ct, ns')\n\n  NoiseResultNeedPSK ns' -> case mpsk of\n    Nothing -> error \"psk required but not provided\"\n    Just k  -> case operation k ns' of\n      NoiseResultMessage ct ns'' -> (ct, ns'')\n      _ -> error \"something terrible happened\"\n\n  _ -> error \"something terrible happened\"\n  where\n    operation = if write then writeMessage else readMessage\n\nmain :: IO ()\nmain = do\n  let ip           = \"demo.wireguard.com\"\n      port         = \"12913\"\n      myKeyB64     = \"WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=\" -- private key\n      serverKeyB64 = \"qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=\" -- public key\n      pskB64       = \"FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=\"\n\n  addrInfo <- head <$> getAddrInfo Nothing (Just ip) (Just port)\n  sock     <- socket (addrFamily addrInfo) Datagram defaultProtocol\n\n  let addr        = addrAddress addrInfo\n      myStaticKey = fromMaybe (error \"invalid private key\")\n                    . dhBytesToPair\n                    . convert\n                    . either (error \"error Base64 decoding my private key\") id\n                    . B64.decode\n                    $ myKeyB64 :: KeyPair Curve25519\n\n      serverKey   = fromMaybe (error \"invalid public key\")\n                    . dhBytesToPub\n                    . convert\n                    . either (error \"error Base64 decoding server public key\") id\n                    . B64.decode\n                    $ serverKeyB64 :: PublicKey Curve25519\n\n      psk         = convert\n                    . either (error \"error decoding PSK\") id\n                    . B64.decode\n                    $ pskB64 :: ScrubbedBytes\n\n  myEphemeralKey <- dhGenKey\n\n  let dho  = defaultHandshakeOpts InitiatorRole \"WireGuard v1 zx2c4 Jason@zx2c4.com\"\n      opts = setLocalEphemeral (Just myEphemeralKey)\n             . setLocalStatic  (Just myStaticKey)\n             . setRemoteStatic (Just serverKey)\n             $ dho\n      ns0  = noiseState opts noiseIKpsk2 :: NoiseState ChaChaPoly1305 Curve25519 BLAKE2s\n\n  tai64n <- convert . S.encode <$> getCurrentTAI64N\n\n  -- Handshake: Initiator to responder -----------------------------------------\n\n  let (msg0, ns1) = unsafeMessage True Nothing tai64n ns0\n      macKey      = hash 32 mempty $ \"mac1----\" `mappend` (convert . dhPubToBytes) serverKey\n      initiation  = \"\\x01\\x00\\x00\\x00\\x1c\\x00\\x00\\x00\" <> convert msg0 -- sender index = 28 to match other examples\n      mac1        = hash 16 macKey initiation\n\n  void $ NBS.sendTo sock (initiation <> mac1 <> replicate 16 0) addr\n\n  -- Handshake: Responder to initiator -----------------------------------------\n\n  (response0, _) <- NBS.recvFrom sock 1024\n\n  let theirIndex  = take 4  . drop 4  $ response0\n      (_, ns2)    = unsafeMessage False (Just psk) (convert . take 48 . drop 12 $ response0) ns1\n\n  -- ICMP: Initiator to responder ----------------------------------------------\n\n  let (msg1, ns3) = unsafeMessage True Nothing (convert sampleICMPRequest) ns2\n      icmp        = \"\\x04\\x00\\x00\\x00\" <> theirIndex <> replicate 8 0 <> convert msg1\n\n  void $ NBS.sendTo sock icmp addr\n\n  -- ICMP: Responder to initiator ----------------------------------------------\n\n  (response1, _) <- NBS.recvFrom sock 1024\n\n  let (icmpPayload, ns4) = unsafeMessage False Nothing (convert . drop 16 $ response1) ns3\n\n  -- KeepAlive: Initiator to responder -----------------------------------------\n\n  if validateICMPResponse . convert $ icmpPayload\n    then do\n      let (msg2, _) = unsafeMessage True Nothing mempty ns4\n          keepAlive = \"\\x04\\x00\\x00\\x00\" <> theirIndex <> \"\\x01\" <> replicate 7 0 <> convert msg2\n\n      void $ NBS.sendTo sock keepAlive addr\n\n    else error \"unexpected ICMP response from server!\"\n"
  },
  {
    "path": "contrib/external-tests/haskell/stack.yaml",
    "content": "resolver: lts-8.18\npackages:\n  - '.'\nextra-deps: []\nflags: {}\nextra-package-dbs: []\n"
  },
  {
    "path": "contrib/external-tests/python/main.py",
    "content": "#!/usr/bin/python3\n\n# SPDX-License-Identifier: MIT\n# Author: Piotr Lizonczyk <plizonczyk.public@gmail.com>\n\nimport base64\nimport datetime\nfrom hashlib import blake2s\nimport socket\nimport struct\n\nfrom scapy.layers.inet import IP, ICMP\n\nfrom noise.connection import NoiseConnection, Keypair\n\n\naddress = ('demo.wireguard.com', 12913)\n\nour_private = base64.b64decode('WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=')\ntheir_public = base64.b64decode('qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=')\npreshared = base64.b64decode('FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=')\nprologue = b'WireGuard v1 zx2c4 Jason@zx2c4.com'\n\nnoise = NoiseConnection.from_name(b'Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s')\nnoise.set_as_initiator()\nnoise.set_keypair_from_private_bytes(Keypair.STATIC, our_private)\nnoise.set_keypair_from_public_bytes(Keypair.REMOTE_STATIC, their_public)\nnoise.set_psks(psk=preshared)\nnoise.set_prologue(prologue)\nnoise.start_handshake()\n\nsock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)\n\n\n# 1. Prepare and send handshake initiation packet\nnow = datetime.datetime.now()\ntai = struct.pack('!qi', 4611686018427387914 + int(now.timestamp()), int(now.microsecond * 1e3))\ninitiation_packet = b'\\x01'  # Type: initiation\ninitiation_packet += b'\\x00' * 3  # Reserved\ninitiation_packet += struct.pack('<i', 28)  # Sender index: 28 (arbitrary)\ninitiation_packet += noise.write_message(payload=tai)\nmac_key = blake2s(b'mac1----' + their_public).digest()\ninitiation_packet += blake2s(initiation_packet, digest_size=16, key=mac_key).digest()\ninitiation_packet += b'\\x00' * 16\n\nsock.sendto(initiation_packet, address)\n\n\n# 2. Receive response to finalize handshake\nresponse_packet = sock.recv(92)\nassert response_packet[0] == 2  # Type: response\nassert response_packet[1:4] == b'\\x00' * 3  # Reserved\ntheir_index, our_index = struct.unpack('<ii', response_packet[4:12])\nassert our_index == 28\npayload = noise.read_message(response_packet[12:60])\nassert payload == b''\nassert noise.handshake_finished\n\n\n# 3. Prepare, encrypt and send ping packet\nicmp_packet = ICMP(type=8, id=921, seq=438)/b'WireGuard'\nip_packet = IP(proto=1, ttl=20, src=\"10.189.129.2\", dst=\"10.189.129.1\", id=0)/icmp_packet\nping_packet = b'\\x04'  # Type: data\nping_packet += b'\\x00' * 3  # Reserved\nping_packet += struct.pack('<iq', their_index, 0)\nping_packet += noise.encrypt(bytes(ip_packet))\n\nsock.sendto(ping_packet, address)\n\n\n# 4. Retrieve ping response, decrypt and verify\nencrypted_response = sock.recv(80)\nassert encrypted_response[0] == 4  # Type: data\nassert encrypted_response[1:4] == b'\\x00' * 3  # Reserved\nour_index, nonce = struct.unpack('<iq', encrypted_response[4:16])\nassert our_index == 28\nassert nonce == 0\nip = IP(noise.decrypt(encrypted_response[16:]))\nicmp = ip[1]\npayload = ip[2]\nassert icmp.type == 0\nassert icmp.code == 0\nassert icmp.id == 921\nassert icmp.seq == 438\nassert payload.load == b'WireGuard'\n\n\n# 5. Send keepalive\nkeepalive = b'\\x04'  # Type: data\nkeepalive += b'\\x00' * 3  # Reserved\nkeepalive += struct.pack('<iq', their_index, 1)\nkeepalive += noise.encrypt(b'')\n\nsock.sendto(keepalive, address)\n"
  },
  {
    "path": "contrib/external-tests/rust/.gitignore",
    "content": "Cargo.lock\ntarget/\n"
  },
  {
    "path": "contrib/external-tests/rust/Cargo.toml",
    "content": "[package]\nname = \"wireguard-ping\"\nversion = \"0.1.0\"\nauthors = [\"jason@zx2c4.com\", \"me@jake.su\"]\npublish = false\n\n[dependencies]\nsnow = \"^0.1.0-preview\"\nbase64 = \"^0.5\"\nrust-crypto = \"*\"\nbyteorder = \"*\"\ntime = \"*\"\n\n[dependencies.pnet]\nversion = \"*\"\nfeatures = [ ]\n"
  },
  {
    "path": "contrib/external-tests/rust/src/main.rs",
    "content": "/* Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. */\n\nextern crate snow;\nextern crate base64;\nextern crate time;\nextern crate byteorder;\nextern crate crypto;\nextern crate pnet;\n\nuse byteorder::{ByteOrder, BigEndian, LittleEndian};\nuse crypto::blake2s::Blake2s;\nuse snow::NoiseBuilder;\nuse pnet::packet::Packet;\nuse pnet::packet::ip::IpNextHeaderProtocols;\nuse pnet::packet::ipv4::{MutableIpv4Packet, self};\nuse pnet::packet::icmp::{MutableIcmpPacket, IcmpTypes, echo_reply, echo_request, self};\nuse std::net::*;\nuse std::str::FromStr;\n\nstatic TEST_SERVER: &'static str = \"demo.wireguard.com:12913\";\n\nfn memcpy(out: &mut [u8], data: &[u8]) {\n\tout[..data.len()].copy_from_slice(data);\n}\n\nfn main() {\n\tlet socket = UdpSocket::bind(\"0.0.0.0:0\").unwrap();\n\n\tlet their_public = base64::decode(&\"qRCwZSKInrMAq5sepfCdaCsRJaoLe5jhtzfiw7CjbwM=\").unwrap();\n\tlet my_private = base64::decode(&\"WAmgVYXkbT2bCtdcDwolI88/iVi/aV3/PHcUBTQSYmo=\").unwrap();\n\tlet my_preshared = base64::decode(&\"FpCyhws9cxwWoV4xELtfJvjJN+zQVRPISllRWgeopVE=\").unwrap();\n\n\tlet mut noise = NoiseBuilder::new(\"Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s\".parse().unwrap())\n\t\t.local_private_key(&my_private[..])\n\t\t.remote_public_key(&their_public[..])\n\t\t.prologue(\"WireGuard v1 zx2c4 Jason@zx2c4.com\".as_bytes())\n\t\t.psk(2, &my_preshared[..])\n\t\t.build_initiator().unwrap();\n\n\tlet now = time::get_time();\n\tlet mut tai64n = [0; 12];\n\tBigEndian::write_i64(&mut tai64n[0..], 4611686018427387914 + now.sec);\n\tBigEndian::write_i32(&mut tai64n[8..], now.nsec);\n\tlet mut initiation_packet = [0; 148];\n\tinitiation_packet[0] = 1; /* Type: Initiation */\n\tinitiation_packet[1] = 0; /* Reserved */\n\tinitiation_packet[2] = 0; /* Reserved */\n\tinitiation_packet[3] = 0; /* Reserved */\n\tLittleEndian::write_u32(&mut initiation_packet[4..], 28); /* Sender index: 28 (arbitrary) */\n\tnoise.write_message(&tai64n, &mut initiation_packet[8..]).unwrap();\n\tlet mut mac_key_input = [0; 40];\n\tlet mut mac_key = [0; 32];\n\tmemcpy(&mut mac_key_input, b\"mac1----\");\n\tmemcpy(&mut mac_key_input[8..], &their_public);\n\tBlake2s::blake2s(&mut mac_key, &mac_key_input, &[0; 0]);\n\tlet mut mac = [0; 16];\n\tBlake2s::blake2s(&mut mac, &initiation_packet[0..116], &mac_key);\n\tmemcpy(&mut initiation_packet[116..], &mac);\n\tsocket.send_to(&initiation_packet, TEST_SERVER).unwrap();\n\n\tlet mut response_packet = [0; 92];\n\tsocket.recv_from(&mut response_packet).unwrap();\n\tassert!(response_packet[0] == 2 /* Type: Response */);\n\tassert!(response_packet[1] == 0 /* Reserved */);\n\tassert!(response_packet[2] == 0 /* Reserved */);\n\tassert!(response_packet[3] == 0 /* Reserved */);\n\tlet their_index = LittleEndian::read_u32(&response_packet[4..]);\n\tlet our_index = LittleEndian::read_u32(&response_packet[8..]);\n\tassert!(our_index == 28);\n\tlet payload_len = noise.read_message(&response_packet[12..60], &mut []).unwrap();\n\tassert!(payload_len == 0);\n\tnoise = noise.into_transport_mode().unwrap();\n\n\tlet mut icmp_packet = [0; 48];\n\t{\n\t\tlet mut ipv4 = MutableIpv4Packet::new(&mut icmp_packet).unwrap();\n\t\tipv4.set_version(4);\n\t\tipv4.set_header_length(5);\n\t\tipv4.set_total_length(37);\n\t\tipv4.set_ttl(20);\n\t\tipv4.set_next_level_protocol(IpNextHeaderProtocols::Icmp);\n\t\tipv4.set_source(Ipv4Addr::from_str(\"10.189.129.2\").unwrap());\n\t\tipv4.set_destination(Ipv4Addr::from_str(\"10.189.129.1\").unwrap());\n\t\tlet checksum = ipv4::checksum(&ipv4.to_immutable());\n\t\tipv4.set_checksum(checksum);\n\t}\n\t{\n\t\tlet mut icmp = echo_request::MutableEchoRequestPacket::new(&mut icmp_packet[20..]).unwrap();\n\t\ticmp.set_icmp_type(IcmpTypes::EchoRequest);\n\t\ticmp.set_icmp_code(echo_request::IcmpCodes::NoCode);\n\t\ticmp.set_identifier(921);\n\t\ticmp.set_sequence_number(438);\n\t\ticmp.set_payload(b\"WireGuard\");\n\t}\n\t{\n\t\tlet mut icmp = MutableIcmpPacket::new(&mut icmp_packet[20..]).unwrap();\n\t\tlet checksum = icmp::checksum(&icmp.to_immutable());\n\t\ticmp.set_checksum(checksum);\n\t}\n\n\tlet mut ping_packet = [0; 80];\n\tping_packet[0] = 4; /* Type: Data */\n\tping_packet[1] = 0; /* Reserved */\n\tping_packet[2] = 0; /* Reserved */\n\tping_packet[3] = 0; /* Reserved */\n\tLittleEndian::write_u32(&mut ping_packet[4..], their_index);\n\tLittleEndian::write_u64(&mut ping_packet[8..], 0);\n\tnoise.write_message(&icmp_packet, &mut ping_packet[16..]).unwrap();\n\tsocket.send_to(&ping_packet, TEST_SERVER).unwrap();\n\n\tsocket.recv_from(&mut ping_packet).unwrap();\n\tassert!(ping_packet[0] == 4 /* Type: Data */);\n\tassert!(ping_packet[1] == 0 /* Reserved */);\n\tassert!(ping_packet[2] == 0 /* Reserved */);\n\tassert!(ping_packet[3] == 0 /* Reserved */);\n\tlet our_index_received = LittleEndian::read_u32(&ping_packet[4..]);\n\tassert!(our_index_received == 28);\n\tlet nonce = LittleEndian::read_u64(&ping_packet[8..]);\n\tassert!(nonce == 0);\n\tlet payload_len = noise.read_message(&ping_packet[16..], &mut icmp_packet).unwrap();\n\tassert!(payload_len == 48);\n\tlet icmp_reply = echo_reply::EchoReplyPacket::new(&icmp_packet[20..37]).unwrap();\n\tassert!(icmp_reply.get_icmp_type() == IcmpTypes::EchoReply && icmp_reply.get_icmp_code() == echo_reply::IcmpCodes::NoCode);\n\tassert!(icmp_reply.get_identifier() == 921 && icmp_reply.get_sequence_number() == 438);\n\tassert!(icmp_reply.payload() == b\"WireGuard\");\n\n\tlet mut keepalive_packet = [0; 32];\n\tkeepalive_packet[0] = 4; /* Type: Data */\n\tkeepalive_packet[1] = 0; /* Reserved */\n\tkeepalive_packet[2] = 0; /* Reserved */\n\tkeepalive_packet[3] = 0; /* Reserved */\n\tLittleEndian::write_u32(&mut keepalive_packet[4..], their_index);\n\tLittleEndian::write_u64(&mut keepalive_packet[8..], 1);\n\tlet empty_payload = [0; 0]; /* Empty payload means keepalive */\n\tnoise.write_message(&empty_payload, &mut keepalive_packet[16..]).unwrap();\n\tsocket.send_to(&keepalive_packet, TEST_SERVER).unwrap();\n}\n"
  },
  {
    "path": "contrib/extract-handshakes/.gitignore",
    "content": "offset-finder.o\noffset-finder\noffsets.include\n"
  },
  {
    "path": "contrib/extract-handshakes/Makefile",
    "content": "ifeq ($(KERNELRELEASE),)\nKERNELDIR ?= /lib/modules/$(shell uname -r)/build\nPWD := $(shell pwd)\nCFLAGS ?= -O3 -march=native\nCFLAGS += -Wall -pedantic -std=gnu11\n\noffsets.include: offset-finder\n\t./$^ > $@\n\noffset-finder: offset-finder.c offset-finder.o\n\t$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^\n\noffset-finder.o: offset-finder.c\n\t$(MAKE) -C $(KERNELDIR) M=$(PWD) $@\n\tobjcopy -j '.rodata*' $@ $@\n\nclean:\n\trm -f offset-finder offsets.include\n\t$(MAKE) -C $(KERNELDIR) M=$(PWD) clean\n\n.PHONY: clean\nelse\nobj-m := offset-finder.o\nendif\n"
  },
  {
    "path": "contrib/extract-handshakes/README",
    "content": "Handshake Extractor\n===================\n\nThis will extract private keys from outgoing handshake sessions, prior\nto them being sent, via kprobes. It exports the bare minimum to be\nable to then decrypt all packets in the handshake and in the subsequent\ntransport data session.\n\nBuild:\n\n    $ make\n\nRun (as root):\n\n    # ./extract-handshakes.sh\n    New handshake session:\n      LOCAL_STATIC_PRIVATE_KEY = QChaGDXeH3eQsbFAhueUNWFdq9KfpF3yl+eITjZbXEk=\n      REMOTE_STATIC_PUBLIC_KEY = HzgTY6aWXtuSyW/PUquZtg8LB/DyMwEXGkPiEmdSsUU=\n      LOCAL_EPHEMERAL_PRIVATE_KEY = UNGdRHuKDeqbFvmiV5FD4wP7a8PqI6v3Xnnz6Jc6NXQ=\n      PRESHARED_KEY = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n"
  },
  {
    "path": "contrib/extract-handshakes/extract-handshakes.sh",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n# Copyright (C) 2017-2018 Peter Wu <peter@lekensteyn.nl>. All Rights Reserved.\n\nset -e\n\nME_DIR=\"${BASH_SOURCE[0]}\"\nME_DIR=\"${ME_DIR%/*}\"\nsource \"$ME_DIR/offsets.include\" || { echo \"Did you forget to run make?\" >&2; exit 1; }\n\ncase \"$(uname -m)\" in\n\tx86_64) ARGUMENT_REGISTER=\"%si\" ;;\n\ti386|i686) ARGUMENT_REGISTER=\"%dx\" ;;\n\taarch64) ARGUMENT_REGISTER=\"%x1\" ;;\n\tarm) ARGUMENT_REGISTER=\"%r1\" ;;\n\t*) echo \"ERROR: Unknown architecture\" >&2; exit 1 ;;\nesac\n\nARGS=( )\nREGEX=\".*: idxadd: .*\"\nfor key in \"${!OFFSETS[@]}\"; do\n\tvalues=\"${OFFSETS[$key]}\"\n\tvalues=( ${values//,/ } )\n\tfor i in {0..3}; do\n\t\tvalue=\"$ARGUMENT_REGISTER\"\n\t\tfor indirection in \"${values[@]:1}\"; do\n\t\t\tvalue=\"+$indirection($value)\"\n\t\tdone\n\t\tvalue=\"+$((i * 8 + values[0]))($value)\"\n\t\tARGS+=( \"${key,,}$i=$value:x64\" )\n\t\tREGEX=\"$REGEX ${key,,}$i=0x([0-9a-f]+)\"\n\tdone\ndone\n\nturn_off() {\n\tset +e\n\t[[ -f /sys/kernel/debug/tracing/events/wireguard/idxadd/enable ]] || exit\n\techo 0 > /sys/kernel/debug/tracing/events/wireguard/idxadd/enable\n\techo \"-:wireguard/idxadd\" >> /sys/kernel/debug/tracing/kprobe_events\n\texit\n}\n\ntrap turn_off INT TERM EXIT\necho \"p:wireguard/idxadd index_hashtable_insert ${ARGS[*]}\" >> /sys/kernel/debug/tracing/kprobe_events\necho 1 > /sys/kernel/debug/tracing/events/wireguard/idxadd/enable\n\nunpack_u64() {\n\tlocal i expanded=\"$1\"\n\tif [[ $ENDIAN == big ]]; then\n\t\tprintf -v expanded \"%.*s$expanded\" $((16 - ${#expanded})) 0000000000000000\n\t\tfor i in {0..7}; do\n\t\t\techo -n \"\\\\x${expanded:(i * 2):2}\"\n\t\tdone\n\telif [[ $ENDIAN == little ]]; then\n\t\t(( ${#expanded} % 2 == 1 )) && expanded=\"0$expanded\"\n\t\texpanded=\"${expanded}0000000000000000\"\n\t\tfor i in {0..7}; do\n\t\t\techo -n \"\\\\x${expanded:((7 - i) * 2):2}\"\n\t\tdone\n\telse\n\t\techo \"ERROR: Unable to determine endian\" >&2\n\t\texit 1\n\tfi\n}\n\nwhile read -r line; do\n\t[[ $line =~ $REGEX ]] || continue\n\techo \"New handshake session:\"\n\tj=1\n\tfor key in \"${!OFFSETS[@]}\"; do\n\t\tbytes=\"\"\n\t\tfor i in {0..3}; do\n\t\t\tbytes=\"$bytes$(unpack_u64 \"${BASH_REMATCH[j]}\")\"\n\t\t\t((++j))\n\t\tdone\n\t\techo \"  $key = $(printf \"$bytes\" | base64)\"\n\tdone\ndone < /sys/kernel/debug/tracing/trace_pipe\n"
  },
  {
    "path": "contrib/extract-handshakes/offset-finder.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\nstruct def {\n\tconst char *name;\n\tunsigned long offset;\n\tunsigned long indirection_offset;\n};\nextern const struct def defs[];\n\n#ifdef __KERNEL__\n#include \"../drivers/net/wireguard/noise.h\"\n\nconst struct def defs[] = {\n\t{ \"LOCAL_STATIC_PRIVATE_KEY\", offsetof(struct noise_static_identity, static_private), offsetof(struct noise_handshake, static_identity) },\n\t{ \"LOCAL_EPHEMERAL_PRIVATE_KEY\", offsetof(struct noise_handshake, ephemeral_private), -1 },\n\t{ \"REMOTE_STATIC_PUBLIC_KEY\", offsetof(struct noise_handshake, remote_static), -1 },\n\t{ \"PRESHARED_KEY\", offsetof(struct noise_handshake, preshared_key), -1 },\n\t{ NULL, 0 }\n};\n#else\n#include <stdio.h>\nint main(int argc, char *argv[])\n{\n\tputs(\"declare -A OFFSETS=(\");\n\tfor (const struct def *def = defs; def->name; ++def) {\n\t\tprintf(\"\\t[%s]=%ld\", def->name, def->offset);\n\t\tif (def->indirection_offset != -1)\n\t\t\tprintf(\",%ld\", def->indirection_offset);\n\t\tputchar('\\n');\n\t}\n\tputs(\")\");\n#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n\tputs(\"ENDIAN=big\");\n#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n\tputs(\"ENDIAN=little\");\n#else\n#error \"Unsupported endianness\"\n#endif\n\treturn 0;\n}\n#endif\n"
  },
  {
    "path": "contrib/highlighter/Makefile",
    "content": "CFLAGS ?= -O3 -march=native\nCFLAGS += -std=gnu99\nCFLAGS += -Wall\nCFLAGS += -MMD -MP\n\nhighlight: highlight.o highlighter.o\n\nfuzz: CC := clang\nfuzz: CFLAGS += -fsanitize=fuzzer\nfuzz: fuzz.c highlighter.c\n\ngui/Makefile: gui/highlight.pro\n\tcd gui && qmake\ngui: gui/Makefile\n\t@$(MAKE) -C gui\n\nclean:\n\trm -f highlight fuzz *.o *.d\n\t@if [ -f gui/Makefile ]; then $(MAKE) -C gui distclean; fi\n\n.PHONY: clean gui\n.DEFAULT_GOAL: highlight\nMAKEFLAGS += --no-print-directory\n\n-include *.d\n"
  },
  {
    "path": "contrib/highlighter/README",
    "content": "wg(8) and wg-quick(8) syntax highlighter library\n================================================\n\nhighlighter.c contains a simple portable highlighter for the wg(8) and\nwg-quick(8) configuration files. Simply copy `highlight.c` and\n`highlight.h` into your project wholesale.\n\nAs a demo, a simple console highlighter program is included, alongside a\nsimple Qt5 GUI app to show its usage in realtime.\n\nThere is also a basic fuzzer, because why not?\n\nUsage:\n\n    $ make\n    $ ./highlight < path/to/tunnel.conf\n\n    $ make gui\n    $ ./gui/highlight\n\n    $ make fuzz\n    $ ./fuzz -workers=$(nproc) -jobs=$(nproc) ./corpus\n"
  },
  {
    "path": "contrib/highlighter/fuzz.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stdlib.h>\n#include <string.h>\n#include \"highlighter.h\"\n\nint LLVMFuzzerTestOneInput(const char *data, size_t size)\n{\n\tchar *str = strndup(data, size);\n\tif (!str)\n\t\treturn 0;\n\tstruct highlight_span *spans = highlight_config(str);\n\tif (!spans)\n\t\treturn 0;\n\tfor (struct highlight_span *span = spans; span->type != HighlightEnd; ++span);\n\tfree(spans);\n\tfree(str);\n\treturn 0;\n}\n"
  },
  {
    "path": "contrib/highlighter/gui/highlight.cpp",
    "content": "#include <QApplication>\n#include <QPlainTextEdit>\n#include <QPalette>\n#include <QFontDatabase>\n#include <QVBoxLayout>\n#include <QPushButton>\n\nextern \"C\" {\n#include \"../highlighter.h\"\n}\n\nstatic QColor colormap[] = {\n\t[HighlightSection] = QColor(\"#ababab\"),\n\t[HighlightField] = QColor(\"#70c0b1\"),\n\t[HighlightPrivateKey] = QColor(\"#7aa6da\"),\n\t[HighlightPublicKey] = QColor(\"#7aa6da\"),\n\t[HighlightPresharedKey] = QColor(\"#7aa6da\"),\n\t[HighlightIP] = QColor(\"#b9ca4a\"),\n\t[HighlightCidr] = QColor(\"#e78c45\"),\n\t[HighlightHost] = QColor(\"#b9ca4a\"),\n\t[HighlightPort] = QColor(\"#e78c45\"),\n\t[HighlightMTU] = QColor(\"#c397d8\"),\n\t[HighlightKeepalive] = QColor(\"#c397d8\"),\n\t[HighlightComment] = QColor(\"#969896\"),\n\t[HighlightDelimiter] = QColor(\"#7aa6da\"),\n#ifndef MOBILE_WGQUICK_SUBSET\n\t[HighlightTable] = QColor(\"#c397d8\"),\n\t[HighlightFwMark] = QColor(\"#c397d8\"),\n\t[HighlightSaveConfig] = QColor(\"#c397d8\"),\n\t[HighlightCmd] = QColor(\"#969896\"),\n#endif\n\t[HighlightError] = QColor(\"#d54e53\")\n};\n\nint main(int argc, char *argv[])\n{\n\tQApplication a(argc, argv);\n\tQWidget w;\n\tw.setWindowTitle(QObject::tr(\"WireGuard Configuration Highlighter\"));\n\tQVBoxLayout v;\n\tw.setLayout(&v);\n\tQPlainTextEdit e;\n\tv.addWidget(&e);\n\tQPalette p(e.palette());\n\tp.setColor(QPalette::Base, QColor(\"#010101\"));\n\tp.setColor(QPalette::Text, QColor(\"#eaeaea\"));\n\te.setPalette(p);\n\tQFont f(QFontDatabase::systemFont(QFontDatabase::FixedFont));\n\tf.setPointSize(16);\n\te.setFont(f);\n\te.setMinimumSize(400, 500);\n\tbool guard = false;\n\tQObject::connect(&e, &QPlainTextEdit::textChanged, [&]() {\n\t\tif (guard)\n\t\t\treturn;\n\t\tstruct highlight_span *spans = highlight_config(e.toPlainText().toLatin1().data());\n\t\tif (!spans)\n\t\t\treturn;\n\t\tQTextCursor cursor(e.document());\n\t\tQTextCharFormat format;\n\t\tcursor.beginEditBlock();\n\t\tcursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);\n\t\tcursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);\n\t\tformat.setForeground(p.color(QPalette::Text));\n\t\tformat.setUnderlineStyle(QTextCharFormat::NoUnderline);\n\t\tcursor.mergeCharFormat(format);\n\t\tfor (struct highlight_span *span = spans; span->type != HighlightEnd; ++span) {\n\t\t\tcursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);\n\t\t\tcursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, span->start);\n\t\t\tcursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, span->len);\n\t\t\tformat.setForeground(colormap[span->type]);\n\t\t\tformat.setUnderlineStyle(span->type == HighlightError ? QTextCharFormat::SpellCheckUnderline : QTextCharFormat::NoUnderline);\n\t\t\tcursor.mergeCharFormat(format);\n\t\t}\n\t\tfree(spans);\n\t\tguard = true;\n\t\tcursor.endEditBlock();\n\t\tguard = false;\n\t});\n\tQPushButton b;\n\tv.addWidget(&b);\n\tb.setText(QObject::tr(\"&Randomize colors\"));\n\tQObject::connect(&b, &QPushButton::clicked, [&]() {\n\t\tfor (size_t i = 0; i < sizeof(colormap) / sizeof(colormap[0]); ++i)\n\t\t\tcolormap[i] = QColor::fromHsl(qrand() % 360, qrand() % 192 + 64, qrand() % 128 + 128);\n\t\te.setPlainText(e.toPlainText());\n\t});\n\tw.show();\n\treturn a.exec();\n}\n"
  },
  {
    "path": "contrib/highlighter/gui/highlight.pro",
    "content": "QT += core gui widgets\nTEMPLATE = app\nTARGET = highlight\nSOURCES += highlight.cpp ../highlighter.c\nHEADERS += ../highlighter.h\n"
  },
  {
    "path": "contrib/highlighter/highlight.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include \"highlighter.h\"\n\n#define TERMINAL_FG_BLACK\t\"\\x1b[30m\"\n#define TERMINAL_FG_RED\t\t\"\\x1b[31m\"\n#define TERMINAL_FG_GREEN\t\"\\x1b[32m\"\n#define TERMINAL_FG_YELLOW\t\"\\x1b[33m\"\n#define TERMINAL_FG_BLUE\t\"\\x1b[34m\"\n#define TERMINAL_FG_MAGENTA\t\"\\x1b[35m\"\n#define TERMINAL_FG_CYAN\t\"\\x1b[36m\"\n#define TERMINAL_FG_WHITE\t\"\\x1b[37m\"\n#define TERMINAL_FG_DEFAULT\t\"\\x1b[39m\"\n\n#define TERMINAL_BG_BLACK\t\"\\x1b[40m\"\n#define TERMINAL_BG_RED\t\t\"\\x1b[41m\"\n#define TERMINAL_BG_GREEN\t\"\\x1b[42m\"\n#define TERMINAL_BG_YELLOW\t\"\\x1b[43m\"\n#define TERMINAL_BG_BLUE\t\"\\x1b[44m\"\n#define TERMINAL_BG_MAGENTA\t\"\\x1b[45m\"\n#define TERMINAL_BG_CYAN\t\"\\x1b[46m\"\n#define TERMINAL_BG_WHITE\t\"\\x1b[47m\"\n#define TERMINAL_BG_DEFAULT\t\"\\x1b[49m\"\n\n#define TERMINAL_BOLD\t\t\"\\x1b[1m\"\n#define TERMINAL_NO_BOLD\t\"\\x1b[22m\"\n#define TERMINAL_UNDERLINE\t\"\\x1b[4m\"\n#define TERMINAL_NO_UNDERLINE\t\"\\x1b[24m\"\n\n#define TERMINAL_RESET\t\t\"\\x1b[0m\"\n\nstatic const char *colormap[] = {\n\t[HighlightSection] = TERMINAL_FG_BLACK TERMINAL_BOLD,\n\t[HighlightField] = TERMINAL_FG_BLUE TERMINAL_BOLD,\n\t[HighlightPrivateKey] = TERMINAL_FG_YELLOW TERMINAL_BOLD,\n\t[HighlightPublicKey] = TERMINAL_FG_YELLOW TERMINAL_BOLD,\n\t[HighlightPresharedKey] = TERMINAL_FG_YELLOW TERMINAL_BOLD,\n\t[HighlightIP] = TERMINAL_FG_GREEN,\n\t[HighlightCidr] = TERMINAL_FG_YELLOW,\n\t[HighlightHost] = TERMINAL_FG_GREEN TERMINAL_BOLD,\n\t[HighlightPort] = TERMINAL_FG_MAGENTA,\n\t[HighlightMTU] = TERMINAL_FG_BLUE,\n\t[HighlightKeepalive] = TERMINAL_FG_BLUE,\n\t[HighlightComment] = TERMINAL_FG_CYAN,\n\t[HighlightDelimiter] = TERMINAL_FG_CYAN,\n#ifndef MOBILE_WGQUICK_SUBSET\n\t[HighlightTable] = TERMINAL_FG_BLUE,\n\t[HighlightFwMark] = TERMINAL_FG_BLUE,\n\t[HighlightSaveConfig] = TERMINAL_FG_BLUE,\n\t[HighlightCmd] = TERMINAL_FG_WHITE,\n#endif\n\t[HighlightError] = TERMINAL_FG_RED TERMINAL_UNDERLINE\n};\n\nint main(int argc, char *argv[])\n{\n\tchar input[1024 * 1024];\n\tstruct highlight_span *spans;\n\tsize_t last = 0, total_len;\n\n\ttotal_len = fread(input, 1, sizeof(input) - 1, stdin);\n\tinput[total_len] = '\\0';\n\tspans = highlight_config(input);\n\n\tfputs(TERMINAL_RESET, stdout);\n\tfor (struct highlight_span *span = spans; span->type != HighlightEnd; ++span) {\n\t\tfwrite(input + last, 1, span->start - last, stdout);\n\t\tfputs(colormap[span->type], stdout);\n\t\tfwrite(input + span->start, 1, span->len, stdout);\n\t\tfputs(TERMINAL_RESET, stdout);\n\t\tlast = span->start + span->len;\n\t}\n\tfwrite(input + last, 1, total_len - last, stdout);\n\n\tfree(spans);\n\treturn 0;\n}\n"
  },
  {
    "path": "contrib/highlighter/highlighter.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <errno.h>\n#include \"highlighter.h\"\n\ntypedef struct {\n\tconst char *s;\n\tsize_t len;\n} string_span_t;\n\nstatic bool is_decimal(char c)\n{\n\treturn c >= '0' && c <= '9';\n}\n\nstatic bool is_hexadecimal(char c)\n{\n\treturn is_decimal(c) || ((c | 32) >= 'a' && (c | 32) <= 'f');\n}\n\nstatic bool is_alphabet(char c)\n{\n\treturn (c | 32) >= 'a' && (c | 32) <= 'z';\n}\n\nstatic bool is_same(string_span_t s, const char *c)\n{\n\tsize_t len = strlen(c);\n\n\tif (len != s.len)\n\t\treturn false;\n\treturn !memcmp(s.s, c, len);\n}\n\nstatic bool is_caseless_same(string_span_t s, const char *c)\n{\n\tsize_t len = strlen(c);\n\n\tif (len != s.len)\n\t\treturn false;\n\tfor (size_t i = 0; i < len; ++i) {\n\t\tchar a = c[i], b = s.s[i];\n\t\tif ((unsigned)a - 'a' < 26)\n\t\t\ta &= 95;\n\t\tif ((unsigned)b - 'a' < 26)\n\t\t\tb &= 95;\n\t\tif (a != b)\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic bool is_valid_key(string_span_t s)\n{\n\tif (s.len != 44 || s.s[43] != '=')\n\t\treturn false;\n\n\tfor (size_t i = 0; i < 42; ++i) {\n\t\tif (!is_decimal(s.s[i]) && !is_alphabet(s.s[i]) &&\n\t\t    s.s[i] != '/' && s.s[i] != '+')\n\t\t\treturn false;\n\t}\n\tswitch (s.s[42]) {\n\tcase 'A':\n\tcase 'E':\n\tcase 'I':\n\tcase 'M':\n\tcase 'Q':\n\tcase 'U':\n\tcase 'Y':\n\tcase 'c':\n\tcase 'g':\n\tcase 'k':\n\tcase 'o':\n\tcase 's':\n\tcase 'w':\n\tcase '4':\n\tcase '8':\n\tcase '0':\n\t\tbreak;\n\tdefault:\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic bool is_valid_hostname(string_span_t s)\n{\n\tsize_t num_digit = 0, num_entity = s.len;\n\n\tif (s.len > 63 || !s.len)\n\t\treturn false;\n\tif (s.s[0] == '-' || s.s[s.len - 1] == '-')\n\t\treturn false;\n\tif (s.s[0] == '.' || s.s[s.len - 1] == '.')\n\t\treturn false;\n\n\tfor (size_t i = 0; i < s.len; ++i) {\n\t\tif (is_decimal(s.s[i])) {\n\t\t\t++num_digit;\n\t\t\tcontinue;\n\t\t}\n\t\tif (s.s[i] == '.') {\n\t\t\t--num_entity;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!is_alphabet(s.s[i]) && s.s[i] != '-')\n\t\t\treturn false;\n\n\t\tif (i && s.s[i] == '.' && s.s[i - 1] == '.')\n\t\t\treturn false;\n\t}\n\treturn num_digit != num_entity;\n}\n\nstatic bool is_valid_ipv4(string_span_t s)\n{\n\tfor (size_t j, i = 0, pos = 0; i < 4 && pos < s.len; ++i) {\n\t\tuint32_t val = 0;\n\n\t\tfor (j = 0; j < 3 && pos + j < s.len && is_decimal(s.s[pos + j]); ++j)\n\t\t\tval = 10 * val + s.s[pos + j] - '0';\n\t\tif (j == 0 || (j > 1 && s.s[pos] == '0') || val > 255)\n\t\t\treturn false;\n\t\tif (pos + j == s.len && i == 3)\n\t\t\treturn true;\n\t\tif (s.s[pos + j] != '.')\n\t\t\treturn false;\n\t\tpos += j + 1;\n\t}\n\treturn false;\n}\n\nstatic bool is_valid_ipv6(string_span_t s)\n{\n\tsize_t pos = 0;\n\tbool seen_colon = false;\n\n\tif (s.len < 2)\n\t\treturn false;\n\tif (s.s[pos] == ':' && s.s[++pos] != ':')\n\t\treturn false;\n\tif (s.s[s.len - 1] == ':' && s.s[s.len - 2] != ':')\n\t\treturn false;\n\n\tfor (size_t j, i = 0; pos < s.len; ++i) {\n\t\tif (s.s[pos] == ':' && !seen_colon) {\n\t\t\tseen_colon = true;\n\t\t\tif (++pos == s.len)\n\t\t\t\tbreak;\n\t\t\tif (i == 7)\n\t\t\t\treturn false;\n\t\t\tcontinue;\n\t\t}\n\t\tfor (j = 0; j < 4 && pos + j < s.len && is_hexadecimal(s.s[pos + j]); ++j);\n\t\tif (j == 0)\n\t\t\treturn false;\n\t\tif (pos + j == s.len && (seen_colon || i == 7))\n\t\t\tbreak;\n\t\tif (i == 7)\n\t\t\treturn false;\n\t\tif (s.s[pos + j] != ':') {\n\t\t\tif (s.s[pos + j] != '.' || (i < 6 && !seen_colon))\n\t\t\t\treturn false;\n\t\t\treturn is_valid_ipv4((string_span_t){ s.s + pos, s.len - pos });\n\t\t}\n\t\tpos += j + 1;\n\t}\n\treturn true;\n}\n\nstatic bool is_valid_uint(string_span_t s, bool support_hex, uint64_t min, uint64_t max)\n{\n\tuint64_t val = 0;\n\n\t/* Bound this around 32 bits, so that we don't have to write overflow logic. */\n\tif (s.len > 10 || !s.len)\n\t\treturn false;\n\n\tif (support_hex && s.len > 2 && s.s[0] == '0' && s.s[1] == 'x') {\n\t\tfor (size_t i = 2; i < s.len; ++i) {\n\t\t\tif ((unsigned)s.s[i] - '0' < 10)\n\t\t\t\tval = 16 * val + (s.s[i] - '0');\n\t\t\telse if (((unsigned)s.s[i] | 32) - 'a' < 6)\n\t\t\t\tval = 16 * val + (s.s[i] | 32) - 'a' + 10;\n\t\t\telse\n\t\t\t\treturn false;\n\t\t}\n\t} else {\n\t\tfor (size_t i = 0; i < s.len; ++i) {\n\t\t\tif (!is_decimal(s.s[i]))\n\t\t\t\treturn false;\n\t\t\tval = 10 * val + s.s[i] - '0';\n\t\t}\n\t}\n\treturn val <= max && val >= min;\n}\n\nstatic bool is_valid_port(string_span_t s)\n{\n\treturn is_valid_uint(s, false, 0, 65535);\n}\n\nstatic bool is_valid_mtu(string_span_t s)\n{\n\treturn is_valid_uint(s, false, 576, 65535);\n}\n\nstatic bool is_valid_persistentkeepalive(string_span_t s)\n{\n\tif (is_same(s, \"off\"))\n\t\treturn true;\n\treturn is_valid_uint(s, false, 0, 65535);\n}\n\n#ifndef MOBILE_WGQUICK_SUBSET\n\nstatic bool is_valid_fwmark(string_span_t s)\n{\n\tif (is_same(s, \"off\"))\n\t\treturn true;\n\treturn is_valid_uint(s, true, 0, 4294967295);\n}\n\nstatic bool is_valid_table(string_span_t s)\n{\n\tif (is_same(s, \"auto\"))\n\t\treturn true;\n\tif (is_same(s, \"off\"))\n\t\treturn true;\n\t/* This pretty much invalidates the other checks, but rt_names.c's\n\t * fread_id_name does no validation aside from this. */\n\tif (s.len < 512)\n\t\treturn true;\n\treturn is_valid_uint(s, false, 0, 4294967295);\n}\n\nstatic bool is_valid_saveconfig(string_span_t s)\n{\n\treturn is_same(s, \"true\") || is_same(s, \"false\");\n}\n\nstatic bool is_valid_prepostupdown(string_span_t s)\n{\n\t/* It's probably not worthwhile to try to validate a bash expression.\n\t * So instead we just demand non-zero length. */\n\treturn s.len;\n}\n#endif\n\nstatic bool is_valid_scope(string_span_t s)\n{\n\tif (s.len > 64 || !s.len)\n\t\treturn false;\n\tfor (size_t i = 0; i < s.len; ++i) {\n\t\tif (!is_alphabet(s.s[i]) && !is_decimal(s.s[i]) &&\n\t\t    s.s[i] != '_' && s.s[i] != '=' && s.s[i] != '+' &&\n\t\t    s.s[i] != '.' && s.s[i] != '-')\n\t\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic bool is_valid_endpoint(string_span_t s)\n{\n\n\tif (!s.len)\n\t\treturn false;\n\n\tif (s.s[0] == '[') {\n\t\tbool seen_scope = false;\n\t\tstring_span_t hostspan = { s.s + 1, 0 };\n\n\t\tfor (size_t i = 1; i < s.len; ++i) {\n\t\t\tif (s.s[i] == '%') {\n\t\t\t\tif (seen_scope)\n\t\t\t\t\treturn false;\n\t\t\t\tseen_scope = true;\n\t\t\t\tif (!is_valid_ipv6(hostspan))\n\t\t\t\t\treturn false;\n\t\t\t\thostspan = (string_span_t){ s.s + i + 1, 0 };\n\t\t\t} else if (s.s[i] == ']') {\n\t\t\t\tif (seen_scope) {\n\t\t\t\t\tif (!is_valid_scope(hostspan))\n\t\t\t\t\t\treturn false;\n\t\t\t\t} else if (!is_valid_ipv6(hostspan)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\tif (i == s.len - 1 || s.s[i + 1] != ':')\n\t\t\t\t\treturn false;\n\t\t\t\treturn is_valid_port((string_span_t){ s.s + i + 2, s.len - i - 2 });\n\t\t\t} else {\n\t\t\t\t++hostspan.len;\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\tfor (size_t i = 0; i < s.len; ++i) {\n\t\tif (s.s[i] == ':') {\n\t\t\tstring_span_t host = { s.s, i }, port = { s.s + i + 1, s.len - i - 1};\n\t\t\treturn is_valid_port(port) && (is_valid_ipv4(host) || is_valid_hostname(host));\n\t\t}\n\t}\n\treturn false;\n}\n\nstatic bool is_valid_network(string_span_t s)\n{\n\tfor (size_t i = 0; i < s.len; ++i) {\n\t\tif (s.s[i] == '/') {\n\t\t\tstring_span_t ip = { s.s, i }, cidr = { s.s + i + 1, s.len - i - 1};\n\t\t\tuint16_t cidrval = 0;\n\n\t\t\tif (cidr.len > 3 || !cidr.len)\n\t\t\t\treturn false;\n\n\t\t\tfor (size_t j = 0; j < cidr.len; ++j) {\n\t\t\t\tif (!is_decimal(cidr.s[j]))\n\t\t\t\t\treturn false;\n\t\t\t\tcidrval = 10 * cidrval + cidr.s[j] - '0';\n\t\t\t}\n\t\t\tif (is_valid_ipv4(ip))\n\t\t\t\treturn cidrval <= 32;\n\t\t\telse if (is_valid_ipv6(ip))\n\t\t\t\treturn cidrval <= 128;\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn is_valid_ipv4(s) || is_valid_ipv6(s);\n}\n\nenum field {\n\tInterfaceSection,\n\tPrivateKey,\n\tListenPort,\n\tAddress,\n\tDNS,\n\tMTU,\n#ifndef MOBILE_WGQUICK_SUBSET\n\tFwMark,\n\tTable,\n\tPreUp, PostUp, PreDown, PostDown,\n\tSaveConfig,\n#endif\n\n\tPeerSection,\n\tPublicKey,\n\tPresharedKey,\n\tAllowedIPs,\n\tEndpoint,\n\tPersistentKeepalive,\n\n\tInvalid\n};\n\nstatic enum field section_for_field(enum field t)\n{\n\tif (t > InterfaceSection && t < PeerSection)\n\t\treturn InterfaceSection;\n\tif (t > PeerSection && t < Invalid)\n\t\treturn PeerSection;\n\treturn Invalid;\n}\n\nstatic enum field get_field(string_span_t s)\n{\n#define check_enum(t) do { if (is_caseless_same(s, #t)) return t; } while (0)\n\tcheck_enum(PrivateKey);\n\tcheck_enum(ListenPort);\n\tcheck_enum(Address);\n\tcheck_enum(DNS);\n\tcheck_enum(MTU);\n\tcheck_enum(PublicKey);\n\tcheck_enum(PresharedKey);\n\tcheck_enum(AllowedIPs);\n\tcheck_enum(Endpoint);\n\tcheck_enum(PersistentKeepalive);\n#ifndef MOBILE_WGQUICK_SUBSET\n\tcheck_enum(FwMark);\n\tcheck_enum(Table);\n\tcheck_enum(PreUp);\n\tcheck_enum(PostUp);\n\tcheck_enum(PreDown);\n\tcheck_enum(PostDown);\n\tcheck_enum(SaveConfig);\n#endif\n\treturn Invalid;\n#undef check_enum\n}\n\nstatic enum field get_sectiontype(string_span_t s)\n{\n\tif (is_caseless_same(s, \"[Peer]\"))\n\t\treturn PeerSection;\n\tif (is_caseless_same(s, \"[Interface]\"))\n\t\treturn InterfaceSection;\n\treturn Invalid;\n}\n\nstruct highlight_span_array {\n\tsize_t len, capacity;\n\tstruct highlight_span *spans;\n};\n\n/* A useful OpenBSD-ism. */\nstatic void *realloc_array(void *optr, size_t nmemb, size_t size)\n{\n\tif ((nmemb >= (size_t)1 << (sizeof(size_t) * 4) ||\n\t     size >= (size_t)1 << (sizeof(size_t) * 4)) &&\n\t    nmemb > 0 && SIZE_MAX / nmemb < size) {\n\t\terrno = ENOMEM;\n\t\treturn NULL;\n\t}\n\treturn realloc(optr, size * nmemb);\n}\n\nstatic bool append_highlight_span(struct highlight_span_array *a, const char *o, string_span_t s, enum highlight_type t)\n{\n\tif (!s.len)\n\t\treturn true;\n\tif (a->len >= a->capacity) {\n\t\tstruct highlight_span *resized;\n\n\t\ta->capacity = a->capacity ? a->capacity * 2 : 64;\n\t\tresized = realloc_array(a->spans, a->capacity, sizeof(*resized));\n\t\tif (!resized) {\n\t\t\tfree(a->spans);\n\t\t\tmemset(a, 0, sizeof(*a));\n\t\t\treturn false;\n\t\t}\n\t\ta->spans = resized;\n\t}\n\ta->spans[a->len++] = (struct highlight_span){ t, s.s - o, s.len };\n\treturn true;\n}\n\nstatic void highlight_multivalue_value(struct highlight_span_array *ret, const string_span_t parent, const string_span_t s, enum field section)\n{\n\tswitch (section) {\n\tcase DNS:\n\t\tif (is_valid_ipv4(s) || is_valid_ipv6(s))\n\t\t\tappend_highlight_span(ret, parent.s, s, HighlightIP);\n\t\telse if (is_valid_hostname(s))\n\t\t\tappend_highlight_span(ret, parent.s, s, HighlightHost);\n\t\telse\n\t\t\tappend_highlight_span(ret, parent.s, s, HighlightError);\n\t\tbreak;\n\tcase Address:\n\tcase AllowedIPs: {\n\t\tsize_t slash;\n\n\t\tif (!is_valid_network(s)) {\n\t\t\tappend_highlight_span(ret, parent.s, s, HighlightError);\n\t\t\tbreak;\n\t\t}\n\t\tfor (slash = 0; slash < s.len; ++slash) {\n\t\t\tif (s.s[slash] == '/')\n\t\t\t\tbreak;\n\t\t}\n\t\tif (slash == s.len) {\n\t\t\tappend_highlight_span(ret, parent.s, s, HighlightIP);\n\t\t} else {\n\t\t\tappend_highlight_span(ret, parent.s, (string_span_t){ s.s, slash }, HighlightIP);\n\t\t\tappend_highlight_span(ret, parent.s, (string_span_t){ s.s + slash, 1 }, HighlightDelimiter);\n\t\t\tappend_highlight_span(ret, parent.s, (string_span_t){ s.s + slash + 1, s.len - slash - 1 }, HighlightCidr);\n\t\t}\n\t\tbreak;\n\t}\n\tdefault:\n\t\tappend_highlight_span(ret, parent.s, s, HighlightError);\n\t}\n}\n\nstatic void highlight_multivalue(struct highlight_span_array *ret, const string_span_t parent, const string_span_t s, enum field section)\n{\n\tstring_span_t current_span = { s.s, 0 };\n\tsize_t len_at_last_space = 0;\n\n\tfor (size_t i = 0; i < s.len; ++i) {\n\t\tif (s.s[i] == ',') {\n\t\t\tcurrent_span.len = len_at_last_space;\n\t\t\thighlight_multivalue_value(ret, parent, current_span, section);\n\t\t\tappend_highlight_span(ret, parent.s, (string_span_t){ s.s + i, 1 }, HighlightDelimiter);\n\t\t\tlen_at_last_space = 0;\n\t\t\tcurrent_span = (string_span_t){ s.s + i + 1, 0 };\n\t\t} else if (s.s[i] == ' ' || s.s[i] == '\\t') {\n\t\t\tif (&s.s[i] == current_span.s && !current_span.len)\n\t\t\t\t++current_span.s;\n\t\t\telse\n\t\t\t\t++current_span.len;\n\t\t} else {\n\t\t\tlen_at_last_space = ++current_span.len;\n\t\t}\n\t}\n\tcurrent_span.len = len_at_last_space;\n\tif (current_span.len)\n\t\thighlight_multivalue_value(ret, parent, current_span, section);\n\telse if (ret->spans[ret->len - 1].type == HighlightDelimiter)\n\t\tret->spans[ret->len - 1].type = HighlightError;\n}\n\nstatic void highlight_value(struct highlight_span_array *ret, const string_span_t parent, const string_span_t s, enum field section)\n{\n\tswitch (section) {\n\tcase PrivateKey:\n\t\tappend_highlight_span(ret, parent.s, s, is_valid_key(s) ? HighlightPrivateKey : HighlightError);\n\t\tbreak;\n\tcase PublicKey:\n\t\tappend_highlight_span(ret, parent.s, s, is_valid_key(s) ? HighlightPublicKey : HighlightError);\n\t\tbreak;\n\tcase PresharedKey:\n\t\tappend_highlight_span(ret, parent.s, s, is_valid_key(s) ? HighlightPresharedKey : HighlightError);\n\t\tbreak;\n\tcase MTU:\n\t\tappend_highlight_span(ret, parent.s, s, is_valid_mtu(s) ? HighlightMTU : HighlightError);\n\t\tbreak;\n#ifndef MOBILE_WGQUICK_SUBSET\n\tcase SaveConfig:\n\t\tappend_highlight_span(ret, parent.s, s, is_valid_saveconfig(s) ? HighlightSaveConfig : HighlightError);\n\t\tbreak;\n\tcase FwMark:\n\t\tappend_highlight_span(ret, parent.s, s, is_valid_fwmark(s) ? HighlightFwMark : HighlightError);\n\t\tbreak;\n\tcase Table:\n\t\tappend_highlight_span(ret, parent.s, s, is_valid_table(s) ? HighlightTable : HighlightError);\n\t\tbreak;\n\tcase PreUp:\n\tcase PostUp:\n\tcase PreDown:\n\tcase PostDown:\n\t\tappend_highlight_span(ret, parent.s, s, is_valid_prepostupdown(s) ? HighlightCmd : HighlightError);\n\t\tbreak;\n#endif\n\tcase ListenPort:\n\t\tappend_highlight_span(ret, parent.s, s, is_valid_port(s) ? HighlightPort : HighlightError);\n\t\tbreak;\n\tcase PersistentKeepalive:\n\t\tappend_highlight_span(ret, parent.s, s, is_valid_persistentkeepalive(s) ? HighlightKeepalive : HighlightError);\n\t\tbreak;\n\tcase Endpoint: {\n\t\tsize_t colon;\n\n\t\tif (!is_valid_endpoint(s)) {\n\t\t\tappend_highlight_span(ret, parent.s, s, HighlightError);\n\t\t\tbreak;\n\t\t}\n\t\tfor (colon = s.len; colon --> 0;) {\n\t\t\tif (s.s[colon] == ':')\n\t\t\t\tbreak;\n\t\t}\n\t\tappend_highlight_span(ret, parent.s, (string_span_t){ s.s, colon }, HighlightHost);\n\t\tappend_highlight_span(ret, parent.s, (string_span_t){ s.s + colon, 1 }, HighlightDelimiter);\n\t\tappend_highlight_span(ret, parent.s, (string_span_t){ s.s + colon + 1, s.len - colon - 1 }, HighlightPort);\n\t\tbreak;\n\t}\n\tcase Address:\n\tcase DNS:\n\tcase AllowedIPs:\n\t\thighlight_multivalue(ret, parent, s, section);\n\t\tbreak;\n\tdefault:\n\t\tappend_highlight_span(ret, parent.s, s, HighlightError);\n\t}\n}\n\nstruct highlight_span *highlight_config(const char *config)\n{\n\tstruct highlight_span_array ret = { 0 };\n\tconst string_span_t s = { config, strlen(config) };\n\tstring_span_t current_span = { s.s, 0 };\n\tenum field current_section = Invalid, current_field = Invalid;\n\tenum { OnNone, OnKey, OnValue, OnComment, OnSection } state = OnNone;\n\tsize_t len_at_last_space = 0, equals_location = 0;\n\n\tfor (size_t i = 0; i <= s.len; ++i) {\n\t\tif (i == s.len || s.s[i] == '\\n' || (state != OnComment && s.s[i] == '#')) {\n\t\t\tif (state == OnKey) {\n\t\t\t\tcurrent_span.len = len_at_last_space;\n\t\t\t\tappend_highlight_span(&ret, s.s, current_span, HighlightError);\n\t\t\t} else if (state == OnValue) {\n\t\t\t\tif (current_span.len) {\n\t\t\t\t\tappend_highlight_span(&ret, s.s, (string_span_t){ s.s + equals_location, 1 }, HighlightDelimiter);\n\t\t\t\t\tcurrent_span.len = len_at_last_space;\n\t\t\t\t\thighlight_value(&ret, s, current_span, current_field);\n\t\t\t\t} else {\n\t\t\t\t\tappend_highlight_span(&ret, s.s, (string_span_t){ s.s + equals_location, 1 }, HighlightError);\n\t\t\t\t}\n\t\t\t} else if (state == OnSection) {\n\t\t\t\tcurrent_span.len = len_at_last_space;\n\t\t\t\tcurrent_section = get_sectiontype(current_span);\n\t\t\t\tappend_highlight_span(&ret, s.s, current_span, current_section == Invalid ? HighlightError : HighlightSection);\n\t\t\t} else if (state == OnComment) {\n\t\t\t\tappend_highlight_span(&ret, s.s, current_span, HighlightComment);\n\t\t\t}\n\t\t\tif (i == s.len)\n\t\t\t\tbreak;\n\t\t\tlen_at_last_space = 0;\n\t\t\tcurrent_field = Invalid;\n\t\t\tif (s.s[i] == '#') {\n\t\t\t\tcurrent_span = (string_span_t){ s.s + i, 1 };\n\t\t\t\tstate = OnComment;\n\t\t\t} else {\n\t\t\t\tcurrent_span = (string_span_t){ s.s + i + 1, 0 };\n\t\t\t\tstate = OnNone;\n\t\t\t}\n\t\t} else if (state == OnComment) {\n\t\t\t++current_span.len;\n\t\t} else if (s.s[i] == ' ' || s.s[i] == '\\t') {\n\t\t\tif (&s.s[i] == current_span.s && !current_span.len)\n\t\t\t\t++current_span.s;\n\t\t\telse\n\t\t\t\t++current_span.len;\n\t\t} else if (s.s[i] == '=' && state == OnKey) {\n\t\t\tcurrent_span.len = len_at_last_space;\n\t\t\tcurrent_field = get_field(current_span);\n\t\t\tenum field section = section_for_field(current_field);\n\t\t\tif (section == Invalid || current_field == Invalid || section != current_section)\n\t\t\t\tappend_highlight_span(&ret, s.s, current_span, HighlightError);\n\t\t\telse\n\t\t\t\tappend_highlight_span(&ret, s.s, current_span, HighlightField);\n\t\t\tequals_location = i;\n\t\t\tcurrent_span = (string_span_t){ s.s + i + 1, 0 };\n\t\t\tstate = OnValue;\n\t\t} else {\n\t\t\tif (state == OnNone)\n\t\t\t\tstate = s.s[i] == '[' ? OnSection : OnKey;\n\t\t\tlen_at_last_space = ++current_span.len;\n\t\t}\n\t}\n\n\tappend_highlight_span(&ret, s.s, (string_span_t){ s.s, -1 }, HighlightEnd);\n\treturn ret.spans;\n}\n"
  },
  {
    "path": "contrib/highlighter/highlighter.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 */\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <sys/types.h>\n\nenum highlight_type {\n\tHighlightSection,\n\tHighlightField,\n\tHighlightPrivateKey,\n\tHighlightPublicKey,\n\tHighlightPresharedKey,\n\tHighlightIP,\n\tHighlightCidr,\n\tHighlightHost,\n\tHighlightPort,\n\tHighlightMTU,\n\tHighlightKeepalive,\n\tHighlightComment,\n\tHighlightDelimiter,\n#ifndef MOBILE_WGQUICK_SUBSET\n\tHighlightTable,\n\tHighlightFwMark,\n\tHighlightSaveConfig,\n\tHighlightCmd,\n#endif\n\tHighlightError,\n\tHighlightEnd\n};\n\nstruct highlight_span {\n\tenum highlight_type type;\n\tsize_t start, len;\n};\n\nstruct highlight_span *highlight_config(const char *config);\n"
  },
  {
    "path": "contrib/json/README",
    "content": "wg-json\n=======\n\nThis will dump all current WireGuard status as JSON output.\n\nUsage:\n\n    # wg-json\n"
  },
  {
    "path": "contrib/json/wg-json",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n\nexec < <(exec wg show all dump)\n\nprintf '{'\nwhile read -r -d $'\\t' device; do\n\tif [[ $device != \"$last_device\" ]]; then\n\t\t[[ -z $last_device ]] && printf '\\n' || printf '%s,\\n' \"$end\"\n\t\tlast_device=\"$device\"\n\t\tread -r private_key public_key listen_port fwmark\n\t\tprintf '\\t\"%s\": {' \"$device\"\n\t\tdelim=$'\\n'\n\t\t[[ $private_key == \"(none)\" ]] || { printf '%s\\t\\t\"privateKey\": \"%s\"' \"$delim\" \"$private_key\"; delim=$',\\n'; }\n\t\t[[ $public_key == \"(none)\" ]] || { printf '%s\\t\\t\"publicKey\": \"%s\"' \"$delim\" \"$public_key\"; delim=$',\\n'; }\n\t\t[[ $listen_port == \"0\" ]] || { printf '%s\\t\\t\"listenPort\": %u' \"$delim\" $(( $listen_port )); delim=$',\\n'; }\n\t\t[[ $fwmark == \"off\" ]] || { printf '%s\\t\\t\"fwmark\": %u' \"$delim\" $(( $fwmark )); delim=$',\\n'; }\n\t\tprintf '%s\\t\\t\"peers\": {' \"$delim\"; end=$'\\n\\t\\t}\\n\\t}'\n\t\tdelim=$'\\n'\n\telse\n\t\tread -r public_key preshared_key endpoint allowed_ips latest_handshake transfer_rx transfer_tx persistent_keepalive\n\t\tprintf '%s\\t\\t\\t\"%s\": {' \"$delim\" \"$public_key\"\n\t\tdelim=$'\\n'\n\t\t[[ $preshared_key == \"(none)\" ]] || { printf '%s\\t\\t\\t\\t\"presharedKey\": \"%s\"' \"$delim\" \"$preshared_key\"; delim=$',\\n'; }\n\t\t[[ $endpoint == \"(none)\" ]] || { printf '%s\\t\\t\\t\\t\"endpoint\": \"%s\"' \"$delim\" \"$endpoint\"; delim=$',\\n'; }\n\t\t[[ $latest_handshake == \"0\" ]] || { printf '%s\\t\\t\\t\\t\"latestHandshake\": %u' \"$delim\" $(( $latest_handshake )); delim=$',\\n'; }\n\t\t[[ $transfer_rx == \"0\" ]] || { printf '%s\\t\\t\\t\\t\"transferRx\": %u' \"$delim\" $(( $transfer_rx )); delim=$',\\n'; }\n\t\t[[ $transfer_tx == \"0\" ]] || { printf '%s\\t\\t\\t\\t\"transferTx\": %u' \"$delim\" $(( $transfer_tx )); delim=$',\\n'; }\n\t\t[[ $persistent_keepalive == \"off\" ]] || { printf '%s\\t\\t\\t\\t\"persistentKeepalive\": %u' \"$delim\" $(( $persistent_keepalive )); delim=$',\\n'; }\n\t\tprintf '%s\\t\\t\\t\\t\"allowedIps\": [' \"$delim\"\n\t\tdelim=$'\\n'\n\t\tif [[ $allowed_ips != \"(none)\" ]]; then\n\t\t\told_ifs=\"$IFS\"\n\t\t\tIFS=,\n\t\t\tfor ip in $allowed_ips; do\n\t\t\t\tprintf '%s\\t\\t\\t\\t\\t\"%s\"' \"$delim\" \"$ip\"\n\t\t\t\tdelim=$',\\n'\n\t\t\tdone\n\t\t\tIFS=\"$old_ifs\"\n\t\t\tdelim=$'\\n'\n\t\tfi\n\t\tprintf '%s\\t\\t\\t\\t]' \"$delim\"\n\t\tprintf '\\n\\t\\t\\t}'\n\t\tdelim=$',\\n'\n\tfi\n\n\ndone\nprintf '%s\\n' \"$end\"\nprintf '}\\n'\n"
  },
  {
    "path": "contrib/keygen-html/.gitignore",
    "content": "curve25519_generate.js\n"
  },
  {
    "path": "contrib/keygen-html/README",
    "content": "WireGuard Key Generation in JavaScript\n======================================\n\nVarious people believe in JavaScript crypto, unfortunately. This small\nexample helps them fuel their poor taste.\n\nIt's possible to generate WireGuard keys (and thus configurations) in the\nbrowser. The webpage here simulates talking to a server to exchange keys\nand then generates a configuration file for the user to download.\n\nBugs\n----\n\nWho knows how emscripten actually compiles this and whether or not it\nintroduces interesting side-channel attacks.\n\nSecrets aren't zerored after use. Maybe you can get around this with\nsome tricks taking advantage of browser allocator behavior and different\nprocesses, but it seems pretty hard.\n"
  },
  {
    "path": "contrib/keygen-html/keygen.html",
    "content": "<script src=\"wireguard.js\"></script>\n<script>\n/* SPDX-License-Identifier: GPL-2.0\n *\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\nfunction sendPubkeyToServer(pubkey, username, password)\n{\n\tvar node = document.createElement(\"li\");\n\tnode.innerText = \"Sending \" + username + \":\" + password + \" to server for new pubkey \" + pubkey + \"...\";\n\tdocument.body.appendChild(node);\n\n\t// send info to server\n\n\tvar serverResponse = {\n\t\tpublicKey: \"6spHEFoJrp9pijbxjJoS6fHjZaAWQqtdFFO/OtpVe3w=\",\n\t\tallowedIPs: [ \"0.0.0.0/0\", \"::/0\" ],\n\t\tendpoint: \"demo.wireguard.com:63321\",\n\t\taddress: [ \"192.168.18.42/32\", \"fd08:1234:1111::77/128\" ],\n\t\tdns: [ \"8.8.8.8\", \"8.8.4.4\" ]\n\t}\n\n\treturn serverResponse;\n}\n\nfunction downloadNewConfiguration()\n{\n\tvar keypair = wireguard.generateKeypair();\n\tvar serverResponse = sendPubkeyToServer(keypair.publicKey, \"zx2c4\", \"supersecretpassword\");\n\n\tvar config = [];\n\tconfig.push(\"[Interface]\");\n\tconfig.push(\"PrivateKey = \" + keypair.privateKey);\n\tconfig.push(\"Address = \" + serverResponse.address.join(\", \"));\n\tconfig.push(\"DNS = \" + serverResponse.dns.join(\", \"));\n\tconfig.push(\"\");\n\tconfig.push(\"[Peer]\");\n\tconfig.push(\"PublicKey = \" + serverResponse.publicKey);\n\tconfig.push(\"AllowedIPs = \" + serverResponse.allowedIPs.join(\", \"));\n\tconfig.push(\"Endpoint = \" + serverResponse.endpoint);\n\tconfig.push(\"\");\n\treturn config.join(\"\\n\");\n}\n\nfunction giveOneConfigurationToUser()\n{\n\tvar config = downloadNewConfiguration();\n\tvar blob = new Blob([config], { type: \"text/plain\" });\n\tvar a = document.createElement(\"a\");\n\ta.download = \"demo0.conf\";\n\ta.href = URL.createObjectURL(blob);\n\ta.style.display = \"none\";\n\tdocument.body.appendChild(a);\n\ta.click();\n\tdocument.body.removeChild(a);\n}\n\nfunction putU32(b, n)\n{\n\tb.push(n & 0xff, (n >>> 8) & 0xff, (n >>> 16) & 0xff, (n >>> 24) & 0xff);\n}\n\nfunction putU16(b, n)\n{\n\tb.push(n & 0xff, (n >>> 8) & 0xff);\n}\n\nfunction putBytes(b, a)\n{\n\tfor (var i = 0; i < a.length; ++i)\n\t\tb.push(a[i] & 0xff);\n}\n\nfunction encodeString(s)\n{\n\tvar utf8 = unescape(encodeURIComponent(s));\n\tvar b = new Uint8Array(utf8.length);\n\tfor (var i = 0; i < utf8.length; ++i)\n\t\tb[i] = utf8.charCodeAt(i);\n\treturn b;\n}\n\nfunction crc32(b)\n{\n\tif (!crc32.table) {\n\t\tcrc32.table = [];\n\t\tfor (var c = 0, n = 0; n < 256; c = ++n) {\n\t\t\tfor (var k = 0; k < 8; ++k)\n\t\t\t\tc = ((c & 1) ? (0xedb88320 ^ (c >>> 1)) : (c >>> 1));\n\t\t\tcrc32.table[n] = c;\n\t\t}\n\t}\n\tvar crc = -1;\n\tfor (var i = 0; i < b.length; ++i)\n\t\tcrc = (crc >>> 8) ^ crc32.table[(crc ^ b[i]) & 0xff];\n\treturn (crc ^ (-1)) >>> 0;\n}\n\nfunction createZipFile(files)\n{\n\tvar b = [];\n\tvar cd = [];\n\tvar offset = 0;\n\n\tfor (var i = 0; i < files.length; ++i) {\n\t\tvar name = encodeString(files[i].name);\n\t\tvar contents = encodeString(files[i].contents);\n\t\tvar crc = crc32(contents);\n\n\t\tputU32(b, 0x04034b50); /* signature */\n\t\tputU16(b, 20); /* version needed */\n\t\tputU16(b, 0); /* flags */\n\t\tputU16(b, 0); /* compression method */\n\t\tputU16(b, 0); /* mtime */\n\t\tputU16(b, 0); /* mdate */\n\t\tputU32(b, crc); /* crc32 */\n\t\tputU32(b, contents.length); /* compressed size */\n\t\tputU32(b, contents.length); /* uncompressed size */\n\t\tputU16(b, name.length); /* file name length */\n\t\tputU16(b, 0); /* extra field length */\n\t\tputBytes(b, name);\n\t\tputBytes(b, contents);\n\n\t\tputU32(cd, 0x02014b50); /* signature */\n\t\tputU16(cd, 0); /* version made */\n\t\tputU16(cd, 20); /* version needed */\n\t\tputU16(cd, 0); /* flags */\n\t\tputU16(cd, 0); /* compression method */\n\t\tputU16(cd, 0); /* mtime */\n\t\tputU16(cd, 0); /* mdate */\n\t\tputU32(cd, crc); /* crc32 */\n\t\tputU32(cd, contents.length); /* compressed size */\n\t\tputU32(cd, contents.length); /* uncompressed size */\n\t\tputU16(cd, name.length); /* file name length */\n\t\tputU16(cd, 0); /* extra field length */\n\t\tputU16(cd, 0); /* file comment length */\n\t\tputU16(cd, 0); /* disk number start */\n\t\tputU16(cd, 0); /* internal file attributes */\n\t\tputU32(cd, 32); /* external file attributes - 'archive' bit set (32) */\n\t\tputU32(cd, offset); /* relative offset of local header */\n\t\tputBytes(cd, name); /* file name */\n\n\t\toffset += 30 + contents.length + name.length\n\t}\n\tputBytes(b, cd); /* central directory */\n\tputU32(b, 0x06054b50); /* end of central directory signature */\n\tputU16(b, 0); /* number of this disk */\n\tputU16(b, 0); /* number of disk with central directory start */\n\tputU16(b, files.length); /* number of entries on disk */\n\tputU16(b, files.length); /* number of entries */\n\tputU32(b, cd.length); /* length of central directory */\n\tputU32(b, offset); /* offset to start of central directory */\n\tputU16(b, 0); /* zip comment size */\n\treturn Uint8Array.from(b);\n}\n\nfunction giveMultipleConfigurationsToUser()\n{\n\tvar zipFile = createZipFile([\n\t\t{ name: \"demo0.conf\", contents: downloadNewConfiguration() },\n\t\t{ name: \"demo1.conf\", contents: downloadNewConfiguration() },\n\t\t{ name: \"demo2.conf\", contents: downloadNewConfiguration() }\n\t]);\n\tvar blob = new Blob([zipFile], { type: \"application/zip\" });\n\tvar a = document.createElement(\"a\");\n\ta.download = \"demo-configs.zip\";\n\ta.href = URL.createObjectURL(blob);\n\ta.style.display = \"none\";\n\tdocument.body.appendChild(a);\n\ta.click();\n\tdocument.body.removeChild(a);\n}\n\n</script>\n\n<p><a href=\"javascript:giveOneConfigurationToUser()\">Download a WireGuard configuration file</a></p>\n<p><a href=\"javascript:giveMultipleConfigurationsToUser()\">Download several WireGuard configuration files</a></p>\n"
  },
  {
    "path": "contrib/keygen-html/wireguard.js",
    "content": "/*! SPDX-License-Identifier: GPL-2.0\n *\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n(function() {\n\tfunction gf(init) {\n\t\tvar r = new Float64Array(16);\n\t\tif (init) {\n\t\t\tfor (var i = 0; i < init.length; ++i)\n\t\t\t\tr[i] = init[i];\n\t\t}\n\t\treturn r;\n\t}\n\n\tfunction pack(o, n) {\n\t\tvar b, m = gf(), t = gf();\n\t\tfor (var i = 0; i < 16; ++i)\n\t\t\tt[i] = n[i];\n\t\tcarry(t);\n\t\tcarry(t);\n\t\tcarry(t);\n\t\tfor (var j = 0; j < 2; ++j) {\n\t\t\tm[0] = t[0] - 0xffed;\n\t\t\tfor (var i = 1; i < 15; ++i) {\n\t\t\t\tm[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);\n\t\t\t\tm[i - 1] &= 0xffff;\n\t\t\t}\n\t\t\tm[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);\n\t\t\tb = (m[15] >> 16) & 1;\n\t\t\tm[14] &= 0xffff;\n\t\t\tcswap(t, m, 1 - b);\n\t\t}\n\t\tfor (var i = 0; i < 16; ++i) {\n\t\t\to[2 * i] = t[i] & 0xff;\n\t\t\to[2 * i + 1] = t[i] >> 8;\n\t\t}\n\t}\n\n\tfunction carry(o) {\n\t\tvar c;\n\t\tfor (var i = 0; i < 16; ++i) {\n\t\t\to[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536);\n\t\t\to[i] &= 0xffff;\n\t\t}\n\t}\n\n\tfunction cswap(p, q, b) {\n\t\tvar t, c = ~(b - 1);\n\t\tfor (var i = 0; i < 16; ++i) {\n\t\t\tt = c & (p[i] ^ q[i]);\n\t\t\tp[i] ^= t;\n\t\t\tq[i] ^= t;\n\t\t}\n\t}\n\n\tfunction add(o, a, b) {\n\t\tfor (var i = 0; i < 16; ++i)\n\t\t\to[i] = (a[i] + b[i]) | 0;\n\t}\n\n\tfunction subtract(o, a, b) {\n\t\tfor (var i = 0; i < 16; ++i)\n\t\t\to[i] = (a[i] - b[i]) | 0;\n\t}\n\n\tfunction multmod(o, a, b) {\n\t\tvar t = new Float64Array(31);\n\t\tfor (var i = 0; i < 16; ++i) {\n\t\t\tfor (var j = 0; j < 16; ++j)\n\t\t\t\tt[i + j] += a[i] * b[j];\n\t\t}\n\t\tfor (var i = 0; i < 15; ++i)\n\t\t\tt[i] += 38 * t[i + 16];\n\t\tfor (var i = 0; i < 16; ++i)\n\t\t\to[i] = t[i];\n\t\tcarry(o);\n\t\tcarry(o);\n\t}\n\n\tfunction invert(o, i) {\n\t\tvar c = gf();\n\t\tfor (var a = 0; a < 16; ++a)\n\t\t\tc[a] = i[a];\n\t\tfor (var a = 253; a >= 0; --a) {\n\t\t\tmultmod(c, c, c);\n\t\t\tif (a !== 2 && a !== 4)\n\t\t\t\tmultmod(c, c, i);\n\t\t}\n\t\tfor (var a = 0; a < 16; ++a)\n\t\t\to[a] = c[a];\n\t}\n\n\tfunction clamp(z) {\n\t\tz[31] = (z[31] & 127) | 64;\n\t\tz[0] &= 248;\n\t}\n\n\tfunction generatePublicKey(privateKey) {\n\t\tvar r, z = new Uint8Array(32);\n\t\tvar a = gf([1]),\n\t\t\tb = gf([9]),\n\t\t\tc = gf(),\n\t\t\td = gf([1]),\n\t\t\te = gf(),\n\t\t\tf = gf(),\n\t\t\t_121665 = gf([0xdb41, 1]),\n\t\t\t_9 = gf([9]);\n\t\tfor (var i = 0; i < 32; ++i)\n\t\t\tz[i] = privateKey[i];\n\t\tclamp(z);\n\t\tfor (var i = 254; i >= 0; --i) {\n\t\t\tr = (z[i >>> 3] >>> (i & 7)) & 1;\n\t\t\tcswap(a, b, r);\n\t\t\tcswap(c, d, r);\n\t\t\tadd(e, a, c);\n\t\t\tsubtract(a, a, c);\n\t\t\tadd(c, b, d);\n\t\t\tsubtract(b, b, d);\n\t\t\tmultmod(d, e, e);\n\t\t\tmultmod(f, a, a);\n\t\t\tmultmod(a, c, a);\n\t\t\tmultmod(c, b, e);\n\t\t\tadd(e, a, c);\n\t\t\tsubtract(a, a, c);\n\t\t\tmultmod(b, a, a);\n\t\t\tsubtract(c, d, f);\n\t\t\tmultmod(a, c, _121665);\n\t\t\tadd(a, a, d);\n\t\t\tmultmod(c, c, a);\n\t\t\tmultmod(a, d, f);\n\t\t\tmultmod(d, b, _9);\n\t\t\tmultmod(b, e, e);\n\t\t\tcswap(a, b, r);\n\t\t\tcswap(c, d, r);\n\t\t}\n\t\tinvert(c, c);\n\t\tmultmod(a, a, c);\n\t\tpack(z, a);\n\t\treturn z;\n\t}\n\n\tfunction generatePresharedKey() {\n\t\tvar privateKey = new Uint8Array(32);\n\t\twindow.crypto.getRandomValues(privateKey);\n\t\treturn privateKey;\n\t}\n\n\tfunction generatePrivateKey() {\n\t\tvar privateKey = generatePresharedKey();\n\t\tclamp(privateKey);\n\t\treturn privateKey;\n\t}\n\n\tfunction encodeBase64(dest, src) {\n\t\tvar input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]);\n\t\tfor (var i = 0; i < 4; ++i)\n\t\t\tdest[i] = input[i] + 65 +\n\t\t\t(((25 - input[i]) >> 8) & 6) -\n\t\t\t(((51 - input[i]) >> 8) & 75) -\n\t\t\t(((61 - input[i]) >> 8) & 15) +\n\t\t\t(((62 - input[i]) >> 8) & 3);\n\t}\n\n\tfunction keyToBase64(key) {\n\t\tvar i, base64 = new Uint8Array(44);\n\t\tfor (i = 0; i < 32 / 3; ++i)\n\t\t\tencodeBase64(base64.subarray(i * 4), key.subarray(i * 3));\n\t\tencodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0]));\n\t\tbase64[43] = 61;\n\t\treturn String.fromCharCode.apply(null, base64);\n\t}\n\n\twindow.wireguard = {\n\t\tgenerateKeypair: function() {\n\t\t\tvar privateKey = generatePrivateKey();\n\t\t\tvar publicKey = generatePublicKey(privateKey);\n\t\t\treturn {\n\t\t\t\tpublicKey: keyToBase64(publicKey),\n\t\t\t\tprivateKey: keyToBase64(privateKey)\n\t\t\t};\n\t\t}\n\t};\n})();\n"
  },
  {
    "path": "contrib/launchd/README",
    "content": "WireGuard for Launchd\n=====================\n\nThe example `com.wireguard.wg0.plist` file may be used for running wg-quick(8)\nas a launchd service. Note that the `PATH` variable is modified to point to\nthe PATH used by Homebrew or Macports, so that it uses the non-system bash(1).\n\nUsage\n-----\n\n$ sudo cp com.wireguard.wg0.plist /Library/LaunchDaemons\n$ sudo launchctl load /Library/LaunchDaemons/com.wireguard.wg0.plist\n"
  },
  {
    "path": "contrib/launchd/com.wireguard.wg0.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n  <key>Label</key>\n  <string>com.wireguard.wg0</string>\n  <key>ProgramArguments</key>\n  <array>\n    <string>/usr/local/bin/wg-quick</string>\n    <string>up</string>\n    <string>/usr/local/etc/wireguard/wg0.conf</string>\n  </array>\n  <key>OnDemand</key>\n  <false/>\n  <key>RunAtLoad</key>\n  <true/>\n  <key>TimeOut</key>\n  <integer>90</integer>\n  <key>EnvironmentVariables</key>\n  <dict>\n    <key>PATH</key>\n    <string>/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>\n  </dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "contrib/nat-hole-punching/README",
    "content": "== NAT Hole Punching Example ==\n\nThis code should never be used, ever. But, it's a nice demonstration of how\nto punch holes and have two NAT'd peers talk to each other.\n\nCompile with:\n    $ gcc nat-punch-client.c -o client -lresolv\n    $ gcc nat-punch-server.c -o server\n\n\nServer is 1.2.3.4 and is on the public internet accepting UDP:49918.\nClient A is NAT'd and doesn't know its IP address.\nClient B is NAT'd and doesn't know its IP address.\n\n\nServer runs:\n   $ ./server\n\nClient A runs:\n   # ip link add wg0 type wireguard\n   # ip addr add 10.200.200.1 peer 10.200.200.2 dev wg0\n   # wg set wg0 private-key ... peer ... allowed-ips 10.200.200.2/32\n   # ./client 1.2.3.4 wg0\n   # ping 10.200.200.2\n\nClient B runs:\n   # ip link add wg0 type wireguard\n   # ip addr add 10.200.200.2 peer 10.200.200.1 dev wg0\n   # wg set wg0 private-key ... peer ... allowed-ips 10.200.200.1/32\n   # ./client 1.2.3.4 wg0\n   # ping 10.200.200.1\n\nAnd voila! Client A and Client B can speak from behind NAT.\n\n\n\n-----\nKeep in mind that this is proof-of-concept example code. It is not code that\nshould be used in production, ever. It is woefully insecure, and is unsuitable\nfor any real usage. With that said, this is useful as a learning example of\nhow NAT hole punching might work within a more developed solution.\n"
  },
  {
    "path": "contrib/nat-hole-punching/nat-punch-client.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n *\n * Example only. Do not run in production.\n */\n\n#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <errno.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <netinet/udp.h>\n#include <netinet/ip.h>\n#include <linux/filter.h>\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <stdarg.h>\n#include <string.h>\n#include <resolv.h>\n#include <stdint.h>\n#include <stdbool.h>\n\nenum { MAX_PEERS = 65536, PORT = 49918 };\n\nstatic struct {\n\tuint8_t base64_key[45];\n\tbool have_seen;\n} peers[MAX_PEERS];\nstatic unsigned int total_peers;\n\nstatic const char *cmd(const char *line, ...)\n{\n\tstatic char buf[2048];\n\tchar full_cmd[2048] = { 0 };\n\tsize_t len;\n\tFILE *f;\n\tva_list args;\n\tva_start(args, line);\n\tvsnprintf(full_cmd, 2047, line, args);\n\tva_end(args);\n\tf = popen(full_cmd, \"r\");\n\tif (!f) {\n\t\tperror(\"popen\");\n\t\texit(errno);\n\t}\n\tif (!fgets(buf, 2048, f)) {\n\t\tpclose(f);\n\t\treturn NULL;\n\t}\n\tpclose(f);\n\tlen = strlen(buf);\n\tif (!len)\n\t\treturn NULL;\n\tif (buf[len - 1] == '\\n')\n\t\tbuf[len - 1] = '\\0';\n\treturn buf;\n}\n\nstatic void read_peers(const char *interface)\n{\n\tchar full_cmd[2048] = { 0 };\n\tsize_t len;\n\tFILE *f;\n\tsnprintf(full_cmd, 2047, \"wg show %s peers\", interface);\n\tf = popen(full_cmd, \"r\");\n\tif (!f) {\n\t\tperror(\"popen\");\n\t\texit(errno);\n\t}\n\tfor (;;) {\n\t\tif (!fgets(peers[total_peers].base64_key, 45, f))\n\t\t\tbreak;\n\t\tlen = strlen(peers[total_peers].base64_key);\n\t\tif (len != 44 && len != 45)\n\t\t\tcontinue;\n\t\tif (peers[total_peers].base64_key[len - 1] == '\\n')\n\t\t\tpeers[total_peers].base64_key[len - 1] = '\\0';\n\t\t++total_peers;\n\t}\n\tpclose(f);\n}\n\nstatic void unbase64(uint8_t dstkey[32], const char *srckey)\n{\n\tuint8_t buf[33];\n\tif (b64_pton(srckey, buf, 33) != 32) {\n\t\tfprintf(stderr, \"Could not parse base64 key: %s\\n\", srckey);\n\t\texit(EINVAL);\n\t}\n\tmemcpy(dstkey, buf, 32);\n}\n\nstatic void apply_bpf(int sock, uint16_t port, uint32_t ip)\n{\n\tstruct sock_filter filter[] = {\n\t\tBPF_STMT(BPF_LD + BPF_W + BPF_ABS, 12 /* src ip */),\n\t\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ip, 0, 5),\n\t\tBPF_STMT(BPF_LD + BPF_H + BPF_ABS, 20 /* src port */),\n\t\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, PORT, 0, 3),\n\t\tBPF_STMT(BPF_LD + BPF_H + BPF_ABS, 22 /* dst port */),\n\t\tBPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, port, 0, 1),\n\t\tBPF_STMT(BPF_RET + BPF_K, -1),\n\t\tBPF_STMT(BPF_RET + BPF_K, 0)\n\t};\n\tstruct sock_fprog filter_prog = {\n\t\t.len = sizeof(filter) / sizeof(filter[0]),\n\t\t.filter = filter\n\t};\n\tif (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &filter_prog, sizeof(filter_prog)) < 0) {\n\t\tperror(\"setsockopt(bpf)\");\n\t\texit(errno);\n\t}\n}\n\nint main(int argc, char *argv[])\n{\n\tstruct sockaddr_in addr = {\n\t\t.sin_family = AF_INET\n\t};\n\tstruct {\n\t\tstruct udphdr udp;\n\t\tuint8_t my_pubkey[32];\n\t\tuint8_t their_pubkey[32];\n\t} __attribute__((packed)) packet = {\n\t\t.udp = {\n\t\t\t.len = htons(sizeof(packet)),\n\t\t\t.dest = htons(PORT)\n\t\t}\n\t};\n\tstruct {\n\t\tstruct iphdr iphdr;\n\t\tstruct udphdr udp;\n\t\tuint32_t ip;\n\t\tuint16_t port;\n\t} __attribute__((packed)) reply;\n\tssize_t len;\n\tint sock, i;\n\tbool repeat;\n\tstruct hostent *ent;\n\tconst char *server = argv[1], *interface = argv[2];\n\n\tif (argc < 3) {\n\t\tfprintf(stderr, \"Usage: %s SERVER WIREGUARD_INTERFACE\\nExample:\\n    %s demo.wireguard.com wg0\\n\", argv[0], argv[0]);\n\t\treturn EINVAL;\n\t}\n\n\tif (getuid() != 0) {\n\t\tfprintf(stderr, \"Must be root!\\n\");\n\t\treturn EPERM;\n\t}\n\n\tent = gethostbyname2(server, AF_INET);\n\tif (!ent) {\n\t\therror(\"gethostbyname2\");\n\t\treturn h_errno;\n\t}\n\taddr.sin_addr = *(struct in_addr *)ent->h_addr;\n\tread_peers(interface);\n\tcmd(\"ip link set %s up\", interface);\n\tunbase64(packet.my_pubkey, cmd(\"wg show %s public-key\", interface));\n\tpacket.udp.source = htons(atoi(cmd(\"wg show %s listen-port\", interface)));\n\n\t/* We use raw sockets so that the WireGuard interface can actually own the real socket. */\n\tsock = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);\n\tif (sock < 0) {\n\t\tperror(\"socket\");\n\t\treturn errno;\n\t}\n\tapply_bpf(sock, ntohs(packet.udp.source), ntohl(addr.sin_addr.s_addr));\n\ncheck_again:\n\trepeat = false;\n\tfor (i = 0; i < total_peers; ++i) {\n\t\tif (peers[i].have_seen)\n\t\t\tcontinue;\n\t\tprintf(\"[+] Requesting IP and port of %s: \", peers[i].base64_key);\n\t\tunbase64(packet.their_pubkey, peers[i].base64_key);\n\t\tif (sendto(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&addr, sizeof(addr)) < 0) {\n\t\t\tputchar('\\n');\n\t\t\tperror(\"sendto\");\n\t\t\treturn errno;\n\t\t}\n\t\tlen = recv(sock, &reply, sizeof(reply), 0);\n\t\tif (len < 0) {\n\t\t\tputchar('\\n');\n\t\t\tperror(\"recv\");\n\t\t\treturn errno;\n\t\t}\n\t\tif (len != sizeof(reply)) {\n\t\t\tprintf(\"server does not yet have it\\n\");\n\t\t\trepeat = true;\n\t\t} else {\n\t\t\tprintf(\"%s:%d\\n\", inet_ntoa(*(struct in_addr *)&reply.ip), ntohs(reply.port));\n\t\t\tpeers[i].have_seen = true;\n\t\t\tcmd(\"wg set %s peer %s persistent-keepalive 25 endpoint %s:%d\", interface, peers[i].base64_key, inet_ntoa(*(struct in_addr *)&reply.ip), ntohs(reply.port));\n\t\t}\n\t}\n\tif (repeat) {\n\t\tsleep(2);\n\t\tgoto check_again;\n\t}\n\n\tclose(sock);\n\treturn 0;\n}\n"
  },
  {
    "path": "contrib/nat-hole-punching/nat-punch-server.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n *\n * Example only. Do not run in production.\n */\n\n#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <errno.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n\nstruct entry {\n\tuint8_t pubkey[32];\n\tuint32_t ip;\n\tuint16_t port;\n};\n\nenum { MAX_ENTRIES = 65536, PORT = 49918 };\n\nstatic struct entry entries[MAX_ENTRIES];\nstatic unsigned int next_entry;\n\n/* XX: this should use a hash table */\nstatic struct entry *find_entry(uint8_t key[32])\n{\n\tint i;\n\tfor (i = 0; i < MAX_ENTRIES; ++i) {\n\t\tif (!memcmp(entries[i].pubkey, key, 32))\n\t\t\treturn &entries[i];\n\t}\n\treturn NULL;\n}\n\n/* XX: this is obviously vulnerable to DoS */\nstatic struct entry *find_or_insert_entry(uint8_t key[32])\n{\n\tstruct entry *entry = find_entry(key);\n\tif (!entry) {\n\t\tentry = &entries[next_entry++ % MAX_ENTRIES];\n\t\tmemcpy(entry->pubkey, key, 32);\n\t}\n\treturn entry;\n}\n\nint main(int argc, char *argv[])\n{\n\tstruct sockaddr_in addr = {\n\t\t.sin_family = AF_INET,\n\t\t.sin_addr = { .s_addr = htonl(INADDR_ANY) },\n\t\t.sin_port = htons(PORT)\n\t};\n\tstruct {\n\t\tuint8_t my_pubkey[32];\n\t\tuint8_t their_pubkey[32];\n\t} __attribute__((packed)) packet;\n\tstruct {\n\t\tuint32_t ip;\n\t\tuint16_t port;\n\t} __attribute__((packed)) reply;\n\tstruct entry *entry;\n\tsocklen_t len;\n\tssize_t retlen;\n\tint optval;\n\tint sock = socket(AF_INET, SOCK_DGRAM, 0);\n\tif (sock < 0) {\n\t\tperror(\"socket\");\n\t\treturn errno;\n\t}\n\n\toptval = 1;\n\tif (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {\n\t\tperror(\"setsockopt\");\n\t\treturn errno;\n\t}\n\n\tif (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {\n\t\tperror(\"bind\");\n\t\treturn errno;\n\t}\n\n\tfor (;;) {\n\t\tlen = sizeof(addr);\n\t\tif (recvfrom(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&addr, &len) != sizeof(packet)) {\n\t\t\tperror(\"recvfrom\");\n\t\t\tcontinue;\n\t\t}\n\t\tentry = find_or_insert_entry(packet.my_pubkey);\n\t\tentry->ip = addr.sin_addr.s_addr;\n\t\tentry->port = addr.sin_port;\n\t\tentry = find_entry(packet.their_pubkey);\n\t\tif (entry) {\n\t\t\treply.ip = entry->ip;\n\t\t\treply.port = entry->port;\n\t\t\tif (sendto(sock, &reply, sizeof(reply), 0, (struct sockaddr *)&addr, len) < 0) {\n\t\t\t\tperror(\"sendto\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t} else {\n\t\t\tif (sendto(sock, NULL, 0, 0, (struct sockaddr *)&addr, len) < 0) {\n\t\t\t\tperror(\"sendto\");\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n\n\tclose(sock);\n\treturn 0;\n}\n"
  },
  {
    "path": "contrib/ncat-client-server/README",
    "content": "                === IMPORTANT NOTE ===\n\nDo not use these scripts in production. They are simply a\ndemonstration of how easy the `wg(8)` tool is at the command\nline, but by no means should you actually attempt to use\nthese. They are horribly insecure and defeat the purpose\nof WireGuard.\n                     STAY AWAY!\n\nThat all said, this is a pretty cool example of just how\ndarn easy WireGuard can be.\n\nDisclaimer:\n  The `demo.wireguard.com` server in client.sh is for testing\n  purposes only. You may not use this server for abusive or\n  illegal purposes.\n"
  },
  {
    "path": "contrib/ncat-client-server/client-quick.sh",
    "content": "#!/usr/bin/env bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n\nset -e\n\necho \"[!] Warning: This server is for testing purposes only. You may not use this server for abusive or illegal purposes.\"\n\necho \"[+] Generating private key.\"\nprivatekey=\"$(wg genkey)\"\n\necho \"[+] Sending public key to server.\"\nexec 7<>/dev/tcp/demo.wireguard.com/42912\nwg pubkey <<<\"$privatekey\" >&7\n\necho \"[+] Parsing server response.\"\nIFS=: read -r status server_pubkey server_port internal_ip <&7\n[[ $status == OK ]] || exit 1\n\necho \"[+] Writing config file.\"\n[[ $UID -eq 0 ]] || sudo=sudo\n$sudo sh -c 'umask 077; mkdir -p /etc/wireguard; cat > /etc/wireguard/demo.conf' <<_EOF\n[Interface]\nPrivateKey = $privatekey\nAddress = $internal_ip/24\nDNS = 8.8.8.8, 8.8.4.4, 1.1.1.1, 1.0.0.1\n\n[Peer]\nPublicKey = $server_pubkey\nEndpoint = demo.wireguard.com:$server_port\nAllowedIPs = 0.0.0.0/0\n_EOF\n\necho \"[+] Success. Run \\`wg-quick up demo\\` to turn on the tunnel to the demo server and \\`wg-quick down demo\\` to turn it off.\"\n"
  },
  {
    "path": "contrib/ncat-client-server/client.sh",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n\nset -e\n[[ $UID == 0 ]] || { echo \"You must be root to run this.\"; exit 1; }\nexec 3<>/dev/tcp/demo.wireguard.com/42912\nprivatekey=\"$(wg genkey)\"\nwg pubkey <<<\"$privatekey\" >&3\nIFS=: read -r status server_pubkey server_port internal_ip <&3\n[[ $status == OK ]]\nip link del dev wg0 2>/dev/null || true\nip link add dev wg0 type wireguard\nwg set wg0 private-key <(echo \"$privatekey\") peer \"$server_pubkey\" allowed-ips 0.0.0.0/0 endpoint \"demo.wireguard.com:$server_port\" persistent-keepalive 25\nip address add \"$internal_ip\"/24 dev wg0\nip link set up dev wg0\nif [ \"$1\" == \"default-route\" ]; then\n\thost=\"$(wg show wg0 endpoints | sed -n 's/.*\\t\\(.*\\):.*/\\1/p')\"\n\tip route add $(ip route get $host | sed '/ via [0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}/{s/^\\(.* via [0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\.[0-9]\\{1,3\\}\\).*/\\1/}' | head -n 1) 2>/dev/null || true\n\tip route add 0/1 dev wg0\n\tip route add 128/1 dev wg0\nfi\n"
  },
  {
    "path": "contrib/ncat-client-server/server.sh",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n\nif [[ -z $NCAT_REMOTE_ADDR ]]; then\n\tip link del dev wg0 2>/dev/null\n\tset -e\n\tip link add dev wg0 type wireguard\n\tip address add 192.168.4.1/24 dev wg0\n\twg set wg0 private-key <(wg genkey) listen-port 12912\n\tip link set up dev wg0\n\texec ncat -e \"$(readlink -f \"$0\")\" -k -l -p 42912 -v\nfi\nread -r public_key\n[[ $(wg show wg0 peers | wc -l) -ge 253 ]] && wg set wg0 peer $(wg show wg0 latest-handshakes | sort -k 2 -b -n | head -n 1 | cut -f 1) remove\nnext_ip=$(all=\"$(wg show wg0 allowed-ips)\"; for ((i=2; i<=254; i++)); do ip=\"192.168.4.$i\"; [[ $all != *$ip/32* ]] && echo $ip && break; done)\nwg set wg0 peer \"$public_key\" allowed-ips $next_ip/32 2>/dev/null && echo \"OK:$(wg show wg0 private-key | wg pubkey):$(wg show wg0 listen-port):$next_ip\" || echo ERROR\n"
  },
  {
    "path": "contrib/reresolve-dns/README",
    "content": "reresolve-dns\n=============\n\nRun this script from cron every thirty seconds or so, and it will ensure\nthat if, when using a dynamic DNS service, the DNS entry for a hosts\nchanges, the kernel will get the update to the DNS entry.\n\nThis works by parsing configuration files, and simply running:\n    $ wg set wg0 peer ... endpoint ...\n"
  },
  {
    "path": "contrib/reresolve-dns/reresolve-dns.sh",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n\nset -e\nshopt -s nocasematch\nshopt -s extglob\nexport LC_ALL=C\n\nCONFIG_FILE=\"$1\"\n[[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE=\"/etc/wireguard/$CONFIG_FILE.conf\"\n[[ $CONFIG_FILE =~ /?([a-zA-Z0-9_=+.-]{1,15})\\.conf$ ]]\nINTERFACE=\"${BASH_REMATCH[1]}\"\n\nprocess_peer() {\n\t[[ $PEER_SECTION -ne 1 || -z $PUBLIC_KEY || -z $ENDPOINT ]] && return 0\n\t[[ $(wg show \"$INTERFACE\" latest-handshakes) =~ ${PUBLIC_KEY//+/\\\\+}\\\t([0-9]+) ]] || return 0\n\t(( ($EPOCHSECONDS - ${BASH_REMATCH[1]}) > 135 )) || return 0\n\twg set \"$INTERFACE\" peer \"$PUBLIC_KEY\" endpoint \"$ENDPOINT\"\n\treset_peer_section\n}\n\nreset_peer_section() {\n\tPEER_SECTION=0\n\tPUBLIC_KEY=\"\"\n\tENDPOINT=\"\"\n}\n\nreset_peer_section\nwhile read -r line || [[ -n $line ]]; do\n\tstripped=\"${line%%\\#*}\"\n\tkey=\"${stripped%%=*}\"; key=\"${key##*([[:space:]])}\"; key=\"${key%%*([[:space:]])}\"\n\tvalue=\"${stripped#*=}\"; value=\"${value##*([[:space:]])}\"; value=\"${value%%*([[:space:]])}\"\n\t[[ $key == \"[\"* ]] && { process_peer; reset_peer_section; }\n\t[[ $key == \"[Peer]\" ]] && PEER_SECTION=1\n\tif [[ $PEER_SECTION -eq 1 ]]; then\n\t\tcase \"$key\" in\n\t\tPublicKey) PUBLIC_KEY=\"$value\"; continue ;;\n\t\tEndpoint) ENDPOINT=\"$value\"; continue ;;\n\t\tesac\n\tfi\ndone < \"$CONFIG_FILE\"\nprocess_peer\n"
  },
  {
    "path": "contrib/sticky-sockets/README",
    "content": "Sticky Sockets\n==============\n\nThis is a small userspace mini-library that implements as close to\npossible how the kernel does its sticky src address sending.\n"
  },
  {
    "path": "contrib/sticky-sockets/sticky-sockets.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n *\n * This implements userspace semantics of \"sticky sockets\", modeled after\n * WireGuard's kernelspace implementation.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <errno.h>\n#include <unistd.h>\n#include <linux/ipv6.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <netinet/udp.h>\n#include <arpa/inet.h>\n\nstruct magic_endpoint {\n\tunion {\n\t\tstruct sockaddr addr;\n\t\tstruct sockaddr_in addr4;\n\t\tstruct sockaddr_in6 addr6;\n\t};\n\tunion {\n\t\tstruct {\n\t\t\tstruct in_addr src4;\n\t\t\tint src_if4; /* Essentially the same as addr6->scope_id */\n\t\t};\n\t\tstruct in6_addr src6;\n\t};\n};\n\nssize_t magic_send4(int sock, struct magic_endpoint *endpoint, void *buffer, size_t len)\n{\n\tssize_t ret;\n\tstruct iovec iovec = {\n\t\t.iov_base = buffer,\n\t\t.iov_len = len\n\t};\n\tstruct {\n\t\tstruct cmsghdr cmsghdr;\n\t\tstruct in_pktinfo pktinfo;\n\t} cmsg = {\n\t\t.cmsghdr.cmsg_level = IPPROTO_IP,\n\t\t.cmsghdr.cmsg_type = IP_PKTINFO,\n\t\t.cmsghdr.cmsg_len = CMSG_LEN(sizeof(cmsg.pktinfo)),\n\t\t.pktinfo.ipi_spec_dst = endpoint->src4,\n\t\t.pktinfo.ipi_ifindex = endpoint->src_if4\n\t};\n\tstruct msghdr msghdr = {\n\t\t.msg_iov = &iovec,\n\t\t.msg_iovlen = 1,\n\t\t.msg_name = &endpoint->addr4,\n\t\t.msg_namelen = sizeof(endpoint->addr4),\n\t\t.msg_control = &cmsg,\n\t\t.msg_controllen = sizeof(cmsg)\n\t};\n\tret = sendmsg(sock, &msghdr, 0);\n\tif (ret < 0 && errno == EINVAL) {\n\t\tmemset(&cmsg.pktinfo, 0, sizeof(cmsg.pktinfo));\n\t\tendpoint->src4.s_addr = endpoint->src_if4 = 0;\n\t\treturn sendmsg(sock, &msghdr, 0);\n\t}\n\treturn ret;\n}\n\nssize_t magic_send6(int sock, struct magic_endpoint *endpoint, void *buffer, size_t len)\n{\n\tssize_t ret;\n\tstruct iovec iovec = {\n\t\t.iov_base = buffer,\n\t\t.iov_len = len\n\t};\n\tstruct {\n\t\tstruct cmsghdr cmsghdr;\n\t\tstruct in6_pktinfo pktinfo;\n\t} cmsg = {\n\t\t.cmsghdr.cmsg_level = IPPROTO_IPV6,\n\t\t.cmsghdr.cmsg_type = IPV6_PKTINFO,\n\t\t.cmsghdr.cmsg_len = CMSG_LEN(sizeof(cmsg.pktinfo)),\n\t\t.pktinfo.ipi6_addr = endpoint->src6,\n\t\t.pktinfo.ipi6_ifindex = memcmp(&in6addr_any, &endpoint->src6, sizeof(endpoint->src6)) ? endpoint->addr6.sin6_scope_id : 0\n\t};\n\tstruct msghdr msghdr = {\n\t\t.msg_iov = &iovec,\n\t\t.msg_iovlen = 1,\n\t\t.msg_name = &endpoint->addr6,\n\t\t.msg_namelen = sizeof(endpoint->addr6),\n\t\t.msg_control = &cmsg,\n\t\t.msg_controllen = sizeof(cmsg)\n\t};\n\n\tret = sendmsg(sock, &msghdr, 0);\n\tif (ret < 0 && errno == EINVAL) {\n\t\tmemset(&cmsg.pktinfo, 0, sizeof(cmsg.pktinfo));\n\t\tmemset(&endpoint->src6, 0, sizeof(endpoint->src6));\n\t\treturn sendmsg(sock, &msghdr, 0);\n\t}\n\treturn ret;\n}\n\nssize_t magic_receive4(int sock, struct magic_endpoint *endpoint, void *buffer, size_t len)\n{\n\tssize_t ret;\n\tstruct iovec iovec = {\n\t\t.iov_base = buffer,\n\t\t.iov_len = len\n\t};\n\tstruct {\n\t\tstruct cmsghdr cmsghdr;\n\t\tstruct in_pktinfo pktinfo;\n\t} cmsg;\n\tstruct msghdr msghdr = {\n\t\t.msg_iov = &iovec,\n\t\t.msg_iovlen = 1,\n\t\t.msg_name = &endpoint->addr4,\n\t\t.msg_namelen = sizeof(endpoint->addr4),\n\t\t.msg_control = &cmsg,\n\t\t.msg_controllen = sizeof(cmsg)\n\t};\n\n\tret = recvmsg(sock, &msghdr, 0);\n\tif (ret < 0)\n\t\treturn ret;\n\tif (cmsg.cmsghdr.cmsg_level == IPPROTO_IP && cmsg.cmsghdr.cmsg_type == IP_PKTINFO && cmsg.cmsghdr.cmsg_len >= CMSG_LEN(sizeof(cmsg.pktinfo))) {\n\t\tendpoint->src4 = cmsg.pktinfo.ipi_spec_dst;\n\t\tendpoint->src_if4 = cmsg.pktinfo.ipi_ifindex;\n\t}\n\treturn ret;\n}\n\nssize_t magic_receive6(int sock, struct magic_endpoint *endpoint, void *buffer, size_t len)\n{\n\tssize_t ret;\n\tstruct iovec iovec = {\n\t\t.iov_base = buffer,\n\t\t.iov_len = len\n\t};\n\tstruct {\n\t\tstruct cmsghdr cmsghdr;\n\t\tstruct in6_pktinfo pktinfo;\n\t} cmsg;\n\tstruct msghdr msghdr = {\n\t\t.msg_iov = &iovec,\n\t\t.msg_iovlen = 1,\n\t\t.msg_name = &endpoint->addr6,\n\t\t.msg_namelen = sizeof(endpoint->addr6),\n\t\t.msg_control = &cmsg,\n\t\t.msg_controllen = sizeof(cmsg)\n\t};\n\n\tret = recvmsg(sock, &msghdr, 0);\n\tif (ret < 0)\n\t\treturn ret;\n\tif (cmsg.cmsghdr.cmsg_level == IPPROTO_IPV6 && cmsg.cmsghdr.cmsg_type == IPV6_PKTINFO && cmsg.cmsghdr.cmsg_len >= CMSG_LEN(sizeof(cmsg.pktinfo))) {\n\t\tendpoint->src6 = cmsg.pktinfo.ipi6_addr;\n\t\tendpoint->addr6.sin6_scope_id = cmsg.pktinfo.ipi6_ifindex;\n\t}\n\treturn ret;\n}\n\nvoid magic_endpoint_clearsrc(struct magic_endpoint *endpoint)\n{\n\tif (endpoint->addr.sa_family == AF_INET)\n\t\tendpoint->src4.s_addr = endpoint->src_if4 = 0;\n\telse if (endpoint->addr.sa_family == AF_INET6)\n\t\tmemset(&endpoint->src6, 0, sizeof(endpoint->src6));\n\telse\n\t\tmemset(endpoint, 0, sizeof(*endpoint));\n}\n\nvoid magic_endpoint_set(struct magic_endpoint *endpoint, const struct sockaddr *addr)\n{\n\tif (addr->sa_family == AF_INET)\n\t\tendpoint->addr4 = *(struct sockaddr_in *)addr;\n\telse if (addr->sa_family == AF_INET6)\n\t\tendpoint->addr6 = *(struct sockaddr_in6 *)addr;\n\tmagic_endpoint_clearsrc(endpoint);\n}\n\nint magic_create_sock4(uint16_t listen_port)\n{\n\tstatic const int on = 1;\n\tstruct sockaddr_in listen_addr = {\n\t\t.sin_family = AF_INET,\n\t\t.sin_port = htons(listen_port),\n\t\t.sin_addr = INADDR_ANY\n\t};\n\tint fd, ret;\n\t\n\tfd = socket(AF_INET, SOCK_DGRAM, 0);\n\tif (fd < 0)\n\t\treturn fd;\n\t\n\tret = setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));\n\tif (ret < 0)\n\t\tgoto err;\n\t\n\tret = bind(fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr));\n\tif (ret < 0)\n\t\tgoto err;\n\t\n\treturn fd;\n\nerr:\n\tclose(fd);\n\treturn ret;\n}\n\nint magic_create_sock6(uint16_t listen_port)\n{\n\tstatic const int on = 1;\n\tstruct sockaddr_in6 listen_addr = {\n\t\t.sin6_family = AF_INET6,\n\t\t.sin6_port = htons(listen_port),\n\t\t.sin6_addr = IN6ADDR_ANY_INIT\n\t};\n\tint fd, ret;\n\t\n\tfd = socket(AF_INET6, SOCK_DGRAM, 0);\n\tif (fd < 0)\n\t\treturn fd;\n\t\n\tret = setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));\n\tif (ret < 0)\n\t\tgoto err;\n\n\tret = setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));\n\tif (ret < 0)\n\t\tgoto err;\n\t\n\tret = bind(fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr));\n\tif (ret < 0)\n\t\tgoto err;\n\t\n\treturn fd;\n\nerr:\n\tclose(fd);\n\treturn ret;\n}\n\nint main(int argc, char *argv[])\n{\n\tstruct magic_endpoint endpoint = { 0 };\n\tint sock;\n\tssize_t ret;\n\tuint8_t buffer[1024] = { 0 };\n\tchar srcaddr[40], dstaddr[40];\n\n\tif (argc == 2 && !strcmp(argv[1], \"-4\"))\n\t\tgoto v4;\n\tif (argc == 2 && !strcmp(argv[1], \"-6\"))\n\t\tgoto v6;\n\treturn 1;\n\nv6:\n\tsock = magic_create_sock6(51820);\n\tif (sock < 0) {\n\t\tperror(\"magic_create_sock6\");\n\t\treturn 1;\n\t}\n\n\tret = magic_receive6(sock, &endpoint, buffer, sizeof(buffer));\n\tif (ret < 0) {\n\t\tperror(\"magic_receive6\");\n\t\treturn 1;\n\t}\n\n\tif (!inet_ntop(AF_INET6, &endpoint.src6, srcaddr, sizeof(srcaddr))) {\n\t\tperror(\"inet_ntop\");\n\t\treturn 1;\n\t}\n\n\tif (!inet_ntop(AF_INET6, &endpoint.addr6.sin6_addr, dstaddr, sizeof(dstaddr))) {\n\t\tperror(\"inet_ntop\");\n\t\treturn 1;\n\t}\n\n\tprintf(\"if:%d src:%s dst:%s\\n\", endpoint.addr6.sin6_scope_id, srcaddr, dstaddr);\n\tprintf(\"Received a packet. Sleeping for 10 seconds before replying, so you have time to mess with your networking setup.\\n\");\n\tsleep(10);\n\n\tret = magic_send6(sock, &endpoint, buffer, sizeof(buffer));\n\tif (ret < 0) {\n\t\tperror(\"magic_send6\");\n\t\treturn 1;\n\t}\n\n\tclose(sock);\n\treturn 0;\n\nv4:\n\tsock = magic_create_sock4(51820);\n\tif (sock < 0) {\n\t\tperror(\"magic_create_sock4\");\n\t\treturn 1;\n\t}\n\n\tret = magic_receive4(sock, &endpoint, buffer, sizeof(buffer));\n\tif (ret < 0) {\n\t\tperror(\"magic_receive4\");\n\t\treturn 1;\n\t}\n\n\tif (!inet_ntop(AF_INET, &endpoint.src4, srcaddr, sizeof(srcaddr))) {\n\t\tperror(\"inet_ntop\");\n\t\treturn 1;\n\t}\n\n\tif (!inet_ntop(AF_INET, &endpoint.addr4.sin_addr, dstaddr, sizeof(dstaddr))) {\n\t\tperror(\"inet_ntop\");\n\t\treturn 1;\n\t}\n\n\tprintf(\"if:%d src:%s dst:%s\\n\", endpoint.src_if4, srcaddr, dstaddr);\n\tprintf(\"Received a packet. Sleeping for 10 seconds before replying, so you have time to mess with your networking setup.\\n\");\n\tsleep(10);\n\n\tret = magic_send4(sock, &endpoint, buffer, sizeof(buffer));\n\tif (ret < 0) {\n\t\tperror(\"magic_send4\");\n\t\treturn 1;\n\t}\n\t\n\tclose(sock);\n\treturn 0;\n}\n"
  },
  {
    "path": "contrib/synergy/README",
    "content": "These scripts should be modified according to your precise setup.\nThey provide a very simple way of tunneling synergy inside of a\nWireGuard tunnel, to protect your data in transit.\n"
  },
  {
    "path": "contrib/synergy/synergy-client.sh",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n\nset -ex\nif [[ $UID == 0 ]]; then\n\tip link del dev synergy || true\n\tip link add dev synergy type wireguard\n\tip address add 10.193.125.39/32 peer 10.193.125.38/32 dev synergy\n\twg set synergy \\\n\t\tlisten-port 29184 \\\n\t\tprivate-key <(echo oNcsXA5Ma56q9xHmvvKuzLfwXYy7Uqy+bTmmXg/XtVs=) \\\n\t\tpeer m321UMZXoJ6qw8Jli2spbAVBc2MdOzV/EHDKfZQy0g0= \\\n\t\t\tallowed-ips 10.193.125.38/32 \\\n\t\t\tendpoint 10.10.10.100:29184\n\tip link set up dev synergy\nelse\n\tsudo \"$(readlink -f \"$0\")\"\n\tkillall synergyc || true\n\tsynergyc 10.193.125.38:38382\nfi\n"
  },
  {
    "path": "contrib/synergy/synergy-server.sh",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n\nset -ex\nif [[ $UID == 0 ]]; then\n\tip link del dev synergy || true\n\tip link add dev synergy type wireguard\n\tip address add 10.193.125.38/32 peer 10.193.125.39/32 dev synergy\n\twg set synergy \\\n\t\tlisten-port 29184 \\\n\t\tprivate-key <(echo 2InSrlZA5eQfI/MvnvPieqNTBo9cd+udc3SOO9yFpXo=) \\\n\t\tpeer CBnoidQLjlbRsrqrI56WQbANWwkll41w/rVUIW9zISI= \\\n\t\t\tallowed-ips 10.193.125.39/32\n\tip link set up dev synergy\nelse\n\tsudo \"$(readlink -f \"$0\")\"\n\tkillall synergys || true\n\tsynergys -a 10.193.125.38:38382\nfi\n"
  },
  {
    "path": "src/Makefile",
    "content": "# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n\nPKG_CONFIG ?= pkg-config\nPREFIX ?= /usr\nDESTDIR ?=\nSYSCONFDIR ?= /etc\nBINDIR ?= $(PREFIX)/bin\nLIBDIR ?= $(PREFIX)/lib\nMANDIR ?= $(PREFIX)/share/man\nBASHCOMPDIR ?= $(PREFIX)/share/bash-completion/completions\nSYSTEMDUNITDIR ?= $(shell $(PKG_CONFIG) --variable=systemdsystemunitdir systemd 2>/dev/null || echo \"$(PREFIX)/lib/systemd/system\")\nRUNSTATEDIR ?= /var/run\nWITH_BASHCOMPLETION ?=\nWITH_WGQUICK ?=\nWITH_SYSTEMDUNITS ?=\n\nifeq ($(WITH_BASHCOMPLETION),)\nifneq ($(strip $(wildcard $(BASHCOMPDIR))),)\nWITH_BASHCOMPLETION := yes\nendif\nendif\nifeq ($(WITH_WGQUICK),)\nifneq ($(strip $(wildcard $(BINDIR)/bash)),)\nWITH_WGQUICK := yes\nendif\nifneq ($(strip $(wildcard $(DESTDIR)/bin/bash)),)\nWITH_WGQUICK := yes\nendif\nendif\nifeq ($(WITH_SYSTEMDUNITS),)\nifneq ($(strip $(wildcard $(SYSTEMDUNITDIR))),)\nWITH_SYSTEMDUNITS := yes\nendif\nendif\n\nPLATFORM ?= $(shell uname -s | tr '[:upper:]' '[:lower:]')\n\nCFLAGS ?= -O3\nifneq ($(wildcard uapi/$(PLATFORM)/.),)\nCFLAGS += -isystem uapi/$(PLATFORM)\nendif\nCFLAGS += -std=gnu99 -D_GNU_SOURCE\nCFLAGS += -Wall -Wextra\nCFLAGS += -MMD -MP\nCFLAGS += -DRUNSTATEDIR=\"\\\"$(RUNSTATEDIR)\\\"\"\nifeq ($(DEBUG),yes)\nCFLAGS += -g\nendif\nWIREGUARD_TOOLS_VERSION = $(patsubst v%,%,$(shell GIT_DIR=\"$(PWD)/../.git\" git describe --dirty 2>/dev/null))\nifneq ($(WIREGUARD_TOOLS_VERSION),)\nCFLAGS += -D'WIREGUARD_TOOLS_VERSION=\"$(WIREGUARD_TOOLS_VERSION)\"'\nendif\nifeq ($(PLATFORM),freebsd)\nLDLIBS += -lnv\nendif\nifeq ($(PLATFORM),haiku)\nLDLIBS += -lnetwork -lbsd\nendif\nifeq ($(PLATFORM),windows)\nCC := x86_64-w64-mingw32-clang\nWINDRES := $(shell $(CC) $(CFLAGS) -print-prog-name=windres 2>/dev/null)\nCFLAGS += -Iwincompat/include -include wincompat/compat.h -DWINVER=0x0A00 -D_WIN32_WINNT=0x0A00 -flto\nLDLIBS += -lws2_32 -lsetupapi -lole32 -ladvapi32 -lntdll -Lwincompat\nLDFLAGS += -flto -Wl,--dynamicbase -Wl,--nxcompat -Wl,--tsaware -mconsole\nLDFLAGS += -Wl,--major-os-version=10 -Wl,--minor-os-version=0 -Wl,--major-subsystem-version=10 -Wl,--minor-subsystem-version=0\n# The use of -Wl,/delayload: here implies we're using llvm-mingw\nLDFLAGS += -Wl,/delayload:ws2_32.dll -Wl,/delayload:setupapi.dll -Wl,/delayload:ole32.dll -Wl,/delayload:advapi32.dll\nVERSION := $(patsubst \"%\",%,$(filter \"%\",$(file < version.h)))\nwg: wincompat/libc.o wincompat/init.o wincompat/load_config.o wincompat/loader.o wincompat/resources.o\nwincompat/resources.o: wincompat/resources.rc wincompat/manifest.xml\n\t$(WINDRES) -DVERSION_STR=$(VERSION) -O coff -c 65001 -i $< -o $@\nendif\n\nifneq ($(V),1)\nBUILT_IN_LINK.o := $(LINK.o)\nLINK.o = @echo \"  LD      $@\";\nLINK.o += $(BUILT_IN_LINK.o)\nBUILT_IN_COMPILE.c := $(COMPILE.c)\nCOMPILE.c = @echo \"  CC      $@\";\nCOMPILE.c += $(BUILT_IN_COMPILE.c)\nBUILT_IN_RM := $(RM)\nRM := @a() { echo \"  CLEAN   $$@\"; $(BUILT_IN_RM) \"$$@\"; }; a\nWINDRES := @a() { echo \"  WINDRES $${@: -1}\"; $(WINDRES) \"$$@\"; }; a\nendif\n\nwg: $(sort $(patsubst %.c,%.o,$(wildcard *.c)))\n\nclean:\n\t$(RM) wg *.o *.d $(wildcard wincompat/*.o wincompat/*.lib wincompat/*.dll)\n\ninstall: wg\n\t@install -v -d \"$(DESTDIR)$(BINDIR)\" && install -v -m 0755 wg \"$(DESTDIR)$(BINDIR)/wg\"\n\t@install -v -d \"$(DESTDIR)$(MANDIR)/man8\" && install -v -m 0644 man/wg.8 \"$(DESTDIR)$(MANDIR)/man8/wg.8\"\n\t@[ \"$(WITH_BASHCOMPLETION)\" = \"yes\" ] || exit 0; \\\n\tinstall -v -d \"$(DESTDIR)$(BASHCOMPDIR)\" && install -v -m 0644 completion/wg.bash-completion \"$(DESTDIR)$(BASHCOMPDIR)/wg\"\n\t@[ \"$(WITH_WGQUICK)\" = \"yes\" ] || exit 0; \\\n\tinstall -v -m 0755 wg-quick/$(PLATFORM).bash \"$(DESTDIR)$(BINDIR)/wg-quick\" && install -v -m 0700 -d \"$(DESTDIR)$(SYSCONFDIR)/wireguard\"\n\t@[ \"$(WITH_WGQUICK)\" = \"yes\" ] || exit 0; \\\n\tinstall -v -m 0644 man/wg-quick.8 \"$(DESTDIR)$(MANDIR)/man8/wg-quick.8\"\n\t@[ \"$(WITH_WGQUICK)\" = \"yes\" -a \"$(WITH_BASHCOMPLETION)\" = \"yes\" ] || exit 0; \\\n\tinstall -v -m 0644 completion/wg-quick.bash-completion \"$(DESTDIR)$(BASHCOMPDIR)/wg-quick\"\n\t@[ \"$(WITH_WGQUICK)\" = \"yes\" -a \"$(WITH_SYSTEMDUNITS)\" = \"yes\" ] || exit 0; \\\n\tinstall -v -d \"$(DESTDIR)$(SYSTEMDUNITDIR)\" && install -v -m 0644 systemd/* \"$(DESTDIR)$(SYSTEMDUNITDIR)/\"\n\ncheck: clean\n\tscan-build --html-title=wireguard-tools -maxloop 100 --view --keep-going $(MAKE) wg\n\nall: wg\n.DEFAULT_GOAL: all\n.PHONY: clean install check\n\n-include *.d\n"
  },
  {
    "path": "src/completion/wg-quick.bash-completion",
    "content": "# SPDX-License-Identifier: GPL-2.0\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n\n_wg_quick_completion() {\n\tlocal p i a search_paths old_glob\n\tsearch_paths=( /etc/wireguard )\n\n\told_glob=\"$(shopt -p nullglob)\"\n\tshopt -s nullglob\n\n\t[[ $OSTYPE == *freebsd* || $OSTYPE == *darwin* ]] && search_paths+=( /usr/local/etc/wireguard )\n\n\tif [[ $COMP_CWORD -eq 1 ]]; then\n\t\tCOMPREPLY+=( $(compgen -W \"up down\" -- \"${COMP_WORDS[1]}\") )\n\telif [[ $COMP_CWORD -eq 2 ]]; then\n\t\tif [[ ${COMP_WORDS[1]} == up ]]; then\n\t\t\tfor p in \"${search_paths[@]}\"; do\n\t\t\t\tfor i in \"$p\"/*.conf; do\n\t\t\t\t\ti=\"${i##*/}\"; i=\"${i%.conf}\"\n\t\t\t\t\tmapfile -t a < <(compgen -W \"$i\" -- \"${COMP_WORDS[2]}\")\n\t\t\t\t\tCOMPREPLY+=( \"${a[@]}\" )\n\t\t\t\tdone\n\t\t\tdone\n\t\t\tmapfile -t a < <(compgen -f -X '!*.conf' -- \"${COMP_WORDS[2]}\")\n\t\t\tCOMPREPLY+=( \"${a[@]}\" )\n\t\t\tmapfile -t a < <(compgen -d -- \"${COMP_WORDS[2]}\")\n\t\t\tCOMPREPLY+=( \"${a[@]}\" )\n\t\telif [[ ${COMP_WORDS[1]} == down ]]; then\n\t\t\tif [[ $OSTYPE == *openbsd* || $OSTYPE == *darwin* ]]; then\n\t\t\t\tfor i in /var/run/wireguard/*.name; do\n\t\t\t\t\ti=\"${i##*/}\"; i=\"${i%.name}\"\n\t\t\t\t\tmapfile -t a < <(compgen -W \"$i\" -- \"${COMP_WORDS[2]}\")\n\t\t\t\t\tCOMPREPLY+=( \"${a[@]}\" )\n\t\t\t\tdone\n\t\t\telse\n\t\t\t\tCOMPREPLY+=( $(compgen -W \"$(wg show interfaces)\" -- \"${COMP_WORDS[2]}\") )\n\t\t\tfi\n\t\tfi\n\tfi\n\teval \"$old_glob\"\n}\n\ncomplete -o filenames -o nosort -F _wg_quick_completion wg-quick\n"
  },
  {
    "path": "src/completion/wg.bash-completion",
    "content": "# SPDX-License-Identifier: GPL-2.0\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n\n_wg_completion() {\n\tlocal a\n\n\tif [[ $COMP_CWORD -eq 1 ]]; then\n\t\tCOMPREPLY+=( $(compgen -W \"help show showconf set setconf addconf syncconf genkey genpsk pubkey\" -- \"${COMP_WORDS[1]}\") )\n\t\treturn\n\tfi\n\tcase \"${COMP_WORDS[1]}\" in\n\t\tgenkey|genpsk|pubkey|help) return; ;;\n\t\tshow|showconf|set|setconf|addconf|syncconf) ;;\n\t\t*) return;\n\tesac\n\n\tif [[ $COMP_CWORD -eq 2 ]]; then\n\t\tlocal extra\n\t\t[[ ${COMP_WORDS[1]} == show ]] && extra=\" all interfaces\"\n\t\tCOMPREPLY+=( $(compgen -W \"$(wg show interfaces 2>/dev/null)$extra\" -- \"${COMP_WORDS[2]}\") )\n\t\treturn\n\tfi\n\n\tif [[ $COMP_CWORD -eq 3 && ${COMP_WORDS[1]} == show && ${COMP_WORDS[2]} != interfaces ]]; then\n\t\tCOMPREPLY+=( $(compgen -W \"public-key private-key listen-port peers preshared-keys endpoints allowed-ips fwmark latest-handshakes persistent-keepalive transfer dump\" -- \"${COMP_WORDS[3]}\") )\n\t\treturn\n\tfi\n\n\tif [[ $COMP_CWORD -eq 3 && ( ${COMP_WORDS[1]} == setconf || ${COMP_WORDS[1]} == addconf || ${COMP_WORDS[1]} == syncconf) ]]; then\n\t\tcompopt -o filenames\n\t\tmapfile -t a < <(compgen -f -- \"${COMP_WORDS[3]}\")\n\t\tCOMPREPLY+=( \"${a[@]}\" )\n\t\treturn\n\tfi\n\n\t[[ ${COMP_WORDS[1]} == set ]] || return\n\n\tlocal has_listen_port=0 has_fwmark=0 has_private_key=0 has_preshared_key=0 has_peer=0 has_remove=0 has_endpoint=0 has_persistent_keepalive=0 has_allowed_ips=0 words=() i j\n\tfor ((i=3;i<COMP_CWORD;i+=2)); do\n\t\t[[ ${COMP_WORDS[i]} == listen-port ]] && has_listen_port=1\n\t\t[[ ${COMP_WORDS[i]} == fwmark ]] && has_fwmark=1\n\t\t[[ ${COMP_WORDS[i]} == private-key ]] && has_private_key=1\n\t\t[[ ${COMP_WORDS[i]} == peer ]] && { has_peer=$i; break; }\n\tdone\n\tif [[ $has_peer -eq 0 ]]; then\n\t\tif ((COMP_CWORD % 2 != 0)); then\n\t\t\t[[ $has_listen_port -eq 1 ]] || words+=( listen-port )\n\t\t\t[[ $has_fwmark -eq 1 ]] || words+=( fwmark )\n\t\t\t[[ $has_private_key -eq 1 ]] || words+=( private-key )\n\t\t\twords+=( peer )\n\t\t\tCOMPREPLY+=( $(compgen -W \"${words[*]}\" -- \"${COMP_WORDS[COMP_CWORD]}\") )\n\t\telif [[ ${COMP_WORDS[COMP_CWORD-1]} == *-key ]]; then\n\t\t\tcompopt -o filenames\n\t\t\tmapfile -t a < <(compgen -f -- \"${COMP_WORDS[COMP_CWORD]}\")\n\t\t\tCOMPREPLY+=( \"${a[@]}\" )\n\t\tfi\n\t\treturn\n\tfi\n\n\tif [[ ${COMP_WORDS[COMP_CWORD-1]} == peer ]]; then\n\t\tCOMPREPLY+=( $(compgen -W \"$(wg show \"${COMP_WORDS[2]}\" peers 2>/dev/null)\" -- \"${COMP_WORDS[COMP_CWORD]}\") )\n\t\treturn\n\tfi\n\n\tfor ((i=has_peer;i<COMP_CWORD;i++)); do\n\t\tj=i\n\t\tif [[ ${COMP_WORDS[i]} == peer ]]; then\n\t\t\thas_remove=0\n\t\t\thas_endpoint=0\n\t\t\thas_persistent_keepalive=0\n\t\t\thas_allowed_ips=0\n\t\t\thas_preshared_key=0\n\t\t\t[[ ${COMP_WORDS[i+2]} == = ]] && ((i+=2)) || ((i++))\n\t\t\tcontinue\n\t\tfi\n\t\t[[ ${COMP_WORDS[i]} == remove ]] && has_remove=1\n\t\t[[ ${COMP_WORDS[i]} == endpoint ]] && has_endpoint=1\n\t\t[[ ${COMP_WORDS[i]} == persistent-keepalive ]] && has_persistent_keepalive=1\n\t\t[[ ${COMP_WORDS[i]} == allowed-ips ]] && has_allowed_ips=1\n\t\t[[ ${COMP_WORDS[i]} == preshared-key ]] && has_preshared_key=1\n\n\t\t[[ ${COMP_WORDS[i]} == remove ]] || ((i++))\n\tdone\n\n\t((COMP_CWORD == j)) || return\n\n\tif [[ $has_remove -ne 1 ]]; then\n\t\t[[ $has_preshared_key -eq 1 ]] || words+=( preshared-key )\n\t\t[[ $has_endpoint -eq 1 ]] || words+=( endpoint )\n\t\t[[ $has_allowed_ips -eq 1 ]] || words+=( allowed-ips )\n\t\t[[ $has_persistent_keepalive -eq 1 ]] || words+=( persistent-keepalive )\n\t\twords+=( remove )\n\tfi\n\twords+=( peer )\n\n\tCOMPREPLY+=( $(compgen -W \"${words[*]}\" -- \"${COMP_WORDS[COMP_CWORD]}\") )\n}\n\ncomplete -o nosort -F _wg_completion wg\n"
  },
  {
    "path": "src/config.c",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <arpa/inet.h>\n#include <limits.h>\n#include <netdb.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <errno.h>\n\n#include \"config.h\"\n#include \"containers.h\"\n#include \"ipc.h\"\n#include \"encoding.h\"\n#include \"ctype.h\"\n\n#define COMMENT_CHAR '#'\n\nstatic const char *get_value(const char *line, const char *key)\n{\n\tsize_t linelen = strlen(line);\n\tsize_t keylen = strlen(key);\n\n\tif (keylen >= linelen)\n\t\treturn NULL;\n\n\tif (strncasecmp(line, key, keylen))\n\t\treturn NULL;\n\n\treturn line + keylen;\n}\n\nstatic inline bool parse_port(uint16_t *port, uint32_t *flags, const char *value)\n{\n\tint ret;\n\tstruct addrinfo *resolved;\n\tstruct addrinfo hints = {\n\t\t.ai_family = AF_UNSPEC,\n\t\t.ai_socktype = SOCK_DGRAM,\n\t\t.ai_protocol = IPPROTO_UDP,\n\t\t.ai_flags = AI_PASSIVE\n\t};\n\n\tif (!strlen(value)) {\n\t\tfprintf(stderr, \"Unable to parse empty port\\n\");\n\t\treturn false;\n\t}\n\n\tret = getaddrinfo(NULL, value, &hints, &resolved);\n\tif (ret) {\n\t\tfprintf(stderr, \"%s: `%s'\\n\", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);\n\t\treturn false;\n\t}\n\n\tret = -1;\n\tif (resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) {\n\t\t*port = ntohs(((struct sockaddr_in *)resolved->ai_addr)->sin_port);\n\t\tret = 0;\n\t} else if (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)) {\n\t\t*port = ntohs(((struct sockaddr_in6 *)resolved->ai_addr)->sin6_port);\n\t\tret = 0;\n\t} else\n\t\tfprintf(stderr, \"Neither IPv4 nor IPv6 address found: `%s'\\n\", value);\n\n\tfreeaddrinfo(resolved);\n\tif (!ret)\n\t\t*flags |= WGDEVICE_HAS_LISTEN_PORT;\n\treturn ret == 0;\n}\n\nstatic inline bool parse_fwmark(uint32_t *fwmark, uint32_t *flags, const char *value)\n{\n\tunsigned long ret;\n\tchar *end;\n\tint base = 10;\n\n\tif (!strcasecmp(value, \"off\")) {\n\t\t*fwmark = 0;\n\t\t*flags |= WGDEVICE_HAS_FWMARK;\n\t\treturn true;\n\t}\n\n\tif (!char_is_digit(value[0]))\n\t\tgoto err;\n\n\tif (strlen(value) > 2 && value[0] == '0' && value[1] == 'x')\n\t\tbase = 16;\n\n\tret = strtoul(value, &end, base);\n\tif (*end || ret > UINT32_MAX)\n\t\tgoto err;\n\n\t*fwmark = ret;\n\t*flags |= WGDEVICE_HAS_FWMARK;\n\treturn true;\nerr:\n\tfprintf(stderr, \"Fwmark is neither 0/off nor 0-0xffffffff: `%s'\\n\", value);\n\treturn false;\n}\n\nstatic inline bool parse_key(uint8_t key[static WG_KEY_LEN], const char *value)\n{\n\tif (!key_from_base64(key, value)) {\n\t\tfprintf(stderr, \"Key is not the correct length or format: `%s'\\n\", value);\n\t\tmemset(key, 0, WG_KEY_LEN);\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic bool parse_keyfile(uint8_t key[static WG_KEY_LEN], const char *path)\n{\n\tFILE *f;\n\tint c;\n\tchar dst[WG_KEY_LEN_BASE64];\n\tbool ret = false;\n\n\tf = fopen(path, \"r\");\n\tif (!f) {\n\t\tperror(\"fopen\");\n\t\treturn false;\n\t}\n\n\tif (fread(dst, WG_KEY_LEN_BASE64 - 1, 1, f) != 1) {\n\t\t/* If we're at the end and we didn't read anything, we're /dev/null or an empty file. */\n\t\tif (!ferror(f) && feof(f) && !ftell(f)) {\n\t\t\tmemset(key, 0, WG_KEY_LEN);\n\t\t\tret = true;\n\t\t\tgoto out;\n\t\t}\n\n\t\tfprintf(stderr, \"Invalid length key in key file\\n\");\n\t\tgoto out;\n\t}\n\tdst[WG_KEY_LEN_BASE64 - 1] = '\\0';\n\n\twhile ((c = getc(f)) != EOF) {\n\t\tif (!char_is_space(c)) {\n\t\t\tfprintf(stderr, \"Found trailing character in key file: `%c'\\n\", c);\n\t\t\tgoto out;\n\t\t}\n\t}\n\tif (ferror(f) && errno) {\n\t\tperror(\"getc\");\n\t\tgoto out;\n\t}\n\tret = parse_key(key, dst);\n\nout:\n\tfclose(f);\n\treturn ret;\n}\n\nstatic inline bool parse_ip(struct wgallowedip *allowedip, const char *value)\n{\n\tallowedip->family = AF_UNSPEC;\n\tif (strchr(value, ':')) {\n\t\tif (inet_pton(AF_INET6, value, &allowedip->ip6) == 1)\n\t\t\tallowedip->family = AF_INET6;\n\t} else {\n\t\tif (inet_pton(AF_INET, value, &allowedip->ip4) == 1)\n\t\t\tallowedip->family = AF_INET;\n\t}\n\tif (allowedip->family == AF_UNSPEC) {\n\t\tfprintf(stderr, \"Unable to parse IP address: `%s'\\n\", value);\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nstatic inline int parse_dns_retries(void)\n{\n\tunsigned long ret;\n\tchar *retries = getenv(\"WG_ENDPOINT_RESOLUTION_RETRIES\"), *end;\n\n\tif (!retries)\n\t\treturn 15;\n\tif (!strcmp(retries, \"infinity\"))\n\t\treturn -1;\n\n\tret = strtoul(retries, &end, 10);\n\tif (*end || ret > INT_MAX) {\n\t\tfprintf(stderr, \"Unable to parse WG_ENDPOINT_RESOLUTION_RETRIES: `%s'\\n\", retries);\n\t\texit(1);\n\t}\n\treturn (int)ret;\n}\n\nstatic inline bool parse_endpoint(struct sockaddr *endpoint, const char *value)\n{\n\tchar *mutable = strdup(value);\n\tchar *begin, *end;\n\tint ret, retries = parse_dns_retries();\n\tstruct addrinfo *resolved;\n\tstruct addrinfo hints = {\n\t\t.ai_family = AF_UNSPEC,\n\t\t.ai_socktype = SOCK_DGRAM,\n\t\t.ai_protocol = IPPROTO_UDP\n\t};\n\tif (!mutable) {\n\t\tperror(\"strdup\");\n\t\treturn false;\n\t}\n\tif (!strlen(value)) {\n\t\tfree(mutable);\n\t\tfprintf(stderr, \"Unable to parse empty endpoint\\n\");\n\t\treturn false;\n\t}\n\tif (mutable[0] == '[') {\n\t\tbegin = &mutable[1];\n\t\tend = strchr(mutable, ']');\n\t\tif (!end) {\n\t\t\tfree(mutable);\n\t\t\tfprintf(stderr, \"Unable to find matching brace of endpoint: `%s'\\n\", value);\n\t\t\treturn false;\n\t\t}\n\t\t*end++ = '\\0';\n\t\tif (*end++ != ':' || !*end) {\n\t\t\tfree(mutable);\n\t\t\tfprintf(stderr, \"Unable to find port of endpoint: `%s'\\n\", value);\n\t\t\treturn false;\n\t\t}\n\t} else {\n\t\tbegin = mutable;\n\t\tend = strrchr(mutable, ':');\n\t\tif (!end || !*(end + 1)) {\n\t\t\tfree(mutable);\n\t\t\tfprintf(stderr, \"Unable to find port of endpoint: `%s'\\n\", value);\n\t\t\treturn false;\n\t\t}\n\t\t*end++ = '\\0';\n\t}\n\n\t#define min(a, b) ((a) < (b) ? (a) : (b))\n\tfor (unsigned int timeout = 1000000;; timeout = min(20000000, timeout * 6 / 5)) {\n\t\tret = getaddrinfo(begin, end, &hints, &resolved);\n\t\tif (!ret)\n\t\t\tbreak;\n\t\t/* The set of return codes that are \"permanent failures\". All other possibilities are potentially transient.\n\t\t *\n\t\t * This is according to https://sourceware.org/glibc/wiki/NameResolver which states:\n\t\t *\t\"From the perspective of the application that calls getaddrinfo() it perhaps\n\t\t *\t doesn't matter that much since EAI_FAIL, EAI_NONAME and EAI_NODATA are all\n\t\t *\t permanent failure codes and the causes are all permanent failures in the\n\t\t *\t sense that there is no point in retrying later.\"\n\t\t *\n\t\t * So this is what we do, except FreeBSD removed EAI_NODATA some time ago, so that's conditional.\n\t\t */\n\t\tif (ret == EAI_NONAME || ret == EAI_FAIL ||\n\t\t\t#ifdef EAI_NODATA\n\t\t\t\tret == EAI_NODATA ||\n\t\t\t#endif\n\t\t\t\t(retries >= 0 && !retries--)) {\n\t\t\tfree(mutable);\n\t\t\tfprintf(stderr, \"%s: `%s'\\n\", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value);\n\t\t\treturn false;\n\t\t}\n\t\tfprintf(stderr, \"%s: `%s'. Trying again in %.2f seconds...\\n\", ret == EAI_SYSTEM ? strerror(errno) : gai_strerror(ret), value, timeout / 1000000.0);\n\t\tusleep(timeout);\n\t}\n\n\tif ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||\n\t    (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))\n\t\tmemcpy(endpoint, resolved->ai_addr, resolved->ai_addrlen);\n\telse {\n\t\tfreeaddrinfo(resolved);\n\t\tfree(mutable);\n\t\tfprintf(stderr, \"Neither IPv4 nor IPv6 address found: `%s'\\n\", value);\n\t\treturn false;\n\t}\n\tfreeaddrinfo(resolved);\n\tfree(mutable);\n\treturn true;\n}\n\nstatic inline bool parse_persistent_keepalive(uint16_t *interval, uint32_t *flags, const char *value)\n{\n\tunsigned long ret;\n\tchar *end;\n\n\tif (!strcasecmp(value, \"off\")) {\n\t\t*interval = 0;\n\t\t*flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;\n\t\treturn true;\n\t}\n\n\tif (!char_is_digit(value[0]))\n\t\tgoto err;\n\n\tret = strtoul(value, &end, 10);\n\tif (*end || ret > 65535)\n\t\tgoto err;\n\n\t*interval = (uint16_t)ret;\n\t*flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;\n\treturn true;\nerr:\n\tfprintf(stderr, \"Persistent keepalive interval is neither 0/off nor 1-65535: `%s'\\n\", value);\n\treturn false;\n}\n\nstatic bool validate_netmask(struct wgallowedip *allowedip)\n{\n\tuint32_t *ip;\n\tint last;\n\n\tswitch (allowedip->family) {\n\t\tcase AF_INET:\n\t\t\tlast = 0;\n\t\t\tip = (uint32_t *)&allowedip->ip4;\n\t\t\tbreak;\n\t\tcase AF_INET6:\n\t\t\tlast = 3;\n\t\t\tip = (uint32_t *)&allowedip->ip6;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn true; /* We don't know how to validate it, so say 'okay'. */\n\t}\n\n\tfor (int i = last; i >= 0; --i) {\n\t\tuint32_t mask = ~0;\n\n\t\tif (allowedip->cidr >= 32 * (i + 1))\n\t\t\tbreak;\n\t\tif (allowedip->cidr > 32 * i)\n\t\t\tmask >>= (allowedip->cidr - 32 * i);\n\t\tif (ntohl(ip[i]) & mask)\n\t\t\treturn false;\n\t}\n\n\treturn true;\n}\n\nstatic inline void parse_ip_prefix(struct wgpeer *peer, uint32_t *flags, char **mask)\n{\n\t/* If the IP is prefixed with either '+' or '-' consider this an\n\t * incremental change. Disable WGPEER_REPLACE_ALLOWEDIPS. */\n\tswitch ((*mask)[0]) {\n\tcase '-':\n\t\t*flags |= WGALLOWEDIP_REMOVE_ME;\n\t\t/* fall through */\n\tcase '+':\n\t\tpeer->flags &= ~WGPEER_REPLACE_ALLOWEDIPS;\n\t\t++(*mask);\n\t}\n}\n\nstatic inline bool parse_allowedips(struct wgpeer *peer, struct wgallowedip **last_allowedip, const char *value)\n{\n\tstruct wgallowedip *allowedip = *last_allowedip, *new_allowedip;\n\tchar *mask, *mutable = strdup(value), *sep, *saved_entry;\n\n\tif (!mutable) {\n\t\tperror(\"strdup\");\n\t\treturn false;\n\t}\n\tpeer->flags |= WGPEER_REPLACE_ALLOWEDIPS;\n\tif (!strlen(value)) {\n\t\tfree(mutable);\n\t\treturn true;\n\t}\n\tsep = mutable;\n\twhile ((mask = strsep(&sep, \",\"))) {\n\t\tuint32_t flags = 0;\n\t\tunsigned long cidr;\n\t\tchar *end, *ip;\n\n\t\tparse_ip_prefix(peer, &flags, &mask);\n\n\t\tsaved_entry = strdup(mask);\n\t\tif (!saved_entry) {\n\t\t\tperror(\"strdup\");\n\t\t\tfree(mutable);\n\t\t\treturn false;\n\t\t}\n\t\tip = strsep(&mask, \"/\");\n\n\t\tnew_allowedip = calloc(1, sizeof(*new_allowedip));\n\t\tif (!new_allowedip) {\n\t\t\tperror(\"calloc\");\n\t\t\tfree(saved_entry);\n\t\t\tfree(mutable);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (!parse_ip(new_allowedip, ip)) {\n\t\t\tfree(new_allowedip);\n\t\t\tfree(saved_entry);\n\t\t\tfree(mutable);\n\t\t\treturn false;\n\t\t}\n\n\t\tif (mask) {\n\t\t\tif (!char_is_digit(mask[0]))\n\t\t\t\tgoto err;\n\t\t\tcidr = strtoul(mask, &end, 10);\n\t\t\tif (*end || (cidr > 32 && new_allowedip->family == AF_INET) || (cidr > 128 && new_allowedip->family == AF_INET6))\n\t\t\t\tgoto err;\n\t\t} else if (new_allowedip->family == AF_INET)\n\t\t\tcidr = 32;\n\t\telse if (new_allowedip->family == AF_INET6)\n\t\t\tcidr = 128;\n\t\telse\n\t\t\tgoto err;\n\t\tnew_allowedip->cidr = cidr;\n\t\tnew_allowedip->flags = flags;\n\n\t\tif (!validate_netmask(new_allowedip))\n\t\t\tfprintf(stderr, \"Warning: AllowedIP has nonzero host part: %s/%s\\n\", ip, mask);\n\n\t\tif (allowedip)\n\t\t\tallowedip->next_allowedip = new_allowedip;\n\t\telse\n\t\t\tpeer->first_allowedip = new_allowedip;\n\t\tallowedip = new_allowedip;\n\t\tfree(saved_entry);\n\t}\n\tfree(mutable);\n\t*last_allowedip = allowedip;\n\treturn true;\n\nerr:\n\tfree(new_allowedip);\n\tfree(mutable);\n\tfprintf(stderr, \"AllowedIP is not in the correct format: `%s'\\n\", saved_entry);\n\tfree(saved_entry);\n\treturn false;\n}\n\nstatic bool process_line(struct config_ctx *ctx, const char *line)\n{\n\tconst char *value;\n\tbool ret = true;\n\n\tif (!strcasecmp(line, \"[Interface]\")) {\n\t\tctx->is_peer_section = false;\n\t\tctx->is_device_section = true;\n\t\treturn true;\n\t}\n\tif (!strcasecmp(line, \"[Peer]\")) {\n\t\tstruct wgpeer *new_peer = calloc(1, sizeof(struct wgpeer));\n\n\t\tif (!new_peer) {\n\t\t\tperror(\"calloc\");\n\t\t\treturn false;\n\t\t}\n\t\tctx->last_allowedip = NULL;\n\t\tif (ctx->last_peer)\n\t\t\tctx->last_peer->next_peer = new_peer;\n\t\telse\n\t\t\tctx->device->first_peer = new_peer;\n\t\tctx->last_peer = new_peer;\n\t\tctx->is_peer_section = true;\n\t\tctx->is_device_section = false;\n\t\tctx->last_peer->flags |= WGPEER_REPLACE_ALLOWEDIPS;\n\t\treturn true;\n\t}\n\n#define key_match(key) (value = get_value(line, key \"=\"))\n\n\tif (ctx->is_device_section) {\n\t\tif (key_match(\"ListenPort\"))\n\t\t\tret = parse_port(&ctx->device->listen_port, &ctx->device->flags, value);\n\t\telse if (key_match(\"FwMark\"))\n\t\t\tret = parse_fwmark(&ctx->device->fwmark, &ctx->device->flags, value);\n\t\telse if (key_match(\"PrivateKey\")) {\n\t\t\tret = parse_key(ctx->device->private_key, value);\n\t\t\tif (ret)\n\t\t\t\tctx->device->flags |= WGDEVICE_HAS_PRIVATE_KEY;\n\t\t} else\n\t\t\tgoto error;\n\t} else if (ctx->is_peer_section) {\n\t\tif (key_match(\"Endpoint\"))\n\t\t\tret = parse_endpoint(&ctx->last_peer->endpoint.addr, value);\n\t\telse if (key_match(\"PublicKey\")) {\n\t\t\tret = parse_key(ctx->last_peer->public_key, value);\n\t\t\tif (ret)\n\t\t\t\tctx->last_peer->flags |= WGPEER_HAS_PUBLIC_KEY;\n\t\t} else if (key_match(\"AllowedIPs\"))\n\t\t\tret = parse_allowedips(ctx->last_peer, &ctx->last_allowedip, value);\n\t\telse if (key_match(\"PersistentKeepalive\"))\n\t\t\tret = parse_persistent_keepalive(&ctx->last_peer->persistent_keepalive_interval, &ctx->last_peer->flags, value);\n\t\telse if (key_match(\"PresharedKey\")) {\n\t\t\tret = parse_key(ctx->last_peer->preshared_key, value);\n\t\t\tif (ret)\n\t\t\t\tctx->last_peer->flags |= WGPEER_HAS_PRESHARED_KEY;\n\t\t} else\n\t\t\tgoto error;\n\t} else\n\t\tgoto error;\n\treturn ret;\n\n#undef key_match\n\nerror:\n\tfprintf(stderr, \"Line unrecognized: `%s'\\n\", line);\n\treturn false;\n}\n\nbool config_read_line(struct config_ctx *ctx, const char *input)\n{\n\tsize_t len, cleaned_len = 0;\n\tconst char *comment;\n\tbool ret = true;\n\tchar *line;\n\n\t/* This is what strchrnul is for, but that isn't portable. */\n\tcomment = strchr(input, COMMENT_CHAR);\n\tif (comment)\n\t\tlen = comment - input;\n\telse\n\t\tlen = strlen(input);\n\n\tline = calloc(len + 1, sizeof(char));\n\tif (!line) {\n\t\tperror(\"calloc\");\n\t\tret = false;\n\t\tgoto out;\n\t}\n\n\tfor (size_t i = 0; i < len; ++i) {\n\t\tif (!char_is_space(input[i]))\n\t\t\tline[cleaned_len++] = input[i];\n\t}\n\tif (!cleaned_len)\n\t\tgoto out;\n\tret = process_line(ctx, line);\nout:\n\tfree(line);\n\tif (!ret)\n\t\tfree_wgdevice(ctx->device);\n\treturn ret;\n}\n\nbool config_read_init(struct config_ctx *ctx, bool append)\n{\n\tmemset(ctx, 0, sizeof(*ctx));\n\tctx->device = calloc(1, sizeof(*ctx->device));\n\tif (!ctx->device) {\n\t\tperror(\"calloc\");\n\t\treturn false;\n\t}\n\tif (!append)\n\t\tctx->device->flags |= WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_FWMARK | WGDEVICE_HAS_LISTEN_PORT;\n\treturn true;\n}\n\nstruct wgdevice *config_read_finish(struct config_ctx *ctx)\n{\n\tstruct wgpeer *peer;\n\n\tfor_each_wgpeer(ctx->device, peer) {\n\t\tif (!(peer->flags & WGPEER_HAS_PUBLIC_KEY)) {\n\t\t\tfprintf(stderr, \"A peer is missing a public key\\n\");\n\t\t\tgoto err;\n\t\t}\n\t}\n\treturn ctx->device;\nerr:\n\tfree_wgdevice(ctx->device);\n\treturn NULL;\n}\n\nstatic char *strip_spaces(const char *in)\n{\n\tchar *out;\n\tsize_t t, l, i;\n\n\tt = strlen(in);\n\tout = calloc(t + 1, sizeof(char));\n\tif (!out) {\n\t\tperror(\"calloc\");\n\t\treturn NULL;\n\t}\n\tfor (i = 0, l = 0; i < t; ++i) {\n\t\tif (!char_is_space(in[i]))\n\t\t\tout[l++] = in[i];\n\t}\n\treturn out;\n}\n\nstruct wgdevice *config_read_cmd(const char *argv[], int argc)\n{\n\tstruct wgdevice *device = calloc(1, sizeof(*device));\n\tstruct wgpeer *peer = NULL;\n\tstruct wgallowedip *allowedip = NULL;\n\n\tif (!device) {\n\t\tperror(\"calloc\");\n\t\treturn false;\n\t}\n\twhile (argc > 0) {\n\t\tif (!strcmp(argv[0], \"listen-port\") && argc >= 2 && !peer) {\n\t\t\tif (!parse_port(&device->listen_port, &device->flags, argv[1]))\n\t\t\t\tgoto error;\n\t\t\targv += 2;\n\t\t\targc -= 2;\n\t\t} else if (!strcmp(argv[0], \"fwmark\") && argc >= 2 && !peer) {\n\t\t\tif (!parse_fwmark(&device->fwmark, &device->flags, argv[1]))\n\t\t\t\tgoto error;\n\t\t\targv += 2;\n\t\t\targc -= 2;\n\t\t} else if (!strcmp(argv[0], \"private-key\") && argc >= 2 && !peer) {\n\t\t\tif (!parse_keyfile(device->private_key, argv[1]))\n\t\t\t\tgoto error;\n\t\t\tdevice->flags |= WGDEVICE_HAS_PRIVATE_KEY;\n\t\t\targv += 2;\n\t\t\targc -= 2;\n\t\t} else if (!strcmp(argv[0], \"peer\") && argc >= 2) {\n\t\t\tstruct wgpeer *new_peer = calloc(1, sizeof(*new_peer));\n\n\t\t\tallowedip = NULL;\n\t\t\tif (!new_peer) {\n\t\t\t\tperror(\"calloc\");\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t\tif (peer)\n\t\t\t\tpeer->next_peer = new_peer;\n\t\t\telse\n\t\t\t\tdevice->first_peer = new_peer;\n\t\t\tpeer = new_peer;\n\t\t\tif (!parse_key(peer->public_key, argv[1]))\n\t\t\t\tgoto error;\n\t\t\tpeer->flags |= WGPEER_HAS_PUBLIC_KEY;\n\t\t\targv += 2;\n\t\t\targc -= 2;\n\t\t} else if (!strcmp(argv[0], \"remove\") && argc >= 1 && peer) {\n\t\t\tpeer->flags |= WGPEER_REMOVE_ME;\n\t\t\targv += 1;\n\t\t\targc -= 1;\n\t\t} else if (!strcmp(argv[0], \"endpoint\") && argc >= 2 && peer) {\n\t\t\tif (!parse_endpoint(&peer->endpoint.addr, argv[1]))\n\t\t\t\tgoto error;\n\t\t\targv += 2;\n\t\t\targc -= 2;\n\t\t} else if (!strcmp(argv[0], \"allowed-ips\") && argc >= 2 && peer) {\n\t\t\tchar *line = strip_spaces(argv[1]);\n\n\t\t\tif (!line)\n\t\t\t\tgoto error;\n\t\t\tif (!parse_allowedips(peer, &allowedip, line)) {\n\t\t\t\tfree(line);\n\t\t\t\tgoto error;\n\t\t\t}\n\t\t\tfree(line);\n\t\t\targv += 2;\n\t\t\targc -= 2;\n\t\t} else if (!strcmp(argv[0], \"persistent-keepalive\") && argc >= 2 && peer) {\n\t\t\tif (!parse_persistent_keepalive(&peer->persistent_keepalive_interval, &peer->flags, argv[1]))\n\t\t\t\tgoto error;\n\t\t\targv += 2;\n\t\t\targc -= 2;\n\t\t} else if (!strcmp(argv[0], \"preshared-key\") && argc >= 2 && peer) {\n\t\t\tif (!parse_keyfile(peer->preshared_key, argv[1]))\n\t\t\t\tgoto error;\n\t\t\tpeer->flags |= WGPEER_HAS_PRESHARED_KEY;\n\t\t\targv += 2;\n\t\t\targc -= 2;\n\t\t} else {\n\t\t\tfprintf(stderr, \"Invalid argument: %s\\n\", argv[0]);\n\t\t\tgoto error;\n\t\t}\n\t}\n\treturn device;\nerror:\n\tfree_wgdevice(device);\n\treturn false;\n}\n"
  },
  {
    "path": "src/config.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 OR MIT */\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#ifndef CONFIG_H\n#define CONFIG_H\n\n#include <stdbool.h>\n\nstruct wgdevice;\nstruct wgpeer;\nstruct wgallowedip;\n\nstruct config_ctx {\n\tstruct wgdevice *device;\n\tstruct wgpeer *last_peer;\n\tstruct wgallowedip *last_allowedip;\n\tbool is_peer_section, is_device_section;\n};\n\nstruct wgdevice *config_read_cmd(const char *argv[], int argc);\nbool config_read_init(struct config_ctx *ctx, bool append);\nbool config_read_line(struct config_ctx *ctx, const char *line);\nstruct wgdevice *config_read_finish(struct config_ctx *ctx);\n\n#endif\n"
  },
  {
    "path": "src/containers.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 OR MIT */\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#ifndef CONTAINERS_H\n#define CONTAINERS_H\n\n#include <stdint.h>\n#include <stdlib.h>\n#include <time.h>\n#include <sys/socket.h>\n#include <net/if.h>\n#include <netinet/in.h>\n#if defined(__linux__)\n#include <linux/wireguard.h>\n#elif defined(__OpenBSD__)\n#include <net/if_wg.h>\n#endif\n\n#ifndef WG_KEY_LEN\n#define WG_KEY_LEN 32\n#endif\n\n/* Cross platform __kernel_timespec */\nstruct timespec64 {\n\tint64_t tv_sec;\n\tint64_t tv_nsec;\n};\n\nenum {\n\tWGALLOWEDIP_REMOVE_ME = 1U << 0,\n};\n\nstruct wgallowedip {\n\tuint16_t family;\n\tunion {\n\t\tstruct in_addr ip4;\n\t\tstruct in6_addr ip6;\n\t};\n\tuint8_t cidr;\n\tuint32_t flags;\n\tstruct wgallowedip *next_allowedip;\n};\n\nenum {\n\tWGPEER_REMOVE_ME = 1U << 0,\n\tWGPEER_REPLACE_ALLOWEDIPS = 1U << 1,\n\tWGPEER_HAS_PUBLIC_KEY = 1U << 2,\n\tWGPEER_HAS_PRESHARED_KEY = 1U << 3,\n\tWGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL = 1U << 4\n};\n\nstruct wgpeer {\n\tuint32_t flags;\n\n\tuint8_t public_key[WG_KEY_LEN];\n\tuint8_t preshared_key[WG_KEY_LEN];\n\n\tunion {\n\t\tstruct sockaddr addr;\n\t\tstruct sockaddr_in addr4;\n\t\tstruct sockaddr_in6 addr6;\n\t} endpoint;\n\n\tstruct timespec64 last_handshake_time;\n\tuint64_t rx_bytes, tx_bytes;\n\tuint16_t persistent_keepalive_interval;\n\n\tstruct wgallowedip *first_allowedip, *last_allowedip;\n\tstruct wgpeer *next_peer;\n};\n\nenum {\n\tWGDEVICE_REPLACE_PEERS = 1U << 0,\n\tWGDEVICE_HAS_PRIVATE_KEY = 1U << 1,\n\tWGDEVICE_HAS_PUBLIC_KEY = 1U << 2,\n\tWGDEVICE_HAS_LISTEN_PORT = 1U << 3,\n\tWGDEVICE_HAS_FWMARK = 1U << 4\n};\n\nstruct wgdevice {\n\tchar name[IFNAMSIZ];\n\tuint32_t ifindex;\n\n\tuint32_t flags;\n\n\tuint8_t public_key[WG_KEY_LEN];\n\tuint8_t private_key[WG_KEY_LEN];\n\n\tuint32_t fwmark;\n\tuint16_t listen_port;\n\n\tstruct wgpeer *first_peer, *last_peer;\n};\n\n#define for_each_wgpeer(__dev, __peer) for ((__peer) = (__dev)->first_peer; (__peer); (__peer) = (__peer)->next_peer)\n#define for_each_wgallowedip(__peer, __allowedip) for ((__allowedip) = (__peer)->first_allowedip; (__allowedip); (__allowedip) = (__allowedip)->next_allowedip)\n\nstatic inline void free_wgdevice(struct wgdevice *dev)\n{\n\tif (!dev)\n\t\treturn;\n\tfor (struct wgpeer *peer = dev->first_peer, *np = peer ? peer->next_peer : NULL; peer; peer = np, np = peer ? peer->next_peer : NULL) {\n\t\tfor (struct wgallowedip *allowedip = peer->first_allowedip, *na = allowedip ? allowedip->next_allowedip : NULL; allowedip; allowedip = na, na = allowedip ? allowedip->next_allowedip : NULL)\n\t\t\tfree(allowedip);\n\t\tfree(peer);\n\t}\n\tfree(dev);\n}\n\n#endif\n"
  },
  {
    "path": "src/ctype.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 OR MIT */\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n *\n * Specialized constant-time ctype.h reimplementations that aren't locale-specific.\n */\n\n#ifndef CTYPE_H\n#define CTYPE_H\n\n#include <stdbool.h>\n\nstatic inline bool char_is_space(int c)\n{\n\tunsigned char d = c - 9;\n\treturn (0x80001FU >> (d & 31)) & (1U >> (d >> 5));\n}\n\nstatic inline bool char_is_digit(int c)\n{\n\treturn (unsigned int)(('0' - 1 - c) & (c - ('9' + 1))) >> (sizeof(c) * 8 - 1);\n}\n\nstatic inline bool char_is_alpha(int c)\n{\n\treturn (unsigned int)(('a' - 1 - (c | 32)) & ((c | 32) - ('z' + 1))) >> (sizeof(c) * 8 - 1);\n}\n\n#endif\n"
  },
  {
    "path": "src/curve25519-fiat32.h",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2015-2016 The fiat-crypto Authors.\n * Copyright (C) 2018-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n *\n * This is a machine-generated formally verified implementation of Curve25519\n * ECDH from: <https://github.com/mit-plv/fiat-crypto>. Though originally\n * machine generated, it has been tweaked to be suitable for use in the kernel.\n * It is optimized for 32-bit machines and machines that cannot work efficiently\n * with 128-bit integer types.\n */\n\n/* fe means field element. Here the field is \\Z/(2^255-19). An element t,\n * entries t[0]...t[9], represents the integer t[0]+2^26 t[1]+2^51 t[2]+2^77\n * t[3]+2^102 t[4]+...+2^230 t[9].\n * fe limbs are bounded by 1.125*2^26,1.125*2^25,1.125*2^26,1.125*2^25,etc.\n * Multiplication and carrying produce fe from fe_loose.\n */\ntypedef struct fe { u32 v[10]; } fe;\n\n/* fe_loose limbs are bounded by 3.375*2^26,3.375*2^25,3.375*2^26,3.375*2^25,etc\n * Addition and subtraction produce fe_loose from (fe, fe).\n */\ntypedef struct fe_loose { u32 v[10]; } fe_loose;\n\nstatic __always_inline void fe_frombytes_impl(u32 h[10], const u8 *s)\n{\n\t/* Ignores top bit of s. */\n\tu32 a0 = get_unaligned_le32(s);\n\tu32 a1 = get_unaligned_le32(s+4);\n\tu32 a2 = get_unaligned_le32(s+8);\n\tu32 a3 = get_unaligned_le32(s+12);\n\tu32 a4 = get_unaligned_le32(s+16);\n\tu32 a5 = get_unaligned_le32(s+20);\n\tu32 a6 = get_unaligned_le32(s+24);\n\tu32 a7 = get_unaligned_le32(s+28);\n\th[0] = a0&((1<<26)-1);                    /* 26 used, 32-26 left.   26 */\n\th[1] = (a0>>26) | ((a1&((1<<19)-1))<< 6); /* (32-26) + 19 =  6+19 = 25 */\n\th[2] = (a1>>19) | ((a2&((1<<13)-1))<<13); /* (32-19) + 13 = 13+13 = 26 */\n\th[3] = (a2>>13) | ((a3&((1<< 6)-1))<<19); /* (32-13) +  6 = 19+ 6 = 25 */\n\th[4] = (a3>> 6);                          /* (32- 6)              = 26 */\n\th[5] = a4&((1<<25)-1);                    /*                        25 */\n\th[6] = (a4>>25) | ((a5&((1<<19)-1))<< 7); /* (32-25) + 19 =  7+19 = 26 */\n\th[7] = (a5>>19) | ((a6&((1<<12)-1))<<13); /* (32-19) + 12 = 13+12 = 25 */\n\th[8] = (a6>>12) | ((a7&((1<< 6)-1))<<20); /* (32-12) +  6 = 20+ 6 = 26 */\n\th[9] = (a7>> 6)&((1<<25)-1); /*                                     25 */\n}\n\nstatic __always_inline void fe_frombytes(fe *h, const u8 *s)\n{\n\tfe_frombytes_impl(h->v, s);\n}\n\nstatic __always_inline u8 /*bool*/\naddcarryx_u25(u8 /*bool*/ c, u32 a, u32 b, u32 *low)\n{\n\t/* This function extracts 25 bits of result and 1 bit of carry\n\t * (26 total), so a 32-bit intermediate is sufficient.\n\t */\n\tu32 x = a + b + c;\n\t*low = x & ((1 << 25) - 1);\n\treturn (x >> 25) & 1;\n}\n\nstatic __always_inline u8 /*bool*/\naddcarryx_u26(u8 /*bool*/ c, u32 a, u32 b, u32 *low)\n{\n\t/* This function extracts 26 bits of result and 1 bit of carry\n\t * (27 total), so a 32-bit intermediate is sufficient.\n\t */\n\tu32 x = a + b + c;\n\t*low = x & ((1 << 26) - 1);\n\treturn (x >> 26) & 1;\n}\n\nstatic __always_inline u8 /*bool*/\nsubborrow_u25(u8 /*bool*/ c, u32 a, u32 b, u32 *low)\n{\n\t/* This function extracts 25 bits of result and 1 bit of borrow\n\t * (26 total), so a 32-bit intermediate is sufficient.\n\t */\n\tu32 x = a - b - c;\n\t*low = x & ((1 << 25) - 1);\n\treturn x >> 31;\n}\n\nstatic __always_inline u8 /*bool*/\nsubborrow_u26(u8 /*bool*/ c, u32 a, u32 b, u32 *low)\n{\n\t/* This function extracts 26 bits of result and 1 bit of borrow\n\t *(27 total), so a 32-bit intermediate is sufficient.\n\t */\n\tu32 x = a - b - c;\n\t*low = x & ((1 << 26) - 1);\n\treturn x >> 31;\n}\n\nstatic __always_inline u32 cmovznz32(u32 t, u32 z, u32 nz)\n{\n\tt = -!!t; /* all set if nonzero, 0 if 0 */\n\treturn (t&nz) | ((~t)&z);\n}\n\nstatic __always_inline void fe_freeze(u32 out[10], const u32 in1[10])\n{\n\t{ const u32 x17 = in1[9];\n\t{ const u32 x18 = in1[8];\n\t{ const u32 x16 = in1[7];\n\t{ const u32 x14 = in1[6];\n\t{ const u32 x12 = in1[5];\n\t{ const u32 x10 = in1[4];\n\t{ const u32 x8 = in1[3];\n\t{ const u32 x6 = in1[2];\n\t{ const u32 x4 = in1[1];\n\t{ const u32 x2 = in1[0];\n\t{ u32 x20; u8/*bool*/ x21 = subborrow_u26(0x0, x2, 0x3ffffed, &x20);\n\t{ u32 x23; u8/*bool*/ x24 = subborrow_u25(x21, x4, 0x1ffffff, &x23);\n\t{ u32 x26; u8/*bool*/ x27 = subborrow_u26(x24, x6, 0x3ffffff, &x26);\n\t{ u32 x29; u8/*bool*/ x30 = subborrow_u25(x27, x8, 0x1ffffff, &x29);\n\t{ u32 x32; u8/*bool*/ x33 = subborrow_u26(x30, x10, 0x3ffffff, &x32);\n\t{ u32 x35; u8/*bool*/ x36 = subborrow_u25(x33, x12, 0x1ffffff, &x35);\n\t{ u32 x38; u8/*bool*/ x39 = subborrow_u26(x36, x14, 0x3ffffff, &x38);\n\t{ u32 x41; u8/*bool*/ x42 = subborrow_u25(x39, x16, 0x1ffffff, &x41);\n\t{ u32 x44; u8/*bool*/ x45 = subborrow_u26(x42, x18, 0x3ffffff, &x44);\n\t{ u32 x47; u8/*bool*/ x48 = subborrow_u25(x45, x17, 0x1ffffff, &x47);\n\t{ u32 x49 = cmovznz32(x48, 0x0, 0xffffffff);\n\t{ u32 x50 = (x49 & 0x3ffffed);\n\t{ u32 x52; u8/*bool*/ x53 = addcarryx_u26(0x0, x20, x50, &x52);\n\t{ u32 x54 = (x49 & 0x1ffffff);\n\t{ u32 x56; u8/*bool*/ x57 = addcarryx_u25(x53, x23, x54, &x56);\n\t{ u32 x58 = (x49 & 0x3ffffff);\n\t{ u32 x60; u8/*bool*/ x61 = addcarryx_u26(x57, x26, x58, &x60);\n\t{ u32 x62 = (x49 & 0x1ffffff);\n\t{ u32 x64; u8/*bool*/ x65 = addcarryx_u25(x61, x29, x62, &x64);\n\t{ u32 x66 = (x49 & 0x3ffffff);\n\t{ u32 x68; u8/*bool*/ x69 = addcarryx_u26(x65, x32, x66, &x68);\n\t{ u32 x70 = (x49 & 0x1ffffff);\n\t{ u32 x72; u8/*bool*/ x73 = addcarryx_u25(x69, x35, x70, &x72);\n\t{ u32 x74 = (x49 & 0x3ffffff);\n\t{ u32 x76; u8/*bool*/ x77 = addcarryx_u26(x73, x38, x74, &x76);\n\t{ u32 x78 = (x49 & 0x1ffffff);\n\t{ u32 x80; u8/*bool*/ x81 = addcarryx_u25(x77, x41, x78, &x80);\n\t{ u32 x82 = (x49 & 0x3ffffff);\n\t{ u32 x84; u8/*bool*/ x85 = addcarryx_u26(x81, x44, x82, &x84);\n\t{ u32 x86 = (x49 & 0x1ffffff);\n\t{ u32 x88; addcarryx_u25(x85, x47, x86, &x88);\n\tout[0] = x52;\n\tout[1] = x56;\n\tout[2] = x60;\n\tout[3] = x64;\n\tout[4] = x68;\n\tout[5] = x72;\n\tout[6] = x76;\n\tout[7] = x80;\n\tout[8] = x84;\n\tout[9] = x88;\n\t}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}\n}\n\nstatic __always_inline void fe_tobytes(u8 s[32], const fe *f)\n{\n\tu32 h[10];\n\tfe_freeze(h, f->v);\n\ts[0] = h[0] >> 0;\n\ts[1] = h[0] >> 8;\n\ts[2] = h[0] >> 16;\n\ts[3] = (h[0] >> 24) | (h[1] << 2);\n\ts[4] = h[1] >> 6;\n\ts[5] = h[1] >> 14;\n\ts[6] = (h[1] >> 22) | (h[2] << 3);\n\ts[7] = h[2] >> 5;\n\ts[8] = h[2] >> 13;\n\ts[9] = (h[2] >> 21) | (h[3] << 5);\n\ts[10] = h[3] >> 3;\n\ts[11] = h[3] >> 11;\n\ts[12] = (h[3] >> 19) | (h[4] << 6);\n\ts[13] = h[4] >> 2;\n\ts[14] = h[4] >> 10;\n\ts[15] = h[4] >> 18;\n\ts[16] = h[5] >> 0;\n\ts[17] = h[5] >> 8;\n\ts[18] = h[5] >> 16;\n\ts[19] = (h[5] >> 24) | (h[6] << 1);\n\ts[20] = h[6] >> 7;\n\ts[21] = h[6] >> 15;\n\ts[22] = (h[6] >> 23) | (h[7] << 3);\n\ts[23] = h[7] >> 5;\n\ts[24] = h[7] >> 13;\n\ts[25] = (h[7] >> 21) | (h[8] << 4);\n\ts[26] = h[8] >> 4;\n\ts[27] = h[8] >> 12;\n\ts[28] = (h[8] >> 20) | (h[9] << 6);\n\ts[29] = h[9] >> 2;\n\ts[30] = h[9] >> 10;\n\ts[31] = h[9] >> 18;\n}\n\n/* h = f */\nstatic __always_inline void fe_copy(fe *h, const fe *f)\n{\n\tmemmove(h, f, sizeof(u32) * 10);\n}\n\nstatic __always_inline void fe_copy_lt(fe_loose *h, const fe *f)\n{\n\tmemmove(h, f, sizeof(u32) * 10);\n}\n\n/* h = 0 */\nstatic __always_inline void fe_0(fe *h)\n{\n\tmemset(h, 0, sizeof(u32) * 10);\n}\n\n/* h = 1 */\nstatic __always_inline void fe_1(fe *h)\n{\n\tmemset(h, 0, sizeof(u32) * 10);\n\th->v[0] = 1;\n}\n\nstatic void fe_add_impl(u32 out[10], const u32 in1[10], const u32 in2[10])\n{\n\t{ const u32 x20 = in1[9];\n\t{ const u32 x21 = in1[8];\n\t{ const u32 x19 = in1[7];\n\t{ const u32 x17 = in1[6];\n\t{ const u32 x15 = in1[5];\n\t{ const u32 x13 = in1[4];\n\t{ const u32 x11 = in1[3];\n\t{ const u32 x9 = in1[2];\n\t{ const u32 x7 = in1[1];\n\t{ const u32 x5 = in1[0];\n\t{ const u32 x38 = in2[9];\n\t{ const u32 x39 = in2[8];\n\t{ const u32 x37 = in2[7];\n\t{ const u32 x35 = in2[6];\n\t{ const u32 x33 = in2[5];\n\t{ const u32 x31 = in2[4];\n\t{ const u32 x29 = in2[3];\n\t{ const u32 x27 = in2[2];\n\t{ const u32 x25 = in2[1];\n\t{ const u32 x23 = in2[0];\n\tout[0] = (x5 + x23);\n\tout[1] = (x7 + x25);\n\tout[2] = (x9 + x27);\n\tout[3] = (x11 + x29);\n\tout[4] = (x13 + x31);\n\tout[5] = (x15 + x33);\n\tout[6] = (x17 + x35);\n\tout[7] = (x19 + x37);\n\tout[8] = (x21 + x39);\n\tout[9] = (x20 + x38);\n\t}}}}}}}}}}}}}}}}}}}}\n}\n\n/* h = f + g\n * Can overlap h with f or g.\n */\nstatic __always_inline void fe_add(fe_loose *h, const fe *f, const fe *g)\n{\n\tfe_add_impl(h->v, f->v, g->v);\n}\n\nstatic void fe_sub_impl(u32 out[10], const u32 in1[10], const u32 in2[10])\n{\n\t{ const u32 x20 = in1[9];\n\t{ const u32 x21 = in1[8];\n\t{ const u32 x19 = in1[7];\n\t{ const u32 x17 = in1[6];\n\t{ const u32 x15 = in1[5];\n\t{ const u32 x13 = in1[4];\n\t{ const u32 x11 = in1[3];\n\t{ const u32 x9 = in1[2];\n\t{ const u32 x7 = in1[1];\n\t{ const u32 x5 = in1[0];\n\t{ const u32 x38 = in2[9];\n\t{ const u32 x39 = in2[8];\n\t{ const u32 x37 = in2[7];\n\t{ const u32 x35 = in2[6];\n\t{ const u32 x33 = in2[5];\n\t{ const u32 x31 = in2[4];\n\t{ const u32 x29 = in2[3];\n\t{ const u32 x27 = in2[2];\n\t{ const u32 x25 = in2[1];\n\t{ const u32 x23 = in2[0];\n\tout[0] = ((0x7ffffda + x5) - x23);\n\tout[1] = ((0x3fffffe + x7) - x25);\n\tout[2] = ((0x7fffffe + x9) - x27);\n\tout[3] = ((0x3fffffe + x11) - x29);\n\tout[4] = ((0x7fffffe + x13) - x31);\n\tout[5] = ((0x3fffffe + x15) - x33);\n\tout[6] = ((0x7fffffe + x17) - x35);\n\tout[7] = ((0x3fffffe + x19) - x37);\n\tout[8] = ((0x7fffffe + x21) - x39);\n\tout[9] = ((0x3fffffe + x20) - x38);\n\t}}}}}}}}}}}}}}}}}}}}\n}\n\n/* h = f - g\n * Can overlap h with f or g.\n */\nstatic __always_inline void fe_sub(fe_loose *h, const fe *f, const fe *g)\n{\n\tfe_sub_impl(h->v, f->v, g->v);\n}\n\nstatic void fe_mul_impl(u32 out[10], const u32 in1[10], const u32 in2[10])\n{\n\t{ const u32 x20 = in1[9];\n\t{ const u32 x21 = in1[8];\n\t{ const u32 x19 = in1[7];\n\t{ const u32 x17 = in1[6];\n\t{ const u32 x15 = in1[5];\n\t{ const u32 x13 = in1[4];\n\t{ const u32 x11 = in1[3];\n\t{ const u32 x9 = in1[2];\n\t{ const u32 x7 = in1[1];\n\t{ const u32 x5 = in1[0];\n\t{ const u32 x38 = in2[9];\n\t{ const u32 x39 = in2[8];\n\t{ const u32 x37 = in2[7];\n\t{ const u32 x35 = in2[6];\n\t{ const u32 x33 = in2[5];\n\t{ const u32 x31 = in2[4];\n\t{ const u32 x29 = in2[3];\n\t{ const u32 x27 = in2[2];\n\t{ const u32 x25 = in2[1];\n\t{ const u32 x23 = in2[0];\n\t{ u64 x40 = ((u64)x23 * x5);\n\t{ u64 x41 = (((u64)x23 * x7) + ((u64)x25 * x5));\n\t{ u64 x42 = ((((u64)(0x2 * x25) * x7) + ((u64)x23 * x9)) + ((u64)x27 * x5));\n\t{ u64 x43 = (((((u64)x25 * x9) + ((u64)x27 * x7)) + ((u64)x23 * x11)) + ((u64)x29 * x5));\n\t{ u64 x44 = (((((u64)x27 * x9) + (0x2 * (((u64)x25 * x11) + ((u64)x29 * x7)))) + ((u64)x23 * x13)) + ((u64)x31 * x5));\n\t{ u64 x45 = (((((((u64)x27 * x11) + ((u64)x29 * x9)) + ((u64)x25 * x13)) + ((u64)x31 * x7)) + ((u64)x23 * x15)) + ((u64)x33 * x5));\n\t{ u64 x46 = (((((0x2 * ((((u64)x29 * x11) + ((u64)x25 * x15)) + ((u64)x33 * x7))) + ((u64)x27 * x13)) + ((u64)x31 * x9)) + ((u64)x23 * x17)) + ((u64)x35 * x5));\n\t{ u64 x47 = (((((((((u64)x29 * x13) + ((u64)x31 * x11)) + ((u64)x27 * x15)) + ((u64)x33 * x9)) + ((u64)x25 * x17)) + ((u64)x35 * x7)) + ((u64)x23 * x19)) + ((u64)x37 * x5));\n\t{ u64 x48 = (((((((u64)x31 * x13) + (0x2 * (((((u64)x29 * x15) + ((u64)x33 * x11)) + ((u64)x25 * x19)) + ((u64)x37 * x7)))) + ((u64)x27 * x17)) + ((u64)x35 * x9)) + ((u64)x23 * x21)) + ((u64)x39 * x5));\n\t{ u64 x49 = (((((((((((u64)x31 * x15) + ((u64)x33 * x13)) + ((u64)x29 * x17)) + ((u64)x35 * x11)) + ((u64)x27 * x19)) + ((u64)x37 * x9)) + ((u64)x25 * x21)) + ((u64)x39 * x7)) + ((u64)x23 * x20)) + ((u64)x38 * x5));\n\t{ u64 x50 = (((((0x2 * ((((((u64)x33 * x15) + ((u64)x29 * x19)) + ((u64)x37 * x11)) + ((u64)x25 * x20)) + ((u64)x38 * x7))) + ((u64)x31 * x17)) + ((u64)x35 * x13)) + ((u64)x27 * x21)) + ((u64)x39 * x9));\n\t{ u64 x51 = (((((((((u64)x33 * x17) + ((u64)x35 * x15)) + ((u64)x31 * x19)) + ((u64)x37 * x13)) + ((u64)x29 * x21)) + ((u64)x39 * x11)) + ((u64)x27 * x20)) + ((u64)x38 * x9));\n\t{ u64 x52 = (((((u64)x35 * x17) + (0x2 * (((((u64)x33 * x19) + ((u64)x37 * x15)) + ((u64)x29 * x20)) + ((u64)x38 * x11)))) + ((u64)x31 * x21)) + ((u64)x39 * x13));\n\t{ u64 x53 = (((((((u64)x35 * x19) + ((u64)x37 * x17)) + ((u64)x33 * x21)) + ((u64)x39 * x15)) + ((u64)x31 * x20)) + ((u64)x38 * x13));\n\t{ u64 x54 = (((0x2 * ((((u64)x37 * x19) + ((u64)x33 * x20)) + ((u64)x38 * x15))) + ((u64)x35 * x21)) + ((u64)x39 * x17));\n\t{ u64 x55 = (((((u64)x37 * x21) + ((u64)x39 * x19)) + ((u64)x35 * x20)) + ((u64)x38 * x17));\n\t{ u64 x56 = (((u64)x39 * x21) + (0x2 * (((u64)x37 * x20) + ((u64)x38 * x19))));\n\t{ u64 x57 = (((u64)x39 * x20) + ((u64)x38 * x21));\n\t{ u64 x58 = ((u64)(0x2 * x38) * x20);\n\t{ u64 x59 = (x48 + (x58 << 0x4));\n\t{ u64 x60 = (x59 + (x58 << 0x1));\n\t{ u64 x61 = (x60 + x58);\n\t{ u64 x62 = (x47 + (x57 << 0x4));\n\t{ u64 x63 = (x62 + (x57 << 0x1));\n\t{ u64 x64 = (x63 + x57);\n\t{ u64 x65 = (x46 + (x56 << 0x4));\n\t{ u64 x66 = (x65 + (x56 << 0x1));\n\t{ u64 x67 = (x66 + x56);\n\t{ u64 x68 = (x45 + (x55 << 0x4));\n\t{ u64 x69 = (x68 + (x55 << 0x1));\n\t{ u64 x70 = (x69 + x55);\n\t{ u64 x71 = (x44 + (x54 << 0x4));\n\t{ u64 x72 = (x71 + (x54 << 0x1));\n\t{ u64 x73 = (x72 + x54);\n\t{ u64 x74 = (x43 + (x53 << 0x4));\n\t{ u64 x75 = (x74 + (x53 << 0x1));\n\t{ u64 x76 = (x75 + x53);\n\t{ u64 x77 = (x42 + (x52 << 0x4));\n\t{ u64 x78 = (x77 + (x52 << 0x1));\n\t{ u64 x79 = (x78 + x52);\n\t{ u64 x80 = (x41 + (x51 << 0x4));\n\t{ u64 x81 = (x80 + (x51 << 0x1));\n\t{ u64 x82 = (x81 + x51);\n\t{ u64 x83 = (x40 + (x50 << 0x4));\n\t{ u64 x84 = (x83 + (x50 << 0x1));\n\t{ u64 x85 = (x84 + x50);\n\t{ u64 x86 = (x85 >> 0x1a);\n\t{ u32 x87 = ((u32)x85 & 0x3ffffff);\n\t{ u64 x88 = (x86 + x82);\n\t{ u64 x89 = (x88 >> 0x19);\n\t{ u32 x90 = ((u32)x88 & 0x1ffffff);\n\t{ u64 x91 = (x89 + x79);\n\t{ u64 x92 = (x91 >> 0x1a);\n\t{ u32 x93 = ((u32)x91 & 0x3ffffff);\n\t{ u64 x94 = (x92 + x76);\n\t{ u64 x95 = (x94 >> 0x19);\n\t{ u32 x96 = ((u32)x94 & 0x1ffffff);\n\t{ u64 x97 = (x95 + x73);\n\t{ u64 x98 = (x97 >> 0x1a);\n\t{ u32 x99 = ((u32)x97 & 0x3ffffff);\n\t{ u64 x100 = (x98 + x70);\n\t{ u64 x101 = (x100 >> 0x19);\n\t{ u32 x102 = ((u32)x100 & 0x1ffffff);\n\t{ u64 x103 = (x101 + x67);\n\t{ u64 x104 = (x103 >> 0x1a);\n\t{ u32 x105 = ((u32)x103 & 0x3ffffff);\n\t{ u64 x106 = (x104 + x64);\n\t{ u64 x107 = (x106 >> 0x19);\n\t{ u32 x108 = ((u32)x106 & 0x1ffffff);\n\t{ u64 x109 = (x107 + x61);\n\t{ u64 x110 = (x109 >> 0x1a);\n\t{ u32 x111 = ((u32)x109 & 0x3ffffff);\n\t{ u64 x112 = (x110 + x49);\n\t{ u64 x113 = (x112 >> 0x19);\n\t{ u32 x114 = ((u32)x112 & 0x1ffffff);\n\t{ u64 x115 = (x87 + (0x13 * x113));\n\t{ u32 x116 = (u32) (x115 >> 0x1a);\n\t{ u32 x117 = ((u32)x115 & 0x3ffffff);\n\t{ u32 x118 = (x116 + x90);\n\t{ u32 x119 = (x118 >> 0x19);\n\t{ u32 x120 = (x118 & 0x1ffffff);\n\tout[0] = x117;\n\tout[1] = x120;\n\tout[2] = (x119 + x93);\n\tout[3] = x96;\n\tout[4] = x99;\n\tout[5] = x102;\n\tout[6] = x105;\n\tout[7] = x108;\n\tout[8] = x111;\n\tout[9] = x114;\n\t}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}\n}\n\nstatic __always_inline void fe_mul_ttt(fe *h, const fe *f, const fe *g)\n{\n\tfe_mul_impl(h->v, f->v, g->v);\n}\n\nstatic __always_inline void fe_mul_tlt(fe *h, const fe_loose *f, const fe *g)\n{\n\tfe_mul_impl(h->v, f->v, g->v);\n}\n\nstatic __always_inline void\nfe_mul_tll(fe *h, const fe_loose *f, const fe_loose *g)\n{\n\tfe_mul_impl(h->v, f->v, g->v);\n}\n\nstatic void fe_sqr_impl(u32 out[10], const u32 in1[10])\n{\n\t{ const u32 x17 = in1[9];\n\t{ const u32 x18 = in1[8];\n\t{ const u32 x16 = in1[7];\n\t{ const u32 x14 = in1[6];\n\t{ const u32 x12 = in1[5];\n\t{ const u32 x10 = in1[4];\n\t{ const u32 x8 = in1[3];\n\t{ const u32 x6 = in1[2];\n\t{ const u32 x4 = in1[1];\n\t{ const u32 x2 = in1[0];\n\t{ u64 x19 = ((u64)x2 * x2);\n\t{ u64 x20 = ((u64)(0x2 * x2) * x4);\n\t{ u64 x21 = (0x2 * (((u64)x4 * x4) + ((u64)x2 * x6)));\n\t{ u64 x22 = (0x2 * (((u64)x4 * x6) + ((u64)x2 * x8)));\n\t{ u64 x23 = ((((u64)x6 * x6) + ((u64)(0x4 * x4) * x8)) + ((u64)(0x2 * x2) * x10));\n\t{ u64 x24 = (0x2 * ((((u64)x6 * x8) + ((u64)x4 * x10)) + ((u64)x2 * x12)));\n\t{ u64 x25 = (0x2 * (((((u64)x8 * x8) + ((u64)x6 * x10)) + ((u64)x2 * x14)) + ((u64)(0x2 * x4) * x12)));\n\t{ u64 x26 = (0x2 * (((((u64)x8 * x10) + ((u64)x6 * x12)) + ((u64)x4 * x14)) + ((u64)x2 * x16)));\n\t{ u64 x27 = (((u64)x10 * x10) + (0x2 * ((((u64)x6 * x14) + ((u64)x2 * x18)) + (0x2 * (((u64)x4 * x16) + ((u64)x8 * x12))))));\n\t{ u64 x28 = (0x2 * ((((((u64)x10 * x12) + ((u64)x8 * x14)) + ((u64)x6 * x16)) + ((u64)x4 * x18)) + ((u64)x2 * x17)));\n\t{ u64 x29 = (0x2 * (((((u64)x12 * x12) + ((u64)x10 * x14)) + ((u64)x6 * x18)) + (0x2 * (((u64)x8 * x16) + ((u64)x4 * x17)))));\n\t{ u64 x30 = (0x2 * (((((u64)x12 * x14) + ((u64)x10 * x16)) + ((u64)x8 * x18)) + ((u64)x6 * x17)));\n\t{ u64 x31 = (((u64)x14 * x14) + (0x2 * (((u64)x10 * x18) + (0x2 * (((u64)x12 * x16) + ((u64)x8 * x17))))));\n\t{ u64 x32 = (0x2 * ((((u64)x14 * x16) + ((u64)x12 * x18)) + ((u64)x10 * x17)));\n\t{ u64 x33 = (0x2 * ((((u64)x16 * x16) + ((u64)x14 * x18)) + ((u64)(0x2 * x12) * x17)));\n\t{ u64 x34 = (0x2 * (((u64)x16 * x18) + ((u64)x14 * x17)));\n\t{ u64 x35 = (((u64)x18 * x18) + ((u64)(0x4 * x16) * x17));\n\t{ u64 x36 = ((u64)(0x2 * x18) * x17);\n\t{ u64 x37 = ((u64)(0x2 * x17) * x17);\n\t{ u64 x38 = (x27 + (x37 << 0x4));\n\t{ u64 x39 = (x38 + (x37 << 0x1));\n\t{ u64 x40 = (x39 + x37);\n\t{ u64 x41 = (x26 + (x36 << 0x4));\n\t{ u64 x42 = (x41 + (x36 << 0x1));\n\t{ u64 x43 = (x42 + x36);\n\t{ u64 x44 = (x25 + (x35 << 0x4));\n\t{ u64 x45 = (x44 + (x35 << 0x1));\n\t{ u64 x46 = (x45 + x35);\n\t{ u64 x47 = (x24 + (x34 << 0x4));\n\t{ u64 x48 = (x47 + (x34 << 0x1));\n\t{ u64 x49 = (x48 + x34);\n\t{ u64 x50 = (x23 + (x33 << 0x4));\n\t{ u64 x51 = (x50 + (x33 << 0x1));\n\t{ u64 x52 = (x51 + x33);\n\t{ u64 x53 = (x22 + (x32 << 0x4));\n\t{ u64 x54 = (x53 + (x32 << 0x1));\n\t{ u64 x55 = (x54 + x32);\n\t{ u64 x56 = (x21 + (x31 << 0x4));\n\t{ u64 x57 = (x56 + (x31 << 0x1));\n\t{ u64 x58 = (x57 + x31);\n\t{ u64 x59 = (x20 + (x30 << 0x4));\n\t{ u64 x60 = (x59 + (x30 << 0x1));\n\t{ u64 x61 = (x60 + x30);\n\t{ u64 x62 = (x19 + (x29 << 0x4));\n\t{ u64 x63 = (x62 + (x29 << 0x1));\n\t{ u64 x64 = (x63 + x29);\n\t{ u64 x65 = (x64 >> 0x1a);\n\t{ u32 x66 = ((u32)x64 & 0x3ffffff);\n\t{ u64 x67 = (x65 + x61);\n\t{ u64 x68 = (x67 >> 0x19);\n\t{ u32 x69 = ((u32)x67 & 0x1ffffff);\n\t{ u64 x70 = (x68 + x58);\n\t{ u64 x71 = (x70 >> 0x1a);\n\t{ u32 x72 = ((u32)x70 & 0x3ffffff);\n\t{ u64 x73 = (x71 + x55);\n\t{ u64 x74 = (x73 >> 0x19);\n\t{ u32 x75 = ((u32)x73 & 0x1ffffff);\n\t{ u64 x76 = (x74 + x52);\n\t{ u64 x77 = (x76 >> 0x1a);\n\t{ u32 x78 = ((u32)x76 & 0x3ffffff);\n\t{ u64 x79 = (x77 + x49);\n\t{ u64 x80 = (x79 >> 0x19);\n\t{ u32 x81 = ((u32)x79 & 0x1ffffff);\n\t{ u64 x82 = (x80 + x46);\n\t{ u64 x83 = (x82 >> 0x1a);\n\t{ u32 x84 = ((u32)x82 & 0x3ffffff);\n\t{ u64 x85 = (x83 + x43);\n\t{ u64 x86 = (x85 >> 0x19);\n\t{ u32 x87 = ((u32)x85 & 0x1ffffff);\n\t{ u64 x88 = (x86 + x40);\n\t{ u64 x89 = (x88 >> 0x1a);\n\t{ u32 x90 = ((u32)x88 & 0x3ffffff);\n\t{ u64 x91 = (x89 + x28);\n\t{ u64 x92 = (x91 >> 0x19);\n\t{ u32 x93 = ((u32)x91 & 0x1ffffff);\n\t{ u64 x94 = (x66 + (0x13 * x92));\n\t{ u32 x95 = (u32) (x94 >> 0x1a);\n\t{ u32 x96 = ((u32)x94 & 0x3ffffff);\n\t{ u32 x97 = (x95 + x69);\n\t{ u32 x98 = (x97 >> 0x19);\n\t{ u32 x99 = (x97 & 0x1ffffff);\n\tout[0] = x96;\n\tout[1] = x99;\n\tout[2] = (x98 + x72);\n\tout[3] = x75;\n\tout[4] = x78;\n\tout[5] = x81;\n\tout[6] = x84;\n\tout[7] = x87;\n\tout[8] = x90;\n\tout[9] = x93;\n\t}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}\n}\n\nstatic __always_inline void fe_sq_tl(fe *h, const fe_loose *f)\n{\n\tfe_sqr_impl(h->v, f->v);\n}\n\nstatic __always_inline void fe_sq_tt(fe *h, const fe *f)\n{\n\tfe_sqr_impl(h->v, f->v);\n}\n\nstatic __always_inline void fe_loose_invert(fe *out, const fe_loose *z)\n{\n\tfe t0;\n\tfe t1;\n\tfe t2;\n\tfe t3;\n\tint i;\n\n\tfe_sq_tl(&t0, z);\n\tfe_sq_tt(&t1, &t0);\n\tfor (i = 1; i < 2; ++i)\n\t\tfe_sq_tt(&t1, &t1);\n\tfe_mul_tlt(&t1, z, &t1);\n\tfe_mul_ttt(&t0, &t0, &t1);\n\tfe_sq_tt(&t2, &t0);\n\tfe_mul_ttt(&t1, &t1, &t2);\n\tfe_sq_tt(&t2, &t1);\n\tfor (i = 1; i < 5; ++i)\n\t\tfe_sq_tt(&t2, &t2);\n\tfe_mul_ttt(&t1, &t2, &t1);\n\tfe_sq_tt(&t2, &t1);\n\tfor (i = 1; i < 10; ++i)\n\t\tfe_sq_tt(&t2, &t2);\n\tfe_mul_ttt(&t2, &t2, &t1);\n\tfe_sq_tt(&t3, &t2);\n\tfor (i = 1; i < 20; ++i)\n\t\tfe_sq_tt(&t3, &t3);\n\tfe_mul_ttt(&t2, &t3, &t2);\n\tfe_sq_tt(&t2, &t2);\n\tfor (i = 1; i < 10; ++i)\n\t\tfe_sq_tt(&t2, &t2);\n\tfe_mul_ttt(&t1, &t2, &t1);\n\tfe_sq_tt(&t2, &t1);\n\tfor (i = 1; i < 50; ++i)\n\t\tfe_sq_tt(&t2, &t2);\n\tfe_mul_ttt(&t2, &t2, &t1);\n\tfe_sq_tt(&t3, &t2);\n\tfor (i = 1; i < 100; ++i)\n\t\tfe_sq_tt(&t3, &t3);\n\tfe_mul_ttt(&t2, &t3, &t2);\n\tfe_sq_tt(&t2, &t2);\n\tfor (i = 1; i < 50; ++i)\n\t\tfe_sq_tt(&t2, &t2);\n\tfe_mul_ttt(&t1, &t2, &t1);\n\tfe_sq_tt(&t1, &t1);\n\tfor (i = 1; i < 5; ++i)\n\t\tfe_sq_tt(&t1, &t1);\n\tfe_mul_ttt(out, &t1, &t0);\n}\n\nstatic __always_inline void fe_invert(fe *out, const fe *z)\n{\n\tfe_loose l;\n\tfe_copy_lt(&l, z);\n\tfe_loose_invert(out, &l);\n}\n\n/* Replace (f,g) with (g,f) if b == 1;\n * replace (f,g) with (f,g) if b == 0.\n *\n * Preconditions: b in {0,1}\n */\nstatic __always_inline void fe_cswap(fe *f, fe *g, unsigned int b)\n{\n\tunsigned i;\n\tb = 0 - b;\n\tfor (i = 0; i < 10; i++) {\n\t\tu32 x = f->v[i] ^ g->v[i];\n\t\tx &= b;\n\t\tf->v[i] ^= x;\n\t\tg->v[i] ^= x;\n\t}\n}\n\n/* NOTE: based on fiat-crypto fe_mul, edited for in2=121666, 0, 0.*/\nstatic __always_inline void fe_mul_121666_impl(u32 out[10], const u32 in1[10])\n{\n\t{ const u32 x20 = in1[9];\n\t{ const u32 x21 = in1[8];\n\t{ const u32 x19 = in1[7];\n\t{ const u32 x17 = in1[6];\n\t{ const u32 x15 = in1[5];\n\t{ const u32 x13 = in1[4];\n\t{ const u32 x11 = in1[3];\n\t{ const u32 x9 = in1[2];\n\t{ const u32 x7 = in1[1];\n\t{ const u32 x5 = in1[0];\n\t{ const u32 x38 = 0;\n\t{ const u32 x39 = 0;\n\t{ const u32 x37 = 0;\n\t{ const u32 x35 = 0;\n\t{ const u32 x33 = 0;\n\t{ const u32 x31 = 0;\n\t{ const u32 x29 = 0;\n\t{ const u32 x27 = 0;\n\t{ const u32 x25 = 0;\n\t{ const u32 x23 = 121666;\n\t{ u64 x40 = ((u64)x23 * x5);\n\t{ u64 x41 = (((u64)x23 * x7) + ((u64)x25 * x5));\n\t{ u64 x42 = ((((u64)(0x2 * x25) * x7) + ((u64)x23 * x9)) + ((u64)x27 * x5));\n\t{ u64 x43 = (((((u64)x25 * x9) + ((u64)x27 * x7)) + ((u64)x23 * x11)) + ((u64)x29 * x5));\n\t{ u64 x44 = (((((u64)x27 * x9) + (0x2 * (((u64)x25 * x11) + ((u64)x29 * x7)))) + ((u64)x23 * x13)) + ((u64)x31 * x5));\n\t{ u64 x45 = (((((((u64)x27 * x11) + ((u64)x29 * x9)) + ((u64)x25 * x13)) + ((u64)x31 * x7)) + ((u64)x23 * x15)) + ((u64)x33 * x5));\n\t{ u64 x46 = (((((0x2 * ((((u64)x29 * x11) + ((u64)x25 * x15)) + ((u64)x33 * x7))) + ((u64)x27 * x13)) + ((u64)x31 * x9)) + ((u64)x23 * x17)) + ((u64)x35 * x5));\n\t{ u64 x47 = (((((((((u64)x29 * x13) + ((u64)x31 * x11)) + ((u64)x27 * x15)) + ((u64)x33 * x9)) + ((u64)x25 * x17)) + ((u64)x35 * x7)) + ((u64)x23 * x19)) + ((u64)x37 * x5));\n\t{ u64 x48 = (((((((u64)x31 * x13) + (0x2 * (((((u64)x29 * x15) + ((u64)x33 * x11)) + ((u64)x25 * x19)) + ((u64)x37 * x7)))) + ((u64)x27 * x17)) + ((u64)x35 * x9)) + ((u64)x23 * x21)) + ((u64)x39 * x5));\n\t{ u64 x49 = (((((((((((u64)x31 * x15) + ((u64)x33 * x13)) + ((u64)x29 * x17)) + ((u64)x35 * x11)) + ((u64)x27 * x19)) + ((u64)x37 * x9)) + ((u64)x25 * x21)) + ((u64)x39 * x7)) + ((u64)x23 * x20)) + ((u64)x38 * x5));\n\t{ u64 x50 = (((((0x2 * ((((((u64)x33 * x15) + ((u64)x29 * x19)) + ((u64)x37 * x11)) + ((u64)x25 * x20)) + ((u64)x38 * x7))) + ((u64)x31 * x17)) + ((u64)x35 * x13)) + ((u64)x27 * x21)) + ((u64)x39 * x9));\n\t{ u64 x51 = (((((((((u64)x33 * x17) + ((u64)x35 * x15)) + ((u64)x31 * x19)) + ((u64)x37 * x13)) + ((u64)x29 * x21)) + ((u64)x39 * x11)) + ((u64)x27 * x20)) + ((u64)x38 * x9));\n\t{ u64 x52 = (((((u64)x35 * x17) + (0x2 * (((((u64)x33 * x19) + ((u64)x37 * x15)) + ((u64)x29 * x20)) + ((u64)x38 * x11)))) + ((u64)x31 * x21)) + ((u64)x39 * x13));\n\t{ u64 x53 = (((((((u64)x35 * x19) + ((u64)x37 * x17)) + ((u64)x33 * x21)) + ((u64)x39 * x15)) + ((u64)x31 * x20)) + ((u64)x38 * x13));\n\t{ u64 x54 = (((0x2 * ((((u64)x37 * x19) + ((u64)x33 * x20)) + ((u64)x38 * x15))) + ((u64)x35 * x21)) + ((u64)x39 * x17));\n\t{ u64 x55 = (((((u64)x37 * x21) + ((u64)x39 * x19)) + ((u64)x35 * x20)) + ((u64)x38 * x17));\n\t{ u64 x56 = (((u64)x39 * x21) + (0x2 * (((u64)x37 * x20) + ((u64)x38 * x19))));\n\t{ u64 x57 = (((u64)x39 * x20) + ((u64)x38 * x21));\n\t{ u64 x58 = ((u64)(0x2 * x38) * x20);\n\t{ u64 x59 = (x48 + (x58 << 0x4));\n\t{ u64 x60 = (x59 + (x58 << 0x1));\n\t{ u64 x61 = (x60 + x58);\n\t{ u64 x62 = (x47 + (x57 << 0x4));\n\t{ u64 x63 = (x62 + (x57 << 0x1));\n\t{ u64 x64 = (x63 + x57);\n\t{ u64 x65 = (x46 + (x56 << 0x4));\n\t{ u64 x66 = (x65 + (x56 << 0x1));\n\t{ u64 x67 = (x66 + x56);\n\t{ u64 x68 = (x45 + (x55 << 0x4));\n\t{ u64 x69 = (x68 + (x55 << 0x1));\n\t{ u64 x70 = (x69 + x55);\n\t{ u64 x71 = (x44 + (x54 << 0x4));\n\t{ u64 x72 = (x71 + (x54 << 0x1));\n\t{ u64 x73 = (x72 + x54);\n\t{ u64 x74 = (x43 + (x53 << 0x4));\n\t{ u64 x75 = (x74 + (x53 << 0x1));\n\t{ u64 x76 = (x75 + x53);\n\t{ u64 x77 = (x42 + (x52 << 0x4));\n\t{ u64 x78 = (x77 + (x52 << 0x1));\n\t{ u64 x79 = (x78 + x52);\n\t{ u64 x80 = (x41 + (x51 << 0x4));\n\t{ u64 x81 = (x80 + (x51 << 0x1));\n\t{ u64 x82 = (x81 + x51);\n\t{ u64 x83 = (x40 + (x50 << 0x4));\n\t{ u64 x84 = (x83 + (x50 << 0x1));\n\t{ u64 x85 = (x84 + x50);\n\t{ u64 x86 = (x85 >> 0x1a);\n\t{ u32 x87 = ((u32)x85 & 0x3ffffff);\n\t{ u64 x88 = (x86 + x82);\n\t{ u64 x89 = (x88 >> 0x19);\n\t{ u32 x90 = ((u32)x88 & 0x1ffffff);\n\t{ u64 x91 = (x89 + x79);\n\t{ u64 x92 = (x91 >> 0x1a);\n\t{ u32 x93 = ((u32)x91 & 0x3ffffff);\n\t{ u64 x94 = (x92 + x76);\n\t{ u64 x95 = (x94 >> 0x19);\n\t{ u32 x96 = ((u32)x94 & 0x1ffffff);\n\t{ u64 x97 = (x95 + x73);\n\t{ u64 x98 = (x97 >> 0x1a);\n\t{ u32 x99 = ((u32)x97 & 0x3ffffff);\n\t{ u64 x100 = (x98 + x70);\n\t{ u64 x101 = (x100 >> 0x19);\n\t{ u32 x102 = ((u32)x100 & 0x1ffffff);\n\t{ u64 x103 = (x101 + x67);\n\t{ u64 x104 = (x103 >> 0x1a);\n\t{ u32 x105 = ((u32)x103 & 0x3ffffff);\n\t{ u64 x106 = (x104 + x64);\n\t{ u64 x107 = (x106 >> 0x19);\n\t{ u32 x108 = ((u32)x106 & 0x1ffffff);\n\t{ u64 x109 = (x107 + x61);\n\t{ u64 x110 = (x109 >> 0x1a);\n\t{ u32 x111 = ((u32)x109 & 0x3ffffff);\n\t{ u64 x112 = (x110 + x49);\n\t{ u64 x113 = (x112 >> 0x19);\n\t{ u32 x114 = ((u32)x112 & 0x1ffffff);\n\t{ u64 x115 = (x87 + (0x13 * x113));\n\t{ u32 x116 = (u32) (x115 >> 0x1a);\n\t{ u32 x117 = ((u32)x115 & 0x3ffffff);\n\t{ u32 x118 = (x116 + x90);\n\t{ u32 x119 = (x118 >> 0x19);\n\t{ u32 x120 = (x118 & 0x1ffffff);\n\tout[0] = x117;\n\tout[1] = x120;\n\tout[2] = (x119 + x93);\n\tout[3] = x96;\n\tout[4] = x99;\n\tout[5] = x102;\n\tout[6] = x105;\n\tout[7] = x108;\n\tout[8] = x111;\n\tout[9] = x114;\n\t}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}\n}\n\nstatic __always_inline void fe_mul121666(fe *h, const fe_loose *f)\n{\n\tfe_mul_121666_impl(h->v, f->v);\n}\n\nstatic void curve25519_generic(u8 out[CURVE25519_KEY_SIZE],\n\t\t\t       const u8 scalar[CURVE25519_KEY_SIZE],\n\t\t\t       const u8 point[CURVE25519_KEY_SIZE])\n{\n\tfe x1, x2, z2, x3, z3;\n\tfe_loose x2l, z2l, x3l;\n\tunsigned swap = 0;\n\tint pos;\n\tu8 e[32];\n\n\tmemcpy(e, scalar, 32);\n\tcurve25519_clamp_secret(e);\n\n\t/* The following implementation was transcribed to Coq and proven to\n\t * correspond to unary scalar multiplication in affine coordinates given\n\t * that x1 != 0 is the x coordinate of some point on the curve. It was\n\t * also checked in Coq that doing a ladderstep with x1 = x3 = 0 gives\n\t * z2' = z3' = 0, and z2 = z3 = 0 gives z2' = z3' = 0. The statement was\n\t * quantified over the underlying field, so it applies to Curve25519\n\t * itself and the quadratic twist of Curve25519. It was not proven in\n\t * Coq that prime-field arithmetic correctly simulates extension-field\n\t * arithmetic on prime-field values. The decoding of the byte array\n\t * representation of e was not considered.\n\t *\n\t * Specification of Montgomery curves in affine coordinates:\n\t * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Spec/MontgomeryCurve.v#L27>\n\t *\n\t * Proof that these form a group that is isomorphic to a Weierstrass\n\t * curve:\n\t * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/AffineProofs.v#L35>\n\t *\n\t * Coq transcription and correctness proof of the loop\n\t * (where scalarbits=255):\n\t * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZ.v#L118>\n\t * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L278>\n\t * preconditions: 0 <= e < 2^255 (not necessarily e < order),\n\t * fe_invert(0) = 0\n\t */\n\tfe_frombytes(&x1, point);\n\tfe_1(&x2);\n\tfe_0(&z2);\n\tfe_copy(&x3, &x1);\n\tfe_1(&z3);\n\n\tfor (pos = 254; pos >= 0; --pos) {\n\t\tfe tmp0, tmp1;\n\t\tfe_loose tmp0l, tmp1l;\n\t\t/* loop invariant as of right before the test, for the case\n\t\t * where x1 != 0:\n\t\t *   pos >= -1; if z2 = 0 then x2 is nonzero; if z3 = 0 then x3\n\t\t *   is nonzero\n\t\t *   let r := e >> (pos+1) in the following equalities of\n\t\t *   projective points:\n\t\t *   to_xz (r*P)     === if swap then (x3, z3) else (x2, z2)\n\t\t *   to_xz ((r+1)*P) === if swap then (x2, z2) else (x3, z3)\n\t\t *   x1 is the nonzero x coordinate of the nonzero\n\t\t *   point (r*P-(r+1)*P)\n\t\t */\n\t\tunsigned b = 1 & (e[pos / 8] >> (pos & 7));\n\t\tswap ^= b;\n\t\tfe_cswap(&x2, &x3, swap);\n\t\tfe_cswap(&z2, &z3, swap);\n\t\tswap = b;\n\t\t/* Coq transcription of ladderstep formula (called from\n\t\t * transcribed loop):\n\t\t * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZ.v#L89>\n\t\t * <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L131>\n\t\t * x1 != 0 <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L217>\n\t\t * x1  = 0 <https://github.com/mit-plv/fiat-crypto/blob/2456d821825521f7e03e65882cc3521795b0320f/src/Curves/Montgomery/XZProofs.v#L147>\n\t\t */\n\t\tfe_sub(&tmp0l, &x3, &z3);\n\t\tfe_sub(&tmp1l, &x2, &z2);\n\t\tfe_add(&x2l, &x2, &z2);\n\t\tfe_add(&z2l, &x3, &z3);\n\t\tfe_mul_tll(&z3, &tmp0l, &x2l);\n\t\tfe_mul_tll(&z2, &z2l, &tmp1l);\n\t\tfe_sq_tl(&tmp0, &tmp1l);\n\t\tfe_sq_tl(&tmp1, &x2l);\n\t\tfe_add(&x3l, &z3, &z2);\n\t\tfe_sub(&z2l, &z3, &z2);\n\t\tfe_mul_ttt(&x2, &tmp1, &tmp0);\n\t\tfe_sub(&tmp1l, &tmp1, &tmp0);\n\t\tfe_sq_tl(&z2, &z2l);\n\t\tfe_mul121666(&z3, &tmp1l);\n\t\tfe_sq_tl(&x3, &x3l);\n\t\tfe_add(&tmp0l, &tmp0, &z3);\n\t\tfe_mul_ttt(&z3, &x1, &z2);\n\t\tfe_mul_tll(&z2, &tmp1l, &tmp0l);\n\t}\n\t/* here pos=-1, so r=e, so to_xz (e*P) === if swap then (x3, z3)\n\t * else (x2, z2)\n\t */\n\tfe_cswap(&x2, &x3, swap);\n\tfe_cswap(&z2, &z3, swap);\n\n\tfe_invert(&z2, &z2);\n\tfe_mul_ttt(&x2, &x2, &z2);\n\tfe_tobytes(out, &x2);\n\n\tmemzero_explicit(&x1, sizeof(x1));\n\tmemzero_explicit(&x2, sizeof(x2));\n\tmemzero_explicit(&z2, sizeof(z2));\n\tmemzero_explicit(&x3, sizeof(x3));\n\tmemzero_explicit(&z3, sizeof(z3));\n\tmemzero_explicit(&x2l, sizeof(x2l));\n\tmemzero_explicit(&z2l, sizeof(z2l));\n\tmemzero_explicit(&x3l, sizeof(x3l));\n\tmemzero_explicit(&e, sizeof(e));\n}\n"
  },
  {
    "path": "src/curve25519-hacl64.h",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2016-2017 INRIA and Microsoft Corporation.\n * Copyright (C) 2018-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n *\n * This is a machine-generated formally verified implementation of Curve25519\n * ECDH from: <https://github.com/mitls/hacl-star>. Though originally machine\n * generated, it has been tweaked to be suitable for use in the kernel. It is\n * optimized for 64-bit machines that can efficiently work with 128-bit\n * integer types.\n */\n\ntypedef __uint128_t u128;\n\nstatic __always_inline u64 u64_eq_mask(u64 a, u64 b)\n{\n\tu64 x = a ^ b;\n\tu64 minus_x = ~x + (u64)1U;\n\tu64 x_or_minus_x = x | minus_x;\n\tu64 xnx = x_or_minus_x >> (u32)63U;\n\tu64 c = xnx - (u64)1U;\n\treturn c;\n}\n\nstatic __always_inline u64 u64_gte_mask(u64 a, u64 b)\n{\n\tu64 x = a;\n\tu64 y = b;\n\tu64 x_xor_y = x ^ y;\n\tu64 x_sub_y = x - y;\n\tu64 x_sub_y_xor_y = x_sub_y ^ y;\n\tu64 q = x_xor_y | x_sub_y_xor_y;\n\tu64 x_xor_q = x ^ q;\n\tu64 x_xor_q_ = x_xor_q >> (u32)63U;\n\tu64 c = x_xor_q_ - (u64)1U;\n\treturn c;\n}\n\nstatic __always_inline void modulo_carry_top(u64 *b)\n{\n\tu64 b4 = b[4];\n\tu64 b0 = b[0];\n\tu64 b4_ = b4 & 0x7ffffffffffffLLU;\n\tu64 b0_ = b0 + 19 * (b4 >> 51);\n\tb[4] = b4_;\n\tb[0] = b0_;\n}\n\nstatic __always_inline void fproduct_copy_from_wide_(u64 *output, u128 *input)\n{\n\t{\n\t\tu128 xi = input[0];\n\t\toutput[0] = ((u64)(xi));\n\t}\n\t{\n\t\tu128 xi = input[1];\n\t\toutput[1] = ((u64)(xi));\n\t}\n\t{\n\t\tu128 xi = input[2];\n\t\toutput[2] = ((u64)(xi));\n\t}\n\t{\n\t\tu128 xi = input[3];\n\t\toutput[3] = ((u64)(xi));\n\t}\n\t{\n\t\tu128 xi = input[4];\n\t\toutput[4] = ((u64)(xi));\n\t}\n}\n\nstatic __always_inline void\nfproduct_sum_scalar_multiplication_(u128 *output, u64 *input, u64 s)\n{\n\toutput[0] += (u128)input[0] * s;\n\toutput[1] += (u128)input[1] * s;\n\toutput[2] += (u128)input[2] * s;\n\toutput[3] += (u128)input[3] * s;\n\toutput[4] += (u128)input[4] * s;\n}\n\nstatic __always_inline void fproduct_carry_wide_(u128 *tmp)\n{\n\t{\n\t\tu32 ctr = 0;\n\t\tu128 tctr = tmp[ctr];\n\t\tu128 tctrp1 = tmp[ctr + 1];\n\t\tu64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;\n\t\tu128 c = ((tctr) >> (51));\n\t\ttmp[ctr] = ((u128)(r0));\n\t\ttmp[ctr + 1] = ((tctrp1) + (c));\n\t}\n\t{\n\t\tu32 ctr = 1;\n\t\tu128 tctr = tmp[ctr];\n\t\tu128 tctrp1 = tmp[ctr + 1];\n\t\tu64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;\n\t\tu128 c = ((tctr) >> (51));\n\t\ttmp[ctr] = ((u128)(r0));\n\t\ttmp[ctr + 1] = ((tctrp1) + (c));\n\t}\n\n\t{\n\t\tu32 ctr = 2;\n\t\tu128 tctr = tmp[ctr];\n\t\tu128 tctrp1 = tmp[ctr + 1];\n\t\tu64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;\n\t\tu128 c = ((tctr) >> (51));\n\t\ttmp[ctr] = ((u128)(r0));\n\t\ttmp[ctr + 1] = ((tctrp1) + (c));\n\t}\n\t{\n\t\tu32 ctr = 3;\n\t\tu128 tctr = tmp[ctr];\n\t\tu128 tctrp1 = tmp[ctr + 1];\n\t\tu64 r0 = ((u64)(tctr)) & 0x7ffffffffffffLLU;\n\t\tu128 c = ((tctr) >> (51));\n\t\ttmp[ctr] = ((u128)(r0));\n\t\ttmp[ctr + 1] = ((tctrp1) + (c));\n\t}\n}\n\nstatic __always_inline void fmul_shift_reduce(u64 *output)\n{\n\tu64 tmp = output[4];\n\tu64 b0;\n\t{\n\t\tu32 ctr = 5 - 0 - 1;\n\t\tu64 z = output[ctr - 1];\n\t\toutput[ctr] = z;\n\t}\n\t{\n\t\tu32 ctr = 5 - 1 - 1;\n\t\tu64 z = output[ctr - 1];\n\t\toutput[ctr] = z;\n\t}\n\t{\n\t\tu32 ctr = 5 - 2 - 1;\n\t\tu64 z = output[ctr - 1];\n\t\toutput[ctr] = z;\n\t}\n\t{\n\t\tu32 ctr = 5 - 3 - 1;\n\t\tu64 z = output[ctr - 1];\n\t\toutput[ctr] = z;\n\t}\n\toutput[0] = tmp;\n\tb0 = output[0];\n\toutput[0] = 19 * b0;\n}\n\nstatic __always_inline void fmul_mul_shift_reduce_(u128 *output, u64 *input,\n\t\t\t\t\t\t   u64 *input21)\n{\n\tu32 i;\n\tu64 input2i;\n\t{\n\t\tu64 input2i = input21[0];\n\t\tfproduct_sum_scalar_multiplication_(output, input, input2i);\n\t\tfmul_shift_reduce(input);\n\t}\n\t{\n\t\tu64 input2i = input21[1];\n\t\tfproduct_sum_scalar_multiplication_(output, input, input2i);\n\t\tfmul_shift_reduce(input);\n\t}\n\t{\n\t\tu64 input2i = input21[2];\n\t\tfproduct_sum_scalar_multiplication_(output, input, input2i);\n\t\tfmul_shift_reduce(input);\n\t}\n\t{\n\t\tu64 input2i = input21[3];\n\t\tfproduct_sum_scalar_multiplication_(output, input, input2i);\n\t\tfmul_shift_reduce(input);\n\t}\n\ti = 4;\n\tinput2i = input21[i];\n\tfproduct_sum_scalar_multiplication_(output, input, input2i);\n}\n\nstatic __always_inline void fmul_fmul(u64 *output, u64 *input, u64 *input21)\n{\n\tu64 tmp[5] = { input[0], input[1], input[2], input[3], input[4] };\n\t{\n\t\tu128 b4;\n\t\tu128 b0;\n\t\tu128 b4_;\n\t\tu128 b0_;\n\t\tu64 i0;\n\t\tu64 i1;\n\t\tu64 i0_;\n\t\tu64 i1_;\n\t\tu128 t[5] = { 0 };\n\t\tfmul_mul_shift_reduce_(t, tmp, input21);\n\t\tfproduct_carry_wide_(t);\n\t\tb4 = t[4];\n\t\tb0 = t[0];\n\t\tb4_ = ((b4) & (((u128)(0x7ffffffffffffLLU))));\n\t\tb0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51))))))));\n\t\tt[4] = b4_;\n\t\tt[0] = b0_;\n\t\tfproduct_copy_from_wide_(output, t);\n\t\ti0 = output[0];\n\t\ti1 = output[1];\n\t\ti0_ = i0 & 0x7ffffffffffffLLU;\n\t\ti1_ = i1 + (i0 >> 51);\n\t\toutput[0] = i0_;\n\t\toutput[1] = i1_;\n\t}\n}\n\nstatic __always_inline void fsquare_fsquare__(u128 *tmp, u64 *output)\n{\n\tu64 r0 = output[0];\n\tu64 r1 = output[1];\n\tu64 r2 = output[2];\n\tu64 r3 = output[3];\n\tu64 r4 = output[4];\n\tu64 d0 = r0 * 2;\n\tu64 d1 = r1 * 2;\n\tu64 d2 = r2 * 2 * 19;\n\tu64 d419 = r4 * 19;\n\tu64 d4 = d419 * 2;\n\tu128 s0 = ((((((u128)(r0) * (r0))) + (((u128)(d4) * (r1))))) +\n\t\t   (((u128)(d2) * (r3))));\n\tu128 s1 = ((((((u128)(d0) * (r1))) + (((u128)(d4) * (r2))))) +\n\t\t   (((u128)(r3 * 19) * (r3))));\n\tu128 s2 = ((((((u128)(d0) * (r2))) + (((u128)(r1) * (r1))))) +\n\t\t   (((u128)(d4) * (r3))));\n\tu128 s3 = ((((((u128)(d0) * (r3))) + (((u128)(d1) * (r2))))) +\n\t\t   (((u128)(r4) * (d419))));\n\tu128 s4 = ((((((u128)(d0) * (r4))) + (((u128)(d1) * (r3))))) +\n\t\t   (((u128)(r2) * (r2))));\n\ttmp[0] = s0;\n\ttmp[1] = s1;\n\ttmp[2] = s2;\n\ttmp[3] = s3;\n\ttmp[4] = s4;\n}\n\nstatic __always_inline void fsquare_fsquare_(u128 *tmp, u64 *output)\n{\n\tu128 b4;\n\tu128 b0;\n\tu128 b4_;\n\tu128 b0_;\n\tu64 i0;\n\tu64 i1;\n\tu64 i0_;\n\tu64 i1_;\n\tfsquare_fsquare__(tmp, output);\n\tfproduct_carry_wide_(tmp);\n\tb4 = tmp[4];\n\tb0 = tmp[0];\n\tb4_ = ((b4) & (((u128)(0x7ffffffffffffLLU))));\n\tb0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51))))))));\n\ttmp[4] = b4_;\n\ttmp[0] = b0_;\n\tfproduct_copy_from_wide_(output, tmp);\n\ti0 = output[0];\n\ti1 = output[1];\n\ti0_ = i0 & 0x7ffffffffffffLLU;\n\ti1_ = i1 + (i0 >> 51);\n\toutput[0] = i0_;\n\toutput[1] = i1_;\n}\n\nstatic __always_inline void fsquare_fsquare_times_(u64 *output, u128 *tmp,\n\t\t\t\t\t\t   u32 count1)\n{\n\tu32 i;\n\tfsquare_fsquare_(tmp, output);\n\tfor (i = 1; i < count1; ++i)\n\t\tfsquare_fsquare_(tmp, output);\n}\n\nstatic __always_inline void fsquare_fsquare_times(u64 *output, u64 *input,\n\t\t\t\t\t\t  u32 count1)\n{\n\tu128 t[5];\n\tmemcpy(output, input, 5 * sizeof(*input));\n\tfsquare_fsquare_times_(output, t, count1);\n}\n\nstatic __always_inline void fsquare_fsquare_times_inplace(u64 *output,\n\t\t\t\t\t\t\t  u32 count1)\n{\n\tu128 t[5];\n\tfsquare_fsquare_times_(output, t, count1);\n}\n\nstatic __always_inline void crecip_crecip(u64 *out, u64 *z)\n{\n\tu64 buf[20] = { 0 };\n\tu64 *a0 = buf;\n\tu64 *t00 = buf + 5;\n\tu64 *b0 = buf + 10;\n\tu64 *t01;\n\tu64 *b1;\n\tu64 *c0;\n\tu64 *a;\n\tu64 *t0;\n\tu64 *b;\n\tu64 *c;\n\tfsquare_fsquare_times(a0, z, 1);\n\tfsquare_fsquare_times(t00, a0, 2);\n\tfmul_fmul(b0, t00, z);\n\tfmul_fmul(a0, b0, a0);\n\tfsquare_fsquare_times(t00, a0, 1);\n\tfmul_fmul(b0, t00, b0);\n\tfsquare_fsquare_times(t00, b0, 5);\n\tt01 = buf + 5;\n\tb1 = buf + 10;\n\tc0 = buf + 15;\n\tfmul_fmul(b1, t01, b1);\n\tfsquare_fsquare_times(t01, b1, 10);\n\tfmul_fmul(c0, t01, b1);\n\tfsquare_fsquare_times(t01, c0, 20);\n\tfmul_fmul(t01, t01, c0);\n\tfsquare_fsquare_times_inplace(t01, 10);\n\tfmul_fmul(b1, t01, b1);\n\tfsquare_fsquare_times(t01, b1, 50);\n\ta = buf;\n\tt0 = buf + 5;\n\tb = buf + 10;\n\tc = buf + 15;\n\tfmul_fmul(c, t0, b);\n\tfsquare_fsquare_times(t0, c, 100);\n\tfmul_fmul(t0, t0, c);\n\tfsquare_fsquare_times_inplace(t0, 50);\n\tfmul_fmul(t0, t0, b);\n\tfsquare_fsquare_times_inplace(t0, 5);\n\tfmul_fmul(out, t0, a);\n}\n\nstatic __always_inline void fsum(u64 *a, u64 *b)\n{\n\ta[0] += b[0];\n\ta[1] += b[1];\n\ta[2] += b[2];\n\ta[3] += b[3];\n\ta[4] += b[4];\n}\n\nstatic __always_inline void fdifference(u64 *a, u64 *b)\n{\n\tu64 tmp[5] = { 0 };\n\tu64 b0;\n\tu64 b1;\n\tu64 b2;\n\tu64 b3;\n\tu64 b4;\n\tmemcpy(tmp, b, 5 * sizeof(*b));\n\tb0 = tmp[0];\n\tb1 = tmp[1];\n\tb2 = tmp[2];\n\tb3 = tmp[3];\n\tb4 = tmp[4];\n\ttmp[0] = b0 + 0x3fffffffffff68LLU;\n\ttmp[1] = b1 + 0x3ffffffffffff8LLU;\n\ttmp[2] = b2 + 0x3ffffffffffff8LLU;\n\ttmp[3] = b3 + 0x3ffffffffffff8LLU;\n\ttmp[4] = b4 + 0x3ffffffffffff8LLU;\n\t{\n\t\tu64 xi = a[0];\n\t\tu64 yi = tmp[0];\n\t\ta[0] = yi - xi;\n\t}\n\t{\n\t\tu64 xi = a[1];\n\t\tu64 yi = tmp[1];\n\t\ta[1] = yi - xi;\n\t}\n\t{\n\t\tu64 xi = a[2];\n\t\tu64 yi = tmp[2];\n\t\ta[2] = yi - xi;\n\t}\n\t{\n\t\tu64 xi = a[3];\n\t\tu64 yi = tmp[3];\n\t\ta[3] = yi - xi;\n\t}\n\t{\n\t\tu64 xi = a[4];\n\t\tu64 yi = tmp[4];\n\t\ta[4] = yi - xi;\n\t}\n}\n\nstatic __always_inline void fscalar(u64 *output, u64 *b, u64 s)\n{\n\tu128 tmp[5];\n\tu128 b4;\n\tu128 b0;\n\tu128 b4_;\n\tu128 b0_;\n\t{\n\t\tu64 xi = b[0];\n\t\ttmp[0] = ((u128)(xi) * (s));\n\t}\n\t{\n\t\tu64 xi = b[1];\n\t\ttmp[1] = ((u128)(xi) * (s));\n\t}\n\t{\n\t\tu64 xi = b[2];\n\t\ttmp[2] = ((u128)(xi) * (s));\n\t}\n\t{\n\t\tu64 xi = b[3];\n\t\ttmp[3] = ((u128)(xi) * (s));\n\t}\n\t{\n\t\tu64 xi = b[4];\n\t\ttmp[4] = ((u128)(xi) * (s));\n\t}\n\tfproduct_carry_wide_(tmp);\n\tb4 = tmp[4];\n\tb0 = tmp[0];\n\tb4_ = ((b4) & (((u128)(0x7ffffffffffffLLU))));\n\tb0_ = ((b0) + (((u128)(19) * (((u64)(((b4) >> (51))))))));\n\ttmp[4] = b4_;\n\ttmp[0] = b0_;\n\tfproduct_copy_from_wide_(output, tmp);\n}\n\nstatic __always_inline void fmul(u64 *output, u64 *a, u64 *b)\n{\n\tfmul_fmul(output, a, b);\n}\n\nstatic __always_inline void crecip(u64 *output, u64 *input)\n{\n\tcrecip_crecip(output, input);\n}\n\nstatic __always_inline void point_swap_conditional_step(u64 *a, u64 *b,\n\t\t\t\t\t\t\tu64 swap1, u32 ctr)\n{\n\tu32 i = ctr - 1;\n\tu64 ai = a[i];\n\tu64 bi = b[i];\n\tu64 x = swap1 & (ai ^ bi);\n\tu64 ai1 = ai ^ x;\n\tu64 bi1 = bi ^ x;\n\ta[i] = ai1;\n\tb[i] = bi1;\n}\n\nstatic __always_inline void point_swap_conditional5(u64 *a, u64 *b, u64 swap1)\n{\n\tpoint_swap_conditional_step(a, b, swap1, 5);\n\tpoint_swap_conditional_step(a, b, swap1, 4);\n\tpoint_swap_conditional_step(a, b, swap1, 3);\n\tpoint_swap_conditional_step(a, b, swap1, 2);\n\tpoint_swap_conditional_step(a, b, swap1, 1);\n}\n\nstatic __always_inline void point_swap_conditional(u64 *a, u64 *b, u64 iswap)\n{\n\tu64 swap1 = 0 - iswap;\n\tpoint_swap_conditional5(a, b, swap1);\n\tpoint_swap_conditional5(a + 5, b + 5, swap1);\n}\n\nstatic __always_inline void point_copy(u64 *output, u64 *input)\n{\n\tmemcpy(output, input, 5 * sizeof(*input));\n\tmemcpy(output + 5, input + 5, 5 * sizeof(*input));\n}\n\nstatic __always_inline void addanddouble_fmonty(u64 *pp, u64 *ppq, u64 *p,\n\t\t\t\t\t\tu64 *pq, u64 *qmqp)\n{\n\tu64 *qx = qmqp;\n\tu64 *x2 = pp;\n\tu64 *z2 = pp + 5;\n\tu64 *x3 = ppq;\n\tu64 *z3 = ppq + 5;\n\tu64 *x = p;\n\tu64 *z = p + 5;\n\tu64 *xprime = pq;\n\tu64 *zprime = pq + 5;\n\tu64 buf[40] = { 0 };\n\tu64 *origx = buf;\n\tu64 *origxprime0 = buf + 5;\n\tu64 *xxprime0;\n\tu64 *zzprime0;\n\tu64 *origxprime;\n\txxprime0 = buf + 25;\n\tzzprime0 = buf + 30;\n\tmemcpy(origx, x, 5 * sizeof(*x));\n\tfsum(x, z);\n\tfdifference(z, origx);\n\tmemcpy(origxprime0, xprime, 5 * sizeof(*xprime));\n\tfsum(xprime, zprime);\n\tfdifference(zprime, origxprime0);\n\tfmul(xxprime0, xprime, z);\n\tfmul(zzprime0, x, zprime);\n\torigxprime = buf + 5;\n\t{\n\t\tu64 *xx0;\n\t\tu64 *zz0;\n\t\tu64 *xxprime;\n\t\tu64 *zzprime;\n\t\tu64 *zzzprime;\n\t\txx0 = buf + 15;\n\t\tzz0 = buf + 20;\n\t\txxprime = buf + 25;\n\t\tzzprime = buf + 30;\n\t\tzzzprime = buf + 35;\n\t\tmemcpy(origxprime, xxprime, 5 * sizeof(*xxprime));\n\t\tfsum(xxprime, zzprime);\n\t\tfdifference(zzprime, origxprime);\n\t\tfsquare_fsquare_times(x3, xxprime, 1);\n\t\tfsquare_fsquare_times(zzzprime, zzprime, 1);\n\t\tfmul(z3, zzzprime, qx);\n\t\tfsquare_fsquare_times(xx0, x, 1);\n\t\tfsquare_fsquare_times(zz0, z, 1);\n\t\t{\n\t\t\tu64 *zzz;\n\t\t\tu64 *xx;\n\t\t\tu64 *zz;\n\t\t\tu64 scalar;\n\t\t\tzzz = buf + 10;\n\t\t\txx = buf + 15;\n\t\t\tzz = buf + 20;\n\t\t\tfmul(x2, xx, zz);\n\t\t\tfdifference(zz, xx);\n\t\t\tscalar = 121665;\n\t\t\tfscalar(zzz, zz, scalar);\n\t\t\tfsum(zzz, xx);\n\t\t\tfmul(z2, zzz, zz);\n\t\t}\n\t}\n}\n\nstatic __always_inline void\nladder_smallloop_cmult_small_loop_step(u64 *nq, u64 *nqpq, u64 *nq2, u64 *nqpq2,\n\t\t\t\t       u64 *q, u8 byt)\n{\n\tu64 bit0 = (u64)(byt >> 7);\n\tu64 bit;\n\tpoint_swap_conditional(nq, nqpq, bit0);\n\taddanddouble_fmonty(nq2, nqpq2, nq, nqpq, q);\n\tbit = (u64)(byt >> 7);\n\tpoint_swap_conditional(nq2, nqpq2, bit);\n}\n\nstatic __always_inline void\nladder_smallloop_cmult_small_loop_double_step(u64 *nq, u64 *nqpq, u64 *nq2,\n\t\t\t\t\t      u64 *nqpq2, u64 *q, u8 byt)\n{\n\tu8 byt1;\n\tladder_smallloop_cmult_small_loop_step(nq, nqpq, nq2, nqpq2, q, byt);\n\tbyt1 = byt << 1;\n\tladder_smallloop_cmult_small_loop_step(nq2, nqpq2, nq, nqpq, q, byt1);\n}\n\nstatic __always_inline void\nladder_smallloop_cmult_small_loop(u64 *nq, u64 *nqpq, u64 *nq2, u64 *nqpq2,\n\t\t\t\t  u64 *q, u8 byt, u32 i)\n{\n\twhile (i--) {\n\t\tladder_smallloop_cmult_small_loop_double_step(nq, nqpq, nq2,\n\t\t\t\t\t\t\t      nqpq2, q, byt);\n\t\tbyt <<= 2;\n\t}\n}\n\nstatic __always_inline void ladder_bigloop_cmult_big_loop(u8 *n1, u64 *nq,\n\t\t\t\t\t\t\t  u64 *nqpq, u64 *nq2,\n\t\t\t\t\t\t\t  u64 *nqpq2, u64 *q,\n\t\t\t\t\t\t\t  u32 i)\n{\n\twhile (i--) {\n\t\tu8 byte = n1[i];\n\t\tladder_smallloop_cmult_small_loop(nq, nqpq, nq2, nqpq2, q,\n\t\t\t\t\t\t  byte, 4);\n\t}\n}\n\nstatic void ladder_cmult(u64 *result, u8 *n1, u64 *q)\n{\n\tu64 point_buf[40] = { 0 };\n\tu64 *nq = point_buf;\n\tu64 *nqpq = point_buf + 10;\n\tu64 *nq2 = point_buf + 20;\n\tu64 *nqpq2 = point_buf + 30;\n\tpoint_copy(nqpq, q);\n\tnq[0] = 1;\n\tladder_bigloop_cmult_big_loop(n1, nq, nqpq, nq2, nqpq2, q, 32);\n\tpoint_copy(result, nq);\n}\n\nstatic __always_inline void format_fexpand(u64 *output, const u8 *input)\n{\n\tconst u8 *x00 = input + 6;\n\tconst u8 *x01 = input + 12;\n\tconst u8 *x02 = input + 19;\n\tconst u8 *x0 = input + 24;\n\tu64 i0, i1, i2, i3, i4, output0, output1, output2, output3, output4;\n\ti0 = get_unaligned_le64(input);\n\ti1 = get_unaligned_le64(x00);\n\ti2 = get_unaligned_le64(x01);\n\ti3 = get_unaligned_le64(x02);\n\ti4 = get_unaligned_le64(x0);\n\toutput0 = i0 & 0x7ffffffffffffLLU;\n\toutput1 = i1 >> 3 & 0x7ffffffffffffLLU;\n\toutput2 = i2 >> 6 & 0x7ffffffffffffLLU;\n\toutput3 = i3 >> 1 & 0x7ffffffffffffLLU;\n\toutput4 = i4 >> 12 & 0x7ffffffffffffLLU;\n\toutput[0] = output0;\n\toutput[1] = output1;\n\toutput[2] = output2;\n\toutput[3] = output3;\n\toutput[4] = output4;\n}\n\nstatic __always_inline void format_fcontract_first_carry_pass(u64 *input)\n{\n\tu64 t0 = input[0];\n\tu64 t1 = input[1];\n\tu64 t2 = input[2];\n\tu64 t3 = input[3];\n\tu64 t4 = input[4];\n\tu64 t1_ = t1 + (t0 >> 51);\n\tu64 t0_ = t0 & 0x7ffffffffffffLLU;\n\tu64 t2_ = t2 + (t1_ >> 51);\n\tu64 t1__ = t1_ & 0x7ffffffffffffLLU;\n\tu64 t3_ = t3 + (t2_ >> 51);\n\tu64 t2__ = t2_ & 0x7ffffffffffffLLU;\n\tu64 t4_ = t4 + (t3_ >> 51);\n\tu64 t3__ = t3_ & 0x7ffffffffffffLLU;\n\tinput[0] = t0_;\n\tinput[1] = t1__;\n\tinput[2] = t2__;\n\tinput[3] = t3__;\n\tinput[4] = t4_;\n}\n\nstatic __always_inline void format_fcontract_first_carry_full(u64 *input)\n{\n\tformat_fcontract_first_carry_pass(input);\n\tmodulo_carry_top(input);\n}\n\nstatic __always_inline void format_fcontract_second_carry_pass(u64 *input)\n{\n\tu64 t0 = input[0];\n\tu64 t1 = input[1];\n\tu64 t2 = input[2];\n\tu64 t3 = input[3];\n\tu64 t4 = input[4];\n\tu64 t1_ = t1 + (t0 >> 51);\n\tu64 t0_ = t0 & 0x7ffffffffffffLLU;\n\tu64 t2_ = t2 + (t1_ >> 51);\n\tu64 t1__ = t1_ & 0x7ffffffffffffLLU;\n\tu64 t3_ = t3 + (t2_ >> 51);\n\tu64 t2__ = t2_ & 0x7ffffffffffffLLU;\n\tu64 t4_ = t4 + (t3_ >> 51);\n\tu64 t3__ = t3_ & 0x7ffffffffffffLLU;\n\tinput[0] = t0_;\n\tinput[1] = t1__;\n\tinput[2] = t2__;\n\tinput[3] = t3__;\n\tinput[4] = t4_;\n}\n\nstatic __always_inline void format_fcontract_second_carry_full(u64 *input)\n{\n\tu64 i0;\n\tu64 i1;\n\tu64 i0_;\n\tu64 i1_;\n\tformat_fcontract_second_carry_pass(input);\n\tmodulo_carry_top(input);\n\ti0 = input[0];\n\ti1 = input[1];\n\ti0_ = i0 & 0x7ffffffffffffLLU;\n\ti1_ = i1 + (i0 >> 51);\n\tinput[0] = i0_;\n\tinput[1] = i1_;\n}\n\nstatic __always_inline void format_fcontract_trim(u64 *input)\n{\n\tu64 a0 = input[0];\n\tu64 a1 = input[1];\n\tu64 a2 = input[2];\n\tu64 a3 = input[3];\n\tu64 a4 = input[4];\n\tu64 mask0 = u64_gte_mask(a0, 0x7ffffffffffedLLU);\n\tu64 mask1 = u64_eq_mask(a1, 0x7ffffffffffffLLU);\n\tu64 mask2 = u64_eq_mask(a2, 0x7ffffffffffffLLU);\n\tu64 mask3 = u64_eq_mask(a3, 0x7ffffffffffffLLU);\n\tu64 mask4 = u64_eq_mask(a4, 0x7ffffffffffffLLU);\n\tu64 mask = (((mask0 & mask1) & mask2) & mask3) & mask4;\n\tu64 a0_ = a0 - (0x7ffffffffffedLLU & mask);\n\tu64 a1_ = a1 - (0x7ffffffffffffLLU & mask);\n\tu64 a2_ = a2 - (0x7ffffffffffffLLU & mask);\n\tu64 a3_ = a3 - (0x7ffffffffffffLLU & mask);\n\tu64 a4_ = a4 - (0x7ffffffffffffLLU & mask);\n\tinput[0] = a0_;\n\tinput[1] = a1_;\n\tinput[2] = a2_;\n\tinput[3] = a3_;\n\tinput[4] = a4_;\n}\n\nstatic __always_inline void format_fcontract_store(u8 *output, u64 *input)\n{\n\tu64 t0 = input[0];\n\tu64 t1 = input[1];\n\tu64 t2 = input[2];\n\tu64 t3 = input[3];\n\tu64 t4 = input[4];\n\tu64 o0 = t1 << 51 | t0;\n\tu64 o1 = t2 << 38 | t1 >> 13;\n\tu64 o2 = t3 << 25 | t2 >> 26;\n\tu64 o3 = t4 << 12 | t3 >> 39;\n\tu8 *b0 = output;\n\tu8 *b1 = output + 8;\n\tu8 *b2 = output + 16;\n\tu8 *b3 = output + 24;\n\tput_unaligned_le64(o0, b0);\n\tput_unaligned_le64(o1, b1);\n\tput_unaligned_le64(o2, b2);\n\tput_unaligned_le64(o3, b3);\n}\n\nstatic __always_inline void format_fcontract(u8 *output, u64 *input)\n{\n\tformat_fcontract_first_carry_full(input);\n\tformat_fcontract_second_carry_full(input);\n\tformat_fcontract_trim(input);\n\tformat_fcontract_store(output, input);\n}\n\nstatic __always_inline void format_scalar_of_point(u8 *scalar, u64 *point)\n{\n\tu64 *x = point;\n\tu64 *z = point + 5;\n\tu64 buf[10] __aligned(32) = { 0 };\n\tu64 *zmone = buf;\n\tu64 *sc = buf + 5;\n\tcrecip(zmone, z);\n\tfmul(sc, x, zmone);\n\tformat_fcontract(scalar, sc);\n}\n\nstatic void curve25519_generic(u8 mypublic[CURVE25519_KEY_SIZE],\n\t\t\t       const u8 secret[CURVE25519_KEY_SIZE],\n\t\t\t       const u8 basepoint[CURVE25519_KEY_SIZE])\n{\n\tu64 buf0[10] __aligned(32) = { 0 };\n\tu64 *x0 = buf0;\n\tu64 *z = buf0 + 5;\n\tu64 *q;\n\tformat_fexpand(x0, basepoint);\n\tz[0] = 1;\n\tq = buf0;\n\t{\n\t\tu8 e[32] __aligned(32) = { 0 };\n\t\tu8 *scalar;\n\t\tmemcpy(e, secret, 32);\n\t\tcurve25519_clamp_secret(e);\n\t\tscalar = e;\n\t\t{\n\t\t\tu64 buf[15] = { 0 };\n\t\t\tu64 *nq = buf;\n\t\t\tu64 *x = nq;\n\t\t\tx[0] = 1;\n\t\t\tladder_cmult(nq, scalar, q);\n\t\t\tformat_scalar_of_point(mypublic, nq);\n\t\t\tmemzero_explicit(buf, sizeof(buf));\n\t\t}\n\t\tmemzero_explicit(e, sizeof(e));\n\t}\n\tmemzero_explicit(buf0, sizeof(buf0));\n}\n"
  },
  {
    "path": "src/curve25519.c",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2018-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include \"curve25519.h\"\n\n#include <stdint.h>\n#include <string.h>\n\n#ifndef __BYTE_ORDER__\n#include <sys/param.h>\n#if !defined(BYTE_ORDER) || !defined(BIG_ENDIAN) || !defined(LITTLE_ENDIAN)\n#error \"Unable to determine endianness.\"\n#endif\n#define __BYTE_ORDER__ BYTE_ORDER\n#define __ORDER_BIG_ENDIAN__ BIG_ENDIAN\n#define __ORDER_LITTLE_ENDIAN__ LITTLE_ENDIAN\n#endif\n\n#ifdef __linux__\n#include <linux/types.h>\ntypedef __u64 u64;\ntypedef __u32 u32;\ntypedef __u8 u8;\ntypedef __s64 s64;\n#else\ntypedef uint64_t u64, __le64;\ntypedef uint32_t u32, __le32;\ntypedef uint8_t u8;\ntypedef int64_t s64;\n#endif\n#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n#define le64_to_cpup(a) __builtin_bswap64(*(a))\n#define le32_to_cpup(a) __builtin_bswap32(*(a))\n#define cpu_to_le64(a) __builtin_bswap64(a)\n#else\n#define le64_to_cpup(a) (*(a))\n#define le32_to_cpup(a) (*(a))\n#define cpu_to_le64(a) (a)\n#endif\n#ifndef __unused\n#define __unused  __attribute__((unused))\n#endif\n#ifndef __always_inline\n#define __always_inline __inline __attribute__((__always_inline__))\n#endif\n#ifndef noinline\n#define noinline __attribute__((noinline))\n#endif\n#ifndef __aligned\n#define __aligned(x) __attribute__((aligned(x)))\n#endif\n#ifndef __force\n#define __force\n#endif\n\nstatic __always_inline __unused __le32 get_unaligned_le32(const u8 *a)\n{\n\t__le32 l;\n\t__builtin_memcpy(&l, a, sizeof(l));\n\treturn le32_to_cpup(&l);\n}\nstatic __always_inline __unused __le64 get_unaligned_le64(const u8 *a)\n{\n\t__le64 l;\n\t__builtin_memcpy(&l, a, sizeof(l));\n\treturn le64_to_cpup(&l);\n}\nstatic __always_inline __unused void put_unaligned_le64(u64 s, u8 *d)\n{\n\t__le64 l = cpu_to_le64(s);\n\t__builtin_memcpy(d, &l, sizeof(l));\n}\n\nstatic noinline void memzero_explicit(void *s, size_t count)\n{\n\tmemset(s, 0, count);\n\tasm volatile(\"\": :\"r\"(s) : \"memory\");\n}\n\n#ifdef __SIZEOF_INT128__\n#include \"curve25519-hacl64.h\"\n#else\n#include \"curve25519-fiat32.h\"\n#endif\n\nvoid curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE])\n{\n\tstatic const uint8_t basepoint[CURVE25519_KEY_SIZE] __aligned(sizeof(uintptr_t)) = { 9 };\n\n\tcurve25519(pub, secret, basepoint);\n}\n\nvoid curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE], const uint8_t basepoint[static CURVE25519_KEY_SIZE])\n{\n\tcurve25519_generic(mypublic, secret, basepoint);\n}\n"
  },
  {
    "path": "src/curve25519.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 OR MIT */\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#ifndef CURVE25519_H\n#define CURVE25519_H\n\n#include <stdint.h>\n#include <sys/types.h>\n\nenum curve25519_lengths {\n\tCURVE25519_KEY_SIZE = 32\n};\n\nvoid curve25519(uint8_t mypublic[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE], const uint8_t basepoint[static CURVE25519_KEY_SIZE]);\nvoid curve25519_generate_public(uint8_t pub[static CURVE25519_KEY_SIZE], const uint8_t secret[static CURVE25519_KEY_SIZE]);\nstatic inline void curve25519_clamp_secret(uint8_t secret[static CURVE25519_KEY_SIZE])\n{\n\tsecret[0] &= 248;\n\tsecret[31] = (secret[31] & 127) | 64;\n}\n\n#endif\n"
  },
  {
    "path": "src/encoding.c",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n *\n * This is a specialized constant-time base64/hex implementation that resists side-channel attacks.\n */\n\n#include <string.h>\n#include \"encoding.h\"\n\nstatic inline void encode_base64(char dest[static 4], const uint8_t src[static 3])\n{\n\tconst uint8_t input[] = { (src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63 };\n\n\tfor (unsigned int i = 0; i < 4; ++i)\n\t\tdest[i] = input[i] + 'A'\n\t\t\t  + (((25 - input[i]) >> 8) & 6)\n\t\t\t  - (((51 - input[i]) >> 8) & 75)\n\t\t\t  - (((61 - input[i]) >> 8) & 15)\n\t\t\t  + (((62 - input[i]) >> 8) & 3);\n\n}\n\nvoid key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN])\n{\n\tunsigned int i;\n\n\tfor (i = 0; i < WG_KEY_LEN / 3; ++i)\n\t\tencode_base64(&base64[i * 4], &key[i * 3]);\n\tencode_base64(&base64[i * 4], (const uint8_t[]){ key[i * 3 + 0], key[i * 3 + 1], 0 });\n\tbase64[WG_KEY_LEN_BASE64 - 2] = '=';\n\tbase64[WG_KEY_LEN_BASE64 - 1] = '\\0';\n}\n\nstatic inline int decode_base64(const char src[static 4])\n{\n\tint val = 0;\n\n\tfor (unsigned int i = 0; i < 4; ++i)\n\t\tval |= (-1\n\t\t\t    + ((((('A' - 1) - src[i]) & (src[i] - ('Z' + 1))) >> 8) & (src[i] - 64))\n\t\t\t    + ((((('a' - 1) - src[i]) & (src[i] - ('z' + 1))) >> 8) & (src[i] - 70))\n\t\t\t    + ((((('0' - 1) - src[i]) & (src[i] - ('9' + 1))) >> 8) & (src[i] + 5))\n\t\t\t    + ((((('+' - 1) - src[i]) & (src[i] - ('+' + 1))) >> 8) & 63)\n\t\t\t    + ((((('/' - 1) - src[i]) & (src[i] - ('/' + 1))) >> 8) & 64)\n\t\t\t) << (18 - 6 * i);\n\treturn val;\n}\n\nbool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64)\n{\n\tunsigned int i;\n\tvolatile uint8_t ret = 0;\n\tint val;\n\n\tif (strlen(base64) != WG_KEY_LEN_BASE64 - 1 || base64[WG_KEY_LEN_BASE64 - 2] != '=')\n\t\treturn false;\n\n\tfor (i = 0; i < WG_KEY_LEN / 3; ++i) {\n\t\tval = decode_base64(&base64[i * 4]);\n\t\tret |= (uint32_t)val >> 31;\n\t\tkey[i * 3 + 0] = (val >> 16) & 0xff;\n\t\tkey[i * 3 + 1] = (val >> 8) & 0xff;\n\t\tkey[i * 3 + 2] = val & 0xff;\n\t}\n\tval = decode_base64((const char[]){ base64[i * 4 + 0], base64[i * 4 + 1], base64[i * 4 + 2], 'A' });\n\tret |= ((uint32_t)val >> 31) | (val & 0xff);\n\tkey[i * 3 + 0] = (val >> 16) & 0xff;\n\tkey[i * 3 + 1] = (val >> 8) & 0xff;\n\n\treturn 1 & ((ret - 1) >> 8);\n}\n\nvoid key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN])\n{\n\tunsigned int i;\n\n\tfor (i = 0; i < WG_KEY_LEN; ++i) {\n\t\thex[i * 2] = 87U + (key[i] >> 4) + ((((key[i] >> 4) - 10U) >> 8) & ~38U);\n\t\thex[i * 2 + 1] = 87U + (key[i] & 0xf) + ((((key[i] & 0xf) - 10U) >> 8) & ~38U);\n\t}\n\thex[i * 2] = '\\0';\n}\n\nbool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex)\n{\n\tuint8_t c, c_acc, c_alpha0, c_alpha, c_num0, c_num, c_val;\n\tvolatile uint8_t ret = 0;\n\n\tif (strlen(hex) != WG_KEY_LEN_HEX - 1)\n\t\treturn false;\n\n\tfor (unsigned int i = 0; i < WG_KEY_LEN_HEX - 1; i += 2) {\n\t\tc = (uint8_t)hex[i];\n\t\tc_num = c ^ 48U;\n\t\tc_num0 = (c_num - 10U) >> 8;\n\t\tc_alpha = (c & ~32U) - 55U;\n\t\tc_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;\n\t\tret |= ((c_num0 | c_alpha0) - 1) >> 8;\n\t\tc_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);\n\t\tc_acc = c_val * 16U;\n\n\t\tc = (uint8_t)hex[i + 1];\n\t\tc_num = c ^ 48U;\n\t\tc_num0 = (c_num - 10U) >> 8;\n\t\tc_alpha = (c & ~32U) - 55U;\n\t\tc_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8;\n\t\tret |= ((c_num0 | c_alpha0) - 1) >> 8;\n\t\tc_val = (c_num0 & c_num) | (c_alpha0 & c_alpha);\n\t\tkey[i / 2] = c_acc | c_val;\n\t}\n\n\treturn 1 & ((ret - 1) >> 8);\n}\n\nbool key_is_zero(const uint8_t key[static WG_KEY_LEN])\n{\n\tvolatile uint8_t acc = 0;\n\n\tfor (unsigned int i = 0; i < WG_KEY_LEN; ++i) {\n\t\tacc |= key[i];\n\t\tasm volatile(\"\" : \"=r\"(acc) : \"0\"(acc));\n\t}\n\treturn 1 & ((acc - 1) >> 8);\n}\n"
  },
  {
    "path": "src/encoding.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 OR MIT */\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#ifndef ENCODING_H\n#define ENCODING_H\n\n#include <stdbool.h>\n#include <stdint.h>\n#include \"containers.h\"\n\n#define WG_KEY_LEN_BASE64 ((((WG_KEY_LEN) + 2) / 3) * 4 + 1)\n#define WG_KEY_LEN_HEX (WG_KEY_LEN * 2 + 1)\n\nvoid key_to_base64(char base64[static WG_KEY_LEN_BASE64], const uint8_t key[static WG_KEY_LEN]);\nbool key_from_base64(uint8_t key[static WG_KEY_LEN], const char *base64);\n\nvoid key_to_hex(char hex[static WG_KEY_LEN_HEX], const uint8_t key[static WG_KEY_LEN]);\nbool key_from_hex(uint8_t key[static WG_KEY_LEN], const char *hex);\n\nbool key_is_zero(const uint8_t key[static WG_KEY_LEN]);\n\n#endif\n"
  },
  {
    "path": "src/fuzz/.gitignore",
    "content": "config\nuapi\nstringlist\ncmd\nset\nsetconf\n"
  },
  {
    "path": "src/fuzz/Makefile",
    "content": "# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2018-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n\nFUZZERS := config uapi stringlist cmd set setconf\n\nall: $(FUZZERS)\n\nCFLAGS ?= -O3 -march=native -g\nCFLAGS += -fsanitize=fuzzer -fsanitize=address -std=gnu11 -idirafter ../uapi -D_GNU_SOURCE\nCC := clang\n\nconfig: config.c ../config.c ../encoding.c\n\t$(CC) $(CFLAGS) -o $@ $<\n\nuapi: uapi.c ../ipc.c ../curve25519.c ../encoding.c\n\t$(CC) $(CFLAGS) -o $@ $<\n\nstringlist: stringlist.c ../ipc.c ../curve25519.c ../encoding.c\n\t$(CC) $(CFLAGS) -o $@ $<\n\ncmd: cmd.c $(wildcard ../*.c)\n\t$(CC) $(CFLAGS) -D'RUNSTATEDIR=\"/var/empty\"' -D'main(a,b)=wg_main(a,b)' -o $@ $^\n\nset: set.c ../set.c ../ipc.c ../encoding.c ../curve25519.c ../config.c\n\t$(CC) $(CFLAGS) -o $@ $<\n\nsetconf: setconf.c ../setconf.c ../ipc.c ../encoding.c ../curve25519.c ../config.c\n\t$(CC) $(CFLAGS) -o $@ $<\n\nclean:\n\t$(RM) $(FUZZERS)\n\n.PHONY: all clean\n"
  },
  {
    "path": "src/fuzz/cmd.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2018-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <string.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n\nconst char *__asan_default_options()\n{\n\treturn \"verbosity=1\";\n}\n\nint wg_main(int argc, char *argv[]);\n\nstatic FILE *devnull;\n\nint LLVMFuzzerTestOneInput(const char *data, size_t data_len)\n{\n\tchar *argv[8192] = { 0 }, *args;\n\tsize_t argc = 0;\n\tFILE *fake_stdin = NULL;\n\n\tif (!devnull) {\n\t\tassert((devnull = fopen(\"/dev/null\", \"r+\")));\n\t\tstdin = stdout = stderr = devnull;\n\t}\n\n\tassert((args = malloc(data_len)));\n\tmemcpy(args, data, data_len);\n\tif (data_len)\n\t\targs[data_len - 1] = '\\0';\n\n\tfor (const char *arg = args; argc < 8192 && arg - args < data_len; arg += strlen(arg) + 1) {\n\t\tif (arg[0])\n\t\t\tassert((argv[argc++] = strdup(arg)));\n\t}\n\tif (!argc)\n\t\tassert((argv[argc++] = strdup(\"no argv[0]!\")));\n\tif (argc > 2 && (!strcmp(argv[1], \"show\") || !strcmp(argv[1], \"showconf\") || !strcmp(argv[1], \"set\") || !strcmp(argv[1], \"setconf\") || !strcmp(argv[1], \"addconf\") || !strcmp(argv[1], \"syncconf\"))) {\n\t\tfree(argv[2]);\n\t\tassert((argv[2] = strdup(\"wg0\")));\n\t}\n\tif (argc >= 2 && !strcmp(argv[1], \"pubkey\")) {\n\t\tchar *arg;\n\t\tsize_t len;\n\n\t\tfor (size_t i = 2; i < argc; ++i)\n\t\t\tfree(argv[i]);\n\t\targc = 2;\n\t\targ = args;\n\t\tfor (; !arg[0]; ++arg);\n\t\targ += strlen(arg) + 1;\n\t\tfor (; !arg[0]; ++arg);\n\t\targ += strlen(arg) + 1;\n\t\tlen = data_len - (arg - args);\n\t\tif (len <= 1)\n\t\t\tgoto done;\n\t\tassert((fake_stdin = fmemopen(arg, len - 1, \"r\")));\n\t\tstdin = fake_stdin;\n\t}\n\twg_main(argc, argv);\ndone:\n\tfor (size_t i = 0; i < argc; ++i)\n\t\tfree(argv[i]);\n\tfree(args);\n\tif (fake_stdin)\n\t\tfclose(fake_stdin);\n\treturn 0;\n}\n"
  },
  {
    "path": "src/fuzz/config.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2018-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stdio.h>\n#undef stderr\n#define stderr stdin\n#include \"../config.c\"\n#include \"../encoding.c\"\n#undef stderr\n\n#include <stdint.h>\n#include <string.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include \"../config.h\"\n\nconst char *__asan_default_options()\n{\n\treturn \"verbosity=1\";\n}\n\nint LLVMFuzzerTestOneInput(const uint8_t *data, size_t len)\n{\n\tbool file;\n\tchar *input;\n\n\tif (len < 2)\n\t\treturn 0;\n\tfile = !!(data[0] >> 7);\n\tinput = malloc(len);\n\tif (!input)\n\t\treturn 0;\n\tmemcpy(input, data + 1, len - 1);\n\tinput[len - 1] = '\\0';\n\n\tif (file) {\n\t\tstruct config_ctx ctx;\n\t\tchar *saveptr;\n\n\t\tconfig_read_init(&ctx, false);\n\t\tfor (char *line = strtok_r(input, \"\\n\", &saveptr); line; line = strtok_r(NULL, \"\\n\", &saveptr)) {\n\t\t\tif (!config_read_line(&ctx, line))\n\t\t\t\tconfig_read_init(&ctx, false);\n\t\t}\n\t\tfree_wgdevice(config_read_finish(&ctx));\n\t} else {\n\t\tsize_t spaces = 0;\n\t\tchar **argv, *saveptr;\n\n\t\tfor (char *c = input; *c; ++c) {\n\t\t\tif (*c == ' ')\n\t\t\t\t++spaces;\n\t\t}\n\t\targv = calloc(spaces + 1, sizeof(char *));\n\t\tif (!argv)\n\t\t\tgoto out;\n\t\tspaces = 0;\n\t\tfor (char *token = strtok_r(input, \" \", &saveptr); token; token = strtok_r(NULL, \" \", &saveptr))\n\t\t\targv[spaces++] = token;\n\t\tfree_wgdevice(config_read_cmd(argv, spaces));\n\t\tfree(argv);\n\t}\n\nout:\n\tfree(input);\n\treturn 0;\n}\n"
  },
  {
    "path": "src/fuzz/set.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2018-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stdio.h>\n#undef stderr\n#define stderr stdin\n#define RUNSTATEDIR \"/var/empty\"\n#include \"../curve25519.c\"\n#define parse_allowedips parse_allowedips_ipc\n#include \"../ipc.c\"\n#undef parse_allowedips\n#include \"../encoding.c\"\nstatic FILE *hacked_fopen(const char *pathname, const char *mode);\n#define fopen hacked_fopen\n#include \"../config.c\"\n#include \"../set.c\"\n#undef stderr\n\n#include <string.h>\n#include <stdlib.h>\n#include <assert.h>\n\nconst char *__asan_default_options()\n{\n\treturn \"verbosity=1\";\n}\n\nconst char *PROG_NAME = \"wg\";\n\nstatic FILE *hacked_fopen(const char *pathname, const char *mode)\n{\n\treturn fmemopen((char *)pathname, strlen(pathname), \"r\");\n}\n\nint LLVMFuzzerTestOneInput(const char *data, size_t data_len)\n{\n\tchar *argv[8192] = { \"set\", \"wg0\" }, *args;\n\tsize_t argc = 2;\n\n\tif (!data_len)\n\t\treturn 0;\n\n\tassert((args = malloc(data_len)));\n\tmemcpy(args, data, data_len);\n\targs[data_len - 1] = '\\0';\n\n\tfor (char *arg = strtok(args, \" \\t\\n\\r\"); arg && argc < 8192; arg = strtok(NULL, \" \\t\\n\\r\")) {\n\t\tif (arg[0])\n\t\t\targv[argc++] = arg;\n\t}\n\tset_main(argc, argv);\n\tfree(args);\n\treturn 0;\n}\n"
  },
  {
    "path": "src/fuzz/setconf.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2018-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stdio.h>\n#undef stderr\n#define stderr stdin\n#define RUNSTATEDIR \"/var/empty\"\n#include \"../curve25519.c\"\n#define parse_allowedips parse_allowedips_ipc\n#include \"../ipc.c\"\n#undef parse_allowedips\n#include \"../encoding.c\"\n#include \"../config.c\"\nstatic FILE *hacked_fopen(const char *pathname, const char *mode);\n#define fopen hacked_fopen\n#include \"../setconf.c\"\n#undef fopen\n#undef stderr\n\n#include <string.h>\n#include <stdlib.h>\n#include <assert.h>\n\nconst char *__asan_default_options()\n{\n\treturn \"verbosity=1\";\n}\n\nconst char *PROG_NAME = \"wg\";\n\nstruct hacked_pointers {\n\tconst char *data;\n\tsize_t data_len;\n};\n\nstatic FILE *hacked_fopen(const char *pathname, const char *mode)\n{\n\tstruct hacked_pointers *h = (struct hacked_pointers *)strtoul(pathname, NULL, 10);\n\treturn fmemopen((char *)h->data, h->data_len, \"r\");\n}\n\nint LLVMFuzzerTestOneInput(const char *data, size_t data_len)\n{\n\tchar strptr[32];\n\tchar *argv[3] = { \"setconf\", \"wg0\", strptr };\n\tstruct hacked_pointers h = { data, data_len };\n\n\tsnprintf(strptr, sizeof(strptr), \"%lu\", (unsigned long)&h);\n\tsetconf_main(3, argv);\n\treturn 0;\n}\n"
  },
  {
    "path": "src/fuzz/stringlist.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2018-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#define RUNSTATEDIR \"/var/empty\"\n#include \"../curve25519.c\"\n#undef __linux__\n#include \"../ipc.c\"\n#include \"../encoding.c\"\n\n#include <stdint.h>\n#include <string.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <assert.h>\n\nconst char *__asan_default_options()\n{\n\treturn \"verbosity=1\";\n}\n\nint LLVMFuzzerTestOneInput(const uint8_t *data, size_t data_len)\n{\n\tstruct string_list list = { 0 };\n\tchar *interfaces;\n\n\tif (!data_len)\n\t\treturn 0;\n\n\tinterfaces = malloc(data_len);\n\tassert(interfaces);\n\tmemcpy(interfaces, data, data_len);\n\tinterfaces[data_len - 1] = '\\0';\n\n\tfor (char *interface = interfaces; interface - interfaces < data_len; interface += strlen(interface) + 1)\n\t\tassert(string_list_add(&list, interface) == 0);\n\n\tfor (char *interface = interfaces, *interface2 = list.buffer;;) {\n\t\tsize_t len;\n\n\t\tif (interface - interfaces >= data_len) {\n\t\t\tassert(!interface2 || !strlen(interface2));\n\t\t\tbreak;\n\t\t}\n\t\tlen = strlen(interface);\n\t\tif (!len) {\n\t\t\t++interface;\n\t\t\tcontinue;\n\t\t}\n\t\tassert(strlen(interface2) == len);\n\t\tassert(!memcmp(interface, interface2, len + 1));\n\t\tinterface += len + 1;\n\t\tinterface2 += len + 1;\n\t}\n\tfree(list.buffer);\n\tfree(interfaces);\n\treturn 0;\n}\n"
  },
  {
    "path": "src/fuzz/uapi.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2018-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stdio.h>\n#include <sys/stat.h>\nstatic FILE *hacked_userspace_interface_file(const char *iface);\n#define stat(a, b) ({ return hacked_userspace_interface_file(iface); 0; })\n#define RUNSTATEDIR \"/var/empty\"\n#include \"../curve25519.c\"\n#undef __linux__\n#include \"../ipc.c\"\n#include \"../encoding.c\"\n\n#include <stdint.h>\n#include <string.h>\n#include <stdlib.h>\n#include <stdio.h>\n\nconst char *__asan_default_options()\n{\n\treturn \"verbosity=1\";\n}\n\nunion hackiface {\n\tchar ifname[IFNAMSIZ];\n\tstruct {\n\t\tconst uint8_t *data;\n\t\tsize_t len;\n\t};\n};\n\nstatic FILE *hacked_userspace_interface_file(const char *iface)\n{\n\tunion hackiface *hack = (union hackiface *)iface;\n\tFILE *f = fmemopen(NULL, hack->len + 7, \"r+\");\n\tfseek(f, 7, SEEK_SET);\n\tfwrite(hack->data, hack->len, 1, f);\n\tfseek(f, 0, SEEK_SET);\n\tmemcpy(hack->ifname, \"hack\", 5);\n\treturn f;\n}\n\nint LLVMFuzzerTestOneInput(const uint8_t *data, size_t len)\n{\n\tunion hackiface hack = {\n\t\t.data = data,\n\t\t.len = len\n\t};\n\tstruct wgdevice *dev = NULL;\n\n\tuserspace_get_device(&dev, (const char *)&hack);\n\tfree_wgdevice(dev);\n\treturn 0;\n}\n"
  },
  {
    "path": "src/genkey.c",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdbool.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <string.h>\n#include <fcntl.h>\n#ifdef __linux__\n#include <sys/syscall.h>\n#endif\n#ifdef __APPLE__\n#include <AvailabilityMacros.h>\n#ifndef MAC_OS_X_VERSION_10_12\n#define MAC_OS_X_VERSION_10_12 101200\n#endif\n#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12\n#include <sys/random.h>\n#endif\n#endif\n\n#include \"curve25519.h\"\n#include \"encoding.h\"\n#include \"subcommands.h\"\n\n#ifndef _WIN32\nstatic inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len)\n{\n\tssize_t ret = 0;\n\tsize_t i;\n\tint fd;\n\n\tif (len > 256) {\n\t\terrno = EOVERFLOW;\n\t\treturn false;\n\t}\n\n#if defined(__OpenBSD__) || (defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) || (defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)))\n\tif (!getentropy(out, len))\n\t\treturn true;\n#endif\n\n#if defined(__NR_getrandom) && defined(__linux__)\n\tif (syscall(__NR_getrandom, out, len, 0) == (ssize_t)len)\n\t\treturn true;\n#endif\n\n\tfd = open(\"/dev/urandom\", O_RDONLY);\n\tif (fd < 0)\n\t\treturn false;\n\tfor (errno = 0, i = 0; i < len; i += ret, ret = 0) {\n\t\tret = read(fd, out + i, len - i);\n\t\tif (ret <= 0) {\n\t\t\tret = errno ? -errno : -EIO;\n\t\t\tbreak;\n\t\t}\n\t}\n\tclose(fd);\n\terrno = -ret;\n\treturn i == len;\n}\n#else\n#include <ntsecapi.h>\nstatic inline bool __attribute__((__warn_unused_result__)) get_random_bytes(uint8_t *out, size_t len)\n{\n        return RtlGenRandom(out, len);\n}\n#endif\n\nint genkey_main(int argc, const char *argv[])\n{\n\tuint8_t key[WG_KEY_LEN];\n\tchar base64[WG_KEY_LEN_BASE64];\n\tstruct stat stat;\n\n\tif (argc != 1) {\n\t\tfprintf(stderr, \"Usage: %s %s\\n\", PROG_NAME, argv[0]);\n\t\treturn 1;\n\t}\n\n\tif (!fstat(STDOUT_FILENO, &stat) && S_ISREG(stat.st_mode) && stat.st_mode & S_IRWXO)\n\t\tfputs(\"Warning: writing to world accessible file.\\nConsider setting the umask to 077 and trying again.\\n\", stderr);\n\n\tif (!get_random_bytes(key, WG_KEY_LEN)) {\n\t\tperror(\"getrandom\");\n\t\treturn 1;\n\t}\n\tif (!strcmp(argv[0], \"genkey\"))\n\t\tcurve25519_clamp_secret(key);\n\n\tkey_to_base64(base64, key);\n\tputs(base64);\n\treturn 0;\n}\n"
  },
  {
    "path": "src/ipc-freebsd.h",
    "content": "// SPDX-License-Identifier: MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n *\n */\n\n#include <assert.h>\n#include <sys/nv.h>\n#include <sys/sockio.h>\n#include <dev/wg/if_wg.h>\n\n#define IPC_SUPPORTS_KERNEL_INTERFACE\n\nstatic int get_dgram_socket(void)\n{\n\tstatic int sock = -1;\n\tif (sock < 0)\n\t\tsock = socket(AF_LOCAL, SOCK_DGRAM, 0);\n\treturn sock;\n}\n\nstatic int kernel_get_wireguard_interfaces(struct string_list *list)\n{\n\tstruct ifgroupreq ifgr = { .ifgr_name = \"wg\" };\n\tstruct ifg_req *ifg;\n\tint s = get_dgram_socket(), ret = 0;\n\n\tif (s < 0)\n\t\treturn -errno;\n\n\tif (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)\n\t\treturn errno == ENOENT ? 0 : -errno;\n\n\tifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);\n\tif (!ifgr.ifgr_groups)\n\t\treturn -errno;\n\tif (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) {\n\t\tret = -errno;\n\t\tgoto out;\n\t}\n\n\tfor (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) {\n\t\tif ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)\n\t\t\tgoto out;\n\t\tifgr.ifgr_len -= sizeof(struct ifg_req);\n\t}\n\nout:\n\tfree(ifgr.ifgr_groups);\n\treturn ret;\n}\n\nstatic int kernel_get_device(struct wgdevice **device, const char *ifname)\n{\n\tstruct wg_data_io wgd = { 0 };\n\tnvlist_t *nvl_device = NULL;\n\tconst nvlist_t *const *nvl_peers;\n\tstruct wgdevice *dev = NULL;\n\tsize_t size, peer_count, i;\n\tuint64_t number;\n\tconst void *binary;\n\tint ret = 0, s;\n\n\t*device = NULL;\n\ts = get_dgram_socket();\n\tif (s < 0)\n\t\tgoto err;\n\n\tstrlcpy(wgd.wgd_name, ifname, sizeof(wgd.wgd_name));\n\tif (ioctl(s, SIOCGWG, &wgd) < 0)\n\t\tgoto err;\n\n\twgd.wgd_data = malloc(wgd.wgd_size);\n\tif (!wgd.wgd_data)\n\t\tgoto err;\n\tif (ioctl(s, SIOCGWG, &wgd) < 0)\n\t\tgoto err;\n\n\tdev = calloc(1, sizeof(*dev));\n\tif (!dev)\n\t\tgoto err;\n\tstrlcpy(dev->name, ifname, sizeof(dev->name));\n\tnvl_device = nvlist_unpack(wgd.wgd_data, wgd.wgd_size, 0);\n\tif (!nvl_device)\n\t\tgoto err;\n\n\tif (nvlist_exists_number(nvl_device, \"listen-port\")) {\n\t\tnumber = nvlist_get_number(nvl_device, \"listen-port\");\n\t\tif (number <= UINT16_MAX) {\n\t\t\tdev->listen_port = number;\n\t\t\tdev->flags |= WGDEVICE_HAS_LISTEN_PORT;\n\t\t}\n\t}\n\tif (nvlist_exists_number(nvl_device, \"user-cookie\")) {\n\t\tnumber = nvlist_get_number(nvl_device, \"user-cookie\");\n\t\tif (number <= UINT32_MAX) {\n\t\t\tdev->fwmark = number;\n\t\t\tdev->flags |= WGDEVICE_HAS_FWMARK;\n\t\t}\n\t}\n\tif (nvlist_exists_binary(nvl_device, \"public-key\")) {\n\t\tbinary = nvlist_get_binary(nvl_device, \"public-key\", &size);\n\t\tif (binary && size == sizeof(dev->public_key)) {\n\t\t\tmemcpy(dev->public_key, binary, sizeof(dev->public_key));\n\t\t\tdev->flags |= WGDEVICE_HAS_PUBLIC_KEY;\n\t\t}\n\t}\n\tif (nvlist_exists_binary(nvl_device, \"private-key\")) {\n\t\tbinary = nvlist_get_binary(nvl_device, \"private-key\", &size);\n\t\tif (binary && size == sizeof(dev->private_key)) {\n\t\t\tmemcpy(dev->private_key, binary, sizeof(dev->private_key));\n\t\t\tdev->flags |= WGDEVICE_HAS_PRIVATE_KEY;\n\t\t}\n\t}\n\tif (!nvlist_exists_nvlist_array(nvl_device, \"peers\"))\n\t\tgoto skip_peers;\n\tnvl_peers = nvlist_get_nvlist_array(nvl_device, \"peers\", &peer_count);\n\tif (!nvl_peers)\n\t\tgoto skip_peers;\n\tfor (i = 0; i < peer_count; ++i) {\n\t\tstruct wgpeer *peer;\n\t\tstruct wgallowedip *aip = NULL;\n\t\tconst nvlist_t *const *nvl_aips;\n\t\tsize_t aip_count, j;\n\n\t\tpeer = calloc(1, sizeof(*peer));\n\t\tif (!peer)\n\t\t\tgoto err_peer;\n\t\tif (nvlist_exists_binary(nvl_peers[i], \"public-key\")) {\n\t\t\tbinary = nvlist_get_binary(nvl_peers[i], \"public-key\", &size);\n\t\t\tif (binary && size == sizeof(peer->public_key)) {\n\t\t\t\tmemcpy(peer->public_key, binary, sizeof(peer->public_key));\n\t\t\t\tpeer->flags |= WGPEER_HAS_PUBLIC_KEY;\n\t\t\t}\n\t\t}\n\t\tif (nvlist_exists_binary(nvl_peers[i], \"preshared-key\")) {\n\t\t\tbinary = nvlist_get_binary(nvl_peers[i], \"preshared-key\", &size);\n\t\t\tif (binary && size == sizeof(peer->preshared_key)) {\n\t\t\t\tmemcpy(peer->preshared_key, binary, sizeof(peer->preshared_key));\n\t\t\t\tif (!key_is_zero(peer->preshared_key))\n\t\t\t\t\tpeer->flags |= WGPEER_HAS_PRESHARED_KEY;\n\t\t\t}\n\t\t}\n\t\tif (nvlist_exists_number(nvl_peers[i], \"persistent-keepalive-interval\")) {\n\t\t\tnumber = nvlist_get_number(nvl_peers[i], \"persistent-keepalive-interval\");\n\t\t\tif (number <= UINT16_MAX) {\n\t\t\t\tpeer->persistent_keepalive_interval = number;\n\t\t\t\tpeer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;\n\t\t\t}\n\t\t}\n\t\tif (nvlist_exists_binary(nvl_peers[i], \"endpoint\")) {\n\t\t\tconst struct sockaddr *endpoint = nvlist_get_binary(nvl_peers[i], \"endpoint\", &size);\n\t\t\tif (endpoint && size <= sizeof(peer->endpoint) && size >= sizeof(peer->endpoint.addr) &&\n\t\t\t    (endpoint->sa_family == AF_INET || endpoint->sa_family == AF_INET6))\n\t\t\t\tmemcpy(&peer->endpoint.addr, endpoint, size);\n\t\t}\n\t\tif (nvlist_exists_number(nvl_peers[i], \"rx-bytes\"))\n\t\t\tpeer->rx_bytes = nvlist_get_number(nvl_peers[i], \"rx-bytes\");\n\t\tif (nvlist_exists_number(nvl_peers[i], \"tx-bytes\"))\n\t\t\tpeer->tx_bytes = nvlist_get_number(nvl_peers[i], \"tx-bytes\");\n\t\tif (nvlist_exists_binary(nvl_peers[i], \"last-handshake-time\")) {\n\t\t\tbinary = nvlist_get_binary(nvl_peers[i], \"last-handshake-time\", &size);\n\t\t\tif (binary && size == sizeof(peer->last_handshake_time))\n\t\t\t\tmemcpy(&peer->last_handshake_time, binary, sizeof(peer->last_handshake_time));\n\t\t}\n\n\t\tif (!nvlist_exists_nvlist_array(nvl_peers[i], \"allowed-ips\"))\n\t\t\tgoto skip_allowed_ips;\n\t\tnvl_aips = nvlist_get_nvlist_array(nvl_peers[i], \"allowed-ips\", &aip_count);\n\t\tif (!aip_count || !nvl_aips)\n\t\t\tgoto skip_allowed_ips;\n\t\tfor (j = 0; j < aip_count; ++j) {\n\t\t\tif (!nvlist_exists_number(nvl_aips[j], \"cidr\"))\n\t\t\t\tcontinue;\n\t\t\tif (!nvlist_exists_binary(nvl_aips[j], \"ipv4\") && !nvlist_exists_binary(nvl_aips[j], \"ipv6\"))\n\t\t\t\tcontinue;\n\t\t\taip = calloc(1, sizeof(*aip));\n\t\t\tif (!aip)\n\t\t\t\tgoto err_allowed_ips;\n\t\t\tnumber = nvlist_get_number(nvl_aips[j], \"cidr\");\n\t\t\tif (nvlist_exists_binary(nvl_aips[j], \"ipv4\")) {\n\t\t\t\tbinary = nvlist_get_binary(nvl_aips[j], \"ipv4\", &size);\n\t\t\t\tif (!binary || number > 32) {\n\t\t\t\t\tret = EINVAL;\n\t\t\t\t\tgoto err_allowed_ips;\n\t\t\t\t}\n\t\t\t\taip->family = AF_INET;\n\t\t\t\taip->cidr = number;\n\t\t\t\tmemcpy(&aip->ip4, binary, sizeof(aip->ip4));\n\t\t\t} else {\n\t\t\t\tassert(nvlist_exists_binary(nvl_aips[j], \"ipv6\"));\n\t\t\t\tbinary = nvlist_get_binary(nvl_aips[j], \"ipv6\", &size);\n\t\t\t\tif (!binary || number > 128) {\n\t\t\t\t\tret = EINVAL;\n\t\t\t\t\tgoto err_allowed_ips;\n\t\t\t\t}\n\t\t\t\taip->family = AF_INET6;\n\t\t\t\taip->cidr = number;\n\t\t\t\tmemcpy(&aip->ip6, binary, sizeof(aip->ip6));\n\t\t\t}\n\n\t\t\tif (!peer->first_allowedip)\n\t\t\t\tpeer->first_allowedip = aip;\n\t\t\telse\n\t\t\t\tpeer->last_allowedip->next_allowedip = aip;\n\t\t\tpeer->last_allowedip = aip;\n\t\t\taip = NULL;\n\t\t\tcontinue;\n\n\t\terr_allowed_ips:\n\t\t\tif (!ret)\n\t\t\t\tret = -errno;\n\t\t\tfree(aip);\n\t\t\tgoto err_peer;\n\t\t}\n\n\t\t/* Nothing leaked, hopefully -- ownership transferred or aip freed. */\n\t\tassert(aip == NULL);\n\tskip_allowed_ips:\n\t\tif (!dev->first_peer)\n\t\t\tdev->first_peer = peer;\n\t\telse\n\t\t\tdev->last_peer->next_peer = peer;\n\t\tdev->last_peer = peer;\n\t\tcontinue;\n\n\terr_peer:\n\t\tif (!ret)\n\t\t\tret = -errno;\n\t\tfree(peer);\n\t\tgoto err;\n\t}\n\nskip_peers:\n\tfree(wgd.wgd_data);\n\tnvlist_destroy(nvl_device);\n\t*device = dev;\n\treturn 0;\n\nerr:\n\tif (!ret)\n\t\tret = -errno;\n\tfree(wgd.wgd_data);\n\tnvlist_destroy(nvl_device);\n\tfree(dev);\n\treturn ret;\n}\n\n\nstatic int kernel_set_device(struct wgdevice *dev)\n{\n\tstruct wg_data_io wgd = { 0 };\n\tnvlist_t *nvl_device = NULL, **nvl_peers = NULL;\n\tsize_t peer_count = 0, i = 0;\n\tstruct wgpeer *peer;\n\tint ret = 0, s;\n\n\tstrlcpy(wgd.wgd_name, dev->name, sizeof(wgd.wgd_name));\n\n\tnvl_device = nvlist_create(0);\n\tif (!nvl_device)\n\t\tgoto err;\n\n\tfor_each_wgpeer(dev, peer)\n\t\t++peer_count;\n\tif (peer_count) {\n\t\tnvl_peers = calloc(peer_count, sizeof(*nvl_peers));\n\t\tif (!nvl_peers)\n\t\t\tgoto err;\n\t}\n\tif (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)\n\t\tnvlist_add_binary(nvl_device, \"private-key\", dev->private_key, sizeof(dev->private_key));\n\tif (dev->flags & WGDEVICE_HAS_LISTEN_PORT)\n\t\tnvlist_add_number(nvl_device, \"listen-port\", dev->listen_port);\n\tif (dev->flags & WGDEVICE_HAS_FWMARK)\n\t\tnvlist_add_number(nvl_device, \"user-cookie\", dev->fwmark);\n\tif (dev->flags & WGDEVICE_REPLACE_PEERS)\n\t\tnvlist_add_bool(nvl_device, \"replace-peers\", true);\n\n\tfor_each_wgpeer(dev, peer) {\n\t\tsize_t aip_count = 0, j = 0;\n\t\tnvlist_t **nvl_aips = NULL;\n\t\tstruct wgallowedip *aip;\n\n\t\tnvl_peers[i]  = nvlist_create(0);\n\t\tif (!nvl_peers[i])\n\t\t\tgoto err_peer;\n\t\tfor_each_wgallowedip(peer, aip)\n\t\t\t++aip_count;\n\t\tif (aip_count) {\n\t\t\tnvl_aips = calloc(aip_count, sizeof(*nvl_aips));\n\t\t\tif (!nvl_aips)\n\t\t\t\tgoto err_peer;\n\t\t}\n\t\tnvlist_add_binary(nvl_peers[i], \"public-key\", peer->public_key, sizeof(peer->public_key));\n\t\tif (peer->flags & WGPEER_HAS_PRESHARED_KEY)\n\t\t\tnvlist_add_binary(nvl_peers[i], \"preshared-key\", peer->preshared_key, sizeof(peer->preshared_key));\n\t\tif (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)\n\t\t\tnvlist_add_number(nvl_peers[i], \"persistent-keepalive-interval\", peer->persistent_keepalive_interval);\n\t\tif (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)\n\t\t\tnvlist_add_binary(nvl_peers[i], \"endpoint\", &peer->endpoint.addr, peer->endpoint.addr.sa_len);\n\t\tif (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)\n\t\t\tnvlist_add_bool(nvl_peers[i], \"replace-allowedips\", true);\n\t\tif (peer->flags & WGPEER_REMOVE_ME)\n\t\t\tnvlist_add_bool(nvl_peers[i], \"remove\", true);\n\t\tfor_each_wgallowedip(peer, aip) {\n\t\t\tnvl_aips[j] = nvlist_create(0);\n\t\t\tif (!nvl_aips[j])\n\t\t\t\tgoto err_peer;\n\t\t\tif (aip->flags)\n\t\t\t\tnvlist_add_number(nvl_aips[j], \"flags\", aip->flags);\n\t\t\tnvlist_add_number(nvl_aips[j], \"cidr\", aip->cidr);\n\t\t\tif (aip->family == AF_INET)\n\t\t\t\tnvlist_add_binary(nvl_aips[j], \"ipv4\", &aip->ip4, sizeof(aip->ip4));\n\t\t\telse if (aip->family == AF_INET6)\n\t\t\t\tnvlist_add_binary(nvl_aips[j], \"ipv6\", &aip->ip6, sizeof(aip->ip6));\n\t\t\t++j;\n\t\t}\n\t\tif (j) {\n\t\t\tnvlist_add_nvlist_array(nvl_peers[i], \"allowed-ips\", (const nvlist_t *const *)nvl_aips, j);\n\t\t\tfor (j = 0; j < aip_count; ++j)\n\t\t\t\tnvlist_destroy(nvl_aips[j]);\n\t\t\tfree(nvl_aips);\n\t\t}\n\t\t++i;\n\t\tcontinue;\n\n\terr_peer:\n\t\tret = -errno;\n\t\tfor (j = 0; j < aip_count && nvl_aips; ++j)\n\t\t\tnvlist_destroy(nvl_aips[j]);\n\t\tfree(nvl_aips);\n\t\tnvlist_destroy(nvl_peers[i]);\n\t\tnvl_peers[i] = NULL;\n\t\tgoto err;\n\t}\n\tif (i) {\n\t\tnvlist_add_nvlist_array(nvl_device, \"peers\", (const nvlist_t *const *)nvl_peers, i);\n\t\tfor (i = 0; i < peer_count; ++i)\n\t\t\tnvlist_destroy(nvl_peers[i]);\n\t\tfree(nvl_peers);\n\t\tnvl_peers = NULL;\n\t}\n\twgd.wgd_data = nvlist_pack(nvl_device, &wgd.wgd_size);\n\tnvlist_destroy(nvl_device);\n\tnvl_device = NULL;\n\tif (!wgd.wgd_data)\n\t\tgoto err;\n\ts = get_dgram_socket();\n\tif (s < 0)\n\t\treturn -errno;\n\treturn ioctl(s, SIOCSWG, &wgd);\n\nerr:\n\tif (!ret)\n\t\tret = -errno;\n\tfor (i = 0; i < peer_count && nvl_peers; ++i)\n\t\tnvlist_destroy(nvl_peers[i]);\n\tfree(nvl_peers);\n\tnvlist_destroy(nvl_device);\n\treturn ret;\n}\n"
  },
  {
    "path": "src/ipc-linux.h",
    "content": "// SPDX-License-Identifier: MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <errno.h>\n#include <string.h>\n#include <time.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <linux/genetlink.h>\n#include <linux/if_link.h>\n#include <linux/netlink.h>\n#include <linux/rtnetlink.h>\n#include <linux/wireguard.h>\n#include <netinet/in.h>\n#include \"containers.h\"\n#include \"encoding.h\"\n#include \"netlink.h\"\n\n#define IPC_SUPPORTS_KERNEL_INTERFACE\n\n#define SOCKET_BUFFER_SIZE (mnl_ideal_socket_buffer_size())\n\nstruct interface {\n\tconst char *name;\n\tbool is_wireguard;\n};\n\nstatic int parse_linkinfo(const struct nlattr *attr, void *data)\n{\n\tstruct interface *interface = data;\n\n\tif (mnl_attr_get_type(attr) == IFLA_INFO_KIND && !strcmp(WG_GENL_NAME, mnl_attr_get_str(attr)))\n\t\tinterface->is_wireguard = true;\n\treturn MNL_CB_OK;\n}\n\nstatic int parse_infomsg(const struct nlattr *attr, void *data)\n{\n\tstruct interface *interface = data;\n\n\tif (mnl_attr_get_type(attr) == IFLA_LINKINFO)\n\t\treturn mnl_attr_parse_nested(attr, parse_linkinfo, data);\n\telse if (mnl_attr_get_type(attr) == IFLA_IFNAME)\n\t\tinterface->name = mnl_attr_get_str(attr);\n\treturn MNL_CB_OK;\n}\n\nstatic int read_devices_cb(const struct nlmsghdr *nlh, void *data)\n{\n\tstruct string_list *list = data;\n\tstruct interface interface = { 0 };\n\tint ret;\n\n\tret = mnl_attr_parse(nlh, sizeof(struct ifinfomsg), parse_infomsg, &interface);\n\tif (ret != MNL_CB_OK)\n\t\treturn ret;\n\tif (interface.name && interface.is_wireguard)\n\t\tret = string_list_add(list, interface.name);\n\tif (ret < 0)\n\t\treturn ret;\n\tif (nlh->nlmsg_type != NLMSG_DONE)\n\t\treturn MNL_CB_OK + 1;\n\treturn MNL_CB_OK;\n}\n\nstatic int kernel_get_wireguard_interfaces(struct string_list *list)\n{\n\tstruct mnl_socket *nl = NULL;\n\tchar *rtnl_buffer = NULL;\n\tsize_t message_len;\n\tunsigned int portid, seq;\n\tssize_t len;\n\tint ret = 0;\n\tstruct nlmsghdr *nlh;\n\tstruct ifinfomsg *ifm;\n\n\tret = -ENOMEM;\n\trtnl_buffer = calloc(SOCKET_BUFFER_SIZE, 1);\n\tif (!rtnl_buffer)\n\t\tgoto cleanup;\n\n\tnl = mnl_socket_open(NETLINK_ROUTE);\n\tif (!nl) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\n\tif (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\n\tseq = time(NULL);\n\tportid = mnl_socket_get_portid(nl);\n\tnlh = mnl_nlmsg_put_header(rtnl_buffer);\n\tnlh->nlmsg_type = RTM_GETLINK;\n\tnlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;\n\tnlh->nlmsg_seq = seq;\n\tifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));\n\tifm->ifi_family = AF_UNSPEC;\n\tmessage_len = nlh->nlmsg_len;\n\n\tif (mnl_socket_sendto(nl, rtnl_buffer, message_len) < 0) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\nanother:\n\tif ((len = mnl_socket_recvfrom(nl, rtnl_buffer, SOCKET_BUFFER_SIZE)) < 0) {\n\t\tret = -errno;\n\t\tgoto cleanup;\n\t}\n\tif ((len = mnl_cb_run(rtnl_buffer, len, seq, portid, read_devices_cb, list)) < 0) {\n\t\t/* Netlink returns NLM_F_DUMP_INTR if the set of all tunnels changed\n\t\t * during the dump. That's unfortunate, but is pretty common on busy\n\t\t * systems that are adding and removing tunnels all the time. Rather\n\t\t * than retrying, potentially indefinitely, we just work with the\n\t\t * partial results. */\n\t\tif (errno != EINTR) {\n\t\t\tret = -errno;\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\tif (len == MNL_CB_OK + 1)\n\t\tgoto another;\n\tret = 0;\n\ncleanup:\n\tfree(rtnl_buffer);\n\tif (nl)\n\t\tmnl_socket_close(nl);\n\treturn ret;\n}\n\nstatic int kernel_set_device(struct wgdevice *dev)\n{\n\tint ret = 0;\n\tstruct wgpeer *peer = NULL;\n\tstruct wgallowedip *allowedip = NULL;\n\tstruct nlattr *peers_nest, *peer_nest, *allowedips_nest, *allowedip_nest;\n\tstruct nlmsghdr *nlh;\n\tstruct mnlg_socket *nlg;\n\n\tnlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);\n\tif (!nlg)\n\t\treturn -errno;\n\nagain:\n\tnlh = mnlg_msg_prepare(nlg, WG_CMD_SET_DEVICE, NLM_F_REQUEST | NLM_F_ACK);\n\tmnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, dev->name);\n\n\tif (!peer) {\n\t\tuint32_t flags = 0;\n\n\t\tif (dev->flags & WGDEVICE_HAS_PRIVATE_KEY)\n\t\t\tmnl_attr_put(nlh, WGDEVICE_A_PRIVATE_KEY, sizeof(dev->private_key), dev->private_key);\n\t\tif (dev->flags & WGDEVICE_HAS_LISTEN_PORT)\n\t\t\tmnl_attr_put_u16(nlh, WGDEVICE_A_LISTEN_PORT, dev->listen_port);\n\t\tif (dev->flags & WGDEVICE_HAS_FWMARK)\n\t\t\tmnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark);\n\t\tif (dev->flags & WGDEVICE_REPLACE_PEERS)\n\t\t\tflags |= WGDEVICE_F_REPLACE_PEERS;\n\t\tif (flags)\n\t\t\tmnl_attr_put_u32(nlh, WGDEVICE_A_FLAGS, flags);\n\t}\n\tif (!dev->first_peer)\n\t\tgoto send;\n\tpeers_nest = peer_nest = allowedips_nest = allowedip_nest = NULL;\n\tpeers_nest = mnl_attr_nest_start(nlh, WGDEVICE_A_PEERS);\n\tfor (peer = peer ? peer : dev->first_peer; peer; peer = peer->next_peer) {\n\t\tuint32_t flags = 0;\n\n\t\tpeer_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0);\n\t\tif (!peer_nest)\n\t\t\tgoto toobig_peers;\n\t\tif (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PUBLIC_KEY, sizeof(peer->public_key), peer->public_key))\n\t\t\tgoto toobig_peers;\n\t\tif (peer->flags & WGPEER_REMOVE_ME)\n\t\t\tflags |= WGPEER_F_REMOVE_ME;\n\t\tif (!allowedip) {\n\t\t\tif (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)\n\t\t\t\tflags |= WGPEER_F_REPLACE_ALLOWEDIPS;\n\t\t\tif (peer->flags & WGPEER_HAS_PRESHARED_KEY) {\n\t\t\t\tif (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PRESHARED_KEY, sizeof(peer->preshared_key), peer->preshared_key))\n\t\t\t\t\tgoto toobig_peers;\n\t\t\t}\n\t\t\tif (peer->endpoint.addr.sa_family == AF_INET) {\n\t\t\t\tif (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr4), &peer->endpoint.addr4))\n\t\t\t\t\tgoto toobig_peers;\n\t\t\t} else if (peer->endpoint.addr.sa_family == AF_INET6) {\n\t\t\t\tif (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ENDPOINT, sizeof(peer->endpoint.addr6), &peer->endpoint.addr6))\n\t\t\t\t\tgoto toobig_peers;\n\t\t\t}\n\t\t\tif (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {\n\t\t\t\tif (!mnl_attr_put_u16_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL, peer->persistent_keepalive_interval))\n\t\t\t\t\tgoto toobig_peers;\n\t\t\t}\n\t\t}\n\t\tif (flags) {\n\t\t\tif (!mnl_attr_put_u32_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_FLAGS, flags))\n\t\t\t\tgoto toobig_peers;\n\t\t}\n\t\tif (peer->first_allowedip) {\n\t\t\tif (!allowedip)\n\t\t\t\tallowedip = peer->first_allowedip;\n\t\t\tallowedips_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, WGPEER_A_ALLOWEDIPS);\n\t\t\tif (!allowedips_nest)\n\t\t\t\tgoto toobig_allowedips;\n\t\t\tfor (; allowedip; allowedip = allowedip->next_allowedip) {\n\t\t\t\tallowedip_nest = mnl_attr_nest_start_check(nlh, SOCKET_BUFFER_SIZE, 0);\n\t\t\t\tif (!allowedip_nest)\n\t\t\t\t\tgoto toobig_allowedips;\n\t\t\t\tif (!mnl_attr_put_u16_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FAMILY, allowedip->family))\n\t\t\t\t\tgoto toobig_allowedips;\n\t\t\t\tif (allowedip->family == AF_INET) {\n\t\t\t\t\tif (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip4), &allowedip->ip4))\n\t\t\t\t\t\tgoto toobig_allowedips;\n\t\t\t\t} else if (allowedip->family == AF_INET6) {\n\t\t\t\t\tif (!mnl_attr_put_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_IPADDR, sizeof(allowedip->ip6), &allowedip->ip6))\n\t\t\t\t\t\tgoto toobig_allowedips;\n\t\t\t\t}\n\t\t\t\tif (!mnl_attr_put_u8_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_CIDR_MASK, allowedip->cidr))\n\t\t\t\t\tgoto toobig_allowedips;\n\t\t\t\tif (allowedip->flags && !mnl_attr_put_u32_check(nlh, SOCKET_BUFFER_SIZE, WGALLOWEDIP_A_FLAGS, allowedip->flags))\n\t\t\t\t\tgoto toobig_allowedips;\n\t\t\t\tmnl_attr_nest_end(nlh, allowedip_nest);\n\t\t\t\tallowedip_nest = NULL;\n\t\t\t}\n\t\t\tmnl_attr_nest_end(nlh, allowedips_nest);\n\t\t\tallowedips_nest = NULL;\n\t\t}\n\n\t\tmnl_attr_nest_end(nlh, peer_nest);\n\t\tpeer_nest = NULL;\n\t}\n\tmnl_attr_nest_end(nlh, peers_nest);\n\tpeers_nest = NULL;\n\tgoto send;\ntoobig_allowedips:\n\tif (allowedip_nest)\n\t\tmnl_attr_nest_cancel(nlh, allowedip_nest);\n\tif (allowedips_nest)\n\t\tmnl_attr_nest_end(nlh, allowedips_nest);\n\tmnl_attr_nest_end(nlh, peer_nest);\n\tmnl_attr_nest_end(nlh, peers_nest);\n\tgoto send;\ntoobig_peers:\n\tif (peer_nest)\n\t\tmnl_attr_nest_cancel(nlh, peer_nest);\n\tmnl_attr_nest_end(nlh, peers_nest);\n\tgoto send;\nsend:\n\tif (mnlg_socket_send(nlg, nlh) < 0) {\n\t\tret = -errno;\n\t\tgoto out;\n\t}\n\terrno = 0;\n\tif (mnlg_socket_recv_run(nlg, NULL, NULL) < 0) {\n\t\tret = errno ? -errno : -EINVAL;\n\t\tgoto out;\n\t}\n\tif (peer)\n\t\tgoto again;\n\nout:\n\tmnlg_socket_close(nlg);\n\terrno = -ret;\n\treturn ret;\n}\n\nstatic int parse_allowedip(const struct nlattr *attr, void *data)\n{\n\tstruct wgallowedip *allowedip = data;\n\n\tswitch (mnl_attr_get_type(attr)) {\n\tcase WGALLOWEDIP_A_UNSPEC:\n\t\tbreak;\n\tcase WGALLOWEDIP_A_FAMILY:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U16))\n\t\t\tallowedip->family = mnl_attr_get_u16(attr);\n\t\tbreak;\n\tcase WGALLOWEDIP_A_IPADDR:\n\t\tif (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip4))\n\t\t\tmemcpy(&allowedip->ip4, mnl_attr_get_payload(attr), sizeof(allowedip->ip4));\n\t\telse if (mnl_attr_get_payload_len(attr) == sizeof(allowedip->ip6))\n\t\t\tmemcpy(&allowedip->ip6, mnl_attr_get_payload(attr), sizeof(allowedip->ip6));\n\t\tbreak;\n\tcase WGALLOWEDIP_A_CIDR_MASK:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U8))\n\t\t\tallowedip->cidr = mnl_attr_get_u8(attr);\n\t\tbreak;\n\t}\n\n\treturn MNL_CB_OK;\n}\n\nstatic int parse_allowedips(const struct nlattr *attr, void *data)\n{\n\tstruct wgpeer *peer = data;\n\tstruct wgallowedip *new_allowedip = calloc(1, sizeof(*new_allowedip));\n\tint ret;\n\n\tif (!new_allowedip) {\n\t\tperror(\"calloc\");\n\t\treturn MNL_CB_ERROR;\n\t}\n\tif (!peer->first_allowedip)\n\t\tpeer->first_allowedip = peer->last_allowedip = new_allowedip;\n\telse {\n\t\tpeer->last_allowedip->next_allowedip = new_allowedip;\n\t\tpeer->last_allowedip = new_allowedip;\n\t}\n\tret = mnl_attr_parse_nested(attr, parse_allowedip, new_allowedip);\n\tif (!ret)\n\t\treturn ret;\n\tif (!((new_allowedip->family == AF_INET && new_allowedip->cidr <= 32) || (new_allowedip->family == AF_INET6 && new_allowedip->cidr <= 128)))\n\t\treturn MNL_CB_ERROR;\n\treturn MNL_CB_OK;\n}\n\nstatic int parse_peer(const struct nlattr *attr, void *data)\n{\n\tstruct wgpeer *peer = data;\n\n\tswitch (mnl_attr_get_type(attr)) {\n\tcase WGPEER_A_UNSPEC:\n\t\tbreak;\n\tcase WGPEER_A_PUBLIC_KEY:\n\t\tif (mnl_attr_get_payload_len(attr) == sizeof(peer->public_key)) {\n\t\t\tmemcpy(peer->public_key, mnl_attr_get_payload(attr), sizeof(peer->public_key));\n\t\t\tpeer->flags |= WGPEER_HAS_PUBLIC_KEY;\n\t\t}\n\t\tbreak;\n\tcase WGPEER_A_PRESHARED_KEY:\n\t\tif (mnl_attr_get_payload_len(attr) == sizeof(peer->preshared_key)) {\n\t\t\tmemcpy(peer->preshared_key, mnl_attr_get_payload(attr), sizeof(peer->preshared_key));\n\t\t\tif (!key_is_zero(peer->preshared_key))\n\t\t\t\tpeer->flags |= WGPEER_HAS_PRESHARED_KEY;\n\t\t}\n\t\tbreak;\n\tcase WGPEER_A_ENDPOINT: {\n\t\tstruct sockaddr *addr;\n\n\t\tif (mnl_attr_get_payload_len(attr) < sizeof(*addr))\n\t\t\tbreak;\n\t\taddr = mnl_attr_get_payload(attr);\n\t\tif (addr->sa_family == AF_INET && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr4))\n\t\t\tmemcpy(&peer->endpoint.addr4, addr, sizeof(peer->endpoint.addr4));\n\t\telse if (addr->sa_family == AF_INET6 && mnl_attr_get_payload_len(attr) == sizeof(peer->endpoint.addr6))\n\t\t\tmemcpy(&peer->endpoint.addr6, addr, sizeof(peer->endpoint.addr6));\n\t\tbreak;\n\t}\n\tcase WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U16))\n\t\t\tpeer->persistent_keepalive_interval = mnl_attr_get_u16(attr);\n\t\tbreak;\n\tcase WGPEER_A_LAST_HANDSHAKE_TIME:\n\t\tif (mnl_attr_get_payload_len(attr) == sizeof(peer->last_handshake_time))\n\t\t\tmemcpy(&peer->last_handshake_time, mnl_attr_get_payload(attr), sizeof(peer->last_handshake_time));\n\t\tbreak;\n\tcase WGPEER_A_RX_BYTES:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U64))\n\t\t\tpeer->rx_bytes = mnl_attr_get_u64(attr);\n\t\tbreak;\n\tcase WGPEER_A_TX_BYTES:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U64))\n\t\t\tpeer->tx_bytes = mnl_attr_get_u64(attr);\n\t\tbreak;\n\tcase WGPEER_A_ALLOWEDIPS:\n\t\treturn mnl_attr_parse_nested(attr, parse_allowedips, peer);\n\t}\n\n\treturn MNL_CB_OK;\n}\n\nstatic int parse_peers(const struct nlattr *attr, void *data)\n{\n\tstruct wgdevice *device = data;\n\tstruct wgpeer *new_peer = calloc(1, sizeof(*new_peer));\n\tint ret;\n\n\tif (!new_peer) {\n\t\tperror(\"calloc\");\n\t\treturn MNL_CB_ERROR;\n\t}\n\tif (!device->first_peer)\n\t\tdevice->first_peer = device->last_peer = new_peer;\n\telse {\n\t\tdevice->last_peer->next_peer = new_peer;\n\t\tdevice->last_peer = new_peer;\n\t}\n\tret = mnl_attr_parse_nested(attr, parse_peer, new_peer);\n\tif (!ret)\n\t\treturn ret;\n\tif (!(new_peer->flags & WGPEER_HAS_PUBLIC_KEY))\n\t\treturn MNL_CB_ERROR;\n\treturn MNL_CB_OK;\n}\n\nstatic int parse_device(const struct nlattr *attr, void *data)\n{\n\tstruct wgdevice *device = data;\n\n\tswitch (mnl_attr_get_type(attr)) {\n\tcase WGDEVICE_A_UNSPEC:\n\t\tbreak;\n\tcase WGDEVICE_A_IFINDEX:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U32))\n\t\t\tdevice->ifindex = mnl_attr_get_u32(attr);\n\t\tbreak;\n\tcase WGDEVICE_A_IFNAME:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_STRING)) {\n\t\t\tstrncpy(device->name, mnl_attr_get_str(attr), sizeof(device->name) - 1);\n\t\t\tdevice->name[sizeof(device->name) - 1] = '\\0';\n\t\t}\n\t\tbreak;\n\tcase WGDEVICE_A_PRIVATE_KEY:\n\t\tif (mnl_attr_get_payload_len(attr) == sizeof(device->private_key)) {\n\t\t\tmemcpy(device->private_key, mnl_attr_get_payload(attr), sizeof(device->private_key));\n\t\t\tdevice->flags |= WGDEVICE_HAS_PRIVATE_KEY;\n\t\t}\n\t\tbreak;\n\tcase WGDEVICE_A_PUBLIC_KEY:\n\t\tif (mnl_attr_get_payload_len(attr) == sizeof(device->public_key)) {\n\t\t\tmemcpy(device->public_key, mnl_attr_get_payload(attr), sizeof(device->public_key));\n\t\t\tdevice->flags |= WGDEVICE_HAS_PUBLIC_KEY;\n\t\t}\n\t\tbreak;\n\tcase WGDEVICE_A_LISTEN_PORT:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U16))\n\t\t\tdevice->listen_port = mnl_attr_get_u16(attr);\n\t\tbreak;\n\tcase WGDEVICE_A_FWMARK:\n\t\tif (!mnl_attr_validate(attr, MNL_TYPE_U32))\n\t\t\tdevice->fwmark = mnl_attr_get_u32(attr);\n\t\tbreak;\n\tcase WGDEVICE_A_PEERS:\n\t\treturn mnl_attr_parse_nested(attr, parse_peers, device);\n\t}\n\n\treturn MNL_CB_OK;\n}\n\nstatic int read_device_cb(const struct nlmsghdr *nlh, void *data)\n{\n\treturn mnl_attr_parse(nlh, sizeof(struct genlmsghdr), parse_device, data);\n}\n\nstatic void coalesce_peers(struct wgdevice *device)\n{\n\tstruct wgpeer *old_next_peer, *peer = device->first_peer;\n\n\twhile (peer && peer->next_peer) {\n\t\tif (memcmp(peer->public_key, peer->next_peer->public_key, sizeof(peer->public_key))) {\n\t\t\tpeer = peer->next_peer;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!peer->first_allowedip) {\n\t\t\tpeer->first_allowedip = peer->next_peer->first_allowedip;\n\t\t\tpeer->last_allowedip = peer->next_peer->last_allowedip;\n\t\t} else {\n\t\t\tpeer->last_allowedip->next_allowedip = peer->next_peer->first_allowedip;\n\t\t\tpeer->last_allowedip = peer->next_peer->last_allowedip;\n\t\t}\n\t\told_next_peer = peer->next_peer;\n\t\tpeer->next_peer = old_next_peer->next_peer;\n\t\tfree(old_next_peer);\n\t}\n}\n\nstatic int kernel_get_device(struct wgdevice **device, const char *iface)\n{\n\tint ret;\n\tstruct nlmsghdr *nlh;\n\tstruct mnlg_socket *nlg;\n\n\t/* libmnl doesn't check the buffer size, so enforce that before using. */\n\tif (strlen(iface) >= IFNAMSIZ) {\n\t\terrno = ENAMETOOLONG;\n\t\treturn -ENAMETOOLONG;\n\t}\n\ntry_again:\n\tret = 0;\n\t*device = calloc(1, sizeof(**device));\n\tif (!*device)\n\t\treturn -errno;\n\n\tnlg = mnlg_socket_open(WG_GENL_NAME, WG_GENL_VERSION);\n\tif (!nlg) {\n\t\tfree_wgdevice(*device);\n\t\t*device = NULL;\n\t\treturn -errno;\n\t}\n\n\tnlh = mnlg_msg_prepare(nlg, WG_CMD_GET_DEVICE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);\n\tmnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, iface);\n\tif (mnlg_socket_send(nlg, nlh) < 0) {\n\t\tret = -errno;\n\t\tgoto out;\n\t}\n\terrno = 0;\n\tif (mnlg_socket_recv_run(nlg, read_device_cb, *device) < 0) {\n\t\tret = errno ? -errno : -EINVAL;\n\t\tgoto out;\n\t}\n\tcoalesce_peers(*device);\n\nout:\n\tif (nlg)\n\t\tmnlg_socket_close(nlg);\n\tif (ret) {\n\t\tfree_wgdevice(*device);\n\t\tif (ret == -EINTR)\n\t\t\tgoto try_again;\n\t\t*device = NULL;\n\t}\n\terrno = -ret;\n\treturn ret;\n}\n"
  },
  {
    "path": "src/ipc-openbsd.h",
    "content": "// SPDX-License-Identifier: MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <errno.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n#include <sys/sockio.h>\n#include <sys/types.h>\n#include <net/if.h>\n#include <net/if_wg.h>\n#include <netinet/in.h>\n#include \"containers.h\"\n\n#define IPC_SUPPORTS_KERNEL_INTERFACE\n\nstatic int get_dgram_socket(void)\n{\n\tstatic int sock = -1;\n\tif (sock < 0)\n\t\tsock = socket(AF_INET, SOCK_DGRAM, 0);\n\treturn sock;\n}\n\nstatic int kernel_get_wireguard_interfaces(struct string_list *list)\n{\n\tstruct ifgroupreq ifgr = { .ifgr_name = \"wg\" };\n\tstruct ifg_req *ifg;\n\tint s = get_dgram_socket(), ret = 0;\n\n\tif (s < 0)\n\t\treturn -errno;\n\n\tif (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0)\n\t\treturn errno == ENOENT ? 0 : -errno;\n\n\tifgr.ifgr_groups = calloc(1, ifgr.ifgr_len);\n\tif (!ifgr.ifgr_groups)\n\t\treturn -errno;\n\tif (ioctl(s, SIOCGIFGMEMB, (caddr_t)&ifgr) < 0) {\n\t\tret = -errno;\n\t\tgoto out;\n\t}\n\n\tfor (ifg = ifgr.ifgr_groups; ifg && ifgr.ifgr_len > 0; ++ifg) {\n\t\tif ((ret = string_list_add(list, ifg->ifgrq_member)) < 0)\n\t\t\tgoto out;\n\t\tifgr.ifgr_len -= sizeof(struct ifg_req);\n\t}\n\nout:\n\tfree(ifgr.ifgr_groups);\n\treturn ret;\n}\n\nstatic int kernel_get_device(struct wgdevice **device, const char *iface)\n{\n\tstruct wg_data_io wgdata = { .wgd_size = 0 };\n\tstruct wg_interface_io *wg_iface;\n\tstruct wg_peer_io *wg_peer;\n\tstruct wg_aip_io *wg_aip;\n\tstruct wgdevice *dev;\n\tstruct wgpeer *peer;\n\tstruct wgallowedip *aip;\n\tint s = get_dgram_socket(), ret;\n\n\tif (s < 0)\n\t\treturn -errno;\n\n\t*device = NULL;\n\tstrlcpy(wgdata.wgd_name, iface, sizeof(wgdata.wgd_name));\n\tfor (size_t last_size = wgdata.wgd_size;; last_size = wgdata.wgd_size) {\n\t\tif (ioctl(s, SIOCGWG, (caddr_t)&wgdata) < 0)\n\t\t\tgoto out;\n\t\tif (last_size >= wgdata.wgd_size)\n\t\t\tbreak;\n\t\twgdata.wgd_interface = realloc(wgdata.wgd_interface, wgdata.wgd_size);\n\t\tif (!wgdata.wgd_interface)\n\t\t\tgoto out;\n\t}\n\n\twg_iface = wgdata.wgd_interface;\n\tdev = calloc(1, sizeof(*dev));\n\tif (!dev)\n\t\tgoto out;\n\tstrlcpy(dev->name, iface, sizeof(dev->name));\n\n\tif (wg_iface->i_flags & WG_INTERFACE_HAS_RTABLE) {\n\t\tdev->fwmark = wg_iface->i_rtable;\n\t\tdev->flags |= WGDEVICE_HAS_FWMARK;\n\t}\n\n\tif (wg_iface->i_flags & WG_INTERFACE_HAS_PORT) {\n\t\tdev->listen_port = wg_iface->i_port;\n\t\tdev->flags |= WGDEVICE_HAS_LISTEN_PORT;\n\t}\n\n\tif (wg_iface->i_flags & WG_INTERFACE_HAS_PUBLIC) {\n\t\tmemcpy(dev->public_key, wg_iface->i_public, sizeof(dev->public_key));\n\t\tdev->flags |= WGDEVICE_HAS_PUBLIC_KEY;\n\t}\n\n\tif (wg_iface->i_flags & WG_INTERFACE_HAS_PRIVATE) {\n\t\tmemcpy(dev->private_key, wg_iface->i_private, sizeof(dev->private_key));\n\t\tdev->flags |= WGDEVICE_HAS_PRIVATE_KEY;\n\t}\n\n\twg_peer = &wg_iface->i_peers[0];\n\tfor (size_t i = 0; i < wg_iface->i_peers_count; ++i) {\n\t\tpeer = calloc(1, sizeof(*peer));\n\t\tif (!peer)\n\t\t\tgoto out;\n\n\t\tif (dev->first_peer == NULL)\n\t\t\tdev->first_peer = peer;\n\t\telse\n\t\t\tdev->last_peer->next_peer = peer;\n\t\tdev->last_peer = peer;\n\n\t\tif (wg_peer->p_flags & WG_PEER_HAS_PUBLIC) {\n\t\t\tmemcpy(peer->public_key, wg_peer->p_public, sizeof(peer->public_key));\n\t\t\tpeer->flags |= WGPEER_HAS_PUBLIC_KEY;\n\t\t}\n\n\t\tif (wg_peer->p_flags & WG_PEER_HAS_PSK) {\n\t\t\tmemcpy(peer->preshared_key, wg_peer->p_psk, sizeof(peer->preshared_key));\n\t\t\tif (!key_is_zero(peer->preshared_key))\n\t\t\t\tpeer->flags |= WGPEER_HAS_PRESHARED_KEY;\n\t\t}\n\n\t\tif (wg_peer->p_flags & WG_PEER_HAS_PKA) {\n\t\t\tpeer->persistent_keepalive_interval = wg_peer->p_pka;\n\t\t\tpeer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;\n\t\t}\n\n\t\tif (wg_peer->p_flags & WG_PEER_HAS_ENDPOINT && wg_peer->p_sa.sa_len <= sizeof(peer->endpoint.addr))\n\t\t\tmemcpy(&peer->endpoint.addr, &wg_peer->p_sa, wg_peer->p_sa.sa_len);\n\n\t\tpeer->rx_bytes = wg_peer->p_rxbytes;\n\t\tpeer->tx_bytes = wg_peer->p_txbytes;\n\n\t\tpeer->last_handshake_time.tv_sec = wg_peer->p_last_handshake.tv_sec;\n\t\tpeer->last_handshake_time.tv_nsec = wg_peer->p_last_handshake.tv_nsec;\n\n\t\twg_aip = &wg_peer->p_aips[0];\n\t\tfor (size_t j = 0; j < wg_peer->p_aips_count; ++j) {\n\t\t\taip = calloc(1, sizeof(*aip));\n\t\t\tif (!aip)\n\t\t\t\tgoto out;\n\n\t\t\tif (peer->first_allowedip == NULL)\n\t\t\t\tpeer->first_allowedip = aip;\n\t\t\telse\n\t\t\t\tpeer->last_allowedip->next_allowedip = aip;\n\t\t\tpeer->last_allowedip = aip;\n\n\t\t\taip->family = wg_aip->a_af;\n\t\t\tif (wg_aip->a_af == AF_INET) {\n\t\t\t\tmemcpy(&aip->ip4, &wg_aip->a_ipv4, sizeof(aip->ip4));\n\t\t\t\taip->cidr = wg_aip->a_cidr;\n\t\t\t} else if (wg_aip->a_af == AF_INET6) {\n\t\t\t\tmemcpy(&aip->ip6, &wg_aip->a_ipv6, sizeof(aip->ip6));\n\t\t\t\taip->cidr = wg_aip->a_cidr;\n\t\t\t}\n\t\t\t++wg_aip;\n\t\t}\n\t\twg_peer = (struct wg_peer_io *)wg_aip;\n\t}\n\t*device = dev;\n\terrno = 0;\nout:\n\tret = -errno;\n\tfree(wgdata.wgd_interface);\n\treturn ret;\n}\n\nstatic int kernel_set_device(struct wgdevice *dev)\n{\n\tstruct wg_data_io wgdata = { .wgd_size = sizeof(struct wg_interface_io) };\n\tstruct wg_interface_io *wg_iface;\n\tstruct wg_peer_io *wg_peer;\n\tstruct wg_aip_io *wg_aip;\n\tstruct wgpeer *peer;\n\tstruct wgallowedip *aip;\n\tint s = get_dgram_socket(), ret;\n\tsize_t peer_count, aip_count;\n\n\tif (s < 0)\n\t\treturn -errno;\n\n\tfor_each_wgpeer(dev, peer) {\n\t\twgdata.wgd_size += sizeof(struct wg_peer_io);\n\t\tfor_each_wgallowedip(peer, aip)\n\t\t\twgdata.wgd_size += sizeof(struct wg_aip_io);\n\t}\n\twg_iface = wgdata.wgd_interface = calloc(1, wgdata.wgd_size);\n\tif (!wgdata.wgd_interface)\n\t\treturn -errno;\n\tstrlcpy(wgdata.wgd_name, dev->name, sizeof(wgdata.wgd_name));\n\n\tif (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {\n\t\tmemcpy(wg_iface->i_private, dev->private_key, sizeof(wg_iface->i_private));\n\t\twg_iface->i_flags |= WG_INTERFACE_HAS_PRIVATE;\n\t}\n\n\tif (dev->flags & WGDEVICE_HAS_LISTEN_PORT) {\n\t\twg_iface->i_port = dev->listen_port;\n\t\twg_iface->i_flags |= WG_INTERFACE_HAS_PORT;\n\t}\n\n\tif (dev->flags & WGDEVICE_HAS_FWMARK) {\n\t\twg_iface->i_rtable = dev->fwmark;\n\t\twg_iface->i_flags |= WG_INTERFACE_HAS_RTABLE;\n\t}\n\n\tif (dev->flags & WGDEVICE_REPLACE_PEERS)\n\t\twg_iface->i_flags |= WG_INTERFACE_REPLACE_PEERS;\n\n\tpeer_count = 0;\n\twg_peer = &wg_iface->i_peers[0];\n\tfor_each_wgpeer(dev, peer) {\n\t\twg_peer->p_flags = WG_PEER_HAS_PUBLIC;\n\t\tmemcpy(wg_peer->p_public, peer->public_key, sizeof(wg_peer->p_public));\n\n\t\tif (peer->flags & WGPEER_HAS_PRESHARED_KEY) {\n\t\t\tmemcpy(wg_peer->p_psk, peer->preshared_key, sizeof(wg_peer->p_psk));\n\t\t\twg_peer->p_flags |= WG_PEER_HAS_PSK;\n\t\t}\n\n\t\tif (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {\n\t\t\twg_peer->p_pka = peer->persistent_keepalive_interval;\n\t\t\twg_peer->p_flags |= WG_PEER_HAS_PKA;\n\t\t}\n\n\t\tif ((peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) &&\n\t\t    peer->endpoint.addr.sa_len <= sizeof(wg_peer->p_endpoint)) {\n\t\t\tmemcpy(&wg_peer->p_endpoint, &peer->endpoint.addr, peer->endpoint.addr.sa_len);\n\t\t\twg_peer->p_flags |= WG_PEER_HAS_ENDPOINT;\n\t\t}\n\n\t\tif (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)\n\t\t\twg_peer->p_flags |= WG_PEER_REPLACE_AIPS;\n\n\t\tif (peer->flags & WGPEER_REMOVE_ME)\n\t\t\twg_peer->p_flags |= WG_PEER_REMOVE;\n\n\t\taip_count = 0;\n\t\twg_aip = &wg_peer->p_aips[0];\n\t\tfor_each_wgallowedip(peer, aip) {\n\t\t\tif (aip->flags) {\n\t\t\t\t//TODO: implement me\n\t\t\t\terrno = EOPNOTSUPP;\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\twg_aip->a_af = aip->family;\n\t\t\twg_aip->a_cidr = aip->cidr;\n\n\t\t\tif (aip->family == AF_INET)\n\t\t\t\tmemcpy(&wg_aip->a_ipv4, &aip->ip4, sizeof(wg_aip->a_ipv4));\n\t\t\telse if (aip->family == AF_INET6)\n\t\t\t\tmemcpy(&wg_aip->a_ipv6, &aip->ip6, sizeof(wg_aip->a_ipv6));\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t\t++aip_count;\n\t\t\t++wg_aip;\n\t\t}\n\t\twg_peer->p_aips_count = aip_count;\n\t\t++peer_count;\n\t\twg_peer = (struct wg_peer_io *)wg_aip;\n\t}\n\twg_iface->i_peers_count = peer_count;\n\n\tif (ioctl(s, SIOCSWG, (caddr_t)&wgdata) < 0)\n\t\tgoto out;\n\terrno = 0;\n\nout:\n\tret = -errno;\n\tfree(wgdata.wgd_interface);\n\treturn ret;\n}\n"
  },
  {
    "path": "src/ipc-uapi-unix.h",
    "content": "// SPDX-License-Identifier: MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n#include <dirent.h>\n#include <errno.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/un.h>\n\n#define SOCK_PATH RUNSTATEDIR \"/wireguard/\"\n#define SOCK_SUFFIX \".sock\"\n\nstatic FILE *userspace_interface_file(const char *iface)\n{\n\tstruct stat sbuf;\n\tstruct sockaddr_un addr = { .sun_family = AF_UNIX };\n\tint fd = -1, ret;\n\tFILE *f = NULL;\n\n\terrno = EINVAL;\n\tif (strchr(iface, '/'))\n\t\tgoto out;\n\tret = snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH \"%s\" SOCK_SUFFIX, iface);\n\tif (ret < 0)\n\t\tgoto out;\n\tret = stat(addr.sun_path, &sbuf);\n\tif (ret < 0)\n\t\tgoto out;\n\terrno = EBADF;\n\tif (!S_ISSOCK(sbuf.st_mode))\n\t\tgoto out;\n\n\tret = fd = socket(AF_UNIX, SOCK_STREAM, 0);\n\tif (ret < 0)\n\t\tgoto out;\n\n\tret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));\n\tif (ret < 0) {\n\t\tif (errno == ECONNREFUSED) /* If the process is gone, we try to clean up the socket. */\n\t\t\tunlink(addr.sun_path);\n\t\tgoto out;\n\t}\n\tf = fdopen(fd, \"r+\");\n\tif (f)\n\t\terrno = 0;\nout:\n\tret = -errno;\n\tif (ret) {\n\t\tif (fd >= 0)\n\t\t\tclose(fd);\n\t\terrno = -ret;\n\t\treturn NULL;\n\t}\n\treturn f;\n}\n\nstatic bool userspace_has_wireguard_interface(const char *iface)\n{\n\tstruct stat sbuf;\n\tstruct sockaddr_un addr = { .sun_family = AF_UNIX };\n\tint fd, ret;\n\n\tif (strchr(iface, '/'))\n\t\treturn false;\n\tif (snprintf(addr.sun_path, sizeof(addr.sun_path), SOCK_PATH \"%s\" SOCK_SUFFIX, iface) < 0)\n\t\treturn false;\n\tif (stat(addr.sun_path, &sbuf) < 0)\n\t\treturn false;\n\tif (!S_ISSOCK(sbuf.st_mode))\n\t\treturn false;\n\tret = fd = socket(AF_UNIX, SOCK_STREAM, 0);\n\tif (ret < 0)\n\t\treturn false;\n\tret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));\n\tif (ret < 0 && errno == ECONNREFUSED) { /* If the process is gone, we try to clean up the socket. */\n\t\tclose(fd);\n\t\tunlink(addr.sun_path);\n\t\treturn false;\n\t}\n\tclose(fd);\n\treturn true;\n}\n\nstatic int userspace_get_wireguard_interfaces(struct string_list *list)\n{\n\tDIR *dir;\n\tstruct dirent *ent;\n\tsize_t len;\n\tchar *end;\n\tint ret = 0;\n\n\tdir = opendir(SOCK_PATH);\n\tif (!dir)\n\t\treturn errno == ENOENT ? 0 : -errno;\n\twhile ((ent = readdir(dir))) {\n\t\tlen = strlen(ent->d_name);\n\t\tif (len <= strlen(SOCK_SUFFIX))\n\t\t\tcontinue;\n\t\tend = &ent->d_name[len - strlen(SOCK_SUFFIX)];\n\t\tif (strncmp(end, SOCK_SUFFIX, strlen(SOCK_SUFFIX)))\n\t\t\tcontinue;\n\t\t*end = '\\0';\n\t\tif (!userspace_has_wireguard_interface(ent->d_name))\n\t\t\tcontinue;\n\t\tret = string_list_add(list, ent->d_name);\n\t\tif (ret < 0)\n\t\t\tgoto out;\n\t}\nout:\n\tclosedir(dir);\n\treturn ret;\n}\n"
  },
  {
    "path": "src/ipc-uapi-windows.h",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <windows.h>\n#include <tlhelp32.h>\n#include <accctrl.h>\n#include <aclapi.h>\n#include <stdio.h>\n#include <stdbool.h>\n#include <fcntl.h>\n#include <hashtable.h>\n\nstatic FILE *userspace_interface_file(const char *iface)\n{\n\tchar fname[MAX_PATH];\n\tHANDLE pipe_handle;\n\tSID expected_sid;\n\tDWORD bytes = sizeof(expected_sid);\n\tPSID pipe_sid;\n\tPSECURITY_DESCRIPTOR pipe_sd;\n\tbool equal;\n\tint fd;\n\n\tif (!CreateWellKnownSid(WinLocalSystemSid, NULL, &expected_sid, &bytes))\n\t\tgoto err;\n\n\tsnprintf(fname, sizeof(fname), \"\\\\\\\\.\\\\pipe\\\\ProtectedPrefix\\\\Administrators\\\\WireGuard\\\\%s\", iface);\n\tpipe_handle = CreateFileA(fname, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);\n\tif (pipe_handle == INVALID_HANDLE_VALUE)\n\t\tgoto err;\n\tif (GetSecurityInfo(pipe_handle, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pipe_sid, NULL, NULL, NULL, &pipe_sd) != ERROR_SUCCESS)\n\t\tgoto err_close;\n\tequal = EqualSid(&expected_sid, pipe_sid);\n\tLocalFree(pipe_sd);\n\tif (!equal)\n\t\tgoto err_close;\n\tfd = _open_osfhandle((intptr_t)pipe_handle, _O_RDWR);\n\tif (fd == -1) {\n\t\tCloseHandle(pipe_handle);\n\t\treturn NULL;\n\t}\n\treturn _fdopen(fd, \"r+\");\nerr_close:\n\tCloseHandle(pipe_handle);\nerr:\n\terrno = EACCES;\n\treturn NULL;\n}\n\nstatic bool have_cached_interfaces;\nstatic struct hashtable cached_interfaces;\n\nstatic bool userspace_has_wireguard_interface(const char *iface)\n{\n\tchar fname[MAX_PATH];\n\tWIN32_FIND_DATA find_data;\n\tHANDLE find_handle;\n\tbool ret = false;\n\n\tif (have_cached_interfaces)\n\t\treturn hashtable_find_entry(&cached_interfaces, iface) != NULL;\n\n\tsnprintf(fname, sizeof(fname), \"ProtectedPrefix\\\\Administrators\\\\WireGuard\\\\%s\", iface);\n\tfind_handle = FindFirstFile(\"\\\\\\\\.\\\\pipe\\\\*\", &find_data);\n\tif (find_handle == INVALID_HANDLE_VALUE)\n\t\treturn -EIO;\n\tdo {\n\t\tif (!strcmp(fname, find_data.cFileName)) {\n\t\t\tret = true;\n\t\t\tbreak;\n\t\t}\n\t} while (FindNextFile(find_handle, &find_data));\n\tFindClose(find_handle);\n\treturn ret;\n}\n\nstatic int userspace_get_wireguard_interfaces(struct string_list *list)\n{\n\tstatic const char prefix[] = \"ProtectedPrefix\\\\Administrators\\\\WireGuard\\\\\";\n\tWIN32_FIND_DATA find_data;\n\tHANDLE find_handle;\n\tchar *iface;\n\tint ret = 0;\n\n\tfind_handle = FindFirstFile(\"\\\\\\\\.\\\\pipe\\\\*\", &find_data);\n\tif (find_handle == INVALID_HANDLE_VALUE)\n\t\treturn -EIO;\n\tdo {\n\t\tif (strncmp(prefix, find_data.cFileName, strlen(prefix)))\n\t\t\tcontinue;\n\t\tiface = find_data.cFileName + strlen(prefix);\n\t\tret = string_list_add(list, iface);\n\t\tif (ret < 0)\n\t\t\tgoto out;\n\t\tif (!hashtable_find_or_insert_entry(&cached_interfaces, iface)) {\n\t\t\tret = -errno;\n\t\t\tgoto out;\n\t\t}\n\t} while (FindNextFile(find_handle, &find_data));\n\thave_cached_interfaces = true;\n\nout:\n\tFindClose(find_handle);\n\treturn ret;\n}\n"
  },
  {
    "path": "src/ipc-uapi.h",
    "content": "// SPDX-License-Identifier: MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <arpa/inet.h>\n#include <errno.h>\n#include <limits.h>\n#include <net/if.h>\n#include <netdb.h>\n#include <netinet/in.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include \"containers.h\"\n#include \"curve25519.h\"\n#include \"encoding.h\"\n#include \"ctype.h\"\n\n#ifdef _WIN32\n#include \"ipc-uapi-windows.h\"\n#else\n#include \"ipc-uapi-unix.h\"\n#endif\n\nstatic int userspace_set_device(struct wgdevice *dev)\n{\n\tchar hex[WG_KEY_LEN_HEX], ip[INET6_ADDRSTRLEN], host[4096 + 1], service[512 + 1];\n\tstruct wgpeer *peer;\n\tstruct wgallowedip *allowedip;\n\tFILE *f;\n\tint ret, set_errno = -EPROTO;\n\tsocklen_t addr_len;\n\tsize_t line_buffer_len = 0, line_len;\n\tchar *key = NULL, *value;\n\n\tf = userspace_interface_file(dev->name);\n\tif (!f)\n\t\treturn -errno;\n\tfprintf(f, \"set=1\\n\");\n\n\tif (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {\n\t\tkey_to_hex(hex, dev->private_key);\n\t\tfprintf(f, \"private_key=%s\\n\", hex);\n\t}\n\tif (dev->flags & WGDEVICE_HAS_LISTEN_PORT)\n\t\tfprintf(f, \"listen_port=%u\\n\", dev->listen_port);\n\tif (dev->flags & WGDEVICE_HAS_FWMARK)\n\t\tfprintf(f, \"fwmark=%u\\n\", dev->fwmark);\n\tif (dev->flags & WGDEVICE_REPLACE_PEERS)\n\t\tfprintf(f, \"replace_peers=true\\n\");\n\n\tfor_each_wgpeer(dev, peer) {\n\t\tkey_to_hex(hex, peer->public_key);\n\t\tfprintf(f, \"public_key=%s\\n\", hex);\n\t\tif (peer->flags & WGPEER_REMOVE_ME) {\n\t\t\tfprintf(f, \"remove=true\\n\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (peer->flags & WGPEER_HAS_PRESHARED_KEY) {\n\t\t\tkey_to_hex(hex, peer->preshared_key);\n\t\t\tfprintf(f, \"preshared_key=%s\\n\", hex);\n\t\t}\n\t\tif (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {\n\t\t\taddr_len = 0;\n\t\t\tif (peer->endpoint.addr.sa_family == AF_INET)\n\t\t\t\taddr_len = sizeof(struct sockaddr_in);\n\t\t\telse if (peer->endpoint.addr.sa_family == AF_INET6)\n\t\t\t\taddr_len = sizeof(struct sockaddr_in6);\n\t\t\tif (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {\n\t\t\t\tif (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':'))\n\t\t\t\t\tfprintf(f, \"endpoint=[%s]:%s\\n\", host, service);\n\t\t\t\telse\n\t\t\t\t\tfprintf(f, \"endpoint=%s:%s\\n\", host, service);\n\t\t\t}\n\t\t}\n\t\tif (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL)\n\t\t\tfprintf(f, \"persistent_keepalive_interval=%u\\n\", peer->persistent_keepalive_interval);\n\t\tif (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)\n\t\t\tfprintf(f, \"replace_allowed_ips=true\\n\");\n\t\tfor_each_wgallowedip(peer, allowedip) {\n\t\t\tif (allowedip->family == AF_INET) {\n\t\t\t\tif (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN))\n\t\t\t\t\tcontinue;\n\t\t\t} else if (allowedip->family == AF_INET6) {\n\t\t\t\tif (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN))\n\t\t\t\t\tcontinue;\n\t\t\t} else\n\t\t\t\tcontinue;\n\t\t\tfprintf(f, \"allowed_ip=%s%s/%d\\n\", (allowedip->flags & WGALLOWEDIP_REMOVE_ME) ? \"-\" : \"\", ip, allowedip->cidr);\n\t\t}\n\t}\n\tfprintf(f, \"\\n\");\n\tfflush(f);\n\n\twhile (getline(&key, &line_buffer_len, f) > 0) {\n\t\tline_len = strlen(key);\n\t\tret = set_errno;\n\t\tif (line_len == 1 && key[0] == '\\n')\n\t\t\tgoto out;\n\t\tvalue = strchr(key, '=');\n\t\tif (!value || line_len == 0 || key[line_len - 1] != '\\n')\n\t\t\tbreak;\n\t\t*value++ = key[--line_len] = '\\0';\n\n\t\tif (!strcmp(key, \"errno\")) {\n\t\t\tlong long num;\n\t\t\tchar *end;\n\t\t\tif (value[0] != '-' && !char_is_digit(value[0]))\n\t\t\t\tbreak;\n\t\t\tnum = strtoll(value, &end, 10);\n\t\t\tif (*end || num > INT_MAX || num < INT_MIN)\n\t\t\t\tbreak;\n\t\t\tset_errno = num;\n\t\t}\n\t}\n\tret = errno ? -errno : -EPROTO;\nout:\n\tfree(key);\n\tfclose(f);\n\terrno = -ret;\n\treturn ret;\n}\n\n#define NUM(max) ({ \\\n\tunsigned long long num; \\\n\tchar *end; \\\n\tif (!char_is_digit(value[0])) \\\n\t\tbreak; \\\n\tnum = strtoull(value, &end, 10); \\\n\tif (*end || num > max) \\\n\t\tbreak; \\\n\tnum; \\\n})\n\nstatic int userspace_get_device(struct wgdevice **out, const char *iface)\n{\n\tstruct wgdevice *dev;\n\tstruct wgpeer *peer = NULL;\n\tstruct wgallowedip *allowedip = NULL;\n\tsize_t line_buffer_len = 0, line_len;\n\tchar *key = NULL, *value;\n\tFILE *f;\n\tint ret = -EPROTO;\n\n\t*out = dev = calloc(1, sizeof(*dev));\n\tif (!dev)\n\t\treturn -errno;\n\n\tf = userspace_interface_file(iface);\n\tif (!f) {\n\t\tret = -errno;\n\t\tfree(dev);\n\t\t*out = NULL;\n\t\treturn ret;\n\t}\n\n\tfprintf(f, \"get=1\\n\\n\");\n\tfflush(f);\n\n\tstrncpy(dev->name, iface, IFNAMSIZ - 1);\n\tdev->name[IFNAMSIZ - 1] = '\\0';\n\n\twhile (getline(&key, &line_buffer_len, f) > 0) {\n\t\tline_len = strlen(key);\n\t\tif (line_len == 1 && key[0] == '\\n')\n\t\t\tgoto err;\n\t\tvalue = strchr(key, '=');\n\t\tif (!value || line_len == 0 || key[line_len - 1] != '\\n')\n\t\t\tbreak;\n\t\t*value++ = key[--line_len] = '\\0';\n\n\t\tif (!peer && !strcmp(key, \"private_key\")) {\n\t\t\tif (!key_from_hex(dev->private_key, value))\n\t\t\t\tbreak;\n\t\t\tcurve25519_generate_public(dev->public_key, dev->private_key);\n\t\t\tdev->flags |= WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_HAS_PUBLIC_KEY;\n\t\t} else if (!peer && !strcmp(key, \"listen_port\")) {\n\t\t\tdev->listen_port = NUM(0xffffU);\n\t\t\tdev->flags |= WGDEVICE_HAS_LISTEN_PORT;\n\t\t} else if (!peer && !strcmp(key, \"fwmark\")) {\n\t\t\tdev->fwmark = NUM(0xffffffffU);\n\t\t\tdev->flags |= WGDEVICE_HAS_FWMARK;\n\t\t} else if (!strcmp(key, \"public_key\")) {\n\t\t\tstruct wgpeer *new_peer = calloc(1, sizeof(*new_peer));\n\n\t\t\tif (!new_peer) {\n\t\t\t\tret = -ENOMEM;\n\t\t\t\tgoto err;\n\t\t\t}\n\t\t\tallowedip = NULL;\n\t\t\tif (peer)\n\t\t\t\tpeer->next_peer = new_peer;\n\t\t\telse\n\t\t\t\tdev->first_peer = new_peer;\n\t\t\tpeer = new_peer;\n\t\t\tif (!key_from_hex(peer->public_key, value))\n\t\t\t\tbreak;\n\t\t\tpeer->flags |= WGPEER_HAS_PUBLIC_KEY;\n\t\t} else if (peer && !strcmp(key, \"preshared_key\")) {\n\t\t\tif (!key_from_hex(peer->preshared_key, value))\n\t\t\t\tbreak;\n\t\t\tif (!key_is_zero(peer->preshared_key))\n\t\t\t\tpeer->flags |= WGPEER_HAS_PRESHARED_KEY;\n\t\t} else if (peer && !strcmp(key, \"endpoint\")) {\n\t\t\tchar *begin, *end;\n\t\t\tstruct addrinfo *resolved;\n\t\t\tstruct addrinfo hints = {\n\t\t\t\t.ai_family = AF_UNSPEC,\n\t\t\t\t.ai_socktype = SOCK_DGRAM,\n\t\t\t\t.ai_protocol = IPPROTO_UDP\n\t\t\t};\n\t\t\tif (!strlen(value))\n\t\t\t\tbreak;\n\t\t\tif (value[0] == '[') {\n\t\t\t\tbegin = &value[1];\n\t\t\t\tend = strchr(value, ']');\n\t\t\t\tif (!end)\n\t\t\t\t\tbreak;\n\t\t\t\t*end++ = '\\0';\n\t\t\t\tif (*end++ != ':' || !*end)\n\t\t\t\t\tbreak;\n\t\t\t} else {\n\t\t\t\tbegin = value;\n\t\t\t\tend = strrchr(value, ':');\n\t\t\t\tif (!end || !*(end + 1))\n\t\t\t\t\tbreak;\n\t\t\t\t*end++ = '\\0';\n\t\t\t}\n\t\t\tif (getaddrinfo(begin, end, &hints, &resolved) != 0) {\n\t\t\t\tret = ENETUNREACH;\n\t\t\t\tgoto err;\n\t\t\t}\n\t\t\tif ((resolved->ai_family == AF_INET && resolved->ai_addrlen == sizeof(struct sockaddr_in)) ||\n\t\t\t    (resolved->ai_family == AF_INET6 && resolved->ai_addrlen == sizeof(struct sockaddr_in6)))\n\t\t\t\tmemcpy(&peer->endpoint.addr, resolved->ai_addr, resolved->ai_addrlen);\n\t\t\telse  {\n\t\t\t\tfreeaddrinfo(resolved);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tfreeaddrinfo(resolved);\n\t\t} else if (peer && !strcmp(key, \"persistent_keepalive_interval\")) {\n\t\t\tpeer->persistent_keepalive_interval = NUM(0xffffU);\n\t\t\tpeer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;\n\t\t} else if (peer && !strcmp(key, \"allowed_ip\")) {\n\t\t\tstruct wgallowedip *new_allowedip;\n\t\t\tchar *end, *mask = value, *ip = strsep(&mask, \"/\");\n\n\t\t\tif (!mask || !char_is_digit(mask[0]))\n\t\t\t\tbreak;\n\t\t\tnew_allowedip = calloc(1, sizeof(*new_allowedip));\n\t\t\tif (!new_allowedip) {\n\t\t\t\tret = -ENOMEM;\n\t\t\t\tgoto err;\n\t\t\t}\n\t\t\tif (allowedip)\n\t\t\t\tallowedip->next_allowedip = new_allowedip;\n\t\t\telse\n\t\t\t\tpeer->first_allowedip = new_allowedip;\n\t\t\tallowedip = new_allowedip;\n\t\t\tallowedip->family = AF_UNSPEC;\n\t\t\tif (strchr(ip, ':')) {\n\t\t\t\tif (inet_pton(AF_INET6, ip, &allowedip->ip6) == 1)\n\t\t\t\t\tallowedip->family = AF_INET6;\n\t\t\t} else {\n\t\t\t\tif (inet_pton(AF_INET, ip, &allowedip->ip4) == 1)\n\t\t\t\t\tallowedip->family = AF_INET;\n\t\t\t}\n\t\t\tallowedip->cidr = strtoul(mask, &end, 10);\n\t\t\tif (*end || allowedip->family == AF_UNSPEC || (allowedip->family == AF_INET6 && allowedip->cidr > 128) || (allowedip->family == AF_INET && allowedip->cidr > 32))\n\t\t\t\tbreak;\n\t\t} else if (peer && !strcmp(key, \"last_handshake_time_sec\"))\n\t\t\tpeer->last_handshake_time.tv_sec = NUM(0x7fffffffffffffffULL);\n\t\telse if (peer && !strcmp(key, \"last_handshake_time_nsec\"))\n\t\t\tpeer->last_handshake_time.tv_nsec = NUM(0x7fffffffffffffffULL);\n\t\telse if (peer && !strcmp(key, \"rx_bytes\"))\n\t\t\tpeer->rx_bytes = NUM(0xffffffffffffffffULL);\n\t\telse if (peer && !strcmp(key, \"tx_bytes\"))\n\t\t\tpeer->tx_bytes = NUM(0xffffffffffffffffULL);\n\t\telse if (!strcmp(key, \"errno\"))\n\t\t\tret = -NUM(0x7fffffffU);\n\t}\n\tret = -EPROTO;\nerr:\n\tfree(key);\n\tif (ret) {\n\t\tfree_wgdevice(dev);\n\t\t*out = NULL;\n\t}\n\tfclose(f);\n\terrno = -ret;\n\treturn ret;\n\n}\n#undef NUM\n"
  },
  {
    "path": "src/ipc-windows.h",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include \"containers.h\"\n#include <windows.h>\n#include <setupapi.h>\n#include <cfgmgr32.h>\n#include <iphlpapi.h>\n#include <initguid.h>\n#include <devguid.h>\n#include <ddk/ndisguid.h>\n#include <wireguard.h>\n#include <hashtable.h>\n\n#define IPC_SUPPORTS_KERNEL_INTERFACE\n\nstatic bool have_cached_kernel_interfaces;\nstatic struct hashtable cached_kernel_interfaces;\nstatic const DEVPROPKEY devpkey_name = DEVPKEY_WG_NAME;\n\nstatic int kernel_get_wireguard_interfaces(struct string_list *list)\n{\n\tHDEVINFO dev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, L\"SWD\\\\WireGuard\", NULL, DIGCF_PRESENT, NULL, NULL, NULL);\n\tbool will_have_cached_kernel_interfaces = true;\n\n\tif (dev_info == INVALID_HANDLE_VALUE) {\n\t\terrno = EACCES;\n\t\treturn -errno;\n\t}\n\n\tfor (DWORD i = 0;; ++i) {\n\t\tDWORD buf_len;\n\t\tWCHAR adapter_name[MAX_ADAPTER_NAME];\n\t\tSP_DEVINFO_DATA dev_info_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };\n\t\tDEVPROPTYPE prop_type;\n\t\tULONG status, problem_code;\n\t\tchar *interface_name;\n\t\tstruct hashtable_entry *entry;\n\n\t\tif (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {\n\t\t\tif (GetLastError() == ERROR_NO_MORE_ITEMS)\n\t\t\t\tbreak;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!SetupDiGetDevicePropertyW(dev_info, &dev_info_data, &devpkey_name,\n\t\t\t\t\t       &prop_type, (PBYTE)adapter_name,\n\t\t\t\t\t       sizeof(adapter_name), NULL, 0) ||\n\t\t\t\tprop_type != DEVPROP_TYPE_STRING)\n\t\t\tcontinue;\n\t\tadapter_name[_countof(adapter_name) - 1] = L'0';\n\t\tif (!adapter_name[0])\n\t\t\tcontinue;\n\t\tbuf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, NULL, 0, NULL, NULL);\n\t\tif (!buf_len)\n\t\t\tcontinue;\n\t\tinterface_name = malloc(buf_len);\n\t\tif (!interface_name)\n\t\t\tcontinue;\n\t\tbuf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, interface_name, buf_len, NULL, NULL);\n\t\tif (!buf_len) {\n\t\t\tfree(interface_name);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (CM_Get_DevNode_Status(&status, &problem_code, dev_info_data.DevInst, 0) == CR_SUCCESS &&\n\t\t    (status & (DN_DRIVER_LOADED | DN_STARTED)) == (DN_DRIVER_LOADED | DN_STARTED))\n\t\t\tstring_list_add(list, interface_name);\n\n\t\tentry = hashtable_find_or_insert_entry(&cached_kernel_interfaces, interface_name);\n\t\tfree(interface_name);\n\t\tif (!entry)\n\t\t\tcontinue;\n\n\t\tif (SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, NULL, 0, &buf_len) || GetLastError() != ERROR_INSUFFICIENT_BUFFER)\n\t\t\tcontinue;\n\t\tentry->value = calloc(sizeof(WCHAR), buf_len);\n\t\tif (!entry->value)\n\t\t\tcontinue;\n\t\tif (!SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, entry->value, buf_len, &buf_len)) {\n\t\t\tfree(entry->value);\n\t\t\tentry->value = NULL;\n\t\t\tcontinue;\n\t\t}\n\n\t\twill_have_cached_kernel_interfaces = true;\n\t}\n\tSetupDiDestroyDeviceInfoList(dev_info);\n\thave_cached_kernel_interfaces = will_have_cached_kernel_interfaces;\n\treturn 0;\n}\n\nstatic HANDLE kernel_interface_handle(const char *iface)\n{\n\tHDEVINFO dev_info;\n\tWCHAR *interfaces = NULL;\n\tHANDLE handle;\n\n\tif (have_cached_kernel_interfaces) {\n\t\tstruct hashtable_entry *entry = hashtable_find_entry(&cached_kernel_interfaces, iface);\n\t\tif (entry) {\n\t\t\tDWORD buf_len;\n\t\t\tif (CM_Get_Device_Interface_List_SizeW(\n\t\t\t\t&buf_len, (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)entry->value,\n\t\t\t\tCM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS)\n\t\t\t\tgoto err_hash;\n\t\t\tinterfaces = calloc(buf_len, sizeof(*interfaces));\n\t\t\tif (!interfaces)\n\t\t\t\tgoto err_hash;\n\t\t\tif (CM_Get_Device_Interface_ListW(\n\t\t\t\t(GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)entry->value, interfaces, buf_len,\n\t\t\t\tCM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS || !interfaces[0]) {\n\t\t\t\tfree(interfaces);\n\t\t\t\tinterfaces = NULL;\n\t\t\t\tgoto err_hash;\n\t\t\t}\n\t\t\thandle = CreateFileW(interfaces, GENERIC_READ | GENERIC_WRITE,\n\t\t\t\t\t     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,\n\t\t\t\t\t     OPEN_EXISTING, 0, NULL);\n\t\t\tfree(interfaces);\n\t\t\tif (handle == INVALID_HANDLE_VALUE)\n\t\t\t\tgoto err_hash;\n\t\t\treturn handle;\nerr_hash:\n\t\t\terrno = EACCES;\n\t\t\treturn NULL;\n\t\t}\n\t}\n\n\tdev_info = SetupDiGetClassDevsExW(&GUID_DEVCLASS_NET, L\"SWD\\\\WireGuard\", NULL, DIGCF_PRESENT, NULL, NULL, NULL);\n\tif (dev_info == INVALID_HANDLE_VALUE)\n\t\treturn NULL;\n\n\tfor (DWORD i = 0; !interfaces; ++i) {\n\t\tbool found;\n\t\tDWORD buf_len;\n\t\tWCHAR *buf, adapter_name[MAX_ADAPTER_NAME];\n\t\tSP_DEVINFO_DATA dev_info_data = { .cbSize = sizeof(SP_DEVINFO_DATA) };\n\t\tDEVPROPTYPE prop_type;\n\t\tchar *interface_name;\n\n\t\tif (!SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data)) {\n\t\t\tif (GetLastError() == ERROR_NO_MORE_ITEMS)\n\t\t\t\tbreak;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!SetupDiGetDevicePropertyW(dev_info, &dev_info_data, &devpkey_name,\n\t\t\t\t\t       &prop_type, (PBYTE)adapter_name,\n\t\t\t\t\t       sizeof(adapter_name), NULL, 0) ||\n\t\t\t\tprop_type != DEVPROP_TYPE_STRING)\n\t\t\tcontinue;\n\t\tadapter_name[_countof(adapter_name) - 1] = L'0';\n\t\tif (!adapter_name[0])\n\t\t\tcontinue;\n\t\tbuf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, NULL, 0, NULL, NULL);\n\t\tif (!buf_len)\n\t\t\tcontinue;\n\t\tinterface_name = malloc(buf_len);\n\t\tif (!interface_name)\n\t\t\tcontinue;\n\t\tbuf_len = WideCharToMultiByte(CP_UTF8, 0, adapter_name, -1, interface_name, buf_len, NULL, NULL);\n\t\tif (!buf_len) {\n\t\t\tfree(interface_name);\n\t\t\tcontinue;\n\t\t}\n\t\tfound = !strcmp(interface_name, iface);\n\t\tfree(interface_name);\n\t\tif (!found)\n\t\t\tcontinue;\n\n\t\tif (SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, NULL, 0, &buf_len) || GetLastError() != ERROR_INSUFFICIENT_BUFFER)\n\t\t\tcontinue;\n\t\tbuf = calloc(sizeof(*buf), buf_len);\n\t\tif (!buf)\n\t\t\tcontinue;\n\t\tif (!SetupDiGetDeviceInstanceIdW(dev_info, &dev_info_data, buf, buf_len, &buf_len))\n\t\t\tgoto cleanup_instance_id;\n\t\tif (CM_Get_Device_Interface_List_SizeW(\n\t\t\t&buf_len, (GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)buf,\n\t\t\tCM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS)\n\t\t\tgoto cleanup_instance_id;\n\t\tinterfaces = calloc(buf_len, sizeof(*interfaces));\n\t\tif (!interfaces)\n\t\t\tgoto cleanup_instance_id;\n\t\tif (CM_Get_Device_Interface_ListW(\n\t\t\t(GUID *)&GUID_DEVINTERFACE_NET, (DEVINSTID_W)buf, interfaces, buf_len,\n\t\t\tCM_GET_DEVICE_INTERFACE_LIST_PRESENT) != CR_SUCCESS || !interfaces[0]) {\n\t\t\tfree(interfaces);\n\t\t\tinterfaces = NULL;\n\t\t\tgoto cleanup_instance_id;\n\t\t}\ncleanup_instance_id:\n\t\tfree(buf);\n\t}\n\tSetupDiDestroyDeviceInfoList(dev_info);\n\tif (!interfaces) {\n\t\terrno = ENOENT;\n\t\treturn NULL;\n\t}\n\thandle = CreateFileW(interfaces, GENERIC_READ | GENERIC_WRITE,\n\t\t\t     FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,\n\t\t\t     OPEN_EXISTING, 0, NULL);\n\tfree(interfaces);\n\tif (handle == INVALID_HANDLE_VALUE) {\n\t\terrno = EACCES;\n\t\treturn NULL;\n\t}\n\treturn handle;\n}\n\nstatic int kernel_get_device(struct wgdevice **device, const char *iface)\n{\n\tWG_IOCTL_INTERFACE *wg_iface;\n\tWG_IOCTL_PEER *wg_peer;\n\tWG_IOCTL_ALLOWED_IP *wg_aip;\n\tvoid *buf = NULL;\n\tDWORD buf_len = 0;\n\tHANDLE handle = kernel_interface_handle(iface);\n\tstruct wgdevice *dev;\n\tstruct wgpeer *peer;\n\tstruct wgallowedip *aip;\n\tint ret;\n\n\t*device = NULL;\n\n\tif (!handle)\n\t\treturn -errno;\n\n\twhile (!DeviceIoControl(handle, WG_IOCTL_GET, NULL, 0, buf, buf_len, &buf_len, NULL)) {\n\t\tfree(buf);\n\t\tif (GetLastError() != ERROR_MORE_DATA) {\n\t\t\terrno = EACCES;\n\t\t\treturn -errno;\n\t\t}\n\t\tbuf = malloc(buf_len);\n\t\tif (!buf)\n\t\t\treturn -errno;\n\t}\n\n\twg_iface = (WG_IOCTL_INTERFACE *)buf;\n\tdev = calloc(1, sizeof(*dev));\n\tif (!dev)\n\t\tgoto out;\n\tstrncpy(dev->name, iface, sizeof(dev->name));\n\tdev->name[sizeof(dev->name) - 1] = '\\0';\n\n\tif (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_LISTEN_PORT) {\n\t\tdev->listen_port = wg_iface->ListenPort;\n\t\tdev->flags |= WGDEVICE_HAS_LISTEN_PORT;\n\t}\n\n\tif (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_PUBLIC_KEY) {\n\t\tmemcpy(dev->public_key, wg_iface->PublicKey, sizeof(dev->public_key));\n\t\tdev->flags |= WGDEVICE_HAS_PUBLIC_KEY;\n\t}\n\n\tif (wg_iface->Flags & WG_IOCTL_INTERFACE_HAS_PRIVATE_KEY) {\n\t\tmemcpy(dev->private_key, wg_iface->PrivateKey, sizeof(dev->private_key));\n\t\tdev->flags |= WGDEVICE_HAS_PRIVATE_KEY;\n\t}\n\n\twg_peer = buf + sizeof(WG_IOCTL_INTERFACE);\n\tfor (ULONG i = 0; i < wg_iface->PeersCount; ++i) {\n\t\tpeer = calloc(1, sizeof(*peer));\n\t\tif (!peer)\n\t\t\tgoto out;\n\n\t\tif (dev->first_peer == NULL)\n\t\t\tdev->first_peer = peer;\n\t\telse\n\t\t\tdev->last_peer->next_peer = peer;\n\t\tdev->last_peer = peer;\n\n\t\tif (wg_peer->Flags & WG_IOCTL_PEER_HAS_PUBLIC_KEY) {\n\t\t\tmemcpy(peer->public_key, wg_peer->PublicKey, sizeof(peer->public_key));\n\t\t\tpeer->flags |= WGPEER_HAS_PUBLIC_KEY;\n\t\t}\n\n\t\tif (wg_peer->Flags & WG_IOCTL_PEER_HAS_PRESHARED_KEY) {\n\t\t\tmemcpy(peer->preshared_key, wg_peer->PresharedKey, sizeof(peer->preshared_key));\n\t\t\tif (!key_is_zero(peer->preshared_key))\n\t\t\t\tpeer->flags |= WGPEER_HAS_PRESHARED_KEY;\n\t\t}\n\n\t\tif (wg_peer->Flags & WG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE) {\n\t\t\tpeer->persistent_keepalive_interval = wg_peer->PersistentKeepalive;\n\t\t\tpeer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;\n\t\t}\n\n\t\tif (wg_peer->Flags & WG_IOCTL_PEER_HAS_ENDPOINT) {\n\t\t\tif (wg_peer->Endpoint.si_family == AF_INET)\n\t\t\t\tpeer->endpoint.addr4 = wg_peer->Endpoint.Ipv4;\n\t\t\telse if (wg_peer->Endpoint.si_family == AF_INET6)\n\t\t\t\tpeer->endpoint.addr6 = wg_peer->Endpoint.Ipv6;\n\t\t}\n\n\t\tpeer->rx_bytes = wg_peer->RxBytes;\n\t\tpeer->tx_bytes = wg_peer->TxBytes;\n\n\t\tif (wg_peer->LastHandshake) {\n\t\t\tpeer->last_handshake_time.tv_sec = wg_peer->LastHandshake / 10000000 - 11644473600LL;\n\t\t\tpeer->last_handshake_time.tv_nsec = wg_peer->LastHandshake % 10000000 * 100;\n\t\t}\n\n\t\twg_aip = (void *)wg_peer + sizeof(WG_IOCTL_PEER);\n\t\tfor (ULONG j = 0; j < wg_peer->AllowedIPsCount; ++j) {\n\t\t\taip = calloc(1, sizeof(*aip));\n\t\t\tif (!aip)\n\t\t\t\tgoto out;\n\n\t\t\tif (peer->first_allowedip == NULL)\n\t\t\t\tpeer->first_allowedip = aip;\n\t\t\telse\n\t\t\t\tpeer->last_allowedip->next_allowedip = aip;\n\t\t\tpeer->last_allowedip = aip;\n\n\t\t\taip->family = wg_aip->AddressFamily;\n\t\t\tif (wg_aip->AddressFamily == AF_INET) {\n\t\t\t\tmemcpy(&aip->ip4, &wg_aip->Address.V4, sizeof(aip->ip4));\n\t\t\t\taip->cidr = wg_aip->Cidr;\n\t\t\t} else if (wg_aip->AddressFamily == AF_INET6) {\n\t\t\t\tmemcpy(&aip->ip6, &wg_aip->Address.V6, sizeof(aip->ip6));\n\t\t\t\taip->cidr = wg_aip->Cidr;\n\t\t\t}\n\t\t\t++wg_aip;\n\t\t}\n\t\twg_peer = (WG_IOCTL_PEER *)wg_aip;\n\t}\n\t*device = dev;\n\terrno = 0;\nout:\n\tret = -errno;\n\tfree(buf);\n\tCloseHandle(handle);\n\treturn ret;\n}\n\nstatic int kernel_set_device(struct wgdevice *dev)\n{\n\tWG_IOCTL_INTERFACE *wg_iface = NULL;\n\tWG_IOCTL_PEER *wg_peer;\n\tWG_IOCTL_ALLOWED_IP *wg_aip;\n\tDWORD buf_len = sizeof(WG_IOCTL_INTERFACE);\n\tHANDLE handle = kernel_interface_handle(dev->name);\n\tstruct wgpeer *peer;\n\tstruct wgallowedip *aip;\n\tsize_t peer_count, aip_count;\n\tint ret = 0;\n\n\tif (!handle)\n\t\treturn -errno;\n\n\tfor_each_wgpeer(dev, peer) {\n\t\tif (DWORD_MAX - buf_len < sizeof(WG_IOCTL_PEER)) {\n\t\t\terrno = EOVERFLOW;\n\t\t\tgoto out;\n\t\t}\n\t\tbuf_len += sizeof(WG_IOCTL_PEER);\n\t\tfor_each_wgallowedip(peer, aip) {\n\t\t\tif (DWORD_MAX - buf_len < sizeof(WG_IOCTL_ALLOWED_IP)) {\n\t\t\t\terrno = EOVERFLOW;\n\t\t\t\tgoto out;\n\t\t\t}\n\t\t\tbuf_len += sizeof(WG_IOCTL_ALLOWED_IP);\n\t\t}\n\t}\n\twg_iface = calloc(1, buf_len);\n\tif (!wg_iface)\n\t\tgoto out;\n\n\tif (dev->flags & WGDEVICE_HAS_PRIVATE_KEY) {\n\t\tmemcpy(wg_iface->PrivateKey, dev->private_key, sizeof(wg_iface->PrivateKey));\n\t\twg_iface->Flags |= WG_IOCTL_INTERFACE_HAS_PRIVATE_KEY;\n\t}\n\n\tif (dev->flags & WGDEVICE_HAS_LISTEN_PORT) {\n\t\twg_iface->ListenPort = dev->listen_port;\n\t\twg_iface->Flags |= WG_IOCTL_INTERFACE_HAS_LISTEN_PORT;\n\t}\n\n\tif (dev->flags & WGDEVICE_REPLACE_PEERS)\n\t\twg_iface->Flags |= WG_IOCTL_INTERFACE_REPLACE_PEERS;\n\n\tpeer_count = 0;\n\twg_peer = (void *)wg_iface + sizeof(WG_IOCTL_INTERFACE);\n\tfor_each_wgpeer(dev, peer) {\n\t\twg_peer->Flags = WG_IOCTL_PEER_HAS_PUBLIC_KEY;\n\t\tmemcpy(wg_peer->PublicKey, peer->public_key, sizeof(wg_peer->PublicKey));\n\n\t\tif (peer->flags & WGPEER_HAS_PRESHARED_KEY) {\n\t\t\tmemcpy(wg_peer->PresharedKey, peer->preshared_key, sizeof(wg_peer->PresharedKey));\n\t\t\twg_peer->Flags |= WG_IOCTL_PEER_HAS_PRESHARED_KEY;\n\t\t}\n\n\t\tif (peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) {\n\t\t\twg_peer->PersistentKeepalive = peer->persistent_keepalive_interval;\n\t\t\twg_peer->Flags |= WG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE;\n\t\t}\n\n\t\tif (peer->endpoint.addr.sa_family == AF_INET) {\n\t\t\twg_peer->Endpoint.Ipv4 = peer->endpoint.addr4;\n\t\t\twg_peer->Flags |= WG_IOCTL_PEER_HAS_ENDPOINT;\n\t\t} else if (peer->endpoint.addr.sa_family == AF_INET6) {\n\t\t\twg_peer->Endpoint.Ipv6 = peer->endpoint.addr6;\n\t\t\twg_peer->Flags |= WG_IOCTL_PEER_HAS_ENDPOINT;\n\t\t}\n\n\t\tif (peer->flags & WGPEER_REPLACE_ALLOWEDIPS)\n\t\t\twg_peer->Flags |= WG_IOCTL_PEER_REPLACE_ALLOWED_IPS;\n\n\t\tif (peer->flags & WGPEER_REMOVE_ME)\n\t\t\twg_peer->Flags |= WG_IOCTL_PEER_REMOVE;\n\n\t\taip_count = 0;\n\t\twg_aip = (void *)wg_peer + sizeof(WG_IOCTL_PEER);\n\t\tfor_each_wgallowedip(peer, aip) {\n\t\t\twg_aip->Flags = aip->flags;\n\t\t\twg_aip->AddressFamily = aip->family;\n\t\t\twg_aip->Cidr = aip->cidr;\n\n\t\t\tif (aip->family == AF_INET)\n\t\t\t\twg_aip->Address.V4 = aip->ip4;\n\t\t\telse if (aip->family == AF_INET6)\n\t\t\t\twg_aip->Address.V6 = aip->ip6;\n\t\t\telse\n\t\t\t\tcontinue;\n\t\t\t++aip_count;\n\t\t\t++wg_aip;\n\t\t}\n\t\twg_peer->AllowedIPsCount = aip_count;\n\t\t++peer_count;\n\t\twg_peer = (WG_IOCTL_PEER *)wg_aip;\n\t}\n\twg_iface->PeersCount = peer_count;\n\n\tif (!DeviceIoControl(handle, WG_IOCTL_SET, NULL, 0, wg_iface, buf_len, &buf_len, NULL)) {\n\t\terrno = EACCES;\n\t\tgoto out;\n\t}\n\terrno = 0;\n\nout:\n\tret = -errno;\n\tfree(wg_iface);\n\tCloseHandle(handle);\n\treturn ret;\n}\n"
  },
  {
    "path": "src/ipc.c",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <string.h>\n#include <stdlib.h>\n#include <errno.h>\n#include \"containers.h\"\n#include \"ipc.h\"\n\nstruct string_list {\n\tchar *buffer;\n\tsize_t len;\n\tsize_t cap;\n};\n\nstatic int string_list_add(struct string_list *list, const char *str)\n{\n\tsize_t len = strlen(str) + 1;\n\n\tif (len == 1)\n\t\treturn 0;\n\n\tif (len >= list->cap - list->len) {\n\t\tchar *new_buffer;\n\t\tsize_t new_cap = list->cap * 2;\n\n\t\tif (new_cap < list->len + len + 1)\n\t\t\tnew_cap = list->len + len + 1;\n\t\tnew_buffer = realloc(list->buffer, new_cap);\n\t\tif (!new_buffer)\n\t\t\treturn -errno;\n\t\tlist->buffer = new_buffer;\n\t\tlist->cap = new_cap;\n\t}\n\tmemcpy(list->buffer + list->len, str, len);\n\tlist->len += len;\n\tlist->buffer[list->len] = '\\0';\n\treturn 0;\n}\n\n#include \"ipc-uapi.h\"\n#if defined(__linux__)\n#include \"ipc-linux.h\"\n#elif defined(__OpenBSD__)\n#include \"ipc-openbsd.h\"\n#elif defined(__FreeBSD__)\n#include \"ipc-freebsd.h\"\n#elif defined(_WIN32)\n#include \"ipc-windows.h\"\n#endif\n\n/* first\\0second\\0third\\0forth\\0last\\0\\0 */\nchar *ipc_list_devices(void)\n{\n\tstruct string_list list = { 0 };\n\tint ret;\n\n#ifdef IPC_SUPPORTS_KERNEL_INTERFACE\n\tret = kernel_get_wireguard_interfaces(&list);\n\tif (ret < 0)\n\t\tgoto cleanup;\n#endif\n\tret = userspace_get_wireguard_interfaces(&list);\n\tif (ret < 0)\n\t\tgoto cleanup;\n\ncleanup:\n\terrno = -ret;\n\tif (errno) {\n\t\tfree(list.buffer);\n\t\treturn NULL;\n\t}\n\treturn list.buffer ?: strdup(\"\\0\");\n}\n\nint ipc_get_device(struct wgdevice **dev, const char *iface)\n{\n#ifdef IPC_SUPPORTS_KERNEL_INTERFACE\n\tif (userspace_has_wireguard_interface(iface))\n\t\treturn userspace_get_device(dev, iface);\n\treturn kernel_get_device(dev, iface);\n#else\n\treturn userspace_get_device(dev, iface);\n#endif\n}\n\nint ipc_set_device(struct wgdevice *dev)\n{\n#ifdef IPC_SUPPORTS_KERNEL_INTERFACE\n\tif (userspace_has_wireguard_interface(dev->name))\n\t\treturn userspace_set_device(dev);\n\treturn kernel_set_device(dev);\n#else\n\treturn userspace_set_device(dev);\n#endif\n}\n"
  },
  {
    "path": "src/ipc.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 OR MIT */\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#ifndef IPC_H\n#define IPC_H\n\n#include <stdbool.h>\n\nstruct wgdevice;\n\nint ipc_set_device(struct wgdevice *dev);\nint ipc_get_device(struct wgdevice **dev, const char *interface);\nchar *ipc_list_devices(void);\n\n#endif\n"
  },
  {
    "path": "src/man/wg-quick.8",
    "content": ".TH WG-QUICK 8 \"2016 January 1\" ZX2C4 \"WireGuard\"\n\n.SH NAME\nwg-quick - set up a WireGuard interface simply\n\n.SH SYNOPSIS\n.B wg-quick\n[\n.I up\n|\n.I down\n|\n.I save\n|\n.I strip\n] [\n.I CONFIG_FILE\n|\n.I INTERFACE\n]\n\n.SH DESCRIPTION\n\nThis is an extremely simple script for easily bringing up a WireGuard interface,\nsuitable for a few common use cases.\n\nUse \\fIup\\fP to add and set up an interface, and use \\fIdown\\fP to tear down and remove\nan interface. Running \\fIup\\fP adds a WireGuard interface, brings up the interface with the\nsupplied IP addresses, sets up mtu and routes, and optionally runs pre/post up scripts. Running \\fIdown\\fP\noptionally saves the current configuration, removes the WireGuard interface, and optionally\nruns pre/post down scripts. Running \\fIsave\\fP saves the configuration of an existing\ninterface without bringing the interface down. Use \\fIstrip\\fP to output a configuration file\nwith all\n.BR wg-quick (8)-specific\noptions removed, suitable for use with\n.BR wg (8).\n\n\\fICONFIG_FILE\\fP is a configuration file, whose filename is the interface name\nfollowed by `.conf'. Otherwise, \\fIINTERFACE\\fP is an interface name, with configuration\nfound at `/etc/wireguard/\\fIINTERFACE\\fP.conf', searched first, followed by distro-specific\nsearch paths.\n\nGenerally speaking, this utility is just a simple script that wraps invocations to\n.BR wg (8)\nand\n.BR ip (8)\nin order to set up a WireGuard interface. It is designed for users with simple\nneeds, and users with more advanced needs are highly encouraged to use a more\nspecific tool, a more complete network manager, or otherwise just use\n.BR wg (8)\nand\n.BR ip (8),\nas usual.\n\n.SH CONFIGURATION\n\nThe configuration file adds a few extra configuration values to the format understood by\n.BR wg (8)\nin order to configure additional attributes of an interface. It handles the\nvalues that it understands, and then it passes the remaining ones directly to\n.BR wg (8)\nfor further processing.\n\nIt infers all routes from the list of peers' allowed IPs, and automatically adds\nthem to the system routing table. If one of those routes is the default route\n(0.0.0.0/0 or ::/0), then it uses\n.BR ip-rule (8)\nto handle overriding of the default gateway.\n\nThe configuration file will be passed directly to \\fBwg\\fP(8)'s `setconf'\nsub-command, with the exception of the following additions to the \\fIInterface\\fP section,\nwhich are handled by this tool:\n\n.IP \\(bu\nAddress \\(em a comma-separated list of IP (v4 or v6) addresses (optionally with CIDR masks)\nto be assigned to the interface. May be specified multiple times.\n.IP \\(bu\nDNS \\(em a comma-separated list of IP (v4 or v6) addresses to be set as the interface's\nDNS servers, or non-IP hostnames to be set as the interface's DNS search domains. May be\nspecified multiple times. Upon bringing the interface up, this runs\n`resolvconf -a tun.\\fIINTERFACE\\fP -m 0 -x` and upon bringing it down, this runs\n`resolvconf -d tun.\\fIINTERFACE\\fP`. If these particular invocations of\n.BR resolvconf (8)\nare undesirable, the PostUp and PostDown keys below may be used instead.\n.IP \\(bu\nMTU \\(em if not specified, the MTU is automatically determined from the endpoint addresses\nor the system default route, which is usually a sane choice. However, to manually specify\nan MTU to override this automatic discovery, this value may be specified explicitly.\n.IP \\(bu\nTable \\(em Controls the routing table to which routes are added. There are two\nspecial values: `off' disables the creation of routes altogether, and `auto'\n(the default) adds routes to the default table and enables special handling of\ndefault routes.\n.IP \\(bu\nPreUp, PostUp, PreDown, PostDown \\(em script snippets which will be executed by\n.BR bash (1)\nbefore/after setting up/tearing down the interface, most commonly used\nto configure custom DNS options or firewall rules. The special string `%i'\nis expanded to \\fIINTERFACE\\fP. Each one may be specified multiple times, in which case\nthe commands are executed in order.\n.IP \\(bu\nSaveConfig \\(em if set to `true', the configuration is saved from the current state of the\ninterface upon shutdown. Any changes made to the configuration file before the\ninterface is removed will therefore be overwritten.\n\n.P\nRecommended \\fIINTERFACE\\fP names include `wg0' or `wgvpn0' or even `wgmgmtlan0'.\nHowever, the number at the end is in fact optional, and really\nany free-form string [a-zA-Z0-9_=+.-]{1,15} will work. So even interface names corresponding\nto geographic locations would suffice, such as `cincinnati', `nyc', or `paris', if that's\nsomehow desirable.\n\n.SH EXAMPLES\n\nThese examples draw on the same syntax found for\n.BR wg (8),\nand a more complete description may be found there. Bold lines below are for options that extend\n.BR wg (8).\n\nThe following might be used for connecting as a client to a VPN gateway for tunneling all\ntraffic:\n\n    [Interface]\n.br\n    \\fBAddress = 10.200.100.8/24\\fP\n.br\n    \\fBDNS = 10.200.100.1\\fP\n.br\n    PrivateKey = oK56DE9Ue9zK76rAc8pBl6opph+1v36lm7cXXsQKrQM=\n.br\n\n.br\n    [Peer]\n.br\n    PublicKey = GtL7fZc/bLnqZldpVofMCD6hDjrK28SsdLxevJ+qtKU=\n.br\n    PresharedKey = /UwcSPg38hW/D9Y3tcS1FOV0K1wuURMbS0sesJEP5ak=\n.br\n    AllowedIPs = 0.0.0.0/0\n.br\n    Endpoint = demo.wireguard.com:51820\n.br\n\nThe `Address` field is added here in order to set up the address for the interface. The `DNS` field\nindicates that a DNS server for the interface should be configured via\n.BR resolvconf (8).\nThe peer's allowed IPs entry implies that this interface should be configured as the default gateway,\nwhich this script does.\n\nBuilding on the last example, one might attempt the so-called ``kill-switch'', in order\nto prevent the flow of unencrypted packets through the non-WireGuard interfaces, by adding the following\ntwo lines `PostUp` and `PreDown` lines to the `[Interface]` section:\n\n    \\fBPostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT\\fP\n.br\n    \\fBPreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT\\fP\n.br\n\nThe `PostUp' and `PreDown' fields have been added to specify an\n.BR iptables (8)\ncommand which, when used with interfaces that have a peer that specifies 0.0.0.0/0 as part of the\n`AllowedIPs', works together with wg-quick's fwmark usage in order to drop all packets that\nare either not coming out of the tunnel encrypted or not going through the tunnel itself. (Note\nthat this continues to allow most DHCP traffic through, since most DHCP clients make use of PF_PACKET\nsockets, which bypass Netfilter.) When IPv6 is in use, additional similar lines could be added using\n.BR ip6tables (8).\n\nOr, perhaps it is desirable to store private keys in encrypted form, such as through use of\n.BR pass (1):\n\n    \\fBPreUp = wg set %i private-key <(pass WireGuard/private-keys/%i)\\fP\n.br\n\nFor use on a server, the following is a more complicated example involving multiple peers:\n\n    [Interface]\n.br\n    \\fBAddress = 10.192.122.1/24\\fP\n.br\n    \\fBAddress = 10.10.0.1/16\\fP\n.br\n    \\fBSaveConfig = true\\fP\n.br\n    PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=\n.br\n    ListenPort = 51820\n.br\n\n.br\n    [Peer]\n.br\n    PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=\n.br\n    AllowedIPs = 10.192.122.3/32, 10.192.124.1/24\n.br\n\n.br\n    [Peer]\n.br\n    PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=\n.br\n    AllowedIPs = 10.192.122.4/32, 192.168.0.0/16\n.br\n\n.br\n    [Peer]\n.br\n    PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=\n.br\n    AllowedIPs = 10.10.10.230/32\n\nNotice the two `Address' lines at the top, and that `SaveConfig' is set to `true', indicating\nthat the configuration file should be saved on shutdown using the current status of the\ninterface.\n\nA combination of the `Table', `PostUp', and `PreDown' fields may be used for policy routing\nas well. For example, the following may be used to send SSH traffic (TCP port 22) traffic\nthrough the tunnel:\n\n    [Interface]\n.br\n    Address = 10.192.122.1/24\n.br\n    PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=\n.br\n    ListenPort = 51820\n.br\n    \\fBTable = 1234\\fP\n.br\n    \\fBPostUp = ip rule add ipproto tcp dport 22 table 1234\\fP\n.br\n    \\fBPreDown = ip rule delete ipproto tcp dport 22 table 1234\\fP\n.br\n\n.br\n    [Peer]\n.br\n    PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=\n.br\n    AllowedIPs = 0.0.0.0/0\n\nThese configuration files may be placed in any directory, putting the desired interface name\nin the filename:\n\n\\fB    # wg-quick up /path/to/wgnet0.conf\\fP\n\nFor convenience, if only an interface name is supplied, it automatically chooses a path in\n`/etc/wireguard/':\n\n\\fB    # wg-quick up wgnet0\\fP\n\nThis will load the configuration file `/etc/wireguard/wgnet0.conf'.\n\nThe \\fIstrip\\fP command is useful for reloading configuration files without disrupting active\nsessions:\n\n\\fB    # wg syncconf wgnet0 <(wg-quick strip wgnet0)\\fP\n\n.SH SEE ALSO\n.BR wg (8),\n.BR ip (8),\n.BR ip-link (8),\n.BR ip-address (8),\n.BR ip-route (8),\n.BR ip-rule (8),\n.BR resolvconf (8).\n\n.SH AUTHOR\n.B wg-quick\nwas written by\n.MT Jason@zx2c4.com\nJason A. Donenfeld\n.ME .\nFor updates and more information, a project page is available on the\n.UR https://\\:www.wireguard.com/\nWorld Wide Web\n.UE .\n"
  },
  {
    "path": "src/man/wg.8",
    "content": ".TH WG 8 \"2015 August 13\" ZX2C4 \"WireGuard\"\n\n.SH NAME\nwg - set and retrieve configuration of WireGuard interfaces\n\n.SH SYNOPSIS\n.B wg\n[\n.I COMMAND\n] [\n.I OPTIONS\n]... [\n.I ARGS\n]...\n\n.SH DESCRIPTION\n\n.B wg\nis the configuration utility for getting and setting the configuration of\nWireGuard tunnel interfaces. The interfaces themselves can be added and removed\nusing\n.BR ip-link (8)\nand their IP addresses and routing tables can be set using\n.BR ip-address (8)\nand\n.BR ip-route (8).\nThe\n.B wg\nutility provides a series of sub-commands for changing WireGuard-specific\naspects of WireGuard interfaces.\n\nIf no COMMAND is specified, COMMAND defaults to\n.BR show .\nSub-commands that take an INTERFACE must be passed a WireGuard interface.\n\n.SH COMMANDS\n\n.TP\n\\fBshow\\fP { \\fI<interface>\\fP | \\fIall\\fP | \\fIinterfaces\\fP } [\\fIpublic-key\\fP | \\fIprivate-key\\fP | \\fIlisten-port\\fP | \\fIfwmark\\fP | \\fIpeers\\fP | \\fIpreshared-keys\\fP | \\fIendpoints\\fP | \\fIallowed-ips\\fP | \\fIlatest-handshakes\\fP | \\fIpersistent-keepalive\\fP | \\fItransfer\\fP | \\fIdump\\fP]\nShows current WireGuard configuration and runtime information of specified \\fI<interface>\\fP.\nIf no \\fI<interface>\\fP is specified, \\fI<interface>\\fP defaults to \\fIall\\fP.\nIf \\fIinterfaces\\fP is specified, prints a list of all WireGuard interfaces,\none per line, and quits. If no options are given after the interface\nspecification, then prints a list of all attributes in a visually pleasing way\nmeant for the terminal. Otherwise, prints specified information grouped by\nnewlines and tabs, meant to be used in scripts. For this script-friendly display,\nif \\fIall\\fP is specified, then the first field for all categories of information\nis the interface name. If \\fPdump\\fP is specified, then several lines are printed;\nthe first contains in order separated by tab: private-key, public-key, listen-port,\nfwmark. Subsequent lines are printed for each peer and contain in order separated\nby tab: public-key, preshared-key, endpoint, allowed-ips, latest-handshake,\ntransfer-rx, transfer-tx, persistent-keepalive.\n.TP\n\\fBshowconf\\fP \\fI<interface>\\fP\nShows the current configuration of \\fI<interface>\\fP in the format described\nby \\fICONFIGURATION FILE FORMAT\\fP below.\n.TP\n\\fBset\\fP \\fI<interface>\\fP [\\fIlisten-port\\fP \\fI<port>\\fP] [\\fIfwmark\\fP \\fI<fwmark>\\fP] [\\fIprivate-key\\fP \\fI<file-path>\\fP] [\\fIpeer\\fP \\fI<base64-public-key>\\fP [\\fIremove\\fP] [\\fIpreshared-key\\fP \\fI<file-path>\\fP] [\\fIendpoint\\fP \\fI<ip>:<port>\\fP] [\\fIpersistent-keepalive\\fP \\fI<interval seconds>\\fP] [\\fIallowed-ips\\fP \\fI[+|-]<ip1>/<cidr1>\\fP[,\\fI[+|-]<ip2>/<cidr2>\\fP]...] ]...\nSets configuration values for the specified \\fI<interface>\\fP. Multiple\n\\fIpeer\\fPs may be specified, and if the \\fIremove\\fP argument is given\nfor a peer, that peer is removed, not configured. If \\fIlisten-port\\fP\nis not specified, or set to 0, the port will be chosen randomly when the\ninterface comes up. Both \\fIprivate-key\\fP and \\fIpreshared-key\\fP must\nbe files, because command line arguments are not considered private on\nmost systems but if you are using\n.BR bash (1),\nyou may safely pass in a string by specifying as \\fIprivate-key\\fP or\n\\fIpreshared-key\\fP the expression: <(echo PRIVATEKEYSTRING). If\n\\fI/dev/null\\fP or another empty file is specified as the filename for\neither \\fIprivate-key\\fP or \\fIpreshared-key\\fP, the key is removed from\nthe device. The use of \\fIpreshared-key\\fP is optional, and may be omitted;\nit adds an additional layer of symmetric-key cryptography to be mixed into\nthe already existing public-key cryptography, for post-quantum resistance.\nIf \\fIallowed-ips\\fP is specified, but the value is the empty string, all\nallowed ips are removed from the peer. By default, \\fIallowed-ips\\fP replaces\na peer's allowed ips. If + or - is prepended to any of the ips then\nthe update is incremental; ips prefixed with '+' or '' are added to the peer's\nallowed ips if not present while ips prefixed with '-' are removed if present.\nThe use of \\fIpersistent-keepalive\\fP\nis optional and is by default off; setting it to 0 or \"off\" disables it.\nOtherwise it represents, in seconds, between 1 and 65535 inclusive, how often\nto send an authenticated empty packet to the peer, for the purpose of keeping\na stateful firewall or NAT mapping valid persistently. For example, if the\ninterface very rarely sends traffic, but it might at anytime receive traffic\nfrom a peer, and it is behind NAT, the interface might benefit from having a\npersistent keepalive interval of 25 seconds; however, most users will not need\nthis. The use of \\fIfwmark\\fP is optional and is by default off; setting it to\n0 or \"off\" disables it. Otherwise it is a 32-bit fwmark for outgoing packets\nand may be specified in hexadecimal by prepending \"0x\".\n.TP\n\\fBsetconf\\fP \\fI<interface>\\fP \\fI<configuration-filename>\\fP\nSets the current configuration of \\fI<interface>\\fP to the contents of\n\\fI<configuration-filename>\\fP, which must be in the format described\nby \\fICONFIGURATION FILE FORMAT\\fP below.\n.TP\n\\fBaddconf\\fP \\fI<interface>\\fP \\fI<configuration-filename>\\fP\nAppends the contents of \\fI<configuration-filename>\\fP, which must\nbe in the format described by \\fICONFIGURATION FILE FORMAT\\fP below,\nto the current configuration of \\fI<interface>\\fP.\n.TP\n\\fBsyncconf\\fP \\fI<interface>\\fP \\fI<configuration-filename>\\fP\nLike \\fBsetconf\\fP, but reads back the existing configuration first\nand only makes changes that are explicitly different between the configuration\nfile and the interface. This is much less efficient than \\fBsetconf\\fP,\nbut has the benefit of not disrupting current peer sessions. The contents of\n\\fI<configuration-filename>\\fP must be in the format described by\n\\fICONFIGURATION FILE FORMAT\\fP below.\n.TP\n\\fBgenkey\\fP\nGenerates a random \\fIprivate\\fP key in base64 and prints it to\nstandard output.\n.TP\n\\fBgenpsk\\fP\nGenerates a random \\fIpreshared\\fP key in base64 and prints it to\nstandard output.\n.TP\n\\fBpubkey\\fP\nCalculates a \\fIpublic\\fP key and prints it in base64 to standard\noutput from a corresponding \\fIprivate\\fP key (generated with\n\\fIgenkey\\fP) given in base64 on standard input.\n\nA private key and a corresponding public key may be generated at once by calling:\n.br\n    $ umask 077\n.br\n    $ wg genkey | tee private.key | wg pubkey > public.key\n.TP\n\\fBhelp\\fP\nShows usage message.\n\n.SH CONFIGURATION FILE FORMAT\nThe configuration file format is based on \\fIINI\\fP. There are two top level sections\n-- \\fIInterface\\fP and \\fIPeer\\fP. Multiple \\fIPeer\\fP sections may be specified, but\nonly one \\fIInterface\\fP section may be specified.\n\n.P\nThe \\fIInterface\\fP section may contain the following fields:\n.IP \\(bu\nPrivateKey \\(em a base64 private key generated by \\fIwg genkey\\fP. Required.\n.IP \\(bu\nListenPort \\(em a 16-bit port for listening. Optional; if not specified, chosen\nrandomly.\n.IP \\(bu\nFwMark \\(em a 32-bit fwmark for outgoing packets. If set to 0 or \"off\", this\noption is disabled. May be specified in hexadecimal by prepending \"0x\". Optional.\n.P\nThe \\fIPeer\\fP sections may contain the following fields:\n.IP \\(bu\nPublicKey \\(em a base64 public key calculated by \\fIwg pubkey\\fP from a\nprivate key, and usually transmitted out of band to the author of the\nconfiguration file. Required.\n.IP \\(bu\nPresharedKey \\(em a base64 preshared key generated by \\fIwg genpsk\\fP. Optional,\nand may be omitted. This option adds an additional layer of symmetric-key\ncryptography to be mixed into the already existing public-key cryptography,\nfor post-quantum resistance.\n.IP \\(bu\nAllowedIPs \\(em a comma-separated list of IP (v4 or v6) addresses with\nCIDR masks from which incoming traffic for this peer is allowed and to\nwhich outgoing traffic for this peer is directed. The catch-all\n\\fI0.0.0.0/0\\fP may be specified for matching all IPv4 addresses, and\n\\fI::/0\\fP may be specified for matching all IPv6 addresses. May be specified\nmultiple times.\n.IP \\(bu\nEndpoint \\(em an endpoint IP or hostname, followed by a colon, and then a\nport number. This endpoint will be updated automatically to the most recent\nsource IP address and port of correctly authenticated packets from the peer.\nOptional.\n.IP \\(bu\nPersistentKeepalive \\(em a seconds interval, between 1 and 65535 inclusive, of\nhow often to send an authenticated empty packet to the peer for the purpose of keeping a\nstateful firewall or NAT mapping valid persistently. For example, if the interface\nvery rarely sends traffic, but it might at anytime receive traffic from a peer,\nand it is behind NAT, the interface might benefit from having a persistent keepalive\ninterval of 25 seconds. If set to 0 or \"off\", this option is disabled. By default or\nwhen unspecified, this option is off. Most users will not need this. Optional.\n\n.SH CONFIGURATION FILE FORMAT EXAMPLE\nThis example may be used as a model for writing configuration files, following an\nINI-like syntax. Characters after and including a '#' are considered comments and\nare thus ignored.\n\n    [Interface]\n.br\n    PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk=\n.br\n    ListenPort = 51820\n.br\n    \n.br\n    [Peer]\n.br\n    PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg=\n.br\n    Endpoint = 192.95.5.67:1234\n.br\n    AllowedIPs = 10.192.122.3/32, 10.192.124.1/24\n.br\n    \n.br\n    [Peer]\n.br\n    PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0=\n.br\n    Endpoint = [2607:5300:60:6b0::c05f:543]:2468\n.br\n    AllowedIPs = 10.192.122.4/32, 192.168.0.0/16\n.br\n    \n.br\n    [Peer]\n.br\n    PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA=\n.br\n    Endpoint = test.wireguard.com:18981\n.br\n    AllowedIPs = 10.10.10.230/32\n\n.SH DEBUGGING INFORMATION\nSometimes it is useful to have information on the current runtime state of a tunnel. When using the Linux kernel module on a kernel that supports dynamic debugging, debugging information can be written into\n.BR dmesg (1)\nby running as root:\n\n\\fB    # modprobe wireguard && echo module wireguard +p > /sys/kernel/debug/dynamic_debug/control\\fP\n\nOn OpenBSD and FreeBSD, debugging information can be written into\n.BR dmesg (1)\non a per-interface basis by using\n.BR ifconfig (1):\n\n\\fB    # ifconfig wg0 debug\n\nOn userspace implementations, it is customary to set the \\fILOG_LEVEL\\fP environment variable to \\fIverbose\\fP.\n\n.SH ENVIRONMENT VARIABLES\n.TP\n.I WG_COLOR_MODE\nIf set to \\fIalways\\fP, always print ANSI colorized output. If set to \\fInever\\fP, never print ANSI colorized output. If set to \\fIauto\\fP, something invalid, or unset, then print ANSI colorized output only when writing to a TTY.\n.TP\n.I WG_HIDE_KEYS\nIf set to \\fInever\\fP, then the pretty-printing \\fBshow\\fP sub-command will show private and preshared keys in the output. If set to \\fIalways\\fP, something invalid, or unset, then private and preshared keys will be printed as \"(hidden)\".\n.TP\n.I WG_ENDPOINT_RESOLUTION_RETRIES\nIf set to an integer or to \\fIinfinity\\fP, DNS resolution for each peer's endpoint will be retried that many times for non-permanent errors, with an increasing delay between retries. If unset, the default is 15 retries.\n\n.SH SEE ALSO\n.BR wg-quick (8),\n.BR ip (8),\n.BR ip-link (8),\n.BR ip-address (8),\n.BR ip-route (8).\n\n.SH AUTHOR\n.B wg\nwas written by\n.MT Jason@zx2c4.com\nJason A. Donenfeld\n.ME .\nFor updates and more information, a project page is available on the\n.UR https://\\:www.wireguard.com/\nWorld Wide Web\n.UE .\n"
  },
  {
    "path": "src/netlink.h",
    "content": "// SPDX-License-Identifier: LGPL-2.1+\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n * Copyright (C) 2008-2012 Pablo Neira Ayuso <pablo@netfilter.org>.\n */\n\n/* This is a minimized version of libmnl meant to be #include'd */\n\n#include <unistd.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdint.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <errno.h>\n#include <time.h>\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <linux/netlink.h>\n#include <linux/genetlink.h>\n\n#define MNL_SOCKET_AUTOPID 0\n#define MNL_ALIGNTO 4\n#define MNL_ALIGN(len) (((len)+MNL_ALIGNTO-1) & ~(MNL_ALIGNTO-1))\n#define MNL_NLMSG_HDRLEN MNL_ALIGN(sizeof(struct nlmsghdr))\n#define MNL_ATTR_HDRLEN MNL_ALIGN(sizeof(struct nlattr))\n\nenum mnl_attr_data_type {\n\tMNL_TYPE_UNSPEC,\n\tMNL_TYPE_U8,\n\tMNL_TYPE_U16,\n\tMNL_TYPE_U32,\n\tMNL_TYPE_U64,\n\tMNL_TYPE_STRING,\n\tMNL_TYPE_FLAG,\n\tMNL_TYPE_MSECS,\n\tMNL_TYPE_NESTED,\n\tMNL_TYPE_NESTED_COMPAT,\n\tMNL_TYPE_NUL_STRING,\n\tMNL_TYPE_BINARY,\n\tMNL_TYPE_MAX,\n};\n\n#define mnl_attr_for_each(attr, nlh, offset) \\\n\tfor ((attr) = mnl_nlmsg_get_payload_offset((nlh), (offset)); \\\n\t     mnl_attr_ok((attr), (char *)mnl_nlmsg_get_payload_tail(nlh) - (char *)(attr)); \\\n\t     (attr) = mnl_attr_next(attr))\n\n#define mnl_attr_for_each_nested(attr, nest) \\\n\tfor ((attr) = mnl_attr_get_payload(nest); \\\n\t     mnl_attr_ok((attr), (char *)mnl_attr_get_payload(nest) + mnl_attr_get_payload_len(nest) - (char *)(attr)); \\\n\t     (attr) = mnl_attr_next(attr))\n\n#define mnl_attr_for_each_payload(payload, payload_size) \\\n\tfor ((attr) = (payload); \\\n\t     mnl_attr_ok((attr), (char *)(payload) + payload_size - (char *)(attr)); \\\n\t     (attr) = mnl_attr_next(attr))\n\n#define MNL_CB_ERROR\t-1\n#define MNL_CB_STOP\t0\n#define MNL_CB_OK\t1\n\ntypedef int (*mnl_attr_cb_t)(const struct nlattr *attr, void *data);\ntypedef int (*mnl_cb_t)(const struct nlmsghdr *nlh, void *data);\n\n#ifndef MNL_ARRAY_SIZE\n#define MNL_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))\n#endif\n\nstatic size_t mnl_ideal_socket_buffer_size(void)\n{\n\tstatic size_t size = 0;\n\n\tif (size)\n\t\treturn size;\n\tsize = (size_t)sysconf(_SC_PAGESIZE);\n\tif (size > 8192)\n\t\tsize = 8192;\n\treturn size;\n}\n\nstatic size_t mnl_nlmsg_size(size_t len)\n{\n\treturn len + MNL_NLMSG_HDRLEN;\n}\n\nstatic struct nlmsghdr *mnl_nlmsg_put_header(void *buf)\n{\n\tint len = MNL_ALIGN(sizeof(struct nlmsghdr));\n\tstruct nlmsghdr *nlh = buf;\n\n\tmemset(buf, 0, len);\n\tnlh->nlmsg_len = len;\n\treturn nlh;\n}\n\nstatic void *mnl_nlmsg_put_extra_header(struct nlmsghdr *nlh, size_t size)\n{\n\tchar *ptr = (char *)nlh + nlh->nlmsg_len;\n\tsize_t len = MNL_ALIGN(size);\n\tnlh->nlmsg_len += len;\n\tmemset(ptr, 0, len);\n\treturn ptr;\n}\n\nstatic void *mnl_nlmsg_get_payload(const struct nlmsghdr *nlh)\n{\n\treturn (void *)nlh + MNL_NLMSG_HDRLEN;\n}\n\nstatic void *mnl_nlmsg_get_payload_offset(const struct nlmsghdr *nlh, size_t offset)\n{\n\treturn (void *)nlh + MNL_NLMSG_HDRLEN + MNL_ALIGN(offset);\n}\n\nstatic bool mnl_nlmsg_ok(const struct nlmsghdr *nlh, int len)\n{\n\treturn len >= (int)sizeof(struct nlmsghdr) &&\n\t       nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&\n\t       (int)nlh->nlmsg_len <= len;\n}\n\nstatic struct nlmsghdr *mnl_nlmsg_next(const struct nlmsghdr *nlh, int *len)\n{\n\t*len -= MNL_ALIGN(nlh->nlmsg_len);\n\treturn (struct nlmsghdr *)((void *)nlh + MNL_ALIGN(nlh->nlmsg_len));\n}\n\nstatic void *mnl_nlmsg_get_payload_tail(const struct nlmsghdr *nlh)\n{\n\treturn (void *)nlh + MNL_ALIGN(nlh->nlmsg_len);\n}\n\nstatic bool mnl_nlmsg_seq_ok(const struct nlmsghdr *nlh, unsigned int seq)\n{\n\treturn nlh->nlmsg_seq && seq ? nlh->nlmsg_seq == seq : true;\n}\n\nstatic bool mnl_nlmsg_portid_ok(const struct nlmsghdr *nlh, unsigned int portid)\n{\n\treturn nlh->nlmsg_pid && portid ? nlh->nlmsg_pid == portid : true;\n}\n\nstatic uint16_t mnl_attr_get_type(const struct nlattr *attr)\n{\n\treturn attr->nla_type & NLA_TYPE_MASK;\n}\n\nstatic uint16_t mnl_attr_get_payload_len(const struct nlattr *attr)\n{\n\treturn attr->nla_len - MNL_ATTR_HDRLEN;\n}\n\nstatic void *mnl_attr_get_payload(const struct nlattr *attr)\n{\n\treturn (void *)attr + MNL_ATTR_HDRLEN;\n}\n\nstatic bool mnl_attr_ok(const struct nlattr *attr, int len)\n{\n\treturn len >= (int)sizeof(struct nlattr) &&\n\t       attr->nla_len >= sizeof(struct nlattr) &&\n\t       (int)attr->nla_len <= len;\n}\n\nstatic struct nlattr *mnl_attr_next(const struct nlattr *attr)\n{\n\treturn (struct nlattr *)((void *)attr + MNL_ALIGN(attr->nla_len));\n}\n\nstatic int mnl_attr_type_valid(const struct nlattr *attr, uint16_t max)\n{\n\tif (mnl_attr_get_type(attr) > max) {\n\t\terrno = EOPNOTSUPP;\n\t\treturn -1;\n\t}\n\treturn 1;\n}\n\nstatic int __mnl_attr_validate(const struct nlattr *attr,\n\t\t\t       enum mnl_attr_data_type type, size_t exp_len)\n{\n\tuint16_t attr_len = mnl_attr_get_payload_len(attr);\n\tconst char *attr_data = mnl_attr_get_payload(attr);\n\n\tif (attr_len < exp_len) {\n\t\terrno = ERANGE;\n\t\treturn -1;\n\t}\n\tswitch(type) {\n\tcase MNL_TYPE_FLAG:\n\t\tif (attr_len > 0) {\n\t\t\terrno = ERANGE;\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase MNL_TYPE_NUL_STRING:\n\t\tif (attr_len == 0) {\n\t\t\terrno = ERANGE;\n\t\t\treturn -1;\n\t\t}\n\t\tif (attr_data[attr_len-1] != '\\0') {\n\t\t\terrno = EINVAL;\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase MNL_TYPE_STRING:\n\t\tif (attr_len == 0) {\n\t\t\terrno = ERANGE;\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tcase MNL_TYPE_NESTED:\n\n\t\tif (attr_len == 0)\n\t\t\tbreak;\n\n\t\tif (attr_len < MNL_ATTR_HDRLEN) {\n\t\t\terrno = ERANGE;\n\t\t\treturn -1;\n\t\t}\n\t\tbreak;\n\tdefault:\n\n\t\tbreak;\n\t}\n\tif (exp_len && attr_len > exp_len) {\n\t\terrno = ERANGE;\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\nstatic const size_t mnl_attr_data_type_len[MNL_TYPE_MAX] = {\n\t[MNL_TYPE_U8]\t\t= sizeof(uint8_t),\n\t[MNL_TYPE_U16]\t\t= sizeof(uint16_t),\n\t[MNL_TYPE_U32]\t\t= sizeof(uint32_t),\n\t[MNL_TYPE_U64]\t\t= sizeof(uint64_t),\n\t[MNL_TYPE_MSECS]\t= sizeof(uint64_t),\n};\n\nstatic int mnl_attr_validate(const struct nlattr *attr, enum mnl_attr_data_type type)\n{\n\tint exp_len;\n\n\tif (type >= MNL_TYPE_MAX) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\texp_len = mnl_attr_data_type_len[type];\n\treturn __mnl_attr_validate(attr, type, exp_len);\n}\n\nstatic int mnl_attr_parse(const struct nlmsghdr *nlh, unsigned int offset,\n\t\t\t  mnl_attr_cb_t cb, void *data)\n{\n\tint ret = MNL_CB_OK;\n\tconst struct nlattr *attr;\n\n\tmnl_attr_for_each(attr, nlh, offset)\n\t\tif ((ret = cb(attr, data)) <= MNL_CB_STOP)\n\t\t\treturn ret;\n\treturn ret;\n}\n\nstatic int mnl_attr_parse_nested(const struct nlattr *nested, mnl_attr_cb_t cb,\n\t\t\t\t void *data)\n{\n\tint ret = MNL_CB_OK;\n\tconst struct nlattr *attr;\n\n\tmnl_attr_for_each_nested(attr, nested)\n\t\tif ((ret = cb(attr, data)) <= MNL_CB_STOP)\n\t\t\treturn ret;\n\treturn ret;\n}\n\nstatic uint8_t mnl_attr_get_u8(const struct nlattr *attr)\n{\n\treturn *((uint8_t *)mnl_attr_get_payload(attr));\n}\n\nstatic uint16_t mnl_attr_get_u16(const struct nlattr *attr)\n{\n\treturn *((uint16_t *)mnl_attr_get_payload(attr));\n}\n\nstatic uint32_t mnl_attr_get_u32(const struct nlattr *attr)\n{\n\treturn *((uint32_t *)mnl_attr_get_payload(attr));\n}\n\nstatic uint64_t mnl_attr_get_u64(const struct nlattr *attr)\n{\n\tuint64_t tmp;\n\tmemcpy(&tmp, mnl_attr_get_payload(attr), sizeof(tmp));\n\treturn tmp;\n}\n\nstatic const char *mnl_attr_get_str(const struct nlattr *attr)\n{\n\treturn mnl_attr_get_payload(attr);\n}\n\nstatic void mnl_attr_put(struct nlmsghdr *nlh, uint16_t type, size_t len,\n\t\t\t const void *data)\n{\n\tstruct nlattr *attr = mnl_nlmsg_get_payload_tail(nlh);\n\tuint16_t payload_len = MNL_ALIGN(sizeof(struct nlattr)) + len;\n\tint pad;\n\n\tattr->nla_type = type;\n\tattr->nla_len = payload_len;\n\tmemcpy(mnl_attr_get_payload(attr), data, len);\n\tnlh->nlmsg_len += MNL_ALIGN(payload_len);\n\tpad = MNL_ALIGN(len) - len;\n\tif (pad > 0)\n\t\tmemset(mnl_attr_get_payload(attr) + len, 0, pad);\n}\n\nstatic void mnl_attr_put_u16(struct nlmsghdr *nlh, uint16_t type, uint16_t data)\n{\n\tmnl_attr_put(nlh, type, sizeof(uint16_t), &data);\n}\n\nstatic void mnl_attr_put_u32(struct nlmsghdr *nlh, uint16_t type, uint32_t data)\n{\n\tmnl_attr_put(nlh, type, sizeof(uint32_t), &data);\n}\n\nstatic void mnl_attr_put_strz(struct nlmsghdr *nlh, uint16_t type, const char *data)\n{\n\tmnl_attr_put(nlh, type, strlen(data)+1, data);\n}\n\nstatic struct nlattr *mnl_attr_nest_start(struct nlmsghdr *nlh, uint16_t type)\n{\n\tstruct nlattr *start = mnl_nlmsg_get_payload_tail(nlh);\n\n\tstart->nla_type = NLA_F_NESTED | type;\n\tnlh->nlmsg_len += MNL_ALIGN(sizeof(struct nlattr));\n\treturn start;\n}\n\nstatic bool mnl_attr_put_check(struct nlmsghdr *nlh, size_t buflen,\n\t\t\t       uint16_t type, size_t len, const void *data)\n{\n\tif (nlh->nlmsg_len + MNL_ATTR_HDRLEN + MNL_ALIGN(len) > buflen)\n\t\treturn false;\n\tmnl_attr_put(nlh, type, len, data);\n\treturn true;\n}\n\nstatic bool mnl_attr_put_u8_check(struct nlmsghdr *nlh, size_t buflen,\n\t\t\t\t  uint16_t type, uint8_t data)\n{\n\treturn mnl_attr_put_check(nlh, buflen, type, sizeof(uint8_t), &data);\n}\n\nstatic bool mnl_attr_put_u16_check(struct nlmsghdr *nlh, size_t buflen,\n\t\t\t\t   uint16_t type, uint16_t data)\n{\n\treturn mnl_attr_put_check(nlh, buflen, type, sizeof(uint16_t), &data);\n}\n\nstatic bool mnl_attr_put_u32_check(struct nlmsghdr *nlh, size_t buflen,\n\t\t\t\t   uint16_t type, uint32_t data)\n{\n\treturn mnl_attr_put_check(nlh, buflen, type, sizeof(uint32_t), &data);\n}\n\nstatic struct nlattr *mnl_attr_nest_start_check(struct nlmsghdr *nlh, size_t buflen,\n\t\t\t\t\t\tuint16_t type)\n{\n\tif (nlh->nlmsg_len + MNL_ATTR_HDRLEN > buflen)\n\t\treturn NULL;\n\treturn mnl_attr_nest_start(nlh, type);\n}\n\nstatic void mnl_attr_nest_end(struct nlmsghdr *nlh, struct nlattr *start)\n{\n\tstart->nla_len = mnl_nlmsg_get_payload_tail(nlh) - (void *)start;\n}\n\nstatic void mnl_attr_nest_cancel(struct nlmsghdr *nlh, struct nlattr *start)\n{\n\tnlh->nlmsg_len -= mnl_nlmsg_get_payload_tail(nlh) - (void *)start;\n}\n\nstatic int mnl_cb_noop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)\n{\n\treturn MNL_CB_OK;\n}\n\nstatic int mnl_cb_error(const struct nlmsghdr *nlh, __attribute__((unused)) void *data)\n{\n\tconst struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);\n\n\tif (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {\n\t\terrno = EBADMSG;\n\t\treturn MNL_CB_ERROR;\n\t}\n\n\tif (err->error < 0)\n\t\terrno = -err->error;\n\telse\n\t\terrno = err->error;\n\n\treturn err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;\n}\n\nstatic int mnl_cb_stop(__attribute__((unused)) const struct nlmsghdr *nlh, __attribute__((unused)) void *data)\n{\n\treturn MNL_CB_STOP;\n}\n\nstatic const mnl_cb_t default_cb_array[NLMSG_MIN_TYPE] = {\n\t[NLMSG_NOOP]\t= mnl_cb_noop,\n\t[NLMSG_ERROR]\t= mnl_cb_error,\n\t[NLMSG_DONE]\t= mnl_cb_stop,\n\t[NLMSG_OVERRUN]\t= mnl_cb_noop,\n};\n\nstatic int __mnl_cb_run(const void *buf, size_t numbytes,\n\t\t\tunsigned int seq, unsigned int portid,\n\t\t\tmnl_cb_t cb_data, void *data,\n\t\t\tconst mnl_cb_t *cb_ctl_array,\n\t\t\tunsigned int cb_ctl_array_len)\n{\n\tint ret = MNL_CB_OK, len = numbytes;\n\tconst struct nlmsghdr *nlh = buf;\n\n\twhile (mnl_nlmsg_ok(nlh, len)) {\n\n\t\tif (!mnl_nlmsg_portid_ok(nlh, portid)) {\n\t\t\terrno = ESRCH;\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (!mnl_nlmsg_seq_ok(nlh, seq)) {\n\t\t\terrno = EPROTO;\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (nlh->nlmsg_flags & NLM_F_DUMP_INTR) {\n\t\t\terrno = EINTR;\n\t\t\treturn -1;\n\t\t}\n\n\t\tif (nlh->nlmsg_type >= NLMSG_MIN_TYPE) {\n\t\t\tif (cb_data){\n\t\t\t\tret = cb_data(nlh, data);\n\t\t\t\tif (ret <= MNL_CB_STOP)\n\t\t\t\t\tgoto out;\n\t\t\t}\n\t\t} else if (nlh->nlmsg_type < cb_ctl_array_len) {\n\t\t\tif (cb_ctl_array && cb_ctl_array[nlh->nlmsg_type]) {\n\t\t\t\tret = cb_ctl_array[nlh->nlmsg_type](nlh, data);\n\t\t\t\tif (ret <= MNL_CB_STOP)\n\t\t\t\t\tgoto out;\n\t\t\t}\n\t\t} else if (default_cb_array[nlh->nlmsg_type]) {\n\t\t\tret = default_cb_array[nlh->nlmsg_type](nlh, data);\n\t\t\tif (ret <= MNL_CB_STOP)\n\t\t\t\tgoto out;\n\t\t}\n\t\tnlh = mnl_nlmsg_next(nlh, &len);\n\t}\nout:\n\treturn ret;\n}\n\nstatic int mnl_cb_run2(const void *buf, size_t numbytes, unsigned int seq,\n\t\t       unsigned int portid, mnl_cb_t cb_data, void *data,\n\t\t       const mnl_cb_t *cb_ctl_array, unsigned int cb_ctl_array_len)\n{\n\treturn __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data,\n\t\t\t    cb_ctl_array, cb_ctl_array_len);\n}\n\nstatic int mnl_cb_run(const void *buf, size_t numbytes, unsigned int seq,\n\t\t      unsigned int portid, mnl_cb_t cb_data, void *data)\n{\n\treturn __mnl_cb_run(buf, numbytes, seq, portid, cb_data, data, NULL, 0);\n}\n\nstruct mnl_socket {\n\tint \t\t\tfd;\n\tstruct sockaddr_nl\taddr;\n};\n\nstatic unsigned int mnl_socket_get_portid(const struct mnl_socket *nl)\n{\n\treturn nl->addr.nl_pid;\n}\n\nstatic struct mnl_socket *__mnl_socket_open(int bus, int flags)\n{\n\tstruct mnl_socket *nl;\n\n\tnl = calloc(1, sizeof(struct mnl_socket));\n\tif (nl == NULL)\n\t\treturn NULL;\n\n\tnl->fd = socket(AF_NETLINK, SOCK_RAW | flags, bus);\n\tif (nl->fd == -1) {\n\t\tfree(nl);\n\t\treturn NULL;\n\t}\n\n\treturn nl;\n}\n\nstatic struct mnl_socket *mnl_socket_open(int bus)\n{\n\treturn __mnl_socket_open(bus, 0);\n}\n\nstatic int mnl_socket_bind(struct mnl_socket *nl, unsigned int groups, pid_t pid)\n{\n\tint ret;\n\tsocklen_t addr_len;\n\n\tnl->addr.nl_family = AF_NETLINK;\n\tnl->addr.nl_groups = groups;\n\tnl->addr.nl_pid = pid;\n\n\tret = bind(nl->fd, (struct sockaddr *) &nl->addr, sizeof (nl->addr));\n\tif (ret < 0)\n\t\treturn ret;\n\n\taddr_len = sizeof(nl->addr);\n\tret = getsockname(nl->fd, (struct sockaddr *) &nl->addr, &addr_len);\n\tif (ret < 0)\n\t\treturn ret;\n\n\tif (addr_len != sizeof(nl->addr)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\tif (nl->addr.nl_family != AF_NETLINK) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\treturn 0;\n}\n\nstatic ssize_t mnl_socket_sendto(const struct mnl_socket *nl, const void *buf,\n\t\t\t\t size_t len)\n{\n\tstatic const struct sockaddr_nl snl = {\n\t\t.nl_family = AF_NETLINK\n\t};\n\treturn sendto(nl->fd, buf, len, 0,\n\t\t      (struct sockaddr *) &snl, sizeof(snl));\n}\n\nstatic ssize_t mnl_socket_recvfrom(const struct mnl_socket *nl, void *buf,\n\t\t\t\t   size_t bufsiz)\n{\n\tssize_t ret;\n\tstruct sockaddr_nl addr;\n\tstruct iovec iov = {\n\t\t.iov_base\t= buf,\n\t\t.iov_len\t= bufsiz,\n\t};\n\tstruct msghdr msg = {\n\t\t.msg_name\t= &addr,\n\t\t.msg_namelen\t= sizeof(struct sockaddr_nl),\n\t\t.msg_iov\t= &iov,\n\t\t.msg_iovlen\t= 1,\n\t\t.msg_control\t= NULL,\n\t\t.msg_controllen\t= 0,\n\t\t.msg_flags\t= 0,\n\t};\n\tret = recvmsg(nl->fd, &msg, 0);\n\tif (ret == -1)\n\t\treturn ret;\n\n\tif (msg.msg_flags & MSG_TRUNC) {\n\t\terrno = ENOSPC;\n\t\treturn -1;\n\t}\n\tif (msg.msg_namelen != sizeof(struct sockaddr_nl)) {\n\t\terrno = EINVAL;\n\t\treturn -1;\n\t}\n\treturn ret;\n}\n\nstatic int mnl_socket_close(struct mnl_socket *nl)\n{\n\tint ret = close(nl->fd);\n\tfree(nl);\n\treturn ret;\n}\n\n/* This is a wrapper for generic netlink, originally from Jiri Pirko <jiri@mellanox.com>: */\n\nstruct mnlg_socket {\n\tstruct mnl_socket *nl;\n\tchar *buf;\n\tuint16_t id;\n\tuint8_t version;\n\tunsigned int seq;\n\tunsigned int portid;\n};\n\nstatic struct nlmsghdr *__mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,\n\t\t\t\t\t   uint16_t flags, uint16_t id,\n\t\t\t\t\t   uint8_t version)\n{\n\tstruct nlmsghdr *nlh;\n\tstruct genlmsghdr *genl;\n\n\tnlh = mnl_nlmsg_put_header(nlg->buf);\n\tnlh->nlmsg_type\t= id;\n\tnlh->nlmsg_flags = flags;\n\tnlg->seq = time(NULL);\n\tnlh->nlmsg_seq = nlg->seq;\n\n\tgenl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));\n\tgenl->cmd = cmd;\n\tgenl->version = version;\n\n\treturn nlh;\n}\n\nstatic struct nlmsghdr *mnlg_msg_prepare(struct mnlg_socket *nlg, uint8_t cmd,\n\t\t\t\t\t uint16_t flags)\n{\n\treturn __mnlg_msg_prepare(nlg, cmd, flags, nlg->id, nlg->version);\n}\n\nstatic int mnlg_socket_send(struct mnlg_socket *nlg, const struct nlmsghdr *nlh)\n{\n\treturn mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len);\n}\n\nstatic int mnlg_cb_noop(const struct nlmsghdr *nlh, void *data)\n{\n\t(void)nlh;\n\t(void)data;\n\treturn MNL_CB_OK;\n}\n\nstatic int mnlg_cb_error(const struct nlmsghdr *nlh, void *data)\n{\n\tconst struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh);\n\t(void)data;\n\n\tif (nlh->nlmsg_len < mnl_nlmsg_size(sizeof(struct nlmsgerr))) {\n\t\terrno = EBADMSG;\n\t\treturn MNL_CB_ERROR;\n\t}\n\t/* Netlink subsystems returns the errno value with different signess */\n\tif (err->error < 0)\n\t\terrno = -err->error;\n\telse\n\t\terrno = err->error;\n\n\treturn err->error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;\n}\n\nstatic int mnlg_cb_stop(const struct nlmsghdr *nlh, void *data)\n{\n\t(void)data;\n\tif (nlh->nlmsg_flags & NLM_F_MULTI && nlh->nlmsg_len == mnl_nlmsg_size(sizeof(int))) {\n\t\tint error = *(int *)mnl_nlmsg_get_payload(nlh);\n\t\t/* Netlink subsystems returns the errno value with different signess */\n\t\tif (error < 0)\n\t\t\terrno = -error;\n\t\telse\n\t\t\terrno = error;\n\n\t\treturn error == 0 ? MNL_CB_STOP : MNL_CB_ERROR;\n\t}\n\treturn MNL_CB_STOP;\n}\n\nstatic const mnl_cb_t mnlg_cb_array[] = {\n\t[NLMSG_NOOP]\t= mnlg_cb_noop,\n\t[NLMSG_ERROR]\t= mnlg_cb_error,\n\t[NLMSG_DONE]\t= mnlg_cb_stop,\n\t[NLMSG_OVERRUN]\t= mnlg_cb_noop,\n};\n\nstatic int mnlg_socket_recv_run(struct mnlg_socket *nlg, mnl_cb_t data_cb, void *data)\n{\n\tint err;\n\n\tdo {\n\t\terr = mnl_socket_recvfrom(nlg->nl, nlg->buf,\n\t\t\t\t\t  mnl_ideal_socket_buffer_size());\n\t\tif (err <= 0)\n\t\t\tbreak;\n\t\terr = mnl_cb_run2(nlg->buf, err, nlg->seq, nlg->portid,\n\t\t\t\t  data_cb, data, mnlg_cb_array, MNL_ARRAY_SIZE(mnlg_cb_array));\n\t} while (err > 0);\n\n\treturn err;\n}\n\nstatic int get_family_id_attr_cb(const struct nlattr *attr, void *data)\n{\n\tconst struct nlattr **tb = data;\n\tint type = mnl_attr_get_type(attr);\n\n\tif (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)\n\t\treturn MNL_CB_ERROR;\n\n\tif (type == CTRL_ATTR_FAMILY_ID &&\n\t    mnl_attr_validate(attr, MNL_TYPE_U16) < 0)\n\t\treturn MNL_CB_ERROR;\n\ttb[type] = attr;\n\treturn MNL_CB_OK;\n}\n\nstatic int get_family_id_cb(const struct nlmsghdr *nlh, void *data)\n{\n\tuint16_t *p_id = data;\n\tstruct nlattr *tb[CTRL_ATTR_MAX + 1] = { 0 };\n\n\tmnl_attr_parse(nlh, sizeof(struct genlmsghdr), get_family_id_attr_cb, tb);\n\tif (!tb[CTRL_ATTR_FAMILY_ID])\n\t\treturn MNL_CB_ERROR;\n\t*p_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);\n\treturn MNL_CB_OK;\n}\n\nstatic struct mnlg_socket *mnlg_socket_open(const char *family_name, uint8_t version)\n{\n\tstruct mnlg_socket *nlg;\n\tstruct nlmsghdr *nlh;\n\tint err;\n\n\tnlg = malloc(sizeof(*nlg));\n\tif (!nlg)\n\t\treturn NULL;\n\tnlg->id = 0;\n\n\terr = -ENOMEM;\n\tnlg->buf = malloc(mnl_ideal_socket_buffer_size());\n\tif (!nlg->buf)\n\t\tgoto err_buf_alloc;\n\n\tnlg->nl = mnl_socket_open(NETLINK_GENERIC);\n\tif (!nlg->nl) {\n\t\terr = -errno;\n\t\tgoto err_mnl_socket_open;\n\t}\n\n\tif (mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID) < 0) {\n\t\terr = -errno;\n\t\tgoto err_mnl_socket_bind;\n\t}\n\n\tnlg->portid = mnl_socket_get_portid(nlg->nl);\n\n\tnlh = __mnlg_msg_prepare(nlg, CTRL_CMD_GETFAMILY,\n\t\t\t\t NLM_F_REQUEST | NLM_F_ACK, GENL_ID_CTRL, 1);\n\tmnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name);\n\n\tif (mnlg_socket_send(nlg, nlh) < 0) {\n\t\terr = -errno;\n\t\tgoto err_mnlg_socket_send;\n\t}\n\n\terrno = 0;\n\tif (mnlg_socket_recv_run(nlg, get_family_id_cb, &nlg->id) < 0) {\n\t\terrno = errno == ENOENT ? EPROTONOSUPPORT : errno;\n\t\terr = errno ? -errno : -ENOSYS;\n\t\tgoto err_mnlg_socket_recv_run;\n\t}\n\n\tnlg->version = version;\n\terrno = 0;\n\treturn nlg;\n\nerr_mnlg_socket_recv_run:\nerr_mnlg_socket_send:\nerr_mnl_socket_bind:\n\tmnl_socket_close(nlg->nl);\nerr_mnl_socket_open:\n\tfree(nlg->buf);\nerr_buf_alloc:\n\tfree(nlg);\n\terrno = -err;\n\treturn NULL;\n}\n\nstatic void mnlg_socket_close(struct mnlg_socket *nlg)\n{\n\tmnl_socket_close(nlg->nl);\n\tfree(nlg->buf);\n\tfree(nlg);\n}\n"
  },
  {
    "path": "src/pubkey.c",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <errno.h>\n#include <stdio.h>\n\n#include \"curve25519.h\"\n#include \"encoding.h\"\n#include \"subcommands.h\"\n#include \"ctype.h\"\n\nint pubkey_main(int argc, const char *argv[])\n{\n\tuint8_t key[WG_KEY_LEN] __attribute__((aligned(sizeof(uintptr_t))));\n\tchar base64[WG_KEY_LEN_BASE64];\n\tint trailing_char;\n\n\tif (argc != 1) {\n\t\tfprintf(stderr, \"Usage: %s %s\\n\", PROG_NAME, argv[0]);\n\t\treturn 1;\n\t}\n\n\tif (fread(base64, 1, sizeof(base64) - 1, stdin) != sizeof(base64) - 1) {\n\t\terrno = EINVAL;\n\t\tfprintf(stderr, \"%s: Key is not the correct length or format\\n\", PROG_NAME);\n\t\treturn 1;\n\t}\n\tbase64[WG_KEY_LEN_BASE64 - 1] = '\\0';\n\n\tfor (;;) {\n\t\ttrailing_char = getc(stdin);\n\t\tif (!trailing_char || char_is_space(trailing_char))\n\t\t\tcontinue;\n\t\tif (trailing_char == EOF)\n\t\t\tbreak;\n\t\tfprintf(stderr, \"%s: Trailing characters found after key\\n\", PROG_NAME);\n\t\treturn 1;\n\t}\n\n\tif (!key_from_base64(key, base64)) {\n\t\tfprintf(stderr, \"%s: Key is not the correct length or format\\n\", PROG_NAME);\n\t\treturn 1;\n\t}\n\tcurve25519_generate_public(key, key);\n\tkey_to_base64(base64, key);\n\tputs(base64);\n\treturn 0;\n}\n"
  },
  {
    "path": "src/set.c",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"containers.h\"\n#include \"config.h\"\n#include \"ipc.h\"\n#include \"subcommands.h\"\n\nint set_main(int argc, const char *argv[])\n{\n\tstruct wgdevice *device = NULL;\n\tint ret = 1;\n\n\tif (argc < 3) {\n\t\tfprintf(stderr, \"Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips [+|-]<ip1>/<cidr1>[,[+|-]<ip2>/<cidr2>]...] ]...\\n\", PROG_NAME, argv[0]);\n\t\treturn 1;\n\t}\n\n\tdevice = config_read_cmd(argv + 2, argc - 2);\n\tif (!device)\n\t\tgoto cleanup;\n\tstrncpy(device->name, argv[1], IFNAMSIZ -  1);\n\tdevice->name[IFNAMSIZ - 1] = '\\0';\n\n\tif (ipc_set_device(device) != 0) {\n\t\tperror(\"Unable to modify interface\");\n\t\tgoto cleanup;\n\t}\n\n\tret = 0;\n\ncleanup:\n\tfree_wgdevice(device);\n\treturn ret;\n}\n"
  },
  {
    "path": "src/setconf.c",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"containers.h\"\n#include \"config.h\"\n#include \"ipc.h\"\n#include \"subcommands.h\"\n\nstruct peer_origin {\n\tstruct wgpeer *peer;\n\tbool from_file;\n};\n\nstatic int peer_cmp(const void *first, const void *second)\n{\n\tconst struct peer_origin *a = first, *b = second;\n\tint ret = memcmp(a->peer->public_key, b->peer->public_key, WG_KEY_LEN);\n\tif (ret)\n\t\treturn ret;\n\treturn a->from_file - b->from_file;\n}\n\nstatic bool sync_conf(struct wgdevice *file)\n{\n\tstruct wgdevice *runtime;\n\tstruct wgpeer *peer;\n\tstruct peer_origin *peers;\n\tsize_t peer_count = 0, i = 0;\n\n\tif (!file->first_peer)\n\t\treturn true;\n\n\tfor_each_wgpeer(file, peer)\n\t\t++peer_count;\n\n\tif (ipc_get_device(&runtime, file->name) != 0) {\n\t\tperror(\"Unable to retrieve current interface configuration\");\n\t\treturn false;\n\t}\n\n\tif (!runtime->first_peer) {\n\t\tfree_wgdevice(runtime);\n\t\treturn true;\n\t}\n\n\tfile->flags &= ~WGDEVICE_REPLACE_PEERS;\n\n\tfor_each_wgpeer(runtime, peer)\n\t\t++peer_count;\n\n\tpeers = calloc(peer_count, sizeof(*peers));\n\tif (!peers) {\n\t\tfree_wgdevice(runtime);\n\t\tperror(\"Peer list allocation\");\n\t\treturn false;\n\t}\n\n\tfor_each_wgpeer(file, peer) {\n\t\tpeers[i].peer = peer;\n\t\tpeers[i].from_file = true;\n\t\t++i;\n\t}\n\tfor_each_wgpeer(runtime, peer) {\n\t\tpeers[i].peer = peer;\n\t\tpeers[i].from_file = false;\n\t\t++i;\n\t}\n\tqsort(peers, peer_count, sizeof(*peers), peer_cmp);\n\n\tfor (i = 0; i < peer_count; ++i) {\n\t\tif (peers[i].from_file)\n\t\t\tcontinue;\n\t\tif (i == peer_count - 1 || !peers[i + 1].from_file || memcmp(peers[i].peer->public_key, peers[i + 1].peer->public_key, WG_KEY_LEN)) {\n\t\t\tpeer = calloc(1, sizeof(struct wgpeer));\n\t\t\tif (!peer) {\n\t\t\t\tfree_wgdevice(runtime);\n\t\t\t\tfree(peers);\n\t\t\t\tperror(\"Peer allocation\");\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tpeer->flags = WGPEER_REMOVE_ME;\n\t\t\tmemcpy(peer->public_key, peers[i].peer->public_key, WG_KEY_LEN);\n\t\t\tpeer->next_peer = file->first_peer;\n\t\t\tfile->first_peer = peer;\n\t\t\tif (!file->last_peer)\n\t\t\t\tfile->last_peer = peer;\n\t\t} else {\n\t\t\tif (i < peer_count - 1 && peers[i + 1].from_file &&\n\t\t\t    (peers[i].peer->flags & WGPEER_HAS_PRESHARED_KEY) &&\n\t\t\t    !(peers[i + 1].peer->flags & WGPEER_HAS_PRESHARED_KEY) &&\n\t\t\t    !memcmp(peers[i].peer->public_key, peers[i + 1].peer->public_key, WG_KEY_LEN)) {\n\t\t\t\tmemset(peers[i + 1].peer->preshared_key, 0, WG_KEY_LEN);\n\t\t\t\tpeers[i + 1].peer->flags |= WGPEER_HAS_PRESHARED_KEY;\n\t\t\t}\n\t\t\tif (i < peer_count - 1 && peers[i + 1].from_file &&\n\t\t\t    peers[i].peer->persistent_keepalive_interval &&\n\t\t\t    !(peers[i + 1].peer->flags & WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL) &&\n\t\t\t    !memcmp(peers[i].peer->public_key, peers[i + 1].peer->public_key, WG_KEY_LEN)) {\n\t\t\t\tpeers[i + 1].peer->persistent_keepalive_interval = 0;\n\t\t\t\tpeers[i + 1].peer->flags |= WGPEER_HAS_PERSISTENT_KEEPALIVE_INTERVAL;\n\t\t\t}\n\t\t}\n\t}\n\tfree_wgdevice(runtime);\n\tfree(peers);\n\treturn true;\n}\n\nint setconf_main(int argc, const char *argv[])\n{\n\tstruct wgdevice *device = NULL;\n\tstruct config_ctx ctx;\n\tFILE *config_input = NULL;\n\tchar *config_buffer = NULL;\n\tsize_t config_buffer_len = 0;\n\tint ret = 1;\n\n\tif (argc != 3) {\n\t\tfprintf(stderr, \"Usage: %s %s <interface> <configuration filename>\\n\", PROG_NAME, argv[0]);\n\t\treturn 1;\n\t}\n\n\tconfig_input = fopen(argv[2], \"r\");\n\tif (!config_input) {\n\t\tperror(\"fopen\");\n\t\treturn 1;\n\t}\n\tif (!config_read_init(&ctx, !strcmp(argv[0], \"addconf\"))) {\n\t\tfclose(config_input);\n\t\treturn 1;\n\t}\n\twhile (getline(&config_buffer, &config_buffer_len, config_input) >= 0) {\n\t\tif (!config_read_line(&ctx, config_buffer)) {\n\t\t\tfprintf(stderr, \"Configuration parsing error\\n\");\n\t\t\tgoto cleanup;\n\t\t}\n\t}\n\tdevice = config_read_finish(&ctx);\n\tif (!device) {\n\t\tfprintf(stderr, \"Invalid configuration\\n\");\n\t\tgoto cleanup;\n\t}\n\tstrncpy(device->name, argv[1], IFNAMSIZ - 1);\n\tdevice->name[IFNAMSIZ - 1] = '\\0';\n\n\tif (!strcmp(argv[0], \"syncconf\")) {\n\t\tif (!sync_conf(device))\n\t\t\tgoto cleanup;\n\t}\n\n\tif (ipc_set_device(device) != 0) {\n\t\tperror(\"Unable to modify interface\");\n\t\tgoto cleanup;\n\t}\n\n\tret = 0;\n\ncleanup:\n\tif (config_input)\n\t\tfclose(config_input);\n\tfree(config_buffer);\n\tfree_wgdevice(device);\n\treturn ret;\n}\n"
  },
  {
    "path": "src/show.c",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <arpa/inet.h>\n#include <inttypes.h>\n#include <netinet/in.h>\n#include <sys/socket.h>\n#include <net/if.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <errno.h>\n#include <time.h>\n#include <netdb.h>\n\n#include \"containers.h\"\n#include \"ipc.h\"\n#include \"terminal.h\"\n#include \"encoding.h\"\n#include \"subcommands.h\"\n\nstatic int peer_cmp(const void *first, const void *second)\n{\n\ttime_t diff;\n\tconst struct wgpeer *a = *(void *const *)first, *b = *(void *const *)second;\n\n\tif (!a->last_handshake_time.tv_sec && !a->last_handshake_time.tv_nsec && (b->last_handshake_time.tv_sec || b->last_handshake_time.tv_nsec))\n\t\treturn 1;\n\tif (!b->last_handshake_time.tv_sec && !b->last_handshake_time.tv_nsec && (a->last_handshake_time.tv_sec || a->last_handshake_time.tv_nsec))\n\t\treturn -1;\n\tdiff = a->last_handshake_time.tv_sec - b->last_handshake_time.tv_sec;\n\tif (!diff)\n\t\tdiff = a->last_handshake_time.tv_nsec - b->last_handshake_time.tv_nsec;\n\tif (diff < 0)\n\t\treturn 1;\n\tif (diff > 0)\n\t\treturn -1;\n\treturn 0;\n}\n\n/* This, hilariously, is not the right way to sort a linked list... */\nstatic void sort_peers(struct wgdevice *device)\n{\n\tsize_t peer_count = 0, i = 0;\n\tstruct wgpeer *peer, **peers;\n\n\tfor_each_wgpeer(device, peer)\n\t\t++peer_count;\n\tif (!peer_count)\n\t\treturn;\n\tpeers = calloc(peer_count, sizeof(*peers));\n\tif (!peers)\n\t\treturn;\n\tfor_each_wgpeer(device, peer)\n\t\tpeers[i++] = peer;\n\tqsort(peers, peer_count, sizeof(*peers), peer_cmp);\n\tdevice->first_peer = peers[0];\n\tfor (i = 1; i < peer_count; ++i) {\n\t\tpeers[i - 1]->next_peer = peers[i];\n\t}\n\tpeers[peer_count - 1]->next_peer = NULL;\n\tfree(peers);\n}\n\nstatic char *key(const uint8_t key[static WG_KEY_LEN])\n{\n\tstatic char base64[WG_KEY_LEN_BASE64];\n\n\tkey_to_base64(base64, key);\n\treturn base64;\n}\n\nstatic const char *maybe_key(const uint8_t maybe_key[static WG_KEY_LEN], bool have_it)\n{\n\tif (!have_it)\n\t\treturn \"(none)\";\n\treturn key(maybe_key);\n}\n\nstatic const char *masked_key(const uint8_t masked_key[static WG_KEY_LEN])\n{\n\tconst char *var = getenv(\"WG_HIDE_KEYS\");\n\n\tif (var && !strcmp(var, \"never\"))\n\t\treturn key(masked_key);\n\treturn \"(hidden)\";\n}\n\nstatic char *ip(const struct wgallowedip *ip)\n{\n\tstatic char buf[INET6_ADDRSTRLEN + 1];\n\n\tmemset(buf, 0, INET6_ADDRSTRLEN + 1);\n\tif (ip->family == AF_INET)\n\t\tinet_ntop(AF_INET, &ip->ip4, buf, INET6_ADDRSTRLEN);\n\telse if (ip->family == AF_INET6)\n\t\tinet_ntop(AF_INET6, &ip->ip6, buf, INET6_ADDRSTRLEN);\n\treturn buf;\n}\n\nstatic char *endpoint(const struct sockaddr *addr)\n{\n\tchar host[4096 + 1];\n\tchar service[512 + 1];\n\tstatic char buf[sizeof(host) + sizeof(service) + 4];\n\tint ret;\n\tsocklen_t addr_len = 0;\n\n\tmemset(buf, 0, sizeof(buf));\n\tif (addr->sa_family == AF_INET)\n\t\taddr_len = sizeof(struct sockaddr_in);\n\telse if (addr->sa_family == AF_INET6)\n\t\taddr_len = sizeof(struct sockaddr_in6);\n\n\tret = getnameinfo(addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST);\n\tif (ret) {\n\t\tstrncpy(buf, gai_strerror(ret), sizeof(buf) - 1);\n\t\tbuf[sizeof(buf) - 1] = '\\0';\n\t} else\n\t\tsnprintf(buf, sizeof(buf), (addr->sa_family == AF_INET6 && strchr(host, ':')) ? \"[%s]:%s\" : \"%s:%s\", host, service);\n\treturn buf;\n}\n\nstatic size_t pretty_time(char *buf, const size_t len, unsigned long long left)\n{\n\tsize_t offset = 0;\n\tunsigned long long years, days, hours, minutes, seconds;\n\n\tyears = left / (365 * 24 * 60 * 60);\n\tleft = left % (365 * 24 * 60 * 60);\n\tdays = left / (24 * 60 * 60);\n\tleft = left % (24 * 60 * 60);\n\thours = left / (60 * 60);\n\tleft = left % (60 * 60);\n\tminutes = left / 60;\n\tseconds = left % 60;\n\n\tif (years)\n\t\toffset += snprintf(buf + offset, len - offset, \"%s%llu \" TERMINAL_FG_CYAN \"year%s\" TERMINAL_RESET, offset ? \", \" : \"\", years, years == 1 ? \"\" : \"s\");\n\tif (days)\n\t\toffset += snprintf(buf + offset, len - offset, \"%s%llu \" TERMINAL_FG_CYAN  \"day%s\" TERMINAL_RESET, offset ? \", \" : \"\", days, days == 1 ? \"\" : \"s\");\n\tif (hours)\n\t\toffset += snprintf(buf + offset, len - offset, \"%s%llu \" TERMINAL_FG_CYAN  \"hour%s\" TERMINAL_RESET, offset ? \", \" : \"\", hours, hours == 1 ? \"\" : \"s\");\n\tif (minutes)\n\t\toffset += snprintf(buf + offset, len - offset, \"%s%llu \" TERMINAL_FG_CYAN \"minute%s\" TERMINAL_RESET, offset ? \", \" : \"\", minutes, minutes == 1 ? \"\" : \"s\");\n\tif (seconds)\n\t\toffset += snprintf(buf + offset, len - offset, \"%s%llu \" TERMINAL_FG_CYAN  \"second%s\" TERMINAL_RESET, offset ? \", \" : \"\", seconds, seconds == 1 ? \"\" : \"s\");\n\n\treturn offset;\n}\n\nstatic char *ago(const struct timespec64 *t)\n{\n\tstatic char buf[1024];\n\tsize_t offset;\n\ttime_t now = time(NULL);\n\n\tif (now == t->tv_sec)\n\t\tstrncpy(buf, \"Now\", sizeof(buf) - 1);\n\telse if (now < t->tv_sec)\n\t\tstrncpy(buf, \"(\" TERMINAL_FG_RED \"System clock wound backward; connection problems may ensue.\" TERMINAL_RESET \")\", sizeof(buf) - 1);\n\telse {\n\t\toffset = pretty_time(buf, sizeof(buf), now - t->tv_sec);\n\t\tstrncpy(buf + offset, \" ago\", sizeof(buf) - offset - 1);\n\t}\n\tbuf[sizeof(buf) - 1] = '\\0';\n\n\treturn buf;\n}\n\nstatic char *every(uint16_t seconds)\n{\n\tstatic char buf[1024] = \"every \";\n\n\tpretty_time(buf + strlen(\"every \"), sizeof(buf) - strlen(\"every \") - 1, seconds);\n\treturn buf;\n}\n\nstatic char *bytes(uint64_t b)\n{\n\tstatic char buf[1024];\n\n\tif (b < 1024ULL)\n\t\tsnprintf(buf, sizeof(buf), \"%u \" TERMINAL_FG_CYAN \"B\" TERMINAL_RESET, (unsigned int)b);\n\telse if (b < 1024ULL * 1024ULL)\n\t\tsnprintf(buf, sizeof(buf), \"%.2f \" TERMINAL_FG_CYAN \"KiB\" TERMINAL_RESET, (double)b / 1024);\n\telse if (b < 1024ULL * 1024ULL * 1024ULL)\n\t\tsnprintf(buf, sizeof(buf), \"%.2f \" TERMINAL_FG_CYAN \"MiB\" TERMINAL_RESET, (double)b / (1024 * 1024));\n\telse if (b < 1024ULL * 1024ULL * 1024ULL * 1024ULL)\n\t\tsnprintf(buf, sizeof(buf), \"%.2f \" TERMINAL_FG_CYAN \"GiB\" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024));\n\telse\n\t\tsnprintf(buf, sizeof(buf), \"%.2f \" TERMINAL_FG_CYAN \"TiB\" TERMINAL_RESET, (double)b / (1024 * 1024 * 1024) / 1024);\n\n\treturn buf;\n}\n\nstatic const char *COMMAND_NAME;\nstatic void show_usage(void)\n{\n\tfprintf(stderr, \"Usage: %s %s { <interface> | all | interfaces } [public-key | private-key | listen-port | fwmark | peers | preshared-keys | endpoints | allowed-ips | latest-handshakes | transfer | persistent-keepalive | dump]\\n\", PROG_NAME, COMMAND_NAME);\n}\n\nstatic void pretty_print(struct wgdevice *device)\n{\n\tstruct wgpeer *peer;\n\tstruct wgallowedip *allowedip;\n\n\tterminal_printf(TERMINAL_RESET);\n\tterminal_printf(TERMINAL_FG_GREEN TERMINAL_BOLD \"interface\" TERMINAL_RESET \": \" TERMINAL_FG_GREEN \"%s\" TERMINAL_RESET \"\\n\", device->name);\n\tif (device->flags & WGDEVICE_HAS_PUBLIC_KEY)\n\t\tterminal_printf(\"  \" TERMINAL_BOLD \"public key\" TERMINAL_RESET \": %s\\n\", key(device->public_key));\n\tif (device->flags & WGDEVICE_HAS_PRIVATE_KEY)\n\t\tterminal_printf(\"  \" TERMINAL_BOLD \"private key\" TERMINAL_RESET \": %s\\n\", masked_key(device->private_key));\n\tif (device->listen_port)\n\t\tterminal_printf(\"  \" TERMINAL_BOLD \"listening port\" TERMINAL_RESET \": %u\\n\", device->listen_port);\n\tif (device->fwmark)\n\t\tterminal_printf(\"  \" TERMINAL_BOLD \"fwmark\" TERMINAL_RESET \": 0x%x\\n\", device->fwmark);\n\tif (device->first_peer) {\n\t\tsort_peers(device);\n\t\tterminal_printf(\"\\n\");\n\t}\n\tfor_each_wgpeer(device, peer) {\n\t\tterminal_printf(TERMINAL_FG_YELLOW TERMINAL_BOLD \"peer\" TERMINAL_RESET \": \" TERMINAL_FG_YELLOW \"%s\" TERMINAL_RESET \"\\n\", key(peer->public_key));\n\t\tif (peer->flags & WGPEER_HAS_PRESHARED_KEY)\n\t\t\tterminal_printf(\"  \" TERMINAL_BOLD \"preshared key\" TERMINAL_RESET \": %s\\n\", masked_key(peer->preshared_key));\n\t\tif (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)\n\t\t\tterminal_printf(\"  \" TERMINAL_BOLD \"endpoint\" TERMINAL_RESET \": %s\\n\", endpoint(&peer->endpoint.addr));\n\t\tterminal_printf(\"  \" TERMINAL_BOLD \"allowed ips\" TERMINAL_RESET \": \");\n\t\tif (peer->first_allowedip) {\n\t\t\tfor_each_wgallowedip(peer, allowedip)\n\t\t\t\tterminal_printf(\"%s\" TERMINAL_FG_CYAN \"/\" TERMINAL_RESET \"%u%s\", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? \", \" : \"\\n\");\n\t\t} else\n\t\t\tterminal_printf(\"(none)\\n\");\n\t\tif (peer->last_handshake_time.tv_sec)\n\t\t\tterminal_printf(\"  \" TERMINAL_BOLD \"latest handshake\" TERMINAL_RESET \": %s\\n\", ago(&peer->last_handshake_time));\n\t\tif (peer->rx_bytes || peer->tx_bytes) {\n\t\t\tterminal_printf(\"  \" TERMINAL_BOLD \"transfer\" TERMINAL_RESET \": \");\n\t\t\tterminal_printf(\"%s received, \", bytes(peer->rx_bytes));\n\t\t\tterminal_printf(\"%s sent\\n\", bytes(peer->tx_bytes));\n\t\t}\n\t\tif (peer->persistent_keepalive_interval)\n\t\t\tterminal_printf(\"  \" TERMINAL_BOLD \"persistent keepalive\" TERMINAL_RESET \": %s\\n\", every(peer->persistent_keepalive_interval));\n\t\tif (peer->next_peer)\n\t\t\tterminal_printf(\"\\n\");\n\t}\n}\n\nstatic void dump_print(struct wgdevice *device, bool with_interface)\n{\n\tstruct wgpeer *peer;\n\tstruct wgallowedip *allowedip;\n\n\tif (with_interface)\n\t\tprintf(\"%s\\t\", device->name);\n\tprintf(\"%s\\t\", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY));\n\tprintf(\"%s\\t\", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY));\n\tprintf(\"%u\\t\", device->listen_port);\n\tif (device->fwmark)\n\t\tprintf(\"0x%x\\n\", device->fwmark);\n\telse\n\t\tprintf(\"off\\n\");\n\tfor_each_wgpeer(device, peer) {\n\t\tif (with_interface)\n\t\t\tprintf(\"%s\\t\", device->name);\n\t\tprintf(\"%s\\t\", key(peer->public_key));\n\t\tprintf(\"%s\\t\", maybe_key(peer->preshared_key, peer->flags & WGPEER_HAS_PRESHARED_KEY));\n\t\tif (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)\n\t\t\tprintf(\"%s\\t\", endpoint(&peer->endpoint.addr));\n\t\telse\n\t\t\tprintf(\"(none)\\t\");\n\t\tif (peer->first_allowedip) {\n\t\t\tfor_each_wgallowedip(peer, allowedip)\n\t\t\t\tprintf(\"%s/%u%c\", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ',' : '\\t');\n\t\t} else\n\t\t\tprintf(\"(none)\\t\");\n\t\tprintf(\"%llu\\t\", (unsigned long long)peer->last_handshake_time.tv_sec);\n\t\tprintf(\"%\" PRIu64 \"\\t%\" PRIu64 \"\\t\", (uint64_t)peer->rx_bytes, (uint64_t)peer->tx_bytes);\n\t\tif (peer->persistent_keepalive_interval)\n\t\t\tprintf(\"%u\\n\", peer->persistent_keepalive_interval);\n\t\telse\n\t\t\tprintf(\"off\\n\");\n\t}\n}\n\nstatic bool ugly_print(struct wgdevice *device, const char *param, bool with_interface)\n{\n\tstruct wgpeer *peer;\n\tstruct wgallowedip *allowedip;\n\n\tif (!strcmp(param, \"public-key\")) {\n\t\tif (with_interface)\n\t\t\tprintf(\"%s\\t\", device->name);\n\t\tprintf(\"%s\\n\", maybe_key(device->public_key, device->flags & WGDEVICE_HAS_PUBLIC_KEY));\n\t} else if (!strcmp(param, \"private-key\")) {\n\t\tif (with_interface)\n\t\t\tprintf(\"%s\\t\", device->name);\n\t\tprintf(\"%s\\n\", maybe_key(device->private_key, device->flags & WGDEVICE_HAS_PRIVATE_KEY));\n\t} else if (!strcmp(param, \"listen-port\")) {\n\t\tif (with_interface)\n\t\t\tprintf(\"%s\\t\", device->name);\n\t\tprintf(\"%u\\n\", device->listen_port);\n\t} else if (!strcmp(param, \"fwmark\")) {\n\t\tif (with_interface)\n\t\t\tprintf(\"%s\\t\", device->name);\n\t\tif (device->fwmark)\n\t\t\tprintf(\"0x%x\\n\", device->fwmark);\n\t\telse\n\t\t\tprintf(\"off\\n\");\n\t} else if (!strcmp(param, \"endpoints\")) {\n\t\tfor_each_wgpeer(device, peer) {\n\t\t\tif (with_interface)\n\t\t\t\tprintf(\"%s\\t\", device->name);\n\t\t\tprintf(\"%s\\t\", key(peer->public_key));\n\t\t\tif (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6)\n\t\t\t\tprintf(\"%s\\n\", endpoint(&peer->endpoint.addr));\n\t\t\telse\n\t\t\t\tprintf(\"(none)\\n\");\n\t\t}\n\t} else if (!strcmp(param, \"allowed-ips\")) {\n\t\tfor_each_wgpeer(device, peer) {\n\t\t\tif (with_interface)\n\t\t\t\tprintf(\"%s\\t\", device->name);\n\t\t\tprintf(\"%s\\t\", key(peer->public_key));\n\t\t\tif (peer->first_allowedip) {\n\t\t\t\tfor_each_wgallowedip(peer, allowedip)\n\t\t\t\t\tprintf(\"%s/%u%c\", ip(allowedip), allowedip->cidr, allowedip->next_allowedip ? ' ' : '\\n');\n\t\t\t} else\n\t\t\t\tprintf(\"(none)\\n\");\n\t\t}\n\t} else if (!strcmp(param, \"latest-handshakes\")) {\n\t\tfor_each_wgpeer(device, peer) {\n\t\t\tif (with_interface)\n\t\t\t\tprintf(\"%s\\t\", device->name);\n\t\t\tprintf(\"%s\\t%llu\\n\", key(peer->public_key), (unsigned long long)peer->last_handshake_time.tv_sec);\n\t\t}\n\t} else if (!strcmp(param, \"transfer\")) {\n\t\tfor_each_wgpeer(device, peer) {\n\t\t\tif (with_interface)\n\t\t\t\tprintf(\"%s\\t\", device->name);\n\t\t\tprintf(\"%s\\t%\" PRIu64 \"\\t%\" PRIu64 \"\\n\", key(peer->public_key), (uint64_t)peer->rx_bytes, (uint64_t)peer->tx_bytes);\n\t\t}\n\t} else if (!strcmp(param, \"persistent-keepalive\")) {\n\t\tfor_each_wgpeer(device, peer) {\n\t\t\tif (with_interface)\n\t\t\t\tprintf(\"%s\\t\", device->name);\n\t\t\tif (peer->persistent_keepalive_interval)\n\t\t\t\tprintf(\"%s\\t%u\\n\", key(peer->public_key), peer->persistent_keepalive_interval);\n\t\t\telse\n\t\t\t\tprintf(\"%s\\toff\\n\", key(peer->public_key));\n\t\t}\n\t} else if (!strcmp(param, \"preshared-keys\")) {\n\t\tfor_each_wgpeer(device, peer) {\n\t\t\tif (with_interface)\n\t\t\t\tprintf(\"%s\\t\", device->name);\n\t\t\tprintf(\"%s\\t\", key(peer->public_key));\n\t\t\tprintf(\"%s\\n\", maybe_key(peer->preshared_key, peer->flags & WGPEER_HAS_PRESHARED_KEY));\n\t\t}\n\t} else if (!strcmp(param, \"peers\")) {\n\t\tfor_each_wgpeer(device, peer) {\n\t\t\tif (with_interface)\n\t\t\t\tprintf(\"%s\\t\", device->name);\n\t\t\tprintf(\"%s\\n\", key(peer->public_key));\n\t\t}\n\t} else if (!strcmp(param, \"dump\"))\n\t\tdump_print(device, with_interface);\n\telse {\n\t\tfprintf(stderr, \"Invalid parameter: `%s'\\n\", param);\n\t\tshow_usage();\n\t\treturn false;\n\t}\n\treturn true;\n}\n\nint show_main(int argc, const char *argv[])\n{\n\tint ret = 0;\n\n\tCOMMAND_NAME = argv[0];\n\n\tif (argc > 3) {\n\t\tshow_usage();\n\t\treturn 1;\n\t}\n\n\tif (argc == 1 || !strcmp(argv[1], \"all\")) {\n\t\tchar *interfaces = ipc_list_devices(), *interface;\n\n\t\tif (!interfaces) {\n\t\t\tperror(\"Unable to list interfaces\");\n\t\t\treturn 1;\n\t\t}\n\t\tret = !!*interfaces;\n\t\tinterface = interfaces;\n\t\tfor (size_t len = 0; (len = strlen(interface)); interface += len + 1) {\n\t\t\tstruct wgdevice *device = NULL;\n\n\t\t\tif (ipc_get_device(&device, interface) < 0) {\n\t\t\t\tfprintf(stderr, \"Unable to access interface %s: %s\\n\", interface, strerror(errno));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (argc == 3) {\n\t\t\t\tif (!ugly_print(device, argv[2], true)) {\n\t\t\t\t\tret = 1;\n\t\t\t\t\tfree_wgdevice(device);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tpretty_print(device);\n\t\t\t\tif (strlen(interface + len + 1))\n\t\t\t\t\tprintf(\"\\n\");\n\t\t\t}\n\t\t\tfree_wgdevice(device);\n\t\t\tret = 0;\n\t\t}\n\t\tfree(interfaces);\n\t} else if (!strcmp(argv[1], \"interfaces\")) {\n\t\tchar *interfaces, *interface;\n\n\t\tif (argc > 2) {\n\t\t\tshow_usage();\n\t\t\treturn 1;\n\t\t}\n\t\tinterfaces = ipc_list_devices();\n\t\tif (!interfaces) {\n\t\t\tperror(\"Unable to list interfaces\");\n\t\t\treturn 1;\n\t\t}\n\t\tinterface = interfaces;\n\t\tfor (size_t len = 0; (len = strlen(interface)); interface += len + 1)\n\t\t\tprintf(\"%s%c\", interface, strlen(interface + len + 1) ? ' ' : '\\n');\n\t\tfree(interfaces);\n\t} else if (argc == 2 && (!strcmp(argv[1], \"-h\") || !strcmp(argv[1], \"--help\") || !strcmp(argv[1], \"help\")))\n\t\tshow_usage();\n\telse {\n\t\tstruct wgdevice *device = NULL;\n\n\t\tif (ipc_get_device(&device, argv[1]) < 0) {\n\t\t\tperror(\"Unable to access interface\");\n\t\t\treturn 1;\n\t\t}\n\t\tif (argc == 3) {\n\t\t\tif (!ugly_print(device, argv[2], false))\n\t\t\t\tret = 1;\n\t\t} else\n\t\t\tpretty_print(device);\n\t\tfree_wgdevice(device);\n\t}\n\treturn ret;\n}\n"
  },
  {
    "path": "src/showconf.c",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <arpa/inet.h>\n#include <netinet/in.h>\n#include <sys/socket.h>\n#include <net/if.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <string.h>\n#include <stdlib.h>\n#include <netdb.h>\n\n#include \"containers.h\"\n#include \"encoding.h\"\n#include \"ipc.h\"\n#include \"subcommands.h\"\n\nint showconf_main(int argc, const char *argv[])\n{\n\tchar base64[WG_KEY_LEN_BASE64];\n\tchar ip[INET6_ADDRSTRLEN];\n\tstruct wgdevice *device = NULL;\n\tstruct wgpeer *peer;\n\tstruct wgallowedip *allowedip;\n\tint ret = 1;\n\n\tif (argc != 2) {\n\t\tfprintf(stderr, \"Usage: %s %s <interface>\\n\", PROG_NAME, argv[0]);\n\t\treturn 1;\n\t}\n\n\tif (ipc_get_device(&device, argv[1])) {\n\t\tperror(\"Unable to access interface\");\n\t\tgoto cleanup;\n\t}\n\n\tprintf(\"[Interface]\\n\");\n\tif (device->listen_port)\n\t\tprintf(\"ListenPort = %u\\n\", device->listen_port);\n\tif (device->fwmark)\n\t\tprintf(\"FwMark = 0x%x\\n\", device->fwmark);\n\tif (device->flags & WGDEVICE_HAS_PRIVATE_KEY) {\n\t\tkey_to_base64(base64, device->private_key);\n\t\tprintf(\"PrivateKey = %s\\n\", base64);\n\t}\n\tprintf(\"\\n\");\n\tfor_each_wgpeer(device, peer) {\n\t\tkey_to_base64(base64, peer->public_key);\n\t\tprintf(\"[Peer]\\nPublicKey = %s\\n\", base64);\n\t\tif (peer->flags & WGPEER_HAS_PRESHARED_KEY) {\n\t\t\tkey_to_base64(base64, peer->preshared_key);\n\t\t\tprintf(\"PresharedKey = %s\\n\", base64);\n\t\t}\n\t\tif (peer->first_allowedip)\n\t\t\tprintf(\"AllowedIPs = \");\n\t\tfor_each_wgallowedip(peer, allowedip) {\n\t\t\tif (allowedip->family == AF_INET) {\n\t\t\t\tif (!inet_ntop(AF_INET, &allowedip->ip4, ip, INET6_ADDRSTRLEN))\n\t\t\t\t\tcontinue;\n\t\t\t} else if (allowedip->family == AF_INET6) {\n\t\t\t\tif (!inet_ntop(AF_INET6, &allowedip->ip6, ip, INET6_ADDRSTRLEN))\n\t\t\t\t\tcontinue;\n\t\t\t} else\n\t\t\t\tcontinue;\n\t\t\tprintf(\"%s/%d\", ip, allowedip->cidr);\n\t\t\tif (allowedip->next_allowedip)\n\t\t\t\tprintf(\", \");\n\t\t}\n\t\tif (peer->first_allowedip)\n\t\t\tprintf(\"\\n\");\n\n\t\tif (peer->endpoint.addr.sa_family == AF_INET || peer->endpoint.addr.sa_family == AF_INET6) {\n\t\t\tchar host[4096 + 1];\n\t\t\tchar service[512 + 1];\n\t\t\tsocklen_t addr_len = 0;\n\n\t\t\tif (peer->endpoint.addr.sa_family == AF_INET)\n\t\t\t\taddr_len = sizeof(struct sockaddr_in);\n\t\t\telse if (peer->endpoint.addr.sa_family == AF_INET6)\n\t\t\t\taddr_len = sizeof(struct sockaddr_in6);\n\t\t\tif (!getnameinfo(&peer->endpoint.addr, addr_len, host, sizeof(host), service, sizeof(service), NI_DGRAM | NI_NUMERICSERV | NI_NUMERICHOST)) {\n\t\t\t\tif (peer->endpoint.addr.sa_family == AF_INET6 && strchr(host, ':'))\n\t\t\t\t\tprintf(\"Endpoint = [%s]:%s\\n\", host, service);\n\t\t\t\telse\n\t\t\t\t\tprintf(\"Endpoint = %s:%s\\n\", host, service);\n\t\t\t}\n\t\t}\n\n\t\tif (peer->persistent_keepalive_interval)\n\t\t\tprintf(\"PersistentKeepalive = %u\\n\", peer->persistent_keepalive_interval);\n\n\t\tif (peer->next_peer)\n\t\t\tprintf(\"\\n\");\n\t}\n\tret = 0;\n\ncleanup:\n\tfree_wgdevice(device);\n\treturn ret;\n}\n"
  },
  {
    "path": "src/subcommands.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 OR MIT */\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#ifndef SUBCOMMANDS_H\n#define SUBCOMMANDS_H\n\nextern const char *PROG_NAME;\nint show_main(int argc, const char *argv[]);\nint showconf_main(int argc, const char *argv[]);\nint set_main(int argc, const char *argv[]);\nint setconf_main(int argc, const char *argv[]);\nint genkey_main(int argc, const char *argv[]);\nint pubkey_main(int argc, const char *argv[]);\n\n#endif\n"
  },
  {
    "path": "src/systemd/wg-quick.target",
    "content": "[Unit]\nDescription=WireGuard Tunnels via wg-quick(8)\n"
  },
  {
    "path": "src/systemd/wg-quick@.service",
    "content": "[Unit]\nDescription=WireGuard via wg-quick(8) for %I\nBefore=wg-quick.target\nAfter=network-online.target nss-lookup.target\nWants=network-online.target nss-lookup.target\nPartOf=wg-quick.target\nDocumentation=man:wg-quick(8)\nDocumentation=man:wg(8)\nDocumentation=https://www.wireguard.com/\nDocumentation=https://www.wireguard.com/quickstart/\nDocumentation=https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8\nDocumentation=https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8\n\n[Service]\nType=oneshot\nRemainAfterExit=yes\nExecStart=/usr/bin/wg-quick up %i\nExecStop=/usr/bin/wg-quick down %i\nExecReload=/bin/bash -c 'exec /usr/bin/wg syncconf %i <(exec /usr/bin/wg-quick strip %i)'\nEnvironment=WG_ENDPOINT_RESOLUTION_RETRIES=infinity\n\n[Install]\nWantedBy=multi-user.target wg-quick.target\n"
  },
  {
    "path": "src/terminal.c",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stdarg.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n#include <unistd.h>\n#include \"ctype.h\"\n#include \"terminal.h\"\n\nstatic bool color_mode(void)\n{\n\tstatic int mode = -1;\n\tconst char *var;\n\n\tif (mode != -1)\n\t\treturn mode;\n\tvar = getenv(\"WG_COLOR_MODE\");\n\tif (var && !strcmp(var, \"always\"))\n\t\tmode = true;\n\telse if (var && !strcmp(var, \"never\"))\n\t\tmode = false;\n\telse\n\t\tmode = isatty(fileno(stdout));\n\treturn mode;\n}\n\nstatic void filter_ansi(const char *fmt, va_list args)\n{\n\tchar *str = NULL;\n\tsize_t len, i, j;\n\n\tif (color_mode()) {\n\t\tvfprintf(stdout, fmt, args);\n\t\treturn;\n\t}\n\n\tlen = vasprintf(&str, fmt, args);\n\n\tif (len >= 2) {\n\t\tfor (i = 0; i < len - 2; ++i) {\n\t\t\tif (str[i] == '\\x1b' && str[i + 1] == '[') {\n\t\t\t\tstr[i] = str[i + 1] = '\\0';\n\t\t\t\tfor (j = i + 2; j < len; ++j) {\n\t\t\t\t\tif (char_is_alpha(str[j]))\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tstr[j] = '\\0';\n\t\t\t\t}\n\t\t\t\tstr[j] = '\\0';\n\t\t\t}\n\t\t}\n\t}\n\tfor (i = 0; i < len; i = j) {\n\t\tfputs(&str[i], stdout);\n\t\tfor (j = i + strlen(&str[i]); j < len; ++j) {\n\t\t\tif (str[j] != '\\0')\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tfree(str);\n}\n\nvoid terminal_printf(const char *fmt, ...)\n{\n\tva_list args;\n\n\tva_start(args, fmt);\n\tfilter_ansi(fmt, args);\n\tva_end(args);\n}\n"
  },
  {
    "path": "src/terminal.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 OR MIT */\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#ifndef TERMINAL_H\n#define TERMINAL_H\n\n#define TERMINAL_FG_BLACK\t\"\\x1b[30m\"\n#define TERMINAL_FG_RED\t\t\"\\x1b[31m\"\n#define TERMINAL_FG_GREEN\t\"\\x1b[32m\"\n#define TERMINAL_FG_YELLOW\t\"\\x1b[33m\"\n#define TERMINAL_FG_BLUE\t\"\\x1b[34m\"\n#define TERMINAL_FG_MAGENTA\t\"\\x1b[35m\"\n#define TERMINAL_FG_CYAN\t\"\\x1b[36m\"\n#define TERMINAL_FG_WHITE\t\"\\x1b[37m\"\n#define TERMINAL_FG_DEFAULT\t\"\\x1b[39m\"\n\n#define TERMINAL_BG_BLACK\t\"\\x1b[40m\"\n#define TERMINAL_BG_RED\t\t\"\\x1b[41m\"\n#define TERMINAL_BG_GREEN\t\"\\x1b[42m\"\n#define TERMINAL_BG_YELLOW\t\"\\x1b[43m\"\n#define TERMINAL_BG_BLUE\t\"\\x1b[44m\"\n#define TERMINAL_BG_MAGENTA\t\"\\x1b[45m\"\n#define TERMINAL_BG_CYAN\t\"\\x1b[46m\"\n#define TERMINAL_BG_WHITE\t\"\\x1b[47m\"\n#define TERMINAL_BG_DEFAULT\t\"\\x1b[49m\"\n\n#define TERMINAL_BOLD\t\t\"\\x1b[1m\"\n#define TERMINAL_NO_BOLD\t\"\\x1b[22m\"\n#define TERMINAL_UNDERLINE\t\"\\x1b[4m\"\n#define TERMINAL_NO_UNDERLINE\t\"\\x1b[24m\"\n\n#define TERMINAL_RESET\t\t\"\\x1b[0m\"\n\n#define TERMINAL_SAVE_CURSOR\t\"\\x1b[s\"\n#define TERMINAL_RESTORE_CURSOR\t\"\\x1b[u\"\n#define TERMINAL_UP_CURSOR(l)\t\"\\x1b[\" #l \"A\"\n#define TERMINAL_DOWN_CURSOR(l)\t\"\\x1b[\" #l \"B\"\n#define TERMINAL_RIGHT_CURSOR(c) \"\\x1b[\" #c \"C\"\n#define TERMINAL_LEFT_CURSOR(c)\t\"\\x1b[\" #c \"D\"\n#define TERMINAL_CLEAR_DOWN\t\"\\x1b[0J\"\n#define TERMINAL_CLEAR_UP\t\"\\x1b[1J\"\n#define TERMINAL_CLEAR_RIGHT\t\"\\x1b[0K\"\n#define TERMINAL_CLEAR_LEFT\t\"\\x1b[1K\"\n#define TERMINAL_CLEAR_LINE\t\"\\x1b[2K\"\n#define TERMINAL_CLEAR_ALL\t\"\\x1b[2J\"\n\nvoid terminal_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));\n\n#endif\n"
  },
  {
    "path": "src/uapi/freebsd/dev/wg/if_wg.h",
    "content": "#ifndef __IF_WG_H__\n#define __IF_WG_H__\n\n#include <net/if.h>\n#include <netinet/in.h>\n\nstruct wg_data_io {\n\tchar wgd_name[IFNAMSIZ];\n\tvoid *wgd_data;\n\tsize_t wgd_size;\n};\n\n#define SIOCSWG _IOWR('i', 210, struct wg_data_io)\n#define SIOCGWG _IOWR('i', 211, struct wg_data_io)\n\n#endif\n"
  },
  {
    "path": "src/uapi/linux/linux/wireguard.h",
    "content": "/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR MIT */\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n *\n * Documentation\n * =============\n *\n * The below enums and macros are for interfacing with WireGuard, using generic\n * netlink, with family WG_GENL_NAME and version WG_GENL_VERSION. It defines two\n * methods: get and set. Note that while they share many common attributes,\n * these two functions actually accept a slightly different set of inputs and\n * outputs.\n *\n * WG_CMD_GET_DEVICE\n * -----------------\n *\n * May only be called via NLM_F_REQUEST | NLM_F_DUMP. The command should contain\n * one but not both of:\n *\n *    WGDEVICE_A_IFINDEX: NLA_U32\n *    WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1\n *\n * The kernel will then return several messages (NLM_F_MULTI) containing the\n * following tree of nested items:\n *\n *    WGDEVICE_A_IFINDEX: NLA_U32\n *    WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1\n *    WGDEVICE_A_PRIVATE_KEY: NLA_EXACT_LEN, len WG_KEY_LEN\n *    WGDEVICE_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN\n *    WGDEVICE_A_LISTEN_PORT: NLA_U16\n *    WGDEVICE_A_FWMARK: NLA_U32\n *    WGDEVICE_A_PEERS: NLA_NESTED\n *        0: NLA_NESTED\n *            WGPEER_A_PUBLIC_KEY: NLA_EXACT_LEN, len WG_KEY_LEN\n *            WGPEER_A_PRESHARED_KEY: NLA_EXACT_LEN, len WG_KEY_LEN\n *            WGPEER_A_ENDPOINT: NLA_MIN_LEN(struct sockaddr), struct sockaddr_in or struct sockaddr_in6\n *            WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16\n *            WGPEER_A_LAST_HANDSHAKE_TIME: NLA_EXACT_LEN, struct __kernel_timespec\n *            WGPEER_A_RX_BYTES: NLA_U64\n *            WGPEER_A_TX_BYTES: NLA_U64\n *            WGPEER_A_ALLOWEDIPS: NLA_NESTED\n *                0: NLA_NESTED\n *                    WGALLOWEDIP_A_FAMILY: NLA_U16\n *                    WGALLOWEDIP_A_IPADDR: NLA_MIN_LEN(struct in_addr), struct in_addr or struct in6_addr\n *                    WGALLOWEDIP_A_CIDR_MASK: NLA_U8\n *                0: NLA_NESTED\n *                    ...\n *                0: NLA_NESTED\n *                    ...\n *                ...\n *            WGPEER_A_PROTOCOL_VERSION: NLA_U32\n *        0: NLA_NESTED\n *            ...\n *        ...\n *\n * It is possible that all of the allowed IPs of a single peer will not\n * fit within a single netlink message. In that case, the same peer will\n * be written in the following message, except it will only contain\n * WGPEER_A_PUBLIC_KEY and WGPEER_A_ALLOWEDIPS. This may occur several\n * times in a row for the same peer. It is then up to the receiver to\n * coalesce adjacent peers. Likewise, it is possible that all peers will\n * not fit within a single message. So, subsequent peers will be sent\n * in following messages, except those will only contain WGDEVICE_A_IFNAME\n * and WGDEVICE_A_PEERS. It is then up to the receiver to coalesce these\n * messages to form the complete list of peers.\n *\n * Since this is an NLA_F_DUMP command, the final message will always be\n * NLMSG_DONE, even if an error occurs. However, this NLMSG_DONE message\n * contains an integer error code. It is either zero or a negative error\n * code corresponding to the errno.\n *\n * WG_CMD_SET_DEVICE\n * -----------------\n *\n * May only be called via NLM_F_REQUEST. The command should contain the\n * following tree of nested items, containing one but not both of\n * WGDEVICE_A_IFINDEX and WGDEVICE_A_IFNAME:\n *\n *    WGDEVICE_A_IFINDEX: NLA_U32\n *    WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMSIZ - 1\n *    WGDEVICE_A_FLAGS: NLA_U32, 0 or WGDEVICE_F_REPLACE_PEERS if all current\n *                      peers should be removed prior to adding the list below.\n *    WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove\n *    WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly\n *    WGDEVICE_A_FWMARK: NLA_U32, 0 to disable\n *    WGDEVICE_A_PEERS: NLA_NESTED\n *        0: NLA_NESTED\n *            WGPEER_A_PUBLIC_KEY: len WG_KEY_LEN\n *            WGPEER_A_FLAGS: NLA_U32, 0 and/or WGPEER_F_REMOVE_ME if the\n *                            specified peer should not exist at the end of the\n *                            operation, rather than added/updated and/or\n *                            WGPEER_F_REPLACE_ALLOWEDIPS if all current allowed\n *                            IPs of this peer should be removed prior to adding\n *                            the list below and/or WGPEER_F_UPDATE_ONLY if the\n *                            peer should only be set if it already exists.\n *            WGPEER_A_PRESHARED_KEY: len WG_KEY_LEN, all zeros to remove\n *            WGPEER_A_ENDPOINT: struct sockaddr_in or struct sockaddr_in6\n *            WGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL: NLA_U16, 0 to disable\n *            WGPEER_A_ALLOWEDIPS: NLA_NESTED\n *                0: NLA_NESTED\n *                    WGALLOWEDIP_A_FAMILY: NLA_U16\n *                    WGALLOWEDIP_A_IPADDR: struct in_addr or struct in6_addr\n *                    WGALLOWEDIP_A_CIDR_MASK: NLA_U8\n *                    WGALLOWEDIP_A_FLAGS: NLA_U32, WGALLOWEDIP_F_REMOVE_ME if\n *                                         the specified IP should be removed;\n *                                         otherwise, this IP will be added if\n *                                         it is not already present.\n *                0: NLA_NESTED\n *                    ...\n *                0: NLA_NESTED\n *                    ...\n *                ...\n *            WGPEER_A_PROTOCOL_VERSION: NLA_U32, should not be set or used at\n *                                       all by most users of this API, as the\n *                                       most recent protocol will be used when\n *                                       this is unset. Otherwise, must be set\n *                                       to 1.\n *        0: NLA_NESTED\n *            ...\n *        ...\n *\n * It is possible that the amount of configuration data exceeds that of\n * the maximum message length accepted by the kernel. In that case, several\n * messages should be sent one after another, with each successive one\n * filling in information not contained in the prior. Note that if\n * WGDEVICE_F_REPLACE_PEERS is specified in the first message, it probably\n * should not be specified in fragments that come after, so that the list\n * of peers is only cleared the first time but appended after. Likewise for\n * peers, if WGPEER_F_REPLACE_ALLOWEDIPS is specified in the first message\n * of a peer, it likely should not be specified in subsequent fragments.\n *\n * If an error occurs, NLMSG_ERROR will reply containing an errno.\n */\n\n#ifndef _WG_UAPI_WIREGUARD_H\n#define _WG_UAPI_WIREGUARD_H\n\n#define WG_GENL_NAME \"wireguard\"\n#define WG_GENL_VERSION 1\n\n#define WG_KEY_LEN 32\n\nenum wg_cmd {\n\tWG_CMD_GET_DEVICE,\n\tWG_CMD_SET_DEVICE,\n\t__WG_CMD_MAX\n};\n#define WG_CMD_MAX (__WG_CMD_MAX - 1)\n\nenum wgdevice_flag {\n\tWGDEVICE_F_REPLACE_PEERS = 1U << 0,\n\t__WGDEVICE_F_ALL = WGDEVICE_F_REPLACE_PEERS\n};\nenum wgdevice_attribute {\n\tWGDEVICE_A_UNSPEC,\n\tWGDEVICE_A_IFINDEX,\n\tWGDEVICE_A_IFNAME,\n\tWGDEVICE_A_PRIVATE_KEY,\n\tWGDEVICE_A_PUBLIC_KEY,\n\tWGDEVICE_A_FLAGS,\n\tWGDEVICE_A_LISTEN_PORT,\n\tWGDEVICE_A_FWMARK,\n\tWGDEVICE_A_PEERS,\n\t__WGDEVICE_A_LAST\n};\n#define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1)\n\nenum wgpeer_flag {\n\tWGPEER_F_REMOVE_ME = 1U << 0,\n\tWGPEER_F_REPLACE_ALLOWEDIPS = 1U << 1,\n\tWGPEER_F_UPDATE_ONLY = 1U << 2,\n\t__WGPEER_F_ALL = WGPEER_F_REMOVE_ME | WGPEER_F_REPLACE_ALLOWEDIPS |\n\t\t\t WGPEER_F_UPDATE_ONLY\n};\nenum wgpeer_attribute {\n\tWGPEER_A_UNSPEC,\n\tWGPEER_A_PUBLIC_KEY,\n\tWGPEER_A_PRESHARED_KEY,\n\tWGPEER_A_FLAGS,\n\tWGPEER_A_ENDPOINT,\n\tWGPEER_A_PERSISTENT_KEEPALIVE_INTERVAL,\n\tWGPEER_A_LAST_HANDSHAKE_TIME,\n\tWGPEER_A_RX_BYTES,\n\tWGPEER_A_TX_BYTES,\n\tWGPEER_A_ALLOWEDIPS,\n\tWGPEER_A_PROTOCOL_VERSION,\n\t__WGPEER_A_LAST\n};\n#define WGPEER_A_MAX (__WGPEER_A_LAST - 1)\n\nenum wgallowedip_flag {\n\tWGALLOWEDIP_F_REMOVE_ME = 1U << 0,\n\t__WGALLOWEDIP_F_ALL = WGALLOWEDIP_F_REMOVE_ME\n};\nenum wgallowedip_attribute {\n\tWGALLOWEDIP_A_UNSPEC,\n\tWGALLOWEDIP_A_FAMILY,\n\tWGALLOWEDIP_A_IPADDR,\n\tWGALLOWEDIP_A_CIDR_MASK,\n\tWGALLOWEDIP_A_FLAGS,\n\t__WGALLOWEDIP_A_LAST\n};\n#define WGALLOWEDIP_A_MAX (__WGALLOWEDIP_A_LAST - 1)\n\n#endif /* _WG_UAPI_WIREGUARD_H */\n"
  },
  {
    "path": "src/uapi/openbsd/net/if_wg.h",
    "content": "/* SPDX-License-Identifier: ISC */\n/*\n * Copyright (C) 2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n * Copyright (c) 2020 Matt Dunwoodie <ncon@noconroy.net>\n */\n\n#ifndef __IF_WG_H__\n#define __IF_WG_H__\n\n#include <sys/limits.h>\n#include <sys/errno.h>\n\n#include <net/if.h>\n#include <netinet/in.h>\n\n\n/*\n * This is the public interface to the WireGuard network interface.\n *\n * It is designed to be used by tools such as ifconfig(8) and wg(8).\n */\n\n#define WG_KEY_LEN 32\n\n#define SIOCSWG _IOWR('i', 210, struct wg_data_io)\n#define SIOCGWG _IOWR('i', 211, struct wg_data_io)\n\n#define a_ipv4\ta_addr.addr_ipv4\n#define a_ipv6\ta_addr.addr_ipv6\n\nstruct wg_aip_io {\n\tsa_family_t\t a_af;\n\tint\t\t a_cidr;\n\tunion wg_aip_addr {\n\t\tstruct in_addr\t\taddr_ipv4;\n\t\tstruct in6_addr\t\taddr_ipv6;\n\t}\t\t a_addr;\n};\n\n#define WG_PEER_HAS_PUBLIC\t\t(1 << 0)\n#define WG_PEER_HAS_PSK\t\t\t(1 << 1)\n#define WG_PEER_HAS_PKA\t\t\t(1 << 2)\n#define WG_PEER_HAS_ENDPOINT\t\t(1 << 3)\n#define WG_PEER_REPLACE_AIPS\t\t(1 << 4)\n#define WG_PEER_REMOVE\t\t\t(1 << 5)\n#define WG_PEER_UPDATE\t\t\t(1 << 6)\n\n#define p_sa\t\tp_endpoint.sa_sa\n#define p_sin\t\tp_endpoint.sa_sin\n#define p_sin6\t\tp_endpoint.sa_sin6\n\nstruct wg_peer_io {\n\tint\t\t\tp_flags;\n\tint\t\t\tp_protocol_version;\n\tuint8_t\t\t\tp_public[WG_KEY_LEN];\n\tuint8_t\t\t\tp_psk[WG_KEY_LEN];\n\tuint16_t\t\tp_pka;\n\tunion wg_peer_endpoint {\n\t\tstruct sockaddr\t\tsa_sa;\n\t\tstruct sockaddr_in\tsa_sin;\n\t\tstruct sockaddr_in6\tsa_sin6;\n\t}\t\t\tp_endpoint;\n\tuint64_t\t\tp_txbytes;\n\tuint64_t\t\tp_rxbytes;\n\tstruct timespec\t\tp_last_handshake; /* nanotime */\n\tsize_t\t\t\tp_aips_count;\n\tstruct wg_aip_io\tp_aips[];\n};\n\n#define WG_INTERFACE_HAS_PUBLIC\t\t(1 << 0)\n#define WG_INTERFACE_HAS_PRIVATE\t(1 << 1)\n#define WG_INTERFACE_HAS_PORT\t\t(1 << 2)\n#define WG_INTERFACE_HAS_RTABLE\t\t(1 << 3)\n#define WG_INTERFACE_REPLACE_PEERS\t(1 << 4)\n\nstruct wg_interface_io {\n\tuint8_t\t\t\ti_flags;\n\tin_port_t\t\ti_port;\n\tint\t\t\ti_rtable;\n\tuint8_t\t\t\ti_public[WG_KEY_LEN];\n\tuint8_t\t\t\ti_private[WG_KEY_LEN];\n\tsize_t\t\t\ti_peers_count;\n\tstruct wg_peer_io\ti_peers[];\n};\n\nstruct wg_data_io {\n\tchar\t \t\t wgd_name[IFNAMSIZ];\n\tsize_t\t\t\t wgd_size;\t/* total size of the memory pointed to by wgd_interface */\n\tstruct wg_interface_io\t*wgd_interface;\n};\n\n#endif /* __IF_WG_H__ */\n"
  },
  {
    "path": "src/uapi/windows/wireguard.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0\n *\n * Copyright (C) 2021 WireGuard LLC. All Rights Reserved.\n */\n\n#ifndef _WIREGUARD_NT_H\n#define _WIREGUARD_NT_H\n\n#include <ntdef.h>\n#include <ws2def.h>\n#include <ws2ipdef.h>\n#include <inaddr.h>\n#include <in6addr.h>\n\n#define WG_KEY_LEN 32\n\ntypedef enum\n{\n    WG_IOCTL_ALLOWED_IP_REMOVE = 1 << 0\n} WG_IOCTL_ALLOWED_IP_FLAG;\n\ntypedef struct _WG_IOCTL_ALLOWED_IP\n{\n\tunion\n\t{\n\t\tIN_ADDR V4;\n\t\tIN6_ADDR V6;\n\t} Address;\n\tADDRESS_FAMILY AddressFamily;\n\tUCHAR Cidr;\n\tWG_IOCTL_ALLOWED_IP_FLAG Flags;\n} __attribute__((aligned(8))) WG_IOCTL_ALLOWED_IP;\n\ntypedef enum\n{\n\tWG_IOCTL_PEER_HAS_PUBLIC_KEY = 1 << 0,\n\tWG_IOCTL_PEER_HAS_PRESHARED_KEY = 1 << 1,\n\tWG_IOCTL_PEER_HAS_PERSISTENT_KEEPALIVE = 1 << 2,\n\tWG_IOCTL_PEER_HAS_ENDPOINT = 1 << 3,\n\tWG_IOCTL_PEER_HAS_PROTOCOL_VERSION = 1 << 4,\n\tWG_IOCTL_PEER_REPLACE_ALLOWED_IPS = 1 << 5,\n\tWG_IOCTL_PEER_REMOVE = 1 << 6,\n\tWG_IOCTL_PEER_UPDATE = 1 << 7\n} WG_IOCTL_PEER_FLAG;\n\ntypedef struct _WG_IOCTL_PEER\n{\n\tWG_IOCTL_PEER_FLAG Flags;\n\tULONG ProtocolVersion; /* 0 = latest protocol, 1 = this protocol. */\n\tUCHAR PublicKey[WG_KEY_LEN];\n\tUCHAR PresharedKey[WG_KEY_LEN];\n\tUSHORT PersistentKeepalive;\n\tSOCKADDR_INET Endpoint;\n\tULONG64 TxBytes;\n\tULONG64 RxBytes;\n\tULONG64 LastHandshake;\n\tULONG AllowedIPsCount;\n} __attribute__((aligned(8))) WG_IOCTL_PEER;\n\ntypedef enum\n{\n\tWG_IOCTL_INTERFACE_HAS_PUBLIC_KEY = 1 << 0,\n\tWG_IOCTL_INTERFACE_HAS_PRIVATE_KEY = 1 << 1,\n\tWG_IOCTL_INTERFACE_HAS_LISTEN_PORT = 1 << 2,\n\tWG_IOCTL_INTERFACE_REPLACE_PEERS = 1 << 3\n} WG_IOCTL_INTERFACE_FLAG;\n\ntypedef struct _WG_IOCTL_INTERFACE\n{\n\tWG_IOCTL_INTERFACE_FLAG Flags;\n\tUSHORT ListenPort;\n\tUCHAR PrivateKey[WG_KEY_LEN];\n\tUCHAR PublicKey[WG_KEY_LEN];\n\tULONG PeersCount;\n} __attribute__((aligned(8))) WG_IOCTL_INTERFACE;\n\n#define WG_IOCTL_GET CTL_CODE(45208U, 321, METHOD_OUT_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)\n#define WG_IOCTL_SET CTL_CODE(45208U, 322, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)\n\n#define DEVPKEY_WG_NAME (DEVPROPKEY) { \\\n\t\t{ 0x65726957, 0x7547, 0x7261, { 0x64, 0x4e, 0x61, 0x6d, 0x65, 0x4b, 0x65, 0x79 } }, \\\n\t\tDEVPROPID_FIRST_USABLE + 1 \\\n\t}\n\n\n#endif\n"
  },
  {
    "path": "src/version.h",
    "content": "#ifndef WIREGUARD_TOOLS_VERSION\n#define WIREGUARD_TOOLS_VERSION \"1.0.20260223\"\n#endif\n"
  },
  {
    "path": "src/wg-quick/android.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n *\n * This is a shell script written in C. It very intentionally still functions like\n * a shell script, calling out to external executables such as ip(8).\n */\n\n#define _GNU_SOURCE\n#include <stddef.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdbool.h>\n#include <string.h>\n#include <strings.h>\n#include <stdarg.h>\n#include <stdint.h>\n#include <ctype.h>\n#include <time.h>\n#include <unistd.h>\n#include <errno.h>\n#include <regex.h>\n#include <dlfcn.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n#include <sys/param.h>\n#include <sys/system_properties.h>\n\n#ifndef WG_PACKAGE_NAME\n#define WG_PACKAGE_NAME \"com.wireguard.android\"\n#endif\n#ifndef WG_CONFIG_SEARCH_PATHS\n#define WG_CONFIG_SEARCH_PATHS \"/data/misc/wireguard /data/data/\" WG_PACKAGE_NAME \"/files\"\n#endif\n\n#define _printf_(x, y) __attribute__((format(printf, x, y)))\n#define _cleanup_(x) __attribute__((cleanup(x)))\n#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))\n\nstatic bool is_exiting = false;\nstatic bool binder_available = false;\nstatic unsigned int sdk_version;\n\nstatic void *xmalloc(size_t size)\n{\n\tvoid *ret = malloc(size);\n\tif (ret)\n\t\treturn ret;\n\tperror(\"Error: malloc\");\n\texit(errno);\n}\n\nstatic void *xcalloc(size_t nmemb, size_t size)\n{\n\tvoid *ret = calloc(nmemb, size);\n\tif (ret)\n\t\treturn ret;\n\tperror(\"Error: calloc\");\n\texit(errno);\n}\n\nstatic void *xstrdup(const char *str)\n{\n\tchar *ret = strdup(str);\n\tif (ret)\n\t\treturn ret;\n\tperror(\"Error: strdup\");\n\texit(errno);\n}\n\nstatic void xregcomp(regex_t *preg, const char *regex, int cflags)\n{\n\tif (regcomp(preg, regex, cflags)) {\n\t\tfprintf(stderr, \"Error: Regex compilation error\\n\");\n\t\texit(EBADR);\n\t}\n}\n\nstatic char *concat(char *first, ...)\n{\n\tva_list args;\n\tsize_t len = 0;\n\tchar *ret;\n\n\tva_start(args, first);\n\tfor (char *i = first; i; i = va_arg(args, char *))\n\t\tlen += strlen(i);\n\tva_end(args);\n\n\tret = xmalloc(len + 1);\n\tret[0] = '\\0';\n\n\tva_start(args, first);\n\tfor (char *i = first; i; i = va_arg(args, char *))\n\t\tstrcat(ret, i);\n\tva_end(args);\n\n\treturn ret;\n}\n\nstatic char *concat_and_free(char *orig, const char *delim, const char *new_line)\n{\n\tchar *ret;\n\n\tif (!orig)\n\t\tret = xstrdup(new_line);\n\telse\n\t\tret = concat(orig, delim, new_line, NULL);\n\tfree(orig);\n\treturn ret;\n}\n\nstruct command_buffer {\n\tchar *line;\n\tsize_t len;\n\tFILE *stream;\n};\n\nstatic void free_command_buffer(struct command_buffer *c)\n{\n\tif (!c)\n\t\treturn;\n\tif (c->stream)\n\t\tpclose(c->stream);\n\tfree(c->line);\n}\n\nstatic void freep(void *p)\n{\n\tfree(*(void **)p);\n}\nstatic void fclosep(FILE **f)\n{\n\tif (*f)\n\t\tfclose(*f);\n}\n#define _cleanup_free_ _cleanup_(freep)\n#define _cleanup_fclose_ _cleanup_(fclosep)\n#define _cleanup_regfree_ _cleanup_(regfree)\n\n#define DEFINE_CMD(name) _cleanup_(free_command_buffer) struct command_buffer name = { 0 };\n\nstatic char *vcmd_ret(struct command_buffer *c, const char *cmd_fmt, va_list args)\n{\n\t_cleanup_free_ char *cmd = NULL;\n\n\tif (!c->stream && !cmd_fmt)\n\t\treturn NULL;\n\tif (c->stream && cmd_fmt)\n\t\tpclose(c->stream);\n\n\tif (cmd_fmt) {\n\t\tif (vasprintf(&cmd, cmd_fmt, args) < 0) {\n\t\t\tperror(\"Error: vasprintf\");\n\t\t\texit(errno);\n\t\t}\n\n\t\tc->stream = popen(cmd, \"r\");\n\t\tif (!c->stream) {\n\t\t\tperror(\"Error: popen\");\n\t\t\texit(errno);\n\t\t}\n\t}\n\terrno = 0;\n\tif (getline(&c->line, &c->len, c->stream) < 0) {\n\t\tif (errno) {\n\t\t\tperror(\"Error: getline\");\n\t\t\texit(errno);\n\t\t}\n\t\treturn NULL;\n\t}\n\treturn c->line;\n}\n\n_printf_(1, 2) static void cmd(const char *cmd_fmt, ...)\n{\n\t_cleanup_free_ char *cmd = NULL;\n\tva_list args;\n\tint ret;\n\n\tva_start(args, cmd_fmt);\n\tif (vasprintf(&cmd, cmd_fmt, args) < 0) {\n\t\tperror(\"Error: vasprintf\");\n\t\texit(errno);\n\t}\n\tva_end(args);\n\n\tprintf(\"[#] %s\\n\", cmd);\n\tret = system(cmd);\n\n\tif (ret < 0)\n\t\tret = ESRCH;\n\telse if (ret > 0)\n\t\tret = WIFEXITED(ret) ? WEXITSTATUS(ret) : EIO;\n\n\tif (ret && !is_exiting)\n\t\texit(ret);\n}\n\n_printf_(2, 3) static char *cmd_ret(struct command_buffer *c, const char *cmd_fmt, ...)\n{\n\tva_list args;\n\tchar *ret;\n\n\tva_start(args, cmd_fmt);\n\tret = vcmd_ret(c, cmd_fmt, args);\n\tva_end(args);\n\treturn ret;\n}\n\n_printf_(1, 2) static void cndc(const char *cmd_fmt, ...)\n{\n\tDEFINE_CMD(c);\n\tint error_code;\n\tchar *ret;\n\tva_list args;\n\t_cleanup_free_ char *ndc_fmt = concat(\"ndc \", cmd_fmt, NULL);\n\n\tva_start(args, cmd_fmt);\n\tprintf(\"[#] \");\n\tvprintf(ndc_fmt, args);\n\tprintf(\"\\n\");\n\tva_end(args);\n\n\tva_start(args, cmd_fmt);\n\tret = vcmd_ret(&c, ndc_fmt, args);\n\tva_end(args);\n\n\tif (!ret) {\n\t\tfprintf(stderr, \"Error: could not call ndc\\n\");\n\t\texit(ENOSYS);\n\t}\n\n\terror_code = atoi(ret);\n\tif (error_code >= 400 && error_code < 600) {\n\t\tfprintf(stderr, \"Error: %s\\n\", ret);\n\t\texit(ENONET);\n\t}\n}\n\n/* Values are from AOSP repository platform/frameworks/native in libs/binder/ndk/include_ndk/android/binder_status.h. */\nenum {\n\tSTATUS_OK = 0,\n\tSTATUS_UNKNOWN_ERROR = -2147483647 - 1,\n\tSTATUS_NO_MEMORY = -ENOMEM,\n\tSTATUS_INVALID_OPERATION = -ENOSYS,\n\tSTATUS_BAD_VALUE = -EINVAL,\n\tSTATUS_BAD_TYPE = STATUS_UNKNOWN_ERROR + 1,\n\tSTATUS_NAME_NOT_FOUND = -ENOENT,\n\tSTATUS_PERMISSION_DENIED = -EPERM,\n\tSTATUS_NO_INIT = -ENODEV,\n\tSTATUS_ALREADY_EXISTS = -EEXIST,\n\tSTATUS_DEAD_OBJECT = -EPIPE,\n\tSTATUS_FAILED_TRANSACTION = STATUS_UNKNOWN_ERROR + 2,\n\tSTATUS_BAD_INDEX = -EOVERFLOW,\n\tSTATUS_NOT_ENOUGH_DATA = -ENODATA,\n\tSTATUS_WOULD_BLOCK = -EWOULDBLOCK,\n\tSTATUS_TIMED_OUT = -ETIMEDOUT,\n\tSTATUS_UNKNOWN_TRANSACTION = -EBADMSG,\n\tSTATUS_FDS_NOT_ALLOWED = STATUS_UNKNOWN_ERROR + 7,\n\tSTATUS_UNEXPECTED_NULL = STATUS_UNKNOWN_ERROR + 8\n};\nenum {\n\tEX_NONE = 0,\n\tEX_SECURITY = -1,\n\tEX_BAD_PARCELABLE = -2,\n\tEX_ILLEGAL_ARGUMENT = -3,\n\tEX_NULL_POINTER = -4,\n\tEX_ILLEGAL_STATE = -5,\n\tEX_NETWORK_MAIN_THREAD = -6,\n\tEX_UNSUPPORTED_OPERATION = -7,\n\tEX_SERVICE_SPECIFIC = -8,\n\tEX_PARCELABLE = -9,\n\tEX_TRANSACTION_FAILED = -129\n};\nenum {\n\tFLAG_ONEWAY = 0x01,\n};\nenum {\n\tFIRST_CALL_TRANSACTION = 0x00000001,\n\tLAST_CALL_TRANSACTION = 0x00ffffff\n};\nstruct AIBinder;\nstruct AParcel;\nstruct AStatus;\nstruct AIBinder_Class;\ntypedef struct AIBinder AIBinder;\ntypedef struct AParcel AParcel;\ntypedef struct AStatus AStatus;\ntypedef struct AIBinder_Class AIBinder_Class;\ntypedef int32_t binder_status_t;\ntypedef int32_t binder_exception_t;\ntypedef uint32_t transaction_code_t;\ntypedef uint32_t binder_flags_t;\ntypedef void *(*AIBinder_Class_onCreate)(void *args);\ntypedef void (*AIBinder_Class_onDestroy)(void *userData);\ntypedef binder_status_t (*AIBinder_Class_onTransact)(AIBinder *binder, transaction_code_t code, const AParcel *in, AParcel *out);\ntypedef const char *(*AParcel_stringArrayElementGetter)(const void *arrayData, size_t index, int32_t *outLength);\nstatic AIBinder_Class *(*AIBinder_Class_define)(const char *interfaceDescriptor, AIBinder_Class_onCreate onCreate, AIBinder_Class_onDestroy onDestroy, AIBinder_Class_onTransact onTransact) __attribute__((warn_unused_result));\nstatic bool (*AIBinder_associateClass)(AIBinder *binder, const AIBinder_Class *clazz);\nstatic void (*AIBinder_decStrong)(AIBinder *binder);\nstatic binder_status_t (*AIBinder_prepareTransaction)(AIBinder *binder, AParcel **in);\nstatic binder_status_t (*AIBinder_transact)(AIBinder *binder, transaction_code_t code, AParcel **in, AParcel **out, binder_flags_t flags);\nstatic binder_status_t (*AIBinder_ping)(AIBinder *binder);\nstatic binder_status_t (*AIBinder_dump)(AIBinder *binder, int fd, const char **args, uint32_t numArgs);\nstatic binder_status_t (*AParcel_readStatusHeader)(const AParcel *parcel, AStatus **status);\nstatic binder_status_t (*AParcel_readBool)(const AParcel *parcel, bool *value);\nstatic void (*AParcel_delete)(AParcel *parcel);\nstatic binder_status_t (*AParcel_setDataPosition)(const AParcel *parcel, int32_t position);\nstatic int32_t (*AParcel_getDataPosition)(const AParcel *parcel);\nstatic binder_status_t (*AParcel_writeInt32)(AParcel *parcel, int32_t value);\nstatic binder_status_t (*AParcel_writeStringArray)(AParcel *parcel, const void *arrayData, int32_t length, AParcel_stringArrayElementGetter getter);\nstatic binder_status_t (*AParcel_writeString)(AParcel *parcel, const char *string, int32_t length);\nstatic bool (*AStatus_isOk)(const AStatus *status);\nstatic void (*AStatus_delete)(AStatus *status);\nstatic binder_exception_t (*AStatus_getExceptionCode)(const AStatus *status);\nstatic int32_t (*AStatus_getServiceSpecificError)(const AStatus *status);\nstatic const char* (*AStatus_getMessage)(const AStatus *status);\nstatic binder_status_t (*AStatus_getStatus)(const AStatus *status);\nstatic AIBinder *(*AServiceManager_getService)(const char *instance) __attribute__((__warn_unused_result__));\n\nstatic\t__attribute__((__constructor__(65535))) void load_symbols(void)\n{\n\tvoid *handle = dlopen(\"libbinder_ndk.so\", RTLD_LAZY);\n\tbinder_available = !!handle;\n\tif (!binder_available)\n\t\treturn;\n\n#define X(symb) do {\t\t\t\t\t\t\t\t\t\t\t\t\\\n\t\t\tif (!((symb) = (typeof(symb))dlsym(handle, #symb))) {\t\t\t\t\t\\\n\t\t\t\tfprintf(stderr, \"Error: unable to import \" #symb \" from libbinder_ndk.so\\n\");\t\\\n\t\t\t\texit(ELIBACC);\t\t\t\t\t\t\t\t\t\\\n\t\t\t}\t\t\t\t\t\t\t\t\t\t\t\\\n\t\t} while (0)\n\tX(AIBinder_Class_define);\n\tX(AIBinder_associateClass);\n\tX(AIBinder_decStrong);\n\tX(AIBinder_prepareTransaction);\n\tX(AIBinder_transact);\n\tX(AIBinder_ping);\n\tX(AIBinder_dump);\n\tX(AParcel_readStatusHeader);\n\tX(AParcel_readBool);\n\tX(AParcel_delete);\n\tX(AParcel_setDataPosition);\n\tX(AParcel_getDataPosition);\n\tX(AParcel_writeInt32);\n\tX(AParcel_writeStringArray);\n\tX(AParcel_writeString);\n\tX(AStatus_isOk);\n\tX(AStatus_delete);\n\tX(AStatus_getExceptionCode);\n\tX(AStatus_getServiceSpecificError);\n\tX(AStatus_getMessage);\n\tX(AStatus_getStatus);\n\tX(AServiceManager_getService);\n#undef X\n}\n\nstatic void cleanup_binder(AIBinder **binder)\n{\n\tif (*binder)\n\t\tAIBinder_decStrong(*binder);\n}\nstatic void cleanup_status(AStatus **status)\n{\n\tif (*status)\n\t\tAStatus_delete(*status);\n}\nstatic void cleanup_parcel(AParcel **parcel)\n{\n\tif (*parcel)\n\t\tAParcel_delete(*parcel);\n}\n\n#define _cleanup_status_ __attribute__((__cleanup__(cleanup_status)))\n#define _cleanup_parcel_ __attribute__((__cleanup__(cleanup_parcel)))\n#define _cleanup_binder_ __attribute__((__cleanup__(cleanup_binder)))\n\nstatic int32_t string_size(const char *str)\n{\n\treturn str ? strlen(str) : -1;\n}\n\nstatic int32_t string_array_size(char *const *array)\n{\n\tint32_t size = -1;\n\tif (!array)\n\t\treturn size;\n\tfor (size = 0; array[size]; ++size);\n\treturn size;\n}\n\nstatic const char *string_array_getter(const void *array_data, size_t index, int32_t *out_length)\n{\n\tconst char **array = (const char **)array_data;\n\t*out_length = array[index] ? strlen(array[index]) : -1;\n\treturn array[index];\n}\n\nstatic binder_status_t meaningful_binder_status(const AStatus *status_out)\n{\n\tbinder_status_t status = STATUS_OK;\n\tbinder_exception_t exc_code;\n\tint32_t exc_code_service;\n\tconst char *message;\n\n\tif (!AStatus_isOk(status_out)) {\n\t\texc_code = AStatus_getExceptionCode(status_out);\n\t\tif (exc_code == EX_TRANSACTION_FAILED) {\n\t\t\tstatus = AStatus_getStatus(status_out);\n\t\t\tfprintf(stderr, \"Error: transaction failed: %d\\n\", status);\n\t\t}\n\t\telse {\n\t\t\tmessage = AStatus_getMessage(status_out);\n\n\t\t\tif (exc_code == EX_SERVICE_SPECIFIC) {\n\t\t\t\texc_code_service = AStatus_getServiceSpecificError(status_out);\n\t\t\t\tfprintf(stderr, \"Error: service specific exception code: %d%s%s\\n\", exc_code_service, message ? \": \" : \"\", message ?: \"\");\n\t\t\t}\n\t\t\telse\n\t\t\t\tfprintf(stderr, \"Error: exception code: %d%s%s\\n\", exc_code, message ? \": \" : \"\", message ?: \"\");\n\n\t\t\tstatus = STATUS_FAILED_TRANSACTION;\n\t\t}\n\t}\n\n\treturn status;\n}\n\n/* These values are default values observed in AOSP. */\nenum {\n\tDNSRESOLVER_SAMPLE_VALIDITY = 1800 /* sec */,\n\tDNSRESOLVER_SUCCESS_THRESHOLD = 25,\n\tDNSRESOLVER_MIN_SAMPLES = 8,\n\tDNSRESOLVER_MAX_SAMPLES = 8,\n\tDNSRESOLVER_BASE_TIMEOUT = 5000 /* msec */,\n\tDNSRESOLVER_RETRY_COUNT = 2\n};\n\nstruct dnsresolver_params {\n\tint32_t netid;\n\tint32_t sample_validity_seconds;\n\tint32_t success_threshold;\n\tint32_t min_samples;\n\tint32_t max_samples;\n\tint32_t base_timeout_msec;\n\tint32_t retry_count;\n\tchar **servers;          /* NULL terminated array of zero-terminated UTF-8 strings */\n\tchar **domains;          /* NULL terminated array of zero-terminated UTF-8 strings */\n\tchar *tls_name;          /* zero-terminated UTF-8 string\t\t\t\t\t\t\t\t\t\t\t\t\t */\n\tchar **tls_servers;      /* NULL terminated array of zero-terminated UTF-8 strings */\n\tchar **tls_fingerprints; /* NULL terminated array of zero-terminated UTF-8 strings */\n};\n\nstatic void *on_create()\n{\n\tfprintf(stderr, \"Error: on_create called on proxy object\\n\");\n\texit(ENOTSUP);\n\treturn NULL;\n}\n\nstatic void on_destroy()\n{\n\tfprintf(stderr, \"Error: on_destroy called on proxy object\\n\");\n\texit(ENOTSUP);\n}\n\nstatic binder_status_t on_transact()\n{\n\tfprintf(stderr, \"Error: on_transact called on a proxy object\\n\");\n\texit(ENOTSUP);\n\treturn 0;\n}\n\nstatic AIBinder *dnsresolver_get_handle(void)\n{\n\tAIBinder *binder;\n\tAIBinder_Class *clazz;\n\n\tif (!binder_available)\n\t\treturn NULL;\n\n\tbinder = AServiceManager_getService(\"dnsresolver\");\n\tif (!binder)\n\t\treturn NULL;\n\tclazz = AIBinder_Class_define(\"android.net.IDnsResolver\", &on_create, &on_destroy, &on_transact);\n\tif (!clazz)\n\t\tgoto error;\n\n\tif (!AIBinder_associateClass(binder, clazz))\n\t\tgoto error;\n\n\treturn binder;\nerror:\n\tAIBinder_decStrong(binder);\n\treturn NULL;\n}\n\nstatic int32_t dnsresolver_create_network_cache(void *handle, int32_t netid)\n{\n\tAIBinder *const binder = handle;\n\tbinder_status_t status;\n\t_cleanup_parcel_ AParcel *parcel_in = NULL;\n\t_cleanup_parcel_ AParcel *parcel_out = NULL;\n\t_cleanup_status_ AStatus *status_out = NULL;\n\n\tstatus = AIBinder_prepareTransaction(binder, &parcel_in);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\n\tstatus = AParcel_writeInt32(parcel_in, netid);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\n\tstatus = AIBinder_transact(binder, FIRST_CALL_TRANSACTION + 7 /* createNetworkCache */, &parcel_in, &parcel_out, 0);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\n\tstatus = AParcel_readStatusHeader(parcel_out, &status_out);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\n\tif (!AStatus_isOk(status_out))\n\t\treturn meaningful_binder_status(status_out);\n\n\treturn STATUS_OK;\n}\n\nstatic int32_t dnsresolver_set_resolver_configuration(void *handle, const struct dnsresolver_params *params)\n{\n\tAIBinder *const binder = handle;\n\tbinder_status_t status;\n\t_cleanup_parcel_ AParcel *parcel_in = NULL;\n\t_cleanup_parcel_ AParcel *parcel_out = NULL;\n\t_cleanup_status_ AStatus *status_out = NULL;\n\tint32_t start_position, end_position;\n\n\tstatus = AIBinder_prepareTransaction(binder, &parcel_in);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\n\tstatus = AParcel_writeInt32(parcel_in, 1);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\n\tstart_position = AParcel_getDataPosition(parcel_in);\n\tstatus = AParcel_writeInt32(parcel_in, 0);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\n\tstatus = AParcel_writeInt32(parcel_in, params->netid);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_writeInt32(parcel_in, params->sample_validity_seconds);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_writeInt32(parcel_in, params->success_threshold);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_writeInt32(parcel_in, params->min_samples);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_writeInt32(parcel_in, params->max_samples);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_writeInt32(parcel_in, params->base_timeout_msec);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_writeInt32(parcel_in, params->retry_count);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_writeStringArray(parcel_in, params->servers, string_array_size(params->servers), &string_array_getter);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_writeStringArray(parcel_in, params->domains, string_array_size(params->domains), &string_array_getter);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_writeString(parcel_in, params->tls_name, string_size(params->tls_name));\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_writeStringArray(parcel_in, params->tls_servers, string_array_size(params->tls_servers), &string_array_getter);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_writeStringArray(parcel_in, params->tls_fingerprints, string_array_size(params->tls_fingerprints), &string_array_getter);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\n\tend_position = AParcel_getDataPosition(parcel_in);\n\tstatus = AParcel_setDataPosition(parcel_in, start_position);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_writeInt32(parcel_in, end_position - start_position);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\tstatus = AParcel_setDataPosition(parcel_in, end_position);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\n\tstatus = AIBinder_transact(binder, FIRST_CALL_TRANSACTION + 2 /* setResolverConfiguration */, &parcel_in, &parcel_out, 0);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\n\tstatus = AParcel_readStatusHeader(parcel_out, &status_out);\n\tif (status != STATUS_OK)\n\t\treturn status;\n\n\treturn meaningful_binder_status(status_out);\n}\n\nstatic void auto_su(int argc, char *argv[])\n{\n\tchar *args[argc + 4];\n\n\tif (!getuid())\n\t\treturn;\n\n\targs[0] = \"su\";\n\targs[1] = \"-p\";\n\targs[2] = \"-c\";\n\tmemcpy(&args[3], argv, argc * sizeof(*args));\n\targs[argc + 3] = NULL;\n\n\tprintf(\"[$] su -p -c \");\n\tfor (int i = 0; i < argc; ++i)\n\t\tprintf(\"%s%c\", argv[i], i == argc - 1 ? '\\n' : ' ');\n\n\texecvp(\"su\", args);\n\texit(errno);\n}\n\nstatic void add_if(const char *iface)\n{\n\tcmd(\"ip link add %s type wireguard\", iface);\n}\n\nstatic void del_if(const char *iface)\n{\n\tDEFINE_CMD(c_rule);\n\tDEFINE_CMD(c_iptables);\n\tDEFINE_CMD(c_ip6tables);\n\t_cleanup_regfree_ regex_t rule_reg = { 0 }, iptables_reg = { 0 };\n\tregmatch_t matches[2];\n\tchar *netid = NULL;\n\t_cleanup_free_ char *rule_regex = concat(\"0xc([0-9a-f]+)/0xcffff lookup \", iface, NULL);\n\t_cleanup_free_ char *iptables_regex = concat(\"^-A (.* --comment \\\"wireguard rule \", iface, \"\\\"[^\\n]*)\\n*$\", NULL);\n\n\txregcomp(&rule_reg, rule_regex, REG_EXTENDED);\n\txregcomp(&iptables_reg, iptables_regex, REG_EXTENDED);\n\n\tcmd(\"ip link del %s\", iface);\n\n\tfor (char *rule = cmd_ret(&c_iptables, \"iptables-save\"); rule; rule = cmd_ret(&c_iptables, NULL)) {\n\t\tif (!regexec(&iptables_reg, rule, ARRAY_SIZE(matches), matches, 0)) {\n\t\t\trule[matches[1].rm_eo] = '\\0';\n\t\t\tcmd(\"iptables -D %s\", &rule[matches[1].rm_so]);\n\t\t}\n\t}\n\tfor (char *rule = cmd_ret(&c_ip6tables, \"ip6tables-save\"); rule; rule = cmd_ret(&c_ip6tables, NULL)) {\n\t\tif (!regexec(&iptables_reg, rule, ARRAY_SIZE(matches), matches, 0)) {\n\t\t\trule[matches[1].rm_eo] = '\\0';\n\t\t\tcmd(\"ip6tables -D %s\", &rule[matches[1].rm_so]);\n\t\t}\n\t}\n\n\tfor (char *rule = cmd_ret(&c_rule, \"ip rule show\"); rule; rule = cmd_ret(&c_rule, NULL)) {\n\t\tif (!regexec(&rule_reg, rule, ARRAY_SIZE(matches), matches, 0)) {\n\t\t\trule[matches[1].rm_eo] = '\\0';\n\t\t\tnetid = &rule[matches[1].rm_so];\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (netid)\n\t\tcndc(\"network destroy %lu\", strtoul(netid, NULL, 16));\n}\n\nstatic bool should_block_ipv6(const char *iface)\n{\n\tDEFINE_CMD(c);\n\tbool has_ipv6 = false, has_all_none = true;\n\n\tfor (char *endpoint = cmd_ret(&c, \"wg show %s endpoints\", iface); endpoint; endpoint = cmd_ret(&c, NULL)) {\n\t\tchar *start = strchr(endpoint, '\\t');\n\n\t\tif (!start)\n\t\t\tcontinue;\n\t\t++start;\n\t\tif (start[0] != '(')\n\t\t\thas_all_none = false;\n\t\tif (start[0] == '[')\n\t\t\thas_ipv6 = true;\n\t}\n\treturn !has_ipv6 && !has_all_none;\n}\n\nstatic uint16_t determine_listen_port(const char *iface)\n{\n\tDEFINE_CMD(c);\n\tunsigned long listen_port = 0;\n\tchar *value;\n\n\tcmd(\"ip link set up dev %s\", iface);\n\tvalue = cmd_ret(&c, \"wg show %s listen-port\", iface);\n\tif (!value)\n\t\tgoto set_back_down;\n\tlisten_port = strtoul(value, NULL, 10);\n\tif (listen_port > UINT16_MAX || !listen_port) {\n\t\tlisten_port = 0;\n\t\tgoto set_back_down;\n\t}\nset_back_down:\n\tcmd(\"ip link set down dev %s\", iface);\n\treturn listen_port;\n}\n\nstatic void up_if(unsigned int *netid, const char *iface, uint16_t listen_port)\n{\n\tsrandom(time(NULL) ^ getpid()); /* Not real randomness. */\n\n\twhile (*netid < 4096)\n\t\t*netid = random() & 0xfffe;\n\n\tcmd(\"wg set %s fwmark 0x20000\", iface);\n\tcmd(\"iptables -I OUTPUT 1 -m mark --mark 0x20000 -j ACCEPT -m comment --comment \\\"wireguard rule %s\\\"\", iface);\n\tcmd(\"ip6tables -I OUTPUT 1 -m mark --mark 0x20000 -j ACCEPT -m comment --comment \\\"wireguard rule %s\\\"\", iface);\n\tif (listen_port) {\n\t\tcmd(\"iptables -I INPUT 1 -p udp --dport %u -j ACCEPT -m comment --comment \\\"wireguard rule %s\\\"\", listen_port, iface);\n\t\tcmd(\"ip6tables -I INPUT 1 -p udp --dport %u -j %s -m comment --comment \\\"wireguard rule %s\\\"\", listen_port, should_block_ipv6(iface) ? \"DROP\" : \"ACCEPT\", iface);\n\t}\n\tcmd(\"ip link set up dev %s\", iface);\n\tcndc(sdk_version < 31 ? \"network create %u vpn 1 1\" : \"network create %u vpn 1\", *netid);\n\tcndc(\"network interface add %u %s\", *netid, iface);\n}\n\nstatic int compare_uid(const void *a, const void *b)\n{\n\treturn *(const uid_t *)a - *(const uid_t *)b;\n}\n\nstatic uid_t *get_uid_list(const char *selected_applications)\n{\n\t_cleanup_fclose_ FILE *package_list = NULL;\n\t_cleanup_free_ char *line = NULL;\n\tuid_t package_uid;\n\tsize_t line_len = 0, i;\n\tconst char *comma, *start;\n\tuid_t *uid_list;\n\n\tif (!selected_applications)\n\t\treturn xcalloc(1, sizeof(*uid_list));\n\n\tfor (i = 1, comma = selected_applications; comma; comma = strchr(comma + 1, ','), ++i);\n\tuid_list = xcalloc(i, sizeof(*uid_list));\n\ti = 0;\n\n\tpackage_list = fopen(\"/data/system/packages.list\", \"r\");\n\tif (!package_list) {\n\t\tperror(\"Error: Unable to open package list\");\n\t\texit(errno);\n\t}\n\n\twhile (getline(&line, &line_len, package_list) >= 0) {\n\t\tchar *package_name, *package_uid_str, *endptr;\n\n\t\tpackage_name = line;\n\t\tpackage_uid_str = strchr(package_name, ' ');\n\t\tif (!package_uid_str)\n\t\t\tcontinue;\n\t\t*package_uid_str++ = '\\0';\n\t\t*strchrnul(package_uid_str, ' ') = '\\0';\n\t\tpackage_uid = strtoul(package_uid_str, &endptr, 10);\n\t\tif (!package_uid || !*package_uid_str || *endptr)\n\t\t\tcontinue;\n\n\t\tfor (start = selected_applications; comma = strchrnul(start, ','), *start; start = comma + 1) {\n\t\t\tptrdiff_t token_len = comma - start;\n\n\t\t\tif (token_len && strlen(package_name) == token_len && !strncmp(start, package_name, token_len))\n\t\t\t\tuid_list[i++] = package_uid;\n\t\t}\n\t}\n\tqsort(uid_list, i, sizeof(*uid_list), compare_uid);\n\treturn uid_list;\n}\n\nstatic void set_users(unsigned int netid, const char *excluded_applications, const char *included_applications)\n{\n\t_cleanup_free_ uid_t *uids = NULL;\n\tuid_t *uid;\n\tunsigned int args_per_command = 0;\n\t_cleanup_free_ char *ranges = NULL;\n\tchar range[22];\n\tuid_t start;\n\n\tif (excluded_applications && included_applications) {\n\t\tfprintf(stderr, \"Error: only one of ExcludedApplications and IncludedApplications may be specified, but not both\\n\");\n\t\texit(EEXIST);\n\t}\n\n\tif (excluded_applications || !included_applications) {\n\t\tuid = uids = get_uid_list(excluded_applications);\n\t\tfor (start = 0; *uid; start = *uid + 1, ++uid) {\n\t\t\tif (start > *uid - 1)\n\t\t\t\tcontinue;\n\t\t\telse if (start == *uid - 1)\n\t\t\t\tsnprintf(range, sizeof(range), \"%u\", start);\n\t\t\telse\n\t\t\t\tsnprintf(range, sizeof(range), \"%u-%u\", start, *uid - 1);\n\t\t\tranges = concat_and_free(ranges, \" \", range);\n\t\t\tif (++args_per_command % 18 == 0) {\n\t\t\t\tcndc(\"network users add %u %s\", netid, ranges);\n\t\t\t\tfree(ranges);\n\t\t\t\tranges = NULL;\n\t\t\t}\n\t\t}\n\t\tif (start < 99999) {\n\t\t\tsnprintf(range, sizeof(range), \"%u-99999\", start);\n\t\t\tranges = concat_and_free(ranges, \" \", range);\n\t\t}\n\t} else {\n\t\tfor (uid = uids = get_uid_list(included_applications); *uid; ++uid) {\n\t\t\tsnprintf(range, sizeof(range), \"%u\", *uid);\n\t\t\tranges = concat_and_free(ranges, \" \", range);\n\t\t\tif (++args_per_command % 18 == 0) {\n\t\t\t\tcndc(\"network users add %u %s\", netid, ranges);\n\t\t\t\tfree(ranges);\n\t\t\t\tranges = NULL;\n\t\t\t}\n\t\t}\n\t}\n\n\tif (ranges)\n\t\tcndc(\"network users add %u %s\", netid, ranges);\n}\n\nstatic void set_dnses(unsigned int netid, const char *dnses)\n{\n\tsize_t len = strlen(dnses);\n\tif (len > (1<<16))\n\t\treturn;\n\t_cleanup_free_ char *mutable = xstrdup(dnses);\n\t_cleanup_free_ char *dns_shell_arglist = xmalloc(len * 4 + 1);\n\t_cleanup_free_ char *dns_search_shell_arglist = xmalloc(len * 4 + 1);\n\t_cleanup_free_ char *dns_function_arglist = xmalloc(len * 4 + 1);\n\t_cleanup_free_ char *dns_search_function_arglist = xmalloc(len * 4 + 1);\n\t_cleanup_free_ char *arg = xmalloc(len + 4);\n\t_cleanup_free_ char **dns_list = NULL;\n\t_cleanup_free_ char **dns_search_list = NULL;\n\t_cleanup_binder_ AIBinder *handle = NULL;\n\t_cleanup_regfree_ regex_t regex_ipnothost = { 0 };\n\tsize_t dns_list_size = 0, dns_search_list_size = 0;\n\tbool is_ip;\n\n\tif (!len)\n\t\treturn;\n\n\txregcomp(&regex_ipnothost, \"(^[0-9.]+$)|(^.*:.*$)\", REG_EXTENDED | REG_NOSUB);\n\tfor (char *dns = strtok(mutable, \", \\t\\n\"); dns; dns = strtok(NULL, \", \\t\\n\")) {\n\t\tif (strchr(dns, '\\'') || strchr(dns, '\\\\'))\n\t\t\tcontinue;\n\t\t++*(!regexec(&regex_ipnothost, dns, 0, NULL, 0) ? &dns_list_size : &dns_search_list_size);\n\t}\n\tif (!dns_list_size)\n\t\treturn;\n\tdns_list = xcalloc(dns_list_size + 1, sizeof(*dns_list));\n\tdns_search_list = xcalloc(dns_search_list_size + 1, sizeof(*dns_search_list));\n\tfree(mutable);\n\tmutable = xstrdup(dnses);\n\n\tdns_shell_arglist[0] = '\\0';\n\tdns_search_shell_arglist[0] = '\\0';\n\tdns_function_arglist[0] = '\\0';\n\tdns_search_function_arglist[0] = '\\0';\n\tdns_list_size = 0;\n\tdns_search_list_size = 0;\n\tfor (char *dns = strtok(mutable, \", \\t\\n\"); dns; dns = strtok(NULL, \", \\t\\n\")) {\n\t\tif (strchr(dns, '\\'') || strchr(dns, '\\\\'))\n\t\t\tcontinue;\n\t\tis_ip = !regexec(&regex_ipnothost, dns, 0, NULL, 0);\n\t\tsnprintf(arg, len + 3, \"'%s' \", dns);\n\t\tstrncat(is_ip ? dns_shell_arglist : dns_search_shell_arglist, arg, len * 4 - 1);\n\t\tsnprintf(arg, len + 2, (is_ip ? dns_function_arglist[0] : dns_search_function_arglist[0]) == '\\0' ? \"%s\" : \", %s\", dns);\n\t\tstrncat(is_ip ? dns_function_arglist : dns_search_function_arglist, arg, len * 4 - 1);\n\t\t*(is_ip ? &dns_list[dns_list_size++] : &dns_search_list[dns_search_list_size++]) = dns;\n\t}\n\n\tif ((handle = dnsresolver_get_handle())) {\n\t\tbinder_status_t status;\n\n\t\tprintf(\"[#] <binder>::dnsResolver->createNetworkCache(%u)\\n\", netid);\n\t\tstatus = dnsresolver_create_network_cache(handle, netid);\n\t\tif (status != 0) {\n\t\t\tfprintf(stderr, \"Error: unable to create network cache\\n\");\n\t\t\texit(ENONET);\n\t\t}\n\n\t\tstruct dnsresolver_params params = {\n\t\t\t.netid = netid,\n\t\t\t.sample_validity_seconds = DNSRESOLVER_SAMPLE_VALIDITY,\n\t\t\t.success_threshold = DNSRESOLVER_SUCCESS_THRESHOLD,\n\t\t\t.min_samples = DNSRESOLVER_MIN_SAMPLES,\n\t\t\t.max_samples = DNSRESOLVER_MAX_SAMPLES,\n\t\t\t.base_timeout_msec = DNSRESOLVER_BASE_TIMEOUT,\n\t\t\t.retry_count = DNSRESOLVER_RETRY_COUNT,\n\t\t\t.servers = dns_list,\n\t\t\t.domains = dns_search_list,\n\t\t\t.tls_name = \"\",\n\t\t\t.tls_servers = (char *[]){NULL},\n\t\t\t.tls_fingerprints = (char *[]){NULL}\n\t\t};\n\n\t\tprintf(\"[#] <binder>::dnsResolver->setResolverConfiguration(%u, [%s], [%s], %d, %d, %d, %d, %d, %d, [], [])\\n\",\n\t\t       netid, dns_function_arglist, dns_search_function_arglist, DNSRESOLVER_SAMPLE_VALIDITY,\n\t\t       DNSRESOLVER_SUCCESS_THRESHOLD, DNSRESOLVER_MIN_SAMPLES, DNSRESOLVER_MAX_SAMPLES,\n\t\t       DNSRESOLVER_BASE_TIMEOUT, DNSRESOLVER_RETRY_COUNT);\n\t\tstatus = dnsresolver_set_resolver_configuration(handle, &params);\n\n\t\tif (status != 0) {\n\t\t\tfprintf(stderr, \"Error: unable to set DNS servers through Binder: %d\\n\", status);\n\t\t\texit(ENONET);\n\t\t}\n\t} else\n\t\tcndc(\"resolver setnetdns %u '%s' %s\", netid, dns_search_shell_arglist, dns_shell_arglist);\n}\n\nstatic void add_addr(const char *iface, const char *addr)\n{\n\tif (strchr(addr, ':')) {\n\t\tcndc(\"interface ipv6 %s enable\", iface);\n\t\tcmd(\"ip -6 addr add '%s' dev %s\", addr, iface);\n\t} else {\n\t\t_cleanup_free_ char *mut_addr = strdup(addr);\n\t\tchar *slash = strchr(mut_addr, '/');\n\t\tunsigned char mask = 32;\n\n\t\tif (slash) {\n\t\t\t*slash = '\\0';\n\t\t\tmask = atoi(slash + 1);\n\t\t}\n\t\tcndc(\"interface setcfg %s '%s' %u\", iface, mut_addr, mask);\n\t}\n}\n\nstatic void set_addr(const char *iface, const char *addrs)\n{\n\t_cleanup_free_ char *mutable = xstrdup(addrs);\n\n\tfor (char *addr = strtok(mutable, \", \\t\\n\"); addr; addr = strtok(NULL, \", \\t\\n\")) {\n\t\tif (strchr(addr, '\\'') || strchr(addr, '\\\\'))\n\t\t\tcontinue;\n\t\tadd_addr(iface, addr);\n\t}\n}\n\nstatic int get_route_mtu(const char *endpoint)\n{\n\tDEFINE_CMD(c_route);\n\tDEFINE_CMD(c_dev);\n\tregmatch_t matches[2];\n\t_cleanup_regfree_ regex_t regex_mtu = { 0 }, regex_dev = { 0 };\n\tchar *route, *mtu, *dev;\n\n\txregcomp(&regex_mtu, \"mtu ([0-9]+)\", REG_EXTENDED);\n\txregcomp(&regex_dev, \"dev ([^ ]+)\", REG_EXTENDED);\n\n\tif (strcmp(endpoint, \"default\"))\n\t\troute = cmd_ret(&c_route, \"ip -o route get %s\", endpoint);\n\telse\n\t\troute = cmd_ret(&c_route, \"ip -o route show %s\", endpoint);\n\tif (!route)\n\t\treturn -1;\n\n\tif (!regexec(&regex_mtu, route, ARRAY_SIZE(matches), matches, 0)) {\n\t\troute[matches[1].rm_eo] = '\\0';\n\t\tmtu = &route[matches[1].rm_so];\n\t} else if (!regexec(&regex_dev, route, ARRAY_SIZE(matches), matches, 0)) {\n\t\troute[matches[1].rm_eo] = '\\0';\n\t\tdev = &route[matches[1].rm_so];\n\t\troute = cmd_ret(&c_dev, \"ip -o link show dev %s\", dev);\n\t\tif (!route)\n\t\t\treturn -1;\n\t\tif (regexec(&regex_mtu, route, ARRAY_SIZE(matches), matches, 0))\n\t\t\treturn -1;\n\t\troute[matches[1].rm_eo] = '\\0';\n\t\tmtu = &route[matches[1].rm_so];\n\t} else\n\t\treturn -1;\n\treturn atoi(mtu);\n}\n\nstatic void set_mtu(const char *iface, unsigned int mtu)\n{\n\tDEFINE_CMD(c_endpoints);\n\t_cleanup_regfree_ regex_t regex_endpoint = { 0 };\n\tregmatch_t matches[2];\n\tint endpoint_mtu, next_mtu;\n\n\tif (mtu) {\n\t\tcndc(\"interface setmtu %s %u\", iface, mtu);\n\t\treturn;\n\t}\n\n\txregcomp(&regex_endpoint, \"^\\\\[?([a-z0-9:.]+)\\\\]?:[0-9]+$\", REG_EXTENDED);\n\n\tendpoint_mtu = get_route_mtu(\"default\");\n\tif (endpoint_mtu == -1)\n\t\tendpoint_mtu = 1500;\n\n\tfor (char *endpoint = cmd_ret(&c_endpoints, \"wg show %s endpoints\", iface); endpoint; endpoint = cmd_ret(&c_endpoints, NULL)) {\n\t\tif (regexec(&regex_endpoint, endpoint, ARRAY_SIZE(matches), matches, 0))\n\t\t\tcontinue;\n\t\tendpoint[matches[1].rm_eo] = '\\0';\n\t\tendpoint = &endpoint[matches[1].rm_so];\n\n\t\tnext_mtu = get_route_mtu(endpoint);\n\t\tif (next_mtu > 0 && next_mtu < endpoint_mtu)\n\t\t\tendpoint_mtu = next_mtu;\n\t}\n\n\tcndc(\"interface setmtu %s %d\", iface, endpoint_mtu - 80);\n}\n\nstatic void add_route(const char *iface, unsigned int netid, const char *route)\n{\n\tcndc(\"network route add %u %s %s\", netid, iface, route);\n}\n\nstatic void set_routes(const char *iface, unsigned int netid)\n{\n\tDEFINE_CMD(c);\n\n\tfor (char *allowedips = cmd_ret(&c, \"wg show %s allowed-ips\", iface); allowedips; allowedips = cmd_ret(&c, NULL)) {\n\t\tchar *start = strchr(allowedips, '\\t');\n\n\t\tif (!start)\n\t\t\tcontinue;\n\t\t++start;\n\t\tfor (char *allowedip = strtok(start, \" \\n\"); allowedip; allowedip = strtok(NULL, \" \\n\")) {\n\t\t\tif (!strcmp(allowedip, \"(none)\"))\n\t\t\t\tcontinue;\n\t\t\tadd_route(iface, netid, allowedip);\n\t\t}\n\t}\n}\n\nstatic void set_config(const char *iface, const char *config)\n{\n\tFILE *config_writer;\n\t_cleanup_free_ char *cmd = concat(\"wg addconf \", iface, \" /proc/self/fd/0\", NULL);\n\tint ret;\n\n\tprintf(\"[#] %s\\n\", cmd);\n\n\tconfig_writer = popen(cmd, \"w\");\n\tif (!config_writer) {\n\t\tperror(\"Error: popen\");\n\t\texit(errno);\n\t}\n\tif (fputs(config, config_writer) < 0) {\n\t\tperror(\"Error: fputs\");\n\t\texit(errno);\n\t}\n\tret = pclose(config_writer);\n\tif (ret)\n\t\texit(WIFEXITED(ret) ? WEXITSTATUS(ret) : EIO);\n}\n\nstatic void broadcast_change(void)\n{\n\tconst char *pkg = getenv(\"CALLING_PACKAGE\");\n\n\tif (!pkg || strcmp(pkg, WG_PACKAGE_NAME))\n\t\tcmd(\"am broadcast -a com.wireguard.android.action.REFRESH_TUNNEL_STATES \" WG_PACKAGE_NAME);\n}\n\nstatic void print_search_paths(FILE *file, const char *prefix)\n{\n\t_cleanup_free_ char *paths = strdup(WG_CONFIG_SEARCH_PATHS);\n\n\tfor (char *path = strtok(paths, \" \"); path; path = strtok(NULL, \" \"))\n\t\tfprintf(file, \"%s%s\\n\", prefix, path);\n}\n\nstatic void cmd_usage(const char *program)\n{\n\tprintf( \"Usage: %s [ up | down ] [ CONFIG_FILE | INTERFACE ]\\n\"\n\t\t\"\\n\"\n\t\t\"  CONFIG_FILE is a configuration file, whose filename is the interface name\\n\"\n\t\t\"  followed by `.conf'. Otherwise, INTERFACE is an interface name, with\\n\"\n\t\t\"  configuration found at:\\n\\n\", program);\n\tprint_search_paths(stdout, \"  - \");\n\tprintf( \"\\n  It is to be readable by wg(8)'s `setconf' sub-command, with the exception\\n\"\n\t\t\"  of the following additions to the [Interface] section, which are handled by\\n\"\n\t\t\"  this program:\\n\\n\"\n\t\t\"  - Address: may be specified one or more times and contains one or more\\n\"\n\t\t\"    IP addresses (with an optional CIDR mask) to be set for the interface.\\n\"\n\t\t\"  - MTU: an optional MTU for the interface; if unspecified, auto-calculated.\\n\"\n\t\t\"  - DNS: an optional DNS server to use while the device is up.\\n\"\n\t\t\"  - ExcludedApplications: optional blacklist of applications to exclude from the tunnel.\\n\\n\"\n\t\t\"  - IncludedApplications: optional whitelist of applications to include in the tunnel.\\n\\n\"\n\t\t\"  See wg-quick(8) for more info and examples.\\n\");\n}\n\nstatic char *cleanup_iface = NULL;\n\nstatic void cmd_up_cleanup(void)\n{\n\tis_exiting = true;\n\tif (cleanup_iface)\n\t\tdel_if(cleanup_iface);\n\tfree(cleanup_iface);\n}\n\nstatic void cmd_up(const char *iface, const char *config, unsigned int mtu, const char *addrs, const char *dnses, const char *excluded_applications, const char *included_applications)\n{\n\tDEFINE_CMD(c);\n\tunsigned int netid = 0;\n\tuint16_t listen_port;\n\n\tif (cmd_ret(&c, \"ip link show dev %s 2>/dev/null\", iface)) {\n\t\tfprintf(stderr, \"Error: %s already exists\\n\", iface);\n\t\texit(EEXIST);\n\t}\n\n\tcleanup_iface = xstrdup(iface);\n\tatexit(cmd_up_cleanup);\n\n\tadd_if(iface);\n\tset_config(iface, config);\n\tlisten_port = determine_listen_port(iface);\n\tup_if(&netid, iface, listen_port);\n\tset_addr(iface, addrs);\n\tset_dnses(netid, dnses);\n\tset_routes(iface, netid);\n\tset_mtu(iface, mtu);\n\tset_users(netid, excluded_applications, included_applications);\n\tbroadcast_change();\n\n\tfree(cleanup_iface);\n\tcleanup_iface = NULL;\n\texit(EXIT_SUCCESS);\n}\n\nstatic void cmd_down(const char *iface)\n{\n\tDEFINE_CMD(c);\n\tbool found = false;\n\n\tchar *ifaces = cmd_ret(&c, \"wg show interfaces\");\n\tif (ifaces) {\n\t\tfor (char *eiface = strtok(ifaces, \" \\n\"); eiface; eiface = strtok(NULL, \" \\n\")) {\n\t\t\tif (!strcmp(iface, eiface)) {\n\t\t\t\tfound = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\tif (!found) {\n\t\tfprintf(stderr, \"Error: %s is not a WireGuard interface\\n\", iface);\n\t\texit(EMEDIUMTYPE);\n\t}\n\n\tdel_if(iface);\n\tbroadcast_change();\n\texit(EXIT_SUCCESS);\n}\n\nstatic void parse_options(char **iface, char **config, unsigned int *mtu, char **addrs, char **dnses, char **excluded_applications, char **included_applications, const char *arg)\n{\n\t_cleanup_fclose_ FILE *file = NULL;\n\t_cleanup_free_ char *line = NULL;\n\t_cleanup_free_ char *filename = NULL;\n\t_cleanup_free_ char *paths = strdup(WG_CONFIG_SEARCH_PATHS);\n\t_cleanup_regfree_ regex_t regex_iface = { 0 }, regex_conf = { 0 };\n\tregmatch_t matches[2];\n\tstruct stat sbuf;\n\tsize_t n = 0;\n\tbool in_interface_section = false;\n\n\t*iface = *config = *addrs = *dnses = NULL;\n\t*mtu = 0;\n\n\txregcomp(&regex_iface, \"^[a-zA-Z0-9_=+.-]{1,15}$\", REG_EXTENDED | REG_NOSUB);\n\txregcomp(&regex_conf, \"/?([a-zA-Z0-9_=+.-]{1,15})\\\\.conf$\", REG_EXTENDED);\n\n\tif (!regexec(&regex_iface, arg, 0, NULL, 0)) {\n\t\tfor (char *path = strtok(paths, \" \"); path; path = strtok(NULL, \" \")) {\n\t\t\tfree(filename);\n\t\t\tif (asprintf(&filename, \"%s/%s.conf\", path, arg) < 0) {\n\t\t\t\tperror(\"Error: asprintf\");\n\t\t\t\texit(errno);\n\t\t\t}\n\t\t\tfile = fopen(filename, \"r\");\n\t\t\tif (file)\n\t\t\t\tbreak;\n\t\t}\n\t\tif (!file) {\n\t\t\tfprintf(stderr, \"Error: Unable to find configuration file for `%s' in:\\n\", arg);\n\t\t\tprint_search_paths(stderr, \"- \");\n\t\t\texit(errno);\n\t\t}\n\t} else {\n\t\tfilename = xstrdup(arg);\n\t\tfile = fopen(filename, \"r\");\n\t\tif (!file) {\n\t\t\tfprintf(stderr, \"Error: Unable to find configuration file at `%s'\\n\", filename);\n\t\t\texit(errno);\n\t\t}\n\t}\n\n\tif (regexec(&regex_conf, filename, ARRAY_SIZE(matches), matches, 0)) {\n\t\tfprintf(stderr, \"Error: The config file must be a valid interface name, followed by .conf\\n\");\n\t\texit(EINVAL);\n\t}\n\n\tif (fstat(fileno(file), &sbuf) < 0) {\n\t\tperror(\"Error: fstat\");\n\t\texit(errno);\n\t}\n\tif (sbuf.st_mode & 0007)\n\t\tfprintf(stderr, \"Warning: `%s' is world accessible\\n\", filename);\n\n\tfilename[matches[1].rm_eo] = '\\0';\n\t*iface = xstrdup(&filename[matches[1].rm_so]);\n\n\twhile (getline(&line, &n, file) >= 0) {\n\t\tsize_t len = strlen(line), j = 0;\n\t\tif (len > (1<<16))\n\t\t\treturn;\n\t\t_cleanup_free_ char *clean = xmalloc(len + 1);\n\n\t\tfor (size_t i = 0; i < len; ++i) {\n\t\t\tif (!isspace(line[i]))\n\t\t\t\tclean[j++] = line[i];\n\t\t}\n\t\tclean[j] = '\\0';\n\n\t\tif (clean[0] == '[')\n\t\t\tin_interface_section = false;\n\t\tif (!strcasecmp(clean, \"[Interface]\"))\n\t\t\tin_interface_section = true;\n\t\tif (in_interface_section) {\n\t\t\tif (!strncasecmp(clean, \"Address=\", 8) && j > 8) {\n\t\t\t\t*addrs = concat_and_free(*addrs, \",\", clean + 8);\n\t\t\t\tcontinue;\n\t\t\t} else if (!strncasecmp(clean, \"DNS=\", 4) && j > 4) {\n\t\t\t\t*dnses = concat_and_free(*dnses, \",\", clean + 4);\n\t\t\t\tcontinue;\n\t\t\t} else if (!strncasecmp(clean, \"ExcludedApplications=\", 21) && j > 4) {\n\t\t\t\t*excluded_applications = concat_and_free(*excluded_applications, \",\", clean + 21);\n\t\t\t\tcontinue;\n\t\t\t} else if (!strncasecmp(clean, \"IncludedApplications=\", 21) && j > 4) {\n\t\t\t\t*included_applications = concat_and_free(*included_applications, \",\", clean + 21);\n\t\t\t\tcontinue;\n\t\t\t} else if (!strncasecmp(clean, \"MTU=\", 4) && j > 4) {\n\t\t\t\t*mtu = atoi(clean + 4);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\t*config = concat_and_free(*config, \"\", line);\n\t}\n\n\tif (!*iface)\n\t\t*iface = xstrdup(\"\");\n\tif (!*config)\n\t\t*config = xstrdup(\"\");\n\tif (!*addrs)\n\t\t*addrs = xstrdup(\"\");\n\tif (!*dnses)\n\t\t*dnses = xstrdup(\"\");\n}\n\nint main(int argc, char *argv[])\n{\n\t_cleanup_free_ char *iface = NULL;\n\t_cleanup_free_ char *config = NULL;\n\t_cleanup_free_ char *addrs = NULL;\n\t_cleanup_free_ char *dnses = NULL;\n\t_cleanup_free_ char *excluded_applications = NULL;\n\t_cleanup_free_ char *included_applications = NULL;\n\tunsigned int mtu;\n\tchar prop[PROP_VALUE_MAX + 1];\n\n\tif (__system_property_get(\"ro.build.version.sdk\", prop))\n\t\tsdk_version = atoi(prop);\n\n\tif (argc == 2 && (!strcmp(argv[1], \"help\") || !strcmp(argv[1], \"--help\") || !strcmp(argv[1], \"-h\")))\n\t\tcmd_usage(argv[0]);\n\telse if (argc == 3 && !strcmp(argv[1], \"up\")) {\n\t\tauto_su(argc, argv);\n\t\tparse_options(&iface, &config, &mtu, &addrs, &dnses, &excluded_applications, &included_applications, argv[2]);\n\t\tcmd_up(iface, config, mtu, addrs, dnses, excluded_applications, included_applications);\n\t} else if (argc == 3 && !strcmp(argv[1], \"down\")) {\n\t\tauto_su(argc, argv);\n\t\tparse_options(&iface, &config, &mtu, &addrs, &dnses, &excluded_applications, &included_applications, argv[2]);\n\t\tcmd_down(iface);\n\t} else {\n\t\tcmd_usage(argv[0]);\n\t\treturn 1;\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "src/wg-quick/darwin.bash",
    "content": "#!/usr/bin/env bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n#\n\nset -e -o pipefail\nshopt -s extglob\nexport LC_ALL=C\n\nSELF=\"${BASH_SOURCE[0]}\"\n[[ $SELF == */* ]] || SELF=\"./$SELF\"\nSELF=\"$(cd \"${SELF%/*}\" && pwd -P)/${SELF##*/}\"\nexport PATH=\"/usr/bin:/bin:/usr/sbin:/sbin:${SELF%/*}:$PATH\"\n\nWG_CONFIG=\"\"\nINTERFACE=\"\"\nADDRESSES=( )\nMTU=\"\"\nDNS=( )\nDNS_SEARCH=( )\nTABLE=\"\"\nPRE_UP=( )\nPOST_UP=( )\nPRE_DOWN=( )\nPOST_DOWN=( )\nSAVE_CONFIG=0\nCONFIG_FILE=\"\"\nPROGRAM=\"${0##*/}\"\nARGS=( \"$@\" )\n\ncmd() {\n\techo \"[#] $*\" >&2\n\t\"$@\"\n}\n\ndie() {\n\techo \"$PROGRAM: $*\" >&2\n\texit 1\n}\n\n[[ ${BASH_VERSINFO[0]} -ge 4 ]] || die \"Version mismatch: bash ${BASH_VERSINFO[0]} detected, when bash 4+ required\"\n\nCONFIG_SEARCH_PATHS=( /etc/wireguard /usr/local/etc/wireguard )\n\nparse_options() {\n\tlocal interface_section=0 line key value stripped path v\n\tCONFIG_FILE=\"$1\"\n\tif [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then\n\t\tfor path in \"${CONFIG_SEARCH_PATHS[@]}\"; do\n\t\t\tCONFIG_FILE=\"$path/$1.conf\"\n\t\t\t[[ -e $CONFIG_FILE ]] && break\n\t\tdone\n\tfi\n\t[[ -e $CONFIG_FILE ]] || die \"\\`$CONFIG_FILE' does not exist\"\n\t[[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\\.conf$ ]] || die \"The config file must be a valid interface name, followed by .conf\"\n\tCONFIG_FILE=\"$(cd \"${CONFIG_FILE%/*}\" && pwd -P)/${CONFIG_FILE##*/}\"\n\t((($(stat -f '0%#p' \"$CONFIG_FILE\") & $(stat -f '0%#p' \"${CONFIG_FILE%/*}\") & 0007) == 0)) || echo \"Warning: \\`$CONFIG_FILE' is world accessible\" >&2\n\tINTERFACE=\"${BASH_REMATCH[2]}\"\n\tshopt -s nocasematch\n\twhile read -r line || [[ -n $line ]]; do\n\t\tstripped=\"${line%%\\#*}\"\n\t\tkey=\"${stripped%%=*}\"; key=\"${key##*([[:space:]])}\"; key=\"${key%%*([[:space:]])}\"\n\t\tvalue=\"${stripped#*=}\"; value=\"${value##*([[:space:]])}\"; value=\"${value%%*([[:space:]])}\"\n\t\tunstripped_value=\"${line#*=}\"; unstripped_value=\"${unstripped_value##*([[:space:]])}\"; unstripped_value=\"${unstripped_value%%*([[:space:]])}\"\n\t\t[[ $key == \"[\"* ]] && interface_section=0\n\t\t[[ $key == \"[Interface]\" ]] && interface_section=1\n\t\tif [[ $interface_section -eq 1 ]]; then\n\t\t\tcase \"$key\" in\n\t\t\tAddress) ADDRESSES+=( ${value//,/ } ); continue ;;\n\t\t\tMTU) MTU=\"$value\"; continue ;;\n\t\t\tDNS) for v in ${value//,/ }; do\n\t\t\t\t[[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )\n\t\t\tdone; continue ;;\n\t\t\tTable) TABLE=\"$value\"; continue ;;\n\t\t\tPreUp) PRE_UP+=( \"$unstripped_value\" ); continue ;;\n\t\t\tPreDown) PRE_DOWN+=( \"$unstripped_value\" ); continue ;;\n\t\t\tPostUp) POST_UP+=( \"$unstripped_value\" ); continue ;;\n\t\t\tPostDown) POST_DOWN+=( \"$unstripped_value\" ); continue ;;\n\t\t\tSaveConfig) read_bool SAVE_CONFIG \"$value\"; continue ;;\n\t\t\tesac\n\t\tfi\n\t\tWG_CONFIG+=\"$line\"$'\\n'\n\tdone < \"$CONFIG_FILE\"\n\tshopt -u nocasematch\n}\n\ndetect_launchd() {\n\tunset LAUNCHED_BY_LAUNCHD\n\tlocal line\n\twhile read -r line; do\n\t\tif [[ $line =~ ^\\s*domain\\ =\\  ]]; then\n\t\t\tLAUNCHED_BY_LAUNCHD=1\n\t\t\tbreak\n\t\tfi\n\tdone < <(launchctl procinfo $$ 2>/dev/null)\n}\n\nread_bool() {\n\tcase \"$2\" in\n\ttrue) printf -v \"$1\" 1 ;;\n\tfalse) printf -v \"$1\" 0 ;;\n\t*) die \"\\`$2' is neither true nor false\"\n\tesac\n}\n\nauto_su() {\n\t[[ $UID == 0 ]] || exec sudo -p \"$PROGRAM must be run as root. Please enter the password for %u to continue: \" -- \"$BASH\" -- \"$SELF\" \"${ARGS[@]}\"\n}\n\nget_real_interface() {\n\tlocal interface diff\n\twg show interfaces >/dev/null\n\t[[ -f \"/var/run/wireguard/$INTERFACE.name\" ]] || return 1\n\tinterface=\"$(< \"/var/run/wireguard/$INTERFACE.name\")\"\n\t[[ -n $interface && -S \"/var/run/wireguard/$interface.sock\" ]] || return 1\n\tdiff=$(( $(stat -f %m \"/var/run/wireguard/$interface.sock\" 2>/dev/null || echo 200) - $(stat -f %m \"/var/run/wireguard/$INTERFACE.name\" 2>/dev/null || echo 100) ))\n\t[[ $diff -ge 2 || $diff -le -2 ]] && return 1\n\tREAL_INTERFACE=\"$interface\"\n\techo \"[+] Interface for $INTERFACE is $REAL_INTERFACE\" >&2\n\treturn 0\n}\n\nadd_if() {\n\texport WG_TUN_NAME_FILE=\"/var/run/wireguard/$INTERFACE.name\"\n\tmkdir -p \"/var/run/wireguard/\"\n\tcmd \"${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}\" utun\n\tget_real_interface\n}\n\ndel_routes() {\n\t[[ -n $REAL_INTERFACE ]] || return 0\n\tlocal todelete=( ) destination gateway netif\n\twhile read -r destination _ _ _ _ netif _; do\n\t\t[[ $netif == \"$REAL_INTERFACE\" ]] && todelete+=( \"$destination\" )\n\tdone < <(netstat -nr -f inet)\n\tfor destination in \"${todelete[@]}\"; do\n\t\tcmd route -q -n delete -inet \"$destination\" >/dev/null || true\n\tdone\n\ttodelete=( )\n\twhile read -r destination gateway _ netif; do\n\t\t[[ $netif == \"$REAL_INTERFACE\" || ( $netif == lo* && $gateway == \"$REAL_INTERFACE\" ) ]] && todelete+=( \"$destination\" )\n\tdone < <(netstat -nr -f inet6)\n\tfor destination in \"${todelete[@]}\"; do\n\t\tcmd route -q -n delete -inet6 \"$destination\" >/dev/null || true\n\tdone\n\tfor destination in \"${ENDPOINTS[@]}\"; do\n\t\tif [[ $destination == *:* ]]; then\n\t\t\tcmd route -q -n delete -inet6 \"$destination\" >/dev/null || true\n\t\telse\n\t\t\tcmd route -q -n delete -inet \"$destination\" >/dev/null || true\n\t\tfi\n\tdone\n}\n\ndel_if() {\n\t[[ -z $REAL_INTERFACE ]] || cmd rm -f \"/var/run/wireguard/$REAL_INTERFACE.sock\"\n\tcmd rm -f \"/var/run/wireguard/$INTERFACE.name\"\n}\n\nup_if() {\n\tcmd ifconfig \"$REAL_INTERFACE\" up\n}\n\nadd_addr() {\n\tif [[ $1 == *:* ]]; then\n\t\tcmd ifconfig \"$REAL_INTERFACE\" inet6 \"$1\" alias\n\telse\n\t\tcmd ifconfig \"$REAL_INTERFACE\" inet \"$1\" \"${1%%/*}\" alias\n\tfi\n}\n\nset_mtu() {\n\t# TODO: use better set_mtu algorithm from freebsd.bash\n\tlocal mtu=0 current_mtu=-1 destination netif defaultif\n\tif [[ -n $MTU ]]; then\n\t\tcmd ifconfig \"$REAL_INTERFACE\" mtu \"$MTU\"\n\t\treturn\n\tfi\n\twhile read -r destination _ _ _ _ netif _; do\n\t\tif [[ $destination == default ]]; then\n\t\t\tdefaultif=\"$netif\"\n\t\t\tbreak\n\t\tfi\n\tdone < <(netstat -nr -f inet)\n\t[[ -n $defaultif && $(ifconfig \"$defaultif\") =~ mtu\\ ([0-9]+) ]] && mtu=\"${BASH_REMATCH[1]}\"\n\t[[ $mtu -gt 0 ]] || mtu=1500\n\tmtu=$(( mtu - 80 ))\n\t[[ $(ifconfig \"$REAL_INTERFACE\") =~ mtu\\ ([0-9]+) ]] && current_mtu=\"${BASH_REMATCH[1]}\"\n\t[[ $mtu -eq $current_mtu ]] || cmd ifconfig \"$REAL_INTERFACE\" mtu \"$mtu\"\n}\n\ncollect_gateways() {\n\tlocal destination gateway\n\n\tGATEWAY4=\"\"\n\twhile read -r destination gateway _; do\n\t\t[[ $destination == default && $gateway != \"link#\"* ]] || continue\n\t\tGATEWAY4=\"$gateway\"\n\t\tbreak\n\tdone < <(netstat -nr -f inet)\n\n\tGATEWAY6=\"\"\n\twhile read -r destination gateway _; do\n\t\t[[ $destination == default && $gateway != \"link#\"* ]] || continue\n\t\tGATEWAY6=\"$gateway\"\n\t\tbreak\n\tdone < <(netstat -nr -f inet6)\n}\n\ncollect_endpoints() {\n\tENDPOINTS=( )\n\twhile read -r _ endpoint; do\n\t\t[[ $endpoint =~ ^\\[?([a-z0-9:.]+)\\]?:[0-9]+$ ]] || continue\n\t\tENDPOINTS+=( \"${BASH_REMATCH[1]}\" )\n\tdone < <(wg show \"$REAL_INTERFACE\" endpoints)\n}\n\ndeclare -A SERVICE_DNS\ndeclare -A SERVICE_DNS_SEARCH\ncollect_new_service_dns() {\n\tlocal service get_response\n\tlocal -A found_services\n\t{ read -r _ && while read -r service; do\n\t\t[[ $service == \"*\"* ]] && service=\"${service:1}\"\n\t\tfound_services[\"$service\"]=1\n\t\t[[ -n ${SERVICE_DNS[\"$service\"]} ]] && continue\n\t\tget_response=\"$(cmd networksetup -getdnsservers \"$service\")\"\n\t\t[[ $get_response == *\" \"* ]] && get_response=\"Empty\"\n\t\t[[ -n $get_response ]] && SERVICE_DNS[\"$service\"]=\"$get_response\"\n\t\tget_response=\"$(cmd networksetup -getsearchdomains \"$service\")\"\n\t\t[[ $get_response == *\" \"* ]] && get_response=\"Empty\"\n\t\t[[ -n $get_response ]] && SERVICE_DNS_SEARCH[\"$service\"]=\"$get_response\"\n\tdone; } < <(networksetup -listallnetworkservices)\n\n\tfor service in \"${!SERVICE_DNS[@]}\"; do\n\t\tif ! [[ -n ${found_services[\"$service\"]} ]]; then\n\t\t\tunset SERVICE_DNS[\"$service\"]\n\t\t\tunset SERVICE_DNS_SEARCH[\"$service\"]\n\t\tfi\n\tdone\n}\n\nset_endpoint_direct_route() {\n\tlocal old_endpoints endpoint old_gateway4 old_gateway6 remove_all_old=0 added=( )\n\told_endpoints=( \"${ENDPOINTS[@]}\" )\n\told_gateway4=\"$GATEWAY4\"\n\told_gateway6=\"$GATEWAY6\"\n\tcollect_gateways\n\tcollect_endpoints\n\n\t[[ $old_gateway4 != \"$GATEWAY4\" || $old_gateway6 != \"$GATEWAY6\" ]] && remove_all_old=1\n\n\tif [[ $remove_all_old -eq 1 ]]; then\n\t\tfor endpoint in \"${ENDPOINTS[@]}\"; do\n\t\t\t[[ \" ${old_endpoints[*]} \" == *\" $endpoint \"* ]] || old_endpoints+=( \"$endpoint\" )\n\t\tdone\n\tfi\n\n\tfor endpoint in \"${old_endpoints[@]}\"; do\n\t\t[[ $remove_all_old -eq 0 && \" ${ENDPOINTS[*]} \" == *\" $endpoint \"* ]] && continue\n\t\tif [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then\n\t\t\tcmd route -q -n delete -inet6 \"$endpoint\" >/dev/null 2>&1 || true\n\t\telif [[ $AUTO_ROUTE4 -eq 1 ]]; then\n\t\t\tcmd route -q -n delete -inet \"$endpoint\" >/dev/null 2>&1 || true\n\t\tfi\n\tdone\n\n\tfor endpoint in \"${ENDPOINTS[@]}\"; do\n\t\tif [[ $remove_all_old -eq 0 && \" ${old_endpoints[*]} \" == *\" $endpoint \"* ]]; then\n\t\t\tadded+=( \"$endpoint\" )\n\t\t\tcontinue\n\t\tfi\n\t\tif [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then\n\t\t\tif [[ -n $GATEWAY6 ]]; then\n\t\t\t\tcmd route -q -n add -inet6 \"$endpoint\" -gateway \"$GATEWAY6\" >/dev/null || true\n\t\t\telse\n\t\t\t\t# Prevent routing loop\n\t\t\t\tcmd route -q -n add -inet6 \"$endpoint\" ::1 -blackhole >/dev/null || true\n\t\t\tfi\n\t\t\tadded+=( \"$endpoint\" )\n\t\telif [[ $AUTO_ROUTE4 -eq 1 ]]; then\n\t\t\tif [[ -n $GATEWAY4 ]]; then\n\t\t\t\tcmd route -q -n add -inet \"$endpoint\" -gateway \"$GATEWAY4\" >/dev/null || true\n\t\t\telse\n\t\t\t\t# Prevent routing loop\n\t\t\t\tcmd route -q -n add -inet \"$endpoint\" 127.0.0.1 -blackhole >/dev/null || true\n\t\t\tfi\n\t\t\tadded+=( \"$endpoint\" )\n\t\tfi\n\tdone\n\tENDPOINTS=( \"${added[@]}\" )\n}\n\nset_dns() {\n\tcollect_new_service_dns\n\tlocal service response\n\tfor service in \"${!SERVICE_DNS[@]}\"; do\n\t\twhile read -r response; do\n\t\t\t[[ $response == *Error* ]] && echo \"$response\" >&2\n\t\tdone < <(\n\t\t\tcmd networksetup -setdnsservers \"$service\" \"${DNS[@]}\"\n\t\t\tif [[ ${#DNS_SEARCH[@]} -eq 0 ]]; then\n\t\t\t\tcmd networksetup -setsearchdomains \"$service\" Empty\n\t\t\telse\n\t\t\t\tcmd networksetup -setsearchdomains \"$service\" \"${DNS_SEARCH[@]}\"\n\t\t\tfi\n\t\t)\n\tdone\n}\n\ndel_dns() {\n\tlocal service response\n\tfor service in \"${!SERVICE_DNS[@]}\"; do\n\t\twhile read -r response; do\n\t\t\t[[ $response == *Error* ]] && echo \"$response\" >&2\n\t\tdone < <(\n\t\t\tcmd networksetup -setdnsservers \"$service\" ${SERVICE_DNS[\"$service\"]} || true\n\t\t\tcmd networksetup -setsearchdomains \"$service\" ${SERVICE_DNS_SEARCH[\"$service\"]} || true\n\t\t)\n\tdone\n}\n\nmonitor_daemon() {\n\techo \"[+] Backgrounding route monitor\" >&2\n\t(trap 'del_routes; del_dns; exit 0' INT TERM EXIT\n\texec >/dev/null 2>&1\n\texec 19< <(exec route -n monitor)\n\tlocal event bpid=$BASHPID mpid=$!\n\t[[ ${#DNS[@]} -gt 0 ]] && trap set_dns ALRM\n\t# TODO: this should also check to see if the endpoint actually changes\n\t# in response to incoming packets, and then call set_endpoint_direct_route\n\t# then too. That function should be able to gracefully cleanup if the\n\t# endpoints change.\n\twhile read -u 19 -r event; do\n\t\t[[ $event == RTM_* ]] || continue\n\t\tifconfig \"$REAL_INTERFACE\" >/dev/null 2>&1 || break\n\t\t[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route\n\t\t[[ -z $MTU ]] && set_mtu\n\t\tif [[ ${#DNS[@]} -gt 0 ]]; then\n\t\t\tset_dns\n\t\t\tsleep 2 && kill -ALRM $bpid 2>/dev/null &\n\t\tfi\n\tdone\n\tkill $mpid) &\n\t[[ -n $LAUNCHED_BY_LAUNCHD ]] || disown\n}\n\nadd_route() {\n\t[[ $TABLE != off ]] || return 0\n\n\tlocal family=inet\n\t[[ $1 == *:* ]] && family=inet6\n\n\tif [[ $1 == */0 && ( -z $TABLE || $TABLE == auto ) ]]; then\n\t\tif [[ $1 == *:* ]]; then\n\t\t\tAUTO_ROUTE6=1\n\t\t\tcmd route -q -n add -inet6 ::/1 -interface \"$REAL_INTERFACE\" >/dev/null\n\t\t\tcmd route -q -n add -inet6 8000::/1 -interface \"$REAL_INTERFACE\" >/dev/null\n\t\telse\n\t\t\tAUTO_ROUTE4=1\n\t\t\tcmd route -q -n add -inet 0.0.0.0/1 -interface \"$REAL_INTERFACE\" >/dev/null\n\t\t\tcmd route -q -n add -inet 128.0.0.0/1 -interface \"$REAL_INTERFACE\" >/dev/null\n\t\tfi\n\telse\n\t\t[[ $TABLE == main || $TABLE == auto || -z $TABLE ]] || die \"Darwin only supports TABLE=auto|main|off\"\n\t\t[[ $(route -n get \"-$family\" \"$1\" 2>/dev/null) =~ interface:\\ $REAL_INTERFACE$'\\n' ]] || cmd route -q -n add -$family \"$1\" -interface \"$REAL_INTERFACE\" >/dev/null\n\n\tfi\n}\n\nset_config() {\n\tcmd wg addconf \"$REAL_INTERFACE\" <(echo \"$WG_CONFIG\")\n}\n\nsave_config() {\n\tlocal old_umask new_config current_config address cmd\n\tnew_config=$'[Interface]\\n'\n\twhile read -r address; do\n\t\t[[ $address =~ inet6?\\ ([^ ]+) ]] && new_config+=\"Address = ${BASH_REMATCH[1]}\"$'\\n'\n\tdone < <(ifconfig \"$REAL_INTERFACE\")\n\t# TODO: actually determine current DNS for interface\n\tfor address in \"${DNS[@]}\"; do\n\t\tnew_config+=\"DNS = $address\"$'\\n'\n\tdone\n\t[[ -n $MTU ]] && new_config+=\"MTU = $MTU\"$'\\n'\n\t[[ -n $TABLE ]] && new_config+=\"Table = $TABLE\"$'\\n'\n\t[[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\\n'\n\tfor cmd in \"${PRE_UP[@]}\"; do\n\t\tnew_config+=\"PreUp = $cmd\"$'\\n'\n\tdone\n\tfor cmd in \"${POST_UP[@]}\"; do\n\t\tnew_config+=\"PostUp = $cmd\"$'\\n'\n\tdone\n\tfor cmd in \"${PRE_DOWN[@]}\"; do\n\t\tnew_config+=\"PreDown = $cmd\"$'\\n'\n\tdone\n\tfor cmd in \"${POST_DOWN[@]}\"; do\n\t\tnew_config+=\"PostDown = $cmd\"$'\\n'\n\tdone\n\told_umask=\"$(umask)\"\n\tumask 077\n\tcurrent_config=\"$(cmd wg showconf \"$REAL_INTERFACE\")\"\n\ttrap 'rm -f \"$CONFIG_FILE.tmp\"; exit' INT TERM EXIT\n\techo \"${current_config/\\[Interface\\]$'\\n'/$new_config}\" > \"$CONFIG_FILE.tmp\" || die \"Could not write configuration file\"\n\tsync \"$CONFIG_FILE.tmp\"\n\tmv \"$CONFIG_FILE.tmp\" \"$CONFIG_FILE\" || die \"Could not move configuration file\"\n\ttrap - INT TERM EXIT\n\tumask \"$old_umask\"\n}\n\nexecute_hooks() {\n\tlocal hook\n\tfor hook in \"$@\"; do\n\t\thook=\"${hook//%i/$REAL_INTERFACE}\"\n\t\thook=\"${hook//%I/$INTERFACE}\"\n\t\techo \"[#] $hook\" >&2\n\t\t(eval \"$hook\")\n\tdone\n}\n\ncmd_usage() {\n\tcat >&2 <<-_EOF\n\tUsage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]\n\n\t  CONFIG_FILE is a configuration file, whose filename is the interface name\n\t  followed by \\`.conf'. Otherwise, INTERFACE is an interface name, with\n\t  configuration found at:\n\t  ${CONFIG_SEARCH_PATHS[@]/%//INTERFACE.conf}.\n\t  It is to be readable by wg(8)'s \\`setconf' sub-command, with the exception\n\t  of the following additions to the [Interface] section, which are handled\n\t  by $PROGRAM:\n\n\t  - Address: may be specified one or more times and contains one or more\n\t    IP addresses (with an optional CIDR mask) to be set for the interface.\n\t  - DNS: an optional DNS server to use while the device is up.\n\t  - MTU: an optional MTU for the interface; if unspecified, auto-calculated.\n\t  - Table: an optional routing table to which routes will be added; if\n\t    unspecified or \\`auto', the default table is used. If \\`off', no routes\n\t    are added. Besides \\`auto' and \\`off', only \\`main' is supported on\n\t    this platform.\n\t  - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed\n\t    by bash(1) at the corresponding phases of the link, most commonly used\n\t    to configure DNS. The string \\`%i' is expanded to INTERFACE.\n\t  - SaveConfig: if set to \\`true', the configuration is saved from the current\n\t    state of the interface upon shutdown.\n\n\tSee wg-quick(8) for more info and examples.\n\t_EOF\n}\n\ncmd_up() {\n\tlocal i\n\tget_real_interface && die \"\\`$INTERFACE' already exists as \\`$REAL_INTERFACE'\"\n\ttrap 'del_if; del_routes; exit' INT TERM EXIT\n\tadd_if\n\texecute_hooks \"${PRE_UP[@]}\"\n\tset_config\n\tfor i in \"${ADDRESSES[@]}\"; do\n\t\tadd_addr \"$i\"\n\tdone\n\tset_mtu\n\tup_if\n\tfor i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo \"$i\"; done; done < <(wg show \"$REAL_INTERFACE\" allowed-ips) | sort -nr -k 2 -t /); do\n\t\tadd_route \"$i\"\n\tdone\n\t[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route\n\t[[ ${#DNS[@]} -gt 0 ]] && set_dns\n\tmonitor_daemon\n\texecute_hooks \"${POST_UP[@]}\"\n\ttrap - INT TERM EXIT\n}\n\ncmd_down() {\n\tif ! get_real_interface || [[ \" $(wg show interfaces) \" != *\" $REAL_INTERFACE \"* ]]; then\n\t\tdie \"\\`$INTERFACE' is not a WireGuard interface\"\n\tfi\n\texecute_hooks \"${PRE_DOWN[@]}\"\n\t[[ $SAVE_CONFIG -eq 0 ]] || save_config\n\tdel_if\n\texecute_hooks \"${POST_DOWN[@]}\"\n}\n\ncmd_save() {\n\tif ! get_real_interface || [[ \" $(wg show interfaces) \" != *\" $REAL_INTERFACE \"* ]]; then\n\t\tdie \"\\`$INTERFACE' is not a WireGuard interface\"\n\tfi\n\tsave_config\n}\n\ncmd_strip() {\n\techo \"$WG_CONFIG\"\n}\n\n# ~~ function override insertion point ~~\n\nif [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then\n\tcmd_usage\nelif [[ $# -eq 2 && $1 == up ]]; then\n\tauto_su\n\tdetect_launchd\n\tparse_options \"$2\"\n\tcmd_up\nelif [[ $# -eq 2 && $1 == down ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_down\nelif [[ $# -eq 2 && $1 == save ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_save\nelif [[ $# -eq 2 && $1 == strip ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_strip\nelse\n\tcmd_usage\n\texit 1\nfi\n\n[[ -n $LAUNCHED_BY_LAUNCHD ]] && wait\n\nexit 0\n"
  },
  {
    "path": "src/wg-quick/freebsd.bash",
    "content": "#!/usr/local/bin/bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n#\n\nset -e -o pipefail\nshopt -s extglob\nexport LC_ALL=C\n\nexec 3>&2\nSELF=\"$(readlink -f \"${BASH_SOURCE[0]}\")\"\nexport PATH=\"${SELF%/*}:$PATH\"\n\nWG_CONFIG=\"\"\nINTERFACE=\"\"\nADDRESSES=( )\nMTU=\"\"\nDNS=( )\nDNS_SEARCH=( )\nTABLE=\"\"\nPRE_UP=( )\nPOST_UP=( )\nPRE_DOWN=( )\nPOST_DOWN=( )\nSAVE_CONFIG=0\nCONFIG_FILE=\"\"\nPROGRAM=\"${0##*/}\"\nARGS=( \"$@\" )\n\ncmd() {\n\techo \"[#] $*\" >&3\n\t\"$@\"\n}\n\ndie() {\n\techo \"$PROGRAM: $*\" >&2\n\texit 1\n}\n\nCONFIG_SEARCH_PATHS=( /etc/wireguard /usr/local/etc/wireguard )\n\nunset ORIGINAL_TMPDIR\nmake_temp() {\n\tlocal old_umask\n\n\t[[ -v ORIGINAL_TMPDIR ]] && export TMPDIR=\"$ORIGINAL_TMPDIR\"\n\tORIGINAL_TMPDIR=\"$TMPDIR\"\n\t[[ -z $TMPDIR ]] && unset TMPDIR\n\n\told_umask=\"$(umask)\"\n\tumask 077\n\texport TMPDIR=\"$(mktemp -d)\"\n\tumask \"$old_umask\"\n\n\t[[ -d $TMPDIR ]] || die \"Unable to create safe temporary directory\"\n\tCLEANUP_TMPDIR=\"$TMPDIR\"\n}\n\nclean_temp() {\n\t[[ -n $CLEANUP_TMPDIR ]] && rm -rf \"$CLEANUP_TMPDIR\"\n}\n\nparse_options() {\n\tlocal interface_section=0 line key value stripped path v\n\tCONFIG_FILE=\"$1\"\n\tif [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then\n\t\tfor path in \"${CONFIG_SEARCH_PATHS[@]}\"; do\n\t\t\tCONFIG_FILE=\"$path/$1.conf\"\n\t\t\t[[ -e $CONFIG_FILE ]] && break\n\t\tdone\n\tfi\n\t[[ -e $CONFIG_FILE ]] || die \"\\`$CONFIG_FILE' does not exist\"\n\t[[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\\.conf$ ]] || die \"The config file must be a valid interface name, followed by .conf\"\n\tCONFIG_FILE=\"$(readlink -f \"$CONFIG_FILE\")\"\n\t((($(stat -f '0%#p' \"$CONFIG_FILE\") & $(stat -f '0%#p' \"${CONFIG_FILE%/*}\") & 0007) == 0)) || echo \"Warning: \\`$CONFIG_FILE' is world accessible\" >&2\n\tINTERFACE=\"${BASH_REMATCH[2]}\"\n\tshopt -s nocasematch\n\twhile read -r line || [[ -n $line ]]; do\n\t\tstripped=\"${line%%\\#*}\"\n\t\tkey=\"${stripped%%=*}\"; key=\"${key##*([[:space:]])}\"; key=\"${key%%*([[:space:]])}\"\n\t\tvalue=\"${stripped#*=}\"; value=\"${value##*([[:space:]])}\"; value=\"${value%%*([[:space:]])}\"\n\t\tunstripped_value=\"${line#*=}\"; unstripped_value=\"${unstripped_value##*([[:space:]])}\"; unstripped_value=\"${unstripped_value%%*([[:space:]])}\"\n\t\t[[ $key == \"[\"* ]] && interface_section=0\n\t\t[[ $key == \"[Interface]\" ]] && interface_section=1\n\t\tif [[ $interface_section -eq 1 ]]; then\n\t\t\tcase \"$key\" in\n\t\t\tAddress) ADDRESSES+=( ${value//,/ } ); continue ;;\n\t\t\tMTU) MTU=\"$value\"; continue ;;\n\t\t\tDNS) for v in ${value//,/ }; do\n\t\t\t\t[[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )\n\t\t\tdone; continue ;;\n\t\t\tTable) TABLE=\"$value\"; continue ;;\n\t\t\tPreUp) PRE_UP+=( \"$unstripped_value\" ); continue ;;\n\t\t\tPreDown) PRE_DOWN+=( \"$unstripped_value\" ); continue ;;\n\t\t\tPostUp) POST_UP+=( \"$unstripped_value\" ); continue ;;\n\t\t\tPostDown) POST_DOWN+=( \"$unstripped_value\" ); continue ;;\n\t\t\tSaveConfig) read_bool SAVE_CONFIG \"$value\"; continue ;;\n\t\t\tesac\n\t\tfi\n\t\tWG_CONFIG+=\"$line\"$'\\n'\n\tdone < \"$CONFIG_FILE\"\n\tshopt -u nocasematch\n}\n\nread_bool() {\n\tcase \"$2\" in\n\ttrue) printf -v \"$1\" 1 ;;\n\tfalse) printf -v \"$1\" 0 ;;\n\t*) die \"\\`$2' is neither true nor false\"\n\tesac\n}\n\nauto_su() {\n\t[[ $UID == 0 ]] || exec sudo -p \"$PROGRAM must be run as root. Please enter the password for %u to continue: \" -- \"$BASH\" -- \"$SELF\" \"${ARGS[@]}\"\n}\n\nadd_if() {\n\tlocal ret rc\n\tif ret=\"$(cmd ifconfig wg create name \"$INTERFACE\" 2>&1 >/dev/null)\"; then\n\t\treturn 0\n\tfi\n\trc=$?\n\tif [[ $ret == *\"ifconfig: ioctl SIOCSIFNAME (set name): File exists\"* ]]; then\n\t\techo \"$ret\" >&3\n\t\treturn $rc\n\tfi\n\techo \"[!] Missing WireGuard kernel support ($ret). Falling back to slow userspace implementation.\" >&3\n\tcmd \"${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}\" \"$INTERFACE\"\n}\n\ndel_routes() {\n\tlocal todelete=( ) destination gateway netif\n\twhile read -r destination _ _ _ _ netif _; do\n\t\t[[ $netif == \"$INTERFACE\" ]] && todelete+=( \"$destination\" )\n\tdone < <(netstat -nr -f inet)\n\tfor destination in \"${todelete[@]}\"; do\n\t\tcmd route -q -n delete -inet \"$destination\" || true\n\tdone\n\ttodelete=( )\n\twhile read -r destination gateway _ netif; do\n\t\t[[ $netif == \"$INTERFACE\" || ( $netif == lo* && $gateway == \"$INTERFACE\" ) ]] && todelete+=( \"$destination\" )\n\tdone < <(netstat -nr -f inet6)\n\tfor destination in \"${todelete[@]}\"; do\n\t\tcmd route -q -n delete -inet6 \"$destination\" || true\n\tdone\n\tfor destination in \"${ENDPOINTS[@]}\"; do\n\t\tif [[ $destination == *:* ]]; then\n\t\t\tcmd route -q -n delete -inet6 \"$destination\" || true\n\t\telse\n\t\t\tcmd route -q -n delete -inet \"$destination\" || true\n\t\tfi\n\tdone\n}\n\ndel_if() {\n\t[[ $HAVE_SET_DNS -eq 0 ]] || unset_dns\n\tif [[ -S /var/run/wireguard/$INTERFACE.sock ]]; then\n\t\tcmd rm -f \"/var/run/wireguard/$INTERFACE.sock\"\n\telse\n\t\tcmd ifconfig \"$INTERFACE\" destroy\n\tfi\n\twhile ifconfig \"$INTERFACE\" >/dev/null 2>&1; do\n\t\t# HACK: it would be nice to `route monitor` here and wait for RTM_IFANNOUNCE\n\t\t# but it turns out that the announcement is made before the interface\n\t\t# disappears so we sometimes get a hang. So, we're instead left with polling\n\t\t# in a sleep loop like this.\n\t\tsleep 0.1\n\tdone\n}\n\nup_if() {\n\tcmd ifconfig \"$INTERFACE\" up\n}\n\nadd_addr() {\n\tif [[ $1 == *:* ]]; then\n\t\tcmd ifconfig \"$INTERFACE\" inet6 \"$1\" alias\n\telse\n\t\tcmd ifconfig \"$INTERFACE\" inet \"$1\" alias\n\tfi\n}\n\nset_mtu() {\n\tlocal mtu=0 endpoint output family\n\tif [[ -n $MTU ]]; then\n\t\tcmd ifconfig \"$INTERFACE\" mtu \"$MTU\"\n\t\treturn\n\tfi\n\twhile read -r _ endpoint; do\n\t\t[[ $endpoint =~ ^\\[?([a-z0-9:.]+)\\]?:[0-9]+$ ]] || continue\n\t\tfamily=inet\n\t\t[[ ${BASH_REMATCH[1]} == *:* ]] && family=inet6\n\t\toutput=\"$(route -n get \"-$family\" \"${BASH_REMATCH[1]}\" || true)\"\n\t\t[[ $output =~ interface:\\ ([^ ]+)$'\\n' && $(ifconfig \"${BASH_REMATCH[1]}\") =~ mtu\\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu=\"${BASH_REMATCH[1]}\"\n\tdone < <(wg show \"$INTERFACE\" endpoints)\n\tif [[ $mtu -eq 0 ]]; then\n\t\tread -r output < <(route -n get default || true) || true\n\t\t[[ $output =~ interface:\\ ([^ ]+)$'\\n' && $(ifconfig \"${BASH_REMATCH[1]}\") =~ mtu\\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu=\"${BASH_REMATCH[1]}\"\n\tfi\n\t[[ $mtu -gt 0 ]] || mtu=1500\n\tcmd ifconfig \"$INTERFACE\" mtu $(( mtu - 80 ))\n}\n\n\ncollect_gateways() {\n\tlocal destination gateway\n\n\tGATEWAY4=\"\"\n\twhile read -r destination gateway _; do\n\t\t[[ $destination == default ]] || continue\n\t\tGATEWAY4=\"$gateway\"\n\t\tbreak\n\tdone < <(netstat -nr -f inet)\n\n\tGATEWAY6=\"\"\n\twhile read -r destination gateway _; do\n\t\t[[ $destination == default ]] || continue\n\t\tGATEWAY6=\"$gateway\"\n\t\tbreak\n\tdone < <(netstat -nr -f inet6)\n}\n\ncollect_endpoints() {\n\tENDPOINTS=( )\n\twhile read -r _ endpoint; do\n\t\t[[ $endpoint =~ ^\\[?([a-z0-9:.]+)\\]?:[0-9]+$ ]] || continue\n\t\tENDPOINTS+=( \"${BASH_REMATCH[1]}\" )\n\tdone < <(wg show \"$INTERFACE\" endpoints)\n}\n\nset_endpoint_direct_route() {\n\tlocal old_endpoints endpoint old_gateway4 old_gateway6 remove_all_old=0 added=( )\n\told_endpoints=( \"${ENDPOINTS[@]}\" )\n\told_gateway4=\"$GATEWAY4\"\n\told_gateway6=\"$GATEWAY6\"\n\tcollect_gateways\n\tcollect_endpoints\n\n\t[[ $old_gateway4 != \"$GATEWAY4\" || $old_gateway6 != \"$GATEWAY6\" ]] && remove_all_old=1\n\n\tif [[ $remove_all_old -eq 1 ]]; then\n\t\tfor endpoint in \"${ENDPOINTS[@]}\"; do\n\t\t\t[[ \" ${old_endpoints[*]} \" == *\" $endpoint \"* ]] || old_endpoints+=( \"$endpoint\" )\n\t\tdone\n\tfi\n\n\tfor endpoint in \"${old_endpoints[@]}\"; do\n\t\t[[ $remove_all_old -eq 0 && \" ${ENDPOINTS[*]} \" == *\" $endpoint \"* ]] && continue\n\t\tif [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then\n\t\t\tcmd route -q -n delete -inet6 \"$endpoint\" 2>/dev/null || true\n\t\telif [[ $AUTO_ROUTE4 -eq 1 ]]; then\n\t\t\tcmd route -q -n delete -inet \"$endpoint\" 2>/dev/null || true\n\t\tfi\n\tdone\n\n\tfor endpoint in \"${ENDPOINTS[@]}\"; do\n\t\tif [[ $remove_all_old -eq 0 && \" ${old_endpoints[*]} \" == *\" $endpoint \"* ]]; then\n\t\t\tadded+=( \"$endpoint\" )\n\t\t\tcontinue\n\t\tfi\n\t\tif [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then\n\t\t\tif [[ -n $GATEWAY6 ]]; then\n\t\t\t\tcmd route -q -n add -inet6 \"$endpoint\" -gateway \"$GATEWAY6\" || true\n\t\t\telse\n\t\t\t\t# Prevent routing loop\n\t\t\t\tcmd route -q -n add -inet6 \"$endpoint\" ::1 -blackhole || true\n\t\t\tfi\n\t\t\tadded+=( \"$endpoint\" )\n\t\telif [[ $AUTO_ROUTE4 -eq 1 ]]; then\n\t\t\tif [[ -n $GATEWAY4 ]]; then\n\t\t\t\tcmd route -q -n add -inet \"$endpoint\" -gateway \"$GATEWAY4\" || true\n\t\t\telse\n\t\t\t\t# Prevent routing loop\n\t\t\t\tcmd route -q -n add -inet \"$endpoint\" 127.0.0.1 -blackhole || true\n\t\t\tfi\n\t\t\tadded+=( \"$endpoint\" )\n\t\tfi\n\tdone\n\tENDPOINTS=( \"${added[@]}\" )\n}\n\nmonitor_daemon() {\n\techo \"[+] Backgrounding route monitor\" >&2\n\t(make_temp\n\ttrap 'del_routes; clean_temp; exit 0' INT TERM EXIT\n\texec >/dev/null 2>&1\n\texec 19< <(exec route -n monitor)\n\tlocal event pid=$!\n\t# TODO: this should also check to see if the endpoint actually changes\n\t# in response to incoming packets, and then call set_endpoint_direct_route\n\t# then too. That function should be able to gracefully cleanup if the\n\t# endpoints change.\n\twhile read -u 19 -r event; do\n\t\t[[ $event == RTM_* ]] || continue\n\t\tifconfig \"$INTERFACE\" >/dev/null 2>&1 || break\n\t\t[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route\n\t\t# TODO: set the mtu as well, but only if up\n\tdone\n\tkill $pid) & disown\n}\n\nHAVE_SET_DNS=0\nset_dns() {\n\t[[ ${#DNS[@]} -gt 0 ]] || return 0\n\t{ printf 'nameserver %s\\n' \"${DNS[@]}\"\n\t  [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\\n' \"${DNS_SEARCH[*]}\"\n\t} | cmd resolvconf -a \"$INTERFACE\" -x\n\tHAVE_SET_DNS=1\n}\n\nunset_dns() {\n\t[[ ${#DNS[@]} -gt 0 ]] || return 0\n\tcmd resolvconf -d \"$INTERFACE\"\n}\n\nadd_route() {\n\t[[ $TABLE != off ]] || return 0\n\n\tlocal family=inet\n\t[[ $1 == *:* ]] && family=inet6\n\n\tif [[ -n $TABLE && $TABLE != auto ]]; then\n\t\tcmd route -q -n add \"-$family\" -fib \"$TABLE\" \"$1\" -interface \"$INTERFACE\"\n\telif [[ $1 == */0 ]]; then\n\t\tif [[ $1 == *:* ]]; then\n\t\t\tAUTO_ROUTE6=1\n\t\t\tcmd route -q -n add -inet6 ::/1 -interface \"$INTERFACE\"\n\t\t\tcmd route -q -n add -inet6 8000::/1 -interface \"$INTERFACE\"\n\t\telse\n\t\t\tAUTO_ROUTE4=1\n\t\t\tcmd route -q -n add -inet 0.0.0.0/1 -interface \"$INTERFACE\"\n\t\t\tcmd route -q -n add -inet 128.0.0.0/1 -interface \"$INTERFACE\"\n\t\tfi\n\telse\n\t\t[[ $(route -n get \"-$family\" \"$1\" 2>/dev/null) =~ interface:\\ $INTERFACE$'\\n' ]] || cmd route -q -n add \"-$family\" \"$1\" -interface \"$INTERFACE\"\n\tfi\n}\n\nset_config() {\n\techo \"$WG_CONFIG\" | cmd wg addconf \"$INTERFACE\" /dev/stdin\n}\n\nsave_config() {\n\tlocal old_umask new_config current_config address cmd\n\tnew_config=$'[Interface]\\n'\n\t{ read -r _; while read -r _ _ _ address _; do\n\t\tnew_config+=\"Address = $address\"$'\\n'\n\tdone } < <(netstat -I \"$INTERFACE\" -n -W -f inet)\n\t{ read -r _; while read -r _ _ _ address _; do\n\t\tnew_config+=\"Address = $address\"$'\\n'\n\tdone } < <(netstat -I \"$INTERFACE\" -n -W -f inet6)\n\twhile read -r address; do\n\t\t[[ $address =~ ^nameserver\\ ([a-zA-Z0-9_=+:%.-]+)$ ]] && new_config+=\"DNS = ${BASH_REMATCH[1]}\"$'\\n'\n\tdone < <(resolvconf -l \"$INTERFACE\" 2>/dev/null)\n\t[[ -n $MTU ]] && new_config+=\"MTU = $MTU\"$'\\n'\n\t[[ -n $TABLE ]] && new_config+=\"Table = $TABLE\"$'\\n'\n\t[[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\\n'\n\tfor cmd in \"${PRE_UP[@]}\"; do\n\t\tnew_config+=\"PreUp = $cmd\"$'\\n'\n\tdone\n\tfor cmd in \"${POST_UP[@]}\"; do\n\t\tnew_config+=\"PostUp = $cmd\"$'\\n'\n\tdone\n\tfor cmd in \"${PRE_DOWN[@]}\"; do\n\t\tnew_config+=\"PreDown = $cmd\"$'\\n'\n\tdone\n\tfor cmd in \"${POST_DOWN[@]}\"; do\n\t\tnew_config+=\"PostDown = $cmd\"$'\\n'\n\tdone\n\told_umask=\"$(umask)\"\n\tumask 077\n\tcurrent_config=\"$(cmd wg showconf \"$INTERFACE\")\"\n\ttrap 'rm -f \"$CONFIG_FILE.tmp\"; clean_temp; exit' INT TERM EXIT\n\techo \"${current_config/\\[Interface\\]$'\\n'/$new_config}\" > \"$CONFIG_FILE.tmp\" || die \"Could not write configuration file\"\n\tsync \"$CONFIG_FILE.tmp\"\n\tmv \"$CONFIG_FILE.tmp\" \"$CONFIG_FILE\" || die \"Could not move configuration file\"\n\ttrap 'clean_temp; exit' INT TERM EXIT\n\tumask \"$old_umask\"\n}\n\nexecute_hooks() {\n\tlocal hook\n\tfor hook in \"$@\"; do\n\t\thook=\"${hook//%i/$INTERFACE}\"\n\t\techo \"[#] $hook\" >&2\n\t\t(eval \"$hook\")\n\tdone\n}\n\ncmd_usage() {\n\tcat >&2 <<-_EOF\n\tUsage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]\n\n\t  CONFIG_FILE is a configuration file, whose filename is the interface name\n\t  followed by \\`.conf'. Otherwise, INTERFACE is an interface name, with\n\t  configuration found at:\n\t  ${CONFIG_SEARCH_PATHS[@]/%//INTERFACE.conf}.\n\t  It is to be readable by wg(8)'s \\`setconf' sub-command, with the exception\n\t  of the following additions to the [Interface] section, which are handled\n\t  by $PROGRAM:\n\n\t  - Address: may be specified one or more times and contains one or more\n\t    IP addresses (with an optional CIDR mask) to be set for the interface.\n\t  - DNS: an optional DNS server to use while the device is up.\n\t  - MTU: an optional MTU for the interface; if unspecified, auto-calculated.\n\t  - Table: an optional routing table to which routes will be added; if\n\t    unspecified or \\`auto', the default table is used. If \\`off', no routes\n\t    are added.\n\t  - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed\n\t    by bash(1) at the corresponding phases of the link, most commonly used\n\t    to configure DNS. The string \\`%i' is expanded to INTERFACE.\n\t  - SaveConfig: if set to \\`true', the configuration is saved from the current\n\t    state of the interface upon shutdown.\n\n\tSee wg-quick(8) for more info and examples.\n\t_EOF\n}\n\ncmd_up() {\n\tlocal i\n\t[[ -z $(ifconfig \"$INTERFACE\" 2>/dev/null) ]] || die \"\\`$INTERFACE' already exists\"\n\ttrap 'del_if; del_routes; clean_temp; exit' INT TERM EXIT\n\tadd_if\n\texecute_hooks \"${PRE_UP[@]}\"\n\tset_config\n\tfor i in \"${ADDRESSES[@]}\"; do\n\t\tadd_addr \"$i\"\n\tdone\n\tset_mtu\n\tup_if\n\tset_dns\n\tfor i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo \"$i\"; done; done < <(wg show \"$INTERFACE\" allowed-ips) | sort -nr -k 2 -t /); do\n\t\tadd_route \"$i\"\n\tdone\n\t[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route\n\tmonitor_daemon\n\texecute_hooks \"${POST_UP[@]}\"\n\ttrap 'clean_temp; exit' INT TERM EXIT\n}\n\ncmd_down() {\n\t[[ \" $(wg show interfaces) \" == *\" $INTERFACE \"* ]] || die \"\\`$INTERFACE' is not a WireGuard interface\"\n\texecute_hooks \"${PRE_DOWN[@]}\"\n\t[[ $SAVE_CONFIG -eq 0 ]] || save_config\n\tdel_if\n\tunset_dns\n\texecute_hooks \"${POST_DOWN[@]}\"\n}\n\ncmd_save() {\n\t[[ \" $(wg show interfaces) \" == *\" $INTERFACE \"* ]] || die \"\\`$INTERFACE' is not a WireGuard interface\"\n\tsave_config\n}\n\ncmd_strip() {\n\techo \"$WG_CONFIG\"\n}\n\n# ~~ function override insertion point ~~\n\nmake_temp\ntrap 'clean_temp; exit' INT TERM EXIT\n\nif [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then\n\tcmd_usage\nelif [[ $# -eq 2 && $1 == up ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_up\nelif [[ $# -eq 2 && $1 == down ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_down\nelif [[ $# -eq 2 && $1 == save ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_save\nelif [[ $# -eq 2 && $1 == strip ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_strip\nelse\n\tcmd_usage\n\texit 1\nfi\n\nexit 0\n"
  },
  {
    "path": "src/wg-quick/linux.bash",
    "content": "#!/bin/bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n#\n\nset -e -o pipefail\nshopt -s extglob\nexport LC_ALL=C\n\nSELF=\"$(readlink -f \"${BASH_SOURCE[0]}\")\"\nexport PATH=\"${SELF%/*}:$PATH\"\n\nWG_CONFIG=\"\"\nINTERFACE=\"\"\nADDRESSES=( )\nMTU=\"\"\nDNS=( )\nDNS_SEARCH=( )\nTABLE=\"\"\nPRE_UP=( )\nPOST_UP=( )\nPRE_DOWN=( )\nPOST_DOWN=( )\nSAVE_CONFIG=0\nCONFIG_FILE=\"\"\nPROGRAM=\"${0##*/}\"\nARGS=( \"$@\" )\n\ncmd() {\n\techo \"[#] $*\" >&2\n\t\"$@\"\n}\n\ndie() {\n\techo \"$PROGRAM: $*\" >&2\n\texit 1\n}\n\nparse_options() {\n\tlocal interface_section=0 line key value stripped v\n\tCONFIG_FILE=\"$1\"\n\t[[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE=\"/etc/wireguard/$CONFIG_FILE.conf\"\n\t[[ -e $CONFIG_FILE ]] || die \"\\`$CONFIG_FILE' does not exist\"\n\t[[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\\.conf$ ]] || die \"The config file must be a valid interface name, followed by .conf\"\n\tCONFIG_FILE=\"$(readlink -f \"$CONFIG_FILE\")\"\n\t((($(stat -c '0%#a' \"$CONFIG_FILE\") & $(stat -c '0%#a' \"${CONFIG_FILE%/*}\") & 0007) == 0)) || echo \"Warning: \\`$CONFIG_FILE' is world accessible\" >&2\n\tINTERFACE=\"${BASH_REMATCH[2]}\"\n\tshopt -s nocasematch\n\twhile read -r line || [[ -n $line ]]; do\n\t\tstripped=\"${line%%\\#*}\"\n\t\tkey=\"${stripped%%=*}\"; key=\"${key##*([[:space:]])}\"; key=\"${key%%*([[:space:]])}\"\n\t\tvalue=\"${stripped#*=}\"; value=\"${value##*([[:space:]])}\"; value=\"${value%%*([[:space:]])}\"\n\t\tunstripped_value=\"${line#*=}\"; unstripped_value=\"${unstripped_value##*([[:space:]])}\"; unstripped_value=\"${unstripped_value%%*([[:space:]])}\"\n\t\t[[ $key == \"[\"* ]] && interface_section=0\n\t\t[[ $key == \"[Interface]\" ]] && interface_section=1\n\t\tif [[ $interface_section -eq 1 ]]; then\n\t\t\tcase \"$key\" in\n\t\t\tAddress) ADDRESSES+=( ${value//,/ } ); continue ;;\n\t\t\tMTU) MTU=\"$value\"; continue ;;\n\t\t\tDNS) for v in ${value//,/ }; do\n\t\t\t\t[[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )\n\t\t\tdone; continue ;;\n\t\t\tTable) TABLE=\"$value\"; continue ;;\n\t\t\tPreUp) PRE_UP+=( \"$unstripped_value\" ); continue ;;\n\t\t\tPreDown) PRE_DOWN+=( \"$unstripped_value\" ); continue ;;\n\t\t\tPostUp) POST_UP+=( \"$unstripped_value\" ); continue ;;\n\t\t\tPostDown) POST_DOWN+=( \"$unstripped_value\" ); continue ;;\n\t\t\tSaveConfig) read_bool SAVE_CONFIG \"$value\"; continue ;;\n\t\t\tesac\n\t\tfi\n\t\tWG_CONFIG+=\"$line\"$'\\n'\n\tdone < \"$CONFIG_FILE\"\n\tshopt -u nocasematch\n}\n\nread_bool() {\n\tcase \"$2\" in\n\ttrue) printf -v \"$1\" 1 ;;\n\tfalse) printf -v \"$1\" 0 ;;\n\t*) die \"\\`$2' is neither true nor false\"\n\tesac\n}\n\nauto_su() {\n\t[[ $UID == 0 ]] || exec sudo -p \"$PROGRAM must be run as root. Please enter the password for %u to continue: \" -- \"$BASH\" -- \"$SELF\" \"${ARGS[@]}\"\n}\n\nadd_if() {\n\tlocal ret\n\tif ! cmd ip link add dev \"$INTERFACE\" type wireguard; then\n\t\tret=$?\n\t\t[[ -e /sys/module/wireguard ]] || ! command -v \"${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}\" >/dev/null && exit $ret\n\t\techo \"[!] Missing WireGuard kernel module. Falling back to slow userspace implementation.\" >&2\n\t\tcmd \"${WG_QUICK_USERSPACE_IMPLEMENTATION:-wireguard-go}\" \"$INTERFACE\"\n\tfi\n}\n\ndel_if() {\n\tlocal table\n\t[[ $HAVE_SET_DNS -eq 0 ]] || unset_dns\n\t[[ $HAVE_SET_FIREWALL -eq 0 ]] || remove_firewall\n\tif [[ -z $TABLE || $TABLE == auto ]] && get_fwmark table && [[ $(wg show \"$INTERFACE\" allowed-ips) =~ /0(\\ |$'\\n'|$) ]]; then\n\t\twhile [[ $(ip -4 rule show 2>/dev/null) == *\"lookup $table\"* ]]; do\n\t\t\tcmd ip -4 rule delete table $table\n\t\tdone\n\t\twhile [[ $(ip -4 rule show 2>/dev/null) == *\"from all lookup main suppress_prefixlength 0\"* ]]; do\n\t\t\tcmd ip -4 rule delete table main suppress_prefixlength 0\n\t\tdone\n\t\twhile [[ $(ip -6 rule show 2>/dev/null) == *\"lookup $table\"* ]]; do\n\t\t\tcmd ip -6 rule delete table $table\n\t\tdone\n\t\twhile [[ $(ip -6 rule show 2>/dev/null) == *\"from all lookup main suppress_prefixlength 0\"* ]]; do\n\t\t\tcmd ip -6 rule delete table main suppress_prefixlength 0\n\t\tdone\n\tfi\n\tcmd ip link delete dev \"$INTERFACE\"\n}\n\nadd_addr() {\n\tlocal proto=-4\n\t[[ $1 == *:* ]] && proto=-6\n\tcmd ip $proto address add \"$1\" dev \"$INTERFACE\"\n}\n\nset_mtu_up() {\n\tlocal mtu=2147483647 endpoint output\n\tif [[ -n $MTU ]]; then\n\t\tcmd ip link set mtu \"$MTU\" up dev \"$INTERFACE\"\n\t\treturn\n\tfi\n\twhile read -r _ endpoint; do\n\t\t[[ $endpoint =~ ^\\[?([a-z0-9:.]+)\\]?:[0-9]+$ ]] || continue\n\t\toutput=\"$(ip route get \"${BASH_REMATCH[1]}\" || true)\"\n\t\t[[ ( $output =~ mtu\\ ([0-9]+) || ( $output =~ dev\\ ([^ ]+) && $(ip link show dev \"${BASH_REMATCH[1]}\") =~ mtu\\ ([0-9]+) ) ) && ${BASH_REMATCH[1]} -lt $mtu ]] && mtu=\"${BASH_REMATCH[1]}\"\n\tdone < <(wg show \"$INTERFACE\" endpoints)\n\tif [[ $mtu -eq 2147483647 ]]; then\n\t\tread -r output < <(ip route show default || true) || true\n\t\t[[ ( $output =~ mtu\\ ([0-9]+) || ( $output =~ dev\\ ([^ ]+) && $(ip link show dev \"${BASH_REMATCH[1]}\") =~ mtu\\ ([0-9]+) ) ) && ${BASH_REMATCH[1]} -lt $mtu ]] && mtu=\"${BASH_REMATCH[1]}\"\n\tfi\n\t[[ $mtu -gt 0 && $mtu -lt 2147483647 ]] || mtu=1500\n\tcmd ip link set mtu $(( mtu - 80 )) up dev \"$INTERFACE\"\n}\n\nresolvconf_iface_prefix() {\n\t[[ -f /etc/resolvconf/interface-order && ! -L $(type -P resolvconf) ]] || return 0\n\tlocal iface\n\twhile read -r iface; do\n\t\t[[ $iface =~ ^([A-Za-z0-9-]+)\\*$ ]] || continue\n\t\techo \"${BASH_REMATCH[1]}.\" && return 0\n\tdone < /etc/resolvconf/interface-order\n}\n\nHAVE_SET_DNS=0\nset_dns() {\n\t[[ ${#DNS[@]} -gt 0 ]] || return 0\n\t{ printf 'nameserver %s\\n' \"${DNS[@]}\"\n\t  [[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\\n' \"${DNS_SEARCH[*]}\"\n\t} | cmd resolvconf -a \"$(resolvconf_iface_prefix)$INTERFACE\" -m 0 -x\n\tHAVE_SET_DNS=1\n}\n\nunset_dns() {\n\t[[ ${#DNS[@]} -gt 0 ]] || return 0\n\tcmd resolvconf -d \"$(resolvconf_iface_prefix)$INTERFACE\" -f\n}\n\nadd_route() {\n\tlocal proto=-4\n\t[[ $1 == *:* ]] && proto=-6\n\t[[ $TABLE != off ]] || return 0\n\n\tif [[ -n $TABLE && $TABLE != auto ]]; then\n\t\tcmd ip $proto route add \"$1\" dev \"$INTERFACE\" table \"$TABLE\"\n\telif [[ $1 == */0 ]]; then\n\t\tadd_default \"$1\"\n\telse\n\t\t[[ -n $(ip $proto route show dev \"$INTERFACE\" match \"$1\" 2>/dev/null) ]] || cmd ip $proto route add \"$1\" dev \"$INTERFACE\"\n\tfi\n}\n\nget_fwmark() {\n\tlocal fwmark\n\tfwmark=\"$(wg show \"$INTERFACE\" fwmark)\" || return 1\n\t[[ -n $fwmark && $fwmark != off ]] || return 1\n\tprintf -v \"$1\" \"%d\" \"$fwmark\"\n\treturn 0\n}\n\nremove_firewall() {\n\tif type -p nft >/dev/null; then\n\t\tlocal table nftcmd\n\t\twhile read -r table; do\n\t\t\t[[ $table == *\" wg-quick-$INTERFACE\" ]] && printf -v nftcmd '%sdelete %s\\n' \"$nftcmd\" \"$table\"\n\t\tdone < <(nft list tables 2>/dev/null)\n\t\t[[ -z $nftcmd ]] || cmd nft -f <(echo -n \"$nftcmd\")\n\tfi\n\tif type -p iptables >/dev/null; then\n\t\tlocal line iptables found restore\n\t\tfor iptables in iptables ip6tables; do\n\t\t\trestore=\"\" found=0\n\t\t\twhile read -r line; do\n\t\t\t\t[[ $line == \"*\"* || $line == COMMIT || $line == \"-A \"*\"-m comment --comment \\\"wg-quick(8) rule for $INTERFACE\\\"\"* ]] || continue\n\t\t\t\t[[ $line == \"-A\"* ]] && found=1\n\t\t\t\tprintf -v restore '%s%s\\n' \"$restore\" \"${line/#-A/-D}\"\n\t\t\tdone < <($iptables-save 2>/dev/null)\n\t\t\t[[ $found -ne 1 ]] || echo -n \"$restore\" | cmd $iptables-restore -n\n\t\tdone\n\tfi\n}\n\nHAVE_SET_FIREWALL=0\nadd_default() {\n\tlocal table line\n\tif ! get_fwmark table; then\n\t\ttable=51820\n\t\twhile [[ -n $(ip -4 route show table $table 2>/dev/null) || -n $(ip -6 route show table $table 2>/dev/null) ]]; do\n\t\t\t((table++))\n\t\tdone\n\t\tcmd wg set \"$INTERFACE\" fwmark $table\n\tfi\n\tlocal proto=-4 iptables=iptables pf=ip\n\t[[ $1 == *:* ]] && proto=-6 iptables=ip6tables pf=ip6\n\tcmd ip $proto rule add not fwmark $table table $table\n\tcmd ip $proto rule add table main suppress_prefixlength 0\n\tcmd ip $proto route add \"$1\" dev \"$INTERFACE\" table $table\n\n\tlocal marker=\"-m comment --comment \\\"wg-quick(8) rule for $INTERFACE\\\"\" restore=$'*raw\\n' nftable=\"wg-quick-$INTERFACE\" nftcmd \n\tprintf -v nftcmd '%sadd table %s %s\\n' \"$nftcmd\" \"$pf\" \"$nftable\"\n\tprintf -v nftcmd '%sadd chain %s %s preraw { type filter hook prerouting priority -300; }\\n' \"$nftcmd\" \"$pf\" \"$nftable\"\n\tprintf -v nftcmd '%sadd chain %s %s premangle { type filter hook prerouting priority -150; }\\n' \"$nftcmd\" \"$pf\" \"$nftable\"\n\tprintf -v nftcmd '%sadd chain %s %s postmangle { type filter hook postrouting priority -150; }\\n' \"$nftcmd\" \"$pf\" \"$nftable\"\n\twhile read -r line; do\n\t\t[[ $line =~ .*inet6?\\ ([0-9a-f:.]+)/[0-9]+.* ]] || continue\n\t\tprintf -v restore '%s-I PREROUTING ! -i %s -d %s -m addrtype ! --src-type LOCAL -j DROP %s\\n' \"$restore\" \"$INTERFACE\" \"${BASH_REMATCH[1]}\" \"$marker\"\n\t\tprintf -v nftcmd '%sadd rule %s %s preraw iifname != \"%s\" %s daddr %s fib saddr type != local drop\\n' \"$nftcmd\" \"$pf\" \"$nftable\" \"$INTERFACE\" \"$pf\" \"${BASH_REMATCH[1]}\"\n\tdone < <(ip -o $proto addr show dev \"$INTERFACE\" 2>/dev/null)\n\tprintf -v restore '%sCOMMIT\\n*mangle\\n-I POSTROUTING -m mark --mark %d -p udp -j CONNMARK --save-mark %s\\n-I PREROUTING -p udp -j CONNMARK --restore-mark %s\\nCOMMIT\\n' \"$restore\" $table \"$marker\" \"$marker\"\n\tprintf -v nftcmd '%sadd rule %s %s postmangle meta l4proto udp mark %d ct mark set mark \\n' \"$nftcmd\" \"$pf\" \"$nftable\" $table\n\tprintf -v nftcmd '%sadd rule %s %s premangle meta l4proto udp meta mark set ct mark \\n' \"$nftcmd\" \"$pf\" \"$nftable\"\n\t[[ $proto == -4 ]] && [[ $(sysctl -n net.ipv4.conf.all.src_valid_mark) -ne 1 ]] && cmd sysctl -q net.ipv4.conf.all.src_valid_mark=1\n\tif type -p nft >/dev/null; then\n\t\tcmd nft -f <(echo -n \"$nftcmd\")\n\telse\n\t\techo -n \"$restore\" | cmd $iptables-restore -n\n\tfi\n\tHAVE_SET_FIREWALL=1\n\treturn 0\n}\n\nset_config() {\n\tcmd wg addconf \"$INTERFACE\" <(echo \"$WG_CONFIG\")\n}\n\nsave_config() {\n\tlocal old_umask new_config current_config address cmd\n\t[[ $(ip -all -brief address show dev \"$INTERFACE\") =~ ^$INTERFACE\\ +\\ [A-Z]+\\ +(.+)$ ]] || true\n\tnew_config=$'[Interface]\\n'\n\tfor address in ${BASH_REMATCH[1]}; do\n\t\tnew_config+=\"Address = $address\"$'\\n'\n\tdone\n\twhile read -r address; do\n\t\t[[ $address =~ ^nameserver\\ ([a-zA-Z0-9_=+:%.-]+)$ ]] && new_config+=\"DNS = ${BASH_REMATCH[1]}\"$'\\n'\n\tdone < <(resolvconf -l \"$(resolvconf_iface_prefix)$INTERFACE\" 2>/dev/null || cat \"/etc/resolvconf/run/interface/$(resolvconf_iface_prefix)$INTERFACE\" 2>/dev/null)\n\t[[ -n $MTU && $(ip link show dev \"$INTERFACE\") =~ mtu\\ ([0-9]+) ]] && new_config+=\"MTU = ${BASH_REMATCH[1]}\"$'\\n'\n\t[[ -n $TABLE ]] && new_config+=\"Table = $TABLE\"$'\\n'\n\t[[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\\n'\n\tfor cmd in \"${PRE_UP[@]}\"; do\n\t\tnew_config+=\"PreUp = $cmd\"$'\\n'\n\tdone\n\tfor cmd in \"${POST_UP[@]}\"; do\n\t\tnew_config+=\"PostUp = $cmd\"$'\\n'\n\tdone\n\tfor cmd in \"${PRE_DOWN[@]}\"; do\n\t\tnew_config+=\"PreDown = $cmd\"$'\\n'\n\tdone\n\tfor cmd in \"${POST_DOWN[@]}\"; do\n\t\tnew_config+=\"PostDown = $cmd\"$'\\n'\n\tdone\n\told_umask=\"$(umask)\"\n\tumask 077\n\tcurrent_config=\"$(cmd wg showconf \"$INTERFACE\")\"\n\ttrap 'rm -f \"$CONFIG_FILE.tmp\"; exit' INT TERM EXIT\n\techo \"${current_config/\\[Interface\\]$'\\n'/$new_config}\" > \"$CONFIG_FILE.tmp\" || die \"Could not write configuration file\"\n\tsync \"$CONFIG_FILE.tmp\"\n\tmv \"$CONFIG_FILE.tmp\" \"$CONFIG_FILE\" || die \"Could not move configuration file\"\n\ttrap - INT TERM EXIT\n\tumask \"$old_umask\"\n}\n\nexecute_hooks() {\n\tlocal hook\n\tfor hook in \"$@\"; do\n\t\thook=\"${hook//%i/$INTERFACE}\"\n\t\techo \"[#] $hook\" >&2\n\t\t(eval \"$hook\")\n\tdone\n}\n\ncmd_usage() {\n\tcat >&2 <<-_EOF\n\tUsage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]\n\n\t  CONFIG_FILE is a configuration file, whose filename is the interface name\n\t  followed by \\`.conf'. Otherwise, INTERFACE is an interface name, with\n\t  configuration found at /etc/wireguard/INTERFACE.conf. It is to be readable\n\t  by wg(8)'s \\`setconf' sub-command, with the exception of the following additions\n\t  to the [Interface] section, which are handled by $PROGRAM:\n\n\t  - Address: may be specified one or more times and contains one or more\n\t    IP addresses (with an optional CIDR mask) to be set for the interface.\n\t  - DNS: an optional DNS server to use while the device is up.\n\t  - MTU: an optional MTU for the interface; if unspecified, auto-calculated.\n\t  - Table: an optional routing table to which routes will be added; if\n\t    unspecified or \\`auto', the default table is used. If \\`off', no routes\n\t    are added.\n\t  - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed\n\t    by bash(1) at the corresponding phases of the link, most commonly used\n\t    to configure DNS. The string \\`%i' is expanded to INTERFACE.\n\t  - SaveConfig: if set to \\`true', the configuration is saved from the current\n\t    state of the interface upon shutdown.\n\n\tSee wg-quick(8) for more info and examples.\n\t_EOF\n}\n\ncmd_up() {\n\tlocal i\n\t[[ -z $(ip link show dev \"$INTERFACE\" 2>/dev/null) ]] || die \"\\`$INTERFACE' already exists\"\n\ttrap 'del_if; exit' INT TERM EXIT\n\tadd_if\n\texecute_hooks \"${PRE_UP[@]}\"\n\tset_config\n\tfor i in \"${ADDRESSES[@]}\"; do\n\t\tadd_addr \"$i\"\n\tdone\n\tset_mtu_up\n\tset_dns\n\tfor i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo \"$i\"; done; done < <(wg show \"$INTERFACE\" allowed-ips) | sort -nr -k 2 -t /); do\n\t\tadd_route \"$i\"\n\tdone\n\texecute_hooks \"${POST_UP[@]}\"\n\ttrap - INT TERM EXIT\n}\n\ncmd_down() {\n\t[[ \" $(wg show interfaces) \" == *\" $INTERFACE \"* ]] || die \"\\`$INTERFACE' is not a WireGuard interface\"\n\texecute_hooks \"${PRE_DOWN[@]}\"\n\t[[ $SAVE_CONFIG -eq 0 ]] || save_config\n\tdel_if\n\tunset_dns || true\n\tremove_firewall || true\n\texecute_hooks \"${POST_DOWN[@]}\"\n}\n\ncmd_save() {\n\t[[ \" $(wg show interfaces) \" == *\" $INTERFACE \"* ]] || die \"\\`$INTERFACE' is not a WireGuard interface\"\n\tsave_config\n}\n\ncmd_strip() {\n\techo \"$WG_CONFIG\"\n}\n\n# ~~ function override insertion point ~~\n\nif [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then\n\tcmd_usage\nelif [[ $# -eq 2 && $1 == up ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_up\nelif [[ $# -eq 2 && $1 == down ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_down\nelif [[ $# -eq 2 && $1 == save ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_save\nelif [[ $# -eq 2 && $1 == strip ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_strip\nelse\n\tcmd_usage\n\texit 1\nfi\n\nexit 0\n"
  },
  {
    "path": "src/wg-quick/openbsd.bash",
    "content": "#!/usr/local/bin/bash\n# SPDX-License-Identifier: GPL-2.0\n#\n# Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n#\n\nset -e -o pipefail\nshopt -s extglob\nexport LC_ALL=C\n\nexec 3>&2\nSELF=\"$(readlink -f \"${BASH_SOURCE[0]}\")\"\nexport PATH=\"${SELF%/*}:$PATH\"\n\nWG_CONFIG=\"\"\nINTERFACE=\"\"\nADDRESSES=( )\nMTU=\"\"\nDNS=( )\nDNS_SEARCH=( )\nTABLE=\"\"\nPRE_UP=( )\nPOST_UP=( )\nPRE_DOWN=( )\nPOST_DOWN=( )\nSAVE_CONFIG=0\nCONFIG_FILE=\"\"\nPROGRAM=\"${0##*/}\"\nARGS=( \"$@\" )\n\ncmd() {\n\techo \"[#] $*\" >&3\n\t\"$@\"\n}\n\ndie() {\n\techo \"$PROGRAM: $*\" >&2\n\texit 1\n}\n\nparse_options() {\n\tlocal interface_section=0 line key value stripped\n\tCONFIG_FILE=\"$1\"\n\t[[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE=\"/etc/wireguard/$CONFIG_FILE.conf\"\n\t[[ -e $CONFIG_FILE ]] || die \"\\`$CONFIG_FILE' does not exist\"\n\t[[ $CONFIG_FILE =~ (^|/)([a-zA-Z0-9_=+.-]{1,15})\\.conf$ ]] || die \"The config file must be a valid interface name, followed by .conf\"\n\tCONFIG_FILE=\"$(readlink -f \"$CONFIG_FILE\")\"\n\t((($(stat -f '0%#p' \"$CONFIG_FILE\") & $(stat -f '0%#p' \"${CONFIG_FILE%/*}\") & 0007) == 0)) || echo \"Warning: \\`$CONFIG_FILE' is world accessible\" >&2\n\tINTERFACE=\"${BASH_REMATCH[2]}\"\n\tshopt -s nocasematch\n\twhile read -r line || [[ -n $line ]]; do\n\t\tstripped=\"${line%%\\#*}\"\n\t\tkey=\"${stripped%%=*}\"; key=\"${key##*([[:space:]])}\"; key=\"${key%%*([[:space:]])}\"\n\t\tvalue=\"${stripped#*=}\"; value=\"${value##*([[:space:]])}\"; value=\"${value%%*([[:space:]])}\"\n\t\tunstripped_value=\"${line#*=}\"; unstripped_value=\"${unstripped_value##*([[:space:]])}\"; unstripped_value=\"${unstripped_value%%*([[:space:]])}\"\n\t\t[[ $key == \"[\"* ]] && interface_section=0\n\t\t[[ $key == \"[Interface]\" ]] && interface_section=1\n\t\tif [[ $interface_section -eq 1 ]]; then\n\t\t\tcase \"$key\" in\n\t\t\tAddress) ADDRESSES+=( ${value//,/ } ); continue ;;\n\t\t\tMTU) MTU=\"$value\"; continue ;;\n\t\t\tDNS) for v in ${value//,/ }; do\n\t\t\t\t[[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )\n\t\t\tdone; continue ;;\n\t\t\tTable) TABLE=\"$value\"; continue ;;\n\t\t\tPreUp) PRE_UP+=( \"$unstripped_value\" ); continue ;;\n\t\t\tPreDown) PRE_DOWN+=( \"$unstripped_value\" ); continue ;;\n\t\t\tPostUp) POST_UP+=( \"$unstripped_value\" ); continue ;;\n\t\t\tPostDown) POST_DOWN+=( \"$unstripped_value\" ); continue ;;\n\t\t\tSaveConfig) read_bool SAVE_CONFIG \"$value\"; continue ;;\n\t\t\tesac\n\t\tfi\n\t\tWG_CONFIG+=\"$line\"$'\\n'\n\tdone < \"$CONFIG_FILE\"\n\tshopt -u nocasematch\n}\n\nread_bool() {\n\tcase \"$2\" in\n\ttrue) printf -v \"$1\" 1 ;;\n\tfalse) printf -v \"$1\" 0 ;;\n\t*) die \"\\`$2' is neither true nor false\"\n\tesac\n}\n\nauto_su() {\n\t[[ $UID == 0 ]] || exec doas -- \"$BASH\" -- \"$SELF\" \"${ARGS[@]}\"\n}\n\n\nget_real_interface() {\n\tlocal interface line\n\twhile IFS= read -r line; do\n\t\tif [[ $line =~ ^([a-z]+[0-9]+):\\ .+ ]]; then\n\t\t\tinterface=\"${BASH_REMATCH[1]}\"\n\t\t\tcontinue\n\t\tfi\n\t\tif [[ $interface == wg* && $line =~ ^\\\tdescription:\\ wg-quick:\\ (.+) && ${BASH_REMATCH[1]} == \"$INTERFACE\" ]]; then\n\t\t\tREAL_INTERFACE=\"$interface\"\n\t\t\treturn 0\n\t\tfi\n\tdone < <(ifconfig)\n\treturn 1\n}\n\nadd_if() {\n\twhile true; do\n\t\tlocal -A existing_ifs=\"( $(wg show interfaces | sed 's/\\([^ ]*\\)/[\\1]=1/g') )\"\n\t\tlocal index ret\n\t\tfor ((index=0; index <= 2147483647; ++index)); do [[ -v existing_ifs[wg$index] ]] || break; done\n\t\tif ret=\"$(cmd ifconfig wg$index create description \"wg-quick: $INTERFACE\" 2>&1)\"; then\n\t\t\tREAL_INTERFACE=\"wg$index\"\n\t\t\treturn 0\n\t\tfi\n\t\t[[ $ret == *\"ifconfig: SIOCIFCREATE: File exists\"* ]] && continue\n\t\techo \"$ret\" >&3\n\t\treturn 1\n\tdone\n}\n\ndel_routes() {\n\tlocal todelete=( ) destination gateway netif\n\t[[ -n $REAL_INTERFACE ]] || return 0\n\twhile read -r destination _ _ _ _ netif _; do\n\t\t[[ $netif == \"$REAL_INTERFACE\" ]] && todelete+=( \"$destination\" )\n\tdone < <(netstat -nr -f inet)\n\tfor destination in \"${todelete[@]}\"; do\n\t\tcmd route -q -n delete -inet \"$destination\" || true\n\tdone\n\ttodelete=( )\n\twhile read -r destination gateway _ netif; do\n\t\t[[ $netif == \"$REAL_INTERFACE\" || ( $netif == lo* && $gateway == \"$REAL_INTERFACE\" ) ]] && todelete+=( \"$destination\" )\n\tdone < <(netstat -nr -f inet6)\n\tfor destination in \"${todelete[@]}\"; do\n\t\tcmd route -q -n delete -inet6 \"$destination\" || true\n\tdone\n\tfor destination in \"${ENDPOINTS[@]}\"; do\n\t\tif [[ $destination == *:* ]]; then\n\t\t\tcmd route -q -n delete -inet6 \"$destination\" || true\n\t\telse\n\t\t\tcmd route -q -n delete -inet \"$destination\" || true\n\t\tfi\n\tdone\n}\n\ndel_if() {\n\tunset_dns\n\t[[ -n $REAL_INTERFACE ]] && cmd ifconfig $REAL_INTERFACE destroy\n}\n\nup_if() {\n\tcmd ifconfig \"$REAL_INTERFACE\" up\n}\n\nadd_addr() {\n\tlocal family\n\tif [[ $1 == *:* ]]; then\n\t\tfamily=inet6\n\t\t[[ -n $FIRSTADDR6 ]] || FIRSTADDR6=\"${1%/*}\"\n\telse\n\t\tfamily=inet\n\t\t[[ -n $FIRSTADDR4 ]] || FIRSTADDR4=\"${1%/*}\"\n\tfi\n\tcmd ifconfig \"$REAL_INTERFACE\" $family \"$1\" alias\n}\n\nset_mtu() {\n\tlocal mtu=0 endpoint output family\n\tif [[ -n $MTU ]]; then\n\t\tcmd ifconfig \"$REAL_INTERFACE\" mtu \"$MTU\"\n\t\treturn\n\tfi\n\twhile read -r _ endpoint; do\n\t\t[[ $endpoint =~ ^\\[?([a-z0-9:.]+)\\]?:[0-9]+$ ]] || continue\n\t\tfamily=inet\n\t\t[[ ${BASH_REMATCH[1]} == *:* ]] && family=inet6\n\t\toutput=\"$(route -n get \"-$family\" \"${BASH_REMATCH[1]}\" || true)\"\n\t\t[[ $output =~ interface:\\ ([^ ]+)$'\\n' && $(ifconfig \"${BASH_REMATCH[1]}\") =~ mtu\\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu=\"${BASH_REMATCH[1]}\"\n\tdone < <(wg show \"$REAL_INTERFACE\" endpoints)\n\tif [[ $mtu -eq 0 ]]; then\n\t\tread -r output < <(route -n get default || true) || true\n\t\t[[ $output =~ interface:\\ ([^ ]+)$'\\n' && $(ifconfig \"${BASH_REMATCH[1]}\") =~ mtu\\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu=\"${BASH_REMATCH[1]}\"\n\tfi\n\t[[ $mtu -gt 0 ]] || mtu=1500\n\tcmd ifconfig \"$REAL_INTERFACE\" mtu $(( mtu - 80 ))\n}\n\n\ncollect_gateways() {\n\tlocal destination gateway\n\n\tGATEWAY4=\"\"\n\twhile read -r destination gateway _; do\n\t\t[[ $destination == default ]] || continue\n\t\tGATEWAY4=\"$gateway\"\n\t\tbreak\n\tdone < <(netstat -nr -f inet)\n\n\tGATEWAY6=\"\"\n\twhile read -r destination gateway _; do\n\t\t[[ $destination == default ]] || continue\n\t\tGATEWAY6=\"$gateway\"\n\t\tbreak\n\tdone < <(netstat -nr -f inet6)\n}\n\ncollect_endpoints() {\n\tENDPOINTS=( )\n\twhile read -r _ endpoint; do\n\t\t[[ $endpoint =~ ^\\[?([a-z0-9:.]+)\\]?:[0-9]+$ ]] || continue\n\t\tENDPOINTS+=( \"${BASH_REMATCH[1]}\" )\n\tdone < <(wg show \"$REAL_INTERFACE\" endpoints)\n}\n\nset_endpoint_direct_route() {\n\tlocal old_endpoints endpoint old_gateway4 old_gateway6 remove_all_old=0 added=( )\n\told_endpoints=( \"${ENDPOINTS[@]}\" )\n\told_gateway4=\"$GATEWAY4\"\n\told_gateway6=\"$GATEWAY6\"\n\tcollect_gateways\n\tcollect_endpoints\n\n\t[[ $old_gateway4 != \"$GATEWAY4\" || $old_gateway6 != \"$GATEWAY6\" ]] && remove_all_old=1\n\n\tif [[ $remove_all_old -eq 1 ]]; then\n\t\tfor endpoint in \"${ENDPOINTS[@]}\"; do\n\t\t\t[[ \" ${old_endpoints[*]} \" == *\" $endpoint \"* ]] || old_endpoints+=( \"$endpoint\" )\n\t\tdone\n\tfi\n\n\tfor endpoint in \"${old_endpoints[@]}\"; do\n\t\t[[ $remove_all_old -eq 0 && \" ${ENDPOINTS[*]} \" == *\" $endpoint \"* ]] && continue\n\t\tif [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then\n\t\t\tcmd route -q -n delete -inet6 \"$endpoint\" 2>/dev/null || true\n\t\telif [[ $AUTO_ROUTE4 -eq 1 ]]; then\n\t\t\tcmd route -q -n delete -inet \"$endpoint\" 2>/dev/null || true\n\t\tfi\n\tdone\n\n\tfor endpoint in \"${ENDPOINTS[@]}\"; do\n\t\tif [[ $remove_all_old -eq 0 && \" ${old_endpoints[*]} \" == *\" $endpoint \"* ]]; then\n\t\t\tadded+=( \"$endpoint\" )\n\t\t\tcontinue\n\t\tfi\n\t\tif [[ $endpoint == *:* && $AUTO_ROUTE6 -eq 1 ]]; then\n\t\t\tif [[ -n $GATEWAY6 ]]; then\n\t\t\t\tcmd route -q -n add -inet6 \"$endpoint\" -gateway \"$GATEWAY6\" || true\n\t\t\telse\n\t\t\t\t# Prevent routing loop\n\t\t\t\tcmd route -q -n add -inet6 \"$endpoint\" ::1 -blackhole || true\n\t\t\tfi\n\t\t\tadded+=( \"$endpoint\" )\n\t\telif [[ $AUTO_ROUTE4 -eq 1 ]]; then\n\t\t\tif [[ -n $GATEWAY4 ]]; then\n\t\t\t\tcmd route -q -n add -inet \"$endpoint\" -gateway \"$GATEWAY4\" || true\n\t\t\telse\n\t\t\t\t# Prevent routing loop\n\t\t\t\tcmd route -q -n add -inet \"$endpoint\" 127.0.0.1 -blackhole || true\n\t\t\tfi\n\t\t\tadded+=( \"$endpoint\" )\n\t\tfi\n\tdone\n\tENDPOINTS=( \"${added[@]}\" )\n}\n\nmonitor_daemon() {\n\techo \"[+] Backgrounding route monitor\" >&2\n\t(trap 'del_routes; exit 0' INT TERM EXIT\n\texec >/dev/null 2>&1\n\texec 19< <(exec route -n monitor)\n\tlocal event pid=$!\n\t# TODO: this should also check to see if the endpoint actually changes\n\t# in response to incoming packets, and then call set_endpoint_direct_route\n\t# then too. That function should be able to gracefully cleanup if the\n\t# endpoints change.\n\twhile read -u 19 -r event; do\n\t\t[[ $event == RTM_* ]] || continue\n\t\tifconfig \"$REAL_INTERFACE\" >/dev/null 2>&1 || break\n\t\t[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route\n\t\t# TODO: set the mtu as well, but only if up\n\tdone\n\tkill $pid) & disown\n}\n\nset_dns() {\n\t[[ ${#DNS[@]} -gt 0 ]] || return 0\n\n\t# TODO: add exclusive support for nameservers\n\tif pgrep -qx unwind; then\n\t\techo \"[!] WARNING: unwind will leak DNS queries\" >&2\n\telif pgrep -qx resolvd; then\n\t\techo \"[!] WARNING: resolvd may leak DNS queries\" >&2\n\telse\n\t\techo \"[+] resolvd is not running, DNS will not be configured\" >&2\n\t\treturn 0\n\tfi\n\n\tcmd cp /etc/resolv.conf \"/etc/resolv.conf.wg-quick-backup.$INTERFACE\"\n\t[[ ${#DNS_SEARCH[@]} -eq 0 ]] || cmd printf 'search %s\\n' \"${DNS_SEARCH[*]}\" > /etc/resolv.conf\n\troute nameserver ${REAL_INTERFACE} ${DNS[@]}\n}\n\nunset_dns() {\n\t[[ -f \"/etc/resolv.conf.wg-quick-backup.$INTERFACE\" ]] || return 0\n\troute nameserver ${REAL_INTERFACE}\n\tcmd mv \"/etc/resolv.conf.wg-quick-backup.$INTERFACE\" /etc/resolv.conf\n}\n\nadd_route() {\n\t[[ $TABLE != off ]] || return 0\n\tlocal family ifaceroute\n\n\tif [[ $1 == *:* ]]; then\n\t\tfamily=inet6\n\t\t[[ -n $FIRSTADDR6 ]] || die \"Local IPv6 address must be set to have routes\"\n\t\tifaceroute=\"$FIRSTADDR6\"\n\telse\n\t\tfamily=inet\n\t\t[[ -n $FIRSTADDR4 ]] || die \"Local IPv4 address must be set to have routes\"\n\t\tifaceroute=\"$FIRSTADDR4\"\n\tfi\n\n\tif [[ -n $TABLE && $TABLE != auto ]]; then\n\t\tcmd route -q -n -T \"$TABLE\" add \"-$family\" \"$1\" -iface \"$ifaceroute\"\n\telif [[ $1 == */0 ]]; then\n\t\tif [[ $1 == *:* ]]; then\n\t\t\tAUTO_ROUTE6=1\n\t\t\tcmd route -q -n add -inet6 ::/1 -iface \"$ifaceroute\"\n\t\t\tcmd route -q -n add -inet6 8000::/1 -iface \"$ifaceroute\"\n\t\telse\n\t\t\tAUTO_ROUTE4=1\n\t\t\tcmd route -q -n add -inet 0.0.0.0/1 -iface \"$ifaceroute\"\n\t\t\tcmd route -q -n add -inet 128.0.0.0/1 -iface \"$ifaceroute\"\n\t\tfi\n\telse\n\t\t[[ $(route -n get \"-$family\" \"$1\" 2>/dev/null) =~ interface:\\ $REAL_INTERFACE$'\\n' ]] || cmd route -q -n add \"-$family\" \"$1\" -iface \"$ifaceroute\"\n\tfi\n}\n\nset_config() {\n\tcmd wg addconf \"$REAL_INTERFACE\" <(echo \"$WG_CONFIG\")\n}\n\nsave_config() {\n\tlocal old_umask new_config current_config address network cmd\n\tnew_config=$'[Interface]\\n'\n\t{ read -r _; while read -r _ _ network address _; do\n\t\t[[ $network == *Link* ]] || new_config+=\"Address = $address\"$'\\n'\n\tdone } < <(netstat -I \"$REAL_INTERFACE\" -n -v)\n\t# TODO: actually determine current DNS for interface\n\tfor address in \"${DNS[@]}\"; do\n\t\tnew_config+=\"DNS = $address\"$'\\n'\n\tdone\n\t[[ -n $MTU ]] && new_config+=\"MTU = $MTU\"$'\\n'\n\t[[ -n $TABLE ]] && new_config+=\"Table = $TABLE\"$'\\n'\n\t[[ $SAVE_CONFIG -eq 0 ]] || new_config+=$'SaveConfig = true\\n'\n\tfor cmd in \"${PRE_UP[@]}\"; do\n\t\tnew_config+=\"PreUp = $cmd\"$'\\n'\n\tdone\n\tfor cmd in \"${POST_UP[@]}\"; do\n\t\tnew_config+=\"PostUp = $cmd\"$'\\n'\n\tdone\n\tfor cmd in \"${PRE_DOWN[@]}\"; do\n\t\tnew_config+=\"PreDown = $cmd\"$'\\n'\n\tdone\n\tfor cmd in \"${POST_DOWN[@]}\"; do\n\t\tnew_config+=\"PostDown = $cmd\"$'\\n'\n\tdone\n\told_umask=\"$(umask)\"\n\tumask 077\n\tcurrent_config=\"$(cmd wg showconf \"$REAL_INTERFACE\")\"\n\ttrap 'rm -f \"$CONFIG_FILE.tmp\"; exit' INT TERM EXIT\n\techo \"${current_config/\\[Interface\\]$'\\n'/$new_config}\" > \"$CONFIG_FILE.tmp\" || die \"Could not write configuration file\"\n\tsync \"$CONFIG_FILE.tmp\"\n\tmv \"$CONFIG_FILE.tmp\" \"$CONFIG_FILE\" || die \"Could not move configuration file\"\n\ttrap - INT TERM EXIT\n\tumask \"$old_umask\"\n}\n\nexecute_hooks() {\n\tlocal hook\n\tfor hook in \"$@\"; do\n\t\thook=\"${hook//%i/$REAL_INTERFACE}\"\n\t\thook=\"${hook//%I/$INTERFACE}\"\n\t\techo \"[#] $hook\" >&2\n\t\t(eval \"$hook\")\n\tdone\n}\n\ncmd_usage() {\n\tcat >&2 <<-_EOF\n\tUsage: $PROGRAM [ up | down | save | strip ] [ CONFIG_FILE | INTERFACE ]\n\n\t  CONFIG_FILE is a configuration file, whose filename is the interface name\n\t  followed by \\`.conf'. Otherwise, INTERFACE is an interface name, with\n\t  configuration found at /etc/wireguard/INTERFACE.conf. It is to be readable\n\t  by wg(8)'s \\`setconf' sub-command, with the exception of the following additions\n\t  to the [Interface] section, which are handled by $PROGRAM:\n\n\t  - Address: may be specified one or more times and contains one or more\n\t    IP addresses (with an optional CIDR mask) to be set for the interface.\n\t  - DNS: an optional DNS server to use while the device is up.\n\t  - MTU: an optional MTU for the interface; if unspecified, auto-calculated.\n\t  - Table: an optional routing table to which routes will be added; if\n\t    unspecified or \\`auto', the default table is used. If \\`off', no routes\n\t    are added.\n\t  - PreUp, PostUp, PreDown, PostDown: script snippets which will be executed\n\t    by bash(1) at the corresponding phases of the link, most commonly used\n\t    to configure DNS. The string \\`%i' is expanded to INTERFACE.\n\t  - SaveConfig: if set to \\`true', the configuration is saved from the current\n\t    state of the interface upon shutdown.\n\n\tSee wg-quick(8) for more info and examples.\n\t_EOF\n}\n\ncmd_up() {\n\tlocal i\n\tget_real_interface && die \"\\`$INTERFACE' already exists as \\`$REAL_INTERFACE'\"\n\ttrap 'del_if; del_routes; exit' INT TERM EXIT\n\tadd_if\n\texecute_hooks \"${PRE_UP[@]}\"\n\tset_config\n\tfor i in \"${ADDRESSES[@]}\"; do\n\t\tadd_addr \"$i\"\n\tdone\n\tset_mtu\n\tup_if\n\tset_dns\n\tfor i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo \"$i\"; done; done < <(wg show \"$REAL_INTERFACE\" allowed-ips) | sort -nr -k 2 -t /); do\n\t\tadd_route \"$i\"\n\tdone\n\t[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route\n\tmonitor_daemon\n\texecute_hooks \"${POST_UP[@]}\"\n\ttrap - INT TERM EXIT\n}\n\ncmd_down() {\n\tget_real_interface || die \"\\`$INTERFACE' is not a WireGuard interface\"\n\texecute_hooks \"${PRE_DOWN[@]}\"\n\t[[ $SAVE_CONFIG -eq 0 ]] || save_config\n\tdel_if\n\tunset_dns\n\texecute_hooks \"${POST_DOWN[@]}\"\n}\n\ncmd_save() {\n\tget_real_interface || die \"\\`$INTERFACE' is not a WireGuard interface\"\n\tsave_config\n}\n\ncmd_strip() {\n\techo \"$WG_CONFIG\"\n}\n\n# ~~ function override insertion point ~~\n\nif [[ $# -eq 1 && ( $1 == --help || $1 == -h || $1 == help ) ]]; then\n\tcmd_usage\nelif [[ $# -eq 2 && $1 == up ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_up\nelif [[ $# -eq 2 && $1 == down ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_down\nelif [[ $# -eq 2 && $1 == save ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_save\nelif [[ $# -eq 2 && $1 == strip ]]; then\n\tauto_su\n\tparse_options \"$2\"\n\tcmd_strip\nelse\n\tcmd_usage\n\texit 1\nfi\n\nexit 0\n"
  },
  {
    "path": "src/wg.c",
    "content": "// SPDX-License-Identifier: GPL-2.0 OR MIT\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stddef.h>\n#include <stdio.h>\n#include <string.h>\n\n#include \"subcommands.h\"\n#include \"version.h\"\n\nconst char *PROG_NAME;\n\nstatic const struct {\n\tconst char *subcommand;\n\tint (*function)(int, const char**);\n\tconst char *description;\n} subcommands[] = {\n\t{ \"show\", show_main, \"Shows the current configuration and device information\" },\n\t{ \"showconf\", showconf_main, \"Shows the current configuration of a given WireGuard interface, for use with `setconf'\" },\n\t{ \"set\", set_main, \"Change the current configuration, add peers, remove peers, or change peers\" },\n\t{ \"setconf\", setconf_main, \"Applies a configuration file to a WireGuard interface\" },\n\t{ \"addconf\", setconf_main, \"Appends a configuration file to a WireGuard interface\" },\n\t{ \"syncconf\", setconf_main, \"Synchronizes a configuration file to a WireGuard interface\" },\n\t{ \"genkey\", genkey_main, \"Generates a new private key and writes it to stdout\" },\n\t{ \"genpsk\", genkey_main, \"Generates a new preshared key and writes it to stdout\" },\n\t{ \"pubkey\", pubkey_main, \"Reads a private key from stdin and writes a public key to stdout\" }\n};\n\nstatic void show_usage(FILE *file)\n{\n\tfprintf(file, \"Usage: %s <cmd> [<args>]\\n\\n\", PROG_NAME);\n\tfprintf(file, \"Available subcommands:\\n\");\n\tfor (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i)\n\t\tfprintf(file, \"  %s: %s\\n\", subcommands[i].subcommand, subcommands[i].description);\n\tfprintf(file, \"You may pass `--help' to any of these subcommands to view usage.\\n\");\n}\n\nint main(int argc, const char *argv[])\n{\n\tPROG_NAME = argv[0];\n\n\tif (argc == 2 && (!strcmp(argv[1], \"-v\") || !strcmp(argv[1], \"--version\") || !strcmp(argv[1], \"version\"))) {\n\t\tprintf(\"wireguard-tools v%s - https://git.zx2c4.com/wireguard-tools/\\n\", WIREGUARD_TOOLS_VERSION);\n\t\treturn 0;\n\t}\n\tif (argc == 2 && (!strcmp(argv[1], \"-h\") || !strcmp(argv[1], \"--help\") || !strcmp(argv[1], \"help\"))) {\n\t\tshow_usage(stdout);\n\t\treturn 0;\n\t}\n\n\tif (argc == 1) {\n\t\tstatic const char *new_argv[] = { \"show\", NULL };\n\t\treturn show_main(1, new_argv);\n\t}\n\n\tfor (size_t i = 0; i < sizeof(subcommands) / sizeof(subcommands[0]); ++i) {\n\t\tif (!strcmp(argv[1], subcommands[i].subcommand))\n\t\t\treturn subcommands[i].function(argc - 1, argv + 1);\n\t}\n\n\tfprintf(stderr, \"Invalid subcommand: `%s'\\n\", argv[1]);\n\tshow_usage(stderr);\n\treturn 1;\n}\n"
  },
  {
    "path": "src/wincompat/compat.h",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#define __USE_MINGW_ANSI_STDIO 1\n#include <stdio.h>\n#include <stdbool.h>\n#include <stdint.h>\n\n#include <winsock2.h>\n#include <ws2ipdef.h>\n#include <ws2tcpip.h>\n#include <in6addr.h>\n#include <windows.h>\n\n#undef interface\n#undef min\n#undef max\n\n#define IFNAMSIZ 64\n#define EAI_SYSTEM -99\n\n/* libc.c */\nchar *strsep(char **str, const char *sep);\nssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp);\nssize_t getline(char **buf, size_t *bufsiz, FILE *fp);\n"
  },
  {
    "path": "src/wincompat/include/arpa/inet.h",
    "content": ""
  },
  {
    "path": "src/wincompat/include/hashtable.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0\n *\n * Copyright (C) 2018-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#ifndef _HASHTABLE_H\n#define _HASHTABLE_H\n\n#include <string.h>\n\nenum { HASHTABLE_ENTRY_BUCKETS_POW2 = 1 << 10 };\n\nstruct hashtable_entry {\n\tchar *key;\n\tvoid *value;\n\tstruct hashtable_entry *next;\n};\n\nstruct hashtable {\n\tstruct hashtable_entry *entry_buckets[HASHTABLE_ENTRY_BUCKETS_POW2];\n};\n\nstatic unsigned int hashtable_bucket(const char *str)\n{\n\tunsigned long hash = 5381;\n\tchar c;\n\twhile ((c = *str++))\n\t\thash = ((hash << 5) + hash) ^ c;\n\treturn hash & (HASHTABLE_ENTRY_BUCKETS_POW2 - 1);\n}\n\nstatic struct hashtable_entry *hashtable_find_entry(struct hashtable *hashtable, const char *key)\n{\n\tstruct hashtable_entry *entry;\n\tfor (entry = hashtable->entry_buckets[hashtable_bucket(key)]; entry; entry = entry->next) {\n\t\tif (!strcmp(entry->key, key))\n\t\t\treturn entry;\n\t}\n\treturn NULL;\n}\n\nstatic struct hashtable_entry *hashtable_find_or_insert_entry(struct hashtable *hashtable, const char *key)\n{\n\tstruct hashtable_entry **entry;\n\tfor (entry = &hashtable->entry_buckets[hashtable_bucket(key)]; *entry; entry = &(*entry)->next) {\n\t\tif (!strcmp((*entry)->key, key))\n\t\t\treturn *entry;\n\t}\n\t*entry = calloc(1, sizeof(**entry));\n\tif (!*entry)\n\t\treturn NULL;\n\t(*entry)->key = strdup(key);\n\tif (!(*entry)->key) {\n\t\tfree(*entry);\n\t\t*entry = NULL;\n\t\treturn NULL;\n\t}\n\treturn *entry;\n}\n\n#endif\n"
  },
  {
    "path": "src/wincompat/include/net/if.h",
    "content": ""
  },
  {
    "path": "src/wincompat/include/netdb.h",
    "content": ""
  },
  {
    "path": "src/wincompat/include/netinet/in.h",
    "content": ""
  },
  {
    "path": "src/wincompat/include/sys/socket.h",
    "content": ""
  },
  {
    "path": "src/wincompat/init.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <winsock2.h>\n#include <windows.h>\n\n#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING\n#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4\n#endif\n\n__attribute__((constructor)) static void init(void)\n{\n\tHANDLE stdout_handle;\n\tDWORD console_mode;\n\tWSADATA wsaData;\n\tchar *colormode;\n\n\tif (!SetDllDirectoryA(\"\") || !SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32))\n\t\tabort();\n\n\tWSAStartup(MAKEWORD(2, 2), &wsaData);\n\n\tstdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); // We don't close this.\n\tif (stdout_handle == INVALID_HANDLE_VALUE)\n\t\tgoto no_color;\n\tif (!GetConsoleMode(stdout_handle, &console_mode))\n\t\tgoto no_color;\n\tif (!SetConsoleMode(stdout_handle, ENABLE_VIRTUAL_TERMINAL_PROCESSING | console_mode))\n\t\tgoto no_color;\n\treturn;\n\nno_color:\n\tcolormode = getenv(\"WG_COLOR_MODE\");\n\tif (!colormode)\n\t\tputenv(\"WG_COLOR_MODE=never\");\n}\n\n__attribute__((destructor)) static void deinit(void)\n{\n\tWSACleanup();\n}\n"
  },
  {
    "path": "src/wincompat/libc.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <stdio.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <windows.h>\n\nchar *strsep(char **str, const char *sep)\n{\n\tchar *s = *str, *end;\n\tif (!s)\n\t\treturn NULL;\n\tend = s + strcspn(s, sep);\n\tif (*end)\n\t\t*end++ = 0;\n\telse\n\t\tend = 0;\n\t*str = end;\n\treturn s;\n}\n\nssize_t getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp)\n{\n\tchar *ptr, *eptr;\n\n\tif (!*buf || !*bufsiz) {\n\t\t*bufsiz = BUFSIZ;\n\t\tif (!(*buf = malloc(*bufsiz)))\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\nssize_t getline(char **buf, size_t *bufsiz, FILE *fp)\n{\n\treturn getdelim(buf, bufsiz, '\\n', fp);\n}\n"
  },
  {
    "path": "src/wincompat/load_config.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld. All Rights Reserved.\n *\n * This here is a bit of a hack. We're compiling with subsystem=10.0 in the PE\n * header, and so the Windows loader expects to see either\n * _load_config.SecurityCookie set to the initial magic value, or for\n * IMAGE_GUARD_SECURITY_COOKIE_UNUSED to be set. libssp doesn't use\n * SecurityCookie anyway; SecurityCookie is for MSVC's /GS protection. So it\n * seems like the proper thing to do is signal to the OS that it doesn't need\n * to initialize SecurityCookie.\n */\n\n#include <windows.h>\n\n#define IMAGE_GUARD_SECURITY_COOKIE_UNUSED 0x00000800\nconst IMAGE_LOAD_CONFIG_DIRECTORY _load_config_used = {\n\t.Size = sizeof(_load_config_used),\n\t.GuardFlags = IMAGE_GUARD_SECURITY_COOKIE_UNUSED\n};\n"
  },
  {
    "path": "src/wincompat/loader.c",
    "content": "// SPDX-License-Identifier: GPL-2.0\n/*\n * Copyright (C) 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\n */\n\n#include <windows.h>\n#include <delayimp.h>\n\nstatic FARPROC WINAPI delayed_load_library_hook(unsigned dliNotify, PDelayLoadInfo pdli)\n{\n\tHMODULE library;\n\tif (dliNotify != dliNotePreLoadLibrary)\n\t\treturn NULL;\n\tlibrary = LoadLibraryExA(pdli->szDll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);\n\tif (!library)\n\t\tabort();\n\treturn (FARPROC)library;\n}\n\nPfnDliHook __pfnDliNotifyHook2 = delayed_load_library_hook;\n"
  },
  {
    "path": "src/wincompat/manifest.xml",
    "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=\"*\" name=\"wg\" type=\"win32\" />\n    <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n        <application>\n            <!-- Windows 10 and 11 -->\n            <supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\" />\n        </application>\n    </compatibility>\n    <trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v2\">\n        <security>\n            <requestedPrivileges xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n                <requestedExecutionLevel level=\"asInvoker\" />\n            </requestedPrivileges>\n        </security>\n    </trustInfo>\n</assembly>\n"
  },
  {
    "path": "src/wincompat/resources.rc",
    "content": "/* SPDX-License-Identifier: GPL-2.0\n *\n * Copyright (C) 2020-2026 Jason A. Donenfeld. All Rights Reserved.\n */\n\n#include <windows.h>\n\n#pragma code_page(65001) // UTF-8\n\nLANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL\nCREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST manifest.xml\n\n#define STRINGIZE(x) #x\n#define EXPAND(x) STRINGIZE(x)\n\nVS_VERSION_INFO VERSIONINFO\nFILEOS         VOS_NT_WINDOWS32\nFILETYPE       VFT_APP\nFILESUBTYPE    VFT2_UNKNOWN\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"040904b0\"\n    BEGIN\n      VALUE \"CompanyName\", \"WireGuard LLC\"\n      VALUE \"FileDescription\", \"WireGuard wg(8) CLI: Fast, Modern, Secure VPN Tunnel\"\n      VALUE \"FileVersion\", EXPAND(VERSION_STR)\n      VALUE \"InternalName\", \"wg\"\n      VALUE \"LegalCopyright\", \"Copyright © 2015-2026 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.\"\n      VALUE \"OriginalFilename\", \"wg.exe\"\n      VALUE \"ProductName\", \"WireGuard\"\n      VALUE \"ProductVersion\", EXPAND(VERSION_STR)\n      VALUE \"Comments\", \"https://www.wireguard.com/\"\n    END\n  END\n  BLOCK \"VarFileInfo\"\n  BEGIN\n    VALUE \"Translation\", 0x409, 0x4b0\n  END\nEND\n"
  }
]