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

            
3
use rand::Rng;
4
use serde::{Deserialize, Serialize};
5
use std::fmt::{self, Display};
6
use std::sync::atomic::{AtomicU64, Ordering};
7
use std::sync::Arc;
8
use tor_error::bad_api_usage;
9
use tracing::debug;
10

            
11
use crate::path::{dirpath::DirPathBuilder, exitpath::ExitPathBuilder, TorPath};
12
use tor_guardmgr::{GuardMgr, GuardMonitor, GuardUsable};
13
use tor_netdir::Relay;
14
use tor_netdoc::types::policy::PortPolicy;
15
use tor_rtcompat::Runtime;
16

            
17
use crate::mgr::{abstract_spec_find_supported, AbstractCirc, OpenEntry};
18
use crate::Result;
19

            
20
/// An exit policy, as supported by the last hop of a circuit.
21
13
#[derive(Clone, Debug, PartialEq)]
22
pub(crate) struct ExitPolicy {
23
    /// Permitted IPv4 ports.
24
    v4: Arc<PortPolicy>,
25
    /// Permitted IPv6 ports.
26
    v6: Arc<PortPolicy>,
27
}
28

            
29
/// A port that we want to connect to as a client.
30
///
31
/// Ordinarily, this is a TCP port, plus a flag to indicate whether we
32
/// must support IPv4 or IPv6.
33
#[derive(
34
59
    Clone, Copy, Debug, Deserialize, Eq, PartialEq, Hash, PartialOrd, Ord, Serialize, Default,
35
)]
36
pub struct TargetPort {
37
    /// True if this is a request to connect to an IPv6 address
38
    ipv6: bool,
39
    /// The port that the client wants to connect to
40
    port: u16,
41
}
42

            
43
impl TargetPort {
44
    /// Create a request to make sure that a circuit supports a given
45
    /// ipv4 exit port.
46
104
    pub fn ipv4(port: u16) -> TargetPort {
47
104
        TargetPort { ipv6: false, port }
48
104
    }
49

            
50
    /// Create a request to make sure that a circuit supports a given
51
    /// ipv6 exit port.
52
9
    pub fn ipv6(port: u16) -> TargetPort {
53
9
        TargetPort { ipv6: true, port }
54
9
    }
55

            
56
    /// Return true if this port is supported by the provided Relay.
57
49056
    pub fn is_supported_by(&self, r: &tor_netdir::Relay<'_>) -> bool {
58
49056
        if self.ipv6 {
59
2
            r.supports_exit_port_ipv6(self.port)
60
        } else {
61
49054
            r.supports_exit_port_ipv4(self.port)
62
        }
63
49056
    }
64
}
65

            
66
impl Display for TargetPort {
67
3
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
68
3
        write!(f, "{}{}", self.port, if self.ipv6 { "v6" } else { "v4" })
69
3
    }
70
}
71

            
72
/// Set of requested target ports, mostly for use in error reporting
73
///
74
/// Displays nicely.
75
#[derive(Debug, Clone, Default)]
76
pub struct TargetPorts(Vec<TargetPort>);
77

            
78
impl From<&'_ [TargetPort]> for TargetPorts {
79
3
    fn from(ports: &'_ [TargetPort]) -> Self {
80
3
        TargetPorts(ports.into())
81
3
    }
82
}
83

            
84
impl Display for TargetPorts {
85
3
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86
3
        let brackets = self.0.len() != 1;
87
3
        if brackets {
88
2
            write!(f, "[")?;
89
1
        }
90
3
        for (i, port) in self.0.iter().enumerate() {
91
3
            if i > 0 {
92
1
                write!(f, ",")?;
93
2
            }
94
3
            write!(f, "{}", port)?;
95
        }
96
3
        if brackets {
97
2
            write!(f, "]")?;
98
1
        }
99
3
        Ok(())
100
3
    }
