1
//! Types and code to map circuit IDs to circuits.
2

            
3
// NOTE: This is a work in progress and I bet I'll refactor it a lot;
4
// it needs to stay opaque!
5

            
6
use crate::circuit::CircuitRxSender;
7
use crate::client::circuit::padding::{PaddingController, QueuedCellPaddingInfo};
8
use crate::{Error, Result};
9
use tor_basic_utils::RngExt;
10
use tor_cell::chancell::CircId;
11
use tor_cell::chancell::msg::DestroyReason;
12

            
13
use crate::circuit::celltypes::CreateResponse;
14
use crate::client::circuit::halfcirc::HalfCirc;
15

            
16
use oneshot_fused_workaround as oneshot;
17

            
18
use rand::Rng;
19
use rand::distr::Distribution;
20
use std::collections::{HashMap, hash_map::Entry};
21
use std::ops::{Deref, DerefMut};
22
use std::result::Result as StdResult;
23
use std::sync::Arc;
24

            
25
#[cfg(feature = "relay")]
26
use crate::relay::RelayCirc;
27

            
28
/// Which group of circuit IDs are we allowed to allocate in this map?
29
///
30
/// If we initiated the channel, we use High circuit ids.  If we're the
31
/// responder, we use low circuit ids.
32
#[derive(Copy, Clone)]
33
pub(crate) enum CircIdRange {
34
    /// Only use circuit IDs with the MSB cleared.
35
    #[allow(dead_code)] // Relays will need this.
36
    Low,
37
    /// Only use circuit IDs with the MSB set.
38
    High,
39
    // Historical note: There used to be an "All" range of circuit IDs
40
    // available to clients only.  We stopped using "All" when we moved to link
41
    // protocol version 4.
42
}
43

            
44
impl CircIdRange {
45
    /// The range of integer circuit IDs that we are allowed to allocate.
46
    /// Prefer using other more specific methods over this one.
47
608
    const fn integer_range(&self) -> std::ops::RangeInclusive<u32> {
48
        const MIDPOINT: u32 = 0x8000_0000;
49

            
50
608
        match self {
51
            // 0 is an invalid value
52
270
            Self::Low => 1..=(MIDPOINT - 1),
53
338
            Self::High => MIDPOINT..=u32::MAX,
54
        }
55
608
    }
56

            
57
    /// Is this circuit ID allowed to be allocated by the channel's peer?
58
14
    pub(crate) fn is_allowed_for_peer(&self, id: CircId) -> bool {
59
        // If our range does not contain it, then it is allowed.
60
        // Note that a `CircId` never contains a value of zero,
61
        // so no need to consider it here.
62
14
        !self.integer_range().contains(&id.into())
63
14
    }
64
}
65

            
66
impl rand::distr::Distribution<CircId> for CircIdRange {
67
    /// Return a random circuit ID in the appropriate range.
68
594
    fn sample<R: Rng + ?Sized>(&self, mut rng: &mut R) -> CircId {
69
594
        let v = rng.gen_range_checked(self.integer_range());
70
594
        let v = v.expect("Unexpected empty range passed to gen_range_checked");
71
594
        CircId::new(v).expect("Unexpected zero value")
72
594
    }
73
}
74

            
75
/// An entry in the circuit map.  Right now, we only have "here's the
76
/// way to send cells to a given circuit", but that's likely to
77
/// change.
78
#[derive(Debug)]
79
pub(super) enum CircEnt {
80
    /// An origin circuit that has not yet received a CREATED cell.
81
    ///
82
    /// For this circuit, the CREATED* cell or DESTROY cell gets sent
83
    /// to the oneshot sender to tell the corresponding
84
    /// PendingClientCirc that the handshake is done.
85
    ///
86
    /// Once that's done, the `CircuitRxSender` mpsc sender will be used to send subsequent
87
    /// cells to the circuit.
88
    Opening {
89
        /// The oneshot sender on which to report a create response
90
        create_response_sender: oneshot::Sender<CreateResponse>,
91
        /// A sink which should receive all the relay cells for this circuit
92
        /// from this channel
93
        cell_sender: CircuitRxSender,
94
        /// A padding controller we should use when reporting flushed cells.
95
        padding_ctrl: PaddingController,
96
    },
97

            
98
    /// An origin circuit (a circuit which originated here)
99
    /// that is open and can be given relay cells.
100
    OpenOrigin {
101
        /// A sink which should receive all the relay cells for this circuit
102
        /// from this channel
103
        cell_sender: CircuitRxSender,
104
        /// A padding controller we should use when reporting flushed cells.
105
        padding_ctrl: PaddingController,
106
    },
107

            
108
    /// A relay circuit (a circuit in which we are a hop on the path)
109
    /// that is open and can be given relay cells.
110
    #[cfg(feature = "relay")]
111
    OpenRelay {
112
        /// A handle to the circuit.
113
        /// TODO(relay): We need to store the `Arc<RelayCirc>` somewhere
114
        /// and currently this seems like the best place to store it.
115
        /// As we implement more functionality maybe we'll find a better place to store it,
116
        /// in which case we should consider combining the `OpenOrigin` and `OpenRelay` variants.
117
        _circ: Arc<RelayCirc>,
118
        /// A sink which should receive all the relay cells for this circuit
119
        /// from this channel
120
        cell_sender: CircuitRxSender,
121
        /// A padding controller we should use when reporting flushed cells.
122
        padding_ctrl: PaddingController,
123
    },
124

            
125
    /// A circuit where we have sent a DESTROY, but the other end might
126
    /// not have gotten a DESTROY yet.
127
    DestroySent(HalfCirc),
128
}
129

            
130
/// An "smart pointer" that wraps an exclusive reference
131
/// of a `CircEnt`.
132
///
133
/// When being dropped, this object updates the open or opening entries
134
/// counter of the `CircMap`.
135
pub(super) struct MutCircEnt<'a> {
136
    /// An exclusive reference to the `CircEnt`.
