1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
//! Functionality for exposing details about a relay that most users should avoid.
//!
//! ## Design notes
//!
//! These types aren't meant to be a dumping grounds
//! for every function in `Relay` or `UncheckedRelay`:
//! instead, they are for methods that are easy to misuse or misunderstand
//! if applied out-of-context.
//!
//! For example, it's generally wrong in most contexts
//! to check for a specific relay flag.
//! Instead, we should be checking whether the relay is suitable
//! for some particular _usage_,
//! which will itself depend on a combination of flags.
//!
//! Therefore, this module should be used for checking properties only when:
//! - The property is one that is usually subsumed
//! in a higher-level check.
//! - Using the lower-level property on its own poses a risk
//! of accidentally forgetting to check other important properties.
//!
//! If you find that your code is using this module, you should ask yourself
//! - whether the actual thing that you're testing
//! is something that _any other piece of code_ might want to test
//! - whether the collection of properties that you're testing
//! creates a risk of leaving out some other properties
//! that should also be tested,
//! for example in the future, if new relay flags or properties are introduced
//! that are supposed to influence relay selection or reuse.
//!
//! If you answer "yes" to either of these, it's better to define a higher-level property,
//! and have your code use that instead.
use std::sync::Arc;
use tor_linkspec::HasRelayIds;
use tor_netdoc::{doc::netstatus, types::policy::PortPolicy};
use crate::{Relay, SubnetConfig};
/// A view for lower-level details about a [`Relay`].
///
/// Most callers should avoid using this structure;
/// they should instead call higher-level functions
/// like those in the `tor-relay-selection` crate.
#[derive(Clone)]
pub struct RelayDetails<'a>(pub(crate) &'a super::Relay<'a>);
impl<'a> RelayDetails<'a> {
/// Return true if this relay allows exiting to `port` on IPv4.
pub fn supports_exit_port_ipv4(&self, port: u16) -> bool {
self.ipv4_policy().allows_port(port)
}
/// Return true if this relay allows exiting to `port` on IPv6.
pub fn supports_exit_port_ipv6(&self, port: u16) -> bool {
self.ipv6_policy().allows_port(port)
}
/// Return true if this relay is suitable for use as a directory
/// cache.
pub fn is_dir_cache(&self) -> bool {
rs_is_dir_cache(self.0.rs)
}
/// Return true if this relay has the "Fast" flag.
///
/// Most relays have this flag. It indicates that the relay is suitable for
/// circuits that need more than a minimal amount of bandwidth.
pub fn is_flagged_fast(&self) -> bool {
self.0.rs.is_flagged_fast()
}
/// Return true if this relay has the "Stable" flag.
///
/// Most relays have this flag. It indicates that the relay is suitable for
/// long-lived circuits.
pub fn is_flagged_stable(&self) -> bool {
self.0.rs.is_flagged_stable()
}
/// Return true if this relay is a potential HS introduction point
pub fn is_hs_intro_point(&self) -> bool {
self.is_flagged_fast() && self.0.rs.is_flagged_stable()
}
/// Return true if this relay is suitable for use as a newly sampled guard,
/// or for continuing to use as a guard.
pub fn is_suitable_as_guard(&self) -> bool {
self.0.rs.is_flagged_guard() && self.is_flagged_fast() && self.is_flagged_stable()
}
/// Return true if both relays are in the same subnet, as configured by
/// `subnet_config`.
///
/// Two relays are considered to be in the same subnet if they
/// have IPv4 addresses with the same `subnets_family_v4`-bit
/// prefix, or if they have IPv6 addresses with the same
/// `subnets_family_v6`-bit prefix.
pub fn in_same_subnet(&self, other: &Relay<'_>, subnet_config: &SubnetConfig) -> bool {
subnet_config.any_addrs_in_same_subnet(self.0, other)
}
/// Return true if both relays are in the same family.
///
/// (Every relay is considered to be in the same family as itself.)
pub fn in_same_family(&self, other: &Relay<'_>) -> bool {
if self.0.same_relay_ids(other) {
return true;
}
self.0.md.family().contains(other.rsa_id()) && other.md.family().contains(self.0.rsa_id())
}
/// Return true if there are any ports for which this Relay can be
/// used for exit traffic.
///
/// (Returns false if this relay doesn't allow exit traffic, or if it
/// has been flagged as a bad exit.)
pub fn policies_allow_some_port(&self) -> bool {
if self.0.rs.is_flagged_bad_exit() {
return false;
}
self.0.md.ipv4_policy().allows_some_port() || self.0.md.ipv6_policy().allows_some_port()
}
/// Return the IPv4 exit policy for this relay. If the relay has been marked BadExit, return an
/// empty policy
pub fn ipv4_policy(&self) -> Arc<PortPolicy> {
if !self.0.rs.is_flagged_bad_exit() {
Arc::clone(self.0.md.ipv4_policy())
} else {
Arc::new(PortPolicy::new_reject_all())
}
}
/// Return the IPv6 exit policy for this relay. If the relay has been marked BadExit, return an
/// empty policy
pub fn ipv6_policy(&self) -> Arc<PortPolicy> {
if !self.0.rs.is_flagged_bad_exit() {
Arc::clone(self.0.md.ipv6_policy())
} else {
Arc::new(PortPolicy::new_reject_all())
}
}
/// Return the IPv4 exit policy declared by this relay.
///
/// In contrast to [`RelayDetails::ipv4_policy`],
/// this does not verify if the relay is marked BadExit.
pub fn ipv4_declared_policy(&self) -> &Arc<PortPolicy> {
self.0.md.ipv4_policy()
}
/// Return the IPv6 exit policy declared by this relay.
///
/// In contrast to [`RelayDetails::ipv6_policy`],
/// this does not verify if the relay is marked BadExit.
pub fn ipv6_declared_policy(&self) -> &Arc<PortPolicy> {
self.0.md.ipv6_policy()
}
}
/// A view for lower-level details about a [`UncheckedRelay`](crate::UncheckedRelay).
///
/// Most callers should avoid using this structure;
/// they should instead call higher-level functions
/// like those in the `tor-relay-selection` crate.
#[derive(Debug, Clone)]
pub struct UncheckedRelayDetails<'a>(pub(crate) &'a super::UncheckedRelay<'a>);
impl<'a> UncheckedRelayDetails<'a> {
/// Return true if this relay is suitable for use as a newly sampled guard,
/// or for continuing to use as a guard.
pub fn is_suitable_as_guard(&self) -> bool {
self.0.rs.is_flagged_guard() && self.0.rs.is_flagged_fast() && self.0.rs.is_flagged_stable()
}
/// Return true if this relay is a potential directory cache.
pub fn is_dir_cache(&self) -> bool {
rs_is_dir_cache(self.0.rs)
}
}
/// Return true if `rs` is usable as a directory cache.
fn rs_is_dir_cache(rs: &netstatus::MdConsensusRouterStatus) -> bool {
use tor_protover::ProtoKind;
rs.is_flagged_v2dir() && rs.protovers().supports_known_subver(ProtoKind::DirCache, 2)
}