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

            
13
use base64ct::{Base64Unpadded, Encoding as _};
14
use derive_deftly::Deftly;
15
use tor_config::derive::prelude::*;
16
use tor_config::{ConfigBuildError, define_list_builder_accessors, define_list_builder_helper};
17
use tor_llcrypto::pk::ed25519::Ed25519Identity;
18
use tor_llcrypto::pk::rsa::RsaIdentity;
19

            
20
use 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
))]
35
pub 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

            
50
define_list_builder_accessors!(
51
    struct FallbackDirBuilder {
52
        pub orports: [SocketAddr],
53
    }
54
);
55

            
56
impl FallbackDirBuilder {
57
    /// Checks whether the built FallbackDir obeys its configuration.
58
1559367
    fn post_build_validate(built: FallbackDir) -> Result<FallbackDir, ConfigBuildError> {
59
1559367
        if built.orports.is_empty() {
60
            return Err(ConfigBuildError::Invalid {
61
                field: "orport".to_string(),
62
                problem: "list was empty".to_string(),
63
            });
64
1559367
        }
65
1559367
        Ok(built)
66
1559367
    }
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)]
75
pub struct FallbackList {
76
    /// The underlying fallbacks in this set.
77
    fallbacks: Vec<FallbackDir>,
78
}
79

            
80
impl<T: IntoIterator<Item = FallbackDir>> From<T> for FallbackList {
81
16
    fn from(fallbacks: T) -> Self {
82
16
        FallbackList {
83
16
            fallbacks: fallbacks.into_iter().collect(),
84
16
        }
85
16
    }
86
}
87

            
88
define_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

            
97
impl FallbackList {
98
    /// Return the number of fallbacks in this list.
99
96
    pub fn len(&self) -> usize {
100
96
        self.fallbacks.len()
101
96
    }
102
    /// Return true if there are no fallbacks in this list.
103
45
    pub fn is_empty(&self) -> bool {
104
45
        self.fallbacks.is_empty()
105
45
    }
106
    /// Returns an iterator over the [`FallbackDir`] items.
107
16155
    pub fn iter(&self) -> std::slice::Iter<'_, FallbackDir> {
108
16155
        self.fallbacks.iter()
109
16155
    }
110
}
111

            
112
/// Return a list of the default fallback directories shipped with
113
/// arti.
114
7793
pub(crate) fn default_fallbacks() -> Vec<FallbackDirBuilder> {
115
    /// Build a fallback directory; panic if input is bad.
116
1558600
    fn fallback(rsa: &str, ed: &str, ports: &[&str]) -> FallbackDirBuilder {
117
1558600
        let rsa = RsaIdentity::from_hex(rsa).expect("Bad hex in built-in fallback list");
118
1558600
        let ed = Base64Unpadded::decode_vec(ed).expect("Bad hex in built-in fallback list");
119
1558600
        let ed = Ed25519Identity::from_bytes(&ed).expect("Wrong length in built-in fallback list");
120
1558600
        let mut bld = FallbackDir::builder();
121
1558600
        bld.rsa_identity(rsa).ed_identity(ed);
122

            
123
1558600
        ports
124
1558600
            .iter()
125
2381093
            .map(|s| s.parse().expect("Bad socket address in fallbacklist"))
126
2381093
            .for_each(|p| {
127
2345693
                bld.orports().push(p);
128
2345693
            });
129

            
130
1558600
        bld
131
1558600
    }
132
    include!("../data/fallback_dirs.rs")
133
7793
}
134

            
135
impl tor_linkspec::HasAddrs for FallbackDir {
136
    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
137
        self.orports.iter().copied()
138
    }
139
}
140
impl tor_linkspec::HasRelayIdsLegacy for FallbackDir {
141
2175975
    fn ed_identity(&self) -> &Ed25519Identity {
142
2175975
        &self.ed_identity
143
2175975
    }
144
106875
    fn rsa_identity(&self) -> &RsaIdentity {
145
106875
        &self.rsa_identity
146
106875
    }
147
}
148

            
149
impl tor_linkspec::DirectChanMethodsHelper for FallbackDir {}
150

            
151
impl tor_linkspec::ChanTarget for FallbackDir {}