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///
47/// For flags, see <https://spec.torproject.org/dir-spec/assigning-flags-vote.html>.
48
49#[derive(Clone)]
50pub struct RelayDetails<'a>(pub(crate) &'a super::Relay<'a>);
51
52impl<'a> RelayDetails<'a> {
53 /// Return true if this relay allows exiting to `port` on IPv4.
54 pub fn supports_exit_port_ipv4(&self, port: u16) -> bool {
55 self.ipv4_policy().allows_port(port)
56 }
57 /// Return true if this relay allows exiting to `port` on IPv6.
58 pub fn supports_exit_port_ipv6(&self, port: u16) -> bool {
59 self.ipv6_policy().allows_port(port)
60 }
61 /// Return true if this relay is suitable for use as a directory
62 /// cache.
63 pub fn is_dir_cache(&self) -> bool {
64 rs_is_dir_cache(self.0.rs)
65 }
66 /// Return true if this relay has the "Fast" flag.
67 ///
68 /// Most relays have this flag. It indicates that the relay is suitable for
69 /// circuits that need more than a minimal amount of bandwidth.
70 pub fn is_flagged_fast(&self) -> bool {
71 self.0.rs.is_flagged_fast()
72 }
73 /// Return true if this relay has the "Exit" flag.
74 pub fn is_flagged_exit(&self) -> bool {
75 self.0.rs.is_flagged_exit()
76 }
77 /// Return true if this relay has the "Stable" flag.
78 ///
79 /// Most relays have this flag. It indicates that the relay is suitable for
80 /// long-lived circuits.
81 pub fn is_flagged_stable(&self) -> bool {
82 self.0.rs.is_flagged_stable()
83 }
84 /// Return true if this relay is a potential HS introduction point
85 pub fn is_hs_intro_point(&self) -> bool {
86 self.is_flagged_fast()
87 && self.0.rs.is_flagged_stable()
88 && !self.0.rs.is_flagged_middle_only()
89 }
90 /// Return true if this relay is a potential HS rendezvous point
91 pub fn is_hs_rend_point(&self) -> bool {
92 self.is_flagged_fast()
93 && self.0.rs.is_flagged_stable()
94 && !self.0.rs.is_flagged_middle_only()
95 }
96 /// Return true if this relay is suitable for use as a newly sampled guard,
97 /// or for continuing to use as a guard.
98 pub fn is_suitable_as_guard(&self) -> bool {
99 self.0.rs.is_flagged_guard() && self.is_flagged_fast() && self.is_flagged_stable()
100 }
101 /// Return true if both relays are in the same subnet, as configured by
102 /// `subnet_config`.
103 ///
104 /// Two relays are considered to be in the same subnet if they
105 /// have IPv4 addresses with the same `subnets_family_v4`-bit
106 /// prefix, or if they have IPv6 addresses with the same
107 /// `subnets_family_v6`-bit prefix.
108 pub fn in_same_subnet(&self, other: &Relay<'_>, subnet_config: &SubnetConfig) -> bool {
109 subnet_config.any_addrs_in_same_subnet(self.0, other)
110 }
111 /// Return true if both relays are in the same family.
112 ///
113 /// (Every relay is considered to be in the same family as itself.)
114 pub fn in_same_family(&self, other: &Relay<'_>, family_rules: FamilyRules) -> bool {
115 #![allow(clippy::collapsible_if)] // I prefer this style here.
116 if self.0.same_relay_ids(other) {
117 return true;
118 }
119 if family_rules.use_family_lists {
120 if self.0.md.family().contains(other.rsa_id())
121 && other.md.family().contains(self.0.rsa_id())
122 {
123 return true;
124 }
125 }
126 if family_rules.use_family_ids {
127 let my_ids = self.0.md.family_ids();
128 let their_ids = other.md.family_ids();
129 if my_ids.iter().any(|my_id| their_ids.contains(my_id)) {
130 return true;
131 }
132 }
133
134 false
135 }
136
137 /// Return true if there are any ports for which this Relay can be
138 /// used for exit traffic.
139 ///
140 /// (Returns false if this relay doesn't allow exit traffic, or if it
141 /// has been flagged as a bad exit.)
142 pub fn policies_allow_some_port(&self) -> bool {
143 if self.0.rs.is_flagged_bad_exit() {
144 return false;
145 }
146
147 self.0.md.ipv4_policy().allows_some_port() || self.0.md.ipv6_policy().allows_some_port()
148 }
149
150 /// Return the IPv4 exit policy for this relay. If the relay has been marked BadExit, return an
151 /// empty policy
152 pub fn ipv4_policy(&self) -> Arc<PortPolicy> {
153 if !self.0.rs.is_flagged_bad_exit() {
154 Arc::clone(self.0.md.ipv4_policy())
155 } else {
156 Arc::new(PortPolicy::new_reject_all())
157 }
158 }
159 /// Return the IPv6 exit policy for this relay. If the relay has been marked BadExit, return an
160 /// empty policy
161 pub fn ipv6_policy(&self) -> Arc<PortPolicy> {
162 if !self.0.rs.is_flagged_bad_exit() {
163 Arc::clone(self.0.md.ipv6_policy())
164 } else {
165 Arc::new(PortPolicy::new_reject_all())
166 }
167 }
168 /// Return the IPv4 exit policy declared by this relay.
169 ///
170 /// In contrast to [`RelayDetails::ipv4_policy`],
171 /// this does not verify if the relay is marked BadExit.
172 pub fn ipv4_declared_policy(&self) -> &Arc<PortPolicy> {
173 self.0.md.ipv4_policy()
174 }
175 /// Return the IPv6 exit policy declared by this relay.
176 ///
177 /// In contrast to [`RelayDetails::ipv6_policy`],
178 /// this does not verify if the relay is marked BadExit.
179 pub fn ipv6_declared_policy(&self) -> &Arc<PortPolicy> {
180 self.0.md.ipv6_policy()
181 }
182}
183
184/// A view for lower-level details about a [`UncheckedRelay`](crate::UncheckedRelay).
185///
186/// Most callers should avoid using this structure;
187/// they should instead call higher-level functions
188/// like those in the `tor-relay-selection` crate.
189#[derive(Debug, Clone)]
190pub struct UncheckedRelayDetails<'a>(pub(crate) &'a super::UncheckedRelay<'a>);
191
192impl<'a> UncheckedRelayDetails<'a> {
193 /// Return true if this relay is suitable for use as a newly sampled guard,
194 /// or for continuing to use as a guard.
195 pub fn is_suitable_as_guard(&self) -> bool {
196 self.0.rs.is_flagged_guard() && self.0.rs.is_flagged_fast() && self.0.rs.is_flagged_stable()
197 }
198 /// Return true if this relay is a potential directory cache.
199 pub fn is_dir_cache(&self) -> bool {
200 rs_is_dir_cache(self.0.rs)
201 }
202}
203
204/// Return true if `rs` is usable as a directory cache.
205fn rs_is_dir_cache(rs: &netstatus::MdRouterStatus) -> bool {
206 use tor_protover::named::DIRCACHE_CONSDIFF;
207 rs.is_flagged_v2dir() && rs.protovers().supports_named_subver(DIRCACHE_CONSDIFF)
208}