1
//! Circuit-related types and helpers.
2
//!
3
//! This code is shared between the client and relay implementations.
4

            
5
pub(crate) mod cell_sender;
6
pub(crate) mod celltypes;
7
pub(crate) mod circ_sender;
8
pub(crate) mod circhop;
9
pub(crate) mod create;
10
pub(crate) mod padding;
11
pub(crate) mod reactor;
12
pub(crate) mod syncview;
13
pub(crate) mod unique_id;
14

            
15
pub use crate::memquota::StreamAccount;
16
pub use syncview::CircHopSyncView;
17
pub use unique_id::UniqId;
18

            
19
use crate::ccparams::CongestionControlParams;
20
use crate::stream::flow_ctrl::params::FlowCtrlParameters;
21
use tor_cell::relaycell::extend::SubprotocolRequest;
22
use tor_error::ErrorKind;
23
use tor_protover::Protocols;
24

            
25
pub(crate) use circ_sender::{CircuitRxReceiver, CircuitRxSender};
26

            
27
/// Estimated upper bound for the likely number of hops.
28
pub(crate) const HOPS: usize = 6;
29

            
30
/// Description of the network's current rules for building circuits.
31
///
32
/// This type describes rules derived from the consensus,
33
/// and possibly amended by our own configuration.
34
///
35
/// Typically, this type created once for an entire circuit,
36
/// and any special per-hop information is derived
37
/// from each hop as a CircTarget.
38
/// Note however that callers _may_ provide different `CircParameters`
39
/// for different hops within a circuit if they have some reason to do so,
40
/// so we do not enforce that every hop in a circuit has the same `CircParameters`.
41
#[non_exhaustive]
42
#[derive(Clone, Debug)]
43
pub struct CircParameters {
44
    /// Whether we should include ed25519 identities when we send
45
    /// EXTEND2 cells.
46
    pub extend_by_ed25519_id: bool,
47
    /// Congestion control parameters for this circuit.
48
    pub ccontrol: CongestionControlParams,
49

            
50
    /// Flow control parameters to use for all streams on this circuit.
51
    // While flow control is a stream property and not a circuit property,
52
    // and it may seem better to pass the flow control parameters to for example `begin_stream()`,
53
    // it's included in [`CircParameters`] for the following reasons:
54
    //
55
    // - When endpoints (exits + hs) receive new stream requests, they need the flow control
56
    //   parameters immediately. It would be easy to pass flow control parameters when creating a
57
    //   stream, but it's not as easy to get flow control parameters when receiving a new stream
58
    //   request, unless those parameters are already available to the circuit (like
59
    //   `CircParameters` are).
60
    // - It's unclear if new streams on existing circuits should switch to new flow control
61
    //   parameters if the consensus changes. This behaviour doesn't appear to be specified. It
62
    //   might also leak information to the circuit's endpoint about when we downloaded new
63
    //   directory documents. So it seems best to stick with the same flow control parameters for
64
    //   the lifetime of the circuit.
65
    // - It doesn't belong in [`StreamParameters`] as `StreamParameters` is a set of preferences
66
    //   with defaults, and consensus parameters aren't preferences and don't have defaults.
67
    //   (Technically they have defaults, but `StreamParameters` isn't the place to set them.)
68
    pub flow_ctrl: FlowCtrlParameters,
69

            
70
    /// Maximum number of permitted incoming relay cells for each hop.
71
    ///
72
    /// If we would receive more relay cells than this from a single hop,
73
    /// we close the circuit with [`ExcessInboundCells`](crate::Error::ExcessInboundCells).
74
    ///
75
    /// If this value is None, then there is no limit to the number of inbound cells.
76
    ///
77
    /// Known limitation: If this value if `u32::MAX`,
78
    /// then a limit of `u32::MAX - 1` is enforced.
79
    pub n_incoming_cells_permitted: Option<u32>,
80

            
81
    /// Maximum number of permitted outgoing relay cells for each hop.
82
    ///
83
    /// If we would try to send more relay cells than this from a single hop,
84
    /// we close the circuit with [`ExcessOutboundCells`](crate::Error::ExcessOutboundCells).
85
    /// It is the circuit-user's responsibility to make sure that this does not happen.
86
    ///
87
    /// This setting is used to ensure that we do not violate a limit
88
    /// imposed by `n_incoming_cells_permitted`
89
    /// on the other side of a circuit.
90
    ///
91
    /// If this value is None, then there is no limit to the number of outbound cells.
92
    ///
93
    /// Known limitation: If this value if `u32::MAX`,
94
    /// then a limit of `u32::MAX - 1` is enforced.
95
    pub n_outgoing_cells_permitted: Option<u32>,
96
}
97

            
98
tor_protover::subprotocol_restricted_set! {
99
    /// The enabled/disabled status of subprotocols that are allowed to be requested through a
100
    /// subprotocol request during a circuit handshake.
101
    ///
102
    /// The allowed subprotocols are defined in:
103
    /// <https://spec.torproject.org/tor-spec/create-created-cells.html#subproto-request>
104
    #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
105
    pub(crate) struct HandshakeSubprotocols {
106
        RELAY_CRYPT_CGO,
107
    }
108
}
109

            
110
impl HandshakeSubprotocols {
111
    /// Build a [`HandshakeSubprotocols`] from a [`SubprotocolRequest`]
112
    /// provided during a circuit handshake.
113
    ///
114
    /// If the `SubprotocolRequest` contains subprotocols that aren't
115
    /// allowed to be requested through a subprotocol request,
116
    /// this returns an error containing the original `SubprotocolRequest`.
117
    //
118
    // It would be nice to return a list of only the invalid subprotocols,
119
    // but it seems a bit expensive to compute on the error path when we probably
120
    // want to fail quickly.
121
14
    pub(crate) fn try_from_request(
122
14
        protos: SubprotocolRequest,
123
14
    ) -> Result<Self, InvalidHandshakeSubprotocolError> {
124
        use std::sync::LazyLock;
125
        static ALL: LazyLock<Protocols> =
126
2
            LazyLock::new(|| Protocols::from(HandshakeSubprotocols::ALL));
127

            
128
14
        if !protos.contains_only(&ALL) {
129
4
            return Err(InvalidHandshakeSubprotocolError(protos));
130
10
        }
131

            
132
10
        Ok(Self {
133
10
            relay_crypt_cgo: protos.contains(tor_protover::named::RELAY_CRYPT_CGO),
134
10
        })
135
14
    }
136
}
137

            
138
/// The subprotocol request had subprotocols that are not all supported in circuit handshakes.
139
///
140
/// Contains the requested subprotocols (both valid and invalid).
141
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
142
#[error("Request included subprotocols that we do not support in circuit handshakes: {0:?}")]
143
pub(crate) struct InvalidHandshakeSubprotocolError(SubprotocolRequest);
144

            
145
impl tor_error::HasKind for InvalidHandshakeSubprotocolError {
146
    fn kind(&self) -> ErrorKind {
147
        ErrorKind::TorProtocolViolation
148
    }
149
}
150

            
151
#[cfg(test)]
152
pub(crate) mod test {
153
    use super::*;
154

            
155
    #[cfg(feature = "relay")]
156
    use crate::relay::{CircNetParameters, CongestionControlNetParams};
157

            
158
    pub(crate) use super::circ_sender::test::fake_mpsc;
159

            
160
    /// Return a new [`CircNetParameters`] using default values for unit tests. They are based on
161
    /// consensus defaults but should not be considered to be accurate from the one used on the
162
    /// production network.
163
    #[cfg(feature = "relay")]
164
    pub(crate) fn new_circ_net_params() -> CircNetParameters {
165
        CircNetParameters {
166
            cc: CongestionControlNetParams::defaults_for_tests(),
167
        }
168
    }
169

            
170
    #[test]
171
    fn handshake_subprotocols() {
172
        let empty_iter: [tor_protover::NumberedSubver; 0] = [];
173
        let request = SubprotocolRequest::from_iter(empty_iter);
174
        assert_eq!(
175
            HandshakeSubprotocols::try_from_request(request),
176
            Ok(HandshakeSubprotocols {
177
                relay_crypt_cgo: false,
178
            }),
179
        );
180

            
181
        let request = SubprotocolRequest::from_iter([tor_protover::named::RELAY_CRYPT_CGO]);
182
        assert_eq!(
183
            HandshakeSubprotocols::try_from_request(request),
184
            Ok(HandshakeSubprotocols {
185
                relay_crypt_cgo: true,
186
            }),
187
        );
188

            
189
        let request =
190
            SubprotocolRequest::from_iter([tor_protover::named::RELAY_NEGOTIATE_SUBPROTO]);
191
        assert!(HandshakeSubprotocols::try_from_request(request).is_err());
192

            
193
        let request = SubprotocolRequest::from_iter([
194
            tor_protover::named::RELAY_NEGOTIATE_SUBPROTO,
195
            tor_protover::named::RELAY_CRYPT_CGO,
196
        ]);
197
        assert!(HandshakeSubprotocols::try_from_request(request).is_err());
198
    }
199
}