diff options
| -rw-r--r-- | Cargo.lock | 10 | ||||
| -rw-r--r-- | Cargo.toml | 10 | ||||
| -rw-r--r-- | mullvad_cli/Cargo.toml | 4 | ||||
| -rw-r--r-- | mullvad_cli/src/cmds/account.rs | 13 | ||||
| -rw-r--r-- | mullvad_cli/src/cmds/connect.rs | 22 | ||||
| -rw-r--r-- | mullvad_cli/src/cmds/disconnect.rs | 22 | ||||
| -rw-r--r-- | mullvad_cli/src/cmds/mod.rs | 18 | ||||
| -rw-r--r-- | mullvad_cli/src/cmds/status.rs | 29 | ||||
| -rw-r--r-- | mullvad_cli/src/main.rs | 1 | ||||
| -rw-r--r-- | mullvad_cli/src/rpc.rs | 15 | ||||
| -rw-r--r-- | mullvad_daemon/Cargo.toml | 1 | ||||
| -rw-r--r-- | mullvad_daemon/src/main.rs | 4 | ||||
| -rw-r--r-- | mullvad_daemon/src/management_interface.rs | 2 | ||||
| -rw-r--r-- | mullvad_types/Cargo.toml | 8 | ||||
| -rw-r--r-- | mullvad_types/src/lib.rs | 5 | ||||
| -rw-r--r-- | mullvad_types/src/states.rs (renamed from mullvad_daemon/src/states.rs) | 6 | ||||
| -rw-r--r-- | talpid_ipc/src/client.rs | 76 | ||||
| -rw-r--r-- | talpid_ipc/tests/ipc-client-server.rs | 5 | ||||
| -rw-r--r-- | talpid_openvpn_plugin/src/processing.rs | 2 |
19 files changed, 183 insertions, 70 deletions
diff --git a/Cargo.lock b/Cargo.lock index 7fd6a496da..0de80b44fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -366,6 +366,7 @@ dependencies = [ "env_logger 0.4.3 (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.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mullvad_types 0.1.0", "serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "talpid_ipc 0.1.0", @@ -385,6 +386,7 @@ dependencies = [ "jsonrpc-ws-server 7.0.1 (git+https://github.com/paritytech/jsonrpc)", "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mullvad_types 0.1.0", "serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "simple-signal 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -394,6 +396,14 @@ dependencies = [ ] [[package]] +name = "mullvad_types" +version = "0.1.0" +dependencies = [ + "serde 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "net2" version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml index 2ade69a1d5..00105baebc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,10 @@ [workspace] -members = ["mullvad_daemon", "mullvad_cli", "talpid_openvpn_plugin", "openvpn_ffi", "talpid_ipc"] +members = [ + "mullvad_daemon", + "mullvad_cli", + "mullvad_types", + "talpid_openvpn_plugin", + "talpid_core", + "talpid_ipc", + "openvpn_ffi", +] diff --git a/mullvad_cli/Cargo.toml b/mullvad_cli/Cargo.toml index 5d758482c9..00eda111e9 100644 --- a/mullvad_cli/Cargo.toml +++ b/mullvad_cli/Cargo.toml @@ -12,6 +12,8 @@ clap = "2.20" error-chain = "0.10" log = "0.3" env_logger = "0.4" -talpid_ipc = { path = "../talpid_ipc" } serde = "1.0" serde_json = "1.0" + +mullvad_types = { path = "../mullvad_types" } +talpid_ipc = { path = "../talpid_ipc" } diff --git a/mullvad_cli/src/cmds/account.rs b/mullvad_cli/src/cmds/account.rs index c6300e0eb3..fcfe9f9901 100644 --- a/mullvad_cli/src/cmds/account.rs +++ b/mullvad_cli/src/cmds/account.rs @@ -2,7 +2,6 @@ use Command; use Result; use clap; use rpc; -use serde_json; pub struct Account; @@ -39,17 +38,17 @@ impl Command for Account { impl Account { fn set(&self, token: &str) -> Result<()> { rpc::call("set_account", &[token]).map( - |_| { - println!("Mullvad account {} set", token); + |_: Option<()>| { + println!("Mullvad account \"{}\" set", token); }, ) } fn get(&self) -> Result<()> { - match rpc::call("get_account", &[] as &[u8; 0])? { - serde_json::Value::String(token) => println!("Mullvad account: {:?}", token), - serde_json::Value::Null => println!("No account configured"), - _ => bail!("Unable to fetch account token"), + let token: Option<String> = rpc::call("get_account", &[] as &[u8; 0])?; + match token { + Some(token) => println!("Mullvad account: {:?}", token), + None => println!("No account configured"), } Ok(()) } diff --git a/mullvad_cli/src/cmds/connect.rs b/mullvad_cli/src/cmds/connect.rs new file mode 100644 index 0000000000..4847a0805a --- /dev/null +++ b/mullvad_cli/src/cmds/connect.rs @@ -0,0 +1,22 @@ +use Command; +use Result; +use clap; +use rpc; + +pub struct Connect; + +impl Command for Connect { + fn name(&self) -> &'static str { + "connect" + } + + fn clap_subcommand(&self) -> clap::App<'static, 'static> { + clap::SubCommand::with_name(self.name()) + .about("Command the client to start establishing a VPN tunnel") + } + + fn run(&self, _matches: &clap::ArgMatches) -> Result<()> { + let _response: Option<()> = rpc::call("connect", &[] as &[u8; 0])?; + Ok(()) + } +} diff --git a/mullvad_cli/src/cmds/disconnect.rs b/mullvad_cli/src/cmds/disconnect.rs new file mode 100644 index 0000000000..543aa9d0cc --- /dev/null +++ b/mullvad_cli/src/cmds/disconnect.rs @@ -0,0 +1,22 @@ +use Command; +use Result; +use clap; +use rpc; + +pub struct Disconnect; + +impl Command for Disconnect { + fn name(&self) -> &'static str { + "disconnect" + } + + fn clap_subcommand(&self) -> clap::App<'static, 'static> { + clap::SubCommand::with_name(self.name()) + .about("Command the client to disconnect the VPN tunnel") + } + + fn run(&self, _matches: &clap::ArgMatches) -> Result<()> { + let _response: Option<()> = rpc::call("disconnect", &[] as &[u8; 0])?; + Ok(()) + } +} diff --git a/mullvad_cli/src/cmds/mod.rs b/mullvad_cli/src/cmds/mod.rs index 3f881bcfb8..c621c0840a 100644 --- a/mullvad_cli/src/cmds/mod.rs +++ b/mullvad_cli/src/cmds/mod.rs @@ -2,11 +2,25 @@ use Command; use std::collections::HashMap; mod account; -pub use self::account::*; +pub use self::account::Account; + +mod status; +pub use self::status::Status; + +mod connect; +pub use self::connect::Connect; + +mod disconnect; +pub use self::disconnect::Disconnect; /// Returns a map of all available subcommands with their name as key. pub fn get_commands() -> HashMap<&'static str, Box<Command>> { - let commands = vec![Box::new(Account) as Box<Command>]; + let commands: Vec<Box<Command>> = vec![ + Box::new(Account), + Box::new(Status), + Box::new(Connect), + Box::new(Disconnect), + ]; let mut map = HashMap::new(); for cmd in commands { if let Some(_) = map.insert(cmd.name(), cmd) { diff --git a/mullvad_cli/src/cmds/status.rs b/mullvad_cli/src/cmds/status.rs new file mode 100644 index 0000000000..e02773285f --- /dev/null +++ b/mullvad_cli/src/cmds/status.rs @@ -0,0 +1,29 @@ +use Command; +use Result; +use clap; + +use mullvad_types::states::{DaemonState, SecurityState, TargetState}; +use rpc; + +pub struct Status; + +impl Command for Status { + fn name(&self) -> &'static str { + "status" + } + + fn clap_subcommand(&self) -> clap::App<'static, 'static> { + clap::SubCommand::with_name(self.name()).about("View the state of the VPN tunnel") + } + + fn run(&self, _matches: &clap::ArgMatches) -> Result<()> { + let state: DaemonState = rpc::call("get_state", &[] as &[u8; 0])?; + match (state.state, state.target_state) { + (SecurityState::Unsecured, TargetState::Unsecured) => println!("Disconnected"), + (SecurityState::Unsecured, TargetState::Secured) => println!("Connecting..."), + (SecurityState::Secured, TargetState::Unsecured) => println!("Disconnecting..."), + (SecurityState::Secured, TargetState::Secured) => println!("Connected"), + } + Ok(()) + } +} diff --git a/mullvad_cli/src/main.rs b/mullvad_cli/src/main.rs index 3d49be3964..08d5459dd7 100644 --- a/mullvad_cli/src/main.rs +++ b/mullvad_cli/src/main.rs @@ -1,6 +1,7 @@ // `error_chain!` can recurse deeply #![recursion_limit = "1024"] +extern crate mullvad_types; extern crate talpid_ipc; #[macro_use] extern crate clap; diff --git a/mullvad_cli/src/rpc.rs b/mullvad_cli/src/rpc.rs index 11a6da33fb..73c0c64dc1 100644 --- a/mullvad_cli/src/rpc.rs +++ b/mullvad_cli/src/rpc.rs @@ -2,19 +2,26 @@ use {Result, ResultExt}; use serde; -use serde_json; use std::fs::File; use std::io::Read; use talpid_ipc::WsIpcClient; -pub fn call<T>(method: &str, args: &T) -> Result<serde_json::Value> - where T: serde::Serialize +pub fn call<T, O>(method: &str, args: &T) -> Result<O> + where T: serde::Serialize, + O: for<'de> serde::Deserialize<'de> +{ + call_internal(method, args).chain_err(|| "Unable to call backend over RPC") +} + +pub fn call_internal<T, O>(method: &str, args: &T) -> Result<O> + where T: serde::Serialize, + O: for<'de> serde::Deserialize<'de> { let address = read_rpc_address()?; info!("Using RPC address {}", address); let mut rpc_client = WsIpcClient::new(address) .chain_err(|| "Unable to create RPC client")?; - rpc_client.call(method, args).chain_err(|| "Unable to call RPC method") + rpc_client.call(method, args).chain_err(|| format!("Unable to call RPC method {}", method)) } fn read_rpc_address() -> Result<String> { diff --git a/mullvad_daemon/Cargo.toml b/mullvad_daemon/Cargo.toml index 165ecfd735..73c9fe40dd 100644 --- a/mullvad_daemon/Cargo.toml +++ b/mullvad_daemon/Cargo.toml @@ -20,6 +20,7 @@ jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc" } uuid = { version = "0.5", features = ["v4"] } lazy_static = "0.2" +mullvad_types = { path = "../mullvad_types" } talpid_core = { path = "../talpid_core" } talpid_ipc = { path = "../talpid_ipc" } diff --git a/mullvad_daemon/src/main.rs b/mullvad_daemon/src/main.rs index 1b5ab91cdc..51bf264465 100644 --- a/mullvad_daemon/src/main.rs +++ b/mullvad_daemon/src/main.rs @@ -17,16 +17,16 @@ extern crate uuid; #[macro_use] extern crate lazy_static; +extern crate mullvad_types; extern crate talpid_core; extern crate talpid_ipc; mod management_interface; -mod states; mod rpc_info; mod shutdown; use management_interface::{ManagementInterfaceServer, TunnelCommand}; -use states::{DaemonState, SecurityState, TargetState}; +use mullvad_types::states::{DaemonState, SecurityState, TargetState}; use std::io; use std::sync::{Arc, Mutex, mpsc}; diff --git a/mullvad_daemon/src/management_interface.rs b/mullvad_daemon/src/management_interface.rs index 65e6e9b788..ffba3dca0f 100644 --- a/mullvad_daemon/src/management_interface.rs +++ b/mullvad_daemon/src/management_interface.rs @@ -5,9 +5,9 @@ use jsonrpc_core::futures::{BoxFuture, Future, future, sync}; use jsonrpc_macros::pubsub; use jsonrpc_pubsub::{PubSubHandler, PubSubMetadata, Session, SubscriptionId}; use jsonrpc_ws_server; +use mullvad_types::states::{DaemonState, TargetState}; use serde; -use states::{DaemonState, TargetState}; use std::collections::HashMap; use std::collections::hash_map::Entry; diff --git a/mullvad_types/Cargo.toml b/mullvad_types/Cargo.toml new file mode 100644 index 0000000000..0485cd1bc2 --- /dev/null +++ b/mullvad_types/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "mullvad_types" +version = "0.1.0" +authors = ["Linus Färnstrand <linus@mullvad.net>"] + +[dependencies] +serde_derive = "1.0" +serde = "1.0" diff --git a/mullvad_types/src/lib.rs b/mullvad_types/src/lib.rs new file mode 100644 index 0000000000..602a43cb29 --- /dev/null +++ b/mullvad_types/src/lib.rs @@ -0,0 +1,5 @@ +#[macro_use] +extern crate serde_derive; +extern crate serde; + +pub mod states; diff --git a/mullvad_daemon/src/states.rs b/mullvad_types/src/states.rs index eb7520d36c..e40ab94b0b 100644 --- a/mullvad_daemon/src/states.rs +++ b/mullvad_types/src/states.rs @@ -1,4 +1,4 @@ -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] pub struct DaemonState { pub state: SecurityState, pub target_state: TargetState, @@ -10,7 +10,7 @@ pub struct DaemonState { /// but disconnected. The frontend should probably reflect these states in some way. I think it /// be reasonable to have three states, since unsecured but tunnel is up is probably an invalid /// state. -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum SecurityState { Unsecured, @@ -20,7 +20,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, Copy, Clone, Eq, PartialEq, Hash, Serialize)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum TargetState { Unsecured, diff --git a/talpid_ipc/src/client.rs b/talpid_ipc/src/client.rs index 763d5b225e..5d63db24f9 100644 --- a/talpid_ipc/src/client.rs +++ b/talpid_ipc/src/client.rs @@ -10,13 +10,13 @@ mod errors { pub use self::errors::*; -struct Factory { +struct Factory<O: for<'de> serde::Deserialize<'de>> { request: String, - result_tx: mpsc::Sender<Result<serde_json::Value>>, + result_tx: mpsc::Sender<Result<O>>, } -impl ws::Factory for Factory { - type Handler = Handler; +impl<O: for<'de> serde::Deserialize<'de>> ws::Factory for Factory<O> { + type Handler = Handler<O>; fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler { debug!("Sending: {}", self.request); @@ -32,39 +32,42 @@ impl ws::Factory for Factory { } -struct Handler { +struct Handler<O: for<'de> serde::Deserialize<'de>> { sender: ws::Sender, - result_tx: mpsc::Sender<Result<serde_json::Value>>, + result_tx: mpsc::Sender<Result<O>>, } -impl Handler { - fn validate_reply(&self, msg: ws::Message) -> ws::Result<serde_json::Value> { - let json: serde_json::Value = match msg { - ws::Message::Text(s) => serde_json::from_str(&s), - ws::Message::Binary(b) => serde_json::from_slice(&b), - } - .map_err(|e| ws::Error::from(Box::new(e)))?; - debug!("JSON response: {}", json); - let result = - match json { - serde_json::Value::Object(mut map) => map.remove("result"), - _ => None, +impl<O: for<'de> serde::Deserialize<'de>> Handler<O> { + fn parse_reply(&self, msg: ws::Message) -> Result<O> { + let json: serde_json::Value = + match msg { + ws::Message::Text(s) => serde_json::from_str(&s), + ws::Message::Binary(b) => serde_json::from_slice(&b), } - .ok_or(ws::Error::new(ws::ErrorKind::Protocol, "Invalid reply, no 'result'"),)?; - // TODO(linus): Properly validate reply - Ok(result) + .chain_err(|| "Unable to deserialize ws message as JSON")?; + let result: Option<serde_json::Value> = match json { + serde_json::Value::Object(mut map) => map.remove("result"), + _ => None, + }; + match result { + Some(result) => { + serde_json::from_value(result) + .chain_err(|| "Unable to deserialize result into derisred type") + } + None => bail!("Invalid reply, no 'result' field"), + } } } -impl ws::Handler for Handler { +impl<O: for<'de> serde::Deserialize<'de>> ws::Handler for Handler<O> { fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> { debug!("WsIpcClient incoming message: {:?}", msg); - let reply = self.validate_reply(msg)?; + let reply_result = self.parse_reply(msg); let close_result = self.sender.close(ws::CloseCode::Normal); if let Err(e) = close_result.chain_err(|| "Unable to close WebSocket") { self.result_tx.send(Err(e)).unwrap(); } - self.result_tx.send(Ok(reply)).unwrap(); + self.result_tx.send(reply_result).unwrap(); Ok(()) } } @@ -81,8 +84,9 @@ impl WsIpcClient { Ok(WsIpcClient { url, next_id: 1 }) } - pub fn call<T>(&mut self, method: &str, params: &T) -> Result<serde_json::Value> - where T: serde::Serialize + pub fn call<T, O>(&mut self, method: &str, params: &T) -> Result<O> + where T: serde::Serialize, + O: for<'de> serde::Deserialize<'de> { let (result_tx, result_rx) = mpsc::channel(); let factory = Factory { @@ -117,23 +121,3 @@ impl WsIpcClient { id } } - - -#[cfg(test)] -mod tests { - extern crate env_logger; - use super::*; - - // TODO(linus): This is not a test. Just an ugly way to quickly test the client implementation - #[test] - #[ignore] - fn ws_ipc_client_tester() { - env_logger::init().unwrap(); - - let mut ws = WsIpcClient::new("ws://127.0.0.1:INSERT_PORT".to_owned()).unwrap(); - let event = serde_json::Value::String("Up".to_owned()); - let env = serde_json::Value::Object(serde_json::Map::new()); - let params = serde_json::Value::Array(vec![event, env]); - println!("CALL RESULT: {:?}", ws.call("openvpn_event", ¶ms)); - } -} diff --git a/talpid_ipc/tests/ipc-client-server.rs b/talpid_ipc/tests/ipc-client-server.rs index a41bce282a..654eff4dcc 100644 --- a/talpid_ipc/tests/ipc-client-server.rs +++ b/talpid_ipc/tests/ipc-client-server.rs @@ -36,7 +36,7 @@ fn ipc_client_server() { let server_id = server.address().to_owned(); let mut client = create_client(server_id); - client.call("foo", &[97]).unwrap(); + let _result: () = client.call("foo", &[97]).unwrap(); assert_eq!(Ok(97), rx.recv_timeout(Duration::from_millis(500))); } @@ -49,7 +49,8 @@ fn ipc_client_invalid_url() { #[test] fn ipc_client_invalid_method() { let mut client = create_client("ws://127.0.0.1:9876".to_owned()); - assert_matches!(client.call("invalid_method", &[0]), Err(_)); + let result: Result<(), _> = client.call("invalid_method", &[0]); + assert_matches!(result, Err(_)); } fn create_server() -> (talpid_ipc::IpcServer, mpsc::Receiver<i64>) { diff --git a/talpid_openvpn_plugin/src/processing.rs b/talpid_openvpn_plugin/src/processing.rs index 8b1591f63d..e33097c633 100644 --- a/talpid_openvpn_plugin/src/processing.rs +++ b/talpid_openvpn_plugin/src/processing.rs @@ -30,7 +30,7 @@ impl EventProcessor { trace!("Processing \"{:?}\" event", event); self.ipc_client .call("openvpn_event", &(event, env)) - .map(|_| ()) + .map(|_: Option<()>| ()) .chain_err(|| ErrorKind::IpcSendingError) } } |
