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")]
9
use {derive_deftly::Deftly, tor_rpcbase::templates::*};
10

            
11
use crate::address::{IntoTorAddr, ResolveInstructions, StreamInstructions};
12

            
13
use crate::config::{ClientAddrConfig, StreamTimeoutConfig, TorClientConfig};
14
use crate::status::BootstrapStatus;
15
use safelog::{Sensitive, sensitive};
16
use tor_async_utils::{DropNotifyWatchSender, PostageWatchSenderExt};
17
use tor_chanmgr::ChanMgrConfig;
18
use tor_circmgr::ClientDataTunnel;
19
use tor_circmgr::isolation::{Isolation, StreamIsolation};
20
use tor_circmgr::{IsolationToken, TargetPort, isolation::StreamIsolationBuilder};
21
use tor_config::{BoolOrAuto, MutCfg};
22
#[cfg(feature = "bridge-client")]
23
use tor_dirmgr::bridgedesc::BridgeDescMgr;
24
use tor_dirmgr::{DirMgrStore, Timeliness};
25
use tor_error::{Bug, error_report, internal};
26
use tor_guardmgr::{GuardMgr, RetireCircuits};
27
use tor_keymgr::Keystore;
28
use tor_memquota::MemoryQuotaTracker;
29
use tor_netdir::{NetDirProvider, params::NetParameters};
30
use tor_persist::StateMgr;
31
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
32
use tor_persist::TestingStateMgr;
33
use 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
))]
38
use tor_rtcompat::PreferredRuntime;
39
use tor_rtcompat::{Runtime, SleepProviderExt};
40
#[cfg(feature = "onion-service-client")]
41
use {
42
    tor_hsclient::{HsClientConnector, HsClientDescEncKeypairSpecifier, HsClientSecretKeysBuilder},
43
    tor_hscrypto::pk::{HsClientDescEncKey, HsClientDescEncKeypair, HsClientDescEncSecretKey},
44
    tor_keymgr::CTorClientKeystore,
45
    tor_netdir::DirEvent,
46
};
47
#[cfg(feature = "onion-service-service")]
48
use {tor_keymgr::CTorServiceKeystore, tor_persist::state_dir::StateDirectory};
49

            
50
#[cfg(all(feature = "onion-service-service", feature = "experimental-api"))]
51
use tor_hsservice::HsIdKeypairSpecifier;
52
#[cfg(all(feature = "onion-service-client", feature = "experimental-api"))]
53
use {tor_hscrypto::pk::HsId, tor_hscrypto::pk::HsIdKeypair, tor_keymgr::KeystoreSelector};
54

            
55
use tor_keymgr::{ArtiNativeKeystore, KeyMgr, KeyMgrBuilder, config::ArtiKeystoreKind};
56

            
57
#[cfg(feature = "ephemeral-keystore")]
58
use tor_keymgr::ArtiEphemeralKeystore;
59

            
60
use futures::StreamExt as _;
61
use futures::lock::Mutex as AsyncMutex;
62
use std::net::IpAddr;
63
use std::result::Result as StdResult;
64
use std::sync::{Arc, Mutex};
65
use tor_rtcompat::SpawnExt;
66

            
67
use crate::err::ErrorDetail;
68
use crate::{TorClientBuilder, status, util};
69
#[cfg(feature = "geoip")]
70
use tor_geoip::CountryCode;
71
use tor_rtcompat::scheduler::TaskHandle;
72
use tracing::{debug, info, instrument, warn};
73

            
74
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
75
use tor_persist::FsStateMgr as UsingStateMgr;
76

            
77
// TODO wasm: This is not the right choice, but at least it compiles.
78
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
79
use tor_persist::TestingStateMgr as UsingStateMgr;
80

            
81
/// An active client session on the Tor network.
82
///
83
/// While it's running, it will fetch directory information, build
84
/// circuits, and make connections for you.
85
///
86
/// # In the Arti RPC System
87
///
88
/// An open client on the Tor network.
89
///
90
/// A `TorClient` can be used to open anonymous connections,
91
/// and (eventually) perform other activities.
92
///
93
/// You can use an `RpcSession` as a `TorClient`, or use the `isolated_client` method
94
/// to create a new `TorClient` whose stream will not share circuits with any other Tor client.
95
///
96
/// This ObjectID for this object can be used as the target of a SOCKS stream.
97
#[cfg_attr(
98
    feature = "rpc",
99
    derive(Deftly),
100
    derive_deftly(Object),
101
    deftly(rpc(expose_outside_of_session))
102
)]
103
pub struct TorClient<R: Runtime> {
104
    /// Default isolation token for streams through this client.
105
    ///
106
    /// This is eventually used for `owner_token` in `tor-circmgr/src/usage.rs`, and is orthogonal
107
    /// to the `stream_isolation` which comes from `connect_prefs` (or a passed-in `StreamPrefs`).
108
    /// (ie, both must be the same to share a circuit).
109
    client_isolation: IsolationToken,
110
    /// Connection preferences.  Starts out as `Default`,  Inherited by our clones.
111
    connect_prefs: StreamPrefs,
112

            
113
    /// Inner structure respresenting all components shared across different
114
    /// TorClients.
115
    client: Arc<ClientShared<R>>,
116
}
117

            
118
/// Shared pieces of a `TorClient`, used to implement client functionality.
119
///
120
/// In the future, we might choose to expose this along with APIs.
121
struct ClientShared<R: Runtime> {
122
    /// Asynchronous runtime object.
123
    runtime: R,
124

            
125
    /// Inner typestate object to represent the parts of the ClientShared that may be absent
126
    /// depending on whether we are running.
127
    inner: Mutex<Inner<R>>,
128

            
129
    /// Memory quota tracker
130
    memquota: Arc<MemoryQuotaTracker>,
131

            
132
    /// A handle to this client's [`InertTorClient`].
133
    ///
134
    /// Used for accessing the key manager and other persistent state.
135
    inert_client: InertTorClient,
136

            
137
    /// Location on disk where we store persistent data containing both location and Mistrust information.
138
    ///
139
    ///
140
    /// This path is configured via `[storage]` in the config but is not used directly as a
141
    /// StateDirectory in most places. Instead, its path and Mistrust information are copied
142
    /// to subsystems like `dirmgr`, `keymgr`, and `statemgr` during `TorClient` creation.
143
    #[cfg(feature = "onion-service-service")]
144
    state_directory: StateDirectory,
145
    /// Location on disk where we store persistent data (cooked state manager).
146
    statemgr: UsingStateMgr,
147

            
148
    /// Directory manager persistent storage.
149
    dirmgr_store: DirMgrStore<R>,
150

            
151
    /// Client address configuration
152
    addrcfg: MutCfg<ClientAddrConfig>,
153
    /// Client DNS configuration
154
    timeoutcfg: MutCfg<StreamTimeoutConfig>,
155
    /// Mutex used to serialize concurrent attempts to reconfigure a TorClient.
156
    ///
157
    /// See [`TorClient::reconfigure`] for more information on its use.
158
    reconfigure_lock: Arc<Mutex<()>>,
159

            
160
    /// A stream of bootstrap messages that we can clone when a client asks for
161
    /// it.
162
    ///
163
    /// (We don't need to observe this stream ourselves, since it drops each
164
    /// unobserved status change when the next status change occurs.)
165
    status_receiver: status::BootstrapEvents,
166

            
167
    /// mutex used to prevent two tasks from trying to bootstrap at once.
168
    bootstrap_in_progress: AsyncMutex<()>,
169

            
170
    /// Sender used to update changes in our bootstrap settings.
171
    bootstrap_setting_sender: Mutex<postage::watch::Sender<BootstrapSetting>>,
172

            
173
    /// Whether or not we should call `bootstrap` before doing things that require
174
    /// bootstrapping.
175
    ///
176
    /// If this is [`BootstrapBehavior::OnDemand`], we wait for the client to bootstrap
177
    /// (launching a bootstrap if necessary) before performing any operation that needs circuits.
178
    /// If this is [`BootstrapBehavior::Manual`], we give an error if we are told to do
179
    /// something that needs circuits and we have not been told to bootstrap.
180
    should_bootstrap: BootstrapBehavior,
181

            
182
    /// Shared boolean for whether we're currently in "dormant mode" or not.
183
    //
184
    // The sent value is `Option`, so that `None` is sent when the sender, here,
185
    // is dropped,.  That shuts down the monitoring task.
186
    dormant: Mutex<DropNotifyWatchSender<Option<DormantMode>>>,
187

            
188
    /// The path resolver given to us by a [`TorClientConfig`].
189
    ///
190
    /// We must not add our own variables to it since `TorClientConfig` uses it to perform its own
191
    /// path expansions. If we added our own variables, it would introduce an inconsistency where
192
    /// paths expanded by the `TorClientConfig` would expand differently than when expanded by us.
193
    path_resolver: Arc<tor_config_path::CfgPathResolver>,
194
}
195

            
196
/// A typestate object holding the parts of the client state that we may or may not have
197
/// depending on whether we are running.
198
enum Inner<R: Runtime> {
199
    /// The client is not constructed.
200
    ///
201
    /// In this state, the client won't try to connect to the network.
202
    NotConstructed(Box<NotConstructedInner<R>>),
203

            
204
    /// The client is either bootstrapped or trying to bootstrap.
205
    Running(Arc<RunningInner<R>>),
206

            
207
    /// The client has failed in a non-recoverable way.
208
    Poisoned(Box<ErrorDetail>),
209
}
210

            
211
/// Information stored by a never-bootstrapped [`TorClient`],
212
/// used to eventually construct a [`RunningInner`] and bootstrap.
213
struct NotConstructedInner<R: Runtime> {
214
    /// The client's configuration.
215
    config: TorClientConfig,
216

            
217
    /// A receiver to give to various tasks that want to monitor our dormant status.
218
    dormant_recv: postage::watch::Receiver<Option<DormantMode>>,
219

            
220
    /// A sender used to produce updates about our bootstrapping status.
221
    ///
222
    /// NOTE: The fact that this type is not Clone is the only reason
223
    /// that [`RunningInner::new`] needs to take NotConstructedInner by value.
224
    /// With some redesign we could simplify this, and do away with [`Inner::Poisoned`].
225
    status_sender: postage::watch::Sender<BootstrapStatus>,
226

            
227
    /// A receiver used to inform the bootstrap status processor about changes in our settings.
228
    bootstrap_setting_receiver: postage::watch::Receiver<BootstrapSetting>,
229

            
230
    /// A (possibly user-provided) builder used to construct our NetDirProvider.
231
    dirmgr_builder: Arc<dyn crate::builder::DirProviderBuilder<R>>,
232

            
233
    /// A (possibly user-provided) set of in-process extensions for our NetDirProvider.
234
    dirmgr_extensions: tor_dirmgr::config::DirMgrExtensions,
235
}
236

            
237
/// Data structures for a "running" client.
238
///
239
/// A running client is one that is either bootstrapped, or potentially trying to bootstrap.
240
///
241
/// All structures that potentially interact with the network belong here.
242
///
243
/// We defer the creation of this structure and its members until bootstrap time,
244
/// to make sure that before we are bootstrapping, nothing will try to connect to the network
245
/// or launch expensive background tasks.
246
struct RunningInner<R: Runtime> {
247
    /// Channel manager, used by circuits etc.,
248
    ///
249
    /// Used directly by client only for reconfiguration.
250
    chanmgr: Arc<tor_chanmgr::ChanMgr<R>>,
251
    /// Circuit manager for keeping our circuits up to date and building
252
    /// them on-demand.
253
    circmgr: Arc<tor_circmgr::CircMgr<R>>,
254
    /// Directory manager for keeping our directory material up to date.
255
    dirmgr: Arc<dyn tor_dirmgr::DirProvider>,
256
    /// Bridge descriptor manager
257
    ///
258
    /// None until we have bootstrapped.
259
    ///
260
    /// Lock hierarchy: don't acquire this before dormant
261
    //
262
    // TODO: after or as part of https://gitlab.torproject.org/tpo/core/arti/-/issues/634
263
    // this can be   bridge_desc_mgr: BridgeDescMgr<R>>
264
    // since BridgeDescMgr is Clone and all its methods take `&self` (it has a lock inside)
265
    // Or maybe BridgeDescMgr should not be Clone, since we want to make Weaks of it,
266
    // which we can't do when the Arc is inside.
267
    #[cfg(feature = "bridge-client")]
268
    bridge_desc_mgr: Arc<Mutex<Option<Arc<BridgeDescMgr<R>>>>>,
269
    /// Pluggable transport manager.
270
    #[cfg(feature = "pt-client")]
271
    pt_mgr: Arc<tor_ptmgr::PtMgr<R>>,
272
    /// HS client connector
273
    #[cfg(feature = "onion-service-client")]
274
    hsclient: HsClientConnector<R>,
275
    /// Circuit pool for providing onion services with circuits.
276
    #[cfg(any(feature = "onion-service-client", feature = "onion-service-service"))]
277
    hs_circ_pool: Arc<tor_circmgr::hspool::HsCircPool<R>>,
278
    /// Guard manager
279
    #[cfg_attr(not(feature = "bridge-client"), allow(dead_code))]
280
    guardmgr: GuardMgr<R>,
281
}
282

            
283
/// A Tor client that is not runnable.
284
///
285
/// Can be used to access the state that would be used by a running [`TorClient`].
286
///
287
/// An `InertTorClient` never connects to the network.
288
#[derive(Clone)]
289
pub struct InertTorClient {
290
    /// The key manager.
291
    ///
292
    /// This is used for retrieving private keys, certificates, and other sensitive data (for
293
    /// example, for retrieving the keys necessary for connecting to hidden services that are
294
    /// running in restricted discovery mode).
295
    ///
296
    /// If this crate is compiled _with_ the `keymgr` feature, [`TorClient`] will use a functional
297
    /// key manager implementation.
298
    ///
299
    /// If this crate is compiled _without_ the `keymgr` feature, then [`TorClient`] will use a
300
    /// no-op key manager implementation instead.
301
    ///
302
    /// See the [`KeyMgr`] documentation for more details.
303
    keymgr: Option<Arc<KeyMgr>>,
304
}
305

            
306
impl InertTorClient {
307
    /// Create an `InertTorClient` from a `TorClientConfig`.
308
1524
    pub(crate) fn new(config: &TorClientConfig) -> StdResult<Self, ErrorDetail> {
309
1524
        let keymgr = Self::create_keymgr(config)?;
310

            
311
1524
        Ok(Self { keymgr })
312
1524
    }
313

            
314
    /// Create a [`KeyMgr`] using the specified configuration.
315
    ///
316
    /// Returns `Ok(None)` if keystore use is disabled.
317
1524
    fn create_keymgr(config: &TorClientConfig) -> StdResult<Option<Arc<KeyMgr>>, ErrorDetail> {
318
1524
        let keystore = config.storage.keystore();
319
1524
        let permissions = config.storage.permissions();
320
1524
        let primary_store: Box<dyn Keystore> = match keystore.primary_kind() {
321
            Some(ArtiKeystoreKind::Native) => {
322
1524
                let (state_dir, _mistrust) = config.state_dir()?;
323
1524
                let key_store_dir = state_dir.join("keystore");
324

            
325
1524
                let native_store =
326
1524
                    ArtiNativeKeystore::from_path_and_mistrust(&key_store_dir, permissions)?;
327
                // Should only log fs paths at debug level or lower,
328
                // unless they're part of a diagnostic message.
329
1524
                debug!("Using keystore from {key_store_dir:?}");
330

            
331
1524
                Box::new(native_store)
332
            }
333
            #[cfg(feature = "ephemeral-keystore")]
334
            Some(ArtiKeystoreKind::Ephemeral) => {
335
                // TODO: make the keystore ID somehow configurable
336
                let ephemeral_store: ArtiEphemeralKeystore =
337
                    ArtiEphemeralKeystore::new("ephemeral".to_string());
338
                Box::new(ephemeral_store)
339
            }
340
            None => {
341
                info!("Running without a keystore");
342
                return Ok(None);
343
            }
344
            ty => return Err(internal!("unrecognized keystore type {ty:?}").into()),
345
        };
346

            
347
1524
        let mut builder = KeyMgrBuilder::default().primary_store(primary_store);
348

            
349
        #[cfg(feature = "onion-service-service")]
350
1524
        for config in config.storage.keystore().ctor_svc_stores() {
351
396
            let store: Box<dyn Keystore> = Box::new(CTorServiceKeystore::from_path_and_mistrust(
352
396
                config.path(),
353
396
                permissions,
354
396
                config.id().clone(),
355
                // TODO: these nicknames should be cross-checked with configured
356
                // svc nicknames as part of config validation!!!
357
396
                config.nickname().clone(),
358
            )?);
359

            
360
396
            builder.secondary_stores().push(store);
361
        }
362

            
363
        #[cfg(feature = "onion-service-client")]
364
1524
        for config in config.storage.keystore().ctor_client_stores() {
365
198
            let store: Box<dyn Keystore> = Box::new(CTorClientKeystore::from_path_and_mistrust(
366
198
                config.path(),
367
198
                permissions,
368
198
                config.id().clone(),
369
            )?);
370

            
371
198
            builder.secondary_stores().push(store);
372
        }
373

            
374
1524
        let keymgr = builder
375
1524
            .build()
376
1524
            .map_err(|_| internal!("failed to build keymgr"))?;
377
1524
        Ok(Some(Arc::new(keymgr)))
378
1524
    }
379

            
380
    /// Generate a service discovery keypair for connecting to a hidden service running in
381
    /// "restricted discovery" mode.
382
    ///
383
    /// See [`TorClient::generate_service_discovery_key`].
384
    //
385
    // TODO: decide whether this should use get_or_generate before making it
386
    // non-experimental
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
99
    pub fn generate_service_discovery_key(
401
99
        &self,
402
99
        selector: KeystoreSelector,
403
99
        hsid: HsId,
404
99
    ) -> crate::Result<HsClientDescEncKey> {
405
99
        let mut rng = tor_llcrypto::rng::CautiousRng;
406
99
        let spec = HsClientDescEncKeypairSpecifier::new(hsid);
407
99
        let key = self
408
99
            .keymgr
409
99
            .as_ref()
410
99
            .ok_or(ErrorDetail::KeystoreRequired {
411
99
                action: "generate client service discovery key",
412
99
            })?
413
99
            .generate::<HsClientDescEncKeypair>(
414
99
                &spec, selector, &mut rng, false, /* overwrite */
415
            )?;
416

            
417
99
        Ok(key.public().clone())
418
99
    }
419

            
420
    /// Rotate the service discovery keypair for connecting to a hidden service running in
421
    /// "restricted discovery" mode.
422
    ///
423
    /// See [`TorClient::rotate_service_discovery_key`].
424
    #[cfg(all(
425
        feature = "onion-service-client",
426
        feature = "experimental-api",
427
        feature = "keymgr"
428
    ))]
