Skip to content

Commit

Permalink
Fail loudly if no bridge was selected
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkusPettersson98 committed Apr 8, 2024
1 parent de28009 commit e2e7310
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 26 deletions.
3 changes: 3 additions & 0 deletions mullvad-relay-selector/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ pub enum Error {
relay: EndpointErrorDetails,
},

#[error("No candidates remain")]
NoCandidates,

#[error("Failure in serialization of the relay list")]
Serialize(#[from] serde_json::Error),

Expand Down
27 changes: 14 additions & 13 deletions mullvad-relay-selector/src/relay_selector/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,20 @@ pub fn pick_random_bridge(data: &BridgeEndpointData, relay: &Relay) -> Option<Cu
if relay.endpoint_data != RelayEndpointData::Bridge {
return None;
}
let shadowsocks_endpoint = data.shadowsocks.choose(&mut rand::thread_rng());
if let Some(shadowsocks_endpoint) = shadowsocks_endpoint {
log::info!(
"Selected Shadowsocks bridge {} at {}:{}/{}",
relay.hostname,
relay.ipv4_addr_in,
shadowsocks_endpoint.port,
shadowsocks_endpoint.protocol
);
Some(shadowsocks_endpoint.to_proxy_settings(relay.ipv4_addr_in.into()))
} else {
None
}
data.shadowsocks
.choose(&mut rand::thread_rng())
.inspect(|shadowsocks_endpoint| {
log::info!(
"Selected Shadowsocks bridge {} at {}:{}/{}",
relay.hostname,
relay.ipv4_addr_in,
shadowsocks_endpoint.port,
shadowsocks_endpoint.protocol
);
})
.map(|shadowsocks_endpoint| {
shadowsocks_endpoint.to_proxy_settings(relay.ipv4_addr_in.into())
})
}

pub fn get_udp2tcp_obfuscator(
Expand Down
37 changes: 24 additions & 13 deletions mullvad-relay-selector/src/relay_selector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,8 @@ impl RelaySelector {
let custom_lists = &config.custom_lists;
Self::get_proxy_settings(parsed_relays, &constraints, near_location, custom_lists)
.map(|(settings, _relay)| settings)
.inspect_err(|error| log::error!("Failed to get bridge: {error}"))
.ok()
}

/// Returns random relay and relay endpoint matching `query`.
Expand Down Expand Up @@ -892,26 +894,28 @@ impl RelaySelector {
}
TransportProtocol::Tcp => {
let location = relay.location.as_ref().ok_or(Error::NoRelay)?;
Ok(Self::get_bridge_for(
Self::get_bridge_for(
bridge_query,
location,
// FIXME: This is temporary while talpid-core only supports TCP proxies
TransportProtocol::Tcp,
parsed_relays,
custom_lists,
))
)
.transpose()
}
}
}
}

// TODO(markus): Document return value
fn get_bridge_for(
query: &BridgeQuery,
location: &Location,
transport_protocol: TransportProtocol,
parsed_relays: &ParsedRelays,
custom_lists: &CustomListsSettings,
) -> Option<SelectedBridge> {
) -> Option<Result<SelectedBridge, Error>> {
match query {
BridgeQuery::Normal(settings) => {
let bridge_constraints = InternalBridgeConstraints {
Expand All @@ -921,15 +925,18 @@ impl RelaySelector {
transport_protocol: Constraint::Only(transport_protocol),
};

Self::get_proxy_settings(
let bridge = Self::get_proxy_settings(
parsed_relays,
&bridge_constraints,
Some(location),
custom_lists,
)
.map(|(settings, relay)| SelectedBridge::Normal { settings, relay })
.map(|(settings, relay)| SelectedBridge::Normal { settings, relay });
Some(bridge)
}
BridgeQuery::Custom(settings) => {
settings.clone().map(SelectedBridge::Custom).map(Result::Ok)
}
BridgeQuery::Custom(settings) => settings.clone().map(SelectedBridge::Custom),
BridgeQuery::Off | BridgeQuery::Auto => None,
}
}
Expand All @@ -942,22 +949,24 @@ impl RelaySelector {
constraints: &InternalBridgeConstraints,
location: Option<T>,
custom_lists: &CustomListsSettings,
) -> Option<(CustomProxy, Relay)> {
) -> Result<(CustomProxy, Relay), Error> {
let bridges = filter_matching_bridges(constraints, parsed_relays.relays(), custom_lists);
let bridge_data = &parsed_relays.parsed_list().bridge;
let bridge = match location {
Some(location) => Self::get_proximate_bridge(bridges, location),
None => helpers::pick_random_relay(&bridges).cloned(),
None => helpers::pick_random_relay(&bridges)
.cloned()
.ok_or(Error::NoRelay),
}?;

let bridge_data = &parsed_relays.parsed_list().bridge;
helpers::pick_random_bridge(bridge_data, &bridge).map(|endpoint| (endpoint, bridge.clone()))
let endpoint = helpers::pick_random_bridge(bridge_data, &bridge).ok_or(Error::NoBridge)?;
Ok((endpoint, bridge))
}

/// Try to get a bridge which is close to `location`.
fn get_proximate_bridge<T: Into<Coordinates>>(
relays: Vec<Relay>,
location: T,
) -> Option<Relay> {
) -> Result<Relay, Error> {
/// Number of bridges to keep for selection by distance.
const MIN_BRIDGE_COUNT: usize = 5;
let location = location.into();
Expand All @@ -983,13 +992,15 @@ impl RelaySelector {
let greatest_distance: f64 = matching_bridges
.iter()
.map(|relay| relay.distance)
.reduce(f64::max)?;
.reduce(f64::max)
.ok_or(Error::NoCandidates)?;
// Define the weight function to prioritize bridges which are closer to `location`.
let weight_fn = |relay: &RelayWithDistance| 1 + (greatest_distance - relay.distance) as u64;

helpers::pick_random_relay_weighted(&matching_bridges, weight_fn)
.cloned()
.map(|relay_with_distance| relay_with_distance.relay)
.ok_or(Error::NoBridge)
}

/// Returns the average location of relays that match the given constraints.
Expand Down

0 comments on commit e2e7310

Please sign in to comment.