1
1
//! This module contains various helper functions for the relay selector implementation.
2
2
3
- use std:: net:: { IpAddr , SocketAddr } ;
3
+ use std:: {
4
+ net:: { IpAddr , SocketAddr } ,
5
+ ops:: { RangeBounds , RangeInclusive } ,
6
+ } ;
4
7
5
8
use mullvad_types:: {
6
9
constraints:: Constraint ,
@@ -18,12 +21,10 @@ use crate::SelectedObfuscator;
18
21
19
22
/// Port ranges available for WireGuard relays that have extra IPs for Shadowsocks.
20
23
/// For relays that have no additional IPs, only ports provided by the relay list are available.
21
- const SHADOWSOCKS_EXTRA_PORT_RANGES : & [ ( u16 , u16 ) ] = & [ ( 1 , u16:: MAX ) ] ;
24
+ const SHADOWSOCKS_EXTRA_PORT_RANGES : & [ RangeInclusive < u16 > ] = & [ 1 ..= u16:: MAX ] ;
22
25
23
26
#[ derive( thiserror:: Error , Debug ) ]
24
27
pub enum Error {
25
- #[ error( "Port selection algorithm is broken" ) ]
26
- PortSelectionAlgorithm ,
27
28
#[ error( "Found no valid port matching the selected settings" ) ]
28
29
NoMatchingPort ,
29
30
}
@@ -107,7 +108,7 @@ fn get_udp2tcp_obfuscator_port(
107
108
108
109
pub fn get_shadowsocks_obfuscator (
109
110
settings : & ShadowsocksSettings ,
110
- non_extra_port_ranges : & [ ( u16 , u16 ) ] ,
111
+ non_extra_port_ranges : & [ RangeInclusive < u16 > ] ,
111
112
relay : Relay ,
112
113
endpoint : & MullvadWireguardEndpoint ,
113
114
) -> Result < SelectedObfuscator , Error > {
@@ -135,9 +136,9 @@ pub fn get_shadowsocks_obfuscator(
135
136
/// Return an obfuscation config for the wireguard server at `wg_in_addr` or one of `extra_in_addrs`
136
137
/// (unless empty). `wg_in_addr_port_ranges` contains all valid ports for `wg_in_addr`, and
137
138
/// `SHADOWSOCKS_EXTRA_PORT_RANGES` contains valid ports for `extra_in_addrs`.
138
- fn get_shadowsocks_obfuscator_inner (
139
+ fn get_shadowsocks_obfuscator_inner < R : RangeBounds < u16 > + Iterator < Item = u16 > + Clone > (
139
140
wg_in_addr : IpAddr ,
140
- wg_in_addr_port_ranges : & [ ( u16 , u16 ) ] ,
141
+ wg_in_addr_port_ranges : & [ R ] ,
141
142
extra_in_addrs : & [ IpAddr ] ,
142
143
desired_port : Constraint < u16 > ,
143
144
) -> Result < SocketAddr , Error > {
@@ -154,23 +155,27 @@ fn get_shadowsocks_obfuscator_inner(
154
155
. copied ( )
155
156
. unwrap_or ( wg_in_addr) ;
156
157
157
- let port_ranges = if extra_in_addrs. is_empty ( ) {
158
- wg_in_addr_port_ranges
158
+ let selected_port = if extra_in_addrs. is_empty ( ) {
159
+ desired_port_from_range ( wg_in_addr_port_ranges, desired_port )
159
160
} else {
160
- SHADOWSOCKS_EXTRA_PORT_RANGES
161
- } ;
161
+ desired_port_from_range ( SHADOWSOCKS_EXTRA_PORT_RANGES , desired_port)
162
+ } ?;
163
+
164
+ Ok ( SocketAddr :: from ( ( in_ip, selected_port) ) )
165
+ }
162
166
163
- let selected_port = match desired_port {
167
+ fn desired_port_from_range < R : RangeBounds < u16 > + Iterator < Item = u16 > + Clone > (
168
+ port_ranges : & [ R ] ,
169
+ desired_port : Constraint < u16 > ,
170
+ ) -> Result < u16 , Error > {
171
+ match desired_port {
164
172
// Selected a specific, in-range port
165
- Constraint :: Only ( port) if super :: helpers :: port_in_range ( port, port_ranges) => Some ( port) ,
173
+ Constraint :: Only ( port) if port_in_range ( port, port_ranges) => Ok ( port) ,
166
174
// Selected a specific, out-of-range port
167
- Constraint :: Only ( _port) => None ,
175
+ Constraint :: Only ( _port) => Err ( Error :: NoMatchingPort ) ,
168
176
// Selected no specific port
169
- Constraint :: Any => super :: helpers :: select_random_port ( port_ranges) . ok ( ) ,
177
+ Constraint :: Any => select_random_port ( port_ranges) ,
170
178
}
171
- . ok_or ( Error :: NoMatchingPort ) ?;
172
-
173
- Ok ( SocketAddr :: from ( ( in_ip, selected_port) ) )
174
179
}
175
180
176
181
/// Selects a random port number from a list of provided port ranges.
@@ -185,45 +190,32 @@ fn get_shadowsocks_obfuscator_inner(
185
190
///
186
191
/// # Returns
187
192
/// - A randomly selected port number within the given ranges.
188
- ///
189
- /// # Panic
190
- /// - If port ranges contains no ports, this function panics.
191
- pub fn select_random_port ( port_ranges : & [ ( u16 , u16 ) ] ) -> Result < u16 , Error > {
192
- let get_port_amount = |range : & ( u16 , u16 ) | -> u64 { 1 + range. 1 as u64 - range. 0 as u64 } ;
193
- let port_amount: u64 = port_ranges. iter ( ) . map ( get_port_amount) . sum ( ) ;
194
-
195
- if port_amount < 1 {
196
- return Err ( Error :: PortSelectionAlgorithm ) ;
197
- }
198
-
199
- let mut port_index = rand:: thread_rng ( ) . gen_range ( 0 ..port_amount) ;
200
-
201
- for range in port_ranges. iter ( ) {
202
- let ports_in_range = get_port_amount ( range) ;
203
- if port_index < ports_in_range {
204
- return Ok ( port_index as u16 + range. 0 ) ;
205
- }
206
- port_index -= ports_in_range;
207
- }
208
- Err ( Error :: PortSelectionAlgorithm )
209
- }
210
-
211
- pub fn port_in_range ( port : u16 , port_ranges : & [ ( u16 , u16 ) ] ) -> bool {
193
+ /// - An error if `port_ranges` is empty.
194
+ pub fn select_random_port < R : RangeBounds < u16 > + Iterator < Item = u16 > + Clone > (
195
+ port_ranges : & [ R ] ,
196
+ ) -> Result < u16 , Error > {
212
197
port_ranges
213
198
. iter ( )
214
- . any ( |range| ( range. 0 <= port && port <= range. 1 ) )
199
+ . cloned ( )
200
+ . flatten ( )
201
+ . choose ( & mut rand:: thread_rng ( ) )
202
+ . ok_or ( Error :: NoMatchingPort )
203
+ }
204
+
205
+ pub fn port_in_range < R : RangeBounds < u16 > > ( port : u16 , port_ranges : & [ R ] ) -> bool {
206
+ port_ranges. iter ( ) . any ( |range| range. contains ( & port) )
215
207
}
216
208
217
209
#[ cfg( test) ]
218
210
mod tests {
219
211
use super :: { get_shadowsocks_obfuscator_inner, port_in_range, SHADOWSOCKS_EXTRA_PORT_RANGES } ;
220
212
use mullvad_types:: constraints:: Constraint ;
221
- use std:: net:: IpAddr ;
213
+ use std:: { net:: IpAddr , ops :: RangeInclusive } ;
222
214
223
215
/// Test whether select ports are available when relay has no extra IPs
224
216
#[ test]
225
217
fn test_shadowsocks_no_extra_addrs ( ) {
226
- const PORT_RANGES : & [ ( u16 , u16 ) ] = & [ ( 100 , 200 ) , ( 1000 , 2000 ) ] ;
218
+ const PORT_RANGES : & [ RangeInclusive < u16 > ] = & [ 100 ..= 200 , 1000 ..= 2000 ] ;
227
219
const WITHIN_RANGE_PORT : u16 = 100 ;
228
220
const OUT_OF_RANGE_PORT : u16 = 1 ;
229
221
let wg_in_ip: IpAddr = "1.2.3.4" . parse ( ) . unwrap ( ) ;
@@ -267,7 +259,7 @@ mod tests {
267
259
/// All ports should be available when relay has extra IPs, and only extra IPs should be used
268
260
#[ test]
269
261
fn test_shadowsocks_extra_addrs ( ) {
270
- const PORT_RANGES : & [ ( u16 , u16 ) ] = & [ ( 100 , 200 ) , ( 1000 , 2000 ) ] ;
262
+ const PORT_RANGES : & [ RangeInclusive < u16 > ] = & [ 100 ..= 200 , 1000 ..= 2000 ] ;
271
263
const OUT_OF_RANGE_PORT : u16 = 1 ;
272
264
let wg_in_ip: IpAddr = "1.2.3.4" . parse ( ) . unwrap ( ) ;
273
265
@@ -312,7 +304,7 @@ mod tests {
312
304
/// Extra addresses that belong to the wrong IP family should be ignored
313
305
#[ test]
314
306
fn test_shadowsocks_irrelevant_extra_addrs ( ) {
315
- const PORT_RANGES : & [ ( u16 , u16 ) ] = & [ ( 100 , 200 ) , ( 1000 , 2000 ) ] ;
307
+ const PORT_RANGES : & [ RangeInclusive < u16 > ] = & [ 100 ..= 200 , 1000 ..= 2000 ] ;
316
308
const IN_RANGE_PORT : u16 = 100 ;
317
309
const OUT_OF_RANGE_PORT : u16 = 1 ;
318
310
let wg_in_ip: IpAddr = "1.2.3.4" . parse ( ) . unwrap ( ) ;
0 commit comments