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::{instrument, 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
93
    fn from(ports: &'_ [TargetPort]) -> Self {
67
93
        TargetPorts(ports.into())
68
93
    }
69
}
70

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

            
90
impl ExitPolicy {
91
    /// Make a new exit policy from a given Relay.
92
31
    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
31
        Self {
97
31
            v4: relay.low_level_details().ipv4_policy(),
98
31
            v6: relay.low_level_details().ipv6_policy(),
99
31
        }
100
31
    }
101

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

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

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

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

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

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

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

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

            
302
6
                builder.require_stability(*require_stability);
303

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

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

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

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

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

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

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

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

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

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

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

            
578
    /// True if this usage is compatible with a long-lived circuit.
579
    ///
580
    /// (We leave long-lived circuits alive until they have been disused for a long time,
581
    /// and never expire them for being dirty.)
582
224
    pub(crate) fn is_long_lived(&self) -> bool {
583
        use SupportedTunnelUsage::*;
584
224
        match self {
585
            // We _could_ say "true" for these, but in practice we are done with them pretty
586
            // quickly, and we can make another cheaply.
587
            //
588
            // If we did make these "true", we'd want to give them a different lifetime.
589
            Dir => false,
590
            #[cfg(feature = "specific-relay")]
591
            DirSpecificTarget(_) => false,
592

            
593
224
            Exit { isolation, .. } => {
594
                // For Exit usage, we just care about whether the isolation enables long-lived circuits.
595
224
                isolation
596
224
                    .as_ref()
597
224
                    .is_some_and(StreamIsolation::enables_long_lived_circuits)
598
            }
599
            NoUsage => {
600
                // If the circuit is suitable for nothing, it is not long-lived.
601
                false
602
            }
603
            #[cfg(feature = "hs-common")]
604
            HsOnly => {
605
                // This circuit's lifetime is managed by the hspool code, and later by the hsclient
606
                // code.  We will not manage it within the mgr.rs code.
607
                false
608
            }
609
        }
610
224
    }
