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