diff options
| -rw-r--r-- | Cargo.lock | 7 | ||||
| -rw-r--r-- | mullvad-tests/Cargo.toml | 13 | ||||
| -rw-r--r-- | mullvad-tests/build.rs | 5 | ||||
| -rw-r--r-- | mullvad-tests/src/lib.rs | 88 | ||||
| -rw-r--r-- | mullvad-tests/tests/connection.rs | 74 | ||||
| -rw-r--r-- | mullvad-tests/tests/startup.rs | 4 | ||||
| -rw-r--r-- | mullvad-types/src/location.rs | 2 | ||||
| -rw-r--r-- | mullvad-types/src/states.rs | 2 |
8 files changed, 141 insertions, 54 deletions
diff --git a/Cargo.lock b/Cargo.lock index e9415bc4b6..ca7fff3b06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1563,14 +1563,21 @@ dependencies = [ "libc 0.2.69 (registry+https://github.com/rust-lang/crates.io-index)", "mullvad-ipc-client 0.1.0", "mullvad-paths 0.1.0", + "mullvad-rpc 0.1.0", "mullvad-types 0.1.0", "notify 4.0.13 (registry+https://github.com/rust-lang/crates.io-index)", "openvpn-plugin 0.3.0 (git+https://github.com/mullvad/openvpn-plugin-rs?branch=auth-failed-event)", + "parity-tokio-ipc 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "prost 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "talpid-ipc 0.1.0", "talpid-types 0.1.0", "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.22 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio 0.2.21 (registry+https://github.com/rust-lang/crates.io-index)", "tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "tonic 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tonic-build 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tower 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] diff --git a/mullvad-tests/Cargo.toml b/mullvad-tests/Cargo.toml index 7aa2857c26..152343c1e9 100644 --- a/mullvad-tests/Cargo.toml +++ b/mullvad-tests/Cargo.toml @@ -14,6 +14,7 @@ integration-tests = [] duct = "0.13" mullvad-ipc-client = { path = "../mullvad-ipc-client" } mullvad-paths = { path = "../mullvad-paths" } +mullvad-rpc = { path = "../mullvad-rpc" } mullvad-types = { path = "../mullvad-types" } notify = "4.0" openvpn-plugin = { git = "https://github.com/mullvad/openvpn-plugin-rs", branch = "auth-failed-event", features = ["serde"] } @@ -23,9 +24,17 @@ tempfile = "3.0" jsonrpc-client-core = { git = "https://github.com/mullvad/jsonrpc-client-rs", rev = "68aac55b" } jsonrpc-client-ipc = { git = "https://github.com/mullvad/jsonrpc-client-rs", rev = "68aac55b" } jsonrpc-client-pubsub = { git = "https://github.com/mullvad/jsonrpc-client-rs", rev = "68aac55b" } -tokio = "0.1" -tokio-timer = "0.1" futures = "0.1.23" +tokio01 = { package = "tokio", version = "0.1" } +tokio-timer = "0.1" +tokio = { version = "0.2", features = [ "io-util", "process", "rt-core", "rt-threaded", "stream", "fs"] } +tonic = "0.2" +tower = "0.3" +prost = "0.6" +parity-tokio-ipc = "0.7" + +[build-dependencies] +tonic-build = { version = "0.2", default-features = false, features = ["transport", "prost"] } [target.'cfg(unix)'.dependencies] libc = "0.2" diff --git a/mullvad-tests/build.rs b/mullvad-tests/build.rs new file mode 100644 index 0000000000..71c9068925 --- /dev/null +++ b/mullvad-tests/build.rs @@ -0,0 +1,5 @@ +fn main() { + const OPENVPN_PROTO_FILE: &str = "../talpid-openvpn-plugin/proto/openvpn_plugin.proto"; + tonic_build::compile_protos(OPENVPN_PROTO_FILE).unwrap(); + println!("cargo:rerun-if-changed={}", OPENVPN_PROTO_FILE); +} diff --git a/mullvad-tests/src/lib.rs b/mullvad-tests/src/lib.rs index 38266139c1..ad0d85bf89 100644 --- a/mullvad-tests/src/lib.rs +++ b/mullvad-tests/src/lib.rs @@ -1,10 +1,9 @@ #![cfg(not(target_os = "android"))] use self::{mock_openvpn::MOCK_OPENVPN_ARGS_FILE, platform_specific::*}; -use futures::sync::oneshot; -use jsonrpc_client_core::{Future, Transport}; use jsonrpc_client_ipc::IpcTransport; use mullvad_ipc_client::DaemonRpcClient; +use mullvad_rpc::API_IP_CACHE_FILENAME; use notify::{RawEvent, RecommendedWatcher, RecursiveMode, Watcher}; use std::{ cmp, @@ -16,10 +15,20 @@ use std::{ time::{Duration, Instant}, }; use tempfile::TempDir; -use tokio::reactor::Handle; pub use notify::op::{self as watch_event, Op as WatchEvent}; +use parity_tokio_ipc::Endpoint as IpcEndpoint; +use tonic::{ + self, + transport::{Endpoint, Uri}, +}; +use tower::service_fn; + +mod openvpn_proto { + tonic::include_proto!("talpid_openvpn_plugin"); +} +use openvpn_proto::openvpn_event_proxy_client::OpenvpnEventProxyClient; pub mod mock_openvpn; @@ -205,6 +214,7 @@ fn prepare_test_dirs() -> (TempDir, PathBuf, PathBuf, PathBuf) { fs::create_dir(&settings_dir).expect("Failed to create settings directory"); prepare_resource_dir(&resource_dir); + prepare_cache_dir(&cache_dir); (temp_dir, cache_dir, resource_dir, settings_dir) } @@ -216,8 +226,13 @@ fn prepare_resource_dir(resource_dir: &Path) { fs::copy(MOCK_OPENVPN_EXECUTABLE_PATH, openvpn_binary) .expect("Failed to copy mock OpenVPN binary"); File::create(talpid_openvpn_plugin).expect("Failed to create mock Talpid OpenVPN plugin"); +} + +fn prepare_cache_dir(cache_dir: &Path) { + prepare_relay_list(cache_dir.join("relays.json")); - prepare_relay_list(resource_dir.join("relays.json")); + fs::write(cache_dir.join(API_IP_CACHE_FILENAME), "192.168.0.123") + .expect("Failed to cache API IP"); } fn prepare_relay_list<T: AsRef<Path>>(path: T) { @@ -235,14 +250,25 @@ fn prepare_relay_list<T: AsRef<Path>>(path: T) { "relays": [{ "hostname": "fakehost", "ipv4_addr_in": "192.168.0.100", - "ipv4_addr_exit": "192.168.0.101", + "ipv6_addr_in": null, "include_in_country": true, + "active": true, + "owned": false, + "provider": "M247", "weight": 100, "tunnels": { "openvpn": [ { "port": 1000, "protocol": "udp" } ], "wireguard": [] } - }] + }], + "location": { + "country": "Sweden", + "country_code": "se", + "city": "Gothenburg", + "city_code": "got", + "latitude": 57.70887, + "longitude": 11.97456 + } }] }] }"#, @@ -297,7 +323,7 @@ impl DaemonRunner { 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| { - IpcTransport::new(&path, &Handle::default()) + IpcTransport::new(&path, &tokio01::reactor::Handle::default()) }) .map_err(|e| e.to_string()) .map_err(|e| format!("Failed to construct an RPC client - {}", e)) @@ -347,35 +373,30 @@ impl Drop for DaemonRunner { } pub struct MockOpenVpnPluginRpcClient { - rpc: jsonrpc_client_core::ClientHandle, + runtime: tokio::runtime::Runtime, + rpc: OpenvpnEventProxyClient<tonic::transport::Channel>, } impl MockOpenVpnPluginRpcClient { - fn spawn_event_loop(address: String) -> Result<jsonrpc_client_core::ClientHandle> { - let (tx, rx) = oneshot::channel(); - thread::spawn(move || { - let result = IpcTransport::new(&address, &Handle::default()) - .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(); - tokio::run(client.map_err(|e| { - println!("RPC client failed: {}", e); - })); - } - Err(e) => tx.send(Err(e)).unwrap(), - } - }); + async fn spawn_event_loop( + address: String, + ) -> Result<OpenvpnEventProxyClient<tonic::transport::Channel>> { + // The URI will be ignored + let channel = Endpoint::from_static("lttp://[::]:50051") + .connect_with_connector(service_fn(move |_: Uri| { + IpcEndpoint::connect(address.clone()) + })) + .await + .map_err(|e| format!("Failed to construct an RPC client - {}", e.to_string()))?; - rx.wait().unwrap() + Ok(OpenvpnEventProxyClient::new(channel)) } pub fn new(address: String) -> Result<Self> { - let rpc = Self::spawn_event_loop(address)?; - Ok(MockOpenVpnPluginRpcClient { rpc }) + let mut runtime = tokio::runtime::Runtime::new().unwrap(); + + let rpc = runtime.block_on(Self::spawn_event_loop(address))?; + Ok(MockOpenVpnPluginRpcClient { runtime, rpc }) } pub fn up(&mut self) -> Result<()> { @@ -403,9 +424,12 @@ impl MockOpenVpnPluginRpcClient { event: openvpn_plugin::EventType, env: HashMap<String, String>, ) -> Result<()> { - self.rpc - .call_method("openvpn_event", &(event, env)) - .wait() + self.runtime + .block_on(self.rpc.event(openvpn_proto::EventType { + event: event as i16 as i32, + env, + })) + .map(|_| ()) .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 509ffb5bf8..2d294232ef 100644 --- a/mullvad-tests/tests/connection.rs +++ b/mullvad-tests/tests/connection.rs @@ -5,11 +5,11 @@ use mullvad_tests::{ mock_openvpn::search_openvpn_args, watch_event, DaemonRunner, MockOpenVpnPluginRpcClient, PathWatcher, }; -use mullvad_types::DaemonEvent; +use mullvad_types::{location::GeoIpLocation, states::TunnelState, DaemonEvent}; use std::{fs, path::Path, time::Duration}; use talpid_types::{ net::{Endpoint, TransportProtocol, TunnelEndpoint, TunnelType}, - tunnel::{ActionAfterDisconnect, TunnelStateTransition}, + tunnel::ActionAfterDisconnect, }; #[cfg(target_os = "linux")] @@ -67,11 +67,17 @@ fn changes_to_connecting_state() { let _ = assert_state_event( state_events, - TunnelStateTransition::Connecting(get_default_endpoint()), + TunnelState::Connecting { + endpoint: get_default_endpoint(), + location: get_default_location(), + }, ); assert_eq!( rpc_client.get_state().unwrap(), - TunnelStateTransition::Connecting(get_default_endpoint()) + TunnelState::Connecting { + endpoint: get_default_endpoint(), + location: get_default_location(), + }, ); } @@ -88,7 +94,10 @@ fn changes_to_connected_state() { let state_events = assert_state_event( state_events, - TunnelStateTransition::Connecting(get_default_endpoint()), + TunnelState::Connecting { + endpoint: get_default_endpoint(), + location: get_default_location(), + }, ); openvpn_args_file_events.assert_create_write_close_sequence(); @@ -98,11 +107,17 @@ fn changes_to_connected_state() { assert_state_event( state_events, - TunnelStateTransition::Connected(get_default_endpoint()), + TunnelState::Connected { + endpoint: get_default_endpoint(), + location: get_default_location(), + }, ); assert_eq!( rpc_client.get_state().unwrap(), - TunnelStateTransition::Connected(get_default_endpoint()) + TunnelState::Connected { + endpoint: get_default_endpoint(), + location: get_default_location(), + } ); } @@ -119,7 +134,10 @@ fn returns_to_connecting_state() { let state_events = assert_state_event( state_events, - TunnelStateTransition::Connecting(get_default_endpoint()), + TunnelState::Connecting { + endpoint: get_default_endpoint(), + location: get_default_location(), + }, ); openvpn_args_file_events.assert_create_write_close_sequence(); @@ -129,7 +147,10 @@ fn returns_to_connecting_state() { let state_events = assert_state_event( state_events, - TunnelStateTransition::Connected(get_default_endpoint()), + TunnelState::Connected { + endpoint: get_default_endpoint(), + location: get_default_location(), + }, ); mock_plugin_client.route_predown().unwrap(); @@ -140,7 +161,7 @@ fn returns_to_connecting_state() { let _ = assert_state_event( state_events, - TunnelStateTransition::Disconnecting(ActionAfterDisconnect::Reconnect), + TunnelState::Disconnecting(ActionAfterDisconnect::Reconnect), ); } @@ -157,7 +178,10 @@ fn disconnects() { let state_events = assert_state_event( state_events, - TunnelStateTransition::Connecting(get_default_endpoint()), + TunnelState::Connecting { + endpoint: get_default_endpoint(), + location: get_default_location(), + }, ); openvpn_args_file_events.assert_create_write_close_sequence(); @@ -167,16 +191,19 @@ fn disconnects() { let state_events = assert_state_event( state_events, - TunnelStateTransition::Connected(get_default_endpoint()), + TunnelState::Connected { + endpoint: get_default_endpoint(), + location: get_default_location(), + }, ); rpc_client.disconnect().unwrap(); let state_events = assert_state_event( state_events, - TunnelStateTransition::Disconnecting(ActionAfterDisconnect::Nothing), + TunnelState::Disconnecting(ActionAfterDisconnect::Nothing), ); - let _ = assert_state_event(state_events, TunnelStateTransition::Disconnected); + let _ = assert_state_event(state_events, TunnelState::Disconnected); } fn get_default_endpoint() -> TunnelEndpoint { @@ -186,14 +213,29 @@ fn get_default_endpoint() -> TunnelEndpoint { protocol: TransportProtocol::Udp, }, tunnel_type: TunnelType::OpenVpn, + proxy: None, } } +fn get_default_location() -> Option<GeoIpLocation> { + Some(GeoIpLocation { + ipv4: None, + ipv6: None, + country: "Sweden".to_string(), + city: Some("Gothenburg".to_string()), + latitude: 57.70887, + longitude: 11.97456, + mullvad_exit_ip: true, + hostname: Some("fakehost".to_string()), + bridge_hostname: None, + }) +} + fn assert_state_event< S: Stream<Item = DaemonEvent, Error = jsonrpc_client_core::Error> + std::fmt::Debug, >( mut receiver: S, - expected_state: TunnelStateTransition, + expected_state: TunnelState, ) -> S { use futures::future::Either; @@ -206,7 +248,7 @@ fn assert_state_event< _ => panic!("Timed out waiting for tunnel state transition"), }; receiver = receiver2; - if let DaemonEvent::StateTransition(new_state) = event.unwrap() { + if let DaemonEvent::TunnelState(new_state) = event.unwrap() { transition = Some(new_state); } } diff --git a/mullvad-tests/tests/startup.rs b/mullvad-tests/tests/startup.rs index 3e6ad205ce..812d443f66 100644 --- a/mullvad-tests/tests/startup.rs +++ b/mullvad-tests/tests/startup.rs @@ -1,7 +1,7 @@ #![cfg(feature = "integration-tests")] use mullvad_tests::DaemonRunner; -use talpid_types::tunnel::TunnelStateTransition; +use mullvad_types::states::TunnelState; #[test] fn starts_in_disconnected_state() { @@ -10,5 +10,5 @@ fn starts_in_disconnected_state() { let state = rpc_client.get_state().expect("Failed to read daemon state"); - assert_eq!(state, TunnelStateTransition::Disconnected); + assert_eq!(state, TunnelState::Disconnected); } diff --git a/mullvad-types/src/location.rs b/mullvad-types/src/location.rs index 243c0768e8..17cec7c020 100644 --- a/mullvad-types/src/location.rs +++ b/mullvad-types/src/location.rs @@ -66,7 +66,7 @@ pub struct AmIMullvad { } /// GeoIP information exposed from the daemon to frontends. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[cfg_attr(target_os = "android", derive(IntoJava))] #[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))] pub struct GeoIpLocation { diff --git a/mullvad-types/src/states.rs b/mullvad-types/src/states.rs index 805210a7aa..e80b795bba 100644 --- a/mullvad-types/src/states.rs +++ b/mullvad-types/src/states.rs @@ -18,7 +18,7 @@ pub enum TargetState { } /// Represents the state the client tunnel is in. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(tag = "state", content = "details")] #[cfg_attr(target_os = "android", derive(IntoJava))] |
