1
//! Define different restrictions that can be applied to relays.
2

            
3
#[cfg(feature = "geoip")]
4
use tor_geoip::HasCountryCode;
5
use tor_linkspec::{ChanTarget, HasAddrs, HasRelayIds, RelayIdSet};
6
use tor_netdir::{NetDir, Relay, SubnetConfig};
7
use tor_netdoc::types::policy::AddrPortPattern;
8

            
9
use crate::{LowLevelRelayPredicate, RelaySelectionConfig, RelayUsage};
10
use std::{fmt, net::IpAddr};
11

            
12
/// A restriction that we use when picking relays.
13
///
14
/// Differs from [`RelayUsage`] in that it does not say what
15
/// the relay is _used for_;
16
/// instead, it describes an additional set of requirements that a relay must
17
/// satisfy.
18
#[derive(Clone, Debug)]
19
pub struct RelayRestriction<'a> {
20
    /// The actual restriction object.
21
    inner: RestrictionInner<'a>,
22
}
23

            
24
/// Enumeration of possible [`RelayRestriction`]s.
25
///
26
/// This is a separate type so that we can hide its variants.
27
///
28
// TODO: I'm not sure about having this be relative to `'a``,
29
// but that is the only way to hold a `Relay<'a>`
30
//
31
// NOTE: Any time that you are extending this type, make sure that you are not
32
// describing a new _mandatory_ restriction that all `RelaySelector` users
33
// need to consider adding (or not).  If you *are* describing such a restriction,
34
// then it should have its own type, and it should become a new argument to
35
// RelaySelector::new().
36
#[derive(Clone, Debug)]
37
enum RestrictionInner<'a> {
38
    /// Do not restrict any relays.
39
    ///
40
    /// This is present so that we can construct a no-op restriction when
41
    /// relaxing a selector.
42
    NoRestriction,
43
    /// Require a given usage.
44
    SupportsUsage(crate::RelayUsage),
45
    /// Exclude a set of relays explicitly, by family, or by identity.
46
    Exclude(RelayExclusion<'a>),
47
    /// Require that, if the relay's contact method uses addresses, the relay
48
    /// has at least one address matching one of the provided patterns.
49
    HasAddrInSet(Vec<AddrPortPattern>),
50
    /// Require that the relay has a given country code.
51
    #[cfg(feature = "geoip")]
52
    RequireCountry(tor_geoip::CountryCode),
53
}
54

            
55
impl<'a> RelayRestriction<'a> {
56
    /// Create a restriction that allows every relay.
57
2
    pub(crate) fn no_restriction() -> Self {
58
2
        RelayRestriction {
59
2
            inner: RestrictionInner::NoRestriction,
60
2
        }
61
2
    }
62

            
63
    /// Convert a usage into a restriction.
64
    ///
65
    /// This is crate-internal since we never want to support requiring a relay
66
    /// to provide multiple usages.
67
621876
    pub(crate) fn for_usage(usage: crate::RelayUsage) -> Self {
68
621876
        RelayRestriction {
69
621876
            inner: RestrictionInner::SupportsUsage(usage),
70
621876
        }
71
621876
    }
72

            
73
    /// Require a relay that appears to be in the provided country,
74
    /// according ot our geoip subsystem.
75
    #[cfg(feature = "geoip")]
76
    pub fn require_country_code(cc: tor_geoip::CountryCode) -> Self {
77
        RelayRestriction {
78
            inner: RestrictionInner::RequireCountry(cc),
79
        }
80
    }
81

            
82
    /// Require that a relay has at least one address
83
    /// listed in `addr_patterns`.
84
290
    pub fn require_address(addr_patterns: Vec<AddrPortPattern>) -> Self {
85
290
        // TODO: It's plausible that this restriction should be mandatory
86
290
        // whenever we are picking new guards.
87
290
        RelayRestriction {
88
290
            inner: RestrictionInner::HasAddrInSet(addr_patterns),
89
290
        }
90
290
    }
91

            
92
    /// Return a restriction that represents having "relaxed" this restriction.
93
    ///
94
    /// (Relaxing a restriction replaces it with a no-op, or with an almost-no-op.)
95
290
    pub(crate) fn relax(&self) -> Self {
96
        use RestrictionInner::*;
97
290
        match &self.inner {
98
            // We must always have a usage, so relaxing a usage must always
99
            // return a usage.
100
288
            SupportsUsage(usage) => Self::for_usage(RelayUsage::middle_relay(Some(usage))),
101
            // Relaxing any other restriction returns a no-op
102
2
            _ => Self::no_restriction(),
103
        }
104
290
    }
105

            
106
    /// If this restriction represents a usage, return a reference to that usage.
107
920766
    pub(crate) fn as_usage(&self) -> Option<&RelayUsage> {
108
        use RestrictionInner::*;
109
920766
        match &self.inner {
110
920766
            SupportsUsage(usage) => Some(usage),
111
            _ => None,
112
        }
113
920766
    }
114

            
115
    /// Return a string describing why we rejected the relays that _don't_ match
116
    /// this restriction.
117
1024
    pub(crate) fn rejection_description(&self) -> Option<&'static str> {
118
        use RestrictionInner::*;
119
1024
        match &self.inner {
120
            NoRestriction => None,
121
512
            SupportsUsage(u) => Some(u.rejection_description()),
122
512
            Exclude(e) => e.rejection_description(),
123
            HasAddrInSet(_) => Some("not reachable (according to address filter)"),
124
            #[cfg(feature = "geoip")]
125
            RequireCountry(_) => Some("not in correct country"),
126
        }
127
1024
    }
