1
//! Code related to tracking what activities a circuit can be used for.
2

            
3
use rand::Rng;
4
use std::fmt::{self, Display};
5
use std::sync::Arc;
6
use std::time::SystemTime;
7
use tracing::trace;
8
#[cfg(not(feature = "geoip"))]
9
use void::Void;
10

            
11
use crate::path::{TorPath, dirpath::DirPathBuilder, exitpath::ExitPathBuilder};
12
use tor_chanmgr::ChannelUsage;
13
#[cfg(feature = "geoip")]
14
use tor_error::internal;
15
use tor_guardmgr::{GuardMgr, GuardMonitor, GuardUsable};
16
use tor_netdir::Relay;
17
use tor_netdoc::types::policy::PortPolicy;
18
use tor_rtcompat::Runtime;
19
#[cfg(feature = "hs-common")]
20
use {crate::HsCircKind, crate::HsCircStemKind, crate::path::hspath::HsPathBuilder};
21

            
22
#[cfg(feature = "specific-relay")]
23
use tor_linkspec::{HasChanMethod, HasRelayIds};
24

            
25
#[cfg(feature = "geoip")]
26
use tor_geoip::CountryCode;
27
/// A non-existent country code type, used as a placeholder for the real `tor_geoip::CountryCode`
28
/// when the `geoip` crate feature is not present.
29
///
30
/// This type exists to simplify conditional compilation: without it, we'd have to duplicate a lot
31
/// of match patterns and things would suck a lot.
32
// TODO GEOIP: propagate this refactor down through the stack (i.e. all the way down to the
33
//            `tor-geoip` crate)
34
//             We can also get rid of a lot of #[cfg] then.
35
#[cfg(not(feature = "geoip"))]
36
pub(crate) type CountryCode = Void;
37

            
38
#[cfg(any(feature = "specific-relay", feature = "hs-common"))]
39
use tor_linkspec::OwnedChanTarget;
40

            
41
#[cfg(all(feature = "vanguards", feature = "hs-common"))]
42
use tor_guardmgr::vanguards::VanguardMgr;
43

            
44
use crate::Result;
45
use crate::isolation::{IsolationHelper, StreamIsolation};
46
use crate::mgr::{AbstractTunnel, OpenEntry, RestrictionFailed};
47

            
48
pub use tor_relay_selection::TargetPort;
49

            
50
/// An exit policy, as supported by the last hop of a circuit.
51
#[derive(Clone, Debug, PartialEq, Eq)]
52
pub(crate) struct ExitPolicy {
53
    /// Permitted IPv4 ports.
54
    v4: Arc<PortPolicy>,
55
    /// Permitted IPv6 ports.
56
    v6: Arc<PortPolicy>,
57
}
58

            
59
/// Set of requested target ports, mostly for use in error reporting
60
///
61
/// Displays nicely.
62
#[derive(Debug, Clone, Default)]
63
pub struct TargetPorts(Vec<TargetPort>);
64

            
65
impl From<&'_ [TargetPort]> for TargetPorts {
66
186
    fn from(ports: &'_ [TargetPort]) -> Self {
67
186
        TargetPorts(ports.into())
68
186
    }
69
}
70

            
71
impl Display for TargetPorts {
72
6
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73
6
        let brackets = self.0.len() != 1;
74
6
        if brackets {
75
4
            write!(f, "[")?;
76
2
        }
77
6
        for (i, port) in self.0.iter().enumerate() {
78
6
            if i > 0 {
79
2
                write!(f, ",")?;
80
4
            }
81
6
            write!(f, "{}", port)?;
82
        }
83
6
        if brackets {
84
4
            write!(f, "]")?;
85
2
        }
86
6
        Ok(())
87
6
    }
