1use rand::Rng;
4use std::fmt::{self, Display};
5use std::sync::Arc;
6use std::time::SystemTime;
7use tracing::{instrument, trace};
8#[cfg(not(feature = "geoip"))]
9use void::Void;
10
11use crate::path::{TorPath, dirpath::DirPathBuilder, exitpath::ExitPathBuilder};
12use tor_chanmgr::ChannelUsage;
13#[cfg(feature = "geoip")]
14use tor_error::internal;
15use tor_guardmgr::{GuardMgr, GuardMonitor, GuardUsable};
16use tor_netdir::Relay;
17use tor_netdoc::types::policy::PortPolicy;
18use tor_rtcompat::Runtime;
19#[cfg(feature = "hs-common")]
20use {crate::HsCircKind, crate::HsCircStemKind, crate::path::hspath::HsPathBuilder};
21
22#[cfg(feature = "specific-relay")]
23use tor_linkspec::{HasChanMethod, HasRelayIds};
24
25#[cfg(feature = "geoip")]
26use tor_geoip::CountryCode;
27#[cfg(not(feature = "geoip"))]
36pub(crate) type CountryCode = Void;
37
38#[cfg(any(feature = "specific-relay", feature = "hs-common"))]
39use tor_linkspec::OwnedChanTarget;
40
41#[cfg(all(feature = "vanguards", feature = "hs-common"))]
42use tor_guardmgr::vanguards::VanguardMgr;
43
44use crate::Result;
45use crate::isolation::{IsolationHelper, StreamIsolation};
46use crate::mgr::{AbstractTunnel, OpenEntry, RestrictionFailed};
47
48pub use tor_relay_selection::TargetPort;
49
50#[derive(Clone, Debug, PartialEq, Eq)]
52pub(crate) struct ExitPolicy {
53 v4: Arc<PortPolicy>,
55 v6: Arc<PortPolicy>,
57}
58
59#[derive(Debug, Clone, Default)]
63pub struct TargetPorts(Vec<TargetPort>);
64
65impl From<&'_ [TargetPort]> for TargetPorts {
66 fn from(ports: &'_ [TargetPort]) -> Self {
67 TargetPorts(ports.into())
68 }
69}
70
71impl Display for TargetPorts {
72 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73 let brackets = self.0.len() != 1;
74 if brackets {
75 write!(f, "[")?;
76 }
77 for (i, port) in self.0.iter().enumerate() {
78 if i > 0 {
79 write!(f, ",")?;
80 }
81 write!(f, "{}", port)?;
82 }
83 if brackets {
84 write!(f, "]")?;
85 }
86 Ok(())
87 }
88}
89
90impl ExitPolicy {
91 pub(crate) fn from_relay(relay: &Relay<'_>) -> Self {
93 Self {
97 v4: relay.low_level_details().ipv4_policy(),
98 v6: relay.low_level_details().ipv6_policy(),
99 }
100 }
101
102 #[cfg(test)]
104 pub(crate) fn from_target_ports(target_ports: &TargetPorts) -> Self {
105 let (v6_ports, v4_ports) = target_ports
106 .0
107 .iter()
108 .partition::<Vec<TargetPort>, _>(|port| port.ipv6);
109
110 Self {
111 v4: PortPolicy::from_allowed_port_list(v4_ports.iter().map(|port| port.port).collect())
112 .intern(),
113 v6: PortPolicy::from_allowed_port_list(v6_ports.iter().map(|port| port.port).collect())
114 .intern(),
115 }
116 }
117
118 fn allows_port(&self, p: TargetPort) -> bool {
120 let policy = if p.ipv6 { &self.v6 } else { &self.v4 };
121 policy.allows_port(p.port)
122 }
123
124 fn allows_some_port(&self) -> bool {
126 self.v4.allows_some_port() || self.v6.allows_some_port()
127 }
128}
129
130#[derive(Clone, Debug)]
135pub(crate) enum TargetTunnelUsage {
136 Dir,
138 Exit {
140 ports: Vec<TargetPort>,
145 isolation: StreamIsolation,
147 country_code: Option<CountryCode>,
149 require_stability: bool,
153 },
154 TimeoutTesting,
156 Preemptive {
164 port: Option<TargetPort>,
168 circs: usize,
170 require_stability: bool,
173 },
174 #[cfg(feature = "specific-relay")]
177 DirSpecificTarget(OwnedChanTarget),
178
179 #[cfg(feature = "hs-common")]
182 HsCircBase {
183 compatible_with_target: Option<OwnedChanTarget>,
190 stem_kind: HsCircStemKind,
192 circ_kind: Option<HsCircKind>,
195 },
196}
197
198#[derive(Clone, Debug)]
203pub(crate) enum SupportedTunnelUsage {
204 Dir,
206 Exit {
208 policy: ExitPolicy,
210 isolation: Option<StreamIsolation>,
213 country_code: Option<CountryCode>,
215 all_relays_stable: bool,
219 },
220 NoUsage,
222 #[cfg(feature = "hs-common")]
226 HsOnly,
227 #[cfg(feature = "specific-relay")]
230 DirSpecificTarget(OwnedChanTarget),
231}
232
233impl TargetTunnelUsage {
234 #[instrument(level = "trace", skip_all)]
237 pub(crate) fn build_path<'a, R: Rng, RT: Runtime>(
238 &self,
239 rng: &mut R,
240 netdir: crate::DirInfo<'a>,
241 guards: &GuardMgr<RT>,
242 #[cfg(all(feature = "vanguards", feature = "hs-common"))] vanguards: &VanguardMgr<RT>,
243 config: &crate::PathConfig,
244 now: SystemTime,
245 ) -> Result<(
246 TorPath<'a>,
247 SupportedTunnelUsage,
248 Option<GuardMonitor>,
249 Option<GuardUsable>,
250 )> {
251 match self {
252 TargetTunnelUsage::Dir => {
253 let (path, mon, usable) = DirPathBuilder::new().pick_path(guards)?;
254 Ok((path, SupportedTunnelUsage::Dir, Some(mon), Some(usable)))
255 }
256 TargetTunnelUsage::Preemptive {
257 port,
258 require_stability,
259 ..
260 } => {
261 let (path, mon, usable) = ExitPathBuilder::from_target_ports(port.iter().copied())
263 .require_stability(*require_stability)
264 .pick_path(rng, netdir, guards, config, now)?;
265 let policy = path
266 .exit_policy()
267 .expect("ExitPathBuilder gave us a one-hop circuit?");
268 #[cfg(feature = "geoip")]
269 let country_code = path.country_code();
270 #[cfg(not(feature = "geoip"))]
271 let country_code = None;
272 let all_relays_stable = path.appears_stable();
273 Ok((
274 path,
275 SupportedTunnelUsage::Exit {
276 policy,
277 isolation: None,
278 country_code,
279 all_relays_stable,
280 },
281 Some(mon),
282 Some(usable),
283 ))
284 }
285 TargetTunnelUsage::Exit {
286 ports: p,
287 isolation,
288 country_code,
289 require_stability,
290 } => {
291 #[cfg(feature = "geoip")]
292 let mut builder = if let Some(cc) = country_code {
293 ExitPathBuilder::in_given_country(*cc, p.clone())
294 } else {
295 ExitPathBuilder::from_target_ports(p.clone())
296 };
297 #[cfg(not(feature = "geoip"))]
298 let mut builder = ExitPathBuilder::from_target_ports(p.clone());
299
300 builder.require_stability(*require_stability);
301
302 let (path, mon, usable) = builder.pick_path(rng, netdir, guards, config, now)?;
303 let policy = path
304 .exit_policy()
305 .expect("ExitPathBuilder gave us a one-hop circuit?");
306
307 #[cfg(feature = "geoip")]
308 let resulting_cc = path.country_code();
309 #[cfg(feature = "geoip")]
310 if resulting_cc != *country_code {
311 internal!(
312 "asked for a country code of {:?}, got {:?}",
313 country_code,
314 resulting_cc
315 );
316 }
317 let all_relays_stable = path.appears_stable();
318
319 #[cfg(not(feature = "geoip"))]
320 let resulting_cc = *country_code; Ok((
322 path,
323 SupportedTunnelUsage::Exit {
324 policy,
325 isolation: Some(isolation.clone()),
326 country_code: resulting_cc,
327 all_relays_stable,
328 },
329 Some(mon),
330 Some(usable),
331 ))
332 }
333 TargetTunnelUsage::TimeoutTesting => {
334 let (path, mon, usable) = ExitPathBuilder::for_timeout_testing()
335 .require_stability(false)
336 .pick_path(rng, netdir, guards, config, now)?;
337 let policy = path.exit_policy();
338 #[cfg(feature = "geoip")]
339 let country_code = path.country_code();
340 #[cfg(not(feature = "geoip"))]
341 let country_code = None;
342 let usage = match policy {
343 Some(policy) if policy.allows_some_port() => SupportedTunnelUsage::Exit {
344 policy,
345 isolation: None,
346 country_code,
347 all_relays_stable: path.appears_stable(),
348 },
349 _ => SupportedTunnelUsage::NoUsage,
350 };
351
352 Ok((path, usage, Some(mon), Some(usable)))
353 }
354 #[cfg(feature = "specific-relay")]
355 TargetTunnelUsage::DirSpecificTarget(target) => {
356 let path = TorPath::new_one_hop_owned(target);
357 let usage = SupportedTunnelUsage::DirSpecificTarget(target.clone());
358 Ok((path, usage, None, None))
359 }
360 #[cfg(feature = "hs-common")]
361 TargetTunnelUsage::HsCircBase {
362 compatible_with_target,
363 stem_kind,
364 circ_kind,
365 } => {
366 let path_builder =
367 HsPathBuilder::new(compatible_with_target.clone(), *stem_kind, *circ_kind);
368 cfg_if::cfg_if! {
369 if #[cfg(all(feature = "vanguards", feature = "hs-common"))] {
370 let (path, mon, usable) = path_builder
371 .pick_path_with_vanguards::<_, RT>(rng, netdir, guards, vanguards, config, now)?;
372 } else {
373 let (path, mon, usable) = path_builder
374 .pick_path::<_, RT>(rng, netdir, guards, config, now)?;
375 }
376 };
377 let usage = SupportedTunnelUsage::HsOnly;
378 Ok((path, usage, Some(mon), Some(usable)))
379 }
380 }
381 }
382
383 #[cfg(test)]
386 pub(crate) fn new_from_ipv4_ports(ports: &[u16]) -> Self {
387 TargetTunnelUsage::Exit {
388 ports: ports.iter().map(|p| TargetPort::ipv4(*p)).collect(),
389 isolation: StreamIsolation::no_isolation(),
390 country_code: None,
391 require_stability: false,
392 }
393 }
394}
395
396#[cfg(feature = "specific-relay")]
399fn owned_targets_equivalent(a: &OwnedChanTarget, b: &OwnedChanTarget) -> bool {
400 a.same_relay_ids(b) && a.chan_method() == b.chan_method()
404}
405
406impl SupportedTunnelUsage {
407 pub(crate) fn supports(&self, target: &TargetTunnelUsage) -> bool {
412 use SupportedTunnelUsage::*;
413 match (self, target) {
414 (Dir, TargetTunnelUsage::Dir) => true,
415 (
416 Exit {
417 policy: p1,
418 isolation: i1,
419 country_code: cc1,
420 all_relays_stable,
421 },
422 TargetTunnelUsage::Exit {
423 ports: p2,
424 isolation: i2,
425 country_code: cc2,
426 require_stability,
427 },
428 ) => {
429 i1.as_ref()
432 .map(|i1| i1.compatible_same_type(i2))
433 .unwrap_or(true)
434 && (!require_stability || *all_relays_stable)
435 && p2.iter().all(|port| p1.allows_port(*port))
436 && (cc2.is_none() || cc1 == cc2)
437 }
438 (
439 Exit {
440 policy,
441 isolation,
442 all_relays_stable,
443 ..
444 },
445 TargetTunnelUsage::Preemptive {
446 port,
447 require_stability,
448 ..
449 },
450 ) => {
451 if *require_stability && !all_relays_stable {
454 return false;
455 }
456 if isolation.is_some() {
457 return false;
460 }
461 if let Some(p) = port {
464 policy.allows_port(*p)
465 } else {
466 true
467 }
468 }
469 (Exit { .. } | NoUsage, TargetTunnelUsage::TimeoutTesting) => true,
470 #[cfg(feature = "specific-relay")]
471 (DirSpecificTarget(a), TargetTunnelUsage::DirSpecificTarget(b)) => {
472 owned_targets_equivalent(a, b)
473 }
474 (_, _) => false,
475 }
476 }
477
478 pub(crate) fn restrict_mut(
485 &mut self,
486 usage: &TargetTunnelUsage,
487 ) -> std::result::Result<(), RestrictionFailed> {
488 use SupportedTunnelUsage::*;
489 match (self, usage) {
490 (Dir, TargetTunnelUsage::Dir) => Ok(()),
491 (Exit { .. }, TargetTunnelUsage::Preemptive { .. }) => Ok(()),
495 (
496 Exit {
497 isolation: isol1, ..
498 },
499 TargetTunnelUsage::Exit { isolation: i2, .. },
500 ) => {
501 if let Some(i1) = isol1 {
502 if let Some(new_isolation) = i1.join_same_type(i2) {
503 *isol1 = Some(new_isolation);
506 Ok(())
507 } else {
508 Err(RestrictionFailed::NotSupported)
509 }
510 } else {
511 *isol1 = Some(i2.clone());
513 Ok(())
514 }
515 }
516 (Exit { .. } | NoUsage, TargetTunnelUsage::TimeoutTesting) => Ok(()),
517 #[cfg(feature = "specific-relay")]
518 (DirSpecificTarget(a), TargetTunnelUsage::DirSpecificTarget(b))
519 if owned_targets_equivalent(a, b) =>
520 {
521 Ok(())
522 }
523 (_, _) => Err(RestrictionFailed::NotSupported),
524 }
525 }
526
527 pub(crate) fn find_supported<'a, 'b, C: AbstractTunnel>(
529 list: impl Iterator<Item = &'b mut OpenEntry<C>>,
530 usage: &TargetTunnelUsage,
531 ) -> Vec<&'b mut OpenEntry<C>> {
532 fn find_supported_internal<'a, 'b, C: AbstractTunnel>(
534 list: impl Iterator<Item = &'b mut OpenEntry<C>>,
535 usage: &TargetTunnelUsage,
536 ) -> Vec<&'b mut OpenEntry<C>> {
537 list.filter(|circ| circ.supports(usage)).collect()
538 }
539
540 match usage {
541 TargetTunnelUsage::Preemptive { circs, .. } => {
542 let supported = find_supported_internal(list, usage);
543 trace!(
547 "preemptive usage {:?} matches {} active circuits",
548 usage,
549 supported.len()
550 );
551 if supported.len() >= *circs {
552 supported
553 } else {
554 vec![]
555 }
556 }
557 _ => find_supported_internal(list, usage),
558 }
559 }
560
561 pub(crate) fn channel_usage(&self) -> ChannelUsage {
563 use ChannelUsage as CU;
564 use SupportedTunnelUsage as SCU;
565 match self {
566 SCU::Dir => CU::Dir,
567 #[cfg(feature = "specific-relay")]
568 SCU::DirSpecificTarget(_) => CU::Dir,
569 SCU::Exit { .. } => CU::UserTraffic,
570 SCU::NoUsage => CU::UselessCircuit,
571 #[cfg(feature = "hs-common")]
572 SCU::HsOnly => CU::UserTraffic,
573 }
574 }
575
576 pub(crate) fn is_long_lived(&self) -> bool {
581 use SupportedTunnelUsage::*;
582 match self {
583 Dir => false,
588 #[cfg(feature = "specific-relay")]
589 DirSpecificTarget(_) => false,
590
591 Exit { isolation, .. } => {
592 isolation
594 .as_ref()
595 .is_some_and(StreamIsolation::enables_long_lived_circuits)
596 }
597 NoUsage => {
598 false
600 }
601 #[cfg(feature = "hs-common")]
602 HsOnly => {
603 false
606 }
607 }
608 }
609}
610
611#[cfg(test)]
612pub(crate) mod test {
613 #![allow(clippy::unwrap_used)]
614 use super::*;
615 use crate::isolation::test::{IsolationTokenEq, assert_isoleq};
616 use crate::isolation::{IsolationToken, StreamIsolationBuilder};
617 use crate::path::OwnedPath;
618 use tor_basic_utils::test_rng::testing_rng;
619 use tor_guardmgr::TestConfig;
620 use tor_llcrypto::pk::ed25519::Ed25519Identity;
621 use tor_netdir::testnet;
622 use tor_persist::TestingStateMgr;
623 use web_time_compat::SystemTimeExt;
624
625 impl IsolationTokenEq for TargetTunnelUsage {
626 fn isol_eq(&self, other: &Self) -> bool {
627 use TargetTunnelUsage::*;
628 match (self, other) {
629 (Dir, Dir) => true,
630 (
631 Exit {
632 ports: p1,
633 isolation: is1,
634 country_code: cc1,
635 ..
636 },
637 Exit {
638 ports: p2,
639 isolation: is2,
640 country_code: cc2,
641 ..
642 },
643 ) => p1 == p2 && cc1 == cc2 && is1.isol_eq(is2),
644 (TimeoutTesting, TimeoutTesting) => true,
645 (
646 Preemptive {
647 port: p1,
648 circs: c1,
649 ..
650 },
651 Preemptive {
652 port: p2,
653 circs: c2,
654 ..
655 },
656 ) => p1 == p2 && c1 == c2,
657 _ => false,
658 }
659 }
660 }
661
662 impl IsolationTokenEq for SupportedTunnelUsage {
663 fn isol_eq(&self, other: &Self) -> bool {
664 use SupportedTunnelUsage::*;
665 match (self, other) {
666 (Dir, Dir) => true,
667 (
668 Exit {
669 policy: p1,
670 isolation: is1,
671 country_code: cc1,
672 ..
673 },
674 Exit {
675 policy: p2,
676 isolation: is2,
677 country_code: cc2,
678 ..
679 },
680 ) => p1 == p2 && is1.isol_eq(is2) && cc1 == cc2,
681 (NoUsage, NoUsage) => true,
682 _ => false,
683 }
684 }
685 }
686
687 #[test]
688 fn exit_policy() {
689 use tor_netdir::testnet::construct_custom_netdir;
690 use tor_netdoc::types::relay_flags::RelayFlag;
691
692 let network = construct_custom_netdir(|idx, nb, _| {
693 if (0x21..0x27).contains(&idx) {
694 nb.rs.add_flags(RelayFlag::BadExit);
695 }
696 })
697 .unwrap()
698 .unwrap_if_sufficient()
699 .unwrap();
700
701 let id_noexit: Ed25519Identity = [0x05; 32].into();
706 let id_webexit: Ed25519Identity = [0x11; 32].into();
707 let id_fullexit: Ed25519Identity = [0x20; 32].into();
708 let id_badexit: Ed25519Identity = [0x25; 32].into();
709
710 let not_exit = network.by_id(&id_noexit).unwrap();
711 let web_exit = network.by_id(&id_webexit).unwrap();
712 let full_exit = network.by_id(&id_fullexit).unwrap();
713 let bad_exit = network.by_id(&id_badexit).unwrap();
714
715 let ep_none = ExitPolicy::from_relay(¬_exit);
716 let ep_web = ExitPolicy::from_relay(&web_exit);
717 let ep_full = ExitPolicy::from_relay(&full_exit);
718 let ep_bad = ExitPolicy::from_relay(&bad_exit);
719
720 assert!(!ep_none.allows_port(TargetPort::ipv4(80)));
721 assert!(!ep_none.allows_port(TargetPort::ipv4(9999)));
722
723 assert!(ep_web.allows_port(TargetPort::ipv4(80)));
724 assert!(ep_web.allows_port(TargetPort::ipv4(443)));
725 assert!(!ep_web.allows_port(TargetPort::ipv4(9999)));
726
727 assert!(ep_full.allows_port(TargetPort::ipv4(80)));
728 assert!(ep_full.allows_port(TargetPort::ipv4(443)));
729 assert!(ep_full.allows_port(TargetPort::ipv4(9999)));
730
731 assert!(!ep_bad.allows_port(TargetPort::ipv4(80)));
732
733 assert!(!ep_none.allows_port(TargetPort::ipv6(80)));
735 assert!(!ep_web.allows_port(TargetPort::ipv6(80)));
736 assert!(!ep_full.allows_port(TargetPort::ipv6(80)));
737 assert!(!ep_bad.allows_port(TargetPort::ipv6(80)));
738
739 assert!(TargetPort::ipv4(80).is_supported_by(&web_exit.low_level_details()));
741 assert!(!TargetPort::ipv6(80).is_supported_by(&web_exit.low_level_details()));
742 assert!(!TargetPort::ipv6(80).is_supported_by(&bad_exit.low_level_details()));
743 }
744
745 #[test]
746 fn usage_ops() {
747 let policy = ExitPolicy {
750 v4: Arc::new("accept 80,443".parse().unwrap()),
751 v6: Arc::new("accept 23".parse().unwrap()),
752 };
753 let tok1 = IsolationToken::new();
754 let tok2 = IsolationToken::new();
755 let isolation = StreamIsolationBuilder::new()
756 .owner_token(tok1)
757 .build()
758 .unwrap();
759 let isolation2 = StreamIsolationBuilder::new()
760 .owner_token(tok2)
761 .build()
762 .unwrap();
763
764 let supp_dir = SupportedTunnelUsage::Dir;
765 let targ_dir = TargetTunnelUsage::Dir;
766 let supp_exit = SupportedTunnelUsage::Exit {
767 policy: policy.clone(),
768 isolation: Some(isolation.clone()),
769 country_code: None,
770 all_relays_stable: true,
771 };
772 let supp_exit_iso2 = SupportedTunnelUsage::Exit {
773 policy: policy.clone(),
774 isolation: Some(isolation2.clone()),
775 country_code: None,
776 all_relays_stable: true,
777 };
778 let supp_exit_no_iso = SupportedTunnelUsage::Exit {
779 policy,
780 isolation: None,
781 country_code: None,
782 all_relays_stable: true,
783 };
784 let supp_none = SupportedTunnelUsage::NoUsage;
785
786 let targ_80_v4 = TargetTunnelUsage::Exit {
787 ports: vec![TargetPort::ipv4(80)],
788 isolation: isolation.clone(),
789 country_code: None,
790 require_stability: false,
791 };
792 let targ_80_v4_iso2 = TargetTunnelUsage::Exit {
793 ports: vec![TargetPort::ipv4(80)],
794 isolation: isolation2,
795 country_code: None,
796 require_stability: false,
797 };
798 let targ_80_23_v4 = TargetTunnelUsage::Exit {
799 ports: vec![TargetPort::ipv4(80), TargetPort::ipv4(23)],
800 isolation: isolation.clone(),
801 country_code: None,
802 require_stability: false,
803 };
804
805 let targ_80_23_mixed = TargetTunnelUsage::Exit {
806 ports: vec![TargetPort::ipv4(80), TargetPort::ipv6(23)],
807 isolation: isolation.clone(),
808 country_code: None,
809 require_stability: false,
810 };
811 let targ_999_v6 = TargetTunnelUsage::Exit {
812 ports: vec![TargetPort::ipv6(999)],
813 isolation,
814 country_code: None,
815 require_stability: false,
816 };
817 let targ_testing = TargetTunnelUsage::TimeoutTesting;
818
819 assert!(supp_dir.supports(&targ_dir));
820 assert!(!supp_dir.supports(&targ_80_v4));
821 assert!(!supp_exit.supports(&targ_dir));
822 assert!(supp_exit.supports(&targ_80_v4));
823 assert!(!supp_exit.supports(&targ_80_v4_iso2));
824 assert!(supp_exit.supports(&targ_80_23_mixed));
825 assert!(!supp_exit.supports(&targ_80_23_v4));
826 assert!(!supp_exit.supports(&targ_999_v6));
827 assert!(!supp_exit_iso2.supports(&targ_80_v4));
828 assert!(supp_exit_iso2.supports(&targ_80_v4_iso2));
829 assert!(supp_exit_no_iso.supports(&targ_80_v4));
830 assert!(supp_exit_no_iso.supports(&targ_80_v4_iso2));
831 assert!(!supp_exit_no_iso.supports(&targ_80_23_v4));
832 assert!(!supp_none.supports(&targ_dir));
833 assert!(!supp_none.supports(&targ_80_23_v4));
834 assert!(!supp_none.supports(&targ_80_v4_iso2));
835 assert!(!supp_dir.supports(&targ_testing));
836 assert!(supp_exit.supports(&targ_testing));
837 assert!(supp_exit_no_iso.supports(&targ_testing));
838 assert!(supp_exit_iso2.supports(&targ_testing));
839 assert!(supp_none.supports(&targ_testing));
840 }
841
842 #[test]
843 fn restrict_mut() {
844 let policy = ExitPolicy {
845 v4: Arc::new("accept 80,443".parse().unwrap()),
846 v6: Arc::new("accept 23".parse().unwrap()),
847 };
848
849 let tok1 = IsolationToken::new();
850 let tok2 = IsolationToken::new();
851 let isolation = StreamIsolationBuilder::new()
852 .owner_token(tok1)
853 .build()
854 .unwrap();
855 let isolation2 = StreamIsolationBuilder::new()
856 .owner_token(tok2)
857 .build()
858 .unwrap();
859
860 let supp_dir = SupportedTunnelUsage::Dir;
861 let targ_dir = TargetTunnelUsage::Dir;
862 let supp_exit = SupportedTunnelUsage::Exit {
863 policy: policy.clone(),
864 isolation: Some(isolation.clone()),
865 country_code: None,
866 all_relays_stable: true,
867 };
868 let supp_exit_iso2 = SupportedTunnelUsage::Exit {
869 policy: policy.clone(),
870 isolation: Some(isolation2.clone()),
871 country_code: None,
872 all_relays_stable: true,
873 };
874 let supp_exit_no_iso = SupportedTunnelUsage::Exit {
875 policy,
876 isolation: None,
877 country_code: None,
878 all_relays_stable: true,
879 };
880 let supp_none = SupportedTunnelUsage::NoUsage;
881 let targ_exit = TargetTunnelUsage::Exit {
882 ports: vec![TargetPort::ipv4(80)],
883 isolation,
884 country_code: None,
885 require_stability: false,
886 };
887 let targ_exit_iso2 = TargetTunnelUsage::Exit {
888 ports: vec![TargetPort::ipv4(80)],
889 isolation: isolation2,
890 country_code: None,
891 require_stability: false,
892 };
893 let targ_testing = TargetTunnelUsage::TimeoutTesting;
894
895 let mut supp_dir_c = supp_dir.clone();
897 assert!(supp_dir_c.restrict_mut(&targ_exit).is_err());
898 assert!(supp_dir_c.restrict_mut(&targ_testing).is_err());
899 assert_isoleq!(supp_dir, supp_dir_c);
900
901 let mut supp_exit_c = supp_exit.clone();
902 assert!(supp_exit_c.restrict_mut(&targ_dir).is_err());
903 assert_isoleq!(supp_exit, supp_exit_c);
904
905 let mut supp_exit_c = supp_exit.clone();
906 assert!(supp_exit_c.restrict_mut(&targ_exit_iso2).is_err());
907 assert_isoleq!(supp_exit, supp_exit_c);
908
909 let mut supp_exit_iso2_c = supp_exit_iso2.clone();
910 assert!(supp_exit_iso2_c.restrict_mut(&targ_exit).is_err());
911 assert_isoleq!(supp_exit_iso2, supp_exit_iso2_c);
912
913 let mut supp_none_c = supp_none.clone();
914 assert!(supp_none_c.restrict_mut(&targ_exit).is_err());
915 assert!(supp_none_c.restrict_mut(&targ_dir).is_err());
916 assert_isoleq!(supp_none_c, supp_none);
917
918 let mut supp_dir_c = supp_dir.clone();
920 supp_dir_c.restrict_mut(&targ_dir).unwrap();
921 assert_isoleq!(supp_dir, supp_dir_c);
922
923 let mut supp_exit_c = supp_exit.clone();
924 supp_exit_c.restrict_mut(&targ_exit).unwrap();
925 assert_isoleq!(supp_exit, supp_exit_c);
926
927 let mut supp_exit_iso2_c = supp_exit_iso2.clone();
928 supp_exit_iso2_c.restrict_mut(&targ_exit_iso2).unwrap();
929 supp_none_c.restrict_mut(&targ_testing).unwrap();
930 assert_isoleq!(supp_exit_iso2, supp_exit_iso2_c);
931
932 let mut supp_none_c = supp_none.clone();
933 supp_none_c.restrict_mut(&targ_testing).unwrap();
934 assert_isoleq!(supp_none_c, supp_none);
935
936 let mut supp_exit_no_iso_c = supp_exit_no_iso.clone();
938 supp_exit_no_iso_c.restrict_mut(&targ_exit).unwrap();
939 assert!(supp_exit_no_iso_c.supports(&targ_exit));
940 assert!(!supp_exit_no_iso_c.supports(&targ_exit_iso2));
941
942 let mut supp_exit_no_iso_c = supp_exit_no_iso;
943 supp_exit_no_iso_c.restrict_mut(&targ_exit_iso2).unwrap();
944 assert!(!supp_exit_no_iso_c.supports(&targ_exit));
945 assert!(supp_exit_no_iso_c.supports(&targ_exit_iso2));
946 }
947
948 #[test]
949 fn buildpath() {
950 tor_rtcompat::test_with_all_runtimes!(|rt| async move {
951 let mut rng = testing_rng();
952 let netdir = testnet::construct_netdir().unwrap_if_sufficient().unwrap();
953 let di = (&netdir).into();
954 let config = crate::PathConfig::default();
955 let statemgr = TestingStateMgr::new();
956 let guards =
957 tor_guardmgr::GuardMgr::new(rt.clone(), statemgr.clone(), &TestConfig::default())
958 .unwrap();
959 guards.install_test_netdir(&netdir);
960 let now = SystemTime::get();
961
962 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
967 let vanguards =
968 VanguardMgr::new(&Default::default(), rt.clone(), statemgr, false).unwrap();
969
970 let (p_dir, u_dir, _, _) = TargetTunnelUsage::Dir
972 .build_path(
973 &mut rng,
974 di,
975 &guards,
976 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
977 &vanguards,
978 &config,
979 now,
980 )
981 .unwrap();
982 assert!(matches!(u_dir, SupportedTunnelUsage::Dir));
983 assert_eq!(p_dir.len(), 1);
984
985 let tok1 = IsolationToken::new();
987 let isolation = StreamIsolationBuilder::new()
988 .owner_token(tok1)
989 .build()
990 .unwrap();
991
992 let exit_usage = TargetTunnelUsage::Exit {
993 ports: vec![TargetPort::ipv4(995)],
994 isolation: isolation.clone(),
995 country_code: None,
996 require_stability: false,
997 };
998 let (p_exit, u_exit, _, _) = exit_usage
999 .build_path(
1000 &mut rng,
1001 di,
1002 &guards,
1003 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
1004 &vanguards,
1005 &config,
1006 now,
1007 )
1008 .unwrap();
1009 assert!(matches!(
1010 u_exit,
1011 SupportedTunnelUsage::Exit {
1012 isolation: ref iso,
1013 ..
1014 } if iso.isol_eq(&Some(isolation))
1015 ));
1016 assert!(u_exit.supports(&exit_usage));
1017 assert_eq!(p_exit.len(), 3);
1018
1019 let (path, usage, _, _) = TargetTunnelUsage::TimeoutTesting
1021 .build_path(
1022 &mut rng,
1023 di,
1024 &guards,
1025 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
1026 &vanguards,
1027 &config,
1028 now,
1029 )
1030 .unwrap();
1031 let path = match OwnedPath::try_from(&path).unwrap() {
1032 OwnedPath::ChannelOnly(_) => panic!("Impossible path type."),
1033 OwnedPath::Normal(p) => p,
1034 };
1035 assert_eq!(path.len(), 3);
1036
1037 let last_relay = netdir.by_ids(&path[2]).unwrap();
1039 let policy = ExitPolicy::from_relay(&last_relay);
1040 assert!(policy.allows_some_port());
1043 assert!(last_relay.low_level_details().policies_allow_some_port());
1044 assert_isoleq!(
1045 usage,
1046 SupportedTunnelUsage::Exit {
1047 policy,
1048 isolation: None,
1049 country_code: None,
1050 all_relays_stable: true
1051 }
1052 );
1053 });
1054 }
1055
1056 #[test]
1057 fn build_testing_noexit() {
1058 tor_rtcompat::test_with_all_runtimes!(|rt| async move {
1061 let mut rng = testing_rng();
1062 let netdir = testnet::construct_custom_netdir(|_idx, bld, _| {
1063 bld.md.parse_ipv4_policy("reject 1-65535").unwrap();
1064 })
1065 .unwrap()
1066 .unwrap_if_sufficient()
1067 .unwrap();
1068 let di = (&netdir).into();
1069 let config = crate::PathConfig::default();
1070 let statemgr = TestingStateMgr::new();
1071 let guards =
1072 tor_guardmgr::GuardMgr::new(rt.clone(), statemgr.clone(), &TestConfig::default())
1073 .unwrap();
1074 guards.install_test_netdir(&netdir);
1075 let now = SystemTime::get();
1076
1077 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
1078 let vanguards =
1079 VanguardMgr::new(&Default::default(), rt.clone(), statemgr, false).unwrap();
1080
1081 let (path, usage, _, _) = TargetTunnelUsage::TimeoutTesting
1082 .build_path(
1083 &mut rng,
1084 di,
1085 &guards,
1086 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
1087 &vanguards,
1088 &config,
1089 now,
1090 )
1091 .unwrap();
1092 assert_eq!(path.len(), 3);
1093 assert_isoleq!(usage, SupportedTunnelUsage::NoUsage);
1094 });
1095 }
1096
1097 #[test]
1098 fn display_target_ports() {
1099 let ports = [];
1100 assert_eq!(TargetPorts::from(&ports[..]).to_string(), "[]");
1101
1102 let ports = [TargetPort::ipv4(80)];
1103 assert_eq!(TargetPorts::from(&ports[..]).to_string(), "80v4");
1104 let ports = [TargetPort::ipv4(80), TargetPort::ipv6(443)];
1105 assert_eq!(TargetPorts::from(&ports[..]).to_string(), "[80v4,443v6]");
1106 }
1107}