Skip to main content

tor_proto/circuit/reactor/
circhop.rs

1//! Module exposing structures relating to the reactor's view of a circuit's hops.
2//!
3//! TODO(DEDUP): this will eventually replace [crate::client::reactor::circuit::circhop].
4
5use crate::circuit::HOPS;
6use crate::circuit::circhop::{CircHopInbound, HopSettings};
7use crate::circuit::reactor::stream::StreamMsg;
8use crate::congestion::CongestionControl;
9use crate::{HopNum, Result};
10use std::sync::{Arc, Mutex};
11
12use futures::channel::mpsc;
13use smallvec::SmallVec;
14
15use tor_cell::relaycell::RelayCellDecoder;
16use tor_error::internal;
17
18/// Per-hop state.
19pub(crate) struct CircHop {
20    /// A sender for sending relay messages to this hop's stream reactor.
21    ///
22    /// Set to `None` if we haven't yet spawned a stream reactor for this hop.
23    pub(crate) tx: Option<mpsc::Sender<StreamMsg>>,
24    /// The congestion control state.
25    ///
26    /// This is shared with `CircHopOutbound`.
27    ///
28    // TODO(DEDUP): the Arc is not actually needed in the new generic circuit reactor
29    // (it only exists because CircHopOutbound/CircHop needs it).
30    pub(crate) ccontrol: Arc<Mutex<CongestionControl>>,
31    /// The inbound hop state of this hop.
32    pub(crate) inbound: CircHopInbound,
33    /// Settings negotiated with this hop.
34    pub(crate) settings: HopSettings,
35}
36
37/// Represents the reactor's view of a circuit's hops.
38#[derive(Default)]
39pub(crate) struct CircHopList {
40    /// The list of hops.
41    ///
42    /// Relays have only one.
43    /// Clients have one entry per circuit hop.
44    hops: SmallVec<[CircHop; HOPS]>,
45}
46
47impl CircHopList {
48    /// Return a reference to the hop corresponding to `hopnum`, if there is one.
49    ///
50    /// Relays pass `None` for the `hopnum`.
51    pub(crate) fn get(&self, hop: Option<HopNum>) -> Option<&CircHop> {
52        self.hops.get(Self::index(hop))
53    }
54
55    /// Return a mutable reference to the hop corresponding to `hopnum`, if there is one.
56    ///
57    /// Relays pass `None` for the `hopnum`.
58    pub(crate) fn get_mut(&mut self, hop: Option<HopNum>) -> Option<&mut CircHop> {
59        self.hops.get_mut(Self::index(hop))
60    }
61
62    /// Push a new hop to our hop list.
63    ///
64    /// Prepares a cc object for the hop, but does not spawn a stream reactor.
65    ///
66    /// Will return an error if the circuit already has [`u8::MAX`] hops.
67    pub(crate) fn add_hop(&mut self, settings: HopSettings) -> Result<()> {
68        let hop_num = self.hops.len();
69        debug_assert_eq!(hop_num, usize::from(self.num_hops()));
70
71        // There are several places in the code that assume that a `usize` hop number
72        // can be cast or converted to a `u8` hop number,
73        // so this check is important to prevent panics or incorrect behaviour.
74        if hop_num == usize::from(u8::MAX) {
75            return Err(internal!("cannot add more hops to a circuit with `u8::MAX` hops").into());
76        }
77
78        let relay_format = settings.relay_crypt_protocol().relay_cell_format();
79        let inbound = CircHopInbound::new(RelayCellDecoder::new(relay_format), &settings);
80        let ccontrol = Arc::new(Mutex::new(CongestionControl::new(&settings.ccontrol)));
81
82        self.hops.push(CircHop {
83            inbound,
84            ccontrol,
85            settings,
86            tx: None,
87        });
88
89        Ok(())
90    }
91
92    /// The number of hops in this circuit.
93    fn num_hops(&self) -> u8 {
94        // `Self::add_hop` checks to make sure that we never have more than `u8::MAX` hops,
95        // so `self.hops.len()` should be safe to cast to a `u8`.
96        // If that assumption is violated,
97        // we choose to panic rather than silently use the wrong hop due to an `as` cast.
98        self.hops
99            .len()
100            .try_into()
101            .expect("`hops.len()` has more than `u8::MAX` hops")
102    }
103
104    /// Return the index of the specified `hop`.
105    ///
106    /// Returns 0 if the `hop` is `None`.
107    fn index(hop: Option<HopNum>) -> usize {
108        // unwrap_or_default(), because for relays, hop is None,
109        // and we just want to use the first slot of the vec
110        hop.map(usize::from).unwrap_or_default()
111    }
112}