diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72c62f8..16cf149 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,11 +4,13 @@ on: push: branches: - main + - dev paths-ignore: - "LICENSE" pull_request: branches: - main + - dev paths-ignore: - "LICENSE" diff --git a/Cargo.lock b/Cargo.lock index eb6a69f..6143813 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,9 +23,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -181,7 +181,7 @@ dependencies = [ "parking_lot", "ring", "socket2", - "thiserror", + "thiserror 2.0.17", "tracing", "uniffi", "untrusted", @@ -229,14 +229,14 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror", + "thiserror 2.0.17", ] [[package]] name = "cc" -version = "1.2.40" +version = "1.2.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb" +checksum = "ac9fe6cdbb24b6ade63616c0a0688e45bb56732262c158df3c0c4bea4ca47cb7" dependencies = [ "find-msvc-tools", "shlex", @@ -244,9 +244,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -291,9 +291,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.48" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f" dependencies = [ "clap_builder", "clap_derive", @@ -301,9 +301,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730" dependencies = [ "anstyle", "clap_lex", @@ -312,9 +312,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.47" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", @@ -324,9 +324,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "colorchoice" @@ -387,6 +387,7 @@ dependencies = [ "base64", "boringtun", "env_logger", + "ipnet", "libc", "log", "netlink-packet-core", @@ -398,9 +399,11 @@ dependencies = [ "nix", "serde", "serde_test", - "thiserror", + "thiserror 2.0.17", "tracing", "tracing-subscriber", + "windows", + "wireguard-nt", "x25519-dalek", ] @@ -417,9 +420,9 @@ dependencies = [ [[package]] name = "env_filter" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" dependencies = [ "log", "regex", @@ -451,7 +454,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -468,9 +471,9 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "find-msvc-tools" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" +checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" [[package]] name = "fs-err" @@ -483,9 +486,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.7" +version = "0.14.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" dependencies = [ "typenum", "version_check", @@ -499,19 +502,19 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi 0.11.1+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", "r-efi", - "wasi 0.14.7+wasi-0.2.4", + "wasip2", ] [[package]] @@ -599,6 +602,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e537132deb99c0eb4b752f0346b6a836200eaaa3516dd7e5514b63930a09e5d" +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -643,9 +652,19 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.176" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] [[package]] name = "linux-raw-sys" @@ -727,7 +746,7 @@ checksum = "3176f18d11a1ae46053e59ec89d46ba318ae1343615bd3f8c908bfc84edae35c" dependencies = [ "byteorder", "pastey", - "thiserror", + "thiserror 2.0.17", ] [[package]] @@ -778,11 +797,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -926,9 +945,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.3" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -938,9 +957,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -949,9 +968,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "ring" @@ -992,7 +1011,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1124,12 +1143,12 @@ checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1168,10 +1187,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -1183,13 +1202,33 @@ dependencies = [ "smawk", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -1463,15 +1502,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] - [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" @@ -1490,11 +1520,112 @@ dependencies = [ "nom", ] +[[package]] +name = "widestring" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] [[package]] name = "windows-sys" @@ -1520,7 +1651,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.4", + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", ] [[package]] @@ -1541,19 +1681,28 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.4" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" dependencies = [ "windows-link", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -1564,9 +1713,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -1576,9 +1725,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -1588,9 +1737,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -1600,9 +1749,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -1612,9 +1761,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -1624,9 +1773,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -1636,9 +1785,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -1648,9 +1797,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" @@ -1661,6 +1810,22 @@ dependencies = [ "memchr", ] +[[package]] +name = "wireguard-nt" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22b4dbcc6c93786cf22e420ef96e8976bfb92a455070282302b74de5848191f4" +dependencies = [ + "bitflags", + "getrandom 0.2.16", + "ipnet", + "libloading", + "log", + "thiserror 1.0.69", + "widestring", + "windows-sys 0.59.0", +] + [[package]] name = "wit-bindgen" version = "0.46.0" diff --git a/Cargo.toml b/Cargo.toml index 180befb..fe11c87 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,13 +24,24 @@ serde_test = "1.0" tracing = "0.1" tracing-subscriber = "0.3" -[target.'cfg(any(target_os = "freebsd", target_os = "macos", target_os = "netbsd"))'.dependencies] +[target.'cfg(any(target_family = "unix"))'.dependencies] boringtun = { path = "boringtun/boringtun", default-features = false, features = [ "device", ] } libc = { version = "0.2", default-features = false } nix = { version = "0.30", features = ["ioctl", "socket"] } + +[target.'cfg(target_os = "windows")'.dependencies] +ipnet = "2.11.0" +windows = { version = "0.62.1", features = [ + "Win32_NetworkManagement_IpHelper", + "Win32_NetworkManagement_Ndis", + "Win32_Networking_WinSock", + "Win32_System_Com", +]} +wireguard-nt = "0.5.0" + [target.'cfg(target_os = "linux")'.dependencies] netlink-packet-core = "0.8" netlink-packet-generic = "0.4" diff --git a/README.md b/README.md index 9e24472..cddbb61 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,37 @@ It was developed as part of [defguard](https://github.com/defguard/defguard) sec ### Windows support -Please note that [WireGuard](https://www.wireguard.com/install/) needs to be installed on Windows with commands `wg` and `wireguard` available to be called from the command line. +Please note that [WireGuard-NT](https://git.zx2c4.com/wireguard-nt/about/) [dll file](https://download.wireguard.com/wireguard-nt/) has to be placed under `resources-windows/binaries/wireguard.dll` path relative to your binary. + +#### Windows development + +For Windows development you'll need: + +1. The `stable-x86_64-pc-windows-gnu` Rust toolchain. Use `rustup` to change the toolchain: + +``` +rustup install stable-x86_64-pc-windows-gnu +rustup default stable-x86_64-pc-windows-gnu +``` + +2. Install [MSYS2](https://www.msys2.org/) + +3. Then run this in the MSYS2 terminal: + +``` +pacman -S --needed base-devel mingw-w64-ucrt-x86_64-toolchain mingw-w64-ucrt-x86_64-nasm +``` + +4. Finally add msys to your PATH: + +``` +# cmd +set PATH=C:\msys64\ucrt64\bin;%PATH% +# power-shell +$env:PATH = "C:\msys64\ucrt64\bin;" + $env:PATH +``` + +More info can be found [here](https://stackoverflow.com/a/79640980). ## Examples diff --git a/src/dependencies.rs b/src/dependencies.rs index 9e63789..2e0d33c 100644 --- a/src/dependencies.rs +++ b/src/dependencies.rs @@ -6,7 +6,7 @@ use crate::error::WireguardInterfaceError; const COMMANDS: [&str; 2] = ["resolvconf", "ip"]; #[cfg(target_os = "windows")] -const COMMANDS: [&str; 1] = [("wireguard.exe")]; +const COMMANDS: [&str; 0] = []; #[cfg(target_os = "macos")] const COMMANDS: [&str; 1] = ["networksetup"]; diff --git a/src/error.rs b/src/error.rs index 64775d0..7dc7b9b 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,6 +2,9 @@ use thiserror::Error; +#[cfg(target_os = "windows")] +use crate::wgapi_windows::WindowsError; + #[derive(Debug, Error)] #[non_exhaustive] pub enum WireguardInterfaceError { @@ -41,7 +44,10 @@ pub enum WireguardInterfaceError { ServiceRemovalFailed(String), #[error("Socket is closed: {0}")] SocketClosed(String), - #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "netbsd"))] + #[cfg(target_os = "windows")] + #[error(transparent)] + WindowsError(#[from] WindowsError), + #[cfg(target_family = "unix")] #[error("BoringTun {0}")] BoringTun(#[from] boringtun::device::Error), } diff --git a/src/wgapi.rs b/src/wgapi.rs index 55b5e15..14f040a 100644 --- a/src/wgapi.rs +++ b/src/wgapi.rs @@ -1,8 +1,10 @@ //! Shared multi-platform management API abstraction use std::marker::PhantomData; -#[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "netbsd"))] +#[cfg(target_family = "unix")] use boringtun::device::DeviceHandle; +#[cfg(target_os = "windows")] +use wireguard_nt::Adapter; #[cfg(feature = "check_dependencies")] use crate::dependencies::check_external_dependencies; @@ -17,8 +19,10 @@ pub struct Userspace; /// to detect the correct API implementation for most common platforms. pub struct WGApi { pub(super) ifname: String, - #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "netbsd"))] + #[cfg(target_family = "unix")] pub(super) device_handle: Option, + #[cfg(target_os = "windows")] + pub(super) adapter: Option, pub(super) _api: PhantomData, } @@ -29,8 +33,10 @@ impl WGApi { check_external_dependencies()?; Ok(WGApi { ifname: ifname.into(), - #[cfg(any(target_os = "freebsd", target_os = "macos", target_os = "netbsd"))] + #[cfg(target_family = "unix")] device_handle: None, + #[cfg(target_os = "windows")] + adapter: None, _api: PhantomData, }) } diff --git a/src/wgapi_windows.rs b/src/wgapi_windows.rs index 4d36e72..782e662 100644 --- a/src/wgapi_windows.rs +++ b/src/wgapi_windows.rs @@ -1,14 +1,27 @@ use std::{ - env, - fs::File, - io::{BufRead, BufReader, Cursor, Write}, - net::{IpAddr, SocketAddr}, - process::Command, + collections::HashMap, + net::IpAddr, str::FromStr, - thread::sleep, - time::{Duration, SystemTime}, + sync::{LazyLock, Mutex}, }; +use ipnet::{IpNet, Ipv4Net, Ipv6Net}; +use thiserror::Error; +use windows::{ + Win32::{ + Foundation::{ERROR_BUFFER_OVERFLOW, NO_ERROR}, + NetworkManagement::IpHelper::{ + DNS_INTERFACE_SETTINGS, DNS_INTERFACE_SETTINGS_VERSION1, DNS_SETTING_IPV6, + DNS_SETTING_NAMESERVER, GAA_FLAG_INCLUDE_PREFIX, GetAdaptersAddresses, + IP_ADAPTER_ADDRESSES_LH, SetInterfaceDnsSettings, + }, + Networking::WinSock::AF_UNSPEC, + System::Com::CLSIDFromString, + }, + core::{GUID, PCSTR, PCWSTR, PSTR}, +}; +use wireguard_nt::Wireguard; + use crate::{ InterfaceConfiguration, WireguardInterfaceApi, error::WireguardInterfaceError, @@ -18,10 +31,172 @@ use crate::{ wgapi::{Kernel, WGApi}, }; +static WIREGUARD_DLL_PATH: &str = "resources-windows/binaries/wireguard.dll"; +// Load wireguard.dll. Unsafe because we are loading an arbitrary dll file. +static WIREGUARD_DLL: LazyLock> = LazyLock::new(|| { + Mutex::new( + unsafe { wireguard_nt::load_from_path(WIREGUARD_DLL_PATH) } + .expect("Failed to load wireguard.dll"), + ) +}); + +#[derive(Debug, Error)] +pub enum WindowsError { + #[error("Empty interface array")] + EmptyInterfaceArrayError, + #[error("Invalid adapter id: {0}")] + InvalidAdapterId(String), + #[error("Non-zero return value: {0}")] + NonZeroReturnValue(u32), + #[error("Adapter not found: {0}")] + AdapterNotFound(String), + #[error(transparent)] + WireguardNtError(#[from] wireguard_nt::Error), + #[error(transparent)] + FromUtf16Error(#[from] std::string::FromUtf16Error), + #[error(transparent)] + FromUtf8Error(#[from] std::string::FromUtf8Error), + #[error(transparent)] + WindowsCoreError(#[from] windows::core::Error), + #[error("Missing peer endpoint for peer {0}")] + MissingPeerEndpoint(String), +} + +/// Converts a string representation of a GUID into a `windows::core::GUID`. +/// Example guid string: "{6B29FC40-CA47-1067-B31D-00DD010662DA}". +fn guid_from_str(s: &str) -> Result { + let wide = str_to_wide_null_terminated(s); + let guid = unsafe { CLSIDFromString(PCWSTR(wide.as_ptr())).map_err(WindowsError::from)? }; + Ok(guid) +} + +/// Returns the GUID of a network adapter given its name. +/// Example adapter name: "Ethernet", "WireGuard". +fn get_adapter_guid(adapter_name: &str) -> Result { + debug!("Finding adapter {adapter_name}"); + // We have to call `GetAdaptersAddresses` twice - first call to just get the `buffer_size` to hold the adapters. + // Before the second call we allocate the buffer with `buffer_size` capacity so that the call can actually + // store the adapters in the buffer. + let mut buffer_size: u32 = 0; + let mut result = unsafe { + // Sets the `buffer_size` + GetAdaptersAddresses( + AF_UNSPEC.0 as u32, + GAA_FLAG_INCLUDE_PREFIX, + None, + None, + &mut buffer_size, + ) + }; + + // We expect the overflow here, since `buffer_size = 0`. No overflow means no adapters. + if result != ERROR_BUFFER_OVERFLOW.0 { + return Err(WindowsError::EmptyInterfaceArrayError); + } + + // Allocate the buffer and actually get the adapters + let mut buffer = vec![0u8; buffer_size as usize]; + let addresses = buffer.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH; + result = unsafe { + GetAdaptersAddresses( + AF_UNSPEC.0 as u32, + GAA_FLAG_INCLUDE_PREFIX, + None, + Some(addresses), + &mut buffer_size, + ) + }; + if result != NO_ERROR.0 { + return Err(WindowsError::NonZeroReturnValue(result)); + } + + // Find our adapter + let mut current = buffer.as_ptr() as *const IP_ADAPTER_ADDRESSES_LH; + let mut guid: Option = None; + while !current.is_null() { + // SAFETY: + // `current` comes from the linked list allocated and initialized by + // `GetAdaptersAddresses`. The pointer is valid, properly aligned, + // non-null (checked above), and the backing `buffer` lives for the + // duration of this loop. No concurrent mutation occurs, so aliasing + // rules are respected. + let adapter = unsafe { &*current }; + + let friendly_name = unsafe { PCWSTR(adapter.FriendlyName.0).to_string()? }; + if friendly_name == adapter_name { + let adapter_name_str = unsafe { PCSTR(PSTR(adapter.AdapterName.0).0).to_string()? }; + guid = Some(guid_from_str(&adapter_name_str)?); + info!("Found adapter {adapter_name}, GUID: {guid:?}"); + break; + } + + current = adapter.Next; + } + + guid.ok_or_else(|| WindowsError::AdapterNotFound(adapter_name.to_string())) +} + +impl From for Peer { + fn from(peer: wireguard_nt::WireguardPeer) -> Self { + Self { + public_key: Key::new(peer.public_key), + preshared_key: Some(Key::new(peer.preshared_key)), + protocol_version: None, + endpoint: Some(peer.endpoint), + tx_bytes: peer.tx_bytes, + rx_bytes: peer.rx_bytes, + last_handshake: peer.last_handshake, + persistent_keepalive_interval: Some(peer.persistent_keepalive), + allowed_ips: peer + .allowed_ips + .iter() + .map(|ip| IpAddrMask::new(ip.addr(), ip.prefix_len())) + .collect(), + } + } +} + +impl From for Host { + fn from(iface: wireguard_nt::WireguardInterface) -> Self { + let mut peers = HashMap::new(); + for peer in iface.peers { + peers.insert(Key::new(peer.public_key), peer.into()); + } + Self { + listen_port: iface.listen_port, + private_key: Some(Key::new(iface.private_key)), + fwmark: None, + peers, + } + } +} + +/// Converts an str to wide (u16), null-terminated +fn str_to_wide_null_terminated(s: &str) -> Vec { + s.encode_utf16().chain(std::iter::once(0)).collect() +} + /// Manages interfaces created with Windows kernel using https://git.zx2c4.com/wireguard-nt. impl WireguardInterfaceApi for WGApi { fn create_interface(&mut self) -> Result<(), WireguardInterfaceError> { - info!("Opening/creating interface {}", self.ifname); + debug!("Opening/creating interface {}", self.ifname); + + // Try to open the adapter. If it's not present create it. + let wireguard = WIREGUARD_DLL.lock().expect("Failed to lock WIREGUARD_DLL"); + let adapter = match wireguard_nt::Adapter::open(&wireguard, &self.ifname) { + Ok(adapter) => { + debug!("Found existing adapter {}", self.ifname); + adapter + } + Err(_) => { + debug!("Adapter {} does not exist, creating", self.ifname); + wireguard_nt::Adapter::create(&wireguard, &self.ifname, &self.ifname, None) + .map_err(WindowsError::from)? + } + }; + self.adapter = Some(adapter); + + info!("Opened/created interface {}", self.ifname); Ok(()) } @@ -33,213 +208,78 @@ impl WireguardInterfaceApi for WGApi { fn configure_interface( &self, config: &InterfaceConfiguration, - dns: &[IpAddr], - search_domains: &[&str], ) -> Result<(), WireguardInterfaceError> { debug!( "Configuring interface {} with config: {config:?}", self.ifname ); - // Interface is created here so that there is no need to pass private key only for Windows - let file_name = format!("{}.conf", &self.ifname); - let path = env::current_dir()?; - let file_path_buf = path.join(&file_name); - let file_path = file_path_buf.to_str().unwrap_or_default(); - - debug!("Creating WireGuard configuration file {file_name} in: {file_path}"); - - let mut file = File::create(&file_name)?; + // Retrieve the adapter - should be created by calling `Self::create_interface` first. + let Some(ref adapter) = self.adapter else { + Err(WindowsError::AdapterNotFound(self.ifname.clone()))? + }; - debug!( - "WireGuard configuration file {file_name} created in {file_path}. Preparing configuration..." - ); - - let address = config - .addresses + // Prepare peers + debug!("Preparing peers for adapter {}", self.ifname); + let peers: Result, WindowsError> = config + .peers .iter() - .map(|addr| addr.to_string()) - .collect::>() - .join(","); - let mut wireguard_configuration = format!( - "[Interface]\nPrivateKey = {}\nAddress = {address}\n", - config.prvkey - ); - - if !dns.is_empty() { - // Format: - // DNS = , - // If search domains are present: - // DNS = ,,, - let dns_addresses = format!( - "\nDNS = {}{}", - // DNS addresses part - dns.iter() - .map(|v| v.to_string()) - .collect::>() - .join(","), - // Search domains part, optional - if !search_domains.is_empty() { - format!( - ",{}", - search_domains - .iter() - .map(|v| v.to_string()) - .collect::>() - .join(",") - ) - } else { - "".to_string() - } - ); - wireguard_configuration.push_str(dns_addresses.as_str()); - } - - for peer in &config.peers { - wireguard_configuration - .push_str(format!("\n[Peer]\nPublicKey = {}", peer.public_key).as_str()); - - if let Some(preshared_key) = &peer.preshared_key { - wireguard_configuration - .push_str(format!("\nPresharedKey = {}", preshared_key).as_str()); - } - - if let Some(keep_alive) = peer.persistent_keepalive_interval { - wireguard_configuration - .push_str(format!("\nPersistentKeepalive = {}", keep_alive).as_str()); - } - - if let Some(endpoint) = peer.endpoint { - wireguard_configuration.push_str(format!("\nEndpoint = {}", endpoint).as_str()); - } - - if !peer.allowed_ips.is_empty() { - let allowed_ips = format!( - "\nAllowedIPs = {}", - peer.allowed_ips + .map(|peer| { + Ok(wireguard_nt::SetPeer { + public_key: Some(peer.public_key.as_array()), + preshared_key: peer.preshared_key.as_ref().map(|key| key.as_array()), + keep_alive: peer.persistent_keepalive_interval, + allowed_ips: peer + .allowed_ips .iter() - .map(|v| v.to_string()) - .collect::>() - .join(",") - ); - wireguard_configuration.push_str(allowed_ips.as_str()); - } - } - + .filter_map(|ip| match ip.ip { + IpAddr::V4(addr) => Some(IpNet::V4(Ipv4Net::new(addr, ip.cidr).ok()?)), + IpAddr::V6(addr) => Some(IpNet::V6(Ipv6Net::new(addr, ip.cidr).ok()?)), + }) + .collect(), + endpoint: peer.endpoint.ok_or_else(|| { + WindowsError::MissingPeerEndpoint(peer.public_key.to_string()) + })?, + }) + }) + .collect(); + let peers = peers?; + + // Configure the interface + debug!("Applying configuration for adapter {}", self.ifname); + let interface = wireguard_nt::SetInterface { + listen_port: Some(config.port as u16), + public_key: None, // derived from private key + private_key: Some(Key::from_str(&config.prvkey)?.as_array()), + peers, + }; + adapter.set_config(&interface).map_err(WindowsError::from)?; + + // Set adapter addresses debug!( - "WireGuard configuration prepared: {wireguard_configuration}, writing to the file at {file_path}..." + "Assigning addresses to adapter {}: {:?}", + self.ifname, config.addresses ); - file.write_all(wireguard_configuration.as_bytes())?; - info!("WireGuard configuration written to file: {file_path}",); - - // Check for existing service and remove it - debug!( - "Checking for existing wireguard service for interface {}", - self.ifname - ); - let output = Command::new("wg") - .arg("show") - .arg(&self.ifname) - .output() - .map_err(|err| { - error!("Failed to read interface data. Error: {err}"); - WireguardInterfaceError::ReadInterfaceError(err.to_string()) - })?; - debug!("WireGuard service check output: {output:?}",); - - // Service already exists - if output.status.success() { - debug!("Service already exists, removing it first"); - Command::new("wireguard") - .arg("/uninstalltunnelservice") - .arg(&self.ifname) - .output()?; - - debug!("Waiting for service to be removed"); - let mut counter = 1; - loop { - // Occasionally the tunnel is still available even though wg show cannot find it, causing /installtunnelservice to fail - // This might be excessive as closing the application closes the WireGuard tunnel. - sleep(Duration::from_secs(1)); - - let output = Command::new("wg") - .arg("show") - .arg(&self.ifname) - .output() - .map_err(|err| { - error!("Failed to read interface data. Error: {err}"); - WireguardInterfaceError::ReadInterfaceError(err.to_string()) - })?; - - // Service has been removed - if !output.status.success() || counter == 5 { - break; - } - - counter += 1; - } - debug!( - "Finished waiting for service to be removed, the service is considered to be removed, proceeding further" - ); - } - - debug!("Installing the new service for interface {}", self.ifname); - let service_installation_output = Command::new("wireguard") - .arg("/installtunnelservice") - .arg(file_path) - .output() - .map_err(|err| { - error!("Failed to create interface. Error: {err}"); - WireguardInterfaceError::ServiceInstallationFailed(err.to_string()) - })?; - - debug!( - "Done installing the new service. Service installation output: {service_installation_output:?}", - ); - - if !service_installation_output.status.success() { - let message = format!( - "Failed to install WireGuard tunnel as a Windows service: {:?}", - service_installation_output.stdout - ); - return Err(WireguardInterfaceError::ServiceInstallationFailed(message)); - } - - debug!( - "Disabling automatic restart for interface {} tunnel service", - self.ifname - ); - let service_update_output = Command::new("sc") - .arg("config") - .arg(format!("WireGuardTunnel${}", self.ifname)) - .arg("start=demand") - .output() - .map_err(|err| { - error!("Failed to configure tunnel service. Error: {err}"); - WireguardInterfaceError::ServiceInstallationFailed(err.to_string()) - })?; - - debug!( - "Done disabling automatic restart for the new service. Service update output: {service_update_output:?}", - ); - if !service_update_output.status.success() { - let message = format!( - "Failed to configure WireGuard tunnel service: {:?}", - service_update_output.stdout - ); - return Err(WireguardInterfaceError::ServiceInstallationFailed(message)); - } - - // TODO: set maximum transfer unit (MTU) + let addresses: Vec<_> = config + .addresses + .iter() + .filter_map(|ip| match ip.ip { + IpAddr::V4(addr) => Some(IpNet::V4(Ipv4Net::new(addr, ip.cidr).ok()?)), + IpAddr::V6(addr) => Some(IpNet::V6(Ipv6Net::new(addr, ip.cidr).ok()?)), + }) + .collect(); + adapter + .set_default_route(&addresses, &interface) + .map_err(WindowsError::from)?; + + // Bring the adapter up + debug!("Bringing up adapter {}", self.ifname); + adapter.up().map_err(WindowsError::from)?; info!( "Interface {} has been successfully configured.", self.ifname ); - debug!( - "Interface {} configured with config: {config:?}", - self.ifname - ); Ok(()) } @@ -247,26 +287,9 @@ impl WireguardInterfaceApi for WGApi { Ok(()) } - fn remove_interface(&self) -> Result<(), WireguardInterfaceError> { + fn remove_interface(&mut self) -> Result<(), WireguardInterfaceError> { debug!("Removing interface {}", self.ifname); - - let command_output = Command::new("wireguard") - .arg("/uninstalltunnelservice") - .arg(&self.ifname) - .output() - .map_err(|err| { - error!("Failed to remove interface. Error: {err}"); - WireguardInterfaceError::CommandExecutionFailed(err) - })?; - - if !command_output.status.success() { - let message = format!( - "Failed to remove WireGuard tunnel service: {:?}", - command_output.stdout - ); - return Err(WireguardInterfaceError::ServiceRemovalFailed(message)); - } - + self.adapter = None; info!("Interface {} removed successfully", self.ifname); Ok(()) } @@ -287,67 +310,11 @@ impl WireguardInterfaceApi for WGApi { fn read_interface_data(&self) -> Result { debug!("Reading host info for interface {}", self.ifname); - let output = Command::new("wg") - .arg("show") - .arg(&self.ifname) - .arg("dump") - .output() - .map_err(|err| { - error!("Failed to read interface. Error: {err}"); - WireguardInterfaceError::CommandExecutionFailed(err) - })?; - - let reader = BufReader::new(Cursor::new(output.stdout)); - let mut host = Host::default(); - let lines = reader.lines(); - - for (index, line_result) in lines.enumerate() { - let line = match &line_result { - Ok(line) => line, - Err(_err) => { - continue; - } - }; - - let data: Vec<&str> = line.split("\t").collect(); - - // First line contains [Interface] section data, every other line is a separate [Peer] - if index == 0 { - // Interface data: private key, public key, listen port, fwmark - host.private_key = Key::from_str(data[0]).ok(); - host.listen_port = data[2].parse().unwrap_or_default(); - - if data[3] != "off" { - host.fwmark = Some(data[3].parse().unwrap()); - } - } else { - // Peer data: public key, preshared key, endpoint, allowed ips, latest handshake, transfer-rx, transfer-tx, persistent-keepalive - if let Ok(public_key) = Key::from_str(data[0]) { - let mut peer = Peer::new(public_key.clone()); - - if data[1] != "(none)" { - peer.preshared_key = Key::from_str(data[0]).ok(); - } - - peer.endpoint = SocketAddr::from_str(data[2]).ok(); - - for allowed_ip in data[3].split(",") { - let addr = IpAddrMask::from_str(allowed_ip.trim())?; - peer.allowed_ips.push(addr); - } - - let handshake = peer.last_handshake.get_or_insert(SystemTime::UNIX_EPOCH); - *handshake += Duration::from_secs(data[4].parse().unwrap_or_default()); - - peer.rx_bytes = data[5].parse().unwrap_or_default(); - peer.tx_bytes = data[6].parse().unwrap_or_default(); - peer.persistent_keepalive_interval = data[7].parse().ok(); - - host.peers.insert(public_key.clone(), peer); - } - } - } - + // Retrieve the adapter - should be created by calling `Self::create_interface` first. + let Some(ref adapter) = self.adapter else { + Err(WindowsError::AdapterNotFound(self.ifname.clone()))? + }; + let host = adapter.get_config().into(); debug!("Read interface data: {host:?}"); Ok(host) } @@ -355,12 +322,63 @@ impl WireguardInterfaceApi for WGApi { fn configure_dns( &self, dns: &[IpAddr], - _search_domains: &[&str], + search_domains: &[&str], ) -> Result<(), WireguardInterfaceError> { debug!( "Configuring DNS for interface {}, using address: {dns:?}", self.ifname ); + let guid = get_adapter_guid(&self.ifname)?; + let (ipv4_dns_ips, ipv6_dns_ips): (Vec<&IpAddr>, Vec<&IpAddr>) = + dns.iter().partition(|ip| ip.is_ipv4()); + let ipv4_dns_servers: Vec = ipv4_dns_ips.iter().map(|ip| ip.to_string()).collect(); + let ipv6_dns_servers: Vec = ipv6_dns_ips.iter().map(|ip| ip.to_string()).collect(); + + let mut search_domains_vec: Vec = + str_to_wide_null_terminated(&search_domains.join(",")); + let search_domains_wide = windows::core::PWSTR(search_domains_vec.as_mut_ptr()); + + if !ipv4_dns_servers.is_empty() { + let dns_str = ipv4_dns_servers.join(","); + let mut wide: Vec = str_to_wide_null_terminated(&dns_str); + let name_server = windows::core::PWSTR(wide.as_mut_ptr()); + + let settings = DNS_INTERFACE_SETTINGS { + Version: DNS_INTERFACE_SETTINGS_VERSION1, + Flags: DNS_SETTING_NAMESERVER as u64, + NameServer: name_server, + SearchList: search_domains_wide, + ..Default::default() + }; + + let status = unsafe { SetInterfaceDnsSettings(guid, &settings) }; + if status != NO_ERROR { + Err(WindowsError::NonZeroReturnValue(status.0))?; + } + } + if !ipv6_dns_servers.is_empty() { + let dns_str = ipv6_dns_servers.join(","); + let mut wide: Vec = str_to_wide_null_terminated(&dns_str); + let name_server = windows::core::PWSTR(wide.as_mut_ptr()); + + let settings = DNS_INTERFACE_SETTINGS { + Version: DNS_INTERFACE_SETTINGS_VERSION1, + Flags: (DNS_SETTING_NAMESERVER | DNS_SETTING_IPV6) as u64, + NameServer: name_server, + SearchList: search_domains_wide, + ..Default::default() + }; + + let status = unsafe { SetInterfaceDnsSettings(guid, &settings) }; + if status != NO_ERROR { + Err(WindowsError::NonZeroReturnValue(status.0))?; + } + } + + info!( + "Configured DNS for interface {}, using address: {dns:?}", + self.ifname + ); Ok(()) } } diff --git a/src/wireguard_interface.rs b/src/wireguard_interface.rs index ace1999..bd6a37f 100644 --- a/src/wireguard_interface.rs +++ b/src/wireguard_interface.rs @@ -24,25 +24,20 @@ pub trait WireguardInterfaceApi { } /// Updates configuration of an existing WireGuard interface. - #[cfg(not(target_os = "windows"))] - fn configure_interface( - &self, - config: &InterfaceConfiguration, - ) -> Result<(), WireguardInterfaceError>; - - #[cfg(target_os = "windows")] fn configure_interface( &self, config: &InterfaceConfiguration, - dns: &[IpAddr], - search_domains: &[&str], ) -> Result<(), WireguardInterfaceError>; /// Removes the WireGuard interface being managed. /// /// Meant to be used in `drop` method for a given API struct. + #[cfg(not(target_os = "windows"))] fn remove_interface(&self) -> Result<(), WireguardInterfaceError>; + #[cfg(target_os = "windows")] + fn remove_interface(&mut self) -> Result<(), WireguardInterfaceError>; + /// Adds a peer or updates peer configuration. fn configure_peer(&self, peer: &Peer) -> Result<(), WireguardInterfaceError>;