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