429
33
    pub fn rotate_service_discovery_key(
430
33
        &self,
431
33
        selector: KeystoreSelector,
432
33
        hsid: HsId,
433
33
    ) -> crate::Result<HsClientDescEncKey> {
434
33
        let mut rng = tor_llcrypto::rng::CautiousRng;
435
33
        let spec = HsClientDescEncKeypairSpecifier::new(hsid);
436
33
        let key = self
437
33
            .keymgr
438
33
            .as_ref()
439
33
            .ok_or(ErrorDetail::KeystoreRequired {
440
33
                action: "rotate client service discovery key",
441
33
            })?
442
33
            .generate::<HsClientDescEncKeypair>(
443
33
                &spec, selector, &mut rng, true, /* overwrite */
444
            )?;
445

            
446
33
        Ok(key.public().clone())
447
33
    }
448

            
449
    /// Insert a service discovery secret key for connecting to a hidden service running in
450
    /// "restricted discovery" mode
451
    ///
452
    /// See [`TorClient::insert_service_discovery_key`].
453
    #[cfg(all(
454
        feature = "onion-service-client",
455
        feature = "experimental-api",
456
        feature = "keymgr"
457
    ))]
458
    #[cfg_attr(
459
        docsrs,
460
        doc(cfg(all(
461
            feature = "onion-service-client",
462
            feature = "experimental-api",
463
            feature = "keymgr"
464
        )))
465
    )]
466
    pub fn insert_service_discovery_key(
467
        &self,
468
        selector: KeystoreSelector,
469
        hsid: HsId,
470
        hs_client_desc_enc_secret_key: HsClientDescEncSecretKey,
471
    ) -> crate::Result<HsClientDescEncKey> {
472
        let spec = HsClientDescEncKeypairSpecifier::new(hsid);
473
        let client_desc_enc_key = HsClientDescEncKey::from(&hs_client_desc_enc_secret_key);
474
        let client_desc_enc_keypair =
475
            HsClientDescEncKeypair::new(client_desc_enc_key.clone(), hs_client_desc_enc_secret_key);
476
        let _key = self
477
            .keymgr
478
            .as_ref()
479
            .ok_or(ErrorDetail::KeystoreRequired {
480
                action: "insert client service discovery key",
481
            })?
482
            .insert::<HsClientDescEncKeypair>(client_desc_enc_keypair, &spec, selector, false)?;
483
        Ok(client_desc_enc_key)
484
    }
485

            
486
    /// Return the service discovery public key for the service with the specified `hsid`.
487
    ///
488
    /// See [`TorClient::get_service_discovery_key`].
489
    #[cfg(all(feature = "onion-service-client", feature = "experimental-api"))]
490
    #[cfg_attr(
491
        docsrs,
492
        doc(cfg(all(feature = "onion-service-client", feature = "experimental-api")))
493
    )]
494
462
    pub fn get_service_discovery_key(
495
462
        &self,
496
462
        hsid: HsId,
497
462
    ) -> crate::Result<Option<HsClientDescEncKey>> {
498
462
        let spec = HsClientDescEncKeypairSpecifier::new(hsid);
499
462
        let key = self
500
462
            .keymgr
501
462
            .as_ref()
502
462
            .ok_or(ErrorDetail::KeystoreRequired {
503
462
                action: "get client service discovery key",
504
462
            })?
505
462
            .get::<HsClientDescEncKeypair>(&spec)?
506
462
            .map(|key| key.public().clone());
507

            
508
462
        Ok(key)
509
462
    }
510

            
511
    /// Removes the service discovery keypair for the service with the specified `hsid`.
512
    ///
513
    /// See [`TorClient::remove_service_discovery_key`].
514
    #[cfg(all(
515
        feature = "onion-service-client",
516
        feature = "experimental-api",
517
        feature = "keymgr"
518
    ))]
519
    #[cfg_attr(
520
        docsrs,
521
        doc(cfg(all(
522
            feature = "onion-service-client",
523
            feature = "experimental-api",
524
            feature = "keymgr"
525
        )))
526
    )]
527
33
    pub fn remove_service_discovery_key(
528
33
        &self,
529
33
        selector: KeystoreSelector,
530
33
        hsid: HsId,
531
33
    ) -> crate::Result<Option<()>> {
532
33
        let spec = HsClientDescEncKeypairSpecifier::new(hsid);
533
33
        let result = self
534
33
            .keymgr
535
33
            .as_ref()
536
33
            .ok_or(ErrorDetail::KeystoreRequired {
537
33
                action: "remove client service discovery key",
538
33
            })?
539
33
            .remove::<HsClientDescEncKeypair>(&spec, selector)?;
540
33
        match result {
541
33
            Some(_) => Ok(Some(())),
542
            None => Ok(None),
543
        }
544
33
    }
545

            
546
    /// Getter for keymgr.
547
    #[cfg(feature = "onion-service-cli-extra")]
548
561
    pub fn keymgr(&self) -> crate::Result<&KeyMgr> {
549
561
        Ok(self.keymgr.as_ref().ok_or(ErrorDetail::KeystoreRequired {
550
561
            action: "get key manager handle",
551
561
        })?)
552
561
    }
553

            
554
    /// Create (but do not launch) a new
555
    /// [`OnionService`](tor_hsservice::OnionService)
556
    /// using the given configuration.
557
    ///
558
    /// See [`TorClient::create_onion_service`].
559
    #[cfg(feature = "onion-service-service")]
560
    #[instrument(skip_all, level = "trace")]
561
264
    pub fn create_onion_service(
562
264
        &self,
563
264
        config: &TorClientConfig,
564
264
        svc_config: tor_hsservice::OnionServiceConfig,
565
264
    ) -> crate::Result<tor_hsservice::OnionService> {
566
264
        let keymgr = self.keymgr.as_ref().ok_or(ErrorDetail::KeystoreRequired {
567
264
            action: "create onion service",
568
264
        })?;
569

            
570
264
        let (state_dir, mistrust) = config.state_dir()?;
571
264
        let state_dir =
572
264
            self::StateDirectory::new(state_dir, mistrust).map_err(ErrorDetail::StateAccess)?;
573

            
574
264
        Ok(tor_hsservice::OnionService::builder()
575
264
            .config(svc_config)
576
264
            .keymgr(keymgr.clone())
577
264
            .state_dir(state_dir)
578
264
            .build()
579
264
            .map_err(ErrorDetail::OnionServiceSetup)?)
580
264
    }
