diff options
| author | Odd Stranne <odd@mullvad.net> | 2018-10-23 12:57:27 +0200 |
|---|---|---|
| committer | Odd Stranne <odd@mullvad.net> | 2018-10-30 14:57:46 +0100 |
| commit | 7921dcd9d1f888dc8afbac1aea943af20ef27dba (patch) | |
| tree | fb389be65ebc1e6be172f90170b7dfd9274aa0bc | |
| parent | 313a6657a0c3d35e6e83836d6edf99a87d423fc3 (diff) | |
| download | mullvadvpn-7921dcd9d1f888dc8afbac1aea943af20ef27dba.tar.xz mullvadvpn-7921dcd9d1f888dc8afbac1aea943af20ef27dba.zip | |
Add OpenVPN proxy support in Daemon
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 30 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 34 | ||||
| -rw-r--r-- | mullvad-ipc-client/src/lib.rs | 6 | ||||
| -rw-r--r-- | mullvad-types/src/settings.rs | 26 | ||||
| -rw-r--r-- | talpid-core/src/process/openvpn.rs | 47 | ||||
| -rw-r--r-- | talpid-core/src/security/linux/mod.rs | 8 | ||||
| -rw-r--r-- | talpid-core/src/security/macos/mod.rs | 8 | ||||
| -rw-r--r-- | talpid-core/src/security/mod.rs | 20 | ||||
| -rw-r--r-- | talpid-core/src/security/windows/mod.rs | 8 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 60 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connected_state.rs | 16 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connecting_state.rs | 33 | ||||
| -rw-r--r-- | talpid-types/src/net.rs | 62 |
13 files changed, 311 insertions, 47 deletions
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 13ae19e7a7..03a789362a 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -55,6 +55,7 @@ use mullvad_types::{ location::GeoIpLocation, relay_constraints::{RelaySettings, RelaySettingsUpdate}, relay_list::{Relay, RelayList}, + settings, settings::Settings, states::TargetState, version::{AppVersion, AppVersionInfo}, @@ -64,7 +65,10 @@ use talpid_core::{ mpsc::IntoSender, tunnel_state_machine::{self, TunnelCommand, TunnelParameters, TunnelParametersGenerator}, }; -use talpid_types::tunnel::{BlockReason, TunnelStateTransition}; +use talpid_types::{ + net::OpenVpnProxySettings, + tunnel::{BlockReason, TunnelStateTransition}, +}; error_chain!{ @@ -369,7 +373,7 @@ impl Daemon { tunnel_parameters_tx .send(TunnelParameters { endpoint, - options: self.settings.get_tunnel_options(), + options: self.settings.get_tunnel_options().clone(), username: account_token, }) .map_err(|_| Error::from("Tunnel parameters receiver stopped listening")) @@ -417,6 +421,7 @@ impl Daemon { SetAllowLan(tx, allow_lan) => self.on_set_allow_lan(tx, allow_lan), SetAutoConnect(tx, auto_connect) => self.on_set_auto_connect(tx, auto_connect), SetOpenVpnMssfix(tx, mssfix_arg) => self.on_set_openvpn_mssfix(tx, mssfix_arg), + SetOpenVpnProxy(tx, proxy) => self.on_set_openvpn_proxy(tx, proxy), SetEnableIpv6(tx, enable_ipv6) => self.on_set_enable_ipv6(tx, enable_ipv6), GetSettings(tx) => self.on_get_settings(tx), GetVersionInfo(tx) => self.on_get_version_info(tx), @@ -619,6 +624,27 @@ impl Daemon { } } + fn on_set_openvpn_proxy( + &mut self, + tx: oneshot::Sender<::std::result::Result<(), settings::Error>>, + proxy: Option<OpenVpnProxySettings>, + ) { + let save_result = self.settings.set_openvpn_proxy(proxy); + match save_result { + Ok(settings_changed) => { + Self::oneshot_send(tx, Ok(()), "set_openvpn_proxy response"); + if settings_changed { + self.management_interface_broadcaster + .notify_settings(&self.settings); + } + } + Err(settings_error) => { + error!("{}", settings_error.display_chain()); + Self::oneshot_send(tx, Err(settings_error), "set_openvpn_proxy response"); + } + } + } + fn on_set_enable_ipv6(&mut self, tx: oneshot::Sender<()>, enable_ipv6: bool) { let save_result = self.settings.set_enable_ipv6(enable_ipv6); match save_result.chain_err(|| "Unable to save settings") { diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 07fd603e2a..3e204245ae 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -11,6 +11,7 @@ use mullvad_types::account::{AccountData, AccountToken}; use mullvad_types::location::GeoIpLocation; use mullvad_types::relay_constraints::RelaySettingsUpdate; use mullvad_types::relay_list::RelayList; +use mullvad_types::settings; use mullvad_types::settings::Settings; use mullvad_types::states::TargetState; use mullvad_types::version; @@ -24,7 +25,7 @@ use std::sync::{Arc, Mutex, RwLock}; use talpid_core::mpsc::IntoSender; use talpid_ipc; -use talpid_types::tunnel::TunnelStateTransition; +use talpid_types::{net::OpenVpnProxySettings, tunnel::TunnelStateTransition}; use uuid; use account_history::{AccountHistory, Error as AccountHistoryError}; @@ -101,6 +102,10 @@ build_rpc_trait! { #[rpc(meta, name = "set_openvpn_mssfix")] fn set_openvpn_mssfix(&self, Self::Metadata, Option<u16>) -> BoxFuture<(), Error>; + /// Sets proxy details for OpenVPN + #[rpc(meta, name = "set_openvpn_proxy")] + fn set_openvpn_proxy(&self, Self::Metadata, Option<OpenVpnProxySettings>) -> BoxFuture<(), Error>; + /// Set if IPv6 is enabled in the tunnel #[rpc(meta, name = "set_enable_ipv6")] fn set_enable_ipv6(&self, Self::Metadata, bool) -> BoxFuture<(), Error>; @@ -170,6 +175,11 @@ pub enum ManagementCommand { SetAutoConnect(OneshotSender<()>, bool), /// Set the mssfix argument for OpenVPN SetOpenVpnMssfix(OneshotSender<()>, Option<u16>), + /// Set proxy details for OpenVPN + SetOpenVpnProxy( + OneshotSender<Result<(), settings::Error>>, + Option<OpenVpnProxySettings>, + ), /// Set if IPv6 should be enabled in the tunnel SetEnableIpv6(OneshotSender<()>, bool), /// Get the daemon settings @@ -537,6 +547,28 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } + fn set_openvpn_proxy( + &self, + _: Self::Metadata, + proxy: Option<OpenVpnProxySettings>, + ) -> BoxFuture<(), Error> { + log::debug!("set_openvpn_proxy({:?})", proxy); + let (tx, rx) = sync::oneshot::channel(); + let future = self + .send_command_to_daemon(ManagementCommand::SetOpenVpnProxy(tx, proxy)) + .and_then(|_| rx.map_err(|_| Error::internal_error())) + .and_then(|settings_result| { + settings_result.map_err(|err| match err.kind() { + settings::ErrorKind::InvalidProxyData(msg) => { + Error::invalid_params(msg.to_owned()) + } + _ => Error::internal_error(), + }) + }); + + Box::new(future) + } + fn set_enable_ipv6(&self, _: Self::Metadata, enable_ipv6: bool) -> BoxFuture<(), Error> { log::debug!("set_enable_ipv6({})", enable_ipv6); let (tx, rx) = sync::oneshot::channel(); diff --git a/mullvad-ipc-client/src/lib.rs b/mullvad-ipc-client/src/lib.rs index a98ab74f92..9b20899ab2 100644 --- a/mullvad-ipc-client/src/lib.rs +++ b/mullvad-ipc-client/src/lib.rs @@ -26,7 +26,7 @@ use mullvad_types::relay_list::RelayList; use mullvad_types::settings::Settings; use mullvad_types::version::AppVersionInfo; use serde::{Deserialize, Serialize}; -use talpid_types::net::TunnelOptions; +use talpid_types::net::{OpenVpnProxySettings, TunnelOptions}; use talpid_types::tunnel::TunnelStateTransition; use futures::stream::{self, Stream}; @@ -199,6 +199,10 @@ impl DaemonRpcClient { self.call("set_openvpn_mssfix", &[mssfix]) } + pub fn set_openvpn_proxy(&mut self, proxy: Option<OpenVpnProxySettings>) -> Result<()> { + self.call("set_openvpn_proxy", &[proxy]) + } + pub fn shutdown(&mut self) -> Result<()> { self.call("shutdown", &NO_ARGS) } diff --git a/mullvad-types/src/settings.rs b/mullvad-types/src/settings.rs index 34bc00e820..e78ab3c422 100644 --- a/mullvad-types/src/settings.rs +++ b/mullvad-types/src/settings.rs @@ -4,10 +4,11 @@ use log::{debug, info}; use relay_constraints::{ Constraint, LocationConstraint, RelayConstraints, RelaySettings, RelaySettingsUpdate, }; + use std::fs::File; use std::io; use std::path::PathBuf; -use talpid_types::net::TunnelOptions; +use talpid_types::net::{OpenVpnProxySettings, OpenVpnProxySettingsValidation, TunnelOptions}; error_chain! { errors { @@ -25,6 +26,10 @@ error_chain! { ParseError { description("Malformed settings") } + InvalidProxyData(reason: String) { + description("Invalid proxy configuration was rejected") + display("Invalid proxy configuration was rejected: {}", reason) + } } } @@ -182,6 +187,21 @@ impl Settings { } } + pub fn set_openvpn_proxy(&mut self, proxy: Option<OpenVpnProxySettings>) -> Result<bool> { + if let Some(ref settings) = proxy { + if let Err(validation_error) = OpenVpnProxySettingsValidation::validate(settings) { + bail!(ErrorKind::InvalidProxyData(validation_error)); + } + } + + if self.tunnel_options.openvpn.proxy != proxy { + self.tunnel_options.openvpn.proxy = proxy; + self.save().map(|_| true) + } else { + Ok(false) + } + } + pub fn set_enable_ipv6(&mut self, enable_ipv6: bool) -> Result<bool> { if self.tunnel_options.enable_ipv6 != enable_ipv6 { self.tunnel_options.enable_ipv6 = enable_ipv6; @@ -191,7 +211,7 @@ impl Settings { } } - pub fn get_tunnel_options(&self) -> TunnelOptions { - self.tunnel_options + pub fn get_tunnel_options(&self) -> &TunnelOptions { + &self.tunnel_options } } diff --git a/talpid-core/src/process/openvpn.rs b/talpid-core/src/process/openvpn.rs index 761d4e6206..014e475524 100644 --- a/talpid-core/src/process/openvpn.rs +++ b/talpid-core/src/process/openvpn.rs @@ -46,6 +46,7 @@ pub struct OpenVpnCommand { config: Option<PathBuf>, remote: Option<net::Endpoint>, user_pass_path: Option<PathBuf>, + proxy_auth_path: Option<PathBuf>, ca: Option<PathBuf>, crl: Option<PathBuf>, iproute_bin: Option<OsString>, @@ -65,6 +66,7 @@ impl OpenVpnCommand { config: None, remote: None, user_pass_path: None, + proxy_auth_path: None, ca: None, crl: None, iproute_bin: None, @@ -95,6 +97,13 @@ impl OpenVpnCommand { self } + /// Sets the path to the file where the username and password for proxy authentication + /// is stored. + pub fn proxy_auth<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { + self.proxy_auth_path = Some(path.as_ref().to_path_buf()); + self + } + /// Sets the path to the CA certificate file. pub fn ca<P: AsRef<Path>>(&mut self, path: P) -> &mut Self { self.ca = Some(path.as_ref().to_path_buf()); @@ -133,7 +142,7 @@ impl OpenVpnCommand { /// Sets extra options pub fn tunnel_options(&mut self, tunnel_options: &net::OpenVpnTunnelOptions) -> &mut Self { - self.tunnel_options = *tunnel_options; + self.tunnel_options = tunnel_options.clone(); self } @@ -208,6 +217,7 @@ impl OpenVpnCommand { } args.extend(Self::security_arguments().iter().map(OsString::from)); + args.extend(self.proxy_arguments().iter().map(OsString::from)); args } @@ -252,6 +262,41 @@ impl OpenVpnCommand { } args } + + fn proxy_arguments(&self) -> Vec<String> { + let mut args = vec![]; + match self.tunnel_options.proxy { + Some(net::OpenVpnProxySettings::Local(ref local_proxy)) => { + args.push("--socks-proxy".to_owned()); + args.push("127.0.0.1".to_owned()); + args.push(local_proxy.port.to_string()); + args.push("--route".to_owned()); + args.push(local_proxy.peer.ip().to_string()); + args.push("255.255.255.255".to_owned()); + args.push("net_gateway".to_owned()); + } + Some(net::OpenVpnProxySettings::Remote(ref remote_proxy)) => { + args.push("--socks-proxy".to_owned()); + args.push(remote_proxy.address.ip().to_string()); + args.push(remote_proxy.address.port().to_string()); + + if let Some(ref _auth) = remote_proxy.auth { + if let Some(ref auth_file) = self.proxy_auth_path { + args.push(auth_file.to_string_lossy().to_string()); + } else { + log::error!("Proxy credentials present but credentials file missing"); + } + } + + args.push("--route".to_owned()); + args.push(remote_proxy.address.ip().to_string()); + args.push("255.255.255.255".to_owned()); + args.push("net_gateway".to_owned()); + } + None => {} + }; + args + } } impl fmt::Display for OpenVpnCommand { diff --git a/talpid-core/src/security/linux/mod.rs b/talpid-core/src/security/linux/mod.rs index 83b0816988..9641830f2a 100644 --- a/talpid-core/src/security/linux/mod.rs +++ b/talpid-core/src/security/linux/mod.rs @@ -227,18 +227,18 @@ impl<'a> PolicyBatch<'a> { fn add_policy_specific_rules(&mut self, policy: &SecurityPolicy) -> Result<()> { let allow_lan = match policy { SecurityPolicy::Connecting { - relay_endpoint, + peer_endpoint, allow_lan, } => { - self.add_allow_endpoint_rules(relay_endpoint)?; + self.add_allow_endpoint_rules(peer_endpoint)?; *allow_lan } SecurityPolicy::Connected { - relay_endpoint, + peer_endpoint, tunnel, allow_lan, } => { - self.add_allow_endpoint_rules(relay_endpoint)?; + self.add_allow_endpoint_rules(peer_endpoint)?; self.add_dns_rule(tunnel, TransportProtocol::Udp)?; self.add_dns_rule(tunnel, TransportProtocol::Tcp)?; self.add_allow_tunnel_rules(tunnel)?; diff --git a/talpid-core/src/security/macos/mod.rs b/talpid-core/src/security/macos/mod.rs index f83567f91e..d588d21177 100644 --- a/talpid-core/src/security/macos/mod.rs +++ b/talpid-core/src/security/macos/mod.rs @@ -87,17 +87,17 @@ impl NetworkSecurity { ) -> Result<Vec<pfctl::FilterRule>> { match policy { SecurityPolicy::Connecting { - relay_endpoint, + peer_endpoint, allow_lan, } => { - let mut rules = vec![Self::get_allow_relay_rule(relay_endpoint)?]; + let mut rules = vec![Self::get_allow_relay_rule(peer_endpoint)?]; if allow_lan { rules.append(&mut Self::get_allow_lan_rules()?); } Ok(rules) } SecurityPolicy::Connected { - relay_endpoint, + peer_endpoint, tunnel, allow_lan, } => { @@ -139,7 +139,7 @@ impl NetworkSecurity { allow_udp_dns_to_relay_rule, block_tcp_dns_rule, block_udp_dns_rule, - Self::get_allow_relay_rule(relay_endpoint)?, + Self::get_allow_relay_rule(peer_endpoint)?, Self::get_allow_tunnel_rule(tunnel.interface.as_str())?, ]; diff --git a/talpid-core/src/security/mod.rs b/talpid-core/src/security/mod.rs index a18ecc26da..6e8149e87b 100644 --- a/talpid-core/src/security/mod.rs +++ b/talpid-core/src/security/mod.rs @@ -43,18 +43,18 @@ lazy_static! { /// A enum that describes network security strategy #[derive(Debug, Clone, Eq, PartialEq)] pub enum SecurityPolicy { - /// Allow traffic only to relay server + /// Allow traffic only to server Connecting { - /// The relay endpoint that should be allowed. - relay_endpoint: Endpoint, + /// The peer endpoint that should be allowed. + peer_endpoint: Endpoint, /// Flag setting if communication with LAN networks should be possible. allow_lan: bool, }, - /// Allow traffic only to relay server and over tunnel interface + /// Allow traffic only to server and over tunnel interface Connected { - /// The relay endpoint that should be allowed. - relay_endpoint: Endpoint, + /// The peer endpoint that should be allowed. + peer_endpoint: Endpoint, /// Metadata about the tunnel and tunnel interface. tunnel: ::tunnel::TunnelMetadata, /// Flag setting if communication with LAN networks should be possible. @@ -72,22 +72,22 @@ impl fmt::Display for SecurityPolicy { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self { SecurityPolicy::Connecting { - relay_endpoint, + peer_endpoint, allow_lan, } => write!( f, "Connecting to {}, {} LAN", - relay_endpoint, + peer_endpoint, if *allow_lan { "Allowing" } else { "Blocking" } ), SecurityPolicy::Connected { - relay_endpoint, + peer_endpoint, tunnel, allow_lan, } => write!( f, "Connected to {} over \"{}\" (ip: {}, gw: {}), {} LAN", - relay_endpoint, + peer_endpoint, tunnel.interface, tunnel.ip, tunnel.gateway, diff --git a/talpid-core/src/security/windows/mod.rs b/talpid-core/src/security/windows/mod.rs index 75961aa69f..65590f6f19 100644 --- a/talpid-core/src/security/windows/mod.rs +++ b/talpid-core/src/security/windows/mod.rs @@ -87,19 +87,19 @@ impl NetworkSecurityT for NetworkSecurity { fn apply_policy(&mut self, policy: SecurityPolicy) -> Result<()> { match policy { SecurityPolicy::Connecting { - relay_endpoint, + peer_endpoint, allow_lan, } => { let cfg = &WinFwSettings::new(allow_lan); - self.set_connecting_state(&relay_endpoint, &cfg) + self.set_connecting_state(&peer_endpoint, &cfg) } SecurityPolicy::Connected { - relay_endpoint, + peer_endpoint, tunnel, allow_lan, } => { let cfg = &WinFwSettings::new(allow_lan); - self.set_connected_state(&relay_endpoint, &cfg, &tunnel) + self.set_connected_state(&peer_endpoint, &cfg, &tunnel) } SecurityPolicy::Blocked { allow_lan } => { let cfg = &WinFwSettings::new(allow_lan); diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 3084341af5..1d8d2e967f 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -8,13 +8,16 @@ use std::fs; use std::io::{self, Write}; use std::net::Ipv4Addr; use std::path::{Path, PathBuf}; +use std::result::Result as StdResult; #[cfg(target_os = "linux")] use failure::ResultExt as FailureResultExt; #[cfg(target_os = "linux")] use which; -use talpid_types::net::{Endpoint, TunnelEndpoint, TunnelEndpointData, TunnelOptions}; +use talpid_types::net::{ + Endpoint, OpenVpnProxySettings, TunnelEndpoint, TunnelEndpointData, TunnelOptions, +}; /// A module for all OpenVPN related tunnel management. pub mod openvpn; @@ -141,6 +144,8 @@ pub struct TunnelMonitor { monitor: OpenVpnMonitor, /// Keep the `TempFile` for the user-pass file in the struct, so it's removed on drop. _user_pass_file: mktemp::TempFile, + /// Keep the 'TempFile' for the proxy user-pass file in the struct, so it's removed on drop. + _proxy_auth_file: Option<mktemp::TempFile>, } impl TunnelMonitor { @@ -161,22 +166,42 @@ impl TunnelMonitor { Self::ensure_endpoint_is_openvpn(&tunnel_endpoint)?; Self::ensure_ipv6_can_be_used_if_enabled(tunnel_options)?; - let user_pass_file = - Self::create_user_pass_file(username).chain_err(|| ErrorKind::CredentialsWriteError)?; + let user_pass_file = Self::create_credentials_file(username, "-") + .chain_err(|| ErrorKind::CredentialsWriteError)?; + + let proxy_auth_file = Self::create_proxy_auth_file(&tunnel_options.openvpn.proxy) + .chain_err(|| ErrorKind::CredentialsWriteError)?; + + // todo: also send proxy file let cmd = Self::create_openvpn_cmd( tunnel_endpoint.to_endpoint(), tunnel_alias, &tunnel_options, user_pass_file.as_ref(), + match proxy_auth_file { + Some(ref file) => Some(file.as_ref()), + _ => None, + }, log, resource_dir, )?; let user_pass_file_path = user_pass_file.to_path_buf(); + + let proxy_auth_file_path = match proxy_auth_file { + Some(ref file) => Some(file.to_path_buf()), + _ => None, + }; + let on_openvpn_event = move |event, env| { if event == OpenVpnPluginEvent::Up { // The user-pass file has been read. Try to delete it early. let _ = fs::remove_file(&user_pass_file_path); + + // The proxy auth file has been read. Try to delete it early. + if let Some(ref file_path) = &proxy_auth_file_path { + let _ = fs::remove_file(file_path); + } } match TunnelEvent::from_openvpn_event(event, &env) { Some(tunnel_event) => on_event(tunnel_event), @@ -193,6 +218,7 @@ impl TunnelMonitor { Ok(TunnelMonitor { monitor, _user_pass_file: user_pass_file, + _proxy_auth_file: proxy_auth_file, }) } @@ -216,6 +242,7 @@ impl TunnelMonitor { tunnel_alias: Option<OsString>, options: &TunnelOptions, user_pass_file: &Path, + proxy_auth_file: Option<&Path>, log: Option<&Path>, resource_dir: &Path, ) -> Result<OpenVpnCommand> { @@ -239,6 +266,10 @@ impl TunnelMonitor { if let Some(log) = log { cmd.log(log); } + if let Some(proxy_auth_file) = proxy_auth_file { + cmd.proxy_auth(proxy_auth_file); + } + Ok(cmd) } @@ -271,18 +302,29 @@ impl TunnelMonitor { } } - fn create_user_pass_file(username: &str) -> io::Result<mktemp::TempFile> { + fn create_credentials_file(username: &str, password: &str) -> io::Result<mktemp::TempFile> { let temp_file = mktemp::TempFile::new(); - log::debug!( - "Writing user-pass credentials to {}", - temp_file.as_ref().display() - ); + log::debug!("Writing credentials to {}", temp_file.as_ref().display()); let mut file = fs::File::create(&temp_file)?; Self::set_user_pass_file_permissions(&file)?; - write!(file, "{}\n-\n", username)?; + write!(file, "{}\n{}\n", username, password)?; Ok(temp_file) } + fn create_proxy_auth_file( + proxy: &Option<OpenVpnProxySettings>, + ) -> StdResult<Option<mktemp::TempFile>, io::Error> { + if let Some(OpenVpnProxySettings::Remote(ref remote_proxy)) = proxy { + if let Some(ref proxy_auth) = remote_proxy.auth { + return Ok(Some(Self::create_credentials_file( + &proxy_auth.username, + &proxy_auth.password, + )?)); + } + } + Ok(None) + } + #[cfg(unix)] fn set_user_pass_file_permissions(file: &fs::File) -> io::Result<()> { use std::os::unix::fs::PermissionsExt; diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs index c9004556c5..abb5c3cad5 100644 --- a/talpid-core/src/tunnel_state_machine/connected_state.rs +++ b/talpid-core/src/tunnel_state_machine/connected_state.rs @@ -1,6 +1,7 @@ use error_chain::ChainedError; use futures::sync::{mpsc, oneshot}; use futures::{Async, Future, Stream}; +use talpid_types::net::{Endpoint, OpenVpnProxySettings, TransportProtocol}; use talpid_types::tunnel::BlockReason; use super::{ @@ -40,8 +41,21 @@ impl ConnectedState { } fn set_security_policy(&self, shared_values: &mut SharedTunnelStateValues) -> Result<()> { + // If a proxy is specified we need to pass it on as the peer endpoint. + let peer_endpoint = match self.tunnel_parameters.options.openvpn.proxy { + Some(OpenVpnProxySettings::Local(ref local_proxy)) => Endpoint { + address: local_proxy.peer, + protocol: TransportProtocol::Tcp, + }, + Some(OpenVpnProxySettings::Remote(ref remote_proxy)) => Endpoint { + address: remote_proxy.address, + protocol: TransportProtocol::Tcp, + }, + _ => self.tunnel_parameters.endpoint.to_endpoint(), + }; + let policy = SecurityPolicy::Connected { - relay_endpoint: self.tunnel_parameters.endpoint.to_endpoint(), + peer_endpoint, tunnel: self.metadata.clone(), allow_lan: shared_values.allow_lan, }; diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index cc30d88ac9..873eaa9933 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -7,7 +7,9 @@ use error_chain::ChainedError; use futures::sync::{mpsc, oneshot}; use futures::{Async, Future, Stream}; use log::{debug, error, info, trace, warn}; -use talpid_types::net::{TunnelEndpoint, TunnelEndpointData}; +use talpid_types::net::{ + Endpoint, OpenVpnProxySettings, TransportProtocol, TunnelEndpoint, TunnelEndpointData, +}; use talpid_types::tunnel::BlockReason; use super::{ @@ -53,10 +55,24 @@ pub struct ConnectingState { impl ConnectingState { fn set_security_policy( shared_values: &mut SharedTunnelStateValues, + proxy: &Option<OpenVpnProxySettings>, endpoint: TunnelEndpoint, ) -> Result<()> { + // If a proxy is specified we need to pass it on as the peer endpoint. + let peer_endpoint = match proxy { + Some(OpenVpnProxySettings::Local(ref local_proxy)) => Endpoint { + address: local_proxy.peer, + protocol: TransportProtocol::Tcp, + }, + Some(OpenVpnProxySettings::Remote(ref remote_proxy)) => Endpoint { + address: remote_proxy.address, + protocol: TransportProtocol::Tcp, + }, + _ => endpoint.to_endpoint(), + }; + let policy = SecurityPolicy::Connecting { - relay_endpoint: endpoint.to_endpoint(), + peer_endpoint, allow_lan: shared_values.allow_lan, }; shared_values @@ -172,7 +188,11 @@ impl ConnectingState { match try_handle_event!(self, commands.poll()) { Ok(TunnelCommand::AllowLan(allow_lan)) => { shared_values.allow_lan = allow_lan; - match Self::set_security_policy(shared_values, self.tunnel_parameters.endpoint) { + match Self::set_security_policy( + shared_values, + &self.tunnel_parameters.options.openvpn.proxy, + self.tunnel_parameters.endpoint, + ) { Ok(()) => SameState(self), Err(error) => { error!("{}", error.display_chain()); @@ -284,8 +304,11 @@ impl TunnelState for ConnectingState { None => BlockedState::enter(shared_values, BlockReason::NoMatchingRelay), Some(tunnel_parameters) => { let tunnel_endpoint = tunnel_parameters.endpoint; - - if let Err(error) = Self::set_security_policy(shared_values, tunnel_endpoint) { + if let Err(error) = Self::set_security_policy( + shared_values, + &tunnel_parameters.options.openvpn.proxy, + tunnel_endpoint, + ) { error!("{}", error.display_chain()); BlockedState::enter(shared_values, BlockReason::StartTunnelError) } else { diff --git a/talpid-types/src/net.rs b/talpid-types/src/net.rs index e871e7a93e..8798ecf1d9 100644 --- a/talpid-types/src/net.rs +++ b/talpid-types/src/net.rs @@ -161,7 +161,7 @@ impl Error for TransportProtocolParseError { /// TunnelOptions holds optional settings for tunnels, that are to be applied to any tunnel of the /// appropriate type. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(default)] pub struct TunnelOptions { /// openvpn holds OpenVPN specific tunnel options. @@ -184,10 +184,68 @@ impl Default for TunnelOptions { /// OpenVpnTunnelOptions contains options for an openvpn tunnel that should be applied irrespective /// of the relay parameters - i.e. have nothing to do with the particular OpenVPN server, but do /// affect the connection. -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Default)] #[serde(default)] pub struct OpenVpnTunnelOptions { /// Optional argument for openvpn to try and limit TCP packet size, /// as discussed [here](https://openvpn.net/archive/openvpn-users/2003-11/msg00154.html) pub mssfix: Option<u16>, + /// Proxy settings, for when the relay connection should be via a proxy. + pub proxy: Option<OpenVpnProxySettings>, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum OpenVpnProxySettings { + Local(LocalOpenVpnProxySettings), + Remote(RemoteOpenVpnProxySettings), +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] +pub struct LocalOpenVpnProxySettings { + pub port: u16, + pub peer: SocketAddr, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] +pub struct RemoteOpenVpnProxySettings { + pub address: SocketAddr, + pub auth: Option<OpenVpnProxyAuth>, +} + +#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] +pub struct OpenVpnProxyAuth { + pub username: String, + pub password: String, +} + +pub struct OpenVpnProxySettingsValidation; + +impl OpenVpnProxySettingsValidation { + pub fn validate(proxy: &OpenVpnProxySettings) -> Result<(), String> { + match proxy { + OpenVpnProxySettings::Local(local) => { + if local.port == 0 { + return Err(String::from("Invalid local port number")); + } + if local.peer.ip().is_loopback() { + return Err(String::from( + "localhost is not a valid peer in this context", + )); + } + if local.peer.port() == 0 { + return Err(String::from("Invalid remote port number")); + } + } + OpenVpnProxySettings::Remote(remote) => { + if remote.address.port() == 0 { + return Err(String::from("Invalid port number")); + } + if remote.address.ip().is_loopback() { + return Err(String::from("localhost is not a valid remote server")); + } + } + }; + Ok(()) + } } |