88
}
89

            
90
impl ExitPolicy {
91
    /// Make a new exit policy from a given Relay.
92
62
    pub(crate) fn from_relay(relay: &Relay<'_>) -> Self {
93
        // TODO #504: it might be a good idea to lower this whole type into
94
        // tor-netdir or tor-relay-selection.  That way we wouldn't need to
95
        // invoke these Relay-specific methods in tor-circmgr.
96
62
        Self {
97
62
            v4: relay.low_level_details().ipv4_policy(),
98
62
            v6: relay.low_level_details().ipv6_policy(),
99
62
        }
100
62
    }
101

            
102
    /// Make a exit policy based on the allowed ports in TargetPorts.
103
    #[cfg(test)]
104
180
    pub(crate) fn from_target_ports(target_ports: &TargetPorts) -> Self {
105
180
        let (v6_ports, v4_ports) = target_ports
106
180
            .0
107
180
            .iter()
108
180
            .partition::<Vec<TargetPort>, _>(|port| port.ipv6);
109

            
110
        Self {
111
180
            v4: PortPolicy::from_allowed_port_list(v4_ports.iter().map(|port| port.port).collect())
112
180
                .intern(),
113
180
            v6: PortPolicy::from_allowed_port_list(v6_ports.iter().map(|port| port.port).collect())
114
180
                .intern(),
115
        }
116
180
    }
117

            
118
    /// Return true if a given port is contained in this ExitPolicy.
119
714
    fn allows_port(&self, p: TargetPort) -> bool {
120
714
        let policy = if p.ipv6 { &self.v6 } else { &self.v4 };
121
714
        policy.allows_port(p.port)
122
714
    }
123

            
124
    /// Returns true if this policy allows any ports at all.
125
36
    fn allows_some_port(&self) -> bool {
126
36
        self.v4.allows_some_port() || self.v6.allows_some_port()
127
36
    }
128
}
129

            
130
/// The purpose for which a circuit is being created.
131
///
132
/// This type should stay internal to the circmgr crate for now: we'll probably
133
/// want to refactor it a lot.
134
#[derive(Clone, Debug)]
135
pub(crate) enum TargetTunnelUsage {
136
    /// Use for BEGINDIR-based non-anonymous directory connections
137
    Dir,
138
    /// Use to exit to one or more ports.
139
    Exit {
140
        /// List of ports the circuit has to allow.
141
        ///
142
        /// If this list of ports is empty, then the circuit doesn't need
143
        /// to support any particular port, but it still needs to be an exit.
144
        ports: Vec<TargetPort>,
145
        /// Isolation group the circuit shall be part of
146
        isolation: StreamIsolation,
147
        /// Restrict the circuit to only exits in the provided country code.
148
        country_code: Option<CountryCode>,
149
        /// If true, all relays on this circuit need to have the Stable flag.
150
        //
151
        // TODO #504: It would be good to remove this field, if we can.
152
        require_stability: bool,
153
    },
154
    /// For a circuit is only used for the purpose of building it.
155
    TimeoutTesting,
156
    /// For internal usage only: build a circuit preemptively, to reduce wait times.
157
    ///
158
    /// # Warning
159
    ///
160
    /// This **MUST NOT** be used by code outside of the preemptive circuit predictor. In
161
    /// particular, this usage doesn't support stream isolation, so using it to ask for
162
    /// circuits (for example, by passing it to `get_or_launch`) could be unsafe!
163
    Preemptive {
164
        /// A port the circuit has to allow, if specified.
165
        ///
166
        /// If this is `None`, we just want a circuit capable of doing DNS resolution.
167
        port: Option<TargetPort>,
168
        /// The number of exit circuits needed for a port
169
        circs: usize,
170
        /// If true, all relays on this circuit need to have the Stable flag.
171
        // TODO #504: It would be good to remove this field, if we can.
172
        require_stability: bool,
173
    },
174
    /// Use for BEGINDIR-based non-anonymous directory connections to a particular target,
175
    /// and therefore to a specific relay (which need not be in any netdir).
176
    #[cfg(feature = "specific-relay")]
177
    DirSpecificTarget(OwnedChanTarget),
178

            
179
    /// Used to build a circuit (currently always 3 hops) to serve as the basis of some
180
    /// onion-serivice-related operation.
181
    #[cfg(feature = "hs-common")]
182
    HsCircBase {
183
        /// A target to avoid when constructing this circuit.
184
        ///
185
        /// This target is not appended to the end of the circuit; rather, the
186
        /// circuit is built so that its relays are all allowed to share a
187
        /// circuit with this target (without, for example, violating any
188
        /// family restrictions).
189
        compatible_with_target: Option<OwnedChanTarget>,
190
        /// The kind of circuit stem to build.
191
        stem_kind: HsCircStemKind,
192
        /// If present, add additional rules to the stem so it can _definitely_
193
        /// be used as a circuit of this kind.
194
        circ_kind: Option<HsCircKind>,
195
    },
196
}
197

            
198
/// The purposes for which a circuit is usable.
199
///
200
/// This type should stay internal to the circmgr crate for now: we'll probably
201
/// want to refactor it a lot.
202
#[derive(Clone, Debug)]
203
pub(crate) enum SupportedTunnelUsage {
204
    /// Usable for BEGINDIR-based non-anonymous directory connections
205
    Dir,
206
    /// Usable to exit to a set of ports.
207
    Exit {
208
        /// Exit policy of the circuit
209
        policy: ExitPolicy,
210
        /// Isolation group the circuit is part of. None when the circuit is not yet assigned to an
211
        /// isolation group.
212
        isolation: Option<StreamIsolation>,
213
        /// Country code the exit is in, or `None` if no country could be determined.
214
        country_code: Option<CountryCode>,
215
        /// Whether every relay in this circuit has the "Stable" flag.
216
        //
217
        // TODO #504: It would be good to remove this field, if we can.
218
        all_relays_stable: bool,
219
    },
220
    /// This circuit is not suitable for any usage.
221
    NoUsage,
222
    /// This circuit is for some hs-related usage.
223
    /// (It should never be given to the circuit manager; the
224
    /// `HsPool` code will handle it instead.)
225
    #[cfg(feature = "hs-common")]
226
    HsOnly,
227
    /// Use only for BEGINDIR-based non-anonymous directory connections
228
    /// to a particular target (which may not be in the netdir).
229
    #[cfg(feature = "specific-relay")]
230
    DirSpecificTarget(OwnedChanTarget),
231
}
232

            
233
impl TargetTunnelUsage {
234
    /// Construct path for a given circuit purpose; return it and the
235
    /// usage that it _actually_ supports.
236
48
    pub(crate) fn build_path<'a, R: Rng, RT: Runtime>(
237
48
        &self,
238
48
        rng: &mut R,
239
48
        netdir: crate::DirInfo<'a>,
240
48
        guards: &GuardMgr<RT>,
241
48
        #[cfg(all(feature = "vanguards", feature = "hs-common"))] vanguards: &VanguardMgr<RT>,
242
48
        config: &crate::PathConfig,
243
48
        now: SystemTime,
244
48
    ) -> Result<(
245
48
        TorPath<'a>,
246
48
        SupportedTunnelUsage,
247
48
        Option<GuardMonitor>,
248
48
        Option<GuardUsable>,
249
48
    )> {
250
48
        match self {
251
            TargetTunnelUsage::Dir => {
252
12
                let (path, mon, usable) = DirPathBuilder::new().pick_path(guards)?;
253
12
                Ok((path, SupportedTunnelUsage::Dir, Some(mon), Some(usable)))
254
            }
255
            TargetTunnelUsage::Preemptive {
256
                port,
257
                require_stability,
258
                ..
259
            } => {
260
                // FIXME(eta): this is copypasta from `TargetCircUsage::Exit`.
261
                let (path, mon, usable) = ExitPathBuilder::from_target_ports(port.iter().copied())
262
                    .require_stability(*require_stability)
263
                    .pick_path(rng, netdir, guards, config, now)?;
264
                let policy = path
265
                    .exit_policy()
266
                    .expect("ExitPathBuilder gave us a one-hop circuit?");
267
                #[cfg(feature = "geoip")]
268
                let country_code = path.country_code();
269
                #[cfg(not(feature = "geoip"))]
270
                let country_code = None;
271
                let all_relays_stable = path.appears_stable();
272
                Ok((
273
                    path,
274
                    SupportedTunnelUsage::Exit {
275
                        policy,
276
                        isolation: None,
277
                        country_code,
278
                        all_relays_stable,
279
                    },
280
                    Some(mon),
281
                    Some(usable),
282
                ))
283
            }
284
            TargetTunnelUsage::Exit {
285
12
                ports: p,
286
12
                isolation,
287
12
                country_code,
288
12
                require_stability,
289
            } => {
290
                #[cfg(feature = "geoip")]
291
12
                let mut builder = if let Some(cc) = country_code {
292
                    ExitPathBuilder::in_given_country(*cc, p.clone())
293
                } else {
294
12
                    ExitPathBuilder::from_target_ports(p.clone())
295
                };
296
                #[cfg(not(feature = "geoip"))]
297
                let mut builder = ExitPathBuilder::from_target_ports(p.clone());
298

            
299
12
                builder.require_stability(*require_stability);
300

            
301
12
                let (path, mon, usable) = builder.pick_path(rng, netdir, guards, config, now)?;
302
12
                let policy = path
303
12
                    .exit_policy()
304
12
                    .expect("ExitPathBuilder gave us a one-hop circuit?");
305

            
306
                #[cfg(feature = "geoip")]
307
12
                let resulting_cc = path.country_code();
308
                #[cfg(feature = "geoip")]
309
12
                if resulting_cc != *country_code {
310
                    internal!(
311
                        "asked for a country code of {:?}, got {:?}",
312
                        country_code,
313
                        resulting_cc
314
                    );
315
12
                }
316
12
                let all_relays_stable = path.appears_stable();
317

            
318
                #[cfg(not(feature = "geoip"))]
319
                let resulting_cc = *country_code; // avoid unused var warning
320
12
                Ok((
321
12
                    path,
322
12
                    SupportedTunnelUsage::Exit {
323
12
                        policy,
324
12
                        isolation: Some(isolation.clone()),
325
12
                        country_code: resulting_cc,
326
12
                        all_relays_stable,
327
12
                    },
328
12
                    Some(mon),
329
12
                    Some(usable),
330
12
                ))
331
            }
332
            TargetTunnelUsage::TimeoutTesting => {
333
24
                let (path, mon, usable) = ExitPathBuilder::for_timeout_testing()
334
24
                    .require_stability(false)
335
24
                    .pick_path(rng, netdir, guards, config, now)?;
336
24
                let policy = path.exit_policy();
337
                #[cfg(feature = "geoip")]
338
24
                let country_code = path.country_code();
339
                #[cfg(not(feature = "geoip"))]
340
                let country_code = None;
341
24
                let usage = match policy {
342
24
                    Some(policy) if policy.allows_some_port() => SupportedTunnelUsage::Exit {
343
12
                        policy,
344
12
                        isolation: None,
345
12
                        country_code,
346
12
                        all_relays_stable: path.appears_stable(),
347
12
                    },
348
12
                    _ => SupportedTunnelUsage::NoUsage,
349
                };
350

            
351
24
                Ok((path, usage, Some(mon), Some(usable)))
352
            }
353
            #[cfg(feature = "specific-relay")]
354
            TargetTunnelUsage::DirSpecificTarget(target) => {
355
                let path = TorPath::new_one_hop_owned(target);
356
                let usage = SupportedTunnelUsage::DirSpecificTarget(target.clone());
357
                Ok((path, usage, None, None))
358
            }
359
            #[cfg(feature = "hs-common")]
360
            TargetTunnelUsage::HsCircBase {
361
                compatible_with_target,
362
                stem_kind,
363
                circ_kind,
364
            } => {
365
                let path_builder =
366
                    HsPathBuilder::new(compatible_with_target.clone(), *stem_kind, *circ_kind);
367
                cfg_if::cfg_if! {
368
                    if #[cfg(all(feature = "vanguards", feature = "hs-common"))] {
369
                        let (path, mon, usable) = path_builder
370
                            .pick_path_with_vanguards::<_, RT>(rng, netdir, guards, vanguards, config, now)?;
371
                    } else {
372
                        let (path, mon, usable) = path_builder
373
                            .pick_path::<_, RT>(rng, netdir, guards, config, now)?;
374
                    }
375
                };
376
                let usage = SupportedTunnelUsage::HsOnly;
377
                Ok((path, usage, Some(mon), Some(usable)))
378
            }
379
        }
