Skip to content

Commit 0286fdd

Browse files
committed
Simplify random port selection
1 parent 6ea0db9 commit 0286fdd

File tree

3 files changed

+41
-39
lines changed

3 files changed

+41
-39
lines changed

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

+2-9
Original file line numberDiff line numberDiff line change
@@ -180,15 +180,8 @@ fn get_port_for_wireguard_relay(
180180
query: &WireguardRelayQuery,
181181
data: &WireguardEndpointData,
182182
) -> Result<u16, Error> {
183-
if let Constraint::Only(port) = query.port {
184-
if super::helpers::port_in_range(port, &data.port_ranges) {
185-
Ok(port)
186-
} else {
187-
Err(Error::PortSelectionError)
188-
}
189-
} else {
190-
super::helpers::select_random_port(&data.port_ranges).map_err(|_| Error::PortSelectionError)
191-
}
183+
super::helpers::desired_or_random_port_from_range(&data.port_ranges, query.port)
184+
.map_err(|_err| Error::PortSelectionError)
192185
}
193186

194187
/// Read the [`PublicKey`] of a relay. This will only succeed if [relay][`Relay`] is a

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

+32-30
Original file line numberDiff line numberDiff line change
@@ -156,41 +156,48 @@ fn get_shadowsocks_obfuscator_inner<R: RangeBounds<u16> + Iterator<Item = u16> +
156156
.unwrap_or(wg_in_addr);
157157

158158
let selected_port = if extra_in_addrs.is_empty() {
159-
desired_port_from_range(wg_in_addr_port_ranges, desired_port)
159+
desired_or_random_port_from_range(wg_in_addr_port_ranges, desired_port)
160160
} else {
161-
desired_port_from_range(SHADOWSOCKS_EXTRA_PORT_RANGES, desired_port)
161+
desired_or_random_port_from_range(SHADOWSOCKS_EXTRA_PORT_RANGES, desired_port)
162162
}?;
163163

164164
Ok(SocketAddr::from((in_ip, selected_port)))
165165
}
166166

167-
fn desired_port_from_range<R: RangeBounds<u16> + Iterator<Item = u16> + Clone>(
167+
/// Return `desired_port` if it is specified and included in `port_ranges`.
168+
/// If `desired_port` isn't specified, a random port from the ranges is returned.
169+
/// If `desired_port` is specified but not in range, an error is returned.
170+
pub fn desired_or_random_port_from_range<R: RangeBounds<u16> + Iterator<Item = u16> + Clone>(
168171
port_ranges: &[R],
169172
desired_port: Constraint<u16>,
170173
) -> Result<u16, Error> {
171-
match desired_port {
172-
// Selected a specific, in-range port
173-
Constraint::Only(port) if port_in_range(port, port_ranges) => Ok(port),
174-
// Selected a specific, out-of-range port
175-
Constraint::Only(_port) => Err(Error::NoMatchingPort),
176-
// Selected no specific port
177-
Constraint::Any => select_random_port(port_ranges),
178-
}
174+
desired_port
175+
.map(|port| port_if_in_range(port_ranges, port))
176+
.unwrap_or_else(|| select_random_port(port_ranges))
177+
}
178+
179+
/// Return `Ok(port)`, if and only if `port` is in `port_ranges`. Otherwise, return an error.
180+
fn port_if_in_range<R: RangeBounds<u16>>(port_ranges: &[R], port: u16) -> Result<u16, Error> {
181+
port_ranges
182+
.iter()
183+
.find_map(|range| {
184+
if range.contains(&port) {
185+
Some(port)
186+
} else {
187+
None
188+
}
189+
})
190+
.ok_or(Error::NoMatchingPort)
179191
}
180192

181193
/// Selects a random port number from a list of provided port ranges.
182194
///
183-
/// This function iterates over a list of port ranges, each represented as a tuple (u16, u16)
184-
/// where the first element is the start of the range and the second is the end (inclusive),
185-
/// and selects a random port from the set of all ranges.
186-
///
187195
/// # Parameters
188-
/// - `port`: Constraint to apply to the port selection
189-
/// - `port_ranges`: A slice of tuples, each representing a range of valid port numbers.
196+
/// - `port_ranges`: A slice of port numbers.
190197
///
191198
/// # Returns
192-
/// - A randomly selected port number within the given ranges.
193-
/// - An error if `port_ranges` is empty.
199+
/// - On success, a randomly selected port number within the given ranges. Otherwise,
200+
/// an error is returned.
194201
pub fn select_random_port<R: RangeBounds<u16> + Iterator<Item = u16> + Clone>(
195202
port_ranges: &[R],
196203
) -> Result<u16, Error> {
@@ -202,13 +209,11 @@ pub fn select_random_port<R: RangeBounds<u16> + Iterator<Item = u16> + Clone>(
202209
.ok_or(Error::NoMatchingPort)
203210
}
204211

205-
pub fn port_in_range<R: RangeBounds<u16>>(port: u16, port_ranges: &[R]) -> bool {
206-
port_ranges.iter().any(|range| range.contains(&port))
207-
}
208-
209212
#[cfg(test)]
210213
mod tests {
211-
use super::{get_shadowsocks_obfuscator_inner, port_in_range, SHADOWSOCKS_EXTRA_PORT_RANGES};
214+
use super::{
215+
get_shadowsocks_obfuscator_inner, port_if_in_range, SHADOWSOCKS_EXTRA_PORT_RANGES,
216+
};
212217
use mullvad_types::constraints::Constraint;
213218
use std::{net::IpAddr, ops::RangeInclusive};
214219

@@ -226,7 +231,7 @@ mod tests {
226231

227232
assert_eq!(selected_addr.ip(), wg_in_ip);
228233
assert!(
229-
port_in_range(selected_addr.port(), PORT_RANGES),
234+
port_if_in_range(PORT_RANGES, selected_addr.port()).is_ok(),
230235
"expected port in port range"
231236
);
232237

@@ -240,7 +245,7 @@ mod tests {
240245

241246
assert_eq!(selected_addr.ip(), wg_in_ip);
242247
assert!(
243-
port_in_range(selected_addr.port(), PORT_RANGES),
248+
port_if_in_range(PORT_RANGES, selected_addr.port()).is_ok(),
244249
"expected port in port range"
245250
);
246251

@@ -278,10 +283,7 @@ mod tests {
278283
extra_in_addrs.contains(&selected_addr.ip()),
279284
"expected extra IP to be selected"
280285
);
281-
assert!(port_in_range(
282-
selected_addr.port(),
283-
SHADOWSOCKS_EXTRA_PORT_RANGES
284-
));
286+
assert!(port_if_in_range(SHADOWSOCKS_EXTRA_PORT_RANGES, selected_addr.port(),).is_ok());
285287

286288
let selected_addr = get_shadowsocks_obfuscator_inner(
287289
wg_in_ip,

mullvad-types/src/constraints/constraint.rs

+7
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,13 @@ impl<T> Constraint<T> {
5656
}
5757
}
5858

59+
pub fn unwrap_or_else<F: FnOnce() -> T>(self, or_else: F) -> T {
60+
match self {
61+
Constraint::Only(value) => value,
62+
Constraint::Any => or_else(),
63+
}
64+
}
65+
5966
pub fn or(self, other: Constraint<T>) -> Constraint<T> {
6067
match self {
6168
Constraint::Any => other,

0 commit comments

Comments
 (0)