581
}
582

            
583
/// Preferences for whether a [`TorClient`] should bootstrap on its own or not.
584
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
585
#[non_exhaustive]
586
pub enum BootstrapBehavior {
587
    /// Bootstrap the client automatically when requests are made that require the client to be
588
    /// bootstrapped.
589
    #[default]
590
    OnDemand,
591
    /// Make no attempts to automatically bootstrap. [`TorClient::bootstrap`] must be manually
592
    /// invoked in order for the [`TorClient`] to become useful.
593
    ///
594
    /// Attempts to use the client (e.g. by creating connections or resolving hosts over the Tor
595
    /// network) before calling [`bootstrap`](TorClient::bootstrap) will fail, and
596
    /// return an error that has kind [`ErrorKind::BootstrapRequired`](crate::ErrorKind::BootstrapRequired).
597
    Manual,
598
}
599

            
600
/// A representation of whether a [`TorClient`] is allowed to bootstrap, and whether it
601
/// has begun to do so.
602
#[derive(Debug, Clone, Copy)]
603
pub(crate) struct BootstrapSetting {
604
    /// The configured [`BootstrapBehavior`] for the `TorClient`.
605
    behavior: BootstrapBehavior,
606

            
607
    /// If true, we have a [`RunningInner`] in the `TorClient`,
608
    /// indicating that we are trying to bootstrap it.
609
    running_inner_is_present: bool,
610
}
611

            
612
impl Default for BootstrapSetting {
613
171
    fn default() -> Self {
614
171
        Self {
615
171
            behavior: BootstrapBehavior::Manual,
616
171
            running_inner_is_present: false,
617
171
        }
618
171
    }
619
}
620

            
621
impl BootstrapSetting {
622
    /// Return true if this [`BootstrapSetting`]
623
    /// indicates that the client is not trying to bootstrap,
624
    /// and will not try until it is told explicitly to do so.
625
    pub(crate) fn blocked(&self) -> bool {
626
        use BootstrapBehavior::*;
627
        match (self.behavior, self.running_inner_is_present) {
628
            (OnDemand, _) => false,
629
            (Manual, true) => false,
630
            (Manual, false) => true,
631
        }
632
    }
633
}
634

            
635
/// What level of sleep to put a Tor client into.
636
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
637
#[non_exhaustive]
638
pub enum DormantMode {
639
    /// The client functions as normal, and background tasks run periodically.
640
    #[default]
641
    Normal,
642
    /// Background tasks are suspended, conserving CPU usage. Attempts to use the client will
643
    /// wake it back up again.
644
    Soft,
645
}
646

            
647
/// Preferences for how to route a stream over the Tor network.
648
#[derive(Debug, Default, Clone)]
649
pub struct StreamPrefs {
650
    /// What kind of IPv6/IPv4 we'd prefer, and how strongly.
651
    ip_ver_pref: IpVersionPreference,
652
    /// How should we isolate connection(s)?
653
    isolation: StreamIsolationPreference,
654
    /// Whether to return the stream optimistically.
655
    optimistic_stream: bool,
656
    // TODO GEOIP Ideally this would be unconditional, with CountryCode maybe being Void
657
    // This probably applies in many other places, so probably:   git grep 'cfg.*geoip'
658
    // and consider each one with a view to making it unconditional.  Background:
659
    //   https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/1537#note_2935256
660
    //   https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/1537#note_2942214
661
    #[cfg(feature = "geoip")]
662
    /// A country to restrict the exit relay's location to.
663
    country_code: Option<CountryCode>,
664
    /// Whether to try to make connections to onion services.
665
    ///
666
    /// `Auto` means to use the client configuration.
667
    #[cfg(feature = "onion-service-client")]
668
    pub(crate) connect_to_onion_services: BoolOrAuto,
669
    /// Whether to try to make connections to local addresses.
670
    ///
671
    /// `Auto` means to use the client configuration.
672
    pub(crate) connect_to_local_addrs: BoolOrAuto,
673
    /// Whether to accept and return local addresses in anonymously retrieved DNS answers.
674
    ///
675
    /// `Auto` means to use the client configuration.
676
    pub(crate) resolve_local_addrs: BoolOrAuto,
677
}
678

            
679
/// Record of how we are isolating connections
680
#[derive(Debug, Default, Clone)]
681
enum StreamIsolationPreference {
682
    /// No additional isolation
683
    #[default]
684
    None,
685
    /// Isolation parameter to use for connections
686
    Explicit(Box<dyn Isolation>),
687
    /// Isolate every connection!
688
    EveryStream,
689
}
690

            
691
impl From<DormantMode> for tor_chanmgr::Dormancy {
692
    fn from(dormant: DormantMode) -> tor_chanmgr::Dormancy {
693
        match dormant {
694
            DormantMode::Normal => tor_chanmgr::Dormancy::Active,
695
            DormantMode::Soft => tor_chanmgr::Dormancy::Dormant,
696
        }
697
    }
698
}
699
#[cfg(feature = "bridge-client")]
700
impl From<DormantMode> for tor_dirmgr::bridgedesc::Dormancy {
701
    fn from(dormant: DormantMode) -> tor_dirmgr::bridgedesc::Dormancy {
702
        match dormant {
703
            DormantMode::Normal => tor_dirmgr::bridgedesc::Dormancy::Active,
704
            DormantMode::Soft => tor_dirmgr::bridgedesc::Dormancy::Dormant,
705
        }
706
    }
707
}
708

            
709
impl StreamPrefs {
710
    /// Construct a new StreamPrefs.
711
10
    pub fn new() -> Self {
712
10
        Self::default()
713
10
    }
714

            
715
    /// Indicate that a stream may be made over IPv4 or IPv6, but that
716
    /// we'd prefer IPv6.
717
1
    pub fn ipv6_preferred(&mut self) -> &mut Self {
718
1
        self.ip_ver_pref = IpVersionPreference::Ipv6Preferred;
719
1
        self
720
1
    }
721

            
722
    /// Indicate that a stream may only be made over IPv6.
723
    ///
724
    /// When this option is set, we will only pick exit relays that
725
    /// support IPv6, and we will tell them to only give us IPv6
726
    /// connections.
727
1
    pub fn ipv6_only(&mut self) -> &mut Self {
728
1
        self.ip_ver_pref = IpVersionPreference::Ipv6Only;
729
1
        self
730
1
    }
731

            
732
    /// Indicate that a stream may be made over IPv4 or IPv6, but that
733
    /// we'd prefer IPv4.
734
    ///
735
    /// This is the default.
736
1
    pub fn ipv4_preferred(&mut self) -> &mut Self {
737
1
        self.ip_ver_pref = IpVersionPreference::Ipv4Preferred;
738
1
        self
739
1
    }
740

            
741
    /// Indicate that a stream may only be made over IPv4.
742
    ///
743
    /// When this option is set, we will only pick exit relays that
744
    /// support IPv4, and we will tell them to only give us IPv4
745
    /// connections.
746
1
    pub fn ipv4_only(&mut self) -> &mut Self {
747
1
        self.ip_ver_pref = IpVersionPreference::Ipv4Only;
748
1
        self
749
1
    }
750

            
751
    /// Indicate that a stream should appear to come from the given country.
752
    ///
753
    /// When this option is set, we will only pick exit relays that
754
    /// have an IP address that matches the country in our GeoIP database.
755
    #[cfg(feature = "geoip")]
756
    pub fn exit_country(&mut self, country_code: CountryCode) -> &mut Self {
757
        self.country_code = Some(country_code);
758
        self
759
    }
760

            
761
    /// Indicate that we don't care which country a stream appears to come from.
762
    ///
763
    /// This is available even in the case where GeoIP support is compiled out,
764
    /// to make things easier.
765
    pub fn any_exit_country(&mut self) -> &mut Self {
766
        #[cfg(feature = "geoip")]
767
        {
768
            self.country_code = None;
769
        }
770
        self
771
    }
772

            
773
    /// Indicate that the stream should be opened "optimistically".
774
    ///
775
    /// By default, streams are not "optimistic". When you call
776
    /// [`TorClient::connect()`], it won't give you a stream until the
777
    /// exit node has confirmed that it has successfully opened a
778
    /// connection to your target address.  It's safer to wait in this
779
    /// way, but it is slower: it takes an entire round trip to get
780
    /// your confirmation.
781
    ///
782
    /// If a stream _is_ configured to be "optimistic", on the other
783
    /// hand, then `TorClient::connect()` will return the stream
784
    /// immediately, without waiting for an answer from the exit.  You
785
    /// can start sending data on the stream right away, though of
786
    /// course this data will be lost if the connection is not
787
    /// actually successful.
788
1
    pub fn optimistic(&mut self) -> &mut Self {
789
1
        self.optimistic_stream = true;
790
1
        self
791
1
    }
792

            
793
    /// Return true if this stream has been configured as "optimistic".
794
    ///
795
    /// See [`StreamPrefs::optimistic`] for more info.
796
    pub fn is_optimistic(&self) -> bool {
797
        self.optimistic_stream
798
    }
799

            
800
    /// Indicate whether connection to a hidden service (`.onion` service) should be allowed
801
    ///
802
    /// If `Explicit(false)`, attempts to connect to Onion Services will be forced to fail with
803
    /// an error of kind [`InvalidStreamTarget`](crate::ErrorKind::InvalidStreamTarget).
804
    ///
805
    /// If `Explicit(true)`, Onion Service connections are enabled.
806
    ///
807
    /// If `Auto`, the behaviour depends on the `address_filter.allow_onion_addrs`
808
    /// configuration option, which is in turn enabled by default.
809
    #[cfg(feature = "onion-service-client")]
810
21
    pub fn connect_to_onion_services(
811
21
        &mut self,
812
21
        connect_to_onion_services: BoolOrAuto,
813
21
    ) -> &mut Self {
814
21
        self.connect_to_onion_services = connect_to_onion_services;
815
21
        self
816
21
    }
817

            
818
    /// Indicate whether connection to a local address should be allowed
819
    ///
820
    /// If `Explicit(false)`, attempts to connect to local addresses will be forced to fail with
821
    /// an error of kind [`InvalidStreamTarget`](crate::ErrorKind::InvalidStreamTarget).
822
    ///
823
    /// If `Explicit(true)`, connections to local addresses are allowed.
824
    ///
825
    /// If `Auto`, the behaviour depends on the `address_filter.allow_local_addrs`
826
    /// configuration option, which is in turn disabled by default.
827
1
    pub fn connect_to_local_addrs(&mut self, connect_to_local_addrs: BoolOrAuto) -> &mut Self {
828
1
        self.connect_to_local_addrs = connect_to_local_addrs;
829
1
        self
830
1
    }
831

            
832
    /// Indicate whether to accept and return local addresses in anonymously retrieved DNS answers.
833
    ///
834
    /// If `Explicit(false)`, we will filter out the local addresses
835
    /// from anonymously retrieved DNS answers.
836
    ///
837
    /// If `Explicit(true)`, we will not filter out the local addresses
838
    /// from anonymously retrieved DNS answers.
839
    ///
840
    /// If `Auto`, the behaviour depends on the `address_filter.allow_resolving_local_addrs`
841
    /// configuration option, which is in turn disabled by default.
842
    pub fn resolve_local_addrs(&mut self, resolve_local_addrs: BoolOrAuto) -> &mut Self {
843
        self.resolve_local_addrs = resolve_local_addrs;
844
        self
845
    }
846

            
847
    /// Return a TargetPort to describe what kind of exit policy our
848
    /// target circuit needs to support.
849
2
    fn wrap_target_port(&self, port: u16) -> TargetPort {
850
2
        match self.ip_ver_pref {
851
            IpVersionPreference::Ipv6Only => TargetPort::ipv6(port),
852
2
            _ => TargetPort::ipv4(port),
853
        }
854
2
    }
855

            
856
    /// Return a new StreamParameters based on this configuration.
857
2
    fn stream_parameters(&self) -> StreamParameters {
858
2
        let mut params = StreamParameters::default();
859
2
        params
860
2
            .ip_version(self.ip_ver_pref)
861
2
            .optimistic(self.optimistic_stream);
862
2
        params
863
2
    }
864

            
865
    /// Indicate that connections with these preferences should have their own isolation group
866
    ///
867
    /// This is a convenience method which creates a fresh [`IsolationToken`]
868
    /// and sets it for these preferences.
869
    ///
870
    /// This connection preference is orthogonal to isolation established by
871
    /// [`TorClient::isolated_client`].  Connections made with an `isolated_client`
872
    ///  will not share circuits with the original client, even if the same
873
    /// `isolation` is specified via the `ConnectionPrefs` in force.
874
1
    pub fn new_isolation_group(&mut self) -> &mut Self {
875
1
        self.isolation = StreamIsolationPreference::Explicit(Box::new(IsolationToken::new()));
876
1
        self
877
1
    }
878

            
879
    /// Indicate which other connections might use the same circuit
880
    /// as this one.
881
    ///
882
    /// By default all connections made on a `TorClient` may share connections.
883
    /// Connections made with a particular `isolation` may share circuits with each other.
884
    ///
885
    /// This connection preference is orthogonal to isolation established by
886
    /// [`TorClient::isolated_client`].  Connections made with an `isolated_client`
887
    /// will not share circuits with the original client, even if the same
888
    /// `isolation` is specified via the `ConnectionPrefs` in force.
889
1
    pub fn set_isolation<T>(&mut self, isolation: T) -> &mut Self
890
1
    where
891
1
        T: Into<Box<dyn Isolation>>,
892
    {
893
1
        self.isolation = StreamIsolationPreference::Explicit(isolation.into());
894
1
        self
895
1
    }
896

            
897
    /// Indicate that no connection should share a circuit with any other.
898
    ///
899
    /// **Use with care:** This is likely to have poor performance, and imposes a much greater load
900
    /// on the Tor network.  Use this option only to make small numbers of connections each of
901
    /// which needs to be isolated from all other connections.
902
    ///
903
    /// (Don't just use this as a "get more privacy!!" method: the circuits
904
    /// that it put connections on will have no more privacy than any other
905
    /// circuits.  The only benefit is that these circuits will not be shared
906
    /// by multiple streams.)
907
    ///
908
    /// This can be undone by calling `set_isolation` or `new_isolation_group` on these
909
    /// preferences.
910
1
    pub fn isolate_every_stream(&mut self) -> &mut Self {
911
1
        self.isolation = StreamIsolationPreference::EveryStream;
912
1
        self
913
1
    }
914

            
915
    /// Return an [`Isolation`] which separates according to these `StreamPrefs` (only)
916
    ///
917
    /// This describes which connections or operations might use
918
    /// the same circuit(s) as this one.
919
    ///
920
    /// Since this doesn't have access to the `TorClient`,
921
    /// it doesn't separate streams which ought to be separated because of
922
    /// the way their `TorClient`s are isolated.
923
    /// For that, use [`TorClient::isolation`].
924
    fn prefs_isolation(&self) -> Option<Box<dyn Isolation>> {
925
        use StreamIsolationPreference as SIP;
926
        match self.isolation {
927
            SIP::None => None,
928
            SIP::Explicit(ref ig) => Some(ig.clone()),
929
            SIP::EveryStream => Some(Box::new(IsolationToken::new())),
930
        }
931
    }
932

            
933
    // TODO: Add some way to be IPFlexible, and require exit to support both.
934
}
935

            
936
#[cfg(all(
937
    any(feature = "native-tls", feature = "rustls"),
938
    any(feature = "async-std", feature = "tokio")