101
}
102

            
103
/// A token used to isolate unrelated streams on different circuits.
104
///
105
/// When two streams are associated with different isolation tokens, they
106
/// can never share the same circuit.
107
///
108
/// Tokens created with [`IsolationToken::new`] are all different from
109
/// one another, and different from tokens created with
110
/// [`IsolationToken::no_isolation`]. However, tokens created with
111
/// [`IsolationToken::no_isolation`] are all equal to one another.
112
///
113
/// # Examples
114
///
115
/// Creating distinct isolation tokens:
116
///
117
/// ```rust
118
/// # use tor_circmgr::IsolationToken;
119
/// let token_1 = IsolationToken::new();
120
/// let token_2 = IsolationToken::new();
121
///
122
/// assert_ne!(token_1, token_2);
123
///
124
/// // Demonstrating the behaviour of no_isolation() tokens:
125
/// assert_ne!(token_1, IsolationToken::no_isolation());
126
/// assert_eq!(IsolationToken::no_isolation(), IsolationToken::no_isolation());
127
/// ```
128
///
129
/// Using an isolation token to route streams differently over the Tor network:
130
///
131
/// ```ignore
132
/// use arti_client::StreamPrefs;
133
///
134
/// let token_1 = IsolationToken::new();
135
/// let token_2 = IsolationToken::new();
136
///
137
/// let mut prefs_1 = StreamPrefs::new();
138
/// prefs_1.set_isolation_group(token_1);
139
///
140
/// let mut prefs_2 = StreamPrefs::new();
141
/// prefs_2.set_isolation_group(token_2);
142
///
143
/// // These two connections will come from different source IP addresses.
144
/// tor_client.connect(("example.com", 80), Some(prefs_1)).await?;
145
/// tor_client.connect(("example.com", 80), Some(prefs_2)).await?;
146
/// ```
147
// # Semver note
148
//
149
// This type is re-exported by `arti-client`: any changes to it must be
150
// reflected in `arti-client`'s version.
151
73
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
152
pub struct IsolationToken(u64);
153

            
154
#[allow(clippy::new_without_default)]
155
impl IsolationToken {
156
    /// Create a new IsolationToken, unequal to any other token this function
157
    /// has created.
158
    ///
159
    /// # Panics
160
    ///
161
    /// Panics if we have already allocated 2^64 isolation tokens: in that
162
    /// case, we have exhausted the space of possible tokens, and it is
163
    /// no longer possible to ensure isolation.
164
202
    pub fn new() -> Self {
165
202
        /// Internal counter used to generate different tokens each time
166
202
        static COUNTER: AtomicU64 = AtomicU64::new(1);
167
202
        // Ordering::Relaxed is fine because we don't care about causality, we just want a
168
202
        // different number each time
169
202
        let token = COUNTER.fetch_add(1, Ordering::Relaxed);
170
202
        assert!(token < u64::MAX);
171
202
        IsolationToken(token)
172
202
    }
173

            
174
    /// Create a new IsolationToken equal to every other token created
175
    /// with this function, but different from all tokens created with
176
    /// `new`.
177
    ///
178
    /// This can be used when no isolation is wanted for some streams.
179
13
    pub fn no_isolation() -> Self {
180
13
        IsolationToken(0)
181
13
    }
182
}
183

            
184
/// A set of information about how a stream should be isolated.
185
///
186
/// If two streams are isolated from one another, they may not share
187
/// a circuit.
188
28
#[derive(Copy, Clone, Eq, Debug, PartialEq, PartialOrd, Ord, derive_builder::Builder)]
189
pub struct StreamIsolation {
190
    /// Any isolation token set on the stream.
191
    #[builder(default = "IsolationToken::no_isolation()")]
192
    stream_token: IsolationToken,
193
    /// Any additional isolation token set on an object that "owns" this
194
    /// stream.  This is typically owned by a `TorClient`.
195
    #[builder(default = "IsolationToken::no_isolation()")]
196
    owner_token: IsolationToken,
197
}
198

            
199
impl StreamIsolation {
200
    /// Construct a new StreamIsolation with no isolation enabled.
201
2
    pub fn no_isolation() -> Self {
202
2
        StreamIsolationBuilder::new()
203
2
            .build()
204
2
            .expect("Bug constructing StreamIsolation")
205
2
    }
206

            
207
    /// Return a new StreamIsolationBuilder for constructing
208
    /// StreamIsolation objects.
209
3
    pub fn builder() -> StreamIsolationBuilder {
210
3
        StreamIsolationBuilder::new()
211
3
    }
212

            
213
    /// Return true if this StreamIsolation can share a circuit with
214
    /// `other`.
215
21
    fn may_share_circuit(&self, other: &StreamIsolation) -> bool {
216
21
        self == other
217
21
    }
218
}
219

            
220
impl StreamIsolationBuilder {
221
    /// Construct a builder with no items set.
222
10
    pub fn new() -> Self {
223
10
        StreamIsolationBuilder::default()
224
10
    }
225
}
226

            
227
impl ExitPolicy {
228
    /// Make a new exit policy from a given Relay.
229
11
    pub(crate) fn from_relay(relay: &Relay<'_>) -> Self {
230
11
        Self {
231
11
            v4: relay.ipv4_policy(),
232
11
            v6: relay.ipv6_policy(),
233
11
        }
234
11
    }
235

            
236
    /// Return true if a given port is contained in this ExitPolicy.
237
39
    fn allows_port(&self, p: TargetPort) -> bool {
238
39
        let policy = if p.ipv6 { &self.v6 } else { &self.v4 };
239
39
        policy.allows_port(p.port)
240
39
    }
241

            
242
    /// Returns true if this policy allows any ports at all.
243
3
    fn allows_some_port(&self) -> bool {
244
3
        self.v4.allows_some_port() || self.v6.allows_some_port()
245
3
    }
246
}
247

            
248
/// The purpose for which a circuit is being created.
249
///
250
/// This type should stay internal to the circmgr crate for now: we'll probably
251
/// want to refactor it a lot.
252
7
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
253
pub(crate) enum TargetCircUsage {
254
    /// Use for BEGINDIR-based non-anonymous directory connections
255
    Dir,
256
    /// Use to exit to one or more ports.
257
    Exit {
258
        /// List of ports the circuit has to allow.
259
        ///
260
        /// If this list of ports is empty, then the circuit doesn't need
261
        /// to support any particular port, but it still needs to be an exit.
262
        ports: Vec<TargetPort>,
263
        /// Isolation group the circuit shall be part of
264
        isolation: StreamIsolation,
265
    },
266
    /// For a circuit is only used for the purpose of building it.
267
    TimeoutTesting,
268
    /// For internal usage only: build a circuit preemptively, to reduce wait times.
269
    ///
270
    /// # Warning
271
    ///
272
    /// This **MUST NOT** be used by code outside of the preemptive circuit predictor. In
273
    /// particular, this usage doesn't support stream isolation, so using it to ask for
274
    /// circuits (for example, by passing it to `get_or_launch`) could be unsafe!
275
    Preemptive {
276
        /// A port the circuit has to allow, if specified.
277
        ///
278
        /// If this is `None`, we just want a circuit capable of doing DNS resolution.
279
        port: Option<TargetPort>,
280
        /// The number of exit circuits needed for a port
281
        circs: usize,
282
    },
283
}
284

            
285
/// The purposes for which a circuit is usable.
286
///
287
/// This type should stay internal to the circmgr crate for now: we'll probably
288
/// want to refactor it a lot.
289
18
#[derive(Clone, Debug, PartialEq)]
290
pub(crate) enum SupportedCircUsage {
291
    /// Usable for BEGINDIR-based non-anonymous directory connections
292
    Dir,
293
    /// Usable to exit to a set of ports.
294
    Exit {
295
        /// Exit policy of the circuit
296
        policy: ExitPolicy,
297
        /// Isolation group the circuit is part of. None when the circuit is not yet assigned to an
298
        /// isolation group.
299
        isolation: Option<StreamIsolation>,
300
    },
301
    /// This circuit is not suitable for any usage.
302
    NoUsage,
303
}
304

            
305
impl TargetCircUsage {
306
    /// Construct path for a given circuit purpose; return it and the
307
    /// usage that it _actually_ supports.
308
4
    pub(crate) fn build_path<'a, R: Rng, RT: Runtime>(
309
4
        &self,
310
4
        rng: &mut R,
311
4
        netdir: crate::DirInfo<'a>,
312
4
        guards: Option<&GuardMgr<RT>>,
313
4
        config: &crate::PathConfig,
314
4
    ) -> Result<(
315
4
        TorPath<'a>,
316
4
        SupportedCircUsage,
317
4
        Option<GuardMonitor>,
318
4
        Option<GuardUsable>,
319
4
    )> {
320
4
        match self {
321
            TargetCircUsage::Dir => {
322
1
                let (path, mon, usable) = DirPathBuilder::new().pick_path(rng, netdir, guards)?;
323
1
                Ok((path, SupportedCircUsage::Dir, mon, usable))
324
            }
325
            TargetCircUsage::Preemptive { port, .. } => {
326
                // FIXME(eta): this is copypasta from `TargetCircUsage::Exit`.
327
                let (path, mon, usable) = ExitPathBuilder::from_target_ports(port.iter().copied())
328
                    .pick_path(rng, netdir, guards, config)?;
329
                let policy = path
330
                    .exit_policy()
331
                    .expect("ExitPathBuilder gave us a one-hop circuit?");
332
                Ok((
333
                    path,
334
                    SupportedCircUsage::Exit {
335
                        policy,
336
                        isolation: None,
337
                    },
338
                    mon,
339
                    usable,
340
                ))
341
            }
342
            TargetCircUsage::Exit {
343
1
                ports: p,
344
1
                isolation,
345
            } => {
346
1
                let (path, mon, usable) = ExitPathBuilder::from_target_ports(p.clone())
347
1
                    .pick_path(rng, netdir, guards, config)?;
348
1
                let policy = path
349
1
                    .exit_policy()
350
1
                    .expect("ExitPathBuilder gave us a one-hop circuit?");
351
1
                Ok((
352
1
                    path,
353
1
                    SupportedCircUsage::Exit {
354
1
                        policy,
355
1
                        isolation: Some(*isolation),
356
1
                    },
357
1
                    mon,
358
1
                    usable,
359
1
                ))
360
            }
361
            TargetCircUsage::TimeoutTesting => {
362
2
                let (path, mon, usable) = ExitPathBuilder::for_timeout_testing()
363
2
                    .pick_path(rng, netdir, guards, config)?;
364
2
                let policy = path.exit_policy();
365
2
                let usage = match policy {
366
1
                    Some(policy) if policy.allows_some_port() => SupportedCircUsage::Exit {
367
1
                        policy,
368
1
                        isolation: None,
369
1
                    },
370
1
                    _ => SupportedCircUsage::NoUsage,
371
                };
372

            
373
2
                Ok((path, usage, mon, usable))
374
            }
375
        }
376
4
    }
