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