Skip to main content

arti_client/
client.rs

1//! A general interface for Tor client usage.
2//!
3//! To construct a client, run the [`TorClient::create_bootstrapped`] method.
4//! Once the client is bootstrapped, you can make anonymous
5//! connections ("streams") over the Tor network using
6//! [`TorClient::connect`].
7
8#[cfg(feature = "rpc")]
9use {derive_deftly::Deftly, tor_rpcbase::templates::*};
10
11use crate::address::{IntoTorAddr, ResolveInstructions, StreamInstructions};
12
13use crate::config::{ClientAddrConfig, StreamTimeoutConfig, TorClientConfig};
14use crate::status::BootstrapStatus;
15use safelog::{Sensitive, sensitive};
16use tor_async_utils::{DropNotifyWatchSender, PostageWatchSenderExt};
17use tor_chanmgr::ChanMgrConfig;
18use tor_circmgr::ClientDataTunnel;
19use tor_circmgr::isolation::{Isolation, StreamIsolation};
20use tor_circmgr::{IsolationToken, TargetPort, isolation::StreamIsolationBuilder};
21use tor_config::{BoolOrAuto, MutCfg};
22#[cfg(feature = "bridge-client")]
23use tor_dirmgr::bridgedesc::BridgeDescMgr;
24use tor_dirmgr::{DirMgrStore, Timeliness};
25use tor_error::{Bug, error_report, internal};
26use tor_guardmgr::{GuardMgr, RetireCircuits};
27use tor_keymgr::Keystore;
28use tor_memquota::MemoryQuotaTracker;
29use tor_netdir::{NetDirProvider, params::NetParameters};
30use tor_persist::StateMgr;
31#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
32use tor_persist::TestingStateMgr;
33use tor_proto::client::stream::{DataStream, IpVersionPreference, StreamParameters};
34#[cfg(all(
35    any(feature = "native-tls", feature = "rustls"),
36    any(feature = "async-std", feature = "tokio"),
37))]
38use tor_rtcompat::PreferredRuntime;
39use tor_rtcompat::{Runtime, SleepProviderExt};
40#[cfg(feature = "onion-service-client")]
41use {
42    tor_hsclient::{HsClientConnector, HsClientDescEncKeypairSpecifier, HsClientSecretKeysBuilder},
43    tor_hscrypto::pk::{HsClientDescEncKey, HsClientDescEncKeypair, HsClientDescEncSecretKey},
44    tor_keymgr::CTorClientKeystore,
45    tor_netdir::DirEvent,
46};
47#[cfg(feature = "onion-service-service")]
48use {tor_keymgr::CTorServiceKeystore, tor_persist::state_dir::StateDirectory};
49
50#[cfg(all(feature = "onion-service-service", feature = "experimental-api"))]
51use tor_hsservice::HsIdKeypairSpecifier;
52#[cfg(all(feature = "onion-service-client", feature = "experimental-api"))]
53use {tor_hscrypto::pk::HsId, tor_hscrypto::pk::HsIdKeypair, tor_keymgr::KeystoreSelector};
54
55use tor_keymgr::{ArtiNativeKeystore, KeyMgr, KeyMgrBuilder, config::ArtiKeystoreKind};
56
57#[cfg(feature = "ephemeral-keystore")]
58use tor_keymgr::ArtiEphemeralKeystore;
59
60use futures::StreamExt as _;
61use futures::lock::Mutex as AsyncMutex;
62use std::net::IpAddr;
63use std::result::Result as StdResult;
64use std::sync::{Arc, Mutex};
65use tor_rtcompat::SpawnExt;
66
67use crate::err::ErrorDetail;
68use crate::{TorClientBuilder, status, util};
69#[cfg(feature = "geoip")]
70use tor_geoip::CountryCode;
71use tor_rtcompat::scheduler::TaskHandle;
72use tracing::{debug, info, instrument, warn};
73
74#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
75use tor_persist::FsStateMgr as UsingStateMgr;
76
77// TODO wasm: This is not the right choice, but at least it compiles.
78#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
79use tor_persist::TestingStateMgr as UsingStateMgr;
80
81/// An active client session on the Tor network.
82///
83/// While it's running, it will fetch directory information, build
84/// circuits, and make connections for you.
85///
86/// # In the Arti RPC System
87///
88/// An open client on the Tor network.
89///
90/// A `TorClient` can be used to open anonymous connections,
91/// and (eventually) perform other activities.
92///
93/// You can use an `RpcSession` as a `TorClient`, or use the `isolated_client` method
94/// to create a new `TorClient` whose stream will not share circuits with any other Tor client.
95///
96/// This ObjectID for this object can be used as the target of a SOCKS stream.
97#[cfg_attr(
98    feature = "rpc",
99    derive(Deftly),
100    derive_deftly(Object),
101    deftly(rpc(expose_outside_of_session))
102)]
103pub struct TorClient<R: Runtime> {
104    /// Default isolation token for streams through this client.
105    ///
106    /// This is eventually used for `owner_token` in `tor-circmgr/src/usage.rs`, and is orthogonal
107    /// to the `stream_isolation` which comes from `connect_prefs` (or a passed-in `StreamPrefs`).
108    /// (ie, both must be the same to share a circuit).
109    client_isolation: IsolationToken,
110    /// Connection preferences.  Starts out as `Default`,  Inherited by our clones.
111    connect_prefs: StreamPrefs,
112
113    /// Inner structure respresenting all components shared across different
114    /// TorClients.
115    client: Arc<ClientShared<R>>,
116}
117
118/// Shared pieces of a `TorClient`, used to implement client functionality.
119///
120/// In the future, we might choose to expose this along with APIs.
121struct ClientShared<R: Runtime> {
122    /// Asynchronous runtime object.
123    runtime: R,
124
125    /// Inner typestate object to represent the parts of the ClientShared that may be absent
126    /// depending on whether we are running.
127    inner: Mutex<Inner<R>>,
128
129    /// Memory quota tracker
130    memquota: Arc<MemoryQuotaTracker>,
131
132    /// A handle to this client's [`InertTorClient`].
133    ///
134    /// Used for accessing the key manager and other persistent state.
135    inert_client: InertTorClient,
136
137    /// Location on disk where we store persistent data containing both location and Mistrust information.
138    ///
139    ///
140    /// This path is configured via `[storage]` in the config but is not used directly as a
141    /// StateDirectory in most places. Instead, its path and Mistrust information are copied
142    /// to subsystems like `dirmgr`, `keymgr`, and `statemgr` during `TorClient` creation.
143    #[cfg(feature = "onion-service-service")]
144    state_directory: StateDirectory,
145    /// Location on disk where we store persistent data (cooked state manager).
146    statemgr: UsingStateMgr,
147
148    /// Directory manager persistent storage.
149    dirmgr_store: DirMgrStore<R>,
150
151    /// Client address configuration
152    addrcfg: MutCfg<ClientAddrConfig>,
153    /// Client DNS configuration
154    timeoutcfg: MutCfg<StreamTimeoutConfig>,
155    /// Mutex used to serialize concurrent attempts to reconfigure a TorClient.
156    ///
157    /// See [`TorClient::reconfigure`] for more information on its use.
158    reconfigure_lock: Arc<Mutex<()>>,
159
160    /// A stream of bootstrap messages that we can clone when a client asks for
161    /// it.
162    ///
163    /// (We don't need to observe this stream ourselves, since it drops each
164    /// unobserved status change when the next status change occurs.)
165    status_receiver: status::BootstrapEvents,
166
167    /// mutex used to prevent two tasks from trying to bootstrap at once.
168    bootstrap_in_progress: AsyncMutex<()>,
169
170    /// Sender used to update changes in our bootstrap settings.
171    bootstrap_setting_sender: Mutex<postage::watch::Sender<BootstrapSetting>>,
172
173    /// Whether or not we should call `bootstrap` before doing things that require
174    /// bootstrapping.
175    ///
176    /// If this is [`BootstrapBehavior::OnDemand`], we wait for the client to bootstrap
177    /// (launching a bootstrap if necessary) before performing any operation that needs circuits.
178    /// If this is [`BootstrapBehavior::Manual`], we give an error if we are told to do
179    /// something that needs circuits and we have not been told to bootstrap.
180    should_bootstrap: BootstrapBehavior,
181
182    /// Shared boolean for whether we're currently in "dormant mode" or not.
183    //
184    // The sent value is `Option`, so that `None` is sent when the sender, here,
185    // is dropped,.  That shuts down the monitoring task.
186    dormant: Mutex<DropNotifyWatchSender<Option<DormantMode>>>,
187
188    /// The path resolver given to us by a [`TorClientConfig`].
189    ///
190    /// We must not add our own variables to it since `TorClientConfig` uses it to perform its own
191    /// path expansions. If we added our own variables, it would introduce an inconsistency where
192    /// paths expanded by the `TorClientConfig` would expand differently than when expanded by us.
193    path_resolver: Arc<tor_config_path::CfgPathResolver>,
194}
195
196/// A typestate object holding the parts of the client state that we may or may not have
197/// depending on whether we are running.
198enum Inner<R: Runtime> {
199    /// The client is not constructed.
200    ///
201    /// In this state, the client won't try to connect to the network.
202    NotConstructed(Box<NotConstructedInner<R>>),
203
204    /// The client is either bootstrapped or trying to bootstrap.
205    Running(Arc<RunningInner<R>>),
206
207    /// The client has failed in a non-recoverable way.
208    Poisoned(Box<ErrorDetail>),
209}
210
211/// Information stored by a never-bootstrapped [`TorClient`],
212/// used to eventually construct a [`RunningInner`] and bootstrap.
213struct NotConstructedInner<R: Runtime> {
214    /// The client's configuration.
215    config: TorClientConfig,
216
217    /// A receiver to give to various tasks that want to monitor our dormant status.
218    dormant_recv: postage::watch::Receiver<Option<DormantMode>>,
219
220    /// A sender used to produce updates about our bootstrapping status.
221    ///
222    /// NOTE: The fact that this type is not Clone is the only reason
223    /// that [`RunningInner::new`] needs to take NotConstructedInner by value.
224    /// With some redesign we could simplify this, and do away with [`Inner::Poisoned`].
225    status_sender: postage::watch::Sender<BootstrapStatus>,
226
227    /// A receiver used to inform the bootstrap status processor about changes in our settings.
228    bootstrap_setting_receiver: postage::watch::Receiver<BootstrapSetting>,
229
230    /// A (possibly user-provided) builder used to construct our NetDirProvider.
231    dirmgr_builder: Arc<dyn crate::builder::DirProviderBuilder<R>>,
232
233    /// A (possibly user-provided) set of in-process extensions for our NetDirProvider.
234    dirmgr_extensions: tor_dirmgr::config::DirMgrExtensions,
235}
236
237/// Data structures for a "running" client.
238///
239/// A running client is one that is either bootstrapped, or potentially trying to bootstrap.
240///
241/// All structures that potentially interact with the network belong here.
242///
243/// We defer the creation of this structure and its members until bootstrap time,
244/// to make sure that before we are bootstrapping, nothing will try to connect to the network
245/// or launch expensive background tasks.
246struct RunningInner<R: Runtime> {
247    /// Channel manager, used by circuits etc.,
248    ///
249    /// Used directly by client only for reconfiguration.
250    chanmgr: Arc<tor_chanmgr::ChanMgr<R>>,
251    /// Circuit manager for keeping our circuits up to date and building
252    /// them on-demand.
253    circmgr: Arc<tor_circmgr::CircMgr<R>>,
254    /// Directory manager for keeping our directory material up to date.
255    dirmgr: Arc<dyn tor_dirmgr::DirProvider>,
256    /// Bridge descriptor manager
257    ///
258    /// None until we have bootstrapped.
259    ///
260    /// Lock hierarchy: don't acquire this before dormant
261    //
262    // TODO: after or as part of https://gitlab.torproject.org/tpo/core/arti/-/issues/634
263    // this can be   bridge_desc_mgr: BridgeDescMgr<R>>
264    // since BridgeDescMgr is Clone and all its methods take `&self` (it has a lock inside)
265    // Or maybe BridgeDescMgr should not be Clone, since we want to make Weaks of it,
266    // which we can't do when the Arc is inside.
267    #[cfg(feature = "bridge-client")]
268    bridge_desc_mgr: Arc<Mutex<Option<Arc<BridgeDescMgr<R>>>>>,
269    /// Pluggable transport manager.
270    #[cfg(feature = "pt-client")]
271    pt_mgr: Arc<tor_ptmgr::PtMgr<R>>,
272    /// HS client connector
273    #[cfg(feature = "onion-service-client")]
274    hsclient: HsClientConnector<R>,
275    /// Circuit pool for providing onion services with circuits.
276    #[cfg(any(feature = "onion-service-client", feature = "onion-service-service"))]
277    hs_circ_pool: Arc<tor_circmgr::hspool::HsCircPool<R>>,
278    /// Guard manager
279    #[cfg_attr(not(feature = "bridge-client"), allow(dead_code))]
280    guardmgr: GuardMgr<R>,
281}
282
283/// A Tor client that is not runnable.
284///
285/// Can be used to access the state that would be used by a running [`TorClient`].
286///
287/// An `InertTorClient` never connects to the network.
288#[derive(Clone)]
289pub struct InertTorClient {
290    /// The key manager.
291    ///
292    /// This is used for retrieving private keys, certificates, and other sensitive data (for
293    /// example, for retrieving the keys necessary for connecting to hidden services that are
294    /// running in restricted discovery mode).
295    ///
296    /// If this crate is compiled _with_ the `keymgr` feature, [`TorClient`] will use a functional
297    /// key manager implementation.
298    ///
299    /// If this crate is compiled _without_ the `keymgr` feature, then [`TorClient`] will use a
300    /// no-op key manager implementation instead.
301    ///
302    /// See the [`KeyMgr`] documentation for more details.
303    keymgr: Option<Arc<KeyMgr>>,
304}
305
306impl InertTorClient {
307    /// Create an `InertTorClient` from a `TorClientConfig`.
308    pub(crate) fn new(config: &TorClientConfig) -> StdResult<Self, ErrorDetail> {
309        let keymgr = Self::create_keymgr(config)?;
310
311        Ok(Self { keymgr })
312    }
313
314    /// Create a [`KeyMgr`] using the specified configuration.
315    ///
316    /// Returns `Ok(None)` if keystore use is disabled.
317    fn create_keymgr(config: &TorClientConfig) -> StdResult<Option<Arc<KeyMgr>>, ErrorDetail> {
318        let keystore = config.storage.keystore();
319        let permissions = config.storage.permissions();
320        let primary_store: Box<dyn Keystore> = match keystore.primary_kind() {
321            Some(ArtiKeystoreKind::Native) => {
322                let (state_dir, _mistrust) = config.state_dir()?;
323                let key_store_dir = state_dir.join("keystore");
324
325                let native_store =
326                    ArtiNativeKeystore::from_path_and_mistrust(&key_store_dir, permissions)?;
327                // Should only log fs paths at debug level or lower,
328                // unless they're part of a diagnostic message.
329                debug!("Using keystore from {key_store_dir:?}");
330
331                Box::new(native_store)
332            }
333            #[cfg(feature = "ephemeral-keystore")]
334            Some(ArtiKeystoreKind::Ephemeral) => {
335                // TODO: make the keystore ID somehow configurable
336                let ephemeral_store: ArtiEphemeralKeystore =
337                    ArtiEphemeralKeystore::new("ephemeral".to_string());
338                Box::new(ephemeral_store)
339            }
340            None => {
341                info!("Running without a keystore");
342                return Ok(None);
343            }
344            ty => return Err(internal!("unrecognized keystore type {ty:?}").into()),
345        };
346
347        let mut builder = KeyMgrBuilder::default().primary_store(primary_store);
348
349        #[cfg(feature = "onion-service-service")]
350        for config in config.storage.keystore().ctor_svc_stores() {
351            let store: Box<dyn Keystore> = Box::new(CTorServiceKeystore::from_path_and_mistrust(
352                config.path(),
353                permissions,
354                config.id().clone(),
355                // TODO: these nicknames should be cross-checked with configured
356                // svc nicknames as part of config validation!!!
357                config.nickname().clone(),
358            )?);
359
360            builder.secondary_stores().push(store);
361        }
362
363        #[cfg(feature = "onion-service-client")]
364        for config in config.storage.keystore().ctor_client_stores() {
365            let store: Box<dyn Keystore> = Box::new(CTorClientKeystore::from_path_and_mistrust(
366                config.path(),
367                permissions,
368                config.id().clone(),
369            )?);
370
371            builder.secondary_stores().push(store);
372        }
373
374        let keymgr = builder
375            .build()
376            .map_err(|_| internal!("failed to build keymgr"))?;
377        Ok(Some(Arc::new(keymgr)))
378    }
379
380    /// Generate a service discovery keypair for connecting to a hidden service running in
381    /// "restricted discovery" mode.
382    ///
383    /// See [`TorClient::generate_service_discovery_key`].
384    //
385    // TODO: decide whether this should use get_or_generate before making it
386    // non-experimental
387    #[cfg(all(
388        feature = "onion-service-client",
389        feature = "experimental-api",
390        feature = "keymgr"
391    ))]
392    #[cfg_attr(
393        docsrs,
394        doc(cfg(all(
395            feature = "onion-service-client",
396            feature = "experimental-api",
397            feature = "keymgr"
398        )))
399    )]
400    pub fn generate_service_discovery_key(
401        &self,
402        selector: KeystoreSelector,
403        hsid: HsId,
404    ) -> crate::Result<HsClientDescEncKey> {
405        let mut rng = tor_llcrypto::rng::CautiousRng;
406        let spec = HsClientDescEncKeypairSpecifier::new(hsid);
407        let key = self
408            .keymgr
409            .as_ref()
410            .ok_or(ErrorDetail::KeystoreRequired {
411                action: "generate client service discovery key",
412            })?
413            .generate::<HsClientDescEncKeypair>(
414                &spec, selector, &mut rng, false, /* overwrite */
415            )?;
416
417        Ok(key.public().clone())
418    }
419
420    /// Rotate the service discovery keypair for connecting to a hidden service running in
421    /// "restricted discovery" mode.
422    ///
423    /// See [`TorClient::rotate_service_discovery_key`].
424    #[cfg(all(
425        feature = "onion-service-client",
426        feature = "experimental-api",
427        feature = "keymgr"
428    ))]
429    pub fn rotate_service_discovery_key(
430        &self,
431        selector: KeystoreSelector,
432        hsid: HsId,
433    ) -> crate::Result<HsClientDescEncKey> {
434        let mut rng = tor_llcrypto::rng::CautiousRng;
435        let spec = HsClientDescEncKeypairSpecifier::new(hsid);
436        let key = self
437            .keymgr
438            .as_ref()
439            .ok_or(ErrorDetail::KeystoreRequired {
440                action: "rotate client service discovery key",
441            })?
442            .generate::<HsClientDescEncKeypair>(
443                &spec, selector, &mut rng, true, /* overwrite */
444            )?;
445
446        Ok(key.public().clone())
447    }
448
449    /// Insert a service discovery secret key for connecting to a hidden service running in
450    /// "restricted discovery" mode
451    ///
452    /// See [`TorClient::insert_service_discovery_key`].
453    #[cfg(all(
454        feature = "onion-service-client",
455        feature = "experimental-api",
456        feature = "keymgr"
457    ))]
458    #[cfg_attr(
459        docsrs,
460        doc(cfg(all(
461            feature = "onion-service-client",
462            feature = "experimental-api",
463            feature = "keymgr"
464        )))
465    )]
466    pub fn insert_service_discovery_key(
467        &self,
468        selector: KeystoreSelector,
469        hsid: HsId,
470        hs_client_desc_enc_secret_key: HsClientDescEncSecretKey,
471    ) -> crate::Result<HsClientDescEncKey> {
472        let spec = HsClientDescEncKeypairSpecifier::new(hsid);
473        let client_desc_enc_key = HsClientDescEncKey::from(&hs_client_desc_enc_secret_key);
474        let client_desc_enc_keypair =
475            HsClientDescEncKeypair::new(client_desc_enc_key.clone(), hs_client_desc_enc_secret_key);
476        let _key = self
477            .keymgr
478            .as_ref()
479            .ok_or(ErrorDetail::KeystoreRequired {
480                action: "insert client service discovery key",
481            })?
482            .insert::<HsClientDescEncKeypair>(client_desc_enc_keypair, &spec, selector, false)?;
483        Ok(client_desc_enc_key)
484    }
485
486    /// Return the service discovery public key for the service with the specified `hsid`.
487    ///
488    /// See [`TorClient::get_service_discovery_key`].
489    #[cfg(all(feature = "onion-service-client", feature = "experimental-api"))]
490    #[cfg_attr(
491        docsrs,
492        doc(cfg(all(feature = "onion-service-client", feature = "experimental-api")))
493    )]
494    pub fn get_service_discovery_key(
495        &self,
496        hsid: HsId,
497    ) -> crate::Result<Option<HsClientDescEncKey>> {
498        let spec = HsClientDescEncKeypairSpecifier::new(hsid);
499        let key = self
500            .keymgr
501            .as_ref()
502            .ok_or(ErrorDetail::KeystoreRequired {
503                action: "get client service discovery key",
504            })?
505            .get::<HsClientDescEncKeypair>(&spec)?
506            .map(|key| key.public().clone());
507
508        Ok(key)
509    }
510
511    /// Removes the service discovery keypair for the service with the specified `hsid`.
512    ///
513    /// See [`TorClient::remove_service_discovery_key`].
514    #[cfg(all(
515        feature = "onion-service-client",
516        feature = "experimental-api",
517        feature = "keymgr"
518    ))]
519    #[cfg_attr(
520        docsrs,
521        doc(cfg(all(
522            feature = "onion-service-client",
523            feature = "experimental-api",
524            feature = "keymgr"
525        )))
526    )]
527    pub fn remove_service_discovery_key(
528        &self,
529        selector: KeystoreSelector,
530        hsid: HsId,
531    ) -> crate::Result<Option<()>> {
532        let spec = HsClientDescEncKeypairSpecifier::new(hsid);
533        let result = self
534            .keymgr
535            .as_ref()
536            .ok_or(ErrorDetail::KeystoreRequired {
537                action: "remove client service discovery key",
538            })?
539            .remove::<HsClientDescEncKeypair>(&spec, selector)?;
540        match result {
541            Some(_) => Ok(Some(())),
542            None => Ok(None),
543        }
544    }
545
546    /// Getter for keymgr.
547    #[cfg(feature = "onion-service-cli-extra")]
548    pub fn keymgr(&self) -> crate::Result<&KeyMgr> {
549        Ok(self.keymgr.as_ref().ok_or(ErrorDetail::KeystoreRequired {
550            action: "get key manager handle",
551        })?)
552    }
553
554    /// Create (but do not launch) a new
555    /// [`OnionService`](tor_hsservice::OnionService)
556    /// using the given configuration.
557    ///
558    /// See [`TorClient::create_onion_service`].
559    #[cfg(feature = "onion-service-service")]
560    #[instrument(skip_all, level = "trace")]
561    pub fn create_onion_service(
562        &self,
563        config: &TorClientConfig,
564        svc_config: tor_hsservice::OnionServiceConfig,
565    ) -> crate::Result<tor_hsservice::OnionService> {
566        let keymgr = self.keymgr.as_ref().ok_or(ErrorDetail::KeystoreRequired {
567            action: "create onion service",
568        })?;
569
570        let (state_dir, mistrust) = config.state_dir()?;
571        let state_dir =
572            self::StateDirectory::new(state_dir, mistrust).map_err(ErrorDetail::StateAccess)?;
573
574        Ok(tor_hsservice::OnionService::builder()
575            .config(svc_config)
576            .keymgr(keymgr.clone())
577            .state_dir(state_dir)
578            .build()
579            .map_err(ErrorDetail::OnionServiceSetup)?)
580    }
581}
582
583/// Preferences for whether a [`TorClient`] should bootstrap on its own or not.
584#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
585#[non_exhaustive]
586pub enum BootstrapBehavior {
587    /// Bootstrap the client automatically when requests are made that require the client to be
588    /// bootstrapped.
589    #[default]
590    OnDemand,
591    /// Make no attempts to automatically bootstrap. [`TorClient::bootstrap`] must be manually
592    /// invoked in order for the [`TorClient`] to become useful.
593    ///
594    /// Attempts to use the client (e.g. by creating connections or resolving hosts over the Tor
595    /// network) before calling [`bootstrap`](TorClient::bootstrap) will fail, and
596    /// return an error that has kind [`ErrorKind::BootstrapRequired`](crate::ErrorKind::BootstrapRequired).
597    Manual,
598}
599
600/// A representation of whether a [`TorClient`] is allowed to bootstrap, and whether it
601/// has begun to do so.
602#[derive(Debug, Clone, Copy)]
603pub(crate) struct BootstrapSetting {
604    /// The configured [`BootstrapBehavior`] for the `TorClient`.
605    behavior: BootstrapBehavior,
606
607    /// If true, we have a [`RunningInner`] in the `TorClient`,
608    /// indicating that we are trying to bootstrap it.
609    running_inner_is_present: bool,
610}
611
612impl Default for BootstrapSetting {
613    fn default() -> Self {
614        Self {
615            behavior: BootstrapBehavior::Manual,
616            running_inner_is_present: false,
617        }
618    }
619}
620
621impl BootstrapSetting {
622    /// Return true if this [`BootstrapSetting`]
623    /// indicates that the client is not trying to bootstrap,
624    /// and will not try until it is told explicitly to do so.
625    pub(crate) fn blocked(&self) -> bool {
626        use BootstrapBehavior::*;
627        match (self.behavior, self.running_inner_is_present) {
628            (OnDemand, _) => false,
629            (Manual, true) => false,
630            (Manual, false) => true,
631        }
632    }
633}
634
635/// What level of sleep to put a Tor client into.
636#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
637#[non_exhaustive]
638pub enum DormantMode {
639    /// The client functions as normal, and background tasks run periodically.
640    #[default]
641    Normal,
642    /// Background tasks are suspended, conserving CPU usage. Attempts to use the client will
643    /// wake it back up again.
644    Soft,
645}
646
647/// Preferences for how to route a stream over the Tor network.
648#[derive(Debug, Default, Clone)]
649pub struct StreamPrefs {
650    /// What kind of IPv6/IPv4 we'd prefer, and how strongly.
651    ip_ver_pref: IpVersionPreference,
652    /// How should we isolate connection(s)?
653    isolation: StreamIsolationPreference,
654    /// Whether to return the stream optimistically.
655    optimistic_stream: bool,
656    // TODO GEOIP Ideally this would be unconditional, with CountryCode maybe being Void
657    // This probably applies in many other places, so probably:   git grep 'cfg.*geoip'
658    // and consider each one with a view to making it unconditional.  Background:
659    //   https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/1537#note_2935256
660    //   https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/1537#note_2942214
661    #[cfg(feature = "geoip")]
662    /// A country to restrict the exit relay's location to.
663    country_code: Option<CountryCode>,
664    /// Whether to try to make connections to onion services.
665    ///
666    /// `Auto` means to use the client configuration.
667    #[cfg(feature = "onion-service-client")]
668    pub(crate) connect_to_onion_services: BoolOrAuto,
669    /// Whether to try to make connections to local addresses.
670    ///
671    /// `Auto` means to use the client configuration.
672    pub(crate) connect_to_local_addrs: BoolOrAuto,
673    /// Whether to accept and return local addresses in anonymously retrieved DNS answers.
674    ///
675    /// `Auto` means to use the client configuration.
676    pub(crate) resolve_local_addrs: BoolOrAuto,
677}
678
679/// Record of how we are isolating connections
680#[derive(Debug, Default, Clone)]
681enum StreamIsolationPreference {
682    /// No additional isolation
683    #[default]
684    None,
685    /// Isolation parameter to use for connections
686    Explicit(Box<dyn Isolation>),
687    /// Isolate every connection!
688    EveryStream,
689}
690
691impl From<DormantMode> for tor_chanmgr::Dormancy {
692    fn from(dormant: DormantMode) -> tor_chanmgr::Dormancy {
693        match dormant {
694            DormantMode::Normal => tor_chanmgr::Dormancy::Active,
695            DormantMode::Soft => tor_chanmgr::Dormancy::Dormant,
696        }
697    }
698}
699#[cfg(feature = "bridge-client")]
700impl From<DormantMode> for tor_dirmgr::bridgedesc::Dormancy {
701    fn from(dormant: DormantMode) -> tor_dirmgr::bridgedesc::Dormancy {
702        match dormant {
703            DormantMode::Normal => tor_dirmgr::bridgedesc::Dormancy::Active,
704            DormantMode::Soft => tor_dirmgr::bridgedesc::Dormancy::Dormant,
705        }
706    }
707}
708
709impl StreamPrefs {
710    /// Construct a new StreamPrefs.
711    pub fn new() -> Self {
712        Self::default()
713    }
714
715    /// Indicate that a stream may be made over IPv4 or IPv6, but that
716    /// we'd prefer IPv6.
717    pub fn ipv6_preferred(&mut self) -> &mut Self {
718        self.ip_ver_pref = IpVersionPreference::Ipv6Preferred;
719        self
720    }
721
722    /// Indicate that a stream may only be made over IPv6.
723    ///
724    /// When this option is set, we will only pick exit relays that
725    /// support IPv6, and we will tell them to only give us IPv6
726    /// connections.
727    pub fn ipv6_only(&mut self) -> &mut Self {
728        self.ip_ver_pref = IpVersionPreference::Ipv6Only;
729        self
730    }
731
732    /// Indicate that a stream may be made over IPv4 or IPv6, but that
733    /// we'd prefer IPv4.
734    ///
735    /// This is the default.
736    pub fn ipv4_preferred(&mut self) -> &mut Self {
737        self.ip_ver_pref = IpVersionPreference::Ipv4Preferred;
738        self
739    }
740
741    /// Indicate that a stream may only be made over IPv4.
742    ///
743    /// When this option is set, we will only pick exit relays that
744    /// support IPv4, and we will tell them to only give us IPv4
745    /// connections.
746    pub fn ipv4_only(&mut self) -> &mut Self {
747        self.ip_ver_pref = IpVersionPreference::Ipv4Only;
748        self
749    }
750
751    /// Indicate that a stream should appear to come from the given country.
752    ///
753    /// When this option is set, we will only pick exit relays that
754    /// have an IP address that matches the country in our GeoIP database.
755    #[cfg(feature = "geoip")]
756    pub fn exit_country(&mut self, country_code: CountryCode) -> &mut Self {
757        self.country_code = Some(country_code);
758        self
759    }
760
761    /// Indicate that we don't care which country a stream appears to come from.
762    ///
763    /// This is available even in the case where GeoIP support is compiled out,
764    /// to make things easier.
765    pub fn any_exit_country(&mut self) -> &mut Self {
766        #[cfg(feature = "geoip")]
767        {
768            self.country_code = None;
769        }
770        self
771    }
772
773    /// Indicate that the stream should be opened "optimistically".
774    ///
775    /// By default, streams are not "optimistic". When you call
776    /// [`TorClient::connect()`], it won't give you a stream until the
777    /// exit node has confirmed that it has successfully opened a
778    /// connection to your target address.  It's safer to wait in this
779    /// way, but it is slower: it takes an entire round trip to get
780    /// your confirmation.
781    ///
782    /// If a stream _is_ configured to be "optimistic", on the other
783    /// hand, then `TorClient::connect()` will return the stream
784    /// immediately, without waiting for an answer from the exit.  You
785    /// can start sending data on the stream right away, though of
786    /// course this data will be lost if the connection is not
787    /// actually successful.
788    pub fn optimistic(&mut self) -> &mut Self {
789        self.optimistic_stream = true;
790        self
791    }
792
793    /// Return true if this stream has been configured as "optimistic".
794    ///
795    /// See [`StreamPrefs::optimistic`] for more info.
796    pub fn is_optimistic(&self) -> bool {
797        self.optimistic_stream
798    }
799
800    /// Indicate whether connection to a hidden service (`.onion` service) should be allowed
801    ///
802    /// If `Explicit(false)`, attempts to connect to Onion Services will be forced to fail with
803    /// an error of kind [`InvalidStreamTarget`](crate::ErrorKind::InvalidStreamTarget).
804    ///
805    /// If `Explicit(true)`, Onion Service connections are enabled.
806    ///
807    /// If `Auto`, the behaviour depends on the `address_filter.allow_onion_addrs`
808    /// configuration option, which is in turn enabled by default.
809    #[cfg(feature = "onion-service-client")]
810    pub fn connect_to_onion_services(
811        &mut self,
812        connect_to_onion_services: BoolOrAuto,
813    ) -> &mut Self {
814        self.connect_to_onion_services = connect_to_onion_services;
815        self
816    }
817
818    /// Indicate whether connection to a local address should be allowed
819    ///
820    /// If `Explicit(false)`, attempts to connect to local addresses will be forced to fail with
821    /// an error of kind [`InvalidStreamTarget`](crate::ErrorKind::InvalidStreamTarget).
822    ///
823    /// If `Explicit(true)`, connections to local addresses are allowed.
824    ///
825    /// If `Auto`, the behaviour depends on the `address_filter.allow_local_addrs`
826    /// configuration option, which is in turn disabled by default.
827    pub fn connect_to_local_addrs(&mut self, connect_to_local_addrs: BoolOrAuto) -> &mut Self {
828        self.connect_to_local_addrs = connect_to_local_addrs;
829        self
830    }
831
832    /// Indicate whether to accept and return local addresses in anonymously retrieved DNS answers.
833    ///
834    /// If `Explicit(false)`, we will filter out the local addresses
835    /// from anonymously retrieved DNS answers.
836    ///
837    /// If `Explicit(true)`, we will not filter out the local addresses
838    /// from anonymously retrieved DNS answers.
839    ///
840    /// If `Auto`, the behaviour depends on the `address_filter.allow_resolving_local_addrs`
841    /// configuration option, which is in turn disabled by default.
842    pub fn resolve_local_addrs(&mut self, resolve_local_addrs: BoolOrAuto) -> &mut Self {
843        self.resolve_local_addrs = resolve_local_addrs;
844        self
845    }
846
847    /// Return a TargetPort to describe what kind of exit policy our
848    /// target circuit needs to support.
849    fn wrap_target_port(&self, port: u16) -> TargetPort {
850        match self.ip_ver_pref {
851            IpVersionPreference::Ipv6Only => TargetPort::ipv6(port),
852            _ => TargetPort::ipv4(port),
853        }
854    }
855
856    /// Return a new StreamParameters based on this configuration.
857    fn stream_parameters(&self) -> StreamParameters {
858        let mut params = StreamParameters::default();
859        params
860            .ip_version(self.ip_ver_pref)
861            .optimistic(self.optimistic_stream);
862        params
863    }
864
865    /// Indicate that connections with these preferences should have their own isolation group
866    ///
867    /// This is a convenience method which creates a fresh [`IsolationToken`]
868    /// and sets it for these preferences.
869    ///
870    /// This connection preference is orthogonal to isolation established by
871    /// [`TorClient::isolated_client`].  Connections made with an `isolated_client`
872    ///  will not share circuits with the original client, even if the same
873    /// `isolation` is specified via the `ConnectionPrefs` in force.
874    pub fn new_isolation_group(&mut self) -> &mut Self {
875        self.isolation = StreamIsolationPreference::Explicit(Box::new(IsolationToken::new()));
876        self
877    }
878
879    /// Indicate which other connections might use the same circuit
880    /// as this one.
881    ///
882    /// By default all connections made on a `TorClient` may share connections.
883    /// Connections made with a particular `isolation` may share circuits with each other.
884    ///
885    /// This connection preference is orthogonal to isolation established by
886    /// [`TorClient::isolated_client`].  Connections made with an `isolated_client`
887    /// will not share circuits with the original client, even if the same
888    /// `isolation` is specified via the `ConnectionPrefs` in force.
889    pub fn set_isolation<T>(&mut self, isolation: T) -> &mut Self
890    where
891        T: Into<Box<dyn Isolation>>,
892    {
893        self.isolation = StreamIsolationPreference::Explicit(isolation.into());
894        self
895    }
896
897    /// Indicate that no connection should share a circuit with any other.
898    ///
899    /// **Use with care:** This is likely to have poor performance, and imposes a much greater load
900    /// on the Tor network.  Use this option only to make small numbers of connections each of
901    /// which needs to be isolated from all other connections.
902    ///
903    /// (Don't just use this as a "get more privacy!!" method: the circuits
904    /// that it put connections on will have no more privacy than any other
905    /// circuits.  The only benefit is that these circuits will not be shared
906    /// by multiple streams.)
907    ///
908    /// This can be undone by calling `set_isolation` or `new_isolation_group` on these
909    /// preferences.
910    pub fn isolate_every_stream(&mut self) -> &mut Self {
911        self.isolation = StreamIsolationPreference::EveryStream;
912        self
913    }
914
915    /// Return an [`Isolation`] which separates according to these `StreamPrefs` (only)
916    ///
917    /// This describes which connections or operations might use
918    /// the same circuit(s) as this one.
919    ///
920    /// Since this doesn't have access to the `TorClient`,
921    /// it doesn't separate streams which ought to be separated because of
922    /// the way their `TorClient`s are isolated.
923    /// For that, use [`TorClient::isolation`].
924    fn prefs_isolation(&self) -> Option<Box<dyn Isolation>> {
925        use StreamIsolationPreference as SIP;
926        match self.isolation {
927            SIP::None => None,
928            SIP::Explicit(ref ig) => Some(ig.clone()),
929            SIP::EveryStream => Some(Box::new(IsolationToken::new())),
930        }
931    }
932
933    // TODO: Add some way to be IPFlexible, and require exit to support both.
934}
935
936#[cfg(all(
937    any(feature = "native-tls", feature = "rustls"),
938    any(feature = "async-std", feature = "tokio")
939))]
940impl TorClient<PreferredRuntime> {
941    /// Bootstrap a connection to the Tor network, using the provided `config`.
942    ///
943    /// Returns a client once there is enough directory material to
944    /// connect safely over the Tor network.
945    ///
946    /// Consider using [`TorClient::builder`] for more fine-grained control.
947    ///
948    /// # Panics
949    ///
950    /// If Tokio is being used (the default), panics if created outside the context of a currently
951    /// running Tokio runtime. See the documentation for [`PreferredRuntime::current`] for
952    /// more information.
953    ///
954    /// If using `async-std`, either take care to ensure Arti is not compiled with Tokio support,
955    /// or manually create an `async-std` runtime using [`tor_rtcompat`] and use it with
956    /// [`TorClient::with_runtime`].
957    ///
958    /// # Do not fork
959    ///
960    /// The process [**may not fork**](tor_rtcompat#do-not-fork)
961    /// (except, very carefully, before exec)
962    /// after calling this function, because it creates a [`PreferredRuntime`].
963    pub async fn create_bootstrapped(config: TorClientConfig) -> crate::Result<Arc<Self>> {
964        let runtime = PreferredRuntime::current()
965            .expect("TorClient could not get an asynchronous runtime; are you running in the right context?");
966
967        Self::with_runtime(runtime)
968            .config(config)
969            .create_bootstrapped()
970            .await
971    }
972
973    /// Return a new builder for creating TorClient objects.
974    ///
975    /// If you want to make a [`TorClient`] synchronously, this is what you want; call
976    /// `TorClientBuilder::create_unbootstrapped` on the returned builder.
977    ///
978    /// # Panics
979    ///
980    /// If Tokio is being used (the default), panics if created outside the context of a currently
981    /// running Tokio runtime. See the documentation for `tokio::runtime::Handle::current` for
982    /// more information.
983    ///
984    /// If using `async-std`, either take care to ensure Arti is not compiled with Tokio support,
985    /// or manually create an `async-std` runtime using [`tor_rtcompat`] and use it with
986    /// [`TorClient::with_runtime`].
987    ///
988    /// # Do not fork
989    ///
990    /// The process [**may not fork**](tor_rtcompat#do-not-fork)
991    /// (except, very carefully, before exec)
992    /// after calling this function, because it creates a [`PreferredRuntime`].
993    pub fn builder() -> TorClientBuilder<PreferredRuntime> {
994        let runtime = PreferredRuntime::current()
995            .expect("TorClient could not get an asynchronous runtime; are you running in the right context?");
996
997        TorClientBuilder::new(runtime)
998    }
999}
1000
1001impl<R: Runtime> TorClient<R> {
1002    /// Return a new builder for creating TorClient objects, with a custom provided [`Runtime`].
1003    ///
1004    /// See the [`tor_rtcompat`] crate for more information on custom runtimes.
1005    pub fn with_runtime(runtime: R) -> TorClientBuilder<R> {
1006        TorClientBuilder::new(runtime)
1007    }
1008
1009    /// Implementation of `create_unbootstrapped`, split out in order to avoid manually specifying
1010    /// double error conversions.
1011    #[instrument(skip_all, level = "trace")]
1012    pub(crate) fn create_impl(
1013        runtime: R,
1014        config: &TorClientConfig,
1015        autobootstrap: BootstrapBehavior,
1016        dirmgr_builder: Arc<dyn crate::builder::DirProviderBuilder<R>>,
1017        dirmgr_extensions: tor_dirmgr::config::DirMgrExtensions,
1018    ) -> StdResult<Arc<Self>, ErrorDetail> {
1019        if crate::util::running_as_setuid() {
1020            return Err(tor_error::bad_api_usage!(
1021                "Arti does not support running in a setuid or setgid context."
1022            )
1023            .into());
1024        }
1025
1026        let memquota = MemoryQuotaTracker::new(&runtime, config.system.memory.clone())?;
1027
1028        let path_resolver = Arc::new(config.path_resolver.clone());
1029
1030        let (state_dir, mistrust) = config.state_dir()?;
1031        #[cfg(feature = "onion-service-service")]
1032        let state_directory =
1033            StateDirectory::new(&state_dir, mistrust).map_err(ErrorDetail::StateAccess)?;
1034
1035        let dormant = DormantMode::Normal;
1036
1037        let statemgr = Self::statemgr_from_config(config)?;
1038
1039        // Try to take state ownership early, so we'll know if we have it.
1040        // Note that this `try_lock()` may return `Ok` even if we can't acquire the lock.
1041        // (At this point we don't yet care if we have it.)
1042        let _ignore_status = statemgr.try_lock().map_err(ErrorDetail::StateMgrSetup)?;
1043
1044        let addr_cfg = config.address_filter.clone();
1045
1046        let bootstrap_setting = BootstrapSetting {
1047            behavior: autobootstrap,
1048            running_inner_is_present: false,
1049        };
1050        let (bootstrap_setting_sender, bootstrap_setting_receiver) =
1051            postage::watch::channel_with(bootstrap_setting);
1052        let bootstrap_setting_sender = Mutex::new(bootstrap_setting_sender);
1053        let (status_sender, status_receiver) =
1054            postage::watch::channel_with(BootstrapStatus::from_setting(bootstrap_setting));
1055        let status_receiver = status::BootstrapEvents {
1056            inner: status_receiver,
1057        };
1058        let timeout_cfg = config.stream_timeouts.clone();
1059
1060        let (dormant_send, dormant_recv) = postage::watch::channel_with(Some(dormant));
1061        let dormant_send = DropNotifyWatchSender::new(dormant_send);
1062        let client_isolation = IsolationToken::new();
1063        let inert_client = InertTorClient::new(config)?;
1064
1065        let dirmgr_store = DirMgrStore::new(&config.dir_mgr_config()?, runtime.clone(), false)
1066            .map_err(ErrorDetail::DirMgrSetup)?;
1067
1068        let inner = Box::new(NotConstructedInner {
1069            config: config.clone(),
1070            dormant_recv,
1071            status_sender,
1072            bootstrap_setting_receiver,
1073            dirmgr_builder,
1074            dirmgr_extensions,
1075        });
1076
1077        let inner = Mutex::new(Inner::NotConstructed(inner));
1078
1079        let client = Arc::new(ClientShared {
1080            runtime,
1081            inner,
1082            memquota,
1083            inert_client,
1084            statemgr,
1085            dirmgr_store,
1086            addrcfg: addr_cfg.into(),
1087            timeoutcfg: timeout_cfg.into(),
1088            reconfigure_lock: Arc::new(Mutex::new(())),
1089            status_receiver,
1090            bootstrap_in_progress: AsyncMutex::new(()),
1091            bootstrap_setting_sender,
1092            should_bootstrap: autobootstrap,
1093            dormant: Mutex::new(dormant_send),
1094            #[cfg(feature = "onion-service-service")]
1095            state_directory,
1096            path_resolver,
1097        });
1098
1099        Ok(Arc::new(TorClient {
1100            client_isolation,
1101            connect_prefs: Default::default(),
1102            client,
1103        }))
1104    }
1105
1106    /// Construct a state manager from the client configuration.
1107    fn statemgr_from_config(config: &TorClientConfig) -> Result<UsingStateMgr, ErrorDetail> {
1108        #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1109        {
1110            use tor_persist::FsStateMgr;
1111
1112            let (state_dir, mistrust) = config.state_dir()?;
1113            FsStateMgr::from_path_and_mistrust(state_dir, mistrust)
1114                .map_err(ErrorDetail::StateMgrSetup)
1115        }
1116        #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1117        {
1118            unimplemented!()
1119        }
1120    }
1121
1122    /// Bootstrap a connection to the Tor network, with a client created by `create_unbootstrapped`.
1123    ///
1124    /// Returns once there is enough directory material to connect safely over the Tor network.
1125    /// If the client has already been bootstrapped, returns immediately with
1126    /// success. If a bootstrap is in progress, waits for it to finish, then retries it if it
1127    /// failed (returning success if it succeeded).
1128    ///
1129    /// Bootstrap progress can be tracked by listening to the event receiver returned by
1130    /// [`bootstrap_events`](TorClient::bootstrap_events).
1131    ///
1132    /// # Failures
1133    ///
1134    /// If the bootstrapping process fails, returns an error. This function can safely be called
1135    /// again later to attempt to bootstrap another time.
1136    #[instrument(skip_all, level = "trace")]
1137    pub async fn bootstrap(&self) -> crate::Result<()> {
1138        self.client
1139            .bootstrap_inner()
1140            .await
1141            .map_err(ErrorDetail::into)
1142    }
1143}
1144
1145impl<R: Runtime> NotConstructedInner<R> {
1146    /// Replace the configuration for this unconstructed client.
1147    ///
1148    /// Since most of the client's internals are not yet constructed,
1149    /// we can still replace nearly all of the items.
1150    fn reconfigure(
1151        &mut self,
1152        new_config: &TorClientConfig,
1153        how: tor_config::Reconfigure,
1154    ) -> StdResult<(), ErrorDetail> {
1155        // We _do_ have to check the cache_dir, since we can't and won't change that
1156        // while we're running.
1157        // (We already checked the state_dir in ClientShared::reconfigure_inner.)
1158        if new_config.storage.cache_dir != self.config.storage.cache_dir {
1159            how.cannot_change("storage.cache_dir")?;
1160        }
1161
1162        if how == tor_config::Reconfigure::CheckAllOrNothing {
1163            return Ok(());
1164        }
1165
1166        self.config = new_config.clone();
1167
1168        Ok(())
1169    }
1170}
1171
1172impl<R: Runtime> RunningInner<R> {
1173    /// Construct a new [`RunningInner`] and launch its associated tasks.
1174    fn new(
1175        pending: NotConstructedInner<R>,
1176        client: &ClientShared<R>,
1177    ) -> StdResult<Arc<Self>, ErrorDetail> {
1178        let NotConstructedInner {
1179            config,
1180            dormant_recv,
1181            status_sender,
1182            bootstrap_setting_receiver,
1183            dirmgr_builder,
1184            dirmgr_extensions,
1185        } = pending;
1186
1187        let runtime = client.runtime.clone();
1188        let dormant = dormant_recv
1189            .borrow()
1190            .expect("Client somehow dropped while creating RunningInner");
1191        let memquota = &client.memquota;
1192        let statemgr = &client.statemgr;
1193        let path_resolver = &client.path_resolver;
1194        let (state_dir, _) = config.state_dir()?;
1195
1196        let chanmgr = Arc::new(
1197            tor_chanmgr::ChanMgr::new(
1198                runtime.clone(),
1199                ChanMgrConfig::new(config.channel.clone()),
1200                dormant.into(),
1201                &NetParameters::from_map(&config.override_net_params),
1202                memquota.clone(),
1203            )
1204            .map_err(ErrorDetail::ChanMgrSetup)?,
1205        );
1206        let guardmgr = tor_guardmgr::GuardMgr::new(runtime.clone(), statemgr.clone(), &config)
1207            .map_err(ErrorDetail::GuardMgrSetup)?;
1208
1209        #[cfg(feature = "pt-client")]
1210        let pt_mgr = {
1211            let pt_state_dir = state_dir.as_path().join("pt_state");
1212            config.storage.permissions().make_directory(&pt_state_dir)?;
1213
1214            let mgr = Arc::new(tor_ptmgr::PtMgr::new(
1215                config.bridges.transports.clone(),
1216                pt_state_dir,
1217                Arc::clone(path_resolver),
1218                config.channel.outbound_proxy().cloned(),
1219                runtime.clone(),
1220            )?);
1221
1222            chanmgr.set_pt_mgr(mgr.clone());
1223
1224            mgr
1225        };
1226
1227        let circmgr = Arc::new(
1228            tor_circmgr::CircMgr::new(
1229                &config,
1230                statemgr.clone(),
1231                &runtime,
1232                Arc::clone(&chanmgr),
1233                &guardmgr,
1234            )
1235            .map_err(ErrorDetail::CircMgrSetup)?,
1236        );
1237
1238        let dir_cfg = {
1239            let mut c: tor_dirmgr::DirMgrConfig = config.dir_mgr_config()?;
1240            c.extensions = dirmgr_extensions;
1241            c
1242        };
1243        let dirmgr = dirmgr_builder
1244            .build(
1245                runtime.clone(),
1246                client.dirmgr_store.clone(),
1247                Arc::clone(&circmgr),
1248                dir_cfg,
1249            )
1250            .map_err(crate::Error::into_detail)?;
1251
1252        let mut periodic_task_handles = circmgr
1253            .launch_background_tasks(&runtime, &dirmgr, statemgr.clone())
1254            .map_err(ErrorDetail::CircMgrSetup)?;
1255        periodic_task_handles.extend(dirmgr.download_task_handle());
1256
1257        periodic_task_handles.extend(
1258            chanmgr
1259                .launch_background_tasks(&runtime, dirmgr.clone().upcast_arc())
1260                .map_err(ErrorDetail::ChanMgrSetup)?,
1261        );
1262
1263        #[cfg(feature = "bridge-client")]
1264        // TODO: We can just construct this.
1265        let bridge_desc_mgr = Arc::new(Mutex::new(None));
1266
1267        #[cfg(any(feature = "onion-service-client", feature = "onion-service-service"))]
1268        let hs_circ_pool = {
1269            let circpool = Arc::new(tor_circmgr::hspool::HsCircPool::new(&circmgr));
1270            circpool
1271                .launch_background_tasks(&runtime, &dirmgr.clone().upcast_arc())
1272                .map_err(ErrorDetail::CircMgrSetup)?;
1273            circpool
1274        };
1275
1276        #[cfg(feature = "onion-service-client")]
1277        let hsclient = {
1278            // Prompt the hs connector to do its data housekeeping when we get a new consensus.
1279            // That's a time we're doing a bunch of thinking anyway, and it's not very frequent.
1280            let housekeeping = dirmgr.events().filter_map(|event| async move {
1281                match event {
1282                    DirEvent::NewConsensus => Some(()),
1283                    _ => None,
1284                }
1285            });
1286            let housekeeping = Box::pin(housekeeping);
1287
1288            HsClientConnector::new(runtime.clone(), hs_circ_pool.clone(), &config, housekeeping)?
1289        };
1290        let conn_status = chanmgr.bootstrap_events();
1291        let dir_status = dirmgr.bootstrap_events();
1292        let skew_status = circmgr.skew_events();
1293
1294        let rtclone = runtime.clone();
1295
1296        // TODO: It might be a good idea to check this earlier, in `create_impl`,
1297        // when we have only the DirMgrStore.
1298        // But if we do that we need to add a method to DirMgrStore
1299        // to look at the protocol recommentations.
1300        #[allow(clippy::print_stderr)]
1301        crate::protostatus::enforce_protocol_recommendations(
1302            &runtime,
1303            Arc::clone(&dirmgr),
1304            crate::software_release_date(),
1305            crate::supported_protocols(),
1306            // TODO #1932: It would be nice to have a cleaner shutdown mechanism here,
1307            // but that will take some work.
1308            |fatal| async move {
1309                use tor_error::ErrorReport as _;
1310                // We already logged this error, but let's tell stderr too.
1311                eprintln!(
1312                    "Shutting down because of unsupported software version.\nError was:\n{}",
1313                    fatal.report(),
1314                );
1315                if let Some(hint) = crate::err::Error::from(fatal).hint() {
1316                    eprintln!("{}", hint);
1317                }
1318                // Give the tracing module a while to flush everything, since it has no built-in
1319                // flush function.
1320                rtclone.sleep(std::time::Duration::new(5, 0)).await;
1321                std::process::exit(1);
1322            },
1323        )?;
1324
1325        runtime
1326            .spawn(status::report_status(
1327                status_sender,
1328                conn_status,
1329                dir_status,
1330                skew_status,
1331                bootstrap_setting_receiver,
1332            ))
1333            .map_err(|e| ErrorDetail::from_spawn("top-level status reporter", e))?;
1334
1335        runtime
1336            .spawn(tasks_monitor_dormant(
1337                dormant_recv.clone(),
1338                dirmgr.clone().upcast_arc(),
1339                chanmgr.clone(),
1340                #[cfg(feature = "bridge-client")]
1341                bridge_desc_mgr.clone(),
1342                periodic_task_handles,
1343            ))
1344            .map_err(|e| ErrorDetail::from_spawn("periodic task dormant monitor", e))?;
1345
1346        let running_inner = Arc::new(RunningInner {
1347            chanmgr,
1348            circmgr,
1349            dirmgr,
1350            #[cfg(feature = "bridge-client")]
1351            bridge_desc_mgr,
1352            #[cfg(feature = "pt-client")]
1353            pt_mgr,
1354            #[cfg(feature = "onion-service-client")]
1355            hsclient,
1356            #[cfg(any(feature = "onion-service-client", feature = "onion-service-service"))]
1357            hs_circ_pool,
1358            guardmgr,
1359        });
1360
1361        Ok(running_inner)
1362    }
1363
1364    /// Tell the parts of this [`RunningInner`] to reconfigure themselves
1365    /// (or to check the new configuration, if `how == CheckAllOrNothing`).
1366    fn reconfigure(
1367        &self,
1368        new_config: &TorClientConfig,
1369        how: tor_config::Reconfigure,
1370    ) -> crate::Result<()> {
1371        let dir_cfg = new_config.dir_mgr_config().map_err(wrap_err)?;
1372
1373        let retire_circuits = self
1374            .circmgr
1375            .reconfigure(new_config, how)
1376            .map_err(wrap_err)?;
1377
1378        #[cfg(any(feature = "onion-service-client", feature = "onion-service-service"))]
1379        if retire_circuits != RetireCircuits::None {
1380            self.hs_circ_pool.retire_all_circuits().map_err(wrap_err)?;
1381        }
1382
1383        self.dirmgr.reconfigure(&dir_cfg, how).map_err(wrap_err)?;
1384
1385        let netparams = self.dirmgr.params();
1386
1387        self.chanmgr
1388            .reconfigure(&new_config.channel, how, netparams)
1389            .map_err(wrap_err)?;
1390
1391        #[cfg(feature = "pt-client")]
1392        self.pt_mgr
1393            .reconfigure(
1394                how,
1395                new_config.bridges.transports.clone(),
1396                new_config.channel.outbound_proxy().cloned(),
1397            )
1398            .map_err(wrap_err)?;
1399
1400        Ok(())
1401    }
1402}
1403
1404impl<R: Runtime> TorClient<R> {
1405    /// Change the configuration of this TorClient to `new_config`.
1406    ///
1407    /// The `how` describes whether to perform an all-or-nothing
1408    /// reconfiguration: either all of the configuration changes will be
1409    /// applied, or none will. If you have disabled all-or-nothing changes, then
1410    /// only fatal errors will be reported in this function's return value.
1411    ///
1412    /// When performing a reconfiguration,
1413    /// a returned error may indicate that the client is now in an inconsistent state.
1414    ///
1415    /// This function applies its changes to **all** TorClient instances derived
1416    /// from the same call to `TorClient::create_*`: even ones whose circuits
1417    /// are isolated from this handle.
1418    ///
1419    /// # Limitations
1420    ///
1421    /// Although most options are reconfigurable, there are some whose values
1422    /// can't be changed on an a running TorClient.  Those options (or their
1423    /// sections) are explicitly documented not to be changeable.
1424    /// NOTE: Currently, not all of these non-reconfigurable options are
1425    /// documented. See [arti#1721][arti-1721].
1426    ///
1427    /// [arti-1721]: https://gitlab.torproject.org/tpo/core/arti/-/issues/1721
1428    ///
1429    /// Changing some options do not take effect immediately on all open streams
1430    /// and circuits, but rather affect only future streams and circuits.  Those
1431    /// are also explicitly documented.
1432    #[instrument(skip_all, level = "trace")]
1433    pub fn reconfigure(
1434        &self,
1435        new_config: &TorClientConfig,
1436        how: tor_config::Reconfigure,
1437    ) -> crate::Result<()> {
1438        // We need to hold this lock while we're reconfiguring the client: even
1439        // though the individual fields have their own synchronization, we can't
1440        // safely let two threads change them at once.  If we did, then we'd
1441        // introduce time-of-check/time-of-use bugs in checking our configuration,
1442        // deciding how to change it, then applying the changes.
1443        let guard = self.client.reconfigure_lock.lock().expect("Poisoned lock");
1444
1445        use tor_config::Reconfigure::*;
1446
1447        match how {
1448            AllOrNothing => {
1449                // We have to check before we make any changes.
1450                self.client
1451                    .reconfigure_inner(new_config, CheckAllOrNothing, &guard)?;
1452
1453                // Hopefully this doesn't fail,
1454                // otherwise we may have returned early from the reconfiguration
1455                // and its no longer "all-or-nothing".
1456                let result = self
1457                    .client
1458                    .reconfigure_inner(new_config, AllOrNothing, &guard);
1459
1460                if result.is_err() {
1461                    warn!(
1462                        "Attempted an \"all-or-nothing\" reconfigure, but unexpectedly failed. \
1463                        The client will continue to run in an inconsistent state."
1464                    );
1465                }
1466
1467                result
1468            }
1469            WarnOnFailures => {
1470                let result = self.client.reconfigure_inner(new_config, how, &guard);
1471
1472                // If there's a fatal error,
1473                // we may have reconfigured some components and not others.
1474                if result.is_err() {
1475                    warn!(
1476                        "Attempted a reconfigure, but failed. \
1477                        The client will continue to run in an inconsistent state."
1478                    );
1479                }
1480
1481                result
1482            }
1483            CheckAllOrNothing => self.client.reconfigure_inner(new_config, how, &guard),
1484            _ => self.client.reconfigure_inner(new_config, how, &guard),
1485        }
1486    }
1487
1488    /// Return a new isolated `TorClient` handle.
1489    ///
1490    /// The two `TorClient`s will share internal state and configuration, but
1491    /// their streams will never share circuits with one another.
1492    ///
1493    /// Use this function when you want separate parts of your program to
1494    /// each have a TorClient handle, but where you don't want their
1495    /// activities to be linkable to one another over the Tor network.
1496    ///
1497    /// Calling this function is usually preferable to creating a
1498    /// completely separate TorClient instance, since it can share its
1499    /// internals with the existing `TorClient`.
1500    #[must_use]
1501    pub fn isolated_client(&self) -> Arc<TorClient<R>> {
1502        let result = TorClient {
1503            client_isolation: IsolationToken::new(),
1504            connect_prefs: self.connect_prefs.clone(),
1505            client: Arc::clone(&self.client),
1506        };
1507        Arc::new(result)
1508    }
1509
1510    /// Launch an anonymized connection to the provided address and port over
1511    /// the Tor network.
1512    ///
1513    /// Note that because Tor prefers to do DNS resolution on the remote side of
1514    /// the network, this function takes its address as a string:
1515    ///
1516    /// ```no_run
1517    /// # use arti_client::*;use tor_rtcompat::Runtime;
1518    /// # async fn ex<R:Runtime>(tor_client: TorClient<R>) -> Result<()> {
1519    /// // The most usual way to connect is via an address-port tuple.
1520    /// let socket = tor_client.connect(("www.example.com", 443)).await?;
1521    ///
1522    /// // You can also specify an address and port as a colon-separated string.
1523    /// let socket = tor_client.connect("www.example.com:443").await?;
1524    /// # Ok(())
1525    /// # }
1526    /// ```
1527    ///
1528    /// Hostnames are _strongly_ preferred here: if this function allowed the
1529    /// caller here to provide an IPAddr or [`IpAddr`] or
1530    /// [`SocketAddr`](std::net::SocketAddr) address, then
1531    ///
1532    /// ```no_run
1533    /// # use arti_client::*; use tor_rtcompat::Runtime;
1534    /// # async fn ex<R:Runtime>(tor_client: TorClient<R>) -> Result<()> {
1535    /// # use std::net::ToSocketAddrs;
1536    /// // BAD: We're about to leak our target address to the local resolver!
1537    /// let address = "www.example.com:443".to_socket_addrs().unwrap().next().unwrap();
1538    /// // 🤯 Oh no! Now any eavesdropper can tell where we're about to connect! 🤯
1539    ///
1540    /// // Fortunately, this won't compile, since SocketAddr doesn't implement IntoTorAddr.
1541    /// // let socket = tor_client.connect(address).await?;
1542    /// //                                 ^^^^^^^ the trait `IntoTorAddr` is not implemented for `std::net::SocketAddr`
1543    /// # Ok(())
1544    /// # }
1545    /// ```
1546    ///
1547    /// If you really do need to connect to an IP address rather than a
1548    /// hostname, and if you're **sure** that the IP address came from a safe
1549    /// location, there are a few ways to do so.
1550    ///
1551    /// ```no_run
1552    /// # use arti_client::{TorClient,Result};use tor_rtcompat::Runtime;
1553    /// # use std::net::{SocketAddr,IpAddr};
1554    /// # async fn ex<R:Runtime>(tor_client: TorClient<R>) -> Result<()> {
1555    /// # use std::net::ToSocketAddrs;
1556    /// // ⚠️This is risky code!⚠️
1557    /// // (Make sure your addresses came from somewhere safe...)
1558    ///
1559    /// // If we have a fixed address, we can just provide it as a string.
1560    /// let socket = tor_client.connect("192.0.2.22:443").await?;
1561    /// let socket = tor_client.connect(("192.0.2.22", 443)).await?;
1562    ///
1563    /// // If we have a SocketAddr or an IpAddr, we can use the
1564    /// // DangerouslyIntoTorAddr trait.
1565    /// use arti_client::DangerouslyIntoTorAddr;
1566    /// let sockaddr = SocketAddr::from(([192, 0, 2, 22], 443));
1567    /// let ipaddr = IpAddr::from([192, 0, 2, 22]);
1568    /// let socket = tor_client.connect(sockaddr.into_tor_addr_dangerously().unwrap()).await?;
1569    /// let socket = tor_client.connect((ipaddr, 443).into_tor_addr_dangerously().unwrap()).await?;
1570    /// # Ok(())
1571    /// # }
1572    /// ```
1573    #[instrument(skip_all, level = "trace")]
1574    pub async fn connect<A: IntoTorAddr>(&self, target: A) -> crate::Result<DataStream> {
1575        self.connect_with_prefs(target, &self.connect_prefs).await
1576    }
1577
1578    /// Launch an anonymized connection to the provided address and
1579    /// port over the Tor network, with explicit connection preferences.
1580    ///
1581    /// Note that because Tor prefers to do DNS resolution on the remote
1582    /// side of the network, this function takes its address as a string.
1583    /// (See [`TorClient::connect()`] for more information.)
1584    #[instrument(skip_all, level = "trace")]
1585    pub async fn connect_with_prefs<A: IntoTorAddr>(
1586        &self,
1587        target: A,
1588        prefs: &StreamPrefs,
1589    ) -> crate::Result<DataStream> {
1590        let addr = target.into_tor_addr().map_err(wrap_err)?;
1591        let mut stream_parameters = prefs.stream_parameters();
1592        // This macro helps prevent code duplication in the match below.
1593        //
1594        // Ideally, the match should resolve to a tuple consisting of the
1595        // tunnel, and the address, port and stream params,
1596        // but that's not currently possible because
1597        // the Exit and Hs branches use different tunnel types.
1598        //
1599        // TODO: replace with an async closure (when our MSRV allows it),
1600        // or with a more elegant approach.
1601        macro_rules! begin_stream {
1602            ($tunnel:expr, $addr:expr, $port:expr, $stream_params:expr) => {{
1603                let fut = $tunnel.begin_stream($addr, $port, $stream_params);
1604                self.client
1605                    .runtime
1606                    .timeout(self.client.timeoutcfg.get().connect_timeout, fut)
1607                    .await
1608                    .map_err(|_| ErrorDetail::ExitTimeout)?
1609                    .map_err(|cause| ErrorDetail::StreamFailed {
1610                        cause,
1611                        kind: "data",
1612                    })
1613            }};
1614        }
1615
1616        let stream = match addr.into_stream_instructions(&self.client.addrcfg.get(), prefs)? {
1617            StreamInstructions::Exit {
1618                hostname: addr,
1619                port,
1620            } => {
1621                let exit_ports = [prefs.wrap_target_port(port)];
1622                let tunnel = self
1623                    .get_or_launch_exit_tunnel(&exit_ports, prefs)
1624                    .await
1625                    .map_err(wrap_err)?;
1626                debug!(
1627                    tunnel_id = %tunnel.unique_id(),
1628                    "Got a circuit for {}:{}", sensitive(&addr), port);
1629
1630                begin_stream!(tunnel, &addr, port, Some(stream_parameters))
1631            }
1632
1633            #[cfg(not(feature = "onion-service-client"))]
1634            #[allow(unused_variables)] // for hostname and port
1635            StreamInstructions::Hs {
1636                hsid,
1637                hostname,
1638                port,
1639            } => void::unreachable(hsid.0),
1640
1641            #[cfg(feature = "onion-service-client")]
1642            StreamInstructions::Hs {
1643                hsid,
1644                hostname,
1645                port,
1646            } => {
1647                use safelog::DisplayRedacted as _;
1648
1649                let running = self
1650                    .client
1651                    .wait_for_bootstrap_running("connect to hidden service")
1652                    .await?;
1653
1654                let netdir = self.netdir(Timeliness::Timely, "connect to a hidden service")?;
1655
1656                let mut hs_client_secret_keys_builder = HsClientSecretKeysBuilder::default();
1657
1658                if let Some(keymgr) = &self.client.inert_client.keymgr {
1659                    let desc_enc_key_spec = HsClientDescEncKeypairSpecifier::new(hsid);
1660
1661                    let ks_hsc_desc_enc =
1662                        keymgr.get::<HsClientDescEncKeypair>(&desc_enc_key_spec)?;
1663
1664                    if let Some(ks_hsc_desc_enc) = ks_hsc_desc_enc {
1665                        debug!(
1666                            "Found descriptor decryption key for {}",
1667                            hsid.display_redacted()
1668                        );
1669                        hs_client_secret_keys_builder.ks_hsc_desc_enc(ks_hsc_desc_enc);
1670                    }
1671                };
1672
1673                let hs_client_secret_keys = hs_client_secret_keys_builder
1674                    .build()
1675                    .map_err(ErrorDetail::Configuration)?;
1676
1677                let tunnel = running
1678                    .hsclient
1679                    .get_or_launch_tunnel(
1680                        &netdir,
1681                        hsid,
1682                        hs_client_secret_keys,
1683                        self.isolation(prefs),
1684                    )
1685                    .await
1686                    .map_err(|cause| ErrorDetail::ObtainHsCircuit { cause, hsid })?;
1687                // On connections to onion services, we have to suppress
1688                // everything except the port from the BEGIN message.  We also
1689                // disable optimistic data.
1690                stream_parameters
1691                    .suppress_hostname()
1692                    .suppress_begin_flags()
1693                    .optimistic(false);
1694
1695                begin_stream!(tunnel, &hostname, port, Some(stream_parameters))
1696            }
1697        };
1698
1699        Ok(stream?)
1700    }
1701
1702    /// Provides a new handle on this client, but with adjusted default preferences.
1703    ///
1704    /// Connections made with e.g. [`connect`](TorClient::connect) on the returned handle will use
1705    /// `connect_prefs`.
1706    #[must_use]
1707    pub fn with_prefs(&self, connect_prefs: StreamPrefs) -> Arc<Self> {
1708        let result = TorClient {
1709            client_isolation: self.client_isolation,
1710            connect_prefs,
1711            client: Arc::clone(&self.client),
1712        };
1713        Arc::new(result)
1714    }
1715
1716    /// On success, return a list of IP addresses.
1717    #[instrument(skip_all, level = "trace")]
1718    pub async fn resolve(&self, hostname: &str) -> crate::Result<Vec<IpAddr>> {
1719        self.resolve_with_prefs(hostname, &self.connect_prefs).await
1720    }
1721
1722    /// On success, return a list of IP addresses, but use prefs.
1723    #[instrument(skip_all, level = "trace")]
1724    pub async fn resolve_with_prefs(
1725        &self,
1726        hostname: &str,
1727        prefs: &StreamPrefs,
1728    ) -> crate::Result<Vec<IpAddr>> {
1729        // TODO This dummy port is only because `address::Host` is not pub(crate),
1730        // but I see no reason why it shouldn't be?  Then `into_resolve_instructions`
1731        // should be a method on `Host`, not `TorAddr`.  -Diziet.
1732        let addr = (hostname, 1).into_tor_addr().map_err(wrap_err)?;
1733
1734        let addrcfg = self.client.addrcfg.get();
1735        let mut addrs = match addr.into_resolve_instructions(&addrcfg, prefs)? {
1736            ResolveInstructions::Exit(hostname) => {
1737                let circ = self.get_or_launch_exit_tunnel(&[], prefs).await?;
1738
1739                let resolve_future = circ.resolve(&hostname);
1740                self.client
1741                    .runtime
1742                    .timeout(self.client.timeoutcfg.get().resolve_timeout, resolve_future)
1743                    .await
1744                    .map_err(|_| ErrorDetail::ExitTimeout)?
1745                    .map_err(|cause| ErrorDetail::StreamFailed {
1746                        cause,
1747                        kind: "DNS lookup",
1748                    })?
1749            }
1750            ResolveInstructions::Return(addrs) => addrs,
1751        };
1752
1753        let allow_resolving_local_addrs = prefs
1754            .resolve_local_addrs
1755            .as_bool()
1756            .unwrap_or(addrcfg.allow_resolving_local_addrs);
1757
1758        if !allow_resolving_local_addrs {
1759            addrs.retain(|addr| {
1760                let keep = crate::address::is_globally_reachable_unicast(*addr);
1761
1762                if !keep {
1763                    debug!("Dropping non-routable address {addr} from RESOLVED answer");
1764                }
1765
1766                keep
1767            });
1768
1769            if addrs.is_empty() {
1770                debug!("Got RESOLVED containing only non-routable addresses");
1771                return Err(ErrorDetail::NoRoutableAddress.into());
1772            }
1773        }
1774
1775        Ok(addrs)
1776    }
1777
1778    /// Perform a remote DNS reverse lookup with the provided IP address.
1779    ///
1780    /// On success, return a list of hostnames.
1781    #[instrument(skip_all, level = "trace")]
1782    pub async fn resolve_ptr(&self, addr: IpAddr) -> crate::Result<Vec<String>> {
1783        self.resolve_ptr_with_prefs(addr, &self.connect_prefs).await
1784    }
1785
1786    /// Perform a remote DNS reverse lookup with the provided IP address.
1787    ///
1788    /// On success, return a list of hostnames.
1789    #[instrument(level = "trace", skip_all)]
1790    pub async fn resolve_ptr_with_prefs(
1791        &self,
1792        addr: IpAddr,
1793        prefs: &StreamPrefs,
1794    ) -> crate::Result<Vec<String>> {
1795        let addrcfg = self.client.addrcfg.get();
1796        let allow_resolving_local_addrs = prefs
1797            .resolve_local_addrs
1798            .as_bool()
1799            .unwrap_or(addrcfg.allow_resolving_local_addrs);
1800
1801        if !allow_resolving_local_addrs && !crate::address::is_globally_reachable_unicast(addr) {
1802            debug!("Rejecting reverse lookup request for non-routable address {addr}");
1803
1804            // Note: this is not 100% accurate, but I'm not sure if it makes sense
1805            // to introuce another ErrorDetail just for this
1806            return Err(ErrorDetail::NoRoutableAddress.into());
1807        }
1808
1809        let circ = self.get_or_launch_exit_tunnel(&[], prefs).await?;
1810
1811        let resolve_ptr_future = circ.resolve_ptr(addr);
1812        let hostnames = self
1813            .client
1814            .runtime
1815            .timeout(
1816                self.client.timeoutcfg.get().resolve_ptr_timeout,
1817                resolve_ptr_future,
1818            )
1819            .await
1820            .map_err(|_| ErrorDetail::ExitTimeout)?
1821            .map_err(|cause| ErrorDetail::StreamFailed {
1822                cause,
1823                kind: "reverse DNS lookup",
1824            })?;
1825
1826        Ok(hostnames)
1827    }
1828
1829    /// Return a reference to this client's directory manager.
1830    ///
1831    /// This function is unstable. It is only enabled if the crate was
1832    /// built with the `experimental-api` feature.
1833    #[cfg(feature = "experimental-api")]
1834    pub fn dirmgr(&self) -> crate::Result<Arc<dyn tor_dirmgr::DirProvider>> {
1835        Ok(self
1836            .client
1837            .running_inner("access internal functionality")?
1838            .dirmgr
1839            .clone())
1840    }
1841
1842    /// Return a reference to this client's circuit manager.
1843    ///
1844    /// This function is unstable. It is only enabled if the crate was
1845    /// built with the `experimental-api` feature.
1846    #[cfg(feature = "experimental-api")]
1847    pub fn circmgr(&self) -> crate::Result<Arc<tor_circmgr::CircMgr<R>>> {
1848        Ok(self
1849            .client
1850            .running_inner("access internal functionality")?
1851            .circmgr
1852            .clone())
1853    }
1854
1855    /// Return a reference to this client's channel manager.
1856    ///
1857    /// This function is unstable. It is only enabled if the crate was
1858    /// built with the `experimental-api` feature.
1859    #[cfg(feature = "experimental-api")]
1860    pub fn chanmgr(&self) -> crate::Result<Arc<tor_chanmgr::ChanMgr<R>>> {
1861        Ok(self
1862            .client
1863            .running_inner("access internal functionality")?
1864            .chanmgr
1865            .clone())
1866    }
1867
1868    /// Return a reference to this client's circuit pool.
1869    ///
1870    /// This function is unstable. It is only enabled if the crate was
1871    /// built with the `experimental-api` feature and any of `onion-service-client`
1872    /// or `onion-service-service` features. This method is required to invoke
1873    /// tor_hsservice::OnionService::launch()
1874    #[cfg(all(
1875        feature = "experimental-api",
1876        any(feature = "onion-service-client", feature = "onion-service-service")
1877    ))]
1878    pub fn hs_circ_pool(&self) -> crate::Result<Arc<tor_circmgr::hspool::HsCircPool<R>>> {
1879        Ok(self
1880            .client
1881            .running_inner("access internal functionality")?
1882            .hs_circ_pool
1883            .clone())
1884    }
1885
1886    /// Return a reference to the runtime being used by this client.
1887    //
1888    // This API is not a hostage to fortune since we already require that R: Clone,
1889    // and necessarily a TorClient must have a clone of it.
1890    //
1891    // We provide it simply to save callers who have a TorClient from
1892    // having to separately keep their own handle,
1893    pub fn runtime(&self) -> &R {
1894        &self.client.runtime
1895    }
1896
1897    /// Return a netdir that is timely according to the rules of `timeliness`.
1898    ///
1899    /// The `action` string is a description of what we wanted to do with the
1900    /// directory, to be put into the error message if we couldn't find a directory.
1901    fn netdir(
1902        &self,
1903        timeliness: Timeliness,
1904        action: &'static str,
1905    ) -> StdResult<Arc<tor_netdir::NetDir>, ErrorDetail> {
1906        use tor_netdir::Error as E;
1907        // TODO: Conceivably we could take a NetDir from our DirMgrStore.
1908        match self.client.running_inner(action)?.dirmgr.netdir(timeliness) {
1909            Ok(netdir) => Ok(netdir),
1910            Err(E::NoInfo) | Err(E::NotEnoughInfo) => {
1911                Err(ErrorDetail::BootstrapRequired { action })
1912            }
1913            Err(error) => Err(ErrorDetail::NoDir { error, action }),
1914        }
1915    }
1916
1917    /// Get or launch an exit-suitable circuit with a given set of
1918    /// exit ports.
1919    #[instrument(skip_all, level = "trace")]
1920    async fn get_or_launch_exit_tunnel(
1921        &self,
1922        exit_ports: &[TargetPort],
1923        prefs: &StreamPrefs,
1924    ) -> StdResult<ClientDataTunnel, ErrorDetail> {
1925        let running = self
1926            .client
1927            .wait_for_bootstrap_running("build a circuit")
1928            .await?;
1929        // TODO HS probably this netdir ought to be made in connect_with_prefs
1930        // like for StreamInstructions::Hs.
1931        let dir = self.netdir(Timeliness::Timely, "build a circuit")?;
1932
1933        let tunnel = running
1934            .circmgr
1935            .get_or_launch_exit(
1936                dir.as_ref().into(),
1937                exit_ports,
1938                self.isolation(prefs),
1939                #[cfg(feature = "geoip")]
1940                prefs.country_code,
1941            )
1942            .await
1943            .map_err(|cause| ErrorDetail::ObtainExitCircuit {
1944                cause,
1945                exit_ports: Sensitive::new(exit_ports.into()),
1946            })?;
1947        drop(dir); // This decreases the refcount on the netdir.
1948
1949        Ok(tunnel)
1950    }
1951
1952    /// Return an overall [`Isolation`] for this `TorClient` and a `StreamPrefs`.
1953    ///
1954    /// This describes which operations might use
1955    /// circuit(s) with this one.
1956    ///
1957    /// This combines isolation information from
1958    /// [`StreamPrefs::prefs_isolation`]
1959    /// and the `TorClient`'s isolation (eg from [`TorClient::isolated_client`]).
1960    fn isolation(&self, prefs: &StreamPrefs) -> StreamIsolation {
1961        let mut b = StreamIsolationBuilder::new();
1962        // Always consider our client_isolation.
1963        b.owner_token(self.client_isolation);
1964        // Consider stream isolation too, if it's set.
1965        if let Some(tok) = prefs.prefs_isolation() {
1966            b.stream_isolation(tok);
1967        }
1968        // Failure should be impossible with this builder.
1969        b.build().expect("Failed to construct StreamIsolation")
1970    }
1971
1972    /// Try to launch an onion service with a given configuration.
1973    ///
1974    /// Returns `Ok(None)` if the service specified is disabled in the config.
1975    ///
1976    /// This onion service will not actually handle any requests on its own: you
1977    /// will need to
1978    /// pull [`RendRequest`](tor_hsservice::RendRequest) objects from the returned stream,
1979    /// [`accept`](tor_hsservice::RendRequest::accept) the ones that you want to
1980    /// answer, and then wait for them to give you [`StreamRequest`](tor_hsservice::StreamRequest)s.
1981    ///
1982    /// You may find the [`tor_hsservice::handle_rend_requests`] API helpful for
1983    /// translating `RendRequest`s into `StreamRequest`s.
1984    ///
1985    /// If you want to forward all the requests from an onion service to a set
1986    /// of local ports, you may want to use the `tor-hsrproxy` crate.
1987    #[cfg(feature = "onion-service-service")]
1988    #[instrument(skip_all, level = "trace")]
1989    pub fn launch_onion_service(
1990        &self,
1991        config: tor_hsservice::OnionServiceConfig,
1992    ) -> crate::Result<
1993        Option<(
1994            Arc<tor_hsservice::RunningOnionService>,
1995            impl futures::Stream<Item = tor_hsservice::RendRequest> + use<R>,
1996        )>,
1997    > {
1998        let nickname = config.nickname();
1999
2000        if !config.enabled() {
2001            info!(
2002                nickname=%nickname,
2003                "Skipping onion service because it was disabled in the config"
2004            );
2005            return Ok(None);
2006        }
2007
2008        let running = self
2009            .client
2010            .initiate_bootstrap_if_needed("launch onion service")?;
2011
2012        let keymgr = self
2013            .client
2014            .inert_client
2015            .keymgr
2016            .as_ref()
2017            .ok_or(ErrorDetail::KeystoreRequired {
2018                action: "launch onion service",
2019            })?
2020            .clone();
2021        let state_dir = self.client.state_directory.clone();
2022
2023        let service = tor_hsservice::OnionService::builder()
2024            .config(config) // TODO #1186: Allow override of KeyMgr for "ephemeral" operation?
2025            .keymgr(keymgr)
2026            // TODO #1186: Allow override of StateMgr for "ephemeral" operation?
2027            .state_dir(state_dir)
2028            .build()
2029            .map_err(ErrorDetail::LaunchOnionService)?;
2030        Ok(service
2031            .launch(
2032                self.client.runtime.clone(),
2033                running.dirmgr.clone().upcast_arc(),
2034                running.hs_circ_pool.clone(),
2035                Arc::clone(&self.client.path_resolver),
2036            )
2037            .map_err(ErrorDetail::LaunchOnionService)?)
2038    }
2039
2040    /// Try to launch an onion service with a given configuration and provided
2041    /// [`HsIdKeypair`]. If an onion service with the given nickname already has an
2042    /// associated `HsIdKeypair`  in this `TorClient`'s `KeyMgr`, then this operation
2043    /// fails rather than overwriting the existing key.
2044    ///
2045    /// Returns `Ok(None)` if the service specified is disabled in the config.
2046    ///
2047    /// The specified `HsIdKeypair` will be inserted in the primary keystore.
2048    ///
2049    /// **Important**: depending on the configuration of your
2050    /// [primary keystore](tor_keymgr::config::PrimaryKeystoreConfig),
2051    /// the `HsIdKeypair` **may** get persisted to disk.
2052    /// By default, Arti's primary keystore is the [native](ArtiKeystoreKind::Native),
2053    /// disk-based keystore.
2054    ///
2055    /// This onion service will not actually handle any requests on its own: you
2056    /// will need to
2057    /// pull [`RendRequest`](tor_hsservice::RendRequest) objects from the returned stream,
2058    /// [`accept`](tor_hsservice::RendRequest::accept) the ones that you want to
2059    /// answer, and then wait for them to give you [`StreamRequest`](tor_hsservice::StreamRequest)s.
2060    ///
2061    /// You may find the [`tor_hsservice::handle_rend_requests`] API helpful for
2062    /// translating `RendRequest`s into `StreamRequest`s.
2063    ///
2064    /// If you want to forward all the requests from an onion service to a set
2065    /// of local ports, you may want to use the `tor-hsrproxy` crate.
2066    #[cfg(all(feature = "onion-service-service", feature = "experimental-api"))]
2067    #[instrument(skip_all, level = "trace")]
2068    pub fn launch_onion_service_with_hsid(
2069        &self,
2070        config: tor_hsservice::OnionServiceConfig,
2071        id_keypair: HsIdKeypair,
2072    ) -> crate::Result<
2073        Option<(
2074            Arc<tor_hsservice::RunningOnionService>,
2075            impl futures::Stream<Item = tor_hsservice::RendRequest> + use<R>,
2076        )>,
2077    > {
2078        let nickname = config.nickname();
2079        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
2080        let selector = KeystoreSelector::Primary;
2081
2082        let _kp = self
2083            .client
2084            .inert_client
2085            .keymgr
2086            .as_ref()
2087            .ok_or(ErrorDetail::KeystoreRequired {
2088                action: "launch onion service ex",
2089            })?
2090            .insert::<HsIdKeypair>(id_keypair, &hsid_spec, selector, false)?;
2091
2092        self.launch_onion_service(config)
2093    }
2094
2095    /// Generate a service discovery keypair for connecting to a hidden service running in
2096    /// "restricted discovery" mode.
2097    ///
2098    /// The `selector` argument is used for choosing the keystore in which to generate the keypair.
2099    /// While most users will want to write to the [`Primary`](KeystoreSelector::Primary), if you
2100    /// have configured this `TorClient` with a non-default keystore and wish to generate the
2101    /// keypair in it, you can do so by calling this function with a [KeystoreSelector::Id]
2102    /// specifying the keystore ID of your keystore.
2103    ///
2104    // Note: the selector argument exists for future-proofing reasons. We don't currently support
2105    // configuring custom or non-default keystores (see #1106).
2106    ///
2107    /// Returns an error if the key already exists in the specified key store.
2108    ///
2109    /// Important: the public part of the generated keypair must be shared with the service, and
2110    /// the service needs to be configured to allow the owner of its private counterpart to
2111    /// discover its introduction points. The caller is responsible for sharing the public part of
2112    /// the key with the hidden service.
2113    ///
2114    /// This function does not require the `TorClient` to be running or bootstrapped.
2115    //
2116    // TODO: decide whether this should use get_or_generate before making it
2117    // non-experimental
2118    #[cfg(all(
2119        feature = "onion-service-client",
2120        feature = "experimental-api",
2121        feature = "keymgr"
2122    ))]
2123    pub fn generate_service_discovery_key(
2124        &self,
2125        selector: KeystoreSelector,
2126        hsid: HsId,
2127    ) -> crate::Result<HsClientDescEncKey> {
2128        self.client
2129            .inert_client
2130            .generate_service_discovery_key(selector, hsid)
2131    }
2132
2133    /// Rotate the service discovery keypair for connecting to a hidden service running in
2134    /// "restricted discovery" mode.
2135    ///
2136    /// **If the specified keystore already contains a restricted discovery keypair
2137    /// for the service, it will be overwritten.** Otherwise, a new keypair is generated.
2138    ///
2139    /// The `selector` argument is used for choosing the keystore in which to generate the keypair.
2140    /// While most users will want to write to the [`Primary`](KeystoreSelector::Primary), if you
2141    /// have configured this `TorClient` with a non-default keystore and wish to generate the
2142    /// keypair in it, you can do so by calling this function with a [KeystoreSelector::Id]
2143    /// specifying the keystore ID of your keystore.
2144    ///
2145    // Note: the selector argument exists for future-proofing reasons. We don't currently support
2146    // configuring custom or non-default keystores (see #1106).
2147    ///
2148    /// Important: the public part of the generated keypair must be shared with the service, and
2149    /// the service needs to be configured to allow the owner of its private counterpart to
2150    /// discover its introduction points. The caller is responsible for sharing the public part of
2151    /// the key with the hidden service.
2152    ///
2153    /// This function does not require the `TorClient` to be running or bootstrapped.
2154    #[cfg(all(
2155        feature = "onion-service-client",
2156        feature = "experimental-api",
2157        feature = "keymgr"
2158    ))]
2159    #[cfg_attr(
2160        docsrs,
2161        doc(cfg(all(
2162            feature = "onion-service-client",
2163            feature = "experimental-api",
2164            feature = "keymgr"
2165        )))
2166    )]
2167    pub fn rotate_service_discovery_key(
2168        &self,
2169        selector: KeystoreSelector,
2170        hsid: HsId,
2171    ) -> crate::Result<HsClientDescEncKey> {
2172        self.client
2173            .inert_client
2174            .rotate_service_discovery_key(selector, hsid)
2175    }
2176
2177    /// Insert a service discovery secret key for connecting to a hidden service running in
2178    /// "restricted discovery" mode
2179    ///
2180    /// The `selector` argument is used for choosing the keystore in which to generate the keypair.
2181    /// While most users will want to write to the [`Primary`](KeystoreSelector::Primary), if you
2182    /// have configured this `TorClient` with a non-default keystore and wish to insert the
2183    /// key in it, you can do so by calling this function with a [KeystoreSelector::Id]
2184    ///
2185    // Note: the selector argument exists for future-proofing reasons. We don't currently support
2186    // configuring custom or non-default keystores (see #1106).
2187    ///
2188    /// Returns an error if the key already exists in the specified key store.
2189    ///
2190    /// Important: the public part of the generated keypair must be shared with the service, and
2191    /// the service needs to be configured to allow the owner of its private counterpart to
2192    /// discover its introduction points. The caller is responsible for sharing the public part of
2193    /// the key with the hidden service.
2194    ///
2195    /// This function does not require the `TorClient` to be running or bootstrapped.
2196    #[cfg(all(
2197        feature = "onion-service-client",
2198        feature = "experimental-api",
2199        feature = "keymgr"
2200    ))]
2201    #[cfg_attr(
2202        docsrs,
2203        doc(cfg(all(
2204            feature = "onion-service-client",
2205            feature = "experimental-api",
2206            feature = "keymgr"
2207        )))
2208    )]
2209    pub fn insert_service_discovery_key(
2210        &self,
2211        selector: KeystoreSelector,
2212        hsid: HsId,
2213        hs_client_desc_enc_secret_key: HsClientDescEncSecretKey,
2214    ) -> crate::Result<HsClientDescEncKey> {
2215        self.client.inert_client.insert_service_discovery_key(
2216            selector,
2217            hsid,
2218            hs_client_desc_enc_secret_key,
2219        )
2220    }
2221
2222    /// Return the service discovery public key for the service with the specified `hsid`.
2223    ///
2224    /// Returns `Ok(None)` if no such key exists.
2225    ///
2226    /// This function does not require the `TorClient` to be running or bootstrapped.
2227    #[cfg(all(feature = "onion-service-client", feature = "experimental-api"))]
2228    #[cfg_attr(
2229        docsrs,
2230        doc(cfg(all(feature = "onion-service-client", feature = "experimental-api")))
2231    )]
2232    pub fn get_service_discovery_key(
2233        &self,
2234        hsid: HsId,
2235    ) -> crate::Result<Option<HsClientDescEncKey>> {
2236        self.client.inert_client.get_service_discovery_key(hsid)
2237    }
2238
2239    /// Removes the service discovery keypair for the service with the specified `hsid`.
2240    ///
2241    /// Returns an error if the selected keystore is not the default keystore or one of the
2242    /// configured secondary stores.
2243    ///
2244    /// Returns `Ok(None)` if no such keypair exists whereas `Ok(Some()) means the keypair was successfully removed.
2245    ///
2246    /// Returns `Err` if an error occurred while trying to remove the key.
2247    #[cfg(all(
2248        feature = "onion-service-client",
2249        feature = "experimental-api",
2250        feature = "keymgr"
2251    ))]
2252    #[cfg_attr(
2253        docsrs,
2254        doc(cfg(all(
2255            feature = "onion-service-client",
2256            feature = "experimental-api",
2257            feature = "keymgr"
2258        )))
2259    )]
2260    pub fn remove_service_discovery_key(
2261        &self,
2262        selector: KeystoreSelector,
2263        hsid: HsId,
2264    ) -> crate::Result<Option<()>> {
2265        self.client
2266            .inert_client
2267            .remove_service_discovery_key(selector, hsid)
2268    }
2269
2270    /// Create (but do not launch) a new
2271    /// [`OnionService`](tor_hsservice::OnionService)
2272    /// using the given configuration.
2273    ///
2274    /// This is useful for managing an onion service without needing to start a `TorClient` or the
2275    /// onion service itself.
2276    /// If you only wish to run the onion service, see
2277    /// [`TorClient::launch_onion_service()`]
2278    /// which allows you to launch an onion service from a running `TorClient`.
2279    ///
2280    /// The returned `OnionService` can be launched using
2281    /// [`OnionService::launch()`](tor_hsservice::OnionService::launch).
2282    /// Note that `launch()` requires a [`NetDirProvider`],
2283    /// [`HsCircPool`](tor_circmgr::hspool::HsCircPool), etc,
2284    /// which you should obtain from a running `TorClient`.
2285    /// But these are only accessible from a `TorClient` if the "experimental-api" feature is
2286    /// enabled.
2287    /// The behaviour is not specified if you create the `OnionService` with
2288    /// `create_onion_service()` using one [`TorClientConfig`],
2289    /// but launch it using a `TorClient` generated from a different `TorClientConfig`.
2290    // TODO #2249: Look into this behaviour more, and possibly error if there is a different config.
2291    #[cfg(feature = "onion-service-service")]
2292    #[instrument(skip_all, level = "trace")]
2293    pub fn create_onion_service(
2294        config: &TorClientConfig,
2295        svc_config: tor_hsservice::OnionServiceConfig,
2296    ) -> crate::Result<tor_hsservice::OnionService> {
2297        let inert_client = InertTorClient::new(config)?;
2298        inert_client.create_onion_service(config, svc_config)
2299    }
2300
2301    /// Return a current [`status::BootstrapStatus`] describing how close this client
2302    /// is to being ready for user traffic.
2303    pub fn bootstrap_status(&self) -> status::BootstrapStatus {
2304        self.client.status_receiver.inner.borrow().clone()
2305    }
2306
2307    /// Return a stream of [`status::BootstrapStatus`] events that will be updated
2308    /// whenever the client's status changes.
2309    ///
2310    /// The receiver might not receive every update sent to this stream, though
2311    /// when it does poll the stream it should get the most recent one.
2312    //
2313    // TODO(nickm): will this also need to implement Send and 'static?
2314    pub fn bootstrap_events(&self) -> status::BootstrapEvents {
2315        self.client.status_receiver.clone()
2316    }
2317
2318    /// Change the client's current dormant mode, putting background tasks to sleep
2319    /// or waking them up as appropriate.
2320    ///
2321    /// This can be used to conserve CPU usage if you aren't planning on using the
2322    /// client for a while, especially on mobile platforms.
2323    ///
2324    /// See the [`DormantMode`] documentation for more details.
2325    pub fn set_dormant(&self, mode: DormantMode) {
2326        *self
2327            .client
2328            .dormant
2329            .lock()
2330            .expect("dormant lock poisoned")
2331            .borrow_mut() = Some(mode);
2332    }
2333
2334    /// Return a [`Future`] which resolves
2335    /// once this TorClient has stopped.
2336    #[cfg(feature = "experimental-api")]
2337    #[instrument(skip_all, level = "trace")]
2338    pub fn wait_for_stop(
2339        &self,
2340    ) -> impl futures::Future<Output = ()> + Send + Sync + 'static + use<R> {
2341        // We defer to the "wait for unlock" handle on our statemgr.
2342        //
2343        // The statemgr won't actually be unlocked until it is finally
2344        // dropped, which will happen when this TorClient is
2345        // dropped—which is what we want.
2346        self.client.statemgr.wait_for_unlock()
2347    }
2348
2349    /// Getter for keymgr.
2350    #[cfg(feature = "onion-service-cli-extra")]
2351    pub fn keymgr(&self) -> crate::Result<&KeyMgr> {
2352        self.client.inert_client.keymgr()
2353    }
2354}
2355
2356impl<R: Runtime> ClientShared<R> {
2357    /// Used by `bootstrap_inner`: Return a `RunningInner`, constructing it if necessary.
2358    fn instantiate_running_inner(
2359        &self,
2360        mut inner_guard: std::sync::MutexGuard<'_, Inner<R>>,
2361    ) -> Result<Arc<RunningInner<R>>, ErrorDetail> {
2362        match &*inner_guard {
2363            Inner::Running(running_inner) => Ok(Arc::clone(running_inner)),
2364            Inner::Poisoned(e) => Err(e.as_ref().clone()),
2365            Inner::NotConstructed(_) => {
2366                let error = ErrorDetail::from(internal!("Client under construction"));
2367                let mut pending = Inner::Poisoned(Box::new(error));
2368                std::mem::swap(&mut pending, &mut *inner_guard);
2369                let Inner::NotConstructed(pending) = pending else {
2370                    panic!("Surprising type change");
2371                };
2372                match RunningInner::new(*pending, self) {
2373                    Ok(running_inner) => {
2374                        *inner_guard = Inner::Running(Arc::clone(&running_inner));
2375                        self.bootstrap_setting_sender
2376                            .lock()
2377                            .expect("lock poisoned")
2378                            .borrow_mut()
2379                            .running_inner_is_present = true;
2380                        Ok(running_inner)
2381                    }
2382                    Err(e) => {
2383                        *inner_guard = Inner::Poisoned(Box::new(e.clone()));
2384                        Err(e)
2385                    }
2386                }
2387            }
2388        }
2389    }
2390
2391    /// Implementation of `bootstrap`, split out in order to avoid manually specifying
2392    /// double error conversions.
2393    async fn bootstrap_inner(&self) -> StdResult<(), ErrorDetail> {
2394        // Wait for an existing bootstrap attempt to finish first.
2395        //
2396        // This is a futures::lock::Mutex, so it's okay to await while we hold it.
2397        let _bootstrap_lock = self.bootstrap_in_progress.lock().await;
2398
2399        let running = self.instantiate_running_inner(self.inner.lock().expect("lock poisoned"))?;
2400
2401        // Make sure we have a bridge descriptor manager, which is active iff required
2402        #[cfg(feature = "bridge-client")]
2403        {
2404            let mut dormant = self.dormant.lock().expect("dormant lock poisoned");
2405            let dormant = dormant.borrow();
2406            let dormant = dormant.ok_or_else(|| internal!("dormant dropped"))?.into();
2407
2408            let mut bdm = running.bridge_desc_mgr.lock().expect("bdm lock poisoned");
2409            if bdm.is_none() {
2410                let new_bdm = Arc::new(BridgeDescMgr::new(
2411                    &Default::default(),
2412                    self.runtime.clone(),
2413                    self.dirmgr_store.clone(),
2414                    running.circmgr.clone(),
2415                    dormant,
2416                )?);
2417                running
2418                    .guardmgr
2419                    .install_bridge_desc_provider(&(new_bdm.clone() as _))
2420                    .map_err(ErrorDetail::GuardMgrSetup)?;
2421                // If ^ that fails, we drop the BridgeDescMgr again.  It may do some
2422                // work but will hopefully eventually quit.
2423                *bdm = Some(new_bdm);
2424            }
2425        }
2426
2427        if self
2428            .statemgr
2429            .try_lock()
2430            .map_err(ErrorDetail::StateAccess)?
2431            .held()
2432        {
2433            debug!("It appears we have the lock on our state files.");
2434        } else {
2435            info!(
2436                "Another process has the lock on our state files. We'll proceed in read-only mode."
2437            );
2438        }
2439
2440        // If we fail to bootstrap (i.e. we return before the disarm() point below), attempt to
2441        // unlock the state files.
2442        let unlock_guard = util::StateMgrUnlockGuard::new(&self.statemgr);
2443
2444        running
2445            .dirmgr
2446            .bootstrap()
2447            .await
2448            .map_err(ErrorDetail::DirMgrBootstrap)?;
2449
2450        // Since we succeeded, disarm the unlock guard.
2451        unlock_guard.disarm();
2452
2453        Ok(())
2454    }
2455
2456    /// Ensure that this client is running and bootstrapped, and return a [`RunningInner`] if it is.
2457    ///
2458    /// If we're not bootstrapped,
2459    /// we either try to bootstrap or return an error,
2460    /// depending on `self.should_bootstrap`:
2461    ///
2462    /// ## For `BootstrapBehavior::OnDemand` clients
2463    ///
2464    /// Initiate a bootstrap by calling `bootstrap_inner`
2465    /// (which is idempotent, so attempts to bootstrap twice will just do nothing).
2466    ///
2467    /// ## For `BootstrapBehavior::Manual` clients
2468    ///
2469    /// Check whether a bootstrap is in progress; if one is, wait until it finishes.
2470    /// Then see whether we're bootstrapped, and return either a success or a failure.
2471    #[instrument(skip_all, level = "trace")]
2472    async fn wait_for_bootstrap_running(
2473        &self,
2474        action: &'static str,
2475    ) -> StdResult<Arc<RunningInner<R>>, ErrorDetail> {
2476        match self.should_bootstrap {
2477            BootstrapBehavior::OnDemand => {
2478                self.bootstrap_inner().await?;
2479            }
2480            BootstrapBehavior::Manual => {
2481                // Grab the lock, and immediately release it.  That will ensure that nobody else is trying to bootstrap.
2482                self.bootstrap_in_progress.lock().await;
2483            }
2484        }
2485        self.dormant
2486            .lock()
2487            .map_err(|_| internal!("dormant poisoned"))?
2488            .try_maybe_send(|dormant| {
2489                Ok::<_, Bug>(Some({
2490                    match dormant.ok_or_else(|| internal!("dormant dropped"))? {
2491                        DormantMode::Soft => DormantMode::Normal,
2492                        other @ DormantMode::Normal => other,
2493                    }
2494                }))
2495            })?;
2496        self.running_inner(action)
2497    }
2498
2499    /// If we are currently bootstrapping or running, return a [`RunningInner`].
2500    fn running_inner(&self, action: &'static str) -> StdResult<Arc<RunningInner<R>>, ErrorDetail> {
2501        let guard = self.inner.lock().expect("Lock poisoned");
2502        match &*guard {
2503            Inner::NotConstructed(_) => Err(ErrorDetail::BootstrapRequired { action }),
2504            Inner::Running(running_inner) => Ok(Arc::clone(running_inner)),
2505            Inner::Poisoned(e) => Err(e.as_ref().clone()),
2506        }
2507    }
2508
2509    /// Ensure that our bootstrap state is [`RunningInner`], if possible.
2510    ///
2511    /// Return an error if our [`BootstrapBehavior`] is `Manual` and have not created a
2512    /// [`RunningInner`].
2513    fn initiate_bootstrap_if_needed(
2514        &self,
2515        action: &'static str,
2516    ) -> StdResult<Arc<RunningInner<R>>, ErrorDetail> {
2517        let guard = self.inner.lock().expect("Lock poisoned");
2518        match &*guard {
2519            Inner::Running(running_inner) => Ok(Arc::clone(running_inner)),
2520            Inner::Poisoned(e) => Err(e.as_ref().clone()),
2521            Inner::NotConstructed(_) => match self.should_bootstrap {
2522                BootstrapBehavior::Manual => Err(ErrorDetail::BootstrapRequired { action }),
2523                BootstrapBehavior::OnDemand => self.instantiate_running_inner(guard),
2524            },
2525        }
2526    }
2527
2528    /// This is split out from `reconfigure` so we can do the all-or-nothing
2529    /// check without recursion. the caller to this method must hold the
2530    /// `reconfigure_lock`.
2531    #[instrument(level = "trace", skip_all)]
2532    fn reconfigure_inner(
2533        &self,
2534        new_config: &TorClientConfig,
2535        how: tor_config::Reconfigure,
2536        _reconfigure_lock_guard: &std::sync::MutexGuard<'_, ()>,
2537    ) -> crate::Result<()> {
2538        // We ignore 'new_config.path_resolver' here since CfgPathResolver does not impl PartialEq
2539        // and we have no way to compare them, but this field is explicitly documented as being
2540        // non-reconfigurable anyways.
2541        let addr_cfg = &new_config.address_filter;
2542        let timeout_cfg = &new_config.stream_timeouts;
2543        let state_cfg = new_config
2544            .storage
2545            .expand_state_dir(&self.path_resolver)
2546            .map_err(wrap_err)?;
2547
2548        // TODO wasm: This ins't really how things should be long term,
2549        // but once we have a more generic notion of configuring storage
2550        // we can change this to comply with it.
2551        #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
2552        {
2553            if state_cfg != self.statemgr.path() {
2554                how.cannot_change("storage.state_dir").map_err(wrap_err)?;
2555            }
2556        }
2557
2558        self.memquota
2559            .reconfigure(new_config.system.memory.clone(), how)
2560            .map_err(wrap_err)?;
2561
2562        let mut inner_lock = self.inner.lock().expect("Lock poisoned");
2563        match &mut *inner_lock {
2564            Inner::Poisoned(e) => return Err(e.as_ref().clone().into()),
2565            Inner::NotConstructed(nc) => nc.reconfigure(new_config, how)?,
2566            Inner::Running(r) => {
2567                let running = Arc::clone(r);
2568                drop(inner_lock);
2569                running.reconfigure(new_config, how)?;
2570            }
2571        }
2572        if how == tor_config::Reconfigure::CheckAllOrNothing {
2573            return Ok(());
2574        }
2575
2576        self.addrcfg.replace(addr_cfg.clone());
2577        self.timeoutcfg.replace(timeout_cfg.clone());
2578
2579        Ok(())
2580    }
2581}
2582
2583/// Monitor `dormant_mode` and enable/disable periodic tasks as applicable
2584///
2585/// This function is spawned as a task during client construction.
2586// TODO should this perhaps be done by each TaskHandle?
2587async fn tasks_monitor_dormant<R: Runtime>(
2588    mut dormant_rx: postage::watch::Receiver<Option<DormantMode>>,
2589    netdir: Arc<dyn NetDirProvider>,
2590    chanmgr: Arc<tor_chanmgr::ChanMgr<R>>,
2591    #[cfg(feature = "bridge-client")] bridge_desc_mgr: Arc<Mutex<Option<Arc<BridgeDescMgr<R>>>>>,
2592    periodic_task_handles: Vec<TaskHandle>,
2593) {
2594    while let Some(Some(mode)) = dormant_rx.next().await {
2595        let netparams = netdir.params();
2596
2597        chanmgr
2598            .set_dormancy(mode.into(), netparams)
2599            .unwrap_or_else(|e| error_report!(e, "couldn't set dormancy"));
2600
2601        // IEFI simplifies handling of exceptional cases, as "never mind, then".
2602        #[cfg(feature = "bridge-client")]
2603        (|| {
2604            let mut bdm = bridge_desc_mgr.lock().ok()?;
2605            let bdm = bdm.as_mut()?;
2606            bdm.set_dormancy(mode.into());
2607            Some(())
2608        })();
2609
2610        let is_dormant = matches!(mode, DormantMode::Soft);
2611
2612        for task in periodic_task_handles.iter() {
2613            if is_dormant {
2614                task.cancel();
2615            } else {
2616                task.fire();
2617            }
2618        }
2619    }
2620}
2621
2622/// Alias for TorError::from(Error)
2623pub(crate) fn wrap_err<T>(err: T) -> crate::Error
2624where
2625    ErrorDetail: From<T>,
2626{
2627    ErrorDetail::from(err).into()
2628}
2629
2630#[cfg(test)]
2631mod test {
2632    // @@ begin test lint list maintained by maint/add_warning @@
2633    #![allow(clippy::bool_assert_comparison)]
2634    #![allow(clippy::clone_on_copy)]
2635    #![allow(clippy::dbg_macro)]
2636    #![allow(clippy::mixed_attributes_style)]
2637    #![allow(clippy::print_stderr)]
2638    #![allow(clippy::print_stdout)]
2639    #![allow(clippy::single_char_pattern)]
2640    #![allow(clippy::unwrap_used)]
2641    #![allow(clippy::unchecked_time_subtraction)]
2642    #![allow(clippy::useless_vec)]
2643    #![allow(clippy::needless_pass_by_value)]
2644    #![allow(clippy::string_slice)] // See arti#2571
2645    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
2646
2647    use tor_config::Reconfigure;
2648
2649    use super::*;
2650    use crate::config::TorClientConfigBuilder;
2651    use crate::{ErrorKind, HasKind};
2652
2653    #[test]
2654    fn create_unbootstrapped() {
2655        tor_rtcompat::test_with_one_runtime!(|rt| async {
2656            let state_dir = tempfile::tempdir().unwrap();
2657            let cache_dir = tempfile::tempdir().unwrap();
2658            let cfg = TorClientConfigBuilder::from_directories(state_dir, cache_dir)
2659                .build()
2660                .unwrap();
2661            let _ = TorClient::with_runtime(rt)
2662                .config(cfg)
2663                .bootstrap_behavior(BootstrapBehavior::Manual)
2664                .create_unbootstrapped()
2665                .unwrap();
2666        });
2667        tor_rtcompat::test_with_one_runtime!(|rt| async {
2668            let state_dir = tempfile::tempdir().unwrap();
2669            let cache_dir = tempfile::tempdir().unwrap();
2670            let cfg = TorClientConfigBuilder::from_directories(state_dir, cache_dir)
2671                .build()
2672                .unwrap();
2673            let _ = TorClient::with_runtime(rt)
2674                .config(cfg)
2675                .bootstrap_behavior(BootstrapBehavior::Manual)
2676                .create_unbootstrapped_async()
2677                .await
2678                .unwrap();
2679        });
2680    }
2681
2682    #[test]
2683    fn unbootstrapped_client_unusable() {
2684        tor_rtcompat::test_with_one_runtime!(|rt| async {
2685            let state_dir = tempfile::tempdir().unwrap();
2686            let cache_dir = tempfile::tempdir().unwrap();
2687            let cfg = TorClientConfigBuilder::from_directories(state_dir, cache_dir)
2688                .build()
2689                .unwrap();
2690            // Test sync
2691            let client = TorClient::with_runtime(rt)
2692                .config(cfg)
2693                .bootstrap_behavior(BootstrapBehavior::Manual)
2694                .create_unbootstrapped()
2695                .unwrap();
2696            let result = client.connect("example.com:80").await;
2697            assert!(result.is_err());
2698            assert_eq!(result.err().unwrap().kind(), ErrorKind::BootstrapRequired);
2699        });
2700        // Need a separate test for async because Runtime and TorClientConfig are consumed by the
2701        // builder
2702        tor_rtcompat::test_with_one_runtime!(|rt| async {
2703            let state_dir = tempfile::tempdir().unwrap();
2704            let cache_dir = tempfile::tempdir().unwrap();
2705            let cfg = TorClientConfigBuilder::from_directories(state_dir, cache_dir)
2706                .build()
2707                .unwrap();
2708            // Test sync
2709            let client = TorClient::with_runtime(rt)
2710                .config(cfg)
2711                .bootstrap_behavior(BootstrapBehavior::Manual)
2712                .create_unbootstrapped_async()
2713                .await
2714                .unwrap();
2715            let result = client.connect("example.com:80").await;
2716            assert!(result.is_err());
2717            assert_eq!(result.err().unwrap().kind(), ErrorKind::BootstrapRequired);
2718        });
2719    }
2720
2721    #[test]
2722    fn streamprefs_isolate_every_stream() {
2723        let mut observed = StreamPrefs::new();
2724        observed.isolate_every_stream();
2725        match observed.isolation {
2726            StreamIsolationPreference::EveryStream => (),
2727            _ => panic!("unexpected isolation: {:?}", observed.isolation),
2728        };
2729    }
2730
2731    #[test]
2732    fn streamprefs_new_has_expected_defaults() {
2733        let observed = StreamPrefs::new();
2734        assert_eq!(observed.ip_ver_pref, IpVersionPreference::Ipv4Preferred);
2735        assert!(!observed.optimistic_stream);
2736        // StreamIsolationPreference does not implement Eq, check manually.
2737        match observed.isolation {
2738            StreamIsolationPreference::None => (),
2739            _ => panic!("unexpected isolation: {:?}", observed.isolation),
2740        };
2741    }
2742
2743    #[test]
2744    fn streamprefs_new_isolation_group() {
2745        let mut observed = StreamPrefs::new();
2746        observed.new_isolation_group();
2747        match observed.isolation {
2748            StreamIsolationPreference::Explicit(_) => (),
2749            _ => panic!("unexpected isolation: {:?}", observed.isolation),
2750        };
2751    }
2752
2753    #[test]
2754    fn streamprefs_ipv6_only() {
2755        let mut observed = StreamPrefs::new();
2756        observed.ipv6_only();
2757        assert_eq!(observed.ip_ver_pref, IpVersionPreference::Ipv6Only);
2758    }
2759
2760    #[test]
2761    fn streamprefs_ipv6_preferred() {
2762        let mut observed = StreamPrefs::new();
2763        observed.ipv6_preferred();
2764        assert_eq!(observed.ip_ver_pref, IpVersionPreference::Ipv6Preferred);
2765    }
2766
2767    #[test]
2768    fn streamprefs_ipv4_only() {
2769        let mut observed = StreamPrefs::new();
2770        observed.ipv4_only();
2771        assert_eq!(observed.ip_ver_pref, IpVersionPreference::Ipv4Only);
2772    }
2773
2774    #[test]
2775    fn streamprefs_ipv4_preferred() {
2776        let mut observed = StreamPrefs::new();
2777        observed.ipv4_preferred();
2778        assert_eq!(observed.ip_ver_pref, IpVersionPreference::Ipv4Preferred);
2779    }
2780
2781    #[test]
2782    fn streamprefs_optimistic() {
2783        let mut observed = StreamPrefs::new();
2784        observed.optimistic();
2785        assert!(observed.optimistic_stream);
2786    }
2787
2788    #[test]
2789    fn streamprefs_set_isolation() {
2790        let mut observed = StreamPrefs::new();
2791        observed.set_isolation(IsolationToken::new());
2792        match observed.isolation {
2793            StreamIsolationPreference::Explicit(_) => (),
2794            _ => panic!("unexpected isolation: {:?}", observed.isolation),
2795        };
2796    }
2797
2798    #[test]
2799    fn reconfigure_all_or_nothing() {
2800        tor_rtcompat::test_with_one_runtime!(|rt| async {
2801            let state_dir = tempfile::tempdir().unwrap();
2802            let cache_dir = tempfile::tempdir().unwrap();
2803            let cfg = TorClientConfigBuilder::from_directories(state_dir, cache_dir)
2804                .build()
2805                .unwrap();
2806            let tor_client = TorClient::with_runtime(rt)
2807                .config(cfg.clone())
2808                .bootstrap_behavior(BootstrapBehavior::Manual)
2809                .create_unbootstrapped()
2810                .unwrap();
2811            tor_client
2812                .reconfigure(&cfg, Reconfigure::AllOrNothing)
2813                .unwrap();
2814        });
2815        tor_rtcompat::test_with_one_runtime!(|rt| async {
2816            let state_dir = tempfile::tempdir().unwrap();
2817            let cache_dir = tempfile::tempdir().unwrap();
2818            let cfg = TorClientConfigBuilder::from_directories(state_dir, cache_dir)
2819                .build()
2820                .unwrap();
2821            let tor_client = TorClient::with_runtime(rt)
2822                .config(cfg.clone())
2823                .bootstrap_behavior(BootstrapBehavior::Manual)
2824                .create_unbootstrapped_async()
2825                .await
2826                .unwrap();
2827            tor_client
2828                .reconfigure(&cfg, Reconfigure::AllOrNothing)
2829                .unwrap();
2830        });
2831    }
2832}