diff options
| author | Erik Larkö <erik@mullvad.net> | 2017-09-13 07:33:30 +0200 |
|---|---|---|
| committer | Erik Larkö <erik@mullvad.net> | 2017-09-13 07:33:30 +0200 |
| commit | b572d0634b78d3de05ce39e5a49ce893c8065d6b (patch) | |
| tree | 9e955cb2ab5499eb97a76e9c4d22a384fae9b42b | |
| parent | 2bc13231538133ad452b1b72ac4bbfe67235df02 (diff) | |
| parent | c383938bf44bb0b045ec63871a4bb804921fb2db (diff) | |
| download | mullvadvpn-b572d0634b78d3de05ce39e5a49ce893c8065d6b.tar.xz mullvadvpn-b572d0634b78d3de05ce39e5a49ce893c8065d6b.zip | |
Merge branch 'set_custom_relay-endpoint'
| -rw-r--r-- | Cargo.lock | 26 | ||||
| -rw-r--r-- | mullvad-cli/Cargo.toml | 1 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/account.rs | 3 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/custom_relay.rs | 74 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/mod.rs | 4 | ||||
| -rw-r--r-- | mullvad-cli/src/main.rs | 1 | ||||
| -rw-r--r-- | mullvad-daemon/Cargo.toml | 1 | ||||
| -rw-r--r-- | mullvad-daemon/src/main.rs | 84 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 32 | ||||
| -rw-r--r-- | mullvad-daemon/src/settings.rs | 28 | ||||
| -rw-r--r-- | mullvad-types/Cargo.toml | 4 | ||||
| -rw-r--r-- | mullvad-types/src/lib.rs | 9 | ||||
| -rw-r--r-- | mullvad-types/src/relay_endpoint.rs | 71 | ||||
| -rw-r--r-- | talpid-core/Cargo.toml | 1 | ||||
| -rw-r--r-- | talpid-core/src/firewall/macos.rs | 22 | ||||
| -rw-r--r-- | talpid-core/src/firewall/mod.rs | 2 | ||||
| -rw-r--r-- | talpid-core/src/lib.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/net.rs | 29 | ||||
| -rw-r--r-- | talpid-core/src/process/openvpn.rs | 6 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 2 | ||||
| -rw-r--r-- | talpid-types/Cargo.toml | 9 | ||||
| -rw-r--r-- | talpid-types/src/lib.rs | 13 | ||||
| -rw-r--r-- | talpid-types/src/net.rs | 69 |
23 files changed, 414 insertions, 81 deletions
diff --git a/Cargo.lock b/Cargo.lock index 91b97a1db7..fc8263b78d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,12 +1,9 @@ [root] -name = "talpid-openvpn-plugin" +name = "talpid-types" version = "0.1.0" dependencies = [ - "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", - "openvpn-plugin 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "talpid-ipc 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)", ] [[package]] @@ -645,6 +642,7 @@ dependencies = [ "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", + "talpid-types 0.1.0", ] [[package]] @@ -673,6 +671,7 @@ dependencies = [ "simple-signal 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "talpid-core 0.1.0", "talpid-ipc 0.1.0", + "talpid-types 0.1.0", "toml 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -682,8 +681,11 @@ name = "mullvad-types" version = "0.1.0" dependencies = [ "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "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)", + "talpid-types 0.1.0", ] [[package]] @@ -1184,6 +1186,7 @@ dependencies = [ "openvpn-plugin 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "pfctl 0.1.0 (git+https://github.com/mullvad/pfctl-rs.git)", "talpid-ipc 0.1.0", + "talpid-types 0.1.0", "uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1205,6 +1208,17 @@ dependencies = [ ] [[package]] +name = "talpid-openvpn-plugin" +version = "0.1.0" +dependencies = [ + "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", + "openvpn-plugin 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "talpid-ipc 0.1.0", +] + +[[package]] name = "tempdir" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/mullvad-cli/Cargo.toml b/mullvad-cli/Cargo.toml index 63d11a4656..1ae3afbcfc 100644 --- a/mullvad-cli/Cargo.toml +++ b/mullvad-cli/Cargo.toml @@ -19,4 +19,5 @@ serde = "1.0" serde_json = "1.0" mullvad-types = { path = "../mullvad-types" } +talpid-types = { path = "../talpid-types" } talpid-ipc = { path = "../talpid-ipc" } diff --git a/mullvad-cli/src/cmds/account.rs b/mullvad-cli/src/cmds/account.rs index 553458e11a..f7cb106fc3 100644 --- a/mullvad-cli/src/cmds/account.rs +++ b/mullvad-cli/src/cmds/account.rs @@ -1,5 +1,4 @@ -use Command; -use Result; +use {Command, Result}; use clap; use mullvad_types::account::{AccountData, AccountToken}; diff --git a/mullvad-cli/src/cmds/custom_relay.rs b/mullvad-cli/src/cmds/custom_relay.rs new file mode 100644 index 0000000000..ab9a148fb3 --- /dev/null +++ b/mullvad-cli/src/cmds/custom_relay.rs @@ -0,0 +1,74 @@ +pub struct CustomRelay; + +use {Command, Result}; +use clap; +use mullvad_types::relay_endpoint::RelayEndpoint; + +use rpc; + +use talpid_types::net::TransportProtocol; + +impl Command for CustomRelay { + fn name(&self) -> &'static str { + "relay" + } + + fn clap_subcommand(&self) -> clap::App<'static, 'static> { + clap::SubCommand::with_name(self.name()) + .about("Set or remove custom relay") + .setting(clap::AppSettings::SubcommandRequired) + .subcommand(clap::SubCommand::with_name("set") + .about("Set a custom relay") + .arg(clap::Arg::with_name("host") + .help("The host name or IP of the relay") + .required(true)) + .arg(clap::Arg::with_name("port") + .help("The port of the relay") + .required(true)) + .arg(clap::Arg::with_name("protocol") + .help("The transport protocol. UDP is recommended as it usually results in + higher throughput than TCP") + .possible_values(&["udp", "tcp"]) + .default_value("udp"))) + .subcommand(clap::SubCommand::with_name("remove") + .about("Remove the custom relay and use the default relays instead")) + } + + fn run(&self, matches: &clap::ArgMatches) -> Result<()> { + if let Some(set_matches) = matches.subcommand_matches("set") { + let host = value_t_or_exit!(set_matches.value_of("host"), String); + let port = value_t_or_exit!(set_matches.value_of("port"), u16); + let protocol = value_t_or_exit!(set_matches.value_of("protocol"), TransportProtocol); + + self.set(host, port, protocol) + } else if let Some(_) = matches.subcommand_matches("remove") { + self.remove() + } else { + unreachable!("No sub command given"); + } + } +} + +impl CustomRelay { + fn set(&self, host: String, port: u16, protocol: TransportProtocol) -> Result<()> { + let relay_endpoint = RelayEndpoint { + host, + port, + protocol, + }; + + rpc::call( + "set_custom_relay", + &[relay_endpoint], + ) + .map(|_: Option<()>| println!("Custom relay set")) + } + + fn remove(&self) -> Result<()> { + rpc::call( + "remove_custom_relay", + &[] as &[u8; 0], + ) + .map(|_: Option<()>| println!("Custom relay removed")) + } +} diff --git a/mullvad-cli/src/cmds/mod.rs b/mullvad-cli/src/cmds/mod.rs index c621c0840a..8061344b96 100644 --- a/mullvad-cli/src/cmds/mod.rs +++ b/mullvad-cli/src/cmds/mod.rs @@ -13,6 +13,9 @@ pub use self::connect::Connect; mod disconnect; pub use self::disconnect::Disconnect; +mod custom_relay; +pub use self::custom_relay::CustomRelay; + /// 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<Command>> = vec![ @@ -20,6 +23,7 @@ pub fn get_commands() -> HashMap<&'static str, Box<Command>> { Box::new(Status), Box::new(Connect), Box::new(Disconnect), + Box::new(CustomRelay), ]; let mut map = HashMap::new(); for cmd in commands { diff --git a/mullvad-cli/src/main.rs b/mullvad-cli/src/main.rs index 745d2aea34..8543f7eecb 100644 --- a/mullvad-cli/src/main.rs +++ b/mullvad-cli/src/main.rs @@ -10,6 +10,7 @@ #![recursion_limit = "1024"] extern crate mullvad_types; +extern crate talpid_types; extern crate talpid_ipc; #[macro_use] extern crate clap; diff --git a/mullvad-daemon/Cargo.toml b/mullvad-daemon/Cargo.toml index 786baac7a3..b355c4bf53 100644 --- a/mullvad-daemon/Cargo.toml +++ b/mullvad-daemon/Cargo.toml @@ -32,6 +32,7 @@ toml = "0.4" mullvad-types = { path = "../mullvad-types" } talpid-core = { path = "../talpid-core" } talpid-ipc = { path = "../talpid-ipc" } +talpid-types = { path = "../talpid-types" } [target.'cfg(unix)'.dependencies] simple-signal = "1.1" diff --git a/mullvad-daemon/src/main.rs b/mullvad-daemon/src/main.rs index 6953cea498..54b1ac1747 100644 --- a/mullvad-daemon/src/main.rs +++ b/mullvad-daemon/src/main.rs @@ -35,6 +35,7 @@ extern crate lazy_static; extern crate mullvad_types; extern crate talpid_core; extern crate talpid_ipc; +extern crate talpid_types; mod cli; mod management_interface; @@ -43,6 +44,7 @@ mod rpc_info; mod settings; mod shutdown; + use error_chain::ChainedError; use futures::Future; use jsonrpc_client_http::HttpHandle; @@ -50,6 +52,7 @@ use jsonrpc_core::futures::sync::oneshot::Sender as OneshotSender; use management_interface::{BoxFuture, ManagementInterfaceServer, TunnelCommand}; use master::AccountsProxy; use mullvad_types::account::{AccountData, AccountToken}; +use mullvad_types::relay_endpoint::RelayEndpoint; use mullvad_types::states::{DaemonState, SecurityState, TargetState}; use std::io; @@ -60,8 +63,8 @@ use std::thread; use talpid_core::firewall::{Firewall, FirewallProxy, SecurityPolicy}; use talpid_core::mpsc::IntoSender; -use talpid_core::net::{Endpoint, TransportProtocol}; use talpid_core::tunnel::{self, TunnelEvent, TunnelMonitor}; +use talpid_types::net::{Endpoint, TransportProtocol}; error_chain!{ errors { @@ -85,12 +88,15 @@ error_chain!{ description("Invalid settings") display("Invalid Settings: {}", msg) } + NoRelay { + description("Found no valid relays to connect to") + } } } lazy_static! { - // Temporary store of hardcoded remotes. - static ref REMOTES: [Endpoint; 3] = [ + // Temporary store of hardcoded relays. + static ref RELAYS: [Endpoint; 3] = [ // se5.mullvad.net Endpoint::new(Ipv4Addr::new(193, 138, 219, 240), 1300, TransportProtocol::Udp), // se6.mullvad.net @@ -170,12 +176,12 @@ struct Daemon { settings: settings::Settings, accounts_proxy: AccountsProxy<HttpHandle>, firewall: FirewallProxy, - remote_endpoint: Option<Endpoint>, + relay_endpoint: Option<Endpoint>, tunnel_interface: Option<String>, - // Just for testing. A cyclic iterator iterating over the hardcoded remotes, + // Just for testing. A cyclic iterator iterating over the hardcoded relays, // picking a new one for each retry. - remote_iter: std::iter::Cycle<std::iter::Cloned<std::slice::Iter<'static, Endpoint>>>, + relay_iter: std::iter::Cycle<std::iter::Cloned<std::slice::Iter<'static, Endpoint>>>, } impl Daemon { @@ -201,9 +207,9 @@ impl Daemon { accounts_proxy: master::create_account_proxy() .chain_err(|| "Unable to bootstrap RPC client")?, firewall: FirewallProxy::new().chain_err(|| ErrorKind::FirewallError)?, - remote_endpoint: None, + relay_endpoint: None, tunnel_interface: None, - remote_iter: REMOTES.iter().cloned().cycle(), + relay_iter: RELAYS.iter().cloned().cycle(), }, ) } @@ -289,7 +295,7 @@ impl Daemon { if let Err(e) = result.chain_err(|| "Tunnel exited in an unexpected way") { error!("{}", e.display_chain()); } - self.remote_endpoint = None; + self.relay_endpoint = None; self.tunnel_interface = None; self.reset_security_policy()?; self.tunnel_close_handle = None; @@ -308,6 +314,7 @@ impl Daemon { GetAccountData(tx, account_token) => Ok(self.on_get_account_data(tx, account_token)), SetAccount(tx, account_token) => self.on_set_account(tx, account_token), GetAccount(tx) => Ok(self.on_get_account(tx)), + SetCustomRelay(tx, relay_endpoint) => self.on_set_custom_relay(tx, relay_endpoint), } } @@ -360,6 +367,31 @@ impl Daemon { Self::oneshot_send(tx, self.settings.get_account_token(), "current account") } + fn on_set_custom_relay(&mut self, + tx: OneshotSender<()>, + relay_endpoint: Option<RelayEndpoint>) + -> Result<()> { + + let save_result = self.settings.set_custom_relay(relay_endpoint); + + match save_result.chain_err(|| "Unable to save settings") { + Ok(relays_changed) => { + Self::oneshot_send(tx, (), "set_custom_relay response"); + + let tunnel_needs_restart = self.state == TunnelState::Connecting || + self.state == TunnelState::Connected; + + if relays_changed && tunnel_needs_restart { + info!("Initiating tunnel restart because a custom relay was selected"); + self.kill_tunnel()?; + } + } + Err(e) => error!("{}", e.display_chain()), + } + + Ok(()) + } + fn oneshot_send<T>(tx: OneshotSender<T>, t: T, msg: &'static str) { if let Err(_) = tx.send(t) { warn!("Unable to send {} to management interface client", msg); @@ -440,7 +472,7 @@ impl Daemon { debug!("Triggering tunnel start"); if let Err(e) = self.start_tunnel().chain_err(|| "Failed to start tunnel") { error!("{}", e.display_chain()); - self.remote_endpoint = None; + self.relay_endpoint = None; self.reset_security_policy()?; self.management_interface_broadcaster.notify_error(&e); self.set_target_state(TargetState::Unsecured)?; @@ -458,26 +490,42 @@ impl Daemon { self.state == TunnelState::NotRunning, ErrorKind::InvalidState ); - let remote = self.remote_iter.next().unwrap(); + + let relay = self.get_relay() + .chain_err(|| ErrorKind::NoRelay)?; + let account_token = self.settings .get_account_token() .ok_or(ErrorKind::InvalidSettings("No account token"))?; - self.remote_endpoint = Some(remote); + + self.relay_endpoint = Some(relay); self.set_security_policy()?; - let tunnel_monitor = self.spawn_tunnel_monitor(remote, &account_token)?; + + let tunnel_monitor = self.spawn_tunnel_monitor(relay, &account_token)?; self.tunnel_close_handle = Some(tunnel_monitor.close_handle()); self.spawn_tunnel_monitor_wait_thread(tunnel_monitor); + self.set_state(TunnelState::Connecting)?; Ok(()) } - fn spawn_tunnel_monitor(&self, remote: Endpoint, account_token: &str) -> Result<TunnelMonitor> { + fn get_relay(&mut self) -> Result<Endpoint> { + if let Some(relay_endpoint) = self.settings.get_custom_relay() { + relay_endpoint + .to_endpoint() + .chain_err(|| "Invalid custom relay") + } else { + Ok(self.relay_iter.next().unwrap()) + } + } + + fn spawn_tunnel_monitor(&self, relay: Endpoint, account_token: &str) -> 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, account_token, on_tunnel_event) + TunnelMonitor::new(relay, account_token, on_tunnel_event) .chain_err(|| ErrorKind::TunnelError("Unable to start tunnel monitor")) } @@ -515,9 +563,9 @@ impl Daemon { } fn set_security_policy(&mut self) -> Result<()> { - let policy = match (self.remote_endpoint, self.tunnel_interface.as_ref()) { - (Some(remote), None) => SecurityPolicy::Connecting(remote), - (Some(remote), Some(interface)) => SecurityPolicy::Connected(remote, interface.clone()), + let policy = match (self.relay_endpoint, self.tunnel_interface.as_ref()) { + (Some(relay), None) => SecurityPolicy::Connecting(relay), + (Some(relay), Some(interface)) => SecurityPolicy::Connected(relay, interface.clone()), _ => bail!(ErrorKind::InvalidState), }; debug!("Set security policy: {:?}", policy); diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index bca4f68f13..ff63fcfde7 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -10,6 +10,7 @@ use jsonrpc_pubsub::{PubSubHandler, PubSubMetadata, Session, SubscriptionId}; use jsonrpc_ws_server; use mullvad_types::account::{AccountData, AccountToken}; use mullvad_types::location::{CountryCode, Location}; +use mullvad_types::relay_endpoint::RelayEndpoint; use mullvad_types::states::{DaemonState, TargetState}; use serde; @@ -49,9 +50,13 @@ build_rpc_trait! { #[rpc(async, name = "get_account")] fn get_account(&self) -> BoxFuture<Option<AccountToken>, Error>; - /// Set which country to connect to - #[rpc(name = "set_country")] - fn set_country(&self, CountryCode) -> Result<(), Error>; + /// Set which relay to connect to + #[rpc(async, name = "set_custom_relay")] + fn set_custom_relay(&self, RelayEndpoint) -> BoxFuture<(), Error>; + + /// Unset the custom relay, reverting to the default relay listing + #[rpc(async, name = "remove_custom_relay")] + fn remove_custom_relay(&self) -> BoxFuture<(), Error>; /// Set if the client should automatically establish a tunnel on start or not. #[rpc(name = "set_autoconnect")] @@ -115,6 +120,8 @@ pub enum TunnelCommand { SetAccount(OneshotSender<()>, Option<AccountToken>), /// Request the current account token being used. GetAccount(OneshotSender<Option<AccountToken>>), + /// Set a custom relay instead of the default list of relays + SetCustomRelay(OneshotSender<()>, Option<RelayEndpoint>), } #[derive(Default)] @@ -312,9 +319,22 @@ impl<T: From<TunnelCommand> + 'static + Send> ManagementInterfaceApi for Managem Box::new(future) } - fn set_country(&self, _country_code: CountryCode) -> Result<(), Error> { - trace!("set_country"); - Ok(()) + fn set_custom_relay(&self, custom_relay: RelayEndpoint) -> BoxFuture<(), Error> { + trace!("set_custom_relay"); + let (tx, rx) = sync::oneshot::channel(); + + let message = TunnelCommand::SetCustomRelay(tx, Some(custom_relay)); + let future = self.send_command_to_daemon(message) + .and_then(|_| rx.map_err(|_| Error::internal_error())); + Box::new(future) + } + + fn remove_custom_relay(&self) -> BoxFuture<(), Error> { + trace!("remove_custom_relay"); + let (tx, rx) = sync::oneshot::channel(); + let future = self.send_command_to_daemon(TunnelCommand::SetCustomRelay(tx, None)) + .and_then(|_| rx.map_err(|_| Error::internal_error())); + Box::new(future) } fn set_autoconnect(&self, _autoconnect: bool) -> Result<(), Error> { diff --git a/mullvad-daemon/src/settings.rs b/mullvad-daemon/src/settings.rs index d2d652adf0..63a4325d5e 100644 --- a/mullvad-daemon/src/settings.rs +++ b/mullvad-daemon/src/settings.rs @@ -3,6 +3,8 @@ extern crate toml; use self::app_dirs::{AppDataType, AppInfo}; +use mullvad_types::relay_endpoint::RelayEndpoint; + use std::fs::File; use std::io::{self, Read, Write}; use std::path::PathBuf; @@ -36,9 +38,13 @@ static SETTINGS_FILE: &str = "settings.toml"; #[derive(Debug, Clone, Deserialize, Serialize)] pub struct Settings { account_token: Option<String>, + custom_relay: Option<RelayEndpoint>, } -const DEFAULT_SETTINGS: Settings = Settings { account_token: None }; +const DEFAULT_SETTINGS: Settings = Settings { + account_token: None, + custom_relay: None, +}; impl Settings { /// Loads user settings from file. If no file is present it returns the defaults. @@ -111,4 +117,24 @@ impl Settings { None => "[none]".to_owned(), } } + + pub fn get_custom_relay(&self) -> Option<RelayEndpoint> { + self.custom_relay.clone() + } + + pub fn set_custom_relay(&mut self, relay_endpoint: Option<RelayEndpoint>) -> Result<bool> { + if self.custom_relay != relay_endpoint { + match &relay_endpoint { + &Some(ref data) => info!("Setting a custom relay: {}", data), + &None => info!("Removing the custom relay"), + } + + self.custom_relay = relay_endpoint; + + self.save() + .map(|_| true) + } else { + Ok(false) + } + } } diff --git a/mullvad-types/Cargo.toml b/mullvad-types/Cargo.toml index 148970e428..e710e67d87 100644 --- a/mullvad-types/Cargo.toml +++ b/mullvad-types/Cargo.toml @@ -8,3 +8,7 @@ license = "GPL-3.0" chrono = { version = "0.4", features = ["serde"] } serde_derive = "1.0" serde = "1.0" +error-chain = "0.11" +log = "0.3" + +talpid-types = { path = "../talpid-types" } diff --git a/mullvad-types/src/lib.rs b/mullvad-types/src/lib.rs index 0886a3c00b..608e3eee80 100644 --- a/mullvad-types/src/lib.rs +++ b/mullvad-types/src/lib.rs @@ -11,6 +11,15 @@ extern crate chrono; extern crate serde_derive; extern crate serde; +extern crate talpid_types; + +#[macro_use] +extern crate log; + +#[macro_use] +extern crate error_chain; + pub mod account; pub mod location; pub mod states; +pub mod relay_endpoint; diff --git a/mullvad-types/src/relay_endpoint.rs b/mullvad-types/src/relay_endpoint.rs new file mode 100644 index 0000000000..894fcab3b5 --- /dev/null +++ b/mullvad-types/src/relay_endpoint.rs @@ -0,0 +1,71 @@ +use std::fmt; +use std::net::{SocketAddr, ToSocketAddrs}; +use talpid_types; +use talpid_types::net::TransportProtocol; + +error_chain!{ + errors { + InvalidHost(host: String) { + display("Invalid host: {}", host) + } + } +} + +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub struct RelayEndpoint { + pub host: String, + pub port: u16, + pub protocol: TransportProtocol, +} + +impl RelayEndpoint { + pub fn to_endpoint(&self) -> Result<talpid_types::net::Endpoint> { + + let socket_addrs = to_socket_addrs(self.host.as_str(), self.port)?; + ensure!( + socket_addrs.len() > 0, + ErrorKind::InvalidHost(self.host.clone()) + ); + + let socket_addr = choose_ip(&socket_addrs).unwrap(); + + if socket_addrs.len() > 1 { + info!( + "{} resolved to more than one IP, ignoring all but {}", + self.host, + socket_addr.ip() + ) + } + + Ok(talpid_types::net::Endpoint::new(socket_addr.ip(), socket_addr.port(), self.protocol),) + } +} + +/// Does a DNS lookup if the host isn't an IP. +fn to_socket_addrs(host: &str, port: u16) -> Result<Vec<SocketAddr>> { + Ok( + (host, port) + .to_socket_addrs() + .chain_err(|| ErrorKind::InvalidHost(host.to_owned()))? + .collect(), + ) +} + +fn choose_ip(socket_addrs: &Vec<SocketAddr>) -> Option<SocketAddr> { + // We prefer IPv4 addresses, so we split the addresses into + // IPv4 ad IPv6s and take form the IPv4 pile if any. + + let (mut ipv4, mut ipv6): (Vec<SocketAddr>, Vec<SocketAddr>) = + socket_addrs + .into_iter() + .partition(|addr| addr.is_ipv4()); + + // If there are many IP:s, we simply ignore the rest + ipv4.pop().or(ipv6.pop()) +} + +impl fmt::Display for RelayEndpoint { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "{}:{} - {}", self.host, self.port, self.protocol) + } +} diff --git a/talpid-core/Cargo.toml b/talpid-core/Cargo.toml index 9b9cada258..ba70f49071 100644 --- a/talpid-core/Cargo.toml +++ b/talpid-core/Cargo.toml @@ -16,6 +16,7 @@ uuid = { version = "0.5", features = ["v4"] } openvpn-plugin = { version = "0.2", features = ["serialize"] } talpid-ipc = { path = "../talpid-ipc" } +talpid-types = { path = "../talpid-types" } [target.'cfg(unix)'.dependencies] libc = "0.2.20" diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs index 5be5d4764e..e3bfa9870f 100644 --- a/talpid-core/src/firewall/macos.rs +++ b/talpid-core/src/firewall/macos.rs @@ -1,7 +1,7 @@ use super::{Firewall, SecurityPolicy}; -use net; use pfctl; use std::net::Ipv4Addr; +use talpid_types::net; // alias used to instantiate firewall implementation pub type ConcreteFirewall = PacketFilter; @@ -9,15 +9,6 @@ pub use pfctl::{Error, ErrorKind, Result}; const ANCHOR_NAME: &'static str = "talpid_core"; -impl From<net::TransportProtocol> for pfctl::Proto { - fn from(protocol: net::TransportProtocol) -> Self { - match protocol { - net::TransportProtocol::Udp => pfctl::Proto::Udp, - net::TransportProtocol::Tcp => pfctl::Proto::Tcp, - } - } -} - pub struct PacketFilter { pf: pfctl::PfCtl, pf_was_enabled: Option<bool>, @@ -85,11 +76,13 @@ impl PacketFilter { } fn get_relay_rule(relay_endpoint: net::Endpoint) -> Result<pfctl::FilterRule> { + let pfctl_proto = as_pfctl_proto(relay_endpoint.protocol); + pfctl::FilterRuleBuilder::default() .action(pfctl::FilterRuleAction::Pass) .direction(pfctl::Direction::Out) .to(relay_endpoint.address) - .proto(relay_endpoint.protocol) + .proto(pfctl_proto) .keep_state(pfctl::StatePolicy::Keep) .tcp_flags(Self::get_tcp_flags()) .quick(true) @@ -174,3 +167,10 @@ impl PacketFilter { self.pf.try_remove_anchor(ANCHOR_NAME, pfctl::AnchorKind::Filter) } } + +fn as_pfctl_proto(protocol: net::TransportProtocol) -> pfctl::Proto { + match protocol { + net::TransportProtocol::Udp => pfctl::Proto::Udp, + net::TransportProtocol::Tcp => pfctl::Proto::Tcp, + } +} diff --git a/talpid-core/src/firewall/mod.rs b/talpid-core/src/firewall/mod.rs index 16b8139453..acc0f21792 100644 --- a/talpid-core/src/firewall/mod.rs +++ b/talpid-core/src/firewall/mod.rs @@ -1,4 +1,4 @@ -use net::Endpoint; +use talpid_types::net::Endpoint; #[cfg(target_os = "macos")] #[path = "macos.rs"] diff --git a/talpid-core/src/lib.rs b/talpid-core/src/lib.rs index 271553c10c..c4cff6ebfc 100644 --- a/talpid-core/src/lib.rs +++ b/talpid-core/src/lib.rs @@ -30,6 +30,7 @@ extern crate jsonrpc_macros; extern crate uuid; extern crate talpid_ipc; +extern crate talpid_types; extern crate openvpn_plugin; #[cfg(target_os = "macos")] @@ -38,9 +39,6 @@ extern crate pfctl; /// Working with processes. pub mod process; -/// Network primitives. -pub mod net; - /// Abstracts over different VPN tunnel technologies pub mod tunnel; diff --git a/talpid-core/src/net.rs b/talpid-core/src/net.rs deleted file mode 100644 index ba8a3fe665..0000000000 --- a/talpid-core/src/net.rs +++ /dev/null @@ -1,29 +0,0 @@ -use std::net::{IpAddr, SocketAddr}; - -/// Represents a network layer IP address together with the transport layer protocol and port. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct Endpoint { - /// The address part of this endpoint, contains the IP and port. - pub address: SocketAddr, - /// The protocol part of this endpoint. - pub protocol: TransportProtocol, -} - -impl Endpoint { - /// Constructs a new `Endpoint` from the given parameters. - pub fn new<T: Into<IpAddr>>(address: T, port: u16, protocol: TransportProtocol) -> Self { - Endpoint { - address: SocketAddr::new(address.into(), port), - protocol: protocol, - } - } -} - -/// Representation of a transport protocol, either UDP or TCP. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub enum TransportProtocol { - /// Represents the UDP transport protocol. - Udp, - /// Represents the TCP transport protocol. - Tcp, -} diff --git a/talpid-core/src/process/openvpn.rs b/talpid-core/src/process/openvpn.rs index ce663809ce..c756757a75 100644 --- a/talpid-core/src/process/openvpn.rs +++ b/talpid-core/src/process/openvpn.rs @@ -1,11 +1,11 @@ use duct; -use net; - use std::ffi::{OsStr, OsString}; use std::fmt; use std::path::{Path, PathBuf}; +use talpid_types::net; + static BASE_ARGUMENTS: &[&[&str]] = &[ &["--client"], &["--nobind"], @@ -190,9 +190,9 @@ fn write_argument(fmt: &mut fmt::Formatter, arg: &str) -> fmt::Result { #[cfg(test)] mod tests { use super::OpenVpnCommand; - use net::{Endpoint, TransportProtocol}; use std::ffi::OsString; use std::net::Ipv4Addr; + use talpid_types::net::{Endpoint, TransportProtocol}; #[test] fn passes_one_remote() { diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index a2ab25a487..83212e6874 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -1,5 +1,4 @@ use mktemp; -use net; use openvpn_plugin::types::OpenVpnPluginEvent; @@ -11,6 +10,7 @@ use std::ffi::{OsStr, OsString}; use std::fs; use std::io::{self, Write}; use std::path::{Path, PathBuf}; +use talpid_types::net; /// A module for all OpenVPN related tunnel management. pub mod openvpn; diff --git a/talpid-types/Cargo.toml b/talpid-types/Cargo.toml new file mode 100644 index 0000000000..4e1e7f9171 --- /dev/null +++ b/talpid-types/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "talpid-types" +version = "0.1.0" +authors = ["Linus Färnstrand <linus@mullvad.net>"] +license = "GPL-3.0" + +[dependencies] +serde_derive = "1.0" +serde = "1.0" diff --git a/talpid-types/src/lib.rs b/talpid-types/src/lib.rs new file mode 100644 index 0000000000..c32daa26d3 --- /dev/null +++ b/talpid-types/src/lib.rs @@ -0,0 +1,13 @@ +//! # License +//! +//! Copyright (C) 2017 Amagicom AB +//! +//! This program is free software: you can redistribute it and/or modify it under the terms of the +//! GNU General Public License as published by the Free Software Foundation, either version 3 of +//! the License, or (at your option) any later version. + +#[macro_use] +extern crate serde_derive; +extern crate serde; + +pub mod net; diff --git a/talpid-types/src/net.rs b/talpid-types/src/net.rs new file mode 100644 index 0000000000..f470e8a24a --- /dev/null +++ b/talpid-types/src/net.rs @@ -0,0 +1,69 @@ +use std::error::Error; +use std::fmt; +use std::net::{IpAddr, SocketAddr}; +use std::str::FromStr; + +/// Represents a network layer IP address together with the transport layer protocol and port. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct Endpoint { + /// The address part of this endpoint, contains the IP and port. + pub address: SocketAddr, + /// The protocol part of this endpoint. + pub protocol: TransportProtocol, +} + +impl Endpoint { + /// Constructs a new `Endpoint` from the given parameters. + pub fn new<T: Into<IpAddr>>(address: T, port: u16, protocol: TransportProtocol) -> Self { + Endpoint { + address: SocketAddr::new(address.into(), port), + protocol: protocol, + } + } +} + +/// Representation of a transport protocol, either UDP or TCP. +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TransportProtocol { + /// Represents the UDP transport protocol. + Udp, + /// Represents the TCP transport protocol. + Tcp, +} + +impl FromStr for TransportProtocol { + type Err = TransportProtocolParseError; + + fn from_str(s: &str) -> ::std::result::Result<TransportProtocol, Self::Err> { + match s { + "udp" => Ok(TransportProtocol::Udp), + "tcp" => Ok(TransportProtocol::Tcp), + _ => Err(TransportProtocolParseError), + } + } +} + +impl fmt::Display for TransportProtocol { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match *self { + TransportProtocol::Udp => "UDP".fmt(fmt), + TransportProtocol::Tcp => "TCP".fmt(fmt), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct TransportProtocolParseError; + +impl fmt::Display for TransportProtocolParseError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.write_str(self.description()) + } +} + +impl Error for TransportProtocolParseError { + fn description(&self) -> &str { + "Not a valid transport protocol" + } +} |