128
}
129

            
130
impl<'a> LowLevelRelayPredicate for RelayRestriction<'a> {
131
40806736
    fn low_level_predicate_permits_relay(&self, relay: &tor_netdir::Relay<'_>) -> bool {
132
        use RestrictionInner::*;
133
40806736
        match &self.inner {
134
56
            NoRestriction => true,
135
24172496
            SupportsUsage(usage) => usage.low_level_predicate_permits_relay(relay),
136
16631512
            Exclude(exclusion) => exclusion.low_level_predicate_permits_relay(relay),
137
2672
            HasAddrInSet(patterns) => relay_has_addr_in_set(relay, patterns),
138
            #[cfg(feature = "geoip")]
139
            RequireCountry(cc) => relay.country_code() == Some(*cc),
140
        }
141
40806736
    }
142
}
143

            
144
impl<'a> From<RelayExclusion<'a>> for RelayRestriction<'a> {
145
621588
    fn from(value: RelayExclusion<'a>) -> Self {
146
621588
        RelayRestriction {
147
621588
            inner: RestrictionInner::Exclude(value),
148
621588
        }
149
621588
    }
150
}
151

            
152
/// Return true if `relay` has at least one address matching at least one member
153
/// of `patterns`.
154
2672
fn relay_has_addr_in_set(relay: &Relay<'_>, patterns: &[AddrPortPattern]) -> bool {
155
2672
    // NOTE: If we ever make this apply to ChanTarget instead of Relay, we will
156
2672
    // need it to call chan_method().socket_addrs() instead, and handle the case
157
2672
    // where the transport doesn't use an address.
158
2672
    relay
159
2672
        .addrs()
160
2672
        .iter()
161
2848
        .any(|addr| patterns.iter().any(|pat| pat.matches_sockaddr(addr)))
162
2672
}
163

            
164
/// A set of relays that we must not use when picking a given
165
/// relays.
166
///
167
/// Exclusions are generally used to make sure that we obey
168
/// family-based path-selection rules,
169
/// that we avoid putting the same relay into a set more than once,
170
/// or similar purposes.
171
///
172
/// (This is a separate type from [`RelayRestriction`] so that we can
173
/// enforce our rule that every [`RelaySelector`](crate::RelaySelector) must
174
/// have a `RelayExclusion`.)
175
#[derive(Clone, Debug)]
176
pub struct RelayExclusion<'a> {
177
    /// A list of identities to exclude.
178
    ///
179
    /// Any relay with any one of these identities is rejecteed.
180
    exclude_ids: RelayIdSet,
181
    /// A list of subnets from which to exclude addresses.
182
    ///
183
    /// The side of the subnet is determined by subnet_config.
184
    exclude_subnets: Vec<IpAddr>,
185
    /// A list of relays to exclude, along with their families.
186
    exclude_relay_families: RelayList<'a>,
187
    /// The configuration to use when deciding whether two addresses are in the
188
    /// same subnet.
189
    subnet_config: SubnetConfig,
190
}
191

            
192
/// Helper: wraps `Vec[Relay]`, but implements Debug.
193
#[derive(Clone)]
194
struct RelayList<'a>(Vec<Relay<'a>>);
195
impl<'a> fmt::Debug for RelayList<'a> {
196
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197
        write!(f, "[ ")?;
198
        for r in &self.0 {
199
            write!(f, "{}, ", r.display_relay_ids())?;
200
        }
201
        write!(f, "]")
202
    }
