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
#[derive(Clone)]
47
pub struct RelayDetails<'a>(pub(crate) &'a super::Relay<'a>);
48

            
49
impl<'a> RelayDetails<'a> {
50
    /// Return true if this relay allows exiting to `port` on IPv4.
51
11464037
    pub fn supports_exit_port_ipv4(&self, port: u16) -> bool {
52
11464037
        self.ipv4_policy().allows_port(port)
53
11464037
    }
54
    /// Return true if this relay allows exiting to `port` on IPv6.
55
1626
    pub fn supports_exit_port_ipv6(&self, port: u16) -> bool {
56
1626
        self.ipv6_policy().allows_port(port)
57
1626
    }
58
    /// Return true if this relay is suitable for use as a directory
59
    /// cache.
60
523988
    pub fn is_dir_cache(&self) -> bool {
61
523988
        rs_is_dir_cache(self.0.rs)
62
523988
    }
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
31452930
    pub fn is_flagged_fast(&self) -> bool {
68
31452930
        self.0.rs.is_flagged_fast()
69
31452930
    }
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
2205090
    pub fn is_flagged_stable(&self) -> bool {
75
2205090
        self.0.rs.is_flagged_stable()
76
2205090
    }
77
    /// Return true if this relay is a potential HS introduction point
78
26460
    pub fn is_hs_intro_point(&self) -> bool {
79
26460
        self.is_flagged_fast()
80
26460
            && self.0.rs.is_flagged_stable()
81
26460
            && !self.0.rs.is_flagged_middle_only()
82
26460
    }
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
1015380
    pub fn is_suitable_as_guard(&self) -> bool {
92
1015380
        self.0.rs.is_flagged_guard() && self.is_flagged_fast() && self.is_flagged_stable()
93
1015380
    }
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
18
    pub fn in_same_subnet(&self, other: &Relay<'_>, subnet_config: &SubnetConfig) -> bool {
102
18
        subnet_config.any_addrs_in_same_subnet(self.0, other)
103
18
    }
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
32958511
    pub fn in_same_family(&self, other: &Relay<'_>, family_rules: FamilyRules) -> bool {
108
32958511
        #![allow(clippy::collapsible_if)] // I prefer this style here.
109
32958511
        if self.0.same_relay_ids(other) {
110
1117626
            return true;
111
31840885
        }
112
31840885
        if family_rules.use_family_lists {
113
31834315
            if self.0.md.family().contains(other.rsa_id())
114
825306
                && other.md.family().contains(self.0.rsa_id())
115
            {
116
825306
                return true;
117
31009009
            }
118
6570
        }
119
31015579
        if family_rules.use_family_ids {
120
31009189
            let my_ids = self.0.md.family_ids();
121
31009189
            let their_ids = other.md.family_ids();
122
31009335
            if my_ids.iter().any(|my_id| their_ids.contains(my_id)) {
123
180
                return true;
124
31009009
            }
125
6390
        }
126

            
127
31015399
        false
128
32958511
    }
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
7412054
    pub fn policies_allow_some_port(&self) -> bool {
136
7412054
        if self.0.rs.is_flagged_bad_exit() {
137
2
            return false;
138
7412052
        }
139
7412052

            
140
7412052
        self.0.md.ipv4_policy().allows_some_port() || self.0.md.ipv6_policy().allows_some_port()
141
7412054
    }
142

            
143
    /// Return the IPv4 exit policy for this relay. If the relay has been marked BadExit, return an
144
    /// empty policy
145
11651421
    pub fn ipv4_policy(&self) -> Arc<PortPolicy> {
146
11651421
        if !self.0.rs.is_flagged_bad_exit() {
147
11651372
            Arc::clone(self.0.md.ipv4_policy())
148
        } else {
149
49
            Arc::new(PortPolicy::new_reject_all())
150
        }
151
11651421
    }
152
    /// Return the IPv6 exit policy for this relay. If the relay has been marked BadExit, return an
153
    /// empty policy
154
2665
    pub fn ipv6_policy(&self) -> Arc<PortPolicy> {
155
2665
        if !self.0.rs.is_flagged_bad_exit() {
156
2571
            Arc::clone(self.0.md.ipv6_policy())
157
        } else {
158
94
            Arc::new(PortPolicy::new_reject_all())
159
        }
160
2665
    }
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
2
    pub fn ipv4_declared_policy(&self) -> &Arc<PortPolicy> {
166
2
        self.0.md.ipv4_policy()
167
2
    }
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
2
    pub fn ipv6_declared_policy(&self) -> &Arc<PortPolicy> {
173
2
        self.0.md.ipv6_policy()
174
2
    }
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)]
183
pub struct UncheckedRelayDetails<'a>(pub(crate) &'a super::UncheckedRelay<'a>);
184

            
185
impl<'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
822995
    pub fn is_suitable_as_guard(&self) -> bool {
189
822995
        self.0.rs.is_flagged_guard() && self.0.rs.is_flagged_fast() && self.0.rs.is_flagged_stable()
190
822995
    }
191
    /// Return true if this relay is a potential directory cache.
192
469440
    pub fn is_dir_cache(&self) -> bool {
193
469440
        rs_is_dir_cache(self.0.rs)
194
469440
    }
195
}
196

            
197
/// Return true if `rs` is usable as a directory cache.
198
993428
fn rs_is_dir_cache(rs: &netstatus::MdConsensusRouterStatus) -> bool {
199
    use tor_protover::named::DIRCACHE_CONSDIFF;
200
993428
    rs.is_flagged_v2dir() && rs.protovers().supports_named_subver(DIRCACHE_CONSDIFF)
201
993428
}