tor_guardmgr/bridge/descs.rs
1//! Code for working with bridge descriptors.
2//!
3//! Here we need to keep track of which bridge descriptors we need, and inform
4//! the directory manager of them.
5
6use std::collections::HashMap;
7use std::sync::Arc;
8use std::time::SystemTime;
9
10use crate::{
11 bridge::BridgeConfig,
12 sample::{Candidate, CandidateStatus, Universe, WeightThreshold},
13};
14use dyn_clone::DynClone;
15use futures::stream::BoxStream;
16use num_enum::{IntoPrimitive, TryFromPrimitive};
17use strum::{EnumCount, EnumIter};
18use tor_error::{HasKind, HasRetryTime};
19use tor_linkspec::{ChanTarget, HasChanMethod, HasRelayIds, OwnedChanTarget};
20use tor_llcrypto::pk::{ed25519::Ed25519Identity, rsa::RsaIdentity};
21use tor_netdir::RelayWeight;
22use tor_netdoc::doc::routerdesc::RouterDesc;
23
24use super::BridgeRelay;
25
26/// A router descriptor that can be used to build circuits through a bridge.
27///
28/// These descriptors are fetched from the bridges themselves, and used in
29/// conjunction with configured bridge information and pluggable transports to
30/// contact bridges and build circuits through them.
31#[derive(Clone, Debug)]
32pub struct BridgeDesc {
33 /// The inner descriptor.
34 ///
35 /// NOTE: This is wrapped in an `Arc<>` because we expect to pass BridgeDesc
36 /// around a bit and clone it frequently. If that doesn't actually happen,
37 /// we can remove the Arc here.
38 desc: Arc<RouterDesc>,
39}
40
41impl AsRef<RouterDesc> for BridgeDesc {
42 fn as_ref(&self) -> &RouterDesc {
43 self.desc.as_ref()
44 }
45}
46
47impl BridgeDesc {
48 /// Construct a new BridgeDesc from `desc`.
49 ///
50 /// The provided `desc` must be a descriptor retrieved from the bridge
51 /// itself.
52 pub fn new(desc: Arc<RouterDesc>) -> Self {
53 Self { desc }
54 }
55}
56
57impl tor_linkspec::HasRelayIdsLegacy for BridgeDesc {
58 fn ed_identity(&self) -> &Ed25519Identity {
59 self.desc.ed_identity()
60 }
61
62 fn rsa_identity(&self) -> &RsaIdentity {
63 self.desc.rsa_identity()
64 }
65}
66
67/// Trait for an object that knows how to fetch bridge descriptors as needed.
68///
69/// A "bridge descriptor" (represented by [`BridgeDesc`]) is a self-signed
70/// representation of a bridge's keys, capabilities, and other information. We
71/// can connect to a bridge without a descriptor, but we need to have one before
72/// we can build a multi-hop circuit through a bridge.
73///
74/// In arti, the implementor of this trait is `BridgeDescMgr`. We define this
75/// trait here so that we can avoid a circularity in our crate dependencies.
76/// (Since `BridgeDescMgr` uses circuits, it needs `CircMgr`, which needs
77/// `GuardMgr`, which in turn needs `BridgeDescMgr` again. We break this
78/// circularity by having `GuardMgr` use `BridgeDescMgr` only through this
79/// trait's API.)
80pub trait BridgeDescProvider: DynClone + Send + Sync {
81 /// Return the current set of bridge descriptors.
82 fn bridges(&self) -> Arc<BridgeDescList>;
83
84 /// Return a stream that gets a notification when the set of bridge
85 /// descriptors has changed.
86 fn events(&self) -> BoxStream<'static, BridgeDescEvent>;
87
88 /// Change the set of bridges that we want to download descriptors for.
89 ///
90 /// Bridges outside of this set will not have their descriptors updated,
91 /// and will not be revealed in the BridgeDescList.
92 fn set_bridges(&self, bridges: &[BridgeConfig]);
93}
94
95dyn_clone::clone_trait_object!(BridgeDescProvider);
96
97/// An event describing a change in a `BridgeDescList`.
98///
99/// Currently changes are always reported as `BridgeDescEvent::SomethingChanged`.
100///
101/// In the future, as an optimization, more fine-grained information may be provided.
102/// Unrecognized variants should be handled the same way as `SomethingChanged`.
103/// (So right now, it is not necessary to match on the variant at all.)
104#[derive(
105 Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCount, IntoPrimitive, TryFromPrimitive,
106)]
107#[non_exhaustive]
108#[repr(u16)]
109pub enum BridgeDescEvent {
110 /// Some change occurred to the set of descriptors
111 ///
112 /// The return value from [`bridges()`](BridgeDescProvider::bridges)
113 /// may have changed.
114 ///
115 /// The nature of the change is not specified; it might affect multiple descriptors,
116 /// and include multiple different kinds of change.
117 ///
118 /// This event may also be generated spuriously, if nothing has changed,
119 /// but this will usually be avoided for performance reasons.
120 SomethingChanged,
121}
122
123/// An error caused while fetching bridge descriptors
124///
125/// Note that when this appears in `BridgeDescList`, as returned by `BridgeDescMgr`,
126/// the fact that this is `HasRetryTime` does *not* mean the caller should retry.
127/// Retries will be handled by the `BridgeDescMgr`.
128/// The `HasRetryTime` impl can be used as a guide to
129/// whether the situation is likely to improve soon.
130///
131/// Does *not* include the information about which bridge we were trying to
132/// get a descriptor for.
133pub trait BridgeDescError:
134 std::error::Error + DynClone + HasKind + HasRetryTime + Send + Sync + 'static
135{
136}
137
138dyn_clone::clone_trait_object!(BridgeDescError);
139
140/// A set of bridge descriptors, managed and modified by a BridgeDescProvider.
141pub type BridgeDescList = HashMap<BridgeConfig, Result<BridgeDesc, Box<dyn BridgeDescError>>>;
142
143/// A collection of bridges, possibly with their descriptors.
144#[derive(Debug, Clone)]
145pub(crate) struct BridgeSet {
146 /// The configured bridges.
147 config: Arc<[BridgeConfig]>,
148 /// A map from those bridges to their descriptors. It may contain elements
149 /// that are not in `config`.
150 descs: Option<Arc<BridgeDescList>>,
151}
152
153impl BridgeSet {
154 /// Create a new `BridgeSet` from its configuration.
155 pub(crate) fn new(config: Arc<[BridgeConfig]>, descs: Option<Arc<BridgeDescList>>) -> Self {
156 Self { config, descs }
157 }
158
159 /// Returns the bridge that best matches a given guard.
160 ///
161 /// Note that since the guard may have more identities than the bridge the
162 /// match may not be perfect: the caller needs to check for a closer match
163 /// if they want to be certain.
164 ///
165 /// We check for a match by identity _and_ channel method, since channel
166 /// method is part of what makes two bridge lines different.
167 pub(crate) fn bridge_by_guard<T>(&self, guard: &T) -> Option<&BridgeConfig>
168 where
169 T: ChanTarget,
170 {
171 self.config.iter().find(|bridge| {
172 guard.has_all_relay_ids_from(*bridge)
173 // The Guard could have more addresses than the BridgeConfig if
174 // we happen to know its descriptor, it is using a direct
175 // connection, and it has listed more addresses there.
176 && bridge.chan_method().contained_by(&guard.chan_method())
177 })
178 }
179
180 /// Return a BridgeRelay wrapping the provided configuration, plus any known
181 /// descriptor for that configuration.
182 fn relay_by_bridge<'a>(&'a self, bridge: &'a BridgeConfig) -> BridgeRelay<'a> {
183 let desc = match self.descs.as_ref().and_then(|d| d.get(bridge)) {
184 Some(Ok(b)) => Some(b.clone()),
185 _ => None,
186 };
187 BridgeRelay::new(bridge, desc)
188 }
189
190 /// Look up a BridgeRelay corresponding to a given guard.
191 pub(crate) fn bridge_relay_by_guard<T: tor_linkspec::ChanTarget>(
192 &self,
193 guard: &T,
194 ) -> CandidateStatus<BridgeRelay> {
195 match self.bridge_by_guard(guard) {
196 Some(bridge) => {
197 let bridge_relay = self.relay_by_bridge(bridge);
198 if bridge_relay.has_all_relay_ids_from(guard) {
199 // We have all the IDs from the guard, either in the bridge
200 // line or in the descriptor, so the match is exact.
201 CandidateStatus::Present(bridge_relay)
202 } else if bridge_relay.has_descriptor() {
203 // We don't have an exact match and we have have a
204 // descriptor, so we know that this is _not_ a real match.
205 CandidateStatus::Absent
206 } else {
207 // We don't have a descriptor; finding it might make our
208 // match precise.
209 CandidateStatus::Uncertain
210 }
211 }
212 // We found no bridge that matches this guard's identities, so we
213 // can declare it absent.
214 None => CandidateStatus::Absent,
215 }
216 }
217}
218
219impl Universe for BridgeSet {
220 fn contains<T: tor_linkspec::ChanTarget>(&self, guard: &T) -> Option<bool> {
221 match self.bridge_relay_by_guard(guard) {
222 CandidateStatus::Present(_) => Some(true),
223 CandidateStatus::Absent => Some(false),
224 CandidateStatus::Uncertain => None,
225 }
226 }
227
228 fn status<T: tor_linkspec::ChanTarget>(&self, guard: &T) -> CandidateStatus<Candidate> {
229 match self.bridge_relay_by_guard(guard) {
230 CandidateStatus::Present(bridge_relay) => CandidateStatus::Present(Candidate {
231 listed_as_guard: true,
232 is_dir_cache: true, // all bridges are directory caches.
233 full_dir_info: bridge_relay.has_descriptor(),
234 owned_target: OwnedChanTarget::from_chan_target(&bridge_relay),
235 sensitivity: crate::guard::DisplayRule::Redacted,
236 }),
237 CandidateStatus::Absent => CandidateStatus::Absent,
238 CandidateStatus::Uncertain => CandidateStatus::Uncertain,
239 }
240 }
241
242 fn timestamp(&self) -> std::time::SystemTime {
243 // We just use the current time as the timestamp of this BridgeSet.
244 // This makes the guard code treat a BridgeSet as _continuously updated_:
245 // anything listed in the guard set is treated as listed right up to this
246 // moment, and anything unlisted is treated as unlisted right up to this
247 // moment.
248 SystemTime::now()
249 }
250
251 /// Note that for a BridgeSet, we always treat the current weight as 0 and
252 /// the maximum weight as "unlimited". That's because we don't have
253 /// bandwidth measurements for bridges, and so `max_sample_bw_fraction`
254 /// doesn't apply to them.
255 fn weight_threshold<T>(
256 &self,
257 _sample: &tor_linkspec::ByRelayIds<T>,
258 _params: &crate::GuardParams,
259 ) -> WeightThreshold
260 where
261 T: HasRelayIds,
262 {
263 WeightThreshold {
264 current_weight: RelayWeight::from(0),
265 maximum_weight: RelayWeight::from(u64::MAX),
266 }
267 }
268
269 fn sample<T>(
270 &self,
271 pre_existing: &tor_linkspec::ByRelayIds<T>,
272 filter: &crate::GuardFilter,
273 n: usize,
274 ) -> Vec<(Candidate, tor_netdir::RelayWeight)>
275 where
276 T: HasRelayIds,
277 {
278 use rand::seq::IteratorRandom;
279 self.config
280 .iter()
281 .filter(|bridge_conf| {
282 filter.permits(*bridge_conf)
283 && pre_existing.all_overlapping(*bridge_conf).is_empty()
284 })
285 .choose_multiple(&mut rand::thread_rng(), n)
286 .into_iter()
287 .map(|bridge_config| {
288 let relay = self.relay_by_bridge(bridge_config);
289 (
290 Candidate {
291 listed_as_guard: true,
292 is_dir_cache: true,
293 full_dir_info: relay.has_descriptor(),
294 owned_target: OwnedChanTarget::from_chan_target(&relay),
295 sensitivity: crate::guard::DisplayRule::Redacted,
296 },
297 RelayWeight::from(0),
298 )
299 })
300 .collect()
301 }
302}