Skip to main content

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}