1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
//! Types for conveniently constructing TorClients.

#![allow(missing_docs, clippy::missing_docs_in_private_items)]

use crate::{err::ErrorDetail, BootstrapBehavior, Result, TorClient, TorClientConfig};
use fs_mistrust::Mistrust;
use std::sync::Arc;
use tor_dirmgr::DirMgrConfig;
use tor_rtcompat::Runtime;

/// An object that knows how to construct some kind of DirProvider.
///
/// Note that this type is only actually exposed when the `experimental-api`
/// feature is enabled.
#[allow(unreachable_pub)]
pub trait DirProviderBuilder<R: Runtime> {
    fn build(
        &self,
        runtime: R,
        circmgr: Arc<tor_circmgr::CircMgr<R>>,
        config: DirMgrConfig,
    ) -> Result<Arc<dyn tor_dirmgr::DirProvider + 'static>>;
}

/// A DirProviderBuilder that constructs a regular DirMgr.
#[derive(Clone, Debug)]
struct DirMgrBuilder {}

impl<R: Runtime> DirProviderBuilder<R> for DirMgrBuilder {
    fn build(
        &self,
        runtime: R,
        circmgr: Arc<tor_circmgr::CircMgr<R>>,
        config: DirMgrConfig,
    ) -> Result<Arc<dyn tor_dirmgr::DirProvider + 'static>> {
        let dirmgr = tor_dirmgr::DirMgr::create_unbootstrapped(config, runtime, circmgr)
            .map_err(ErrorDetail::from)?;
        Ok(Arc::new(dirmgr))
    }
}

/// Rules about whether to replace the `Mistrust` from the configuration.
#[derive(Clone, Debug)]
enum FsMistrustOverride {
    /// Disable the mistrust in the configuration if the the environment
    /// variable `ARTI_FS_DISABLE_PERMISSION_CHECKS` is set.
    FromEnvironment,
    /// Disable the mistrust in the configuration unconditionally.
    Disable,
    /// Always use the mistrust in the configuration.
    None,
}

/// An object for constructing a [`TorClient`].
///
/// Returned by [`TorClient::builder()`].
#[derive(Clone)]
#[must_use]
pub struct TorClientBuilder<R: Runtime> {
    /// The runtime for the client to use
    runtime: R,
    /// The client's configuration.
    config: TorClientConfig,
    /// How the client should behave when it is asked to do something on the Tor
    /// network before `bootstrap()` is called.
    bootstrap_behavior: BootstrapBehavior,
    /// How the client should decide which file permissions to trust.
    fs_mistrust_override: FsMistrustOverride,
    /// Optional object to construct a DirProvider.
    ///
    /// Wrapped in an Arc so that we don't need to force DirProviderBuilder to
    /// implement Clone.
    dirmgr_builder: Arc<dyn DirProviderBuilder<R>>,
    /// Optional directory filter to install for testing purposes.
    ///
    /// Only available when `arti-client` is built with the `dirfilter` and `experimental-api` features.
    #[cfg(feature = "dirfilter")]
    dirfilter: tor_dirmgr::filter::FilterConfig,
}

impl<R: Runtime> TorClientBuilder<R> {
    /// Construct a new TorClientBuilder with the given runtime.
    pub(crate) fn new(runtime: R) -> Self {
        Self {
            runtime,
            config: TorClientConfig::default(),
            bootstrap_behavior: BootstrapBehavior::default(),
            fs_mistrust_override: FsMistrustOverride::FromEnvironment,
            dirmgr_builder: Arc::new(DirMgrBuilder {}),
            #[cfg(feature = "dirfilter")]
            dirfilter: None,
        }
    }

    /// Set the configuration for the `TorClient` under construction.
    ///
    /// If not called, then a compiled-in default configuration will be used.
    pub fn config(mut self, config: TorClientConfig) -> Self {
        self.config = config;
        self
    }

    /// Set the bootstrap behavior for the `TorClient` under construction.
    ///
    /// If not called, then the default ([`BootstrapBehavior::OnDemand`]) will
    /// be used.
    pub fn bootstrap_behavior(mut self, bootstrap_behavior: BootstrapBehavior) -> Self {
        self.bootstrap_behavior = bootstrap_behavior;
        self
    }

