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

            
5
use crate::circuit::HOPS;
6
use crate::circuit::circhop::{CircHopInbound, HopSettings};
7
use crate::circuit::reactor::stream::StreamMsg;
8
use crate::congestion::CongestionControl;
9
use crate::{HopNum, Result};
10
use std::sync::{Arc, Mutex};
11

            
12
use futures::channel::mpsc;
13
use smallvec::SmallVec;
14

            
15
use tor_cell::relaycell::RelayCellDecoder;
16
use tor_error::internal;
17

            
18
/// Per-hop state.
19
pub(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)]
39
pub(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

            
47
impl 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
}