diff options
| author | David Lönnhager <david.l@mullvad.net> | 2022-04-05 12:56:08 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2022-04-05 12:56:08 +0200 |
| commit | bc883b92c6230fc1b3271a2a562426d1e8d43ef7 (patch) | |
| tree | c539f495ca231fcfdb1da616fd2a0272bb56d8d1 | |
| parent | 59332378ec52e1899c173e22072ce14daf9153e8 (diff) | |
| parent | c1a4b8309aac43a33176887e0629283560c0e7f8 (diff) | |
| download | mullvadvpn-bc883b92c6230fc1b3271a2a562426d1e8d43ef7.tar.xz mullvadvpn-bc883b92c6230fc1b3271a2a562426d1e8d43ef7.zip | |
Merge branch 'use-library-sslocal'
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | Cargo.lock | 269 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | README.md | 6 | ||||
| m--------- | dist-assets/binaries | 0 | ||||
| -rw-r--r-- | docs/security.md | 2 | ||||
| -rw-r--r-- | gui/tasks/distribution.js | 3 | ||||
| -rw-r--r-- | mullvad-api/Cargo.toml | 2 | ||||
| -rw-r--r-- | talpid-core/Cargo.toml | 1 | ||||
| -rw-r--r-- | talpid-core/src/proxy/mod.rs | 70 | ||||
| -rw-r--r-- | talpid-core/src/proxy/noop.rs | 46 | ||||
| -rw-r--r-- | talpid-core/src/proxy/shadowsocks.rs | 353 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 13 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/openvpn/mod.rs | 318 |
14 files changed, 578 insertions, 508 deletions
diff --git a/.gitignore b/.gitignore index b75c65b33e..163b0ac9da 100644 --- a/.gitignore +++ b/.gitignore @@ -25,8 +25,6 @@ /dist-assets/talpid_openvpn_plugin.dll /dist-assets/openvpn /dist-assets/openvpn.exe -/dist-assets/sslocal -/dist-assets/sslocal.exe /dist-assets/shell-completions/ /dist-assets/aarch64-apple-darwin/ /dist-assets/x86_64-apple-darwin/ diff --git a/Cargo.lock b/Cargo.lock index f03c8d1445..3505f87adb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,7 +23,7 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" dependencies = [ - "generic-array", + "generic-array 0.14.4", ] [[package]] @@ -36,7 +36,7 @@ dependencies = [ "cipher", "cpufeatures", "ctr", - "opaque-debug", + "opaque-debug 0.3.0", ] [[package]] @@ -87,6 +87,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" [[package]] +name = "arc-swap" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d78ce20460b82d3fa150275ed9d55e21064fc7951177baacf86a145c4a4b1f" + +[[package]] name = "ascii" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -176,11 +182,32 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" dependencies = [ - "generic-array", + "generic-array 0.14.4", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", ] [[package]] @@ -190,6 +217,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9df67f7bf9ef8498769f994239c45613ef0c5899415fb58e9add412d2c1a538" [[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] name = "byte_string" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -276,7 +309,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array", + "generic-array 0.14.4", ] [[package]] @@ -371,7 +404,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03c6a1d5fa1de37e071642dfa44ec552ca5b299adb128fab16138e24b548fd21" dependencies = [ - "generic-array", + "generic-array 0.14.4", "rand_core 0.6.3", "subtle", "zeroize", @@ -383,7 +416,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0" dependencies = [ - "generic-array", + "generic-array 0.14.4", ] [[package]] @@ -530,11 +563,20 @@ dependencies = [ [[package]] name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + +[[package]] +name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.4", ] [[package]] @@ -543,9 +585,9 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b" dependencies = [ - "block-buffer", + "block-buffer 0.10.0", "crypto-common", - "generic-array", + "generic-array 0.14.4", "subtle", ] @@ -617,7 +659,7 @@ dependencies = [ "base16ct", "crypto-bigint", "der", - "generic-array", + "generic-array 0.14.4", "rand_core 0.6.3", "sec1", "subtle", @@ -636,7 +678,19 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c5f0096a91d210159eceb2ff5e1c4da18388a170e1e3ce948aac9c8fdbbf595" dependencies = [ - "heck", + "heck 0.3.3", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "enum-as-inner" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73" +dependencies = [ + "heck 0.4.0", "proc-macro2", "quote", "syn", @@ -730,6 +784,12 @@ dependencies = [ ] [[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] name = "fern" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -904,6 +964,15 @@ dependencies = [ [[package]] name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" @@ -940,7 +1009,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99" dependencies = [ - "opaque-debug", + "opaque-debug 0.3.0", "polyval", ] @@ -985,6 +1054,12 @@ dependencies = [ ] [[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1237,6 +1312,15 @@ dependencies = [ ] [[package]] +name = "iprange" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37209be0ad225457e63814401415e748e2453a5297f9b637338f5fb8afa4ec00" +dependencies = [ + "ipnet", +] + +[[package]] name = "itertools" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1295,7 +1379,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66a28c447e7a02784315280fb972e692b21ae7c18a44bfb37fce670946dc2dba" dependencies = [ - "heck", + "heck 0.3.3", "proc-macro2", "quote", "syn", @@ -1311,6 +1395,17 @@ dependencies = [ ] [[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] name = "kernel32-sys" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1390,6 +1485,18 @@ dependencies = [ ] [[package]] +name = "lru_time_cache" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9106e1d747ffd48e6be5bb2d97fa706ed25b144fbee4d5c02eae110cd8d6badd" + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] name = "match_cfg" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1963,6 +2070,12 @@ checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" [[package]] name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "opaque-debug" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" @@ -2109,6 +2222,40 @@ dependencies = [ ] [[package]] +name = "pest_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" +dependencies = [ + "maplit", + "pest", + "sha-1", +] + +[[package]] name = "petgraph" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2221,7 +2368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" dependencies = [ "cpufeatures", - "opaque-debug", + "opaque-debug 0.3.0", "universal-hash", ] @@ -2233,7 +2380,7 @@ checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1" dependencies = [ "cfg-if 1.0.0", "cpufeatures", - "opaque-debug", + "opaque-debug 0.3.0", "universal-hash", ] @@ -2305,7 +2452,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "355f634b43cdd80724ee7848f95770e7e70eefa6dcf14fea676216573b8fd603" dependencies = [ "bytes", - "heck", + "heck 0.3.3", "itertools", "log", "multimap", @@ -2531,7 +2678,7 @@ dependencies = [ "cc", "libc", "once_cell", - "spin", + "spin 0.5.2", "untrusted", "web-sys", "winapi 0.3.9", @@ -2539,20 +2686,19 @@ dependencies = [ [[package]] name = "ring-compat" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f9cf4a178de62d388e6502dae2101b37c1becf65227bf1210e6cf12dc633a3" +checksum = "6242f589b69a0555addb0bb759f81e5cba40485d38b36f780ab3a588b2bdf064" dependencies = [ "aead", "digest 0.9.0", "ecdsa", "ed25519", - "generic-array", - "opaque-debug", + "generic-array 0.14.4", + "opaque-debug 0.3.0", "p256", "p384", "ring", - "zeroize", ] [[package]] @@ -2678,7 +2824,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08da66b8b0965a5555b6bd6639e68ccba85e1e2506f5fbb089e93f8a04e1a2d1" dependencies = [ "der", - "generic-array", + "generic-array 0.14.4", "subtle", "zeroize", ] @@ -2790,6 +2936,18 @@ dependencies = [ ] [[package]] +name = "sha-1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", +] + +[[package]] name = "sha1" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2812,9 +2970,8 @@ dependencies = [ [[package]] name = "shadowsocks" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "356770455d9fb911a6b559ceedaa7f2d34e47d2d8ae606041dffba390abb7522" +version = "1.14.2" +source = "git+https://github.com/shadowsocks/shadowsocks-rust?rev=7388ddfb7d36d5b84908c476daabc91c8b065a37#7388ddfb7d36d5b84908c476daabc91c8b065a37" dependencies = [ "async-trait", "base64", @@ -2842,9 +2999,9 @@ dependencies = [ [[package]] name = "shadowsocks-crypto" -version = "0.3.1" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "748be76f2786bcf817d86c08c6cc2245e436e3ae0b064b581e3d4fc81700360c" +checksum = "dd381517e3eb8fec5090696debfdea972d8afe6fc926c26c7bfd5fee9053efbd" dependencies = [ "aes", "aes-gcm", @@ -2859,6 +3016,38 @@ dependencies = [ ] [[package]] +name = "shadowsocks-service" +version = "1.14.3" +source = "git+https://github.com/shadowsocks/shadowsocks-rust?rev=7388ddfb7d36d5b84908c476daabc91c8b065a37#7388ddfb7d36d5b84908c476daabc91c8b065a37" +dependencies = [ + "arc-swap", + "async-trait", + "byte_string", + "byteorder", + "bytes", + "cfg-if 1.0.0", + "futures", + "idna", + "ipnet", + "iprange", + "json5", + "libc", + "log", + "lru_time_cache", + "nix 0.23.1", + "once_cell", + "pin-project", + "regex", + "serde", + "shadowsocks", + "socket2", + "spin 0.9.2", + "thiserror", + "tokio", + "winapi 0.3.9", +] + +[[package]] name = "shared_child" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2937,6 +3126,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] +name = "spin" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +dependencies = [ + "lock_api", +] + +[[package]] name = "strsim" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3048,6 +3246,7 @@ dependencies = [ "regex", "resolv-conf", "rtnetlink", + "shadowsocks-service", "shell-escape", "socket2", "subslice", @@ -3469,14 +3668,14 @@ dependencies = [ [[package]] name = "trust-dns-proto" -version = "0.21.0-alpha.5" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df4689a56fb36e79b76d13f52056c116f1f014afb1bd330162f2d9dc08ef5405" +checksum = "2861b3ed517888174d13909e675c4e94b3291867512068be59d76533e4d1270c" dependencies = [ "async-trait", "cfg-if 1.0.0", "data-encoding", - "enum-as-inner", + "enum-as-inner 0.4.0", "futures-channel", "futures-io", "futures-util", @@ -3495,9 +3694,9 @@ dependencies = [ [[package]] name = "trust-dns-resolver" -version = "0.21.0-alpha.5" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f2ce3a81fcddc72de8da5852c4ea0a5507bd129c0aa20a2354a1b1e51d6813a" +checksum = "d9e737a252a617bd4774649e245dbf705e207275db0893b9fa824d49f074fc1c" dependencies = [ "cfg-if 1.0.0", "futures-util", @@ -3523,7 +3722,7 @@ dependencies = [ "async-trait", "bytes", "cfg-if 1.0.0", - "enum-as-inner", + "enum-as-inner 0.3.3", "env_logger 0.9.0", "futures-executor", "futures-util", @@ -3624,7 +3823,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" dependencies = [ - "generic-array", + "generic-array 0.14.4", "subtle", ] diff --git a/Cargo.toml b/Cargo.toml index 51a87a03d5..d5fff68b4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,6 @@ members = [ "mullvad-management-interface", "tunnel-obfuscation", ] -exclude = ["dist-assets/binaries/shadowsocks-rust"] [profile.release] opt-level = 3 @@ -84,7 +84,7 @@ your checkout, you can find our developer keys on [Mullvad's Open Source page]. ### Binaries submodule This repository has a git submodule at `dist-assets/binaries`. This submodule contains binaries and -build scripts for third party code we need to bundle with the app. Such as OpenVPN, Shadowsocks +build scripts for third party code we need to bundle with the app. Such as OpenVPN, Wintun etc. This submodule conforms to the same integrity/security standards as this repository. Every merge @@ -391,11 +391,11 @@ echo "org.gradle.jvmargs=-Xmx4608M" >> ~/.gradle/gradle.properties cargo build ``` -1. Copy the OpenVPN and Shadowsocks binaries, and our plugin for it, to the directory we will +1. Copy the OpenVPN binaries, and our plugin for it, to the directory we will use as resource directory. If you want to use any other directory, you would need to copy even more files. ```bash - cp dist-assets/binaries/<platform>/{openvpn, sslocal}[.exe] dist-assets/ + cp dist-assets/binaries/<platform>/openvpn[.exe] dist-assets/ cp target/debug/*talpid_openvpn_plugin* dist-assets/ ``` diff --git a/dist-assets/binaries b/dist-assets/binaries -Subproject 973ee47bec89df537b8ecae20235071055693ec +Subproject ba0efeca54538a3191173ff70cc48ab715ba270 diff --git a/docs/security.md b/docs/security.md index b85b5ef0e6..e4e77414ad 100644 --- a/docs/security.md +++ b/docs/security.md @@ -150,7 +150,7 @@ Examples: or any process running as `root`, and incoming matching traffic. 1. Connecting to the same VPN server, but via a bridge. The bridge is at IP `e.f.g.h` and the proxy service listens on TCP port `443` - Allow traffic to `e.f.g.h:443/TCP` for - `sslocal.exe` or any process running as `root`, and incoming matching + `mullvad-daemon.exe` or any process running as `root`, and incoming matching traffic. Do not allow any direct communication with the VPN server. 1. Connecting to `a.b.c.d` port `1234` using WireGuard: Allow `a.b.c.d:1234/UDP` for `mullvad-daemon.exe` or any process running as `root`. diff --git a/gui/tasks/distribution.js b/gui/tasks/distribution.js index dcdde80841..b7b3889a26 100644 --- a/gui/tasks/distribution.js +++ b/gui/tasks/distribution.js @@ -83,7 +83,6 @@ const config = { to: '.', }, { from: distAssets(path.join('binaries', '${env.TARGET_TRIPLE}', 'openvpn')), to: '.' }, - { from: distAssets(path.join('binaries', '${env.TARGET_TRIPLE}', 'sslocal')), to: '.' }, { from: distAssets('uninstall_macos.sh'), to: './uninstall.sh' }, { from: distAssets('shell-completions/_mullvad'), to: '.' }, { from: distAssets('shell-completions/mullvad.fish'), to: '.' }, @@ -140,7 +139,6 @@ const config = { to: '.', }, { from: distAssets('binaries/x86_64-pc-windows-msvc/openvpn.exe'), to: '.' }, - { from: distAssets('binaries/x86_64-pc-windows-msvc/sslocal.exe'), to: '.' }, { from: root('build/lib/x86_64-pc-windows-msvc/libwg.dll'), to: '.' }, { from: distAssets('binaries/x86_64-pc-windows-msvc/wintun/wintun.dll'), to: '.' }, { @@ -162,7 +160,6 @@ const config = { { from: distAssets('mullvad-setup'), to: '.' }, { from: distAssets('libtalpid_openvpn_plugin.so'), to: '.' }, { from: distAssets('binaries/x86_64-unknown-linux-gnu/openvpn'), to: '.' }, - { from: distAssets('binaries/x86_64-unknown-linux-gnu/sslocal'), to: '.' }, { from: distAssets('linux/mullvad-daemon.conf'), to: '.' }, { from: distAssets('linux/mullvad-daemon.service'), to: '.' }, ], diff --git a/mullvad-api/Cargo.toml b/mullvad-api/Cargo.toml index d73aa0f046..cf3c3debd1 100644 --- a/mullvad-api/Cargo.toml +++ b/mullvad-api/Cargo.toml @@ -34,7 +34,7 @@ lazy_static = "1.1.0" mullvad-types = { path = "../mullvad-types" } talpid-types = { path = "../talpid-types" } -shadowsocks = { version = "1.12", default-features = false, features = ["stream-cipher"] } +shadowsocks = { git = "https://github.com/shadowsocks/shadowsocks-rust", rev = "7388ddfb7d36d5b84908c476daabc91c8b065a37", default-features = false, features = ["stream-cipher"] } [target.'cfg(target_os="macos")'.dependencies] tokio-stream = { version = "0.1", features = ["io-util"] } diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index 7ff3b8721f..e1ea19e3cf 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -32,6 +32,7 @@ tokio = { version = "1.8", features = ["process", "rt-multi-thread", "fs"] } tokio-stream = { version = "0.1", features = ["io-util"] } rand = "0.7" tunnel-obfuscation = { path = "../tunnel-obfuscation" } +shadowsocks-service = { git = "https://github.com/shadowsocks/shadowsocks-rust", rev = "7388ddfb7d36d5b84908c476daabc91c8b065a37", default-features = false, features = ["local", "stream-cipher"] } [target.'cfg(not(target_os="android"))'.dependencies] byteorder = "1" diff --git a/talpid-core/src/proxy/mod.rs b/talpid-core/src/proxy/mod.rs index f16c84da5f..dd0e0b6cc9 100644 --- a/talpid-core/src/proxy/mod.rs +++ b/talpid-core/src/proxy/mod.rs @@ -1,22 +1,29 @@ +mod noop; mod shadowsocks; -pub use std::io::Result; - use self::shadowsocks::ShadowsocksProxyMonitor; -use std::{fmt, path::PathBuf, sync::mpsc}; +use async_trait::async_trait; +use std::{fmt, io, path::PathBuf}; use talpid_types::net::openvpn; -pub enum WaitResult { +#[derive(err_derive::Error, Debug)] +pub enum Error { + #[error(display = "Monitor exited unexpectedly: {}", _0)] UnexpectedExit(String), - ProperShutdown, + + #[error(display = "I/O error")] + Io(io::Error), } +pub type Result<T> = std::result::Result<T, Error>; + +#[async_trait] pub trait ProxyMonitor: Send { /// Create a handle than can be used to ask the proxy service to shut down. fn close_handle(&mut self) -> Box<dyn ProxyMonitorCloseHandle>; /// Consume monitor and wait for proxy service to shut down. - fn wait(self: Box<Self>) -> Result<WaitResult>; + async fn wait(self: Box<Self>) -> Result<()>; /// The port bound to. fn port(&self) -> u16; @@ -32,47 +39,6 @@ pub trait ProxyMonitorCloseHandle: Send { fn close(self: Box<Self>) -> Result<()>; } -struct NoopProxyMonitor { - tx: mpsc::Sender<()>, - rx: mpsc::Receiver<()>, - port: u16, -} - -impl NoopProxyMonitor { - fn start(port: u16) -> Result<Self> { - let (tx, rx) = mpsc::channel(); - Ok(NoopProxyMonitor { tx, rx, port }) - } -} - -impl ProxyMonitor for NoopProxyMonitor { - fn close_handle(&mut self) -> Box<dyn ProxyMonitorCloseHandle> { - Box::new(NoopProxyMonitorCloseHandle { - tx: self.tx.clone(), - }) - } - - fn wait(self: Box<Self>) -> Result<WaitResult> { - let _ = self.rx.recv(); - Ok(WaitResult::ProperShutdown) - } - - fn port(&self) -> u16 { - self.port - } -} - -struct NoopProxyMonitorCloseHandle { - tx: mpsc::Sender<()>, -} - -impl ProxyMonitorCloseHandle for NoopProxyMonitorCloseHandle { - fn close(self: Box<Self>) -> Result<()> { - let _ = self.tx.send(()); - Ok(()) - } -} - /// Variables that define the environment to help /// proxy implementations find their way around. /// TODO: Move struct to wider scope and use more generic name. @@ -81,23 +47,25 @@ pub struct ProxyResourceData { pub log_dir: Option<PathBuf>, } -pub fn start_proxy( +pub async fn start_proxy( settings: &openvpn::ProxySettings, resource_data: &ProxyResourceData, ) -> Result<Box<dyn ProxyMonitor>> { match settings { openvpn::ProxySettings::Local(local_settings) => { // These are generic proxy settings with the proxy client not managed by us. - Ok(Box::new(NoopProxyMonitor::start(local_settings.port)?)) + Ok(Box::new(noop::NoopProxyMonitor::start( + local_settings.port, + )?)) } openvpn::ProxySettings::Remote(remote_settings) => { // These are generic proxy settings with the proxy client not managed by us. - Ok(Box::new(NoopProxyMonitor::start( + Ok(Box::new(noop::NoopProxyMonitor::start( remote_settings.address.port(), )?)) } openvpn::ProxySettings::Shadowsocks(ss_settings) => Ok(Box::new( - ShadowsocksProxyMonitor::start(ss_settings, resource_data)?, + ShadowsocksProxyMonitor::start(ss_settings, resource_data).await?, )), } } diff --git a/talpid-core/src/proxy/noop.rs b/talpid-core/src/proxy/noop.rs new file mode 100644 index 0000000000..9eb96e0c95 --- /dev/null +++ b/talpid-core/src/proxy/noop.rs @@ -0,0 +1,46 @@ +use async_trait::async_trait; +use futures::{channel::mpsc, StreamExt}; + +use super::{ProxyMonitor, ProxyMonitorCloseHandle, Result}; + +pub struct NoopProxyMonitor { + tx: mpsc::UnboundedSender<()>, + rx: mpsc::UnboundedReceiver<()>, + port: u16, +} + +impl NoopProxyMonitor { + pub fn start(port: u16) -> Result<Self> { + let (tx, rx) = mpsc::unbounded(); + Ok(NoopProxyMonitor { tx, rx, port }) + } +} + +#[async_trait] +impl ProxyMonitor for NoopProxyMonitor { + fn close_handle(&mut self) -> Box<dyn ProxyMonitorCloseHandle> { + Box::new(NoopProxyMonitorCloseHandle { + tx: self.tx.clone(), + }) + } + + async fn wait(mut self: Box<Self>) -> Result<()> { + let _ = self.rx.next().await; + Ok(()) + } + + fn port(&self) -> u16 { + self.port + } +} + +struct NoopProxyMonitorCloseHandle { + tx: mpsc::UnboundedSender<()>, +} + +impl ProxyMonitorCloseHandle for NoopProxyMonitorCloseHandle { + fn close(self: Box<Self>) -> Result<()> { + let _ = self.tx.unbounded_send(()); + Ok(()) + } +} diff --git a/talpid-core/src/proxy/shadowsocks.rs b/talpid-core/src/proxy/shadowsocks.rs index 6436e967c5..54efbe1205 100644 --- a/talpid-core/src/proxy/shadowsocks.rs +++ b/talpid-core/src/proxy/shadowsocks.rs @@ -1,284 +1,139 @@ -pub use std::io::Result; +pub use std::io; -use crate::logging; -use regex::Regex; +use async_trait::async_trait; +use futures::future::{abortable, AbortHandle, Aborted}; +use socket2::{Domain, SockAddr, Socket}; +use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; +use tokio::task::JoinHandle; -use std::{ - borrow::Cow, - env, - ffi::OsString, - fmt, - fs::File, - io::{BufRead, Error, ErrorKind}, - net::{IpAddr, Ipv4Addr, SocketAddr}, - path::PathBuf, - sync::{ - atomic::{AtomicBool, Ordering}, - Arc, +use shadowsocks_service::{ + config::{Config, ConfigType, LocalConfig, ProtocolType}, + local, + shadowsocks::{ + config::{Mode, ServerConfig}, + ServerAddr, }, - thread, - time::Duration, }; -use super::{ProxyMonitor, ProxyMonitorCloseHandle, ProxyResourceData, WaitResult}; -use talpid_types::net::openvpn::ShadowsocksProxySettings; -#[cfg(target_os = "linux")] -use talpid_types::ErrorExt; - -struct ShadowsocksCommand { - shadowsocks_bin: OsString, - local: Option<SocketAddr>, - peer: Option<SocketAddr>, - peer_password: Option<String>, - // This should map to the shadowsocks-rust `CipherType` type. - cipher: Option<String>, -} - -impl ShadowsocksCommand { - pub fn new(shadowsocks_bin: OsString) -> Self { - ShadowsocksCommand { - shadowsocks_bin, - local: None, - peer: None, - peer_password: None, - cipher: None, - } - } - - pub fn local(&mut self, local: SocketAddr) -> &mut Self { - self.local = Some(local); - self - } - - pub fn peer(&mut self, peer: SocketAddr) -> &mut Self { - self.peer = Some(peer); - self - } - - pub fn peer_password(&mut self, password: String) -> &mut Self { - self.peer_password = Some(password); - self - } - - pub fn cipher(&mut self, cipher: String) -> &mut Self { - self.cipher = Some(cipher); - self - } - - pub fn build(&self) -> duct::Expression { - log::debug!("Building expression: {}", &self); - duct::cmd(&self.shadowsocks_bin, self.get_arguments()).unchecked() - } - - fn get_arguments(&self) -> Vec<String> { - let mut args: Vec<String> = vec![]; - - // Always activate TCP no-delay. - args.push("--no-delay".to_owned()); - - if let Some(ref local) = self.local { - args.push("--local-addr".to_owned()); - args.push(format!("{}:{}", local.ip(), local.port())); - } - - if let Some(ref peer) = self.peer { - args.push("--server-addr".to_owned()); - args.push(format!("{}:{}", peer.ip(), peer.port())); - } - - if let Some(ref peer_password) = self.peer_password { - args.push("--password".to_owned()); - args.push(peer_password.to_owned()); - } - - if let Some(ref cipher) = self.cipher { - args.push("--encrypt-method".to_owned()); - args.push(cipher.to_string()); - } - - args - } -} - -impl fmt::Display for ShadowsocksCommand { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt.write_str(&shell_escape::escape( - self.shadowsocks_bin.to_string_lossy(), - ))?; - for arg in &self.get_arguments() { - fmt.write_str(" ")?; - fmt.write_str(&shell_escape::escape(Cow::from(arg)))?; - } - Ok(()) - } -} +use super::{Error, ProxyMonitor, ProxyMonitorCloseHandle, ProxyResourceData}; +use talpid_types::{net::openvpn::ShadowsocksProxySettings, ErrorExt}; pub struct ShadowsocksProxyMonitor { - subproc: Arc<ProcessHandle>, - closed: Arc<AtomicBool>, port: u16, + server_join_handle: Option<JoinHandle<Result<io::Result<()>, Aborted>>>, + server_abort_handle: AbortHandle, } -const SHADOWSOCKS_LOG_FILENAME: &str = "shadowsocks.log"; -#[cfg(unix)] -const SHADOWSOCKS_BIN_FILENAME: &str = "sslocal"; -#[cfg(windows)] -const SHADOWSOCKS_BIN_FILENAME: &str = "sslocal.exe"; - -struct ProcessHandle { - subproc: duct::Handle, -} - -impl Drop for ProcessHandle { - fn drop(&mut self) { - let _ = self.subproc.kill(); +impl ShadowsocksProxyMonitor { + pub async fn start( + settings: &ShadowsocksProxySettings, + _resource_data: &ProxyResourceData, + ) -> super::Result<Self> { + Self::start_inner(settings).await.map_err(Error::Io) } -} -impl std::ops::Deref for ProcessHandle { - type Target = duct::Handle; + async fn start_inner(settings: &ShadowsocksProxySettings) -> io::Result<Self> { + // TODO: Patch shadowsocks so the bound address can be obtained afterwards. + let addr = SocketAddrV4::new(Ipv4Addr::LOCALHOST, 0); + let sock = Socket::new( + Domain::IPV4, + socket2::Type::STREAM, + Some(socket2::Protocol::TCP), + )?; + sock.set_reuse_address(true)?; + sock.bind(&SockAddr::from(addr))?; - fn deref(&self) -> &Self::Target { - &self.subproc - } -} - -impl ShadowsocksProxyMonitor { - pub fn start( - settings: &ShadowsocksProxySettings, - resource_data: &ProxyResourceData, - ) -> Result<Self> { - let binary = resource_data - .resource_dir - .join(SHADOWSOCKS_BIN_FILENAME) - .into_os_string(); + let bound_addr = sock + .local_addr()? + .as_socket_ipv4() + .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "missing IPv4 address"))?; - let mut cmd = ShadowsocksCommand::new(binary) - .local(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0)) - .peer(settings.peer) - .peer_password(settings.password.clone()) - .cipher(settings.cipher.clone()) - .build(); + let mut config = Config::new(ConfigType::Local); - let log_dir: PathBuf = if let Some(ref log_dir) = resource_data.log_dir { - log_dir.clone() - } else { - env::temp_dir() - }; + config.fast_open = true; - let logfile = log_dir.join(SHADOWSOCKS_LOG_FILENAME); + let mut local = LocalConfig::new(ProtocolType::Socks); + local.mode = Mode::TcpOnly; + local.addr = Some(ServerAddr::SocketAddr(SocketAddr::from(bound_addr))); - logging::rotate_log(&logfile) - .map_err(|_| Error::new(ErrorKind::Other, "Failed to rotate log file"))?; + config.local.push(local); - cmd = cmd.stdin_null().stderr_to_stdout().stdout_path(&logfile); + let server = ServerConfig::new( + settings.peer, + settings.password.clone(), + settings.cipher.parse().map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + format!("Invalid cipher: {}", settings.cipher), + ) + })?, + ); - let subproc = cmd.start()?; + config.server.push(server); #[cfg(target_os = "linux")] { - // Run this process outside the tunnel - use crate::split_tunnel::PidManager; - - let excluded_pids = PidManager::new().map_err(|error| { - Error::new( - ErrorKind::Other, - error.display_chain_with_msg("Failed to initialize PidManager"), - ) - })?; - for pid in subproc.pids() { - excluded_pids.add(pid as i32).map_err(|error| { - Error::new( - ErrorKind::Other, - error.display_chain_with_msg("Failed to exclude Shadowsocks process"), - ) - })?; - } + config.outbound_fwmark = Some(crate::linux::TUNNEL_FW_MARK); } - match Self::get_bound_port(File::open(&logfile)?, &subproc) { - Ok(port) => Ok(Self { - subproc: Arc::new(ProcessHandle { subproc }), - closed: Arc::new(AtomicBool::new(false)), - port, - }), - Err(err) => { - let _ = subproc.kill(); - Err(err) - } - } - } - - fn get_bound_port(logfile: File, subproc: &duct::Handle) -> Result<u16> { - let mut buffered_reader = std::io::BufReader::new(logfile); + let srv = local::create(config).await?; - for _tries in 0..5 { - loop { - // `read_line` appends to the buffer so keep a small scope for the `line` variable. - let mut line = String::new(); - match buffered_reader.read_line(&mut line) { - Ok(bytes_read) => { - if bytes_read == 0 { - break; - } - // `read_line` includes the line break in the returned line. - if let Ok(port) = Self::parse_port(line.trim_end()) { - return Ok(port); - } - } - Err(_) => { - break; - } - } + let (fut, server_abort_handle) = abortable(async move { + let _ = sock; + let result = srv.wait_until_exit().await; + if let Err(error) = &result { + log::error!( + "{}", + error.display_chain_with_msg("sslocal stopped with an error") + ); } - if subproc.try_wait().unwrap().is_some() { - break; - } - thread::sleep(Duration::from_secs(1)); - } + result + }); + let server_join_handle = tokio::spawn(fut); - Err(Error::new( - ErrorKind::Other, - "Could not determine which port Shadowsocks has bound to", - )) + Ok(Self { + port: bound_addr.port(), + server_join_handle: Some(server_join_handle), + server_abort_handle, + }) } +} - fn parse_port(logline: &str) -> Result<u16> { - // TODO: Compile once and reuse. - let re = Regex::new(r"(?:TCP listening on \d+\.\d+\.\d+\.\d+:)(\d+$)").unwrap(); - - if let Some(captures) = re.captures(logline) { - return Ok(captures[1].parse().map_err(|_| { - Error::new(ErrorKind::Other, "Failed to parse port number string") - })?); - } - - Err(Error::new(ErrorKind::Other, "No port number present")) +impl Drop for ShadowsocksProxyMonitor { + fn drop(&mut self) { + self.server_abort_handle.abort(); } } +#[async_trait] impl ProxyMonitor for ShadowsocksProxyMonitor { fn close_handle(&mut self) -> Box<dyn ProxyMonitorCloseHandle> { Box::new(ShadowsocksProxyMonitorCloseHandle { - subproc: self.subproc.clone(), - closed: self.closed.clone(), + server_abort_handle: self.server_abort_handle.clone(), }) } - fn wait(self: Box<Self>) -> Result<WaitResult> { - self.subproc.wait().map(|output| { - if self.closed.load(Ordering::SeqCst) { - Ok(WaitResult::ProperShutdown) - } else { - Ok(WaitResult::UnexpectedExit( - if let Some(exit_code) = output.status.code() { - format!("Exit code: {}", exit_code) - } else { - "Exit code is indeterminable".to_string() - }, - )) + async fn wait(mut self: Box<Self>) -> super::Result<()> { + if let Some(join_handle) = self.server_join_handle.take() { + match join_handle.await { + Ok(Err(Aborted)) => Ok(()), + + Err(join_err) if join_err.is_cancelled() => Ok(()), + Err(_) => Err(Error::UnexpectedExit( + "Shadowsocks task panicked".to_string(), + )), + + Ok(Ok(result)) => match result { + Ok(()) => Err(Error::UnexpectedExit("Exited without error".to_string())), + Err(error) => Err(Error::UnexpectedExit(format!( + "Error: {}", + error.display_chain() + ))), + }, } - })? + } else { + Ok(()) + } } fn port(&self) -> u16 { @@ -286,17 +141,13 @@ impl ProxyMonitor for ShadowsocksProxyMonitor { } } -pub struct ShadowsocksProxyMonitorCloseHandle { - subproc: Arc<ProcessHandle>, - closed: Arc<AtomicBool>, +struct ShadowsocksProxyMonitorCloseHandle { + server_abort_handle: AbortHandle, } impl ProxyMonitorCloseHandle for ShadowsocksProxyMonitorCloseHandle { - fn close(self: Box<Self>) -> Result<()> { - if !self.closed.swap(true, Ordering::SeqCst) { - self.subproc.kill() - } else { - Ok(()) - } + fn close(self: Box<Self>) -> super::Result<()> { + self.server_abort_handle.abort(); + Ok(()) } } diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index ca6665114f..95b9bd808e 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -122,7 +122,7 @@ impl TunnelMonitor { match tunnel_parameters { #[cfg(not(target_os = "android"))] - TunnelParameters::OpenVpn(config) => Self::start_openvpn_tunnel( + TunnelParameters::OpenVpn(config) => runtime.block_on(Self::start_openvpn_tunnel( &config, log_file, resource_dir, @@ -130,7 +130,7 @@ impl TunnelMonitor { tunnel_close_rx, #[cfg(target_os = "linux")] route_manager, - ), + )), #[cfg(target_os = "android")] TunnelParameters::OpenVpn(_) => Err(Error::UnsupportedPlatform), @@ -156,7 +156,9 @@ impl TunnelMonitor { TunnelParameters::OpenVpn(params) => { if let Some(proxy) = ¶ms.proxy { match proxy { - openvpn_types::ProxySettings::Shadowsocks(..) => "sslocal.exe", + openvpn_types::ProxySettings::Shadowsocks(..) => { + return std::env::current_exe().unwrap() + } _ => "openvpn.exe", } } else { @@ -204,7 +206,7 @@ impl TunnelMonitor { } #[cfg(not(target_os = "android"))] - fn start_openvpn_tunnel<L>( + async fn start_openvpn_tunnel<L>( config: &openvpn_types::TunnelParameters, log: Option<PathBuf>, resource_dir: &Path, @@ -226,7 +228,8 @@ impl TunnelMonitor { tunnel_close_rx, #[cfg(target_os = "linux")] route_manager, - )?; + ) + .await?; Ok(TunnelMonitor { monitor: InternalTunnelMonitor::OpenVpn(monitor), }) diff --git a/talpid-core/src/tunnel/openvpn/mod.rs b/talpid-core/src/tunnel/openvpn/mod.rs index 446ce13d79..cb885fb8ec 100644 --- a/talpid-core/src/tunnel/openvpn/mod.rs +++ b/talpid-core/src/tunnel/openvpn/mod.rs @@ -119,7 +119,7 @@ pub enum Error { /// Failures related to the proxy service. #[error(display = "Unable to start the proxy service")] - StartProxyError(#[error(source)] io::Error), + StartProxyError(#[error(source)] proxy::Error), /// Error while monitoring proxy service #[error(display = "Error while monitoring proxy service")] @@ -183,7 +183,7 @@ pub struct OpenVpnMonitor<C: OpenVpnBuilder = OpenVpnCommand> { /// Keep the 'TempFile' for the proxy user-pass file in the struct, so it's removed on drop. _proxy_auth_file: Option<mktemp::TempFile>, - runtime: tokio::runtime::Runtime, + runtime: tokio::runtime::Handle, event_server_abort_tx: triggered::Trigger, server_join_handle: Option<task::JoinHandle<std::result::Result<(), event_server::Error>>>, @@ -241,10 +241,20 @@ impl WintunContext for WintunContextImpl { } } +#[cfg(windows)] +impl WintunContextImpl { + fn alias(&self) -> Result<U16CString> { + self.adapter + .adapter() + .name() + .map_err(Error::WintunFindAlias) + } +} + impl OpenVpnMonitor<OpenVpnCommand> { /// Creates a new `OpenVpnMonitor` with the given listener and using the plugin at the given /// path. - pub fn start<L>( + pub async fn start<L>( on_event: L, params: &openvpn::TunnelParameters, log_path: Option<PathBuf>, @@ -280,29 +290,10 @@ impl OpenVpnMonitor<OpenVpnCommand> { log_dir, }; - let proxy_monitor = Self::start_proxy(¶ms.proxy, &proxy_resources)?; + let proxy_monitor = Self::start_proxy(¶ms.proxy, &proxy_resources).await?; #[cfg(windows)] - let dll = wintun::WintunDll::instance(resource_dir).map_err(Error::WintunDllError)?; - #[cfg(windows)] - let wintun_logger = dll.activate_logging(); - - #[cfg(windows)] - let (wintun_adapter, _reboot_required) = wintun::TemporaryWintunAdapter::create( - dll.clone(), - &*ADAPTER_ALIAS, - &*ADAPTER_POOL, - Some(ADAPTER_GUID.clone()), - ) - .map_err(Error::WintunCreateAdapterError)?; - - #[cfg(windows)] - let adapter_alias = wintun_adapter - .adapter() - .name() - .map_err(Error::WintunFindAlias)?; - #[cfg(windows)] - log::debug!("Adapter alias: {}", adapter_alias.to_string_lossy()); + let wintun = Self::new_wintun_context(params, resource_dir)?; let cmd = Self::create_openvpn_cmd( params, @@ -311,7 +302,7 @@ impl OpenVpnMonitor<OpenVpnCommand> { resource_dir, &proxy_monitor, #[cfg(windows)] - adapter_alias.to_os_string(), + wintun.alias()?.to_os_string(), )?; let plugin_path = Self::get_plugin_path(resource_dir)?; @@ -342,12 +333,32 @@ impl OpenVpnMonitor<OpenVpnCommand> { proxy_monitor, tunnel_close_rx, #[cfg(windows)] - Box::new(WintunContextImpl { - adapter: wintun_adapter, - wait_v6_interface: params.generic_options.enable_ipv6, - _logger: wintun_logger, - }), + Box::new(wintun), + ) + .await + } + + #[cfg(windows)] + fn new_wintun_context( + params: &openvpn::TunnelParameters, + resource_dir: &Path, + ) -> Result<WintunContextImpl> { + let dll = wintun::WintunDll::instance(resource_dir).map_err(Error::WintunDllError)?; + let wintun_logger = dll.activate_logging(); + + let (wintun_adapter, _reboot_required) = wintun::TemporaryWintunAdapter::create( + dll.clone(), + &*ADAPTER_ALIAS, + &*ADAPTER_POOL, + Some(ADAPTER_GUID.clone()), ) + .map_err(Error::WintunCreateAdapterError)?; + + Ok(WintunContextImpl { + adapter: wintun_adapter, + wait_v6_interface: params.generic_options.enable_ipv6, + _logger: wintun_logger, + }) } } @@ -363,7 +374,7 @@ fn extract_routes(env: &HashMap<String, String>) -> Result<HashSet<RequiredRoute } impl<C: OpenVpnBuilder + Send + 'static> OpenVpnMonitor<C> { - fn new_internal<L>( + async fn new_internal<L>( mut cmd: C, event_server_abort_tx: triggered::Trigger, event_server_abort_rx: triggered::Listener, @@ -379,33 +390,9 @@ impl<C: OpenVpnBuilder + Send + 'static> OpenVpnMonitor<C> { where L: event_server::OpenvpnEventProxy + Send + Sync + 'static, { - let uuid = uuid::Uuid::new_v4().to_string(); - let ipc_path = if cfg!(windows) { - format!("//./pipe/talpid-openvpn-{}", uuid) - } else { - format!("/tmp/talpid-openvpn-{}", uuid) - }; - - let runtime = tokio::runtime::Builder::new_multi_thread() - .worker_threads(1) - .enable_all() - .build() - .map_err(Error::RuntimeError)?; - - let (start_tx, start_rx) = mpsc::channel(); - let server_join_handle = runtime.spawn(event_server::start( - ipc_path.clone(), - start_tx, - on_event, - event_server_abort_rx, - )); - if let Err(_) = start_rx.recv() { - return Err(runtime - .block_on(server_join_handle) - .expect("Failed to resolve quit handle future") - .map_err(Error::EventDispatcherError) - .unwrap_err()); - } + let (server_join_handle, ipc_path) = event_server::start(on_event, event_server_abort_rx) + .await + .map_err(Error::EventDispatcherError)?; #[cfg(windows)] let wintun = Arc::new(wintun); @@ -417,9 +404,7 @@ impl<C: OpenVpnBuilder + Send + 'static> OpenVpnMonitor<C> { #[cfg(windows)] wintun.clone(), )); - let spawn_task = runtime.spawn(spawn_task); - - let handle = runtime.handle().clone(); + let spawn_task = tokio::spawn(spawn_task); let monitor = OpenVpnMonitor { spawn_task: Some(spawn_task), @@ -430,7 +415,7 @@ impl<C: OpenVpnBuilder + Send + 'static> OpenVpnMonitor<C> { _user_pass_file: user_pass_file, _proxy_auth_file: proxy_auth_file, - runtime, + runtime: tokio::runtime::Handle::current(), event_server_abort_tx, server_join_handle: Some(server_join_handle), @@ -439,7 +424,7 @@ impl<C: OpenVpnBuilder + Send + 'static> OpenVpnMonitor<C> { }; let close_handle = monitor.close_handle(); - handle.spawn(async move { + tokio::spawn(async move { if tunnel_close_rx.await.is_ok() { tokio::task::spawn_blocking(move || { if let Err(error) = close_handle.close() { @@ -490,16 +475,20 @@ impl<C: OpenVpnBuilder + Send + 'static> OpenVpnMonitor<C> { enum Stopped { Tunnel(Result<()>), - Proxy(proxy::Result<proxy::WaitResult>), + Proxy(proxy::Result<()>), } + let handle = self.runtime.clone(); + thread::spawn(move || { tx_tunnel.send(Stopped::Tunnel(self.wait_tunnel())).unwrap(); let _ = proxy_close_handle.close(); }); thread::spawn(move || { - tx_proxy.send(Stopped::Proxy(proxy_monitor.wait())).unwrap(); + tx_proxy + .send(Stopped::Proxy(handle.block_on(proxy_monitor.wait()))) + .unwrap(); let _ = tunnel_close_handle.close(); }); @@ -508,18 +497,10 @@ impl<C: OpenVpnBuilder + Send + 'static> OpenVpnMonitor<C> { match result { Stopped::Tunnel(tunnel_result) => tunnel_result, - Stopped::Proxy(proxy_result) => { - // The proxy should never exit before openvpn. - match proxy_result { - Ok(proxy::WaitResult::ProperShutdown) => { - Err(Error::ProxyExited("No details".to_owned())) - } - Ok(proxy::WaitResult::UnexpectedExit(details)) => { - Err(Error::ProxyExited(details)) - } - Err(err) => Err(err).map_err(Error::MonitorProxyError), - } - } + Stopped::Proxy(proxy_result) => proxy_result.map_err(|error| match error { + proxy::Error::UnexpectedExit(details) => Error::ProxyExited(details), + proxy::Error::Io(error) => Error::MonitorProxyError(error), + }), } } else { // No proxy active, wait only for the tunnel. @@ -634,13 +615,14 @@ impl<C: OpenVpnBuilder + Send + 'static> OpenVpnMonitor<C> { } /// Starts a proxy service, as applicable. - fn start_proxy( + async fn start_proxy( proxy_settings: &Option<openvpn::ProxySettings>, proxy_resources: &ProxyResourceData, ) -> Result<Option<Box<dyn ProxyMonitor>>> { if let Some(ref settings) = proxy_settings { - let proxy_monitor = - proxy::start_proxy(settings, proxy_resources).map_err(Error::StartProxyError)?; + let proxy_monitor = proxy::start_proxy(settings, proxy_resources) + .await + .map_err(Error::StartProxyError)?; return Ok(Some(proxy_monitor)); } Ok(None) @@ -1059,23 +1041,31 @@ mod event_server { } pub async fn start<L>( - ipc_path: String, - server_start_tx: std::sync::mpsc::Sender<()>, event_proxy: L, abort_rx: triggered::Listener, - ) -> std::result::Result<(), Error> + ) -> std::result::Result<(tokio::task::JoinHandle<Result<(), Error>>, String), Error> where L: OpenvpnEventProxy + Sync + Send + 'static, { - let endpoint = IpcEndpoint::new(ipc_path); - let incoming = endpoint.incoming().map_err(Error::StartServer)?; - let _ = server_start_tx.send(()); + let uuid = uuid::Uuid::new_v4().to_string(); + let ipc_path = if cfg!(windows) { + format!("//./pipe/talpid-openvpn-{}", uuid) + } else { + format!("/tmp/talpid-openvpn-{}", uuid) + }; - Server::builder() - .add_service(OpenvpnEventProxyServer::new(event_proxy)) - .serve_with_incoming_shutdown(incoming.map_ok(StreamBox), abort_rx) - .await - .map_err(Error::TonicError) + let endpoint = IpcEndpoint::new(ipc_path.clone()); + let incoming = endpoint.incoming().map_err(Error::StartServer)?; + Ok(( + tokio::spawn(async move { + Server::builder() + .add_service(OpenvpnEventProxyServer::new(event_proxy)) + .serve_with_incoming_shutdown(incoming.map_ok(StreamBox), abort_rx) + .await + .map_err(Error::TonicError) + }), + ipc_path, + )) } #[derive(Debug)] @@ -1223,12 +1213,21 @@ mod tests { } } + fn new_runtime() -> Result<tokio::runtime::Runtime> { + tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) + .enable_all() + .build() + .map_err(Error::RuntimeError) + } + #[test] fn sets_plugin() { let builder = TestOpenVpnBuilder::default(); let (event_server_abort_tx, event_server_abort_rx) = triggered::trigger(); let (_close_tx, close_rx) = oneshot::channel(); - let _ = OpenVpnMonitor::new_internal( + let runtime = new_runtime().unwrap(); + let _ = runtime.block_on(OpenVpnMonitor::new_internal( builder.clone(), event_server_abort_tx, event_server_abort_rx, @@ -1241,7 +1240,7 @@ mod tests { close_rx, #[cfg(windows)] Box::new(TestWintunContext {}), - ); + )); assert_eq!( Some(PathBuf::from("./my_test_plugin")), *builder.plugin.lock() @@ -1253,7 +1252,8 @@ mod tests { let builder = TestOpenVpnBuilder::default(); let (event_server_abort_tx, event_server_abort_rx) = triggered::trigger(); let (_close_tx, close_rx) = oneshot::channel(); - let _ = OpenVpnMonitor::new_internal( + let runtime = new_runtime().unwrap(); + let _ = runtime.block_on(OpenVpnMonitor::new_internal( builder.clone(), event_server_abort_tx, event_server_abort_rx, @@ -1266,7 +1266,7 @@ mod tests { close_rx, #[cfg(windows)] Box::new(TestWintunContext {}), - ); + )); assert_eq!( Some(PathBuf::from("./my_test_log_file")), *builder.log.lock() @@ -1279,21 +1279,23 @@ mod tests { builder.process_handle = Some(TestProcessHandle(0)); let (event_server_abort_tx, event_server_abort_rx) = triggered::trigger(); let (_close_tx, close_rx) = oneshot::channel(); - let testee = OpenVpnMonitor::new_internal( - builder, - event_server_abort_tx, - event_server_abort_rx, - TestOpenvpnEventProxy {}, - "".into(), - None, - TempFile::new(), - None, - None, - close_rx, - #[cfg(windows)] - Box::new(TestWintunContext {}), - ) - .unwrap(); + let runtime = new_runtime().unwrap(); + let testee = runtime + .block_on(OpenVpnMonitor::new_internal( + builder, + event_server_abort_tx, + event_server_abort_rx, + TestOpenvpnEventProxy {}, + "".into(), + None, + TempFile::new(), + None, + None, + close_rx, + #[cfg(windows)] + Box::new(TestWintunContext {}), + )) + .unwrap(); assert!(testee.wait().is_ok()); } @@ -1303,21 +1305,23 @@ mod tests { builder.process_handle = Some(TestProcessHandle(1)); let (event_server_abort_tx, event_server_abort_rx) = triggered::trigger(); let (_close_tx, close_rx) = oneshot::channel(); - let testee = OpenVpnMonitor::new_internal( - builder, - event_server_abort_tx, - event_server_abort_rx, - TestOpenvpnEventProxy {}, - "".into(), - None, - TempFile::new(), - None, - None, - close_rx, - #[cfg(windows)] - Box::new(TestWintunContext {}), - ) - .unwrap(); + let runtime = new_runtime().unwrap(); + let testee = runtime + .block_on(OpenVpnMonitor::new_internal( + builder, + event_server_abort_tx, + event_server_abort_rx, + TestOpenvpnEventProxy {}, + "".into(), + None, + TempFile::new(), + None, + None, + close_rx, + #[cfg(windows)] + Box::new(TestWintunContext {}), + )) + .unwrap(); assert!(testee.wait().is_err()); } @@ -1327,21 +1331,23 @@ mod tests { builder.process_handle = Some(TestProcessHandle(1)); let (event_server_abort_tx, event_server_abort_rx) = triggered::trigger(); let (_close_tx, close_rx) = oneshot::channel(); - let testee = OpenVpnMonitor::new_internal( - builder, - event_server_abort_tx, - event_server_abort_rx, - TestOpenvpnEventProxy {}, - "".into(), - None, - TempFile::new(), - None, - None, - close_rx, - #[cfg(windows)] - Box::new(TestWintunContext {}), - ) - .unwrap(); + let runtime = new_runtime().unwrap(); + let testee = runtime + .block_on(OpenVpnMonitor::new_internal( + builder, + event_server_abort_tx, + event_server_abort_rx, + TestOpenvpnEventProxy {}, + "".into(), + None, + TempFile::new(), + None, + None, + close_rx, + #[cfg(windows)] + Box::new(TestWintunContext {}), + )) + .unwrap(); testee.close_handle().close().unwrap(); assert!(testee.wait().is_ok()); } @@ -1351,21 +1357,23 @@ mod tests { let builder = TestOpenVpnBuilder::default(); let (event_server_abort_tx, event_server_abort_rx) = triggered::trigger(); let (_close_tx, close_rx) = oneshot::channel(); - let result = OpenVpnMonitor::new_internal( - builder, - event_server_abort_tx, - event_server_abort_rx, - TestOpenvpnEventProxy {}, - "".into(), - None, - TempFile::new(), - None, - None, - close_rx, - #[cfg(windows)] - Box::new(TestWintunContext {}), - ) - .unwrap(); + let runtime = new_runtime().unwrap(); + let result = runtime + .block_on(OpenVpnMonitor::new_internal( + builder, + event_server_abort_tx, + event_server_abort_rx, + TestOpenvpnEventProxy {}, + "".into(), + None, + TempFile::new(), + None, + None, + close_rx, + #[cfg(windows)] + Box::new(TestWintunContext {}), + )) + .unwrap(); match result.wait() { Err(Error::StartProcessError) => (), _ => panic!("Wrong error"), |