377
}
378

            
379
impl crate::mgr::AbstractSpec for SupportedCircUsage {
380
    type Usage = TargetCircUsage;
381

            
382
    fn supports(&self, target: &TargetCircUsage) -> bool {
383
        use SupportedCircUsage::*;
384
41
        match (self, target) {
385
1
            (Dir, TargetCircUsage::Dir) => true,
386
            (
387
                Exit {
388
21
                    policy: p1,
389
21
                    isolation: i1,
390
21
                },
391
21
                TargetCircUsage::Exit {
392
21
                    ports: p2,
393
21
                    isolation: i2,
394
21
                },
395
21
            ) => {
396
21
                i1.map(|i1| i1.may_share_circuit(i2)).unwrap_or(true)
397
20
                    && p2.iter().all(|port| p1.allows_port(*port))
398
            }
399
9
            (Exit { policy, isolation }, TargetCircUsage::Preemptive { port, .. }) => {
400
9
                if isolation.is_some() {
401
                    // If the circuit has a stream isolation token, we might not be able to use it
402
                    // for new streams that don't share it.
403
                    return false;
404
9
                }
405
9
                if let Some(p) = port {
406
6
                    policy.allows_port(*p)
407
                } else {
408
3
                    true
409
                }
410
            }
411
4
            (Exit { .. } | NoUsage, TargetCircUsage::TimeoutTesting) => true,
412
6
            (_, _) => false,
413
        }
414
41
    }
415

            
416
    fn restrict_mut(&mut self, usage: &TargetCircUsage) -> Result<()> {
417
        use SupportedCircUsage::*;
418

            
419
14
        match (self, usage) {
420
1
            (Dir, TargetCircUsage::Dir) => Ok(()),
421
            // This usage is only used to create circuits preemptively, and doesn't actually
422
            // correspond to any streams; accordingly, we don't need to modify the circuit's
423
            // acceptable usage at all.
424
            (Exit { .. }, TargetCircUsage::Preemptive { .. }) => Ok(()),
425
            (
426
                Exit {
427
6
                    isolation: ref mut i1,
428
6
                    ..
429
6
                },
430
6
                TargetCircUsage::Exit { isolation: i2, .. },
431
6
            ) if i1.map(|i1| i1.may_share_circuit(i2)).unwrap_or(true) => {
432
4
                // Once we have more complex isolation, this assignment
433
4
                // won't be correct.
434
4
                *i1 = Some(*i2);
435
4
                Ok(())
436
            }
437
            (Exit { .. }, TargetCircUsage::Exit { .. }) => {
438
2
                Err(bad_api_usage!("Isolation not compatible").into())
439
            }
440
2
            (Exit { .. } | NoUsage, TargetCircUsage::TimeoutTesting) => Ok(()),
441
5
            (_, _) => Err(bad_api_usage!("Mismatched usage types").into()),
442
        }
443
14
    }
444

            
445
8
    fn find_supported<'a, 'b, C: AbstractCirc>(
446
8
        list: impl Iterator<Item = &'b mut OpenEntry<Self, C>>,
447
8
        usage: &TargetCircUsage,
448
8
    ) -> Vec<&'b mut OpenEntry<Self, C>> {
449
8
        match usage {
450
5
            TargetCircUsage::Preemptive { circs, .. } => {
451
5
                let supported = abstract_spec_find_supported(list, usage);
452
5
                // We need to have at least two circuits that support `port` in order
453
5
                // to reuse them; otherwise, we must create a new circuit, so
454
5
                // that we get closer to having two circuits.
455
5
                debug!(
456
                    "preemptive usage {:?} matches {} active circuits",
457
                    usage,
458
                    supported.len()
459
                );
460
5
                if supported.len() >= *circs {
461
2
                    supported
462
                } else {
463
3
                    vec![]
464
                }
465
            }
466
3
            _ => abstract_spec_find_supported(list, usage),
467
        }
468
8
    }
