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}