203
}
204

            
205
impl<'a> RelayExclusion<'a> {
206
    /// Exclude no relays at all.
207
    ///
208
    /// This kind of restriction is useful when picking the first relay for
209
    /// something,
210
    ///
211
    // (Note that this is _not_ Default::default, since we don't want people
212
    // picking it by mistake.)
213
1796564
    pub fn no_relays_excluded() -> Self {
214
1796564
        RelayExclusion {
215
1796564
            exclude_ids: RelayIdSet::new(),
216
1796564
            exclude_subnets: Vec::new(),
217
1796564
            exclude_relay_families: RelayList(Vec::new()),
218
1796564
            subnet_config: SubnetConfig::no_addresses_match(),
219
1796564
        }
220
1796564
    }
221

            
222
    /// Exclude every relay that has an identity in `ids`.
223
29460
    pub fn exclude_identities(ids: RelayIdSet) -> Self {
224
29460
        RelayExclusion {
225
29460
            exclude_ids: ids,
226
29460
            ..RelayExclusion::no_relays_excluded()
227
29460
        }
228
29460
    }
229

            
230
    /// Exclude every relay that appears in `relays`.
231
2
    pub fn exclude_specific_relays(relays: &[Relay<'a>]) -> Self {
232
2
        let ids: RelayIdSet = relays
233
2
            .iter()
234
2
            .flat_map(Relay::identities)
235
9
            .map(|id_ref| id_ref.to_owned())
236
2
            .collect();
237
2

            
238
2
        Self::exclude_identities(ids)
239
2
    }
240

            
241
    /// Try to exclude every relay in the same family as the [`ChanTarget`]
242
    /// `ct`.
243
    ///
244
    /// # Limitations
245
    ///
246
    /// A ChanTarget does not have a listed family.  Thus, if it does not correspond
247
    /// to a relay listed in `netdir`, we can only exclude relays that share the
248
    /// same identity, or relays that are in the same subnet.
249
    ///
250
    /// Whenever possible, it's better to use exclude_relays_in_same_family.
251
    pub fn exclude_channel_target_family<CT: ChanTarget>(
252
        cfg: &RelaySelectionConfig,
253
        ct: &CT,
254
        netdir: &'a NetDir,
255
    ) -> Self {
256
        if let Some(r) = netdir.by_ids(ct) {
257
            return Self::exclude_relays_in_same_family(cfg, vec![r]);
258
        }
259

            
260
        let exclude_ids = ct.identities().map(|id_ref| id_ref.to_owned()).collect();
261
        let exclude_addr_families = ct.addrs().iter().map(|a| a.ip()).collect();
262

            
263
        Self {
264
            exclude_ids,
265
            exclude_subnets: exclude_addr_families,
266
            subnet_config: cfg.subnet_config,
267
            ..Self::no_relays_excluded()
268
        }
269
    }
270

            
271
    /// Exclude every relay that is in the same family as any member of
272
    /// `relays`.
273
    ///
274
    /// (Remember that every relay is considered to be in the same family as
275
    /// itself, so you don't typically need to use `exclude_specific_relays`
276
    /// along with this.)
277
    ///
278
    /// Considers relays that are in the same subnets (according to `cfg`) to
279
    /// belong to the same family.
280
1470354
    pub fn exclude_relays_in_same_family(
281
1470354
        cfg: &RelaySelectionConfig,
282
1470354
        relays: Vec<Relay<'a>>,
283
1470354
    ) -> Self {
284
1470354
        RelayExclusion {
285
1470354
            exclude_relay_families: RelayList(relays),
286
1470354
            subnet_config: cfg.subnet_config,
287
1470354
            ..RelayExclusion::no_relays_excluded()
288
1470354
        }
289
1470354
    }
290

            
291
    /// Modify this `RelayExclusion` by adding every exclusion from `other`.
292
    ///
293
    /// (Any subnet configuration becomes the _union_ of previous subnet
294
    /// configurations.)
295
898632
    pub fn extend(&mut self, other: &RelayExclusion<'a>) {
296
898632
        let RelayExclusion {
297
898632
            exclude_ids,
298
898632
            exclude_subnets: exclude_addr_families,
299
898632
            exclude_relay_families,
300
898632
            subnet_config,
301
898632
        } = other;
302
898632
        self.exclude_ids
303
899052
            .extend(exclude_ids.iter().map(|id_ref| id_ref.to_owned()));
304
898632
        self.exclude_subnets
305
898632
            .extend_from_slice(&exclude_addr_families[..]);
306
898632
        self.exclude_relay_families
307
898632
            .0
308
898632
            .extend_from_slice(&exclude_relay_families.0[..]);
309
898632
        self.subnet_config = self.subnet_config.union(subnet_config);
310
898632
    }
311

            
312
    /// Return a string describing why we rejected the relays that _don't_ match
313
    /// this exclusion.
314
512
    pub(crate) fn rejection_description(&self) -> Option<&'static str> {
315
512
        if self.exclude_relay_families.0.is_empty() && self.exclude_subnets.is_empty() {
316
148
            if self.exclude_ids.is_empty() {
317
                None
318
            } else {
319
148
                Some("already selected")
320
            }
321
        } else {
322
364
            Some("in same family as already selected")
323
        }
324
512
    }
325
}
326

            
327
impl<'a> LowLevelRelayPredicate for RelayExclusion<'a> {
328
17512672
    fn low_level_predicate_permits_relay(&self, relay: &Relay<'_>) -> bool {
329
35423730
        if relay.identities().any(|id| self.exclude_ids.contains(id)) {
330
98824
            return false;
331
17413848
        }
332
17413848

            
333
17900507
        if relay.addrs().iter().any(|addr| {
334
17413848
            self.exclude_subnets
335
17413848
                .iter()
336
17413848
                .any(|fam| self.subnet_config.addrs_in_same_subnet(&addr.ip(), fam))
337
17900507
        }) {
338
            return false;
339
17413848
        }
340
17413848

            
341
17413848
        if self
342
17413848
            .exclude_relay_families
343
17413848
            .0
344
17413848
            .iter()
345
26843850
            .any(|r| relays_in_same_extended_family(&self.subnet_config, relay, r))
346
        {
347
6106100
            return false;
348
11307748
        }
349
11307748

            
350
11307748
        true
351
17512672
    }
352
}
353

            
354
/// Return true if `r1` and `r2` are in the same "extended" family,
355
/// considering both explicitly declared families
356
/// and subnet-based extended families.
357
26354282
fn relays_in_same_extended_family(
358
26354282
    subnet_config: &SubnetConfig,
359
26354282
    r1: &Relay<'_>,
360
26354282
    r2: &Relay<'_>,
361
26354282
) -> bool {
362
26354282
    r1.low_level_details().in_same_family(r2) || subnet_config.any_addrs_in_same_subnet(r1, r2)
363
26354282
}
364

            
365
#[cfg(test)]
366
mod test {
367
    // @@ begin test lint list maintained by maint/add_warning @@
368
    #![allow(clippy::bool_assert_comparison)]
369
    #![allow(clippy::clone_on_copy)]
370
    #![allow(clippy::dbg_macro)]
371
    #![allow(clippy::mixed_attributes_style)]
372
    #![allow(clippy::print_stderr)]
373
    #![allow(clippy::print_stdout)]
374
    #![allow(clippy::single_char_pattern)]
375
    #![allow(clippy::unwrap_used)]
376
    #![allow(clippy::unchecked_duration_subtraction)]
377
    #![allow(clippy::useless_vec)]
378
    #![allow(clippy::needless_pass_by_value)]
379
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
380

            
381
    use tor_linkspec::RelayId;
382

            
383
    use super::*;
384
    use crate::testing::{cfg, split_netdir, testnet};
385

            
386
    #[test]
387
    fn exclude_nothing() {
388
        let nd = testnet();
389
        let usage = RelayExclusion::no_relays_excluded();
390
        assert!(nd
391
            .relays()
392
            .all(|r| usage.low_level_predicate_permits_relay(&r)));
393
    }
394

            
395
    #[test]
396
    fn exclude_ids() {
397
        let nd = testnet();
398
        let id_0 = "$0000000000000000000000000000000000000000".parse().unwrap();
399
        let id_5 = "ed25519:BQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU"
400
            .parse()
401
            .unwrap();
402
        let ids: RelayIdSet = [id_0, id_5].into_iter().collect();
403
        let (yes, no) = split_netdir(&nd, &RelayExclusion::exclude_identities(ids));
404

            
405
        let p = |r: &Relay<'_>| !(r.has_identity(id_0.as_ref()) || r.has_identity(id_5.as_ref()));
406
        assert_eq!(yes.len(), 38);
407
        assert_eq!(no.len(), 2);
408
        assert!(yes.iter().all(p));
409
        assert!(no.iter().all(|r| !p(r)));
410
    }
411

            
412
    #[test]
413
    fn exclude_relays() {
414
        let nd = testnet();
415
        let id_0: RelayId = "$0000000000000000000000000000000000000000".parse().unwrap();
416
        let id_5: RelayId = "ed25519:BQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU"
417
            .parse()
418
            .unwrap();
419
        let relay_0 = nd.by_id(&id_0).unwrap();
420
        let relay_5 = nd.by_id(&id_5).unwrap();
421

            
422
        let (yes, no) = split_netdir(
423
            &nd,
424
            &RelayExclusion::exclude_specific_relays(&[relay_0.clone(), relay_5.clone()]),
425
        );
426
        let p = |r: &Relay<'_>| !(r.same_relay_ids(&relay_0) || r.same_relay_ids(&relay_5));
427
        assert_eq!(yes.len(), 38);
428
        assert_eq!(no.len(), 2);
429
        assert!(yes.iter().all(p));
430
        assert!(no.iter().all(|r| !p(r)));
431
    }
432

            
433
    #[test]
434
    fn exclude_families() {
435
        let nd = testnet();
436
        let id_0: RelayId = "$0000000000000000000000000000000000000000".parse().unwrap();
437
        let id_5: RelayId = "ed25519:BQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQU"
438
            .parse()
439
            .unwrap();
440
        let relay_0 = nd.by_id(&id_0).unwrap();
441
        let relay_5 = nd.by_id(&id_5).unwrap();
442
        let excluding_relays = vec![relay_0, relay_5];
443

            
444
        // in the test netdir, all (2n, 2n+1) pairs are in a family.
445
        let id_1 = "$0101010101010101010101010101010101010101".parse().unwrap();
446
        let id_4 = "$0404040404040404040404040404040404040404".parse().unwrap();
447
        let expect_excluded_ids: RelayIdSet = [id_0, id_1, id_4, id_5].into_iter().collect();
448

            
449
        // Case one: No subnet-based exclusion.
450

            
451
        let cfg_no_subnet = RelaySelectionConfig {
452
            long_lived_ports: cfg().long_lived_ports,
453
            subnet_config: SubnetConfig::new(255, 255),
454
        };
455

            
456
        let (yes, no) = split_netdir(
457
            &nd,
458
            &RelayExclusion::exclude_relays_in_same_family(
459
                &cfg_no_subnet,
460
                excluding_relays.clone(),
461
            ),
462
        );
463
        let p = |r: &Relay<'_>| !r.identities().any(|id| expect_excluded_ids.contains(id));
464
        assert_eq!(yes.len(), 36);
465
        assert_eq!(no.len(), 4);
466
        assert!(yes.iter().all(p));
467
        assert!(no.iter().all(|r| !p(r)));
468

            
469
        // Case two: default subnet-based exclusion.
470
        //
471
        // In the test network, addresses are x.0.0.3 where x is the index of
472
        // the relay, modulo 5.  Since the default ipv4 subnet family rule looks at /16
473
        // prefixes, every one of the 40 relays in the testnet will be in a
474
        // family with 8 other relays.
475
        let expect_excluded_ids: RelayIdSet = nd
476
            .relays()
477
            .filter_map(|r| {
478
                let rsa = r.rsa_identity().unwrap();
479
                let b = rsa.as_bytes()[0];
480
                if [0, 1, 4, 5].contains(&b) || [0, 5].contains(&(b % 5)) {
481
                    Some(RelayId::from(*rsa))
482
                } else {
483
                    None
484
                }
485
            })
486
            .collect();
487

            
488
        let (yes, no) = split_netdir(
489
            &nd,
490
            &RelayExclusion::exclude_relays_in_same_family(&cfg(), excluding_relays),
491
        );
492
        for r in &no {
493
            dbg!(r.rsa_identity().unwrap());
494
        }
495
        dbg!(&expect_excluded_ids);
496
        dbg!(expect_excluded_ids.len());
497
        let p = |r: &Relay<'_>| !r.identities().any(|id| expect_excluded_ids.contains(id));
498
        assert_eq!(yes.len(), 30);
499
        assert_eq!(no.len(), 10);
500
        assert!(yes.iter().all(p));
501

            
502
        assert!(no.iter().all(|r| { !p(r) }));
503
    }
504

            
505
    #[test]
506
    fn filter_addresses() {
507
        let nd = testnet();
508
        let reachable = vec![
509
            "1.0.0.0/8:*".parse().unwrap(),
510
            "2.0.0.0/8:*".parse().unwrap(),
511
        ];
512
        let reachable = RelayRestriction::require_address(reachable);
513

            
514
        let (yes, no) = split_netdir(&nd, &reachable);
515
        assert_eq!(yes.len(), 16);
516
        assert_eq!(no.len(), 24);
517

            
518
        let expected = ["1.0.0.3".parse().unwrap(), "2.0.0.3".parse().unwrap()];
519
        let p = |r: &Relay<'_>| expected.contains(&r.addrs()[0].ip());
520
        assert!(yes.iter().all(p));
521
        assert!(no.iter().all(|r| !p(r)));
522
    }
523

            
524
    // TODO: Write a geoip test?
525
}