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