diff options
| author | Emīls Piņķis <emils@mullvad.net> | 2018-08-29 17:08:03 +0100 |
|---|---|---|
| committer | Emīls Piņķis <emils@mullvad.net> | 2018-08-29 17:08:03 +0100 |
| commit | c0b41134ac1c7e9162c8f42121c3c809e421ce50 (patch) | |
| tree | 9afb4b14db5feac02399467efb870a929166db90 | |
| parent | 559a81c6044d620159d8229295bee3b2e909eca4 (diff) | |
| parent | f47b02cfd9119392b6b39f8a0c9426d4b17575bd (diff) | |
| download | mullvadvpn-c0b41134ac1c7e9162c8f42121c3c809e421ce50.tar.xz mullvadvpn-c0b41134ac1c7e9162c8f42121c3c809e421ce50.zip | |
Merge branch 'swap-ws-to-ipc'
46 files changed, 926 insertions, 1846 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 7808adbf2b..32332e8524 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,8 @@ Line wrap the file at 100 chars. Th having a create account form first. - The CLI command to list relays is now shorter, `mullvad relay list` instead of `mullvad relay list locations`. +- Replace WebSockets with Unix domain sockets/Named pipes for IPC. The location + of the socket can be controlled with `MULLVAD_RPC_SOCKET_PATH`. ### Fixed - Fix incorrect window position when using external display. diff --git a/Cargo.lock b/Cargo.lock index 78db2ce95c..5a1b732b08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -440,6 +440,18 @@ dependencies = [ ] [[package]] +name = "globset" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.6.6 (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.3 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "httparse" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -497,16 +509,6 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "inotify" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -545,6 +547,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jsonrpc-client-core" version = "0.5.0" +source = "git+https://github.com/mullvad/jsonrpc-client-rs#32c0d940f6e0ff0ee11690d746bfbfecd9345ed7" +dependencies = [ + "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (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.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.24 (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.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -569,9 +584,23 @@ dependencies = [ ] [[package]] +name = "jsonrpc-client-ipc" +version = "0.5.0" +source = "git+https://github.com/mullvad/jsonrpc-client-rs#32c0d940f6e0ff0ee11690d746bfbfecd9345ed7" +dependencies = [ + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs)", + "jsonrpc-server-utils 8.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-tokio-ipc 0.1.5 (git+https://github.com/NikVolf/parity-tokio-ipc?rev=stable)", + "tokio 0.1.7 (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.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "jsonrpc-core" version = "8.0.1" -source = "git+https://github.com/paritytech/jsonrpc?tag=v8.0.1#01434b52bb7767c845c3cf43d545036e7680a263" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -582,61 +611,73 @@ dependencies = [ [[package]] name = "jsonrpc-core" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +version = "8.0.2" +source = "git+https://github.com/paritytech/jsonrpc#98e892b07949b030461cd8781b2c7b635370327f" dependencies = [ "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] +name = "jsonrpc-ipc-server" +version = "8.0.1" +source = "git+https://github.com/paritytech/jsonrpc#98e892b07949b030461cd8781b2c7b635370327f" +dependencies = [ + "jsonrpc-core 8.0.2 (git+https://github.com/paritytech/jsonrpc)", + "jsonrpc-server-utils 8.0.1 (git+https://github.com/paritytech/jsonrpc)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc?branch=stable)", + "parking_lot 0.6.3 (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.0" -source = "git+https://github.com/paritytech/jsonrpc?tag=v8.0.1#01434b52bb7767c845c3cf43d545036e7680a263" +version = "8.0.1" +source = "git+https://github.com/paritytech/jsonrpc#98e892b07949b030461cd8781b2c7b635370327f" dependencies = [ - "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", - "jsonrpc-pubsub 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", + "jsonrpc-core 8.0.2 (git+https://github.com/paritytech/jsonrpc)", + "jsonrpc-pubsub 8.0.1 (git+https://github.com/paritytech/jsonrpc)", "serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-pubsub" -version = "8.0.0" -source = "git+https://github.com/paritytech/jsonrpc?tag=v8.0.1#01434b52bb7767c845c3cf43d545036e7680a263" +version = "8.0.1" +source = "git+https://github.com/paritytech/jsonrpc#98e892b07949b030461cd8781b2c7b635370327f" dependencies = [ - "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 8.0.2 (git+https://github.com/paritytech/jsonrpc)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jsonrpc-server-utils" version = "8.0.0" -source = "git+https://github.com/paritytech/jsonrpc?tag=v8.0.1#01434b52bb7767c845c3cf43d545036e7680a263" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.9 (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 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", + "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.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "jsonrpc-ws-server" -version = "8.0.0" -source = "git+https://github.com/paritytech/jsonrpc?tag=v8.0.1#01434b52bb7767c845c3cf43d545036e7680a263" +name = "jsonrpc-server-utils" +version = "8.0.1" +source = "git+https://github.com/paritytech/jsonrpc#98e892b07949b030461cd8781b2c7b635370327f" dependencies = [ - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", - "jsonrpc-server-utils 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ws 0.7.5 (git+https://github.com/tomusdrw/ws-rs)", + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 8.0.2 (git+https://github.com/paritytech/jsonrpc)", + "log 0.4.3 (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.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -685,6 +726,15 @@ dependencies = [ ] [[package]] +name = "lock_api" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "log" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -709,11 +759,6 @@ dependencies = [ ] [[package]] -name = "matches" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "memchr" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -777,6 +822,27 @@ dependencies = [ ] [[package]] +name = "mio-named-pipes" +version = "0.1.6" +source = "git+https://github.com/alexcrichton/mio-named-pipes#2072ae0de5b3632dbb065fcd9c8be6c6a2fc39ae" +dependencies = [ + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "mio-uds" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "miow" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -799,6 +865,15 @@ dependencies = [ ] [[package]] +name = "miow" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "socket2 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "mnl" version = "0.1.0" source = "git+https://github.com/mullvad/mnl-rs#8ceadc9e4a43830cc78eb701453d44271b8628fe" @@ -824,7 +899,9 @@ dependencies = [ "clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", "mullvad-ipc-client 0.1.0", + "mullvad-paths 0.1.0", "mullvad-types 0.1.0", "serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", "talpid-ipc 0.1.0", @@ -842,10 +919,10 @@ dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "fern 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", - "jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", - "jsonrpc-pubsub 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", - "jsonrpc-ws-server 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", + "jsonrpc-core 8.0.2 (git+https://github.com/paritytech/jsonrpc)", + "jsonrpc-ipc-server 8.0.1 (git+https://github.com/paritytech/jsonrpc)", + "jsonrpc-macros 8.0.1 (git+https://github.com/paritytech/jsonrpc)", + "jsonrpc-pubsub 8.0.1 (git+https://github.com/paritytech/jsonrpc)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -875,11 +952,17 @@ name = "mullvad-ipc-client" version = "0.1.0" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs)", + "jsonrpc-client-ipc 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "mullvad-paths 0.1.0", "mullvad-types 0.1.0", "serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", "talpid-ipc 0.1.0", "talpid-types 0.1.0", + "tokio-core 0.1.17 (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.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -935,6 +1018,9 @@ name = "mullvad-tests" version = "0.1.0" dependencies = [ "duct 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs)", + "jsonrpc-client-ipc 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "mullvad-ipc-client 0.1.0", "mullvad-paths 0.1.0", @@ -943,6 +1029,7 @@ dependencies = [ "openvpn-plugin 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "talpid-ipc 0.1.0", "tempfile 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1112,11 +1199,47 @@ dependencies = [ ] [[package]] +name = "parity-tokio-ipc" +version = "0.1.5" +source = "git+https://github.com/nikvolf/parity-tokio-ipc?branch=stable#b4a6cdf7d9b1e51c5d744d7f47d391a69a943232" +dependencies = [ + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-named-pipes 0.1.6 (git+https://github.com/alexcrichton/mio-named-pipes)", + "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.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.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-named-pipes 0.1.0 (git+https://github.com/nikvolf/tokio-named-pipes?branch=stable)", + "tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "parity-tokio-ipc" +version = "0.1.5" +source = "git+https://github.com/NikVolf/parity-tokio-ipc?rev=stable#b4a6cdf7d9b1e51c5d744d7f47d391a69a943232" +dependencies = [ + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-named-pipes 0.1.6 (git+https://github.com/alexcrichton/mio-named-pipes)", + "miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.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.7 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-named-pipes 0.1.0 (git+https://github.com/nikvolf/tokio-named-pipes?branch=stable)", + "tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "parking_lot" -version = "0.4.8" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1359,11 +1482,6 @@ dependencies = [ ] [[package]] -name = "sha1" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "shared_child" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1423,6 +1541,17 @@ dependencies = [ ] [[package]] +name = "socket2" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (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" @@ -1504,8 +1633,8 @@ dependencies = [ "failure 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", "ipnetwork 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", - "jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", + "jsonrpc-core 8.0.2 (git+https://github.com/paritytech/jsonrpc)", + "jsonrpc-macros 8.0.1 (git+https://github.com/paritytech/jsonrpc)", "lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1534,15 +1663,18 @@ dependencies = [ "assert_matches 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", - "jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", - "jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", - "jsonrpc-pubsub 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", - "jsonrpc-ws-server 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs)", + "jsonrpc-client-ipc 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs)", + "jsonrpc-core 8.0.2 (git+https://github.com/paritytech/jsonrpc)", + "jsonrpc-ipc-server 8.0.1 (git+https://github.com/paritytech/jsonrpc)", + "jsonrpc-macros 8.0.1 (git+https://github.com/paritytech/jsonrpc)", + "jsonrpc-pubsub 8.0.1 (git+https://github.com/paritytech/jsonrpc)", "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "ws 0.7.5 (git+https://github.com/tomusdrw/ws-rs)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1551,9 +1683,13 @@ version = "0.1.0" dependencies = [ "env_logger 0.5.12 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs)", + "jsonrpc-client-ipc 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs)", "log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "openvpn-plugin 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "talpid-ipc 0.1.0", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "windres 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1696,6 +1832,18 @@ dependencies = [ ] [[package]] +name = "tokio-named-pipes" +version = "0.1.0" +source = "git+https://github.com/nikvolf/tokio-named-pipes?branch=stable#9a9372618552d2c25c2ee4511e5e0fb4eef318e3" +dependencies = [ + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-named-pipes 0.1.6 (git+https://github.com/alexcrichton/mio-named-pipes)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "tokio-openssl" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1802,6 +1950,22 @@ dependencies = [ ] [[package]] +name = "tokio-uds" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.23 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", + "mio-uds 0.6.6 (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.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "try-lock" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1820,19 +1984,6 @@ dependencies = [ ] [[package]] -name = "unicode-bidi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "matches 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "unicode-width" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1861,16 +2012,6 @@ dependencies = [ ] [[package]] -name = "url" -version = "1.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "matches 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "utf8-ranges" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2004,22 +2145,6 @@ dependencies = [ ] [[package]] -name = "ws" -version = "0.7.5" -source = "git+https://github.com/tomusdrw/ws-rs#f12d19c4c19422fc79af28a3181f598bc07ecd1e" -dependencies = [ - "byteorder 1.2.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bytes 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", - "httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", - "sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "ws2_32-sys" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2088,24 +2213,27 @@ source = "git+https://github.com/mullvad/rust-openssl#4dbd237fe1f6454d8a0042ccf4 "checksum futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "ab90cde24b3319636588d0c35fe03b1333857621051837ed769faefb4c2162e4" "checksum gcc 0.3.54 (registry+https://github.com/rust-lang/crates.io-index)" = "5e33ec290da0d127825013597dbdfc28bee4964690c7ce1166cbc2a7bd08b1bb" "checksum globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90d069fe6beb9be359ef505650b3f73228c5591a3c4b1f32be2f4f44459ffa3a" +"checksum globset 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8e49edbcc9c7fc5beb8c0a54e7319ff8bed353a2b55e85811c6281188c2a6c84" "checksum httparse 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7b6288d7db100340ca12873fd4d08ad1b8f206a9457798dfb17c018a33fee540" "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" "checksum hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)" = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7" "checksum hyper-openssl 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0800c7b541e9b5be3e3cf8c8773d2fdb33975d07551fa1279d90e154c18db4d8" -"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum inotify 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887fcc180136e77a85e6a6128579a719027b1bab9b1c38ea4444244fe262c20c" "checksum ioctl-sys 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2c4b26352496eaaa8ca7cfa9bd99e93419d3f7983dc6e99c2a35fe9e33504a" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum ipnetwork 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b750b2f5cc97f86d0280a173e64fee0cbb4cb73077ee201208ff4f063668b89" "checksum itoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5adb58558dcd1d786b5f0bd15f3226ee23486e24b7b58304b60f64dc68e62606" +"checksum jsonrpc-client-core 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs)" = "<none>" "checksum jsonrpc-client-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f29cb249837420fb0cee7fb0fbf1d22679e121b160e71bb5e0d90b9df241c23e" "checksum jsonrpc-client-http 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e642eb74423b9dfcb4512fda167148746b76f788a823cd712fadf409f31d302" -"checksum jsonrpc-core 8.0.1 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)" = "<none>" +"checksum jsonrpc-client-ipc 0.5.0 (git+https://github.com/mullvad/jsonrpc-client-rs)" = "<none>" "checksum jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ddf83704f4e79979a424d1082dd2c1e52683058056c9280efa19ac5f6bc9033c" -"checksum jsonrpc-macros 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)" = "<none>" -"checksum jsonrpc-pubsub 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)" = "<none>" -"checksum jsonrpc-server-utils 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)" = "<none>" -"checksum jsonrpc-ws-server 8.0.0 (git+https://github.com/paritytech/jsonrpc?tag=v8.0.1)" = "<none>" +"checksum jsonrpc-core 8.0.2 (git+https://github.com/paritytech/jsonrpc)" = "<none>" +"checksum jsonrpc-ipc-server 8.0.1 (git+https://github.com/paritytech/jsonrpc)" = "<none>" +"checksum jsonrpc-macros 8.0.1 (git+https://github.com/paritytech/jsonrpc)" = "<none>" +"checksum jsonrpc-pubsub 8.0.1 (git+https://github.com/paritytech/jsonrpc)" = "<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/paritytech/jsonrpc)" = "<none>" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca488b89a5657b0a2ecd45b95609b3e848cf1755da332a0da46e2b2b1cb371a7" @@ -2113,18 +2241,21 @@ source = "git+https://github.com/mullvad/rust-openssl#4dbd237fe1f6454d8a0042ccf4 "checksum libc 0.2.43 (registry+https://github.com/rust-lang/crates.io-index)" = "76e3a3ef172f1a0b9a9ff0dd1491ae5e6c948b94479a3021819ba7d860c8645d" "checksum linked-hash-map 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "70fb39025bc7cdd76305867c4eccf2f2dcf6e9a57f5b21a93e1c2d86cd03ec9e" "checksum linked_hash_set 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7c91c4c7bbeb4f2f7c4e5be11e6a05bd6830bc37249c47ce1ad86ad453ff9c" +"checksum lock_api 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "949826a5ccf18c1b3a7c3d57692778d21768b79e46eb9dd07bfc4c2160036c54" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "61bd98ae7f7b754bc53dca7d44b604f733c6bba044ea6f41bc8d89272d8161d2" "checksum log-panics 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ae0136257df209261daa18d6c16394757c63e032e27aafd8b07788b051082bef" -"checksum matches 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "835511bab37c34c47da5cb44844bea2cfde0236db0b506f90ea4224482c9774a" "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" "checksum memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "796fba70e76612589ed2ce7f45282f5af869e0fdd7cc6199fa1aa1f1d591ba9d" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum mime 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fe51c8699d2dc522bf8c1ebe26ea2193d151fb54bcdfd7d0318750c189994cd9" "checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e" "checksum mio 0.6.15 (registry+https://github.com/rust-lang/crates.io-index)" = "4fcfcb32d63961fb6f367bfd5d21e4600b92cd310f71f9dca25acae196eb1560" +"checksum mio-named-pipes 0.1.6 (git+https://github.com/alexcrichton/mio-named-pipes)" = "<none>" +"checksum mio-uds 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "84c7b5caa3a118a6e34dbac36504503b1e8dc5835e833306b9d6af0e05929f79" "checksum miow 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3e690c5df6b2f60acd45d56378981e827ff8295562fc8d34f573deb267a59cd1" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" +"checksum miow 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226" "checksum mnl 0.1.0 (git+https://github.com/mullvad/mnl-rs)" = "<none>" "checksum mnl-sys 0.1.0 (git+https://github.com/mullvad/mnl-rs)" = "<none>" "checksum net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "42550d9fb7b6684a6d404d9fa7250c2eb2646df731d1c06afc06dcee9e1bcf88" @@ -2143,7 +2274,9 @@ source = "git+https://github.com/mullvad/rust-openssl#4dbd237fe1f6454d8a0042ccf4 "checksum os_pipe 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fe033225d563042c3eeb22ffd1d2ea1aefcc48e7e37151a064c9e0bae64b253f" "checksum os_pipe 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9d339267cdef39ee54ef165fdfaa2c7289a7465f0188ebe1c8a63872ca64c7" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" -"checksum parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "149d8f5b97f3c1133e3cfcd8886449959e856b557ff281e292b733d7c69e005e" +"checksum parity-tokio-ipc 0.1.5 (git+https://github.com/NikVolf/parity-tokio-ipc?rev=stable)" = "<none>" +"checksum parity-tokio-ipc 0.1.5 (git+https://github.com/nikvolf/parity-tokio-ipc?branch=stable)" = "<none>" +"checksum parking_lot 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "69376b761943787ebd5cc85a5bc95958651a22609c5c1c2b65de21786baec72b" "checksum parking_lot_core 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4db1a8ccf734a7bce794cc19b3df06ed87ab2f3907036b693c68f56b4d4537fa" "checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pfctl 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9aefb0dd38fc99f8bea821a69fa2cdeb57633bd80da078f2f7131d59ec4c91be" @@ -2174,7 +2307,6 @@ source = "git+https://github.com/mullvad/rust-openssl#4dbd237fe1f6454d8a0042ccf4 "checksum serde 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)" = "6dfad05c8854584e5f72fb859385ecdfa03af69c3fd0572f0da2d4c95f060bdb" "checksum serde_derive 1.0.71 (registry+https://github.com/rust-lang/crates.io-index)" = "b719c6d5e9f73fbc37892246d5852333f040caa617b8873c6aced84bcb28e7bb" "checksum serde_json 1.0.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c6908c7b925cd6c590358a4034de93dbddb20c45e1d021931459fd419bf0e2" -"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" "checksum shared_child 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e254d79655b3a1cb86b4079fc080c91664af57d0a81facb59fc828503cd4f48" "checksum shell-escape 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "170a13e64f2a51b77a45702ba77287f5c6829375b04a69cf2222acd17d0cfab9" "checksum simple-signal 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "53f7da44adcc42667d57483bd93f81295f27d66897804b757573b61b6f13288b" @@ -2184,6 +2316,7 @@ source = "git+https://github.com/mullvad/rust-openssl#4dbd237fe1f6454d8a0042ccf4 "checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" "checksum smallvec 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "211a489e65e94b103926d2054ae515a1cdb5d515ea0ef414fee23b7e043ce748" "checksum snailquote 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "abe8023ba7352ceed1705c51d96f503c34eaec3142fa60e0591dce0fb3f81e03" +"checksum socket2 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "962a516af4d3a7c272cb3a1d50a8cc4e5b41802e4ad54cfb7bee8ba61d37d703" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" @@ -2205,6 +2338,7 @@ source = "git+https://github.com/mullvad/rust-openssl#4dbd237fe1f6454d8a0042ccf4 "checksum tokio-executor 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "424f0c87ecd66b863045d84e384cb7ce0ae384d8b065b9f0363d29c0d1b30b2f" "checksum tokio-fs 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b5cbe4ca6e71cb0b62a66e4e6f53a8c06a6eefe46cc5f665ad6f274c9906f135" "checksum tokio-io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a5c9635ee806f26d302b8baa1e145689a280d8f5aa8d0552e7344808da54cc21" +"checksum tokio-named-pipes 0.1.0 (git+https://github.com/nikvolf/tokio-named-pipes?branch=stable)" = "<none>" "checksum tokio-openssl 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4646ae1fd623393de3d796ea53af75acd02938dd5579544fbd6d236d041978a6" "checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" "checksum tokio-reactor 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8703a5762ff6913510dc64272c714c4389ffd8c4b3cf602879b8bd14ff06b604" @@ -2214,17 +2348,15 @@ source = "git+https://github.com/mullvad/rust-openssl#4dbd237fe1f6454d8a0042ccf4 "checksum tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6131e780037787ff1b3f8aad9da83bca02438b72277850dd6ad0d455e0e20efc" "checksum tokio-timer 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1c76b4e97a4f61030edff8bd272364e4f731b9f54c7307eb4eb733c3926eb96a" "checksum tokio-udp 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43eb534af6e8f37d43ab1b612660df14755c42bd003c5f8d2475ee78cc4600c0" +"checksum tokio-uds 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "65ae5d255ce739e8537221ed2942e0445f4b3b813daebac1c0050ddaaa3587f9" "checksum try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicase 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284b6d3db520d67fbe88fd778c21510d1b0ba4a551e5d0fbb023d33405f6de8a" -"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -"checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode_categories 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum url 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2a321979c09843d272956e73700d12c4e7d3d92b2ee112b31548aef0d4efc5a6" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e1436e58182935dcd9ce0add9ea0b558e8a87befe01c1a301e6020aeb0876363" "checksum vcpkg 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cbe533e138811704c0e3cbde65a818b35d3240409b4346256c5ede403e082474" @@ -2244,5 +2376,4 @@ source = "git+https://github.com/mullvad/rust-openssl#4dbd237fe1f6454d8a0042ccf4 "checksum windows-service 0.1.0 (git+https://github.com/mullvad/windows-service-rs.git?rev=55c5dfb372e6b3f5607a3159c5388d27b6b84ff6)" = "<none>" "checksum windres 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dd6e13ff705d7fab032bdf857354035c56c8c889e364029e4300779eb6e1d729" "checksum winreg 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a27a759395c1195c4cc5cda607ef6f8f6498f64e78f7900f5de0a127a424704a" -"checksum ws 0.7.5 (git+https://github.com/tomusdrw/ws-rs)" = "<none>" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" @@ -311,14 +311,14 @@ The cache directory can be changed by setting the `MULLVAD_CACHE_DIR` environmen #### RPC address file -The full path to the RPC address file can be changed by setting the `MULLVAD_RPC_ADDRESS_PATH` +The full path to the RPC address file can be changed by setting the `MULLVAD_RPC_SOCKET_PATH` environment variable. | Platform | Path | |----------|------| -| Linux | `/tmp/.mullvad_rpc_address` | -| macOS | `/tmp/.mullvad_rpc_address` | -| Windows | `C:\ProgramData\Mullvad VPN\.mullvad_rpc_address` | +| Linux | `/var/run/mullvad-vpn` | +| macOS | `/var/run/mullvad-vpn` | +| Windows | `//./pipe/Mullvad VPN` | ## Quirks diff --git a/gui/packages/desktop/package.json b/gui/packages/desktop/package.json index 98b6ab3c85..70ef5cc11c 100644 --- a/gui/packages/desktop/package.json +++ b/gui/packages/desktop/package.json @@ -13,6 +13,7 @@ "license": "GPL-3.0", "dependencies": { "@mullvad/components": "0.1.0", + "JSONStream": "^1.3.4", "babel-runtime": "^6.26.0", "connected-react-router": "^4.3.0", "d3-geo-projection": "^2.3.2", @@ -33,8 +34,7 @@ "validated": "^1.3.0" }, "optionalDependencies": { - "nseventmonitor": "https://github.com/mullvad/NSEventMonitor.git#0.0.9", - "windows-security": "https://github.com/mullvad/windows-security.git#0.0.5" + "nseventmonitor": "https://github.com/mullvad/NSEventMonitor.git#0.0.9" }, "devDependencies": { "babel-cli": "^6.26.0", diff --git a/gui/packages/desktop/src/common/types.js b/gui/packages/desktop/src/common/types.js deleted file mode 100644 index f9ede46014..0000000000 --- a/gui/packages/desktop/src/common/types.js +++ /dev/null @@ -1,6 +0,0 @@ -// @flow - -export type RpcCredentials = { - connectionString: string, - sharedSecret: string, -}; diff --git a/gui/packages/desktop/src/main/index.js b/gui/packages/desktop/src/main/index.js index 34eb88247a..c9067a0146 100644 --- a/gui/packages/desktop/src/main/index.js +++ b/gui/packages/desktop/src/main/index.js @@ -10,7 +10,6 @@ import { app, screen, BrowserWindow, ipcMain, Tray, Menu, nativeImage } from 'el import TrayIconController from './tray-icon-controller'; import WindowController from './window-controller'; -import RpcAddressFile from './rpc-address-file'; import ShutdownCoordinator from './shutdown-coordinator'; import { resolveBin } from './proc'; @@ -191,45 +190,6 @@ const ApplicationMain = { }, _registerIpcListeners() { - ipcMain.on('discover-daemon-connection', async (event) => { - const addressFile = new RpcAddressFile(); - - log.debug(`Waiting for RPC address file: "${addressFile.filePath}"`); - - try { - await addressFile.waitUntilExists(); - } catch (error) { - log.error(`Cannot finish polling the RPC address file: ${error.message}`); - return; - } - - try { - if (!addressFile.isTrusted()) { - log.error(`Cannot verify the credibility of RPC address file`); - return; - } - } catch (error) { - log.error(`An error occurred during the credibility check: ${error.message}`); - return; - } - - // There is a race condition here where the owner and permissions of - // the file can change in the time between we validate the owner and - // permissions and read the contents of the file. We deem the chance - // of that to be small enough to ignore. - - try { - const credentials = await addressFile.parse(); - - log.debug('Read RPC connection info', credentials.connectionString); - - event.sender.send('daemon-connection-ready', credentials); - } catch (error) { - log.error(`Cannot parse the RPC address file: ${error.message}`); - return; - } - }); - ipcMain.on('show-window', () => { const windowController = this._windowController; if (windowController) { diff --git a/gui/packages/desktop/src/main/rpc-address-file.js b/gui/packages/desktop/src/main/rpc-address-file.js deleted file mode 100644 index 9c46165058..0000000000 --- a/gui/packages/desktop/src/main/rpc-address-file.js +++ /dev/null @@ -1,112 +0,0 @@ -// @flow - -import fs from 'fs'; -import path from 'path'; -import { app } from 'electron'; -import { promisify } from 'util'; -import { getSystemTemporaryDirectory } from './tempdir'; - -import type { RpcCredentials } from '../common/types'; - -const fsReadFileAsync = promisify(fs.readFile); - -const POLL_INTERVAL = 200; - -export default class RpcAddressFile { - _filePath = getRpcAddressFilePath(); - _pollIntervalId: ?IntervalID; - _pollPromise: ?Promise<void>; - - get filePath(): string { - return this._filePath; - } - - waitUntilExists(): Promise<void> { - let promise = this._pollPromise; - - if (!promise) { - promise = new Promise((resolve) => { - const timer = setInterval(() => { - fs.exists(this._filePath, (exists) => { - if (exists) { - clearInterval(timer); - resolve(); - - this._pollPromise = null; - } - }); - }, POLL_INTERVAL); - }); - - this._pollPromise = promise; - } - - return promise; - } - - async parse(): Promise<RpcCredentials> { - const data = await fsReadFileAsync(this._filePath, 'utf8'); - const [connectionString, sharedSecret] = data.split('\n', 2); - - if (connectionString && sharedSecret !== undefined) { - return { - connectionString, - sharedSecret, - }; - } else { - throw new Error('Cannot parse the RPC address file'); - } - } - - isTrusted() { - const filePath = this._filePath; - switch (process.platform) { - case 'win32': - return isOwnedByLocalSystem(filePath); - case 'darwin': - case 'linux': - return isOwnedAndOnlyWritableByRoot(filePath); - default: - throw new Error(`Unknown platform: ${process.platform}`); - } - } -} - -function getRpcAddressFilePath() { - const rpcAddressFileName = '.mullvad_rpc_address'; - - switch (process.platform) { - case 'win32': { - // Windows: %ALLUSERSPROFILE%\{appname} - const programDataDirectory = process.env.ALLUSERSPROFILE; - if (programDataDirectory) { - const appDataDirectory = path.join(programDataDirectory, app.getName()); - return path.join(appDataDirectory, rpcAddressFileName); - } else { - throw new Error('Missing %ALLUSERSPROFILE% environment variable'); - } - } - default: - return path.join(getSystemTemporaryDirectory(), rpcAddressFileName); - } -} - -function isOwnedAndOnlyWritableByRoot(path: string): boolean { - const stat = fs.statSync(path); - const isOwnedByRoot = stat.uid === 0; - const isOnlyWritableByOwner = (stat.mode & parseInt('022', 8)) === 0; - - return isOwnedByRoot && isOnlyWritableByOwner; -} - -function isOwnedByLocalSystem(path: string): boolean { - // $FlowFixMe: this module is only available on Windows - const winsec = require('windows-security'); - const ownerSid = winsec.getFileOwnerSid(path, null); - const isWellKnownSid = winsec.isWellKnownSid( - ownerSid, - winsec.WellKnownSid.BuiltinAdministratorsSid, - ); - - return isWellKnownSid; -} diff --git a/gui/packages/desktop/src/main/tempdir.js b/gui/packages/desktop/src/main/tempdir.js deleted file mode 100644 index 938448949a..0000000000 --- a/gui/packages/desktop/src/main/tempdir.js +++ /dev/null @@ -1,20 +0,0 @@ -// @flow -import path from 'path'; - -export function getSystemTemporaryDirectory() { - switch (process.platform) { - case 'win32': { - const windowsPath = process.env.windir; - if (windowsPath) { - return path.join(windowsPath, 'Temp'); - } else { - throw new Error('Missing windir in environment variables.'); - } - } - case 'darwin': - case 'linux': - return '/tmp'; - default: - throw new Error(`Not implemented for ${process.platform}`); - } -} diff --git a/gui/packages/desktop/src/renderer/app.js b/gui/packages/desktop/src/renderer/app.js index ad886f963c..e0374f05d5 100644 --- a/gui/packages/desktop/src/renderer/app.js +++ b/gui/packages/desktop/src/renderer/app.js @@ -26,7 +26,6 @@ import settingsActions from './redux/settings/actions'; import versionActions from './redux/version/actions'; import daemonActions from './redux/daemon/actions'; -import type { RpcCredentials } from '../common/types'; import type { DaemonRpcProtocol, AccountData, @@ -41,7 +40,6 @@ export default class AppRenderer { _notificationController = new NotificationController(); _daemonRpc: DaemonRpcProtocol = new DaemonRpc(); _reconnectBackoff = new ReconnectionBackoff(); - _credentials: ?RpcCredentials; _openConnectionObserver: ?DaemonConnectionObserver; _closeConnectionObserver: ?DaemonConnectionObserver; _memoryHistory = createMemoryHistory(); @@ -418,16 +416,7 @@ export default class AppRenderer { } async _connectToDaemon(): Promise<void> { - let credentials; - try { - credentials = await this._requestCredentials(); - } catch (error) { - log.error(`Cannot request the RPC credentials: ${error.message}`); - return; - } - - this._credentials = credentials; - this._daemonRpc.connect(credentials.connectionString); + this._daemonRpc.connect({ path: getIpcPath() }); } async _onOpenConnection() { @@ -437,17 +426,6 @@ export default class AppRenderer { // reset the reconnect backoff when connection established. this._reconnectBackoff.reset(); - // authenticate once connected - const credentials = this._credentials; - try { - if (!credentials) { - throw new Error('Credentials cannot be unset after connection is established.'); - } - await this._authenticate(credentials.sharedSecret); - } catch (error) { - log.error(`Cannot authenticate: ${error.message}`); - } - // attempt to restore the session try { await this._restoreSession(); @@ -508,15 +486,6 @@ export default class AppRenderer { } } - _requestCredentials(): Promise<RpcCredentials> { - return new Promise((resolve) => { - ipcRenderer.once('daemon-connection-ready', (_event, credentials: RpcCredentials) => { - resolve(credentials); - }); - ipcRenderer.send('discover-daemon-connection'); - }); - } - async _subscribeStateListener() { await this._daemonRpc.subscribeStateListener((newState, error) => { if (error) { @@ -655,3 +624,11 @@ class AccountDataState { } } } + +const getIpcPath = (): string => { + if (process.platform === 'win32') { + return '//./pipe/Mullvad VPN'; + } else { + return '/var/run/mullvad-vpn'; + } +}; diff --git a/gui/packages/desktop/src/renderer/lib/daemon-rpc.js b/gui/packages/desktop/src/renderer/lib/daemon-rpc.js index 3f3d3af9c1..c403e3fa92 100644 --- a/gui/packages/desktop/src/renderer/lib/daemon-rpc.js +++ b/gui/packages/desktop/src/renderer/lib/daemon-rpc.js @@ -1,9 +1,10 @@ // @flow -import JsonRpcTransport, { +import JsonRpcClient, { RemoteError as JsonRpcRemoteError, TimeOutError as JsonRpcTimeOutError, -} from './jsonrpc-transport'; + SocketTransport, +} from './jsonrpc-client'; import { CommunicationError, InvalidAccountError, NoDaemonError } from '../errors'; import { @@ -222,7 +223,7 @@ const AppVersionInfoSchema = object({ }); export interface DaemonRpcProtocol { - connect(string): void; + connect({ path: string }): void; disconnect(): void; getAccountData(AccountToken): Promise<AccountData>; getRelayLocations(): Promise<RelayList>; @@ -268,14 +269,14 @@ export type ConnectionObserver = { }; export class DaemonRpc implements DaemonRpcProtocol { - _transport = new JsonRpcTransport(); + _transport = new JsonRpcClient(new SocketTransport()); async authenticate(sharedSecret: string): Promise<void> { await this._transport.send('auth', sharedSecret); } - connect(connectionString: string) { - this._transport.connect(connectionString); + connect(connectionParams: { path: string }) { + this._transport.connect(connectionParams); } disconnect() { diff --git a/gui/packages/desktop/src/renderer/lib/jsonrpc-transport.js b/gui/packages/desktop/src/renderer/lib/jsonrpc-client.js index e5c81e6b0f..cf779c83be 100644 --- a/gui/packages/desktop/src/renderer/lib/jsonrpc-transport.js +++ b/gui/packages/desktop/src/renderer/lib/jsonrpc-client.js @@ -4,6 +4,8 @@ import { EventEmitter } from 'events'; import log from 'electron-log'; import jsonrpc from 'jsonrpc-lite'; import uuid from 'uuid'; +import net from 'net'; +import JSONStream from 'JSONStream'; export type UnansweredRequest = { resolve: (mixed) => void, @@ -90,11 +92,11 @@ export class SubscriptionError extends Error { } } -export class ConnectionError extends Error { +export class WebSocketError extends Error { _code: number; constructor(code: number) { - super(ConnectionError.reason(code)); + super(WebSocketError.reason(code)); this._code = code; } @@ -118,34 +120,39 @@ export class ConnectionError extends Error { } } +export class TransportError extends Error { + constructor(reason: string) { + super(reason); + } +} + const DEFAULT_TIMEOUT_MILLIS = 5000; -export default class JsonRpcTransport extends EventEmitter { +export default class JsonRpcClient<T> extends EventEmitter { _unansweredRequests: Map<string, UnansweredRequest> = new Map(); _subscriptions: Map<string | number, (mixed) => void> = new Map(); - _webSocket: ?WebSocket; - _websocketFactory: (string) => WebSocket; + _transport: Transport<T>; - constructor(websocketFactory: ?(string) => WebSocket) { + constructor(transport: Transport<T>) { super(); - this._websocketFactory = - websocketFactory || ((connectionString) => new WebSocket(connectionString)); + + this._transport = transport; } /// Connect websocket - connect(connectionString: string): Promise<void> { + connect(connectionParams: T): Promise<void> { return new Promise((resolve, reject) => { this.disconnect(); - log.info('Connecting to websocket', connectionString); - - const webSocket = this._websocketFactory(connectionString); + log.info('Connecting to transport with params', connectionParams); // A flag used to determine if Promise was resolved. let isPromiseResolved = false; - webSocket.onopen = () => { - log.info('Websocket is connected'); + const transport = this._transport; + + transport.onOpen = () => { + log.info('Transport is connected'); this.emit('open'); // Resolve the Promise @@ -153,40 +160,30 @@ export default class JsonRpcTransport extends EventEmitter { isPromiseResolved = true; }; - webSocket.onmessage = (event) => { - const data = event.data; - if (typeof data === 'string') { - this._onMessage(data); - } else { - log.error('Got invalid reply from the server', event); - } + transport.onMessage = (obj) => { + this._onMessage(obj); }; - webSocket.onclose = (event) => { - log.info(`The websocket connection closed with code: ${event.code}`); - + transport.onClose = (error: ?Error) => { // Remove all subscriptions since they are connection based this._subscriptions.clear(); - // 1000 is a code used for normal connection closure. - const connectionError = event.code === 1000 ? null : new ConnectionError(event.code); - - this.emit('close', connectionError); + this.emit('close', error); // Prevent rejecting a previously resolved Promise. if (!isPromiseResolved) { - reject(connectionError); + reject(error); } }; + transport.connect(connectionParams); - this._webSocket = webSocket; + this._transport = transport; }); } disconnect() { - if (this._webSocket) { - this._webSocket.close(); - this._webSocket = null; + if (this._transport) { + this._transport.close(); } } @@ -211,8 +208,8 @@ export default class JsonRpcTransport extends EventEmitter { send(action: string, data: mixed, timeout: number = DEFAULT_TIMEOUT_MILLIS): Promise<mixed> { return new Promise((resolve, reject) => { - const webSocket = this._webSocket; - if (!webSocket) { + const transport = this._transport; + if (!transport) { reject(new Error('Websocket is not connected.')); return; } @@ -230,7 +227,7 @@ export default class JsonRpcTransport extends EventEmitter { try { log.silly('Sending message', id, action); - webSocket.send(JSON.stringify(message)); + transport.send(JSON.stringify(message)); } catch (error) { log.error(`Failed sending RPC message "${action}": ${error.message}`); @@ -272,9 +269,14 @@ export default class JsonRpcTransport extends EventEmitter { } } - _onMessage(message: string) { - const result = jsonrpc.parse(message); - const messages = Array.isArray(result) ? result : [result]; + _onMessage(obj: Object) { + let messages = []; + try { + const message = jsonrpc.parseObject(obj); + messages = Array.isArray(message) ? message : [message]; + } catch (error) { + log.error(`Failed to parse JSON-RPC message: ${error} for object`); + } for (const message of messages) { if (message.type === 'notification') { @@ -319,3 +321,128 @@ export default class JsonRpcTransport extends EventEmitter { } } } + +interface Transport<T> { + close(): void; + onOpen: (event: Event) => void; + onMessage: (Object) => void; + onClose: (error: ?Error) => void; + send(message: string): void; + connect(params: T): void; +} + +export class WebsocketTransport implements Transport<string> { + ws: ?WebSocket; + onOpen: (event: Event) => void; + onMessage: (Object) => void; + onClose: (error: ?Error) => void; + + constructor(ws: ?WebSocket) { + this.ws = ws; + this.onOpen = () => {}; + this.onMessage = () => {}; + this.onClose = () => {}; + } + + close() { + if (this.ws) this.ws.close(); + } + + send(msg: string) { + if (this.ws) { + this.ws.send(msg); + } + } + + connect(params: string): void { + if (this.ws) { + this.ws.close(); + } + this.ws = new WebSocket(params); + this.ws.onopen = this.onOpen; + this.ws.onmessage = (event) => { + try { + const data = event.data; + if (typeof data === 'string') { + const msg = JSON.parse(data); + this.onMessage(msg); + } else { + throw event; + } + } catch (error) { + log.error('Got invalid reply from server: ', error); + } + }; + + this.ws.onclose = (event) => { + log.info(`The websocket connection closed with code: ${event.code}`); + const error = event.code === 1000 ? null : new WebSocketError(event.code); + this.onClose(error); + }; + } +} + +// Given the correct parameters, this transport supports named pipes/unix +// domain sockets, and also TCP/UDP sockets +export class SocketTransport implements Transport<{ path: string }> { + connection: ?net.Socket; + onMessage: (message: Object) => void; + onClose: (error: ?Error) => void; + onOpen: (event: Event) => void; + + constructor() { + this.connection = null; + this.onMessage = () => {}; + this.onClose = () => {}; + this.onOpen = () => {}; + } + + _connect(options: { path: string }) { + const connection = new net.Socket(); + connection.on('error', (err) => { + this.onClose(err); + this.close(); + }); + + connection.on('connect', (event) => { + this.connection = connection; + this.onOpen(event); + }); + + const jsonStream = JSONStream.parse(); + + connection.pipe(jsonStream); + + jsonStream.on('data', this.onMessage); + + jsonStream.on('error', (err) => { + this.onClose(err); + this.close(); + }); + + connection.connect(options); + } + + close() { + try { + if (this.connection) { + this.connection.end(); + } + } catch (error) { + log.error('failed to close the connection: ', error); + } + this.connection = null; + } + + send(msg: string) { + if (this.connection) { + this.connection.write(msg); + } else { + throw new TransportError('Socket not connected'); + } + } + + connect(options: { path: string }): void { + this._connect(options); + } +} diff --git a/gui/packages/desktop/test/jsonrpc-transport.spec.js b/gui/packages/desktop/test/jsonrpc-transport.spec.js index 5e600f2204..ea07e060b8 100644 --- a/gui/packages/desktop/test/jsonrpc-transport.spec.js +++ b/gui/packages/desktop/test/jsonrpc-transport.spec.js @@ -1,15 +1,18 @@ // @flow import jsonrpc from 'jsonrpc-lite'; -import { Server, WebSocket as MockWebSocket } from 'mock-socket'; -import JsonRpcTransport, { TimeOutError } from '../src/renderer/lib/jsonrpc-transport'; +import { Server } from 'mock-socket'; +import JsonRpcClient, { + WebsocketTransport, + TimeOutError, +} from '../src/renderer/lib/jsonrpc-client'; describe('JSON RPC transport', () => { const WEBSOCKET_URL = 'ws://localhost:8080'; - let server: Server, transport: JsonRpcTransport; + let server: Server, transport: JsonRpcClient<string>; beforeEach(() => { server = new Server(WEBSOCKET_URL); - transport = new JsonRpcTransport((url) => new MockWebSocket(url)); + transport = new JsonRpcClient(new WebsocketTransport()); }); afterEach(() => { diff --git a/gui/yarn.lock b/gui/yarn.lock index cded790c63..102a3b710e 100644 --- a/gui/yarn.lock +++ b/gui/yarn.lock @@ -684,6 +684,13 @@ version "2.3.3" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-2.3.3.tgz#7f226d67d654ec9070e755f46daebf014628e9d9" +JSONStream@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.4.tgz#615bb2adb0cd34c8f4c447b5f6512fa1d8f16a2e" + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + abab@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" @@ -4625,6 +4632,10 @@ jsonify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + jsonrpc-lite@^1.2.3: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonrpc-lite/-/jsonrpc-lite-1.3.1.tgz#5c33086071793a0806e6c96e7c1ae92f4460ac50" @@ -7251,7 +7262,7 @@ through2@~0.2.3: readable-stream "~1.1.9" xtend "~2.1.1" -through@2, through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1: +through@2, "through@>=2.2.7 <3", through@^2.3.6, through@^2.3.8, through@~2.3, through@~2.3.1: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" @@ -7728,12 +7739,6 @@ window-size@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" -"windows-security@https://github.com/mullvad/windows-security.git#0.0.5": - version "0.0.5" - resolved "https://github.com/mullvad/windows-security.git#311fcfeb137e9e10b25929b0615f9188e118004f" - dependencies: - node-pre-gyp "^0.10.0" - wordwrap@^1.0.0, wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" diff --git a/mullvad-cli/Cargo.toml b/mullvad-cli/Cargo.toml index 7f9b39d92e..744a0aa2aa 100644 --- a/mullvad-cli/Cargo.toml +++ b/mullvad-cli/Cargo.toml @@ -14,9 +14,11 @@ clap = "2.20" error-chain = "0.12" env_logger = "0.5" serde = "1.0" +futures = "0.1" 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" } diff --git a/mullvad-cli/src/cmds/account.rs b/mullvad-cli/src/cmds/account.rs index 5757aac42d..bab9f525f6 100644 --- a/mullvad-cli/src/cmds/account.rs +++ b/mullvad-cli/src/cmds/account.rs @@ -1,7 +1,6 @@ use clap; -use {Command, Result}; +use {new_rpc_client, Command, Result}; -use mullvad_ipc_client::DaemonRpcClient; use mullvad_types::account::AccountToken; pub struct Account; @@ -48,7 +47,7 @@ impl Command for Account { impl Account { fn set(&self, token: Option<AccountToken>) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; rpc.set_account(token.clone())?; if let Some(token) = token { println!("Mullvad account \"{}\" set", token); @@ -59,7 +58,7 @@ impl Account { } fn get(&self) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; let account_token = rpc.get_account()?; if let Some(account_token) = account_token { println!("Mullvad account: {}", account_token); diff --git a/mullvad-cli/src/cmds/auto_connect.rs b/mullvad-cli/src/cmds/auto_connect.rs index f471fbde56..5061f67fb8 100644 --- a/mullvad-cli/src/cmds/auto_connect.rs +++ b/mullvad-cli/src/cmds/auto_connect.rs @@ -1,8 +1,7 @@ use clap; +use new_rpc_client; use {Command, Result}; -use mullvad_ipc_client::DaemonRpcClient; - pub struct AutoConnect; impl Command for AutoConnect { @@ -42,14 +41,14 @@ impl Command for AutoConnect { impl AutoConnect { fn set(&self, auto_connect: bool) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; rpc.set_auto_connect(auto_connect)?; println!("Changed auto-connect sharing setting"); Ok(()) } fn get(&self) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; let auto_connect = rpc.get_auto_connect()?; println!("Autoconnect: {}", if auto_connect { "on" } else { "off" }); Ok(()) diff --git a/mullvad-cli/src/cmds/connect.rs b/mullvad-cli/src/cmds/connect.rs index b9da45618d..b7cf7cd8c5 100644 --- a/mullvad-cli/src/cmds/connect.rs +++ b/mullvad-cli/src/cmds/connect.rs @@ -1,8 +1,8 @@ use clap; +use new_rpc_client; use Command; use Result; -use mullvad_ipc_client::DaemonRpcClient; pub struct Connect; @@ -17,7 +17,7 @@ impl Command for Connect { } fn run(&self, _matches: &clap::ArgMatches) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; rpc.connect()?; Ok(()) } diff --git a/mullvad-cli/src/cmds/disconnect.rs b/mullvad-cli/src/cmds/disconnect.rs index 70e5699545..99009d705f 100644 --- a/mullvad-cli/src/cmds/disconnect.rs +++ b/mullvad-cli/src/cmds/disconnect.rs @@ -1,8 +1,8 @@ use clap; +use new_rpc_client; use Command; use Result; -use mullvad_ipc_client::DaemonRpcClient; pub struct Disconnect; @@ -17,7 +17,7 @@ impl Command for Disconnect { } fn run(&self, _matches: &clap::ArgMatches) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; rpc.disconnect()?; Ok(()) } diff --git a/mullvad-cli/src/cmds/lan.rs b/mullvad-cli/src/cmds/lan.rs index bd6516f6bf..65754e3425 100644 --- a/mullvad-cli/src/cmds/lan.rs +++ b/mullvad-cli/src/cmds/lan.rs @@ -1,7 +1,5 @@ use clap; -use {Command, Result}; - -use mullvad_ipc_client::DaemonRpcClient; +use {new_rpc_client, Command, Result}; pub struct Lan; @@ -42,14 +40,14 @@ impl Command for Lan { impl Lan { fn set(&self, allow_lan: bool) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; rpc.set_allow_lan(allow_lan)?; println!("Changed local network sharing setting"); Ok(()) } fn get(&self) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; let allow_lan = rpc.get_allow_lan()?; println!( "Local network sharing setting: {}", diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs index 5be05786cc..4de7571307 100644 --- a/mullvad-cli/src/cmds/relay.rs +++ b/mullvad-cli/src/cmds/relay.rs @@ -1,8 +1,7 @@ use clap; use std::str::FromStr; -use {Command, Result, ResultExt}; +use {new_rpc_client, Command, Result, ResultExt}; -use mullvad_ipc_client::DaemonRpcClient; use mullvad_types::relay_constraints::{ Constraint, LocationConstraint, OpenVpnConstraints, RelayConstraintsUpdate, RelaySettingsUpdate, TunnelConstraints, @@ -101,7 +100,7 @@ impl Command for Relay { impl Relay { fn update_constraints(&self, update: RelaySettingsUpdate) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; rpc.update_relay_settings(update)?; println!("Relay constraints updated"); Ok(()) @@ -171,7 +170,7 @@ impl Relay { } fn get(&self) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; let constraints = rpc.get_relay_settings()?; println!("Current constraints: {:#?}", constraints); @@ -179,7 +178,7 @@ impl Relay { } fn list(&self, _matches: &clap::ArgMatches) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; let mut locations = rpc.get_relay_locations()?; locations.countries.sort_by(|c1, c2| c1.name.cmp(&c2.name)); for mut country in locations.countries { diff --git a/mullvad-cli/src/cmds/status.rs b/mullvad-cli/src/cmds/status.rs index 0b6b7f05ef..1a5a2b29e4 100644 --- a/mullvad-cli/src/cmds/status.rs +++ b/mullvad-cli/src/cmds/status.rs @@ -1,4 +1,5 @@ use clap; +use new_rpc_client; use Command; use Result; @@ -31,12 +32,11 @@ impl Command for Status { } fn run(&self, matches: &clap::ArgMatches) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; let state = rpc.get_state()?; print_state(state); print_location(&mut rpc)?; - if matches.subcommand_matches("listen").is_some() { for new_state in rpc.new_state_subscribe()? { print_state(new_state); @@ -46,7 +46,6 @@ impl Command for Status { } } } - Ok(()) } } diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs index a0c86fe1f0..f44a6b8cf5 100644 --- a/mullvad-cli/src/cmds/tunnel.rs +++ b/mullvad-cli/src/cmds/tunnel.rs @@ -1,7 +1,6 @@ use clap; -use {Command, Result}; +use {new_rpc_client, Command, Result}; -use mullvad_ipc_client::DaemonRpcClient; use talpid_types::net::{OpenVpnTunnelOptions, TunnelOptions}; pub struct Tunnel; @@ -79,7 +78,7 @@ impl Tunnel { fn set_openvpn_enable_ipv6_option(args: &clap::ArgMatches) -> Result<()> { let enabled = args.value_of("enable").unwrap() == "on"; - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; rpc.set_openvpn_enable_ipv6(enabled)?; println!("enable_ipv6 parameter updated"); Ok(()) @@ -93,14 +92,14 @@ impl Tunnel { Some(mssfix_str.parse()?) }; - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; rpc.set_openvpn_mssfix(mssfix)?; println!("mssfix parameter updated"); Ok(()) } fn get_tunnel_options() -> Result<TunnelOptions> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; Ok(rpc.get_tunnel_options()?) } diff --git a/mullvad-cli/src/cmds/version.rs b/mullvad-cli/src/cmds/version.rs index c3adca3040..2277e57d68 100644 --- a/mullvad-cli/src/cmds/version.rs +++ b/mullvad-cli/src/cmds/version.rs @@ -1,7 +1,5 @@ use clap; -use {Command, Result}; - -use mullvad_ipc_client::DaemonRpcClient; +use {new_rpc_client, Command, Result}; pub struct Version; @@ -16,7 +14,7 @@ impl Command for Version { } fn run(&self, _: &clap::ArgMatches) -> Result<()> { - let mut rpc = DaemonRpcClient::new()?; + let mut rpc = new_rpc_client()?; let current_version = rpc.get_current_version()?; println!("Current version: {}", current_version); let version_info = rpc.get_version_info()?; diff --git a/mullvad-cli/src/main.rs b/mullvad-cli/src/main.rs index 2c551c5622..0cf9e9307a 100644 --- a/mullvad-cli/src/main.rs +++ b/mullvad-cli/src/main.rs @@ -9,9 +9,11 @@ #[macro_use] extern crate clap; extern crate env_logger; +extern crate futures; #[macro_use] extern crate error_chain; extern crate mullvad_ipc_client; +extern crate mullvad_paths; extern crate mullvad_types; extern crate serde; extern crate talpid_types; @@ -19,6 +21,7 @@ extern crate talpid_types; mod cmds; +use mullvad_ipc_client::{new_standalone_ipc_client, DaemonRpcClient}; use std::io; error_chain! { @@ -28,10 +31,14 @@ error_chain! { } links { - RpcError(mullvad_ipc_client::Error, mullvad_ipc_client::ErrorKind); + RpcClientError(mullvad_ipc_client::Error, mullvad_ipc_client::ErrorKind); } } +pub fn new_rpc_client() -> Result<DaemonRpcClient> { + new_standalone_ipc_client(&mullvad_paths::get_rpc_socket_path()).map_err(|e| Error::from(e)) +} + quick_main!(run); fn run() -> Result<()> { diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml index bd6add0daf..b6cd7a9884 100644 --- a/mullvad-daemon/Cargo.toml +++ b/mullvad-daemon/Cargo.toml @@ -15,10 +15,10 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" log = "0.4" log-panics = "2.0.0" -jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc", tag = "v8.0.1" } -jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc", tag = "v8.0.1" } -jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc", tag = "v8.0.1" } -jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc", tag = "v8.0.1" } +jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc", branch = "master" } +jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc", branch = "master" } +jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc", branch = "master" } +jsonrpc-ipc-server = { git = "https://github.com/paritytech/jsonrpc", branch = "master" } uuid = { version = "0.6", features = ["v4"] } lazy_static = "1.0" rand = "0.5" diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs index 848207fbb1..373aca1715 100644 --- a/mullvad-daemon/src/main.rs +++ b/mullvad-daemon/src/main.rs @@ -25,8 +25,8 @@ extern crate serde_json; extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_macros; +extern crate jsonrpc_ipc_server; extern crate jsonrpc_pubsub; -extern crate jsonrpc_ws_server; extern crate rand; extern crate tokio_core; extern crate tokio_timer; @@ -50,7 +50,6 @@ mod geoip; mod logging; mod management_interface; mod relays; -mod rpc_address_file; mod rpc_uniqueness_check; mod settings; mod shutdown; @@ -126,7 +125,7 @@ pub enum DaemonEvent { /// An event coming from the JSONRPC-2.0 management interface. ManagementInterfaceEvent(ManagementCommand), /// Triggered if the server hosting the JSONRPC-2.0 management interface dies unexpectedly. - ManagementInterfaceExited(talpid_ipc::Result<()>), + ManagementInterfaceExited, /// Daemon shutdown triggered by a signal, ctrl-c or similar. TriggerShutdown, } @@ -286,18 +285,13 @@ impl Daemon { event_tx: IntoSender<ManagementCommand, DaemonEvent>, cache_dir: PathBuf, ) -> Result<ManagementInterfaceServer> { - let shared_secret = uuid::Uuid::new_v4().to_string(); - - let server = ManagementInterfaceServer::start(event_tx, shared_secret.clone(), cache_dir) + let server = ManagementInterfaceServer::start(event_tx, cache_dir) .chain_err(|| ErrorKind::ManagementInterfaceError("Failed to start server"))?; info!( "Mullvad management interface listening on {}", - server.address() + server.socket_path() ); - rpc_address_file::write(server.address(), &shared_secret).chain_err(|| { - ErrorKind::ManagementInterfaceError("Failed to write RPC connection info to file") - })?; Ok(server) } @@ -306,9 +300,9 @@ impl Daemon { exit_tx: mpsc::Sender<DaemonEvent>, ) { thread::spawn(move || { - let result = server.wait(); + server.wait(); error!("Mullvad management interface shut down"); - let _ = exit_tx.send(DaemonEvent::ManagementInterfaceExited(result)); + let _ = exit_tx.send(DaemonEvent::ManagementInterfaceExited); }); } @@ -333,7 +327,7 @@ impl Daemon { match event { TunnelStateTransition(transition) => self.handle_tunnel_state_transition(transition), ManagementInterfaceEvent(event) => self.handle_management_interface_event(event), - ManagementInterfaceExited(result) => self.handle_management_interface_exited(result), + ManagementInterfaceExited => self.handle_management_interface_exited(), TriggerShutdown => self.handle_trigger_shutdown_event(), } } @@ -599,12 +593,8 @@ impl Daemon { } } - fn handle_management_interface_exited(&self, result: talpid_ipc::Result<()>) -> Result<()> { - let error = ErrorKind::ManagementInterfaceError("Server exited unexpectedly"); - match result { - Ok(()) => Err(error.into()), - Err(e) => Err(e).chain_err(|| error), - } + fn handle_management_interface_exited(&self) -> Result<()> { + Err(ErrorKind::ManagementInterfaceError("Server exited unexpectedly").into()) } fn handle_trigger_shutdown_event(&mut self) -> Result<()> { @@ -721,10 +711,12 @@ impl DaemonShutdownHandle { impl Drop for Daemon { fn drop(self: &mut Daemon) { - if let Err(e) = - rpc_address_file::remove().chain_err(|| "Unable to clean up rpc address file") + #[cfg(unix)] { - error!("{}", e.display_chain()); + use std::fs; + if let Err(e) = fs::remove_file(mullvad_paths::get_rpc_socket_path()) { + error!("Failed to remove RPC socket: {}", e); + } } } } diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 3f2d920180..4d7a509018 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -4,13 +4,14 @@ use error_chain::ChainedError; use jsonrpc_core::futures::sync::oneshot::Sender as OneshotSender; use jsonrpc_core::futures::{future, sync, Future}; use jsonrpc_core::{Error, ErrorCode, MetaIoHandler, Metadata}; +use jsonrpc_ipc_server; use jsonrpc_macros::pubsub; use jsonrpc_pubsub::{PubSubHandler, PubSubMetadata, Session, SubscriptionId}; -use jsonrpc_ws_server; use mullvad_rpc; use mullvad_types::account::{AccountData, AccountToken}; use mullvad_types::location::GeoIpLocation; +use mullvad_paths; use mullvad_types::relay_constraints::{RelaySettings, RelaySettingsUpdate}; use mullvad_types::relay_list::RelayList; use mullvad_types::states::{DaemonState, TargetState}; @@ -21,7 +22,6 @@ use serde; use std::collections::hash_map::Entry; use std::collections::HashMap; use std::path::PathBuf; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, RwLock}; use talpid_core::mpsc::IntoSender; @@ -40,10 +40,6 @@ build_rpc_trait! { pub trait ManagementInterfaceApi { type Metadata; - /// Authenticate the client towards this daemon instance. This method must be called once - /// before any other call based on the same connection will work. - #[rpc(meta, name = "auth")] - fn auth(&self, Self::Metadata, String) -> BoxFuture<(), Error>; /// Fetches and returns metadata about an account. Returns an error on non-existing /// accounts. @@ -225,27 +221,31 @@ pub struct ManagementInterfaceServer { impl ManagementInterfaceServer { pub fn start<T>( tunnel_tx: IntoSender<ManagementCommand, T>, - shared_secret: String, cache_dir: PathBuf, ) -> talpid_ipc::Result<Self> where T: From<ManagementCommand> + 'static + Send, { - let rpc = ManagementInterface::new(tunnel_tx, shared_secret, cache_dir); + let rpc = ManagementInterface::new(tunnel_tx, cache_dir); let subscriptions = rpc.subscriptions.clone(); let mut io = PubSubHandler::default(); io.extend_with(rpc.to_delegate()); let meta_io: MetaIoHandler<Meta> = io.into(); - let server = talpid_ipc::IpcServer::start_with_metadata(meta_io, meta_extractor)?; + let path = mullvad_paths::get_rpc_socket_path(); + let server = talpid_ipc::IpcServer::start_with_metadata( + meta_io, + meta_extractor, + path.to_string_lossy().to_string(), + )?; Ok(ManagementInterfaceServer { server, subscriptions, }) } - pub fn address(&self) -> &str { - self.server.address() + pub fn socket_path(&self) -> &str { + self.server.path() } pub fn event_broadcaster(&self) -> EventBroadcaster { @@ -256,12 +256,11 @@ impl ManagementInterfaceServer { /// Consumes the server and waits for it to finish. Returns an error if the server exited /// due to an error. - pub fn wait(self) -> talpid_ipc::Result<()> { + pub fn wait(self) { self.server.wait() } } - /// A handle that allows broadcasting messages to all subscribers of the management interface. pub struct EventBroadcaster { subscriptions: Arc<ActiveSubscriptions>, @@ -300,20 +299,14 @@ impl EventBroadcaster { struct ManagementInterface<T: From<ManagementCommand> + 'static + Send> { subscriptions: Arc<ActiveSubscriptions>, tx: Mutex<IntoSender<ManagementCommand, T>>, - shared_secret: String, cache_dir: PathBuf, } impl<T: From<ManagementCommand> + 'static + Send> ManagementInterface<T> { - pub fn new( - tx: IntoSender<ManagementCommand, T>, - shared_secret: String, - cache_dir: PathBuf, - ) -> Self { + pub fn new(tx: IntoSender<ManagementCommand, T>, cache_dir: PathBuf) -> Self { ManagementInterface { subscriptions: Default::default(), tx: Mutex::new(tx), - shared_secret, cache_dir, } } @@ -379,16 +372,6 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterface<T> { } } - fn check_auth(&self, meta: &Meta) -> Result<(), Error> { - if meta.authenticated.load(Ordering::SeqCst) { - trace!("auth success"); - Ok(()) - } else { - trace!("auth failed"); - Err(Error::invalid_request()) - } - } - fn load_history(&self) -> Result<AccountHistory, AccountHistoryError> { let mut account_history = AccountHistory::new(&self.cache_dir); account_history.load()?; @@ -396,41 +379,17 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterface<T> { } } -/// Evaluates a Result and early returns an error. -/// If it is `Ok(val)`, evaluates to `val`. -/// If it is `Err(e)` it early returns `Box<Future>` where the future will result in `e`. -macro_rules! try_future { - ($result:expr) => { - match $result { - ::std::result::Result::Ok(val) => val, - ::std::result::Result::Err(e) => return Box::new(future::err(e)), - } - }; -} - impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi for ManagementInterface<T> { type Metadata = Meta; - fn auth(&self, meta: Self::Metadata, shared_secret: String) -> BoxFuture<(), Error> { - let authenticated = shared_secret == self.shared_secret; - meta.authenticated.store(authenticated, Ordering::SeqCst); - debug!("authenticated: {}", authenticated); - if authenticated { - Box::new(future::ok(())) - } else { - Box::new(future::err(Error::internal_error())) - } - } - fn get_account_data( &self, - meta: Self::Metadata, + _: Self::Metadata, account_token: AccountToken, ) -> BoxFuture<AccountData, Error> { trace!("get_account_data"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::GetAccountData(tx, account_token)) @@ -447,9 +406,8 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn get_relay_locations(&self, meta: Self::Metadata) -> BoxFuture<RelayList, Error> { + fn get_relay_locations(&self, _: Self::Metadata) -> BoxFuture<RelayList, Error> { trace!("get_relay_locations"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::GetRelayLocations(tx)) @@ -459,11 +417,10 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi fn set_account( &self, - meta: Self::Metadata, + _: Self::Metadata, account_token: Option<AccountToken>, ) -> BoxFuture<(), Error> { trace!("set_account"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::SetAccount(tx, account_token.clone())) @@ -483,9 +440,8 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn get_account(&self, meta: Self::Metadata) -> BoxFuture<Option<AccountToken>, Error> { + fn get_account(&self, _: Self::Metadata) -> BoxFuture<Option<AccountToken>, Error> { trace!("get_account"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::GetAccount(tx)) @@ -495,11 +451,10 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi fn update_relay_settings( &self, - meta: Self::Metadata, + _: Self::Metadata, constraints_update: RelaySettingsUpdate, ) -> BoxFuture<(), Error> { trace!("update_relay_settings"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let message = ManagementCommand::UpdateRelaySettings(tx, constraints_update); @@ -509,9 +464,8 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn get_relay_settings(&self, meta: Self::Metadata) -> BoxFuture<RelaySettings, Error> { + fn get_relay_settings(&self, _: Self::Metadata) -> BoxFuture<RelaySettings, Error> { trace!("get_relay_settings"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::GetRelaySettings(tx)) @@ -519,9 +473,8 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn set_allow_lan(&self, meta: Self::Metadata, allow_lan: bool) -> BoxFuture<(), Error> { + fn set_allow_lan(&self, _: Self::Metadata, allow_lan: bool) -> BoxFuture<(), Error> { trace!("set_allow_lan"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::SetAllowLan(tx, allow_lan)) @@ -529,9 +482,8 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn get_allow_lan(&self, meta: Self::Metadata) -> BoxFuture<bool, Error> { + fn get_allow_lan(&self, _: Self::Metadata) -> BoxFuture<bool, Error> { trace!("get_allow_lan"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::GetAllowLan(tx)) @@ -539,9 +491,8 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn set_auto_connect(&self, meta: Self::Metadata, auto_connect: bool) -> BoxFuture<(), Error> { + fn set_auto_connect(&self, _: Self::Metadata, auto_connect: bool) -> BoxFuture<(), Error> { trace!("set_auto_connect"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::SetAutoConnect(tx, auto_connect)) @@ -549,9 +500,8 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn get_auto_connect(&self, meta: Self::Metadata) -> BoxFuture<bool, Error> { + fn get_auto_connect(&self, _: Self::Metadata) -> BoxFuture<bool, Error> { trace!("get_auto_connect"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::GetAutoConnect(tx)) @@ -559,21 +509,18 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn connect(&self, meta: Self::Metadata) -> BoxFuture<(), Error> { + fn connect(&self, _: Self::Metadata) -> BoxFuture<(), Error> { trace!("connect"); - try_future!(self.check_auth(&meta)); self.send_command_to_daemon(ManagementCommand::SetTargetState(TargetState::Secured)) } - fn disconnect(&self, meta: Self::Metadata) -> BoxFuture<(), Error> { + fn disconnect(&self, _: Self::Metadata) -> BoxFuture<(), Error> { trace!("disconnect"); - try_future!(self.check_auth(&meta)); self.send_command_to_daemon(ManagementCommand::SetTargetState(TargetState::Unsecured)) } - fn get_state(&self, meta: Self::Metadata) -> BoxFuture<DaemonState, Error> { + fn get_state(&self, _: Self::Metadata) -> BoxFuture<DaemonState, Error> { trace!("get_state"); - try_future!(self.check_auth(&meta)); let (state_tx, state_rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::GetState(state_tx)) @@ -581,9 +528,8 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn get_current_location(&self, meta: Self::Metadata) -> BoxFuture<GeoIpLocation, Error> { + fn get_current_location(&self, _: Self::Metadata) -> BoxFuture<GeoIpLocation, Error> { trace!("get_current_location"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::GetCurrentLocation(tx)) @@ -591,15 +537,13 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn shutdown(&self, meta: Self::Metadata) -> BoxFuture<(), Error> { + fn shutdown(&self, _: Self::Metadata) -> BoxFuture<(), Error> { trace!("shutdown"); - try_future!(self.check_auth(&meta)); self.send_command_to_daemon(ManagementCommand::Shutdown) } - fn get_account_history(&self, meta: Self::Metadata) -> BoxFuture<Vec<AccountToken>, Error> { + fn get_account_history(&self, _: Self::Metadata) -> BoxFuture<Vec<AccountToken>, Error> { trace!("get_account_history"); - try_future!(self.check_auth(&meta)); Box::new(future::result( self.load_history() .map(|history| history.get_accounts().to_vec()) @@ -612,11 +556,10 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi fn remove_account_from_history( &self, - meta: Self::Metadata, + _: Self::Metadata, account_token: AccountToken, ) -> BoxFuture<(), Error> { trace!("remove_account_from_history"); - try_future!(self.check_auth(&meta)); Box::new(future::result( self.load_history() .and_then(|mut history| history.remove_account_token(account_token)) @@ -630,13 +573,8 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi )) } - fn set_openvpn_mssfix( - &self, - meta: Self::Metadata, - mssfix: Option<u16>, - ) -> BoxFuture<(), Error> { + fn set_openvpn_mssfix(&self, _: Self::Metadata, mssfix: Option<u16>) -> BoxFuture<(), Error> { trace!("set_openvpn_mssfix"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::SetOpenVpnMssfix(tx, mssfix)) @@ -647,11 +585,10 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi fn set_openvpn_enable_ipv6( &self, - meta: Self::Metadata, + _: Self::Metadata, enable_ipv6: bool, ) -> BoxFuture<(), Error> { trace!("set_openvpn_enable_ipv6"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::SetOpenVpnEnableIpv6(tx, enable_ipv6)) @@ -660,9 +597,8 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn get_tunnel_options(&self, meta: Self::Metadata) -> BoxFuture<TunnelOptions, Error> { + fn get_tunnel_options(&self, _: Self::Metadata) -> BoxFuture<TunnelOptions, Error> { trace!("get_tunnel_options"); - try_future!(self.check_auth(&meta)); let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::GetTunnelOptions(tx)) @@ -670,8 +606,7 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn get_current_version(&self, meta: Self::Metadata) -> BoxFuture<String, Error> { - try_future!(self.check_auth(&meta)); + fn get_current_version(&self, _: Self::Metadata) -> BoxFuture<String, Error> { let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::GetCurrentVersion(tx)) @@ -680,8 +615,7 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn get_version_info(&self, meta: Self::Metadata) -> BoxFuture<version::AppVersionInfo, Error> { - try_future!(self.check_auth(&meta)); + fn get_version_info(&self, _: Self::Metadata) -> BoxFuture<version::AppVersionInfo, Error> { let (tx, rx) = sync::oneshot::channel(); let future = self .send_command_to_daemon(ManagementCommand::GetVersionInfo(tx)) @@ -699,15 +633,8 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn new_state_subscribe( - &self, - meta: Self::Metadata, - subscriber: pubsub::Subscriber<DaemonState>, - ) { + fn new_state_subscribe(&self, _: Self::Metadata, subscriber: pubsub::Subscriber<DaemonState>) { trace!("new_state_subscribe"); - if self.check_auth(&meta).is_err() { - return; - } Self::subscribe(subscriber, &self.subscriptions.new_state_subscriptions); } @@ -716,11 +643,8 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Self::unsubscribe(id, &self.subscriptions.new_state_subscriptions) } - fn error_subscribe(&self, meta: Self::Metadata, subscriber: pubsub::Subscriber<Vec<String>>) { + fn error_subscribe(&self, _: Self::Metadata, subscriber: pubsub::Subscriber<Vec<String>>) { trace!("error_subscribe"); - if self.check_auth(&meta).is_err() { - return; - } Self::subscribe(subscriber, &self.subscriptions.error_subscriptions); } @@ -737,7 +661,6 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi #[derive(Clone, Debug, Default)] pub struct Meta { session: Option<Arc<Session>>, - authenticated: Arc<AtomicBool>, } /// Make the `Meta` type possible to use as jsonrpc metadata type. @@ -751,9 +674,8 @@ impl PubSubMetadata for Meta { } /// Metadata extractor function for `Meta`. -fn meta_extractor(context: &jsonrpc_ws_server::RequestContext) -> Meta { +fn meta_extractor(context: &jsonrpc_ipc_server::RequestContext) -> Meta { Meta { - session: Some(Arc::new(Session::new(context.sender()))), - authenticated: Arc::new(AtomicBool::new(false)), + session: Some(Arc::new(Session::new(context.sender.clone()))), } } diff --git a/mullvad-daemon/src/rpc_address_file.rs b/mullvad-daemon/src/rpc_address_file.rs deleted file mode 100644 index d0df2c1919..0000000000 --- a/mullvad-daemon/src/rpc_address_file.rs +++ /dev/null @@ -1,88 +0,0 @@ -use std::fs::{self, File, OpenOptions}; -use std::io::{self, Write}; -use std::path::{Path, PathBuf}; - -use mullvad_paths; - -#[cfg(windows)] -extern crate winapi; - -error_chain! { - errors { - UnknownFilePath { - description("Failed to find path for RPC connection info file") - } - CreateDirFailed(path: PathBuf) { - description("Failed to create directory for RPC connection info file") - display( - "Failed to create directory for RPC connection info file: {}", - path.display(), - ) - } - WriteFailed(path: PathBuf) { - description("Failed to write RPC connection info to file") - display("Failed to write RPC connection info to {}", path.display()) - } - RemoveFailed(path: PathBuf) { - description("Failed to remove file") - display("Failed to remove {}", path.display()) - } - } -} - -/// Writes down the RPC connection info to the RPC file. -pub fn write(rpc_address: &str, shared_secret: &str) -> Result<()> { - // Avoids opening an existing file owned by another user and writing sensitive data to it. - remove()?; - - let file_path = - mullvad_paths::get_rpc_address_path().chain_err(|| ErrorKind::UnknownFilePath)?; - - if let Some(parent_dir) = file_path.parent() { - fs::create_dir_all(parent_dir) - .chain_err(|| ErrorKind::CreateDirFailed(parent_dir.to_owned()))?; - } - - open_file(&file_path) - .and_then(|mut file| write!(file, "{}\n{}\n", rpc_address, shared_secret)) - .chain_err(|| ErrorKind::WriteFailed(file_path.clone()))?; - - debug!("Wrote RPC connection info to {}", file_path.display()); - Ok(()) -} - -/// Removes the RPC file, if it exists. -pub fn remove() -> Result<()> { - let file_path = - mullvad_paths::get_rpc_address_path().chain_err(|| ErrorKind::UnknownFilePath)?; - - if let Err(error) = fs::remove_file(&file_path) { - if error.kind() == io::ErrorKind::NotFound { - // No previously existing file - Ok(()) - } else { - Err(error).chain_err(|| ErrorKind::RemoveFailed(file_path)) - } - } else { - Ok(()) - } -} - -fn open_file(path: &Path) -> io::Result<File> { - let mut open_options = OpenOptions::new(); - open_options.write(true).truncate(true).create(true); - - #[cfg(windows)] - { - use std::os::windows::fs::OpenOptionsExt; - open_options.share_mode(winapi::um::winnt::FILE_SHARE_READ); - } - - let file = open_options.open(path)?; - #[cfg(unix)] - { - use std::os::unix::fs::PermissionsExt; - file.set_permissions(PermissionsExt::from_mode(0o644))?; - } - Ok(file) -} diff --git a/mullvad-daemon/src/rpc_uniqueness_check.rs b/mullvad-daemon/src/rpc_uniqueness_check.rs index 76130f72e8..37bf8a3f60 100644 --- a/mullvad-daemon/src/rpc_uniqueness_check.rs +++ b/mullvad-daemon/src/rpc_uniqueness_check.rs @@ -1,7 +1,8 @@ use error_chain::ChainedError; use log::Level; -use mullvad_ipc_client::DaemonRpcClient; +use mullvad_ipc_client::new_standalone_ipc_client; +use mullvad_paths; /// Checks if there is another instance of the daemon running. @@ -9,7 +10,7 @@ use mullvad_ipc_client::DaemonRpcClient; /// 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 DaemonRpcClient::new() { + match new_standalone_ipc_client(&mullvad_paths::get_rpc_socket_path()) { Ok(_) => true, Err(error) => { let msg = diff --git a/mullvad-ipc-client/Cargo.toml b/mullvad-ipc-client/Cargo.toml index 7a7513eeab..443cffa885 100644 --- a/mullvad-ipc-client/Cargo.toml +++ b/mullvad-ipc-client/Cargo.toml @@ -12,6 +12,13 @@ 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", branch = "master" } +jsonrpc-client-ipc = { git = "https://github.com/mullvad/jsonrpc-client-rs", branch = "master" } +tokio-core = "0.1" +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 index 6c1734a4c9..62a829b4d0 100644 --- a/mullvad-ipc-client/src/lib.rs +++ b/mullvad-ipc-client/src/lib.rs @@ -1,15 +1,25 @@ #[macro_use] +extern crate log; + +#[macro_use] extern crate error_chain; + +extern crate jsonrpc_client_core; +extern crate jsonrpc_client_ipc; + +extern crate futures; extern crate mullvad_paths; extern crate mullvad_types; extern crate serde; extern crate talpid_ipc; extern crate talpid_types; +extern crate tokio_core; +extern crate tokio_timer; -use std::fs::File; -use std::io::{BufRead, BufReader}; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::sync::mpsc; +use std::thread; +use std::time::Duration; use mullvad_types::account::{AccountData, AccountToken}; use mullvad_types::location::GeoIpLocation; @@ -18,10 +28,14 @@ use mullvad_types::relay_list::RelayList; use mullvad_types::states::DaemonState; use mullvad_types::version::AppVersionInfo; use serde::{Deserialize, Serialize}; -use talpid_ipc::WsIpcClient; use talpid_types::net::TunnelOptions; -use platform_specific::ensure_written_by_admin; +use futures::stream::{self, Stream}; +use futures::sync::oneshot; +use jsonrpc_client_core::{Client, ClientHandle, Future}; +pub use jsonrpc_client_core::{Error as RpcError, ErrorKind as RpcErrorKind}; +use jsonrpc_client_ipc::IpcTransport; +use tokio_core::reactor; error_chain! { errors { @@ -29,32 +43,6 @@ error_chain! { description("Failed to authenticate the connection with the daemon") } - EmptyRpcFile(path: PathBuf) { - description("RPC connection file is empty") - display("RPC connection file \"{}\" is empty", path.display()) - } - - InsecureRpcFile(path: PathBuf) { - description( - "RPC connection file is insecure because it might not have been written by an \ - administrator user" - ) - display( - "RPC connection file \"{}\" is insecure because it might not have been written by \ - an administrator user", path.display() - ) - } - - MissingRpcCredentials(path: PathBuf) { - description("no credentials found in RPC connection file") - display("no credentials found in RPC connection file {}", path.display()) - } - - ReadRpcFileError(path: PathBuf) { - description("Failed to read RPC connection information") - display("Failed to read RPC connection information from {}", path.display()) - } - RpcCallError(method: String) { description("Failed to call RPC method") display("Failed to call RPC method \"{}\"", method) @@ -69,6 +57,14 @@ error_chain! { description("Failed to start RPC client") display("Failed to start RPC client to {}", address) } + + TokioError { + description("Failed to setup a standalone event loop") + } + + TransportError { + description("Failed to setup a transport") + } } links { UnknownRpcAddressPath(mullvad_paths::Error, mullvad_paths::ErrorKind); @@ -77,66 +73,60 @@ error_chain! { static NO_ARGS: [u8; 0] = []; -pub struct DaemonRpcClient { - rpc_client: WsIpcClient, -} - -impl DaemonRpcClient { - pub fn new() -> Result<Self> { - Self::with_rpc_address_file(mullvad_paths::get_rpc_address_path()?) - } - - pub fn with_rpc_address_file<P: AsRef<Path>>(file_path: P) -> Result<Self> { - ensure_written_by_admin(&file_path)?; - - let (address, credentials) = Self::read_rpc_file(file_path)?; - - Self::with_address_and_credentials(address, credentials) - } - - pub fn with_insecure_rpc_address_file<P: AsRef<Path>>(file_path: P) -> Result<Self> { - let (address, credentials) = Self::read_rpc_file(file_path)?; - - Self::with_address_and_credentials(address, credentials) - } - - fn with_address_and_credentials(address: String, credentials: String) -> Result<Self> { - let rpc_client = - WsIpcClient::connect(&address).chain_err(|| ErrorKind::StartRpcClient(address))?; - let mut instance = DaemonRpcClient { rpc_client }; +pub fn new_standalone_transport< + F: Send + 'static + FnOnce(String, reactor::Handle) -> Result<T>, + T: jsonrpc_client_core::Transport, +>( + rpc_path: String, + transport_func: F, +) -> 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((mut core, client, client_handle)) => { + tx.send(Ok(client_handle)) + .expect("Failed to send client handle"); + if let Err(e) = core.run(client) { + error!("JSON-RPC client failed: {}", e.description()); + } + } + }); - instance - .auth(&credentials) - .chain_err(|| ErrorKind::AuthenticationError)?; + rx.wait() + .chain_err(|| ErrorKind::TransportError)? + .map(|client_handle| DaemonRpcClient::new(client_handle)) +} - Ok(instance) - } +pub fn new_standalone_ipc_client(path: &impl AsRef<Path>) -> Result<DaemonRpcClient> { + let path = path.as_ref().to_string_lossy().to_string(); - fn read_rpc_file<P>(file_path: P) -> Result<(String, String)> - where - P: AsRef<Path>, - { - let file_path = file_path.as_ref(); - let rpc_file = File::open(file_path) - .chain_err(|| ErrorKind::ReadRpcFileError(file_path.to_owned()))?; + new_standalone_transport(path, |path, handle| { + IpcTransport::new(&path, &handle).chain_err(|| ErrorKind::TransportError) + }) +} - let reader = BufReader::new(rpc_file); - let mut lines = reader.lines(); +fn spawn_transport< + F: Send + FnOnce(String, reactor::Handle) -> Result<T>, + T: jsonrpc_client_core::Transport, +>( + address: String, + transport_func: F, +) -> Result<(reactor::Core, Client<T>, ClientHandle)> { + let core = reactor::Core::new().chain_err(|| ErrorKind::TokioError)?; + let (client, client_handle) = transport_func(address, core.handle())?.into_client(); + Ok((core, client, client_handle)) +} - let address = lines - .next() - .ok_or_else(|| ErrorKind::EmptyRpcFile(file_path.to_owned()))? - .chain_err(|| ErrorKind::ReadRpcFileError(file_path.to_owned()))?; - let credentials = lines - .next() - .ok_or_else(|| ErrorKind::MissingRpcCredentials(file_path.to_owned()))? - .chain_err(|| ErrorKind::ReadRpcFileError(file_path.to_owned()))?; +pub struct DaemonRpcClient { + rpc_client: jsonrpc_client_core::ClientHandle, +} - Ok((address, credentials)) - } - pub fn auth(&mut self, credentials: &str) -> Result<()> { - self.call("auth", &[credentials]) +impl DaemonRpcClient { + pub fn new(rpc_client: ClientHandle) -> Self { + DaemonRpcClient { rpc_client } } pub fn connect(&mut self) -> Result<()> { @@ -219,139 +209,43 @@ impl DaemonRpcClient { self.call("update_relay_settings", &[update]) } - pub fn call<A, O>(&mut self, method: &str, args: &A) -> Result<O> + pub fn call<A, O>(&mut self, method: &'static str, args: &A) -> Result<O> where - A: Serialize, - O: for<'de> Deserialize<'de>, + A: Serialize + Send + 'static, + O: for<'de> Deserialize<'de> + Send + 'static, { self.rpc_client - .call(method, args) + .call_method(method, args) + .wait() .chain_err(|| ErrorKind::RpcCallError(method.to_owned())) } pub fn new_state_subscribe(&mut self) -> Result<mpsc::Receiver<DaemonState>> { - self.subscribe("new_state") - } - - pub fn subscribe<T>(&mut self, event: &str) -> Result<mpsc::Receiver<T>> - where - T: for<'de> serde::Deserialize<'de> + Send + 'static, - { - let (event_tx, event_rx) = mpsc::channel(); - let subscribe_method = format!("{}_subscribe", event); - let unsubscribe_method = format!("{}_unsubscribe", event); - - self.rpc_client - .subscribe::<T, T>(subscribe_method, unsubscribe_method, event_tx) - .chain_err(|| ErrorKind::RpcSubscribeError(event.to_owned()))?; - - Ok(event_rx) - } -} - -#[cfg(unix)] -mod platform_specific { - use std::os::unix::fs::MetadataExt; - - use super::*; - - pub fn ensure_written_by_admin<P: AsRef<Path>>(path: P) -> Result<()> { - let path = path.as_ref(); - let metadata = path - .metadata() - .chain_err(|| ErrorKind::ReadRpcFileError(path.to_owned()))?; - - let is_owned_by_root = metadata.uid() == 0; - let is_read_only_by_non_owner = (metadata.mode() & 0o022) == 0; - - ensure!( - is_owned_by_root && is_read_only_by_non_owner, - ErrorKind::InsecureRpcFile(path.to_owned()) - ); - - Ok(()) - } -} - -#[cfg(windows)] -mod platform_specific { - extern crate winapi; + let client = self.rpc_client.clone(); + let mut current_state = self.get_state()?; + let first_message = stream::once(Ok(current_state.clone())); - use std::iter::once; - use std::os::windows::ffi::OsStrExt; - use std::ptr; + let (tx, rx) = mpsc::channel(); - use self::winapi::shared::winerror::ERROR_SUCCESS; - use self::winapi::um::accctrl::SE_FILE_OBJECT; - use self::winapi::um::aclapi::GetNamedSecurityInfoW; - use self::winapi::um::securitybaseapi::IsWellKnownSid; - use self::winapi::um::winbase::LocalFree; - use self::winapi::um::winnt::{ - WinBuiltinAdministratorsSid, OWNER_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, PSID, - }; + let polled = tokio_timer::wheel() + .build() + .interval(Duration::from_secs(1)) + .then(move |_| client.call_method("get_state", &NO_ARGS)); - use super::*; - - mod errors { - error_chain! { - errors { - GetSecurityInfoError { - description("Failed to get security information of RPC address file") - } - - OwnerNotAdmin { - description("Owner of RPC address file is not an administrator") - } - } - } - } - use self::errors::{ErrorKind as WinErrorKind, Result as WinResult}; - - pub fn ensure_written_by_admin<P: AsRef<Path>>(file_path: P) -> Result<()> { - let path = file_path.as_ref(); - - ensure_owned_by_admin(&path).chain_err(|| ErrorKind::InsecureRpcFile(path.to_owned()))?; - - Ok(()) - } - - fn ensure_owned_by_admin<P: AsRef<Path>>(path: P) -> WinResult<()> { - let file_path: Vec<u16> = path - .as_ref() - .as_os_str() - .encode_wide() - .chain(once(0)) - .collect(); - - unsafe { - let mut owner_sid: PSID = ptr::null_mut(); - let mut security_descriptor: PSECURITY_DESCRIPTOR = ptr::null_mut(); - - let get_security_info_result = GetNamedSecurityInfoW( - file_path.as_ptr(), - SE_FILE_OBJECT, - OWNER_SECURITY_INFORMATION, - &mut owner_sid, - ptr::null_mut(), - ptr::null_mut(), - ptr::null_mut(), - &mut security_descriptor, - ); - - ensure!( - get_security_info_result == ERROR_SUCCESS, - WinErrorKind::GetSecurityInfoError - ); - - let sid_check_result = IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid); - - if !LocalFree(security_descriptor as *mut _).is_null() { - panic!("Failed to deallocate security descriptor"); - } - - ensure!(sid_check_result != 0, WinErrorKind::OwnerNotAdmin); - - Ok(()) - } + thread::spawn(move || { + let _ = first_message + .chain(polled) + .for_each(move |state| { + if state != current_state { + current_state = state; + if tx.send(state).is_err() { + trace!("can't send new state to subscriber"); + return Err(jsonrpc_client_core::ErrorKind::Shutdown.into()); + }; + } + Ok(()) + }).wait(); + }); + Ok(rx) } } diff --git a/mullvad-paths/src/lib.rs b/mullvad-paths/src/lib.rs index a2f5478d31..d05923c03a 100644 --- a/mullvad-paths/src/lib.rs +++ b/mullvad-paths/src/lib.rs @@ -52,7 +52,7 @@ pub mod resources; pub use resources::get_resource_dir; mod rpc_address; -pub use rpc_address::get_rpc_address_path; +pub use rpc_address::get_rpc_socket_path; mod settings; pub use settings::settings_dir; diff --git a/mullvad-paths/src/rpc_address.rs b/mullvad-paths/src/rpc_address.rs index a97b3e0c36..f71e7a4edd 100644 --- a/mullvad-paths/src/rpc_address.rs +++ b/mullvad-paths/src/rpc_address.rs @@ -1,24 +1,12 @@ -use Result; - use std::env; use std::path::PathBuf; -const RPC_ADDRESS_FILENAME: &str = ".mullvad_rpc_address"; - -pub fn get_rpc_address_path() -> Result<PathBuf> { - match env::var_os("MULLVAD_RPC_ADDRESS_PATH") { - Some(path) => Ok(PathBuf::from(path)), - None => get_default_rpc_address_dir().map(|dir| dir.join(RPC_ADDRESS_FILENAME)), - } -} - -fn get_default_rpc_address_dir() -> Result<PathBuf> { - #[cfg(unix)] - { - Ok(PathBuf::from("/tmp")) - } - #[cfg(windows)] - { - ::get_allusersprofile_dir().map(|dir| dir.join(::PRODUCT_NAME)) +pub fn get_rpc_socket_path() -> PathBuf { + match env::var_os("MULLVAD_RPC_SOCKET_PATH") { + Some(path) => PathBuf::from(path), + #[cfg(unix)] + None => PathBuf::from("/var/run/mullvad-vpn"), + #[cfg(windows)] + None => PathBuf::from("//./pipe/Mullvad VPN"), } } diff --git a/mullvad-tests/Cargo.toml b/mullvad-tests/Cargo.toml index 7e7169c3f4..18f8b959d8 100644 --- a/mullvad-tests/Cargo.toml +++ b/mullvad-tests/Cargo.toml @@ -17,6 +17,10 @@ notify = "4.0" openvpn-plugin = { version = "0.3", features = ["serde"] } talpid-ipc = { path = "../talpid-ipc" } tempfile = "3.0" +jsonrpc-client-core = { git = "https://github.com/mullvad/jsonrpc-client-rs", branch = "master" } +jsonrpc-client-ipc = { git = "https://github.com/mullvad/jsonrpc-client-rs", branch = "master" } +tokio-core = "0.1" +futures = "0.1.23" [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/mullvad-tests/src/lib.rs b/mullvad-tests/src/lib.rs index 0e2f404258..b90aaa3157 100644 --- a/mullvad-tests/src/lib.rs +++ b/mullvad-tests/src/lib.rs @@ -1,5 +1,7 @@ #[macro_use] extern crate duct; +extern crate jsonrpc_client_core; +extern crate jsonrpc_client_ipc; #[cfg(unix)] extern crate libc; extern crate mullvad_ipc_client; @@ -9,6 +11,9 @@ extern crate openvpn_plugin; extern crate talpid_ipc; extern crate tempfile; +extern crate futures; +extern crate tokio_core; + pub mod mock_openvpn; use std::collections::HashMap; @@ -18,12 +23,15 @@ use std::sync::{mpsc, Arc}; use std::time::{Duration, Instant}; use std::{cmp, thread}; -use mullvad_ipc_client::DaemonRpcClient; +use futures::sync::oneshot; +use jsonrpc_client_core::{Future, Transport}; +use jsonrpc_client_ipc::IpcTransport; +use mullvad_ipc_client::{DaemonRpcClient, ResultExt}; use mullvad_paths::resources::API_CA_FILENAME; use notify::{RawEvent, RecommendedWatcher, RecursiveMode, Watcher}; use openvpn_plugin::types::OpenVpnPluginEvent; -use talpid_ipc::WsIpcClient; use tempfile::TempDir; +use tokio_core::reactor::Core; use self::mock_openvpn::MOCK_OPENVPN_ARGS_FILE; use self::platform_specific::*; @@ -261,50 +269,42 @@ fn prepare_relay_list<T: AsRef<Path>>(path: T) { pub struct DaemonRunner { process: Option<duct::Handle>, mock_openvpn_args_file: PathBuf, - rpc_address_file: PathBuf, + rpc_socket_path: PathBuf, _temp_dir: TempDir, } impl DaemonRunner { pub fn spawn_with_real_rpc_address_file() -> Self { - Self::spawn_internal(false) + Self::spawn_internal() } pub fn spawn() -> Self { - Self::spawn_internal(true) + Self::spawn_internal() } - fn spawn_internal(mock_rpc_address_file: bool) -> Self { + fn spawn_internal() -> Self { let (temp_dir, cache_dir, resource_dir, settings_dir) = prepare_test_dirs(); let mock_openvpn_args_file = temp_dir.path().join(MOCK_OPENVPN_ARGS_FILE); - let rpc_address_file = if mock_rpc_address_file { - temp_dir.path().join(".mullvad_rpc_address") - } else { - mullvad_paths::get_rpc_address_path().expect("Failed to build RPC connection file path") - }; - let mut expression = cmd!(DAEMON_EXECUTABLE_PATH, "-v", "--disable-log-to-file") + let rpc_socket_path = temp_dir.path().join("rpc_socket"); + + let expression = cmd!(DAEMON_EXECUTABLE_PATH, "-v", "--disable-log-to-file") .dir("..") .env("MULLVAD_CACHE_DIR", cache_dir) + .env("MULLVAD_RPC_SOCKET_PATH", rpc_socket_path.clone()) .env("MULLVAD_RESOURCE_DIR", resource_dir) .env("MULLVAD_SETTINGS_DIR", settings_dir) .env("MOCK_OPENVPN_ARGS_FILE", mock_openvpn_args_file.clone()) .stdout_null() .stderr_null(); - if mock_rpc_address_file { - expression = expression.env( - "MULLVAD_RPC_ADDRESS_PATH", - rpc_address_file.display().to_string(), - ); - } let process = expression.start().expect("Failed to start daemon"); DaemonRunner { process: Some(process), mock_openvpn_args_file, - rpc_address_file, + rpc_socket_path, _temp_dir: temp_dir, } } @@ -314,10 +314,12 @@ impl DaemonRunner { } pub fn rpc_client(&mut self) -> Result<DaemonRpcClient> { - wait_for_file(&self.rpc_address_file); - - DaemonRpcClient::with_insecure_rpc_address_file(&self.rpc_address_file) - .map_err(|error| format!("Failed to create RPC client: {}", error)) + wait_for_file(&self.rpc_socket_path); + let socket_path: String = self.rpc_socket_path.to_string_lossy().to_string(); + mullvad_ipc_client::new_standalone_transport(socket_path, |path, handle| { + IpcTransport::new(&path, &handle) + .chain_err(|| mullvad_ipc_client::ErrorKind::TransportError) + }).map_err(|e| format!("Failed to construct an RPC client - {}", e)) } #[cfg(unix)] @@ -360,35 +362,38 @@ impl Drop for DaemonRunner { process.kill().unwrap(); } } - - let _ = fs::remove_file(&self.rpc_address_file); } } pub struct MockOpenVpnPluginRpcClient { - credentials: String, - rpc: WsIpcClient, + rpc: jsonrpc_client_core::ClientHandle, } impl MockOpenVpnPluginRpcClient { - pub fn new(address: String, credentials: String) -> Result<Self> { - let rpc = WsIpcClient::connect(&address).map_err(|error| { - format!("Failed to create Mock OpenVPN plugin RPC client: {}", error) - })?; + fn spawn_event_loop(address: String) -> Result<jsonrpc_client_core::ClientHandle> { + let (tx, rx) = oneshot::channel(); + thread::spawn(move || { + let mut core = Core::new().expect("failed to spawn an event loop"); - Ok(MockOpenVpnPluginRpcClient { rpc, credentials }) - } + let result = IpcTransport::new(&address, &core.handle()) + .map_err(|error| { + format!("Failed to create Mock OpenVPN plugin RPC client: {}", error) + }).map(Transport::into_client); + match result { + Ok((client, client_handle)) => { + tx.send(Ok(client_handle)).unwrap(); + core.run(client).expect("client failed"); + } + Err(e) => tx.send(Err(e)).unwrap(), + } + }); - pub fn authenticate(&mut self) -> Result<bool> { - self.rpc - .call("authenticate", &[&self.credentials]) - .map_err(|error| format!("Failed to authenticate mock OpenVPN IPC client: {}", error)) + rx.wait().unwrap() } - pub fn authenticate_with(&mut self, credentials: &str) -> Result<bool> { - self.rpc - .call("authenticate", &[credentials]) - .map_err(|error| format!("Failed to authenticate mock OpenVPN IPC client: {}", error)) + pub fn new(address: String) -> Result<Self> { + let rpc = Self::spawn_event_loop(address)?; + Ok(MockOpenVpnPluginRpcClient { rpc }) } pub fn up(&mut self) -> Result<()> { @@ -417,7 +422,8 @@ impl MockOpenVpnPluginRpcClient { env: HashMap<String, String>, ) -> Result<()> { self.rpc - .call("openvpn_event", &(event, env)) + .call_method("openvpn_event", &(event, env)) + .wait() .map_err(|error| format!("Failed to send mock OpenVPN event {:?}: {}", event, error)) } } diff --git a/mullvad-tests/tests/connection.rs b/mullvad-tests/tests/connection.rs index 88e8a88c74..75ee04d788 100644 --- a/mullvad-tests/tests/connection.rs +++ b/mullvad-tests/tests/connection.rs @@ -34,11 +34,6 @@ const CONNECTED_STATE: DaemonState = DaemonState { target_state: TargetState::Secured, }; -const DISCONNECTING_STATE: DaemonState = DaemonState { - state: SecurityState::Secured, - target_state: TargetState::Unsecured, -}; - #[test] fn spawns_openvpn() { let mut daemon = DaemonRunner::spawn(); @@ -91,86 +86,6 @@ fn changes_to_connecting_state() { } #[test] -fn ignores_event_from_unauthorized_connection_from_openvpn_plugin() { - let mut daemon = DaemonRunner::spawn(); - let mut rpc_client = daemon.rpc_client().unwrap(); - let openvpn_args_file = daemon.mock_openvpn_args_file(); - let mut openvpn_args_file_events = PathWatcher::watch(&openvpn_args_file).unwrap(); - let state_events = rpc_client.new_state_subscribe().unwrap(); - - rpc_client.set_account(Some("123456".to_owned())).unwrap(); - rpc_client.connect().unwrap(); - - assert_state_event(&state_events, CONNECTING_STATE); - openvpn_args_file_events.assert_create_write_close_sequence(); - - let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - let call_result = mock_plugin_client.up(); - - assert!(call_result.is_err()); - assert_no_state_event(&state_events); - assert_eq!(rpc_client.get_state().unwrap(), CONNECTING_STATE); -} - -#[test] -fn authentication_credentials() { - let mut daemon = DaemonRunner::spawn(); - let mut rpc_client = daemon.rpc_client().unwrap(); - let openvpn_args_file = daemon.mock_openvpn_args_file(); - let mut openvpn_args_file_events = PathWatcher::watch(&openvpn_args_file).unwrap(); - let state_events = rpc_client.new_state_subscribe().unwrap(); - - rpc_client.set_account(Some("123456".to_owned())).unwrap(); - rpc_client.connect().unwrap(); - - assert_state_event(&state_events, CONNECTING_STATE); - openvpn_args_file_events.assert_create_write_close_sequence(); - - let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - - assert_eq!( - mock_plugin_client.authenticate_with(&String::new()), - Ok(false) - ); - assert_eq!( - mock_plugin_client.authenticate_with(&"fake-secret".to_owned()), - Ok(false) - ); - assert_eq!(mock_plugin_client.authenticate(), Ok(true)); - // Ensure it doesn't accept additional incorrect credentials - assert_eq!( - mock_plugin_client.authenticate_with(&"different-secret".to_owned()), - Ok(false) - ); -} - -#[test] -fn separate_connections_have_independent_authentication() { - let mut daemon = DaemonRunner::spawn(); - let mut rpc_client = daemon.rpc_client().unwrap(); - let openvpn_args_file = daemon.mock_openvpn_args_file(); - let mut openvpn_args_file_events = PathWatcher::watch(&openvpn_args_file).unwrap(); - let state_events = rpc_client.new_state_subscribe().unwrap(); - - rpc_client.set_account(Some("123456".to_owned())).unwrap(); - rpc_client.connect().unwrap(); - - assert_state_event(&state_events, CONNECTING_STATE); - openvpn_args_file_events.assert_create_write_close_sequence(); - - let mut first_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - let mut second_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - - let auth_result = first_plugin_client.authenticate(); - let call_result = second_plugin_client.up(); - - assert_eq!(auth_result, Ok(true)); - assert!(call_result.is_err()); - assert_no_state_event(&state_events); - assert_eq!(rpc_client.get_state().unwrap(), CONNECTING_STATE); -} - -#[test] fn changes_to_connected_state() { let mut daemon = DaemonRunner::spawn(); let mut rpc_client = daemon.rpc_client().unwrap(); @@ -186,7 +101,6 @@ fn changes_to_connected_state() { let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - mock_plugin_client.authenticate().unwrap(); mock_plugin_client.up().unwrap(); assert_state_event(&state_events, CONNECTED_STATE); @@ -209,7 +123,6 @@ fn returns_to_connecting_state() { let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - mock_plugin_client.authenticate().unwrap(); mock_plugin_client.up().unwrap(); assert_state_event(&state_events, CONNECTED_STATE); @@ -240,14 +153,12 @@ fn disconnects() { let mut mock_plugin_client = create_mock_openvpn_plugin_client(openvpn_args_file); - mock_plugin_client.authenticate().unwrap(); mock_plugin_client.up().unwrap(); assert_state_event(&state_events, CONNECTED_STATE); rpc_client.disconnect().unwrap(); - assert_state_event(&state_events, DISCONNECTING_STATE); assert_state_event(&state_events, DISCONNECTED_STATE); assert_eq!(rpc_client.get_state().unwrap(), DISCONNECTED_STATE); } @@ -260,33 +171,20 @@ fn assert_state_event(receiver: &mpsc::Receiver<DaemonState>, expected_state: Da assert_eq!(received_state, expected_state); } -fn assert_no_state_event(receiver: &mpsc::Receiver<DaemonState>) { - assert_eq!( - receiver.recv_timeout(Duration::from_secs(1)), - Err(mpsc::RecvTimeoutError::Timeout), - ); -} - fn create_mock_openvpn_plugin_client<P: AsRef<Path>>( openvpn_args_file_path: P, ) -> MockOpenVpnPluginRpcClient { - let (address, credentials) = get_plugin_arguments(openvpn_args_file_path); + let address = get_plugin_arguments(openvpn_args_file_path); - MockOpenVpnPluginRpcClient::new(address, credentials) + MockOpenVpnPluginRpcClient::new(address) .expect("Failed to create mock RPC client to connect to OpenVPN plugin event listener") } -fn get_plugin_arguments<P: AsRef<Path>>(openvpn_args_file_path: P) -> (String, String) { +fn get_plugin_arguments<P: AsRef<Path>>(openvpn_args_file_path: P) -> String { let mut arguments = search_openvpn_args(openvpn_args_file_path, OPENVPN_PLUGIN_NAME).skip(1); - let address = arguments + arguments .next() .expect("Missing OpenVPN plugin RPC listener address argument") - .expect("Failed to read from mock OpenVPN arguments file"); - let credentials = arguments - .next() - .expect("Missing OpenVPN plugin RPC listener credentials argument") - .expect("Failed to read from mock OpenVPN arguments file"); - - (address, credentials) + .expect("Failed to read from mock OpenVPN arguments file") } diff --git a/mullvad-tests/tests/startup.rs b/mullvad-tests/tests/startup.rs index 3bf47430cc..0424023515 100644 --- a/mullvad-tests/tests/startup.rs +++ b/mullvad-tests/tests/startup.rs @@ -4,39 +4,9 @@ extern crate mullvad_paths; extern crate mullvad_tests; extern crate mullvad_types; -use std::fs::{self, Metadata}; -use std::io; -use std::time::Duration; - -use mullvad_tests::{DaemonRunner, PathWatcher}; +use mullvad_tests::DaemonRunner; use mullvad_types::states::{DaemonState, SecurityState, TargetState}; -use platform_specific::*; - -#[test] -fn rpc_info_file_permissions() { - let rpc_file = mullvad_paths::get_rpc_address_path().unwrap(); - - if let Err(error) = fs::remove_file(&rpc_file) { - if error.kind() != io::ErrorKind::NotFound { - panic!("failed to remove existing RPC address file"); - } - } - - assert!(!rpc_file.exists()); - - let mut rpc_file_watcher = PathWatcher::watch(&rpc_file).unwrap(); - let _daemon = DaemonRunner::spawn_with_real_rpc_address_file(); - - rpc_file_watcher.wait_for_burst_of_events(Duration::from_secs(10)); - - assert!(rpc_file.exists()); - - ensure_only_admin_can_write( - fs::metadata(&rpc_file).expect("Failed to read RPC address file metadata"), - ); -} - #[test] fn starts_in_not_connected_state() { let mut daemon = DaemonRunner::spawn(); @@ -50,26 +20,3 @@ fn starts_in_not_connected_state() { assert_eq!(state, not_connected_state); } - -#[cfg(unix)] -mod platform_specific { - extern crate libc; - - use super::*; - use std::os::unix::fs::MetadataExt; - - pub fn ensure_only_admin_can_write(metadata: Metadata) { - let process_uid = unsafe { libc::getuid() }; - assert_eq!(metadata.uid(), process_uid); - assert_eq!(metadata.mode() & 0o022, 0); - } -} - -#[cfg(not(unix))] -mod platform_specific { - use super::*; - - pub fn ensure_only_admin_can_write(_metadata: Metadata) { - // TODO: Test when correctly implemented on Windows - } -} diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index 52297f214d..23df76dfd8 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -10,8 +10,8 @@ atty = "0.2" duct = "0.11" error-chain = "0.12" futures = "0.1" -jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc", tag = "v8.0.1" } -jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc", tag = "v8.0.1" } +jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc", branch = "master" } +jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc", branch = "master" } libc = "0.2.20" log = "0.4" openvpn-plugin = { version = "0.3", features = ["serde"] } diff --git a/talpid-core/src/tunnel/openvpn.rs b/talpid-core/src/tunnel/openvpn.rs index c63429a4f5..8e89ed4a5c 100644 --- a/talpid-core/src/tunnel/openvpn.rs +++ b/talpid-core/src/tunnel/openvpn.rs @@ -12,7 +12,6 @@ use std::thread; use std::time::Duration; use talpid_ipc; -use uuid; mod errors { error_chain!{ @@ -60,14 +59,10 @@ impl<C: OpenVpnBuilder> OpenVpnMonitor<C> { L: Fn(OpenVpnPluginEvent, HashMap<String, String>) + Send + Sync + 'static, P: AsRef<Path>, { - let credentials = uuid::Uuid::new_v4().to_string(); - let event_dispatcher = event_server::start(credentials.clone(), on_event) - .chain_err(|| ErrorKind::EventDispatcherError)?; + let event_dispatcher = + event_server::start(on_event).chain_err(|| ErrorKind::EventDispatcherError)?; - cmd.plugin( - plugin_path, - vec![event_dispatcher.address().to_owned(), credentials], - ); + cmd.plugin(plugin_path, vec![event_dispatcher.path().to_owned()]); let child = cmd .start() .chain_err(|| ErrorKind::ChildProcessError("Failed to start"))?; @@ -108,12 +103,9 @@ impl<C: OpenVpnBuilder> OpenVpnMonitor<C> { error!("OpenVPN process wait error: {}", e); Err(e).chain_err(|| ErrorKind::ChildProcessError("Error when waiting")) } - WaitResult::EventDispatcher(result) => { - error!("OpenVPN Event server exited unexpectedly: {:?}", result); - match result { - Ok(()) => Err(ErrorKind::EventDispatcherError.into()), - Err(e) => Err(e).chain_err(|| ErrorKind::EventDispatcherError), - } + WaitResult::EventDispatcher => { + error!("OpenVPN Event server exited unexpectedly"); + Err(ErrorKind::EventDispatcherError.into()) } } } @@ -137,10 +129,8 @@ impl<C: OpenVpnBuilder> OpenVpnMonitor<C> { dispatcher_handle.close(); }); thread::spawn(move || { - let result = event_dispatcher.wait(); - dispatcher_tx - .send(WaitResult::EventDispatcher(result)) - .unwrap(); + event_dispatcher.wait(); + dispatcher_tx.send(WaitResult::EventDispatcher).unwrap(); let _ = child_close_handle.close(); }); @@ -172,7 +162,7 @@ impl<H: ProcessHandle> OpenVpnCloseHandle<H> { #[derive(Debug)] enum WaitResult { Child(io::Result<ExitStatus>, bool), - EventDispatcher(talpid_ipc::Result<()>), + EventDispatcher, } /// Trait for types acting as OpenVPN process starters for `OpenVpnMonitor`. @@ -221,41 +211,39 @@ impl ProcessHandle for OpenVpnProcHandle { mod event_server { use super::OpenVpnPluginEvent; - use jsonrpc_core::{Compatibility, Error, MetaIoHandler, Metadata}; + use jsonrpc_core::{Error, IoHandler, MetaIoHandler}; use std::collections::HashMap; - use std::sync::atomic::{AtomicBool, Ordering}; - use std::sync::Arc; use talpid_ipc; + use uuid; /// Construct and start the IPC server with the given event listener callback. - pub fn start<L>(credentials: String, on_event: L) -> talpid_ipc::Result<talpid_ipc::IpcServer> + pub fn start<L>(on_event: L) -> talpid_ipc::Result<talpid_ipc::IpcServer> where L: Fn(OpenVpnPluginEvent, HashMap<String, String>) + Send + Sync + 'static, { - let rpc = OpenVpnEventApiImpl { - credentials, - on_event, + let uuid = uuid::Uuid::new_v4().to_string(); + let ipc_path = if cfg!(windows) { + format!("//./pipe/talpid-openvpn-{}", uuid) + } else { + format!("/tmp/talpid-openvpn-{}", uuid) }; - let mut io = MetaIoHandler::with_compatibility(Compatibility::V2); + let rpc = OpenVpnEventApiImpl { on_event }; + let mut io = IoHandler::new(); io.extend_with(rpc.to_delegate()); - talpid_ipc::IpcServer::start(io.into()) + let meta_io: MetaIoHandler<()> = MetaIoHandler::from(io); + talpid_ipc::IpcServer::start(meta_io, ipc_path) } build_rpc_trait! { pub trait OpenVpnEventApi { - type Metadata; - - #[rpc(meta, name = "authenticate")] - fn authenticate(&self, Self::Metadata, String) -> Result<bool, Error>; - #[rpc(meta, name = "openvpn_event")] - fn openvpn_event(&self, Self::Metadata, OpenVpnPluginEvent, HashMap<String, String>) + #[rpc(name = "openvpn_event")] + fn openvpn_event(&self, OpenVpnPluginEvent, HashMap<String, String>) -> Result<(), Error>; } } struct OpenVpnEventApiImpl<L> { - credentials: String, on_event: L, } @@ -263,60 +251,16 @@ mod event_server { where L: Fn(OpenVpnPluginEvent, HashMap<String, String>) + Send + Sync + 'static, { - type Metadata = Meta; - - fn authenticate( - &self, - metadata: Self::Metadata, - credentials: String, - ) -> Result<bool, Error> { - if credentials == self.credentials { - metadata.authenticated.store(true, Ordering::Relaxed); - Ok(true) - } else { - Ok(false) - } - } - fn openvpn_event( &self, - metadata: Self::Metadata, event: OpenVpnPluginEvent, env: HashMap<String, String>, ) -> Result<(), Error> { trace!("OpenVPN event {:?}", event); - metadata.check_authentication()?; (self.on_event)(event, env); Ok(()) } } - - #[derive(Clone)] - struct Meta { - authenticated: Arc<AtomicBool>, - } - - impl Default for Meta { - fn default() -> Self { - Meta { - authenticated: Arc::new(AtomicBool::new(false)), - } - } - } - - impl Meta { - fn check_authentication(&self) -> Result<(), Error> { - if self.authenticated.load(Ordering::Relaxed) { - trace!("Authenticated"); - Ok(()) - } else { - trace!("Not authenticated"); - Err(Error::invalid_request()) - } - } - } - - impl Metadata for Meta {} } diff --git a/talpid-ipc/Cargo.toml b/talpid-ipc/Cargo.toml index 591b0b1803..45848b45ca 100644 --- a/talpid-ipc/Cargo.toml +++ b/talpid-ipc/Cargo.toml @@ -10,13 +10,16 @@ error-chain = "0.12" serde = "1.0" serde_json = "1.0" log = "0.4" -jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc", tag = "v8.0.1" } -jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc", tag = "v8.0.1" } -jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc", tag = "v8.0.1" } -ws = { git = "https://github.com/tomusdrw/ws-rs" } -url = "1.4" +jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc", branch = "master" } +jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc", branch = "master" } +jsonrpc-ipc-server = { git = "https://github.com/paritytech/jsonrpc", branch = "master" } +jsonrpc-client-core = { git = "https://github.com/mullvad/jsonrpc-client-rs" } +jsonrpc-client-ipc = { git = "https://github.com/mullvad/jsonrpc-client-rs" } [dev-dependencies] assert_matches = "1.0" env_logger = "0.5" -jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc", tag = "v8.0.1" } +jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc", branch = "master" } +uuid = { version = "0.6", features = ["v4"] } +futures = "0.1.23" +tokio-core = "0.1" diff --git a/talpid-ipc/src/client.rs b/talpid-ipc/src/client.rs deleted file mode 100644 index 63c95cc74f..0000000000 --- a/talpid-ipc/src/client.rs +++ /dev/null @@ -1,585 +0,0 @@ -use std::collections::HashMap; -use std::sync::mpsc; -use std::thread; - -use error_chain::ChainedError; -use jsonrpc_pubsub::SubscriptionId; -use serde; -use serde_json::{self, Result as JsonResult, Value as JsonValue}; -use url::Url; -use ws; - -type JsonMap = serde_json::map::Map<String, JsonValue>; - -mod errors { - error_chain! { - errors { - ConnectError(details: &'static str) { - description("Failed to connect to RPC server") - display("Failed to connect to RPC server: {}", details) - } - - ConnectionHandlerStopped { - description("The WebSocket connection handler thread has stopped") - } - - ErrorResponse(error_message: String) { - description("Received an RPC error response") - display("Received an RPC error response: {}", error_message) - } - - DeserializeResponseError { - description("Failed to deserialize response") - } - - DeserializeSubscriptionEvent(event: String) { - description("Failed to deserialize RPC subscription event") - display("Failed to deserialize RPC subscription event {}", event) - } - - ForwardSubscriptionEvent(event: String) { - description("Failed to forward RPC subscription event") - display("Failed to forward RPC subscription event {}", event) - } - - InvalidJsonRpcResponse(details: &'static str) { - description("Received an invalid JSON-RPC response") - display("Received an invalid JSON-RPC response: {}", details) - } - - InvalidServerIdUrl(server_id: ::IpcServerId) { - description("Unable to parse given server ID as a URL") - display("Unable to parse given server ID as a URL: {}", server_id) - } - - InvalidSubscriptionEvent(details: &'static str) { - description("Received an invalid JSON-RPC PubSub event") - display("Received an invalid JSON-RPC PubSub event: {}", details) - } - - InvalidSubscriptionId(raw_id: ::serde_json::Value) { - description("Received an invalid JSON-RPC subscription ID for subscribe request") - display( - "Received an invalid JSON-RPC subscription ID for subscribe request: {}", - raw_id, - ) - } - - MissingResponse { - description("No response received") - } - - SendRequestError(method: String) { - description("Failed to send a request to call a remote JSON-RPC procedure") - display( - "Failed to send a request to call the \"{}\" remote JSON-RPC procedure", - method - ) - } - - SerializeArgumentsError { - description("Failed to serialize JSON-RPC request arguments") - } - - SerializeSubscriptionId { - description("Failed to serialize JSON-RPC subscription ID") - } - - UnsubscribeError { - description("Failed to unsubscribe from a remote event") - } - - WebSocketError { - description("Error with WebSocket connection") - } - } - } -} -pub use self::errors::*; - -#[derive(Debug, Eq, PartialEq)] -pub enum SubscriptionHandlerResult { - Active, - Finished, -} - -type SubscriptionHandler = Box<Fn(JsonValue) -> SubscriptionHandlerResult + Send>; - -struct ActiveRequest { - id: i64, - response_tx: mpsc::Sender<Result<JsonValue>>, -} - -impl ActiveRequest { - pub fn new(id: i64, response_tx: mpsc::Sender<Result<JsonValue>>) -> Self { - ActiveRequest { id, response_tx } - } - - pub fn id(&self) -> i64 { - self.id - } - - pub fn send_response(&mut self, response: Result<JsonValue>) { - let _ = self.response_tx.send(response); - } -} - -enum WsIpcCommand { - Call { - method: String, - arguments: JsonValue, - response_tx: mpsc::Sender<Result<JsonValue>>, - }, - - Subscribe { - id: SubscriptionId, - handler: SubscriptionHandler, - unsubscribe_method: String, - }, - - Response { - id: i64, - result: Result<JsonValue>, - }, - - Notification { - subscription: SubscriptionId, - event: JsonValue, - }, - - Error(Error), -} - -struct Factory { - connection_tx: mpsc::Sender<WsIpcCommand>, - sender_tx: mpsc::Sender<ws::Sender>, -} - -impl ws::Factory for Factory { - type Handler = Handler; - - fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler { - trace!("Connection established"); - - let _ = self.sender_tx.send(sender); - - Handler { - connection_tx: self.connection_tx.clone(), - } - } -} - - -struct Handler { - connection_tx: mpsc::Sender<WsIpcCommand>, -} - -impl Handler { - fn process_message(&mut self, msg: ws::Message) -> Result<()> { - trace!("WsIpcClient incoming message: {:?}", msg); - let mut message_json_object = self.parse_message_object(msg)?; - let response_id = self.parse_response_id(&mut message_json_object)?; - - let command = if let Some(id) = response_id { - let result = self.parse_response_result(message_json_object); - - WsIpcCommand::Response { id, result } - } else { - let (subscription, event) = self.parse_subscription_event(message_json_object)?; - - WsIpcCommand::Notification { - subscription, - event, - } - }; - - self.connection_tx - .send(command) - .chain_err(|| ErrorKind::ConnectionHandlerStopped) - } - - fn parse_message_object(&self, msg: ws::Message) -> Result<JsonMap> { - let parsed_json: JsonResult<JsonValue> = match msg { - ws::Message::Text(s) => serde_json::from_str(&s), - ws::Message::Binary(b) => serde_json::from_slice(&b), - }; - let json = parsed_json.chain_err(|| { - ErrorKind::InvalidJsonRpcResponse("Unable to deserialize ws message as JSON") - })?; - - let mut json_object_map = match json { - JsonValue::Object(object_map) => object_map, - _ => bail!(ErrorKind::InvalidJsonRpcResponse( - "Received response is not a JSON object" - )), - }; - - ensure!( - json_object_map.remove("jsonrpc") == Some(JsonValue::String("2.0".to_owned())), - ErrorKind::InvalidJsonRpcResponse("Invalid JSON-RPC version field in response") - ); - - Ok(json_object_map) - } - - fn parse_response_id(&self, json_object_map: &mut JsonMap) -> Result<Option<i64>> { - match json_object_map.remove("id") { - Some(JsonValue::Number(id)) => id.as_i64().map(Some).ok_or_else(|| { - ErrorKind::InvalidJsonRpcResponse("Invalid request ID number").into() - }), - Some(_) => Err(ErrorKind::InvalidJsonRpcResponse("Invalid request ID value").into()), - None => Ok(None), - } - } - - fn parse_response_result(&self, mut json_object_map: JsonMap) -> Result<JsonValue> { - let result = json_object_map.remove("result"); - let error = json_object_map.remove("error"); - - match (result, error) { - (Some(remote_result), None) => Ok(remote_result), - (None, Some(JsonValue::String(remote_error))) => { - Err(ErrorKind::ErrorResponse(remote_error).into()) - } - (None, Some(json_value)) => { - Err(ErrorKind::ErrorResponse(json_value.to_string()).into()) - } - (None, None) => Err(ErrorKind::InvalidJsonRpcResponse("Missing RPC result").into()), - (Some(_), Some(_)) => Err(ErrorKind::InvalidJsonRpcResponse( - "Response is ambiguous, contains both a successful result and an error", - ).into()), - } - } - - fn parse_subscription_event( - &mut self, - mut notification: JsonMap, - ) -> Result<(SubscriptionId, JsonValue)> { - match notification.remove("params") { - Some(JsonValue::Object(mut parameters)) => { - let raw_id = parameters.remove("subscription").ok_or_else(|| { - ErrorKind::InvalidSubscriptionEvent("Missing subscription ID") - })?; - let id = SubscriptionId::parse_value(&raw_id).ok_or_else(|| { - ErrorKind::InvalidSubscriptionEvent("Invalid subscription ID") - })?; - let event = parameters - .remove("result") - .ok_or_else(|| ErrorKind::InvalidSubscriptionEvent("Missing event data"))?; - - Ok((id, event)) - } - Some(_) => bail!(ErrorKind::InvalidSubscriptionEvent( - "RPC parameters is not a JSON object map" - )), - None => bail!(ErrorKind::InvalidSubscriptionEvent( - "Missing RPC parameters" - )), - } - } -} - -impl ws::Handler for Handler { - fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> { - if let Err(error) = self.process_message(msg) { - let chained_error = error.chain_err(|| "Failed to process RPC message"); - error!("{}", chained_error.display_chain()); - } - - Ok(()) - } - - fn on_error(&mut self, error: ws::Error) { - let error = Error::with_chain(error, ErrorKind::WebSocketError); - - let _ = self.connection_tx.send(WsIpcCommand::Error(error)); - } -} - -pub struct WsIpcClient { - connection_tx: mpsc::Sender<WsIpcCommand>, -} - -impl WsIpcClient { - pub fn connect(server_id: &::IpcServerId) -> Result<Self> { - let url = Url::parse(&server_id) - .chain_err(|| ErrorKind::InvalidServerIdUrl(server_id.to_owned()))?; - let (connection_tx, connection_rx) = mpsc::channel(); - let sender = Self::open_websocket(url, connection_tx.clone())?; - - WsIpcClientConnection::spawn(sender, connection_rx); - - Ok(WsIpcClient { connection_tx }) - } - - fn open_websocket(url: Url, connection_tx: mpsc::Sender<WsIpcCommand>) -> Result<ws::Sender> { - let (sender_tx, sender_rx) = mpsc::channel(); - let factory = Factory { - connection_tx, - sender_tx, - }; - - let mut websocket = ws::WebSocket::new(factory) - .chain_err(|| ErrorKind::ConnectError("Unable to create WebSocket"))?; - - websocket - .connect(url) - .chain_err(|| ErrorKind::ConnectError("Unable to connect WebSocket to URL"))?; - - thread::spawn(move || { - let result = websocket - .run() - .chain_err(|| ErrorKind::ConnectError("Error while running WebSocket event loop")); - - if let Err(error) = result { - error!("{}", error.display_chain()); - } - }); - - sender_rx - .recv() - .chain_err(|| ErrorKind::ConnectError("WebSocket connection failed")) - } - - pub fn subscribe<V, M>( - &mut self, - subscribe_method: String, - unsubscribe_method: String, - sender: mpsc::Sender<M>, - ) -> Result<()> - where - V: for<'de> serde::Deserialize<'de>, - M: From<V> + Send + 'static, - { - let raw_subscription_id = self.call(&subscribe_method, &[] as &[u8; 0])?; - let subscription_id = SubscriptionId::parse_value(&raw_subscription_id) - .ok_or_else(|| ErrorKind::InvalidSubscriptionId(raw_subscription_id))?; - - let handler = move |json_value| match forward_subscription_event( - &subscribe_method, - json_value, - &sender, - ) { - Ok(()) => SubscriptionHandlerResult::Active, - Err(error) => { - error!("{}", error.display_chain()); - SubscriptionHandlerResult::Finished - } - }; - - self.register_subscription(subscription_id, handler, unsubscribe_method)?; - - Ok(()) - } - - fn register_subscription<H>( - &mut self, - id: SubscriptionId, - handler: H, - unsubscribe_method: String, - ) -> Result<()> - where - H: Fn(JsonValue) -> SubscriptionHandlerResult + Send + 'static, - { - self.connection_tx - .send(WsIpcCommand::Subscribe { - id, - handler: Box::new(handler), - unsubscribe_method, - }).chain_err(|| ErrorKind::ConnectionHandlerStopped) - } - - pub fn call<S, T, O>(&mut self, method: S, params: &T) -> Result<O> - where - S: ToString, - T: serde::Serialize, - O: for<'de> serde::Deserialize<'de>, - { - let arguments = - serde_json::to_value(params).chain_err(|| ErrorKind::SerializeArgumentsError)?; - let (response_tx, response_rx) = mpsc::channel(); - let command = WsIpcCommand::Call { - method: method.to_string(), - arguments, - response_tx, - }; - - self.connection_tx - .send(command) - .chain_err(|| ErrorKind::ConnectionHandlerStopped)?; - - let json_result = response_rx - .recv() - .chain_err(|| ErrorKind::MissingResponse)?; - - Ok(serde_json::from_value(json_result?) - .chain_err(|| ErrorKind::DeserializeResponseError)?) - } -} - -struct WsIpcClientConnection { - next_id: i64, - active_request: Option<ActiveRequest>, - active_subscriptions: HashMap<SubscriptionId, (SubscriptionHandler, String)>, - sender: ws::Sender, -} - -impl WsIpcClientConnection { - pub fn spawn(sender: ws::Sender, commands: mpsc::Receiver<WsIpcCommand>) { - let mut instance = WsIpcClientConnection { - next_id: 1, - active_request: None, - active_subscriptions: HashMap::new(), - sender, - }; - - thread::spawn(move || { - if let Err(error) = instance.run(commands) { - let chained_error = Error::with_chain(error, "WsIpcClient event loop error"); - error!("{}", chained_error.display_chain()); - } - }); - } - - fn run(&mut self, commands: mpsc::Receiver<WsIpcCommand>) -> Result<()> { - use self::WsIpcCommand::*; - - for command in commands { - match command { - Call { - method, - arguments, - response_tx, - } => self.call(method, arguments, response_tx)?, - Subscribe { - id, - handler, - unsubscribe_method, - } => { - self.active_subscriptions - .insert(id, (handler, unsubscribe_method)); - } - Response { id, result } => self.handle_response(id, result)?, - Notification { - subscription, - event, - } => self.handle_notification(subscription, event)?, - Error(error) => self.handle_error(error), - } - } - - Ok(()) - } - - fn call( - &mut self, - method: String, - arguments: JsonValue, - response_tx: mpsc::Sender<Result<JsonValue>>, - ) -> Result<()> { - let id = self.new_id(); - self.queue_request_response(id, response_tx); - self.send_request(id, method, arguments) - } - - fn new_id(&mut self) -> i64 { - let id = self.next_id; - self.next_id += 1; - id - } - - fn queue_request_response(&mut self, id: i64, response_tx: mpsc::Sender<Result<JsonValue>>) { - self.active_request = Some(ActiveRequest::new(id, response_tx)); - } - - fn send_request(&mut self, id: i64, method: String, arguments: JsonValue) -> Result<()> { - let json_request = self.build_json_request(id, &method, arguments); - - self.sender - .send(json_request.as_bytes()) - .chain_err(|| ErrorKind::SendRequestError(method)) - } - - fn build_json_request(&mut self, id: i64, method: &str, params: JsonValue) -> String { - let request_json = json!({ - "jsonrpc": "2.0", - "id": id, - "method": method, - "params": params, - }); - format!("{}", request_json) - } - - fn handle_response(&mut self, id: i64, result: Result<JsonValue>) -> Result<()> { - if let Some(mut request) = self.active_request.take() { - if request.id() == id { - request.send_response(result); - } else { - self.active_request = Some(request); - warn!("Received an unexpected response with ID {}", id); - } - } else { - warn!("Received an unexpected response with ID {}", id); - } - - Ok(()) - } - - fn handle_notification(&mut self, id: SubscriptionId, event: JsonValue) -> Result<()> { - let unsubscribe_method = - if let Some((handler, unsubscribe_method)) = self.active_subscriptions.get(&id) { - match handler(event) { - SubscriptionHandlerResult::Active => None, - SubscriptionHandlerResult::Finished => Some(unsubscribe_method.clone()), - } - } else { - warn!("Received an unexpected notification"); - None - }; - - if let Some(method) = unsubscribe_method { - self.unsubscribe(method, id)?; - } - - Ok(()) - } - - fn unsubscribe(&mut self, method: String, id: SubscriptionId) -> Result<()> { - self.active_subscriptions.remove(&id); - - let (result_tx, _) = mpsc::channel(); - let arguments = match id { - SubscriptionId::Number(id) => serde_json::to_value(&[id]), - SubscriptionId::String(id) => serde_json::to_value(&[id]), - }.chain_err(|| ErrorKind::SerializeSubscriptionId); - - self.call(method, arguments?, result_tx) - .chain_err(|| ErrorKind::UnsubscribeError) - } - - fn handle_error(&mut self, error: Error) { - if let Some(ref mut request) = self.active_request { - let _ = request.response_tx.send(Err(error)); - } else { - error!("{}", error.display_chain()); - } - } -} - -fn forward_subscription_event<V, M>( - subscribe_method: &String, - json_value: JsonValue, - sender: &mpsc::Sender<M>, -) -> Result<()> -where - V: for<'de> serde::Deserialize<'de>, - M: From<V> + Send + 'static, -{ - let value: V = serde_json::from_value(json_value) - .chain_err(|| ErrorKind::DeserializeSubscriptionEvent(subscribe_method.clone()))?; - let message = M::from(value); - - sender - .send(message) - .chain_err(|| ErrorKind::ForwardSubscriptionEvent(subscribe_method.clone())) -} diff --git a/talpid-ipc/src/lib.rs b/talpid-ipc/src/lib.rs index 4c6c737165..6104a59848 100644 --- a/talpid-ipc/src/lib.rs +++ b/talpid-ipc/src/lib.rs @@ -10,28 +10,22 @@ #[macro_use] extern crate error_chain; -#[macro_use] -extern crate log; extern crate serde; -#[macro_use] extern crate serde_json; extern crate jsonrpc_core; +extern crate jsonrpc_ipc_server; extern crate jsonrpc_pubsub; -extern crate jsonrpc_ws_server; -extern crate url; -extern crate ws; -use jsonrpc_core::{MetaIoHandler, Metadata}; -use jsonrpc_ws_server::{MetaExtractor, NoopExtractor, Server, ServerBuilder}; +extern crate jsonrpc_client_core; +extern crate jsonrpc_client_ipc; -use std::fmt; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use jsonrpc_core::{MetaIoHandler, Metadata}; +use jsonrpc_ipc_server::{MetaExtractor, NoopExtractor, SecurityAttributes, Server, ServerBuilder}; -mod client; -pub use client::*; +use std::fmt; /// An Id created by the Ipc server that the client can use to connect to it pub type IpcServerId = String; @@ -41,38 +35,57 @@ error_chain!{ IpcServerError { description("Error in IPC server") } + + PermissionsError { + description("Unable to set permissions for IPC endpoint") + } } } pub struct IpcServer { - address: String, + path: String, server: Server, } impl IpcServer { - pub fn start<M: Metadata>(handler: MetaIoHandler<M>) -> Result<Self> { - Self::start_with_metadata(handler, NoopExtractor) + pub fn start<M: Metadata + Default>(handler: MetaIoHandler<M>, path: String) -> Result<Self> { + Self::start_with_metadata(handler, NoopExtractor, path) } - pub fn start_with_metadata<M, E>(handler: MetaIoHandler<M>, meta_extractor: E) -> Result<Self> + pub fn start_with_metadata<M, E>( + handler: MetaIoHandler<M>, + meta_extractor: E, + path: String, + ) -> Result<Self> where - M: Metadata, + M: Metadata + Default, E: MetaExtractor<M>, { - let listen_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0); - ServerBuilder::new(handler) - .session_meta_extractor(meta_extractor) - .start(&listen_addr) + let security_attributes = SecurityAttributes::allow_everyone_create() + .chain_err(|| ErrorKind::PermissionsError)?; + let server = ServerBuilder::with_meta_extractor(handler, meta_extractor) + .set_security_attributes(security_attributes) + .start(&path) + .chain_err(|| ErrorKind::IpcServerError) .map(|server| IpcServer { - address: format!("ws://{}", server.addr()), - server: server, - }).chain_err(|| ErrorKind::IpcServerError) + path: path.to_owned(), + server, + })?; + + #[cfg(unix)] + { + use std::fs; + use std::os::unix::fs::PermissionsExt; + fs::set_permissions(&path, PermissionsExt::from_mode(0o766)) + .chain_err(|| ErrorKind::PermissionsError)?; + } + Ok(server) } - /// Returns the localhost address this `IpcServer` is listening on. - pub fn address(&self) -> &str { - &self.address + /// 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. @@ -80,10 +93,10 @@ impl IpcServer { CloseHandle(self.server.close_handle()) } - /// Consumes the server and waits for it to finish. Get an `CloseHandle` before calling this + /// 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) -> Result<()> { - self.server.wait().chain_err(|| ErrorKind::IpcServerError) + pub fn wait(self) { + self.server.wait(); } } @@ -92,14 +105,13 @@ impl IpcServer { impl fmt::Debug for IpcServer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("IpcServer") - .field("address", &self.address) + .field("path", &self.path) .finish() } } - #[derive(Clone)] -pub struct CloseHandle(jsonrpc_ws_server::CloseHandle); +pub struct CloseHandle(jsonrpc_ipc_server::CloseHandle); impl CloseHandle { pub fn close(self) { diff --git a/talpid-ipc/tests/ipc-client-server.rs b/talpid-ipc/tests/ipc-client-server.rs index 34b44beb68..dc5c8d9d17 100644 --- a/talpid-ipc/tests/ipc-client-server.rs +++ b/talpid-ipc/tests/ipc-client-server.rs @@ -1,11 +1,22 @@ #[macro_use] extern crate assert_matches; extern crate env_logger; +extern crate jsonrpc_client_core; +extern crate jsonrpc_client_ipc; extern crate jsonrpc_core; #[macro_use] extern crate jsonrpc_macros; extern crate talpid_ipc; +extern crate tokio_core; +extern crate uuid; +extern crate futures; + +use futures::sync::oneshot; +use futures::Future; +use tokio_core::reactor::Core; + +use jsonrpc_client_core::{Error as ClientError, Transport}; use jsonrpc_core::{Error, IoHandler}; use std::sync::{mpsc, Mutex}; use std::time::Duration; @@ -35,13 +46,13 @@ fn can_call_rpcs_on_server() { env_logger::init(); let (server, rx) = create_server(); - let server_id = server.address().to_owned(); - let mut client = create_client(&server_id); + let server_path = server.path().to_owned(); + let client = create_client(server_path); - let _result: () = client.call("foo", &[97]).unwrap(); + let _result: () = client.call_method("foo", &[97]).wait().unwrap(); assert_eq!(Ok(97), rx.recv_timeout(Duration::from_millis(500))); - let result: Result<(), _> = client.call("invalid_method", &[0]); + let result: Result<(), ClientError> = client.call_method("invalid_method", &[0]).wait(); assert_matches!(result, Err(_)); server.close_handle().close(); } @@ -51,14 +62,7 @@ fn can_call_rpcs_on_server() { #[test] #[should_panic] fn ipc_client_invalid_url() { - create_client(&"INVALID ID".to_owned()); -} - -#[test] -fn ipc_client_bad_connection() { - let mut client = create_client(&"ws://127.0.0.1:9876".to_owned()); - let result: Result<(), _> = client.call("invalid_method", &[0]); - assert_matches!(result, Err(_)); + let _client = create_client("INVALID ID".to_owned()); } fn create_server() -> (talpid_ipc::IpcServer, mpsc::Receiver<i64>) { @@ -67,10 +71,30 @@ fn create_server() -> (talpid_ipc::IpcServer, mpsc::Receiver<i64>) { let mut io = IoHandler::new(); io.extend_with(rpc.to_delegate()); - let server = talpid_ipc::IpcServer::start(io.into()).unwrap(); + 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(id: &talpid_ipc::IpcServerId) -> talpid_ipc::WsIpcClient { - talpid_ipc::WsIpcClient::connect(id).unwrap() +fn create_client(ipc_path: String) -> jsonrpc_client_core::ClientHandle { + use std::thread; + let (tx, rx) = oneshot::channel(); + + thread::spawn(move || { + let mut core = Core::new().expect("failed to spawn reactor"); + let (client, client_handle) = + jsonrpc_client_ipc::IpcTransport::new(&ipc_path, &core.handle()) + .expect("failed to construct a transport") + .into_client(); + tx.send(client_handle).unwrap(); + core.run(client).unwrap(); + }); + + let handle = rx.wait().expect("Failed to construct a valid client"); + handle } diff --git a/talpid-openvpn-plugin/Cargo.toml b/talpid-openvpn-plugin/Cargo.toml index c5987d68ef..165a4976be 100644 --- a/talpid-openvpn-plugin/Cargo.toml +++ b/talpid-openvpn-plugin/Cargo.toml @@ -12,9 +12,14 @@ crate-type = ["cdylib"] error-chain = "0.12" log = "0.4" env_logger = "0.5" +jsonrpc-client-core = { git = "https://github.com/mullvad/jsonrpc-client-rs" } +jsonrpc-client-ipc = { git = "https://github.com/mullvad/jsonrpc-client-rs" } +tokio-core = "0.1" +futures = "0.1" openvpn-plugin = { version = "0.3", features = ["serde", "log"] } talpid-ipc = { path = "../talpid-ipc" } + [target.'cfg(windows)'.build-dependencies] windres = "0.2" diff --git a/talpid-openvpn-plugin/src/lib.rs b/talpid-openvpn-plugin/src/lib.rs index 9c6c690339..ed358a5df7 100644 --- a/talpid-openvpn-plugin/src/lib.rs +++ b/talpid-openvpn-plugin/src/lib.rs @@ -13,12 +13,19 @@ extern crate error_chain; extern crate log; #[macro_use] +extern crate jsonrpc_client_core; +extern crate futures; +extern crate jsonrpc_client_ipc; +#[macro_use] extern crate openvpn_plugin; -extern crate talpid_ipc; +extern crate tokio_core; +use error_chain::ChainedError; use openvpn_plugin::types::{EventResult, OpenVpnPluginEvent}; use std::collections::HashMap; use std::ffi::CString; +use std::sync::Mutex; + mod processing; use processing::EventProcessor; @@ -54,26 +61,28 @@ openvpn_plugin!( ::openvpn_open, ::openvpn_close, ::openvpn_event, - ::EventProcessor + ::Mutex<EventProcessor> ); pub struct Arguments { - server_id: talpid_ipc::IpcServerId, - credentials: String, + ipc_socket_path: String, } fn openvpn_open( args: Vec<CString>, _env: HashMap<CString, CString>, -) -> Result<(Vec<OpenVpnPluginEvent>, EventProcessor)> { +) -> Result<(Vec<OpenVpnPluginEvent>, Mutex<EventProcessor>)> { env_logger::init(); debug!("Initializing plugin"); let arguments = parse_args(&args)?; - info!("Connecting back to talpid core at {}", arguments.server_id); - let processor = EventProcessor::new(&arguments).chain_err(|| ErrorKind::InitHandleFailed)?; + info!( + "Connecting back to talpid core at {}", + arguments.ipc_socket_path + ); + let processor = EventProcessor::new(arguments).chain_err(|| ErrorKind::InitHandleFailed)?; - Ok((INTERESTING_EVENTS.to_vec(), processor)) + Ok((INTERESTING_EVENTS.to_vec(), Mutex::new(processor))) } fn parse_args(args: &[CString]) -> Result<Arguments> { @@ -82,37 +91,39 @@ fn parse_args(args: &[CString]) -> Result<Arguments> { .into_iter(); let _plugin_path = args_iter.next(); - let server_id: talpid_ipc::IpcServerId = args_iter + let ipc_socket_path: String = args_iter .next() .ok_or_else(|| ErrorKind::Msg("No core server id given as first argument".to_owned()))?; - let credentials = args_iter - .next() - .ok_or_else(|| ErrorKind::Msg("No IPC credentials given as second argument".to_owned()))?; - Ok(Arguments { - server_id, - credentials, - }) + Ok(Arguments { ipc_socket_path }) } -fn openvpn_close(_handle: EventProcessor) { - debug!("Unloading plugin"); +fn openvpn_close(_handle: Mutex<EventProcessor>) { + info!("Unloading plugin"); } fn openvpn_event( event: OpenVpnPluginEvent, _args: Vec<CString>, env: HashMap<CString, CString>, - handle: &mut EventProcessor, + handle: &mut Mutex<EventProcessor>, ) -> Result<EventResult> { debug!("Received event: {:?}", event); let parsed_env = openvpn_plugin::ffi::parse::env_utf8(&env).chain_err(|| ErrorKind::ParseEnvFailed)?; - handle + let result = handle + .lock() + .expect("failed to obtain mutex for EventProcessor") .process_event(event, parsed_env) - .chain_err(|| ErrorKind::EventProcessingFailed)?; - Ok(EventResult::Success) + .chain_err(|| ErrorKind::EventProcessingFailed); + match result { + Ok(()) => Ok(EventResult::Success), + Err(e) => { + error!("{}", e.display_chain()); + Ok(EventResult::Failure) + } + } } diff --git a/talpid-openvpn-plugin/src/processing.rs b/talpid-openvpn-plugin/src/processing.rs index 99533945ed..66d009b44f 100644 --- a/talpid-openvpn-plugin/src/processing.rs +++ b/talpid-openvpn-plugin/src/processing.rs @@ -1,42 +1,55 @@ use openvpn_plugin; use std::collections::HashMap; -use std::sync::Mutex; -use talpid_ipc::WsIpcClient; + +extern crate futures; + +use jsonrpc_client_core::{Future, Result as ClientResult, Transport}; +use jsonrpc_client_ipc::IpcTransport; +use tokio_core::reactor::Core; use super::Arguments; error_chain! { errors { - AuthDenied { - description("Failed to authenticate with Talpid IPC server") - } IpcSendingError { description("Failed while sending an event over the IPC channel") } + + Shutdown { + description("Connection is shut down") + } + } } /// Struct processing OpenVPN events and notifies listeners over IPC pub struct EventProcessor { - ipc_client: Mutex<WsIpcClient>, + ipc_client: EventProxy, + client_stop: ::std::sync::mpsc::Receiver<ClientResult<()>>, + core: Core, } impl EventProcessor { - pub fn new(arguments: &Arguments) -> Result<EventProcessor> { + pub fn new(arguments: Arguments) -> Result<EventProcessor> { trace!("Creating EventProcessor"); - let mut ipc_client = WsIpcClient::connect(&arguments.server_id) - .chain_err(|| "Unable to create IPC client")?; + let core = Core::new().chain_err(|| "Unable to initialize Tokio Core")?; + let handle = core.handle(); + let (client, client_handle) = IpcTransport::new(&arguments.ipc_socket_path, &handle) + .chain_err(|| "Unable to create IPC transport")? + .into_client(); - trace!("Authenticating EventProcessor"); - match ipc_client.call("authenticate", &[&arguments.credentials]) { - Ok(true) => trace!("Credentials accepted"), - Ok(false) => bail!(ErrorKind::AuthDenied), - Err(error) => bail!(Error::with_chain(error, ErrorKind::AuthDenied)), - } + let (tx, client_stop) = ::std::sync::mpsc::channel(); + + let client_future = client.then(move |result| tx.send(result)).map_err(|_| ()); + handle.spawn(client_future); + + let ipc_client = EventProxy::new(client_handle); Ok(EventProcessor { - ipc_client: Mutex::new(ipc_client), + ipc_client, + client_stop, + core, }) } @@ -46,11 +59,25 @@ impl EventProcessor { env: HashMap<String, String>, ) -> Result<()> { trace!("Processing \"{:?}\" event", event); - self.ipc_client - .lock() - .expect("a thread panicked while using the RPC client in the OpenVPN plugin") - .call("openvpn_event", &(event, env)) - .map(|_: Option<()>| ()) - .chain_err(|| ErrorKind::IpcSendingError) + let call_future = self + .ipc_client + .openvpn_event(event, env) + .map_err(|e| Error::with_chain(e, ErrorKind::IpcSendingError)); + self.core.run(call_future)?; + self.check_client_status() + } + + fn check_client_status(&mut self) -> Result<()> { + use std::sync::mpsc::TryRecvError::*; + match self.client_stop.try_recv() { + Err(Empty) => Ok(()), + Err(Disconnected) => Err(ErrorKind::Shutdown.into()), + Ok(Ok(_)) => Err(ErrorKind::Shutdown.into()), + Ok(Err(e)) => Err(Error::with_chain(e, ErrorKind::IpcSendingError)), + } } } + +jsonrpc_client!(pub struct EventProxy { + pub fn openvpn_event(&mut self, event: openvpn_plugin::types::OpenVpnPluginEvent, env: HashMap<String, String>) -> Future<()>; +}); |
