1
#![cfg_attr(docsrs, feature(doc_cfg))]
2
#![doc = include_str!("../README.md")]
3
// @@ begin lint list maintained by maint/add_warning @@
4
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6
#![warn(missing_docs)]
7
#![warn(noop_method_call)]
8
#![warn(unreachable_pub)]
9
#![warn(clippy::all)]
10
#![deny(clippy::await_holding_lock)]
11
#![deny(clippy::cargo_common_metadata)]
12
#![deny(clippy::cast_lossless)]
13
#![deny(clippy::checked_conversions)]
14
#![allow(clippy::cognitive_complexity)] // See arti#2556
15
#![deny(clippy::debug_assert_with_mut_call)]
16
#![deny(clippy::exhaustive_enums)]
17
#![deny(clippy::exhaustive_structs)]
18
#![deny(clippy::expl_impl_clone_on_copy)]
19
#![deny(clippy::fallible_impl_from)]
20
#![deny(clippy::implicit_clone)]
21
#![deny(clippy::large_stack_arrays)]
22
#![warn(clippy::manual_ok_or)]
23
#![deny(clippy::missing_docs_in_private_items)]
24
#![warn(clippy::needless_borrow)]
25
#![warn(clippy::needless_pass_by_value)]
26
#![warn(clippy::option_option)]
27
#![warn(clippy::or_fun_call)]
28
#![deny(clippy::print_stderr)]
29
#![deny(clippy::print_stdout)]
30
#![warn(clippy::rc_buffer)]
31
#![deny(clippy::ref_option_ref)]
32
#![warn(clippy::semicolon_if_nothing_returned)]
33
#![warn(clippy::trait_duplication_in_bounds)]
34
#![deny(clippy::unchecked_time_subtraction)]
35
#![deny(clippy::unnecessary_wraps)]
36
#![warn(clippy::unseparated_literal_suffix)]
37
#![deny(clippy::unwrap_used)]
38
#![deny(clippy::mod_module_files)]
39
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
40
#![allow(clippy::uninlined_format_args)]
41
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
42
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
43
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
44
#![allow(clippy::needless_lifetimes)] // See arti#1765
45
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
46
#![allow(clippy::collapsible_if)] // See arti#2342
47
#![deny(clippy::unused_async)]
48
#![deny(clippy::string_slice)] // See arti#2571
49
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
50
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
51

            
52
// TODO #1645 (either remove this, or decide to have it everywhere)
53
#![cfg_attr(not(all(feature = "full", feature = "experimental")), allow(unused))]
54

            
55
// Glossary:
56
//     Primary guard
57
//     Sample
58
//     confirmed
59
//     filtered
60

            
61
use derive_deftly::Deftly;
62
use futures::channel::mpsc;
63
use itertools::Either;
64
use serde::{Deserialize, Serialize};
65
use std::collections::HashMap;
66
use std::net::SocketAddr;
67
use std::sync::{Arc, Mutex, Weak};
68
#[cfg(feature = "bridge-client")]
69
use tor_error::internal;
70
use tor_linkspec::{OwnedChanTarget, OwnedCircTarget, RelayId, RelayIdSet};
71
use tor_netdir::NetDirProvider;
72
use tor_proto::ClockSkew;
73
use tor_rtcompat::SpawnExt;
74
use tor_units::BoundedInt32;
75
use tracing::{debug, info, instrument, trace, warn};
76
use web_time_compat::{Duration, Instant, SystemTime};
77

            
78
use tor_config::derive::prelude::*;
79
use tor_config::{ExplicitOrAuto, impl_standard_builder};
80
use tor_config::{ReconfigureError, impl_not_auto_value};
81
use tor_config::{define_list_builder_accessors, define_list_builder_helper};
82
use tor_netdir::{NetDir, Relay, params::NetParameters};
83
use tor_persist::{DynStorageHandle, StateMgr};
84
use tor_rtcompat::Runtime;
85

            
86
#[cfg(feature = "bridge-client")]
87
pub mod bridge;
88
mod config;
89
mod daemon;
90
mod dirstatus;
91
mod err;
92
mod events;
93
pub mod fallback;
94
mod filter;
95
mod guard;
96
mod ids;
97
mod pending;
98
mod sample;
99
mod skew;
100
mod util;
101
#[cfg(feature = "vanguards")]
102
pub mod vanguards;
103

            
104
#[cfg(not(feature = "bridge-client"))]
105
#[path = "bridge_disabled.rs"]
106
pub mod bridge;
107

            
108
#[cfg(any(test, feature = "testing"))]
109
pub use config::testing::TestConfig;
110

            
111
#[cfg(test)]
112
use oneshot_fused_workaround as oneshot;
113

            
114
pub use config::GuardMgrConfig;
115
pub use err::{GuardMgrConfigError, GuardMgrError, PickGuardError};
116
pub use events::ClockSkewEvents;
117
pub use filter::GuardFilter;
118
pub use ids::FirstHopId;
119
pub use pending::{GuardMonitor, GuardStatus, GuardUsable};
120
pub use skew::SkewEstimate;
121

            
122
#[cfg(feature = "vanguards")]
123
pub use vanguards::VanguardMgrError;
124

            
125
use pending::{PendingRequest, RequestId};
126
use sample::{GuardSet, Universe, UniverseRef};
127

            
128
use crate::ids::{FirstHopIdInner, GuardId};
129

            
130
/// A "guard manager" that selects and remembers a persistent set of
131
/// guard nodes.
132
///
133
/// This is a "handle"; clones of it share state.
134
#[derive(Clone)]
135
pub struct GuardMgr<R: Runtime> {
136
    /// An asynchronous runtime object.
137
    ///
138
    /// GuardMgr uses this runtime for timing, timeouts, and spawning
139
    /// tasks.
140
    runtime: R,
141

            
142
    /// Internal state for the guard manager.
143
    inner: Arc<Mutex<GuardMgrInner>>,
144
}
145

            
146
/// Helper type that holds the data used by a [`GuardMgr`].
147
///
148
/// This would just be a [`GuardMgr`], except that it needs to sit inside
149
/// a `Mutex` and get accessed by daemon tasks.
150
struct GuardMgrInner {
151
    /// Last time when marked all of our primary guards as retriable.
152
    ///
153
    /// We keep track of this time so that we can rate-limit
154
    /// these attempts.
155
    last_primary_retry_time: Instant,
156

            
157
    /// Persistent guard manager state.
158
    ///
159
    /// This object remembers one or more persistent set of guards that we can
160
    /// use, along with their relative priorities and statuses.
161
    guards: GuardSets,
162

            
163
    /// The current filter that we're using to decide which guards are
164
    /// supported.
165
    //
166
    // TODO: This field is duplicated in the current active [`GuardSet`]; we
167
    // should fix that.
168
    filter: GuardFilter,
169

            
170
    /// Configuration values derived from the consensus parameters.
171
    ///
172
    /// This is updated whenever the consensus parameters change.
173
    params: GuardParams,
174

            
175
    /// A mpsc channel, used to tell the task running in
176
    /// [`daemon::report_status_events`] about a new event to monitor.
177
    ///
178
    /// This uses an `UnboundedSender` so that we don't have to await
179
    /// while sending the message, which in turn allows the GuardMgr
180
    /// API to be simpler.  The risk, however, is that there's no
181
    /// backpressure in the event that the task running
182
    /// [`daemon::report_status_events`] fails to read from this
183
    /// channel.
184
    ctrl: mpsc::UnboundedSender<daemon::Msg>,
185

            
186
    /// Information about guards that we've given out, but where we have
187
    /// not yet heard whether the guard was successful.
188
    ///
189
    /// Upon leaning whether the guard was successful, the pending
190
    /// requests in this map may be either moved to `waiting`, or
191
    /// discarded.
192
    ///
193
    /// There can be multiple pending requests corresponding to the
194
    /// same guard.
195
    pending: HashMap<RequestId, PendingRequest>,
196

            
197
    /// A list of pending requests for which we have heard that the
198
    /// guard was successful, but we have not yet decided whether the
199
    /// circuit may be used.
200
    ///
201
    /// There can be multiple waiting requests corresponding to the
202
    /// same guard.
203
    waiting: Vec<PendingRequest>,
204

            
205
    /// A list of fallback directories used to access the directory system
206
    /// when no other directory information is yet known.
207
    fallbacks: fallback::FallbackState,
208

            
209
    /// Location in which to store persistent state.
210
    storage: DynStorageHandle<GuardSets>,
211

            
212
    /// A sender object to publish changes in our estimated clock skew.
213
    send_skew: postage::watch::Sender<Option<SkewEstimate>>,
214

            
215
    /// A receiver object to hand out to observers who want to know about
216
    /// changes in our estimated clock skew.
217
    recv_skew: events::ClockSkewEvents,
218

            
219
    /// A netdir provider that we can use for adding new guards when
220
    /// insufficient guards are available.
221
    ///
222
    /// This has to be an Option so it can be initialized from None: at the
223
    /// time a GuardMgr is created, there is no NetDirProvider for it to use.
224
    netdir_provider: Option<Weak<dyn NetDirProvider>>,
225

            
226
    /// A netdir provider that we can use for discovering bridge descriptors.
227
    ///
228
    /// This has to be an Option so it can be initialized from None: at the time
229
    /// a GuardMgr is created, there is no BridgeDescProvider for it to use.
230
    #[cfg(feature = "bridge-client")]
231
    bridge_desc_provider: Option<Weak<dyn bridge::BridgeDescProvider>>,
232

            
233
    /// A list of the bridges that we are configured to use, or "None" if we are
234
    /// not configured to use bridges.
235
    #[cfg(feature = "bridge-client")]
236
    configured_bridges: Option<Arc<[bridge::BridgeConfig]>>,
237
}
238

            
239
/// A selector that tells us which [`GuardSet`] of several is currently in use.
240
#[derive(Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, strum::EnumIter)]
241
enum GuardSetSelector {
242
    /// The default guard set is currently in use: that's the one that we use
243
    /// when we have no filter installed, or the filter permits most of the
244
    /// guards on the network.
245
    #[default]
246
    Default,
247
    /// A "restrictive" guard set is currently in use: that's the one that we
248
    /// use when we have a filter that excludes a large fraction of the guards
249
    /// on the network.
250
    Restricted,
251
    /// The "bridges" guard set is currently in use: we are selecting our guards
252
    /// from among the universe of configured bridges.
253
    #[cfg(feature = "bridge-client")]
254
    Bridges,
255
}
256

            
257
/// Describes the [`Universe`] that a guard sample should take its guards from.
258
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
259
enum UniverseType {
260
    /// Take information from the network directory.
261
    NetDir,
262
    /// Take information from the configured bridges.
263
    #[cfg(feature = "bridge-client")]
264
    BridgeSet,
265
}
266

            
267
impl GuardSetSelector {
268
    /// Return a description of which [`Universe`] this guard sample should take
269
    /// its guards from.
270
1159500
    fn universe_type(&self) -> UniverseType {
271
1159500
        match self {
272
1159500
            GuardSetSelector::Default | GuardSetSelector::Restricted => UniverseType::NetDir,
273
            #[cfg(feature = "bridge-client")]
274
            GuardSetSelector::Bridges => UniverseType::BridgeSet,
275
        }
276
1159500
    }
277
}
278

            
279
/// Persistent state for a guard manager, as serialized to disk.
280
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
281
struct GuardSets {
282
    /// Which set of guards is currently in use?
283
    #[serde(skip)]
284
    active_set: GuardSetSelector,
285

            
286
    /// The default set of guards to use.
287
    ///
288
    /// We use this one when there is no filter, or the filter permits most of the
289
    /// guards on the network.
290
    default: GuardSet,
291

            
292
    /// A guard set to use when we have a restrictive filter.
293
    #[serde(default)]
294
    restricted: GuardSet,
295

            
296
    /// A guard set sampled from our configured bridges.
297
    #[serde(default)]
298
    #[cfg(feature = "bridge-client")]
299
    bridges: GuardSet,
300

            
301
    /// Unrecognized fields, including (possibly) other guard sets.
302
    #[serde(flatten)]
303
    remaining: HashMap<String, tor_persist::JsonValue>,
304
}
305

            
306
/// The key (filename) we use for storing our persistent guard state in the
307
/// `StateMgr`.
308
///
309
/// We used to store this in a different format in a filename called
310
/// "default_guards" (before Arti 0.1.0).
311
const STORAGE_KEY: &str = "guards";
312

            
313
/// A description of which circuits to retire because of a configuration change.
314
///
315
/// TODO(nickm): Eventually we will want to add a "Some" here, to support
316
/// removing only those circuits that correspond to no-longer-usable guards.
317
#[derive(Clone, Debug, Eq, PartialEq)]
318
#[must_use]
319
#[non_exhaustive]
320
pub enum RetireCircuits {
321
    /// There's no need to retire any circuits.
322
    None,
323
    /// All circuits should be retired.
324
    All,
325
}
326

            
327
impl<R: Runtime> GuardMgr<R> {
328
    /// Create a new "empty" guard manager and launch its background tasks.
329
    ///
330
    /// It won't be able to hand out any guards until a [`NetDirProvider`] has
331
    /// been installed.
332
    #[instrument(skip_all, level = "trace")]
333
340
    pub fn new<S>(
334
340
        runtime: R,
335
340
        state_mgr: S,
336
340
        config: &impl GuardMgrConfig,
337
340
    ) -> Result<Self, GuardMgrError>
338
340
    where
339
340
        S: StateMgr + Send + Sync + 'static,
340
    {
341
340
        let (ctrl, rcv) = mpsc::unbounded();
342
340
        let storage: DynStorageHandle<GuardSets> = state_mgr.create_handle(STORAGE_KEY);
343
        // TODO(nickm): We should do something about the old state in
344
        // `default_guards`.  Probably it would be best to delete it.  We could
345
        // try to migrate it instead, but that's beyond the stability guarantee
346
        // that we're getting at this stage of our (pre-0.1) development.
347
340
        let state = storage.load()?.unwrap_or_default();
348

            
349
340
        let (send_skew, recv_skew) = postage::watch::channel();
350
340
        let recv_skew = ClockSkewEvents { inner: recv_skew };
351

            
352
340
        let inner = Arc::new(Mutex::new(GuardMgrInner {
353
340
            guards: state,
354
340
            filter: GuardFilter::unfiltered(),
355
340
            last_primary_retry_time: runtime.now(),
356
340
            params: GuardParams::default(),
357
340
            ctrl,
358
340
            pending: HashMap::new(),
359
340
            waiting: Vec::new(),
360
340
            fallbacks: config.fallbacks().into(),
361
340
            storage,
362
340
            send_skew,
363
340
            recv_skew,
364
340
            netdir_provider: None,
365
340
            #[cfg(feature = "bridge-client")]
366
340
            bridge_desc_provider: None,
367
340
            #[cfg(feature = "bridge-client")]
368
340
            configured_bridges: None,
369
340
        }));
370
        #[cfg(feature = "bridge-client")]
371
        {
372
340
            let mut inner = inner.lock().expect("lock poisoned");
373
            // TODO(nickm): This calls `GuardMgrInner::update`. Will we mind doing so before any
374
            // providers are configured? I think not, but we should make sure.
375
340
            let _: RetireCircuits =
376
340
                inner.replace_bridge_config(config, runtime.wallclock(), runtime.now())?;
377
        }
378
        {
379
340
            let weak_inner = Arc::downgrade(&inner);
380
340
            let rt_clone = runtime.clone();
381
340
            runtime
382
340
                .spawn(daemon::report_status_events(rt_clone, weak_inner, rcv))
383
340
                .map_err(|e| GuardMgrError::from_spawn("guard status event reporter", e))?;
384
        }
385
        {
386
340
            let rt_clone = runtime.clone();
387
340
            let weak_inner = Arc::downgrade(&inner);
388
340
            runtime
389
340
                .spawn(daemon::run_periodic(rt_clone, weak_inner))
390
340
                .map_err(|e| GuardMgrError::from_spawn("periodic guard updater", e))?;
391
        }
392
340
        Ok(GuardMgr { runtime, inner })
393
340
    }
394

            
395
    /// Install a [`NetDirProvider`] for use by this guard manager.
396
    ///
397
    /// It will be used to keep the guards up-to-date with changes from the
398
    /// network directory, and to find new guards when no NetDir is provided to
399
    /// select_guard().
400
    ///
401
    /// TODO: we should eventually return some kind of a task handle from this
402
    /// task, even though it is not strictly speaking periodic.
403
    ///
404
    /// The guardmgr retains only a `Weak` reference to `provider`,
405
    /// `install_netdir_provider` downgrades it on entry,
406
    // TODO add ref to document when https://gitlab.torproject.org/tpo/core/arti/-/issues/624
407
    // is fixed.  Also, maybe take an owned `Weak` to start with.
408
    //
409
    /// # Panics
410
    ///
411
    /// Panics if a [`NetDirProvider`] is already installed.
412
304
    pub fn install_netdir_provider(
413
304
        &self,
414
304
        provider: &Arc<dyn NetDirProvider>,
415
304
    ) -> Result<(), GuardMgrError> {
416
304
        let weak_provider = Arc::downgrade(provider);
417
        {
418
304
            let mut inner = self.inner.lock().expect("Poisoned lock");
419
304
            assert!(inner.netdir_provider.is_none());
420
304
            inner.netdir_provider = Some(weak_provider.clone());
421
        }
422
304
        let weak_inner = Arc::downgrade(&self.inner);
423
304
        let rt_clone = self.runtime.clone();
424
304
        self.runtime
425
304
            .spawn(daemon::keep_netdir_updated(
426
304
                rt_clone,
427
304
                weak_inner,
428
304
                weak_provider,
429
            ))
430
304
            .map_err(|e| GuardMgrError::from_spawn("periodic guard netdir updater", e))?;
431
304
        Ok(())
432
304
    }
433

            
434
    /// Configure a new [`bridge::BridgeDescProvider`] for this [`GuardMgr`].
435
    ///
436
    /// It will be used to learn about changes in the set of available bridge
437
    /// descriptors; we'll inform it whenever our desired set of bridge
438
    /// descriptors changes.
439
    ///
440
    /// TODO: Same todo as in `install_netdir_provider` about task handles.
441
    ///
442
    /// # Panics
443
    ///
444
    /// Panics if a [`bridge::BridgeDescProvider`] is already installed.
445
    #[cfg(feature = "bridge-client")]
446
    pub fn install_bridge_desc_provider(
447
        &self,
448
        provider: &Arc<dyn bridge::BridgeDescProvider>,
449
    ) -> Result<(), GuardMgrError> {
450
        let weak_provider = Arc::downgrade(provider);
451
        {
452
            let mut inner = self.inner.lock().expect("Poisoned lock");
453
            assert!(inner.bridge_desc_provider.is_none());
454
            inner.bridge_desc_provider = Some(weak_provider.clone());
455
        }
456

            
457
        let weak_inner = Arc::downgrade(&self.inner);
458
        let rt_clone = self.runtime.clone();
459
        self.runtime
460
            .spawn(daemon::keep_bridge_descs_updated(
461
                rt_clone,
462
                weak_inner,
463
                weak_provider,
464
            ))
465
            .map_err(|e| GuardMgrError::from_spawn("periodic guard netdir updater", e))?;
466

            
467
        Ok(())
468
    }
469

            
470
    /// Flush our current guard state to the state manager, if there
471
    /// is any unsaved state.
472
6
    pub fn store_persistent_state(&self) -> Result<(), GuardMgrError> {
473
6
        let inner = self.inner.lock().expect("Poisoned lock");
474
6
        trace!("Flushing guard state to disk.");
475
6
        inner.storage.store(&inner.guards)?;
476
6
        Ok(())
477
6
    }
478

            
479
    /// Reload state from the state manager.
480
    ///
481
    /// We only call this method if we _don't_ have the lock on the state
482
    /// files.  If we have the lock, we only want to save.
483
    #[instrument(level = "trace", skip_all)]
484
    pub fn reload_persistent_state(&self) -> Result<(), GuardMgrError> {
485
        let mut inner = self.inner.lock().expect("Poisoned lock");
486
        if let Some(new_guards) = inner.storage.load()? {
487
            inner.replace_guards_with(new_guards, self.runtime.wallclock(), self.runtime.now());
488
        }
489
        Ok(())
490
    }
491

            
492
    /// Switch from having an unowned persistent state to having an owned one.
493
    ///
494
    /// Requires that we hold the lock on the state files.
495
    #[instrument(level = "trace", skip_all)]
496
    pub fn upgrade_to_owned_persistent_state(&self) -> Result<(), GuardMgrError> {
497
        let mut inner = self.inner.lock().expect("Poisoned lock");
498
        debug_assert!(inner.storage.can_store());
499
        let new_guards = inner.storage.load()?.unwrap_or_default();
500
        let wallclock = self.runtime.wallclock();
501
        let now = self.runtime.now();
502
        inner.replace_guards_with(new_guards, wallclock, now);
503
        Ok(())
504
    }
505

            
506
    /// Return true if `netdir` has enough information to safely become our new netdir.
507
    pub fn netdir_is_sufficient(&self, netdir: &NetDir) -> bool {
508
        let mut inner = self.inner.lock().expect("Poisoned lock");
509
        if inner.guards.active_set.universe_type() != UniverseType::NetDir {
510
            // If we aren't using the netdir, this isn't something we want to look at.
511
            return true;
512
        }
513
        inner
514
            .guards
515
            .active_guards_mut()
516
            .n_primary_without_id_info_in(netdir)
517
            == 0
518
    }
519

            
520
    /// Mark every guard as potentially retriable, regardless of how recently we
521
    /// failed to connect to it.
522
    pub fn mark_all_guards_retriable(&self) {
523
        let mut inner = self.inner.lock().expect("Poisoned lock");
524
        inner.guards.active_guards_mut().mark_all_guards_retriable();
525
    }
526

            
527
    /// Configure this guardmgr to use a fixed [`NetDir`] instead of a provider.
528
    ///
529
    /// This function is for testing only, and is exclusive with
530
    /// `install_netdir_provider`.
531
    ///
532
    /// # Panics
533
    ///
534
    /// Panics if any [`NetDirProvider`] has already been installed.
535
    #[cfg(any(test, feature = "testing"))]
536
72
    pub fn install_test_netdir(&self, netdir: &NetDir) {
537
        use tor_netdir::testprovider::TestNetDirProvider;
538
72
        let wallclock = self.runtime.wallclock();
539
72
        let now = self.runtime.now();
540
72
        let netdir_provider: Arc<dyn NetDirProvider> =
541
72
            Arc::new(TestNetDirProvider::from(netdir.clone()));
542
72
        self.install_netdir_provider(&netdir_provider)
543
72
            .expect("Couldn't install testing network provider");
544

            
545
72
        let mut inner = self.inner.lock().expect("Poisoned lock");
546
72
        inner.update(wallclock, now);
547
72
    }
548

            
549
    /// Replace the configuration in this `GuardMgr` with `config`.
550
    #[instrument(level = "trace", skip_all)]
551
    pub fn reconfigure(
552
        &self,
553
        config: &impl GuardMgrConfig,
554
    ) -> Result<RetireCircuits, ReconfigureError> {
555
        let mut inner = self.inner.lock().expect("Poisoned lock");
556
        // Change the set of configured fallbacks.
557
        {
558
            let mut fallbacks: fallback::FallbackState = config.fallbacks().into();
559
            std::mem::swap(&mut inner.fallbacks, &mut fallbacks);
560
            inner.fallbacks.take_status_from(fallbacks);
561
        }
562
        // If we are built to use bridges, change the bridge configuration.
563
        #[cfg(feature = "bridge-client")]
564
        {
565
            let wallclock = self.runtime.wallclock();
566
            let now = self.runtime.now();
567
            Ok(inner.replace_bridge_config(config, wallclock, now)?)
568
        }
569
        // If we are built to use bridges, change the bridge configuration.
570
        #[cfg(not(feature = "bridge-client"))]
571
        {
572
            Ok(RetireCircuits::None)
573
        }
574
    }
575

            
576
    /// Replace the current [`GuardFilter`] used by this `GuardMgr`.
577
    // TODO should this be part of the config?
578
18
    pub fn set_filter(&self, filter: GuardFilter) {
579
18
        let wallclock = self.runtime.wallclock();
580
18
        let now = self.runtime.now();
581
18
        let mut inner = self.inner.lock().expect("Poisoned lock");
582
18
        inner.set_filter(filter, wallclock, now);
583
18
    }
584

            
585
    /// Select a guard for a given [`GuardUsage`].
586
    ///
587
    /// On success, we return a [`FirstHop`] object to identify which
588
    /// guard we have picked, a [`GuardMonitor`] object that the
589
    /// caller can use to report whether its attempt to use the guard
590
    /// succeeded or failed, and a [`GuardUsable`] future that the
591
    /// caller can use to decide whether a circuit built through the
592
    /// guard is actually safe to use.
593
    ///
594
    /// That last point is important: It's okay to build a circuit
595
    /// through the guard returned by this function, but you can't
596
    /// actually use it for traffic unless the [`GuardUsable`] future
597
    /// yields "true".
598
    #[instrument(skip_all, level = "trace")]
599
12698
    pub fn select_guard(
600
12698
        &self,
601
12698
        usage: GuardUsage,
602
12698
    ) -> Result<(FirstHop, GuardMonitor, GuardUsable), PickGuardError> {
603
12698
        let now = self.runtime.now();
604
12698
        let wallclock = self.runtime.wallclock();
605

            
606
12698
        let mut inner = self.inner.lock().expect("Poisoned lock");
607

            
608
        // (I am not 100% sure that we need to consider_all_retries here, but
609
        // it should _probably_ not hurt.)
610
12698
        inner.guards.active_guards_mut().consider_all_retries(now);
611

            
612
12698
        let (origin, guard) = inner.select_guard_with_expand(&usage, now, wallclock)?;
613
12698
        trace!(?guard, ?usage, "Guard selected");
614

            
615
12698
        let (usable, usable_sender) = if origin.usable_immediately() {
616
12686
            (GuardUsable::new_usable_immediately(), None)
617
        } else {
618
12
            let (u, snd) = GuardUsable::new_uncertain();
619
12
            (u, Some(snd))
620
        };
621
12698
        let request_id = pending::RequestId::next();
622
12698
        let ctrl = inner.ctrl.clone();
623
12698
        let monitor = GuardMonitor::new(request_id, ctrl);
624

            
625
        // Note that the network can be down even if all the primary guards
626
        // are not yet marked as unreachable.  But according to guard-spec we
627
        // don't want to acknowledge the net as down before that point, since
628
        // we don't mark all the primary guards as retriable unless
629
        // we've been forced to non-primary guards.
630
12698
        let net_has_been_down =
631
12698
            if let Some(duration) = tor_proto::time_since_last_incoming_traffic() {
632
                inner
633
                    .guards
634
                    .active_guards_mut()
635
                    .all_primary_guards_are_unreachable()
636
                    && duration >= inner.params.internet_down_timeout
637
            } else {
638
                // TODO: Is this the correct behavior in this case?
639
12698
                false
640
            };
641

            
642
12698
        let pending_request = pending::PendingRequest::new(
643
12698
            guard.first_hop_id(),
644
12698
            usage,
645
12698
            usable_sender,
646
12698
            net_has_been_down,
647
        );
648
12698
        inner.pending.insert(request_id, pending_request);
649

            
650
12698
        match &guard.sample {
651
12698
            Some(sample) => {
652
12698
                let guard_id = GuardId::from_relay_ids(&guard);
653
12698
                inner
654
12698
                    .guards
655
12698
                    .guards_mut(sample)
656
12698
                    .record_attempt(&guard_id, now);
657
12698
            }
658
            None => {
659
                // We don't record attempts for fallbacks; we only care when
660
                // they have failed.
661
            }
662
        }
663

            
664
12698
        Ok((guard, monitor, usable))
665
12698
    }
666

            
667
    /// Record that _after_ we built a circuit with a guard, something described
668
    /// in `external_failure` went wrong with it.
669
6
    pub fn note_external_failure<T>(&self, identity: &T, external_failure: ExternalActivity)
670
6
    where
671
6
        T: tor_linkspec::HasRelayIds + ?Sized,
672
    {
673
6
        let now = self.runtime.now();
674
6
        let mut inner = self.inner.lock().expect("Poisoned lock");
675
6
        let ids = inner.lookup_ids(identity);
676
6
        for id in ids {
677
6
            match &id.0 {
678
6
                FirstHopIdInner::Guard(sample, id) => {
679
6
                    inner
680
6
                        .guards
681
6
                        .guards_mut(sample)
682
6
                        .record_failure(id, Some(external_failure), now);
683
6
                }
684
                FirstHopIdInner::Fallback(id) => {
685
                    if external_failure == ExternalActivity::DirCache {
686
                        inner.fallbacks.note_failure(id, now);
687
                    }
688
                }
689
            }
690
        }
691
6
    }
692

            
693
    /// Record that _after_ we built a circuit with a guard, some activity
694
    /// described in `external_activity` was successful with it.
695
6
    pub fn note_external_success<T>(&self, identity: &T, external_activity: ExternalActivity)
696
6
    where
697
6
        T: tor_linkspec::HasRelayIds + ?Sized,
698
    {
699
6
        let mut inner = self.inner.lock().expect("Poisoned lock");
700

            
701
6
        inner.record_external_success(identity, external_activity, self.runtime.wallclock());
702
6
    }
703

            
704
    /// Return a stream of events about our estimated clock skew; these events
705
    /// are `None` when we don't have enough information to make an estimate,
706
    /// and `Some(`[`SkewEstimate`]`)` otherwise.
707
    ///
708
    /// Note that this stream can be lossy: if the estimate changes more than
709
    /// one before you read from the stream, you might only get the most recent
710
    /// update.
711
    pub fn skew_events(&self) -> ClockSkewEvents {
712
        let inner = self.inner.lock().expect("Poisoned lock");
713
        inner.recv_skew.clone()
714
    }
715

            
716
    /// Ensure that the message queue is flushed before proceeding to
717
    /// the next step.  Used for testing.
718
    #[cfg(test)]
719
36
    async fn flush_msg_queue(&self) {
720
36
        let (snd, rcv) = oneshot::channel();
721
36
        let pingmsg = daemon::Msg::Ping(snd);
722
36
        {
723
36
            let inner = self.inner.lock().expect("Poisoned lock");
724
36
            inner
725
36
                .ctrl
726
36
                .unbounded_send(pingmsg)
727
36
                .expect("Guard observer task exited prematurely.");
728
36
        }
729
36
        let _ = rcv.await;
730
36
    }
731
}
732

            
733
/// An activity that can succeed or fail, and whose success or failure can be
734
/// attributed to a guard.
735
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
736
#[non_exhaustive]
737
pub enum ExternalActivity {
738
    /// The activity of using the guard as a directory cache.
739
    DirCache,
740
}
741

            
742
impl GuardSets {
743
    /// Return a reference to the currently active set of guards.
744
    ///
745
    /// (That's easy enough for now, since there is never more than one set of
746
    /// guards.  But eventually that will change, as we add support for more
747
    /// complex filter types, and for bridge relays. Those will use separate
748
    /// `GuardSet` instances, and this accessor will choose the right one.)
749
8244
    fn active_guards(&self) -> &GuardSet {
750
8244
        self.guards(&self.active_set)
751
8244
    }
752

            
753
    /// Return the set of guards corresponding to the provided selector.
754
584504
    fn guards(&self, selector: &GuardSetSelector) -> &GuardSet {
755
584504
        match selector {
756
584468
            GuardSetSelector::Default => &self.default,
757
24
            GuardSetSelector::Restricted => &self.restricted,
758
            #[cfg(feature = "bridge-client")]
759
12
            GuardSetSelector::Bridges => &self.bridges,
760
        }
761
584504
    }
762

            
763
    /// Return a mutable reference to the currently active set of guards.
764
937908
    fn active_guards_mut(&mut self) -> &mut GuardSet {
765
937908
        self.guards_mut(&self.active_set.clone())
766
937908
    }
767

            
768
    /// Return a mutable reference to the set of guards corresponding to the
769
    /// provided selector.
770
1856924
    fn guards_mut(&mut self, selector: &GuardSetSelector) -> &mut GuardSet {
771
1856924
        match selector {
772
1856896
            GuardSetSelector::Default => &mut self.default,
773
28
            GuardSetSelector::Restricted => &mut self.restricted,
774
            #[cfg(feature = "bridge-client")]
775
            GuardSetSelector::Bridges => &mut self.bridges,
776
        }
777
1856924
    }
778

            
779
    /// Update all non-persistent state for the guards in this object with the
780
    /// state in `other`.
781
    fn copy_status_from(&mut self, mut other: GuardSets) {
782
        use strum::IntoEnumIterator;
783
        for sample in GuardSetSelector::iter() {
784
            self.guards_mut(&sample)
785
                .copy_ephemeral_status_into_newly_loaded_state(std::mem::take(
786
                    other.guards_mut(&sample),
787
                ));
788
        }
789
        self.active_set = other.active_set;
790
    }
791
}
792

            
793
impl GuardMgrInner {
794
    /// Look up the latest [`NetDir`] (if there is one) from our
795
    /// [`NetDirProvider`] (if we have one).
796
26608
    fn timely_netdir(&self) -> Option<Arc<NetDir>> {
797
26608
        self.netdir_provider
798
26608
            .as_ref()
799
26608
            .and_then(Weak::upgrade)
800
26608
            .and_then(|np| np.timely_netdir().ok())
801
26608
    }
802

            
803
    /// Look up the latest [`BridgeDescList`](bridge::BridgeDescList) (if there
804
    /// is one) from our [`BridgeDescProvider`](bridge::BridgeDescProvider) (if
805
    /// we have one).
806
    #[cfg(feature = "bridge-client")]
807
    fn latest_bridge_desc_list(&self) -> Option<Arc<bridge::BridgeDescList>> {
808
        self.bridge_desc_provider
809
            .as_ref()
810
            .and_then(Weak::upgrade)
811
            .map(|bp| bp.bridges())
812
    }
813

            
814
    /// Run a function that takes `&mut self` and an optional NetDir.
815
    ///
816
    /// We try to use the netdir from our [`NetDirProvider`] (if we have one).
817
    /// Therefore, although its _parameters_ are suitable for every
818
    /// [`GuardSet`], its _contents_ might not be. For those, call
819
    /// [`with_opt_universe`](Self::with_opt_universe) instead.
820
    //
821
    // This function exists to handle the lifetime mess where sometimes the
822
    // resulting NetDir will borrow from `netdir`, and sometimes it will borrow
823
    // from an Arc returned by `self.latest_netdir()`.
824
8244
    fn with_opt_netdir<F, T>(&mut self, func: F) -> T
825
8244
    where
826
8244
        F: FnOnce(&mut Self, Option<&NetDir>) -> T,
827
    {
828
8244
        if let Some(nd) = self.timely_netdir() {
829
1878
            func(self, Some(nd.as_ref()))
830
        } else {
831
6366
            func(self, None)
832
        }
833
8244
    }
834

            
835
    /// Return the latest `BridgeSet` based on our `BridgeDescProvider` and our
836
    /// configured bridges.
837
    ///
838
    /// Returns `None` if we are not configured to use bridges.
839
    #[cfg(feature = "bridge-client")]
840
    fn latest_bridge_set(&self) -> Option<bridge::BridgeSet> {
841
        let bridge_config = self.configured_bridges.as_ref()?.clone();
842
        let bridge_descs = self.latest_bridge_desc_list();
843
        Some(bridge::BridgeSet::new(bridge_config, bridge_descs))
844
    }
845

            
846
    /// Run a function that takes `&mut self` and an optional [`UniverseRef`].
847
    ///
848
    /// We try to get a universe from the appropriate source for the current
849
    /// active guard set.
850
18364
    fn with_opt_universe<F, T>(&mut self, func: F) -> T
851
18364
    where
852
18364
        F: FnOnce(&mut Self, Option<&UniverseRef>) -> T,
853
    {
854
        // TODO: it might be nice to make `func` take an GuardSet and a set of
855
        // parameters, so we can't get the active set wrong. Doing that will
856
        // require a fair amount of refactoring so that the borrow checker is
857
        // happy, however.
858
18364
        match self.guards.active_set.universe_type() {
859
            UniverseType::NetDir => {
860
18364
                if let Some(nd) = self.timely_netdir() {
861
11998
                    func(self, Some(&UniverseRef::NetDir(nd)))
862
                } else {
863
6366
                    func(self, None)
864
                }
865
            }
866
            #[cfg(feature = "bridge-client")]
867
            UniverseType::BridgeSet => func(
868
                self,
869
                self.latest_bridge_set()
870
                    .map(UniverseRef::BridgeSet)
871
                    .as_ref(),
872
            ),
873
        }
874
18364
    }
875

            
876
    /// Update the status of all guards in the active set, based on the passage
877
    /// of time, our configuration, and the relevant Universe for our active
878
    /// set.
879
    #[instrument(skip_all, level = "trace")]
880
8244
    fn update(&mut self, wallclock: SystemTime, now: Instant) {
881
8244
        self.with_opt_netdir(|this, netdir| {
882
            // Here we update our parameters from the latest NetDir, and check
883
            // whether we need to change to a (non)-restrictive GuardSet based
884
            // on those parameters and our configured filter.
885
            //
886
            // This uses a NetDir unconditionally, since we always want to take
887
            // the network parameters our parameters from the consensus even if
888
            // the guards themselves are from a BridgeSet.
889
8244
            this.update_active_set_params_and_filter(netdir);
890
8244
        });
891
8244
        self.with_opt_universe(|this, univ| {
892
            // Now we update the set of guards themselves based on the
893
            // Universe, which is either the latest NetDir, or the latest
894
            // BridgeSet—depending on what the GuardSet wants.
895
8244
            Self::update_guardset_internal(
896
8244
                &this.params,
897
8244
                wallclock,
898
8244
                this.guards.active_set.universe_type(),
899
8244
                this.guards.active_guards_mut(),
900
8244
                univ,
901
            );
902
            #[cfg(feature = "bridge-client")]
903
8244
            this.update_desired_descriptors(now);
904
            #[cfg(not(feature = "bridge-client"))]
905
            let _ = now;
906
8244
        });
907
8244
    }
908

            
909
    /// Replace our bridge configuration with the one from `new_config`.
910
    #[cfg(feature = "bridge-client")]
911
    #[instrument(level = "trace", skip_all)]
912
340
    fn replace_bridge_config(
913
340
        &mut self,
914
340
        new_config: &impl GuardMgrConfig,
915
340
        wallclock: SystemTime,
916
340
        now: Instant,
917
340
    ) -> Result<RetireCircuits, GuardMgrConfigError> {
918
340
        match (&self.configured_bridges, new_config.bridges_enabled()) {
919
            (None, false) => {
920
340
                assert_ne!(
921
340
                    self.guards.active_set.universe_type(),
922
                    UniverseType::BridgeSet
923
                );
924
340
                return Ok(RetireCircuits::None); // nothing to do
925
            }
926
            (_, true) if !self.storage.can_store() => {
927
                // TODO: Ideally we would try to upgrade, obtaining an exclusive lock,
928
                // but `StorageHandle` currently lacks a method for that.
929
                return Err(GuardMgrConfigError::NoLock("bridges configured".into()));
930
            }
931
            (Some(current_bridges), true) if new_config.bridges() == current_bridges.as_ref() => {
932
                assert_eq!(
933
                    self.guards.active_set.universe_type(),
934
                    UniverseType::BridgeSet
935
                );
936
                return Ok(RetireCircuits::None); // nothing to do.
937
            }
938
            (_, true) => {
939
                self.configured_bridges = Some(new_config.bridges().into());
940
                self.guards.active_set = GuardSetSelector::Bridges;
941
            }
942
            (_, false) => {
943
                self.configured_bridges = None;
944
                self.guards.active_set = GuardSetSelector::Default;
945
            }
946
        }
947

            
948
        // If we have gotten here, we have changed the set of bridges, changed
949
        // which set is active, or changed them both.  We need to make sure that
950
        // our `GuardSet` object is up-to-date with our configuration.
951
        self.update(wallclock, now);
952

            
953
        // We also need to tell the caller that its circuits are no good any
954
        // more.
955
        //
956
        // TODO(nickm): Someday we can do this more judiciously by retuning
957
        // "Some" in the case where we're still using bridges but our new bridge
958
        // set contains different elements; see comment on RetireCircuits.
959
        //
960
        // TODO(nickm): We could also safely return RetireCircuits::None if we
961
        // are using bridges, and our new bridge list is a superset of the older
962
        // one.
963
        Ok(RetireCircuits::All)
964
340
    }
965

            
966
    /// Update our parameters, our selection (based on network parameters and
967
    /// configuration), and make sure the active GuardSet has the right
968
    /// configuration itself.
969
    ///
970
    /// We should call this whenever the NetDir's parameters change, or whenever
971
    /// our filter changes.  We do not need to call it for new elements arriving
972
    /// in our Universe, since those do not affect anything here.
973
    ///
974
    /// We should also call this whenever a new GuardSet becomes active for any
975
    /// reason _other_ than just having called this function.
976
    ///
977
    /// (This function is only invoked from `update`, which should be called
978
    /// under the above circumstances.)
979
8244
    fn update_active_set_params_and_filter(&mut self, netdir: Option<&NetDir>) {
980
        // Set the parameters.  These always come from the NetDir, even if this
981
        // is a bridge set.
982
8244
        if let Some(netdir) = netdir {
983
1878
            match GuardParams::try_from(netdir.params()) {
984
1878
                Ok(params) => self.params = params,
985
                Err(e) => warn!("Unusable guard parameters from consensus: {}", e),
986
            }
987

            
988
1878
            self.select_guard_set_based_on_filter(netdir);
989
6366
        }
990

            
991
        // Change the filter, if it doesn't match what the guards have.
992
        //
993
        // TODO(nickm): We could use a "dirty" flag or something to decide
994
        // whether we need to call set_filter, if this comparison starts to show
995
        // up in profiles.
996
8244
        if self.guards.active_guards().filter() != &self.filter {
997
540
            let restrictive = self.guards.active_set == GuardSetSelector::Restricted;
998
540
            self.guards
999
540
                .active_guards_mut()
540
                .set_filter(self.filter.clone(), restrictive);
7704
        }
8244
    }
    /// Update the status of every guard in `active_guards`, and expand it as
    /// needed.
    ///
    /// This function doesn't take `&self`, to make sure that we are only
    /// affecting a single `GuardSet`, and to avoid confusing the borrow
    /// checker.
    ///
    /// We should call this whenever the contents of the universe have changed.
    ///
    /// We should also call this whenever a new GuardSet becomes active.
18364
    fn update_guardset_internal<U: Universe>(
18364
        params: &GuardParams,
18364
        now: SystemTime,
18364
        universe_type: UniverseType,
18364
        active_guards: &mut GuardSet,
18364
        universe: Option<&U>,
18364
    ) -> ExtendedStatus {
        // Expire guards.  Do that early, in case doing so makes it clear that
        // we need to grab more guards or mark others as primary.
18364
        active_guards.expire_old_guards(params, now);
18364
        let extended = if let Some(universe) = universe {
            // TODO: This check here may be completely unnecessary. I inserted
            // it back in 5ac0fcb7ef603e0d14 because I was originally concerned
            // it might be undesirable to list a primary guard as "missing dir
            // info" (and therefore unusable) if we were expecting to get its
            // microdescriptor "very soon."
            //
            // But due to the other check in `netdir_is_sufficient`, we
            // shouldn't be installing a netdir until it has microdescs for all
            // of the (non-bridge) primary guards that it lists. - nickm
11998
            let n = active_guards.n_primary_without_id_info_in(universe);
11998
            if n > 0 && universe_type == UniverseType::NetDir {
                // We are missing the information from a NetDir needed to see
                // whether our primary guards are listed, so we shouldn't update
                // our guard status.
                //
                // We don't want to do this check if we are using bridges, since
                // a missing bridge descriptor is not guaranteed to temporary
                // problem in the same way that a missing microdescriptor is.
                // (When a bridge desc is missing, the bridge could be down or
                // unreachable, and nobody else can help us. But if a microdesc
                // is missing, we just need to find a cache that has it.)
                trace!(
                    n_primary_without_id_info = n,
                    "Not extending guardset, missing information."
                );
                return ExtendedStatus::No;
11998
            }
11998
            active_guards.update_status_from_dir(universe);
11998
            active_guards.extend_sample_as_needed(now, params, universe)
        } else {
6366
            trace!("Not extending guardset, no universe given.");
6366
            ExtendedStatus::No
        };
18364
        active_guards.select_primary_guards(params);
18364
        extended
18364
    }
    /// If using bridges, tell the BridgeDescProvider which descriptors we want.
    /// We need to check this *after* we select our primary guards.
    #[cfg(feature = "bridge-client")]
8244
    fn update_desired_descriptors(&mut self, now: Instant) {
8244
        if self.guards.active_set.universe_type() != UniverseType::BridgeSet {
8244
            return;
        }
        let provider = self.bridge_desc_provider.as_ref().and_then(Weak::upgrade);
        let bridge_set = self.latest_bridge_set();
        if let (Some(provider), Some(bridge_set)) = (provider, bridge_set) {
            let desired: Vec<_> = self
                .guards
                .active_guards()
                .descriptors_to_request(now, &self.params)
                .into_iter()
                .flat_map(|guard| bridge_set.bridge_by_guard(guard))
                .cloned()
                .collect();
            provider.set_bridges(&desired);
        }
8244
    }
    /// Replace the active guard state with `new_state`, preserving
    /// non-persistent state for any guards that are retained.
    #[instrument(level = "trace", skip_all)]
    fn replace_guards_with(
        &mut self,
        mut new_guards: GuardSets,
        wallclock: SystemTime,
        now: Instant,
    ) {
        std::mem::swap(&mut self.guards, &mut new_guards);
        self.guards.copy_status_from(new_guards);
        self.update(wallclock, now);
    }
    /// Update which guard set is active based on the current filter and the
    /// provided netdir.
    ///
    /// After calling this function, the new guard set's filter may be
    /// out-of-date: be sure to call `set_filter` as appropriate.
1878
    fn select_guard_set_based_on_filter(&mut self, netdir: &NetDir) {
        // In general, we'd like to use the restricted set if we're under the
        // threshold, and the default set if we're over the threshold.  But if
        // we're sitting close to the threshold, we want to avoid flapping back
        // and forth, so we only change when we're more than 5% "off" from
        // whatever our current setting is.
        //
        // (See guard-spec section 2 for more information.)
1878
        let offset = match self.guards.active_set {
1878
            GuardSetSelector::Default => -0.05,
            GuardSetSelector::Restricted => 0.05,
            // If we're using bridges, then we don't switch between the other guard sets based on the filter at all.
            #[cfg(feature = "bridge-client")]
            GuardSetSelector::Bridges => return,
        };
1878
        let frac_permitted = self.filter.frac_bw_permitted(netdir);
1878
        let threshold = self.params.filter_threshold + offset;
1878
        let new_choice = if frac_permitted < threshold {
6
            GuardSetSelector::Restricted
        } else {
1872
            GuardSetSelector::Default
        };
1878
        if new_choice != self.guards.active_set {
6
            info!(
                "Guard selection changed; we are now using the {:?} guard set",
                &new_choice
            );
6
            self.guards.active_set = new_choice;
6
            if frac_permitted < self.params.extreme_threshold {
                warn!(
                    "The number of guards permitted is smaller than the recommended minimum of {:.0}%.",
                    self.params.extreme_threshold * 100.0,
                );
6
            }
1872
        }
1878
    }
    /// Mark all of our primary guards as retriable, if we haven't done
    /// so since long enough before `now`.
    ///
    /// We want to call this function whenever a guard attempt succeeds,
    /// if the internet seemed to be down when the guard attempt was
    /// first launched.
    fn maybe_retry_primary_guards(&mut self, now: Instant) {
        // We don't actually want to mark our primary guards as
        // retriable more than once per internet_down_timeout: after
        // the first time, we would just be noticing the same "coming
        // back online" event more than once.
        let interval = self.params.internet_down_timeout;
        if self.last_primary_retry_time + interval <= now {
            debug!(
                "Successfully reached a guard after a while off the internet; marking all primary guards retriable."
            );
            self.guards
                .active_guards_mut()
                .mark_primary_guards_retriable();
            self.last_primary_retry_time = now;
        }
    }
    /// Replace the current GuardFilter with `filter`.
    #[instrument(level = "trace", skip_all)]
534
    fn set_filter(&mut self, filter: GuardFilter, wallclock: SystemTime, now: Instant) {
534
        self.filter = filter;
534
        self.update(wallclock, now);
534
    }
    /// Called when the circuit manager reports (via [`GuardMonitor`]) that
    /// a guard succeeded or failed.
    ///
    /// Changes the guard's status as appropriate, and updates the pending
    /// request as needed.
8294
    pub(crate) fn handle_msg(
8294
        &mut self,
8294
        request_id: RequestId,
8294
        status: GuardStatus,
8294
        skew: Option<ClockSkew>,
8294
        runtime: &impl tor_rtcompat::SleepProvider,
8294
    ) {
8294
        if let Some(mut pending) = self.pending.remove(&request_id) {
            // If there was a pending request matching this RequestId, great!
8294
            let guard_id = pending.guard_id();
8294
            trace!(?guard_id, ?status, "Received report of guard status");
            // First, handle the skew report (if any)
8294
            if let Some(skew) = skew {
                let now = runtime.now();
                let observation = skew::SkewObservation { skew, when: now };
                match &guard_id.0 {
                    FirstHopIdInner::Guard(_, id) => {
                        self.guards.active_guards_mut().record_skew(id, observation);
                    }
                    FirstHopIdInner::Fallback(id) => {
                        self.fallbacks.note_skew(id, observation);
                    }
                }
                // TODO: We call this whenever we receive an observed clock
                // skew. That's not the perfect timing for two reasons.  First
                // off, it might be too frequent: it does an O(n) calculation,
                // which isn't ideal.  Second, it might be too infrequent: after
                // an hour has passed, a given observation won't be up-to-date
                // any more, and we might want to recalculate the skew
                // accordingly.
                self.update_skew(now);
8294
            }
8294
            match (status, &guard_id.0) {
                (GuardStatus::Failure, FirstHopIdInner::Fallback(id)) => {
                    // We used a fallback, and we weren't able to build a circuit through it.
                    self.fallbacks.note_failure(id, runtime.now());
                }
                (_, FirstHopIdInner::Fallback(_)) => {
                    // We don't record any other kind of circuit activity if we
                    // took the entry from the fallback list.
                }
254
                (GuardStatus::Success, FirstHopIdInner::Guard(sample, id)) => {
                    // If we had gone too long without any net activity when we
                    // gave out this guard, and now we're seeing a circuit
                    // succeed, tell the primary guards that they might be
                    // retriable.
254
                    if pending.net_has_been_down() {
                        self.maybe_retry_primary_guards(runtime.now());
254
                    }
                    // The guard succeeded.  Tell the GuardSet.
254
                    self.guards.guards_mut(sample).record_success(
254
                        id,
254
                        &self.params,
254
                        None,
254
                        runtime.wallclock(),
                    );
                    // Either tell the request whether the guard is
                    // usable, or schedule it as a "waiting" request.
254
                    if let Some(usable) = self.guard_usability_status(&pending, runtime.now()) {
254
                        trace!(?guard_id, usable, "Known usability status");
254
                        pending.reply(usable);
                    } else {
                        // This is the one case where we can't use the
                        // guard yet.
                        trace!(?guard_id, "Not able to answer right now");
                        pending.mark_waiting(runtime.now());
                        self.waiting.push(pending);
                    }
                }
18
                (GuardStatus::Failure, FirstHopIdInner::Guard(sample, id)) => {
18
                    self.guards
18
                        .guards_mut(sample)
18
                        .record_failure(id, None, runtime.now());
18
                    pending.reply(false);
18
                }
8022
                (GuardStatus::AttemptAbandoned, FirstHopIdInner::Guard(sample, id)) => {
8022
                    self.guards.guards_mut(sample).record_attempt_abandoned(id);
8022
                    pending.reply(false);
8022
                }
                (GuardStatus::Indeterminate, FirstHopIdInner::Guard(sample, id)) => {
                    self.guards
                        .guards_mut(sample)
                        .record_indeterminate_result(id);
                    pending.reply(false);
                }
            };
        } else {
            warn!(
                "Got a status {:?} for a request {:?} that wasn't pending",
                status, request_id
            );
        }
        // We might need to update the primary guards based on changes in the
        // status of guards above.
8294
        self.guards
8294
            .active_guards_mut()
8294
            .select_primary_guards(&self.params);
        // Some waiting request may just have become ready (usable or
        // not); we need to give them the information they're waiting
        // for.
8294
        self.expire_and_answer_pending_requests(runtime.now());
8294
    }
    /// Helper to implement `GuardMgr::note_external_success()`.
    ///
    /// (This has to be a separate function so that we can borrow params while
    /// we have `mut self` borrowed.)
6
    fn record_external_success<T>(
6
        &mut self,
6
        identity: &T,
6
        external_activity: ExternalActivity,
6
        now: SystemTime,
6
    ) where
6
        T: tor_linkspec::HasRelayIds + ?Sized,
    {
6
        for id in self.lookup_ids(identity) {
6
            match &id.0 {
6
                FirstHopIdInner::Guard(sample, id) => {
6
                    self.guards.guards_mut(sample).record_success(
6
                        id,
6
                        &self.params,
6
                        Some(external_activity),
6
                        now,
6
                    );
6
                }
                FirstHopIdInner::Fallback(id) => {
                    if external_activity == ExternalActivity::DirCache {
                        self.fallbacks.note_success(id);
                    }
                }
            }
        }
6
    }
    /// Return an iterator over all of the clock skew observations we've made
    /// for guards or fallbacks.
    fn skew_observations(&self) -> impl Iterator<Item = &skew::SkewObservation> {
        self.fallbacks
            .skew_observations()
            .chain(self.guards.active_guards().skew_observations())
    }
    /// Recalculate our estimated clock skew, and publish it to anybody who
    /// cares.
    fn update_skew(&mut self, now: Instant) {
        let estimate = skew::SkewEstimate::estimate_skew(self.skew_observations(), now);
        // TODO: we might want to do this only conditionally, when the skew
        // estimate changes.
        *self.send_skew.borrow_mut() = estimate;
    }
    /// If the circuit built because of a given [`PendingRequest`] may
    /// now be used (or discarded), return `Some(true)` or
    /// `Some(false)` respectively.
    ///
    /// Return None if we can't yet give an answer about whether such
    /// a circuit is usable.
10230
    fn guard_usability_status(&self, pending: &PendingRequest, now: Instant) -> Option<bool> {
10230
        match &pending.guard_id().0 {
10230
            FirstHopIdInner::Guard(sample, id) => self.guards.guards(sample).circ_usability_status(
10230
                id,
10230
                pending.usage(),
10230
                &self.params,
10230
                now,
            ),
            // Fallback circuits are usable immediately, since we don't have to wait to
            // see whether any _other_ circuit succeeds or fails.
            FirstHopIdInner::Fallback(_) => Some(true),
        }
10230
    }
    /// For requests that have been "waiting" for an answer for too long,
    /// expire them and tell the circuit manager that their circuits
    /// are unusable.
368962
    fn expire_and_answer_pending_requests(&mut self, now: Instant) {
        // A bit ugly: we use a separate Vec here to avoid borrowing issues,
        // and put it back when we're done.
368962
        let mut waiting = Vec::new();
368962
        std::mem::swap(&mut waiting, &mut self.waiting);
368962
        waiting.retain_mut(|pending| {
            let expired = pending
                .waiting_since()
                .and_then(|w| now.checked_duration_since(w))
                .map(|d| d >= self.params.np_idle_timeout)
                == Some(true);
            if expired {
                trace!(?pending, "Pending request expired");
                pending.reply(false);
                return false;
            }
            // TODO-SPEC: guard_usability_status isn't what the spec says.  It
            // says instead that we should look at _circuit_ status, saying:
            //  "   Definition: In the algorithm above, C2 "blocks" C1 if:
            // * C2 obeys all the restrictions that C1 had to obey, AND
            // * C2 has higher priority than C1, AND
            // * Either C2 is <complete>, or C2 is <waiting_for_better_guard>,
            // or C2 has been <usable_if_no_better_guard> for no more than
            // {NONPRIMARY_GUARD_CONNECT_TIMEOUT} seconds."
            //
            // See comments in sample::GuardSet::circ_usability_status.
            if let Some(answer) = self.guard_usability_status(pending, now) {
                trace!(?pending, answer, "Pending request now ready");
                pending.reply(answer);
                return false;
            }
            true
        });
        // Put the waiting list back.
368962
        std::mem::swap(&mut waiting, &mut self.waiting);
368962
    }
    /// Return every currently extant FirstHopId for a guard or fallback
    /// directory matching (or possibly matching) the provided keys.
    ///
    /// An identity is _possibly matching_ if it contains some of the IDs in the
    /// provided identity, and it has no _contradictory_ identities, but it does
    /// not necessarily contain _all_ of those identities.
    ///
    /// # TODO
    ///
    /// This function should probably not exist; it's only used so that dirmgr
    /// can report successes or failures, since by the time it observes them it
    /// doesn't know whether its circuit came from a guard or a fallback.  To
    /// solve that, we'll need CircMgr to record and report which one it was
    /// using, which will take some more plumbing.
    ///
    /// TODO relay: we will have to make the change above when we implement
    /// relays; otherwise, it would be possible for an attacker to exploit it to
    /// mislead us about our guard status.
12
    fn lookup_ids<T>(&self, identity: &T) -> Vec<FirstHopId>
12
    where
12
        T: tor_linkspec::HasRelayIds + ?Sized,
    {
        use strum::IntoEnumIterator;
12
        let mut vec = Vec::with_capacity(2);
12
        let id = ids::GuardId::from_relay_ids(identity);
36
        for sample in GuardSetSelector::iter() {
36
            let guard_id = match self.guards.guards(&sample).contains(&id) {
12
                Ok(true) => &id,
                Err(other) => other,
24
                Ok(false) => continue,
            };
12
            vec.push(FirstHopId(FirstHopIdInner::Guard(sample, guard_id.clone())));
        }
12
        let id = ids::FallbackId::from_relay_ids(identity);
12
        if self.fallbacks.contains(&id) {
            vec.push(id.into());
12
        }
12
        vec
12
    }
    /// Run any periodic events that update guard status, and return a
    /// duration after which periodic events should next be run.
    #[instrument(skip_all, level = "trace")]
5832
    pub(crate) fn run_periodic_events(&mut self, wallclock: SystemTime, now: Instant) -> Duration {
5832
        self.update(wallclock, now);
5832
        self.expire_and_answer_pending_requests(now);
5832
        Duration::from_secs(1) // TODO: Too aggressive.
5832
    }
    /// Try to select a guard, expanding the sample if the first attempt fails.
    #[instrument(skip_all, level = "trace")]
555874
    fn select_guard_with_expand(
555874
        &mut self,
555874
        usage: &GuardUsage,
555874
        now: Instant,
555874
        wallclock: SystemTime,
555874
    ) -> Result<(sample::ListKind, FirstHop), PickGuardError> {
        // Try to find a guard.
555874
        let first_error = match self.select_guard_once(usage, now) {
545754
            Ok(res1) => return Ok(res1),
10120
            Err(e) => {
10120
                trace!("Couldn't select guard on first attempt: {}", e);
10120
                e
            }
        };
        // That didn't work. If we have a netdir, expand the sample and try again.
10120
        let res = self.with_opt_universe(|this, univ| {
10120
            let univ = univ?;
10120
            trace!("No guards available, trying to extend the sample.");
            // Make sure that the status on all of our guards are accurate, and
            // expand the sample if we can.
            //
            // Our parameters and configuration did not change, so we do not
            // need to call update() or update_active_set_and_filter(). This
            // call is sufficient to  extend the sample and recompute primary
            // guards.
10120
            let extended = Self::update_guardset_internal(
10120
                &this.params,
10120
                wallclock,
10120
                this.guards.active_set.universe_type(),
10120
                this.guards.active_guards_mut(),
10120
                Some(univ),
            );
10120
            if extended == ExtendedStatus::Yes {
10120
                match this.select_guard_once(usage, now) {
10120
                    Ok(res) => return Some(res),
                    Err(e) => {
                        trace!("Couldn't select guard after update: {}", e);
                    }
                }
            }
            None
10120
        });
10120
        if let Some(res) = res {
10120
            return Ok(res);
        }
        // Okay, that didn't work either.  If we were asked for a directory
        // guard, and we aren't using bridges, then we may be able to use a
        // fallback.
        if usage.kind == GuardUsageKind::OneHopDirectory
            && self.guards.active_set.universe_type() == UniverseType::NetDir
        {
            return self.select_fallback(now);
        }
        // Couldn't extend the sample or use a fallback; return the original error.
        Err(first_error)
555874
    }
    /// Helper: try to pick a single guard, without retrying on failure.
565994
    fn select_guard_once(
565994
        &self,
565994
        usage: &GuardUsage,
565994
        now: Instant,
565994
    ) -> Result<(sample::ListKind, FirstHop), PickGuardError> {
565994
        let active_set = &self.guards.active_set;
        #[cfg_attr(not(feature = "bridge-client"), allow(unused_mut))]
555874
        let (list_kind, mut first_hop) =
565994
            self.guards
565994
                .guards(active_set)
565994
                .pick_guard(active_set, usage, &self.params, now)?;
        #[cfg(feature = "bridge-client")]
555874
        if self.guards.active_set.universe_type() == UniverseType::BridgeSet {
            // See if we can promote first_hop to a viable CircTarget.
            let bridges = self.latest_bridge_set().ok_or_else(|| {
                PickGuardError::Internal(internal!(
                    "No bridge set available, even though this is the Bridges sample"
                ))
            })?;
            first_hop.lookup_bridge_circ_target(&bridges);
            if usage.kind == GuardUsageKind::Data && !first_hop.contains_circ_target() {
                return Err(PickGuardError::Internal(internal!(
                    "Tried to return a non-circtarget guard with Data usage!"
                )));
            }
555874
        }
555874
        Ok((list_kind, first_hop))
565994
    }
    /// Helper: Select a fallback directory.
    ///
    /// Called when we have no guard information to use. Return values are as
    /// for [`GuardMgr::select_guard()`]
    fn select_fallback(
        &self,
        now: Instant,
    ) -> Result<(sample::ListKind, FirstHop), PickGuardError> {
        let filt = self.guards.active_guards().filter();
        let fallback = crate::FirstHop {
            sample: None,
            inner: crate::FirstHopInner::Chan(OwnedChanTarget::from_chan_target(
                self.fallbacks.choose(&mut rand::rng(), now, filt)?,
            )),
        };
        let fallback = filt.modify_hop(fallback)?;
        Ok((sample::ListKind::Fallback, fallback))
    }
}
/// A possible outcome of trying to extend a guard sample.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum ExtendedStatus {
    /// The guard sample was extended. (At least one guard was added to it.)
    Yes,
    /// The guard sample was not extended.
    No,
}
/// A set of parameters, derived from the consensus document, controlling
/// the behavior of a guard manager.
#[derive(Debug, Clone)]
#[cfg_attr(test, derive(PartialEq))]
struct GuardParams {
    /// How long should a sampled, un-confirmed guard be kept in the sample before it expires?
    lifetime_unconfirmed: Duration,
    /// How long should a confirmed guard be kept in the sample before
    /// it expires?
    lifetime_confirmed: Duration,
    /// How long may  a guard be unlisted before we remove it from the sample?
    lifetime_unlisted: Duration,
    /// Largest number of guards we're willing to add to the sample.
    max_sample_size: usize,
    /// Largest fraction of the network's guard bandwidth that we're
    /// willing to add to the sample.
    max_sample_bw_fraction: f64,
    /// Smallest number of guards that we're willing to have in the
    /// sample, after applying a [`GuardFilter`].
    min_filtered_sample_size: usize,
    /// How many guards are considered "Primary"?
    n_primary: usize,
    /// When making a regular circuit, how many primary guards should we
    /// be willing to try?
    data_parallelism: usize,
    /// When making a one-hop directory circuit, how many primary
    /// guards should we be willing to try?
    dir_parallelism: usize,
    /// For how long does a pending attempt to connect to a guard
    /// block an attempt to use a less-favored non-primary guard?
    np_connect_timeout: Duration,
    /// How long do we allow a circuit to a successful but unfavored
    /// non-primary guard to sit around before deciding not to use it?
    np_idle_timeout: Duration,
    /// After how much time without successful activity does a
    /// successful circuit indicate that we should retry our primary
    /// guards?
    internet_down_timeout: Duration,
    /// What fraction of the guards can be can be filtered out before we
    /// decide that our filter is "very restrictive"?
    filter_threshold: f64,
    /// What fraction of the guards determine that our filter is "very
    /// restrictive"?
    extreme_threshold: f64,
}
impl Default for GuardParams {
13685
    fn default() -> Self {
13685
        let one_day = Duration::from_secs(86400);
13685
        GuardParams {
13685
            lifetime_unconfirmed: one_day * 120,
13685
            lifetime_confirmed: one_day * 60,
13685
            lifetime_unlisted: one_day * 20,
13685
            max_sample_size: 60,
13685
            max_sample_bw_fraction: 0.2,
13685
            min_filtered_sample_size: 20,
13685
            n_primary: 3,
13685
            data_parallelism: 1,
13685
            dir_parallelism: 3,
13685
            np_connect_timeout: Duration::from_secs(15),
13685
            np_idle_timeout: Duration::from_secs(600),
13685
            internet_down_timeout: Duration::from_secs(600),
13685
            filter_threshold: 0.2,
13685
            extreme_threshold: 0.01,
13685
        }
13685
    }
}
impl TryFrom<&NetParameters> for GuardParams {
    type Error = tor_units::Error;
1879
    fn try_from(p: &NetParameters) -> Result<GuardParams, Self::Error> {
        Ok(GuardParams {
1879
            lifetime_unconfirmed: p.guard_lifetime_unconfirmed.try_into()?,
1879
            lifetime_confirmed: p.guard_lifetime_confirmed.try_into()?,
1879
            lifetime_unlisted: p.guard_remove_unlisted_after.try_into()?,
1879
            max_sample_size: p.guard_max_sample_size.try_into()?,
1879
            max_sample_bw_fraction: p.guard_max_sample_threshold.as_fraction(),
1879
            min_filtered_sample_size: p.guard_filtered_min_sample_size.try_into()?,
1879
            n_primary: p.guard_n_primary.try_into()?,
1879
            data_parallelism: p.guard_use_parallelism.try_into()?,
1879
            dir_parallelism: p.guard_dir_use_parallelism.try_into()?,
1879
            np_connect_timeout: p.guard_nonprimary_connect_timeout.try_into()?,
1879
            np_idle_timeout: p.guard_nonprimary_idle_timeout.try_into()?,
1879
            internet_down_timeout: p.guard_internet_likely_down.try_into()?,
1879
            filter_threshold: p.guard_meaningful_restriction.as_fraction(),
1879
            extreme_threshold: p.guard_extreme_restriction.as_fraction(),
        })
1879
    }
}
/// Representation of a guard or fallback, as returned by [`GuardMgr::select_guard()`].
#[derive(Debug, Clone)]
pub struct FirstHop {
    /// The sample from which this guard was taken, or `None` if this is a fallback.
    sample: Option<GuardSetSelector>,
    /// Information about connecting to (or through) this guard.
    inner: FirstHopInner,
}
/// The enumeration inside a FirstHop that holds information about how to
/// connect to (and possibly through) a guard or fallback.
#[derive(Debug, Clone)]
enum FirstHopInner {
    /// We have enough information to connect to a guard.
    Chan(OwnedChanTarget),
    /// We have enough information to connect to a guards _and_ to build
    /// multihop circuits through it.
    #[cfg_attr(not(feature = "bridge-client"), allow(dead_code))]
    Circ(OwnedCircTarget),
}
impl FirstHop {
    /// Return a new [`FirstHopId`] for this `FirstHop`.
555874
    fn first_hop_id(&self) -> FirstHopId {
555874
        match &self.sample {
555874
            Some(sample) => {
555874
                let guard_id = GuardId::from_relay_ids(self);
555874
                FirstHopId::in_sample(sample.clone(), guard_id)
            }
            None => {
                let fallback_id = crate::ids::FallbackId::from_relay_ids(self);
                FirstHopId::from(fallback_id)
            }
        }
555874
    }
    /// Look up this guard in `netdir`.
544984
    pub fn get_relay<'a>(&self, netdir: &'a NetDir) -> Option<Relay<'a>> {
