From 3ce7662adaa9255b4e44105471d63161bf050424 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Mon, 14 Oct 2024 16:18:17 +0200 Subject: [PATCH 01/14] Fix doc string referring to non-existant datatype --- mullvad-relay-selector/src/relay_selector/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index b94a5812a011..b495ac206847 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -717,7 +717,7 @@ impl RelaySelector { /// /// # Returns /// * An `Err` if no exit relay can be chosen - /// * `Ok(WireguardInner::Singlehop)` otherwise + /// * `Ok(WireguardConfig)` otherwise fn get_wireguard_singlehop_config( query: &RelayQuery, custom_lists: &CustomListsSettings, @@ -768,7 +768,7 @@ impl RelaySelector { /// /// # Returns /// * An `Err` if no entry/exit relay can be chosen - /// * `Ok(WireguardInner::Multihop)` otherwise + /// * `Ok(WireguardConfig::Multihop)` otherwise fn get_wireguard_auto_multihop_config( query: &RelayQuery, custom_lists: &CustomListsSettings, @@ -819,7 +819,7 @@ impl RelaySelector { /// * An `Err` if no exit relay can be chosen /// * An `Err` if no entry relay can be chosen /// * An `Err` if the chosen entry and exit relays are the same - /// * `Ok(WireguardInner::Multihop)` otherwise + /// * `Ok(WireguardConfig::Multihop)` otherwise fn get_wireguard_multihop_config( query: &RelayQuery, custom_lists: &CustomListsSettings, From 3eb234444144bc139e766c8eb5a7ff6fe81019ee Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Mon, 14 Oct 2024 16:16:51 +0200 Subject: [PATCH 02/14] Move smart routing logic to a contained function --- .../src/relay_selector/mod.rs | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index b495ac206847..1adb49c2b9a2 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -725,6 +725,28 @@ impl RelaySelector { ) -> Result { let candidates = filter_matching_relay_list(query, parsed_relays, custom_lists); + if let Some(x) = Self::select_daita_multihop_config_if_necessary( + query, + custom_lists, + parsed_relays, + &candidates, + )? { + return Ok(x); + } + + helpers::pick_random_relay(&candidates) + .cloned() + .map(WireguardConfig::singlehop) + .ok_or(Error::NoRelay) + } + + fn select_daita_multihop_config_if_necessary( + query: &RelayQuery, + custom_lists: &CustomListsSettings, + parsed_relays: &ParsedRelays, + // TODO: What is this?? + candidates: &[Relay], + ) -> Result, Error> { // are we using daita? let using_daita = || query.wireguard_constraints().daita == Constraint::Only(true); @@ -755,13 +777,10 @@ impl RelaySelector { && no_relay_because_daita()? && use_multihop_if_necessary() { - return Self::get_wireguard_auto_multihop_config(query, custom_lists, parsed_relays); + Self::get_wireguard_auto_multihop_config(query, custom_lists, parsed_relays).map(Some) + } else { + Ok(None) } - - helpers::pick_random_relay(&candidates) - .cloned() - .map(WireguardConfig::singlehop) - .ok_or(Error::NoRelay) } /// Select a valid Wireguard exit relay, together with with an automatically chosen entry relay. From 34d7f3fb102144940503e1db95bbc9a24b5e1154 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Wed, 16 Oct 2024 17:17:16 +0200 Subject: [PATCH 03/14] Tidy up imports --- .../src/relay_selector/mod.rs | 63 +++++++++---------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index 1adb49c2b9a2..7f7517923c37 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -6,45 +6,39 @@ mod matcher; mod parsed_relays; pub mod query; -use chrono::{DateTime, Local}; -use itertools::Itertools; -use query::ObfuscationQuery; -use std::{ - path::Path, - sync::{Arc, LazyLock, Mutex}, - time::SystemTime, -}; +use matcher::{filter_matching_bridges, filter_matching_relay_list}; +use parsed_relays::ParsedRelays; -use mullvad_types::{ - constraints::Constraint, - custom_list::CustomListsSettings, - endpoint::MullvadWireguardEndpoint, - location::{Coordinates, Location}, - relay_constraints::{ - BridgeSettings, BridgeState, InternalBridgeConstraints, ObfuscationSettings, - OpenVpnConstraints, RelayConstraints, RelayOverride, RelaySettings, ResolvedBridgeSettings, - WireguardConstraints, - }, - relay_list::{Relay, RelayEndpointData, RelayList}, - settings::Settings, - wireguard::QuantumResistantState, - CustomTunnelEndpoint, Intersection, -}; -use talpid_types::{ - net::{ - obfuscation::ObfuscatorConfig, proxy::CustomProxy, Endpoint, TransportProtocol, TunnelType, - }, - ErrorExt, +use crate::detailer::{openvpn_endpoint, wireguard_endpoint}; +use crate::error::{EndpointErrorDetails, Error}; +use crate::query::{ + BridgeQuery, ObfuscationQuery, OpenVpnRelayQuery, RelayQuery, WireguardRelayQuery, }; -use crate::error::{EndpointErrorDetails, Error}; +use std::path::Path; +use std::sync::{Arc, LazyLock, Mutex}; +use std::time::SystemTime; + +use chrono::{DateTime, Local}; +use itertools::Itertools; -use self::{ - detailer::{openvpn_endpoint, wireguard_endpoint}, - matcher::{filter_matching_bridges, filter_matching_relay_list}, - parsed_relays::ParsedRelays, - query::{BridgeQuery, OpenVpnRelayQuery, RelayQuery, WireguardRelayQuery}, +use mullvad_types::constraints::Constraint; +use mullvad_types::custom_list::CustomListsSettings; +use mullvad_types::endpoint::MullvadWireguardEndpoint; +use mullvad_types::location::{Coordinates, Location}; +use mullvad_types::relay_constraints::{ + BridgeSettings, BridgeState, InternalBridgeConstraints, ObfuscationSettings, + OpenVpnConstraints, RelayConstraints, RelayOverride, RelaySettings, ResolvedBridgeSettings, + WireguardConstraints, +}; +use mullvad_types::relay_list::{Relay, RelayEndpointData, RelayList}; +use mullvad_types::settings::Settings; +use mullvad_types::wireguard::QuantumResistantState; +use mullvad_types::{CustomTunnelEndpoint, Intersection}; +use talpid_types::net::{ + obfuscation::ObfuscatorConfig, proxy::CustomProxy, Endpoint, TransportProtocol, TunnelType, }; +use talpid_types::ErrorExt; /// [`RETRY_ORDER`] defines an ordered set of relay parameters which the relay selector should /// prioritize on successive connection attempts. Note that these will *never* override user @@ -221,6 +215,7 @@ pub enum GetRelay { Custom(CustomTunnelEndpoint), } +// TODO: Import `Either` and convert `Multihop` and `Singlehop` into concrete types. /// This struct defines the different Wireguard relays the the relay selector can end up selecting /// for an arbitrary Wireguard [`query`]. /// From 4f59f3c66fc397220fdd4fd6c7abca1fda71b9e5 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Wed, 16 Oct 2024 17:39:48 +0200 Subject: [PATCH 04/14] Introduce `Singlehop` and `Multihop` as distinct types --- mullvad-relay-selector/src/error.rs | 3 +- mullvad-relay-selector/src/lib.rs | 8 +- .../src/relay_selector/mod.rs | 94 +++++++------------ .../src/relay_selector/relays.rs | 72 ++++++++++++++ 4 files changed, 111 insertions(+), 66 deletions(-) create mode 100644 mullvad-relay-selector/src/relay_selector/relays.rs diff --git a/mullvad-relay-selector/src/error.rs b/mullvad-relay-selector/src/error.rs index dea6f81c3ee9..0bd5fad5d455 100644 --- a/mullvad-relay-selector/src/error.rs +++ b/mullvad-relay-selector/src/error.rs @@ -3,7 +3,8 @@ use mullvad_types::{relay_constraints::MissingCustomBridgeSettings, relay_list::Relay}; -use crate::{detailer, WireguardConfig}; +use crate::detailer; +use crate::relay_selector::relays::WireguardConfig; #[derive(thiserror::Error, Debug)] pub enum Error { diff --git a/mullvad-relay-selector/src/lib.rs b/mullvad-relay-selector/src/lib.rs index 09708a059eeb..cbd7c94a3628 100644 --- a/mullvad-relay-selector/src/lib.rs +++ b/mullvad-relay-selector/src/lib.rs @@ -8,8 +8,10 @@ mod relay_selector; // Re-exports pub use error::Error; +pub use relay_selector::detailer; +pub use relay_selector::query; +pub use relay_selector::relays::WireguardConfig; pub use relay_selector::{ - detailer, query, AdditionalRelayConstraints, AdditionalWireguardConstraints, GetRelay, - RelaySelector, RuntimeParameters, SelectedBridge, SelectedObfuscator, SelectorConfig, - WireguardConfig, RETRY_ORDER, + AdditionalRelayConstraints, AdditionalWireguardConstraints, GetRelay, RelaySelector, + RuntimeParameters, SelectedBridge, SelectedObfuscator, SelectorConfig, RETRY_ORDER, }; diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index 7f7517923c37..3cdfa58f2683 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -5,9 +5,11 @@ mod helpers; mod matcher; mod parsed_relays; pub mod query; +pub mod relays; use matcher::{filter_matching_bridges, filter_matching_relay_list}; use parsed_relays::ParsedRelays; +use relays::{Multihop, Singlehop, WireguardConfig}; use crate::detailer::{openvpn_endpoint, wireguard_endpoint}; use crate::error::{EndpointErrorDetails, Error}; @@ -215,50 +217,6 @@ pub enum GetRelay { Custom(CustomTunnelEndpoint), } -// TODO: Import `Either` and convert `Multihop` and `Singlehop` into concrete types. -/// This struct defines the different Wireguard relays the the relay selector can end up selecting -/// for an arbitrary Wireguard [`query`]. -/// -/// - [`WireguardConfig::Singlehop`]; A normal wireguard relay where VPN traffic enters and exits -/// through this sole relay. -/// - [`WireguardConfig::Multihop`]; Two wireguard relays to be used in a multihop circuit. VPN -/// traffic will enter through `entry` and eventually come out from `exit` before the traffic will -/// actually be routed to the broader internet. -#[derive(Clone, Debug)] -pub enum WireguardConfig { - /// Strongly prefer to instantiate this variant using [`WireguardConfig::singlehop`] as that - /// will assert that the relay is of the expected type. - Singlehop { exit: Relay }, - /// Strongly prefer to instantiate this variant using [`WireguardConfig::multihop`] as that - /// will assert that the entry & exit relays are of the expected type. - Multihop { exit: Relay, entry: Relay }, -} - -impl WireguardConfig { - const fn singlehop(exit: Relay) -> Self { - // FIXME: This assert would be better to encode at the type level. - assert!(matches!( - exit.endpoint_data, - RelayEndpointData::Wireguard(_) - )); - Self::Singlehop { exit } - } - - const fn multihop(exit: Relay, entry: Relay) -> Self { - // FIXME: This assert would be better to encode at the type level. - assert!(matches!( - exit.endpoint_data, - RelayEndpointData::Wireguard(_) - )); - // FIXME: This assert would be better to encode at the type level. - assert!(matches!( - entry.endpoint_data, - RelayEndpointData::Wireguard(_) - )); - Self::Multihop { exit, entry } - } -} - #[derive(Clone, Debug)] pub enum SelectedBridge { Normal { settings: CustomProxy, relay: Relay }, @@ -693,9 +651,17 @@ impl RelaySelector { Constraint::Only(TunnelType::Wireguard) ); let inner = if !query.wireguard_constraints().multihop() { - Self::get_wireguard_singlehop_config(query, custom_lists, parsed_relays)? + WireguardConfig::from(Self::get_wireguard_singlehop_config( + query, + custom_lists, + parsed_relays, + )?) } else { - Self::get_wireguard_multihop_config(query, custom_lists, parsed_relays)? + WireguardConfig::from(Self::get_wireguard_multihop_config( + query, + custom_lists, + parsed_relays, + )?) }; let endpoint = Self::get_wireguard_endpoint(query, parsed_relays, &inner)?; let obfuscator = @@ -717,21 +683,23 @@ impl RelaySelector { query: &RelayQuery, custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, - ) -> Result { + ) -> Result { let candidates = filter_matching_relay_list(query, parsed_relays, custom_lists); - if let Some(x) = Self::select_daita_multihop_config_if_necessary( - query, - custom_lists, - parsed_relays, - &candidates, - )? { - return Ok(x); - } + // // TODO: Move somewhere + // // Or keep here, but return either. + // if let Some(x) = Self::select_daita_multihop_config_if_necessary( + // query, + // custom_lists, + // parsed_relays, + // &candidates, + // )? { + // return Ok(x); + // } helpers::pick_random_relay(&candidates) .cloned() - .map(WireguardConfig::singlehop) + .map(Singlehop::new) .ok_or(Error::NoRelay) } @@ -741,7 +709,7 @@ impl RelaySelector { parsed_relays: &ParsedRelays, // TODO: What is this?? candidates: &[Relay], - ) -> Result, Error> { + ) -> Result, Error> { // are we using daita? let using_daita = || query.wireguard_constraints().daita == Constraint::Only(true); @@ -767,6 +735,8 @@ impl RelaySelector { // if we found no matching relays because DAITA was enabled, and `use_multihop_if_necessary` // is enabled, try enabling multihop and connecting using an automatically selected // entry relay. + // + // TODO: I think this should be pushed up one level and not be in this function scope. if candidates.is_empty() && using_daita() && no_relay_because_daita()? @@ -782,12 +752,12 @@ impl RelaySelector { /// /// # Returns /// * An `Err` if no entry/exit relay can be chosen - /// * `Ok(WireguardConfig::Multihop)` otherwise + /// * `Ok(Multihop)` otherwise fn get_wireguard_auto_multihop_config( query: &RelayQuery, custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, - ) -> Result { + ) -> Result { let mut exit_relay_query = query.clone(); // DAITA should only be enabled for the entry relay @@ -824,7 +794,7 @@ impl RelaySelector { let entry = helpers::pick_random_relay_excluding(&entry_candidates, exit).ok_or(Error::NoRelay)?; - Ok(WireguardConfig::multihop(exit.clone(), entry.clone())) + Ok(Multihop::new(entry.clone(), exit.clone())) } /// This function selects a valid entry and exit relay to be used in a multihop configuration. @@ -838,7 +808,7 @@ impl RelaySelector { query: &RelayQuery, custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, - ) -> Result { + ) -> Result { // Here, we modify the original query just a bit. // The actual query for an exit relay is identical as for an exit relay, with the // exception that the location is different. It is simply the location as dictated by @@ -878,7 +848,7 @@ impl RelaySelector { } .ok_or(Error::NoRelay)?; - Ok(WireguardConfig::multihop(exit.clone(), entry.clone())) + Ok(Multihop::new(entry.clone(), exit.clone())) } /// Constructs a [`MullvadEndpoint`] with details for how to connect to `relay`. diff --git a/mullvad-relay-selector/src/relay_selector/relays.rs b/mullvad-relay-selector/src/relay_selector/relays.rs new file mode 100644 index 000000000000..e8a1035c1d51 --- /dev/null +++ b/mullvad-relay-selector/src/relay_selector/relays.rs @@ -0,0 +1,72 @@ +//! TODO: Document this module + +use mullvad_types::relay_list::{Relay, RelayEndpointData}; + +// TODO: Import `Either` and convert `Multihop` and `Singlehop` into concrete types. +/// This struct defines the different Wireguard relays the the relay selector can end up selecting +/// for an arbitrary Wireguard [`query`]. +/// +/// - [`WireguardConfig::Singlehop`]; A normal wireguard relay where VPN traffic enters and exits +/// through this sole relay. +/// - [`WireguardConfig::Multihop`]; Two wireguard relays to be used in a multihop circuit. VPN +/// traffic will enter through `entry` and eventually come out from `exit` before the traffic will +/// actually be routed to the broader internet. +#[derive(Clone, Debug)] +pub enum WireguardConfig { + /// Strongly prefer to instantiate this variant using [`WireguardConfig::singlehop`] as that + /// will assert that the relay is of the expected type. + Singlehop { exit: Relay }, + /// Strongly prefer to instantiate this variant using [`WireguardConfig::multihop`] as that + /// will assert that the entry & exit relays are of the expected type. + Multihop { exit: Relay, entry: Relay }, +} + +/// TODO: Document +pub struct Singlehop(Relay); +/// TODO: Document +pub struct Multihop { + entry: Relay, + exit: Relay, +} + +impl From for WireguardConfig { + fn from(relay: Singlehop) -> Self { + Self::Singlehop { exit: relay.0 } + } +} + +impl From for WireguardConfig { + fn from(relay: Multihop) -> Self { + WireguardConfig::Multihop { + exit: relay.exit, + entry: relay.entry, + } + } +} + +impl Singlehop { + pub const fn new(exit: Relay) -> Self { + // FIXME: This assert would be better to encode at the type level. + assert!(matches!( + exit.endpoint_data, + RelayEndpointData::Wireguard(_) + )); + Self(exit) + } +} + +impl Multihop { + pub const fn new(entry: Relay, exit: Relay) -> Self { + // FIXME: This assert would be better to encode at the type level. + assert!(matches!( + exit.endpoint_data, + RelayEndpointData::Wireguard(_) + )); + // FIXME: This assert would be better to encode at the type level. + assert!(matches!( + entry.endpoint_data, + RelayEndpointData::Wireguard(_) + )); + Multihop { exit, entry } + } +} From 137f9d23d9213b85c0a5cac596f393a6aa652949 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Wed, 16 Oct 2024 18:07:01 +0200 Subject: [PATCH 05/14] Refactor smart routing logic a bit --- .../src/relay_selector/mod.rs | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index 3cdfa58f2683..4e78a73c8a3c 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -650,19 +650,7 @@ impl RelaySelector { query.tunnel_protocol(), Constraint::Only(TunnelType::Wireguard) ); - let inner = if !query.wireguard_constraints().multihop() { - WireguardConfig::from(Self::get_wireguard_singlehop_config( - query, - custom_lists, - parsed_relays, - )?) - } else { - WireguardConfig::from(Self::get_wireguard_multihop_config( - query, - custom_lists, - parsed_relays, - )?) - }; + let inner = Self::get_wireguard_relay_config(query, custom_lists, parsed_relays)?; let endpoint = Self::get_wireguard_endpoint(query, parsed_relays, &inner)?; let obfuscator = Self::get_wireguard_obfuscator(query, inner.clone(), &endpoint, parsed_relays)?; @@ -674,42 +662,60 @@ impl RelaySelector { }) } + /// TODO: Document + fn get_wireguard_relay_config( + query: &RelayQuery, + custom_lists: &CustomListsSettings, + parsed_relays: &ParsedRelays, + ) -> Result { + let singlehop = !query.wireguard_constraints().multihop(); + let inner = if singlehop { + match Self::get_wireguard_singlehop_config(query, custom_lists, parsed_relays) { + Some(exit) => WireguardConfig::from(exit), + None => { + // No exit candidate was found.. Check if smart routing stuff works! + WireguardConfig::from(Self::select_daita_multihop_through_smart_routing( + query, + custom_lists, + parsed_relays, + )?; + WireguardConfig::from(multihop) + } + } + } else { + WireguardConfig::from(Self::get_wireguard_multihop_config( + query, + custom_lists, + parsed_relays, + )?) + }; + + Ok(inner) + } + /// Select a valid Wireguard exit relay. /// /// # Returns - /// * An `Err` if no exit relay can be chosen - /// * `Ok(WireguardConfig)` otherwise + /// * `Ok(WireguardConfig)` if an exit relay was selected + /// * `None` otherwise fn get_wireguard_singlehop_config( query: &RelayQuery, custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, - ) -> Result { + ) -> Option { let candidates = filter_matching_relay_list(query, parsed_relays, custom_lists); - - // // TODO: Move somewhere - // // Or keep here, but return either. - // if let Some(x) = Self::select_daita_multihop_config_if_necessary( - // query, - // custom_lists, - // parsed_relays, - // &candidates, - // )? { - // return Ok(x); - // } - helpers::pick_random_relay(&candidates) .cloned() .map(Singlehop::new) - .ok_or(Error::NoRelay) } - fn select_daita_multihop_config_if_necessary( + /// TODO: Make sure to make a comment regarding the fact that we no longer check for 'empty' + /// candidates. + fn select_daita_multihop_through_smart_routing( query: &RelayQuery, custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, - // TODO: What is this?? - candidates: &[Relay], - ) -> Result, Error> { + ) -> Result { // are we using daita? let using_daita = || query.wireguard_constraints().daita == Constraint::Only(true); @@ -735,16 +741,10 @@ impl RelaySelector { // if we found no matching relays because DAITA was enabled, and `use_multihop_if_necessary` // is enabled, try enabling multihop and connecting using an automatically selected // entry relay. - // - // TODO: I think this should be pushed up one level and not be in this function scope. - if candidates.is_empty() - && using_daita() - && no_relay_because_daita()? - && use_multihop_if_necessary() - { - Self::get_wireguard_auto_multihop_config(query, custom_lists, parsed_relays).map(Some) + if using_daita() && no_relay_because_daita()? && use_multihop_if_necessary() { + Self::get_wireguard_auto_multihop_config(query, custom_lists, parsed_relays) } else { - Ok(None) + Err(Error::NoRelay) } } From 89c758e08a81882e59c20489b9398133fd4d79a4 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Wed, 16 Oct 2024 18:50:22 +0200 Subject: [PATCH 06/14] Make smart routing take precedence over multihop If both multihop and DAITA (with smart routing) is enabled, a DAITA-compatible entry relay will be selected. This implies that if the user has selected an entry relay which is not DAITA-enabled, the relay selector will override this choice and force a DAITA-enabled relay as entry. If smart routing is disabled in this case, the user's selected entry will always be selected, even if this means that the user will end up in a blocked state. --- .../src/relay_selector/mod.rs | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index 4e78a73c8a3c..bcd39bd90390 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -673,8 +673,8 @@ impl RelaySelector { match Self::get_wireguard_singlehop_config(query, custom_lists, parsed_relays) { Some(exit) => WireguardConfig::from(exit), None => { - // No exit candidate was found.. Check if smart routing stuff works! - WireguardConfig::from(Self::select_daita_multihop_through_smart_routing( + // No exit candidate was found.. Time to try smart routing! + let multihop = Self::select_daita_multihop_through_smart_routing( query, custom_lists, parsed_relays, @@ -683,11 +683,25 @@ impl RelaySelector { } } } else { - WireguardConfig::from(Self::get_wireguard_multihop_config( - query, - custom_lists, - parsed_relays, - )?) + // A DAITA compatible entry should be used even when the exit is DAITA compatible. + // This only makes sense in context: The user is no longer able to explicitly choose an + // entry relay with smarting routing enabled, even if multihop is turned on + // TODO: Move this somewhere common? + let using_daita = || query.wireguard_constraints().daita == Constraint::Only(true); + // TODO: Move this somewhere common? + let smart_routing_enabled = || { + query + .wireguard_constraints() + .daita_use_multihop_if_necessary + .is_only_and(|on| on) + }; + // Also implied: Multihop is enabled. + let multihop = if using_daita() && smart_routing_enabled() { + Self::get_wireguard_auto_multihop_config(query, custom_lists, parsed_relays)? + } else { + Self::get_wireguard_multihop_config(query, custom_lists, parsed_relays)? + }; + WireguardConfig::from(multihop) }; Ok(inner) @@ -810,7 +824,7 @@ impl RelaySelector { parsed_relays: &ParsedRelays, ) -> Result { // Here, we modify the original query just a bit. - // The actual query for an exit relay is identical as for an exit relay, with the + // The actual query for an entry relay is identical as for an exit relay, with the // exception that the location is different. It is simply the location as dictated by // the query's multihop constraint. let mut entry_relay_query = query.clone(); From dae76a8356427a7f24f7dcafb526eacc7a5ee661 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Wed, 16 Oct 2024 19:06:36 +0200 Subject: [PATCH 07/14] Reconnect when `Direct only` option is toggled even when multihop is on --- mullvad-daemon/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index dd6c828802da..08c523ffd760 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -2403,10 +2403,9 @@ impl Daemon { .intersection(Constraint::Only(TunnelType::Wireguard)) .is_some(); - let multihop_enabled = constraints.wireguard_constraints.use_multihop; let daita_enabled = self.settings.tunnel_options.wireguard.daita.enabled; - if settings_changed && wireguard_enabled && daita_enabled && !multihop_enabled { + if settings_changed && wireguard_enabled && daita_enabled { log::info!("Reconnecting because DAITA settings changed"); self.reconnect_tunnel(); } From 1bb52fb9c98bf1a8dba64e6d12aa6df46574839c Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Wed, 16 Oct 2024 21:24:56 +0200 Subject: [PATCH 08/14] Define the trait `RelayQueryExtensions` for grouping common methods --- .../src/relay_selector/mod.rs | 31 +++---------------- .../src/relay_selector/query.rs | 26 ++++++++++++++++ 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index bcd39bd90390..ef84cebb5a46 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -14,7 +14,8 @@ use relays::{Multihop, Singlehop, WireguardConfig}; use crate::detailer::{openvpn_endpoint, wireguard_endpoint}; use crate::error::{EndpointErrorDetails, Error}; use crate::query::{ - BridgeQuery, ObfuscationQuery, OpenVpnRelayQuery, RelayQuery, WireguardRelayQuery, + BridgeQuery, ObfuscationQuery, OpenVpnRelayQuery, RelayQuery, RelayQueryExt, + WireguardRelayQuery, }; use std::path::Path; @@ -668,8 +669,7 @@ impl RelaySelector { custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, ) -> Result { - let singlehop = !query.wireguard_constraints().multihop(); - let inner = if singlehop { + let inner = if query.singlehop() { match Self::get_wireguard_singlehop_config(query, custom_lists, parsed_relays) { Some(exit) => WireguardConfig::from(exit), None => { @@ -686,17 +686,8 @@ impl RelaySelector { // A DAITA compatible entry should be used even when the exit is DAITA compatible. // This only makes sense in context: The user is no longer able to explicitly choose an // entry relay with smarting routing enabled, even if multihop is turned on - // TODO: Move this somewhere common? - let using_daita = || query.wireguard_constraints().daita == Constraint::Only(true); - // TODO: Move this somewhere common? - let smart_routing_enabled = || { - query - .wireguard_constraints() - .daita_use_multihop_if_necessary - .is_only_and(|on| on) - }; // Also implied: Multihop is enabled. - let multihop = if using_daita() && smart_routing_enabled() { + let multihop = if query.using_daita() && query.use_multihop_if_necessary() { Self::get_wireguard_auto_multihop_config(query, custom_lists, parsed_relays)? } else { Self::get_wireguard_multihop_config(query, custom_lists, parsed_relays)? @@ -730,9 +721,6 @@ impl RelaySelector { custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, ) -> Result { - // are we using daita? - let using_daita = || query.wireguard_constraints().daita == Constraint::Only(true); - // is the `candidates` list empty because DAITA is enabled? let no_relay_because_daita = || { let mut query = query.clone(); @@ -743,19 +731,10 @@ impl RelaySelector { Result::<_, Error>::Ok(!candidates.is_empty()) }; - // is `use_multihop_if_necessary` enabled? - let use_multihop_if_necessary = || { - query - .wireguard_constraints() - .daita_use_multihop_if_necessary - .intersection(Constraint::Only(true)) - .is_some() - }; - // if we found no matching relays because DAITA was enabled, and `use_multihop_if_necessary` // is enabled, try enabling multihop and connecting using an automatically selected // entry relay. - if using_daita() && no_relay_because_daita()? && use_multihop_if_necessary() { + if query.using_daita() && query.use_multihop_if_necessary() && no_relay_because_daita()? { Self::get_wireguard_auto_multihop_config(query, custom_lists, parsed_relays) } else { Err(Error::NoRelay) diff --git a/mullvad-relay-selector/src/relay_selector/query.rs b/mullvad-relay-selector/src/relay_selector/query.rs index 570a2212cb42..3878e2e0c7b6 100644 --- a/mullvad-relay-selector/src/relay_selector/query.rs +++ b/mullvad-relay-selector/src/relay_selector/query.rs @@ -913,6 +913,32 @@ pub mod builder { } } +/// This trait defines a bunch of helper methods on [`RelayQuery`]. +pub trait RelayQueryExt { + /// Are we using daita? + fn using_daita(&self) -> bool; + /// is `use_multihop_if_necessary` enabled? In other words, is `Direct only` disabled? + fn use_multihop_if_necessary(&self) -> bool; + /// Are we using singlehop? I.e. is multihop *not* explicitly enabled? + fn singlehop(&self) -> bool; +} + +impl RelayQueryExt for RelayQuery { + fn using_daita(&self) -> bool { + self.wireguard_constraints() + .daita + .is_only_and(|enabled| enabled) + } + fn use_multihop_if_necessary(&self) -> bool { + self.wireguard_constraints() + .daita_use_multihop_if_necessary + .is_only_and(|enabled| enabled) + } + fn singlehop(&self) -> bool { + !self.wireguard_constraints().multihop() + } +} + #[cfg(test)] mod test { use mullvad_types::{ From 186b736784f8310495317b52898bed24bbbee779 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Thu, 17 Oct 2024 09:20:44 +0200 Subject: [PATCH 09/14] Add missing docs --- .../src/relay_selector/relays.rs | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/mullvad-relay-selector/src/relay_selector/relays.rs b/mullvad-relay-selector/src/relay_selector/relays.rs index e8a1035c1d51..9ebf31f41747 100644 --- a/mullvad-relay-selector/src/relay_selector/relays.rs +++ b/mullvad-relay-selector/src/relay_selector/relays.rs @@ -1,29 +1,38 @@ -//! TODO: Document this module +//! This module defines wrapper types around [`Relay`], often to provide certain runtime guarantees +//! or disambiguate the type of relay which is used in the relay selector's internal APIs. use mullvad_types::relay_list::{Relay, RelayEndpointData}; -// TODO: Import `Either` and convert `Multihop` and `Singlehop` into concrete types. -/// This struct defines the different Wireguard relays the the relay selector can end up selecting -/// for an arbitrary Wireguard [`query`]. -/// -/// - [`WireguardConfig::Singlehop`]; A normal wireguard relay where VPN traffic enters and exits -/// through this sole relay. -/// - [`WireguardConfig::Multihop`]; Two wireguard relays to be used in a multihop circuit. VPN -/// traffic will enter through `entry` and eventually come out from `exit` before the traffic will -/// actually be routed to the broader internet. +/// - [`WireguardConfig::Singlehop`]: A wireguard relay where VPN traffic enters and exits. +/// - [`WireguardConfig::Multihop`]: Two wireguard relays to be used in a multihop circuit. VPN +/// traffic will enter through `entry` and eventually exit through `exit` before the traffic will +/// actually be routed to the internet. #[derive(Clone, Debug)] pub enum WireguardConfig { - /// Strongly prefer to instantiate this variant using [`WireguardConfig::singlehop`] as that - /// will assert that the relay is of the expected type. + /// An exit relay. Singlehop { exit: Relay }, - /// Strongly prefer to instantiate this variant using [`WireguardConfig::multihop`] as that - /// will assert that the entry & exit relays are of the expected type. + /// An entry and an exit relay. Multihop { exit: Relay, entry: Relay }, } -/// TODO: Document +/// A type representing single Wireguard relay. +/// +/// Before you can read any data out of a [`Singlehop`] value uou need to convert it to [`WireguardConfig`]. +/// This is easy since [`Singlehop`] implements [`Into`]. +/// +/// # Why not simply use [`Relay`]? +/// The only way to construct a [`Singlehop`] value is with [`Singlehop::new`] which performs +/// additional validation which guarantees that the relay actually is a Wireguard relay, while +/// [`Relay`] is not guaranteed to be a Wireguard relay. pub struct Singlehop(Relay); -/// TODO: Document +/// A type representing two Wireguard relay - an entry and an exit. +/// +/// Before you can read any data out of a [`Multihop`] value uou need to convert it to [`WireguardConfig`]. +/// This is easy since [`Multihop`] implements [`Into`]. +/// +/// # Why not simply use [`Relay`]? +/// The same rationale as for [`Singlehop`] applies - [`Multihop::new`] performs additional +/// validation on the entry and exit relays. pub struct Multihop { entry: Relay, exit: Relay, From 031b8a797ed88340d0a8a5466805605f00c2300a Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Thu, 17 Oct 2024 09:29:35 +0200 Subject: [PATCH 10/14] Remove superflous function --- .../src/relay_selector/mod.rs | 59 +++++++------------ 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index ef84cebb5a46..9122c340c836 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -629,7 +629,7 @@ impl RelaySelector { Self::get_wireguard_relay(&query, custom_lists, parsed_relays) } - /// Derive a valid Wireguard relay configuration from `query`. + /// Derive a valid relay configuration from `query`. /// /// # Note /// This function should *only* be called with a Wireguard query. @@ -663,23 +663,35 @@ impl RelaySelector { }) } - /// TODO: Document + /// Derive a valid Wireguard relay configuration from `query`. + /// + /// # Returns + /// * An `Err` if no exit relay can be chosen + /// * An `Err` if no entry relay can be chosen (if multihop is enabled on `query`) + /// * `Ok(WireguardConfig)` otherwise fn get_wireguard_relay_config( query: &RelayQuery, custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, ) -> Result { + // TODO: Make sure that this works differently on Android. let inner = if query.singlehop() { match Self::get_wireguard_singlehop_config(query, custom_lists, parsed_relays) { Some(exit) => WireguardConfig::from(exit), None => { - // No exit candidate was found.. Time to try smart routing! - let multihop = Self::select_daita_multihop_through_smart_routing( - query, - custom_lists, - parsed_relays, - )?; - WireguardConfig::from(multihop) + // If we found no matching relays because DAITA was enabled, and `use_multihop_if_necessary` + // is enabled, try enabling multihop and connecting using an automatically selected + // entry relay. + if query.using_daita() && query.use_multihop_if_necessary() { + let multihop = Self::get_wireguard_auto_multihop_config( + query, + custom_lists, + parsed_relays, + )?; + WireguardConfig::from(multihop) + } else { + return Err(Error::NoRelay); + } } } } else { @@ -701,7 +713,7 @@ impl RelaySelector { /// Select a valid Wireguard exit relay. /// /// # Returns - /// * `Ok(WireguardConfig)` if an exit relay was selected + /// * `Ok(Singlehop)` if an exit relay was selected /// * `None` otherwise fn get_wireguard_singlehop_config( query: &RelayQuery, @@ -714,33 +726,6 @@ impl RelaySelector { .map(Singlehop::new) } - /// TODO: Make sure to make a comment regarding the fact that we no longer check for 'empty' - /// candidates. - fn select_daita_multihop_through_smart_routing( - query: &RelayQuery, - custom_lists: &CustomListsSettings, - parsed_relays: &ParsedRelays, - ) -> Result { - // is the `candidates` list empty because DAITA is enabled? - let no_relay_because_daita = || { - let mut query = query.clone(); - let mut wireguard_constraints = query.wireguard_constraints().clone(); - wireguard_constraints.daita = Constraint::Any; - query.set_wireguard_constraints(wireguard_constraints)?; - let candidates = filter_matching_relay_list(&query, parsed_relays, custom_lists); - Result::<_, Error>::Ok(!candidates.is_empty()) - }; - - // if we found no matching relays because DAITA was enabled, and `use_multihop_if_necessary` - // is enabled, try enabling multihop and connecting using an automatically selected - // entry relay. - if query.using_daita() && query.use_multihop_if_necessary() && no_relay_because_daita()? { - Self::get_wireguard_auto_multihop_config(query, custom_lists, parsed_relays) - } else { - Err(Error::NoRelay) - } - } - /// Select a valid Wireguard exit relay, together with with an automatically chosen entry relay. /// /// # Returns From 58e1123f7844104f36ab326d4f2ee90043034c8e Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Thu, 17 Oct 2024 09:33:49 +0200 Subject: [PATCH 11/14] Do not use multihop on Android (yet) --- mullvad-relay-selector/src/relay_selector/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index 9122c340c836..c71a85f37811 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -674,7 +674,12 @@ impl RelaySelector { custom_lists: &CustomListsSettings, parsed_relays: &ParsedRelays, ) -> Result { - // TODO: Make sure that this works differently on Android. + // TODO: Remove when Android gets support for multihop. + if cfg!(target_os = "android") { + let relay = Self::get_wireguard_singlehop_config(query, custom_lists, parsed_relays) + .ok_or(Error::NoRelay)?; + return Ok(WireguardConfig::from(relay)); + } let inner = if query.singlehop() { match Self::get_wireguard_singlehop_config(query, custom_lists, parsed_relays) { Some(exit) => WireguardConfig::from(exit), From 6aac72972450ce598b60f4e1e9264cad5860c3a4 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Thu, 17 Oct 2024 09:52:18 +0200 Subject: [PATCH 12/14] Add test case for smart routing overriding multihop --- .../src/relay_selector/query.rs | 4 +- .../tests/relay_selector.rs | 74 +++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/mullvad-relay-selector/src/relay_selector/query.rs b/mullvad-relay-selector/src/relay_selector/query.rs index 3878e2e0c7b6..162865368acc 100644 --- a/mullvad-relay-selector/src/relay_selector/query.rs +++ b/mullvad-relay-selector/src/relay_selector/query.rs @@ -932,7 +932,9 @@ impl RelayQueryExt for RelayQuery { fn use_multihop_if_necessary(&self) -> bool { self.wireguard_constraints() .daita_use_multihop_if_necessary - .is_only_and(|enabled| enabled) + // The default value is `Any`, which means that we need to check the intersection. + .intersection(Constraint::Only(true)) + .is_some() } fn singlehop(&self) -> bool { !self.wireguard_constraints().multihop() diff --git a/mullvad-relay-selector/tests/relay_selector.rs b/mullvad-relay-selector/tests/relay_selector.rs index 1ce69b0591fb..d80fe359e979 100644 --- a/mullvad-relay-selector/tests/relay_selector.rs +++ b/mullvad-relay-selector/tests/relay_selector.rs @@ -95,6 +95,27 @@ static RELAYS: LazyLock = LazyLock::new(|| RelayList { }), location: DUMMY_LOCATION.clone(), }, + Relay { + hostname: "se11-wireguard".to_string(), + ipv4_addr_in: "185.213.154.69".parse().unwrap(), + ipv6_addr_in: Some("2a03:1b20:5:f011::a11f".parse().unwrap()), + overridden_ipv4: false, + overridden_ipv6: false, + include_in_country: true, + active: true, + owned: false, + provider: "provider2".to_string(), + weight: 1, + endpoint_data: RelayEndpointData::Wireguard(WireguardRelayEndpointData { + public_key: PublicKey::from_base64( + "BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=", + ) + .unwrap(), + daita: true, + shadowsocks_extra_addr_in: vec![], + }), + location: DUMMY_LOCATION.clone(), + }, Relay { hostname: "se-got-001".to_string(), ipv4_addr_in: "185.213.154.131".parse().unwrap(), @@ -1401,6 +1422,59 @@ fn test_daita_any_tunnel_protocol() { ); } +/// Always use smart routing to select a DAITA-enabled entry relay if both smart routing and multihop is enabled. +/// This applies even if the entry is set explicitly. +/// DAITA is a core privacy feature +#[test] +fn test_daita_smart_routing_overrides_multihop() { + let relay_selector = RelaySelector::from_list(SelectorConfig::default(), RELAYS.clone()); + let query = RelayQueryBuilder::new() + .wireguard() + .daita() + .daita_use_multihop_if_necessary(true) + .multihop() + // Set the entry to a relay that explicitly does *not* support DAITA. + // Later, we check that the smart routing disregards this choice and selects a DAITA-enabled + // relay instead. + .entry(NON_DAITA_RELAY_LOCATION.clone()) + .build(); + + for _ in 0..100 { + // Make sure a DAITA-enabled relay is always selected due to smart routing. + let relay = relay_selector + .get_relay_by_query(query.clone()) + .expect("Expected to find a relay with daita_use_multihop_if_necessary"); + match relay { + GetRelay::Wireguard { + inner: WireguardConfig::Multihop { entry, exit: _ }, + .. + } => { + assert!(supports_daita(&entry), "entry relay must support DAITA"); + } + wrong_relay => panic!( + "Relay selector should have picked two Wireguard relays, instead chose {wrong_relay:?}" + ), + } + } + + // Assert that disabling smart routing for this query will fail to generate a valid multihop + // config, thus blocking the user. + let query = RelayQueryBuilder::new() + .wireguard() + .daita() + .daita_use_multihop_if_necessary(false) + .multihop() + .entry(NON_DAITA_RELAY_LOCATION.clone()) + .build(); + + let relay = relay_selector.get_relay_by_query(query); + + assert!( + relay.is_err(), + "expected there to be no valid multihop configuration! Instead got {relay:#?}" + ); +} + /// Always select a WireGuard relay when multihop is enabled /// Multihop is a core privacy feature #[test] From 77a192e96991c885db1febe054005f4fc9658d5f Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Thu, 17 Oct 2024 17:21:44 +0200 Subject: [PATCH 13/14] Update Changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a08634efd48..9c9c19736d72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ Line wrap the file at 100 chars. Th ### Changed - Replace the draft key encapsulation mechanism Kyber (round 3) with the standardized ML-KEM (FIPS 203) dito in the handshake for Quantum-resistant tunnels. +- Make Smart Routing override multihop if both are enabled. To manually set the entry relay, + explicitly enable the "Direct only" option in the DAITA settings. #### Windows - Enable quantum-resistant tunnels by default (when set to `auto`). From a416d9d12f5df6732eeb212e40cbf0800ff89bdf Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Fri, 18 Oct 2024 15:18:34 +0200 Subject: [PATCH 14/14] Run nightly formatter --- mullvad-relay-selector/src/error.rs | 3 +- mullvad-relay-selector/src/lib.rs | 8 +-- .../src/relay_selector/helpers.rs | 4 +- .../src/relay_selector/mod.rs | 59 +++++++++++-------- .../src/relay_selector/relays.rs | 8 +-- .../tests/relay_selector.rs | 4 +- 6 files changed, 46 insertions(+), 40 deletions(-) diff --git a/mullvad-relay-selector/src/error.rs b/mullvad-relay-selector/src/error.rs index 0bd5fad5d455..9f7a50bda36c 100644 --- a/mullvad-relay-selector/src/error.rs +++ b/mullvad-relay-selector/src/error.rs @@ -3,8 +3,7 @@ use mullvad_types::{relay_constraints::MissingCustomBridgeSettings, relay_list::Relay}; -use crate::detailer; -use crate::relay_selector::relays::WireguardConfig; +use crate::{detailer, relay_selector::relays::WireguardConfig}; #[derive(thiserror::Error, Debug)] pub enum Error { diff --git a/mullvad-relay-selector/src/lib.rs b/mullvad-relay-selector/src/lib.rs index cbd7c94a3628..be12c2944353 100644 --- a/mullvad-relay-selector/src/lib.rs +++ b/mullvad-relay-selector/src/lib.rs @@ -8,10 +8,8 @@ mod relay_selector; // Re-exports pub use error::Error; -pub use relay_selector::detailer; -pub use relay_selector::query; -pub use relay_selector::relays::WireguardConfig; pub use relay_selector::{ - AdditionalRelayConstraints, AdditionalWireguardConstraints, GetRelay, RelaySelector, - RuntimeParameters, SelectedBridge, SelectedObfuscator, SelectorConfig, RETRY_ORDER, + detailer, query, relays::WireguardConfig, AdditionalRelayConstraints, + AdditionalWireguardConstraints, GetRelay, RelaySelector, RuntimeParameters, SelectedBridge, + SelectedObfuscator, SelectorConfig, RETRY_ORDER, }; diff --git a/mullvad-relay-selector/src/relay_selector/helpers.rs b/mullvad-relay-selector/src/relay_selector/helpers.rs index c8b529e0cd96..a8a2ba7fcb85 100644 --- a/mullvad-relay-selector/src/relay_selector/helpers.rs +++ b/mullvad-relay-selector/src/relay_selector/helpers.rs @@ -207,8 +207,8 @@ fn port_if_in_range>(port_ranges: &[R], port: u16) -> Result /// - `port_ranges`: A slice of port numbers. /// /// # Returns -/// - On success, a randomly selected port number within the given ranges. Otherwise, -/// an error is returned. +/// - On success, a randomly selected port number within the given ranges. Otherwise, an error is +/// returned. pub fn select_random_port + Iterator + Clone>( port_ranges: &[R], ) -> Result { diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index c71a85f37811..e7a69227272e 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -11,37 +11,45 @@ use matcher::{filter_matching_bridges, filter_matching_relay_list}; use parsed_relays::ParsedRelays; use relays::{Multihop, Singlehop, WireguardConfig}; -use crate::detailer::{openvpn_endpoint, wireguard_endpoint}; -use crate::error::{EndpointErrorDetails, Error}; -use crate::query::{ - BridgeQuery, ObfuscationQuery, OpenVpnRelayQuery, RelayQuery, RelayQueryExt, - WireguardRelayQuery, +use crate::{ + detailer::{openvpn_endpoint, wireguard_endpoint}, + error::{EndpointErrorDetails, Error}, + query::{ + BridgeQuery, ObfuscationQuery, OpenVpnRelayQuery, RelayQuery, RelayQueryExt, + WireguardRelayQuery, + }, }; -use std::path::Path; -use std::sync::{Arc, LazyLock, Mutex}; -use std::time::SystemTime; +use std::{ + path::Path, + sync::{Arc, LazyLock, Mutex}, + time::SystemTime, +}; use chrono::{DateTime, Local}; use itertools::Itertools; -use mullvad_types::constraints::Constraint; -use mullvad_types::custom_list::CustomListsSettings; -use mullvad_types::endpoint::MullvadWireguardEndpoint; -use mullvad_types::location::{Coordinates, Location}; -use mullvad_types::relay_constraints::{ - BridgeSettings, BridgeState, InternalBridgeConstraints, ObfuscationSettings, - OpenVpnConstraints, RelayConstraints, RelayOverride, RelaySettings, ResolvedBridgeSettings, - WireguardConstraints, +use mullvad_types::{ + constraints::Constraint, + custom_list::CustomListsSettings, + endpoint::MullvadWireguardEndpoint, + location::{Coordinates, Location}, + relay_constraints::{ + BridgeSettings, BridgeState, InternalBridgeConstraints, ObfuscationSettings, + OpenVpnConstraints, RelayConstraints, RelayOverride, RelaySettings, ResolvedBridgeSettings, + WireguardConstraints, + }, + relay_list::{Relay, RelayEndpointData, RelayList}, + settings::Settings, + wireguard::QuantumResistantState, + CustomTunnelEndpoint, Intersection, }; -use mullvad_types::relay_list::{Relay, RelayEndpointData, RelayList}; -use mullvad_types::settings::Settings; -use mullvad_types::wireguard::QuantumResistantState; -use mullvad_types::{CustomTunnelEndpoint, Intersection}; -use talpid_types::net::{ - obfuscation::ObfuscatorConfig, proxy::CustomProxy, Endpoint, TransportProtocol, TunnelType, +use talpid_types::{ + net::{ + obfuscation::ObfuscatorConfig, proxy::CustomProxy, Endpoint, TransportProtocol, TunnelType, + }, + ErrorExt, }; -use talpid_types::ErrorExt; /// [`RETRY_ORDER`] defines an ordered set of relay parameters which the relay selector should /// prioritize on successive connection attempts. Note that these will *never* override user @@ -684,8 +692,9 @@ impl RelaySelector { match Self::get_wireguard_singlehop_config(query, custom_lists, parsed_relays) { Some(exit) => WireguardConfig::from(exit), None => { - // If we found no matching relays because DAITA was enabled, and `use_multihop_if_necessary` - // is enabled, try enabling multihop and connecting using an automatically selected + // If we found no matching relays because DAITA was enabled, and + // `use_multihop_if_necessary` is enabled, try enabling + // multihop and connecting using an automatically selected // entry relay. if query.using_daita() && query.use_multihop_if_necessary() { let multihop = Self::get_wireguard_auto_multihop_config( diff --git a/mullvad-relay-selector/src/relay_selector/relays.rs b/mullvad-relay-selector/src/relay_selector/relays.rs index 9ebf31f41747..3cb8ff3c5d23 100644 --- a/mullvad-relay-selector/src/relay_selector/relays.rs +++ b/mullvad-relay-selector/src/relay_selector/relays.rs @@ -17,8 +17,8 @@ pub enum WireguardConfig { /// A type representing single Wireguard relay. /// -/// Before you can read any data out of a [`Singlehop`] value uou need to convert it to [`WireguardConfig`]. -/// This is easy since [`Singlehop`] implements [`Into`]. +/// Before you can read any data out of a [`Singlehop`] value uou need to convert it to +/// [`WireguardConfig`]. This is easy since [`Singlehop`] implements [`Into`]. /// /// # Why not simply use [`Relay`]? /// The only way to construct a [`Singlehop`] value is with [`Singlehop::new`] which performs @@ -27,8 +27,8 @@ pub enum WireguardConfig { pub struct Singlehop(Relay); /// A type representing two Wireguard relay - an entry and an exit. /// -/// Before you can read any data out of a [`Multihop`] value uou need to convert it to [`WireguardConfig`]. -/// This is easy since [`Multihop`] implements [`Into`]. +/// Before you can read any data out of a [`Multihop`] value uou need to convert it to +/// [`WireguardConfig`]. This is easy since [`Multihop`] implements [`Into`]. /// /// # Why not simply use [`Relay`]? /// The same rationale as for [`Singlehop`] applies - [`Multihop::new`] performs additional diff --git a/mullvad-relay-selector/tests/relay_selector.rs b/mullvad-relay-selector/tests/relay_selector.rs index d80fe359e979..6908d468de11 100644 --- a/mullvad-relay-selector/tests/relay_selector.rs +++ b/mullvad-relay-selector/tests/relay_selector.rs @@ -1422,8 +1422,8 @@ fn test_daita_any_tunnel_protocol() { ); } -/// Always use smart routing to select a DAITA-enabled entry relay if both smart routing and multihop is enabled. -/// This applies even if the entry is set explicitly. +/// Always use smart routing to select a DAITA-enabled entry relay if both smart routing and +/// multihop is enabled. This applies even if the entry is set explicitly. /// DAITA is a core privacy feature #[test] fn test_daita_smart_routing_overrides_multihop() {