    /// Build an [`TorClient`] that will not validate permissions and ownership
    /// on the filesystem.
    ///
    /// By default, these checks are configured with the `storage.permissions`
    /// field of the configuration, and can be overridden with the
    /// `ARTI_FS_DISABLE_PERMISSION_CHECKS` environment variable.
    pub fn disable_fs_permission_checks(mut self) -> Self {
        self.fs_mistrust_override = FsMistrustOverride::Disable;
        self
    }

    /// Build a [`TorClient`] that will follow the permissions checks in
    /// the `storage.permissions` field of the configuration, regardless of how the
    /// environment is set.
    pub fn ignore_fs_permission_checks_env_var(mut self) -> Self {
        self.fs_mistrust_override = FsMistrustOverride::None;
        self
    }

    /// Build a [`TorClient`] that will follow the permissions checks in the
    /// `storage.permissions` field of the configuration, unless  the
    /// `ARTI_FS_DISABLE_PERMISSION_CHECKS` environment variable is set.
    ///
    /// This is the default.
    pub fn obey_fs_permission_checks_env_var(mut self) -> Self {
        self.fs_mistrust_override = FsMistrustOverride::FromEnvironment;
        self
    }

    /// Override the default function used to construct the directory provider.
    ///
    /// Only available when compiled with the `experimental-api` feature: this
    /// code is unstable.
    #[cfg(all(feature = "experimental-api", feature = "error_detail"))]
    pub fn dirmgr_builder<B>(mut self, builder: Arc<dyn DirProviderBuilder<R>>) -> Self
    where
        B: DirProviderBuilder<R> + 'static,
    {
        self.dirmgr_builder = builder;
        self
    }

    /// Install a [`DirFilter`](tor_dirmgr::filter::DirFilter) to
    ///
    /// Only available when compiled with the `dirfilter` feature: this code
    /// is unstable and not recommended for production use.
    #[cfg(feature = "dirfilter")]
    pub fn dirfilter<F>(mut self, filter: F) -> Self
    where
        F: Into<Arc<dyn tor_dirmgr::filter::DirFilter + 'static>>,
    {
        self.dirfilter = Some(filter.into());
        self
    }

    /// Create a `TorClient` from this builder, without automatically launching
    /// the bootstrap process.
    ///
    /// If you have left the default [`BootstrapBehavior`] in place, the client
    /// will bootstrap itself as soon any attempt is made to use it.  You can
    /// also bootstrap the client yourself by running its
    /// [`bootstrap()`](TorClient::bootstrap) method.
    ///
    /// If you have replaced the default behavior with [`BootstrapBehavior::Manual`],
    /// any attempts to use the client will fail with an error of kind
    /// [`ErrorKind::BootstrapRequired`](crate::ErrorKind::BootstrapRequired),
    /// until you have called [`TorClient::bootstrap`] yourself.  
    /// This option is useful if you wish to have control over the bootstrap
    /// process (for example, you might wish to avoid initiating network
    /// connections until explicit user confirmation is given).
    pub fn create_unbootstrapped(self) -> Result<TorClient<R>> {
        #[allow(unused_mut)]
        let mut dirmgr_extensions = tor_dirmgr::config::DirMgrExtensions::default();
        #[cfg(feature = "dirfilter")]
        {
            dirmgr_extensions.filter = self.dirfilter;
        }

        let override_mistrust: Option<Mistrust> = match self.fs_mistrust_override {
            FsMistrustOverride::FromEnvironment
                if crate::config::fs_permissions_checks_disabled_via_env() =>
            {
                Some(Mistrust::new_dangerously_trust_everyone())
            }
            FsMistrustOverride::Disable => Some(Mistrust::new_dangerously_trust_everyone()),
            _ => None,
        };

        TorClient::create_inner(
            self.runtime,
            self.config,
            self.bootstrap_behavior,
            override_mistrust,
            self.dirmgr_builder.as_ref(),
            dirmgr_extensions,
        )
        .map_err(ErrorDetail::into)
    }

    /// Create a TorClient from this builder, and try to bootstrap it.
    pub async fn create_bootstrapped(self) -> Result<TorClient<R>> {
        let r = self.create_unbootstrapped()?;
        r.bootstrap().await?;
        Ok(r)
    }
}