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