380
48
    }
381

            
382
    /// Create a TargetCircUsage::Exit for a given set of IPv4 ports, with no stream isolation, for
383
    /// use in tests.
384
    #[cfg(test)]
385
114
    pub(crate) fn new_from_ipv4_ports(ports: &[u16]) -> Self {
386
        TargetTunnelUsage::Exit {
387
207
            ports: ports.iter().map(|p| TargetPort::ipv4(*p)).collect(),
388
114
            isolation: StreamIsolation::no_isolation(),
389
114
            country_code: None,
390
            require_stability: false,
391
        }
392
114
    }
393
}
394

            
395
/// Return true if `a` and `b` count as the same target for the purpose of
396
/// comparing `DirSpecificTarget` values.
397
#[cfg(feature = "specific-relay")]
398
fn owned_targets_equivalent(a: &OwnedChanTarget, b: &OwnedChanTarget) -> bool {
399
    // We ignore `addresses` here, since they can be different if one of our
400
    // arguments comes from only a bridge line, and the other comes from a
401
    // bridge line and a descriptor.
402
    a.same_relay_ids(b) && a.chan_method() == b.chan_method()
403
}
404

            
405
impl SupportedTunnelUsage {
406
    /// Return true if this spec permits the usage described by `other`.
407
    ///
408
    /// If this function returns `true`, then it is okay to use a circuit
409
    /// with this spec for the target usage described by `other`.
410
1474
    pub(crate) fn supports(&self, target: &TargetTunnelUsage) -> bool {
411
        use SupportedTunnelUsage::*;
412
1474
        match (self, target) {
413
2
            (Dir, TargetTunnelUsage::Dir) => true,
414
            (
415
                Exit {
416
1278
                    policy: p1,
417
1278
                    isolation: i1,
418
1278
                    country_code: cc1,
419
1278
                    all_relays_stable,
420
                },
421
                TargetTunnelUsage::Exit {
422
1278
                    ports: p2,
423
1278
                    isolation: i2,
424
1278
                    country_code: cc2,
425
1278
                    require_stability,
426
                },
427
            ) => {
428
                // TODO #504: These calculations don't touch Relays, but they
429
                // seem like they should be done using the types of tor-relay-selection.
430
1278
                i1.as_ref()
431
1881
                    .map(|i1| i1.compatible_same_type(i2))
432
1278
                    .unwrap_or(true)
433
498
                    && (!require_stability || *all_relays_stable)
434
769
                    && p2.iter().all(|port| p1.allows_port(*port))
435
448
                    && (cc2.is_none() || cc1 == cc2)
436
            }
437
            (
438
                Exit {
439
174
                    policy,
440
174
                    isolation,
441
174
                    all_relays_stable,
442
                    ..
443
                },
444
                TargetTunnelUsage::Preemptive {
445
174
                    port,
446
174
                    require_stability,
447
                    ..
448
                },
449
            ) => {
450
                // TODO #504: It would be good to simply remove stability
451
                // calculation from tor-circmgr.
452
174
                if *require_stability && !all_relays_stable {
453
                    return false;
454
174
                }
455
174
                if isolation.is_some() {
456
                    // If the circuit has a stream isolation, we might not be able to use it
457
                    // for new streams that don't share it.
458
                    return false;
459
174
                }
460
                // TODO #504: Similarly, it would be good to have exit port
461
                // calculation done elsewhere.
462
174
                if let Some(p) = port {
463
168
                    policy.allows_port(*p)
464
                } else {
465
6
                    true
466
                }
467
            }
468
8
            (Exit { .. } | NoUsage, TargetTunnelUsage::TimeoutTesting) => true,
469
            #[cfg(feature = "specific-relay")]
470
            (DirSpecificTarget(a), TargetTunnelUsage::DirSpecificTarget(b)) => {
471
                owned_targets_equivalent(a, b)
472
            }
473
12
            (_, _) => false,
474
        }
475
1474
    }
476

            
477
    /// Change the value of this spec based on the circuit having been used for `usage`.
478
    ///
479
    /// Returns an error and makes no changes to `self` if `usage` was not supported by this spec.
480
    ///
481
    /// If this function returns Ok, the resulting spec will be contained by the original spec, and
482
    /// will support `usage`.
483
502
    pub(crate) fn restrict_mut(
484
502
        &mut self,
485
502
        usage: &TargetTunnelUsage,
486
502
    ) -> std::result::Result<(), RestrictionFailed> {
487
        use SupportedTunnelUsage::*;
488
502
        match (self, usage) {
489
2
            (Dir, TargetTunnelUsage::Dir) => Ok(()),
490
            // This usage is only used to create circuits preemptively, and doesn't actually
491
            // correspond to any streams; accordingly, we don't need to modify the circuit's
492
            // acceptable usage at all.
493
            (Exit { .. }, TargetTunnelUsage::Preemptive { .. }) => Ok(()),
494
            (
495
                Exit {
496
486
                    isolation: isol1, ..
497
                },
498
486
                TargetTunnelUsage::Exit { isolation: i2, .. },
499
            ) => {
500
486
                if let Some(i1) = isol1 {
501
416
                    if let Some(new_isolation) = i1.join_same_type(i2) {
502
                        // there was some isolation, and the requested usage is compatible, saving
503
                        // the new isolation into self
504
412
                        *isol1 = Some(new_isolation);
505
412
                        Ok(())
506
                    } else {
507
4
                        Err(RestrictionFailed::NotSupported)
508
                    }
509
                } else {
510
                    // there was no isolation yet on self, applying the restriction from usage
511
70
                    *isol1 = Some(i2.clone());
512
70
                    Ok(())
513
                }
514
            }
515
4
            (Exit { .. } | NoUsage, TargetTunnelUsage::TimeoutTesting) => Ok(()),
516
            #[cfg(feature = "specific-relay")]
517
            (DirSpecificTarget(a), TargetTunnelUsage::DirSpecificTarget(b))
518
                if owned_targets_equivalent(a, b) =>
519
            {
520
                Ok(())
521
            }
522
10
            (_, _) => Err(RestrictionFailed::NotSupported),
523
        }
524
502
    }
525

            
526
    /// Find all open circuits in `list` whose specifications permit `usage`.
527
612
    pub(crate) fn find_supported<'a, 'b, C: AbstractTunnel>(
528
612
        list: impl Iterator<Item = &'b mut OpenEntry<C>>,
529
612
        usage: &TargetTunnelUsage,
530
612
    ) -> Vec<&'b mut OpenEntry<C>> {
531
        /// Returns all circuits in `list` for which `circuit.spec.supports(usage)` returns `true`.
532
612
        fn find_supported_internal<'a, 'b, C: AbstractTunnel>(
533
612
            list: impl Iterator<Item = &'b mut OpenEntry<C>>,
534
612
            usage: &TargetTunnelUsage,
535
612
        ) -> Vec<&'b mut OpenEntry<C>> {
536
1324
            list.filter(|circ| circ.supports(usage)).collect()
537
612
        }
