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

            
6
use std::collections::HashMap;
7
use std::sync::Arc;
8
use std::time::SystemTime;
9

            
10
use crate::{
11
    bridge::BridgeConfig,
12
    sample::{Candidate, CandidateStatus, Universe, WeightThreshold},
13
};
14
use dyn_clone::DynClone;
15
use futures::stream::BoxStream;
16
use num_enum::{IntoPrimitive, TryFromPrimitive};
17
use strum::{EnumCount, EnumIter};
18
use tor_error::{HasKind, HasRetryTime};
19
use tor_linkspec::{ChanTarget, HasChanMethod, HasRelayIds, OwnedChanTarget};
20
use tor_llcrypto::pk::{ed25519::Ed25519Identity, rsa::RsaIdentity};
21
use tor_netdir::RelayWeight;
22
use tor_netdoc::doc::routerdesc::RouterDesc;
23

            
24
use 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)]
32
pub 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

            
41
impl AsRef<RouterDesc> for BridgeDesc {
42
246
    fn as_ref(&self) -> &RouterDesc {
43
246
        self.desc.as_ref()
44
246
    }
45
}
46

            
47
impl BridgeDesc {
48
    /// Construct a new BridgeDesc from `desc`.
49
    ///
50
    /// The provided `desc` must be a descriptor retrieved from the bridge
51
    /// itself.
52
984
    pub fn new(desc: Arc<RouterDesc>) -> Self {
53
984
        Self { desc }
54
984
    }
55
}
56

            
57
impl 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.)
80
pub 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

            
95
dyn_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
110
    Debug, Clone, Copy, PartialEq, Eq, EnumIter, EnumCount, IntoPrimitive, TryFromPrimitive,
106
)]
107
#[non_exhaustive]
108
#[repr(u16)]
109
pub 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.
133
pub trait BridgeDescError:
134
    std::error::Error + DynClone + HasKind + HasRetryTime + Send + Sync + 'static
135
{
136
}
137

            
138
dyn_clone::clone_trait_object!(BridgeDescError);
139

            
140
/// A set of bridge descriptors, managed and modified by a BridgeDescProvider.
141
pub type BridgeDescList = HashMap<BridgeConfig, Result<BridgeDesc, Box<dyn BridgeDescError>>>;
142

            
143
/// A collection of bridges, possibly with their descriptors.
144
#[derive(Debug, Clone)]
145
pub(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

            
153
impl 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

            
219
impl 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::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
}