Skip to main content

tor_dircommon/
fallback.rs

1//! List of directories that ships with Tor, for initial directory
2//! operations.
3//!
4//! When a client doesn't have directory information yet, it uses a
5//! "Fallback Directory" to retrieve its initial information about the
6//! network.
7//!
8//! # Semver note
9//!
10//! The types in this module are re-exported from `arti-client` and
11//! `tor-dirmgr`: any changes here must be reflected there.
12
13use base64ct::{Base64Unpadded, Encoding as _};
14use derive_deftly::Deftly;
15use tor_config::derive::prelude::*;
16use tor_config::{ConfigBuildError, define_list_builder_accessors, define_list_builder_helper};
17use tor_llcrypto::pk::ed25519::Ed25519Identity;
18use tor_llcrypto::pk::rsa::RsaIdentity;
19
20use std::net::SocketAddr;
21
22/// A directory whose location ships with Tor (or arti), and which we
23/// can use for bootstrapping when we don't know anything else about
24/// the network.
25//
26// Note that we do *not* set serde(deny_unknown_fields) on this
27// structure: we want our fallback directory configuration format to
28// be future-proof against adding new info about each fallback.
29#[derive(Debug, Clone, Deftly, Eq, PartialEq)]
30#[derive_deftly(TorConfig)]
31#[deftly(tor_config(
32    no_default_trait,
33    post_build = "FallbackDirBuilder::post_build_validate"
34))]
35pub struct FallbackDir {
36    /// RSA identity for the directory relay
37    #[deftly(tor_config(no_default))]
38    rsa_identity: RsaIdentity,
39    /// Ed25519 identity for the directory relay
40    #[deftly(tor_config(no_default))]
41    ed_identity: Ed25519Identity,
42    /// List of ORPorts for the directory relay
43    //
44    // NOTE that we are not using the list_builder pattern here.
45    // We could change that, but doing so could break compatibility.
46    #[deftly(tor_config(no_magic, setter(skip), no_default))]
47    orports: Vec<SocketAddr>,
48}
49
50define_list_builder_accessors!(
51    struct FallbackDirBuilder {
52        pub orports: [SocketAddr],
53    }
54);
55
56impl FallbackDirBuilder {
57    /// Checks whether the built FallbackDir obeys its configuration.
58    fn post_build_validate(built: FallbackDir) -> Result<FallbackDir, ConfigBuildError> {
59        if built.orports.is_empty() {
60            return Err(ConfigBuildError::Invalid {
61                field: "orport".to_string(),
62                problem: "list was empty".to_string(),
63            });
64        }
65        Ok(built)
66    }
67}
68
69/// A list of fallback directories.
70///
71/// Fallback directories (represented by [`FallbackDir`]) are used by Tor
72/// clients when they don't already have enough other directory information to
73/// contact the network.
74#[derive(Debug, Clone, Default, PartialEq, Eq)]
75pub struct FallbackList {
76    /// The underlying fallbacks in this set.
77    fallbacks: Vec<FallbackDir>,
78}
79
80impl<T: IntoIterator<Item = FallbackDir>> From<T> for FallbackList {
81    fn from(fallbacks: T) -> Self {
82        FallbackList {
83            fallbacks: fallbacks.into_iter().collect(),
84        }
85    }
86}
87
88define_list_builder_helper! {
89    // pub because tor-dirmgr needs it for NetworkConfig.fallback_caches
90    pub struct FallbackListBuilder {
91        pub(crate) fallbacks: [FallbackDirBuilder],
92    }
93    built: FallbackList = FallbackList { fallbacks };
94    default = default_fallbacks();
95}
96
97impl FallbackList {
98    /// Return the number of fallbacks in this list.
99    pub fn len(&self) -> usize {
100        self.fallbacks.len()
101    }
102    /// Return true if there are no fallbacks in this list.
103    pub fn is_empty(&self) -> bool {
104        self.fallbacks.is_empty()
105    }
106    /// Returns an iterator over the [`FallbackDir`] items.
107    pub fn iter(&self) -> std::slice::Iter<'_, FallbackDir> {
108        self.fallbacks.iter()
109    }
110}
111
112/// Return a list of the default fallback directories shipped with
113/// arti.
114pub(crate) fn default_fallbacks() -> Vec<FallbackDirBuilder> {
115    /// Build a fallback directory; panic if input is bad.
116    fn fallback(rsa: &str, ed: &str, ports: &[&str]) -> FallbackDirBuilder {
117        let rsa = RsaIdentity::from_hex(rsa).expect("Bad hex in built-in fallback list");
118        let ed = Base64Unpadded::decode_vec(ed).expect("Bad hex in built-in fallback list");
119        let ed = Ed25519Identity::from_bytes(&ed).expect("Wrong length in built-in fallback list");
120        let mut bld = FallbackDir::builder();
121        bld.rsa_identity(rsa).ed_identity(ed);
122
123        ports
124            .iter()
125            .map(|s| s.parse().expect("Bad socket address in fallbacklist"))
126            .for_each(|p| {
127                bld.orports().push(p);
128            });
129
130        bld
131    }
132    include!("../data/fallback_dirs.rs")
133}
134
135impl tor_linkspec::HasAddrs for FallbackDir {
136    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
137        self.orports.iter().copied()
138    }
139}
140impl tor_linkspec::HasRelayIdsLegacy for FallbackDir {
141    fn ed_identity(&self) -> &Ed25519Identity {
142        &self.ed_identity
143    }
144    fn rsa_identity(&self) -> &RsaIdentity {
145        &self.rsa_identity
146    }
147}
148
149impl tor_linkspec::DirectChanMethodsHelper for FallbackDir {}
150
151impl tor_linkspec::ChanTarget for FallbackDir {}