summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2020-06-16 15:13:32 +0200
committerDavid Lönnhager <david.l@mullvad.net>2020-08-20 14:41:41 +0200
commit8cf02b29a718a7856c80323ee0cf496b9ee24648 (patch)
tree16f378f2cc3d3101d01d58435bf54824e57683a6
parentc2e9303cc7aff29df7941fc08df19b8ffcffa48f (diff)
downloadmullvadvpn-8cf02b29a718a7856c80323ee0cf496b9ee24648.tar.xz
mullvadvpn-8cf02b29a718a7856c80323ee0cf496b9ee24648.zip
Use gRPC for management interface in backend and CLI
-rw-r--r--Cargo.lock390
-rw-r--r--Cargo.toml4
-rw-r--r--mullvad-cli/Cargo.toml15
-rw-r--r--mullvad-cli/build.rs2
-rw-r--r--mullvad-cli/src/cmds/account.rs82
-rw-r--r--mullvad-cli/src/cmds/auto_connect.rs21
-rw-r--r--mullvad-cli/src/cmds/beta_program.rs13
-rw-r--r--mullvad-cli/src/cmds/block_when_disconnected.rs26
-rw-r--r--mullvad-cli/src/cmds/bridge.rs218
-rw-r--r--mullvad-cli/src/cmds/connect.rs9
-rw-r--r--mullvad-cli/src/cmds/disconnect.rs9
-rw-r--r--mullvad-cli/src/cmds/lan.rs21
-rw-r--r--mullvad-cli/src/cmds/reconnect.rs9
-rw-r--r--mullvad-cli/src/cmds/relay.rs387
-rw-r--r--mullvad-cli/src/cmds/reset.rs9
-rw-r--r--mullvad-cli/src/cmds/split_tunnel/linux.rs32
-rw-r--r--mullvad-cli/src/cmds/status.rs252
-rw-r--r--mullvad-cli/src/cmds/tunnel.rs206
-rw-r--r--mullvad-cli/src/cmds/version.rs20
-rw-r--r--mullvad-cli/src/format.rs20
-rw-r--r--mullvad-cli/src/location.rs41
-rw-r--r--mullvad-cli/src/main.rs49
-rw-r--r--mullvad-daemon/Cargo.toml19
-rw-r--r--mullvad-daemon/build.rs2
-rw-r--r--mullvad-daemon/proto/management_interface.proto466
-rw-r--r--mullvad-daemon/src/lib.rs2
-rw-r--r--mullvad-daemon/src/logging.rs5
-rw-r--r--mullvad-daemon/src/main.rs11
-rw-r--r--mullvad-daemon/src/management_interface.rs2217
-rw-r--r--mullvad-daemon/src/rpc_uniqueness_check.rs30
-rw-r--r--mullvad-daemon/src/windows_permissions.rs (renamed from talpid-ipc/src/win.rs)0
-rw-r--r--mullvad-daemon/src/wireguard.rs2
-rw-r--r--mullvad-ipc-client/Cargo.toml27
-rw-r--r--mullvad-ipc-client/src/lib.rs281
-rw-r--r--mullvad-setup/Cargo.toml10
-rw-r--r--mullvad-setup/build.rs2
-rw-r--r--mullvad-setup/src/main.rs53
-rw-r--r--mullvad-tests/Cargo.toml2
-rw-r--r--mullvad-types/Cargo.toml1
-rw-r--r--mullvad-types/src/account.rs12
-rw-r--r--mullvad-types/src/custom_tunnel.rs4
-rw-r--r--mullvad-types/src/lib.rs20
-rw-r--r--mullvad-types/src/relay_constraints.rs17
-rw-r--r--talpid-core/Cargo.toml3
-rw-r--r--talpid-ipc/Cargo.toml33
-rw-r--r--talpid-ipc/src/lib.rs122
-rw-r--r--talpid-ipc/tests/ipc-client-server.rs85
47 files changed, 3098 insertions, 2163 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1c7a581d0d..64dc79c378 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -74,11 +74,6 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "assert_matches"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
name = "async-stream"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -99,7 +94,7 @@ dependencies = [
[[package]]
name = "async-trait"
-version = "0.1.31"
+version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -180,14 +175,6 @@ dependencies = [
]
[[package]]
-name = "bstr"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
name = "bumpalo"
version = "3.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -785,30 +772,6 @@ version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "globset"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "aho-corasick 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "globset"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
name = "h2"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1027,20 +990,6 @@ dependencies = [
[[package]]
name = "jsonrpc-client-core"
version = "0.5.0"
-source = "git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b#68aac55b6ddff5e1242594b54f7f9149fe215ff7"
-dependencies = [
- "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
- "jsonrpc-client-utils 0.1.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)",
- "jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "jsonrpc-client-core"
-version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1052,43 +1001,6 @@ dependencies = [
]
[[package]]
-name = "jsonrpc-client-ipc"
-version = "0.5.0"
-source = "git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b#68aac55b6ddff5e1242594b54f7f9149fe215ff7"
-dependencies = [
- "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
- "jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)",
- "jsonrpc-server-utils 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "parity-tokio-ipc 0.2.0 (git+https://github.com/nikvolf/parity-tokio-ipc)",
- "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "jsonrpc-client-pubsub"
-version = "0.1.0"
-source = "git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b#68aac55b6ddff5e1242594b54f7f9149fe215ff7"
-dependencies = [
- "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
- "jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)",
- "jsonrpc-client-utils 0.1.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "jsonrpc-client-utils"
-version = "0.1.0"
-source = "git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b#68aac55b6ddff5e1242594b54f7f9149fe215ff7"
-dependencies = [
- "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
name = "jsonrpc-core"
version = "8.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1101,79 +1013,6 @@ dependencies = [
]
[[package]]
-name = "jsonrpc-core"
-version = "8.0.2"
-source = "git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork#9de0ea1ad4c296465f44f53050d99459ca885157"
-dependencies = [
- "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "jsonrpc-ipc-server"
-version = "8.0.1"
-source = "git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork#9de0ea1ad4c296465f44f53050d99459ca885157"
-dependencies = [
- "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
- "jsonrpc-core 8.0.2 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "jsonrpc-server-utils 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "parity-tokio-ipc 0.2.0 (git+https://github.com/nikvolf/parity-tokio-ipc)",
- "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "jsonrpc-macros"
-version = "8.0.1"
-source = "git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork#9de0ea1ad4c296465f44f53050d99459ca885157"
-dependencies = [
- "jsonrpc-core 8.0.2 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "jsonrpc-pubsub 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "jsonrpc-pubsub"
-version = "8.0.1"
-source = "git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork#9de0ea1ad4c296465f44f53050d99459ca885157"
-dependencies = [
- "jsonrpc-core 8.0.2 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "jsonrpc-server-utils"
-version = "8.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "jsonrpc-server-utils"
-version = "8.0.1"
-source = "git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork#9de0ea1ad4c296465f44f53050d99459ca885157"
-dependencies = [
- "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "jsonrpc-core 8.0.2 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
name = "kernel32-sys"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1207,15 +1046,6 @@ dependencies = [
[[package]]
name = "lock_api"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "lock_api"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -1248,14 +1078,6 @@ dependencies = [
[[package]]
name = "memchr"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "memchr"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1360,19 +1182,26 @@ dependencies = [
name = "mullvad-cli"
version = "2020.5.0"
dependencies = [
+ "async-trait 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
"chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
"clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"err-derive 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
- "mullvad-ipc-client 0.1.0",
+ "futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"mullvad-paths 0.1.0",
"mullvad-types 0.1.0",
"natord 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "parity-tokio-ipc 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "prost 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "prost-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
- "talpid-ipc 0.1.0",
"talpid-types 0.1.0",
+ "tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tonic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tonic-build 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tower 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"winres 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
@@ -1393,33 +1222,33 @@ dependencies = [
"futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
"futures 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"ipnetwork 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "jsonrpc-client-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "jsonrpc-core 8.0.2 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "jsonrpc-ipc-server 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "jsonrpc-macros 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "jsonrpc-pubsub 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"log-panics 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "mullvad-ipc-client 0.1.0",
"mullvad-paths 0.1.0",
"mullvad-rpc 0.1.0",
"mullvad-types 0.1.0",
"nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "parity-tokio-ipc 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "prost 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "prost-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
"simple-signal 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"talpid-core 0.1.0",
- "talpid-ipc 0.1.0",
"talpid-types 0.1.0",
"tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-retry 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tonic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tonic-build 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "tower 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "triggered 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
"windows-service 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1437,26 +1266,6 @@ dependencies = [
]
[[package]]
-name = "mullvad-ipc-client"
-version = "0.1.0"
-dependencies = [
- "err-derive 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
- "jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)",
- "jsonrpc-client-ipc 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)",
- "jsonrpc-client-pubsub 0.1.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "mullvad-paths 0.1.0",
- "mullvad-types 0.1.0",
- "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
- "talpid-ipc 0.1.0",
- "talpid-types 0.1.0",
- "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
name = "mullvad-jni"
version = "0.1.0"
dependencies = [
@@ -1544,41 +1353,18 @@ dependencies = [
"clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)",
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"err-derive 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "mullvad-ipc-client 0.1.0",
"mullvad-paths 0.1.0",
- "talpid-core 0.1.0",
- "talpid-types 0.1.0",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "winres 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "mullvad-tests"
-version = "0.1.0"
-dependencies = [
- "duct 0.13.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
- "jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)",
- "jsonrpc-client-ipc 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)",
- "jsonrpc-client-pubsub 0.1.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)",
- "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
- "mullvad-ipc-client 0.1.0",
- "mullvad-paths 0.1.0",
- "mullvad-rpc 0.1.0",
- "mullvad-types 0.1.0",
- "notify 4.0.13 (registry+https://github.com/rust-lang/crates.io-index)",
- "openvpn-plugin 0.3.0 (git+https://github.com/mullvad/openvpn-plugin-rs?branch=auth-failed-event)",
"parity-tokio-ipc 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"prost 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "talpid-ipc 0.1.0",
+ "prost-types 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "talpid-core 0.1.0",
"talpid-types 0.1.0",
- "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tonic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tonic-build 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tower 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winres 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1596,6 +1382,7 @@ dependencies = [
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
"talpid-types 0.1.0",
+ "tonic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -1831,31 +1618,6 @@ dependencies = [
]
[[package]]
-name = "owning_ref"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "parity-tokio-ipc"
-version = "0.2.0"
-source = "git+https://github.com/nikvolf/parity-tokio-ipc#2bfc4354fda235f0249b963853701e1571f04dbc"
-dependencies = [
- "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "miow 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-named-pipes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio-uds 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
name = "parity-tokio-ipc"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1872,15 +1634,6 @@ dependencies = [
[[package]]
name = "parking_lot"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
- "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "parking_lot"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -1891,18 +1644,6 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "parking_lot_core"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -2177,18 +1918,6 @@ dependencies = [
[[package]]
name = "rand"
-version = "0.5.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
- "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "rand"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
@@ -2534,11 +2263,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "scopeguard"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
-name = "scopeguard"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2696,11 +2420,6 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
-name = "stable_deref_trait"
-version = "1.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2845,8 +2564,6 @@ dependencies = [
"hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ipnetwork 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
"jnix 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "jsonrpc-core 8.0.2 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "jsonrpc-macros 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2873,7 +2590,6 @@ dependencies = [
"shell-escape 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)",
"system-configuration 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "talpid-ipc 0.1.0",
"talpid-types 0.1.0",
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2893,28 +2609,6 @@ dependencies = [
]
[[package]]
-name = "talpid-ipc"
-version = "0.1.0"
-dependencies = [
- "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "err-derive 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
- "jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)",
- "jsonrpc-client-ipc 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)",
- "jsonrpc-core 8.0.2 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "jsonrpc-ipc-server 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "jsonrpc-macros 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "jsonrpc-pubsub 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
- "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
name = "talpid-openvpn-plugin"
version = "2020.5.0"
dependencies = [
@@ -3143,18 +2837,6 @@ dependencies = [
]
[[package]]
-name = "tokio-named-pipes"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
- "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)",
- "mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)",
- "mio-named-pipes 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
- "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
name = "tokio-reactor"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3330,7 +3012,7 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"async-stream 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "async-trait 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
+ "async-trait 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bytes 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)",
"futures-core 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -3930,10 +3612,9 @@ dependencies = [
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
"checksum ascii 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e"
-"checksum assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7deb0a829ca7bcfaf5da70b073a8d128619259a7be8216a355e23f00763059e5"
"checksum async-stream 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "22068c0c19514942eefcfd4daf8976ef1aad84e61539f95cd200c35202f80af5"
"checksum async-stream-impl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "25f9db3b38af870bf7e5cc649167533b493928e50744e2c30ae350230b414670"
-"checksum async-trait 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "26c4f3195085c36ea8d24d32b2f828d23296a9370a28aa39d111f6f16bef9f3b"
+"checksum async-trait 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a265e3abeffdce30b2e26b7a11b222fe37c6067404001b434101457d0385eb92"
"checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90"
"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875"
"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
@@ -3944,7 +3625,6 @@ dependencies = [
"checksum bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182"
-"checksum bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245"
"checksum bumpalo 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "12ae9db68ad7fac5fe51304d20f016c911539251075a214f8e663babefa35187"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
@@ -4014,8 +3694,6 @@ dependencies = [
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571"
"checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
-"checksum globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90d069fe6beb9be359ef505650b3f73228c5591a3c4b1f32be2f4f44459ffa3a"
-"checksum globset 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "925aa2cac82d8834e2b2a4415b6f6879757fb5c0928fc445ae76461a12eed8f2"
"checksum h2 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "377038bf3c89d18d6ca1431e7a5027194fbd724ca10592b9487ede5e8e144f42"
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
"checksum hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e"
@@ -4039,29 +3717,17 @@ dependencies = [
"checksum jnix 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6354ae923ca4df982181ae2cd77eb4214f8c11d11d0c0cd8606c9347ac2abc57"
"checksum jnix-macros 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c78132fe420156f13b30518fcda9449b0ab8ae3b5584e8a1c53ce390fe770b44"
"checksum js-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)" = "6a27d435371a2fa5b6d2b028a74bbdb1234f308da363226a2854ca3ff8ba7055"
-"checksum jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)" = "<none>"
"checksum jsonrpc-client-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f29cb249837420fb0cee7fb0fbf1d22679e121b160e71bb5e0d90b9df241c23e"
-"checksum jsonrpc-client-ipc 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)" = "<none>"
-"checksum jsonrpc-client-pubsub 0.1.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)" = "<none>"
-"checksum jsonrpc-client-utils 0.1.0 (git+https://github.com/mullvad/jsonrpc-client-rs?rev=68aac55b)" = "<none>"
"checksum jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ddf83704f4e79979a424d1082dd2c1e52683058056c9280efa19ac5f6bc9033c"
-"checksum jsonrpc-core 8.0.2 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)" = "<none>"
-"checksum jsonrpc-ipc-server 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)" = "<none>"
-"checksum jsonrpc-macros 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)" = "<none>"
-"checksum jsonrpc-pubsub 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)" = "<none>"
-"checksum jsonrpc-server-utils 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "513e981828a4953ea7ddbb64c24d15d4983ecf6900dc1cd36f257d61c27138d5"
-"checksum jsonrpc-server-utils 8.0.1 (git+https://github.com/mullvad/jsonrpc?branch=mullvad-fork)" = "<none>"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
"checksum libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)" = "99e85c08494b21a9054e7fe1374a732aeadaff3980b6990b94bfd3a70f690005"
"checksum libdbus-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc12a3bc971424edbbf7edaf6e5740483444db63aa8e23d3751ff12a30f306f0"
-"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
"checksum lock_api 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8912e782533a93a167888781b836336a6ca5da6175c05944c86cf28c31104dc"
"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum log-panics 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ae0136257df209261daa18d6c16394757c63e032e27aafd8b07788b051082bef"
-"checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a"
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f"
"checksum mio 0.6.22 (registry+https://github.com/rust-lang/crates.io-index)" = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
@@ -4096,12 +3762,8 @@ dependencies = [
"checksum openvpn-plugin 0.3.0 (git+https://github.com/mullvad/openvpn-plugin-rs?branch=auth-failed-event)" = "<none>"
"checksum os_pipe 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "81e8dfa4c69d7bde595e9a940fcf1d7f60966d3fce8a8c4cad67c60e35ea2a11"
"checksum os_pipe 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "db4d06355a7090ce852965b2d08e11426c315438462638c6d721448d0b47aa22"
-"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13"
-"checksum parity-tokio-ipc 0.2.0 (git+https://github.com/nikvolf/parity-tokio-ipc)" = "<none>"
"checksum parity-tokio-ipc 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c1d417ba1ab454723ff2271bf999fd700027dc48759a13d43e488cc8ca38b87f"
-"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5"
"checksum parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
-"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c"
"checksum parking_lot_core 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
"checksum paste 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab4fb1930692d1b6a9cfabdde3d06ea0a7d186518e2f4d67660d8970e2fa647a"
"checksum paste-impl 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "a62486e111e571b1e93b710b61e8f493c0013be39629b714cb166bdb06aa5a8a"
@@ -4133,7 +3795,6 @@ dependencies = [
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
"checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
-"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
@@ -4173,7 +3834,6 @@ dependencies = [
"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421"
"checksum schannel 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "039c25b130bd8c1321ee2d7de7fde2659fa9c2744e4bb29711cfc852ea53cd19"
"checksum scoped-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
-"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d"
"checksum sct 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
"checksum security-framework 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "572dfa3a0785509e7a44b5b4bebcf94d41ba34e9ed9eb9df722545c3b3c4144a"
@@ -4194,7 +3854,6 @@ dependencies = [
"checksum snailquote 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fc3e2894a343234fb8a8653cf9d49ef6aea44e6581612ca311c91c4bd356dec4"
"checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85"
"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
-"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum strsim 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
"checksum subtle 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ab3af2eb31c42e8f0ccf43548232556c42737e01a96db6e1777b0be108e79799"
@@ -4226,7 +3885,6 @@ dependencies = [
"checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af"
"checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926"
"checksum tokio-macros 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
-"checksum tokio-named-pipes 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d282d483052288b2308ba5ee795f5673b159c9bdf63c385a05609da782a5eae"
"checksum tokio-reactor 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "c56391be9805bc80163151c0b9e5164ee64f4b0200962c346fea12773158f22d"
"checksum tokio-retry 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9c03755b956458582182941061def32b8123a26c98b08fc6ddcf49ae89d18f33"
"checksum tokio-rustls 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)" = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4"
diff --git a/Cargo.toml b/Cargo.toml
index fab359c830..fed2deb291 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,16 +5,14 @@ members = [
"mullvad-cli",
"mullvad-setup",
"mullvad-problem-report",
- "mullvad-ipc-client",
"mullvad-jni",
"mullvad-paths",
"mullvad-types",
"mullvad-rpc",
- "mullvad-tests",
+# "mullvad-tests",
"mullvad-exclude",
"talpid-openvpn-plugin",
"talpid-core",
- "talpid-ipc",
]
exclude = ["dist-assets/binaries/shadowsocks-rust"]
diff --git a/mullvad-cli/Cargo.toml b/mullvad-cli/Cargo.toml
index 084be415fe..5cb9ecb187 100644
--- a/mullvad-cli/Cargo.toml
+++ b/mullvad-cli/Cargo.toml
@@ -16,6 +16,7 @@ name = "mullvad"
path = "src/main.rs"
[dependencies]
+async-trait = "0.1"
base64 = "0.10"
chrono = { version = "0.4", features = ["serde"] }
clap = "2.32"
@@ -25,11 +26,21 @@ futures = "0.1"
natord = "1.0.9"
serde = "1.0"
-mullvad-ipc-client = { path = "../mullvad-ipc-client" }
mullvad-types = { path = "../mullvad-types" }
mullvad-paths = { path = "../mullvad-paths" }
talpid-types = { path = "../talpid-types" }
-talpid-ipc = { path = "../talpid-ipc" }
+
+tonic = "0.2"
+tower = "0.3"
+prost = "0.6"
+prost-types = "0.6"
+futures03 = { package = "futures", version = "0.3", features = [ "compat" ]}
+tokio = { version = "0.2", features = [ "io-util", "process", "rt-core", "rt-threaded", "stream"] }
+
+parity-tokio-ipc = "0.7"
+
+[build-dependencies]
+tonic-build = { version = "0.2", default-features = false, features = ["transport", "prost"] }
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
diff --git a/mullvad-cli/build.rs b/mullvad-cli/build.rs
index 4c19603b76..6d1586a655 100644
--- a/mullvad-cli/build.rs
+++ b/mullvad-cli/build.rs
@@ -1,6 +1,8 @@
use std::{env, fs, path::PathBuf};
fn main() {
+ tonic_build::compile_protos("../mullvad-daemon/proto/management_interface.proto").unwrap();
+
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let product_version = env!("CARGO_PKG_VERSION").replacen(".0", "", 1);
fs::write(out_dir.join("product-version.txt"), &product_version).unwrap();
diff --git a/mullvad-cli/src/cmds/account.rs b/mullvad-cli/src/cmds/account.rs
index e35699605c..a0a223eaf2 100644
--- a/mullvad-cli/src/cmds/account.rs
+++ b/mullvad-cli/src/cmds/account.rs
@@ -1,9 +1,10 @@
-use crate::{new_rpc_client, Command, Result};
+use crate::{new_grpc_client, Command, Error, Result};
use clap::value_t_or_exit;
use mullvad_types::account::{AccountToken, VoucherError};
pub struct Account;
+#[async_trait::async_trait]
impl Command for Account {
fn name(&self) -> &'static str {
"account"
@@ -49,21 +50,21 @@ impl Command for Account {
)
}
- fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
if let Some(set_matches) = matches.subcommand_matches("set") {
let token = value_t_or_exit!(set_matches.value_of("token"), String);
- self.set(Some(token))
+ self.set(Some(token)).await
} else if let Some(_matches) = matches.subcommand_matches("get") {
- self.get()
+ self.get().await
} else if let Some(_matches) = matches.subcommand_matches("unset") {
- self.set(None)
+ self.set(None).await
} else if let Some(_matches) = matches.subcommand_matches("clear-history") {
- self.clear_history()
+ self.clear_history().await
} else if let Some(_matches) = matches.subcommand_matches("create") {
- self.create()
+ self.create().await
} else if let Some(matches) = matches.subcommand_matches("redeem") {
let voucher = value_t_or_exit!(matches.value_of("voucher"), String);
- self.redeem_voucher(voucher)
+ self.redeem_voucher(voucher).await
} else {
unreachable!("No account command given");
}
@@ -71,9 +72,9 @@ impl Command for Account {
}
impl Account {
- fn set(&self, token: Option<AccountToken>) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- rpc.set_account(token.clone())?;
+ async fn set(&self, token: Option<AccountToken>) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_account(token.clone().unwrap_or_default()).await?;
if let Some(token) = token {
println!("Mullvad account \"{}\" set", token);
} else {
@@ -82,45 +83,55 @@ impl Account {
Ok(())
}
- fn get(&self) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- let settings = rpc.get_settings()?;
- if let Some(account_token) = settings.get_account_token() {
- println!("Mullvad account: {}", account_token);
- let expiry = rpc.get_account_data(account_token)?;
- println!("Expires at : {}", expiry.expiry);
+ async fn get(&self) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ let settings = rpc.get_settings(()).await?.into_inner();
+ if settings.account_token != "" {
+ println!("Mullvad account: {}", settings.account_token);
+ let expiry = rpc
+ .get_account_data(settings.account_token)
+ .await?
+ .into_inner();
+ println!(
+ "Expires at : {}",
+ Self::format_expiry(&expiry.expiry.unwrap())
+ );
} else {
println!("No account configured");
}
Ok(())
}
- fn create(&self) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- rpc.create_new_account()?;
+ async fn create(&self) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ rpc.create_new_account(()).await?;
println!("New account created!");
- self.get()
+ self.get().await
}
- fn redeem_voucher(&self, mut voucher: String) -> Result<()> {
- let mut rpc = new_rpc_client()?;
+ async fn redeem_voucher(&self, mut voucher: String) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
voucher.retain(|c| c.is_alphanumeric());
- match rpc.submit_voucher(voucher) {
+ match rpc.submit_voucher(voucher).await {
Ok(submission) => {
+ let submission = submission.into_inner();
println!(
"Added {} to the account",
- Self::format_duration(submission.time_added)
+ Self::format_duration(submission.seconds_added)
+ );
+ println!(
+ "New expiry date: {}",
+ Self::format_expiry(&submission.new_expiry.unwrap())
);
- println!("New expiry date: {}", submission.new_expiry);
Ok(())
}
Err(err) => {
eprintln!(
"Failed to submit voucher.\n{}",
- VoucherError::from_rpc_error_code(Self::get_redeem_rpc_error_code(&err))
+ VoucherError::from_rpc_error_code(err.code() as i64)
);
- Err(err.into())
+ Err(Error::GrpcClientError(err))
}
}
}
@@ -138,16 +149,13 @@ impl Account {
}
}
- fn get_redeem_rpc_error_code(error: &mullvad_ipc_client::Error) -> i64 {
- match error.kind() {
- mullvad_ipc_client::ErrorKind::JsonRpcError(ref rpc_error) => rpc_error.code.code(),
- _ => 0,
- }
+ fn format_expiry(expiry: &prost_types::Timestamp) -> String {
+ chrono::NaiveDateTime::from_timestamp(expiry.seconds, expiry.nanos as u32).to_string()
}
- fn clear_history(&self) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- rpc.clear_account_history()?;
+ async fn clear_history(&self) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ rpc.clear_account_history(()).await?;
println!("Removed account history and all associated keys");
Ok(())
}
diff --git a/mullvad-cli/src/cmds/auto_connect.rs b/mullvad-cli/src/cmds/auto_connect.rs
index 385a2de7b6..e934e8e37c 100644
--- a/mullvad-cli/src/cmds/auto_connect.rs
+++ b/mullvad-cli/src/cmds/auto_connect.rs
@@ -1,8 +1,9 @@
-use crate::{new_rpc_client, Command, Result};
+use crate::{new_grpc_client, Command, Result};
use clap::value_t_or_exit;
pub struct AutoConnect;
+#[async_trait::async_trait]
impl Command for AutoConnect {
fn name(&self) -> &'static str {
"auto-connect"
@@ -27,12 +28,12 @@ impl Command for AutoConnect {
)
}
- fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
if let Some(set_matches) = matches.subcommand_matches("set") {
let auto_connect = value_t_or_exit!(set_matches.value_of("policy"), String);
- self.set(auto_connect == "on")
+ self.set(auto_connect == "on").await
} else if let Some(_matches) = matches.subcommand_matches("get") {
- self.get()
+ self.get().await
} else {
unreachable!("No auto-connect command given");
}
@@ -40,16 +41,16 @@ impl Command for AutoConnect {
}
impl AutoConnect {
- fn set(&self, auto_connect: bool) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- rpc.set_auto_connect(auto_connect)?;
+ async fn set(&self, auto_connect: bool) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_auto_connect(auto_connect).await?;
println!("Changed auto-connect sharing setting");
Ok(())
}
- fn get(&self) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- let auto_connect = rpc.get_settings()?.auto_connect;
+ async fn get(&self) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ let auto_connect = rpc.get_settings(()).await?.into_inner().auto_connect;
println!("Autoconnect: {}", if auto_connect { "on" } else { "off" });
Ok(())
}
diff --git a/mullvad-cli/src/cmds/beta_program.rs b/mullvad-cli/src/cmds/beta_program.rs
index c4cbe6cf21..3950d54433 100644
--- a/mullvad-cli/src/cmds/beta_program.rs
+++ b/mullvad-cli/src/cmds/beta_program.rs
@@ -1,8 +1,9 @@
-use crate::{new_rpc_client, Command, Error, Result, PRODUCT_VERSION};
+use crate::{new_grpc_client, Command, Error, Result, PRODUCT_VERSION};
use clap::value_t_or_exit;
pub struct BetaProgram;
+#[async_trait::async_trait]
impl Command for BetaProgram {
fn name(&self) -> &'static str {
"beta-program"
@@ -24,11 +25,11 @@ impl Command for BetaProgram {
.subcommand(clap::SubCommand::with_name("get").about("Get beta notifications setting"))
}
- fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
match matches.subcommand() {
("get", Some(_)) => {
- let mut rpc = new_rpc_client()?;
- let settings = rpc.get_settings()?;
+ let mut rpc = new_grpc_client().await?;
+ let settings = rpc.get_settings(()).await?.into_inner();
let enabled_str = if settings.show_beta_releases {
"on"
} else {
@@ -47,8 +48,8 @@ impl Command for BetaProgram {
));
}
- let mut rpc = new_rpc_client()?;
- rpc.set_show_beta_releases(enable)?;
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_show_beta_releases(enable).await?;
println!("Beta program: {}", enable_str);
Ok(())
diff --git a/mullvad-cli/src/cmds/block_when_disconnected.rs b/mullvad-cli/src/cmds/block_when_disconnected.rs
index 57239963d7..b66acf168d 100644
--- a/mullvad-cli/src/cmds/block_when_disconnected.rs
+++ b/mullvad-cli/src/cmds/block_when_disconnected.rs
@@ -1,8 +1,9 @@
-use crate::{new_rpc_client, Command, Result};
+use crate::{new_grpc_client, Command, Result};
use clap::value_t_or_exit;
pub struct BlockWhenDisconnected;
+#[async_trait::async_trait]
impl Command for BlockWhenDisconnected {
fn name(&self) -> &'static str {
"always-require-vpn"
@@ -27,12 +28,12 @@ impl Command for BlockWhenDisconnected {
)
}
- fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
if let Some(set_matches) = matches.subcommand_matches("set") {
let block_when_disconnected = value_t_or_exit!(set_matches.value_of("policy"), String);
- self.set(block_when_disconnected == "on")
+ self.set(block_when_disconnected == "on").await
} else if let Some(_matches) = matches.subcommand_matches("get") {
- self.get()
+ self.get().await
} else {
unreachable!("No block-when-disconnected command given");
}
@@ -40,16 +41,21 @@ impl Command for BlockWhenDisconnected {
}
impl BlockWhenDisconnected {
- fn set(&self, block_when_disconnected: bool) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- rpc.set_block_when_disconnected(block_when_disconnected)?;
+ async fn set(&self, block_when_disconnected: bool) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_block_when_disconnected(block_when_disconnected)
+ .await?;
println!("Changed always require VPN setting");
Ok(())
}
- fn get(&self) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- let block_when_disconnected = rpc.get_settings()?.block_when_disconnected;
+ async fn get(&self) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ let block_when_disconnected = rpc
+ .get_settings(())
+ .await?
+ .into_inner()
+ .block_when_disconnected;
println!(
"Network traffic will be {} when the VPN is disconnected",
if block_when_disconnected {
diff --git a/mullvad-cli/src/cmds/bridge.rs b/mullvad-cli/src/cmds/bridge.rs
index 0e5c339744..7ad4cf1b22 100644
--- a/mullvad-cli/src/cmds/bridge.rs
+++ b/mullvad-cli/src/cmds/bridge.rs
@@ -1,13 +1,18 @@
-use crate::{location, new_rpc_client, Command, Result};
+use crate::{location, new_grpc_client, Command, Result};
use clap::value_t;
-use mullvad_types::relay_constraints::{BridgeConstraints, BridgeSettings, BridgeState};
-use talpid_types::net::openvpn::{self, SHADOWSOCKS_CIPHERS};
+use crate::proto::{
+ bridge_settings::{Type as BridgeSettingsType, *},
+ bridge_state::State as BridgeStateType,
+ BridgeSettings, BridgeState,
+};
+use talpid_types::net::openvpn::SHADOWSOCKS_CIPHERS;
use std::net::{IpAddr, SocketAddr};
pub struct Bridge;
+#[async_trait::async_trait]
impl Command for Bridge {
fn name(&self) -> &'static str {
"bridge"
@@ -24,11 +29,11 @@ impl Command for Bridge {
.subcommand(clap::SubCommand::with_name("list").about("List bridge relays"))
}
- fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
match matches.subcommand() {
- ("set", Some(set_matches)) => Self::handle_set(set_matches),
- ("get", _) => Self::handle_get(),
- ("list", _) => Self::list_bridge_relays(),
+ ("set", Some(set_matches)) => Self::handle_set(set_matches).await,
+ ("get", _) => Self::handle_get().await,
+ ("list", _) => Self::list_bridge_relays().await,
_ => unreachable!("unhandled command"),
}
}
@@ -145,64 +150,66 @@ fn create_set_state_subcommand() -> clap::App<'static, 'static> {
}
impl Bridge {
- fn handle_set(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn handle_set(matches: &clap::ArgMatches<'_>) -> Result<()> {
match matches.subcommand() {
("location", Some(location_matches)) => {
- Self::handle_set_bridge_location(location_matches)
+ Self::handle_set_bridge_location(location_matches).await
}
("custom", Some(custom_matches)) => {
- Self::handle_bridge_set_custom_settings(custom_matches)
+ Self::handle_bridge_set_custom_settings(custom_matches).await
}
- ("state", Some(set_matches)) => Self::handle_set_bridge_state(set_matches),
+ ("state", Some(set_matches)) => Self::handle_set_bridge_state(set_matches).await,
_ => unreachable!("unhandled command"),
}
}
- fn handle_get() -> Result<()> {
- let mut rpc = new_rpc_client()?;
- let settings = rpc.get_settings()?;
- println!("Bridge state - {}", settings.get_bridge_state());
- match settings.bridge_settings {
- BridgeSettings::Custom(proxy) => {
- match proxy {
- openvpn::ProxySettings::Local(local_proxy) => {
- Self::print_local_proxy(&local_proxy)
- }
- openvpn::ProxySettings::Remote(remote_proxy) => {
- Self::print_remote_proxy(&remote_proxy)
- }
- openvpn::ProxySettings::Shadowsocks(shadowsocks_proxy) => {
- Self::print_shadowsocks_proxy(&shadowsocks_proxy)
- }
- };
+ async fn handle_get() -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ let settings = rpc.get_settings(()).await?.into_inner();
+ Self::print_state(settings.bridge_state.unwrap());
+ match settings.bridge_settings.unwrap().r#type.unwrap() {
+ BridgeSettingsType::Local(local_proxy) => Self::print_local_proxy(&local_proxy),
+ BridgeSettingsType::Remote(remote_proxy) => Self::print_remote_proxy(&remote_proxy),
+ BridgeSettingsType::Shadowsocks(shadowsocks_proxy) => {
+ Self::print_shadowsocks_proxy(&shadowsocks_proxy)
}
- BridgeSettings::Normal(constraints) => {
- println!("Bridge constraints: {}", constraints);
+ BridgeSettingsType::Normal(constraints) => {
+ println!(
+ "Bridge constraints - {}",
+ location::format_location(constraints.location.as_ref())
+ );
}
};
Ok(())
}
- fn handle_set_bridge_location(matches: &clap::ArgMatches<'_>) -> Result<()> {
- let location = location::get_constraint(matches);
- let mut rpc = new_rpc_client()?;
- rpc.set_bridge_settings(BridgeSettings::Normal(BridgeConstraints { location }))?;
+ async fn handle_set_bridge_location(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ let constraints = location::get_constraint(matches);
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_bridge_settings(BridgeSettings {
+ r#type: Some(BridgeSettingsType::Normal(BridgeConstraints {
+ location: Some(constraints),
+ })),
+ })
+ .await?;
Ok(())
}
- fn handle_set_bridge_state(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn handle_set_bridge_state(matches: &clap::ArgMatches<'_>) -> Result<()> {
let state = match matches.value_of("state").unwrap() {
- "auto" => BridgeState::Auto,
- "on" => BridgeState::On,
- "off" => BridgeState::Off,
+ "auto" => BridgeStateType::Auto as i32,
+ "on" => BridgeStateType::On as i32,
+ "off" => BridgeStateType::Off as i32,
_ => unreachable!(),
};
- let mut rpc = new_rpc_client()?;
- rpc.set_bridge_state(state)?;
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_bridge_state(BridgeState { state }).await?;
Ok(())
}
- fn handle_bridge_set_custom_settings(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn handle_bridge_set_custom_settings(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ use talpid_types::net::openvpn;
+
if let Some(args) = matches.subcommand_matches("local") {
let local_port =
value_t!(args.value_of("local-port"), u16).unwrap_or_else(|e| e.exit());
@@ -211,19 +218,24 @@ impl Bridge {
let remote_port =
value_t!(args.value_of("remote-port"), u16).unwrap_or_else(|e| e.exit());
- let proxy = openvpn::LocalProxySettings {
+ let local_proxy = openvpn::LocalProxySettings {
port: local_port,
peer: SocketAddr::new(remote_ip, remote_port),
};
-
- let packed_proxy = openvpn::ProxySettings::Local(proxy);
-
+ let prost_proxy = LocalProxySettings {
+ port: local_proxy.port as u32,
+ peer: local_proxy.peer.to_string(),
+ };
+ let packed_proxy = openvpn::ProxySettings::Local(local_proxy);
if let Err(error) = openvpn::validate_proxy_settings(&packed_proxy) {
panic!(error);
}
- let mut rpc = new_rpc_client()?;
- rpc.set_bridge_settings(BridgeSettings::Custom(packed_proxy))?;
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_bridge_settings(BridgeSettings {
+ r#type: Some(BridgeSettingsType::Local(prost_proxy)),
+ })
+ .await?;
} else if let Some(args) = matches.subcommand_matches("remote") {
let remote_ip =
value_t!(args.value_of("remote-ip"), IpAddr).unwrap_or_else(|e| e.exit());
@@ -239,20 +251,30 @@ impl Bridge {
}),
_ => None,
};
+ let prost_auth = auth.clone().map(|auth| RemoteProxyAuth {
+ username: auth.username.clone(),
+ password: auth.password.clone(),
+ });
let proxy = openvpn::RemoteProxySettings {
address: SocketAddr::new(remote_ip, remote_port),
auth,
};
+ let prost_proxy = RemoteProxySettings {
+ address: proxy.address.to_string(),
+ auth: prost_auth,
+ };
let packed_proxy = openvpn::ProxySettings::Remote(proxy);
-
if let Err(error) = openvpn::validate_proxy_settings(&packed_proxy) {
panic!(error);
}
- let mut rpc = new_rpc_client()?;
- rpc.set_bridge_settings(BridgeSettings::Custom(packed_proxy))?;
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_bridge_settings(BridgeSettings {
+ r#type: Some(BridgeSettingsType::Remote(prost_proxy)),
+ })
+ .await?;
} else if let Some(args) = matches.subcommand_matches("shadowsocks") {
let remote_ip =
value_t!(args.value_of("remote-ip"), IpAddr).unwrap_or_else(|e| e.exit());
@@ -266,15 +288,22 @@ impl Bridge {
password,
cipher,
};
+ let prost_proxy = ShadowsocksProxySettings {
+ peer: proxy.peer.to_string(),
+ password: proxy.password.clone(),
+ cipher: proxy.cipher.clone(),
+ };
let packed_proxy = openvpn::ProxySettings::Shadowsocks(proxy);
-
if let Err(error) = openvpn::validate_proxy_settings(&packed_proxy) {
panic!(error);
}
- let mut rpc = new_rpc_client()?;
- rpc.set_bridge_settings(BridgeSettings::Custom(packed_proxy))?;
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_bridge_settings(BridgeSettings {
+ r#type: Some(BridgeSettingsType::Shadowsocks(prost_proxy)),
+ })
+ .await?;
} else {
unreachable!("unhandled proxy type");
}
@@ -283,17 +312,24 @@ impl Bridge {
Ok(())
}
- fn print_local_proxy(proxy: &openvpn::LocalProxySettings) {
+ fn print_state(state: BridgeState) {
+ let state = match BridgeStateType::from_i32(state.state).expect("unknown bridge state") {
+ BridgeStateType::Auto => "auto",
+ BridgeStateType::On => "on",
+ BridgeStateType::Off => "off",
+ };
+ println!("Bridge state - {}", state);
+ }
+
+ fn print_local_proxy(proxy: &LocalProxySettings) {
println!("proxy: local");
println!(" local port: {}", proxy.port);
- println!(" peer IP: {}", proxy.peer.ip());
- println!(" peer port: {}", proxy.peer.port());
+ println!(" peer address: {}", proxy.peer);
}
- fn print_remote_proxy(proxy: &openvpn::RemoteProxySettings) {
+ fn print_remote_proxy(proxy: &RemoteProxySettings) {
println!("proxy: remote");
- println!(" server IP: {}", proxy.address.ip());
- println!(" server port: {}", proxy.address.port());
+ println!(" server address: {}", proxy.address);
if let Some(ref auth) = proxy.auth {
println!(" auth username: {}", auth.username);
@@ -303,47 +339,43 @@ impl Bridge {
}
}
- fn print_shadowsocks_proxy(proxy: &openvpn::ShadowsocksProxySettings) {
+ fn print_shadowsocks_proxy(proxy: &ShadowsocksProxySettings) {
println!("proxy: Shadowsocks");
- println!(" peer IP: {}", proxy.peer.ip());
- println!(" peer port: {}", proxy.peer.port());
+ println!(" peer address: {}", proxy.peer);
println!(" password: {}", proxy.password);
println!(" cipher: {}", proxy.cipher);
}
- fn list_bridge_relays() -> Result<()> {
- let mut rpc = new_rpc_client()?;
- let mut locations = rpc.get_relay_locations()?;
+ async fn list_bridge_relays() -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ let mut locations = rpc.get_relay_locations(()).await?.into_inner();
- locations.countries = locations
- .countries
- .into_iter()
- .filter_map(|mut country| {
- country.cities = country
- .cities
- .into_iter()
- .filter_map(|mut city| {
- city.relays
- .retain(|relay| relay.active && !relay.bridges.is_empty());
- if !city.relays.is_empty() {
- Some(city)
- } else {
- None
- }
- })
- .collect();
- if !country.cities.is_empty() {
- Some(country)
- } else {
- None
- }
- })
- .collect();
+ let mut countries = Vec::new();
+
+ while let Some(mut country) = locations.message().await? {
+ country.cities = country
+ .cities
+ .into_iter()
+ .filter_map(|mut city| {
+ city.relays.retain(|relay| {
+ relay.active
+ && relay.bridges.is_some()
+ && !relay.bridges.as_ref().unwrap().shadowsocks.is_empty()
+ });
+ if !city.relays.is_empty() {
+ Some(city)
+ } else {
+ None
+ }
+ })
+ .collect();
+ if !country.cities.is_empty() {
+ countries.push(country);
+ }
+ }
- locations
- .countries
- .sort_by(|c1, c2| natord::compare_ignore_case(&c1.name, &c2.name));
- for mut country in locations.countries {
+ countries.sort_by(|c1, c2| natord::compare_ignore_case(&c1.name, &c2.name));
+ for mut country in countries {
country
.cities
.sort_by(|c1, c2| natord::compare_ignore_case(&c1.name, &c2.name));
diff --git a/mullvad-cli/src/cmds/connect.rs b/mullvad-cli/src/cmds/connect.rs
index 89cc0f2d2e..899a450547 100644
--- a/mullvad-cli/src/cmds/connect.rs
+++ b/mullvad-cli/src/cmds/connect.rs
@@ -1,8 +1,9 @@
-use crate::{new_rpc_client, Command, Result};
+use crate::{new_grpc_client, Command, Result};
use talpid_types::ErrorExt;
pub struct Connect;
+#[async_trait::async_trait]
impl Command for Connect {
fn name(&self) -> &'static str {
"connect"
@@ -13,9 +14,9 @@ impl Command for Connect {
.about("Command the client to start establishing a VPN tunnel")
}
- fn run(&self, _matches: &clap::ArgMatches<'_>) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- if let Err(e) = rpc.connect() {
+ async fn run(&self, _: &clap::ArgMatches<'_>) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ if let Err(e) = rpc.connect_tunnel(()).await {
eprintln!("{}", e.display_chain());
}
Ok(())
diff --git a/mullvad-cli/src/cmds/disconnect.rs b/mullvad-cli/src/cmds/disconnect.rs
index bf85f80310..a3d6698fc9 100644
--- a/mullvad-cli/src/cmds/disconnect.rs
+++ b/mullvad-cli/src/cmds/disconnect.rs
@@ -1,7 +1,8 @@
-use crate::{new_rpc_client, Command, Result};
+use crate::{new_grpc_client, Command, Result};
pub struct Disconnect;
+#[async_trait::async_trait]
impl Command for Disconnect {
fn name(&self) -> &'static str {
"disconnect"
@@ -12,9 +13,9 @@ impl Command for Disconnect {
.about("Command the client to disconnect the VPN tunnel")
}
- fn run(&self, _matches: &clap::ArgMatches<'_>) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- rpc.disconnect()?;
+ async fn run(&self, _: &clap::ArgMatches<'_>) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ rpc.disconnect_tunnel(()).await?;
Ok(())
}
}
diff --git a/mullvad-cli/src/cmds/lan.rs b/mullvad-cli/src/cmds/lan.rs
index 15d30cde50..05f4f867d2 100644
--- a/mullvad-cli/src/cmds/lan.rs
+++ b/mullvad-cli/src/cmds/lan.rs
@@ -1,8 +1,9 @@
-use crate::{new_rpc_client, Command, Result};
+use crate::{new_grpc_client, Command, Result};
use clap::value_t_or_exit;
pub struct Lan;
+#[async_trait::async_trait]
impl Command for Lan {
fn name(&self) -> &'static str {
"lan"
@@ -27,12 +28,12 @@ impl Command for Lan {
)
}
- fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
if let Some(set_matches) = matches.subcommand_matches("set") {
let allow_lan = value_t_or_exit!(set_matches.value_of("policy"), String);
- self.set(allow_lan == "allow")
+ self.set(allow_lan == "allow").await
} else if let Some(_matches) = matches.subcommand_matches("get") {
- self.get()
+ self.get().await
} else {
unreachable!("No lan command given");
}
@@ -40,16 +41,16 @@ impl Command for Lan {
}
impl Lan {
- fn set(&self, allow_lan: bool) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- rpc.set_allow_lan(allow_lan)?;
+ async fn set(&self, allow_lan: bool) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_allow_lan(allow_lan).await?;
println!("Changed local network sharing setting");
Ok(())
}
- fn get(&self) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- let allow_lan = rpc.get_settings()?.allow_lan;
+ async fn get(&self) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ let allow_lan = rpc.get_settings(()).await?.into_inner().allow_lan;
println!(
"Local network sharing setting: {}",
if allow_lan { "allow" } else { "block" }
diff --git a/mullvad-cli/src/cmds/reconnect.rs b/mullvad-cli/src/cmds/reconnect.rs
index 0cc7f6bfea..d281266e11 100644
--- a/mullvad-cli/src/cmds/reconnect.rs
+++ b/mullvad-cli/src/cmds/reconnect.rs
@@ -1,8 +1,9 @@
-use crate::{new_rpc_client, Command, Result};
+use crate::{new_grpc_client, Command, Result};
use talpid_types::ErrorExt;
pub struct Reconnect;
+#[async_trait::async_trait]
impl Command for Reconnect {
fn name(&self) -> &'static str {
"reconnect"
@@ -12,9 +13,9 @@ impl Command for Reconnect {
clap::SubCommand::with_name(self.name()).about("Command the client to reconnect")
}
- fn run(&self, _matches: &clap::ArgMatches<'_>) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- if let Err(e) = rpc.reconnect() {
+ async fn run(&self, _: &clap::ArgMatches<'_>) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ if let Err(e) = rpc.reconnect_tunnel(()).await {
eprintln!("{}", e.display_chain());
}
Ok(())
diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs
index c4e8c72c81..07d07fc447 100644
--- a/mullvad-cli/src/cmds/relay.rs
+++ b/mullvad-cli/src/cmds/relay.rs
@@ -1,4 +1,4 @@
-use crate::{location, new_rpc_client, Command, Error, Result};
+use crate::{location, new_grpc_client, proto, Command, Error, Result};
use clap::{value_t, values_t};
use std::{
io::{self, BufRead},
@@ -6,19 +6,18 @@ use std::{
str::FromStr,
};
-use mullvad_types::{
- relay_constraints::{
- Constraint, OpenVpnConstraints, RelayConstraintsUpdate, RelaySettingsUpdate,
- WireguardConstraints,
- },
- ConnectionConfig, CustomTunnelEndpoint,
-};
-use talpid_types::net::{
- all_of_the_internet, openvpn, wireguard, Endpoint, TransportProtocol, TunnelType,
+use mullvad_types::relay_constraints::Constraint;
+use proto::{
+ connection_config::{self, OpenvpnConfig, WireguardConfig},
+ relay_settings, relay_settings_update, ConnectionConfig, CustomRelaySettings,
+ NormalRelaySettingsUpdate, OpenvpnConstraints, RelaySettingsUpdate, TransportProtocol,
+ TunnelType, TunnelTypeUpdate, WireguardConstraints,
};
+use talpid_types::net::all_of_the_internet;
pub struct Relay;
+#[async_trait::async_trait]
impl Command for Relay {
fn name(&self) -> &'static str {
"relay"
@@ -155,15 +154,15 @@ impl Command for Relay {
)
}
- fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
if let Some(set_matches) = matches.subcommand_matches("set") {
- self.set(set_matches)
+ self.set(set_matches).await
} else if matches.subcommand_matches("get").is_some() {
- self.get()
+ self.get().await
} else if matches.subcommand_matches("list").is_some() {
- self.list()
+ self.list().await
} else if matches.subcommand_matches("update").is_some() {
- self.update()
+ self.update().await
} else {
unreachable!("No relay command given");
}
@@ -171,54 +170,73 @@ impl Command for Relay {
}
impl Relay {
- fn update_constraints(&self, update: RelaySettingsUpdate) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- rpc.update_relay_settings(update)?;
+ async fn update_constraints(&self, update: RelaySettingsUpdate) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ rpc.update_relay_settings(update).await?;
println!("Relay constraints updated");
Ok(())
}
- fn set(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn set(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
if let Some(custom_matches) = matches.subcommand_matches("custom") {
- self.set_custom(custom_matches)
+ self.set_custom(custom_matches).await
} else if let Some(location_matches) = matches.subcommand_matches("location") {
- self.set_location(location_matches)
+ self.set_location(location_matches).await
} else if let Some(tunnel_matches) = matches.subcommand_matches("tunnel") {
- self.set_tunnel(tunnel_matches)
+ self.set_tunnel(tunnel_matches).await
} else if let Some(tunnel_matches) = matches.subcommand_matches("tunnel-protocol") {
- self.set_tunnel_protocol(tunnel_matches)
+ self.set_tunnel_protocol(tunnel_matches).await
} else {
unreachable!("No set relay command given");
}
}
- fn set_custom(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn set_custom(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
let custom_endpoint = match matches.subcommand() {
("openvpn", Some(openvpn_matches)) => Self::read_custom_openvpn_relay(openvpn_matches),
("wireguard", Some(wg_matches)) => Self::read_custom_wireguard_relay(wg_matches),
(_unknown_tunnel, _) => unreachable!("No set relay command given"),
};
- self.update_constraints(RelaySettingsUpdate::CustomTunnelEndpoint(custom_endpoint))
+
+ self.update_constraints(RelaySettingsUpdate {
+ r#type: Some(relay_settings_update::Type::Custom(custom_endpoint)),
+ })
+ .await
}
- fn read_custom_openvpn_relay(matches: &clap::ArgMatches<'_>) -> CustomTunnelEndpoint {
+ fn read_custom_openvpn_relay(matches: &clap::ArgMatches<'_>) -> CustomRelaySettings {
let host = value_t!(matches.value_of("host"), String).unwrap_or_else(|e| e.exit());
let port = value_t!(matches.value_of("port"), u16).unwrap_or_else(|e| e.exit());
let username = value_t!(matches.value_of("username"), String).unwrap_or_else(|e| e.exit());
let password = value_t!(matches.value_of("password"), String).unwrap_or_else(|e| e.exit());
- let protocol =
- value_t!(matches.value_of("protocol"), TransportProtocol).unwrap_or_else(|e| e.exit());
- CustomTunnelEndpoint::new(
+ let protocol = value_t!(matches.value_of("protocol"), String).unwrap_or_else(|e| e.exit());
+
+ let protocol = match protocol.as_str() {
+ "udp" => TransportProtocol::Udp,
+ "tcp" => TransportProtocol::Tcp,
+ _ => clap::Error::with_description(
+ "unknown transport protocol",
+ clap::ErrorKind::ValueValidation,
+ )
+ .exit(),
+ };
+
+ CustomRelaySettings {
host,
- ConnectionConfig::OpenVpn(openvpn::ConnectionConfig {
- endpoint: Endpoint::new(Ipv4Addr::UNSPECIFIED, port, protocol),
- username,
- password,
+ config: Some(ConnectionConfig {
+ config: Some(connection_config::Config::Openvpn(OpenvpnConfig {
+ address: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port).to_string(),
+ protocol: protocol as i32,
+ username,
+ password,
+ })),
}),
- )
+ }
}
- fn read_custom_wireguard_relay(matches: &clap::ArgMatches<'_>) -> CustomTunnelEndpoint {
+ fn read_custom_wireguard_relay(matches: &clap::ArgMatches<'_>) -> CustomRelaySettings {
+ use connection_config::wireguard_config;
+
let host = value_t!(matches.value_of("host"), String).unwrap_or_else(|e| e.exit());
let port = value_t!(matches.value_of("port"), u16).unwrap_or_else(|e| e.exit());
let addresses = values_t!(matches.values_of("addr"), IpAddr).unwrap_or_else(|e| e.exit());
@@ -239,26 +257,37 @@ impl Relay {
if private_key_str.trim().is_empty() {
eprintln!("Expected to read private key from standard input");
}
- let private_key = Self::validate_wireguard_key(&private_key_str).into();
- let peer_public_key = Self::validate_wireguard_key(&peer_key_str).into();
-
+ let private_key = Self::validate_wireguard_key(&private_key_str);
+ let peer_public_key = Self::validate_wireguard_key(&peer_key_str);
- CustomTunnelEndpoint::new(
+ CustomRelaySettings {
host,
- ConnectionConfig::Wireguard(wireguard::ConnectionConfig {
- tunnel: wireguard::TunnelConfig {
- private_key,
- addresses,
- },
- peer: wireguard::PeerConfig {
- public_key: peer_public_key,
- allowed_ips: all_of_the_internet(),
- endpoint: SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), port),
- },
- ipv4_gateway,
- ipv6_gateway,
+ config: Some(ConnectionConfig {
+ config: Some(connection_config::Config::Wireguard(WireguardConfig {
+ tunnel: Some(wireguard_config::TunnelConfig {
+ private_key: private_key.to_vec(),
+ addresses: addresses
+ .iter()
+ .map(|address| address.to_string())
+ .collect(),
+ }),
+ peer: Some(wireguard_config::PeerConfig {
+ public_key: peer_public_key.to_vec(),
+ allowed_ips: all_of_the_internet()
+ .iter()
+ .map(|address| address.to_string())
+ .collect(),
+ endpoint: SocketAddr::new(IpAddr::V4(Ipv4Addr::UNSPECIFIED), port)
+ .to_string(),
+ }),
+ ipv4_gateway: ipv4_gateway.to_string(),
+ ipv6_gateway: ipv6_gateway
+ .as_ref()
+ .map(|addr| addr.to_string())
+ .unwrap_or_default(),
+ })),
}),
- )
+ }
}
fn validate_wireguard_key(key_str: &str) -> [u8; 32] {
@@ -280,16 +309,21 @@ impl Relay {
key
}
- fn set_location(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn set_location(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
let location_constraint = location::get_constraint(matches);
- self.update_constraints(RelaySettingsUpdate::Normal(RelayConstraintsUpdate {
- location: Some(location_constraint),
- ..Default::default()
- }))
+ self.update_constraints(RelaySettingsUpdate {
+ r#type: Some(relay_settings_update::Type::Normal(
+ NormalRelaySettingsUpdate {
+ location: Some(location_constraint),
+ ..Default::default()
+ },
+ )),
+ })
+ .await
}
- fn set_tunnel(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn set_tunnel(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
let vpn_protocol = matches.value_of("vpn protocol").unwrap();
let port = parse_port_constraint(matches.value_of("port").unwrap())?;
let protocol = parse_protocol_constraint(matches.value_of("transport protocol").unwrap());
@@ -299,79 +333,157 @@ impl Relay {
if let Constraint::Only(TransportProtocol::Tcp) = protocol {
return Err(Error::InvalidCommand("WireGuard does not support TCP"));
}
- self.update_constraints(RelaySettingsUpdate::Normal(RelayConstraintsUpdate {
- location: None,
- tunnel_protocol: None,
- wireguard_constraints: Some(WireguardConstraints { port }),
- ..Default::default()
- }))
+ self.update_constraints(RelaySettingsUpdate {
+ r#type: Some(relay_settings_update::Type::Normal(
+ NormalRelaySettingsUpdate {
+ wireguard_constraints: Some(WireguardConstraints {
+ port: port.unwrap_or(0) as u32,
+ }),
+ ..Default::default()
+ },
+ )),
+ })
+ .await
}
"openvpn" => {
- self.update_constraints(RelaySettingsUpdate::Normal(RelayConstraintsUpdate {
- location: None,
- tunnel_protocol: None,
- openvpn_constraints: Some(OpenVpnConstraints { port, protocol }),
- ..Default::default()
- }))
+ self.update_constraints(RelaySettingsUpdate {
+ r#type: Some(relay_settings_update::Type::Normal(
+ NormalRelaySettingsUpdate {
+ openvpn_constraints: Some(OpenvpnConstraints {
+ port: port.unwrap_or(0) as u32,
+ protocol: protocol.unwrap_or(TransportProtocol::AnyProtocol) as i32,
+ }),
+ ..Default::default()
+ },
+ )),
+ })
+ .await
}
_ => unreachable!(),
}
}
- fn set_tunnel_protocol(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
- let tunnel_protocol = match matches.value_of("tunnel protocol").unwrap() {
- "wireguard" => Constraint::Only(TunnelType::Wireguard),
- "openvpn" => Constraint::Only(TunnelType::OpenVpn),
- "any" => Constraint::Any,
+ async fn set_tunnel_protocol(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ let tunnel_type = match matches.value_of("tunnel protocol").unwrap() {
+ "wireguard" => TunnelType::Wireguard,
+ "openvpn" => TunnelType::Openvpn,
+ "any" => TunnelType::AnyTunnel,
_ => unreachable!(),
};
- self.update_constraints(RelaySettingsUpdate::Normal(RelayConstraintsUpdate {
- tunnel_protocol: Some(tunnel_protocol),
- ..Default::default()
- }))
+ self.update_constraints(RelaySettingsUpdate {
+ r#type: Some(relay_settings_update::Type::Normal(
+ NormalRelaySettingsUpdate {
+ tunnel_type: Some(TunnelTypeUpdate {
+ tunnel_type: tunnel_type as i32,
+ }),
+ ..Default::default()
+ },
+ )),
+ })
+ .await
}
- fn get(&self) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- let constraints = rpc.get_settings()?.get_relay_settings();
- println!("Current constraints: {}", constraints);
+ async fn get(&self) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ let constraints = rpc
+ .get_settings(())
+ .await?
+ .into_inner()
+ .relay_settings
+ .unwrap();
+
+ print!("Current constraints: ");
+
+ match constraints.endpoint.unwrap() {
+ relay_settings::Endpoint::Normal(settings) => {
+ match TunnelType::from_i32(settings.tunnel_type).expect("unknown tunnel type") {
+ TunnelType::AnyTunnel => {
+ println!(
+ "Any tunnel protocol with OpenVPN over {} and WireGuard over {} in {}",
+ Self::format_openvpn_constraints(settings.openvpn_constraints.as_ref()),
+ Self::format_wireguard_constraints(
+ settings.wireguard_constraints.as_ref()
+ ),
+ location::format_location(settings.location.as_ref())
+ );
+ }
+ TunnelType::Wireguard => {
+ println!(
+ "WireGuard over {} in {}",
+ Self::format_wireguard_constraints(
+ settings.wireguard_constraints.as_ref()
+ ),
+ location::format_location(settings.location.as_ref())
+ );
+ }
+ TunnelType::Openvpn => {
+ println!(
+ "OpenVPN over {} in {}",
+ Self::format_openvpn_constraints(settings.openvpn_constraints.as_ref()),
+ location::format_location(settings.location.as_ref())
+ );
+ }
+ }
+ }
+
+ relay_settings::Endpoint::Custom(settings) => {
+ let config = settings.config.unwrap();
+ match config.config.unwrap() {
+ connection_config::Config::Openvpn(config) => {
+ println!(
+ "custom OpenVPN relay - {} {}",
+ config.address,
+ Self::format_transport_protocol(
+ TransportProtocol::from_i32(config.protocol).unwrap()
+ ),
+ );
+ }
+ connection_config::Config::Wireguard(config) => {
+ let peer = config.peer.unwrap();
+ println!(
+ "custom WireGuard relay - {} with public key {}",
+ peer.endpoint,
+ base64::encode(&peer.public_key),
+ );
+ }
+ }
+ }
+ }
Ok(())
}
- fn list(&self) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- let mut locations = rpc.get_relay_locations()?;
+ async fn list(&self) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ let mut locations = rpc.get_relay_locations(()).await?.into_inner();
- locations.countries = locations
- .countries
- .into_iter()
- .filter_map(|mut country| {
- country.cities = country
- .cities
- .into_iter()
- .filter_map(|mut city| {
- city.relays
- .retain(|relay| relay.active && !relay.tunnels.is_empty());
- if !city.relays.is_empty() {
- Some(city)
- } else {
- None
- }
- })
- .collect();
- if !country.cities.is_empty() {
- Some(country)
- } else {
- None
- }
- })
- .collect();
+ let mut countries = Vec::new();
- locations
- .countries
- .sort_by(|c1, c2| natord::compare_ignore_case(&c1.name, &c2.name));
- for mut country in locations.countries {
+ while let Some(mut country) = locations.message().await? {
+ country.cities = country
+ .cities
+ .into_iter()
+ .filter_map(|mut city| {
+ city.relays.retain(|relay| {
+ relay.active
+ && relay.tunnels.is_some()
+ && !(relay.tunnels.as_ref().unwrap().openvpn.is_empty()
+ && relay.tunnels.as_ref().unwrap().wireguard.is_empty())
+ });
+ if !city.relays.is_empty() {
+ Some(city)
+ } else {
+ None
+ }
+ })
+ .collect();
+ if !country.cities.is_empty() {
+ countries.push(country);
+ }
+ }
+
+ countries.sort_by(|c1, c2| natord::compare_ignore_case(&c1.name, &c2.name));
+ for mut country in countries {
country
.cities
.sort_by(|c1, c2| natord::compare_ignore_case(&c1.name, &c2.name));
@@ -384,8 +496,9 @@ impl Relay {
city.name, city.code, city.latitude, city.longitude
);
for relay in &city.relays {
- let supports_openvpn = !relay.tunnels.openvpn.is_empty();
- let supports_wireguard = !relay.tunnels.wireguard.is_empty();
+ let tunnels = relay.tunnels.as_ref().unwrap();
+ let supports_openvpn = !tunnels.openvpn.is_empty();
+ let supports_wireguard = !tunnels.wireguard.is_empty();
let support_msg = match (supports_openvpn, supports_wireguard) {
(true, true) => "OpenVPN and WireGuard",
(true, false) => "OpenVPN",
@@ -403,11 +516,49 @@ impl Relay {
Ok(())
}
- fn update(&self) -> Result<()> {
- new_rpc_client()?.update_relay_locations()?;
+ async fn update(&self) -> Result<()> {
+ new_grpc_client().await?.update_relay_locations(()).await?;
println!("Updating relay list in the background...");
Ok(())
}
+
+ fn format_transport_protocol(protocol: TransportProtocol) -> &'static str {
+ match protocol {
+ TransportProtocol::AnyProtocol => "any transport protocol",
+ TransportProtocol::Udp => "UDP",
+ TransportProtocol::Tcp => "TCP",
+ }
+ }
+
+ fn format_port(port: u32) -> String {
+ if port != 0 {
+ format!("port {}", port)
+ } else {
+ "any port".to_string()
+ }
+ }
+
+ fn format_openvpn_constraints(constraints: Option<&OpenvpnConstraints>) -> String {
+ if let Some(constraints) = constraints {
+ format!(
+ "{} over {}",
+ Self::format_port(constraints.port),
+ Self::format_transport_protocol(
+ TransportProtocol::from_i32(constraints.protocol).unwrap()
+ )
+ )
+ } else {
+ "any port over any transport protocol".to_string()
+ }
+ }
+
+ fn format_wireguard_constraints(constraints: Option<&WireguardConstraints>) -> String {
+ if let Some(constraints) = constraints {
+ Self::format_port(constraints.port)
+ } else {
+ "any port".to_string()
+ }
+ }
}
diff --git a/mullvad-cli/src/cmds/reset.rs b/mullvad-cli/src/cmds/reset.rs
index d859d1e436..2461e86ad5 100644
--- a/mullvad-cli/src/cmds/reset.rs
+++ b/mullvad-cli/src/cmds/reset.rs
@@ -1,7 +1,8 @@
-use crate::{new_rpc_client, Command, Result};
+use crate::{new_grpc_client, Command, Result};
use std::io::stdin;
pub struct Reset;
+#[async_trait::async_trait]
impl Command for Reset {
fn name(&self) -> &'static str {
"factory-reset"
@@ -11,10 +12,10 @@ impl Command for Reset {
clap::SubCommand::with_name(self.name()).about("Reset settings, caches and logs")
}
- fn run(&self, _matches: &clap::ArgMatches<'_>) -> Result<()> {
- let mut rpc = new_rpc_client()?;
+ async fn run(&self, _: &clap::ArgMatches<'_>) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
if Self::receive_confirmation() {
- if rpc.factory_reset().is_err() {
+ if rpc.factory_reset(()).await.is_err() {
eprintln!("FAILED TO PERFORM FACTORY RESET");
} else {
#[cfg(target_os = "linux")]
diff --git a/mullvad-cli/src/cmds/split_tunnel/linux.rs b/mullvad-cli/src/cmds/split_tunnel/linux.rs
index 95b172eb6b..5e8a9b9644 100644
--- a/mullvad-cli/src/cmds/split_tunnel/linux.rs
+++ b/mullvad-cli/src/cmds/split_tunnel/linux.rs
@@ -1,8 +1,9 @@
-use crate::{new_rpc_client, Command, Result};
+use crate::{new_grpc_client, Command, Result};
use clap::value_t_or_exit;
pub struct SplitTunnel;
+#[async_trait::async_trait]
impl Command for SplitTunnel {
fn name(&self) -> &'static str {
"split-tunnel"
@@ -15,9 +16,9 @@ impl Command for SplitTunnel {
.subcommand(create_pid_subcommand())
}
- fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
match matches.subcommand() {
- ("pid", Some(pid_matches)) => Self::handle_pid_cmd(pid_matches),
+ ("pid", Some(pid_matches)) => Self::handle_pid_cmd(pid_matches).await,
_ => unreachable!("unhandled comand"),
}
}
@@ -38,27 +39,40 @@ fn create_pid_subcommand() -> clap::App<'static, 'static> {
}
impl SplitTunnel {
- fn handle_pid_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn handle_pid_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> {
match matches.subcommand() {
("add", Some(matches)) => {
let pid = value_t_or_exit!(matches.value_of("pid"), i32);
- new_rpc_client()?.add_split_tunnel_process(pid)?;
+ new_grpc_client()
+ .await?
+ .add_split_tunnel_process(pid)
+ .await?;
Ok(())
}
("delete", Some(matches)) => {
let pid = value_t_or_exit!(matches.value_of("pid"), i32);
- new_rpc_client()?.remove_split_tunnel_process(pid)?;
+ new_grpc_client()
+ .await?
+ .remove_split_tunnel_process(pid)
+ .await?;
Ok(())
}
("clear", Some(_)) => {
- new_rpc_client()?.clear_split_tunnel_processes()?;
+ new_grpc_client()
+ .await?
+ .clear_split_tunnel_processes(())
+ .await?;
Ok(())
}
("list", Some(_)) => {
- let pids = new_rpc_client()?.get_split_tunnel_processes()?;
+ let mut pids_stream = new_grpc_client()
+ .await?
+ .get_split_tunnel_processes(())
+ .await?
+ .into_inner();
println!("Excluded PIDs:");
- for pid in pids.iter() {
+ while let Some(pid) = pids_stream.message().await? {
println!(" {}", pid);
}
diff --git a/mullvad-cli/src/cmds/status.rs b/mullvad-cli/src/cmds/status.rs
index ffa834134e..ec05492c46 100644
--- a/mullvad-cli/src/cmds/status.rs
+++ b/mullvad-cli/src/cmds/status.rs
@@ -1,11 +1,19 @@
-use crate::{new_rpc_client, Command, Error, Result};
-use futures::{Future, Stream};
-use mullvad_ipc_client::DaemonRpcClient;
-use mullvad_types::{auth_failed::AuthFailed, states::TunnelState, DaemonEvent};
-use talpid_types::tunnel::{ErrorState, ErrorStateCause};
+use crate::{format::print_keygen_event, new_grpc_client, proto, Command, Error, Result};
+use mullvad_types::auth_failed::AuthFailed;
+use proto::{
+ daemon_event::Event as EventType,
+ error_state::{
+ firewall_policy_error::ErrorType as FirewallPolicyErrorType, Cause as ErrorStateCause,
+ FirewallPolicyError, GenerationError,
+ },
+ management_service_client::ManagementServiceClient,
+ ErrorState, ProxyType, TransportProtocol, TunnelEndpoint, TunnelState, TunnelType,
+};
+use std::fmt::Write;
pub struct Status;
+#[async_trait::async_trait]
impl Command for Status {
fn name(&self) -> &'static str {
"status"
@@ -31,127 +39,237 @@ impl Command for Status {
)
}
- fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- let state = rpc.get_state()?;
+ async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ let state = rpc.get_tunnel_state(()).await?.into_inner();
print_state(&state);
if matches.is_present("location") {
- print_location(&mut rpc)?;
+ print_location(&mut rpc).await?;
}
if let Some(listen_matches) = matches.subcommand_matches("listen") {
let verbose = listen_matches.is_present("verbose");
- let subscription = rpc
- .daemon_event_subscribe()
- .wait()
- .map_err(Error::CantSubscribe)?;
- for event in subscription.wait() {
- match event? {
- DaemonEvent::TunnelState(new_state) => {
+
+ let mut events = rpc.events_listen(()).await?.into_inner();
+
+ while let Some(event) = events.message().await? {
+ match event.event.unwrap() {
+ EventType::TunnelState(new_state) => {
print_state(&new_state);
- use self::TunnelState::*;
- match new_state {
- Connected { .. } | Disconnected => {
+ use proto::tunnel_state::State::*;
+ match new_state.state.unwrap() {
+ Connected(..) | Disconnected(..) => {
if matches.is_present("location") {
- print_location(&mut rpc)?;
+ print_location(&mut rpc).await?;
}
}
_ => {}
}
}
- DaemonEvent::Settings(settings) => {
+ EventType::Settings(settings) => {
if verbose {
println!("New settings: {:#?}", settings);
}
}
- DaemonEvent::RelayList(relay_list) => {
+ EventType::RelayList(relay_list) => {
if verbose {
println!("New relay list: {:#?}", relay_list);
}
}
- DaemonEvent::AppVersionInfo(app_version_info) => {
+ EventType::VersionInfo(app_version_info) => {
if verbose {
println!("New app version info: {:#?}", app_version_info);
}
}
- DaemonEvent::WireguardKey(key_event) => {
+ EventType::KeyEvent(key_event) => {
if verbose {
- println!("{}", key_event);
+ print!("Key event: ");
+ print_keygen_event(&key_event);
}
}
}
}
}
+
Ok(())
}
}
fn print_state(state: &TunnelState) {
- use self::TunnelState::*;
+ use proto::{tunnel_state, tunnel_state::State::*};
+
print!("Tunnel status: ");
- match state {
- Error(reason) => print_error_state(reason),
- Connected { endpoint, .. } => {
- println!("Connected to {}", endpoint);
+ match state.state.as_ref().unwrap() {
+ Error(error) => print_error_state(error.error_state.as_ref().unwrap()),
+ Connected(tunnel_state::Connected { relay_info }) => {
+ let endpoint = relay_info
+ .as_ref()
+ .unwrap()
+ .tunnel_endpoint
+ .as_ref()
+ .unwrap();
+ println!("Connected to {}", format_endpoint(&endpoint));
}
- Connecting { endpoint, .. } => println!("Connecting to {}...", endpoint),
- Disconnected => println!("Disconnected"),
+ Connecting(tunnel_state::Connecting { relay_info }) => {
+ let endpoint = relay_info
+ .as_ref()
+ .unwrap()
+ .tunnel_endpoint
+ .as_ref()
+ .unwrap();
+ println!("Connecting to {}...", format_endpoint(&endpoint));
+ }
+ Disconnected(_) => println!("Disconnected"),
Disconnecting(_) => println!("Disconnecting..."),
}
}
+fn format_endpoint(endpoint: &TunnelEndpoint) -> String {
+ let mut out = format!(
+ "{} {} over {}",
+ match TunnelType::from_i32(endpoint.tunnel_type).expect("unknown tunnel protocol") {
+ TunnelType::Wireguard => "WireGuard",
+ TunnelType::Openvpn => "OpenVPN",
+ TunnelType::AnyTunnel => panic!("unexpected tunnel protocol"),
+ },
+ endpoint.address,
+ format_protocol(
+ TransportProtocol::from_i32(endpoint.protocol).expect("unknown transport protocol")
+ ),
+ );
+
+ if let Some(ref proxy) = endpoint.proxy {
+ write!(
+ &mut out,
+ " via {} {} over {}",
+ match ProxyType::from_i32(proxy.proxy_type).expect("unknown proxy type") {
+ ProxyType::Shadowsocks => "Shadowsocks",
+ ProxyType::Custom => "custom bridge",
+ },
+ proxy.address,
+ format_protocol(
+ TransportProtocol::from_i32(proxy.protocol).expect("unknown transport protocol")
+ ),
+ )
+ .unwrap();
+ }
+
+ out
+}
+
fn print_error_state(error_state: &ErrorState) {
- if !error_state.is_blocking() {
+ if !error_state.is_blocking {
eprintln!("Mullvad daemon failed to setup firewall rules!");
eprintln!("Deamon cannot block traffic from flowing, non-local traffic will leak");
}
- print_blocked_reason(error_state.cause());
-}
-
-fn print_blocked_reason(reason: &ErrorStateCause) {
- match reason {
- ErrorStateCause::AuthFailed(ref auth_failure) => {
- let auth_failure_str = auth_failure
- .as_ref()
- .map(|s| s.as_str())
- .unwrap_or("Account authentication failed");
- println!("Blocked: {}", AuthFailed::from(auth_failure_str));
- }
- #[cfg(target_os = "linux")]
- ErrorStateCause::SetFirewallPolicyError(error) => {
+ match ErrorStateCause::from_i32(error_state.cause) {
+ Some(ErrorStateCause::AuthFailed) => {
println!(
"Blocked: {}",
- ErrorStateCause::SetFirewallPolicyError(error.clone())
+ AuthFailed::from(error_state.auth_fail_reason.as_ref())
);
+ }
+ #[cfg(target_os = "linux")]
+ Some(ErrorStateCause::SetFirewallPolicyError) => {
+ println!("Blocked: {}", error_state_to_string(error_state));
println!("Your kernel might be terribly out of date or missing nftables");
}
- other => println!("Blocked: {}", other),
+ _ => println!("Blocked: {}", error_state_to_string(error_state)),
}
}
-fn print_location(rpc: &mut DaemonRpcClient) -> Result<()> {
- let location = match rpc.get_current_location()? {
- Some(loc) => loc,
- None => {
- println!("Location data unavailable");
- return Ok(());
+fn error_state_to_string(error_state: &ErrorState) -> String {
+ use ErrorStateCause::*;
+
+ let error_str = match ErrorStateCause::from_i32(error_state.cause).expect("unknown error cause")
+ {
+ AuthFailed => {
+ return if error_state.auth_fail_reason.is_empty() {
+ "Authentication with remote server failed".to_string()
+ } else {
+ format!(
+ "Authentication with remote server failed: {}",
+ error_state.auth_fail_reason
+ )
+ };
+ }
+ Ipv6Unavailable => "Failed to configure IPv6 because it's disabled in the platform",
+ SetFirewallPolicyError => {
+ return policy_error_to_string(error_state.policy_error.as_ref().unwrap())
}
+ SetDnsError => "Failed to set system DNS server",
+ StartTunnelError => "Failed to start connection to remote server",
+ TunnelParameterError => {
+ return format!(
+ "Failure to generate tunnel parameters: {}",
+ tunnel_parameter_error_to_string(error_state.parameter_error)
+ );
+ }
+ IsOffline => "This device is offline, no tunnels can be established",
+ TapAdapterProblem => "A problem with the TAP adapter has been detected",
+ #[cfg(target_os = "android")]
+ VpnPermissionDenied => "The Android VPN permission was denied when creating the tunnel",
+ #[cfg(not(target_os = "android"))]
+ _ => unreachable!("unknown error cause"),
};
- if let Some(hostname) = location.hostname {
- println!("Relay: {}", hostname);
+
+ error_str.to_string()
+}
+
+fn tunnel_parameter_error_to_string(parameter_error: i32) -> &'static str {
+ match GenerationError::from_i32(parameter_error).expect("unknown generation error") {
+ GenerationError::NoMatchingRelay => "Failure to select a matching tunnel relay",
+ GenerationError::NoMatchingBridgeRelay => "Failure to select a matching bridge relay",
+ GenerationError::NoWireguardKey => "No wireguard key available",
+ GenerationError::CustomTunnelHostResolutionError => {
+ "Can't resolve hostname for custom tunnel host"
+ }
}
- if let Some(ipv4) = location.ipv4 {
- println!("IPv4: {}", ipv4);
+}
+
+fn policy_error_to_string(policy_error: &FirewallPolicyError) -> String {
+ let cause = match FirewallPolicyErrorType::from_i32(policy_error.r#type)
+ .expect("unknown policy error")
+ {
+ FirewallPolicyErrorType::Generic => return "Failed to set firewall policy".to_string(),
+ FirewallPolicyErrorType::Locked => format!(
+ "An application prevented the firewall policy from being set: {} (pid {})",
+ policy_error.lock_name, policy_error.lock_pid
+ ),
+ };
+ format!("Failed to set firewall policy: {}", cause)
+}
+
+async fn print_location(
+ rpc: &mut ManagementServiceClient<tonic::transport::Channel>,
+) -> Result<()> {
+ let location = rpc.get_current_location(()).await;
+ let location = match location {
+ Ok(response) => response.into_inner(),
+ Err(status) => {
+ if status.code() == tonic::Code::NotFound {
+ println!("Location data unavailable");
+ return Ok(());
+ } else {
+ return Err(Error::GrpcClientError(status));
+ }
+ }
+ };
+ if !location.hostname.is_empty() {
+ println!("Relay: {}", location.hostname);
+ }
+ if !location.ipv4.is_empty() {
+ println!("IPv4: {}", location.ipv4);
}
- if let Some(ipv6) = location.ipv6 {
- println!("IPv6: {}", ipv6);
+ if !location.ipv6.is_empty() {
+ println!("IPv6: {}", location.ipv6);
}
print!("Location: ");
- if let Some(city) = location.city {
- print!("{}, ", city);
+ if !location.city.is_empty() {
+ print!("{}, ", location.city);
}
println!("{}", location.country);
@@ -161,3 +279,11 @@ fn print_location(rpc: &mut DaemonRpcClient) -> Result<()> {
);
Ok(())
}
+
+fn format_protocol(protocol: TransportProtocol) -> &'static str {
+ match protocol {
+ TransportProtocol::Udp => "UDP",
+ TransportProtocol::Tcp => "TCP",
+ TransportProtocol::AnyProtocol => panic!("unexpected transport protocol"),
+ }
+}
diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs
index 5349174d4c..3c3d55092f 100644
--- a/mullvad-cli/src/cmds/tunnel.rs
+++ b/mullvad-cli/src/cmds/tunnel.rs
@@ -1,10 +1,10 @@
-use crate::{new_rpc_client, Command, Result};
+use crate::{format::print_keygen_event, new_grpc_client, proto, Command, Error, Result};
use clap::value_t;
-
-use mullvad_types::settings::TunnelOptions;
+use proto::TunnelOptions;
pub struct Tunnel;
+#[async_trait::async_trait]
impl Command for Tunnel {
fn name(&self) -> &'static str {
"tunnel"
@@ -19,11 +19,11 @@ impl Command for Tunnel {
.subcommand(create_ipv6_subcommand())
}
- fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> {
match matches.subcommand() {
- ("openvpn", Some(openvpn_matches)) => Self::handle_openvpn_cmd(openvpn_matches),
- ("wireguard", Some(wg_matches)) => Self::handle_wireguard_cmd(wg_matches),
- ("ipv6", Some(ipv6_matches)) => Self::handle_ipv6_cmd(ipv6_matches),
+ ("openvpn", Some(openvpn_matches)) => Self::handle_openvpn_cmd(openvpn_matches).await,
+ ("wireguard", Some(wg_matches)) => Self::handle_wireguard_cmd(wg_matches).await,
+ ("ipv6", Some(ipv6_matches)) => Self::handle_ipv6_cmd(ipv6_matches).await,
_ => {
unreachable!("unhandled comand");
}
@@ -104,40 +104,42 @@ fn create_ipv6_subcommand() -> clap::App<'static, 'static> {
}
impl Tunnel {
- fn handle_openvpn_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn handle_openvpn_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> {
match matches.subcommand() {
- ("mssfix", Some(mssfix_matches)) => Self::handle_openvpn_mssfix_cmd(mssfix_matches),
+ ("mssfix", Some(mssfix_matches)) => {
+ Self::handle_openvpn_mssfix_cmd(mssfix_matches).await
+ }
_ => unreachable!("unhandled command"),
}
}
- fn handle_openvpn_mssfix_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn handle_openvpn_mssfix_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> {
match matches.subcommand() {
- ("get", Some(_)) => Self::process_openvpn_mssfix_get(),
- ("unset", Some(_)) => Self::process_openvpn_mssfix_unset(),
- ("set", Some(set_matches)) => Self::process_openvpn_mssfix_set(set_matches),
+ ("get", Some(_)) => Self::process_openvpn_mssfix_get().await,
+ ("unset", Some(_)) => Self::process_openvpn_mssfix_unset().await,
+ ("set", Some(set_matches)) => Self::process_openvpn_mssfix_set(set_matches).await,
_ => unreachable!("unhandled command"),
}
}
- fn handle_wireguard_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn handle_wireguard_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> {
match matches.subcommand() {
("mtu", Some(matches)) => match matches.subcommand() {
- ("get", _) => Self::process_wireguard_mtu_get(),
- ("set", Some(matches)) => Self::process_wireguard_mtu_set(matches),
- ("unset", _) => Self::process_wireguard_mtu_unset(),
+ ("get", _) => Self::process_wireguard_mtu_get().await,
+ ("set", Some(matches)) => Self::process_wireguard_mtu_set(matches).await,
+ ("unset", _) => Self::process_wireguard_mtu_unset().await,
_ => unreachable!("unhandled command"),
},
("key", Some(matches)) => match matches.subcommand() {
- ("check", _) => Self::process_wireguard_key_check(),
- ("regenerate", _) => Self::process_wireguard_key_generate(),
+ ("check", _) => Self::process_wireguard_key_check().await,
+ ("regenerate", _) => Self::process_wireguard_key_generate().await,
("rotation-interval", Some(matches)) => match matches.subcommand() {
- ("get", _) => Self::process_wireguard_rotation_interval_get(),
+ ("get", _) => Self::process_wireguard_rotation_interval_get().await,
("set", Some(matches)) => {
- Self::process_wireguard_rotation_interval_set(matches)
+ Self::process_wireguard_rotation_interval_set(matches).await
}
- ("reset", _) => Self::process_wireguard_rotation_interval_reset(),
+ ("reset", _) => Self::process_wireguard_rotation_interval_reset().await,
_ => unreachable!("unhandled command"),
},
_ => unreachable!("unhandled command"),
@@ -147,140 +149,150 @@ impl Tunnel {
}
}
- fn process_wireguard_mtu_get() -> Result<()> {
- let tunnel_options = Self::get_tunnel_options()?;
+ async fn process_wireguard_mtu_get() -> Result<()> {
+ let tunnel_options = Self::get_tunnel_options().await?;
+ let mtu = tunnel_options.wireguard.unwrap().mtu;
println!(
"mtu: {}",
- tunnel_options
- .wireguard
- .mtu
- .map(|mtu| mtu.to_string())
- .unwrap_or_else(|| "unset".to_owned())
+ if mtu != 0 {
+ mtu.to_string()
+ } else {
+ "unset".to_string()
+ },
);
Ok(())
}
- fn process_wireguard_mtu_set(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn process_wireguard_mtu_set(matches: &clap::ArgMatches<'_>) -> Result<()> {
let mtu = value_t!(matches.value_of("mtu"), u16).unwrap_or_else(|e| e.exit());
- let mut rpc = new_rpc_client()?;
- rpc.set_wireguard_mtu(Some(mtu))?;
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_wireguard_mtu(mtu as u32).await?;
println!("Wireguard MTU has been updated");
Ok(())
}
- fn process_wireguard_mtu_unset() -> Result<()> {
- let mut rpc = new_rpc_client()?;
- rpc.set_wireguard_mtu(None)?;
+ async fn process_wireguard_mtu_unset() -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_wireguard_mtu(0).await?;
println!("Wireguard MTU has been unset");
Ok(())
}
- fn process_wireguard_key_check() -> Result<()> {
- let mut rpc = new_rpc_client()?;
- match rpc.get_wireguard_key()? {
- Some(key) => {
- println!("Current key : {}", &key.key);
- println!(
- "Key created on : {}",
- &key.created.with_timezone(&chrono::offset::Local)
- );
- }
- None => {
- println!("No key is set");
- return Ok(());
+ async fn process_wireguard_key_check() -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ let key = rpc.get_wireguard_key(()).await;
+ let key = match key {
+ Ok(response) => Some(response.into_inner()),
+ Err(status) => {
+ if status.code() == tonic::Code::NotFound {
+ None
+ } else {
+ return Err(Error::GrpcClientError(status));
+ }
}
};
+ if let Some(key) = key {
+ println!("Current key : {}", base64::encode(&key.key));
+ println!(
+ "Key created on : {}",
+ Self::format_key_timestamp(&key.created.unwrap())
+ );
+ } else {
+ println!("No key is set");
+ return Ok(());
+ }
- let is_valid = rpc.verify_wireguard_key()?;
+ let is_valid = rpc.verify_wireguard_key(()).await?.into_inner();
println!("Key is valid for use with current account: {}", is_valid);
Ok(())
}
- fn process_wireguard_key_generate() -> Result<()> {
- let mut rpc = new_rpc_client()?;
- let result = rpc
- .generate_wireguard_key()
- .map_err(|e| crate::Error::RpcClientError(e))?;
- println!("{}", result);
+ async fn process_wireguard_key_generate() -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ let keygen_event = rpc.generate_wireguard_key(()).await?;
+ print_keygen_event(&keygen_event.into_inner());
Ok(())
}
- fn process_wireguard_rotation_interval_get() -> Result<()> {
- let tunnel_options = Self::get_tunnel_options()?;
+ async fn process_wireguard_rotation_interval_get() -> Result<()> {
+ let tunnel_options = Self::get_tunnel_options().await?;
println!(
"Rotation interval: {} hour(s)",
- tunnel_options
- .wireguard
- .automatic_rotation
- .map(|interval| interval.to_string())
- .unwrap_or_else(|| "default".to_owned())
+ tunnel_options.wireguard.unwrap().automatic_rotation
);
Ok(())
}
- fn process_wireguard_rotation_interval_set(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn process_wireguard_rotation_interval_set(matches: &clap::ArgMatches<'_>) -> Result<()> {
let rotate_interval =
value_t!(matches.value_of("interval"), u32).unwrap_or_else(|e| e.exit());
- let mut rpc = new_rpc_client()?;
- rpc.set_wireguard_rotation_interval(Some(rotate_interval))?;
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_wireguard_rotation_interval(rotate_interval).await?;
println!("Set key rotation interval: {} hour(s)", rotate_interval);
Ok(())
}
- fn process_wireguard_rotation_interval_reset() -> Result<()> {
- let mut rpc = new_rpc_client()?;
- rpc.set_wireguard_rotation_interval(None)?;
+ async fn process_wireguard_rotation_interval_reset() -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ rpc.reset_wireguard_rotation_interval(()).await?;
println!("Set key rotation interval: default");
Ok(())
}
- fn handle_ipv6_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn handle_ipv6_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> {
if matches.subcommand_matches("get").is_some() {
- Self::process_ipv6_get()
+ Self::process_ipv6_get().await
} else if let Some(m) = matches.subcommand_matches("set") {
- Self::process_ipv6_set(m)
+ Self::process_ipv6_set(m).await
} else {
unreachable!("unhandled command");
}
}
- fn process_openvpn_mssfix_get() -> Result<()> {
- let tunnel_options = Self::get_tunnel_options()?;
+ async fn process_openvpn_mssfix_get() -> Result<()> {
+ let tunnel_options = Self::get_tunnel_options().await?;
+ let mssfix = tunnel_options.openvpn.unwrap().mssfix;
println!(
"mssfix: {}",
- tunnel_options
- .openvpn
- .mssfix
- .map_or_else(|| "unset".to_owned(), |v| v.to_string())
+ if mssfix != 0 {
+ mssfix.to_string()
+ } else {
+ "unset".to_string()
+ },
);
Ok(())
}
- fn get_tunnel_options() -> Result<TunnelOptions> {
- let mut rpc = new_rpc_client()?;
- Ok(rpc.get_settings()?.tunnel_options)
+ async fn get_tunnel_options() -> Result<TunnelOptions> {
+ let mut rpc = new_grpc_client().await?;
+ Ok(rpc
+ .get_settings(())
+ .await?
+ .into_inner()
+ .tunnel_options
+ .unwrap())
}
- fn process_openvpn_mssfix_unset() -> Result<()> {
- let mut rpc = new_rpc_client()?;
- rpc.set_openvpn_mssfix(None)?;
+ async fn process_openvpn_mssfix_unset() -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_openvpn_mssfix(0).await?;
println!("mssfix parameter has been unset");
Ok(())
}
- fn process_openvpn_mssfix_set(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn process_openvpn_mssfix_set(matches: &clap::ArgMatches<'_>) -> Result<()> {
let new_value = value_t!(matches.value_of("mssfix"), u16).unwrap_or_else(|e| e.exit());
- let mut rpc = new_rpc_client()?;
- rpc.set_openvpn_mssfix(Some(new_value))?;
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_openvpn_mssfix(new_value as u32).await?;
println!("mssfix parameter has been updated");
Ok(())
}
- fn process_ipv6_get() -> Result<()> {
- let tunnel_options = Self::get_tunnel_options()?;
+ async fn process_ipv6_get() -> Result<()> {
+ let tunnel_options = Self::get_tunnel_options().await?;
println!(
"IPv6: {}",
- if tunnel_options.generic.enable_ipv6 {
+ if tunnel_options.generic.unwrap().enable_ipv6 {
"on"
} else {
"off"
@@ -289,12 +301,20 @@ impl Tunnel {
Ok(())
}
- fn process_ipv6_set(matches: &clap::ArgMatches<'_>) -> Result<()> {
+ async fn process_ipv6_set(matches: &clap::ArgMatches<'_>) -> Result<()> {
let enabled = matches.value_of("enable").unwrap() == "on";
- let mut rpc = new_rpc_client()?;
- rpc.set_enable_ipv6(enabled)?;
- println!("IPv6 setting has been updated");
+ let mut rpc = new_grpc_client().await?;
+ rpc.set_enable_ipv6(enabled).await?;
+ if enabled {
+ println!("Enabled IPv6");
+ } else {
+ println!("Disabled IPv6");
+ }
Ok(())
}
+
+ fn format_key_timestamp(timestamp: &prost_types::Timestamp) -> String {
+ chrono::NaiveDateTime::from_timestamp(timestamp.seconds, timestamp.nanos as u32).to_string()
+ }
}
diff --git a/mullvad-cli/src/cmds/version.rs b/mullvad-cli/src/cmds/version.rs
index 4e0f3a0d8b..48b3e621ed 100644
--- a/mullvad-cli/src/cmds/version.rs
+++ b/mullvad-cli/src/cmds/version.rs
@@ -1,7 +1,8 @@
-use crate::{new_rpc_client, Command, Result};
+use crate::{new_grpc_client, Command, Result};
pub struct Version;
+#[async_trait::async_trait]
impl Command for Version {
fn name(&self) -> &'static str {
"version"
@@ -12,23 +13,24 @@ impl Command for Version {
.about("Shows current version, and the currently supported versions")
}
- fn run(&self, _: &clap::ArgMatches<'_>) -> Result<()> {
- let mut rpc = new_rpc_client()?;
- let current_version = rpc.get_current_version()?;
+ async fn run(&self, _: &clap::ArgMatches<'_>) -> Result<()> {
+ let mut rpc = new_grpc_client().await?;
+ let current_version = rpc.get_current_version(()).await?.into_inner();
println!("Current version: {}", current_version);
- let version_info = rpc.get_version_info()?;
+ let version_info = rpc.get_version_info(()).await?.into_inner();
println!("\tIs supported: {}", version_info.supported);
- match version_info.suggested_upgrade {
- Some(version) => println!("\tSuggested update: {}", version),
- None => println!("\tNo newer version is available"),
+ if !version_info.suggested_upgrade.is_empty() {
+ println!("\tSuggested update: {}", version_info.suggested_upgrade);
+ } else {
+ println!("\tNo newer version is available");
}
if !version_info.latest_stable.is_empty() {
println!("\tLatest stable version: {}", version_info.latest_stable);
}
- let settings = rpc.get_settings()?;
+ let settings = rpc.get_settings(()).await?.into_inner();
if settings.show_beta_releases {
println!("\t Latest beta version: {}", version_info.latest_beta);
};
diff --git a/mullvad-cli/src/format.rs b/mullvad-cli/src/format.rs
new file mode 100644
index 0000000000..c2e71fce0f
--- /dev/null
+++ b/mullvad-cli/src/format.rs
@@ -0,0 +1,20 @@
+use crate::proto::KeygenEvent;
+
+pub fn print_keygen_event(key_event: &KeygenEvent) {
+ use crate::proto::keygen_event::KeygenEvent as EventType;
+
+ match EventType::from_i32(key_event.event).unwrap() {
+ EventType::NewKey => {
+ println!(
+ "New WireGuard key: {}",
+ base64::encode(&key_event.new_key.as_ref().unwrap().key)
+ );
+ }
+ EventType::TooManyKeys => {
+ println!("Account has too many keys already");
+ }
+ EventType::GenerationFailure => {
+ println!("Failed to generate new WireGuard key");
+ }
+ }
+}
diff --git a/mullvad-cli/src/location.rs b/mullvad-cli/src/location.rs
index 20853893b8..3d2705ae88 100644
--- a/mullvad-cli/src/location.rs
+++ b/mullvad-cli/src/location.rs
@@ -1,4 +1,4 @@
-use mullvad_types::relay_constraints::{Constraint, LocationConstraint};
+use crate::proto::RelayLocation;
pub fn get_subcommand() -> clap::App<'static, 'static> {
clap::SubCommand::with_name("location")
@@ -22,24 +22,33 @@ pub fn get_subcommand() -> clap::App<'static, 'static> {
)
}
-pub fn get_constraint(matches: &clap::ArgMatches<'_>) -> Constraint<LocationConstraint> {
+pub fn get_constraint(matches: &clap::ArgMatches<'_>) -> RelayLocation {
let country_original = matches.value_of("country").unwrap();
let country = country_original.to_lowercase();
let city = matches.value_of("city").map(str::to_lowercase);
let hostname = matches.value_of("hostname").map(str::to_lowercase);
match (country_original, city, hostname) {
- ("any", None, None) => Constraint::Any,
+ ("any", None, None) => RelayLocation::default(),
("any", ..) => clap::Error::with_description(
"City can't be given when selecting 'any' country",
clap::ErrorKind::InvalidValue,
)
.exit(),
- (_, None, None) => Constraint::Only(LocationConstraint::Country(country)),
- (_, Some(city), None) => Constraint::Only(LocationConstraint::City(country, city)),
- (_, Some(city), Some(hostname)) => {
- Constraint::Only(LocationConstraint::Hostname(country, city, hostname))
- }
+ (_, None, None) => RelayLocation {
+ country,
+ ..Default::default()
+ },
+ (_, Some(city), None) => RelayLocation {
+ country,
+ city,
+ ..Default::default()
+ },
+ (_, Some(city), Some(hostname)) => RelayLocation {
+ country,
+ city,
+ hostname,
+ },
(..) => clap::Error::with_description(
"Invalid country, city and hostname combination given",
clap::ErrorKind::InvalidValue,
@@ -48,6 +57,22 @@ pub fn get_constraint(matches: &clap::ArgMatches<'_>) -> Constraint<LocationCons
}
}
+pub fn format_location(location: Option<&RelayLocation>) -> String {
+ if let Some(location) = location {
+ if !location.hostname.is_empty() {
+ return format!(
+ "city {}, {}, hostname {}",
+ location.city, location.country, location.hostname
+ );
+ } else if !location.city.is_empty() {
+ return format!("city {}, {}", location.city, location.country);
+ } else if !location.country.is_empty() {
+ return format!("country {}", location.country);
+ }
+ }
+ "any location".to_string()
+}
+
fn country_code_validator(code: String) -> std::result::Result<(), String> {
if code.len() == 2 || code == "any" {
Ok(())
diff --git a/mullvad-cli/src/main.rs b/mullvad-cli/src/main.rs
index 3d8d2a4aef..11309ff019 100644
--- a/mullvad-cli/src/main.rs
+++ b/mullvad-cli/src/main.rs
@@ -1,11 +1,12 @@
#![deny(rust_2018_idioms)]
+use async_trait::async_trait;
use clap::{crate_authors, crate_description};
-use mullvad_ipc_client::{new_standalone_ipc_client, DaemonRpcClient};
use std::{collections::HashMap, io};
use talpid_types::ErrorExt;
mod cmds;
+mod format;
mod location;
pub const BIN_NAME: &str = "mullvad";
@@ -13,31 +14,50 @@ pub const PRODUCT_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/produc
pub type Result<T> = std::result::Result<T, Error>;
+mod proto {
+ tonic::include_proto!("mullvad_daemon.management_interface");
+}
+use proto::management_service_client::ManagementServiceClient;
+
+use parity_tokio_ipc::Endpoint as IpcEndpoint;
+use tonic::{
+ self,
+ transport::{Endpoint, Uri},
+};
+use tower::service_fn;
+
#[derive(err_derive::Error, Debug)]
pub enum Error {
#[error(display = "Failed to connect to daemon")]
DaemonNotRunning(#[error(source)] io::Error),
- #[error(display = "Can't subscribe to daemon states")]
- CantSubscribe(#[error(source)] mullvad_ipc_client::PubSubError),
+ #[error(display = "Failed to connect to mullvad-daemon over RPC")]
+ GrpcTransportError(#[error(source)] tonic::transport::Error),
#[error(display = "Failed to communicate with mullvad-daemon over RPC")]
- RpcClientError(#[error(source)] mullvad_ipc_client::Error),
+ GrpcClientError(#[error(source)] tonic::Status),
/// The given command is not correct in some way
#[error(display = "Invalid command: {}", _0)]
InvalidCommand(&'static str),
}
-pub fn new_rpc_client() -> Result<DaemonRpcClient> {
- match new_standalone_ipc_client(&mullvad_paths::get_rpc_socket_path()) {
- Err(e) => Err(Error::DaemonNotRunning(e)),
- Ok(client) => Ok(client),
- }
+pub async fn new_grpc_client() -> Result<ManagementServiceClient<tonic::transport::Channel>> {
+ let ipc_path = mullvad_paths::get_rpc_socket_path();
+
+ // The URI will be ignored
+ let channel = Endpoint::from_static("lttp://[::]:50051")
+ .connect_with_connector(service_fn(move |_: Uri| {
+ IpcEndpoint::connect(ipc_path.clone())
+ }))
+ .await?;
+
+ Ok(ManagementServiceClient::new(channel))
}
-fn main() {
- let exit_code = match run() {
+#[tokio::main]
+async fn main() {
+ let exit_code = match run().await {
Ok(_) => 0,
Err(error) => {
eprintln!("{}", error.display_chain());
@@ -47,7 +67,7 @@ fn main() {
std::process::exit(exit_code);
}
-fn run() -> Result<()> {
+async fn run() -> Result<()> {
env_logger::init();
let commands = cmds::get_commands();
@@ -85,7 +105,7 @@ fn run() -> Result<()> {
}
(sub_name, Some(sub_matches)) => {
if let Some(cmd) = commands.get(sub_name) {
- cmd.run(sub_matches)
+ cmd.run(sub_matches).await
} else {
unreachable!("No command matched");
}
@@ -109,10 +129,11 @@ fn build_cli(commands: &HashMap<&'static str, Box<dyn Command>>) -> clap::App<'s
.subcommands(commands.values().map(|cmd| cmd.clap_subcommand()))
}
+#[async_trait]
pub trait Command {
fn name(&self) -> &'static str;
fn clap_subcommand(&self) -> clap::App<'static, 'static>;
- fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()>;
+ async fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()>;
}
diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml
index a33f808ec2..19419a8a81 100644
--- a/mullvad-daemon/Cargo.toml
+++ b/mullvad-daemon/Cargo.toml
@@ -16,11 +16,6 @@ fern = { version = "0.5", features = ["colored"] }
futures01 = { package = "futures", version = "0.1" }
futures = { package = "futures", version = "0.3", features = [ "compat" ]}
ipnetwork = "0.16"
-jsonrpc-client-core = "0.5"
-jsonrpc-core = { git = "https://github.com/mullvad/jsonrpc", branch = "mullvad-fork" }
-jsonrpc-ipc-server = { git = "https://github.com/mullvad/jsonrpc", branch = "mullvad-fork" }
-jsonrpc-macros = { git = "https://github.com/mullvad/jsonrpc", branch = "mullvad-fork" }
-jsonrpc-pubsub = { git = "https://github.com/mullvad/jsonrpc", branch = "mullvad-fork" }
lazy_static = "1.0"
log = "0.4"
log-panics = "2.0.0"
@@ -35,14 +30,24 @@ tokio-retry = "0.2"
tokio-timer = "0.1"
uuid = { version = "0.7", features = ["v4"] }
-mullvad-ipc-client = { path = "../mullvad-ipc-client" }
mullvad-paths = { path = "../mullvad-paths" }
mullvad-types = { path = "../mullvad-types" }
mullvad-rpc = { path = "../mullvad-rpc" }
talpid-core = { path = "../talpid-core" }
-talpid-ipc = { path = "../talpid-ipc" }
talpid-types = { path = "../talpid-types" }
+tonic = "0.2"
+tower = "0.3"
+prost = "0.6"
+prost-types = "0.6"
+
+parity-tokio-ipc = "0.7"
+
+triggered = "0.1.1"
+
+[build-dependencies]
+tonic-build = { version = "0.2", default-features = false, features = ["transport", "prost"] }
+
[target.'cfg(target_os="android")'.dependencies]
android_logger = "0.8"
diff --git a/mullvad-daemon/build.rs b/mullvad-daemon/build.rs
index c2571ca799..ef6fc38a7c 100644
--- a/mullvad-daemon/build.rs
+++ b/mullvad-daemon/build.rs
@@ -1,6 +1,8 @@
use std::{env, fs, path::PathBuf, process::Command};
fn main() {
+ tonic_build::compile_protos("proto/management_interface.proto").unwrap();
+
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let product_version = env!("CARGO_PKG_VERSION").replacen(".0", "", 1);
diff --git a/mullvad-daemon/proto/management_interface.proto b/mullvad-daemon/proto/management_interface.proto
new file mode 100644
index 0000000000..ea62f6976e
--- /dev/null
+++ b/mullvad-daemon/proto/management_interface.proto
@@ -0,0 +1,466 @@
+syntax = "proto3";
+
+package mullvad_daemon.management_interface;
+
+import "google/protobuf/empty.proto";
+import "google/protobuf/timestamp.proto";
+import "google/protobuf/wrappers.proto";
+
+service ManagementService {
+ // Control and get tunnel state
+ rpc ConnectTunnel(google.protobuf.Empty) returns (google.protobuf.Empty) {}
+ rpc DisconnectTunnel(google.protobuf.Empty) returns (google.protobuf.Empty) {}
+ rpc ReconnectTunnel(google.protobuf.Empty) returns (google.protobuf.Empty) {}
+ rpc GetTunnelState(google.protobuf.Empty) returns (TunnelState) {}
+
+ // Control the daemon and receive events
+ rpc EventsListen(google.protobuf.Empty) returns (stream DaemonEvent) {}
+ rpc PrepareRestart(google.protobuf.Empty) returns (google.protobuf.Empty) {}
+ rpc Shutdown(google.protobuf.Empty) returns (google.protobuf.Empty) {}
+ rpc FactoryReset(google.protobuf.Empty) returns (google.protobuf.Empty) {}
+
+ rpc GetCurrentVersion(google.protobuf.Empty) returns (google.protobuf.StringValue) {}
+ rpc GetVersionInfo(google.protobuf.Empty) returns (AppVersionInfo) {}
+
+ // Relays and tunnel constraints
+ rpc UpdateRelayLocations(google.protobuf.Empty) returns (google.protobuf.Empty) {}
+ rpc UpdateRelaySettings(RelaySettingsUpdate) returns (google.protobuf.Empty) {}
+ rpc GetRelayLocations(google.protobuf.Empty) returns (stream RelayListCountry) {}
+ rpc GetCurrentLocation(google.protobuf.Empty) returns (GeoIpLocation) {}
+ rpc SetBridgeSettings(BridgeSettings) returns (google.protobuf.Empty) {}
+ rpc SetBridgeState(BridgeState) returns (google.protobuf.Empty) {}
+
+ // Settings
+ rpc GetSettings(google.protobuf.Empty) returns (Settings) {}
+ rpc SetAllowLan(google.protobuf.BoolValue) returns (google.protobuf.Empty) {}
+ rpc SetShowBetaReleases(google.protobuf.BoolValue) returns (google.protobuf.Empty) {}
+ rpc SetBlockWhenDisconnected(google.protobuf.BoolValue) returns (google.protobuf.Empty) {}
+ rpc SetAutoConnect(google.protobuf.BoolValue) returns (google.protobuf.Empty) {}
+ rpc SetOpenvpnMssfix(google.protobuf.UInt32Value) returns (google.protobuf.Empty) {}
+ rpc SetWireguardMtu(google.protobuf.UInt32Value) returns (google.protobuf.Empty) {}
+ rpc SetEnableIpv6(google.protobuf.BoolValue) returns (google.protobuf.Empty) {}
+
+ // Account management
+ rpc CreateNewAccount(google.protobuf.Empty) returns (google.protobuf.StringValue) {}
+ rpc SetAccount(google.protobuf.StringValue) returns (google.protobuf.Empty) {}
+ rpc GetAccountData(google.protobuf.StringValue) returns (AccountData) {}
+ rpc GetAccountHistory(google.protobuf.Empty) returns (AccountHistory) {}
+ rpc RemoveAccountFromHistory(google.protobuf.StringValue) returns (google.protobuf.Empty) {}
+ rpc ClearAccountHistory(google.protobuf.Empty) returns (google.protobuf.Empty) {}
+ rpc GetWwwAuthToken(google.protobuf.Empty) returns (google.protobuf.StringValue) {}
+ rpc SubmitVoucher(google.protobuf.StringValue) returns (VoucherSubmission) {}
+
+ // WireGuard key management
+ rpc SetWireguardRotationInterval(google.protobuf.UInt32Value) returns (google.protobuf.Empty) {}
+ rpc ResetWireguardRotationInterval(google.protobuf.Empty) returns (google.protobuf.Empty) {}
+ rpc GenerateWireguardKey(google.protobuf.Empty) returns (KeygenEvent) {}
+ rpc GetWireguardKey(google.protobuf.Empty) returns (PublicKey) {}
+ rpc VerifyWireguardKey(google.protobuf.Empty) returns (google.protobuf.BoolValue) {}
+
+ // Split tunneling
+ rpc GetSplitTunnelProcesses(google.protobuf.Empty) returns (stream google.protobuf.Int32Value) {}
+ rpc AddSplitTunnelProcess(google.protobuf.Int32Value) returns (google.protobuf.Empty) {}
+ rpc RemoveSplitTunnelProcess(google.protobuf.Int32Value) returns (google.protobuf.Empty) {}
+ rpc ClearSplitTunnelProcesses(google.protobuf.Empty) returns (google.protobuf.Empty) {}
+}
+
+message RelaySettingsUpdate {
+ oneof type {
+ CustomRelaySettings custom = 1;
+ NormalRelaySettingsUpdate normal = 2;
+ }
+}
+
+message AccountData {
+ google.protobuf.Timestamp expiry = 1;
+}
+
+message VoucherSubmission {
+ uint64 seconds_added = 1;
+ google.protobuf.Timestamp new_expiry = 2;
+}
+
+enum AfterDisconnect {
+ NOTHING = 0;
+ BLOCK = 1;
+ RECONNECT = 2;
+}
+
+message ErrorState {
+ enum Cause {
+ AUTH_FAILED = 0;
+ IPV6_UNAVAILABLE = 1;
+ SET_FIREWALL_POLICY_ERROR = 2;
+ SET_DNS_ERROR = 3;
+ START_TUNNEL_ERROR = 4;
+ TUNNEL_PARAMETER_ERROR = 5;
+ IS_OFFLINE = 6;
+ TAP_ADAPTER_PROBLEM = 7;
+ VPN_PERMISSION_DENIED = 8;
+ }
+
+ enum GenerationError {
+ NO_MATCHING_RELAY = 0;
+ NO_MATCHING_BRIDGE_RELAY = 1;
+ NO_WIREGUARD_KEY = 2;
+ CUSTOM_TUNNEL_HOST_RESOLUTION_ERROR = 3;
+ }
+
+ message FirewallPolicyError {
+ enum ErrorType {
+ GENERIC = 0;
+ LOCKED = 1;
+ }
+ ErrorType type = 1;
+
+ // LOCKED
+ uint32 lock_pid = 2;
+ string lock_name = 3;
+ }
+
+ Cause cause = 1;
+ bool is_blocking = 2;
+
+ // AUTH_FAILED
+ string auth_fail_reason = 3;
+ // TUNNEL_PARAMETER_ERROR
+ GenerationError parameter_error = 4;
+ // SET_FIREWALL_POLICY_ERROR
+ FirewallPolicyError policy_error = 5;
+}
+
+message TunnelState {
+ message Disconnected {
+ }
+ message Connecting {
+ TunnelStateRelayInfo relay_info = 1;
+ }
+ message Connected {
+ TunnelStateRelayInfo relay_info = 1;
+ }
+ message Disconnecting {
+ AfterDisconnect after_disconnect = 1;
+ }
+ message Error {
+ ErrorState error_state = 1;
+ }
+
+ oneof state {
+ Disconnected disconnected = 1;
+ Connecting connecting = 2;
+ Connected connected = 3;
+ Disconnecting disconnecting = 4;
+ Error error = 5;
+ }
+}
+
+enum TunnelType {
+ ANY_TUNNEL = 0;
+ OPENVPN = 1;
+ WIREGUARD = 2;
+}
+
+message TunnelStateRelayInfo {
+ TunnelEndpoint tunnel_endpoint = 1;
+ GeoIpLocation location = 2;
+}
+
+message TunnelEndpoint {
+ string address = 1;
+ TransportProtocol protocol = 2;
+ TunnelType tunnel_type = 3;
+ ProxyEndpoint proxy = 4;
+}
+
+enum ProxyType {
+ SHADOWSOCKS = 0;
+ CUSTOM = 1;
+}
+
+message ProxyEndpoint {
+ string address = 1;
+ TransportProtocol protocol = 2;
+ ProxyType proxy_type = 3;
+}
+
+message GeoIpLocation {
+ string ipv4 = 1;
+ string ipv6 = 2;
+ string country = 3;
+ string city = 4;
+ double latitude = 5;
+ double longitude = 6;
+ bool mullvad_exit_ip = 7;
+ string hostname = 8;
+ string bridge_hostname = 9;
+}
+
+message AccountHistory {
+ repeated string token = 1;
+}
+
+message BridgeSettings {
+ message BridgeConstraints {
+ RelayLocation location = 1;
+ }
+
+ message LocalProxySettings {
+ uint32 port = 1;
+ string peer = 2;
+ }
+ message RemoteProxySettings {
+ string address = 1;
+ // NOTE: optional
+ RemoteProxyAuth auth = 2;
+ }
+ message RemoteProxyAuth {
+ string username = 1;
+ string password = 2;
+ }
+ message ShadowsocksProxySettings {
+ string peer = 1;
+ string password = 2;
+ string cipher = 3;
+ }
+
+ oneof type {
+ BridgeConstraints normal = 1;
+ LocalProxySettings local = 2;
+ RemoteProxySettings remote = 3;
+ ShadowsocksProxySettings shadowsocks = 4;
+ }
+}
+
+message RelayLocation {
+ string country = 1;
+ string city = 2;
+ string hostname = 3;
+}
+
+message BridgeState {
+ enum State {
+ AUTO = 0;
+ ON = 1;
+ OFF = 2;
+ }
+ State state = 1;
+}
+
+message Settings {
+ // NOTE: token is optional
+ string account_token = 1;
+ RelaySettings relay_settings = 2;
+ BridgeSettings bridge_settings = 3;
+ BridgeState bridge_state = 4;
+ bool allow_lan = 5;
+ bool block_when_disconnected = 6;
+ bool auto_connect = 7;
+ TunnelOptions tunnel_options = 8;
+ bool show_beta_releases = 9;
+ // NOTE: skipping version field
+}
+
+message RelaySettings {
+ oneof endpoint {
+ CustomRelaySettings custom = 1;
+ NormalRelaySettings normal = 2;
+ }
+}
+
+message NormalRelaySettings {
+ RelayLocation location = 1;
+ TunnelType tunnel_type = 2;
+ WireguardConstraints wireguard_constraints = 3;
+ OpenvpnConstraints openvpn_constraints = 4;
+}
+
+// Constraints are only updated for fields that are provided
+message NormalRelaySettingsUpdate {
+ RelayLocation location = 1;
+ TunnelTypeUpdate tunnel_type = 2;
+ WireguardConstraints wireguard_constraints = 3;
+ OpenvpnConstraints openvpn_constraints = 4;
+}
+
+message TunnelTypeUpdate {
+ TunnelType tunnel_type = 1;
+}
+
+message OpenvpnConstraints {
+ // NOTE: optional
+ uint32 port = 1;
+ // NOTE: optional
+ TransportProtocol protocol = 2;
+}
+message WireguardConstraints {
+ // NOTE: optional
+ uint32 port = 1;
+}
+
+message CustomRelaySettings {
+ string host = 1;
+ ConnectionConfig config = 2;
+}
+
+message ConnectionConfig {
+ message OpenvpnConfig {
+ string address = 1;
+ TransportProtocol protocol = 2;
+ string username = 3;
+ string password = 4;
+ }
+ message WireguardConfig {
+ message TunnelConfig {
+ bytes private_key = 1;
+ repeated string addresses = 2;
+ }
+ message PeerConfig {
+ bytes public_key = 1;
+ repeated string allowed_ips = 2;
+ string endpoint = 3;
+ }
+
+ TunnelConfig tunnel = 1;
+ PeerConfig peer = 2;
+ string ipv4_gateway = 3;
+ // NOTE: optional
+ string ipv6_gateway = 4;
+ }
+
+ oneof config {
+ OpenvpnConfig openvpn = 1;
+ WireguardConfig wireguard = 2;
+ }
+}
+
+message TunnelOptions {
+ message OpenvpnOptions {
+ // NOTE: optional
+ uint32 mssfix = 1;
+ }
+ message WireguardOptions {
+ // NOTE: optional
+ uint32 mtu = 1;
+ // NOTE: optional
+ uint32 automatic_rotation = 2;
+ }
+ message GenericOptions {
+ bool enable_ipv6 = 1;
+ }
+
+ OpenvpnOptions openvpn = 1;
+ WireguardOptions wireguard = 2;
+ GenericOptions generic = 3;
+}
+
+message PublicKey {
+ bytes key = 1;
+ google.protobuf.Timestamp created = 2;
+}
+
+message KeygenEvent {
+ enum KeygenEvent {
+ NEW_KEY = 0;
+ TOO_MANY_KEYS = 1;
+ GENERATION_FAILURE = 2;
+ }
+ KeygenEvent event = 1;
+ PublicKey new_key = 2;
+}
+
+message AppVersionInfo {
+ bool supported = 1;
+ string latest_stable = 2;
+ string latest_beta = 3;
+ string suggested_upgrade = 4;
+}
+
+message RelayListCountry {
+ string name = 1;
+ string code = 2;
+ repeated RelayListCity cities = 3;
+}
+
+message RelayListCity {
+ string name = 1;
+ string code = 2;
+ double latitude = 3;
+ double longitude = 4;
+ repeated Relay relays = 5;
+}
+
+message Relay {
+ string hostname = 1;
+ string ipv4_addr_in = 2;
+ string ipv6_addr_in = 3;
+ bool include_in_country = 4;
+ bool active = 5;
+ bool owned = 6;
+ string provider = 7;
+ fixed64 weight = 8;
+ RelayTunnels tunnels = 9;
+ RelayBridges bridges = 10;
+ Location location = 11;
+}
+
+message Location {
+ string country = 1;
+ string country_code = 2;
+ string city = 3;
+ string city_code = 4;
+ double latitude = 5;
+ double longitude = 6;
+}
+
+message RelayTunnels {
+ repeated OpenVpnEndpointData openvpn = 1;
+ repeated WireguardEndpointData wireguard = 2;
+}
+
+message RelayBridges {
+ repeated ShadowsocksEndpointData shadowsocks = 1;
+}
+
+enum TransportProtocol {
+ ANY_PROTOCOL = 0;
+ UDP = 1;
+ TCP = 2;
+}
+
+message ShadowsocksEndpointData {
+ uint32 port = 1;
+ string cipher = 2;
+ string password = 3;
+ TransportProtocol protocol = 4;
+}
+
+message OpenVpnEndpointData {
+ uint32 port = 1;
+ TransportProtocol protocol = 2;
+}
+
+message WireguardEndpointData {
+ repeated PortRange port_ranges = 1;
+ string ipv4_gateway = 2;
+ string ipv6_gateway = 3;
+ bytes public_key = 4;
+}
+
+message PortRange {
+ uint32 first = 1;
+ uint32 last = 2;
+}
+
+message DaemonEvent {
+ oneof event {
+ TunnelState tunnel_state = 1;
+ Settings settings = 2;
+ RelayList relay_list = 3;
+ AppVersionInfo version_info = 4;
+ KeygenEvent key_event = 5;
+ }
+}
+
+message RelayList {
+ repeated RelayListCountry countries = 1;
+}
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index 4dc7dcfb47..ef0cff448e 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -17,6 +17,8 @@ pub mod rpc_uniqueness_check;
mod settings;
pub mod version;
mod version_check;
+#[cfg(target_os = "windows")]
+mod windows_permissions;
use futures::future::{abortable, AbortHandle};
use futures01::{
diff --git a/mullvad-daemon/src/logging.rs b/mullvad-daemon/src/logging.rs
index 88fd387c5d..58e10cd3b7 100644
--- a/mullvad-daemon/src/logging.rs
+++ b/mullvad-daemon/src/logging.rs
@@ -26,15 +26,12 @@ pub enum Error {
pub const SILENCED_CRATES: &[&str] = &[
"h2",
- "jsonrpc_core",
- // jsonrpc_core does some logging under the "rpc" target as well.
- "rpc",
"tokio_core",
"tokio_io",
"tokio_proto",
"tokio_reactor",
"tokio_threadpool",
- "jsonrpc_ws_server",
+ "tokio_util",
"want",
"ws",
"mio",
diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs
index 0b02dfbe5e..adb7c39f0a 100644
--- a/mullvad-daemon/src/main.rs
+++ b/mullvad-daemon/src/main.rs
@@ -86,8 +86,15 @@ fn run_platform(_config: &cli::Config, log_dir: Option<PathBuf>) -> Result<(), S
}
fn run_standalone(log_dir: Option<PathBuf>) -> Result<(), String> {
- if rpc_uniqueness_check::is_another_instance_running() {
- return Err("Another instance of the daemon is already running".to_owned());
+ {
+ let mut runtime = tokio02::runtime::Builder::new()
+ .basic_scheduler()
+ .enable_all()
+ .build()
+ .map_err(|e| e.display_chain())?;
+ if runtime.block_on(rpc_uniqueness_check::is_another_instance_running()) {
+ return Err("Another instance of the daemon is already running".to_owned());
+ }
}
if !running_as_admin() {
diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs
index d593f0aa9d..5619c8efd6 100644
--- a/mullvad-daemon/src/management_interface.rs
+++ b/mullvad-daemon/src/management_interface.rs
@@ -1,892 +1,1745 @@
-use crate::{BoxFuture, DaemonCommand, DaemonCommandSender, EventListener};
-use jsonrpc_core::{
- futures::{future, sync, Future},
- Error, ErrorCode, MetaIoHandler, Metadata,
+use crate::{
+ wireguard::DEFAULT_AUTOMATIC_KEY_ROTATION, DaemonCommand, DaemonCommandSender, EventListener,
};
-use jsonrpc_ipc_server;
-use jsonrpc_macros::{build_rpc_trait, metadata, pubsub};
-use jsonrpc_pubsub::{PubSubHandler, PubSubMetadata, Session, SubscriptionId};
+use futures::compat::Future01CompatExt;
+use futures01::{future, sync, Future};
use mullvad_paths;
use mullvad_rpc::{rest::Error as RestError, StatusCode};
use mullvad_types::{
- account::{AccountData, AccountToken, VoucherSubmission},
+ account::AccountToken,
location::GeoIpLocation,
- relay_constraints::{BridgeSettings, BridgeState, RelaySettingsUpdate},
- relay_list::RelayList,
- settings::Settings,
+ relay_constraints::{
+ BridgeConstraints, BridgeSettings, BridgeState, Constraint, LocationConstraint,
+ OpenVpnConstraints, RelayConstraintsUpdate, RelaySettings, RelaySettingsUpdate,
+ WireguardConstraints,
+ },
+ relay_list::{Relay, RelayList, RelayListCountry},
+ settings::{Settings, TunnelOptions},
states::{TargetState, TunnelState},
- version, wireguard, DaemonEvent,
+ version, wireguard, ConnectionConfig,
};
use parking_lot::RwLock;
use std::{
- collections::{hash_map::Entry, HashMap},
- sync::Arc,
+ cmp, io,
+ sync::{mpsc, Arc},
+};
+use talpid_types::{
+ net::{TransportProtocol, TunnelType},
+ ErrorExt,
};
-use talpid_ipc;
-use talpid_types::ErrorExt;
-use uuid;
-
-pub const INVALID_VOUCHER_CODE: i64 = -400;
-pub const VOUCHER_USED_ALREADY_CODE: i64 = -401;
-pub const INVALID_ACCOUNT_CODE: i64 = -200;
-
-
-build_rpc_trait! {
- pub trait ManagementInterfaceApi {
- type Metadata;
-
- /// Creates and sets a new account
- #[rpc(meta, name = "create_new_account")]
- fn create_new_account(&self, Self::Metadata) -> BoxFuture<String, Error>;
-
- /// Fetches and returns metadata about an account. Returns an error on non-existing
- /// accounts.
- #[rpc(meta, name = "get_account_data")]
- fn get_account_data(&self, Self::Metadata, AccountToken) -> BoxFuture<AccountData, Error>;
-
- #[rpc(meta, name = "get_www_auth_token")]
- fn get_www_auth_token(&self, Self::Metadata) -> BoxFuture<String, Error>;
-
- /// Submit voucher to add time to account
- #[rpc(meta, name = "submit_voucher")]
- fn submit_voucher(&self, Self::Metadata, String) -> BoxFuture<VoucherSubmission, Error>;
-
- /// Returns available countries.
- #[rpc(meta, name = "get_relay_locations")]
- fn get_relay_locations(&self, Self::Metadata) -> BoxFuture<RelayList, Error>;
-
- /// Triggers a relay list update
- #[rpc(meta, name = "update_relay_locations")]
- fn update_relay_locations(&self, Self::Metadata) -> BoxFuture<(), Error>;
-
- /// Set which account to connect with.
- #[rpc(meta, name = "set_account")]
- fn set_account(&self, Self::Metadata, Option<AccountToken>) -> BoxFuture<(), Error>;
-
- /// Update constraints put on the type of tunnel connection to use
- #[rpc(meta, name = "update_relay_settings")]
- fn update_relay_settings(
- &self,
- Self::Metadata, RelaySettingsUpdate
- ) -> BoxFuture<(), Error>;
-
- /// Set if the client should allow communication with the LAN while in secured state.
- #[rpc(meta, name = "set_allow_lan")]
- fn set_allow_lan(&self, Self::Metadata, bool) -> BoxFuture<(), Error>;
-
- /// Set whether to enable the beta program.
- #[rpc(meta, name = "set_show_beta_releases")]
- fn set_show_beta_releases(&self, Self::Metadata, bool) -> BoxFuture<(), Error>;
-
- /// Set if the client should allow network communication when in the disconnected state.
- #[rpc(meta, name = "set_block_when_disconnected")]
- fn set_block_when_disconnected(&self, Self::Metadata, bool) -> BoxFuture<(), Error>;
-
- /// Set if the daemon should automatically establish a tunnel on start or not.
- #[rpc(meta, name = "set_auto_connect")]
- fn set_auto_connect(&self, Self::Metadata, bool) -> BoxFuture<(), Error>;
-
- /// Try to connect if disconnected, or do nothing if already connecting/connected.
- #[rpc(meta, name = "connect")]
- fn connect(&self, Self::Metadata) -> BoxFuture<(), Error>;
-
- /// Disconnect the VPN tunnel if it is connecting/connected. Does nothing if already
- /// disconnected.
- #[rpc(meta, name = "disconnect")]
- fn disconnect(&self, Self::Metadata) -> BoxFuture<(), Error>;
-
- /// Reconnect if connecting/connected, or do nothing if disconnected.
- #[rpc(meta, name = "reconnect")]
- fn reconnect(&self, Self::Metadata) -> BoxFuture<(), Error>;
-
- /// Returns the current state of the Mullvad client. Changes to this state will
- /// be announced to subscribers of `new_state`.
- #[rpc(meta, name = "get_state")]
- fn get_state(&self, Self::Metadata) -> BoxFuture<TunnelState, Error>;
-
- /// Performs a geoIP lookup and returns the current location as perceived by the public
- /// internet.
- #[rpc(meta, name = "get_current_location")]
- fn get_current_location(&self, Self::Metadata) -> BoxFuture<Option<GeoIpLocation>, Error>;
-
- /// Makes the daemon exit its main loop and quit.
- #[rpc(meta, name = "shutdown")]
- fn shutdown(&self, Self::Metadata) -> BoxFuture<(), Error>;
-
- /// Saves the target tunnel state and enters a blocking state. The state is restored
- /// upon restart.
- #[rpc(meta, name = "prepare_restart")]
- fn prepare_restart(&self, Self::Metadata) -> BoxFuture<(), Error>;
-
- /// Get previously used account tokens from the account history
- #[rpc(meta, name = "get_account_history")]
- fn get_account_history(&self, Self::Metadata) -> BoxFuture<Vec<AccountToken>, Error>;
-
- /// Remove given account token from the account history
- #[rpc(meta, name = "remove_account_from_history")]
- fn remove_account_from_history(&self, Self::Metadata, AccountToken) -> BoxFuture<(), Error>;
-
- /// Removes all accounts from history, removing any associated keys in the process
- #[rpc(meta, name = "clear_account_history")]
- fn clear_account_history(&self, Self::Metadata) -> BoxFuture<(), Error>;
-
- /// Sets openvpn's mssfix parameter
- #[rpc(meta, name = "set_openvpn_mssfix")]
- fn set_openvpn_mssfix(&self, Self::Metadata, Option<u16>) -> BoxFuture<(), Error>;
-
- /// Sets proxy details for OpenVPN
- #[rpc(meta, name = "set_bridge_settings")]
- fn set_bridge_settings(&self, Self::Metadata, BridgeSettings) -> BoxFuture<(), Error>;
-
- /// Sets bridge state
- #[rpc(meta, name = "set_bridge_state")]
- fn set_bridge_state(&self, Self::Metadata, BridgeState) -> BoxFuture<(), Error>;
-
- /// Set if IPv6 is enabled in the tunnel
- #[rpc(meta, name = "set_enable_ipv6")]
- fn set_enable_ipv6(&self, Self::Metadata, bool) -> BoxFuture<(), Error>;
-
- /// Set MTU for wireguard tunnels
- #[rpc(meta, name = "set_wireguard_mtu")]
- fn set_wireguard_mtu(&self, Self::Metadata, Option<u16>) -> BoxFuture<(), Error>;
-
- /// Set automatic key rotation interval for wireguard tunnels
- #[rpc(meta, name = "set_wireguard_rotation_interval")]
- fn set_wireguard_rotation_interval(&self, Self::Metadata, Option<u32>) -> BoxFuture<(), Error>;
- /// Returns the current daemon settings
- #[rpc(meta, name = "get_settings")]
- fn get_settings(&self, Self::Metadata) -> BoxFuture<Settings, Error>;
- /// Generates new wireguard key for current account
- #[rpc(meta, name = "generate_wireguard_key")]
- fn generate_wireguard_key(&self, Self::Metadata) -> BoxFuture<wireguard::KeygenEvent, Error>;
+mod proto {
+ tonic::include_proto!("mullvad_daemon.management_interface");
+}
- /// Retrieve a public key for current account if the account has one.
- #[rpc(meta, name = "get_wireguard_key")]
- fn get_wireguard_key(&self, Self::Metadata) -> BoxFuture<Option<wireguard::PublicKey>, Error>;
+use proto::{
+ daemon_event::Event as DaemonEventType,
+ management_service_server::{ManagementService, ManagementServiceServer},
+};
- /// Verify if current wireguard key is still valid
- #[rpc(meta, name = "verify_wireguard_key")]
- fn verify_wireguard_key(&self, Self::Metadata) -> BoxFuture<bool, Error>;
+use tonic::{
+ self,
+ transport::{server::Connected, Server},
+ Request, Response,
+};
- /// Retreive version of the app
- #[rpc(meta, name = "get_current_version")]
- fn get_current_version(&self, Self::Metadata) -> BoxFuture<String, Error>;
+#[derive(err_derive::Error, Debug)]
+#[error(no_from)]
+pub enum Error {
+ // Unable to start the management interface server
+ #[error(display = "Unable to start management interface server")]
+ SetupError(tonic::transport::Error),
- /// Retrieve information about the currently running and latest versions of the app
- #[rpc(meta, name = "get_version_info")]
- fn get_version_info(&self, Self::Metadata) -> BoxFuture<version::AppVersionInfo, Error>;
+ // Unable to set the permissions on the named pipe
+ #[error(display = "Unable to set permissions for IPC endpoint")]
+ PermissionsError(#[error(source)] io::Error),
- /// Remove all configuration and cache files
- #[rpc(meta, name = "factory_reset")]
- fn factory_reset(&self, Self::Metadata) -> BoxFuture<(), Error>;
+ // Unable to start the tokio runtime
+ #[error(display = "Failed to create the tokio runtime")]
+ TokioRuntimeError(#[error(source)] tokio02::io::Error),
+}
- /// Retrieve PIDs to exclude from the tunnel
- #[rpc(meta, name = "get_split_tunnel_processes")]
- fn get_split_tunnel_processes(&self, Self::Metadata) -> BoxFuture<Vec<i32>, Error>;
+struct ManagementServiceImpl {
+ daemon_tx: DaemonCommandSender,
+ subscriptions: Arc<RwLock<Vec<EventsListenerSender>>>,
+}
- /// Add a process to exclude from the tunnel
- #[rpc(meta, name = "add_split_tunnel_process")]
- fn add_split_tunnel_process(&self, Self::Metadata, i32) -> BoxFuture<(), Error>;
+pub type ServiceResult<T> = std::result::Result<Response<T>, tonic::Status>;
+type EventsListenerReceiver =
+ tokio02::sync::mpsc::UnboundedReceiver<Result<proto::DaemonEvent, tonic::Status>>;
+type EventsListenerSender =
+ tokio02::sync::mpsc::UnboundedSender<Result<proto::DaemonEvent, tonic::Status>>;
- /// Remove a process excluded from the tunnel
- #[rpc(meta, name = "remove_split_tunnel_process")]
- fn remove_split_tunnel_process(&self, Self::Metadata, i32) -> BoxFuture<(), Error>;
+#[tonic::async_trait]
+impl ManagementService for ManagementServiceImpl {
+ type GetRelayLocationsStream =
+ tokio02::sync::mpsc::Receiver<Result<proto::RelayListCountry, tonic::Status>>;
+ type GetSplitTunnelProcessesStream =
+ tokio02::sync::mpsc::UnboundedReceiver<Result<i32, tonic::Status>>;
+ type EventsListenStream = EventsListenerReceiver;
- /// Clear list of processes to exclude from the tunnel
- #[rpc(meta, name = "clear_split_tunnel_processes")]
- fn clear_split_tunnel_processes(&self, Self::Metadata) -> BoxFuture<(), Error>;
+ // Control and get the tunnel state
+ //
- #[pubsub(name = "daemon_event")] {
- /// Subscribes to events from the daemon.
- #[rpc(name = "daemon_event_subscribe")]
- fn daemon_event_subscribe(
- &self,
- Self::Metadata,
- pubsub::Subscriber<DaemonEvent>
- );
+ async fn connect_tunnel(&self, _: Request<()>) -> ServiceResult<()> {
+ log::debug!("connect_tunnel");
- /// Unsubscribes from the `daemon_event` event notifications.
- #[rpc(name = "daemon_event_unsubscribe")]
- fn daemon_event_unsubscribe(&self, SubscriptionId) -> BoxFuture<(), Error>;
- }
+ let (tx, rx) = sync::oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::SetTargetState(tx, TargetState::Secured))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .and_then(|result| match result {
+ Ok(()) => Ok(Response::new(())),
+ Err(()) => Err(tonic::Status::new(
+ tonic::Code::from(-900),
+ "No account token configured",
+ )),
+ })
+ .compat()
+ .await
}
-}
-
-pub struct ManagementInterfaceServer {
- server: talpid_ipc::IpcServer,
- subscriptions: Arc<RwLock<HashMap<SubscriptionId, pubsub::Sink<DaemonEvent>>>>,
-}
-impl ManagementInterfaceServer {
- pub fn start(tunnel_tx: DaemonCommandSender) -> Result<Self, talpid_ipc::Error> {
- let rpc = ManagementInterface::new(tunnel_tx);
- let subscriptions = rpc.subscriptions.clone();
+ async fn disconnect_tunnel(&self, _: Request<()>) -> ServiceResult<()> {
+ log::debug!("disconnect_tunnel");
- let mut io = PubSubHandler::default();
- io.extend_with(rpc.to_delegate());
- let meta_io: MetaIoHandler<Meta> = io.into();
- let path = mullvad_paths::get_rpc_socket_path();
- let server = talpid_ipc::IpcServer::start_with_metadata(
- meta_io,
- meta_extractor,
- &path.to_string_lossy(),
- )?;
- Ok(ManagementInterfaceServer {
- server,
- subscriptions,
- })
+ let (tx, _) = sync::oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::SetTargetState(tx, TargetState::Unsecured))
+ .then(|_| Ok(Response::new(())))
+ .compat()
+ .await
}
- pub fn socket_path(&self) -> &str {
- self.server.path()
+ async fn reconnect_tunnel(&self, _: Request<()>) -> ServiceResult<()> {
+ log::debug!("reconnect_tunnel");
+ self.send_command_to_daemon(DaemonCommand::Reconnect)
+ .map(Response::new)
+ .compat()
+ .await
}
- pub fn event_broadcaster(&self) -> ManagementInterfaceEventBroadcaster {
- ManagementInterfaceEventBroadcaster {
- subscriptions: self.subscriptions.clone(),
- close_handle: Some(self.server.close_handle()),
- }
+ async fn get_tunnel_state(&self, _: Request<()>) -> ServiceResult<proto::TunnelState> {
+ log::debug!("get_tunnel_state");
+ let (tx, rx) = sync::oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::GetState(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .and_then(|state| Ok(Response::new(convert_state(state))))
+ .compat()
+ .await
}
- /// Consumes the server and waits for it to finish. Returns an error if the server exited
- /// due to an error.
- pub fn wait(self) {
- self.server.wait()
- }
-}
+ // Control the daemon and receive events
+ //
-/// A handle that allows broadcasting messages to all subscribers of the management interface.
-#[derive(Clone)]
-pub struct ManagementInterfaceEventBroadcaster {
- subscriptions: Arc<RwLock<HashMap<SubscriptionId, pubsub::Sink<DaemonEvent>>>>,
- close_handle: Option<talpid_ipc::CloseHandle>,
-}
-
-impl EventListener for ManagementInterfaceEventBroadcaster {
- /// Sends a new state update to all `new_state` subscribers of the management interface.
- fn notify_new_state(&self, new_state: TunnelState) {
- self.notify(DaemonEvent::TunnelState(new_state));
- }
+ async fn events_listen(&self, _: Request<()>) -> ServiceResult<Self::EventsListenStream> {
+ let (tx, rx) = tokio02::sync::mpsc::unbounded_channel();
- /// Sends settings to all `settings` subscribers of the management interface.
- fn notify_settings(&self, settings: Settings) {
- log::debug!("Broadcasting new settings");
- self.notify(DaemonEvent::Settings(settings));
- }
+ let mut subscriptions = self.subscriptions.write();
+ subscriptions.push(tx);
- /// Sends settings to all `settings` subscribers of the management interface.
- fn notify_relay_list(&self, relay_list: RelayList) {
- log::debug!("Broadcasting new relay list");
- self.notify(DaemonEvent::RelayList(relay_list));
+ Ok(Response::new(rx))
}
- fn notify_app_version(&self, app_version_info: version::AppVersionInfo) {
- log::debug!("Broadcasting new app version info");
- self.notify(DaemonEvent::AppVersionInfo(app_version_info));
+ async fn prepare_restart(&self, _: Request<()>) -> ServiceResult<()> {
+ log::debug!("prepare_restart");
+ self.send_command_to_daemon(DaemonCommand::PrepareRestart)
+ .map(Response::new)
+ .compat()
+ .await
}
- fn notify_key_event(&self, key_event: mullvad_types::wireguard::KeygenEvent) {
- log::debug!("Broadcasting new wireguard key event");
- self.notify(DaemonEvent::WireguardKey(key_event));
+ async fn shutdown(&self, _: Request<()>) -> ServiceResult<()> {
+ log::debug!("shutdown");
+ self.send_command_to_daemon(DaemonCommand::Shutdown)
+ .map(Response::new)
+ .compat()
+ .await
}
-}
-impl ManagementInterfaceEventBroadcaster {
- fn notify(&self, value: DaemonEvent) {
- let subscriptions = self.subscriptions.read();
- for sink in subscriptions.values() {
- let _ = sink.notify(Ok(value.clone())).wait();
+ async fn factory_reset(&self, _: Request<()>) -> ServiceResult<()> {
+ #[cfg(not(target_os = "android"))]
+ {
+ log::debug!("factory_reset");
+ let (tx, rx) = sync::oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::FactoryReset(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
+ }
+ #[cfg(target_os = "android")]
+ {
+ Response::new(())
}
}
-}
-impl Drop for ManagementInterfaceEventBroadcaster {
- fn drop(&mut self) {
- if let Some(close_handle) = self.close_handle.take() {
- close_handle.close();
- }
+ async fn get_current_version(&self, _: Request<()>) -> ServiceResult<String> {
+ log::debug!("get_current_version");
+ let (tx, rx) = sync::oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::GetCurrentVersion(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
-}
-struct ManagementInterface {
- subscriptions: Arc<RwLock<HashMap<SubscriptionId, pubsub::Sink<DaemonEvent>>>>,
- tx: DaemonCommandSender,
-}
+ async fn get_version_info(&self, _: Request<()>) -> ServiceResult<proto::AppVersionInfo> {
+ log::debug!("get_version_info");
-impl ManagementInterface {
- pub fn new(tx: DaemonCommandSender) -> Self {
- ManagementInterface {
- subscriptions: Default::default(),
- tx,
- }
- }
+ let (tx, rx) = sync::oneshot::channel();
+ let app_version_info = self
+ .send_command_to_daemon(DaemonCommand::GetVersionInfo(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .compat()
+ .await?;
- /// Sends a command to the daemon and maps the error to an RPC error.
- fn send_command_to_daemon(
- &self,
- command: DaemonCommand,
- ) -> impl Future<Item = (), Error = Error> {
- future::result(self.tx.send(command)).map_err(|_| Error::internal_error())
+ Ok(Response::new(convert_version_info(&app_version_info)))
}
- /// Converts a REST API error for an account into a JSONRPC error for the JSONRPC client.
- fn map_rest_account_error(error: RestError) -> Error {
- match error {
- RestError::ApiError(status, message)
- if status == StatusCode::UNAUTHORIZED || status == StatusCode::FORBIDDEN =>
- {
- Error {
- code: ErrorCode::from(INVALID_ACCOUNT_CODE),
- message,
- data: None,
- }
- }
- _ => Error::internal_error(),
- }
- }
-}
+ // Relays and tunnel constraints
+ //
-impl ManagementInterfaceApi for ManagementInterface {
- type Metadata = Meta;
+ async fn update_relay_locations(&self, _: Request<()>) -> ServiceResult<()> {
+ log::debug!("update_relay_locations");
+ self.send_command_to_daemon(DaemonCommand::UpdateRelayLocations)
+ .compat()
+ .await
+ .map(Response::new)
+ }
- fn create_new_account(&self, _: Self::Metadata) -> BoxFuture<String, Error> {
+ async fn update_relay_settings(
+ &self,
+ request: Request<proto::RelaySettingsUpdate>,
+ ) -> ServiceResult<()> {
+ log::debug!("update_relay_settings");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::CreateNewAccount(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()))
- .and_then(|result| match result {
- Ok(account_token) => Ok(account_token),
- Err(_) => Err(Error::internal_error()),
- });
+ let constraints_update = convert_relay_settings_update(&request.into_inner())?;
- Box::new(future)
+ let message = DaemonCommand::UpdateRelaySettings(tx, constraints_update);
+ self.send_command_to_daemon(message)
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn get_account_data(
+ async fn get_relay_locations(
&self,
- _: Self::Metadata,
- account_token: AccountToken,
- ) -> BoxFuture<AccountData, Error> {
- log::debug!("get_account_data");
+ _: Request<()>,
+ ) -> ServiceResult<Self::GetRelayLocationsStream> {
+ log::debug!("get_relay_locations");
+
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::GetAccountData(tx, account_token))
- .and_then(|_| rx.map_err(|_| Error::internal_error()))
- .and_then(|rpc_future| {
- rpc_future.map_err(|error: RestError| {
+ let locations = self
+ .send_command_to_daemon(DaemonCommand::GetRelayLocations(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .compat()
+ .await?;
+
+ let (mut stream_tx, stream_rx) =
+ tokio02::sync::mpsc::channel(cmp::max(1, locations.countries.len()));
+
+ tokio02::spawn(async move {
+ for country in &locations.countries {
+ if let Err(error) = stream_tx
+ .send(Ok(convert_relay_list_country(country)))
+ .await
+ {
log::error!(
- "Unable to get account data from API: {}",
+ "Error while sending relays to client: {}",
error.display_chain()
);
- Self::map_rest_account_error(error)
- })
- });
- Box::new(future)
+ }
+ }
+ });
+
+ Ok(Response::new(stream_rx))
}
- fn get_www_auth_token(&self, _: Self::Metadata) -> BoxFuture<String, Error> {
- log::debug!("get_account_data");
+ async fn get_current_location(&self, _: Request<()>) -> ServiceResult<proto::GeoIpLocation> {
+ log::debug!("get_current_location");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::GetWwwAuthToken(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()))
- .and_then(|rpc_future| {
- rpc_future.map_err(|error: mullvad_rpc::rest::Error| {
- log::error!(
- "Unable to get account data from API: {}",
- error.display_chain()
- );
- Self::map_rest_account_error(error)
- })
- });
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::GetCurrentLocation(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .and_then(|geoip| {
+ if let Some(geoip) = geoip {
+ Ok(Response::new(convert_geoip_location(geoip)))
+ } else {
+ Err(tonic::Status::not_found("no location was found"))
+ }
+ })
+ .compat()
+ .await
}
- fn submit_voucher(
+ async fn set_bridge_settings(
&self,
- _: Self::Metadata,
- voucher: String,
- ) -> BoxFuture<VoucherSubmission, Error> {
- log::debug!("submit_voucher");
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SubmitVoucher(tx, voucher))
- .and_then(|_| rx.map_err(|_| Error::internal_error()))
- .and_then(|f| {
- f.map_err(|e| match e {
- RestError::ApiError(StatusCode::BAD_REQUEST, message) => {
- match &message.as_str() {
- &mullvad_rpc::INVALID_VOUCHER => Error {
- code: ErrorCode::from(INVALID_VOUCHER_CODE),
- message,
- data: None,
- },
+ request: Request<proto::BridgeSettings>,
+ ) -> ServiceResult<()> {
+ use proto::bridge_settings::Type as BridgeSettingType;
+ use talpid_types::net;
- &mullvad_rpc::VOUCHER_USED => Error {
- code: ErrorCode::from(VOUCHER_USED_ALREADY_CODE),
- message,
- data: None,
- },
+ let settings = request
+ .into_inner()
+ .r#type
+ .ok_or(tonic::Status::invalid_argument("no settings provided"))?;
- _ => Error::internal_error(),
- }
- }
- _ => Error::internal_error(),
- })
- });
- Box::new(future)
- }
+ let settings =
+ match settings {
+ BridgeSettingType::Normal(constraints) => {
+ let constraint = match constraints.location {
+ None => Constraint::Any,
+ Some(location) => convert_proto_location(location),
+ };
+
+ BridgeSettings::Normal(BridgeConstraints {
+ location: constraint,
+ })
+ }
+ BridgeSettingType::Local(proxy_settings) => {
+ let peer = proxy_settings.peer.parse().map_err(|_| {
+ tonic::Status::invalid_argument("failed to parse peer address")
+ })?;
+ let proxy_settings =
+ net::openvpn::ProxySettings::Local(net::openvpn::LocalProxySettings {
+ port: proxy_settings.port as u16,
+ peer,
+ });
+ BridgeSettings::Custom(proxy_settings)
+ }
+ BridgeSettingType::Remote(proxy_settings) => {
+ let address = proxy_settings.address.parse().map_err(|_| {
+ tonic::Status::invalid_argument("failed to parse IP address")
+ })?;
+ let auth = proxy_settings.auth.map(|auth| net::openvpn::ProxyAuth {
+ username: auth.username,
+ password: auth.password,
+ });
+ let proxy_settings =
+ net::openvpn::ProxySettings::Remote(net::openvpn::RemoteProxySettings {
+ address,
+ auth,
+ });
+ BridgeSettings::Custom(proxy_settings)
+ }
+ BridgeSettingType::Shadowsocks(proxy_settings) => {
+ let peer = proxy_settings.peer.parse().map_err(|_| {
+ tonic::Status::invalid_argument("failed to parse peer address")
+ })?;
+ let proxy_settings = net::openvpn::ProxySettings::Shadowsocks(
+ net::openvpn::ShadowsocksProxySettings {
+ peer,
+ password: proxy_settings.password,
+ cipher: proxy_settings.cipher,
+ },
+ );
+ BridgeSettings::Custom(proxy_settings)
+ }
+ };
+
+ log::debug!("set_bridge_settings({:?})", settings);
- fn get_relay_locations(&self, _: Self::Metadata) -> BoxFuture<RelayList, Error> {
- log::debug!("get_relay_locations");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::GetRelayLocations(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::SetBridgeSettings(tx, settings))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .and_then(|settings_result| {
+ settings_result.map_err(|_| tonic::Status::internal("internal error"))
+ })
+ .map(Response::new)
+ .compat()
+ .await
}
- fn update_relay_locations(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
- log::debug!("update_relay_locations");
- Box::new(self.send_command_to_daemon(DaemonCommand::UpdateRelayLocations))
- }
+ async fn set_bridge_state(&self, request: Request<proto::BridgeState>) -> ServiceResult<()> {
+ use proto::bridge_state::State;
- fn set_account(
- &self,
- _: Self::Metadata,
- account_token: Option<AccountToken>,
- ) -> BoxFuture<(), Error> {
- log::debug!("set_account");
+ let bridge_state = match State::from_i32(request.into_inner().state) {
+ Some(State::Auto) => BridgeState::Auto,
+ Some(State::On) => BridgeState::On,
+ Some(State::Off) => BridgeState::Off,
+ None => return Err(tonic::Status::invalid_argument("unknown bridge state")),
+ };
+
+ log::debug!("set_bridge_state({:?})", bridge_state);
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetAccount(tx, account_token))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::SetBridgeState(tx, bridge_state))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .and_then(|settings_result| {
+ settings_result.map_err(|_| tonic::Status::internal("internal error"))
+ })
+ .map(Response::new)
+ .compat()
+ .await
}
- fn update_relay_settings(
- &self,
- _: Self::Metadata,
- constraints_update: RelaySettingsUpdate,
- ) -> BoxFuture<(), Error> {
- log::debug!("update_relay_settings");
- let (tx, rx) = sync::oneshot::channel();
+ // Settings
+ //
- let message = DaemonCommand::UpdateRelaySettings(tx, constraints_update);
- let future = self
- .send_command_to_daemon(message)
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ async fn get_settings(&self, _: Request<()>) -> ServiceResult<proto::Settings> {
+ log::debug!("get_settings");
+ let (tx, rx) = sync::oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::GetSettings(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(|settings| Response::new(convert_settings(&settings)))
+ .compat()
+ .await
}
- fn set_allow_lan(&self, _: Self::Metadata, allow_lan: bool) -> BoxFuture<(), Error> {
+ async fn set_allow_lan(&self, request: Request<bool>) -> ServiceResult<()> {
+ let allow_lan = request.into_inner();
log::debug!("set_allow_lan({})", allow_lan);
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetAllowLan(tx, allow_lan))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::SetAllowLan(tx, allow_lan))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn set_show_beta_releases(&self, _: Self::Metadata, enabled: bool) -> BoxFuture<(), Error> {
+ async fn set_show_beta_releases(&self, request: Request<bool>) -> ServiceResult<()> {
+ let enabled = request.into_inner();
log::debug!("set_show_beta_releases({})", enabled);
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetShowBetaReleases(tx, enabled))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::SetShowBetaReleases(tx, enabled))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn set_block_when_disconnected(
- &self,
- _: Self::Metadata,
- block_when_disconnected: bool,
- ) -> BoxFuture<(), Error> {
+ async fn set_block_when_disconnected(&self, request: Request<bool>) -> ServiceResult<()> {
+ let block_when_disconnected = request.into_inner();
log::debug!("set_block_when_disconnected({})", block_when_disconnected);
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetBlockWhenDisconnected(
- tx,
- block_when_disconnected,
- ))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::SetBlockWhenDisconnected(
+ tx,
+ block_when_disconnected,
+ ))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn set_auto_connect(&self, _: Self::Metadata, auto_connect: bool) -> BoxFuture<(), Error> {
+ async fn set_auto_connect(&self, request: Request<bool>) -> ServiceResult<()> {
+ let auto_connect = request.into_inner();
log::debug!("set_auto_connect({})", auto_connect);
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetAutoConnect(tx, auto_connect))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::SetAutoConnect(tx, auto_connect))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn connect(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
- log::debug!("connect");
+ async fn set_openvpn_mssfix(&self, request: Request<u32>) -> ServiceResult<()> {
+ let mssfix = request.into_inner();
+ let mssfix = if mssfix != 0 {
+ Some(mssfix as u16)
+ } else {
+ None
+ };
+ log::debug!("set_openvpn_mssfix({:?})", mssfix);
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetTargetState(tx, TargetState::Secured))
- .and_then(|_| rx.map_err(|_| Error::internal_error()))
- .and_then(|result| match result {
- Ok(()) => future::ok(()),
- Err(()) => future::err(Error {
- code: ErrorCode::ServerError(-900),
- message: "No account token configured".to_owned(),
- data: None,
- }),
- });
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::SetOpenVpnMssfix(tx, mssfix))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn disconnect(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
- log::debug!("disconnect");
- let (tx, _) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetTargetState(tx, TargetState::Unsecured))
- .then(|_| future::ok(()));
- Box::new(future)
+ async fn set_wireguard_mtu(&self, request: Request<u32>) -> ServiceResult<()> {
+ let mtu = request.into_inner();
+ let mtu = if mtu != 0 { Some(mtu as u16) } else { None };
+ log::debug!("set_wireguard_mtu({:?})", mtu);
+ let (tx, rx) = sync::oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::SetWireguardMtu(tx, mtu))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn reconnect(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
- log::debug!("reconnect");
- let future = self.send_command_to_daemon(DaemonCommand::Reconnect);
- Box::new(future)
+ async fn set_enable_ipv6(&self, request: Request<bool>) -> ServiceResult<()> {
+ let enable_ipv6 = request.into_inner();
+ log::debug!("set_enable_ipv6({})", enable_ipv6);
+ let (tx, rx) = sync::oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::SetEnableIpv6(tx, enable_ipv6))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn get_state(&self, _: Self::Metadata) -> BoxFuture<TunnelState, Error> {
- log::debug!("get_state");
- let (state_tx, state_rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::GetState(state_tx))
- .and_then(|_| state_rx.map_err(|_| Error::internal_error()));
- Box::new(future)
- }
+ // Account management
+ //
- fn get_current_location(&self, _: Self::Metadata) -> BoxFuture<Option<GeoIpLocation>, Error> {
- log::debug!("get_current_location");
+ async fn create_new_account(&self, _: Request<()>) -> ServiceResult<String> {
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::GetCurrentLocation(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::CreateNewAccount(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .and_then(|result| match result {
+ Ok(account_token) => Ok(Response::new(account_token)),
+ Err(_) => Err(tonic::Status::internal("internal error")),
+ })
+ .compat()
+ .await
}
- fn shutdown(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
- log::debug!("shutdown");
- Box::new(self.send_command_to_daemon(DaemonCommand::Shutdown))
+ async fn set_account(&self, request: Request<AccountToken>) -> ServiceResult<()> {
+ log::debug!("set_account");
+ let account_token = request.into_inner();
+ let account_token = if account_token == "" {
+ None
+ } else {
+ Some(account_token)
+ };
+ let (tx, rx) = sync::oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::SetAccount(tx, account_token))
+ .and_then(|_| {
+ rx.map(Response::new)
+ .map_err(|_| tonic::Status::internal("internal error"))
+ })
+ .compat()
+ .await
}
- fn prepare_restart(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
- log::debug!("prepare_restart");
- Box::new(self.send_command_to_daemon(DaemonCommand::PrepareRestart))
+ async fn get_account_data(
+ &self,
+ request: Request<AccountToken>,
+ ) -> ServiceResult<proto::AccountData> {
+ log::debug!("get_account_data");
+ let account_token = request.into_inner();
+ let (tx, rx) = sync::oneshot::channel();
+ self.send_command_to_daemon(DaemonCommand::GetAccountData(tx, account_token))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .and_then(|rpc_future| {
+ rpc_future
+ .map(|account_data| {
+ Response::new(proto::AccountData {
+ expiry: Some(prost_types::Timestamp {
+ seconds: account_data.expiry.timestamp(),
+ nanos: 0,
+ }),
+ })
+ })
+ .map_err(|error: RestError| {
+ log::error!(
+ "Unable to get account data from API: {}",
+ error.display_chain()
+ );
+ map_rest_account_error(error)
+ })
+ })
+ .compat()
+ .await
}
- fn get_account_history(&self, _: Self::Metadata) -> BoxFuture<Vec<AccountToken>, Error> {
+ async fn get_account_history(&self, _: Request<()>) -> ServiceResult<proto::AccountHistory> {
+ // TODO: this might be a stream
log::debug!("get_account_history");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::GetAccountHistory(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::GetAccountHistory(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(|history| Response::new(proto::AccountHistory { token: history }))
+ .compat()
+ .await
}
- fn remove_account_from_history(
+ async fn remove_account_from_history(
&self,
- _: Self::Metadata,
- account_token: AccountToken,
- ) -> BoxFuture<(), Error> {
+ request: Request<AccountToken>,
+ ) -> ServiceResult<()> {
log::debug!("remove_account_from_history");
+ let account_token = request.into_inner();
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::RemoveAccountFromHistory(tx, account_token))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::RemoveAccountFromHistory(tx, account_token))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn clear_account_history(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
+ async fn clear_account_history(&self, _: Request<()>) -> ServiceResult<()> {
log::debug!("clear_account_history");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::ClearAccountHistory(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::ClearAccountHistory(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn set_openvpn_mssfix(&self, _: Self::Metadata, mssfix: Option<u16>) -> BoxFuture<(), Error> {
- log::debug!("set_openvpn_mssfix({:?})", mssfix);
+ async fn get_www_auth_token(&self, _: Request<()>) -> ServiceResult<String> {
+ log::debug!("get_www_auth_token");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetOpenVpnMssfix(tx, mssfix))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
-
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::GetWwwAuthToken(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .and_then(|rpc_future| {
+ rpc_future
+ .map(Response::new)
+ .map_err(|error: mullvad_rpc::rest::Error| {
+ log::error!(
+ "Unable to get account data from API: {}",
+ error.display_chain()
+ );
+ map_rest_account_error(error)
+ })
+ })
+ .compat()
+ .await
}
- fn set_bridge_settings(
+ async fn submit_voucher(
&self,
- _: Self::Metadata,
- bridge_settings: BridgeSettings,
- ) -> BoxFuture<(), Error> {
- log::debug!("set_bridge_settings({:?})", bridge_settings);
+ request: Request<String>,
+ ) -> ServiceResult<proto::VoucherSubmission> {
+ log::debug!("submit_voucher");
+ let voucher = request.into_inner();
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetBridgeSettings(tx, bridge_settings))
- .and_then(|_| rx.map_err(|_| Error::internal_error()))
- .and_then(|settings_result| settings_result.map_err(|_| Error::internal_error()));
-
- Box::new(future)
- }
+ self.send_command_to_daemon(DaemonCommand::SubmitVoucher(tx, voucher))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .and_then(|f| {
+ f.map(|submission| {
+ Response::new(proto::VoucherSubmission {
+ seconds_added: submission.time_added,
+ new_expiry: Some(prost_types::Timestamp {
+ seconds: submission.new_expiry.timestamp(),
+ nanos: 0,
+ }),
+ })
+ })
+ .map_err(|e| match e {
+ RestError::ApiError(StatusCode::BAD_REQUEST, message) => {
+ match &message.as_str() {
+ &mullvad_rpc::INVALID_VOUCHER => {
+ tonic::Status::new(tonic::Code::NotFound, message)
+ }
- fn set_bridge_state(
- &self,
- _: Self::Metadata,
- bridge_state: BridgeState,
- ) -> BoxFuture<(), Error> {
- log::debug!("set_bridge_state({:?})", bridge_state);
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetBridgeState(tx, bridge_state))
- .and_then(|_| rx.map_err(|_| Error::internal_error()))
- .and_then(|settings_result| settings_result.map_err(|_| Error::internal_error()));
+ &mullvad_rpc::VOUCHER_USED => {
+ tonic::Status::new(tonic::Code::ResourceExhausted, message)
+ }
- Box::new(future)
+ _ => tonic::Status::internal("internal error"),
+ }
+ }
+ _ => tonic::Status::internal("internal error"),
+ })
+ })
+ .compat()
+ .await
}
- fn set_enable_ipv6(&self, _: Self::Metadata, enable_ipv6: bool) -> BoxFuture<(), Error> {
- log::debug!("set_enable_ipv6({})", enable_ipv6);
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetEnableIpv6(tx, enable_ipv6))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
-
- Box::new(future)
- }
+ // WireGuard key management
+ //
- /// Set MTU for wireguard tunnels
- fn set_wireguard_mtu(&self, _: Self::Metadata, mtu: Option<u16>) -> BoxFuture<(), Error> {
- log::debug!("set_wireguard_mtu({:?})", mtu);
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetWireguardMtu(tx, mtu))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
- }
+ async fn set_wireguard_rotation_interval(&self, request: Request<u32>) -> ServiceResult<()> {
+ let interval = request.into_inner();
- /// Set automatic key rotation interval for wireguard tunnels
- fn set_wireguard_rotation_interval(
- &self,
- _: Self::Metadata,
- interval: Option<u32>,
- ) -> BoxFuture<(), Error> {
log::debug!("set_wireguard_rotation_interval({:?})", interval);
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::SetWireguardRotationInterval(tx, interval))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::SetWireguardRotationInterval(
+ tx,
+ Some(interval),
+ ))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn get_settings(&self, _: Self::Metadata) -> BoxFuture<Settings, Error> {
- log::debug!("get_settings");
+ async fn reset_wireguard_rotation_interval(&self, _: Request<()>) -> ServiceResult<()> {
+ log::debug!("reset_wireguard_rotation_interval");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::GetSettings(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::SetWireguardRotationInterval(tx, None))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn generate_wireguard_key(
- &self,
- _: Self::Metadata,
- ) -> BoxFuture<mullvad_types::wireguard::KeygenEvent, Error> {
+ async fn generate_wireguard_key(&self, _: Request<()>) -> ServiceResult<proto::KeygenEvent> {
+ // TODO: return error for TooManyKeys, GenerationFailure
+ // on success, simply return the new key or nil
log::debug!("generate_wireguard_key");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::GenerateWireguardKey(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::GenerateWireguardKey(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(|event| Response::new(convert_wireguard_key_event(&event)))
+ .compat()
+ .await
}
- fn get_wireguard_key(
- &self,
- _: Self::Metadata,
- ) -> BoxFuture<Option<wireguard::PublicKey>, Error> {
+ async fn get_wireguard_key(&self, _: Request<()>) -> ServiceResult<proto::PublicKey> {
log::debug!("get_wireguard_key");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::GetWireguardKey(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::GetWireguardKey(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .then(|response| match response {
+ Ok(Some(key)) => Ok(Response::new(convert_public_key(&key))),
+ Ok(None) => Err(tonic::Status::not_found("no WireGuard key was found")),
+ Err(e) => Err(e),
+ })
+ .compat()
+ .await
}
- fn verify_wireguard_key(&self, _: Self::Metadata) -> BoxFuture<bool, Error> {
+ async fn verify_wireguard_key(&self, _: Request<()>) -> ServiceResult<bool> {
log::debug!("verify_wireguard_key");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::VerifyWireguardKey(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
- }
-
- fn get_current_version(&self, _: Self::Metadata) -> BoxFuture<String, Error> {
- log::debug!("get_current_version");
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::GetCurrentVersion(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
-
- Box::new(future)
- }
-
- fn get_version_info(&self, _: Self::Metadata) -> BoxFuture<version::AppVersionInfo, Error> {
- log::debug!("get_version_info");
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::GetVersionInfo(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
-
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::VerifyWireguardKey(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
- fn factory_reset(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
- #[cfg(not(target_os = "android"))]
- {
- log::debug!("factory_reset");
- let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::FactoryReset(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
-
- Box::new(future)
- }
- #[cfg(target_os = "android")]
- {
- Box::new(future::ok(()))
- }
- }
+ // Split tunneling
+ //
- fn get_split_tunnel_processes(&self, _: Self::Metadata) -> BoxFuture<Vec<i32>, Error> {
+ async fn get_split_tunnel_processes(
+ &self,
+ _: Request<()>,
+ ) -> ServiceResult<Self::GetSplitTunnelProcessesStream> {
#[cfg(target_os = "linux")]
{
log::debug!("get_split_tunnel_processes");
let (tx, rx) = sync::oneshot::channel();
- let future = self
+ let pids = self
.send_command_to_daemon(DaemonCommand::GetSplitTunnelProcesses(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .compat()
+ .await?;
+
+ let (tx, rx) = tokio02::sync::mpsc::unbounded_channel();
+ tokio02::spawn(async move {
+ for pid in pids {
+ let _ = tx.send(Ok(pid));
+ }
+ });
+
+ Ok(Response::new(rx))
}
#[cfg(not(target_os = "linux"))]
{
- Box::new(future::ok(Vec::with_capacity(0)))
+ let (_, rx) = tokio02::sync::mpsc::unbounded_channel();
+ Ok(Response::new(rx))
}
}
#[cfg(target_os = "linux")]
- fn add_split_tunnel_process(&self, _: Self::Metadata, pid: i32) -> BoxFuture<(), Error> {
+ async fn add_split_tunnel_process(&self, request: Request<i32>) -> ServiceResult<()> {
+ let pid = request.into_inner();
log::debug!("add_split_tunnel_process");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::AddSplitTunnelProcess(tx, pid))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::AddSplitTunnelProcess(tx, pid))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
#[cfg(not(target_os = "linux"))]
- fn add_split_tunnel_process(&self, _: Self::Metadata, _: i32) -> BoxFuture<(), Error> {
- Box::new(future::ok(()))
+ async fn add_split_tunnel_process(&self, _: Request<i32>) -> ServiceResult<()> {
+ Ok(Response::new(()))
}
#[cfg(target_os = "linux")]
- fn remove_split_tunnel_process(&self, _: Self::Metadata, pid: i32) -> BoxFuture<(), Error> {
+ async fn remove_split_tunnel_process(&self, request: Request<i32>) -> ServiceResult<()> {
+ let pid = request.into_inner();
log::debug!("remove_split_tunnel_process");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::RemoveSplitTunnelProcess(tx, pid))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::RemoveSplitTunnelProcess(tx, pid))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
#[cfg(not(target_os = "linux"))]
- fn remove_split_tunnel_process(&self, _: Self::Metadata, _: i32) -> BoxFuture<(), Error> {
- Box::new(future::ok(()))
+ async fn remove_split_tunnel_process(&self, _: Request<i32>) -> ServiceResult<()> {
+ Ok(Response::new(()))
}
- fn clear_split_tunnel_processes(&self, _: Self::Metadata) -> BoxFuture<(), Error> {
+ async fn clear_split_tunnel_processes(&self, _: Request<()>) -> ServiceResult<()> {
#[cfg(target_os = "linux")]
{
log::debug!("clear_split_tunnel_processes");
let (tx, rx) = sync::oneshot::channel();
- let future = self
- .send_command_to_daemon(DaemonCommand::ClearSplitTunnelProcesses(tx))
- .and_then(|_| rx.map_err(|_| Error::internal_error()));
- Box::new(future)
+ self.send_command_to_daemon(DaemonCommand::ClearSplitTunnelProcesses(tx))
+ .and_then(|_| rx.map_err(|_| tonic::Status::internal("internal error")))
+ .map(Response::new)
+ .compat()
+ .await
}
#[cfg(not(target_os = "linux"))]
{
- Box::new(future::ok(()))
+ Ok(Response::new(()))
}
}
+}
-
- fn daemon_event_subscribe(
+impl ManagementServiceImpl {
+ /// Sends a command to the daemon and maps the error to an RPC error.
+ fn send_command_to_daemon(
&self,
- _: Self::Metadata,
- subscriber: pubsub::Subscriber<DaemonEvent>,
- ) {
- log::debug!("daemon_event_subscribe");
- let mut subscriptions = self.subscriptions.write();
- loop {
- let id = SubscriptionId::String(uuid::Uuid::new_v4().to_string());
- if let Entry::Vacant(entry) = subscriptions.entry(id.clone()) {
- if let Ok(sink) = subscriber.assign_id(id.clone()) {
- log::debug!("Accepting new subscription with id {:?}", id);
- entry.insert(sink);
+ command: DaemonCommand,
+ ) -> impl Future<Item = (), Error = tonic::Status> {
+ future::result(
+ self.daemon_tx
+ .send(command)
+ .map_err(|_| tonic::Status::internal("internal error")),
+ )
+ }
+}
+
+fn convert_settings(settings: &Settings) -> proto::Settings {
+ proto::Settings {
+ account_token: settings.get_account_token().unwrap_or_default(),
+ relay_settings: Some(convert_relay_settings(&settings.get_relay_settings())),
+ bridge_settings: Some(convert_bridge_settings(&settings.bridge_settings)),
+ bridge_state: Some(convert_bridge_state(settings.get_bridge_state())),
+ allow_lan: settings.allow_lan,
+ block_when_disconnected: settings.block_when_disconnected,
+ auto_connect: settings.auto_connect,
+ tunnel_options: Some(convert_tunnel_options(&settings.tunnel_options)),
+ show_beta_releases: settings.show_beta_releases,
+ }
+}
+
+fn convert_relay_settings_update(
+ settings: &proto::RelaySettingsUpdate,
+) -> Result<RelaySettingsUpdate, tonic::Status> {
+ use mullvad_types::CustomTunnelEndpoint;
+ use proto::{
+ connection_config::Config as ProtoConnectionConfig,
+ relay_settings_update::Type as ProtoUpdateType,
+ };
+ use talpid_types::net::{self, openvpn, wireguard};
+
+ let update_value = settings
+ .r#type
+ .clone()
+ .ok_or(tonic::Status::invalid_argument("missing relay settings"))?;
+
+ match update_value {
+ ProtoUpdateType::Custom(settings) => {
+ let config = settings
+ .config
+ .ok_or(tonic::Status::invalid_argument("missing relay settings"))?;
+ let config = config
+ .config
+ .ok_or(tonic::Status::invalid_argument("missing relay settings"))?;
+ let config = match config {
+ ProtoConnectionConfig::Openvpn(config) => {
+ let address = match config.address.parse() {
+ Ok(address) => address,
+ Err(_) => return Err(tonic::Status::invalid_argument("invalid address")),
+ };
+
+ ConnectionConfig::OpenVpn(openvpn::ConnectionConfig {
+ endpoint: net::Endpoint {
+ address,
+ protocol: match proto::TransportProtocol::from_i32(config.protocol) {
+ Some(proto::TransportProtocol::Udp) => TransportProtocol::Udp,
+ Some(proto::TransportProtocol::Tcp) => TransportProtocol::Tcp,
+ None | Some(proto::TransportProtocol::AnyProtocol) => {
+ return Err(tonic::Status::invalid_argument(
+ "unknown transport protocol",
+ ))
+ }
+ },
+ },
+ username: config.username.clone(),
+ password: config.password.clone(),
+ })
}
- break;
+ ProtoConnectionConfig::Wireguard(config) => {
+ let tunnel = config
+ .tunnel
+ .ok_or(tonic::Status::invalid_argument("missing tunnel config"))?;
+
+ // Copy the private key to an array
+ if tunnel.private_key.len() != 32 {
+ return Err(tonic::Status::invalid_argument("invalid private key"));
+ }
+
+ let mut private_key = [0; 32];
+ let buffer = &tunnel.private_key[..private_key.len()];
+ private_key.copy_from_slice(buffer);
+
+ let peer = config
+ .peer
+ .ok_or(tonic::Status::invalid_argument("missing peer config"))?;
+
+ // Copy the public key to an array
+ if peer.public_key.len() != 32 {
+ return Err(tonic::Status::invalid_argument("invalid public key"));
+ }
+
+ let mut public_key = [0; 32];
+ let buffer = &peer.public_key[..public_key.len()];
+ public_key.copy_from_slice(buffer);
+
+ let ipv4_gateway = match config.ipv4_gateway.parse() {
+ Ok(address) => address,
+ Err(_) => {
+ return Err(tonic::Status::invalid_argument("invalid IPv4 gateway"))
+ }
+ };
+ let ipv6_gateway = if !config.ipv6_gateway.is_empty() {
+ let address = match config.ipv6_gateway.parse() {
+ Ok(address) => address,
+ Err(_) => {
+ return Err(tonic::Status::invalid_argument("invalid IPv6 gateway"))
+ }
+ };
+ Some(address)
+ } else {
+ None
+ };
+
+ let endpoint = match peer.endpoint.parse() {
+ Ok(address) => address,
+ Err(_) => {
+ return Err(tonic::Status::invalid_argument("invalid peer address"))
+ }
+ };
+
+ let mut tunnel_addresses = Vec::new();
+ for address in tunnel.addresses {
+ let address = address
+ .parse()
+ .map_err(|_| tonic::Status::invalid_argument("invalid address"))?;
+ tunnel_addresses.push(address);
+ }
+
+ let mut allowed_ips = Vec::new();
+ for address in peer.allowed_ips {
+ let address = address
+ .parse()
+ .map_err(|_| tonic::Status::invalid_argument("invalid address"))?;
+ allowed_ips.push(address);
+ }
+
+ ConnectionConfig::Wireguard(wireguard::ConnectionConfig {
+ tunnel: wireguard::TunnelConfig {
+ private_key: wireguard::PrivateKey::from(private_key),
+ addresses: tunnel_addresses,
+ },
+ peer: wireguard::PeerConfig {
+ public_key: wireguard::PublicKey::from(public_key),
+ allowed_ips,
+ endpoint,
+ },
+ ipv4_gateway,
+ ipv6_gateway,
+ })
+ }
+ };
+
+ Ok(RelaySettingsUpdate::CustomTunnelEndpoint(
+ CustomTunnelEndpoint {
+ host: settings.host.clone(),
+ config,
+ },
+ ))
+ }
+
+ ProtoUpdateType::Normal(settings) => {
+ // If `location` isn't provided, no changes are made.
+ // If `location` is provided, but is an empty vector,
+ // then the constraint is set to `Constraint::Any`.
+ let location = settings.location.map(convert_proto_location);
+
+ let tunnel_protocol = if let Some(update) = settings.tunnel_type {
+ match proto::TunnelType::from_i32(update.tunnel_type) {
+ Some(proto::TunnelType::AnyTunnel) => Some(Constraint::Any),
+ Some(proto::TunnelType::Openvpn) => Some(Constraint::Only(TunnelType::OpenVpn)),
+ Some(proto::TunnelType::Wireguard) => {
+ Some(Constraint::Only(TunnelType::Wireguard))
+ }
+ None => return Err(tonic::Status::invalid_argument("unknown tunnel protocol")),
+ }
+ } else {
+ None
+ };
+
+ Ok(RelaySettingsUpdate::Normal(RelayConstraintsUpdate {
+ location,
+ tunnel_protocol,
+ wireguard_constraints: settings.wireguard_constraints.map(|constraints| {
+ WireguardConstraints {
+ port: if constraints.port != 0 {
+ Constraint::Only(constraints.port as u16)
+ } else {
+ Constraint::Any
+ },
+ }
+ }),
+ openvpn_constraints: settings.openvpn_constraints.map(|constraints| {
+ OpenVpnConstraints {
+ port: if constraints.port != 0 {
+ Constraint::Only(constraints.port as u16)
+ } else {
+ Constraint::Any
+ },
+ protocol: match proto::TransportProtocol::from_i32(constraints.protocol) {
+ Some(proto::TransportProtocol::Udp) => {
+ Constraint::Only(TransportProtocol::Udp)
+ }
+ Some(proto::TransportProtocol::Tcp) => {
+ Constraint::Only(TransportProtocol::Tcp)
+ }
+ _ => Constraint::Any,
+ },
+ }
+ }),
+ }))
+ }
+ }
+}
+
+fn convert_relay_settings(settings: &RelaySettings) -> proto::RelaySettings {
+ use proto::relay_settings;
+
+ let endpoint = match settings {
+ RelaySettings::CustomTunnelEndpoint(endpoint) => {
+ relay_settings::Endpoint::Custom(proto::CustomRelaySettings {
+ host: endpoint.host.clone(),
+ config: Some(convert_connection_config(&endpoint.config)),
+ })
+ }
+ RelaySettings::Normal(constraints) => {
+ relay_settings::Endpoint::Normal(proto::NormalRelaySettings {
+ location: convert_location_constraint(&constraints.location),
+ tunnel_type: match constraints.tunnel_protocol {
+ Constraint::Any => i32::from(proto::TunnelType::AnyTunnel),
+ Constraint::Only(TunnelType::Wireguard) => {
+ i32::from(proto::TunnelType::Wireguard)
+ }
+ Constraint::Only(TunnelType::OpenVpn) => i32::from(proto::TunnelType::Openvpn),
+ },
+
+ wireguard_constraints: Some(proto::WireguardConstraints {
+ port: u32::from(constraints.wireguard_constraints.port.unwrap_or(0)),
+ }),
+
+ openvpn_constraints: Some(proto::OpenvpnConstraints {
+ port: u32::from(constraints.openvpn_constraints.port.unwrap_or(0)),
+ protocol: i32::from(
+ constraints
+ .openvpn_constraints
+ .protocol
+ .map(|protocol| match protocol {
+ TransportProtocol::Tcp => proto::TransportProtocol::Tcp,
+ TransportProtocol::Udp => proto::TransportProtocol::Udp,
+ })
+ .unwrap_or(proto::TransportProtocol::AnyProtocol),
+ ),
+ }),
+ })
+ }
+ };
+
+ proto::RelaySettings {
+ endpoint: Some(endpoint),
+ }
+}
+
+fn convert_connection_config(config: &ConnectionConfig) -> proto::ConnectionConfig {
+ use proto::connection_config;
+
+ proto::ConnectionConfig {
+ config: Some(match config {
+ ConnectionConfig::OpenVpn(config) => {
+ connection_config::Config::Openvpn(connection_config::OpenvpnConfig {
+ address: config.endpoint.address.to_string(),
+ protocol: match config.endpoint.protocol {
+ TransportProtocol::Tcp => i32::from(proto::TransportProtocol::Tcp),
+ TransportProtocol::Udp => i32::from(proto::TransportProtocol::Udp),
+ },
+ username: config.username.clone(),
+ password: config.password.clone(),
+ })
+ }
+ ConnectionConfig::Wireguard(config) => {
+ connection_config::Config::Wireguard(connection_config::WireguardConfig {
+ tunnel: Some(connection_config::wireguard_config::TunnelConfig {
+ private_key: config.tunnel.private_key.to_bytes().to_vec(),
+ addresses: config
+ .tunnel
+ .addresses
+ .iter()
+ .map(|address| address.to_string())
+ .collect(),
+ }),
+ peer: Some(connection_config::wireguard_config::PeerConfig {
+ public_key: config.peer.public_key.as_bytes().to_vec(),
+ allowed_ips: config
+ .peer
+ .allowed_ips
+ .iter()
+ .map(|address| address.to_string())
+ .collect(),
+ endpoint: config.peer.endpoint.to_string(),
+ }),
+ ipv4_gateway: config.ipv4_gateway.to_string(),
+ ipv6_gateway: config
+ .ipv6_gateway
+ .as_ref()
+ .map(|address| address.to_string())
+ .unwrap_or_default(),
+ })
}
+ }),
+ }
+}
+
+fn convert_bridge_settings(settings: &BridgeSettings) -> proto::BridgeSettings {
+ use proto::bridge_settings::{self, Type as BridgeSettingType};
+ use talpid_types::net;
+
+ let settings = match settings {
+ BridgeSettings::Normal(constraints) => {
+ BridgeSettingType::Normal(proto::bridge_settings::BridgeConstraints {
+ location: convert_location_constraint(&constraints.location),
+ })
}
+ BridgeSettings::Custom(proxy_settings) => match proxy_settings {
+ net::openvpn::ProxySettings::Local(proxy_settings) => {
+ BridgeSettingType::Local(bridge_settings::LocalProxySettings {
+ port: u32::from(proxy_settings.port),
+ peer: proxy_settings.peer.to_string(),
+ })
+ }
+ net::openvpn::ProxySettings::Remote(proxy_settings) => {
+ BridgeSettingType::Remote(bridge_settings::RemoteProxySettings {
+ address: proxy_settings.address.to_string(),
+ auth: proxy_settings.auth.as_ref().map(|auth| {
+ bridge_settings::RemoteProxyAuth {
+ username: auth.username.clone(),
+ password: auth.password.clone(),
+ }
+ }),
+ })
+ }
+ net::openvpn::ProxySettings::Shadowsocks(proxy_settings) => {
+ BridgeSettingType::Shadowsocks(bridge_settings::ShadowsocksProxySettings {
+ peer: proxy_settings.peer.to_string(),
+ password: proxy_settings.password.clone(),
+ cipher: proxy_settings.cipher.clone(),
+ })
+ }
+ },
+ };
+
+ proto::BridgeSettings {
+ r#type: Some(settings),
}
+}
- fn daemon_event_unsubscribe(&self, id: SubscriptionId) -> BoxFuture<(), Error> {
- log::debug!("daemon_event_unsubscribe");
- let was_removed = self.subscriptions.write().remove(&id).is_some();
- let result = if was_removed {
- log::debug!("Unsubscribing id {:?}", id);
- future::ok(())
+fn convert_wireguard_key_event(
+ event: &mullvad_types::wireguard::KeygenEvent,
+) -> proto::KeygenEvent {
+ use mullvad_types::wireguard::KeygenEvent::*;
+ use proto::keygen_event::KeygenEvent as ProtoEvent;
+
+ proto::KeygenEvent {
+ event: match event {
+ NewKey(_) => i32::from(ProtoEvent::NewKey),
+ TooManyKeys => i32::from(ProtoEvent::TooManyKeys),
+ GenerationFailure => i32::from(ProtoEvent::GenerationFailure),
+ },
+ new_key: if let NewKey(key) = event {
+ Some(convert_public_key(&key))
} else {
- future::err(Error {
- code: ErrorCode::InvalidParams,
- message: "Invalid subscription".to_owned(),
- data: None,
- })
+ None
+ },
+ }
+}
+
+fn convert_public_key(public_key: &wireguard::PublicKey) -> proto::PublicKey {
+ proto::PublicKey {
+ key: public_key.key.as_bytes().to_vec(),
+ created: Some(prost_types::Timestamp {
+ seconds: public_key.created.timestamp(),
+ nanos: 0,
+ }),
+ }
+}
+
+fn convert_location_constraint(
+ location: &Constraint<LocationConstraint>,
+) -> Option<proto::RelayLocation> {
+ if location.is_any() {
+ return None;
+ }
+
+ Some(match location.as_ref().unwrap() {
+ LocationConstraint::Country(country) => proto::RelayLocation {
+ country: country.to_string(),
+ ..Default::default()
+ },
+ LocationConstraint::City(country, city) => proto::RelayLocation {
+ country: country.to_string(),
+ city: city.to_string(),
+ ..Default::default()
+ },
+ LocationConstraint::Hostname(country, city, hostname) => proto::RelayLocation {
+ country: country.to_string(),
+ city: city.to_string(),
+ hostname: hostname.to_string(),
+ },
+ })
+}
+
+fn convert_bridge_state(state: &BridgeState) -> proto::BridgeState {
+ let state = match state {
+ BridgeState::Auto => proto::bridge_state::State::Auto,
+ BridgeState::On => proto::bridge_state::State::On,
+ BridgeState::Off => proto::bridge_state::State::Off,
+ };
+ proto::BridgeState {
+ state: i32::from(state),
+ }
+}
+
+fn convert_tunnel_options(options: &TunnelOptions) -> proto::TunnelOptions {
+ proto::TunnelOptions {
+ openvpn: Some(proto::tunnel_options::OpenvpnOptions {
+ mssfix: u32::from(options.openvpn.mssfix.unwrap_or_default()),
+ }),
+ wireguard: Some(proto::tunnel_options::WireguardOptions {
+ mtu: u32::from(options.wireguard.mtu.unwrap_or_default()),
+ automatic_rotation: options
+ .wireguard
+ .automatic_rotation
+ .unwrap_or((DEFAULT_AUTOMATIC_KEY_ROTATION.as_secs() / 60u64 / 60u64) as u32),
+ }),
+ generic: Some(proto::tunnel_options::GenericOptions {
+ enable_ipv6: options.generic.enable_ipv6,
+ }),
+ }
+}
+
+fn convert_relay_list_country(country: &RelayListCountry) -> proto::RelayListCountry {
+ let mut proto_country = proto::RelayListCountry {
+ name: country.name.clone(),
+ code: country.code.clone(),
+ cities: Vec::with_capacity(country.cities.len()),
+ };
+
+ for city in &country.cities {
+ proto_country.cities.push(proto::RelayListCity {
+ name: city.name.clone(),
+ code: city.code.clone(),
+ latitude: city.latitude,
+ longitude: city.longitude,
+ relays: city
+ .relays
+ .iter()
+ .map(|relay| convert_relay(relay))
+ .collect(),
+ });
+ }
+
+ proto_country
+}
+
+fn convert_relay(relay: &Relay) -> proto::Relay {
+ proto::Relay {
+ hostname: relay.hostname.clone(),
+ ipv4_addr_in: relay.ipv4_addr_in.to_string(),
+ ipv6_addr_in: relay
+ .ipv6_addr_in
+ .map(|addr| addr.to_string())
+ .unwrap_or_default(),
+ include_in_country: relay.include_in_country,
+ active: relay.active,
+ owned: relay.owned,
+ provider: relay.provider.clone(),
+ weight: relay.weight,
+ tunnels: Some(proto::RelayTunnels {
+ openvpn: relay
+ .tunnels
+ .openvpn
+ .iter()
+ .map(|endpoint| {
+ let protocol = match endpoint.protocol {
+ TransportProtocol::Udp => proto::TransportProtocol::Udp,
+ TransportProtocol::Tcp => proto::TransportProtocol::Tcp,
+ };
+ proto::OpenVpnEndpointData {
+ port: u32::from(endpoint.port),
+ protocol: i32::from(protocol),
+ }
+ })
+ .collect(),
+ wireguard: relay
+ .tunnels
+ .wireguard
+ .iter()
+ .map(|endpoint| {
+ let port_ranges = endpoint
+ .port_ranges
+ .iter()
+ .map(|range| proto::PortRange {
+ first: u32::from(range.0),
+ last: u32::from(range.1),
+ })
+ .collect();
+ proto::WireguardEndpointData {
+ port_ranges,
+ ipv4_gateway: endpoint.ipv4_gateway.to_string(),
+ ipv6_gateway: endpoint.ipv6_gateway.to_string(),
+ public_key: endpoint.public_key.as_bytes().to_vec(),
+ }
+ })
+ .collect(),
+ }),
+ bridges: Some(proto::RelayBridges {
+ shadowsocks: relay
+ .bridges
+ .shadowsocks
+ .iter()
+ .map(|endpoint| {
+ let protocol = match endpoint.protocol {
+ TransportProtocol::Udp => proto::TransportProtocol::Udp,
+ TransportProtocol::Tcp => proto::TransportProtocol::Tcp,
+ };
+ proto::ShadowsocksEndpointData {
+ port: u32::from(endpoint.port),
+ cipher: endpoint.cipher.clone(),
+ password: endpoint.password.clone(),
+ protocol: i32::from(protocol),
+ }
+ })
+ .collect(),
+ }),
+ location: relay.location.as_ref().map(|location| proto::Location {
+ country: location.country.clone(),
+ country_code: location.country_code.clone(),
+ city: location.city.clone(),
+ city_code: location.city_code.clone(),
+ latitude: location.latitude,
+ longitude: location.longitude,
+ }),
+ }
+}
+
+fn convert_state(state: TunnelState) -> proto::TunnelState {
+ use proto::{
+ error_state::{
+ firewall_policy_error::ErrorType as PolicyErrorType, Cause as ProtoErrorCause,
+ FirewallPolicyError as ProtoFirewallPolicyError,
+ GenerationError as ProtoGenerationError,
+ },
+ tunnel_state::{self, State as ProtoState},
+ };
+ use talpid_types::tunnel::{
+ ActionAfterDisconnect, ErrorStateCause, FirewallPolicyError, ParameterGenerationError,
+ };
+ use TunnelState::*;
+
+ let state = match state {
+ Disconnected => ProtoState::Disconnected(tunnel_state::Disconnected {}),
+ Connecting { endpoint, location } => ProtoState::Connecting(tunnel_state::Connecting {
+ relay_info: Some(proto::TunnelStateRelayInfo {
+ tunnel_endpoint: Some(convert_endpoint(endpoint)),
+ location: location.map(convert_geoip_location),
+ }),
+ }),
+ Connected { endpoint, location } => ProtoState::Connected(tunnel_state::Connected {
+ relay_info: Some(proto::TunnelStateRelayInfo {
+ tunnel_endpoint: Some(convert_endpoint(endpoint)),
+ location: location.map(convert_geoip_location),
+ }),
+ }),
+ Disconnecting(after_disconnect) => ProtoState::Disconnecting(tunnel_state::Disconnecting {
+ after_disconnect: match after_disconnect {
+ ActionAfterDisconnect::Nothing => i32::from(proto::AfterDisconnect::Nothing),
+ ActionAfterDisconnect::Block => i32::from(proto::AfterDisconnect::Block),
+ ActionAfterDisconnect::Reconnect => i32::from(proto::AfterDisconnect::Reconnect),
+ },
+ }),
+ Error(error_state) => ProtoState::Error(tunnel_state::Error {
+ error_state: Some(proto::ErrorState {
+ cause: match error_state.cause() {
+ ErrorStateCause::AuthFailed(_) => i32::from(ProtoErrorCause::AuthFailed),
+ ErrorStateCause::Ipv6Unavailable => i32::from(ProtoErrorCause::Ipv6Unavailable),
+ ErrorStateCause::SetFirewallPolicyError(_) => {
+ i32::from(ProtoErrorCause::SetFirewallPolicyError)
+ }
+ ErrorStateCause::SetDnsError => i32::from(ProtoErrorCause::SetDnsError),
+ ErrorStateCause::StartTunnelError => {
+ i32::from(ProtoErrorCause::StartTunnelError)
+ }
+ ErrorStateCause::TunnelParameterError(_) => {
+ i32::from(ProtoErrorCause::TunnelParameterError)
+ }
+ ErrorStateCause::IsOffline => i32::from(ProtoErrorCause::IsOffline),
+ ErrorStateCause::TapAdapterProblem => {
+ i32::from(ProtoErrorCause::TapAdapterProblem)
+ }
+ #[cfg(target_os = "android")]
+ ErrorStateCause::VpnPermissionDenied => {
+ i32::from(ProtoErrorCause::VpnPermissionDenied)
+ }
+ },
+ is_blocking: error_state.is_blocking(),
+ auth_fail_reason: if let ErrorStateCause::AuthFailed(reason) = error_state.cause() {
+ reason.clone().unwrap_or_default()
+ } else {
+ "".to_string()
+ },
+ parameter_error: if let ErrorStateCause::TunnelParameterError(reason) =
+ error_state.cause()
+ {
+ match reason {
+ ParameterGenerationError::NoMatchingRelay => {
+ i32::from(ProtoGenerationError::NoMatchingRelay)
+ }
+ ParameterGenerationError::NoMatchingBridgeRelay => {
+ i32::from(ProtoGenerationError::NoMatchingBridgeRelay)
+ }
+ ParameterGenerationError::NoWireguardKey => {
+ i32::from(ProtoGenerationError::NoWireguardKey)
+ }
+ ParameterGenerationError::CustomTunnelHostResultionError => {
+ i32::from(ProtoGenerationError::CustomTunnelHostResolutionError)
+ }
+ }
+ } else {
+ 0
+ },
+ policy_error: if let ErrorStateCause::SetFirewallPolicyError(reason) =
+ error_state.cause()
+ {
+ match reason {
+ FirewallPolicyError::Generic => Some(ProtoFirewallPolicyError {
+ r#type: i32::from(PolicyErrorType::Generic),
+ ..Default::default()
+ }),
+ #[cfg(windows)]
+ FirewallPolicyError::Locked(blocking_app) => {
+ let (lock_pid, lock_name) = match blocking_app {
+ Some(app) => (app.pid, app.name.clone()),
+ None => (0, "".to_string()),
+ };
+
+ Some(ProtoFirewallPolicyError {
+ r#type: i32::from(PolicyErrorType::Locked),
+ lock_pid,
+ lock_name,
+ })
+ }
+ }
+ } else {
+ None
+ },
+ }),
+ }),
+ };
+
+ proto::TunnelState { state: Some(state) }
+}
+
+fn convert_endpoint(endpoint: talpid_types::net::TunnelEndpoint) -> proto::TunnelEndpoint {
+ use talpid_types::net;
+
+ proto::TunnelEndpoint {
+ address: endpoint.endpoint.address.to_string(),
+ protocol: match endpoint.endpoint.protocol {
+ TransportProtocol::Tcp => i32::from(proto::TransportProtocol::Tcp),
+ TransportProtocol::Udp => i32::from(proto::TransportProtocol::Udp),
+ },
+ tunnel_type: match endpoint.tunnel_type {
+ net::TunnelType::Wireguard => i32::from(proto::TunnelType::Wireguard),
+ net::TunnelType::OpenVpn => i32::from(proto::TunnelType::Openvpn),
+ },
+ proxy: endpoint.proxy.map(|proxy_ep| proto::ProxyEndpoint {
+ address: proxy_ep.endpoint.address.to_string(),
+ protocol: match proxy_ep.endpoint.protocol {
+ TransportProtocol::Tcp => i32::from(proto::TransportProtocol::Tcp),
+ TransportProtocol::Udp => i32::from(proto::TransportProtocol::Udp),
+ },
+ proxy_type: match proxy_ep.proxy_type {
+ net::proxy::ProxyType::Shadowsocks => i32::from(proto::ProxyType::Shadowsocks),
+ net::proxy::ProxyType::Custom => i32::from(proto::ProxyType::Custom),
+ },
+ }),
+ }
+}
+
+fn convert_geoip_location(geoip: GeoIpLocation) -> proto::GeoIpLocation {
+ proto::GeoIpLocation {
+ ipv4: geoip.ipv4.map(|ip| ip.to_string()).unwrap_or_default(),
+ ipv6: geoip.ipv6.map(|ip| ip.to_string()).unwrap_or_default(),
+ country: geoip.country,
+ city: geoip.city.unwrap_or_default(),
+ latitude: geoip.latitude,
+ longitude: geoip.longitude,
+ mullvad_exit_ip: geoip.mullvad_exit_ip,
+ hostname: geoip.hostname.unwrap_or_default(),
+ bridge_hostname: geoip.bridge_hostname.unwrap_or_default(),
+ }
+}
+
+fn convert_version_info(version_info: &version::AppVersionInfo) -> proto::AppVersionInfo {
+ proto::AppVersionInfo {
+ supported: version_info.supported,
+ latest_stable: version_info.latest_stable.clone(),
+ latest_beta: version_info.latest_beta.clone(),
+ suggested_upgrade: version_info.suggested_upgrade.clone().unwrap_or_default(),
+ }
+}
+
+fn convert_proto_location(location: proto::RelayLocation) -> Constraint<LocationConstraint> {
+ if !location.hostname.is_empty() {
+ Constraint::Only(LocationConstraint::Hostname(
+ location.country,
+ location.city,
+ location.hostname,
+ ))
+ } else if !location.city.is_empty() {
+ Constraint::Only(LocationConstraint::City(location.country, location.city))
+ } else if !location.country.is_empty() {
+ Constraint::Only(LocationConstraint::Country(location.country))
+ } else {
+ Constraint::Any
+ }
+}
+
+pub struct ManagementInterfaceServer {
+ subscriptions: Arc<RwLock<Vec<EventsListenerSender>>>,
+ socket_path: String,
+ runtime: tokio02::runtime::Runtime,
+ server_abort_tx: triggered::Trigger,
+ server_join_handle:
+ Option<tokio02::task::JoinHandle<std::result::Result<(), tonic::transport::Error>>>,
+}
+
+impl ManagementInterfaceServer {
+ async fn start_server(
+ socket_path: String,
+ daemon_tx: DaemonCommandSender,
+ server_start_tx: std::sync::mpsc::Sender<()>,
+ abort_rx: triggered::Listener,
+ subscriptions: Arc<RwLock<Vec<EventsListenerSender>>>,
+ ) -> std::result::Result<(), tonic::transport::Error> {
+ use futures::stream::TryStreamExt;
+ use parity_tokio_ipc::{Endpoint as IpcEndpoint, SecurityAttributes};
+
+ let mut endpoint = IpcEndpoint::new(socket_path);
+ endpoint.set_security_attributes(
+ SecurityAttributes::allow_everyone_create()
+ .unwrap()
+ .set_mode(777)
+ .unwrap(),
+ );
+ let incoming = endpoint.incoming().unwrap();
+ let _ = server_start_tx.send(());
+
+ let server = ManagementServiceImpl {
+ daemon_tx,
+ subscriptions,
};
- Box::new(result)
+
+ Server::builder()
+ .add_service(ManagementServiceServer::new(server))
+ .serve_with_incoming_shutdown(incoming.map_ok(StreamBox), abort_rx)
+ .await
+ }
+
+ pub fn start(tunnel_tx: DaemonCommandSender) -> Result<Self, Error> {
+ // TODO: don't spawn a tokio runtime here; make this function async
+ let mut runtime = tokio02::runtime::Builder::new()
+ .threaded_scheduler()
+ .core_threads(1)
+ .enable_all()
+ .build()
+ .map_err(Error::TokioRuntimeError)?;
+
+ let subscriptions = Arc::<RwLock<Vec<EventsListenerSender>>>::default();
+
+ let socket_path = mullvad_paths::get_rpc_socket_path()
+ .to_string_lossy()
+ .to_string();
+
+ let (server_abort_tx, server_abort_rx) = triggered::trigger();
+ let (start_tx, start_rx) = mpsc::channel();
+ let server_join_handle = runtime.spawn(Self::start_server(
+ socket_path.clone(),
+ tunnel_tx,
+ start_tx,
+ server_abort_rx,
+ subscriptions.clone(),
+ ));
+
+ if let Err(_) = start_rx.recv() {
+ return Err(runtime
+ .block_on(server_join_handle)
+ .expect("Failed to resolve quit handle future")
+ .map_err(Error::SetupError)
+ .unwrap_err());
+ }
+
+ #[cfg(unix)]
+ {
+ use std::{fs, os::unix::fs::PermissionsExt};
+ fs::set_permissions(&socket_path, PermissionsExt::from_mode(0o766))
+ .map_err(Error::PermissionsError)?;
+ }
+ #[cfg(windows)]
+ crate::windows_permissions::deny_network_access(&socket_path)
+ .map_err(Error::PermissionsError)?;
+
+ Ok(ManagementInterfaceServer {
+ subscriptions,
+ socket_path: socket_path.to_string(),
+ runtime,
+ server_abort_tx,
+ server_join_handle: Some(server_join_handle),
+ })
+ }
+
+ pub fn socket_path(&self) -> &str {
+ &self.socket_path
+ }
+
+ pub fn event_broadcaster(&self) -> ManagementInterfaceEventBroadcaster {
+ ManagementInterfaceEventBroadcaster {
+ runtime: self.runtime.handle().clone(),
+ subscriptions: self.subscriptions.clone(),
+ close_handle: self.server_abort_tx.clone(),
+ }
+ }
+
+ /// Consumes the server and waits for it to finish. Returns an error if the server exited
+ /// due to an error.
+ pub fn wait(mut self) {
+ if let Some(server_join_handle) = self.server_join_handle {
+ if let Err(error) = self.runtime.block_on(server_join_handle) {
+ log::error!("Management server panic: {:?}", error);
+ }
+ }
+ }
+}
+
+/// A handle that allows broadcasting messages to all subscribers of the management interface.
+#[derive(Clone)]
+pub struct ManagementInterfaceEventBroadcaster {
+ runtime: tokio02::runtime::Handle,
+ subscriptions: Arc<RwLock<Vec<EventsListenerSender>>>,
+ close_handle: triggered::Trigger,
+}
+
+impl EventListener for ManagementInterfaceEventBroadcaster {
+ /// Sends a new state update to all `new_state` subscribers of the management interface.
+ fn notify_new_state(&self, new_state: TunnelState) {
+ self.notify(proto::DaemonEvent {
+ event: Some(DaemonEventType::TunnelState(convert_state(new_state))),
+ })
+ }
+
+ /// Sends settings to all `settings` subscribers of the management interface.
+ fn notify_settings(&self, settings: Settings) {
+ log::debug!("Broadcasting new settings");
+ self.notify(proto::DaemonEvent {
+ event: Some(DaemonEventType::Settings(convert_settings(&settings))),
+ })
+ }
+
+ /// Sends relays to all subscribers of the management interface.
+ fn notify_relay_list(&self, relay_list: RelayList) {
+ log::debug!("Broadcasting new relay list");
+ let mut new_list = proto::RelayList {
+ countries: Vec::new(),
+ };
+ new_list.countries.reserve(relay_list.countries.len());
+ for country in &relay_list.countries {
+ new_list.countries.push(convert_relay_list_country(country));
+ }
+ self.notify(proto::DaemonEvent {
+ event: Some(DaemonEventType::RelayList(new_list)),
+ })
+ }
+
+ fn notify_app_version(&self, app_version_info: version::AppVersionInfo) {
+ log::debug!("Broadcasting new app version info");
+ let new_info = convert_version_info(&app_version_info);
+ self.notify(proto::DaemonEvent {
+ event: Some(DaemonEventType::VersionInfo(new_info)),
+ })
+ }
+
+ fn notify_key_event(&self, key_event: mullvad_types::wireguard::KeygenEvent) {
+ log::debug!("Broadcasting new wireguard key event");
+ let new_event = convert_wireguard_key_event(&key_event);
+ self.notify(proto::DaemonEvent {
+ event: Some(DaemonEventType::KeyEvent(new_event)),
+ })
}
}
+impl ManagementInterfaceEventBroadcaster {
+ fn notify(&self, value: proto::DaemonEvent) {
+ let mut subscriptions = self.subscriptions.write();
+ // TODO: using write-lock everywhere. use a mutex instead?
+ subscriptions.retain(|tx| tx.send(Ok(value.clone())).is_ok());
+ }
+}
-/// The metadata type. There is one instance associated with each connection. In this pubsub
-/// scenario they are created by `meta_extractor` by the server on each new incoming
-/// connection.
-#[derive(Clone, Debug, Default)]
-pub struct Meta {
- session: Option<Arc<Session>>,
+impl Drop for ManagementInterfaceEventBroadcaster {
+ fn drop(&mut self) {
+ self.close_handle.trigger();
+ }
}
-/// Make the `Meta` type possible to use as jsonrpc metadata type.
-impl Metadata for Meta {}
+// Converts a REST API error for an account into a tonic status.
+fn map_rest_account_error(error: RestError) -> tonic::Status {
+ match error {
+ RestError::ApiError(status, message)
+ if status == StatusCode::UNAUTHORIZED || status == StatusCode::FORBIDDEN =>
+ {
+ tonic::Status::new(tonic::Code::Unauthenticated, message)
+ }
+ _ => tonic::Status::internal("internal error"),
+ }
+}
-/// Make the `Meta` type possible to use as a pubsub metadata type.
-impl PubSubMetadata for Meta {
- fn session(&self) -> Option<Arc<Session>> {
- self.session.clone()
+
+// FIXME
+use std::{
+ pin::Pin,
+ task::{Context, Poll},
+};
+use tokio02::io::{AsyncRead, AsyncWrite};
+
+#[derive(Debug)]
+pub struct StreamBox<T: AsyncRead + AsyncWrite>(pub T);
+impl<T: AsyncRead + AsyncWrite> Connected for StreamBox<T> {}
+impl<T: AsyncRead + AsyncWrite + Unpin> AsyncRead for StreamBox<T> {
+ fn poll_read(
+ mut self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &mut [u8],
+ ) -> Poll<std::io::Result<usize>> {
+ Pin::new(&mut self.0).poll_read(cx, buf)
}
}
+impl<T: AsyncRead + AsyncWrite + Unpin> AsyncWrite for StreamBox<T> {
+ fn poll_write(
+ mut self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ buf: &[u8],
+ ) -> Poll<std::io::Result<usize>> {
+ Pin::new(&mut self.0).poll_write(cx, buf)
+ }
+
+ fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
+ Pin::new(&mut self.0).poll_flush(cx)
+ }
-/// Metadata extractor function for `Meta`.
-fn meta_extractor(context: &jsonrpc_ipc_server::RequestContext<'_>) -> Meta {
- Meta {
- session: Some(Arc::new(Session::new(context.sender.clone()))),
+ fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
+ Pin::new(&mut self.0).poll_shutdown(cx)
}
}
diff --git a/mullvad-daemon/src/rpc_uniqueness_check.rs b/mullvad-daemon/src/rpc_uniqueness_check.rs
index 7befdb4ba1..50a9a150a7 100644
--- a/mullvad-daemon/src/rpc_uniqueness_check.rs
+++ b/mullvad-daemon/src/rpc_uniqueness_check.rs
@@ -1,13 +1,37 @@
-use mullvad_ipc_client::new_standalone_ipc_client;
use mullvad_paths;
+use parity_tokio_ipc::Endpoint as IpcEndpoint;
use talpid_types::ErrorExt;
+use tonic::{
+ self,
+ transport::{self, Endpoint, Uri},
+};
+use tower::service_fn;
+
+mod proto {
+ tonic::include_proto!("mullvad_daemon.management_interface");
+}
+use proto::management_service_client::ManagementServiceClient;
+
+async fn new_grpc_client() -> Result<ManagementServiceClient<transport::Channel>, transport::Error>
+{
+ let ipc_path = mullvad_paths::get_rpc_socket_path();
+
+ // The URI will be ignored
+ let channel = Endpoint::from_static("lttp://[::]:50051")
+ .connect_with_connector(service_fn(move |_: Uri| {
+ IpcEndpoint::connect(ipc_path.clone())
+ }))
+ .await?;
+
+ Ok(ManagementServiceClient::new(channel))
+}
/// Checks if there is another instance of the daemon running.
///
/// Tries to connect to another daemon and perform a simple RPC call. If it fails, assumes the
/// other daemon has stopped.
-pub fn is_another_instance_running() -> bool {
- match new_standalone_ipc_client(&mullvad_paths::get_rpc_socket_path()) {
+pub async fn is_another_instance_running() -> bool {
+ match new_grpc_client().await {
Ok(_) => true,
Err(error) => {
let msg =
diff --git a/talpid-ipc/src/win.rs b/mullvad-daemon/src/windows_permissions.rs
index 0b39282348..0b39282348 100644
--- a/talpid-ipc/src/win.rs
+++ b/mullvad-daemon/src/windows_permissions.rs
diff --git a/mullvad-daemon/src/wireguard.rs b/mullvad-daemon/src/wireguard.rs
index 2ebd5fae50..58daf58ca5 100644
--- a/mullvad-daemon/src/wireguard.rs
+++ b/mullvad-daemon/src/wireguard.rs
@@ -22,7 +22,7 @@ use talpid_types::ErrorExt;
use tokio_timer;
/// Default automatic key rotation
-const DEFAULT_AUTOMATIC_KEY_ROTATION: Duration = Duration::from_secs(7 * 24 * 60 * 60);
+pub const DEFAULT_AUTOMATIC_KEY_ROTATION: Duration = Duration::from_secs(7 * 24 * 60 * 60);
/// How long to wait before reattempting to rotate keys on failure
const AUTOMATIC_ROTATION_RETRY_DELAY: Duration = Duration::from_secs(60 * 15);
/// How often to check whether the key has expired.
diff --git a/mullvad-ipc-client/Cargo.toml b/mullvad-ipc-client/Cargo.toml
deleted file mode 100644
index 1107c4f68a..0000000000
--- a/mullvad-ipc-client/Cargo.toml
+++ /dev/null
@@ -1,27 +0,0 @@
-[package]
-name = "mullvad-ipc-client"
-version = "0.1.0"
-authors = ["Mullvad VPN"]
-description = "RPC client for Mullvad daemon"
-license = "GPL-3.0"
-edition = "2018"
-publish = false
-
-[dependencies]
-err-derive = "0.2.1"
-mullvad-types = { path = "../mullvad-types" }
-serde = "1.0"
-talpid-ipc = { path = "../talpid-ipc" }
-talpid-types = { path = "../talpid-types" }
-mullvad-paths = { path = "../mullvad-paths" }
-jsonrpc-client-core = { git = "https://github.com/mullvad/jsonrpc-client-rs", rev = "68aac55b" }
-jsonrpc-client-ipc = { git = "https://github.com/mullvad/jsonrpc-client-rs", rev = "68aac55b" }
-jsonrpc-client-pubsub = { git = "https://github.com/mullvad/jsonrpc-client-rs", rev = "68aac55b" }
-tokio = "0.1.15"
-tokio-timer = "0.1"
-futures = "0.1"
-log = "0.4"
-
-
-[target.'cfg(windows)'.dependencies]
-winapi = { version = "0.3.5", features = ["accctrl", "aclapi", "securitybaseapi", "winbase", "winerror", "winnt"] }
diff --git a/mullvad-ipc-client/src/lib.rs b/mullvad-ipc-client/src/lib.rs
deleted file mode 100644
index f1bae521b8..0000000000
--- a/mullvad-ipc-client/src/lib.rs
+++ /dev/null
@@ -1,281 +0,0 @@
-#![deny(rust_2018_idioms)]
-
-use futures::sync::oneshot;
-use jsonrpc_client_core::{Client, ClientHandle, Future};
-use jsonrpc_client_ipc::IpcTransport;
-use mullvad_types::{
- account::{AccountData, AccountToken, VoucherSubmission},
- location::GeoIpLocation,
- relay_constraints::{BridgeSettings, BridgeState, RelaySettings, RelaySettingsUpdate},
- relay_list::RelayList,
- settings::{Settings, TunnelOptions},
- states::TunnelState,
- version::AppVersionInfo,
- wireguard, DaemonEvent,
-};
-use serde::{Deserialize, Serialize};
-use std::{io, path::Path, thread};
-
-static NO_ARGS: [u8; 0] = [];
-
-pub type Result<T> = std::result::Result<T, jsonrpc_client_core::Error>;
-pub use jsonrpc_client_core::{Error, ErrorKind};
-pub use jsonrpc_client_pubsub::Error as PubSubError;
-
-pub fn new_standalone_ipc_client(path: &impl AsRef<Path>) -> io::Result<DaemonRpcClient> {
- let path = path.as_ref().to_string_lossy().to_string();
-
- new_standalone_transport(path, |path| {
- IpcTransport::new(&path, &tokio::reactor::Handle::default())
- })
-}
-
-pub fn new_standalone_transport<
- F: Send + 'static + FnOnce(String) -> io::Result<T>,
- T: jsonrpc_client_core::DuplexTransport + 'static,
->(
- rpc_path: String,
- transport_func: F,
-) -> io::Result<DaemonRpcClient> {
- let (tx, rx) = oneshot::channel();
- thread::spawn(move || match spawn_transport(rpc_path, transport_func) {
- Err(e) => tx
- .send(Err(e))
- .expect("Failed to send error back to caller"),
- Ok((client, server_handle, client_handle)) => {
- let mut rt = tokio::runtime::current_thread::Runtime::new()
- .expect("Failed to start a standalone tokio runtime for mullvad ipc");
- let handle = rt.handle();
- tx.send(Ok((client_handle, server_handle, handle)))
- .expect("Failed to send client handle");
-
- if let Err(e) = rt.block_on(client) {
- log::error!("JSON-RPC client failed: {}", e.description());
- }
- }
- });
-
- rx.wait()
- .map_err(|_| io::Error::new(io::ErrorKind::NotFound, "No transport handles returned"))?
- .map(|(rpc_client, server_handle, executor)| {
- let subscriber =
- jsonrpc_client_pubsub::Subscriber::new(executor, rpc_client.clone(), server_handle);
- DaemonRpcClient {
- rpc_client,
- subscriber,
- }
- })
-}
-
-fn spawn_transport<
- F: Send + FnOnce(String) -> io::Result<T>,
- T: jsonrpc_client_core::DuplexTransport + 'static,
->(
- address: String,
- transport_func: F,
-) -> io::Result<(
- Client<T, jsonrpc_client_core::server::Server>,
- jsonrpc_client_core::server::ServerHandle,
- ClientHandle,
-)> {
- let (server, server_handle) = jsonrpc_client_core::server::Server::new();
- let transport = transport_func(address)?;
- let (client, client_handle) = jsonrpc_client_core::Client::with_server(transport, server);
- Ok((client, server_handle, client_handle))
-}
-
-pub struct DaemonRpcClient {
- rpc_client: jsonrpc_client_core::ClientHandle,
- subscriber: jsonrpc_client_pubsub::Subscriber<tokio::runtime::current_thread::Handle>,
-}
-
-
-impl DaemonRpcClient {
- pub fn connect(&mut self) -> Result<()> {
- self.call("connect", &NO_ARGS)
- }
-
- pub fn disconnect(&mut self) -> Result<()> {
- self.call("disconnect", &NO_ARGS)
- }
-
- pub fn reconnect(&mut self) -> Result<()> {
- self.call("reconnect", &NO_ARGS)
- }
-
- pub fn create_new_account(&mut self) -> Result<()> {
- self.call("create_new_account", &NO_ARGS)
- }
-
- pub fn get_account(&mut self) -> Result<Option<AccountToken>> {
- self.call("get_account", &NO_ARGS)
- }
-
- pub fn get_account_data(&mut self, account: AccountToken) -> Result<AccountData> {
- self.call("get_account_data", &[account])
- }
-
- pub fn submit_voucher(&mut self, voucher: String) -> Result<VoucherSubmission> {
- self.call("submit_voucher", &[voucher])
- }
-
- pub fn set_allow_lan(&mut self, allow_lan: bool) -> Result<()> {
- self.call("set_allow_lan", &[allow_lan])
- }
-
- pub fn set_show_beta_releases(&mut self, enabled: bool) -> Result<()> {
- self.call("set_show_beta_releases", &[enabled])
- }
-
- pub fn set_block_when_disconnected(&mut self, block_when_disconnected: bool) -> Result<()> {
- self.call("set_block_when_disconnected", &[block_when_disconnected])
- }
-
- pub fn get_allow_lan(&mut self) -> Result<bool> {
- self.call("get_allow_lan", &NO_ARGS)
- }
-
- pub fn set_auto_connect(&mut self, auto_connect: bool) -> Result<()> {
- self.call("set_auto_connect", &[auto_connect])
- }
-
- pub fn get_auto_connect(&mut self) -> Result<bool> {
- self.call("get_auto_connect", &NO_ARGS)
- }
-
- pub fn get_current_location(&mut self) -> Result<Option<GeoIpLocation>> {
- self.call("get_current_location", &NO_ARGS)
- }
-
- pub fn get_current_version(&mut self) -> Result<String> {
- self.call("get_current_version", &NO_ARGS)
- }
-
- pub fn get_relay_locations(&mut self) -> Result<RelayList> {
- self.call("get_relay_locations", &NO_ARGS)
- }
-
- pub fn update_relay_locations(&mut self) -> Result<()> {
- self.call("update_relay_locations", &NO_ARGS)
- }
-
- pub fn get_relay_settings(&mut self) -> Result<RelaySettings> {
- self.call("get_relay_settings", &NO_ARGS)
- }
-
- pub fn get_state(&mut self) -> Result<TunnelState> {
- self.call("get_state", &NO_ARGS)
- }
-
- pub fn get_tunnel_options(&mut self) -> Result<TunnelOptions> {
- self.call("get_tunnel_options", &NO_ARGS)
- }
-
- pub fn get_settings(&mut self) -> Result<Settings> {
- self.call("get_settings", &NO_ARGS)
- }
-
- pub fn generate_wireguard_key(&mut self) -> Result<wireguard::KeygenEvent> {
- self.call("generate_wireguard_key", &NO_ARGS)
- }
-
- pub fn get_wireguard_key(&mut self) -> Result<Option<wireguard::PublicKey>> {
- self.call("get_wireguard_key", &NO_ARGS)
- }
-
- pub fn verify_wireguard_key(&mut self) -> Result<bool> {
- self.call("verify_wireguard_key", &NO_ARGS)
- }
-
- pub fn get_version_info(&mut self) -> Result<AppVersionInfo> {
- self.call("get_version_info", &NO_ARGS)
- }
-
- pub fn set_account(&mut self, account: Option<AccountToken>) -> Result<()> {
- self.call("set_account", &[account])
- }
-
- pub fn clear_account_history(&mut self) -> Result<()> {
- self.call("clear_account_history", &NO_ARGS)
- }
-
- pub fn set_enable_ipv6(&mut self, enabled: bool) -> Result<()> {
- self.call("set_enable_ipv6", &[enabled])
- }
-
- pub fn set_wireguard_mtu(&mut self, mtu: Option<u16>) -> Result<()> {
- self.call("set_wireguard_mtu", &[mtu])
- }
-
- pub fn set_wireguard_rotation_interval(&mut self, interval: Option<u32>) -> Result<()> {
- self.call("set_wireguard_rotation_interval", &[interval])
- }
-
- pub fn set_openvpn_mssfix(&mut self, mssfix: Option<u16>) -> Result<()> {
- self.call("set_openvpn_mssfix", &[mssfix])
- }
-
- pub fn set_bridge_settings(&mut self, settings: BridgeSettings) -> Result<()> {
- self.call("set_bridge_settings", &[settings])
- }
-
- pub fn set_bridge_state(&mut self, state: BridgeState) -> Result<()> {
- self.call("set_bridge_state", &[state])
- }
-
- pub fn shutdown(&mut self) -> Result<()> {
- self.call("shutdown", &NO_ARGS)
- }
-
- pub fn prepare_restart(&mut self) -> Result<()> {
- self.call("prepare_restart", &NO_ARGS)
- }
-
- pub fn factory_reset(&mut self) -> Result<()> {
- self.call("factory_reset", &NO_ARGS)
- }
-
- pub fn update_relay_settings(&mut self, update: RelaySettingsUpdate) -> Result<()> {
- self.call("update_relay_settings", &[update])
- }
-
- pub fn get_split_tunnel_processes(&mut self) -> Result<Vec<i32>> {
- self.call("get_split_tunnel_processes", &NO_ARGS)
- }
-
- pub fn add_split_tunnel_process(&mut self, pid: i32) -> Result<()> {
- self.call("add_split_tunnel_process", &[pid])
- }
-
- pub fn remove_split_tunnel_process(&mut self, pid: i32) -> Result<()> {
- self.call("remove_split_tunnel_process", &[pid])
- }
-
- pub fn clear_split_tunnel_processes(&mut self) -> Result<()> {
- self.call("clear_split_tunnel_processes", &NO_ARGS)
- }
-
-
- pub fn call<A, O>(&mut self, method: &'static str, args: &A) -> Result<O>
- where
- A: Serialize + Send + 'static,
- O: for<'de> Deserialize<'de> + Send + 'static,
- {
- self.rpc_client.call_method(method, args).wait()
- }
-
- pub fn daemon_event_subscribe(
- &mut self,
- ) -> impl Future<
- Item = jsonrpc_client_pubsub::Subscription<DaemonEvent>,
- Error = jsonrpc_client_pubsub::Error,
- > {
- self.subscriber.subscribe(
- "daemon_event_subscribe".to_string(),
- "daemon_event_unsubscribe".to_string(),
- "daemon_event".to_string(),
- 0,
- &NO_ARGS,
- )
- }
-}
diff --git a/mullvad-setup/Cargo.toml b/mullvad-setup/Cargo.toml
index 3d113870d4..dd2e87ad71 100644
--- a/mullvad-setup/Cargo.toml
+++ b/mullvad-setup/Cargo.toml
@@ -15,12 +15,20 @@ path = "src/main.rs"
clap = "2.32"
env_logger = "0.7"
err-derive = "0.2.1"
+prost = "0.6"
+prost-types = "0.6"
+tonic = "0.2"
+tower = "0.3"
+tokio = { version = "0.2", features = [ "io-util", "process", "rt-core", "rt-threaded", "stream"] }
+parity-tokio-ipc = "0.7"
-mullvad-ipc-client = { path = "../mullvad-ipc-client" }
mullvad-paths = { path = "../mullvad-paths" }
talpid-core = { path = "../talpid-core" }
talpid-types = { path = "../talpid-types" }
+[build-dependencies]
+tonic-build = { version = "0.2", default-features = false, features = ["transport", "prost"] }
+
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
winapi = "0.3"
diff --git a/mullvad-setup/build.rs b/mullvad-setup/build.rs
index 8cdd992c7c..61b805cbcb 100644
--- a/mullvad-setup/build.rs
+++ b/mullvad-setup/build.rs
@@ -1,6 +1,8 @@
use std::{env, fs, path::PathBuf};
fn main() {
+ tonic_build::compile_protos("../mullvad-daemon/proto/management_interface.proto").unwrap();
+
let out_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let product_version = env!("CARGO_PKG_VERSION").replacen(".0", "", 1);
fs::write(out_dir.join("product-version.txt"), &product_version).unwrap();
diff --git a/mullvad-setup/src/main.rs b/mullvad-setup/src/main.rs
index f8fda169b5..6f80be9df8 100644
--- a/mullvad-setup/src/main.rs
+++ b/mullvad-setup/src/main.rs
@@ -1,18 +1,28 @@
use clap::{crate_authors, crate_description, crate_name, SubCommand};
-use mullvad_ipc_client::{new_standalone_ipc_client, DaemonRpcClient};
-use std::{io, process};
+use parity_tokio_ipc::Endpoint as IpcEndpoint;
+use std::process;
use talpid_core::firewall::{self, Firewall, FirewallArguments};
use talpid_types::ErrorExt;
+use tonic::{
+ self,
+ transport::{Endpoint, Uri},
+};
+use tower::service_fn;
+
+mod proto {
+ tonic::include_proto!("mullvad_daemon.management_interface");
+}
+use proto::management_service_client::ManagementServiceClient;
pub const PRODUCT_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/product-version.txt"));
#[derive(err_derive::Error, Debug)]
pub enum Error {
#[error(display = "Failed to connect to daemon")]
- DaemonConnect(#[error(source)] io::Error),
+ DaemonConnect(#[error(source)] tonic::transport::Error),
#[error(display = "RPC call failed")]
- DaemonRpcError(#[error(source)] mullvad_ipc_client::Error),
+ DaemonRpcError(#[error(source)] tonic::Status),
#[error(display = "This command cannot be run if the daemon is active")]
DaemonIsRunning,
@@ -21,7 +31,8 @@ pub enum Error {
FirewallError(#[error(source)] firewall::Error),
}
-fn main() {
+#[tokio::main]
+async fn main() {
env_logger::init();
let subcommands = vec![
@@ -44,8 +55,8 @@ fn main() {
let matches = app.get_matches();
let result = match matches.subcommand_name().expect("Subcommand has no name") {
- "prepare-restart" => prepare_restart(),
- "reset-firewall" => reset_firewall(),
+ "prepare-restart" => prepare_restart().await,
+ "reset-firewall" => reset_firewall().await,
_ => unreachable!("No command matched"),
};
@@ -55,14 +66,17 @@ fn main() {
}
}
-fn prepare_restart() -> Result<(), Error> {
- let mut rpc = new_rpc_client()?;
- rpc.prepare_restart().map_err(Error::DaemonRpcError)
+async fn prepare_restart() -> Result<(), Error> {
+ let mut rpc = new_grpc_client().await?;
+ rpc.prepare_restart(())
+ .await
+ .map_err(Error::DaemonRpcError)?;
+ Ok(())
}
-fn reset_firewall() -> Result<(), Error> {
+async fn reset_firewall() -> Result<(), Error> {
// Ensure that the daemon isn't running
- if let Ok(_) = new_rpc_client() {
+ if let Ok(_) = new_grpc_client().await {
return Err(Error::DaemonIsRunning);
}
@@ -75,6 +89,17 @@ fn reset_firewall() -> Result<(), Error> {
firewall.reset_policy().map_err(Error::FirewallError)
}
-fn new_rpc_client() -> Result<DaemonRpcClient, Error> {
- new_standalone_ipc_client(&mullvad_paths::get_rpc_socket_path()).map_err(Error::DaemonConnect)
+pub async fn new_grpc_client() -> Result<ManagementServiceClient<tonic::transport::Channel>, Error>
+{
+ let ipc_path = mullvad_paths::get_rpc_socket_path();
+
+ // The URI will be ignored
+ let channel = Endpoint::from_static("lttp://[::]:50051")
+ .connect_with_connector(service_fn(move |_: Uri| {
+ IpcEndpoint::connect(ipc_path.clone())
+ }))
+ .await
+ .map_err(Error::DaemonConnect)?;
+
+ Ok(ManagementServiceClient::new(channel))
}
diff --git a/mullvad-tests/Cargo.toml b/mullvad-tests/Cargo.toml
index 152343c1e9..f840c207eb 100644
--- a/mullvad-tests/Cargo.toml
+++ b/mullvad-tests/Cargo.toml
@@ -12,13 +12,11 @@ integration-tests = []
[dependencies]
duct = "0.13"
-mullvad-ipc-client = { path = "../mullvad-ipc-client" }
mullvad-paths = { path = "../mullvad-paths" }
mullvad-rpc = { path = "../mullvad-rpc" }
mullvad-types = { path = "../mullvad-types" }
notify = "4.0"
openvpn-plugin = { git = "https://github.com/mullvad/openvpn-plugin-rs", branch = "auth-failed-event", features = ["serde"] }
-talpid-ipc = { path = "../talpid-ipc" }
talpid-types = { path = "../talpid-types" }
tempfile = "3.0"
jsonrpc-client-core = { git = "https://github.com/mullvad/jsonrpc-client-rs", rev = "68aac55b" }
diff --git a/mullvad-types/Cargo.toml b/mullvad-types/Cargo.toml
index d4f7871e52..ca3dab3c86 100644
--- a/mullvad-types/Cargo.toml
+++ b/mullvad-types/Cargo.toml
@@ -16,6 +16,7 @@ log = "0.4"
regex = "1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
+tonic = "0.2"
talpid-types = { path = "../talpid-types" }
mullvad-paths = { path = "../mullvad-paths" }
diff --git a/mullvad-types/src/account.rs b/mullvad-types/src/account.rs
index a22b574856..a2ec85ebba 100644
--- a/mullvad-types/src/account.rs
+++ b/mullvad-types/src/account.rs
@@ -33,13 +33,13 @@ pub struct VoucherSubmission {
/// was rejected by the `/v1/submit-voucher` RPC.
#[derive(err_derive::Error, Debug)]
pub enum VoucherError {
- /// Error code -400
+ /// Error code `tonic::Code::NotFound`
#[error(display = "Bad voucher code")]
BadVoucher,
- /// Error code -401
+ /// Error code `tonic::Code::ResourceExhausted`
#[error(display = "Voucher already used")]
VoucherAlreadyUsed,
- /// Error code -100
+ /// Error code `tonic::Code::Internal`
#[error(display = "Server internal error")]
InternalError,
#[error(display = "Unknown error, {}", _0)]
@@ -50,9 +50,9 @@ impl VoucherError {
/// Create error from RPC error code.
pub fn from_rpc_error_code(err_code: i64) -> VoucherError {
match err_code {
- -400 => VoucherError::BadVoucher,
- -401 => VoucherError::VoucherAlreadyUsed,
- -100 => VoucherError::InternalError,
+ x if x == tonic::Code::NotFound as i64 => VoucherError::BadVoucher,
+ x if x == tonic::Code::ResourceExhausted as i64 => VoucherError::VoucherAlreadyUsed,
+ x if x == tonic::Code::Internal as i64 => VoucherError::InternalError,
err => VoucherError::UnknownError(err),
}
}
diff --git a/mullvad-types/src/custom_tunnel.rs b/mullvad-types/src/custom_tunnel.rs
index 12c293c22d..b3cae97347 100644
--- a/mullvad-types/src/custom_tunnel.rs
+++ b/mullvad-types/src/custom_tunnel.rs
@@ -25,8 +25,8 @@ pub enum Error {
#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]
#[cfg_attr(target_os = "android", jnix(skip_all))]
pub struct CustomTunnelEndpoint {
- host: String,
- config: ConnectionConfig,
+ pub host: String,
+ pub config: ConnectionConfig,
}
impl CustomTunnelEndpoint {
diff --git a/mullvad-types/src/lib.rs b/mullvad-types/src/lib.rs
index d77206fc31..e93ab2f606 100644
--- a/mullvad-types/src/lib.rs
+++ b/mullvad-types/src/lib.rs
@@ -13,23 +13,3 @@ pub mod wireguard;
mod custom_tunnel;
pub use crate::custom_tunnel::*;
-
-/// An event sent out from the daemon to frontends.
-#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
-#[serde(rename_all = "snake_case")]
-pub enum DaemonEvent {
- /// The daemon transitioned into a new state.
- TunnelState(states::TunnelState),
-
- /// The daemon settings changed.
- Settings(settings::Settings),
-
- /// The daemon got an updated relay list.
- RelayList(relay_list::RelayList),
-
- /// The daemon got update version info.
- AppVersionInfo(version::AppVersionInfo),
-
- /// Key event
- WireguardKey(wireguard::KeygenEvent),
-}
diff --git a/mullvad-types/src/relay_constraints.rs b/mullvad-types/src/relay_constraints.rs
index b983c30657..6eba0fbd95 100644
--- a/mullvad-types/src/relay_constraints.rs
+++ b/mullvad-types/src/relay_constraints.rs
@@ -28,6 +28,13 @@ pub enum Constraint<T: fmt::Debug + Clone + Eq + PartialEq> {
}
impl<T: fmt::Debug + Clone + Eq + PartialEq> Constraint<T> {
+ pub fn unwrap(self) -> T {
+ match self {
+ Constraint::Any => panic!("called `Constraint::unwrap()` on an `Any` value"),
+ Constraint::Only(value) => value,
+ }
+ }
+
pub fn unwrap_or(self, other: T) -> T {
match self {
Constraint::Any => other,
@@ -43,7 +50,8 @@ impl<T: fmt::Debug + Clone + Eq + PartialEq> Constraint<T> {
}
pub fn map<U: fmt::Debug + Clone + Eq + PartialEq, F: FnOnce(T) -> U>(
- self, f: F
+ self,
+ f: F,
) -> Constraint<U> {
match self {
Constraint::Any => Constraint::Any,
@@ -57,6 +65,13 @@ impl<T: fmt::Debug + Clone + Eq + PartialEq> Constraint<T> {
Constraint::Only(_value) => false,
}
}
+
+ pub fn as_ref(&self) -> Constraint<&T> {
+ match self {
+ Constraint::Any => Constraint::Any,
+ Constraint::Only(ref value) => Constraint::Only(value),
+ }
+ }
}
impl<T: fmt::Debug + Clone + Eq + PartialEq> Default for Constraint<T> {
diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml
index 07ffb7263a..6b40af9465 100644
--- a/talpid-core/Cargo.toml
+++ b/talpid-core/Cargo.toml
@@ -16,8 +16,6 @@ futures01 = { package = "futures", version = "0.1" }
futures = { package = "futures", version = "0.3", features = [ "compat" ]}
hex = "0.4"
ipnetwork = "0.16"
-jsonrpc-core = { git = "https://github.com/mullvad/jsonrpc", branch = "mullvad-fork" }
-jsonrpc-macros = { git = "https://github.com/mullvad/jsonrpc", branch = "mullvad-fork" }
lazy_static = "1.0"
libc = "0.2"
log = "0.4"
@@ -26,7 +24,6 @@ os_pipe = "0.8"
parking_lot = "0.9"
regex = "1.1.0"
shell-escape = "0.1"
-talpid-ipc = { path = "../talpid-ipc" }
talpid-types = { path = "../talpid-types" }
tokio-core = "0.1"
tokio-executor = "0.1"
diff --git a/talpid-ipc/Cargo.toml b/talpid-ipc/Cargo.toml
deleted file mode 100644
index 0a15db0f48..0000000000
--- a/talpid-ipc/Cargo.toml
+++ /dev/null
@@ -1,33 +0,0 @@
-[package]
-name = "talpid-ipc"
-version = "0.1.0"
-authors = ["Mullvad VPN"]
-description = "IPC client and server for talpid"
-license = "GPL-3.0"
-edition = "2018"
-publish = false
-
-[dependencies]
-err-derive = "0.2.1"
-serde = "1.0"
-serde_json = "1.0"
-log = "0.4"
-jsonrpc-core = { git = "https://github.com/mullvad/jsonrpc", branch = "mullvad-fork" }
-jsonrpc-pubsub = { git = "https://github.com/mullvad/jsonrpc", branch = "mullvad-fork" }
-jsonrpc-ipc-server = { git = "https://github.com/mullvad/jsonrpc", branch = "mullvad-fork" }
-tokio = "0.1"
-futures = "0.1"
-
-jsonrpc-client-core = { git = "https://github.com/mullvad/jsonrpc-client-rs", rev = "68aac55b" }
-jsonrpc-client-ipc = { git = "https://github.com/mullvad/jsonrpc-client-rs", rev = "68aac55b" }
-
-[target.'cfg(windows)'.dependencies]
-winapi = { version = "0.3", features = ["accctrl", "aclapi", "securitybaseapi", "winbase", "winerror", "winnt"] }
-
-[dev-dependencies]
-assert_matches = "1.0"
-env_logger = "0.7"
-jsonrpc-macros = { git = "https://github.com/mullvad/jsonrpc", branch = "mullvad-fork" }
-uuid = { version = "0.7", features = ["v4"] }
-futures = "0.1.23"
-tokio = "0.1"
diff --git a/talpid-ipc/src/lib.rs b/talpid-ipc/src/lib.rs
deleted file mode 100644
index 2ab6d506c3..0000000000
--- a/talpid-ipc/src/lib.rs
+++ /dev/null
@@ -1,122 +0,0 @@
-#![deny(rust_2018_idioms)]
-
-use futures::Future;
-use std::{io, thread};
-
-use jsonrpc_core::{MetaIoHandler, Metadata};
-use jsonrpc_ipc_server::{MetaExtractor, NoopExtractor, SecurityAttributes, Server, ServerBuilder};
-
-use std::fmt;
-
-#[cfg(windows)]
-mod win;
-
-/// An Id created by the Ipc server that the client can use to connect to it
-pub type IpcServerId = String;
-
-#[derive(err_derive::Error, Debug)]
-#[error(no_from)]
-pub enum Error {
- #[error(display = "Unable to start IPC server")]
- StartServerError(#[error(source)] io::Error),
-
- #[error(display = "IPC server thread panicked and never returned a start result")]
- ServerThreadPanicError,
-
- #[error(display = "Error in IPC server")]
- IpcServerError(#[error(source)] io::Error),
-
- #[error(display = "Unable to set permissions for IPC endpoint")]
- PermissionsError(#[error(source)] io::Error),
-}
-
-
-pub struct IpcServer {
- path: String,
- server: Server,
-}
-
-impl IpcServer {
- pub fn start<M: Metadata + Default>(
- handler: MetaIoHandler<M>,
- path: &str,
- ) -> Result<Self, Error> {
- Self::start_with_metadata(handler, NoopExtractor, path)
- }
-
- pub fn start_with_metadata<M, E>(
- handler: MetaIoHandler<M>,
- meta_extractor: E,
- path: &str,
- ) -> Result<Self, Error>
- where
- M: Metadata + Default,
- E: MetaExtractor<M>,
- {
- let security_attributes =
- SecurityAttributes::allow_everyone_create().map_err(Error::PermissionsError)?;
- let server = ServerBuilder::with_meta_extractor(handler, meta_extractor)
- .set_security_attributes(security_attributes)
- .start(path)
- .map_err(Error::StartServerError)
- .and_then(|(fut, start, server)| {
- thread::spawn(move || tokio::run(fut));
- if let Some(error) = start
- .wait()
- .map_err(|_cancelled| Error::ServerThreadPanicError)?
- {
- return Err(Error::IpcServerError(error));
- }
- Ok(server)
- })
- .map(|server| IpcServer {
- path: path.to_owned(),
- server,
- })?;
-
- #[cfg(unix)]
- {
- use std::{fs, os::unix::fs::PermissionsExt};
- fs::set_permissions(&path, PermissionsExt::from_mode(0o766))
- .map_err(Error::PermissionsError)?;
- }
- #[cfg(windows)]
- win::deny_network_access(path).map_err(Error::PermissionsError)?;
- Ok(server)
- }
-
- /// Returns the uds/named pipe path this `IpcServer` is listening on.
- pub fn path(&self) -> &str {
- &self.path
- }
-
- /// Creates a handle bound to this `IpcServer` that can be used to shut it down.
- pub fn close_handle(&self) -> CloseHandle {
- CloseHandle(self.server.close_handle())
- }
-
- /// Consumes the server and waits for it to finish. Get a `CloseHandle` before calling this
- /// if you want to be able to shut the server down.
- pub fn wait(self) {
- self.server.wait();
- }
-}
-
-// FIXME: This custom impl is because `Server` does not implement `Debug` yet:
-// https://github.com/paritytech/jsonrpc/pull/195
-impl fmt::Debug for IpcServer {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("IpcServer")
- .field("path", &self.path)
- .finish()
- }
-}
-
-#[derive(Clone)]
-pub struct CloseHandle(jsonrpc_ipc_server::CloseHandle);
-
-impl CloseHandle {
- pub fn close(self) {
- self.0.close();
- }
-}
diff --git a/talpid-ipc/tests/ipc-client-server.rs b/talpid-ipc/tests/ipc-client-server.rs
deleted file mode 100644
index 6be8349487..0000000000
--- a/talpid-ipc/tests/ipc-client-server.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-// TODO fix these tests on Windows
-#![cfg(not(windows))]
-
-use assert_matches::assert_matches;
-use futures::{sync::oneshot, Future};
-
-use jsonrpc_client_core::{Error as ClientError, Transport};
-use jsonrpc_core::{Error, IoHandler};
-use jsonrpc_macros::build_rpc_trait;
-use std::{
- sync::{mpsc, Mutex},
- time::Duration,
-};
-
-build_rpc_trait! {
- pub trait TestApi {
- #[rpc(name = "foo")]
- fn foo(&self, i64) -> Result<(), Error>;
- }
-}
-
-struct ApiImpl {
- tx: Mutex<mpsc::Sender<i64>>,
-}
-
-impl TestApi for ApiImpl {
- fn foo(&self, i: i64) -> Result<(), Error> {
- self.tx.lock().unwrap().send(i).unwrap();
- Ok(())
- }
-}
-
-#[test]
-fn can_call_rpcs_on_server() {
- env_logger::init();
-
- let (server, rx) = create_server();
- let server_path = server.path().to_owned();
- let client = create_client(server_path);
-
- let _result: () = client.call_method("foo", &[97]).wait().unwrap();
- assert_eq!(Ok(97), rx.recv_timeout(Duration::from_millis(500)));
-
- let result: Result<(), ClientError> = client.call_method("invalid_method", &[0]).wait();
- assert_matches!(result, Err(_));
- server.close_handle().close();
-}
-
-#[test]
-#[should_panic]
-fn ipc_client_invalid_url() {
- let _client = create_client("INVALID ID".to_owned());
-}
-
-fn create_server() -> (talpid_ipc::IpcServer, mpsc::Receiver<i64>) {
- let (tx, rx) = mpsc::channel();
- let rpc = ApiImpl { tx: Mutex::new(tx) };
- let mut io = IoHandler::new();
- io.extend_with(rpc.to_delegate());
-
- let uuid = uuid::Uuid::new_v4().to_string();
- let ipc_path = if cfg!(windows) {
- format!(r"\\.\pipe\ipc-test-{}", uuid)
- } else {
- format!("/tmp/ipc-test-{}", uuid)
- };
- let server = talpid_ipc::IpcServer::start(io.into(), &ipc_path).unwrap();
- (server, rx)
-}
-
-fn create_client(ipc_path: String) -> jsonrpc_client_core::ClientHandle {
- use std::thread;
- let (tx, rx) = oneshot::channel();
-
- thread::spawn(move || {
- let (client, client_handle) =
- jsonrpc_client_ipc::IpcTransport::new(&ipc_path, &tokio::reactor::Handle::default())
- .expect("failed to construct a transport")
- .into_client();
- tx.send(client_handle).unwrap();
- client.wait().unwrap();
- });
-
- rx.wait().expect("Failed to construct a valid client")
-}