939
))]
940
impl TorClient<PreferredRuntime> {
941
    /// Bootstrap a connection to the Tor network, using the provided `config`.
942
    ///
943
    /// Returns a client once there is enough directory material to
944
    /// connect safely over the Tor network.
945
    ///
946
    /// Consider using [`TorClient::builder`] for more fine-grained control.
947
    ///
948
    /// # Panics
949
    ///
950
    /// If Tokio is being used (the default), panics if created outside the context of a currently
951
    /// running Tokio runtime. See the documentation for [`PreferredRuntime::current`] for
952
    /// more information.
953
    ///
954
    /// If using `async-std`, either take care to ensure Arti is not compiled with Tokio support,
955
    /// or manually create an `async-std` runtime using [`tor_rtcompat`] and use it with
956
    /// [`TorClient::with_runtime`].
957
    ///
958
    /// # Do not fork
959
    ///
960
    /// The process [**may not fork**](tor_rtcompat#do-not-fork)
961
    /// (except, very carefully, before exec)
962
    /// after calling this function, because it creates a [`PreferredRuntime`].
963
    pub async fn create_bootstrapped(config: TorClientConfig) -> crate::Result<Arc<Self>> {
964
        let runtime = PreferredRuntime::current()
965
            .expect("TorClient could not get an asynchronous runtime; are you running in the right context?");
966

            
967
        Self::with_runtime(runtime)
968
            .config(config)
969
            .create_bootstrapped()
970
            .await
971
    }
972

            
973
    /// Return a new builder for creating TorClient objects.
974
    ///
975
    /// If you want to make a [`TorClient`] synchronously, this is what you want; call
976
    /// `TorClientBuilder::create_unbootstrapped` on the returned builder.
977
    ///
978
    /// # Panics
979
    ///
980
    /// If Tokio is being used (the default), panics if created outside the context of a currently
981
    /// running Tokio runtime. See the documentation for `tokio::runtime::Handle::current` for
982
    /// more information.
983
    ///
984
    /// If using `async-std`, either take care to ensure Arti is not compiled with Tokio support,
985
    /// or manually create an `async-std` runtime using [`tor_rtcompat`] and use it with
986
    /// [`TorClient::with_runtime`].
987
    ///
988
    /// # Do not fork
989
    ///
990
    /// The process [**may not fork**](tor_rtcompat#do-not-fork)
991
    /// (except, very carefully, before exec)
992
    /// after calling this function, because it creates a [`PreferredRuntime`].
993
    pub fn builder() -> TorClientBuilder<PreferredRuntime> {
994
        let runtime = PreferredRuntime::current()
995
            .expect("TorClient could not get an asynchronous runtime; are you running in the right context?");
996

            
997
        TorClientBuilder::new(runtime)
998
    }
999
}
impl<R: Runtime> TorClient<R> {
    /// Return a new builder for creating TorClient objects, with a custom provided [`Runtime`].
    ///
    /// See the [`tor_rtcompat`] crate for more information on custom runtimes.
176
    pub fn with_runtime(runtime: R) -> TorClientBuilder<R> {
176
        TorClientBuilder::new(runtime)
176
    }
    /// Implementation of `create_unbootstrapped`, split out in order to avoid manually specifying
    /// double error conversions.
    #[instrument(skip_all, level = "trace")]
11
    pub(crate) fn create_impl(
11
        runtime: R,
11
        config: &TorClientConfig,
11
        autobootstrap: BootstrapBehavior,
11
        dirmgr_builder: Arc<dyn crate::builder::DirProviderBuilder<R>>,
11
        dirmgr_extensions: tor_dirmgr::config::DirMgrExtensions,
11
    ) -> StdResult<Arc<Self>, ErrorDetail> {
11
        if crate::util::running_as_setuid() {
            return Err(tor_error::bad_api_usage!(
                "Arti does not support running in a setuid or setgid context."
            )
            .into());
11
        }
11
        let memquota = MemoryQuotaTracker::new(&runtime, config.system.memory.clone())?;
11
        let path_resolver = Arc::new(config.path_resolver.clone());
11
        let (state_dir, mistrust) = config.state_dir()?;
        #[cfg(feature = "onion-service-service")]
11
        let state_directory =
11
            StateDirectory::new(&state_dir, mistrust).map_err(ErrorDetail::StateAccess)?;
11
        let dormant = DormantMode::Normal;
11
        let statemgr = Self::statemgr_from_config(config)?;
        // Try to take state ownership early, so we'll know if we have it.
        // Note that this `try_lock()` may return `Ok` even if we can't acquire the lock.
        // (At this point we don't yet care if we have it.)
11
        let _ignore_status = statemgr.try_lock().map_err(ErrorDetail::StateMgrSetup)?;
11
        let addr_cfg = config.address_filter.clone();
11
        let bootstrap_setting = BootstrapSetting {
11
            behavior: autobootstrap,
11
            running_inner_is_present: false,
11
        };
11
        let (bootstrap_setting_sender, bootstrap_setting_receiver) =
11
            postage::watch::channel_with(bootstrap_setting);
11
        let bootstrap_setting_sender = Mutex::new(bootstrap_setting_sender);
11
        let (status_sender, status_receiver) =
11
            postage::watch::channel_with(BootstrapStatus::from_setting(bootstrap_setting));
11
        let status_receiver = status::BootstrapEvents {
11
            inner: status_receiver,
11
        };
11
        let timeout_cfg = config.stream_timeouts.clone();
11
        let (dormant_send, dormant_recv) = postage::watch::channel_with(Some(dormant));
11
        let dormant_send = DropNotifyWatchSender::new(dormant_send);
11
        let client_isolation = IsolationToken::new();
11
        let inert_client = InertTorClient::new(config)?;
11
        let dirmgr_store = DirMgrStore::new(&config.dir_mgr_config()?, runtime.clone(), false)
11
            .map_err(ErrorDetail::DirMgrSetup)?;
11
        let inner = Box::new(NotConstructedInner {
11
            config: config.clone(),
11
            dormant_recv,
11
            status_sender,
11
            bootstrap_setting_receiver,
11
            dirmgr_builder,
11
            dirmgr_extensions,
11
        });
11
        let inner = Mutex::new(Inner::NotConstructed(inner));
11
        let client = Arc::new(ClientShared {
11
            runtime,
11
            inner,
11
            memquota,
11
            inert_client,
11
            statemgr,
11
            dirmgr_store,
11
            addrcfg: addr_cfg.into(),
11
            timeoutcfg: timeout_cfg.into(),
11
            reconfigure_lock: Arc::new(Mutex::new(())),
11
            status_receiver,
11
            bootstrap_in_progress: AsyncMutex::new(()),
11
            bootstrap_setting_sender,
11
            should_bootstrap: autobootstrap,
11
            dormant: Mutex::new(dormant_send),
11
            #[cfg(feature = "onion-service-service")]
11
            state_directory,
11
            path_resolver,
11
        });
11
        Ok(Arc::new(TorClient {
11
            client_isolation,
11
            connect_prefs: Default::default(),
11
            client,
11
        }))
11
    }
    /// Construct a state manager from the client configuration.
11
    fn statemgr_from_config(config: &TorClientConfig) -> Result<UsingStateMgr, ErrorDetail> {
        #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
        {
            use tor_persist::FsStateMgr;
11
            let (state_dir, mistrust) = config.state_dir()?;
11
            FsStateMgr::from_path_and_mistrust(state_dir, mistrust)
11
                .map_err(ErrorDetail::StateMgrSetup)
        }
        #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
        {
            unimplemented!()
        }
11
    }
    /// Bootstrap a connection to the Tor network, with a client created by `create_unbootstrapped`.
    ///
    /// Returns once there is enough directory material to connect safely over the Tor network.
    /// If the client has already been bootstrapped, returns immediately with
    /// success. If a bootstrap is in progress, waits for it to finish, then retries it if it
    /// failed (returning success if it succeeded).
    ///
    /// Bootstrap progress can be tracked by listening to the event receiver returned by
    /// [`bootstrap_events`](TorClient::bootstrap_events).
    ///
    /// # Failures
    ///
    /// If the bootstrapping process fails, returns an error. This function can safely be called
    /// again later to attempt to bootstrap another time.
    #[instrument(skip_all, level = "trace")]
    pub async fn bootstrap(&self) -> crate::Result<()> {
        self.client
            .bootstrap_inner()
            .await
            .map_err(ErrorDetail::into)
    }
}
impl<R: Runtime> NotConstructedInner<R> {
    /// Replace the configuration for this unconstructed client.
    ///
    /// Since most of the client's internals are not yet constructed,
    /// we can still replace nearly all of the items.
4
    fn reconfigure(
4
        &mut self,
4
        new_config: &TorClientConfig,
4
        how: tor_config::Reconfigure,
4
    ) -> StdResult<(), ErrorDetail> {
        // We _do_ have to check the cache_dir, since we can't and won't change that
        // while we're running.
        // (We already checked the state_dir in ClientShared::reconfigure_inner.)
4
        if new_config.storage.cache_dir != self.config.storage.cache_dir {
            how.cannot_change("storage.cache_dir")?;
4
        }
4
        if how == tor_config::Reconfigure::CheckAllOrNothing {
2
            return Ok(());
2
        }
2
        self.config = new_config.clone();
2
        Ok(())
4
    }
}
impl<R: Runtime> RunningInner<R> {
    /// Construct a new [`RunningInner`] and launch its associated tasks.
    fn new(
        pending: NotConstructedInner<R>,
        client: &ClientShared<R>,
    ) -> StdResult<Arc<Self>, ErrorDetail> {
        let NotConstructedInner {
            config,
            dormant_recv,
            status_sender,
            bootstrap_setting_receiver,
            dirmgr_builder,
            dirmgr_extensions,
        } = pending;
        let runtime = client.runtime.clone();
        let dormant = dormant_recv
            .borrow()
            .expect("Client somehow dropped while creating RunningInner");
        let memquota = &client.memquota;
        let statemgr = &client.statemgr;
        let path_resolver = &client.path_resolver;
        let (state_dir, _) = config.state_dir()?;
        let chanmgr = Arc::new(
            tor_chanmgr::ChanMgr::new(
                runtime.clone(),
                ChanMgrConfig::new(config.channel.clone()),
                dormant.into(),
                &NetParameters::from_map(&config.override_net_params),
                memquota.clone(),
            )
            .map_err(ErrorDetail::ChanMgrSetup)?,
        );
        let guardmgr = tor_guardmgr::GuardMgr::new(runtime.clone(), statemgr.clone(), &config)
            .map_err(ErrorDetail::GuardMgrSetup)?;
        #[cfg(feature = "pt-client")]
        let pt_mgr = {
            let pt_state_dir = state_dir.as_path().join("pt_state");
            config.storage.permissions().make_directory(&pt_state_dir)?;
            let mgr = Arc::new(tor_ptmgr::PtMgr::new(
                config.bridges.transports.clone(),
                pt_state_dir,
                Arc::clone(path_resolver),
                config.channel.outbound_proxy().cloned(),
                runtime.clone(),
            )?);
            chanmgr.set_pt_mgr(mgr.clone());
            mgr
        };
        let circmgr = Arc::new(
            tor_circmgr::CircMgr::new(
                &config,
                statemgr.clone(),
                &runtime,
                Arc::clone(&chanmgr),
                &guardmgr,
            )
            .map_err(ErrorDetail::CircMgrSetup)?,
        );
        let dir_cfg = {
            let mut c: tor_dirmgr::DirMgrConfig = config.dir_mgr_config()?;
            c.extensions = dirmgr_extensions;
            c
        };
        let dirmgr = dirmgr_builder
            .build(
                runtime.clone(),
                client.dirmgr_store.clone(),
                Arc::clone(&circmgr),
                dir_cfg,
            )
            .map_err(crate::Error::into_detail)?;
        let mut periodic_task_handles = circmgr
            .launch_background_tasks(&runtime, &dirmgr, statemgr.clone())
            .map_err(ErrorDetail::CircMgrSetup)?;
        periodic_task_handles.extend(dirmgr.download_task_handle());
        periodic_task_handles.extend(
            chanmgr
                .launch_background_tasks(&runtime, dirmgr.clone().upcast_arc())
                .map_err(ErrorDetail::ChanMgrSetup)?,
        );
        #[cfg(feature = "bridge-client")]
        // TODO: We can just construct this.
        let bridge_desc_mgr = Arc::new(Mutex::new(None));
        #[cfg(any(feature = "onion-service-client", feature = "onion-service-service"))]
        let hs_circ_pool = {
            let circpool = Arc::new(tor_circmgr::hspool::HsCircPool::new(&circmgr));
            circpool
                .launch_background_tasks(&runtime, &dirmgr.clone().upcast_arc())
                .map_err(ErrorDetail::CircMgrSetup)?;
            circpool
        };
        #[cfg(feature = "onion-service-client")]
        let hsclient = {
            // Prompt the hs connector to do its data housekeeping when we get a new consensus.
            // That's a time we're doing a bunch of thinking anyway, and it's not very frequent.
            let housekeeping = dirmgr.events().filter_map(|event| async move {
                match event {
                    DirEvent::NewConsensus => Some(()),
                    _ => None,
                }
            });
            let housekeeping = Box::pin(housekeeping);
            HsClientConnector::new(runtime.clone(), hs_circ_pool.clone(), &config, housekeeping)?
        };
        let conn_status = chanmgr.bootstrap_events();
        let dir_status = dirmgr.bootstrap_events();
        let skew_status = circmgr.skew_events();
        let rtclone = runtime.clone();
        // TODO: It might be a good idea to check this earlier, in `create_impl`,
        // when we have only the DirMgrStore.
        // But if we do that we need to add a method to DirMgrStore
        // to look at the protocol recommentations.
        #[allow(clippy::print_stderr)]
        crate::protostatus::enforce_protocol_recommendations(
            &runtime,
            Arc::clone(&dirmgr),
            crate::software_release_date(),
            crate::supported_protocols(),
            // TODO #1932: It would be nice to have a cleaner shutdown mechanism here,
            // but that will take some work.
            |fatal| async move {
                use tor_error::ErrorReport as _;
                // We already logged this error, but let's tell stderr too.
                eprintln!(
                    "Shutting down because of unsupported software version.\nError was:\n{}",
                    fatal.report(),
                );
                if let Some(hint) = crate::err::Error::from(fatal).hint() {
                    eprintln!("{}", hint);
                }
                // Give the tracing module a while to flush everything, since it has no built-in
                // flush function.
                rtclone.sleep(std::time::Duration::new(5, 0)).await;
                std::process::exit(1);
            },
        )?;
        runtime
            .spawn(status::report_status(
                status_sender,
                conn_status,
                dir_status,
                skew_status,
                bootstrap_setting_receiver,
            ))
            .map_err(|e| ErrorDetail::from_spawn("top-level status reporter", e))?;
        runtime
            .spawn(tasks_monitor_dormant(
                dormant_recv.clone(),
                dirmgr.clone().upcast_arc(),
                chanmgr.clone(),
                #[cfg(feature = "bridge-client")]
                bridge_desc_mgr.clone(),
                periodic_task_handles,
            ))
            .map_err(|e| ErrorDetail::from_spawn("periodic task dormant monitor", e))?;
        let running_inner = Arc::new(RunningInner {
            chanmgr,
            circmgr,
            dirmgr,
            #[cfg(feature = "bridge-client")]
            bridge_desc_mgr,
            #[cfg(feature = "pt-client")]
            pt_mgr,
            #[cfg(feature = "onion-service-client")]
            hsclient,
            #[cfg(any(feature = "onion-service-client", feature = "onion-service-service"))]
            hs_circ_pool,
            guardmgr,
        });
        Ok(running_inner)
    }
    /// Tell the parts of this [`RunningInner`] to reconfigure themselves
    /// (or to check the new configuration, if `how == CheckAllOrNothing`).
    fn reconfigure(
        &self,
        new_config: &TorClientConfig,
        how: tor_config::Reconfigure,
    ) -> crate::Result<()> {
        let dir_cfg = new_config.dir_mgr_config().map_err(wrap_err)?;
        let retire_circuits = self
            .circmgr
            .reconfigure(new_config, how)
            .map_err(wrap_err)?;
        #[cfg(any(feature = "onion-service-client", feature = "onion-service-service"))]
        if retire_circuits != RetireCircuits::None {
            self.hs_circ_pool.retire_all_circuits().map_err(wrap_err)?;
        }
        self.dirmgr.reconfigure(&dir_cfg, how).map_err(wrap_err)?;
        let netparams = self.dirmgr.params();
        self.chanmgr
            .reconfigure(&new_config.channel, how, netparams)
            .map_err(wrap_err)?;
        #[cfg(feature = "pt-client")]
        self.pt_mgr
            .reconfigure(
                how,
                new_config.bridges.transports.clone(),
                new_config.channel.outbound_proxy().cloned(),
            )
            .map_err(wrap_err)?;
        Ok(())
    }
}
impl<R: Runtime> TorClient<R> {
    /// Change the configuration of this TorClient to `new_config`.
    ///
    /// The `how` describes whether to perform an all-or-nothing
    /// reconfiguration: either all of the configuration changes will be
    /// applied, or none will. If you have disabled all-or-nothing changes, then
    /// only fatal errors will be reported in this function's return value.
    ///
    /// When performing a reconfiguration,
    /// a returned error may indicate that the client is now in an inconsistent state.
    ///
    /// This function applies its changes to **all** TorClient instances derived
    /// from the same call to `TorClient::create_*`: even ones whose circuits
    /// are isolated from this handle.
    ///
    /// # Limitations
    ///
    /// Although most options are reconfigurable, there are some whose values
    /// can't be changed on an a running TorClient.  Those options (or their
    /// sections) are explicitly documented not to be changeable.
    /// NOTE: Currently, not all of these non-reconfigurable options are
    /// documented. See [arti#1721][arti-1721].
    ///
    /// [arti-1721]: https://gitlab.torproject.org/tpo/core/arti/-/issues/1721
    ///
    /// Changing some options do not take effect immediately on all open streams
    /// and circuits, but rather affect only future streams and circuits.  Those
    /// are also explicitly documented.
    #[instrument(skip_all, level = "trace")]
2
    pub fn reconfigure(
2
        &self,
2
        new_config: &TorClientConfig,
2
        how: tor_config::Reconfigure,
2
    ) -> crate::Result<()> {
        // We need to hold this lock while we're reconfiguring the client: even
        // though the individual fields have their own synchronization, we can't
        // safely let two threads change them at once.  If we did, then we'd
        // introduce time-of-check/time-of-use bugs in checking our configuration,
        // deciding how to change it, then applying the changes.
2
        let guard = self.client.reconfigure_lock.lock().expect("Poisoned lock");
        use tor_config::Reconfigure::*;
2
        match how {
            AllOrNothing => {
                // We have to check before we make any changes.
2
                self.client
2
                    .reconfigure_inner(new_config, CheckAllOrNothing, &guard)?;
                // Hopefully this doesn't fail,
                // otherwise we may have returned early from the reconfiguration
                // and its no longer "all-or-nothing".
2
                let result = self
2
                    .client
2
                    .reconfigure_inner(new_config, AllOrNothing, &guard);
2
                if result.is_err() {
                    warn!(
                        "Attempted an \"all-or-nothing\" reconfigure, but unexpectedly failed. \
                        The client will continue to run in an inconsistent state."
                    );
2
                }
2
                result
            }
            WarnOnFailures => {
                let result = self.client.reconfigure_inner(new_config, how, &guard);
                // If there's a fatal error,
                // we may have reconfigured some components and not others.
                if result.is_err() {
                    warn!(
                        "Attempted a reconfigure, but failed. \
                        The client will continue to run in an inconsistent state."
                    );
                }
                result
            }
            CheckAllOrNothing => self.client.reconfigure_inner(new_config, how, &guard),
            _ => self.client.reconfigure_inner(new_config, how, &guard),
        }
2
    }
    /// Return a new isolated `TorClient` handle.
    ///
    /// The two `TorClient`s will share internal state and configuration, but
    /// their streams will never share circuits with one another.
    ///
    /// Use this function when you want separate parts of your program to
    /// each have a TorClient handle, but where you don't want their
    /// activities to be linkable to one another over the Tor network.
    ///
    /// Calling this function is usually preferable to creating a
    /// completely separate TorClient instance, since it can share its
    /// internals with the existing `TorClient`.
    #[must_use]
1
    pub fn isolated_client(&self) -> Arc<TorClient<R>> {
1
        let result = TorClient {
1
            client_isolation: IsolationToken::new(),
1
            connect_prefs: self.connect_prefs.clone(),
1
            client: Arc::clone(&self.client),
1
        };
1
        Arc::new(result)
1
    }
    /// Launch an anonymized connection to the provided address and port over
    /// the Tor network.
    ///
    /// Note that because Tor prefers to do DNS resolution on the remote side of
    /// the network, this function takes its address as a string:
    ///
    /// ```no_run
    /// # use arti_client::*;use tor_rtcompat::Runtime;
    /// # async fn ex<R:Runtime>(tor_client: TorClient<R>) -> Result<()> {
    /// // The most usual way to connect is via an address-port tuple.
    /// let socket = tor_client.connect(("www.example.com", 443)).await?;
    ///
    /// // You can also specify an address and port as a colon-separated string.
    /// let socket = tor_client.connect("www.example.com:443").await?;
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// Hostnames are _strongly_ preferred here: if this function allowed the
    /// caller here to provide an IPAddr or [`IpAddr`] or
    /// [`SocketAddr`](std::net::SocketAddr) address, then
    ///
    /// ```no_run
    /// # use arti_client::*; use tor_rtcompat::Runtime;
    /// # async fn ex<R:Runtime>(tor_client: TorClient<R>) -> Result<()> {
    /// # use std::net::ToSocketAddrs;
    /// // BAD: We're about to leak our target address to the local resolver!
    /// let address = "www.example.com:443".to_socket_addrs().unwrap().next().unwrap();
    /// // 🤯 Oh no! Now any eavesdropper can tell where we're about to connect! 🤯
    ///
    /// // Fortunately, this won't compile, since SocketAddr doesn't implement IntoTorAddr.
    /// // let socket = tor_client.connect(address).await?;
    /// //                                 ^^^^^^^ the trait `IntoTorAddr` is not implemented for `std::net::SocketAddr`
    /// # Ok(())
    /// # }
    /// ```
    ///
    /// If you really do need to connect to an IP address rather than a
    /// hostname, and if you're **sure** that the IP address came from a safe
    /// location, there are a few ways to do so.
    ///
    /// ```no_run
    /// # use arti_client::{TorClient,Result};use tor_rtcompat::Runtime;
    /// # use std::net::{SocketAddr,IpAddr};
    /// # async fn ex<R:Runtime>(tor_client: TorClient<R>) -> Result<()> {
    /// # use std::net::ToSocketAddrs;
    /// // ⚠️This is risky code!⚠️
    /// // (Make sure your addresses came from somewhere safe...)
    ///
    /// // If we have a fixed address, we can just provide it as a string.
    /// let socket = tor_client.connect("192.0.2.22:443").await?;
    /// let socket = tor_client.connect(("192.0.2.22", 443)).await?;
    ///
    /// // If we have a SocketAddr or an IpAddr, we can use the
    /// // DangerouslyIntoTorAddr trait.
    /// use arti_client::DangerouslyIntoTorAddr;
    /// let sockaddr = SocketAddr::from(([192, 0, 2, 22], 443));
    /// let ipaddr = IpAddr::from([192, 0, 2, 22]);
    /// let socket = tor_client.connect(sockaddr.into_tor_addr_dangerously().unwrap()).await?;
    /// let socket = tor_client.connect((ipaddr, 443).into_tor_addr_dangerously().unwrap()).await?;
    /// # Ok(())
    /// # }
    /// ```
    #[instrument(skip_all, level = "trace")]
2
    pub async fn connect<A: IntoTorAddr>(&self, target: A) -> crate::Result<DataStream> {
        self.connect_with_prefs(target, &self.connect_prefs).await
2
    }
    /// Launch an anonymized connection to the provided address and
    /// port over the Tor network, with explicit connection preferences.
    ///
    /// Note that because Tor prefers to do DNS resolution on the remote
    /// side of the network, this function takes its address as a string.
    /// (See [`TorClient::connect()`] for more information.)
    #[instrument(skip_all, level = "trace")]
2
    pub async fn connect_with_prefs<A: IntoTorAddr>(
2
        &self,
2
        target: A,
2
        prefs: &StreamPrefs,
2
    ) -> crate::Result<DataStream> {
        let addr = target.into_tor_addr().map_err(wrap_err)?;
        let mut stream_parameters = prefs.stream_parameters();
        // This macro helps prevent code duplication in the match below.
        //
        // Ideally, the match should resolve to a tuple consisting of the
        // tunnel, and the address, port and stream params,
        // but that's not currently possible because
        // the Exit and Hs branches use different tunnel types.
        //
        // TODO: replace with an async closure (when our MSRV allows it),
        // or with a more elegant approach.
        macro_rules! begin_stream {
            ($tunnel:expr, $addr:expr, $port:expr, $stream_params:expr) => {{
                let fut = $tunnel.begin_stream($addr, $port, $stream_params);
                self.client
                    .runtime
                    .timeout(self.client.timeoutcfg.get().connect_timeout, fut)
                    .await
                    .map_err(|_| ErrorDetail::ExitTimeout)?
                    .map_err(|cause| ErrorDetail::StreamFailed {
                        cause,
                        kind: "data",
                    })
            }};
        }
        let stream = match addr.into_stream_instructions(&self.client.addrcfg.get(), prefs)? {
            StreamInstructions::Exit {
                hostname: addr,
                port,
            } => {
                let exit_ports = [prefs.wrap_target_port(port)];
                let tunnel = self
                    .get_or_launch_exit_tunnel(&exit_ports, prefs)
                    .await
                    .map_err(wrap_err)?;
                debug!(
                    tunnel_id = %tunnel.unique_id(),
                    "Got a circuit for {}:{}", sensitive(&addr), port);
                begin_stream!(tunnel, &addr, port, Some(stream_parameters))
            }
            #[cfg(not(feature = "onion-service-client"))]
            #[allow(unused_variables)] // for hostname and port
            StreamInstructions::Hs {
                hsid,
                hostname,
                port,
            } => void::unreachable(hsid.0),
            #[cfg(feature = "onion-service-client")]
            StreamInstructions::Hs {
                hsid,
                hostname,
                port,
            } => {
                use safelog::DisplayRedacted as _;
                let running = self
                    .client
                    .wait_for_bootstrap_running("connect to hidden service")
                    .await?;
                let netdir = self.netdir(Timeliness::Timely, "connect to a hidden service")?;
                let mut hs_client_secret_keys_builder = HsClientSecretKeysBuilder::default();
                if let Some(keymgr) = &self.client.inert_client.keymgr {
                    let desc_enc_key_spec = HsClientDescEncKeypairSpecifier::new(hsid);
                    let ks_hsc_desc_enc =
                        keymgr.get::<HsClientDescEncKeypair>(&desc_enc_key_spec)?;
                    if let Some(ks_hsc_desc_enc) = ks_hsc_desc_enc {
                        debug!(
                            "Found descriptor decryption key for {}",
                            hsid.display_redacted()
                        );
                        hs_client_secret_keys_builder.ks_hsc_desc_enc(ks_hsc_desc_enc);
                    }
                };
                let hs_client_secret_keys = hs_client_secret_keys_builder
                    .build()
                    .map_err(ErrorDetail::Configuration)?;
                let tunnel = running
                    .hsclient
                    .get_or_launch_tunnel(
                        &netdir,
                        hsid,
                        hs_client_secret_keys,
                        self.isolation(prefs),
                    )
                    .await
                    .map_err(|cause| ErrorDetail::ObtainHsCircuit { cause, hsid })?;
                // On connections to onion services, we have to suppress
                // everything except the port from the BEGIN message.  We also
                // disable optimistic data.
                stream_parameters
                    .suppress_hostname()
                    .suppress_begin_flags()
                    .optimistic(false);
                begin_stream!(tunnel, &hostname, port, Some(stream_parameters))
            }
        };
        Ok(stream?)
2
    }
    /// Provides a new handle on this client, but with adjusted default preferences.
    ///
    /// Connections made with e.g. [`connect`](TorClient::connect) on the returned handle will use
    /// `connect_prefs`.
    #[must_use]
    pub fn with_prefs(&self, connect_prefs: StreamPrefs) -> Arc<Self> {
        let result = TorClient {
            client_isolation: self.client_isolation,
            connect_prefs,
            client: Arc::clone(&self.client),
        };
        Arc::new(result)
    }
    /// On success, return a list of IP addresses.
    #[instrument(skip_all, level = "trace")]
    pub async fn resolve(&self, hostname: &str) -> crate::Result<Vec<IpAddr>> {
        self.resolve_with_prefs(hostname, &self.connect_prefs).await
    }
    /// On success, return a list of IP addresses, but use prefs.
    #[instrument(skip_all, level = "trace")]
    pub async fn resolve_with_prefs(
        &self,
        hostname: &str,
        prefs: &StreamPrefs,
    ) -> crate::Result<Vec<IpAddr>> {
        // TODO This dummy port is only because `address::Host` is not pub(crate),
        // but I see no reason why it shouldn't be?  Then `into_resolve_instructions`
        // should be a method on `Host`, not `TorAddr`.  -Diziet.
        let addr = (hostname, 1).into_tor_addr().map_err(wrap_err)?;
        let addrcfg = self.client.addrcfg.get();
        let mut addrs = match addr.into_resolve_instructions(&addrcfg, prefs)? {
            ResolveInstructions::Exit(hostname) => {
                let circ = self.get_or_launch_exit_tunnel(&[], prefs).await?;
                let resolve_future = circ.resolve(&hostname);
                self.client
                    .runtime
                    .timeout(self.client.timeoutcfg.get().resolve_timeout, resolve_future)
                    .await
                    .map_err(|_| ErrorDetail::ExitTimeout)?
                    .map_err(|cause| ErrorDetail::StreamFailed {
                        cause,
                        kind: "DNS lookup",
                    })?
            }
            ResolveInstructions::Return(addrs) => addrs,
        };
        let allow_resolving_local_addrs = prefs
            .resolve_local_addrs
            .as_bool()
            .unwrap_or(addrcfg.allow_resolving_local_addrs);
        if !allow_resolving_local_addrs {
            addrs.retain(|addr| {
                let keep = crate::address::is_globally_reachable_unicast(*addr);
                if !keep {
                    debug!("Dropping non-routable address {addr} from RESOLVED answer");
                }
                keep
            });
            if addrs.is_empty() {
                debug!("Got RESOLVED containing only non-routable addresses");
                return Err(ErrorDetail::NoRoutableAddress.into());
            }
        }
        Ok(addrs)
    }
    /// Perform a remote DNS reverse lookup with the provided IP address.
    ///
    /// On success, return a list of hostnames.
    #[instrument(skip_all, level = "trace")]
    pub async fn resolve_ptr(&self, addr: IpAddr) -> crate::Result<Vec<String>> {
        self.resolve_ptr_with_prefs(addr, &self.connect_prefs).await
    }
    /// Perform a remote DNS reverse lookup with the provided IP address.
    ///
    /// On success, return a list of hostnames.
    #[instrument(level = "trace", skip_all)]
    pub async fn resolve_ptr_with_prefs(
        &self,
        addr: IpAddr,
        prefs: &StreamPrefs,
    ) -> crate::Result<Vec<String>> {
        let addrcfg = self.client.addrcfg.get();
        let allow_resolving_local_addrs = prefs
            .resolve_local_addrs
            .as_bool()
            .unwrap_or(addrcfg.allow_resolving_local_addrs);
        if !allow_resolving_local_addrs && !crate::address::is_globally_reachable_unicast(addr) {
            debug!("Rejecting reverse lookup request for non-routable address {addr}");
            // Note: this is not 100% accurate, but I'm not sure if it makes sense
            // to introuce another ErrorDetail just for this
            return Err(ErrorDetail::NoRoutableAddress.into());
        }
        let circ = self.get_or_launch_exit_tunnel(&[], prefs).await?;
        let resolve_ptr_future = circ.resolve_ptr(addr);
        let hostnames = self
            .client
            .runtime
            .timeout(
                self.client.timeoutcfg.get().resolve_ptr_timeout,
                resolve_ptr_future,
            )
            .await
            .map_err(|_| ErrorDetail::ExitTimeout)?
            .map_err(|cause| ErrorDetail::StreamFailed {
                cause,
                kind: "reverse DNS lookup",
            })?;
        Ok(hostnames)
    }
    /// Return a reference to this client's directory manager.
    ///
    /// This function is unstable. It is only enabled if the crate was
    /// built with the `experimental-api` feature.
    #[cfg(feature = "experimental-api")]
    pub fn dirmgr(&self) -> crate::Result<Arc<dyn tor_dirmgr::DirProvider>> {
        Ok(self
            .client
            .running_inner("access internal functionality")?
            .dirmgr
            .clone())
    }
    /// Return a reference to this client's circuit manager.
    ///
    /// This function is unstable. It is only enabled if the crate was
    /// built with the `experimental-api` feature.
    #[cfg(feature = "experimental-api")]
    pub fn circmgr(&self) -> crate::Result<Arc<tor_circmgr::CircMgr<R>>> {
        Ok(self
            .client
            .running_inner("access internal functionality")?
            .circmgr
            .clone())
    }
    /// Return a reference to this client's channel manager.
    ///
    /// This function is unstable. It is only enabled if the crate was
    /// built with the `experimental-api` feature.
    #[cfg(feature = "experimental-api")]
    pub fn chanmgr(&self) -> crate::Result<Arc<tor_chanmgr::ChanMgr<R>>> {
        Ok(self
            .client
            .running_inner("access internal functionality")?
            .chanmgr
            .clone())
    }
    /// Return a reference to this client's circuit pool.
    ///
    /// This function is unstable. It is only enabled if the crate was
    /// built with the `experimental-api` feature and any of `onion-service-client`
    /// or `onion-service-service` features. This method is required to invoke
    /// tor_hsservice::OnionService::launch()
    #[cfg(all(
        feature = "experimental-api",
        any(feature = "onion-service-client", feature = "onion-service-service")
    ))]
    pub fn hs_circ_pool(&self) -> crate::Result<Arc<tor_circmgr::hspool::HsCircPool<R>>> {
        Ok(self
            .client
            .running_inner("access internal functionality")?
            .hs_circ_pool
            .clone())
    }
    /// Return a reference to the runtime being used by this client.
    //
    // This API is not a hostage to fortune since we already require that R: Clone,
    // and necessarily a TorClient must have a clone of it.
    //
    // We provide it simply to save callers who have a TorClient from
    // having to separately keep their own handle,
