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