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