summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2017-05-15 12:48:11 +0200
committerLinus Färnstrand <linus@mullvad.net>2017-05-15 12:48:11 +0200
commitb3ee3c1e75aa68462595aa9d80ea758b76d1aa82 (patch)
tree0fa16001c0af8d56f64255e0edecc4d912231492
parent0ce25aeb01ffc054033388d97b2974f04335b859 (diff)
parent68078efa3a462fa0c96797458c14896803f5ea76 (diff)
downloadmullvadvpn-b3ee3c1e75aa68462595aa9d80ea758b76d1aa82.tar.xz
mullvadvpn-b3ee3c1e75aa68462595aa9d80ea758b76d1aa82.zip
Merge branch 'ws-jsonrpc-ipc'
-rw-r--r--Cargo.lock71
-rw-r--r--mullvad_daemon/Cargo.toml4
-rw-r--r--mullvad_daemon/src/frontend_ipc_router.rs145
-rw-r--r--mullvad_daemon/src/ipc_api.rs90
-rw-r--r--mullvad_daemon/src/main.rs27
-rw-r--r--mullvad_daemon/src/mock_ipc.rs230
-rw-r--r--talpid_ipc/src/lib.rs2
7 files changed, 412 insertions, 157 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 71e46c965a..b7d4c3f510 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -233,6 +233,26 @@ dependencies = [
]
[[package]]
+name = "jsonrpc-macros"
+version = "7.0.0"
+source = "git+https://github.com/paritytech/jsonrpc#05eca1e1dd80f9b3bd8f1738ca5680d078021c27"
+dependencies = [
+ "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc)",
+ "jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc)",
+ "serde 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "jsonrpc-pubsub"
+version = "7.0.0"
+source = "git+https://github.com/paritytech/jsonrpc#05eca1e1dd80f9b3bd8f1738ca5680d078021c27"
+dependencies = [
+ "jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc)",
+ "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "parking_lot 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "jsonrpc-server-utils"
version = "7.0.0"
source = "git+https://github.com/paritytech/jsonrpc#05eca1e1dd80f9b3bd8f1738ca5680d078021c27"
@@ -342,10 +362,12 @@ dependencies = [
"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)",
"jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc)",
+ "jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc)",
+ "jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc)",
+ "jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc)",
"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)",
- "serde_json 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"talpid_ipc 0.1.0",
]
@@ -398,6 +420,36 @@ dependencies = [
]
[[package]]
+name = "owning_ref"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.4.2"
+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)",
+ "parking_lot_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.2.1"
+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)",
+ "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smallvec 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "pkg-config"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -532,6 +584,16 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "smallvec"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "stable_deref_trait"
+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"
@@ -792,6 +854,8 @@ dependencies = [
"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be"
"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
"checksum jsonrpc-core 7.0.0 (git+https://github.com/paritytech/jsonrpc)" = "<none>"
+"checksum jsonrpc-macros 7.0.0 (git+https://github.com/paritytech/jsonrpc)" = "<none>"
+"checksum jsonrpc-pubsub 7.0.0 (git+https://github.com/paritytech/jsonrpc)" = "<none>"
"checksum jsonrpc-server-utils 7.0.0 (git+https://github.com/paritytech/jsonrpc)" = "<none>"
"checksum jsonrpc-ws-server 7.0.0 (git+https://github.com/paritytech/jsonrpc)" = "<none>"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
@@ -808,6 +872,9 @@ dependencies = [
"checksum nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47e49f6982987135c5e9620ab317623e723bd06738fd85377e8d55f57c8b6487"
"checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99"
"checksum os_pipe 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "998bfbb3042e715190fe2a41abfa047d7e8cb81374d2977d7f100eacd8619cb1"
+"checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37"
+"checksum parking_lot 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8b45855d69c6ad53fdbd2f163b33506cf015befef980f7c13a9d60c12a111241"
+"checksum parking_lot_core 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "56a19dcbb5d1e32b6cccb8a9aa1fc2a38418c8699652e735e2bf391a3dc0aa16"
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
"checksum quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a"
"checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d"
@@ -826,6 +893,8 @@ dependencies = [
"checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c"
"checksum shared_child 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "099b38928dbe4a0a01fcd8c233183072f14a7d126a34bed05880869be66e14cc"
"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"
diff --git a/mullvad_daemon/Cargo.toml b/mullvad_daemon/Cargo.toml
index 42a00a29f6..c3f4e22ae5 100644
--- a/mullvad_daemon/Cargo.toml
+++ b/mullvad_daemon/Cargo.toml
@@ -8,10 +8,12 @@ description = "The meat of Mullvad, the core if you wish"
error-chain = "0.10"
serde = "1.0"
serde_derive = "1.0"
-serde_json = "1.0"
log = "0.3"
env_logger = "0.4"
jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc" }
+jsonrpc-macros = { git = "https://github.com/paritytech/jsonrpc" }
+jsonrpc-pubsub = { git = "https://github.com/paritytech/jsonrpc" }
+jsonrpc-ws-server = { git = "https://github.com/paritytech/jsonrpc" }
[dependencies.talpid_ipc]
path = "../talpid_ipc"
diff --git a/mullvad_daemon/src/frontend_ipc_router.rs b/mullvad_daemon/src/frontend_ipc_router.rs
deleted file mode 100644
index 1c1b74950c..0000000000
--- a/mullvad_daemon/src/frontend_ipc_router.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-extern crate jsonrpc_core;
-extern crate serde_json;
-
-use serde;
-
-static mut MOCK_IS_CONNECTED: bool = false;
-type Result<T> = ::std::result::Result<T, String>;
-
-pub fn build_router() -> jsonrpc_core::IoHandler {
- let mut router = jsonrpc_core::IoHandler::default();
-
- add_route(&mut router, "login", mock_login);
- add_route(&mut router, "logout", mock_logout);
- add_route(&mut router, "connect", mock_connect);
- add_route(&mut router, "disconnect", mock_disconnect);
- add_route(&mut router, "get_connection", mock_get_connection_info);
- add_route(&mut router, "get_location", mock_get_location);
-
- router
-}
-
-fn add_route<T, U, F>(router: &mut jsonrpc_core::IoHandler, method: &str, handler: F)
- where for<'de> T: serde::Deserialize<'de>,
- U: serde::Serialize + 'static,
- F: Fn(&T) -> Result<U> + Send + Sync + 'static
-{
- let c = move |params: jsonrpc_core::params::Params| {
- println!("Got rpc request with params {:?}", params);
- let parsed_params: T = params.parse()?;
-
- let response: U = handler(&parsed_params)
- .map_err(
- |e| {
- error!("Failed responding to RPC request: {}", e);
- jsonrpc_core::Error::internal_error()
- },
- )?;
-
- serde_json::to_value(response).map_err(
- |e| {
- error!("Unable to serialize response to RPC request: {}", e);
- jsonrpc_core::Error::internal_error()
- },
- )
- };
- router.add_method(method, c);
-}
-
-#[derive(Deserialize)]
-struct LoginRequest {
- #[serde(rename = "accountNumber")]
- account_number: String,
-}
-fn mock_login(request: &LoginRequest) -> Result<::std::collections::HashMap<String, String>> {
- let ref account_number = request.account_number;
-
- let mut reply = ::std::collections::HashMap::new();
-
- if account_number.starts_with("1111") {
- // accounts starting with 1111 expire in one month
- reply.insert(
- "paidUntil".to_owned(),
- "2018-12-31T16:00:00.000Z".to_owned(),
- );
- // res.paidUntil = moment().startOf('day').add(15, 'days').toISOString();
- } else if account_number.starts_with("2222") {
- // expired in 2013
- reply.insert(
- "paidUntil".to_owned(),
- "2012-12-31T16:00:00.000Z".to_owned(),
- );
- } else if account_number.starts_with("3333") {
- // expire in 2038
- reply.insert(
- "paidUntil".to_owned(),
- "2037-12-31T16:00:00.000Z".to_owned(),
- );
- } else {
- bail!("you are not welcome {}!", account_number)
- }
-
- Ok(reply)
-}
-
-fn mock_logout(_: &()) -> Result<()> {
- Ok(())
-}
-
-#[derive(Deserialize)]
-struct ConnectRequest {
- address: String,
-}
-fn mock_connect(request: &ConnectRequest) -> Result<()> {
- let ref server_address = request.address;
- if server_address.starts_with("se") {
- bail!("{} is unreachable", server_address)
- }
-
- unsafe { MOCK_IS_CONNECTED = true };
- Ok(())
-}
-
-fn mock_disconnect(_: &()) -> Result<()> {
- unsafe { MOCK_IS_CONNECTED = false };
- Ok(())
-}
-
-#[derive(Serialize)]
-struct ConnectionInfo {
- ip: String,
-}
-fn mock_get_connection_info(_: &()) -> Result<ConnectionInfo> {
- let ip = if unsafe { MOCK_IS_CONNECTED } {
- "1.2.3.4"
- } else {
- "192.168.1.2"
- }
- .to_owned();
-
- Ok(ConnectionInfo { ip: ip })
-}
-
-#[derive(Serialize)]
-struct Location {
- latlong: [u32; 2],
- country: String,
- city: String,
-}
-fn mock_get_location(_: &()) -> Result<Location> {
- let response = if unsafe { MOCK_IS_CONNECTED } {
- Location {
- latlong: [1, 2],
- country: "narnia".to_owned(),
- city: "Le city".to_owned(),
- }
- } else {
- Location {
- latlong: [60, 61],
- country: "sweden".to_owned(),
- city: "bollebygd".to_owned(),
- }
- };
-
- Ok(response)
-}
diff --git a/mullvad_daemon/src/ipc_api.rs b/mullvad_daemon/src/ipc_api.rs
new file mode 100644
index 0000000000..84932a0b8f
--- /dev/null
+++ b/mullvad_daemon/src/ipc_api.rs
@@ -0,0 +1,90 @@
+use jsonrpc_core::Error;
+use jsonrpc_core::futures::BoxFuture;
+use jsonrpc_macros::pubsub;
+use jsonrpc_pubsub::SubscriptionId;
+
+use std::collections::HashMap;
+use std::net::IpAddr;
+
+pub type AccountToken = String;
+pub type CountryCode = String;
+
+build_rpc_trait! {
+ pub trait IpcApi {
+ type Metadata;
+
+ /// Fetches and returns metadata about an account. Returns an error on non-existing
+ /// accounts.
+ #[rpc(name = "get_account_data")]
+ fn get_account_data(&self, AccountToken) -> Result<AccountData, Error>;
+
+ /// Returns available countries.
+ #[rpc(name = "get_countries")]
+ fn get_countries(&self) -> Result<HashMap<CountryCode, String>, Error>;
+
+ /// Set which account to connect with
+ #[rpc(name = "set_account")]
+ fn set_account(&self, AccountToken) -> Result<(), Error>;
+
+ /// Set which country to connect to
+ #[rpc(name = "set_country")]
+ fn set_country(&self, CountryCode) -> Result<(), Error>;
+
+ /// Set if the backend should automatically establish a tunnel on start or not.
+ #[rpc(name = "set_autoconnect")]
+ fn set_autoconnect(&self, bool) -> Result<(), Error>;
+
+ /// Try to connect if disconnected, or do nothing if already connecting/connected.
+ #[rpc(name = "connect")]
+ fn connect(&self) -> Result<(), Error>;
+
+ /// Disconnect the VPN tunnel if it is connecting/connected. Does nothing if already
+ /// disconnected.
+ #[rpc(name = "disconnect")]
+ fn disconnect(&self) -> Result<(), Error>;
+
+ /// Returns the current security state of the Mullvad client. Changes to this state will
+ /// be announced to subscribers of `event`.
+ #[rpc(name = "get_state")]
+ fn get_state(&self) -> Result<SecurityState, Error>;
+
+ /// Returns the current public IP of this computer.
+ #[rpc(name = "get_ip")]
+ fn get_ip(&self) -> Result<IpAddr, Error>;
+
+ /// Performs a geoIP lookup and returns the current location as perceived by the public
+ /// internet.
+ #[rpc(name = "get_location")]
+ fn get_location(&self) -> Result<Location, Error>;
+
+ #[pubsub(name = "event")] {
+ /// Subscribes to the `event` notifications.
+ #[rpc(name = "event_subscribe")]
+ fn subscribe(&self, Self::Metadata, pubsub::Subscriber<String>);
+
+ /// Unsubscribes from the `event` notifications.
+ #[rpc(name = "event_unsubscribe")]
+ fn unsubscribe(&self, SubscriptionId) -> BoxFuture<bool, Error>;
+ }
+ }
+}
+
+#[derive(Serialize)]
+pub struct AccountData {
+ pub paid_until: String,
+}
+
+#[derive(Serialize)]
+pub struct Location {
+ pub latlong: [f64; 2],
+ pub country: String,
+ pub city: String,
+}
+
+#[derive(Serialize)]
+pub enum SecurityState {
+ Unsecured,
+ Securing,
+ Secured,
+ Unsecuring,
+}
diff --git a/mullvad_daemon/src/main.rs b/mullvad_daemon/src/main.rs
index ff4b62dcb3..5bd9563bfd 100644
--- a/mullvad_daemon/src/main.rs
+++ b/mullvad_daemon/src/main.rs
@@ -3,12 +3,21 @@ extern crate log;
extern crate env_logger;
#[macro_use]
extern crate error_chain;
+
extern crate serde;
#[macro_use]
extern crate serde_derive;
+
extern crate talpid_ipc;
-mod frontend_ipc_router;
+extern crate jsonrpc_core;
+extern crate jsonrpc_pubsub;
+#[macro_use]
+extern crate jsonrpc_macros;
+extern crate jsonrpc_ws_server;
+
+pub mod ipc_api;
+pub mod mock_ipc;
error_chain!{}
@@ -16,21 +25,19 @@ quick_main!(run);
fn run() -> Result<()> {
init_logger()?;
- let _server = start_ipc()?;
- main_loop()
+
+ let server = start_ipc()?;
+ main_loop(server)
}
fn init_logger() -> Result<()> {
env_logger::init().chain_err(|| "Failed to bootstrap logging system")
}
-fn start_ipc() -> Result<talpid_ipc::IpcServer> {
- talpid_ipc::IpcServer::start(frontend_ipc_router::build_router().into(), 0)
- .chain_err(|| "Failed to start IPC server")
+fn start_ipc() -> Result<mock_ipc::IpcServer> {
+ mock_ipc::IpcServer::start().chain_err(|| "Failed to start IPC server")
}
-fn main_loop() -> Result<()> {
- let (_tx, rx) = ::std::sync::mpsc::channel::<u8>();
- let _ = rx.recv();
- Ok(())
+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/mock_ipc.rs b/mullvad_daemon/src/mock_ipc.rs
new file mode 100644
index 0000000000..e490357741
--- /dev/null
+++ b/mullvad_daemon/src/mock_ipc.rs
@@ -0,0 +1,230 @@
+use ipc_api::*;
+
+use jsonrpc_core::{self, Error, ErrorCode, Metadata};
+use jsonrpc_core::futures::{BoxFuture, Future, future};
+use jsonrpc_macros::pubsub;
+use jsonrpc_pubsub::{PubSubHandler, PubSubMetadata, Session, SubscriptionId};
+use jsonrpc_ws_server;
+
+use std::collections::HashMap;
+use std::net::{IpAddr, Ipv4Addr};
+use std::sync::{Arc, RwLock, atomic};
+
+use talpid_ipc;
+
+type ActiveSubscriptions = Arc<RwLock<HashMap<SubscriptionId, pubsub::Sink<String>>>>;
+
+pub struct IpcServer {
+ server: talpid_ipc::IpcServer,
+}
+
+impl IpcServer {
+ pub fn start() -> talpid_ipc::Result<Self> {
+ let active_subscriptions = ActiveSubscriptions::default();
+ let mut last_error = None;
+ for i in 0..10 {
+ match Self::try_start(active_subscriptions.clone(), i) {
+ Ok(server) => {
+ Self::spawn_broadcast_thread(active_subscriptions);
+ return Ok(IpcServer { server });
+ }
+ Err(e) => last_error = Some(e),
+ }
+ }
+ bail!(last_error.unwrap());
+ }
+
+ pub fn address(&self) -> &str {
+ &self.server.address()
+ }
+
+ pub fn wait(self) -> talpid_ipc::Result<()> {
+ self.server.wait()
+ }
+
+ fn try_start(active_subscriptions: ActiveSubscriptions,
+ port_offset: u8)
+ -> talpid_ipc::Result<talpid_ipc::IpcServer> {
+ let rpc = MockIpcApi::new(active_subscriptions);
+ let mut io = PubSubHandler::default();
+ io.extend_with(rpc.to_delegate());
+ talpid_ipc::IpcServer::start_with_metadata(io.into(), meta_extractor, port_offset)
+ }
+
+ // TODO(linus): This thread will never die. But this is just mock anyway so not important.
+ fn spawn_broadcast_thread(active_subscriptions: ActiveSubscriptions) {
+ ::std::thread::spawn(
+ move || loop {
+ {
+ let subscribers = active_subscriptions.read().unwrap();
+ for sink in subscribers.values() {
+ let _ = sink.notify(Ok("Hello World!".into())).wait();
+ }
+ }
+ ::std::thread::sleep(::std::time::Duration::from_secs(1));
+ },
+ );
+ }
+}
+
+
+
+/// The metadata type. There is one instance associated with each connection. In this pubsub
+/// scenario they are created by `From<Sender<String>>::from` by the server on each new incoming
+/// connection.
+#[derive(Clone, Debug, Default)]
+pub struct Meta {
+ session: Option<Arc<Session>>,
+}
+
+/// Make the `Meta` type possible to use as jsonrpc metadata type.
+impl Metadata for Meta {}
+
+/// Make the `Meta` type possible to use as a pubsub metadata type.
+impl PubSubMetadata for Meta {
+ fn session(&self) -> Option<Arc<Session>> {
+ self.session.clone()
+ }
+}
+
+/// Metadata extractor function for `Meta`.
+fn meta_extractor(context: &jsonrpc_ws_server::RequestContext) -> Meta {
+ Meta { session: Some(Arc::new(Session::new(context.sender()))) }
+}
+
+/// A mock implementation of the Mullvad frontend API. A very simplified explanation is that for
+/// the real implementation `tunnel_is_up` should be replaced with some kind of handle (or proxy to
+/// a handle) to the jsonrpc client talking with talpid_core.
+pub struct MockIpcApi {
+ next_subscription_id: atomic::AtomicUsize,
+ active: ActiveSubscriptions,
+ country: RwLock<CountryCode>,
+ tunnel_is_up: atomic::AtomicBool,
+}
+
+impl MockIpcApi {
+ pub fn new(active: ActiveSubscriptions) -> Self {
+ MockIpcApi {
+ next_subscription_id: atomic::AtomicUsize::new(0),
+ active: active,
+ country: RwLock::new("se".to_owned()),
+ tunnel_is_up: atomic::AtomicBool::new(false),
+ }
+ }
+}
+
+impl IpcApi for MockIpcApi {
+ type Metadata = Meta;
+
+ fn get_account_data(&self, account_token: AccountToken) -> Result<AccountData, Error> {
+ debug!("Login for {}", account_token);
+
+ let paid_until = if account_token.starts_with("1111") {
+ // accounts starting with 1111 expire in one month
+ Ok("2018-12-31T16:00:00.000Z".to_owned())
+ } else if account_token.starts_with("2222") {
+ Ok("2012-12-31T16:00:00.000Z".to_owned())
+ } else if account_token.starts_with("3333") {
+ Ok("2037-12-31T16:00:00.000Z".to_owned())
+ } else {
+ Err(jsonrpc_core::Error::invalid_params("You are not welcome"))
+ }?;
+ Ok(AccountData { paid_until: paid_until })
+ }
+
+ fn get_countries(&self) -> Result<HashMap<CountryCode, String>, Error> {
+ let mut countries = HashMap::new();
+ countries.insert("se".to_owned(), "Sweden".to_owned());
+ countries.insert("de".to_owned(), "Denmark".to_owned());
+ countries.insert("na".to_owned(), "Narnia".to_owned());
+ Ok(countries)
+ }
+
+ fn set_account(&self, _account_token: AccountToken) -> Result<(), Error> {
+ Ok(())
+ }
+
+ fn set_country(&self, country_code: CountryCode) -> Result<(), Error> {
+ *self.country.write().unwrap() = country_code;
+ Ok(())
+ }
+
+ fn set_autoconnect(&self, _autoconnect: bool) -> Result<(), Error> {
+ Ok(())
+ }
+
+ fn connect(&self) -> Result<(), Error> {
+ if self.country.read().unwrap().starts_with("se") {
+ Err(jsonrpc_core::Error::invalid_params("Invalid server"))
+ } else {
+ self.tunnel_is_up.store(true, atomic::Ordering::SeqCst);
+ Ok(())
+ }
+ }
+
+ fn disconnect(&self) -> Result<(), Error> {
+ self.tunnel_is_up.store(false, atomic::Ordering::SeqCst);
+ Ok(())
+ }
+
+ fn get_state(&self) -> Result<SecurityState, Error> {
+ if self.tunnel_is_up.load(atomic::Ordering::SeqCst) {
+ Ok(SecurityState::Secured)
+ } else {
+ Ok(SecurityState::Unsecured)
+ }
+ }
+
+ fn get_ip(&self) -> Result<IpAddr, Error> {
+ let ip = if self.tunnel_is_up.load(atomic::Ordering::SeqCst) {
+ IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4))
+ } else {
+ IpAddr::V4(Ipv4Addr::new(192, 168, 1, 2))
+ }
+ .to_owned();
+ Ok(ip)
+ }
+
+ fn get_location(&self) -> Result<Location, Error> {
+ Ok(
+ if self.tunnel_is_up.load(atomic::Ordering::SeqCst) {
+ Location {
+ latlong: [1.0, 2.0],
+ country: "narnia".to_owned(),
+ city: "Le city".to_owned(),
+ }
+ } else {
+ Location {
+ latlong: [60.0, 61.0],
+ country: "sweden".to_owned(),
+ city: "bollebygd".to_owned(),
+ }
+ },
+ )
+ }
+
+ fn subscribe(&self, _meta: Self::Metadata, subscriber: pubsub::Subscriber<String>) {
+ let id = self.next_subscription_id.fetch_add(1, atomic::Ordering::SeqCst);
+ let sub_id = SubscriptionId::Number(id as u64);
+ if let Ok(sink) = subscriber.assign_id(sub_id.clone()) {
+ debug!("Accepting new subscription with id {}", id);
+ self.active.write().unwrap().insert(sub_id, sink);
+ }
+ }
+
+ fn unsubscribe(&self, id: SubscriptionId) -> BoxFuture<bool, Error> {
+ debug!("Unsubscribing id {:?}", id);
+ if self.active.write().unwrap().remove(&id).is_some() {
+ future::ok(true).boxed()
+ } else {
+ future::err(
+ Error {
+ code: ErrorCode::InvalidParams,
+ message: "Invalid subscription.".into(),
+ data: None,
+ },
+ )
+ .boxed()
+ }
+ }
+}
diff --git a/talpid_ipc/src/lib.rs b/talpid_ipc/src/lib.rs
index d1f1b816bd..bad341bb32 100644
--- a/talpid_ipc/src/lib.rs
+++ b/talpid_ipc/src/lib.rs
@@ -87,10 +87,12 @@ impl IpcServer {
&self.address
}
+ /// Consumes the server, stops it and waits for it to finish.
pub fn stop(self) {
self.server.close();
}
+ /// Consumes the server and waits for it to finish.
pub fn wait(self) -> Result<()> {
self.server.wait().chain_err(|| ErrorKind::IpcServerError)
}