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