2
    pub fn runtime(&self) -> &R {
2
        &self.client.runtime
2
    }
    /// Return a netdir that is timely according to the rules of `timeliness`.
    ///
    /// The `action` string is a description of what we wanted to do with the
    /// directory, to be put into the error message if we couldn't find a directory.
    fn netdir(
        &self,
        timeliness: Timeliness,
        action: &'static str,
    ) -> StdResult<Arc<tor_netdir::NetDir>, ErrorDetail> {
        use tor_netdir::Error as E;
        // TODO: Conceivably we could take a NetDir from our DirMgrStore.
        match self.client.running_inner(action)?.dirmgr.netdir(timeliness) {
            Ok(netdir) => Ok(netdir),
            Err(E::NoInfo) | Err(E::NotEnoughInfo) => {
                Err(ErrorDetail::BootstrapRequired { action })
            }
            Err(error) => Err(ErrorDetail::NoDir { error, action }),
        }
    }
    /// Get or launch an exit-suitable circuit with a given set of
    /// exit ports.
    #[instrument(skip_all, level = "trace")]
2
    async fn get_or_launch_exit_tunnel(
2
        &self,
2
        exit_ports: &[TargetPort],
2
        prefs: &StreamPrefs,
2
    ) -> StdResult<ClientDataTunnel, ErrorDetail> {
        let running = self
            .client
            .wait_for_bootstrap_running("build a circuit")
            .await?;
        // TODO HS probably this netdir ought to be made in connect_with_prefs
        // like for StreamInstructions::Hs.
        let dir = self.netdir(Timeliness::Timely, "build a circuit")?;
        let tunnel = running
            .circmgr
            .get_or_launch_exit(
                dir.as_ref().into(),
                exit_ports,
                self.isolation(prefs),
                #[cfg(feature = "geoip")]
                prefs.country_code,
            )
            .await
            .map_err(|cause| ErrorDetail::ObtainExitCircuit {
                cause,
                exit_ports: Sensitive::new(exit_ports.into()),
            })?;
        drop(dir); // This decreases the refcount on the netdir.
        Ok(tunnel)
2
    }
    /// Return an overall [`Isolation`] for this `TorClient` and a `StreamPrefs`.
    ///
    /// This describes which operations might use
    /// circuit(s) with this one.
    ///
    /// This combines isolation information from
    /// [`StreamPrefs::prefs_isolation`]
    /// and the `TorClient`'s isolation (eg from [`TorClient::isolated_client`]).
    fn isolation(&self, prefs: &StreamPrefs) -> StreamIsolation {
        let mut b = StreamIsolationBuilder::new();
        // Always consider our client_isolation.
        b.owner_token(self.client_isolation);
        // Consider stream isolation too, if it's set.
        if let Some(tok) = prefs.prefs_isolation() {
            b.stream_isolation(tok);
        }
        // Failure should be impossible with this builder.
        b.build().expect("Failed to construct StreamIsolation")
    }
    /// Try to launch an onion service with a given configuration.
    ///
    /// Returns `Ok(None)` if the service specified is disabled in the config.
    ///
    /// This onion service will not actually handle any requests on its own: you
    /// will need to
    /// pull [`RendRequest`](tor_hsservice::RendRequest) objects from the returned stream,
    /// [`accept`](tor_hsservice::RendRequest::accept) the ones that you want to
    /// answer, and then wait for them to give you [`StreamRequest`](tor_hsservice::StreamRequest)s.
    ///
    /// You may find the [`tor_hsservice::handle_rend_requests`] API helpful for
    /// translating `RendRequest`s into `StreamRequest`s.
    ///
    /// If you want to forward all the requests from an onion service to a set
    /// of local ports, you may want to use the `tor-hsrproxy` crate.
    #[cfg(feature = "onion-service-service")]
    #[instrument(skip_all, level = "trace")]
    pub fn launch_onion_service(
        &self,
        config: tor_hsservice::OnionServiceConfig,
    ) -> crate::Result<
        Option<(
            Arc<tor_hsservice::RunningOnionService>,
            impl futures::Stream<Item = tor_hsservice::RendRequest> + use<R>,
        )>,
    > {
        let nickname = config.nickname();
        if !config.enabled() {
            info!(
                nickname=%nickname,
                "Skipping onion service because it was disabled in the config"
            );
            return Ok(None);
        }
        let running = self
            .client
            .initiate_bootstrap_if_needed("launch onion service")?;
        let keymgr = self
            .client
            .inert_client
            .keymgr
            .as_ref()
            .ok_or(ErrorDetail::KeystoreRequired {
                action: "launch onion service",
            })?
            .clone();
        let state_dir = self.client.state_directory.clone();
        let service = tor_hsservice::OnionService::builder()
            .config(config) // TODO #1186: Allow override of KeyMgr for "ephemeral" operation?
            .keymgr(keymgr)
            // TODO #1186: Allow override of StateMgr for "ephemeral" operation?
            .state_dir(state_dir)
            .build()
            .map_err(ErrorDetail::LaunchOnionService)?;
        Ok(service
            .launch(
                self.client.runtime.clone(),
                running.dirmgr.clone().upcast_arc(),
                running.hs_circ_pool.clone(),
                Arc::clone(&self.client.path_resolver),
            )
            .map_err(ErrorDetail::LaunchOnionService)?)
    }
    /// Try to launch an onion service with a given configuration and provided
    /// [`HsIdKeypair`]. If an onion service with the given nickname already has an
    /// associated `HsIdKeypair`  in this `TorClient`'s `KeyMgr`, then this operation
    /// fails rather than overwriting the existing key.
    ///
    /// Returns `Ok(None)` if the service specified is disabled in the config.
    ///
    /// The specified `HsIdKeypair` will be inserted in the primary keystore.
    ///
    /// **Important**: depending on the configuration of your
    /// [primary keystore](tor_keymgr::config::PrimaryKeystoreConfig),
    /// the `HsIdKeypair` **may** get persisted to disk.
    /// By default, Arti's primary keystore is the [native](ArtiKeystoreKind::Native),
    /// disk-based keystore.
    ///
    /// This onion service will not actually handle any requests on its own: you
    /// will need to
    /// pull [`RendRequest`](tor_hsservice::RendRequest) objects from the returned stream,
    /// [`accept`](tor_hsservice::RendRequest::accept) the ones that you want to
    /// answer, and then wait for them to give you [`StreamRequest`](tor_hsservice::StreamRequest)s.
    ///
    /// You may find the [`tor_hsservice::handle_rend_requests`] API helpful for
    /// translating `RendRequest`s into `StreamRequest`s.
    ///
    /// If you want to forward all the requests from an onion service to a set
    /// of local ports, you may want to use the `tor-hsrproxy` crate.
    #[cfg(all(feature = "onion-service-service", feature = "experimental-api"))]
    #[instrument(skip_all, level = "trace")]
    pub fn launch_onion_service_with_hsid(
        &self,
        config: tor_hsservice::OnionServiceConfig,
        id_keypair: HsIdKeypair,
    ) -> crate::Result<
        Option<(
            Arc<tor_hsservice::RunningOnionService>,
            impl futures::Stream<Item = tor_hsservice::RendRequest> + use<R>,
        )>,
    > {
        let nickname = config.nickname();
        let hsid_spec = HsIdKeypairSpecifier::new(nickname.clone());
        let selector = KeystoreSelector::Primary;
        let _kp = self
            .client
            .inert_client
            .keymgr
            .as_ref()
            .ok_or(ErrorDetail::KeystoreRequired {
                action: "launch onion service ex",
            })?
            .insert::<HsIdKeypair>(id_keypair, &hsid_spec, selector, false)?;
        self.launch_onion_service(config)
    }
    /// Generate a service discovery keypair for connecting to a hidden service running in
    /// "restricted discovery" mode.
    ///
    /// The `selector` argument is used for choosing the keystore in which to generate the keypair.
    /// While most users will want to write to the [`Primary`](KeystoreSelector::Primary), if you
    /// have configured this `TorClient` with a non-default keystore and wish to generate the
    /// keypair in it, you can do so by calling this function with a [KeystoreSelector::Id]
    /// specifying the keystore ID of your keystore.
    ///
    // Note: the selector argument exists for future-proofing reasons. We don't currently support
    // configuring custom or non-default keystores (see #1106).
    ///
    /// Returns an error if the key already exists in the specified key store.
    ///
    /// Important: the public part of the generated keypair must be shared with the service, and
    /// the service needs to be configured to allow the owner of its private counterpart to
    /// discover its introduction points. The caller is responsible for sharing the public part of
    /// the key with the hidden service.
    ///
    /// This function does not require the `TorClient` to be running or bootstrapped.
    //
    // TODO: decide whether this should use get_or_generate before making it
    // non-experimental
    #[cfg(all(
        feature = "onion-service-client",
        feature = "experimental-api",
        feature = "keymgr"
    ))]
    pub fn generate_service_discovery_key(
        &self,
        selector: KeystoreSelector,
        hsid: HsId,
    ) -> crate::Result<HsClientDescEncKey> {
        self.client
            .inert_client
            .generate_service_discovery_key(selector, hsid)
    }
    /// Rotate the service discovery keypair for connecting to a hidden service running in
    /// "restricted discovery" mode.
    ///
    /// **If the specified keystore already contains a restricted discovery keypair
    /// for the service, it will be overwritten.** Otherwise, a new keypair is generated.
    ///
    /// The `selector` argument is used for choosing the keystore in which to generate the keypair.
    /// While most users will want to write to the [`Primary`](KeystoreSelector::Primary), if you
    /// have configured this `TorClient` with a non-default keystore and wish to generate the
    /// keypair in it, you can do so by calling this function with a [KeystoreSelector::Id]
    /// specifying the keystore ID of your keystore.
    ///
    // Note: the selector argument exists for future-proofing reasons. We don't currently support
    // configuring custom or non-default keystores (see #1106).
    ///
    /// Important: the public part of the generated keypair must be shared with the service, and
    /// the service needs to be configured to allow the owner of its private counterpart to
    /// discover its introduction points. The caller is responsible for sharing the public part of
    /// the key with the hidden service.
    ///
    /// This function does not require the `TorClient` to be running or bootstrapped.
    #[cfg(all(
        feature = "onion-service-client",
        feature = "experimental-api",
        feature = "keymgr"
    ))]
    #[cfg_attr(
        docsrs,
        doc(cfg(all(
            feature = "onion-service-client",
            feature = "experimental-api",
            feature = "keymgr"
        )))
    )]
    pub fn rotate_service_discovery_key(
        &self,
        selector: KeystoreSelector,
        hsid: HsId,
    ) -> crate::Result<HsClientDescEncKey> {
        self.client
            .inert_client
            .rotate_service_discovery_key(selector, hsid)
    }
    /// Insert a service discovery secret key for connecting to a hidden service running in
    /// "restricted discovery" mode
    ///
    /// The `selector` argument is used for choosing the keystore in which to generate the keypair.
    /// While most users will want to write to the [`Primary`](KeystoreSelector::Primary), if you
    /// have configured this `TorClient` with a non-default keystore and wish to insert the
    /// key in it, you can do so by calling this function with a [KeystoreSelector::Id]
    ///
    // Note: the selector argument exists for future-proofing reasons. We don't currently support
    // configuring custom or non-default keystores (see #1106).
    ///
    /// Returns an error if the key already exists in the specified key store.
    ///
    /// Important: the public part of the generated keypair must be shared with the service, and
    /// the service needs to be configured to allow the owner of its private counterpart to
    /// discover its introduction points. The caller is responsible for sharing the public part of
    /// the key with the hidden service.
    ///
    /// This function does not require the `TorClient` to be running or bootstrapped.
    #[cfg(all(
        feature = "onion-service-client",
        feature = "experimental-api",
        feature = "keymgr"
    ))]
    #[cfg_attr(
        docsrs,
        doc(cfg(all(
            feature = "onion-service-client",
            feature = "experimental-api",
            feature = "keymgr"
        )))
    )]
    pub fn insert_service_discovery_key(
        &self,
        selector: KeystoreSelector,
        hsid: HsId,
        hs_client_desc_enc_secret_key: HsClientDescEncSecretKey,
    ) -> crate::Result<HsClientDescEncKey> {
        self.client.inert_client.insert_service_discovery_key(
            selector,
            hsid,
            hs_client_desc_enc_secret_key,
        )
    }
    /// Return the service discovery public key for the service with the specified `hsid`.
    ///
    /// Returns `Ok(None)` if no such key exists.
    ///
    /// This function does not require the `TorClient` to be running or bootstrapped.
    #[cfg(all(feature = "onion-service-client", feature = "experimental-api"))]
    #[cfg_attr(
        docsrs,
        doc(cfg(all(feature = "onion-service-client", feature = "experimental-api")))
    )]
    pub fn get_service_discovery_key(
        &self,
        hsid: HsId,
    ) -> crate::Result<Option<HsClientDescEncKey>> {
        self.client.inert_client.get_service_discovery_key(hsid)
    }
    /// Removes the service discovery keypair for the service with the specified `hsid`.
    ///
    /// Returns an error if the selected keystore is not the default keystore or one of the
    /// configured secondary stores.
    ///
    /// Returns `Ok(None)` if no such keypair exists whereas `Ok(Some()) means the keypair was successfully removed.
    ///
    /// Returns `Err` if an error occurred while trying to remove the key.
    #[cfg(all(
        feature = "onion-service-client",
        feature = "experimental-api",
        feature = "keymgr"
    ))]
    #[cfg_attr(
        docsrs,
        doc(cfg(all(
            feature = "onion-service-client",
            feature = "experimental-api",
            feature = "keymgr"
        )))
    )]
    pub fn remove_service_discovery_key(
        &self,
        selector: KeystoreSelector,
        hsid: HsId,
    ) -> crate::Result<Option<()>> {
        self.client
            .inert_client
            .remove_service_discovery_key(selector, hsid)
    }
    /// Create (but do not launch) a new
    /// [`OnionService`](tor_hsservice::OnionService)
    /// using the given configuration.
    ///
    /// This is useful for managing an onion service without needing to start a `TorClient` or the
    /// onion service itself.
    /// If you only wish to run the onion service, see
    /// [`TorClient::launch_onion_service()`]
    /// which allows you to launch an onion service from a running `TorClient`.
    ///
    /// The returned `OnionService` can be launched using
    /// [`OnionService::launch()`](tor_hsservice::OnionService::launch).
    /// Note that `launch()` requires a [`NetDirProvider`],
    /// [`HsCircPool`](tor_circmgr::hspool::HsCircPool), etc,
    /// which you should obtain from a running `TorClient`.
    /// But these are only accessible from a `TorClient` if the "experimental-api" feature is
    /// enabled.
    /// The behaviour is not specified if you create the `OnionService` with
    /// `create_onion_service()` using one [`TorClientConfig`],
    /// but launch it using a `TorClient` generated from a different `TorClientConfig`.
    // TODO #2249: Look into this behaviour more, and possibly error if there is a different config.
    #[cfg(feature = "onion-service-service")]
    #[instrument(skip_all, level = "trace")]