137
    value: &'a mut CircEnt,
138
    /// An exclusive reference to the open or opening
139
    ///  entries counter.
140
    open_count: &'a mut usize,
141
    /// True if the entry was open or opening when borrowed.
142
    was_open: bool,
143
}
144

            
145
impl<'a> Drop for MutCircEnt<'a> {
146
624
    fn drop(&mut self) {
147
624
        let is_open = !matches!(self.value, CircEnt::DestroySent(_));
148
624
        match (self.was_open, is_open) {
149
            (false, true) => *self.open_count = self.open_count.saturating_add(1),
150
            (true, false) => *self.open_count = self.open_count.saturating_sub(1),
151
624
            (_, _) => (),
152
        };
153
624
    }
154
}
155

            
156
impl<'a> Deref for MutCircEnt<'a> {
157
    type Target = CircEnt;
158
284
    fn deref(&self) -> &Self::Target {
159
284
        self.value
160
284
    }
161
}
162

            
163
impl<'a> DerefMut for MutCircEnt<'a> {
164
336
    fn deref_mut(&mut self) -> &mut Self::Target {
165
336
        self.value
166
336
    }
167
}
168

            
169
/// A map from circuit IDs to circuit entries. Each channel has one.
170
pub(super) struct CircMap {
171
    /// Map from circuit IDs to entries
172
    m: HashMap<CircId, CircEnt>,
173
    /// Rule for allocating new circuit IDs.
174
    range: CircIdRange,
175
    /// Number of open or opening entry in this map.
176
    open_count: usize,
177
}
178

            
179
impl CircMap {
180
    /// Make a new empty CircMap
181
587
    pub(super) fn new(idrange: CircIdRange) -> Self {
182
587
        CircMap {
183
587
            m: HashMap::new(),
184
587
            range: idrange,
185
587
            open_count: 0,
186
587
        }
187
587
    }
188

            
189
    /// Add a new set of elements (corresponding to a
190
    /// [`PendingClientTunnel`](crate::client::circuit::PendingClientTunnel))
191
    /// as an entry to this map.
192
    ///
193
    /// On success return the allocated circuit ID.
194
594
    pub(super) fn add_origin_ent<R: Rng>(
195
594
        &mut self,
196
594
        rng: &mut R,
197
594
        createdsink: oneshot::Sender<CreateResponse>,
198
594
        sink: CircuitRxSender,
199
594
        padding_ctrl: PaddingController,
200
594
    ) -> Result<CircId> {
201
        /// How many times do we probe for a random circuit ID before
202
        /// we assume that the range is fully populated?
203
        ///
204
        /// TODO: C tor does 64, but that is probably overkill with 4-byte circuit IDs.
205
        const N_ATTEMPTS: usize = 16;
206
594
        let iter = self.range.sample_iter(rng).take(N_ATTEMPTS);
207
594
        let circ_ent = CircEnt::Opening {
208
594
            create_response_sender: createdsink,
209
594
            cell_sender: sink,
210
594
            padding_ctrl,
211
594
        };
212
594
        for id in iter {
213
594
            let ent = self.m.entry(id);
214
594
            if let Entry::Vacant(_) = &ent {
215
594
                ent.or_insert(circ_ent);
216
594
                self.open_count += 1;
217
594
                return Ok(id);
218
            }
219
        }
220
        Err(Error::IdRangeFull)
221
594
    }
222

            
223
    /// Add a new set of elements (corresponding to a [`RelayCirc`]) as an entry to this map.
224
    ///
225
    /// We use [`DestroyReason`] as the return type since we very likely want to destroy the circuit
226
    /// if this fails, and not return an error and destroy the entire channel.
227
    #[cfg(feature = "relay")]
228
14
    pub(super) fn add_relay_ent(
229
14
        &mut self,
230
14
        circ_id: CircId,
231
14
        circ: Arc<RelayCirc>,
232
14
        sink: CircuitRxSender,
233
14
        padding_ctrl: PaddingController,
234
14
    ) -> StdResult<(), DestroyReason> {
235
        // The peer is only allowed to use a subset of the ID range.
236
14
        if !self.range.is_allowed_for_peer(circ_id) {
237
            return Err(DestroyReason::NONE);
238
14
        }
239

            
240
14
        let circ_ent = CircEnt::OpenRelay {
241
14
            _circ: circ,
242
14
            cell_sender: sink,
243
14
            padding_ctrl,
244
14
        };
245

            
246
14
        if let Entry::Vacant(ent) = self.m.entry(circ_id) {
247
14
            ent.insert(circ_ent);
248
14
            self.open_count += 1;
249
14
            Ok(())
250
        } else {
251
            Err(DestroyReason::NONE)
252
        }
253
14
    }
254

            
255
    /// Testing only: install an entry in this circuit map without regard
256
    /// for consistency.
257
    #[cfg(test)]
258
72
    pub(super) fn put_unchecked(&mut self, id: CircId, ent: CircEnt) {
259
72
        self.m.insert(id, ent);
260
72
    }
261

            
262
    /// Return the entry for `id` in this map, if any.
263
652
    pub(super) fn get_mut(&mut self, id: CircId) -> Option<MutCircEnt> {
264
652
        let open_count = &mut self.open_count;
265
652
        self.m.get_mut(&id).map(move |ent| MutCircEnt {
266
624
            open_count,
267
624
            was_open: !matches!(ent, CircEnt::DestroySent(_)),
268
624
            value: ent,
269
624
        })
270
652
    }
271

            
272
    /// Returns `true` if the circuit with the specified `id`
273
    /// is open or opening.
274
    ///
275
    /// Returns `false` if the circuit is not in the circuit map,
276
    /// or if we have already sent a DESTROY on it.
277
176
    pub(super) fn is_open(&self, id: CircId) -> bool {
278
176
        let Some(entry) = self.m.get(&id) else {
279
114
            return false;
280
        };
281

            
282
62
        match entry {
283
62
            CircEnt::Opening { .. } | CircEnt::OpenOrigin { .. } => true,
284
            #[cfg(feature = "relay")]
285
            CircEnt::OpenRelay { .. } => true,
286
            CircEnt::DestroySent(..) => false,
287
        }
288
176
    }
289

            
290
    /// Inform the relevant circuit's padding subsystem that a given cell has been flushed.
291
4338
    pub(super) fn note_cell_flushed(&mut self, id: CircId, info: QueuedCellPaddingInfo) {
292
4338
        let padding_ctrl = match self.m.get(&id) {
293
18
            Some(CircEnt::Opening { padding_ctrl, .. }) => padding_ctrl,
294
            Some(CircEnt::OpenOrigin { padding_ctrl, .. }) => padding_ctrl,
295
            #[cfg(feature = "relay")]
296
            Some(CircEnt::OpenRelay { padding_ctrl, .. }) => padding_ctrl,
297
4320
            Some(CircEnt::DestroySent(..)) | None => return,
298
        };
299
18
        padding_ctrl.flushed_relay_cell(info);
300
4338
    }
301

            
302
    /// See whether 'id' is an opening circuit.  If so, mark it "open" and
303
    /// return a oneshot::Sender that is waiting for its create cell.
304
    ///
305
    /// Returns `None` if `id` is not in our circuit map,
306
    /// or if it is not an opening circuit.
307
44
    pub(super) fn advance_from_opening(
308
44
        &mut self,
309
44
        id: CircId,
310
44
    ) -> Option<oneshot::Sender<CreateResponse>> {
311
        // TODO: there should be a better way to do
312
        // this. hash_map::Entry seems like it could be better, but
313
        // there seems to be no way to replace the object in-place as
314
        // a consuming function of itself.
315
44
        let ok = matches!(self.m.get(&id), Some(CircEnt::Opening { .. }));
316
44
        if ok {
317
            if let Some(CircEnt::Opening {
318
28
                create_response_sender: oneshot,
319
28
                cell_sender: sink,
320
28
                padding_ctrl,
321
28
            }) = self.m.remove(&id)
322
            {
323
28
                self.m.insert(
324
28
                    id,
325
28
                    CircEnt::OpenOrigin {
326
28
                        cell_sender: sink,
327
28
                        padding_ctrl,
328
28
                    },
329
                );
330
28
                Some(oneshot)
331
            } else {
332
                panic!("internal error: inconsistent circuit state");
333
            }
334
        } else {
335
16
            None
336
        }
337
44
    }
338

            
339
    /// Called when we have sent a DESTROY on a circuit.  Configures
340
    /// a "HalfCirc" object to track how many cells we get on this
341
    /// circuit, and to prevent us from reusing it immediately.
342
64
    pub(super) fn destroy_sent(&mut self, id: CircId, hc: HalfCirc) {
343
64
        if let Some(replaced) = self.m.insert(id, CircEnt::DestroySent(hc)) {
344
62
            if !matches!(replaced, CircEnt::DestroySent(_)) {
345
62
                // replaced an Open/Opening entry with DestroySent
346
62
                self.open_count = self.open_count.saturating_sub(1);
347
62
            }
348
2
        }
349
64
    }
350

            
351
    /// Extract the value from this map with 'id' if any
352
66
    pub(super) fn remove(&mut self, id: CircId) -> Option<CircEnt> {
353
93
        self.m.remove(&id).map(|removed| {
354
54
            if !matches!(removed, CircEnt::DestroySent(_)) {
355
42
                self.open_count = self.open_count.saturating_sub(1);
356
42
            }
357
54
            removed
358
54
        })
359
66
    }
360

            
361
    /// Return the total number of open and opening entries in the map
362
216
    pub(super) fn open_ent_count(&self) -> usize {
363
216
        self.open_count
364
216
    }
365
}
366

            
367
#[cfg(test)]
368
mod test {
369
    // @@ begin test lint list maintained by maint/add_warning @@
370
    #![allow(clippy::bool_assert_comparison)]
371
    #![allow(clippy::clone_on_copy)]
372
    #![allow(clippy::dbg_macro)]
373
    #![allow(clippy::mixed_attributes_style)]
374
    #![allow(clippy::print_stderr)]
375
    #![allow(clippy::print_stdout)]
376
    #![allow(clippy::single_char_pattern)]
377
    #![allow(clippy::unwrap_used)]
378
    #![allow(clippy::unchecked_time_subtraction)]
379
    #![allow(clippy::useless_vec)]
380
    #![allow(clippy::needless_pass_by_value)]
381
    #![allow(clippy::string_slice)] // See arti#2571
382
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
383
    use super::*;
384
    use crate::circuit::test::fake_mpsc;
385
    use crate::client::circuit::padding::new_padding;
386
    use tor_basic_utils::test_rng::testing_rng;
387
    use tor_rtcompat::DynTimeProvider;
388

            
389
    #[test]
390
    fn circmap_basics() {
391
        let mut map_low = CircMap::new(CircIdRange::Low);
392
        let mut map_high = CircMap::new(CircIdRange::High);
393
        let mut ids_low: Vec<CircId> = Vec::new();
394
        let mut ids_high: Vec<CircId> = Vec::new();
395
        let mut rng = testing_rng();
396
        tor_rtcompat::test_with_one_runtime!(|runtime| async {
397
            let (padding_ctrl, _padding_stream) = new_padding(DynTimeProvider::new(runtime));
398

            
399
            assert!(map_low.get_mut(CircId::new(77).unwrap()).is_none());
400

            
401
            for _ in 0..128 {
402
                let (csnd, _) = oneshot::channel();
403
                let (snd, _) = fake_mpsc(8);
404
                let id_low = map_low
405
                    .add_origin_ent(&mut rng, csnd, snd, padding_ctrl.clone())
406
                    .unwrap();
407
                assert!(u32::from(id_low) > 0);
408
                assert!(u32::from(id_low) < 0x80000000);
409
                assert!(!ids_low.contains(&id_low));
410
                ids_low.push(id_low);
411

            
412
                assert!(matches!(
413
                    *map_low.get_mut(id_low).unwrap(),
414
                    CircEnt::Opening { .. }
415
                ));
416

            
417
                let (csnd, _) = oneshot::channel();
418
                let (snd, _) = fake_mpsc(8);
419
                let id_high = map_high
420
                    .add_origin_ent(&mut rng, csnd, snd, padding_ctrl.clone())
421
                    .unwrap();
422
                assert!(u32::from(id_high) >= 0x80000000);
423
                assert!(!ids_high.contains(&id_high));
424
                ids_high.push(id_high);
425
            }
426

            
427
            // Test open / opening entry counting
428
            assert_eq!(128, map_low.open_ent_count());
429
            assert_eq!(128, map_high.open_ent_count());
430

            
431
            // Test remove
432
            assert!(map_low.get_mut(ids_low[0]).is_some());
433
            map_low.remove(ids_low[0]);
434
            assert!(map_low.get_mut(ids_low[0]).is_none());
435
            assert_eq!(127, map_low.open_ent_count());
436

            
437
            // Test DestroySent doesn't count
438
            map_low.destroy_sent(CircId::new(256).unwrap(), HalfCirc::new(1));
439
            assert_eq!(127, map_low.open_ent_count());
440

            
441
            // Test advance_from_opening.
442

            
443
            // Good case.
444
            assert!(map_high.get_mut(ids_high[0]).is_some());
445
            assert!(matches!(
446
                *map_high.get_mut(ids_high[0]).unwrap(),
447
                CircEnt::Opening { .. }
448
            ));
449
            let adv = map_high.advance_from_opening(ids_high[0]);
450
            assert!(adv.is_some());
451
            assert!(matches!(
452
                *map_high.get_mut(ids_high[0]).unwrap(),
453
                CircEnt::OpenOrigin { .. }
454
            ));
455

            
456
            // Can't double-advance.
457
            let adv = map_high.advance_from_opening(ids_high[0]);
458
            assert!(adv.is_none());
459

            
460
            // Can't advance an entry that is not there.  We know "77"
461
            // can't be in map_high, since we only added high circids to
462
            // it.
463
            let adv = map_high.advance_from_opening(CircId::new(77).unwrap());
464
            assert!(adv.is_none());
465
        });
466
    }
467
}