diff options
| -rw-r--r-- | Cargo.lock | 108 | ||||
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | mullvad_daemon/Cargo.toml | 5 | ||||
| -rw-r--r-- | mullvad_daemon/src/main.rs | 325 | ||||
| -rw-r--r-- | mullvad_daemon/src/states.rs | 2 | ||||
| -rw-r--r-- | talpid_core/Cargo.toml | 1 | ||||
| -rw-r--r-- | talpid_core/src/lib.rs | 2 | ||||
| -rw-r--r-- | talpid_core/src/tunnel/mod.rs | 2 | ||||
| -rw-r--r-- | talpid_core/src/tunnel/openvpn.rs | 32 |
9 files changed, 364 insertions, 115 deletions
diff --git a/Cargo.lock b/Cargo.lock index f86b13f7c3..c960739c60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,26 +18,11 @@ dependencies = [ ] [[package]] -name = "ansi_term" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "assert_matches" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "atty" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "backtrace" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -66,11 +51,6 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "bitflags" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "byteorder" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -90,21 +70,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "clap" -version = "2.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "dbghelp-sys" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -205,7 +170,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jsonrpc-core" version = "7.0.0" -source = "git+https://github.com/faern/jsonrpc?branch=ws-close-handle#8c47e034f05b8e42d3440078b758dd732e85894c" +source = "git+https://github.com/faern/jsonrpc?branch=ws-close-handle#e5da3f069fb14f9a90278f09a1841f9149af541b" dependencies = [ "futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -217,7 +182,7 @@ dependencies = [ [[package]] name = "jsonrpc-macros" version = "7.0.0" -source = "git+https://github.com/faern/jsonrpc?branch=ws-close-handle#8c47e034f05b8e42d3440078b758dd732e85894c" +source = "git+https://github.com/faern/jsonrpc?branch=ws-close-handle#e5da3f069fb14f9a90278f09a1841f9149af541b" dependencies = [ "jsonrpc-core 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)", "jsonrpc-pubsub 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)", @@ -227,7 +192,7 @@ dependencies = [ [[package]] name = "jsonrpc-pubsub" version = "7.0.0" -source = "git+https://github.com/faern/jsonrpc?branch=ws-close-handle#8c47e034f05b8e42d3440078b758dd732e85894c" +source = "git+https://github.com/faern/jsonrpc?branch=ws-close-handle#e5da3f069fb14f9a90278f09a1841f9149af541b" dependencies = [ "jsonrpc-core 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -237,7 +202,7 @@ dependencies = [ [[package]] name = "jsonrpc-server-utils" version = "7.0.0" -source = "git+https://github.com/faern/jsonrpc?branch=ws-close-handle#8c47e034f05b8e42d3440078b758dd732e85894c" +source = "git+https://github.com/faern/jsonrpc?branch=ws-close-handle#e5da3f069fb14f9a90278f09a1841f9149af541b" dependencies = [ "globset 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)", @@ -249,7 +214,7 @@ dependencies = [ [[package]] name = "jsonrpc-ws-server" version = "7.0.0" -source = "git+https://github.com/faern/jsonrpc?branch=ws-close-handle#8c47e034f05b8e42d3440078b758dd732e85894c" +source = "git+https://github.com/faern/jsonrpc?branch=ws-close-handle#e5da3f069fb14f9a90278f09a1841f9149af541b" dependencies = [ "jsonrpc-core 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)", "jsonrpc-server-utils 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)", @@ -267,6 +232,11 @@ dependencies = [ ] [[package]] +name = "lazy_static" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "lazycell" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -337,10 +307,13 @@ dependencies = [ "jsonrpc-macros 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)", "jsonrpc-pubsub 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)", "jsonrpc-ws-server 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)", + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "talpid_core 0.0.0", "talpid_ipc 0.1.0", + "uuid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -527,11 +500,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "strsim" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "syn" version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -550,17 +518,6 @@ dependencies = [ ] [[package]] -name = "talpid_cli" -version = "0.0.0" -dependencies = [ - "clap 2.23.2 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", - "talpid_core 0.0.0", -] - -[[package]] name = "talpid_core" version = "0.0.0" dependencies = [ @@ -569,6 +526,7 @@ dependencies = [ "error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)", "jsonrpc-macros 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)", + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "openvpn_ffi 0.1.0", @@ -593,16 +551,6 @@ dependencies = [ ] [[package]] -name = "term_size" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] name = "thread-id" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -659,16 +607,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "unicode-segmentation" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "unicode-width" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] name = "unicode-xid" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -696,9 +634,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] -name = "vec_map" -version = "0.7.0" +name = "uuid" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "void" @@ -742,17 +683,13 @@ dependencies = [ [metadata] "checksum aho-corasick 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "500909c4f87a9e52355b26626d890833e9e1d53ac566db76c36faa984b889699" -"checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" "checksum assert_matches 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e772942dccdf11b368c31e044e4fca9189f80a773d2f0808379de65894cbf57" -"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159" "checksum backtrace 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f551bc2ddd53aea015d453ef0b635af89444afa5ed2405dd0b2062ad5d600d80" "checksum backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d192fd129132fbc97497c1f2ec2c2c5174e376b95f535199ef4fe0a293d33842" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" -"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4" "checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8" "checksum bytes 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3941933da81d8717b427c2ddc2d73567cd15adb6c57514a2726d9ee598a5439a" "checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c" -"checksum clap 2.23.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf1114886d7cde2d6448517161d7db8d681a9a1c09f7d210f0b0864e48195f6" "checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850" "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" "checksum duct 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e2f3154a9285e24d7c3aba0dca9a13adf2ba6160cce3490b157c8b37a0f80e85" @@ -772,6 +709,7 @@ dependencies = [ "checksum jsonrpc-server-utils 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)" = "<none>" "checksum jsonrpc-ws-server 7.0.0 (git+https://github.com/faern/jsonrpc?branch=ws-close-handle)" = "<none>" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" "checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b" "checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b" "checksum libc 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)" = "88ee81885f9f04bff991e306fea7c1c60a5f0f9e409e99f6b40e3311a3363135" @@ -802,23 +740,19 @@ dependencies = [ "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" "checksum smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4f8266519bc1d17d0b5b16f6c21295625d562841c708f6376f49028a43e9c11e" "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" -"checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum syn 0.11.10 (registry+https://github.com/rust-lang/crates.io-index)" = "171b739972d9a1bfb169e8077238b51f9ebeaae4ff6e08072f7ba386a8802da2" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" -"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209" "checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a" "checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7" "checksum tokio-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "99e958104a67877907c1454386d5482fe8e965a55d60be834a15a44328e7dc76" "checksum tokio-io 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "48f55df1341bb92281f229a6030bc2abffde2c7a44c6d6b802b7687dd8be0775" "checksum unicode-bidi 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a078ebdd62c0e71a709c3d53d2af693fe09fe93fbff8344aebe289b78f9032" "checksum unicode-normalization 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e28fa37426fceeb5cf8f41ee273faa7c82c47dc8fba5853402841e665fcd86ff" -"checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3" -"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91" "checksum url 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f5ba8a749fb4479b043733416c244fa9d1d3af3d7c23804944651c8a448cb87e" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" -"checksum vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8cdc8b93bd0198ed872357fb2e667f7125646b1762f16d60b2c96350d361897" +"checksum uuid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5d0f5103675a280a926ec2f9b7bcc2ef49367df54e8c570c3311fec919f9a8b" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index ec561b5566..ebdd8443ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["mullvad_daemon", "talpid_openvpn_plugin", "openvpn_ffi", "talpid_cli", "talpid_ipc"] +members = ["mullvad_daemon", "talpid_openvpn_plugin", "openvpn_ffi", "talpid_ipc"] diff --git a/mullvad_daemon/Cargo.toml b/mullvad_daemon/Cargo.toml index d98c32b0b1..ce2ffafa98 100644 --- a/mullvad_daemon/Cargo.toml +++ b/mullvad_daemon/Cargo.toml @@ -15,9 +15,10 @@ jsonrpc-macros = { git = "https://github.com/faern/jsonrpc", branch = "ws-close- jsonrpc-pubsub = { git = "https://github.com/faern/jsonrpc", branch = "ws-close-handle" } jsonrpc-ws-server = { git = "https://github.com/faern/jsonrpc", branch = "ws-close-handle" } uuid = { version = "0.5", features = ["v4"] } +lazy_static = "0.2" -[dependencies.talpid_ipc] -path = "../talpid_ipc" +talpid_core = { path = "../talpid_core" } +talpid_ipc = { path = "../talpid_ipc" } [dev-dependencies] assert_matches = "1.0" diff --git a/mullvad_daemon/src/main.rs b/mullvad_daemon/src/main.rs index 95033b1564..fa1db7765c 100644 --- a/mullvad_daemon/src/main.rs +++ b/mullvad_daemon/src/main.rs @@ -8,37 +8,330 @@ extern crate serde; #[macro_use] extern crate serde_derive; -extern crate talpid_ipc; - extern crate jsonrpc_core; extern crate jsonrpc_pubsub; #[macro_use] extern crate jsonrpc_macros; extern crate jsonrpc_ws_server; +extern crate uuid; +#[macro_use] +extern crate lazy_static; + +extern crate talpid_core; +extern crate talpid_ipc; + +mod management_interface; +mod states; + +use management_interface::{ManagementInterfaceServer, TunnelCommand}; +use states::{SecurityState, TargetState}; + +use std::sync::{Arc, Mutex, mpsc}; +use std::thread; + +use talpid_core::net::RemoteAddr; +use talpid_core::tunnel::{self, TunnelEvent, TunnelMonitor}; + +error_chain!{ + errors { + /// The client is in the wrong state for the requested operation. Optimally the code should + /// be written in such a way so such states can't exist. + InvalidState { + description("Client is in an invalid state for the requested operation") + } + TunnelError(msg: &'static str) { + description("Error in the tunnel monitor") + display("Tunnel monitor error: {}", msg) + } + ManagementInterfaceError(msg: &'static str) { + description("Error in the management interface") + display("Management interface error: {}", msg) + } + } +} + +lazy_static! { + // Temporary store of hardcoded remotes. + static ref REMOTES: [RemoteAddr; 3] = [ + RemoteAddr::new("se5.mullvad.net", 1300), + RemoteAddr::new("se6.mullvad.net", 1300), + RemoteAddr::new("se7.mullvad.net", 1300), + ]; +} + +pub enum DaemonEvent { + TunnelEvent(TunnelEvent), + TunnelExit(tunnel::Result<()>), + ManagementInterfaceEvent(TunnelCommand), + ManagementInterfaceExit(talpid_ipc::Result<()>), +} + +impl From<TunnelEvent> for DaemonEvent { + fn from(tunnel_event: TunnelEvent) -> Self { + DaemonEvent::TunnelEvent(tunnel_event) + } +} + +impl From<TunnelCommand> for DaemonEvent { + fn from(tunnel_command: TunnelCommand) -> Self { + DaemonEvent::ManagementInterfaceEvent(tunnel_command) + } +} + +/// Represents the internal state of the actual tunnel. +// TODO(linus): Put the tunnel::CloseHandle into this state, so it can't exist when not running. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum TunnelState { + /// No tunnel is running. + NotRunning, + /// The tunnel has been started, but it is not established/functional. + Down, + /// The tunnel is up and working. + Up, +} + +impl TunnelState { + pub fn as_security_state(&self) -> SecurityState { + match *self { + TunnelState::Up => SecurityState::Secured, + _ => SecurityState::Unsecured, + } + } +} + + +struct Daemon { + state: TunnelState, + last_broadcasted_state: SecurityState, + target_state: TargetState, + rx: mpsc::Receiver<DaemonEvent>, + tx: mpsc::Sender<DaemonEvent>, + tunnel_close_handle: Option<tunnel::CloseHandle>, + management_interface_broadcaster: management_interface::EventBroadcaster, + + // Just for testing. A cyclic iterator iterating over the hardcoded remotes, + // picking a new one for each retry. + remote_iter: std::iter::Cycle<std::iter::Cloned<std::slice::Iter<'static, RemoteAddr>>>, +} + +impl Daemon { + pub fn new() -> Result<Self> { + let (tx, rx) = mpsc::channel(); + let management_interface_broadcaster = Self::start_management_interface(tx.clone())?; + Ok( + Daemon { + state: TunnelState::NotRunning, + last_broadcasted_state: SecurityState::Unsecured, + target_state: TargetState::Unsecured, + rx, + tx, + tunnel_close_handle: None, + management_interface_broadcaster, + remote_iter: REMOTES.iter().cloned().cycle(), + }, + ) + } + + // Starts the management interface and spawns a thread that will process it. + // Returns a handle that allows notifying all subscribers on events. + fn start_management_interface(event_tx: mpsc::Sender<DaemonEvent>) + -> Result<management_interface::EventBroadcaster> { + let server = Self::start_management_interface_server(event_tx.clone())?; + let event_broadcaster = server.event_broadcaster(); + Self::spawn_management_interface_wait_thread(server, event_tx); + Ok(event_broadcaster) + } + + fn start_management_interface_server(event_tx: mpsc::Sender<DaemonEvent>) + -> Result<ManagementInterfaceServer> { + let server = + ManagementInterfaceServer::start(event_tx.clone()) + .chain_err(|| ErrorKind::ManagementInterfaceError("Failed to start server"),)?; + info!( + "Mullvad management interface listening on {}", + server.address() + ); + Ok(server) + } + + fn spawn_management_interface_wait_thread(server: ManagementInterfaceServer, + exit_tx: mpsc::Sender<DaemonEvent>) { + thread::spawn( + move || { + let result = server.wait(); + debug!("Mullvad management interface shut down"); + let _ = exit_tx.send(DaemonEvent::ManagementInterfaceExit(result)); + }, + ); + } + + /// Consume the `Daemon` and run the main event loop. Blocks until an error happens. + pub fn run(mut self) -> Result<()> { + while let Ok(event) = self.rx.recv() { + self.handle_event(event)?; + } + Ok(()) + } + + fn handle_event(&mut self, event: DaemonEvent) -> Result<()> { + use DaemonEvent::*; + match event { + TunnelEvent(event) => Ok(self.handle_tunnel_event(event)), + TunnelExit(result) => self.handle_tunnel_exit(result), + ManagementInterfaceEvent(event) => self.handle_management_interface_event(event), + ManagementInterfaceExit(result) => self.handle_management_interface_exit(result), + } + } + + fn handle_tunnel_event(&mut self, tunnel_event: TunnelEvent) { + info!("Tunnel event: {:?}", tunnel_event); + let new_state = match tunnel_event { + TunnelEvent::Up => TunnelState::Up, + TunnelEvent::Down => TunnelState::Down, + }; + self.set_state(new_state); + } + + fn handle_tunnel_exit(&mut self, result: tunnel::Result<()>) -> Result<()> { + self.tunnel_close_handle = None; + if let Err(e) = result { + log_error("Tunnel exited in an unexpected way", e); + } + self.set_state(TunnelState::NotRunning); + self.apply_target_state() + } + + fn handle_management_interface_event(&mut self, event: TunnelCommand) -> Result<()> { + match event { + TunnelCommand::SetTargetState(state) => self.set_target_state(state)?, + TunnelCommand::GetState(tx) => { + if let Err(_) = tx.send(self.last_broadcasted_state) { + warn!("Unable to send current state to management interface client",); + } + } + } + Ok(()) + } + + fn handle_management_interface_exit(&self, result: talpid_ipc::Result<()>) -> Result<()> { + let error = ErrorKind::ManagementInterfaceError("Server exited unexpectedly"); + match result { + Ok(()) => Err(error.into()), + e => e.chain_err(|| error), + } + } + + /// Update the state of the client. If it changed, notify the subscribers. + fn set_state(&mut self, new_state: TunnelState) { + if new_state != self.state { + self.state = new_state; + let new_security_state = self.state.as_security_state(); + if self.last_broadcasted_state != new_security_state { + self.last_broadcasted_state = new_security_state; + self.management_interface_broadcaster.notify_new_state(new_security_state); + } + } + } + + /// Set the target state of the client. If it changed trigger the operations needed to progress + /// towards that state. + fn set_target_state(&mut self, new_state: TargetState) -> Result<()> { + if new_state != self.target_state { + self.target_state = new_state; + self.apply_target_state() + } else { + Ok(()) + } + } -pub mod ipc_api; -pub mod mock_ipc; + fn apply_target_state(&mut self) -> Result<()> { + match (self.target_state, self.state) { + (TargetState::Secured, TunnelState::NotRunning) => { + debug!("Triggering tunnel start"); + self.start_tunnel() + } + (TargetState::Unsecured, TunnelState::Down) | + (TargetState::Unsecured, TunnelState::Up) => { + if let Some(close_handle) = self.tunnel_close_handle.take() { + debug!("Triggering tunnel stop"); + // This close operation will block until the tunnel is dead. + close_handle + .close() + .chain_err(|| ErrorKind::TunnelError("Unable to kill tunnel")) + } else { + Ok(()) + } + } + (target_state, state) => { + trace!( + "apply_target_state does nothing on TargetState::{:?} TunnelState::{:?}", + target_state, + state + ); + Ok(()) + } + } + } + + fn start_tunnel(&mut self) -> Result<()> { + ensure!( + self.state == TunnelState::NotRunning, + ErrorKind::InvalidState + ); + let remote = self.remote_iter.next().unwrap(); + let tunnel_monitor = self.spawn_tunnel_monitor(remote)?; + self.tunnel_close_handle = Some(tunnel_monitor.close_handle()); + self.spawn_tunnel_monitor_wait_thread(tunnel_monitor); + + self.set_state(TunnelState::Down); + Ok(()) + } + + fn spawn_tunnel_monitor(&self, remote: RemoteAddr) -> Result<TunnelMonitor> { + // Must wrap the channel in a Mutex because TunnelMonitor forces the closure to be Sync + let event_tx = Arc::new(Mutex::new(self.tx.clone())); + let on_tunnel_event = move |event| { + let _ = event_tx.lock().unwrap().send(DaemonEvent::TunnelEvent(event)); + }; + TunnelMonitor::new(remote, on_tunnel_event) + .chain_err(|| ErrorKind::TunnelError("Unable to start tunnel monitor")) + } + + fn spawn_tunnel_monitor_wait_thread(&self, tunnel_monitor: TunnelMonitor) { + let error_tx = self.tx.clone(); + thread::spawn( + move || { + let result = tunnel_monitor.wait(); + let _ = error_tx.send(DaemonEvent::TunnelExit(result)); + trace!("Tunnel monitor thread exit"); + }, + ); + } +} + + +fn log_error<E>(msg: &str, error: E) + where E: error_chain::ChainedError +{ + error!("{}: {}", msg, error); + for e in error.iter().skip(1) { + error!("Caused by {}", e); + } +} -error_chain!{} quick_main!(run); fn run() -> Result<()> { init_logger()?; - let server = start_ipc()?; - info!("Mullvad daemon listening on {}", server.address()); - main_loop(server) + let daemon = Daemon::new().chain_err(|| "Unable to initialize daemon")?; + daemon.run()?; + + debug!("Mullvad daemon is quitting"); + Ok(()) } fn init_logger() -> Result<()> { env_logger::init().chain_err(|| "Failed to bootstrap logging system") } - -fn start_ipc() -> Result<mock_ipc::IpcServer> { - mock_ipc::IpcServer::start().chain_err(|| "Failed to start IPC server") -} - -fn main_loop(server: mock_ipc::IpcServer) -> Result<()> { - server.wait().chain_err(|| "Error while waiting for server to process") -} diff --git a/mullvad_daemon/src/states.rs b/mullvad_daemon/src/states.rs index e50015c53b..10e543c58a 100644 --- a/mullvad_daemon/src/states.rs +++ b/mullvad_daemon/src/states.rs @@ -14,7 +14,7 @@ pub enum SecurityState { /// Represents the state the client strives towards. /// When in `Secured`, the client should keep the computer from leaking and try to /// establish a VPN tunnel if it is not up. -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum TargetState { Unsecured, Secured, diff --git a/talpid_core/Cargo.toml b/talpid_core/Cargo.toml index 4b3afa4d80..573cc0b328 100644 --- a/talpid_core/Cargo.toml +++ b/talpid_core/Cargo.toml @@ -8,6 +8,7 @@ description = "Core backend functionality of the Mullvad VPN client" duct = "0.9.1" error-chain = "0.10" log = "0.3" +lazy_static = "0.2" jsonrpc-core = { git = "https://github.com/faern/jsonrpc", branch = "ws-close-handle" } jsonrpc-macros = { git = "https://github.com/faern/jsonrpc", branch = "ws-close-handle" } diff --git a/talpid_core/src/lib.rs b/talpid_core/src/lib.rs index 8a053bffa8..5496d038ce 100644 --- a/talpid_core/src/lib.rs +++ b/talpid_core/src/lib.rs @@ -9,6 +9,8 @@ extern crate assert_matches; extern crate duct; #[macro_use] +extern crate lazy_static; +#[macro_use] extern crate log; #[macro_use] diff --git a/talpid_core/src/tunnel/mod.rs b/talpid_core/src/tunnel/mod.rs index 3c955c563d..aead09b13b 100644 --- a/talpid_core/src/tunnel/mod.rs +++ b/talpid_core/src/tunnel/mod.rs @@ -90,7 +90,7 @@ pub struct CloseHandle(OpenVpnCloseHandle); impl CloseHandle { /// Closes the underlying tunnel, making the `TunnelMonitor::wait` method return. - pub fn close(&self) -> io::Result<()> { + pub fn close(self) -> io::Result<()> { self.0.close() } } diff --git a/talpid_core/src/tunnel/openvpn.rs b/talpid_core/src/tunnel/openvpn.rs index cf33484f38..08c2c14a32 100644 --- a/talpid_core/src/tunnel/openvpn.rs +++ b/talpid_core/src/tunnel/openvpn.rs @@ -2,14 +2,14 @@ use duct; use jsonrpc_core::{Error, IoHandler}; use openvpn_ffi::{OpenVpnEnv, OpenVpnPluginEvent}; use process::openvpn::OpenVpnCommand; -use std::io; +use std::io; use std::path::Path; -use std::process; use std::result::Result as StdResult; use std::sync::{Arc, mpsc}; use std::sync::atomic::{AtomicBool, Ordering}; use std::thread; +use std::time::Duration; use talpid_ipc; @@ -31,6 +31,11 @@ mod errors { pub use self::errors::*; +lazy_static!{ + static ref OPENVPN_DIE_TIMEOUT: Duration = Duration::from_secs(2); +} + + /// Struct for monitoring an OpenVPN process. pub struct OpenVpnMonitor { child: Arc<duct::Handle>, @@ -105,7 +110,7 @@ impl OpenVpnMonitor { /// returned this returns the earliest result. fn wait_result(&mut self) -> WaitResult { let child_wait_handle = self.child.clone(); - let child_kill_handle = self.child.clone(); + let child_close_handle = self.close_handle(); let event_dispatcher = self.event_dispatcher.take().unwrap(); let dispatcher_handle = event_dispatcher.close_handle(); @@ -123,7 +128,7 @@ impl OpenVpnMonitor { move || { let result = event_dispatcher.wait(); dispatcher_tx.send(WaitResult::EventDispatcher(result)).unwrap(); - let _ = child_kill_handle.kill(); + let _ = child_close_handle.close(); }, ); @@ -141,15 +146,28 @@ pub struct OpenVpnCloseHandle { impl OpenVpnCloseHandle { /// Kills the underlying OpenVPN process, making the `OpenVpnMonitor::wait` method return. - pub fn close(&self) -> io::Result<()> { - self.closed.store(true, Ordering::SeqCst); + pub fn close(self) -> io::Result<()> { + if !self.closed.swap(true, Ordering::SeqCst) { + self.kill_openvpn() + } else { + Ok(()) + } + } + + #[cfg(unix)] + fn kill_openvpn(self) -> io::Result<()> { + ::process::unix::nice_kill(self.child, *OPENVPN_DIE_TIMEOUT) + } + + #[cfg(not(unix))] + fn kill_openvpn(self) -> io::Result<()> { self.child.kill() } } /// Internal enum to differentiate between if the child process or the event dispatcher died first. enum WaitResult { - Child(io::Result<process::ExitStatus>), + Child(io::Result<::std::process::ExitStatus>), EventDispatcher(talpid_ipc::Result<()>), } |
