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