summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2017-07-10 17:08:35 +0200
committerLinus Färnstrand <linus@mullvad.net>2017-07-10 17:08:35 +0200
commit1b0c71bc75332d6f10d172e158988d3076ec5e5c (patch)
tree08fa9191ad208b5cfc2259691e7368ce87f0540e
parent5e82c3b62b7bac95e27f7c7782ceed6379537643 (diff)
parent22d6e1347ee8803bf07388d461a90dbeb8fe5c7c (diff)
downloadmullvadvpn-1b0c71bc75332d6f10d172e158988d3076ec5e5c.tar.xz
mullvadvpn-1b0c71bc75332d6f10d172e158988d3076ec5e5c.zip
Merge branch 'cli-support-connect-disconnect'
-rw-r--r--Cargo.lock10
-rw-r--r--Cargo.toml10
-rw-r--r--mullvad_cli/Cargo.toml4
-rw-r--r--mullvad_cli/src/cmds/account.rs13
-rw-r--r--mullvad_cli/src/cmds/connect.rs22
-rw-r--r--mullvad_cli/src/cmds/disconnect.rs22
-rw-r--r--mullvad_cli/src/cmds/mod.rs18
-rw-r--r--mullvad_cli/src/cmds/status.rs29
-rw-r--r--mullvad_cli/src/main.rs1
-rw-r--r--mullvad_cli/src/rpc.rs15
-rw-r--r--mullvad_daemon/Cargo.toml1
-rw-r--r--mullvad_daemon/src/main.rs4
-rw-r--r--mullvad_daemon/src/management_interface.rs2
-rw-r--r--mullvad_types/Cargo.toml8
-rw-r--r--mullvad_types/src/lib.rs5
-rw-r--r--mullvad_types/src/states.rs (renamed from mullvad_daemon/src/states.rs)6
-rw-r--r--talpid_ipc/src/client.rs76
-rw-r--r--talpid_ipc/tests/ipc-client-server.rs5
-rw-r--r--talpid_openvpn_plugin/src/processing.rs2
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", &params));
- }
-}
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)
}
}