538

            
539
612
        match usage {
540
58
            TargetTunnelUsage::Preemptive { circs, .. } => {
541
58
                let supported = find_supported_internal(list, usage);
542
                // We need to have at least two circuits that support `port` in order
543
                // to reuse them; otherwise, we must create a new circuit, so
544
                // that we get closer to having two circuits.
545
58
                trace!(
546
                    "preemptive usage {:?} matches {} active circuits",
547
                    usage,
548
                    supported.len()
549
                );
550
58
                if supported.len() >= *circs {
551
12
                    supported
552
                } else {
553
46
                    vec![]
554
                }
555
            }
556
554
            _ => find_supported_internal(list, usage),
557
        }
558
612
    }
559

            
560
    /// How the circuit will be used, for use by the channel
561
    pub(crate) fn channel_usage(&self) -> ChannelUsage {
562
        use ChannelUsage as CU;
563
        use SupportedTunnelUsage as SCU;
564
        match self {
565
            SCU::Dir => CU::Dir,
566
            #[cfg(feature = "specific-relay")]
567
            SCU::DirSpecificTarget(_) => CU::Dir,
568
            SCU::Exit { .. } => CU::UserTraffic,
569
            SCU::NoUsage => CU::UselessCircuit,
570
            #[cfg(feature = "hs-common")]
571
            SCU::HsOnly => CU::UserTraffic,
572
        }
573
    }
