diff options
Diffstat (limited to 'mullvad-daemon/src')
| -rw-r--r-- | mullvad-daemon/src/custom_list.rs | 7 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 8 | ||||
| -rw-r--r-- | mullvad-daemon/src/migrations/v1.rs | 2 | ||||
| -rw-r--r-- | mullvad-daemon/src/migrations/v4.rs | 2 | ||||
| -rw-r--r-- | mullvad-daemon/src/migrations/v5.rs | 2 | ||||
| -rw-r--r-- | mullvad-daemon/src/migrations/v6.rs | 2 | ||||
| -rw-r--r-- | mullvad-daemon/src/relay_list/mod.rs | 214 | ||||
| -rw-r--r-- | mullvad-daemon/src/relay_list/updater.rs | 201 | ||||
| -rw-r--r-- | mullvad-daemon/src/settings/mod.rs | 13 | ||||
| -rw-r--r-- | mullvad-daemon/src/tunnel.rs | 225 |
10 files changed, 343 insertions, 333 deletions
diff --git a/mullvad-daemon/src/custom_list.rs b/mullvad-daemon/src/custom_list.rs index 8a9573fcb0..c15fd14a70 100644 --- a/mullvad-daemon/src/custom_list.rs +++ b/mullvad-daemon/src/custom_list.rs @@ -1,9 +1,8 @@ use crate::{new_selector_config, Daemon, Error, EventListener}; use mullvad_types::{ + constraints::Constraint, custom_list::{CustomList, Id}, - relay_constraints::{ - BridgeState, Constraint, LocationConstraint, RelaySettings, ResolvedBridgeSettings, - }, + relay_constraints::{BridgeState, LocationConstraint, RelaySettings, ResolvedBridgeSettings}, }; use talpid_types::net::TunnelType; @@ -133,7 +132,7 @@ where { match endpoint.tunnel_type { TunnelType::Wireguard => { - if relay_settings.wireguard_constraints.use_multihop { + if relay_settings.wireguard_constraints.multihop() { if let Constraint::Only(LocationConstraint::CustomList { list_id }) = &relay_settings.wireguard_constraints.entry_location { diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 6ffd696890..c3c64ebacf 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -56,7 +56,7 @@ use mullvad_types::{ version::{AppVersion, AppVersionInfo}, wireguard::{PublicKey, QuantumResistantState, RotationInterval}, }; -use relay_list::updater::{self, RelayListUpdater, RelayListUpdaterHandle}; +use relay_list::{RelayListUpdater, RelayListUpdaterHandle, RELAYS_FILENAME}; use settings::SettingsPersister; #[cfg(target_os = "android")] use std::os::unix::io::RawFd; @@ -698,8 +698,8 @@ where let initial_selector_config = new_selector_config(&settings); let relay_selector = RelaySelector::new( initial_selector_config, - resource_dir.join(updater::RELAYS_FILENAME), - cache_dir.join(updater::RELAYS_FILENAME), + resource_dir.join(RELAYS_FILENAME), + cache_dir.join(RELAYS_FILENAME), ); let settings_relay_selector = relay_selector.clone(); @@ -1105,7 +1105,7 @@ where // Note that `Constraint::Any` corresponds to just IPv4 matches!( relay_constraints.wireguard_constraints.ip_version, - mullvad_types::relay_constraints::Constraint::Only(IpVersion::V6) + mullvad_types::constraints::Constraint::Only(IpVersion::V6) ) } else { false diff --git a/mullvad-daemon/src/migrations/v1.rs b/mullvad-daemon/src/migrations/v1.rs index c3013c7b28..4205f23ebf 100644 --- a/mullvad-daemon/src/migrations/v1.rs +++ b/mullvad-daemon/src/migrations/v1.rs @@ -1,5 +1,5 @@ use super::Result; -use mullvad_types::{relay_constraints::Constraint, settings::SettingsVersion}; +use mullvad_types::{constraints::Constraint, settings::SettingsVersion}; use serde::{Deserialize, Serialize}; // ====================================================== diff --git a/mullvad-daemon/src/migrations/v4.rs b/mullvad-daemon/src/migrations/v4.rs index cd11056d4a..8e03daa51b 100644 --- a/mullvad-daemon/src/migrations/v4.rs +++ b/mullvad-daemon/src/migrations/v4.rs @@ -1,5 +1,5 @@ use super::{Error, Result}; -use mullvad_types::{relay_constraints::Constraint, settings::SettingsVersion}; +use mullvad_types::{constraints::Constraint, settings::SettingsVersion}; use serde::{Deserialize, Serialize}; // ====================================================== diff --git a/mullvad-daemon/src/migrations/v5.rs b/mullvad-daemon/src/migrations/v5.rs index 351b427a85..e458bb1a1d 100644 --- a/mullvad-daemon/src/migrations/v5.rs +++ b/mullvad-daemon/src/migrations/v5.rs @@ -1,5 +1,5 @@ use super::{Error, Result}; -use mullvad_types::{relay_constraints::Constraint, settings::SettingsVersion}; +use mullvad_types::{constraints::Constraint, settings::SettingsVersion}; use serde::{Deserialize, Serialize}; // ====================================================== diff --git a/mullvad-daemon/src/migrations/v6.rs b/mullvad-daemon/src/migrations/v6.rs index 076dc1b71f..c481be8d92 100644 --- a/mullvad-daemon/src/migrations/v6.rs +++ b/mullvad-daemon/src/migrations/v6.rs @@ -1,5 +1,5 @@ use super::{Error, Result}; -use mullvad_types::{relay_constraints::Constraint, settings::SettingsVersion}; +use mullvad_types::{constraints::Constraint, settings::SettingsVersion}; use serde::{Deserialize, Serialize}; // ====================================================== diff --git a/mullvad-daemon/src/relay_list/mod.rs b/mullvad-daemon/src/relay_list/mod.rs index 8936941035..2b4be3db54 100644 --- a/mullvad-daemon/src/relay_list/mod.rs +++ b/mullvad-daemon/src/relay_list/mod.rs @@ -1,3 +1,213 @@ -//! Relay list +//! Relay list updater -pub mod updater; +use futures::{ + channel::mpsc, + future::{Fuse, FusedFuture}, + Future, FutureExt, SinkExt, StreamExt, +}; +use std::{ + path::{Path, PathBuf}, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; +use tokio::fs::File; + +use mullvad_api::{availability::ApiAvailabilityHandle, rest::MullvadRestHandle, RelayListProxy}; +use mullvad_relay_selector::RelaySelector; +use mullvad_types::relay_list::RelayList; +use talpid_future::retry::{retry_future, ExponentialBackoff, Jittered}; +use talpid_types::ErrorExt; + +/// How often the updater should wake up to check the cache of the in-memory cache of relays. +/// This check is very cheap. The only reason to not have it very often is because if downloading +/// constantly fails it will try very often and fill the logs etc. +const UPDATE_CHECK_INTERVAL: Duration = Duration::from_secs(60 * 15); +/// How old the cached relays need to be to trigger an update +const UPDATE_INTERVAL: Duration = Duration::from_secs(60 * 60); + +const DOWNLOAD_RETRY_STRATEGY: Jittered<ExponentialBackoff> = Jittered::jitter( + ExponentialBackoff::new(Duration::from_secs(16), 8) + .max_delay(Some(Duration::from_secs(2 * 60 * 60))), +); + +/// Where the relay list is cached on disk. +pub(crate) const RELAYS_FILENAME: &str = "relays.json"; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Downloader already shut down")] + DownloaderShutdown, + + #[error("Mullvad relay selector error")] + RelaySelector(#[from] mullvad_relay_selector::Error), +} + +#[derive(Clone)] +pub struct RelayListUpdaterHandle { + tx: mpsc::Sender<()>, +} + +impl RelayListUpdaterHandle { + pub async fn update(&mut self) { + if let Err(error) = self + .tx + .send(()) + .await + .map_err(|_| Error::DownloaderShutdown) + { + log::error!( + "{}", + error.display_chain_with_msg("Unable to send update command to relay list updater") + ); + } + } +} + +pub struct RelayListUpdater { + api_client: RelayListProxy, + cache_path: PathBuf, + relay_selector: RelaySelector, + on_update: Box<dyn Fn(&RelayList) + Send + 'static>, + last_check: SystemTime, + api_availability: ApiAvailabilityHandle, +} + +impl RelayListUpdater { + pub fn spawn( + selector: RelaySelector, + api_handle: MullvadRestHandle, + cache_dir: &Path, + on_update: impl Fn(&RelayList) + Send + 'static, + ) -> RelayListUpdaterHandle { + let (tx, cmd_rx) = mpsc::channel(1); + let api_availability = api_handle.availability.clone(); + let api_client = RelayListProxy::new(api_handle); + let updater = RelayListUpdater { + api_client, + cache_path: cache_dir.join(RELAYS_FILENAME), + relay_selector: selector, + on_update: Box::new(on_update), + last_check: UNIX_EPOCH, + api_availability, + }; + + tokio::spawn(updater.run(cmd_rx)); + + RelayListUpdaterHandle { tx } + } + + async fn run(mut self, mut cmd_rx: mpsc::Receiver<()>) { + let mut download_future = Box::pin(Fuse::terminated()); + loop { + let next_check = tokio::time::sleep(UPDATE_CHECK_INTERVAL).fuse(); + tokio::pin!(next_check); + + futures::select! { + _check_update = next_check => { + if download_future.is_terminated() && self.should_update() { + let tag = self.relay_selector.etag(); + download_future = Box::pin(Self::download_relay_list(self.api_availability.clone(), self.api_client.clone(), tag).fuse()); + self.last_check = SystemTime::now(); + } + }, + + new_relay_list = download_future => { + self.consume_new_relay_list(new_relay_list).await; + }, + + cmd = cmd_rx.next() => { + match cmd { + Some(()) => { + let tag = self.relay_selector.etag(); + download_future = Box::pin(Self::download_relay_list(self.api_availability.clone(), self.api_client.clone(), tag).fuse()); + self.last_check = SystemTime::now(); + }, + None => { + log::trace!("Relay list updater shutting down"); + return; + } + } + } + + }; + } + } + + async fn consume_new_relay_list( + &mut self, + result: Result<Option<RelayList>, mullvad_api::Error>, + ) { + match result { + Ok(Some(relay_list)) => { + if let Err(err) = self.update_cache(relay_list).await { + log::error!("Failed to update relay list cache: {}", err); + } + } + Ok(None) => log::debug!("Relay list is up-to-date"), + Err(error) => log::error!( + "{}", + error.display_chain_with_msg("Failed to fetch new relay list") + ), + } + } + + /// Returns true if the current parsed_relays is older than UPDATE_INTERVAL + fn should_update(&mut self) -> bool { + let last_check = std::cmp::max(self.relay_selector.last_updated(), self.last_check); + match SystemTime::now().duration_since(last_check) { + Ok(duration) => duration >= UPDATE_INTERVAL, + // If the clock is skewed we have no idea by how much or when the last update + // actually was, better download again to get in sync and get a `last_updated` + // timestamp corresponding to the new time. + Err(_) => true, + } + } + + fn download_relay_list( + api_handle: ApiAvailabilityHandle, + proxy: RelayListProxy, + tag: Option<String>, + ) -> impl Future<Output = Result<Option<RelayList>, mullvad_api::Error>> + 'static { + let download_futures = move || { + let available = api_handle.wait_background(); + let req = proxy.relay_list(tag.clone()); + async move { + available.await?; + req.await.map_err(mullvad_api::Error::from) + } + }; + + retry_future( + download_futures, + |result| result.is_err(), + DOWNLOAD_RETRY_STRATEGY, + ) + } + + async fn update_cache(&mut self, new_relay_list: RelayList) -> Result<(), Error> { + if let Err(error) = Self::cache_relays(&self.cache_path, &new_relay_list).await { + log::error!( + "{}", + error.display_chain_with_msg("Failed to update relay cache on disk") + ); + } + + self.relay_selector.set_relays(new_relay_list.clone()); + (self.on_update)(&new_relay_list); + Ok(()) + } + + /// Write a `RelayList` to the cache file. + async fn cache_relays(cache_path: &Path, relays: &RelayList) -> Result<(), Error> { + log::debug!("Writing relays cache to {}", cache_path.display()); + let mut file = File::create(cache_path) + .await + .map_err(mullvad_relay_selector::Error::OpenRelayCache)?; + let bytes = + serde_json::to_vec_pretty(relays).map_err(mullvad_relay_selector::Error::Serialize)?; + let mut slice: &[u8] = bytes.as_slice(); + let _ = tokio::io::copy(&mut slice, &mut file) + .await + .map_err(mullvad_relay_selector::Error::WriteRelayCache)?; + Ok(()) + } +} diff --git a/mullvad-daemon/src/relay_list/updater.rs b/mullvad-daemon/src/relay_list/updater.rs deleted file mode 100644 index 317dbd45cd..0000000000 --- a/mullvad-daemon/src/relay_list/updater.rs +++ /dev/null @@ -1,201 +0,0 @@ -use futures::{ - channel::mpsc, - future::{Fuse, FusedFuture}, - Future, FutureExt, SinkExt, StreamExt, -}; -use std::{ - path::{Path, PathBuf}, - time::{Duration, SystemTime, UNIX_EPOCH}, -}; -use tokio::fs::File; - -use mullvad_api::{availability::ApiAvailabilityHandle, rest::MullvadRestHandle, RelayListProxy}; -use mullvad_relay_selector::{Error, RelaySelector}; -use mullvad_types::relay_list::RelayList; -use talpid_future::retry::{retry_future, ExponentialBackoff, Jittered}; -use talpid_types::ErrorExt; - -/// How often the updater should wake up to check the cache of the in-memory cache of relays. -/// This check is very cheap. The only reason to not have it very often is because if downloading -/// constantly fails it will try very often and fill the logs etc. -const UPDATE_CHECK_INTERVAL: Duration = Duration::from_secs(60 * 15); -/// How old the cached relays need to be to trigger an update -const UPDATE_INTERVAL: Duration = Duration::from_secs(60 * 60); - -const DOWNLOAD_RETRY_STRATEGY: Jittered<ExponentialBackoff> = Jittered::jitter( - ExponentialBackoff::new(Duration::from_secs(16), 8) - .max_delay(Some(Duration::from_secs(2 * 60 * 60))), -); - -/// Where the relay list is cached on disk. -pub(crate) const RELAYS_FILENAME: &str = "relays.json"; - -#[derive(Clone)] -pub struct RelayListUpdaterHandle { - tx: mpsc::Sender<()>, -} - -impl RelayListUpdaterHandle { - pub async fn update(&mut self) { - if let Err(error) = self - .tx - .send(()) - .await - .map_err(|_| Error::DownloaderShutDown) - { - log::error!( - "{}", - error.display_chain_with_msg("Unable to send update command to relay list updater") - ); - } - } -} - -pub struct RelayListUpdater { - api_client: RelayListProxy, - cache_path: PathBuf, - relay_selector: RelaySelector, - on_update: Box<dyn Fn(&RelayList) + Send + 'static>, - last_check: SystemTime, - api_availability: ApiAvailabilityHandle, -} - -impl RelayListUpdater { - pub fn spawn( - selector: RelaySelector, - api_handle: MullvadRestHandle, - cache_dir: &Path, - on_update: impl Fn(&RelayList) + Send + 'static, - ) -> RelayListUpdaterHandle { - let (tx, cmd_rx) = mpsc::channel(1); - let api_availability = api_handle.availability.clone(); - let api_client = RelayListProxy::new(api_handle); - let updater = RelayListUpdater { - api_client, - cache_path: cache_dir.join(RELAYS_FILENAME), - relay_selector: selector, - on_update: Box::new(on_update), - last_check: UNIX_EPOCH, - api_availability, - }; - - tokio::spawn(updater.run(cmd_rx)); - - RelayListUpdaterHandle { tx } - } - - async fn run(mut self, mut cmd_rx: mpsc::Receiver<()>) { - let mut download_future = Box::pin(Fuse::terminated()); - loop { - let next_check = tokio::time::sleep(UPDATE_CHECK_INTERVAL).fuse(); - tokio::pin!(next_check); - - futures::select! { - _check_update = next_check => { - if download_future.is_terminated() && self.should_update() { - let tag = self.relay_selector.etag(); - download_future = Box::pin(Self::download_relay_list(self.api_availability.clone(), self.api_client.clone(), tag).fuse()); - self.last_check = SystemTime::now(); - } - }, - - new_relay_list = download_future => { - self.consume_new_relay_list(new_relay_list).await; - }, - - cmd = cmd_rx.next() => { - match cmd { - Some(()) => { - let tag = self.relay_selector.etag(); - download_future = Box::pin(Self::download_relay_list(self.api_availability.clone(), self.api_client.clone(), tag).fuse()); - self.last_check = SystemTime::now(); - }, - None => { - log::trace!("Relay list updater shutting down"); - return; - } - } - } - - }; - } - } - - async fn consume_new_relay_list( - &mut self, - result: Result<Option<RelayList>, mullvad_api::Error>, - ) { - match result { - Ok(Some(relay_list)) => { - if let Err(err) = self.update_cache(relay_list).await { - log::error!("Failed to update relay list cache: {}", err); - } - } - Ok(None) => log::debug!("Relay list is up-to-date"), - Err(error) => log::error!( - "{}", - error.display_chain_with_msg("Failed to fetch new relay list") - ), - } - } - - /// Returns true if the current parsed_relays is older than UPDATE_INTERVAL - fn should_update(&mut self) -> bool { - let last_check = std::cmp::max(self.relay_selector.last_updated(), self.last_check); - match SystemTime::now().duration_since(last_check) { - Ok(duration) => duration >= UPDATE_INTERVAL, - // If the clock is skewed we have no idea by how much or when the last update - // actually was, better download again to get in sync and get a `last_updated` - // timestamp corresponding to the new time. - Err(_) => true, - } - } - - fn download_relay_list( - api_handle: ApiAvailabilityHandle, - proxy: RelayListProxy, - tag: Option<String>, - ) -> impl Future<Output = Result<Option<RelayList>, mullvad_api::Error>> + 'static { - let download_futures = move || { - let available = api_handle.wait_background(); - let req = proxy.relay_list(tag.clone()); - async move { - available.await?; - req.await.map_err(mullvad_api::Error::from) - } - }; - - retry_future( - download_futures, - |result| result.is_err(), - DOWNLOAD_RETRY_STRATEGY, - ) - } - - async fn update_cache(&mut self, new_relay_list: RelayList) -> Result<(), Error> { - if let Err(error) = Self::cache_relays(&self.cache_path, &new_relay_list).await { - log::error!( - "{}", - error.display_chain_with_msg("Failed to update relay cache on disk") - ); - } - - self.relay_selector.set_relays(new_relay_list.clone()); - (self.on_update)(&new_relay_list); - Ok(()) - } - - /// Write a `RelayList` to the cache file. - async fn cache_relays(cache_path: &Path, relays: &RelayList) -> Result<(), Error> { - log::debug!("Writing relays cache to {}", cache_path.display()); - let mut file = File::create(cache_path) - .await - .map_err(Error::OpenRelayCache)?; - let bytes = serde_json::to_vec_pretty(relays).map_err(Error::Serialize)?; - let mut slice: &[u8] = bytes.as_slice(); - let _ = tokio::io::copy(&mut slice, &mut file) - .await - .map_err(Error::WriteRelayCache)?; - Ok(()) - } -} diff --git a/mullvad-daemon/src/settings/mod.rs b/mullvad-daemon/src/settings/mod.rs index abd6ea2e0a..b1d9c2b8e6 100644 --- a/mullvad-daemon/src/settings/mod.rs +++ b/mullvad-daemon/src/settings/mod.rs @@ -376,16 +376,13 @@ impl<'a> Display for SettingsSummary<'a> { write!(f, ", wg ip version: {ip_version}")?; } - let multihop = matches!( - relay_settings, + let multihop = match relay_settings { RelaySettings::Normal(RelayConstraints { - wireguard_constraints: WireguardConstraints { - use_multihop: true, - .. - }, + wireguard_constraints, .. - }) - ); + }) => wireguard_constraints.multihop(), + _ => false, + }; write!( f, diff --git a/mullvad-daemon/src/tunnel.rs b/mullvad-daemon/src/tunnel.rs index 2094838173..f230a3c4b2 100644 --- a/mullvad-daemon/src/tunnel.rs +++ b/mullvad-daemon/src/tunnel.rs @@ -8,20 +8,22 @@ use std::{ use tokio::sync::Mutex; -use mullvad_relay_selector::{RelaySelector, SelectedBridge, SelectedObfuscator, SelectedRelay}; +use mullvad_relay_selector::{GetRelay, RelaySelector, RuntimeParameters, WireguardConfig}; use mullvad_types::{ - endpoint::MullvadEndpoint, location::GeoIpLocation, relay_list::Relay, settings::TunnelOptions, + endpoint::MullvadWireguardEndpoint, location::GeoIpLocation, relay_list::Relay, + settings::TunnelOptions, }; use once_cell::sync::Lazy; use talpid_core::tunnel_state_machine::TunnelParametersGenerator; -use talpid_types::{ - net::{wireguard, TunnelParameters}, - tunnel::ParameterGenerationError, - ErrorExt, +#[cfg(not(target_os = "android"))] +use talpid_types::net::{ + obfuscation::ObfuscatorConfig, openvpn, proxy::CustomProxy, wireguard, Endpoint, + TunnelParameters, }; +#[cfg(target_os = "android")] +use talpid_types::net::{obfuscation::ObfuscatorConfig, wireguard, TunnelParameters}; -#[cfg(not(target_os = "android"))] -use talpid_types::net::openvpn; +use talpid_types::{tunnel::ParameterGenerationError, ErrorExt}; use crate::device::{AccountManagerHandle, PrivateAccountAndDevice}; @@ -138,127 +140,129 @@ impl ParametersGenerator { } impl InnerParametersGenerator { - async fn generate(&mut self, retry_attempt: u32) -> Result<TunnelParameters, Error> { - let _data = self.device().await?; - match self.relay_selector.get_relay(retry_attempt) { - Ok((SelectedRelay::Custom(custom_relay), _bridge, _obfsucator)) => { - self.last_generated_relays = None; - custom_relay - // TODO: generate proxy settings for custom tunnels - .to_tunnel_parameters(self.tunnel_options.clone(), None) - .map_err(|e| { - log::error!("Failed to resolve hostname for custom tunnel config: {}", e); - Error::ResolveCustomHostname - }) - } - Ok((SelectedRelay::Normal(constraints), bridge, obfuscator)) => { - self.create_tunnel_parameters( - &constraints.exit_relay, - &constraints.entry_relay, - constraints.endpoint, - bridge, - obfuscator, - ) - .await - } - Err(mullvad_relay_selector::Error::NoBridge) => Err(Error::NoBridgeAvailable), - Err(_error) => Err(Error::NoRelayAvailable), - } - } - - #[cfg_attr(target_os = "android", allow(unused_variables))] - async fn create_tunnel_parameters( + async fn generate( &mut self, - relay: &Relay, - entry_relay: &Option<Relay>, - endpoint: MullvadEndpoint, - bridge: Option<SelectedBridge>, - obfuscator: Option<SelectedObfuscator>, + retry_attempt: u32, + ipv6: bool, ) -> Result<TunnelParameters, Error> { let data = self.device().await?; - match endpoint { - #[cfg(not(target_os = "android"))] - MullvadEndpoint::OpenVpn(endpoint) => { - let (bridge_settings, bridge_relay) = match bridge { - Some(SelectedBridge::Normal(bridge)) => { - (Some(bridge.settings), Some(bridge.relay)) - } - Some(SelectedBridge::Custom(settings)) => (Some(settings), None), - None => (None, None), - }; + let selected_relay = self + .relay_selector + .get_relay(retry_attempt as usize, RuntimeParameters { ipv6 }) + .map_err(|err| match err { + mullvad_relay_selector::Error::NoBridge => Error::NoBridgeAvailable, + _ => Error::NoRelayAvailable, + })?; + match selected_relay { + #[cfg(not(target_os = "android"))] + GetRelay::OpenVpn { + endpoint, + exit, + bridge, + } => { + let bridge_relay = bridge.as_ref().and_then(|bridge| bridge.relay()); self.last_generated_relays = Some(LastSelectedRelays::OpenVpn { - relay: relay.clone(), - bridge: bridge_relay, + relay: exit.clone(), + bridge: bridge_relay.cloned(), }); - - Ok(openvpn::TunnelParameters { - config: openvpn::ConnectionConfig::new( - endpoint, - data.account_token, - "-".to_string(), - ), - options: self.tunnel_options.openvpn.clone(), - generic_options: self.tunnel_options.generic.clone(), - proxy: bridge_settings, - #[cfg(target_os = "linux")] - fwmark: mullvad_types::TUNNEL_FWMARK, - } - .into()) + let bridge_settings = bridge.as_ref().map(|bridge| bridge.settings()); + Ok(self.create_openvpn_tunnel_parameters(endpoint, data, bridge_settings.cloned())) } - #[cfg(target_os = "android")] - MullvadEndpoint::OpenVpn(endpoint) => { - unreachable!("OpenVPN is not supported on Android"); - } - MullvadEndpoint::Wireguard(endpoint) => { - let tunnel_ipv4 = data.device.wg_data.addresses.ipv4_address.ip(); - let tunnel_ipv6 = data.device.wg_data.addresses.ipv6_address.ip(); - let tunnel = wireguard::TunnelConfig { - private_key: data.device.wg_data.private_key, - addresses: vec![IpAddr::from(tunnel_ipv4), IpAddr::from(tunnel_ipv6)], - }; - // FIXME: Used for debugging purposes during the migration to same IP. Remove when - // the migration is over. - if tunnel_ipv4 == *SAME_IP_V4 || tunnel_ipv6 == *SAME_IP_V6 { - log::debug!("Same IP is being used"); - } else { - log::debug!("Same IP is NOT being used"); - } - + GetRelay::Wireguard { + endpoint, + obfuscator, + inner, + } => { let (obfuscator_relay, obfuscator_config) = match obfuscator { Some(obfuscator) => (Some(obfuscator.relay), Some(obfuscator.config)), None => (None, None), }; + let (wg_entry, wg_exit) = match inner { + WireguardConfig::Singlehop { exit } => (None, exit), + WireguardConfig::Multihop { exit, entry } => (Some(entry), exit), + }; self.last_generated_relays = Some(LastSelectedRelays::WireGuard { - wg_entry: entry_relay.clone(), - wg_exit: relay.clone(), + wg_entry, + wg_exit, obfuscator: obfuscator_relay, }); - Ok(wireguard::TunnelParameters { - connection: wireguard::ConnectionConfig { - tunnel, - peer: endpoint.peer, - exit_peer: endpoint.exit_peer, - ipv4_gateway: endpoint.ipv4_gateway, - ipv6_gateway: Some(endpoint.ipv6_gateway), - #[cfg(target_os = "linux")] - fwmark: Some(mullvad_types::TUNNEL_FWMARK), - }, - options: self - .tunnel_options - .wireguard - .clone() - .into_talpid_tunnel_options(), - generic_options: self.tunnel_options.generic.clone(), - obfuscation: obfuscator_config, - } - .into()) + Ok(self.create_wireguard_tunnel_parameters(endpoint, data, obfuscator_config)) + } + GetRelay::Custom(custom_relay) => { + self.last_generated_relays = None; + custom_relay + // TODO: generate proxy settings for custom tunnels + .to_tunnel_parameters(self.tunnel_options.clone(), None) + .map_err(|e| { + log::error!("Failed to resolve hostname for custom tunnel config: {}", e); + Error::ResolveCustomHostname + }) } } } + #[cfg(not(target_os = "android"))] + fn create_openvpn_tunnel_parameters( + &self, + endpoint: Endpoint, + data: PrivateAccountAndDevice, + bridge_settings: Option<CustomProxy>, + ) -> TunnelParameters { + openvpn::TunnelParameters { + config: openvpn::ConnectionConfig::new(endpoint, data.account_token, "-".to_string()), + options: self.tunnel_options.openvpn.clone(), + generic_options: self.tunnel_options.generic.clone(), + proxy: bridge_settings, + #[cfg(target_os = "linux")] + fwmark: mullvad_types::TUNNEL_FWMARK, + } + .into() + } + + fn create_wireguard_tunnel_parameters( + &self, + endpoint: MullvadWireguardEndpoint, + data: PrivateAccountAndDevice, + obfuscator_config: Option<ObfuscatorConfig>, + ) -> TunnelParameters { + let tunnel_ipv4 = data.device.wg_data.addresses.ipv4_address.ip(); + let tunnel_ipv6 = data.device.wg_data.addresses.ipv6_address.ip(); + let tunnel = wireguard::TunnelConfig { + private_key: data.device.wg_data.private_key, + addresses: vec![IpAddr::from(tunnel_ipv4), IpAddr::from(tunnel_ipv6)], + }; + // FIXME: Used for debugging purposes during the migration to same IP. Remove when + // the migration is over. + if tunnel_ipv4 == *SAME_IP_V4 || tunnel_ipv6 == *SAME_IP_V6 { + log::debug!("Same IP is being used"); + } else { + log::debug!("Same IP is NOT being used"); + } + + wireguard::TunnelParameters { + connection: wireguard::ConnectionConfig { + tunnel, + peer: endpoint.peer, + exit_peer: endpoint.exit_peer, + ipv4_gateway: endpoint.ipv4_gateway, + ipv6_gateway: Some(endpoint.ipv6_gateway), + #[cfg(target_os = "linux")] + fwmark: Some(mullvad_types::TUNNEL_FWMARK), + }, + options: self + .tunnel_options + .wireguard + .clone() + .into_talpid_tunnel_options(), + generic_options: self.tunnel_options.generic.clone(), + obfuscation: obfuscator_config, + } + .into() + } + async fn device(&self) -> Result<PrivateAccountAndDevice, Error> { self.account_manager .data() @@ -274,12 +278,13 @@ impl TunnelParametersGenerator for ParametersGenerator { fn generate( &mut self, retry_attempt: u32, + ipv6: bool, ) -> Pin<Box<dyn Future<Output = Result<TunnelParameters, ParameterGenerationError>>>> { let generator = self.0.clone(); Box::pin(async move { let mut inner = generator.lock().await; inner - .generate(retry_attempt) + .generate(retry_attempt, ipv6) .await .map_err(|error| match error { Error::NoBridgeAvailable => ParameterGenerationError::NoMatchingBridgeRelay, |
