Skip to main content

tor_dirmgr/
config.rs

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
11use crate::Result;
12use crate::storage::DynStore;
13use tor_dircommon::{
14    authority::AuthorityContacts,
15    config::{DirTolerance, DownloadScheduleConfig, NetworkConfig},
16};
17use tor_netdoc::doc::netstatus::{self};
18
19use 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)]
44pub 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
90impl 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    pub(crate) fn open_store(&self, readonly: bool) -> Result<DynStore> {
96        Ok(Box::new(
97            crate::storage::SqliteStore::from_path_and_mistrust(
98                &self.cache_dir,
99                &self.cache_trust,
100                readonly,
101            )?,
102        ))
103    }
104
105    /// Return a slice of the configured authorities
106    pub fn authorities(&self) -> &AuthorityContacts {
107        self.network.authorities()
108    }
109
110    /// Return the configured set of fallback directories
111    pub fn fallbacks(&self) -> &tor_dircommon::fallback::FallbackList {
112        self.network.fallback_caches()
113    }
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    pub(crate) fn update_from_config(&self, new_config: &DirMgrConfig) -> DirMgrConfig {
120        // NOTE: keep this in sync with the behaviour of `DirMgr::reconfigure`
121        DirMgrConfig {
122            cache_dir: self.cache_dir.clone(),
123            cache_trust: self.cache_trust.clone(),
124            network: new_config.network.clone(),
125            schedule: new_config.schedule.clone(),
126            tolerance: new_config.tolerance.clone(),
127            override_net_params: new_config.override_net_params.clone(),
128            extensions: new_config.extensions.clone(),
129        }
130    }
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]
145pub 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)]
152mod 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}