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