Skip to content

Commit 21cdeb1

Browse files
Merge branch 'fix-android-unwrap'
2 parents 45b64f2 + 9db7b5a commit 21cdeb1

File tree

6 files changed

+103
-52
lines changed

6 files changed

+103
-52
lines changed

mullvad-daemon/src/tunnel.rs

+6-11
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,8 @@ pub enum Error {
4444
#[error("Not logged in on a valid device")]
4545
NoAuthDetails,
4646

47-
#[error("No relay available")]
48-
NoRelayAvailable,
49-
50-
#[error("No bridge available")]
51-
NoBridgeAvailable,
47+
#[error("Failed to select a matching relay")]
48+
SelectRelay(#[from] mullvad_relay_selector::Error),
5249

5350
#[error("Failed to resolve hostname for custom relay")]
5451
ResolveCustomHostname,
@@ -148,11 +145,7 @@ impl InnerParametersGenerator {
148145
let data = self.device().await?;
149146
let selected_relay = self
150147
.relay_selector
151-
.get_relay(retry_attempt as usize, RuntimeParameters { ipv6 })
152-
.map_err(|err| match err {
153-
mullvad_relay_selector::Error::NoBridge => Error::NoBridgeAvailable,
154-
_ => Error::NoRelayAvailable,
155-
})?;
148+
.get_relay(retry_attempt as usize, RuntimeParameters { ipv6 })?;
156149

157150
match selected_relay {
158151
#[cfg(not(target_os = "android"))]
@@ -287,7 +280,9 @@ impl TunnelParametersGenerator for ParametersGenerator {
287280
.generate(retry_attempt, ipv6)
288281
.await
289282
.map_err(|error| match error {
290-
Error::NoBridgeAvailable => ParameterGenerationError::NoMatchingBridgeRelay,
283+
Error::SelectRelay(mullvad_relay_selector::Error::NoBridge) => {
284+
ParameterGenerationError::NoMatchingBridgeRelay
285+
}
291286
Error::ResolveCustomHostname => {
292287
ParameterGenerationError::CustomTunnelHostResultionError
293288
}

mullvad-relay-selector/src/relay_selector/detailer.rs

+20-9
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@ use mullvad_types::{
1414
constraints::Constraint,
1515
endpoint::MullvadWireguardEndpoint,
1616
relay_constraints::TransportPort,
17-
relay_list::{OpenVpnEndpoint, OpenVpnEndpointData, Relay, WireguardEndpointData},
17+
relay_list::{
18+
OpenVpnEndpoint, OpenVpnEndpointData, Relay, RelayEndpointData, WireguardEndpointData,
19+
},
1820
};
1921
use talpid_types::net::{
20-
all_of_the_internet, wireguard::PeerConfig, Endpoint, IpVersion, TransportProtocol,
22+
all_of_the_internet,
23+
wireguard::{PeerConfig, PublicKey},
24+
Endpoint, IpVersion, TransportProtocol,
2125
};
2226

2327
use super::{
@@ -31,6 +35,8 @@ pub enum Error {
3135
NoOpenVpnEndpoint,
3236
#[error("No bridge endpoint could be derived")]
3337
NoBridgeEndpoint,
38+
#[error("OpenVPN relays and bridges does not have a public key. Expected a Wireguard relay")]
39+
MissingPublicKey,
3440
#[error("The selected relay does not support IPv6")]
3541
NoIPv6(Box<Relay>),
3642
#[error("Invalid port argument: port {0} is not in any valid Wireguard port range")]
@@ -70,7 +76,7 @@ fn wireguard_singlehop_endpoint(
7076
SocketAddr::new(host, port)
7177
};
7278
let peer_config = PeerConfig {
73-
public_key: exit.endpoint_data.unwrap_wireguard_ref().public_key.clone(),
79+
public_key: get_public_key(exit)?.clone(),
7480
endpoint,
7581
allowed_ips: all_of_the_internet(),
7682
// This will be filled in later, not the relay selector's problem
@@ -106,7 +112,7 @@ fn wireguard_multihop_endpoint(
106112
SocketAddr::from((ip, port))
107113
};
108114
let exit = PeerConfig {
109-
public_key: exit.endpoint_data.unwrap_wireguard_ref().public_key.clone(),
115+
public_key: get_public_key(exit)?.clone(),
110116
endpoint: exit_endpoint,
111117
// The exit peer should be able to route incoming VPN traffic to the rest of
112118
// the internet.
@@ -121,11 +127,7 @@ fn wireguard_multihop_endpoint(
121127
SocketAddr::from((host, port))
122128
};
123129
let entry = PeerConfig {
124-
public_key: entry
125-
.endpoint_data
126-
.unwrap_wireguard_ref()
127-
.public_key
128-
.clone(),
130+
public_key: get_public_key(entry)?.clone(),
129131
endpoint: entry_endpoint,
130132
// The entry peer should only be able to route incoming VPN traffic to the
131133
// exit peer.
@@ -177,6 +179,15 @@ fn get_port_for_wireguard_relay(
177179
}
178180
}
179181

182+
/// Read the [`PublicKey`] of a relay. This will only succeed if [relay][`Relay`] is a
183+
/// [Wireguard][`RelayEndpointData::Wireguard`] relay.
184+
const fn get_public_key(relay: &Relay) -> Result<&PublicKey, Error> {
185+
match &relay.endpoint_data {
186+
RelayEndpointData::Wireguard(endpoint) => Ok(&endpoint.public_key),
187+
RelayEndpointData::Openvpn | RelayEndpointData::Bridge => Err(Error::MissingPublicKey),
188+
}
189+
}
190+
180191
/// Selects a random port number from a list of provided port ranges.
181192
///
182193
/// This function iterates over a list of port ranges, each represented as a tuple (u16, u16)

mullvad-relay-selector/src/relay_selector/matcher.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -114,18 +114,24 @@ pub const fn filter_openvpn(relay: &Relay) -> bool {
114114
}
115115

116116
/// Returns whether the relay matches the tunnel constraint `filter`
117+
#[cfg(not(target_os = "android"))]
117118
pub const fn filter_tunnel_type(filter: &Constraint<TunnelType>, relay: &Relay) -> bool {
118119
match filter {
119120
Constraint::Any => true,
120121
Constraint::Only(typ) => match typ {
121-
// Do not keep OpenVPN relays on Android
122-
TunnelType::OpenVpn if cfg!(target_os = "android") => false,
123122
TunnelType::OpenVpn => filter_openvpn(relay),
124123
TunnelType::Wireguard => filter_wireguard(relay),
125124
},
126125
}
127126
}
128127

128+
/// Returns whether the relay matches the tunnel constraint `filter`
129+
#[cfg(target_os = "android")]
130+
pub const fn filter_tunnel_type(_: &Constraint<TunnelType>, relay: &Relay) -> bool {
131+
// Only keep Wireguard relays on Android (i.e. filter out OpenVPN relays)
132+
filter_wireguard(relay)
133+
}
134+
129135
/// Returns whether the relay is a Wireguard relay.
130136
pub const fn filter_wireguard(relay: &Relay) -> bool {
131137
matches!(relay.endpoint_data, RelayEndpointData::Wireguard(_))

mullvad-relay-selector/src/relay_selector/mod.rs

+68-20
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use mullvad_types::{
2525
OpenVpnConstraints, RelayConstraints, RelayOverride, RelaySettings, ResolvedBridgeSettings,
2626
SelectedObfuscation, WireguardConstraints,
2727
},
28-
relay_list::{Relay, RelayList},
28+
relay_list::{Relay, RelayEndpointData, RelayList},
2929
settings::Settings,
3030
CustomTunnelEndpoint,
3131
};
@@ -201,10 +201,39 @@ pub enum GetRelay {
201201
/// will actually be routed to the broader internet.
202202
#[derive(Clone, Debug)]
203203
pub enum WireguardConfig {
204+
/// Strongly prefer to instantiate this variant using [`WireguardConfig::singlehop`] as that
205+
/// will assert that the relay is of the expected type.
204206
Singlehop { exit: Relay },
207+
/// Strongly prefer to instantiate this variant using [`WireguardConfig::multihop`] as that
208+
/// will assert that the entry & exit relays are of the expected type.
205209
Multihop { exit: Relay, entry: Relay },
206210
}
207211

212+
impl WireguardConfig {
213+
const fn singlehop(exit: Relay) -> Self {
214+
// FIXME: This assert would be better to encode at the type level.
215+
assert!(matches!(
216+
exit.endpoint_data,
217+
RelayEndpointData::Wireguard(_)
218+
));
219+
Self::Singlehop { exit }
220+
}
221+
222+
const fn multihop(exit: Relay, entry: Relay) -> Self {
223+
// FIXME: This assert would be better to encode at the type level.
224+
assert!(matches!(
225+
exit.endpoint_data,
226+
RelayEndpointData::Wireguard(_)
227+
));
228+
// FIXME: This assert would be better to encode at the type level.
229+
assert!(matches!(
230+
entry.endpoint_data,
231+
RelayEndpointData::Wireguard(_)
232+
));
233+
Self::Multihop { exit, entry }
234+
}
235+
}
236+
208237
#[derive(Clone, Debug)]
209238
pub enum SelectedBridge {
210239
Normal { settings: CustomProxy, relay: Relay },
@@ -454,7 +483,7 @@ impl RelaySelector {
454483
}
455484
SpecializedSelectorConfig::Normal(pure_config) => {
456485
let parsed_relays = &self.parsed_relays.lock().unwrap();
457-
Self::get_relay_inner(&query, parsed_relays, &pure_config)
486+
Self::get_relay_inner(query, parsed_relays, &pure_config)
458487
}
459488
}
460489
}
@@ -522,7 +551,7 @@ impl RelaySelector {
522551
runtime_params,
523552
user_preferences,
524553
);
525-
Self::get_relay_inner(&query, parsed_relays, &normal_config)
554+
Self::get_relay_inner(query, parsed_relays, &normal_config)
526555
}
527556
}
528557
}
@@ -568,7 +597,7 @@ impl RelaySelector {
568597
/// * An `Err` if no suitable bridge is found
569598
#[cfg(not(target_os = "android"))]
570599
fn get_relay_inner(
571-
query: &RelayQuery,
600+
query: RelayQuery,
572601
parsed_relays: &ParsedRelays,
573602
config: &NormalSelectorConfig<'_>,
574603
) -> Result<GetRelay, Error> {
@@ -585,7 +614,7 @@ impl RelaySelector {
585614
let mut new_query = query.clone();
586615
new_query.tunnel_protocol = Constraint::Only(tunnel_type);
587616
// If a suitable relay is found, short-circuit and return it
588-
if let Ok(relay) = Self::get_relay_inner(&new_query, parsed_relays, config) {
617+
if let Ok(relay) = Self::get_relay_inner(new_query, parsed_relays, config) {
589618
return Ok(relay);
590619
}
591620
}
@@ -596,15 +625,24 @@ impl RelaySelector {
596625

597626
#[cfg(target_os = "android")]
598627
fn get_relay_inner(
599-
query: &RelayQuery,
628+
mut query: RelayQuery,
600629
parsed_relays: &ParsedRelays,
601630
config: &NormalSelectorConfig<'_>,
602631
) -> Result<GetRelay, Error> {
632+
// FIXME: A bit of defensive programming - calling `get_wiregurad_relay` with a query that doesn't
633+
// specify Wireguard as the desired tunnel type is not valid and will lead to unwanted
634+
// behavior. This should be seen as a workaround, and it would be nicer to lift this
635+
// invariant to be checked by the type system instead.
636+
query.tunnel_protocol = Constraint::Only(TunnelType::Wireguard);
603637
Self::get_wireguard_relay(query, config, parsed_relays)
604638
}
605639

606640
/// Derive a valid Wireguard relay configuration from `query`.
607641
///
642+
/// # Note
643+
/// This function should *only* be called with a Wireguard query.
644+
/// It will panic if the tunnel type is not specified as Wireguard.
645+
///
608646
/// # Returns
609647
/// * An `Err` if no exit relay can be chosen
610648
/// * An `Err` if no entry relay can be chosen (if multihop is enabled on `query`)
@@ -613,18 +651,22 @@ impl RelaySelector {
613651
///
614652
/// [`MullvadEndpoint`]: mullvad_types::endpoint::MullvadEndpoint
615653
fn get_wireguard_relay(
616-
query: &RelayQuery,
654+
query: RelayQuery,
617655
config: &NormalSelectorConfig<'_>,
618656
parsed_relays: &ParsedRelays,
619657
) -> Result<GetRelay, Error> {
658+
assert_eq!(
659+
query.tunnel_protocol,
660+
Constraint::Only(TunnelType::Wireguard)
661+
);
620662
let inner = if !query.wireguard_constraints.multihop() {
621-
Self::get_wireguard_singlehop_config(query, config, parsed_relays)?
663+
Self::get_wireguard_singlehop_config(&query, config, parsed_relays)?
622664
} else {
623-
Self::get_wireguard_multihop_config(query, config, parsed_relays)?
665+
Self::get_wireguard_multihop_config(&query, config, parsed_relays)?
624666
};
625-
let endpoint = Self::get_wireguard_endpoint(query, parsed_relays, &inner)?;
667+
let endpoint = Self::get_wireguard_endpoint(&query, parsed_relays, &inner)?;
626668
let obfuscator =
627-
Self::get_wireguard_obfuscator(query, inner.clone(), &endpoint, parsed_relays)?;
669+
Self::get_wireguard_obfuscator(&query, inner.clone(), &endpoint, parsed_relays)?;
628670

629671
Ok(GetRelay::Wireguard {
630672
endpoint,
@@ -646,7 +688,8 @@ impl RelaySelector {
646688
let candidates =
647689
filter_matching_relay_list(query, parsed_relays.relays(), config.custom_lists);
648690
helpers::pick_random_relay(&candidates)
649-
.map(|exit| WireguardConfig::Singlehop { exit: exit.clone() })
691+
.cloned()
692+
.map(WireguardConfig::singlehop)
650693
.ok_or(Error::NoRelay)
651694
}
652695

@@ -700,10 +743,7 @@ impl RelaySelector {
700743
}
701744
.ok_or(Error::NoRelay)?;
702745

703-
Ok(WireguardConfig::Multihop {
704-
exit: exit.clone(),
705-
entry: entry.clone(),
706-
})
746+
Ok(WireguardConfig::multihop(exit.clone(), entry.clone()))
707747
}
708748

709749
/// Constructs a [`MullvadEndpoint`] with details for how to connect to `relay`.
@@ -754,6 +794,10 @@ impl RelaySelector {
754794

755795
/// Derive a valid OpenVPN relay configuration from `query`.
756796
///
797+
/// # Note
798+
/// This function should *only* be called with an OpenVPN query.
799+
/// It will panic if the tunnel type is not specified as OpenVPN.
800+
///
757801
/// # Returns
758802
/// * An `Err` if no exit relay can be chosen
759803
/// * An `Err` if no entry bridge can be chosen (if bridge mode is enabled on `query`)
@@ -763,16 +807,19 @@ impl RelaySelector {
763807
/// [`MullvadEndpoint`]: mullvad_types::endpoint::MullvadEndpoint
764808
#[cfg(not(target_os = "android"))]
765809
fn get_openvpn_relay(
766-
query: &RelayQuery,
810+
query: RelayQuery,
767811
config: &NormalSelectorConfig<'_>,
768812
parsed_relays: &ParsedRelays,
769813
) -> Result<GetRelay, Error> {
814+
assert_eq!(query.tunnel_protocol, Constraint::Only(TunnelType::OpenVpn));
770815
let exit =
771-
Self::choose_openvpn_relay(query, config, parsed_relays).ok_or(Error::NoRelay)?;
772-
let endpoint = Self::get_openvpn_endpoint(query, &exit, parsed_relays)?;
816+
Self::choose_openvpn_relay(&query, config, parsed_relays).ok_or(Error::NoRelay)?;
817+
let endpoint = Self::get_openvpn_endpoint(&query, &exit, parsed_relays)?;
773818
let bridge =
774-
Self::get_openvpn_bridge(query, &exit, &endpoint.protocol, parsed_relays, config)?;
819+
Self::get_openvpn_bridge(&query, &exit, &endpoint.protocol, parsed_relays, config)?;
775820

821+
// FIXME: This assert would be better to encode at the type level.
822+
assert!(matches!(exit.endpoint_data, RelayEndpointData::Openvpn));
776823
Ok(GetRelay::OpenVpn {
777824
endpoint,
778825
exit,
@@ -964,6 +1011,7 @@ impl RelaySelector {
9641011

9651012
/// # Returns
9661013
/// A randomly selected relay that meets the specified constraints, or `None` if no suitable relay is found.
1014+
#[cfg(not(target_os = "android"))]
9671015
fn choose_openvpn_relay(
9681016
query: &RelayQuery,
9691017
config: &NormalSelectorConfig<'_>,

mullvad-types/src/relay_constraints.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ impl GeographicLocationConstraint {
265265
GeographicLocationConstraint::Hostname(country.into(), city.into(), hostname.into())
266266
}
267267

268-
// TODO(markus): Document
268+
/// Check if `self` is _just_ a country. See [`GeographicLocationConstraint`] for more details.
269269
pub fn is_country(&self) -> bool {
270270
matches!(self, GeographicLocationConstraint::Country(_))
271271
}

mullvad-types/src/relay_list.rs

-9
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,6 @@ pub enum RelayEndpointData {
168168
Wireguard(WireguardRelayEndpointData),
169169
}
170170

171-
impl RelayEndpointData {
172-
pub fn unwrap_wireguard_ref(&self) -> &WireguardRelayEndpointData {
173-
if let RelayEndpointData::Wireguard(wg) = &self {
174-
return wg;
175-
}
176-
panic!("not a wireguard endpoint");
177-
}
178-
}
179-
180171
/// Data needed to connect to OpenVPN endpoints.
181172
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
182173
pub struct OpenVpnEndpointData {

0 commit comments

Comments
 (0)