574
}
575

            
576
#[cfg(test)]
577
pub(crate) mod test {
578
    #![allow(clippy::unwrap_used)]
579
    use super::*;
580
    use crate::isolation::test::{IsolationTokenEq, assert_isoleq};
581
    use crate::isolation::{IsolationToken, StreamIsolationBuilder};
582
    use crate::path::OwnedPath;
583
    use tor_basic_utils::test_rng::testing_rng;
584
    use tor_guardmgr::TestConfig;
585
    use tor_llcrypto::pk::ed25519::Ed25519Identity;
586
    use tor_netdir::testnet;
587
    use tor_persist::TestingStateMgr;
588

            
589
    impl IsolationTokenEq for TargetTunnelUsage {
590
        fn isol_eq(&self, other: &Self) -> bool {
591
            use TargetTunnelUsage::*;
592
            match (self, other) {
593
                (Dir, Dir) => true,
594
                (
595
                    Exit {
596
                        ports: p1,
597
                        isolation: is1,
598
                        country_code: cc1,
599
                        ..
600
                    },
601
                    Exit {
602
                        ports: p2,
603
                        isolation: is2,
604
                        country_code: cc2,
605
                        ..
606
                    },
607
                ) => p1 == p2 && cc1 == cc2 && is1.isol_eq(is2),
608
                (TimeoutTesting, TimeoutTesting) => true,
609
                (
610
                    Preemptive {
611
                        port: p1,
612
                        circs: c1,
613
                        ..
614
                    },
615
                    Preemptive {
616
                        port: p2,
617
                        circs: c2,
618
                        ..
619
                    },
620
                ) => p1 == p2 && c1 == c2,
621
                _ => false,
622
            }
623
        }
624
    }
625

            
626
    impl IsolationTokenEq for SupportedTunnelUsage {
627
        fn isol_eq(&self, other: &Self) -> bool {
628
            use SupportedTunnelUsage::*;
629
            match (self, other) {
630
                (Dir, Dir) => true,
631
                (
632
                    Exit {
633
                        policy: p1,
634
                        isolation: is1,
635
                        country_code: cc1,
636
                        ..
637
                    },
638
                    Exit {
639
                        policy: p2,
640
                        isolation: is2,
641
                        country_code: cc2,
642
                        ..
643
                    },
644
                ) => p1 == p2 && is1.isol_eq(is2) && cc1 == cc2,
645
                (NoUsage, NoUsage) => true,
646
                _ => false,
647
            }
648
        }
649
    }
650

            
651
    #[test]
652
    fn exit_policy() {
653
        use tor_netdir::testnet::construct_custom_netdir;
654
        use tor_netdoc::doc::netstatus::RelayFlags;
655

            
656
        let network = construct_custom_netdir(|idx, nb, _| {
657
            if (0x21..0x27).contains(&idx) {
658
                nb.rs.add_flags(RelayFlags::BAD_EXIT);
659
            }
660
        })
661
        .unwrap()
662
        .unwrap_if_sufficient()
663
        .unwrap();
664

            
665
        // Nodes with ID 0x0a through 0x13 and 0x1e through 0x27 are
666
        // exits.  Odd-numbered ones allow only ports 80 and 443;
667
        // even-numbered ones allow all ports.  Nodes with ID 0x21
668
        // through 0x27 are bad exits.
669
        let id_noexit: Ed25519Identity = [0x05; 32].into();
670
        let id_webexit: Ed25519Identity = [0x11; 32].into();
671
        let id_fullexit: Ed25519Identity = [0x20; 32].into();
672
        let id_badexit: Ed25519Identity = [0x25; 32].into();
673

            
674
        let not_exit = network.by_id(&id_noexit).unwrap();
675
        let web_exit = network.by_id(&id_webexit).unwrap();
676
        let full_exit = network.by_id(&id_fullexit).unwrap();
677
        let bad_exit = network.by_id(&id_badexit).unwrap();
678

            
679
        let ep_none = ExitPolicy::from_relay(&not_exit);
680
        let ep_web = ExitPolicy::from_relay(&web_exit);
681
        let ep_full = ExitPolicy::from_relay(&full_exit);
682
        let ep_bad = ExitPolicy::from_relay(&bad_exit);
683

            
684
        assert!(!ep_none.allows_port(TargetPort::ipv4(80)));
685
        assert!(!ep_none.allows_port(TargetPort::ipv4(9999)));
686

            
687
        assert!(ep_web.allows_port(TargetPort::ipv4(80)));
688
        assert!(ep_web.allows_port(TargetPort::ipv4(443)));
689
        assert!(!ep_web.allows_port(TargetPort::ipv4(9999)));
690

            
691
        assert!(ep_full.allows_port(TargetPort::ipv4(80)));
692
        assert!(ep_full.allows_port(TargetPort::ipv4(443)));
693
        assert!(ep_full.allows_port(TargetPort::ipv4(9999)));
694

            
695
        assert!(!ep_bad.allows_port(TargetPort::ipv4(80)));
696

            
697
        // Note that nobody in the testdir::network allows ipv6.
698
        assert!(!ep_none.allows_port(TargetPort::ipv6(80)));
699
        assert!(!ep_web.allows_port(TargetPort::ipv6(80)));
700
        assert!(!ep_full.allows_port(TargetPort::ipv6(80)));
701
        assert!(!ep_bad.allows_port(TargetPort::ipv6(80)));
702

            
703
        // Check is_supported_by while we're here.
704
        assert!(TargetPort::ipv4(80).is_supported_by(&web_exit.low_level_details()));
705
        assert!(!TargetPort::ipv6(80).is_supported_by(&web_exit.low_level_details()));
706
        assert!(!TargetPort::ipv6(80).is_supported_by(&bad_exit.low_level_details()));
707
    }
708

            
709
    #[test]
710
    fn usage_ops() {
711
        // Make an exit-policy object that allows web on IPv4 and
712
        // smtp on IPv6.
713
        let policy = ExitPolicy {
714
            v4: Arc::new("accept 80,443".parse().unwrap()),
715
            v6: Arc::new("accept 23".parse().unwrap()),
716
        };
717
        let tok1 = IsolationToken::new();
718
        let tok2 = IsolationToken::new();
719
        let isolation = StreamIsolationBuilder::new()
720
            .owner_token(tok1)
721
            .build()
722
            .unwrap();
723
        let isolation2 = StreamIsolationBuilder::new()
724
            .owner_token(tok2)
725
            .build()
726
            .unwrap();
727

            
728
        let supp_dir = SupportedTunnelUsage::Dir;
729
        let targ_dir = TargetTunnelUsage::Dir;
730
        let supp_exit = SupportedTunnelUsage::Exit {
731
            policy: policy.clone(),
732
            isolation: Some(isolation.clone()),
733
            country_code: None,
734
            all_relays_stable: true,
735
        };
736
        let supp_exit_iso2 = SupportedTunnelUsage::Exit {
737
            policy: policy.clone(),
738
            isolation: Some(isolation2.clone()),
739
            country_code: None,
740
            all_relays_stable: true,
741
        };
742
        let supp_exit_no_iso = SupportedTunnelUsage::Exit {
743
            policy,
744
            isolation: None,
745
            country_code: None,
746
            all_relays_stable: true,
747
        };
748
        let supp_none = SupportedTunnelUsage::NoUsage;
749

            
750
        let targ_80_v4 = TargetTunnelUsage::Exit {
751
            ports: vec![TargetPort::ipv4(80)],
752
            isolation: isolation.clone(),
753
            country_code: None,
754
            require_stability: false,
755
        };
756
        let targ_80_v4_iso2 = TargetTunnelUsage::Exit {
757
            ports: vec![TargetPort::ipv4(80)],
758
            isolation: isolation2,
759
            country_code: None,
760
            require_stability: false,
761
        };
762
        let targ_80_23_v4 = TargetTunnelUsage::Exit {
763
            ports: vec![TargetPort::ipv4(80), TargetPort::ipv4(23)],
764
            isolation: isolation.clone(),
765
            country_code: None,
766
            require_stability: false,
767
        };
768

            
769
        let targ_80_23_mixed = TargetTunnelUsage::Exit {
770
            ports: vec![TargetPort::ipv4(80), TargetPort::ipv6(23)],
771
            isolation: isolation.clone(),
772
            country_code: None,
773
            require_stability: false,
774
        };
775
        let targ_999_v6 = TargetTunnelUsage::Exit {
776
            ports: vec![TargetPort::ipv6(999)],
777
            isolation,
778
            country_code: None,
779
            require_stability: false,
780
        };
781
        let targ_testing = TargetTunnelUsage::TimeoutTesting;
782

            
783
        assert!(supp_dir.supports(&targ_dir));
784
        assert!(!supp_dir.supports(&targ_80_v4));
785
        assert!(!supp_exit.supports(&targ_dir));
786
        assert!(supp_exit.supports(&targ_80_v4));
787
        assert!(!supp_exit.supports(&targ_80_v4_iso2));
788
        assert!(supp_exit.supports(&targ_80_23_mixed));
789
        assert!(!supp_exit.supports(&targ_80_23_v4));
790
        assert!(!supp_exit.supports(&targ_999_v6));
791
        assert!(!supp_exit_iso2.supports(&targ_80_v4));
792
        assert!(supp_exit_iso2.supports(&targ_80_v4_iso2));
793
        assert!(supp_exit_no_iso.supports(&targ_80_v4));
794
        assert!(supp_exit_no_iso.supports(&targ_80_v4_iso2));
795
        assert!(!supp_exit_no_iso.supports(&targ_80_23_v4));
796
        assert!(!supp_none.supports(&targ_dir));
797
        assert!(!supp_none.supports(&targ_80_23_v4));
798
        assert!(!supp_none.supports(&targ_80_v4_iso2));
799
        assert!(!supp_dir.supports(&targ_testing));
800
        assert!(supp_exit.supports(&targ_testing));
801
        assert!(supp_exit_no_iso.supports(&targ_testing));
802
        assert!(supp_exit_iso2.supports(&targ_testing));
803
        assert!(supp_none.supports(&targ_testing));
804
    }
805

            
806
    #[test]
807
    fn restrict_mut() {
808
        let policy = ExitPolicy {
809
            v4: Arc::new("accept 80,443".parse().unwrap()),
810
            v6: Arc::new("accept 23".parse().unwrap()),
811
        };
812

            
813
        let tok1 = IsolationToken::new();
814
        let tok2 = IsolationToken::new();
815
        let isolation = StreamIsolationBuilder::new()
816
            .owner_token(tok1)
817
            .build()
818
            .unwrap();
819
        let isolation2 = StreamIsolationBuilder::new()
820
            .owner_token(tok2)
821
            .build()
822
            .unwrap();
823

            
824
        let supp_dir = SupportedTunnelUsage::Dir;
825
        let targ_dir = TargetTunnelUsage::Dir;
826
        let supp_exit = SupportedTunnelUsage::Exit {
827
            policy: policy.clone(),
828
            isolation: Some(isolation.clone()),
829
            country_code: None,
830
            all_relays_stable: true,
831
        };
832
        let supp_exit_iso2 = SupportedTunnelUsage::Exit {
833
            policy: policy.clone(),
834
            isolation: Some(isolation2.clone()),
835
            country_code: None,
836
            all_relays_stable: true,
837
        };
838
        let supp_exit_no_iso = SupportedTunnelUsage::Exit {
839
            policy,
840
            isolation: None,
841
            country_code: None,
842
            all_relays_stable: true,
843
        };
844
        let supp_none = SupportedTunnelUsage::NoUsage;
845
        let targ_exit = TargetTunnelUsage::Exit {
846
            ports: vec![TargetPort::ipv4(80)],
847
            isolation,
848
            country_code: None,
849
            require_stability: false,
850
        };
851
        let targ_exit_iso2 = TargetTunnelUsage::Exit {
852
            ports: vec![TargetPort::ipv4(80)],
853
            isolation: isolation2,
854
            country_code: None,
855
            require_stability: false,
856
        };
857
        let targ_testing = TargetTunnelUsage::TimeoutTesting;
858

            
859
        // not allowed, do nothing
860
        let mut supp_dir_c = supp_dir.clone();
861
        assert!(supp_dir_c.restrict_mut(&targ_exit).is_err());
862
        assert!(supp_dir_c.restrict_mut(&targ_testing).is_err());
863
        assert_isoleq!(supp_dir, supp_dir_c);
864

            
865
        let mut supp_exit_c = supp_exit.clone();
866
        assert!(supp_exit_c.restrict_mut(&targ_dir).is_err());
867
        assert_isoleq!(supp_exit, supp_exit_c);
868

            
869
        let mut supp_exit_c = supp_exit.clone();
870
        assert!(supp_exit_c.restrict_mut(&targ_exit_iso2).is_err());
871
        assert_isoleq!(supp_exit, supp_exit_c);
872

            
873
        let mut supp_exit_iso2_c = supp_exit_iso2.clone();
874
        assert!(supp_exit_iso2_c.restrict_mut(&targ_exit).is_err());
875
        assert_isoleq!(supp_exit_iso2, supp_exit_iso2_c);
876

            
877
        let mut supp_none_c = supp_none.clone();
878
        assert!(supp_none_c.restrict_mut(&targ_exit).is_err());
879
        assert!(supp_none_c.restrict_mut(&targ_dir).is_err());
880
        assert_isoleq!(supp_none_c, supp_none);
881

            
882
        // allowed but nothing to do
883
        let mut supp_dir_c = supp_dir.clone();
884
        supp_dir_c.restrict_mut(&targ_dir).unwrap();
885
        assert_isoleq!(supp_dir, supp_dir_c);
886

            
887
        let mut supp_exit_c = supp_exit.clone();
888
        supp_exit_c.restrict_mut(&targ_exit).unwrap();
889
        assert_isoleq!(supp_exit, supp_exit_c);
890

            
891
        let mut supp_exit_iso2_c = supp_exit_iso2.clone();
892
        supp_exit_iso2_c.restrict_mut(&targ_exit_iso2).unwrap();
893
        supp_none_c.restrict_mut(&targ_testing).unwrap();
894
        assert_isoleq!(supp_exit_iso2, supp_exit_iso2_c);
895

            
896
        let mut supp_none_c = supp_none.clone();
897
        supp_none_c.restrict_mut(&targ_testing).unwrap();
898
        assert_isoleq!(supp_none_c, supp_none);
899

            
900
        // allowed, do something
901
        let mut supp_exit_no_iso_c = supp_exit_no_iso.clone();
902
        supp_exit_no_iso_c.restrict_mut(&targ_exit).unwrap();
903
        assert!(supp_exit_no_iso_c.supports(&targ_exit));
904
        assert!(!supp_exit_no_iso_c.supports(&targ_exit_iso2));
905

            
906
        let mut supp_exit_no_iso_c = supp_exit_no_iso;
907
        supp_exit_no_iso_c.restrict_mut(&targ_exit_iso2).unwrap();
908
        assert!(!supp_exit_no_iso_c.supports(&targ_exit));
909
        assert!(supp_exit_no_iso_c.supports(&targ_exit_iso2));
910
    }
911

            
912
    #[test]
913
    fn buildpath() {
914
        tor_rtcompat::test_with_all_runtimes!(|rt| async move {
915
            let mut rng = testing_rng();
916
            let netdir = testnet::construct_netdir().unwrap_if_sufficient().unwrap();
917
            let di = (&netdir).into();
918
            let config = crate::PathConfig::default();
919
            let statemgr = TestingStateMgr::new();
920
            let guards =
921
                tor_guardmgr::GuardMgr::new(rt.clone(), statemgr.clone(), &TestConfig::default())
922
                    .unwrap();
923
            guards.install_test_netdir(&netdir);
924
            let now = SystemTime::now();
925

            
926
            // Only doing basic tests for now.  We'll test the path
927
            // building code a lot more closely in the tests for TorPath
928
            // and friends.
929

            
930
            #[cfg(all(feature = "vanguards", feature = "hs-common"))]
931
            let vanguards =
932
                VanguardMgr::new(&Default::default(), rt.clone(), statemgr, false).unwrap();
933

            
934
            // First, a one-hop directory circuit
935
            let (p_dir, u_dir, _, _) = TargetTunnelUsage::Dir
936
                .build_path(
937
                    &mut rng,
938
                    di,
939
                    &guards,
940
                    #[cfg(all(feature = "vanguards", feature = "hs-common"))]
941
                    &vanguards,
942
                    &config,
943
                    now,
944
                )
945
                .unwrap();
946
            assert!(matches!(u_dir, SupportedTunnelUsage::Dir));
947
            assert_eq!(p_dir.len(), 1);
948

            
949
            // Now an exit circuit, to port 995.
950
            let tok1 = IsolationToken::new();
951
            let isolation = StreamIsolationBuilder::new()
952
                .owner_token(tok1)
953
                .build()
954
                .unwrap();
955

            
956
            let exit_usage = TargetTunnelUsage::Exit {
957
                ports: vec![TargetPort::ipv4(995)],
958
                isolation: isolation.clone(),
959
                country_code: None,
960
                require_stability: false,
961
            };
962
            let (p_exit, u_exit, _, _) = exit_usage
963
                .build_path(
964
                    &mut rng,
965
                    di,
966
                    &guards,
967
                    #[cfg(all(feature = "vanguards", feature = "hs-common"))]
968
                    &vanguards,
969
                    &config,
970
                    now,
971
                )
972
                .unwrap();
973
            assert!(matches!(
974
                u_exit,
975
                SupportedTunnelUsage::Exit {
976
                    isolation: ref iso,
977
                    ..
978
                } if iso.isol_eq(&Some(isolation))
979
            ));
980
            assert!(u_exit.supports(&exit_usage));
981
            assert_eq!(p_exit.len(), 3);
982

            
983
            // Now try testing circuits.
984
            let (path, usage, _, _) = TargetTunnelUsage::TimeoutTesting
985
                .build_path(
986
                    &mut rng,
987
                    di,
988
                    &guards,
989
                    #[cfg(all(feature = "vanguards", feature = "hs-common"))]
990
                    &vanguards,
991
                    &config,
992
                    now,
993
                )
994
                .unwrap();
995
            let path = match OwnedPath::try_from(&path).unwrap() {
996
                OwnedPath::ChannelOnly(_) => panic!("Impossible path type."),
997
                OwnedPath::Normal(p) => p,
998
            };
999
            assert_eq!(path.len(), 3);
            // Make sure that the usage is correct.
            let last_relay = netdir.by_ids(&path[2]).unwrap();
            let policy = ExitPolicy::from_relay(&last_relay);
            // We'll always get exits for these, since we try to build
            // paths with an exit if there are any exits.
            assert!(policy.allows_some_port());
            assert!(last_relay.low_level_details().policies_allow_some_port());
            assert_isoleq!(
                usage,
                SupportedTunnelUsage::Exit {
                    policy,
                    isolation: None,
                    country_code: None,
                    all_relays_stable: true
                }
            );
        });
    }
    #[test]
    fn build_testing_noexit() {
        // Here we'll try to build paths for testing circuits on a network
        // with no exits.
        tor_rtcompat::test_with_all_runtimes!(|rt| async move {
            let mut rng = testing_rng();
            let netdir = testnet::construct_custom_netdir(|_idx, bld, _| {
                bld.md.parse_ipv4_policy("reject 1-65535").unwrap();
            })
            .unwrap()
            .unwrap_if_sufficient()
            .unwrap();
            let di = (&netdir).into();
            let config = crate::PathConfig::default();
            let statemgr = TestingStateMgr::new();
            let guards =
                tor_guardmgr::GuardMgr::new(rt.clone(), statemgr.clone(), &TestConfig::default())
                    .unwrap();
            guards.install_test_netdir(&netdir);
            let now = SystemTime::now();
            #[cfg(all(feature = "vanguards", feature = "hs-common"))]
            let vanguards =
                VanguardMgr::new(&Default::default(), rt.clone(), statemgr, false).unwrap();
            let (path, usage, _, _) = TargetTunnelUsage::TimeoutTesting
                .build_path(
                    &mut rng,
                    di,
                    &guards,
                    #[cfg(all(feature = "vanguards", feature = "hs-common"))]
                    &vanguards,
                    &config,
                    now,
                )
                .unwrap();
            assert_eq!(path.len(), 3);
            assert_isoleq!(usage, SupportedTunnelUsage::NoUsage);
        });
    }
    #[test]
    fn display_target_ports() {
        let ports = [];
        assert_eq!(TargetPorts::from(&ports[..]).to_string(), "[]");
        let ports = [TargetPort::ipv4(80)];
        assert_eq!(TargetPorts::from(&ports[..]).to_string(), "80v4");
        let ports = [TargetPort::ipv4(80), TargetPort::ipv6(443)];
        assert_eq!(TargetPorts::from(&ports[..]).to_string(), "[80v4,443v6]");
    }
}