544984
        match &self.sample {
            #[cfg(feature = "bridge-client")]
            // Always return "None" for anything that isn't in the netdir.
544984
            Some(s) if s.universe_type() == UniverseType::BridgeSet => None,
            // Otherwise ask the netdir.
544984
            _ => netdir.by_ids(self),
        }
544984
    }
    /// Return true if this guard is a bridge.
    pub fn is_bridge(&self) -> bool {
        match &self.sample {
            #[cfg(feature = "bridge-client")]
            Some(s) if s.universe_type() == UniverseType::BridgeSet => true,
            _ => false,
        }
    }
    /// If possible, return a view of this object that can be used to build a circuit.
544984
    pub fn as_circ_target(&self) -> Option<&OwnedCircTarget> {
544984
        match &self.inner {
544984
            FirstHopInner::Chan(_) => None,
            FirstHopInner::Circ(ct) => Some(ct),
        }
544984
    }
    /// Return a view of this as an OwnedChanTarget.
6
    fn chan_target_mut(&mut self) -> &mut OwnedChanTarget {
6
        match &mut self.inner {
6
            FirstHopInner::Chan(ct) => ct,
            FirstHopInner::Circ(ct) => ct.chan_target_mut(),
        }
6
    }
    /// If possible and appropriate, find a circuit target in `bridges` for this
    /// `FirstHop`, and make this `FirstHop` a viable circuit target.
    ///
    /// (By default, any `FirstHop` that a `GuardSet` returns will have enough
    /// information to be a `ChanTarget`, but it will be lacking the additional
    /// network information in `CircTarget`[^1] necessary for us to build a
    /// multi-hop circuit through it.  If this FirstHop is a regular non-bridge
    /// `Relay`, then the `CircMgr` will later look up that circuit information
    /// itself from the network directory. But if this `FirstHop` *is* a bridge,
    /// then we need to find that information in the `BridgeSet`, since the
    /// CircMgr does not keep track of the `BridgeSet`.)
    ///
    /// [^1]: For example, supported protocol versions and ntor keys.
    #[cfg(feature = "bridge-client")]
    fn lookup_bridge_circ_target(&mut self, bridges: &bridge::BridgeSet) {
        use crate::sample::CandidateStatus::Present;
        if self.sample.as_ref().map(|s| s.universe_type()) == Some(UniverseType::BridgeSet)
            && matches!(self.inner, FirstHopInner::Chan(_))
        {
            if let Present(bridge_relay) = bridges.bridge_relay_by_guard(self) {
                if let Some(circ_target) = bridge_relay.as_relay_with_desc() {
                    self.inner =
                        FirstHopInner::Circ(OwnedCircTarget::from_circ_target(&circ_target));
                }
            }
        }
    }
    /// Return true if this `FirstHop` contains circuit target information.
    ///
    /// This is true if `lookup_bridge_circ_target()` has been called, and it
    /// successfully found the circuit target information.
    #[cfg(feature = "bridge-client")]
    fn contains_circ_target(&self) -> bool {
        matches!(self.inner, FirstHopInner::Circ(_))
    }
}
// This is somewhat redundant with the implementations in crate::guard::Guard.
impl tor_linkspec::HasAddrs for FirstHop {
10830
    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
10830
        match &self.inner {
10830
            FirstHopInner::Chan(ct) => Either::Left(ct.addrs()),
            FirstHopInner::Circ(ct) => Either::Right(ct.addrs()),
        }
10830
    }
}
impl tor_linkspec::HasRelayIds for FirstHop {
3335244
    fn identity(
3335244
        &self,
3335244
        key_type: tor_linkspec::RelayIdType,
3335244
    ) -> Option<tor_linkspec::RelayIdRef<'_>> {
3335244
        match &self.inner {
3335244
            FirstHopInner::Chan(ct) => ct.identity(key_type),
            FirstHopInner::Circ(ct) => ct.identity(key_type),
        }
3335244
    }
}
impl tor_linkspec::HasChanMethod for FirstHop {
10824
    fn chan_method(&self) -> tor_linkspec::ChannelMethod {
10824
        match &self.inner {
10824
            FirstHopInner::Chan(ct) => ct.chan_method(),
            FirstHopInner::Circ(ct) => ct.chan_method(),
        }
10824
    }
}
impl tor_linkspec::ChanTarget for FirstHop {}
/// The purpose for which we plan to use a guard.
///
/// This can affect the guard selection algorithm.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
#[non_exhaustive]
pub enum GuardUsageKind {
    /// We want to use this guard for a data circuit.
    ///
    /// (This encompasses everything except the `OneHopDirectory` case.)
    #[default]
    Data,
    /// We want to use this guard for a one-hop, non-anonymous
    /// directory request.
    ///
    /// (Our algorithm allows more parallelism for the guards that we use
    /// for these circuits.)
    OneHopDirectory,
}
/// A set of parameters describing how a single guard should be selected.
///
/// Used as an argument to [`GuardMgr::select_guard`].
#[derive(Clone, Debug, derive_builder::Builder)]
#[builder(build_fn(error = "tor_config::ConfigBuildError"))]
pub struct GuardUsage {
    /// The purpose for which this guard will be used.
    #[builder(default)]
    kind: GuardUsageKind,
    /// A list of restrictions on which guard may be used.
    ///
    /// The default is the empty list.
    #[builder(sub_builder, setter(custom))]
    restrictions: GuardRestrictionList,
}
impl_standard_builder! { GuardUsage: !Deserialize }
/// List of socket restrictions, as configured
pub type GuardRestrictionList = Vec<GuardRestriction>;
define_list_builder_helper! {
    pub struct GuardRestrictionListBuilder {
        restrictions: [GuardRestriction],
    }
    built: GuardRestrictionList = restrictions;
    default = vec![];
4450
    item_build: |restriction| Ok(restriction.clone());
    item_apply_defaults: |_| Ok::<_, tor_config::ConfigBuildError>(());
}
define_list_builder_accessors! {
    struct GuardUsageBuilder {
        pub restrictions: [GuardRestriction],
    }
}
impl GuardUsageBuilder {
    /// Create a new empty [`GuardUsageBuilder`].
14
    pub fn new() -> Self {
14
        Self::default()
14
    }
}
/// A restriction that applies to a single request for a guard.
///
/// Restrictions differ from filters (see [`GuardFilter`]) in that
/// they apply to single requests, not to our entire set of guards.
/// They're suitable for things like making sure that we don't start
/// and end a circuit at the same relay, or requiring a specific
/// subprotocol version for certain kinds of requests.
#[derive(Clone, Debug, Serialize, Deserialize)]
#[non_exhaustive]
pub enum GuardRestriction {
    /// Don't pick a guard with the provided identity.
    AvoidId(RelayId),
    /// Don't pick a guard with any of the provided Ed25519 identities.
    AvoidAllIds(RelayIdSet),
}
/// The kind of vanguards to use.
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] //
#[derive(Serialize, Deserialize)] //
#[derive(derive_more::Display)] //
#[serde(rename_all = "lowercase")]
#[cfg(feature = "vanguards")]
#[non_exhaustive]
pub enum VanguardMode {
    /// "Lite" vanguards.
    #[default]
    #[display("lite")]
    Lite = 1,
    /// "Full" vanguards.
    #[display("full")]
    Full = 2,
    /// Vanguards are disabled.
    #[display("disabled")]
    Disabled = 0,
}
#[cfg(feature = "vanguards")]
impl VanguardMode {
    /// Build a `VanguardMode` from a [`NetParameters`] parameter.
    ///
    /// Used for converting [`vanguards_enabled`](NetParameters::vanguards_enabled)
    /// or [`vanguards_hs_service`](NetParameters::vanguards_hs_service)
    /// to the corresponding `VanguardMode`.
5080
    pub(crate) fn from_net_parameter(val: BoundedInt32<0, 2>) -> Self {
5080
        match val.get() {
            0 => VanguardMode::Disabled,
2556
            1 => VanguardMode::Lite,
2524
            2 => VanguardMode::Full,
            _ => unreachable!("BoundedInt32 was not bounded?!"),
        }
5080
    }
}
impl_not_auto_value!(VanguardMode);
/// Vanguards configuration.
#[derive(Deftly, Clone, Debug, PartialEq, Eq)]
#[derive_deftly(TorConfig)]
pub struct VanguardConfig {
    /// The kind of vanguards to use.
    #[deftly(tor_config(default))]
    mode: ExplicitOrAuto<VanguardMode>,
}
impl VanguardConfig {
    /// Return the configured [`VanguardMode`].
    ///
    /// Returns the [`Default`] `VanguardMode`
    /// if the mode is [`Auto`](ExplicitOrAuto) or unspecified.
3368
    pub fn mode(&self) -> VanguardMode {
3368
        match self.mode {
1848
            ExplicitOrAuto::Auto => Default::default(),
1520
            ExplicitOrAuto::Explicit(mode) => mode,
        }
3368
    }
}
/// The kind of vanguards to use.
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] //
#[derive(Serialize, Deserialize)] //
#[derive(derive_more::Display)] //
#[serde(rename_all = "lowercase")]
#[cfg(not(feature = "vanguards"))]
#[non_exhaustive]
pub enum VanguardMode {
    /// Vanguards are disabled.
    #[default]
    #[display("disabled")]
    Disabled = 0,
}
#[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 super::*;
    use itertools::Itertools;
    use tor_linkspec::{HasAddrs, HasRelayIds};
    use tor_persist::TestingStateMgr;
    use tor_rtcompat::test_with_all_runtimes;
    #[test]
    fn guard_param_defaults() {
        let p1 = GuardParams::default();
        let p2: GuardParams = (&NetParameters::default()).try_into().unwrap();
        assert_eq!(p1, p2);
    }
    fn init<R: Runtime>(rt: R) -> (GuardMgr<R>, TestingStateMgr, NetDir) {
        use tor_netdir::{MdReceiver, PartialNetDir, testnet};
        let statemgr = TestingStateMgr::new();
        let have_lock = statemgr.try_lock().unwrap();
        assert!(have_lock.held());
        let guardmgr = GuardMgr::new(rt, statemgr.clone(), &TestConfig::default()).unwrap();
        let (con, mds) = testnet::construct_network().unwrap();
        let param_overrides = vec![
            // We make the sample size smaller than usual to compensate for the
            // small testing network.  (Otherwise, we'd sample the whole network,
            // and not be able to observe guards in the tests.)
            "guard-min-filtered-sample-size=5",
            // We choose only two primary guards, to make the tests easier to write.
            "guard-n-primary-guards=2",
            // We define any restriction that allows 75% or fewer of relays as "meaningful",
            // so that we can test the "restrictive" guard sample behavior, and to avoid
            "guard-meaningful-restriction-percent=75",
        ];
        let param_overrides: String = param_overrides.into_iter().join(" ");
        let override_p = param_overrides.parse().unwrap();
        let mut netdir = PartialNetDir::new(con, Some(&override_p));
        for md in mds {
            netdir.add_microdesc(md);
        }
        let netdir = netdir.unwrap_if_sufficient().unwrap();
        (guardmgr, statemgr, netdir)
    }
    #[test]
    #[allow(clippy::clone_on_copy)]
    fn simple_case() {
        test_with_all_runtimes!(|rt| async move {
            let (guardmgr, statemgr, netdir) = init(rt.clone());
            let usage = GuardUsage::default();
            guardmgr.install_test_netdir(&netdir);
            let (id, mon, usable) = guardmgr.select_guard(usage).unwrap();
            // Report that the circuit succeeded.
            mon.succeeded();
            // May we use the circuit?
            let usable = usable.await.unwrap();
            assert!(usable);
            // Save the state...
            guardmgr.flush_msg_queue().await;
            guardmgr.store_persistent_state().unwrap();
            drop(guardmgr);
            // Try reloading from the state...
            let guardmgr2 =
                GuardMgr::new(rt.clone(), statemgr.clone(), &TestConfig::default()).unwrap();
            guardmgr2.install_test_netdir(&netdir);
            // Since the guard was confirmed, we should get the same one this time!
            let usage = GuardUsage::default();
            let (id2, _mon, _usable) = guardmgr2.select_guard(usage).unwrap();
            assert!(id2.same_relay_ids(&id));
        });
    }
    #[test]
    fn simple_waiting() {
        // TODO(nickm): This test fails in rare cases; I suspect a
        // race condition somewhere.
        //
        // I've doubled up on the queue flushing in order to try to make the
        // race less likely, but we should investigate.
        test_with_all_runtimes!(|rt| async move {
            let (guardmgr, _statemgr, netdir) = init(rt);
            let u = GuardUsage::default();
            guardmgr.install_test_netdir(&netdir);
            // We'll have the first two guard fail, which should make us
            // try a non-primary guard.
            let (id1, mon, _usable) = guardmgr.select_guard(u.clone()).unwrap();
            mon.failed();
            guardmgr.flush_msg_queue().await; // avoid race
            guardmgr.flush_msg_queue().await; // avoid race
            let (id2, mon, _usable) = guardmgr.select_guard(u.clone()).unwrap();
            mon.failed();
            guardmgr.flush_msg_queue().await; // avoid race
            guardmgr.flush_msg_queue().await; // avoid race
            assert!(!id1.same_relay_ids(&id2));
            // Now we should get two sampled guards. They should be different.
            let (id3, mon3, usable3) = guardmgr.select_guard(u.clone()).unwrap();
            let (id4, mon4, usable4) = guardmgr.select_guard(u.clone()).unwrap();
            assert!(!id3.same_relay_ids(&id4));
            let (u3, u4) = futures::join!(
                async {
                    mon3.failed();
                    guardmgr.flush_msg_queue().await; // avoid race
                    usable3.await.unwrap()
                },
                async {
                    mon4.succeeded();
                    usable4.await.unwrap()
                }
            );
            assert_eq!((u3, u4), (false, true));
        });
    }
    #[test]
    fn filtering_basics() {
        test_with_all_runtimes!(|rt| async move {
            let (guardmgr, _statemgr, netdir) = init(rt);
            let u = GuardUsage::default();
            let filter = {
                let mut f = GuardFilter::default();
                // All the addresses in the test network are {0,1,2,3,4}.0.0.3:9001.
                // Limit to only 2.0.0.0/8
                f.push_reachable_addresses(vec!["2.0.0.0/8:9001".parse().unwrap()]);
                f
            };
            guardmgr.set_filter(filter);
            guardmgr.install_test_netdir(&netdir);
            let (guard, _mon, _usable) = guardmgr.select_guard(u).unwrap();
            // Make sure that the filter worked.
            let addr = guard.addrs().next().unwrap();
            assert_eq!(addr, "2.0.0.3:9001".parse().unwrap());
        });
    }
    #[test]
    fn external_status() {
        test_with_all_runtimes!(|rt| async move {
            let (guardmgr, _statemgr, netdir) = init(rt);
            let data_usage = GuardUsage::default();
            let dir_usage = GuardUsageBuilder::new()
                .kind(GuardUsageKind::OneHopDirectory)
                .build()
                .unwrap();
            guardmgr.install_test_netdir(&netdir);
            {
                // Override this parameter, so that we can get deterministic results below.
                let mut inner = guardmgr.inner.lock().unwrap();
                inner.params.dir_parallelism = 1;
            }
            let (guard, mon, _usable) = guardmgr.select_guard(data_usage.clone()).unwrap();
            mon.succeeded();
            // Record that this guard gave us a bad directory object.
            guardmgr.note_external_failure(&guard, ExternalActivity::DirCache);
            // We ask for another guard, for data usage.  We should get the same
            // one as last time, since the director failure doesn't mean this
            // guard is useless as a primary guard.
            let (g2, mon, _usable) = guardmgr.select_guard(data_usage).unwrap();
            assert_eq!(g2.ed_identity(), guard.ed_identity());
            mon.succeeded();
            // But if we ask for a guard for directory usage, we should get a
            // different one, since the last guard we gave out failed.
            let (g3, mon, _usable) = guardmgr.select_guard(dir_usage.clone()).unwrap();
            assert_ne!(g3.ed_identity(), guard.ed_identity());
            mon.succeeded();
            // Now record a success for directory usage.
            guardmgr.note_external_success(&guard, ExternalActivity::DirCache);
            // Now that the guard is working as a cache, asking for it should get us the same guard.
            let (g4, _mon, _usable) = guardmgr.select_guard(dir_usage).unwrap();
            assert_eq!(g4.ed_identity(), guard.ed_identity());
        });
    }
    #[cfg(feature = "vanguards")]
    #[test]
    fn vanguard_mode_ord() {
        assert!(VanguardMode::Disabled < VanguardMode::Lite);
        assert!(VanguardMode::Disabled < VanguardMode::Full);
        assert!(VanguardMode::Lite < VanguardMode::Full);
    }
}