arti_client/
client.rs

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