469
}
470

            
471
#[cfg(test)]
472
mod test {
473
    #![allow(clippy::unwrap_used)]
474
    use super::*;
475
    use crate::path::OwnedPath;
476
    use crate::test::OptDummyGuardMgr;
477
    use std::convert::TryFrom;
478
    use tor_linkspec::ChanTarget;
479
    use tor_netdir::testnet;
480

            
481
    #[test]
482
    fn exit_policy() {
483
        use tor_netdir::testnet::construct_custom_netdir;
484
        use tor_netdoc::doc::netstatus::RelayFlags;
485

            
486
        let network = construct_custom_netdir(|idx, nb| {
487
            if (0x21..0x27).contains(&idx) {
488
                nb.rs.add_flags(RelayFlags::BAD_EXIT);
489
            }
490
        })
491
        .unwrap()
492
        .unwrap_if_sufficient()
493
        .unwrap();
494

            
495
        // Nodes with ID 0x0a through 0x13 and 0x1e through 0x27 are
496
        // exits.  Odd-numbered ones allow only ports 80 and 443;
497
        // even-numbered ones allow all ports.  Nodes with ID 0x21
498
        // through 0x27 are bad exits.
499
        let id_noexit = [0x05; 32].into();
500
        let id_webexit = [0x11; 32].into();
501
        let id_fullexit = [0x20; 32].into();
502
        let id_badexit = [0x25; 32].into();
503

            
504
        let not_exit = network.by_id(&id_noexit).unwrap();
505
        let web_exit = network.by_id(&id_webexit).unwrap();
506
        let full_exit = network.by_id(&id_fullexit).unwrap();
507
        let bad_exit = network.by_id(&id_badexit).unwrap();
508

            
509
        let ep_none = ExitPolicy::from_relay(&not_exit);
510
        let ep_web = ExitPolicy::from_relay(&web_exit);
511
        let ep_full = ExitPolicy::from_relay(&full_exit);
512
        let ep_bad = ExitPolicy::from_relay(&bad_exit);
513

            
514
        assert!(!ep_none.allows_port(TargetPort::ipv4(80)));
515
        assert!(!ep_none.allows_port(TargetPort::ipv4(9999)));
516

            
517
        assert!(ep_web.allows_port(TargetPort::ipv4(80)));
518
        assert!(ep_web.allows_port(TargetPort::ipv4(443)));
519
        assert!(!ep_web.allows_port(TargetPort::ipv4(9999)));
520

            
521
        assert!(ep_full.allows_port(TargetPort::ipv4(80)));
522
        assert!(ep_full.allows_port(TargetPort::ipv4(443)));
523
        assert!(ep_full.allows_port(TargetPort::ipv4(9999)));
524

            
525
        assert!(!ep_bad.allows_port(TargetPort::ipv4(80)));
526

            
527
        // Note that nobody in the testdir::network allows ipv6.
528
        assert!(!ep_none.allows_port(TargetPort::ipv6(80)));
529
        assert!(!ep_web.allows_port(TargetPort::ipv6(80)));
530
        assert!(!ep_full.allows_port(TargetPort::ipv6(80)));
531
        assert!(!ep_bad.allows_port(TargetPort::ipv6(80)));
532

            
533
        // Check is_supported_by while we're here.
534
        assert!(TargetPort::ipv4(80).is_supported_by(&web_exit));
535
        assert!(!TargetPort::ipv6(80).is_supported_by(&web_exit));
536
        assert!(!TargetPort::ipv6(80).is_supported_by(&bad_exit));
537
    }
538

            
539
    #[test]
540
    fn usage_ops() {
541
        use crate::mgr::AbstractSpec;
542
        // Make an exit-policy object that allows web on IPv4 and
543
        // smtp on IPv6.
544
        let policy = ExitPolicy {
545
            v4: Arc::new("accept 80,443".parse().unwrap()),
546
            v6: Arc::new("accept 23".parse().unwrap()),
547
        };
548
        let tok1 = IsolationToken::new();
549
        let tok2 = IsolationToken::new();
550
        let isolation = StreamIsolationBuilder::new()
551
            .owner_token(tok1)
552
            .build()
553
            .unwrap();
554
        let isolation2 = StreamIsolationBuilder::new()
555
            .owner_token(tok2)
556
            .build()
557
            .unwrap();
558

            
559
        let supp_dir = SupportedCircUsage::Dir;
560
        let targ_dir = TargetCircUsage::Dir;
561
        let supp_exit = SupportedCircUsage::Exit {
562
            policy: policy.clone(),
563
            isolation: Some(isolation),
564
        };
565
        let supp_exit_iso2 = SupportedCircUsage::Exit {
566
            policy: policy.clone(),
567
            isolation: Some(isolation2),
568
        };
569
        let supp_exit_no_iso = SupportedCircUsage::Exit {
570
            policy,
571
            isolation: None,
572
        };
573
        let supp_none = SupportedCircUsage::NoUsage;
574

            
575
        let targ_80_v4 = TargetCircUsage::Exit {
576
            ports: vec![TargetPort::ipv4(80)],
577
            isolation,
578
        };
579
        let targ_80_v4_iso2 = TargetCircUsage::Exit {
580
            ports: vec![TargetPort::ipv4(80)],
581
            isolation: isolation2,
582
        };
583
        let targ_80_23_v4 = TargetCircUsage::Exit {
584
            ports: vec![TargetPort::ipv4(80), TargetPort::ipv4(23)],
585
            isolation,
586
        };
587
        let targ_80_23_mixed = TargetCircUsage::Exit {
588
            ports: vec![TargetPort::ipv4(80), TargetPort::ipv6(23)],
589
            isolation,
590
        };
591
        let targ_999_v6 = TargetCircUsage::Exit {
592
            ports: vec![TargetPort::ipv6(999)],
593
            isolation,
594
        };
595
        let targ_testing = TargetCircUsage::TimeoutTesting;
596

            
597
        assert!(supp_dir.supports(&targ_dir));
598
        assert!(!supp_dir.supports(&targ_80_v4));
599
        assert!(!supp_exit.supports(&targ_dir));
600
        assert!(supp_exit.supports(&targ_80_v4));
601
        assert!(!supp_exit.supports(&targ_80_v4_iso2));
602
        assert!(supp_exit.supports(&targ_80_23_mixed));
603
        assert!(!supp_exit.supports(&targ_80_23_v4));
604
        assert!(!supp_exit.supports(&targ_999_v6));
605
        assert!(!supp_exit_iso2.supports(&targ_80_v4));
606
        assert!(supp_exit_iso2.supports(&targ_80_v4_iso2));
607
        assert!(supp_exit_no_iso.supports(&targ_80_v4));
608
        assert!(supp_exit_no_iso.supports(&targ_80_v4_iso2));
609
        assert!(!supp_exit_no_iso.supports(&targ_80_23_v4));
610
        assert!(!supp_none.supports(&targ_dir));
611
        assert!(!supp_none.supports(&targ_80_23_v4));
612
        assert!(!supp_none.supports(&targ_80_v4_iso2));
613
        assert!(!supp_dir.supports(&targ_testing));
614
        assert!(supp_exit.supports(&targ_testing));
615
        assert!(supp_exit_no_iso.supports(&targ_testing));
616
        assert!(supp_exit_iso2.supports(&targ_testing));
617
        assert!(supp_none.supports(&targ_testing));
618
    }
619

            
620
    #[test]
621
    fn restrict_mut() {
622
        use crate::mgr::AbstractSpec;
623

            
624
        let policy = ExitPolicy {
625
            v4: Arc::new("accept 80,443".parse().unwrap()),
626
            v6: Arc::new("accept 23".parse().unwrap()),
627
        };
628

            
629
        let tok1 = IsolationToken::new();
630
        let tok2 = IsolationToken::new();
631
        let isolation = StreamIsolationBuilder::new()
632
            .owner_token(tok1)
633
            .build()
634
            .unwrap();
635
        let isolation2 = StreamIsolationBuilder::new()
636
            .owner_token(tok2)
637
            .build()
638
            .unwrap();
639

            
640
        let supp_dir = SupportedCircUsage::Dir;
641
        let targ_dir = TargetCircUsage::Dir;
642
        let supp_exit = SupportedCircUsage::Exit {
643
            policy: policy.clone(),
644
            isolation: Some(isolation),
645
        };
646
        let supp_exit_iso2 = SupportedCircUsage::Exit {
647
            policy: policy.clone(),
648
            isolation: Some(isolation2),
649
        };
650
        let supp_exit_no_iso = SupportedCircUsage::Exit {
651
            policy,
652
            isolation: None,
653
        };
654
        let supp_none = SupportedCircUsage::NoUsage;
655
        let targ_exit = TargetCircUsage::Exit {
656
            ports: vec![TargetPort::ipv4(80)],
657
            isolation,
658
        };
659
        let targ_exit_iso2 = TargetCircUsage::Exit {
660
            ports: vec![TargetPort::ipv4(80)],
661
            isolation: isolation2,
662
        };
663
        let targ_testing = TargetCircUsage::TimeoutTesting;
664

            
665
        // not allowed, do nothing
666
        let mut supp_dir_c = supp_dir.clone();
667
        assert!(supp_dir_c.restrict_mut(&targ_exit).is_err());
668
        assert!(supp_dir_c.restrict_mut(&targ_testing).is_err());
669
        assert_eq!(supp_dir, supp_dir_c);
670

            
671
        let mut supp_exit_c = supp_exit.clone();
672
        assert!(supp_exit_c.restrict_mut(&targ_dir).is_err());
673
        assert_eq!(supp_exit, supp_exit_c);
674

            
675
        let mut supp_exit_c = supp_exit.clone();
676
        assert!(supp_exit_c.restrict_mut(&targ_exit_iso2).is_err());
677
        assert_eq!(supp_exit, supp_exit_c);
678

            
679
        let mut supp_exit_iso2_c = supp_exit_iso2.clone();
680
        assert!(supp_exit_iso2_c.restrict_mut(&targ_exit).is_err());
681
        assert_eq!(supp_exit_iso2, supp_exit_iso2_c);
682

            
683
        let mut supp_none_c = supp_none.clone();
684
        assert!(supp_none_c.restrict_mut(&targ_exit).is_err());
685
        assert!(supp_none_c.restrict_mut(&targ_dir).is_err());
686
        assert_eq!(supp_none_c, supp_none);
687

            
688
        // allowed but nothing to do
689
        let mut supp_dir_c = supp_dir.clone();
690
        supp_dir_c.restrict_mut(&targ_dir).unwrap();
691
        assert_eq!(supp_dir, supp_dir_c);
692

            
693
        let mut supp_exit_c = supp_exit.clone();
694
        supp_exit_c.restrict_mut(&targ_exit).unwrap();
695
        assert_eq!(supp_exit, supp_exit_c);
696

            
697
        let mut supp_exit_iso2_c = supp_exit_iso2.clone();
698
        supp_exit_iso2_c.restrict_mut(&targ_exit_iso2).unwrap();
699
        supp_none_c.restrict_mut(&targ_testing).unwrap();
700
        assert_eq!(supp_exit_iso2, supp_exit_iso2_c);
701

            
702
        let mut supp_none_c = supp_none.clone();
703
        supp_none_c.restrict_mut(&targ_testing).unwrap();
704
        assert_eq!(supp_none_c, supp_none);
705

            
706
        // allowed, do something
707
        let mut supp_exit_no_iso_c = supp_exit_no_iso.clone();
708
        supp_exit_no_iso_c.restrict_mut(&targ_exit).unwrap();
709
        assert!(supp_exit_no_iso_c.supports(&targ_exit));
710
        assert!(!supp_exit_no_iso_c.supports(&targ_exit_iso2));
711

            
712
        let mut supp_exit_no_iso_c = supp_exit_no_iso;
713
        supp_exit_no_iso_c.restrict_mut(&targ_exit_iso2).unwrap();
714
        assert!(!supp_exit_no_iso_c.supports(&targ_exit));
715
        assert!(supp_exit_no_iso_c.supports(&targ_exit_iso2));
716
    }
717

            
718
    #[test]
719
    fn buildpath() {
720
        use crate::mgr::AbstractSpec;
721
        let mut rng = rand::thread_rng();
722
        let netdir = testnet::construct_netdir()
723
            .unwrap()
724
            .unwrap_if_sufficient()
725
            .unwrap();
726
        let di = (&netdir).into();
727
        let config = crate::PathConfig::default();
728
        let guards: OptDummyGuardMgr<'_> = None;
729

            
730
        // Only doing basic tests for now.  We'll test the path
731
        // building code a lot more closely in the tests for TorPath
732
        // and friends.
733

            
734
        // First, a one-hop directory circuit
735
        let (p_dir, u_dir, _, _) = TargetCircUsage::Dir
736
            .build_path(&mut rng, di, guards, &config)
737
            .unwrap();
738
        assert!(matches!(u_dir, SupportedCircUsage::Dir));
739
        assert_eq!(p_dir.len(), 1);
740

            
741
        // Now an exit circuit, to port 995.
742
        let tok1 = IsolationToken::new();
743
        let isolation = StreamIsolationBuilder::new()
744
            .owner_token(tok1)
745
            .build()
746
            .unwrap();
747

            
748
        let exit_usage = TargetCircUsage::Exit {
749
            ports: vec![TargetPort::ipv4(995)],
750
            isolation,
751
        };
752
        let (p_exit, u_exit, _, _) = exit_usage
753
            .build_path(&mut rng, di, guards, &config)
754
            .unwrap();
755
        assert!(matches!(
756
            u_exit,
757
            SupportedCircUsage::Exit {
758
                isolation: iso,
759
                ..
760
            } if iso == Some(isolation)
761
        ));
762
        assert!(u_exit.supports(&exit_usage));
763
        assert_eq!(p_exit.len(), 3);
764

            
765
        // Now try testing circuits.
766
        let (path, usage, _, _) = TargetCircUsage::TimeoutTesting
767
            .build_path(&mut rng, di, guards, &config)
768
            .unwrap();
769
        let path = match OwnedPath::try_from(&path).unwrap() {
770
            OwnedPath::ChannelOnly(_) => panic!("Impossible path type."),
771
            OwnedPath::Normal(p) => p,
772
        };
773
        assert_eq!(path.len(), 3);
774

            
775
        // Make sure that the usage is correct.
776
        let last_relay = netdir.by_id(path[2].ed_identity()).unwrap();
777
        let policy = ExitPolicy::from_relay(&last_relay);
778
        // We'll always get exits for these, since we try to build
779
        // paths with an exit if there are any exits.
780
        assert!(policy.allows_some_port());
781
        assert!(last_relay.policies_allow_some_port());
782
        assert_eq!(
783
            usage,
784
            SupportedCircUsage::Exit {
785
                policy,
786
                isolation: None
787
            }
788
        );
789
    }
790

            
791
    #[test]
792
    fn build_testing_noexit() {
793
        // Here we'll try to build paths for testing circuits on a network
794
        // with no exits.
795
        let mut rng = rand::thread_rng();
796
        let netdir = testnet::construct_custom_netdir(|_idx, bld| {
797
            bld.md.parse_ipv4_policy("reject 1-65535").unwrap();
798
        })
799
        .unwrap()
800
        .unwrap_if_sufficient()
801
        .unwrap();
802
        let di = (&netdir).into();
803
        let config = crate::PathConfig::default();
804
        let guards: OptDummyGuardMgr<'_> = None;
805

            
806
        let (path, usage, _, _) = TargetCircUsage::TimeoutTesting
807
            .build_path(&mut rng, di, guards, &config)
808
            .unwrap();
809
        assert_eq!(path.len(), 3);
810
        assert_eq!(usage, SupportedCircUsage::NoUsage);
811
    }
812

            
813
    #[test]
814
    fn build_isolation() {
815
        let no_isolation = StreamIsolation::no_isolation();
816
        let no_isolation2 = StreamIsolation::builder()
817
            .owner_token(IsolationToken::no_isolation())
818
            .stream_token(IsolationToken::no_isolation())
819
            .build()
820
            .unwrap();
821
        assert_eq!(no_isolation, no_isolation2);
822
        assert!(no_isolation.may_share_circuit(&no_isolation2));
823

            
824
        let tok = IsolationToken::new();
825
        let some_isolation = StreamIsolation::builder().owner_token(tok).build().unwrap();
826
        let some_isolation2 = StreamIsolation::builder()
827
            .stream_token(tok)
828
            .build()
829
            .unwrap();
830
        assert!(!no_isolation.may_share_circuit(&some_isolation));
831
        assert!(!no_isolation.may_share_circuit(&some_isolation2));
832
        assert!(!some_isolation.may_share_circuit(&some_isolation2));
833
        assert!(some_isolation.may_share_circuit(&some_isolation));
834
    }
835

            
836
    #[test]
837
    fn display_target_ports() {
838
        let ports = [];
839
        assert_eq!(TargetPorts::from(&ports[..]).to_string(), "[]");
840

            
841
        let ports = [TargetPort::ipv4(80)];
842
        assert_eq!(TargetPorts::from(&ports[..]).to_string(), "80v4");
843
        let ports = [TargetPort::ipv4(80), TargetPort::ipv6(443)];
844
        assert_eq!(TargetPorts::from(&ports[..]).to_string(), "[80v4,443v6]");
845
    }
846
}