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 .into(),
114 v6: PortPolicy::from_allowed_port_list(v6_ports.iter().map(|port| port.port).collect())
115 .intern()
116 .into(),
117 }
118 }
119
120 fn allows_port(&self, p: TargetPort) -> bool {
122 let policy = if p.ipv6 { &self.v6 } else { &self.v4 };
123 policy.allows_port(p.port)
124 }
125
126 fn allows_some_port(&self) -> bool {
128 self.v4.allows_some_port() || self.v6.allows_some_port()
129 }
130}
131
132#[derive(Clone, Debug)]
137pub(crate) enum TargetTunnelUsage {
138 Dir,
140 Exit {
142 ports: Vec<TargetPort>,
147 isolation: StreamIsolation,
149 country_code: Option<CountryCode>,
151 require_stability: bool,
155 },
156 TimeoutTesting,
158 Preemptive {
166 port: Option<TargetPort>,
170 circs: usize,
172 require_stability: bool,
175 },
176 #[cfg(feature = "specific-relay")]
179 DirSpecificTarget(OwnedChanTarget),
180
181 #[cfg(feature = "hs-common")]
184 HsCircBase {
185 compatible_with_target: Option<OwnedChanTarget>,
192 stem_kind: HsCircStemKind,
194 circ_kind: Option<HsCircKind>,
197 },
198}
199
200#[derive(Clone, Debug)]
205pub(crate) enum SupportedTunnelUsage {
206 Dir,
208 Exit {
210 policy: ExitPolicy,
212 isolation: Option<StreamIsolation>,
215 country_code: Option<CountryCode>,
217 all_relays_stable: bool,
221 },
222 NoUsage,
224 #[cfg(feature = "hs-common")]
228 HsOnly,
229 #[cfg(feature = "specific-relay")]
232 DirSpecificTarget(OwnedChanTarget),
233}
234
235impl TargetTunnelUsage {
236 #[instrument(level = "trace", skip_all)]
239 pub(crate) fn build_path<'a, R: Rng, RT: Runtime>(
240 &self,
241 rng: &mut R,
242 netdir: crate::DirInfo<'a>,
243 guards: &GuardMgr<RT>,
244 #[cfg(all(feature = "vanguards", feature = "hs-common"))] vanguards: &VanguardMgr<RT>,
245 config: &crate::PathConfig,
246 now: SystemTime,
247 ) -> Result<(
248 TorPath<'a>,
249 SupportedTunnelUsage,
250 Option<GuardMonitor>,
251 Option<GuardUsable>,
252 )> {
253 match self {
254 TargetTunnelUsage::Dir => {
255 let (path, mon, usable) = DirPathBuilder::new().pick_path(guards)?;
256 Ok((path, SupportedTunnelUsage::Dir, Some(mon), Some(usable)))
257 }
258 TargetTunnelUsage::Preemptive {
259 port,
260 require_stability,
261 ..
262 } => {
263 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 ports: p,
289 isolation,
290 country_code,
291 require_stability,
292 } => {
293 #[cfg(feature = "geoip")]
294 let mut builder = if let Some(cc) = country_code {
295 ExitPathBuilder::in_given_country(*cc, p.clone())
296 } else {
297 ExitPathBuilder::from_target_ports(p.clone())
298 };
299 #[cfg(not(feature = "geoip"))]
300 let mut builder = ExitPathBuilder::from_target_ports(p.clone());
301
302 builder.require_stability(*require_stability);
303
304 let (path, mon, usable) = builder.pick_path(rng, netdir, guards, config, now)?;
305 let policy = path
306 .exit_policy()
307 .expect("ExitPathBuilder gave us a one-hop circuit?");
308
309 #[cfg(feature = "geoip")]
310 let resulting_cc = path.country_code();
311 #[cfg(feature = "geoip")]
312 if resulting_cc != *country_code {
313 internal!(
314 "asked for a country code of {:?}, got {:?}",
315 country_code,
316 resulting_cc
317 );
318 }
319 let all_relays_stable = path.appears_stable();
320
321 #[cfg(not(feature = "geoip"))]
322 let resulting_cc = *country_code; Ok((
324 path,
325 SupportedTunnelUsage::Exit {
326 policy,
327 isolation: Some(isolation.clone()),
328 country_code: resulting_cc,
329 all_relays_stable,
330 },
331 Some(mon),
332 Some(usable),
333 ))
334 }
335 TargetTunnelUsage::TimeoutTesting => {
336 let (path, mon, usable) = ExitPathBuilder::for_timeout_testing()
337 .require_stability(false)
338 .pick_path(rng, netdir, guards, config, now)?;
339 let policy = path.exit_policy();
340 #[cfg(feature = "geoip")]
341 let country_code = path.country_code();
342 #[cfg(not(feature = "geoip"))]
343 let country_code = None;
344 let usage = match policy {
345 Some(policy) if policy.allows_some_port() => SupportedTunnelUsage::Exit {
346 policy,
347 isolation: None,
348 country_code,
349 all_relays_stable: path.appears_stable(),
350 },
351 _ => SupportedTunnelUsage::NoUsage,
352 };
353
354 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 }
384
385 #[cfg(test)]
388 pub(crate) fn new_from_ipv4_ports(ports: &[u16]) -> Self {
389 TargetTunnelUsage::Exit {
390 ports: ports.iter().map(|p| TargetPort::ipv4(*p)).collect(),
391 isolation: StreamIsolation::no_isolation(),
392 country_code: None,
393 require_stability: false,
394 }
395 }
396}
397
398#[cfg(feature = "specific-relay")]
401fn owned_targets_equivalent(a: &OwnedChanTarget, b: &OwnedChanTarget) -> bool {
402 a.same_relay_ids(b) && a.chan_method() == b.chan_method()
406}
407
408impl SupportedTunnelUsage {
409 pub(crate) fn supports(&self, target: &TargetTunnelUsage) -> bool {
414 use SupportedTunnelUsage::*;
415 match (self, target) {
416 (Dir, TargetTunnelUsage::Dir) => true,
417 (
418 Exit {
419 policy: p1,
420 isolation: i1,
421 country_code: cc1,
422 all_relays_stable,
423 },
424 TargetTunnelUsage::Exit {
425 ports: p2,
426 isolation: i2,
427 country_code: cc2,
428 require_stability,
429 },
430 ) => {
431 i1.as_ref()
434 .map(|i1| i1.compatible_same_type(i2))
435 .unwrap_or(true)
436 && (!require_stability || *all_relays_stable)
437 && p2.iter().all(|port| p1.allows_port(*port))
438 && (cc2.is_none() || cc1 == cc2)
439 }
440 (
441 Exit {
442 policy,
443 isolation,
444 all_relays_stable,
445 ..
446 },
447 TargetTunnelUsage::Preemptive {
448 port,
449 require_stability,
450 ..
451 },
452 ) => {
453 if *require_stability && !all_relays_stable {
456 return false;
457 }
458 if isolation.is_some() {
459 return false;
462 }
463 if let Some(p) = port {
466 policy.allows_port(*p)
467 } else {
468 true
469 }
470 }
471 (Exit { .. } | NoUsage, TargetTunnelUsage::TimeoutTesting) => true,
472 #[cfg(feature = "specific-relay")]
473 (DirSpecificTarget(a), TargetTunnelUsage::DirSpecificTarget(b)) => {
474 owned_targets_equivalent(a, b)
475 }
476 (_, _) => false,
477 }
478 }
479
480 pub(crate) fn restrict_mut(
487 &mut self,
488 usage: &TargetTunnelUsage,
489 ) -> std::result::Result<(), RestrictionFailed> {
490 use SupportedTunnelUsage::*;
491 match (self, usage) {
492 (Dir, TargetTunnelUsage::Dir) => Ok(()),
493 (Exit { .. }, TargetTunnelUsage::Preemptive { .. }) => Ok(()),
497 (
498 Exit {
499 isolation: isol1, ..
500 },
501 TargetTunnelUsage::Exit { isolation: i2, .. },
502 ) => {
503 if let Some(i1) = isol1 {
504 if let Some(new_isolation) = i1.join_same_type(i2) {
505 *isol1 = Some(new_isolation);
508 Ok(())
509 } else {
510 Err(RestrictionFailed::NotSupported)
511 }
512 } else {
513 *isol1 = Some(i2.clone());
515 Ok(())
516 }
517 }
518 (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 (_, _) => Err(RestrictionFailed::NotSupported),
526 }
527 }
528
529 pub(crate) fn find_supported<'a, 'b, C: AbstractTunnel>(
531 list: impl Iterator<Item = &'b mut OpenEntry<C>>,
532 usage: &TargetTunnelUsage,
533 ) -> Vec<&'b mut OpenEntry<C>> {
534 fn find_supported_internal<'a, 'b, C: AbstractTunnel>(
536 list: impl Iterator<Item = &'b mut OpenEntry<C>>,
537 usage: &TargetTunnelUsage,
538 ) -> Vec<&'b mut OpenEntry<C>> {
539 list.filter(|circ| circ.supports(usage)).collect()
540 }
541
542 match usage {
543 TargetTunnelUsage::Preemptive { circs, .. } => {
544 let supported = find_supported_internal(list, usage);
545 trace!(
549 "preemptive usage {:?} matches {} active circuits",
550 usage,
551 supported.len()
552 );
553 if supported.len() >= *circs {
554 supported
555 } else {
556 vec![]
557 }
558 }
559 _ => find_supported_internal(list, usage),
560 }
561 }
562
563 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 pub(crate) fn is_long_lived(&self) -> bool {
583 use SupportedTunnelUsage::*;
584 match self {
585 Dir => false,
590 #[cfg(feature = "specific-relay")]
591 DirSpecificTarget(_) => false,
592
593 Exit { isolation, .. } => {
594 isolation
596 .as_ref()
597 .is_some_and(StreamIsolation::enables_long_lived_circuits)
598 }
599 NoUsage => {
600 false
602 }
603 #[cfg(feature = "hs-common")]
604 HsOnly => {
605 false
608 }
609 }
610 }
611}
612
613#[cfg(test)]
614pub(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 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(¬_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 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 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 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 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 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 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 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
969 let vanguards =
970 VanguardMgr::new(&Default::default(), rt.clone(), statemgr, false).unwrap();
971
972 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 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 };
1000 let (p_exit, u_exit, _, _) = exit_usage
1001 .build_path(
1002 &mut rng,
1003 di,
1004 &guards,
1005 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
1006 &vanguards,
1007 &config,
1008 now,
1009 )
1010 .unwrap();
1011 assert!(matches!(
1012 u_exit,
1013 SupportedTunnelUsage::Exit {
1014 isolation: ref iso,
1015 ..
1016 } if iso.isol_eq(&Some(isolation))
1017 ));
1018 assert!(u_exit.supports(&exit_usage));
1019 assert_eq!(p_exit.len(), 3);
1020
1021 let (path, usage, _, _) = TargetTunnelUsage::TimeoutTesting
1023 .build_path(
1024 &mut rng,
1025 di,
1026 &guards,
1027 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
1028 &vanguards,
1029 &config,
1030 now,
1031 )
1032 .unwrap();
1033 let path = match OwnedPath::try_from(&path).unwrap() {
1034 OwnedPath::ChannelOnly(_) => panic!("Impossible path type."),
1035 OwnedPath::Normal(p) => p,
1036 };
1037 assert_eq!(path.len(), 3);
1038
1039 let last_relay = netdir.by_ids(&path[2]).unwrap();
1041 let policy = ExitPolicy::from_relay(&last_relay);
1042 assert!(policy.allows_some_port());
1045 assert!(last_relay.low_level_details().policies_allow_some_port());
1046 assert_isoleq!(
1047 usage,
1048 SupportedTunnelUsage::Exit {
1049 policy,
1050 isolation: None,
1051 country_code: None,
1052 all_relays_stable: true
1053 }
1054 );
1055 });
1056 }
1057
1058 #[test]
1059 fn build_testing_noexit() {
1060 tor_rtcompat::test_with_all_runtimes!(|rt| async move {
1063 let mut rng = testing_rng();
1064 let netdir = testnet::construct_custom_netdir(|_idx, bld, _| {
1065 bld.md.parse_ipv4_policy("reject 1-65535").unwrap();
1066 })
1067 .unwrap()
1068 .unwrap_if_sufficient()
1069 .unwrap();
1070 let di = (&netdir).into();
1071 let config = crate::PathConfig::default();
1072 let statemgr = TestingStateMgr::new();
1073 let guards =
1074 tor_guardmgr::GuardMgr::new(rt.clone(), statemgr.clone(), &TestConfig::default())
1075 .unwrap();
1076 guards.install_test_netdir(&netdir);
1077 let now = SystemTime::get();
1078
1079 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
1080 let vanguards =
1081 VanguardMgr::new(&Default::default(), rt.clone(), statemgr, false).unwrap();
1082
1083 let (path, usage, _, _) = TargetTunnelUsage::TimeoutTesting
1084 .build_path(
1085 &mut rng,
1086 di,
1087 &guards,
1088 #[cfg(all(feature = "vanguards", feature = "hs-common"))]
1089 &vanguards,
1090 &config,
1091 now,
1092 )
1093 .unwrap();
1094 assert_eq!(path.len(), 3);
1095 assert_isoleq!(usage, SupportedTunnelUsage::NoUsage);
1096 });
1097 }
1098
1099 #[test]
1100 fn display_target_ports() {
1101 let ports = [];
1102 assert_eq!(TargetPorts::from(&ports[..]).to_string(), "[]");
1103
1104 let ports = [TargetPort::ipv4(80)];
1105 assert_eq!(TargetPorts::from(&ports[..]).to_string(), "80v4");
1106 let ports = [TargetPort::ipv4(80), TargetPort::ipv6(443)];
1107 assert_eq!(TargetPorts::from(&ports[..]).to_string(), "[80v4,443v6]");
1108 }
1109}