//! 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::{FamilyRules, 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. /// /// For flags, see . #[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 "Exit" flag. pub fn is_flagged_exit(&self) -> bool { self.0.rs.is_flagged_exit() } /// 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() && !self.0.rs.is_flagged_middle_only() } /// Return true if this relay is a potential HS rendezvous point pub fn is_hs_rend_point(&self) -> bool { self.is_flagged_fast() && self.0.rs.is_flagged_stable() && !self.0.rs.is_flagged_middle_only() } /// 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<'_>, family_rules: FamilyRules) -> bool { #![allow(clippy::collapsible_if)] // I prefer this style here. if self.0.same_relay_ids(other) { return true; } if family_rules.use_family_lists { if self.0.md.family().contains(other.rsa_id()) && other.md.family().contains(self.0.rsa_id()) { return true; } } if family_rules.use_family_ids { let my_ids = self.0.md.family_ids(); let their_ids = other.md.family_ids(); if my_ids.iter().any(|my_id| their_ids.contains(my_id)) { return true; } } false } /// 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 { if !self.0.rs.is_flagged_bad_exit() { Arc::clone(self.0.md.ipv4_policy().into()) } 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 { if !self.0.rs.is_flagged_bad_exit() { Arc::clone(self.0.md.ipv6_policy().into()) } 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 { self.0.md.ipv4_policy().into() } /// 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 { self.0.md.ipv6_policy().into() } } /// 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::MdRouterStatus) -> bool { use tor_protover::named::DIRCACHE_CONSDIFF; rs.is_flagged_v2dir() && rs.protovers().supports_named_subver(DIRCACHE_CONSDIFF) }