Skip to main content

arti_client/
client.rs

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