1
//! Types for managing directory configuration.
2
//!
3
//! Directory configuration tells us where to load and store directory
4
//! information, where to fetch it from, and how to validate it.
5
//!
6
//! # Semver note
7
//!
8
//! The types in this module are re-exported from `arti-client`: any changes
9
//! here must be reflected in the version of `arti-client`.
10

            
11
use crate::Result;
12
use crate::storage::DynStore;
13
use tor_dircommon::{
14
    authority::AuthorityContacts,
15
    config::{DirTolerance, DownloadScheduleConfig, NetworkConfig},
16
};
17
use tor_netdoc::doc::netstatus::{self};
18

            
19
use std::path::PathBuf;
20

            
21
/// Configuration type for network directory operations.
22
///
23
/// If the directory manager gains new configurabilities, this structure will gain additional
24
/// supertraits, as an API break.
25
///
26
/// Prefer to use `TorClientConfig`, which can be converted to this struct via
27
/// the `dir_mgr_config` method.
28
//
29
// We do not use a builder here.  Instead, additions or changes here are API breaks.
30
//
31
// Rationale:
32
//
33
// The purpose of using a builder is to allow the code to continue to
34
// compile when new fields are added to the built struct.
35
//
36
// However, here, the DirMgrConfig is just a subset of the fields of a
37
// TorClientConfig, and it is important that all its fields are
38
// initialized by arti-client.
39
//
40
// If it grows a field, arti-client ought not to compile any more.
41
#[derive(Debug, Clone)]
42
#[cfg_attr(test, derive(Default))]
43
#[allow(clippy::exhaustive_structs)]
44
pub struct DirMgrConfig {
45
    /// Location to use for storing and reading current-format
46
    /// directory information.
47
    ///
48
    /// Cannot be changed on a running Arti client.
49
    pub cache_dir: PathBuf,
50

            
51
    /// Rules for whether to trust the permissions on the cache_path.
52
    pub cache_trust: fs_mistrust::Mistrust,
53

            
54
    /// Configuration information about the network.
55
    pub network: NetworkConfig,
56

            
57
    /// Configuration information about when we download things.
58
    ///
59
    /// This can be replaced on a running Arti client. Doing so affects _future_
60
    /// download attempts, but has no effect on attempts that are currently in
61
    /// progress or being retried.
62
    ///
63
    /// (The above is a limitation: we would like it to someday have an effect
64
    /// on in-progress attempts as well, at least at the top level.  Users
65
    /// should _not_ assume that the effect of changing this option will always
66
    /// be delayed.)
67
    pub schedule: DownloadScheduleConfig,
68

            
69
    /// How much skew do we tolerate in directory validity times?
70
    pub tolerance: DirTolerance,
71

            
72
    /// A map of network parameters that we're overriding from their settings in
73
    /// the consensus.
74
    ///
75
    /// This can be replaced on a running Arti client.  Doing so will take
76
    /// effect the next time a consensus is downloaded.
77
    ///
78
    /// (The above is a limitation: we would like it to someday take effect
79
    /// immediately. Users should _not_ assume that the effect of changing this
80
    /// option will always be delayed.)
81
    pub override_net_params: netstatus::NetParams<i32>,
82

            
83
    /// Extra fields for extension purposes.
84
    ///
85
    /// These are kept in a separate type so that the type can be marked as
86
    /// `non_exhaustive` and used for optional features.
87
    pub extensions: DirMgrExtensions,
88
}
89

            
90
impl DirMgrConfig {
91
    /// Create a store from this configuration.
92
    ///
93
    /// Note that each time this is called, a new store object will be
94
    /// created: you probably only want to call this once.
95
399
    pub(crate) fn open_store(&self, readonly: bool) -> Result<DynStore> {
96
399
        Ok(Box::new(
97
399
            crate::storage::SqliteStore::from_path_and_mistrust(
98
399
                &self.cache_dir,
99
399
                &self.cache_trust,
100
399
                readonly,
101
            )?,
102
        ))
103
399
    }
104

            
105
    /// Return a slice of the configured authorities
106
296
    pub fn authorities(&self) -> &AuthorityContacts {
107
296
        self.network.authorities()
108
296
    }
109

            
110
    /// Return the configured set of fallback directories
111
2
    pub fn fallbacks(&self) -> &tor_dircommon::fallback::FallbackList {
112
2
        self.network.fallback_caches()
113
2
    }
114

            
115
    /// Construct a new configuration object where all replaceable fields in
116
    /// `self` are replaced with those from  `new_config`.
117
    ///
118
    /// Any fields which aren't allowed to change at runtime are copied from self.
119
70
    pub(crate) fn update_from_config(&self, new_config: &DirMgrConfig) -> DirMgrConfig {
120
        // NOTE: keep this in sync with the behaviour of `DirMgr::reconfigure`
121
70
        DirMgrConfig {
122
70
            cache_dir: self.cache_dir.clone(),
123
70
            cache_trust: self.cache_trust.clone(),
124
70
            network: new_config.network.clone(),
125
70
            schedule: new_config.schedule.clone(),
126
70
            tolerance: new_config.tolerance.clone(),
127
70
            override_net_params: new_config.override_net_params.clone(),
128
70
            extensions: new_config.extensions.clone(),
129
70
        }
130
70
    }
131

            
132
    /// Construct a new configuration object where all replaceable fields in
133
    /// `self` are replaced with those from  `new_config`.
134
    ///
135
    /// Any fields which aren't allowed to change at runtime are copied from self.
136
    #[cfg(feature = "experimental-api")]
137
    pub fn update_config(&self, new_config: &DirMgrConfig) -> DirMgrConfig {
138
        self.update_from_config(new_config)
139
    }
140
}
141

            
142
/// Optional extensions for configuring
143
#[derive(Debug, Clone, Default)]
144
#[non_exhaustive]
145
pub struct DirMgrExtensions {
146
    /// A filter to be used when installing new directory objects.
147
    #[cfg(feature = "dirfilter")]
148
    pub filter: crate::filter::FilterConfig,
149
}
150

            
151
#[cfg(test)]
152
mod test {
153
    // @@ begin test lint list maintained by maint/add_warning @@
154
    #![allow(clippy::bool_assert_comparison)]
155
    #![allow(clippy::clone_on_copy)]
156
    #![allow(clippy::dbg_macro)]
157
    #![allow(clippy::mixed_attributes_style)]
158
    #![allow(clippy::print_stderr)]
159
    #![allow(clippy::print_stdout)]
160
    #![allow(clippy::single_char_pattern)]
161
    #![allow(clippy::unwrap_used)]
162
    #![allow(clippy::unchecked_time_subtraction)]
163
    #![allow(clippy::useless_vec)]
164
    #![allow(clippy::needless_pass_by_value)]
165
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
166
    #![allow(clippy::unnecessary_wraps)]
167
    use super::*;
168
    use tempfile::tempdir;
169

            
170
    #[test]
171
    fn simplest_config() -> Result<()> {
172
        let tmp = tempdir().unwrap();
173

            
174
        let dir = DirMgrConfig {
175
            cache_dir: tmp.path().into(),
176
            ..Default::default()
177
        };
178

            
179
        assert!(dir.authorities().v3idents().len() >= 3);
180
        assert!(dir.fallbacks().len() >= 3);
181

            
182
        // TODO: verify other defaults.
183

            
184
        Ok(())
185
    }
186

            
187
    #[test]
188
    fn build_dirmgrcfg() -> Result<()> {
189
        let mut bld = DirMgrConfig::default();
190
        let tmp = tempdir().unwrap();
191

            
192
        bld.override_net_params.set("circwindow".into(), 999);
193
        bld.cache_dir = tmp.path().into();
194

            
195
        assert_eq!(bld.override_net_params.get("circwindow").unwrap(), &999);
196

            
197
        Ok(())
198
    }
199
}