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

            
34
use std::sync::Arc;
35

            
36
use tor_linkspec::HasRelayIds;
37
use tor_netdoc::{doc::netstatus, types::policy::PortPolicy};
38

            
39
use 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)]
50
pub struct RelayDetails<'a>(pub(crate) &'a super::Relay<'a>);
51

            
52
impl<'a> RelayDetails<'a> {
53
    /// Return true if this relay allows exiting to `port` on IPv4.
54
18404857
    pub fn supports_exit_port_ipv4(&self, port: u16) -> bool {
55
18404857
        self.ipv4_policy().allows_port(port)
56
18404857
    }
57
    /// Return true if this relay allows exiting to `port` on IPv6.
58
1770
    pub fn supports_exit_port_ipv6(&self, port: u16) -> bool {
59
1770
        self.ipv6_policy().allows_port(port)
60
1770
    }
61
    /// Return true if this relay is suitable for use as a directory
62
    /// cache.
63
634264
    pub fn is_dir_cache(&self) -> bool {
64
634264
        rs_is_dir_cache(self.0.rs)
65
634264
    }
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
50519686
    pub fn is_flagged_fast(&self) -> bool {
71
50519686
        self.0.rs.is_flagged_fast()
72
50519686
    }
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
2788786
    pub fn is_flagged_stable(&self) -> bool {
82
2788786
        self.0.rs.is_flagged_stable()
83
2788786
    }
84
    /// Return true if this relay is a potential HS introduction point
85
28812
    pub fn is_hs_intro_point(&self) -> bool {
86
28812
        self.is_flagged_fast()
87
28812
            && self.0.rs.is_flagged_stable()
88
28812
            && !self.0.rs.is_flagged_middle_only()
89
28812
    }
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
1430016
    pub fn is_suitable_as_guard(&self) -> bool {
99
1430016
        self.0.rs.is_flagged_guard() && self.is_flagged_fast() && self.is_flagged_stable()
100
1430016
    }
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
18
    pub fn in_same_subnet(&self, other: &Relay<'_>, subnet_config: &SubnetConfig) -> bool {
109
18
        subnet_config.any_addrs_in_same_subnet(self.0, other)
110
18
    }
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
53554860
    pub fn in_same_family(&self, other: &Relay<'_>, family_rules: FamilyRules) -> bool {
115
        #![allow(clippy::collapsible_if)] // I prefer this style here.
116
53554860
        if self.0.same_relay_ids(other) {
117
1565164
            return true;
118
51989696
        }
119
51989696
        if family_rules.use_family_lists {
120
51982542
            if self.0.md.family().contains(other.rsa_id())
121
1213932
                && other.md.family().contains(self.0.rsa_id())
122
            {
123
1213932
                return true;
124
50768610
            }
125
7154
        }
126
50775764
        if family_rules.use_family_ids {
127
50768806
            let my_ids = self.0.md.family_ids();
128
50768806
            let their_ids = other.md.family_ids();
129
50768952
            if my_ids.iter().any(|my_id| their_ids.contains(my_id)) {
130
196
                return true;
131
50768610
            }
132
6958
        }
133

            
134
50775568
        false
135
53554860
    }
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
12104680
    pub fn policies_allow_some_port(&self) -> bool {
143
12104680
        if self.0.rs.is_flagged_bad_exit() {
144
2
            return false;
145
12104678
        }
146

            
147
12104678
        self.0.md.ipv4_policy().allows_some_port() || self.0.md.ipv6_policy().allows_some_port()
148
12104680
    }
149

            
150
    /// Return the IPv4 exit policy for this relay. If the relay has been marked BadExit, return an
151
    /// empty policy
152
18707289
    pub fn ipv4_policy(&self) -> Arc<PortPolicy> {
153
18707289
        if !self.0.rs.is_flagged_bad_exit() {
154
18707236
            Arc::clone(self.0.md.ipv4_policy())
155
        } else {
156
53
            Arc::new(PortPolicy::new_reject_all())
157
        }
158
18707289
    }
159
    /// Return the IPv6 exit policy for this relay. If the relay has been marked BadExit, return an
160
    /// empty policy
161
3293
    pub fn ipv6_policy(&self) -> Arc<PortPolicy> {
162
3293
        if !self.0.rs.is_flagged_bad_exit() {
163
3191
            Arc::clone(self.0.md.ipv6_policy())
164
        } else {
165
102
            Arc::new(PortPolicy::new_reject_all())
166
        }
167
3293
    }
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
2
    pub fn ipv4_declared_policy(&self) -> &Arc<PortPolicy> {
173
2
        self.0.md.ipv4_policy()
174
2
    }
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
2
    pub fn ipv6_declared_policy(&self) -> &Arc<PortPolicy> {
180
2
        self.0.md.ipv6_policy()
181
2
    }
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)]
190
pub struct UncheckedRelayDetails<'a>(pub(crate) &'a super::UncheckedRelay<'a>);
191

            
192
impl<'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
1062615
    pub fn is_suitable_as_guard(&self) -> bool {
196
1062615
        self.0.rs.is_flagged_guard() && self.0.rs.is_flagged_fast() && self.0.rs.is_flagged_stable()
197
1062615
    }
198
    /// Return true if this relay is a potential directory cache.
199
549388
    pub fn is_dir_cache(&self) -> bool {
200
549388
        rs_is_dir_cache(self.0.rs)
201
549388
    }
202
}
203

            
204
/// Return true if `rs` is usable as a directory cache.
205
1183652
fn rs_is_dir_cache(rs: &netstatus::MdRouterStatus) -> bool {
206
    use tor_protover::named::DIRCACHE_CONSDIFF;
207
1183652
    rs.is_flagged_v2dir() && rs.protovers().supports_named_subver(DIRCACHE_CONSDIFF)
208
1183652
}