40
    pub fn create_onion_service(
40
        config: &TorClientConfig,
40
        svc_config: tor_hsservice::OnionServiceConfig,
40
    ) -> crate::Result<tor_hsservice::OnionService> {
40
        let inert_client = InertTorClient::new(config)?;
40
        inert_client.create_onion_service(config, svc_config)
40
    }
    /// Return a current [`status::BootstrapStatus`] describing how close this client
    /// is to being ready for user traffic.
    pub fn bootstrap_status(&self) -> status::BootstrapStatus {
        self.client.status_receiver.inner.borrow().clone()
    }
    /// Return a stream of [`status::BootstrapStatus`] events that will be updated
    /// whenever the client's status changes.
    ///
    /// The receiver might not receive every update sent to this stream, though
    /// when it does poll the stream it should get the most recent one.
    //
    // TODO(nickm): will this also need to implement Send and 'static?
    pub fn bootstrap_events(&self) -> status::BootstrapEvents {
        self.client.status_receiver.clone()
    }
    /// Change the client's current dormant mode, putting background tasks to sleep
    /// or waking them up as appropriate.
    ///
    /// This can be used to conserve CPU usage if you aren't planning on using the
    /// client for a while, especially on mobile platforms.
    ///
    /// See the [`DormantMode`] documentation for more details.
    pub fn set_dormant(&self, mode: DormantMode) {
        *self
            .client
            .dormant
            .lock()
            .expect("dormant lock poisoned")
            .borrow_mut() = Some(mode);
    }
    /// Return a [`Future`] which resolves
    /// once this TorClient has stopped.
    #[cfg(feature = "experimental-api")]
    #[instrument(skip_all, level = "trace")]
    pub fn wait_for_stop(
        &self,
    ) -> impl futures::Future<Output = ()> + Send + Sync + 'static + use<R> {
        // We defer to the "wait for unlock" handle on our statemgr.
        //
        // The statemgr won't actually be unlocked until it is finally
        // dropped, which will happen when this TorClient is
        // dropped—which is what we want.
        self.client.statemgr.wait_for_unlock()
    }
    /// Getter for keymgr.
    #[cfg(feature = "onion-service-cli-extra")]
    pub fn keymgr(&self) -> crate::Result<&KeyMgr> {
        self.client.inert_client.keymgr()
    }
}
impl<R: Runtime> ClientShared<R> {
    /// Used by `bootstrap_inner`: Return a `RunningInner`, constructing it if necessary.
    fn instantiate_running_inner(
        &self,
        mut inner_guard: std::sync::MutexGuard<'_, Inner<R>>,
    ) -> Result<Arc<RunningInner<R>>, ErrorDetail> {
        match &*inner_guard {
            Inner::Running(running_inner) => Ok(Arc::clone(running_inner)),
            Inner::Poisoned(e) => Err(e.as_ref().clone()),
            Inner::NotConstructed(_) => {
                let error = ErrorDetail::from(internal!("Client under construction"));
                let mut pending = Inner::Poisoned(Box::new(error));
                std::mem::swap(&mut pending, &mut *inner_guard);
                let Inner::NotConstructed(pending) = pending else {
                    panic!("Surprising type change");
                };
                match RunningInner::new(*pending, self) {
                    Ok(running_inner) => {
                        *inner_guard = Inner::Running(Arc::clone(&running_inner));
                        self.bootstrap_setting_sender
                            .lock()
                            .expect("lock poisoned")
                            .borrow_mut()
                            .running_inner_is_present = true;
                        Ok(running_inner)
                    }
                    Err(e) => {
                        *inner_guard = Inner::Poisoned(Box::new(e.clone()));
                        Err(e)
                    }
                }
            }
        }
    }
    /// Implementation of `bootstrap`, split out in order to avoid manually specifying
    /// double error conversions.
    async fn bootstrap_inner(&self) -> StdResult<(), ErrorDetail> {
        // Wait for an existing bootstrap attempt to finish first.
        //
        // This is a futures::lock::Mutex, so it's okay to await while we hold it.
        let _bootstrap_lock = self.bootstrap_in_progress.lock().await;
        let running = self.instantiate_running_inner(self.inner.lock().expect("lock poisoned"))?;
        // Make sure we have a bridge descriptor manager, which is active iff required
        #[cfg(feature = "bridge-client")]
        {
            let mut dormant = self.dormant.lock().expect("dormant lock poisoned");
            let dormant = dormant.borrow();
            let dormant = dormant.ok_or_else(|| internal!("dormant dropped"))?.into();
            let mut bdm = running.bridge_desc_mgr.lock().expect("bdm lock poisoned");
            if bdm.is_none() {
                let new_bdm = Arc::new(BridgeDescMgr::new(
                    &Default::default(),
                    self.runtime.clone(),
                    self.dirmgr_store.clone(),
                    running.circmgr.clone(),
                    dormant,
                )?);
                running
                    .guardmgr
                    .install_bridge_desc_provider(&(new_bdm.clone() as _))
                    .map_err(ErrorDetail::GuardMgrSetup)?;
                // If ^ that fails, we drop the BridgeDescMgr again.  It may do some
                // work but will hopefully eventually quit.
                *bdm = Some(new_bdm);
            }
        }
        if self
            .statemgr
            .try_lock()
            .map_err(ErrorDetail::StateAccess)?
            .held()
        {
            debug!("It appears we have the lock on our state files.");
        } else {
            info!(
                "Another process has the lock on our state files. We'll proceed in read-only mode."
            );
        }
        // If we fail to bootstrap (i.e. we return before the disarm() point below), attempt to
        // unlock the state files.
        let unlock_guard = util::StateMgrUnlockGuard::new(&self.statemgr);
        running
            .dirmgr
            .bootstrap()
            .await
            .map_err(ErrorDetail::DirMgrBootstrap)?;
        // Since we succeeded, disarm the unlock guard.
        unlock_guard.disarm();
        Ok(())
    }
    /// Ensure that this client is running and bootstrapped, and return a [`RunningInner`] if it is.
    ///
    /// If we're not bootstrapped,
    /// we either try to bootstrap or return an error,
    /// depending on `self.should_bootstrap`:
    ///
    /// ## For `BootstrapBehavior::OnDemand` clients
    ///
    /// Initiate a bootstrap by calling `bootstrap_inner`
    /// (which is idempotent, so attempts to bootstrap twice will just do nothing).
    ///
    /// ## For `BootstrapBehavior::Manual` clients
    ///
    /// Check whether a bootstrap is in progress; if one is, wait until it finishes.
    /// Then see whether we're bootstrapped, and return either a success or a failure.
    #[instrument(skip_all, level = "trace")]
2
    async fn wait_for_bootstrap_running(
2
        &self,
2
        action: &'static str,
2
    ) -> StdResult<Arc<RunningInner<R>>, ErrorDetail> {
2
        match self.should_bootstrap {
            BootstrapBehavior::OnDemand => {
                self.bootstrap_inner().await?;
            }
            BootstrapBehavior::Manual => {
                // Grab the lock, and immediately release it.  That will ensure that nobody else is trying to bootstrap.
                self.bootstrap_in_progress.lock().await;
            }
        }
        self.dormant
            .lock()
            .map_err(|_| internal!("dormant poisoned"))?
2
            .try_maybe_send(|dormant| {
                Ok::<_, Bug>(Some({
2
                    match dormant.ok_or_else(|| internal!("dormant dropped"))? {
                        DormantMode::Soft => DormantMode::Normal,
2
                        other @ DormantMode::Normal => other,
                    }
                }))
2
            })?;
        self.running_inner(action)
2
    }
    /// If we are currently bootstrapping or running, return a [`RunningInner`].
2
    fn running_inner(&self, action: &'static str) -> StdResult<Arc<RunningInner<R>>, ErrorDetail> {
2
        let guard = self.inner.lock().expect("Lock poisoned");
2
        match &*guard {
2
            Inner::NotConstructed(_) => Err(ErrorDetail::BootstrapRequired { action }),
            Inner::Running(running_inner) => Ok(Arc::clone(running_inner)),
            Inner::Poisoned(e) => Err(e.as_ref().clone()),
        }
2
    }
    /// Ensure that our bootstrap state is [`RunningInner`], if possible.
    ///
    /// Return an error if our [`BootstrapBehavior`] is `Manual` and have not created a
    /// [`RunningInner`].
    fn initiate_bootstrap_if_needed(
        &self,
        action: &'static str,
    ) -> StdResult<Arc<RunningInner<R>>, ErrorDetail> {
        let guard = self.inner.lock().expect("Lock poisoned");
        match &*guard {
            Inner::Running(running_inner) => Ok(Arc::clone(running_inner)),
            Inner::Poisoned(e) => Err(e.as_ref().clone()),
            Inner::NotConstructed(_) => match self.should_bootstrap {
                BootstrapBehavior::Manual => Err(ErrorDetail::BootstrapRequired { action }),
                BootstrapBehavior::OnDemand => self.instantiate_running_inner(guard),
            },
        }
    }
    /// This is split out from `reconfigure` so we can do the all-or-nothing
    /// check without recursion. the caller to this method must hold the
    /// `reconfigure_lock`.
    #[instrument(level = "trace", skip_all)]
4
    fn reconfigure_inner(
4
        &self,
4
        new_config: &TorClientConfig,
4
        how: tor_config::Reconfigure,
4
        _reconfigure_lock_guard: &std::sync::MutexGuard<'_, ()>,
4
    ) -> crate::Result<()> {
        // We ignore 'new_config.path_resolver' here since CfgPathResolver does not impl PartialEq
        // and we have no way to compare them, but this field is explicitly documented as being
        // non-reconfigurable anyways.
4
        let addr_cfg = &new_config.address_filter;
4
        let timeout_cfg = &new_config.stream_timeouts;
4
        let state_cfg = new_config
4
            .storage
4
            .expand_state_dir(&self.path_resolver)
4
            .map_err(wrap_err)?;
        // TODO wasm: This ins't really how things should be long term,
        // but once we have a more generic notion of configuring storage
        // we can change this to comply with it.
        #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
        {
4
            if state_cfg != self.statemgr.path() {
                how.cannot_change("storage.state_dir").map_err(wrap_err)?;
4
            }
        }
4
        self.memquota
4
            .reconfigure(new_config.system.memory.clone(), how)
4
            .map_err(wrap_err)?;
4
        let mut inner_lock = self.inner.lock().expect("Lock poisoned");
4
        match &mut *inner_lock {
            Inner::Poisoned(e) => return Err(e.as_ref().clone().into()),
4
            Inner::NotConstructed(nc) => nc.reconfigure(new_config, how)?,
            Inner::Running(r) => {
                let running = Arc::clone(r);
                drop(inner_lock);
                running.reconfigure(new_config, how)?;
            }
        }
4
        if how == tor_config::Reconfigure::CheckAllOrNothing {
2
            return Ok(());
2
        }
2
        self.addrcfg.replace(addr_cfg.clone());
2
        self.timeoutcfg.replace(timeout_cfg.clone());
2
        Ok(())
4
    }
}
/// Monitor `dormant_mode` and enable/disable periodic tasks as applicable
///
/// This function is spawned as a task during client construction.
// TODO should this perhaps be done by each TaskHandle?
async fn tasks_monitor_dormant<R: Runtime>(
    mut dormant_rx: postage::watch::Receiver<Option<DormantMode>>,
    netdir: Arc<dyn NetDirProvider>,
    chanmgr: Arc<tor_chanmgr::ChanMgr<R>>,
    #[cfg(feature = "bridge-client")] bridge_desc_mgr: Arc<Mutex<Option<Arc<BridgeDescMgr<R>>>>>,
    periodic_task_handles: Vec<TaskHandle>,
) {
    while let Some(Some(mode)) = dormant_rx.next().await {
        let netparams = netdir.params();
        chanmgr
            .set_dormancy(mode.into(), netparams)
            .unwrap_or_else(|e| error_report!(e, "couldn't set dormancy"));
        // IEFI simplifies handling of exceptional cases, as "never mind, then".
        #[cfg(feature = "bridge-client")]
        (|| {
            let mut bdm = bridge_desc_mgr.lock().ok()?;
            let bdm = bdm.as_mut()?;
            bdm.set_dormancy(mode.into());
            Some(())
        })();
        let is_dormant = matches!(mode, DormantMode::Soft);
        for task in periodic_task_handles.iter() {
            if is_dormant {
                task.cancel();
            } else {
                task.fire();
            }
        }
    }
}
/// Alias for TorError::from(Error)
2
pub(crate) fn wrap_err<T>(err: T) -> crate::Error
2
where
2
    ErrorDetail: From<T>,
{
2
    ErrorDetail::from(err).into()
2
}
#[cfg(test)]
mod test {
    // @@ begin test lint list maintained by maint/add_warning @@
    #![allow(clippy::bool_assert_comparison)]
    #![allow(clippy::clone_on_copy)]
    #![allow(clippy::dbg_macro)]
    #![allow(clippy::mixed_attributes_style)]
    #![allow(clippy::print_stderr)]
    #![allow(clippy::print_stdout)]
    #![allow(clippy::single_char_pattern)]
    #![allow(clippy::unwrap_used)]
    #![allow(clippy::unchecked_time_subtraction)]
    #![allow(clippy::useless_vec)]
    #![allow(clippy::needless_pass_by_value)]
    #![allow(clippy::string_slice)] // See arti#2571
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
    use tor_config::Reconfigure;
    use super::*;
    use crate::config::TorClientConfigBuilder;
    use crate::{ErrorKind, HasKind};
    #[test]
    fn create_unbootstrapped() {
        tor_rtcompat::test_with_one_runtime!(|rt| async {
            let state_dir = tempfile::tempdir().unwrap();
            let cache_dir = tempfile::tempdir().unwrap();
            let cfg = TorClientConfigBuilder::from_directories(state_dir, cache_dir)
                .build()
                .unwrap();
            let _ = TorClient::with_runtime(rt)
                .config(cfg)
                .bootstrap_behavior(BootstrapBehavior::Manual)
                .create_unbootstrapped()
                .unwrap();
        });
        tor_rtcompat::test_with_one_runtime!(|rt| async {
            let state_dir = tempfile::tempdir().unwrap();
            let cache_dir = tempfile::tempdir().unwrap();
            let cfg = TorClientConfigBuilder::from_directories(state_dir, cache_dir)
                .build()
                .unwrap();
            let _ = TorClient::with_runtime(rt)
                .config(cfg)
                .bootstrap_behavior(BootstrapBehavior::Manual)
                .create_unbootstrapped_async()
                .await
                .unwrap();
        });
    }
    #[test]
    fn unbootstrapped_client_unusable() {
        tor_rtcompat::test_with_one_runtime!(|rt| async {
            let state_dir = tempfile::tempdir().unwrap();
            let cache_dir = tempfile::tempdir().unwrap();
            let cfg = TorClientConfigBuilder::from_directories(state_dir, cache_dir)
                .build()
                .unwrap();
            // Test sync
            let client = TorClient::with_runtime(rt)
                .config(cfg)
                .bootstrap_behavior(BootstrapBehavior::Manual)
                .create_unbootstrapped()
                .unwrap();
            let result = client.connect("example.com:80").await;
            assert!(result.is_err());
            assert_eq!(result.err().unwrap().kind(), ErrorKind::BootstrapRequired);
        });
        // Need a separate test for async because Runtime and TorClientConfig are consumed by the
        // builder
        tor_rtcompat::test_with_one_runtime!(|rt| async {
            let state_dir = tempfile::tempdir().unwrap();
            let cache_dir = tempfile::tempdir().unwrap();
            let cfg = TorClientConfigBuilder::from_directories(state_dir, cache_dir)
                .build()
                .unwrap();
            // Test sync
            let client = TorClient::with_runtime(rt)
                .config(cfg)
                .bootstrap_behavior(BootstrapBehavior::Manual)
                .create_unbootstrapped_async()
                .await
                .unwrap();
            let result = client.connect("example.com:80").await;
            assert!(result.is_err());
            assert_eq!(result.err().unwrap().kind(), ErrorKind::BootstrapRequired);
        });
    }
    #[test]
    fn streamprefs_isolate_every_stream() {
        let mut observed = StreamPrefs::new();
        observed.isolate_every_stream();
        match observed.isolation {
            StreamIsolationPreference::EveryStream => (),
            _ => panic!("unexpected isolation: {:?}", observed.isolation),
        };
    }
    #[test]
    fn streamprefs_new_has_expected_defaults() {
        let observed = StreamPrefs::new();
        assert_eq!(observed.ip_ver_pref, IpVersionPreference::Ipv4Preferred);
        assert!(!observed.optimistic_stream);
        // StreamIsolationPreference does not implement Eq, check manually.
        match observed.isolation {
            StreamIsolationPreference::None => (),
            _ => panic!("unexpected isolation: {:?}", observed.isolation),
        };
    }
    #[test]
    fn streamprefs_new_isolation_group() {
        let mut observed = StreamPrefs::new();
        observed.new_isolation_group();
        match observed.isolation {
            StreamIsolationPreference::Explicit(_) => (),
            _ => panic!("unexpected isolation: {:?}", observed.isolation),
        };
    }
    #[test]
    fn streamprefs_ipv6_only() {
        let mut observed = StreamPrefs::new();
        observed.ipv6_only();
        assert_eq!(observed.ip_ver_pref, IpVersionPreference::Ipv6Only);
    }
    #[test]
    fn streamprefs_ipv6_preferred() {
        let mut observed = StreamPrefs::new();
        observed.ipv6_preferred();
        assert_eq!(observed.ip_ver_pref, IpVersionPreference::Ipv6Preferred);
    }
    #[test]
    fn streamprefs_ipv4_only() {
        let mut observed = StreamPrefs::new();
        observed.ipv4_only();
        assert_eq!(observed.ip_ver_pref, IpVersionPreference::Ipv4Only);
    }
    #[test]
    fn streamprefs_ipv4_preferred() {
        let mut observed = StreamPrefs::new();
        observed.ipv4_preferred();
        assert_eq!(observed.ip_ver_pref, IpVersionPreference::Ipv4Preferred);
    }
    #[test]
    fn streamprefs_optimistic() {
        let mut observed = StreamPrefs::new();
        observed.optimistic();
        assert!(observed.optimistic_stream);
    }
    #[test]
    fn streamprefs_set_isolation() {
        let mut observed = StreamPrefs::new();
        observed.set_isolation(IsolationToken::new());
        match observed.isolation {
            StreamIsolationPreference::Explicit(_) => (),
            _ => panic!("unexpected isolation: {:?}", observed.isolation),
        };
    }
    #[test]
    fn reconfigure_all_or_nothing() {
        tor_rtcompat::test_with_one_runtime!(|rt| async {
            let state_dir = tempfile::tempdir().unwrap();
            let cache_dir = tempfile::tempdir().unwrap();
            let cfg = TorClientConfigBuilder::from_directories(state_dir, cache_dir)
                .build()
                .unwrap();
            let tor_client = TorClient::with_runtime(rt)
                .config(cfg.clone())
                .bootstrap_behavior(BootstrapBehavior::Manual)
                .create_unbootstrapped()
                .unwrap();
            tor_client
                .reconfigure(&cfg, Reconfigure::AllOrNothing)
                .unwrap();
        });
        tor_rtcompat::test_with_one_runtime!(|rt| async {
            let state_dir = tempfile::tempdir().unwrap();
            let cache_dir = tempfile::tempdir().unwrap();
            let cfg = TorClientConfigBuilder::from_directories(state_dir, cache_dir)
                .build()
                .unwrap();
            let tor_client = TorClient::with_runtime(rt)
                .config(cfg.clone())
                .bootstrap_behavior(BootstrapBehavior::Manual)
                .create_unbootstrapped_async()
                .await
                .unwrap();
            tor_client
                .reconfigure(&cfg, Reconfigure::AllOrNothing)
                .unwrap();
        });
    }
}