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