1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
|
//! Circuit-related types and helpers.
//!
//! This code is shared between the client and relay implementations.
pub(crate) mod cell_sender;
pub(crate) mod celltypes;
pub(crate) mod circ_sender;
pub(crate) mod circhop;
pub(crate) mod create;
pub(crate) mod padding;
pub(crate) mod reactor;
pub(crate) mod syncview;
pub(crate) mod unique_id;
pub use crate::memquota::StreamAccount;
pub use syncview::CircHopSyncView;
pub use unique_id::UniqId;
use crate::ccparams::CongestionControlParams;
use crate::stream::flow_ctrl::params::FlowCtrlParameters;
use tor_cell::relaycell::extend::SubprotocolRequest;
use tor_error::ErrorKind;
use tor_protover::Protocols;
pub(crate) use circ_sender::{CircuitRxReceiver, CircuitRxSender};
/// Estimated upper bound for the likely number of hops.
pub(crate) const HOPS: usize = 6;
/// Description of the network's current rules for building circuits.
///
/// This type describes rules derived from the consensus,
/// and possibly amended by our own configuration.
///
/// Typically, this type created once for an entire circuit,
/// and any special per-hop information is derived
/// from each hop as a CircTarget.
/// Note however that callers _may_ provide different `CircParameters`
/// for different hops within a circuit if they have some reason to do so,
/// so we do not enforce that every hop in a circuit has the same `CircParameters`.
#[non_exhaustive]
#[derive(Clone, Debug)]
pub struct CircParameters {
/// Whether we should include ed25519 identities when we send
/// EXTEND2 cells.
pub extend_by_ed25519_id: bool,
/// Congestion control parameters for this circuit.
pub ccontrol: CongestionControlParams,
/// Flow control parameters to use for all streams on this circuit.
// While flow control is a stream property and not a circuit property,
// and it may seem better to pass the flow control parameters to for example `begin_stream()`,
// it's included in [`CircParameters`] for the following reasons:
//
// - When endpoints (exits + hs) receive new stream requests, they need the flow control
// parameters immediately. It would be easy to pass flow control parameters when creating a
// stream, but it's not as easy to get flow control parameters when receiving a new stream
// request, unless those parameters are already available to the circuit (like
// `CircParameters` are).
// - It's unclear if new streams on existing circuits should switch to new flow control
// parameters if the consensus changes. This behaviour doesn't appear to be specified. It
// might also leak information to the circuit's endpoint about when we downloaded new
// directory documents. So it seems best to stick with the same flow control parameters for
// the lifetime of the circuit.
// - It doesn't belong in [`StreamParameters`] as `StreamParameters` is a set of preferences
// with defaults, and consensus parameters aren't preferences and don't have defaults.
// (Technically they have defaults, but `StreamParameters` isn't the place to set them.)
pub flow_ctrl: FlowCtrlParameters,
/// Maximum number of permitted incoming relay cells for each hop.
///
/// If we would receive more relay cells than this from a single hop,
/// we close the circuit with [`ExcessInboundCells`](crate::Error::ExcessInboundCells).
///
/// If this value is None, then there is no limit to the number of inbound cells.
///
/// Known limitation: If this value if `u32::MAX`,
/// then a limit of `u32::MAX - 1` is enforced.
pub n_incoming_cells_permitted: Option<u32>,
/// Maximum number of permitted outgoing relay cells for each hop.
///
/// If we would try to send more relay cells than this from a single hop,
/// we close the circuit with [`ExcessOutboundCells`](crate::Error::ExcessOutboundCells).
/// It is the circuit-user's responsibility to make sure that this does not happen.
///
/// This setting is used to ensure that we do not violate a limit
/// imposed by `n_incoming_cells_permitted`
/// on the other side of a circuit.
///
/// If this value is None, then there is no limit to the number of outbound cells.
///
/// Known limitation: If this value if `u32::MAX`,
/// then a limit of `u32::MAX - 1` is enforced.
pub n_outgoing_cells_permitted: Option<u32>,
}
tor_protover::subprotocol_restricted_set! {
/// The enabled/disabled status of subprotocols that are allowed to be requested through a
/// subprotocol request during a circuit handshake.
///
/// The allowed subprotocols are defined in:
/// <https://spec.torproject.org/tor-spec/create-created-cells.html#subproto-request>
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub(crate) struct HandshakeSubprotocols {
RELAY_CRYPT_CGO,
}
}
impl HandshakeSubprotocols {
/// Build a [`HandshakeSubprotocols`] from a [`SubprotocolRequest`]
/// provided during a circuit handshake.
///
/// If the `SubprotocolRequest` contains subprotocols that aren't
/// allowed to be requested through a subprotocol request,
/// this returns an error containing the original `SubprotocolRequest`.
//
// It would be nice to return a list of only the invalid subprotocols,
// but it seems a bit expensive to compute on the error path when we probably
// want to fail quickly.
pub(crate) fn try_from_request(
protos: SubprotocolRequest,
) -> Result<Self, InvalidHandshakeSubprotocolError> {
use std::sync::LazyLock;
static ALL: LazyLock<Protocols> =
LazyLock::new(|| Protocols::from(HandshakeSubprotocols::ALL));
if !protos.contains_only(&ALL) {
return Err(InvalidHandshakeSubprotocolError(protos));
}
Ok(Self {
relay_crypt_cgo: protos.contains(tor_protover::named::RELAY_CRYPT_CGO),
})
}
}
/// The subprotocol request had subprotocols that are not all supported in circuit handshakes.
///
/// Contains the requested subprotocols (both valid and invalid).
#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[error("Request included subprotocols that we do not support in circuit handshakes: {0:?}")]
pub(crate) struct InvalidHandshakeSubprotocolError(SubprotocolRequest);
impl tor_error::HasKind for InvalidHandshakeSubprotocolError {
fn kind(&self) -> ErrorKind {
ErrorKind::TorProtocolViolation
}
}
#[cfg(test)]
pub(crate) mod test {
use super::*;
#[cfg(feature = "relay")]
use crate::relay::{CircNetParameters, CongestionControlNetParams};
pub(crate) use super::circ_sender::test::fake_mpsc;
/// Return a new [`CircNetParameters`] using default values for unit tests. They are based on
/// consensus defaults but should not be considered to be accurate from the one used on the
/// production network.
#[cfg(feature = "relay")]
pub(crate) fn new_circ_net_params() -> CircNetParameters {
CircNetParameters {
cc: CongestionControlNetParams::defaults_for_tests(),
}
}
#[test]
fn handshake_subprotocols() {
let empty_iter: [tor_protover::NumberedSubver; 0] = [];
let request = SubprotocolRequest::from_iter(empty_iter);
assert_eq!(
HandshakeSubprotocols::try_from_request(request),
Ok(HandshakeSubprotocols {
relay_crypt_cgo: false,
}),
);
let request = SubprotocolRequest::from_iter([tor_protover::named::RELAY_CRYPT_CGO]);
assert_eq!(
HandshakeSubprotocols::try_from_request(request),
Ok(HandshakeSubprotocols {
relay_crypt_cgo: true,
}),
);
let request =
SubprotocolRequest::from_iter([tor_protover::named::RELAY_NEGOTIATE_SUBPROTO]);
assert!(HandshakeSubprotocols::try_from_request(request).is_err());
let request = SubprotocolRequest::from_iter([
tor_protover::named::RELAY_NEGOTIATE_SUBPROTO,
tor_protover::named::RELAY_CRYPT_CGO,
]);
assert!(HandshakeSubprotocols::try_from_request(request).is_err());
}
}
|