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
17674047
    pub fn supports_exit_port_ipv4(&self, port: u16) -> bool {
52
17674047
        self.ipv4_policy().allows_port(port)
53
17674047
    }
54
    /// Return true if this relay allows exiting to `port` on IPv6.
55
1698
    pub fn supports_exit_port_ipv6(&self, port: u16) -> bool {
56
1698
        self.ipv6_policy().allows_port(port)
57
1698
    }
58
    /// Return true if this relay is suitable for use as a directory
59
    /// cache.
60
603676
    pub fn is_dir_cache(&self) -> bool {
61
603676
        rs_is_dir_cache(self.0.rs)
62
603676
    }
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
48445438
    pub fn is_flagged_fast(&self) -> bool {
68
48445438
        self.0.rs.is_flagged_fast()
69
48445438
    }
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
2662738
    pub fn is_flagged_stable(&self) -> bool {
75
2662738
        self.0.rs.is_flagged_stable()
76
2662738
    }
77
    /// Return true if this relay is a potential HS introduction point
78
27636
    pub fn is_hs_intro_point(&self) -> bool {
79
27636
        self.is_flagged_fast()
80
27636
            && self.0.rs.is_flagged_stable()
81
27636
            && !self.0.rs.is_flagged_middle_only()
82
27636
    }
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
1363188
    pub fn is_suitable_as_guard(&self) -> bool {
92
1363188
        self.0.rs.is_flagged_guard() && self.is_flagged_fast() && self.is_flagged_stable()
93
1363188
    }
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
51369089
    pub fn in_same_family(&self, other: &Relay<'_>, family_rules: FamilyRules) -> bool {
108
        #![allow(clippy::collapsible_if)] // I prefer this style here.
109
51369089
        if self.0.same_relay_ids(other) {
110
1408878
            return true;
111
49960211
        }
112
49960211
        if family_rules.use_family_lists {
113
49953349
            if self.0.md.family().contains(other.rsa_id())
114
1055767
                && other.md.family().contains(self.0.rsa_id())
115
            {
116
1055767
                return true;
117
48897582
            }
118
6862
        }
119
48904444
        if family_rules.use_family_ids {
120
48897770
            let my_ids = self.0.md.family_ids();
121
48897770
            let their_ids = other.md.family_ids();
122
48897916
            if my_ids.iter().any(|my_id| their_ids.contains(my_id)) {
123
188
                return true;
124
48897582
            }
125
6674
        }
126

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

            
140
11610610
        self.0.md.ipv4_policy().allows_some_port() || self.0.md.ipv6_policy().allows_some_port()
141
11610612
    }
142

            
143
    /// Return the IPv4 exit policy for this relay. If the relay has been marked BadExit, return an
144
    /// empty policy
145
17964135
    pub fn ipv4_policy(&self) -> Arc<PortPolicy> {
146
17964135
        if !self.0.rs.is_flagged_bad_exit() {
147
17964084
            Arc::clone(self.0.md.ipv4_policy())
148
        } else {
149
51
            Arc::new(PortPolicy::new_reject_all())
150
        }
151
17964135
    }
152
    /// Return the IPv6 exit policy for this relay. If the relay has been marked BadExit, return an
153
    /// empty policy
154
3159
    pub fn ipv6_policy(&self) -> Arc<PortPolicy> {
155
3159
        if !self.0.rs.is_flagged_bad_exit() {
156
3061
            Arc::clone(self.0.md.ipv6_policy())
157
        } else {
158
98
            Arc::new(PortPolicy::new_reject_all())
159
        }
160
3159
    }
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
1015545
    pub fn is_suitable_as_guard(&self) -> bool {
189
1015545
        self.0.rs.is_flagged_guard() && self.0.rs.is_flagged_fast() && self.0.rs.is_flagged_stable()
190
1015545
    }
191
    /// Return true if this relay is a potential directory cache.
192
525084
    pub fn is_dir_cache(&self) -> bool {
193
525084
        rs_is_dir_cache(self.0.rs)
194
525084
    }
195
}
196

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