summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorErik Larkö <erik@mullvad.net>2017-09-13 07:33:30 +0200
committerErik Larkö <erik@mullvad.net>2017-09-13 07:33:30 +0200
commitb572d0634b78d3de05ce39e5a49ce893c8065d6b (patch)
tree9e955cb2ab5499eb97a76e9c4d22a384fae9b42b
parent2bc13231538133ad452b1b72ac4bbfe67235df02 (diff)
parentc383938bf44bb0b045ec63871a4bb804921fb2db (diff)
downloadmullvadvpn-b572d0634b78d3de05ce39e5a49ce893c8065d6b.tar.xz
mullvadvpn-b572d0634b78d3de05ce39e5a49ce893c8065d6b.zip
Merge branch 'set_custom_relay-endpoint'
-rw-r--r--Cargo.lock26
-rw-r--r--mullvad-cli/Cargo.toml1
-rw-r--r--mullvad-cli/src/cmds/account.rs3
-rw-r--r--mullvad-cli/src/cmds/custom_relay.rs74
-rw-r--r--mullvad-cli/src/cmds/mod.rs4
-rw-r--r--mullvad-cli/src/main.rs1
-rw-r--r--mullvad-daemon/Cargo.toml1
-rw-r--r--mullvad-daemon/src/main.rs84
-rw-r--r--mullvad-daemon/src/management_interface.rs32
-rw-r--r--mullvad-daemon/src/settings.rs28
-rw-r--r--mullvad-types/Cargo.toml4
-rw-r--r--mullvad-types/src/lib.rs9
-rw-r--r--mullvad-types/src/relay_endpoint.rs71
-rw-r--r--talpid-core/Cargo.toml1
-rw-r--r--talpid-core/src/firewall/macos.rs22
-rw-r--r--talpid-core/src/firewall/mod.rs2
-rw-r--r--talpid-core/src/lib.rs4
-rw-r--r--talpid-core/src/net.rs29
-rw-r--r--talpid-core/src/process/openvpn.rs6
-rw-r--r--talpid-core/src/tunnel/mod.rs2
-rw-r--r--talpid-types/Cargo.toml9
-rw-r--r--talpid-types/src/lib.rs13
-rw-r--r--talpid-types/src/net.rs69
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"
+ }
+}