611
}
612

            
613
#[cfg(test)]
614
pub(crate) mod test {
615
    #![allow(clippy::unwrap_used)]
616
    use super::*;
617
    use crate::isolation::test::{IsolationTokenEq, assert_isoleq};
618
    use crate::isolation::{IsolationToken, StreamIsolationBuilder};
619
    use crate::path::OwnedPath;
620
    use tor_basic_utils::test_rng::testing_rng;
621
    use tor_guardmgr::TestConfig;
622
    use tor_llcrypto::pk::ed25519::Ed25519Identity;
623
    use tor_netdir::testnet;
624
    use tor_persist::TestingStateMgr;
625
    use web_time_compat::SystemTimeExt;
626

            
627
    impl IsolationTokenEq for TargetTunnelUsage {
628
        fn isol_eq(&self, other: &Self) -> bool {
629
            use TargetTunnelUsage::*;
630
            match (self, other) {
631
                (Dir, Dir) => true,
632
                (
633
                    Exit {
634
                        ports: p1,
635
                        isolation: is1,
636
                        country_code: cc1,
637
                        ..
638
                    },
639
                    Exit {
640
                        ports: p2,
641
                        isolation: is2,
642
                        country_code: cc2,
643
                        ..
644
                    },
645
                ) => p1 == p2 && cc1 == cc2 && is1.isol_eq(is2),
646
                (TimeoutTesting, TimeoutTesting) => true,
647
                (
648
                    Preemptive {
649
                        port: p1,
650
                        circs: c1,
651
                        ..
652
                    },
653
                    Preemptive {
654
                        port: p2,
655
                        circs: c2,
656
                        ..
657
                    },
658
                ) => p1 == p2 && c1 == c2,
659
                _ => false,
660
            }
661
        }
662
    }
663

            
664
    impl IsolationTokenEq for SupportedTunnelUsage {
665
        fn isol_eq(&self, other: &Self) -> bool {
666
            use SupportedTunnelUsage::*;
667
            match (self, other) {
668
                (Dir, Dir) => true,
669
                (
670
                    Exit {
671
                        policy: p1,
672
                        isolation: is1,
673
                        country_code: cc1,
674
                        ..
675
                    },
676
                    Exit {
677
                        policy: p2,
678
                        isolation: is2,
679
                        country_code: cc2,
680
                        ..
681
                    },
682
                ) => p1 == p2 && is1.isol_eq(is2) && cc1 == cc2,
683
                (NoUsage, NoUsage) => true,
684
                _ => false,
685
            }
686
        }
687
    }
688

            
689
    #[test]
690
    fn exit_policy() {
691
        use tor_netdir::testnet::construct_custom_netdir;
692
        use tor_netdoc::types::relay_flags::RelayFlag;
693

            
694
        let network = construct_custom_netdir(|idx, nb, _| {
695
            if (0x21..0x27).contains(&idx) {
696
                nb.rs.add_flags(RelayFlag::BadExit);
697
            }
698
        })
699
        .unwrap()
700
        .unwrap_if_sufficient()
701
        .unwrap();
702

            
703
        // Nodes with ID 0x0a through 0x13 and 0x1e through 0x27 are
704
        // exits.  Odd-numbered ones allow only ports 80 and 443;
705
        // even-numbered ones allow all ports.  Nodes with ID 0x21
706
        // through 0x27 are bad exits.
707
        let id_noexit: Ed25519Identity = [0x05; 32].into();
708
        let id_webexit: Ed25519Identity = [0x11; 32].into();
709
        let id_fullexit: Ed25519Identity = [0x20; 32].into();
710
        let id_badexit: Ed25519Identity = [0x25; 32].into();
711

            
712
        let not_exit = network.by_id(&id_noexit).unwrap();
713
        let web_exit = network.by_id(&id_webexit).unwrap();
714
        let full_exit = network.by_id(&id_fullexit).unwrap();
715
        let bad_exit = network.by_id(&id_badexit).unwrap();
716

            
717
        let ep_none = ExitPolicy::from_relay(&not_exit);
718
        let ep_web = ExitPolicy::from_relay(&web_exit);
719
        let ep_full = ExitPolicy::from_relay(&full_exit);
720
        let ep_bad = ExitPolicy::from_relay(&bad_exit);
721

            
722
        assert!(!ep_none.allows_port(TargetPort::ipv4(80)));
723
        assert!(!ep_none.allows_port(TargetPort::ipv4(9999)));
724

            
725
        assert!(ep_web.allows_port(TargetPort::ipv4(80)));
726
        assert!(ep_web.allows_port(TargetPort::ipv4(443)));
727
        assert!(!ep_web.allows_port(TargetPort::ipv4(9999)));
728

            
729
        assert!(ep_full.allows_port(TargetPort::ipv4(80)));
730
        assert!(ep_full.allows_port(TargetPort::ipv4(443)));
731
        assert!(ep_full.allows_port(TargetPort::ipv4(9999)));
732

            
733
        assert!(!ep_bad.allows_port(TargetPort::ipv4(80)));
734

            
735
        // Note that nobody in the testdir::network allows ipv6.
736
        assert!(!ep_none.allows_port(TargetPort::ipv6(80)));
737
        assert!(!ep_web.allows_port(TargetPort::ipv6(80)));
738
        assert!(!ep_full.allows_port(TargetPort::ipv6(80)));
739
        assert!(!ep_bad.allows_port(TargetPort::ipv6(80)));
740

            
741
        // Check is_supported_by while we're here.
742
        assert!(TargetPort::ipv4(80).is_supported_by(&web_exit.low_level_details()));
743
        assert!(!TargetPort::ipv6(80).is_supported_by(&web_exit.low_level_details()));
744
        assert!(!TargetPort::ipv6(80).is_supported_by(&bad_exit.low_level_details()));
745
    }
746

            
747
    #[test]
748
    fn usage_ops() {
749
        // Make an exit-policy object that allows web on IPv4 and
750
        // smtp on IPv6.
751
        let policy = ExitPolicy {
752
            v4: Arc::new("accept 80,443".parse().unwrap()),
753
            v6: Arc::new("accept 23".parse().unwrap()),
754
        };
755
        let tok1 = IsolationToken::new();
756
        let tok2 = IsolationToken::new();
757
        let isolation = StreamIsolationBuilder::new()
758
            .owner_token(tok1)
759
            .build()
760
            .unwrap();
761
        let isolation2 = StreamIsolationBuilder::new()
762
            .owner_token(tok2)
763
            .build()
764
            .unwrap();
765

            
766
        let supp_dir = SupportedTunnelUsage::Dir;
767
        let targ_dir = TargetTunnelUsage::Dir;
768
        let supp_exit = SupportedTunnelUsage::Exit {
769
            policy: policy.clone(),
770
            isolation: Some(isolation.clone()),
771
            country_code: None,
772
            all_relays_stable: true,
773
        };
774
        let supp_exit_iso2 = SupportedTunnelUsage::Exit {
775
            policy: policy.clone(),
776
            isolation: Some(isolation2.clone()),
777
            country_code: None,
778
            all_relays_stable: true,
779
        };
780
        let supp_exit_no_iso = SupportedTunnelUsage::Exit {
781
            policy,
782
            isolation: None,
783
            country_code: None,
784
            all_relays_stable: true,
785
        };
786
        let supp_none = SupportedTunnelUsage::NoUsage;
787

            
788
        let targ_80_v4 = TargetTunnelUsage::Exit {
789
            ports: vec![TargetPort::ipv4(80)],
790
            isolation: isolation.clone(),
791
            country_code: None,
792
            require_stability: false,
793
        };
794
        let targ_80_v4_iso2 = TargetTunnelUsage::Exit {
795
            ports: vec![TargetPort::ipv4(80)],
796
            isolation: isolation2,
797
            country_code: None,
798
            require_stability: false,
799
        };
800
        let targ_80_23_v4 = TargetTunnelUsage::Exit {
801
            ports: vec![TargetPort::ipv4(80), TargetPort::ipv4(23)],
802
            isolation: isolation.clone(),
803
            country_code: None,
804
            require_stability: false,
805
        };
806

            
807
        let targ_80_23_mixed = TargetTunnelUsage::Exit {
808
            ports: vec![TargetPort::ipv4(80), TargetPort::ipv6(23)],
809
            isolation: isolation.clone(),
810
            country_code: None,
811
            require_stability: false,
812
        };
813
        let targ_999_v6 = TargetTunnelUsage::Exit {
814
            ports: vec![TargetPort::ipv6(999)],
815
            isolation,
816
            country_code: None,
817
            require_stability: false,
818
        };
819
        let targ_testing = TargetTunnelUsage::TimeoutTesting;
820

            
821
        assert!(supp_dir.supports(&targ_dir));
822
        assert!(!supp_dir.supports(&targ_80_v4));
823
        assert!(!supp_exit.supports(&targ_dir));
824
        assert!(supp_exit.supports(&targ_80_v4));
825
        assert!(!supp_exit.supports(&targ_80_v4_iso2));
826
        assert!(supp_exit.supports(&targ_80_23_mixed));
827
        assert!(!supp_exit.supports(&targ_80_23_v4));
828
        assert!(!supp_exit.supports(&targ_999_v6));
829
        assert!(!supp_exit_iso2.supports(&targ_80_v4));
830
        assert!(supp_exit_iso2.supports(&targ_80_v4_iso2));
831
        assert!(supp_exit_no_iso.supports(&targ_80_v4));
832
        assert!(supp_exit_no_iso.supports(&targ_80_v4_iso2));
833
        assert!(!supp_exit_no_iso.supports(&targ_80_23_v4));
834
        assert!(!supp_none.supports(&targ_dir));
835
        assert!(!supp_none.supports(&targ_80_23_v4));
836
        assert!(!supp_none.supports(&targ_80_v4_iso2));
837
        assert!(!supp_dir.supports(&targ_testing));
838
        assert!(supp_exit.supports(&targ_testing));
839
        assert!(supp_exit_no_iso.supports(&targ_testing));
840
        assert!(supp_exit_iso2.supports(&targ_testing));
841
        assert!(supp_none.supports(&targ_testing));
842
    }
843

            
844
    #[test]
845
    fn restrict_mut() {
846
        let policy = ExitPolicy {
847
            v4: Arc::new("accept 80,443".parse().unwrap()),
848
            v6: Arc::new("accept 23".parse().unwrap()),
849
        };
850

            
851
        let tok1 = IsolationToken::new();
852
        let tok2 = IsolationToken::new();
853
        let isolation = StreamIsolationBuilder::new()
854
            .owner_token(tok1)
855
            .build()
856
            .unwrap();
857
        let isolation2 = StreamIsolationBuilder::new()
858
            .owner_token(tok2)
859
            .build()
860
            .unwrap();
861

            
862
        let supp_dir = SupportedTunnelUsage::Dir;
863
        let targ_dir = TargetTunnelUsage::Dir;
864
        let supp_exit = SupportedTunnelUsage::Exit {
865
            policy: policy.clone(),
866
            isolation: Some(isolation.clone()),
867
            country_code: None,
868
            all_relays_stable: true,
869
        };
870
        let supp_exit_iso2 = SupportedTunnelUsage::Exit {
871
            policy: policy.clone(),
872
            isolation: Some(isolation2.clone()),
873
            country_code: None,
874
            all_relays_stable: true,
875
        };
876
        let supp_exit_no_iso = SupportedTunnelUsage::Exit {
877
            policy,
878
            isolation: None,
879
            country_code: None,
880
            all_relays_stable: true,
881
        };
882
        let supp_none = SupportedTunnelUsage::NoUsage;
883
        let targ_exit = TargetTunnelUsage::Exit {
884
            ports: vec![TargetPort::ipv4(80)],
885
            isolation,
886
            country_code: None,
887
            require_stability: false,
888
        };
889
        let targ_exit_iso2 = TargetTunnelUsage::Exit {
890
            ports: vec![TargetPort::ipv4(80)],
891
            isolation: isolation2,
892
            country_code: None,
893
            require_stability: false,
894
        };
895
        let targ_testing = TargetTunnelUsage::TimeoutTesting;
896

            
897
        // not allowed, do nothing
898
        let mut supp_dir_c = supp_dir.clone();
899
        assert!(supp_dir_c.restrict_mut(&targ_exit).is_err());
900
        assert!(supp_dir_c.restrict_mut(&targ_testing).is_err());
901
        assert_isoleq!(supp_dir, supp_dir_c);
902

            
903
        let mut supp_exit_c = supp_exit.clone();
904
        assert!(supp_exit_c.restrict_mut(&targ_dir).is_err());
905
        assert_isoleq!(supp_exit, supp_exit_c);
906

            
907
        let mut supp_exit_c = supp_exit.clone();
908
        assert!(supp_exit_c.restrict_mut(&targ_exit_iso2).is_err());
909
        assert_isoleq!(supp_exit, supp_exit_c);
910

            
911
        let mut supp_exit_iso2_c = supp_exit_iso2.clone();
912
        assert!(supp_exit_iso2_c.restrict_mut(&targ_exit).is_err());
913
        assert_isoleq!(supp_exit_iso2, supp_exit_iso2_c);
914

            
915
        let mut supp_none_c = supp_none.clone();
916
        assert!(supp_none_c.restrict_mut(&targ_exit).is_err());
917
        assert!(supp_none_c.restrict_mut(&targ_dir).is_err());
918
        assert_isoleq!(supp_none_c, supp_none);
919

            
920
        // allowed but nothing to do
921
        let mut supp_dir_c = supp_dir.clone();
922
        supp_dir_c.restrict_mut(&targ_dir).unwrap();
923
        assert_isoleq!(supp_dir, supp_dir_c);
924

            
925
        let mut supp_exit_c = supp_exit.clone();
926
        supp_exit_c.restrict_mut(&targ_exit).unwrap();
927
        assert_isoleq!(supp_exit, supp_exit_c);
928

            
929
        let mut supp_exit_iso2_c = supp_exit_iso2.clone();
930
        supp_exit_iso2_c.restrict_mut(&targ_exit_iso2).unwrap();
931
        supp_none_c.restrict_mut(&targ_testing).unwrap();
932
        assert_isoleq!(supp_exit_iso2, supp_exit_iso2_c);
933

            
934
        let mut supp_none_c = supp_none.clone();
935
        supp_none_c.restrict_mut(&targ_testing).unwrap();
936
        assert_isoleq!(supp_none_c, supp_none);
937

            
938
        // allowed, do something
939
        let mut supp_exit_no_iso_c = supp_exit_no_iso.clone();
940
        supp_exit_no_iso_c.restrict_mut(&targ_exit).unwrap();
941
        assert!(supp_exit_no_iso_c.supports(&targ_exit));
942
        assert!(!supp_exit_no_iso_c.supports(&targ_exit_iso2));
943

            
944
        let mut supp_exit_no_iso_c = supp_exit_no_iso;
945
        supp_exit_no_iso_c.restrict_mut(&targ_exit_iso2).unwrap();
946
        assert!(!supp_exit_no_iso_c.supports(&targ_exit));
947
        assert!(supp_exit_no_iso_c.supports(&targ_exit_iso2));
948
    }
949

            
950
    #[test]
951
    fn buildpath() {
952
        tor_rtcompat::test_with_all_runtimes!(|rt| async move {
953
            let mut rng = testing_rng();
954
            let netdir = testnet::construct_netdir().unwrap_if_sufficient().unwrap();
955
            let di = (&netdir).into();
956
            let config = crate::PathConfig::default();
957
            let statemgr = TestingStateMgr::new();
958
            let guards =
959
                tor_guardmgr::GuardMgr::new(rt.clone(), statemgr.clone(), &TestConfig::default())
960
                    .unwrap();
961
            guards.install_test_netdir(&netdir);
962
            let now = SystemTime::get();
963

            
964
            // Only doing basic tests for now.  We'll test the path
965
            // building code a lot more closely in the tests for TorPath
966
            // and friends.
967

            
968
            #[cfg(all(feature = "vanguards", feature = "hs-common"))]
969
            let vanguards =
970
                VanguardMgr::new(&Default::default(), rt.clone(), statemgr, false).unwrap();
971

            
972
            // First, a one-hop directory circuit
973
            let (p_dir, u_dir, _, _) = TargetTunnelUsage::Dir
974
                .build_path(
975
                    &mut rng,
976
                    di,
977
                    &guards,
978
                    #[cfg(all(feature = "vanguards", feature = "hs-common"))]
979
                    &vanguards,
980
                    &config,
981
                    now,
982
                )
983
                .unwrap();
984
            assert!(matches!(u_dir, SupportedTunnelUsage::Dir));
985
            assert_eq!(p_dir.len(), 1);
986

            
987
            // Now an exit circuit, to port 995.
988
            let tok1 = IsolationToken::new();
989
            let isolation = StreamIsolationBuilder::new()
990
                .owner_token(tok1)
991
                .build()
992
                .unwrap();
993

            
994
            let exit_usage = TargetTunnelUsage::Exit {
995
                ports: vec![TargetPort::ipv4(995)],
996
                isolation: isolation.clone(),
997
                country_code: None,
998
                require_stability: false,
999
            };
            let (p_exit, u_exit, _, _) = exit_usage
                .build_path(
                    &mut rng,
                    di,
                    &guards,
                    #[cfg(all(feature = "vanguards", feature = "hs-common"))]
                    &vanguards,
                    &config,
                    now,
                )
                .unwrap();
            assert!(matches!(
                u_exit,
                SupportedTunnelUsage::Exit {
                    isolation: ref iso,
                    ..
                } if iso.isol_eq(&Some(isolation))
            ));
            assert!(u_exit.supports(&exit_usage));
            assert_eq!(p_exit.len(), 3);
            // Now try testing circuits.
            let (path, usage, _, _) = TargetTunnelUsage::TimeoutTesting
                .build_path(
                    &mut rng,
                    di,
                    &guards,
                    #[cfg(all(feature = "vanguards", feature = "hs-common"))]
                    &vanguards,
                    &config,
                    now,
                )
                .unwrap();
            let path = match OwnedPath::try_from(&path).unwrap() {
                OwnedPath::ChannelOnly(_) => panic!("Impossible path type."),
                OwnedPath::Normal(p) => p,
            };
            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::get();
            #[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]");
    }
}