Lines
93.98 %
Functions
71.05 %
Branches
100 %
//! 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.
#[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.
/// 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 {
/// 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())
{
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)) {
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<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
pub fn ipv6_policy(&self) -> Arc<PortPolicy> {
Arc::clone(self.0.md.ipv6_policy())
/// 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`],
pub fn ipv6_declared_policy(&self) -> &Arc<PortPolicy> {
self.0.md.ipv6_policy()
/// A view for lower-level details about a [`UncheckedRelay`](crate::UncheckedRelay).
#[derive(Debug, Clone)]
pub struct UncheckedRelayDetails<'a>(pub(crate) &'a super::UncheckedRelay<'a>);
impl<'a> UncheckedRelayDetails<'a> {
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.
/// Return true if `rs` is usable as a directory cache.
fn rs_is_dir_cache(rs: &netstatus::MdConsensusRouterStatus) -> bool {
use tor_protover::named::DIRCACHE_CONSDIFF;
rs.is_flagged_v2dir() && rs.protovers().supports_named_subver(DIRCACHE_CONSDIFF)