tor_netdir/details.rs
1//! Functionality for exposing details about a relay that most users should avoid.
2//!
3//! ## Design notes
4//!
5//! These types aren't meant to be a dumping grounds
6//! for every function in `Relay` or `UncheckedRelay`:
7//! instead, they are for methods that are easy to misuse or misunderstand
8//! if applied out-of-context.
9//!
10//! For example, it's generally wrong in most contexts
11//! to check for a specific relay flag.
12//! Instead, we should be checking whether the relay is suitable
13//! for some particular _usage_,
14//! which will itself depend on a combination of flags.
15//!
16//! Therefore, this module should be used for checking properties only when:
17//! - The property is one that is usually subsumed
18//! in a higher-level check.
19//! - Using the lower-level property on its own poses a risk
20//! of accidentally forgetting to check other important properties.
21//!
22//! If you find that your code is using this module, you should ask yourself
23//! - whether the actual thing that you're testing
24//! is something that _any other piece of code_ might want to test
25//! - whether the collection of properties that you're testing
26//! creates a risk of leaving out some other properties
27//! that should also be tested,
28//! for example in the future, if new relay flags or properties are introduced
29//! that are supposed to influence relay selection or reuse.
30//!
31//! If you answer "yes" to either of these, it's better to define a higher-level property,
32//! and have your code use that instead.
33
34use std::sync::Arc;
35
36use tor_linkspec::HasRelayIds;
37use tor_netdoc::{doc::netstatus, types::policy::PortPolicy};
38
39use crate::{FamilyRules, Relay, SubnetConfig};
40
41/// A view for lower-level details about a [`Relay`].
42///
43/// Most callers should avoid using this structure;
44/// they should instead call higher-level functions
45/// like those in the `tor-relay-selection` crate.
46#[derive(Clone)]
47pub struct RelayDetails<'a>(pub(crate) &'a super::Relay<'a>);
48
49impl<'a> RelayDetails<'a> {
50 /// Return true if this relay allows exiting to `port` on IPv4.
51 pub fn supports_exit_port_ipv4(&self, port: u16) -> bool {
52 self.ipv4_policy().allows_port(port)
53 }
54 /// Return true if this relay allows exiting to `port` on IPv6.
55 pub fn supports_exit_port_ipv6(&self, port: u16) -> bool {
56 self.ipv6_policy().allows_port(port)
57 }
58 /// Return true if this relay is suitable for use as a directory
59 /// cache.
60 pub fn is_dir_cache(&self) -> bool {
61 rs_is_dir_cache(self.0.rs)
62 }
63 /// Return true if this relay has the "Fast" flag.
64 ///
65 /// Most relays have this flag. It indicates that the relay is suitable for
66 /// circuits that need more than a minimal amount of bandwidth.
67 pub fn is_flagged_fast(&self) -> bool {
68 self.0.rs.is_flagged_fast()
69 }
70 /// Return true if this relay has the "Stable" flag.
71 ///
72 /// Most relays have this flag. It indicates that the relay is suitable for
73 /// long-lived circuits.
74 pub fn is_flagged_stable(&self) -> bool {
75 self.0.rs.is_flagged_stable()
76 }
77 /// Return true if this relay is a potential HS introduction point
78 pub fn is_hs_intro_point(&self) -> bool {
79 self.is_flagged_fast()
80 && self.0.rs.is_flagged_stable()
81 && !self.0.rs.is_flagged_middle_only()
82 }
83 /// Return true if this relay is a potential HS rendezvous point
84 pub fn is_hs_rend_point(&self) -> bool {
85 self.is_flagged_fast()
86 && self.0.rs.is_flagged_stable()
87 && !self.0.rs.is_flagged_middle_only()
88 }
89 /// Return true if this relay is suitable for use as a newly sampled guard,
90 /// or for continuing to use as a guard.
91 pub fn is_suitable_as_guard(&self) -> bool {
92 self.0.rs.is_flagged_guard() && self.is_flagged_fast() && self.is_flagged_stable()
93 }
94 /// Return true if both relays are in the same subnet, as configured by
95 /// `subnet_config`.
96 ///
97 /// Two relays are considered to be in the same subnet if they
98 /// have IPv4 addresses with the same `subnets_family_v4`-bit
99 /// prefix, or if they have IPv6 addresses with the same
100 /// `subnets_family_v6`-bit prefix.
101 pub fn in_same_subnet(&self, other: &Relay<'_>, subnet_config: &SubnetConfig) -> bool {
102 subnet_config.any_addrs_in_same_subnet(self.0, other)
103 }
104 /// Return true if both relays are in the same family.
105 ///
106 /// (Every relay is considered to be in the same family as itself.)
107 pub fn in_same_family(&self, other: &Relay<'_>, family_rules: FamilyRules) -> bool {
108 #![allow(clippy::collapsible_if)] // I prefer this style here.
109 if self.0.same_relay_ids(other) {
110 return true;
111 }
112 if family_rules.use_family_lists {
113 if self.0.md.family().contains(other.rsa_id())
114 && other.md.family().contains(self.0.rsa_id())
115 {
116 return true;
117 }
118 }
119 if family_rules.use_family_ids {
120 let my_ids = self.0.md.family_ids();
121 let their_ids = other.md.family_ids();
122 if my_ids.iter().any(|my_id| their_ids.contains(my_id)) {
123 return true;
124 }
125 }
126
127 false
128 }
129
130 /// Return true if there are any ports for which this Relay can be
131 /// used for exit traffic.
132 ///
133 /// (Returns false if this relay doesn't allow exit traffic, or if it
134 /// has been flagged as a bad exit.)
135 pub fn policies_allow_some_port(&self) -> bool {
136 if self.0.rs.is_flagged_bad_exit() {
137 return false;
138 }
139
140 self.0.md.ipv4_policy().allows_some_port() || self.0.md.ipv6_policy().allows_some_port()
141 }
142
143 /// Return the IPv4 exit policy for this relay. If the relay has been marked BadExit, return an
144 /// empty policy
145 pub fn ipv4_policy(&self) -> Arc<PortPolicy> {
146 if !self.0.rs.is_flagged_bad_exit() {
147 Arc::clone(self.0.md.ipv4_policy())
148 } else {
149 Arc::new(PortPolicy::new_reject_all())
150 }
151 }
152 /// Return the IPv6 exit policy for this relay. If the relay has been marked BadExit, return an
153 /// empty policy
154 pub fn ipv6_policy(&self) -> Arc<PortPolicy> {
155 if !self.0.rs.is_flagged_bad_exit() {
156 Arc::clone(self.0.md.ipv6_policy())
157 } else {
158 Arc::new(PortPolicy::new_reject_all())
159 }
160 }
161 /// Return the IPv4 exit policy declared by this relay.
162 ///
163 /// In contrast to [`RelayDetails::ipv4_policy`],
164 /// this does not verify if the relay is marked BadExit.
165 pub fn ipv4_declared_policy(&self) -> &Arc<PortPolicy> {
166 self.0.md.ipv4_policy()
167 }
168 /// Return the IPv6 exit policy declared by this relay.
169 ///
170 /// In contrast to [`RelayDetails::ipv6_policy`],
171 /// this does not verify if the relay is marked BadExit.
172 pub fn ipv6_declared_policy(&self) -> &Arc<PortPolicy> {
173 self.0.md.ipv6_policy()
174 }
175}
176
177/// A view for lower-level details about a [`UncheckedRelay`](crate::UncheckedRelay).
178///
179/// Most callers should avoid using this structure;
180/// they should instead call higher-level functions
181/// like those in the `tor-relay-selection` crate.
182#[derive(Debug, Clone)]
183pub struct UncheckedRelayDetails<'a>(pub(crate) &'a super::UncheckedRelay<'a>);
184
185impl<'a> UncheckedRelayDetails<'a> {
186 /// Return true if this relay is suitable for use as a newly sampled guard,
187 /// or for continuing to use as a guard.
188 pub fn is_suitable_as_guard(&self) -> bool {
189 self.0.rs.is_flagged_guard() && self.0.rs.is_flagged_fast() && self.0.rs.is_flagged_stable()
190 }
191 /// Return true if this relay is a potential directory cache.
192 pub fn is_dir_cache(&self) -> bool {
193 rs_is_dir_cache(self.0.rs)
194 }
195}
196
197/// Return true if `rs` is usable as a directory cache.
198fn rs_is_dir_cache(rs: &netstatus::MdConsensusRouterStatus) -> bool {
199 use tor_protover::named::DIRCACHE_CONSDIFF;
200 rs.is_flagged_v2dir() && rs.protovers().supports_named_subver(DIRCACHE_CONSDIFF)
201}