tor_proto/tunnel/reactor/circuit/
extender.rs1use super::{Circuit, ReactorResultChannel};
4use crate::circuit::HopSettings;
5use crate::crypto::cell::{
6 ClientLayer, CryptInit, HopNum, InboundClientLayer, OutboundClientLayer,
7};
8use crate::crypto::handshake::fast::CreateFastClient;
9use crate::crypto::handshake::ntor_v3::NtorV3Client;
10use crate::tunnel::reactor::MetaCellDisposition;
11use crate::tunnel::TunnelScopedCircId;
12use crate::{congestion, HopLocation};
13use crate::{Error, Result};
14use oneshot_fused_workaround as oneshot;
15use std::borrow::Borrow;
16use std::marker::PhantomData;
17use tor_cell::chancell::msg::HandshakeType;
18use tor_cell::relaycell::msg::{Extend2, Extended2};
19use tor_cell::relaycell::{AnyRelayMsgOuter, UnparsedRelayMsg};
20use tor_error::internal;
21
22use crate::crypto::handshake::ntor::NtorClient;
23use crate::crypto::handshake::{ClientHandshake, KeyGenerator};
24use crate::tunnel::circuit::path;
25use crate::tunnel::reactor::{MetaCellHandler, SendRelayCell};
26use tor_cell::relaycell::extend::CircResponseExt;
27use tor_linkspec::{EncodedLinkSpec, OwnedChanTarget};
28use tracing::trace;
29
30pub(crate) struct CircuitExtender<H, L, FWD, REV>
35where
36 H: ClientHandshake,
37{
38 peer_id: OwnedChanTarget,
42 state: Option<H::StateType>,
44 settings: HopSettings,
46 unique_id: TunnelScopedCircId,
48 expected_hop: HopNum,
50 operation_finished: Option<oneshot::Sender<Result<()>>>,
52 phantom: PhantomData<(L, FWD, REV)>,
59}
60impl<H, L, FWD, REV> CircuitExtender<H, L, FWD, REV>
61where
62 H: ClientHandshake + HandshakeAuxDataHandler,
63 H::KeyGen: KeyGenerator,
64 L: CryptInit + ClientLayer<FWD, REV>,
65 FWD: OutboundClientLayer + 'static + Send,
66 REV: InboundClientLayer + 'static + Send,
67{
68 #[allow(clippy::too_many_arguments)]
77 #[allow(clippy::blocks_in_conditions)]
78 pub(crate) fn begin(
79 peer_id: OwnedChanTarget,
80 handshake_id: HandshakeType,
81 key: &H::KeyType,
82 linkspecs: Vec<EncodedLinkSpec>,
83 settings: HopSettings,
84 client_aux_data: &impl Borrow<H::ClientAuxData>,
85 circ: &mut Circuit,
86 done: ReactorResultChannel<()>,
87 ) -> Result<(Self, SendRelayCell)> {
88 match (|| {
89 let mut rng = rand::rng();
90 let unique_id = circ.unique_id;
91
92 let (state, msg) = H::client1(&mut rng, key, client_aux_data)?;
93 let n_hops = circ.crypto_out.n_layers();
94 let hop = ((n_hops - 1) as u8).into();
95 trace!(
96 circ_id = %unique_id,
97 target_hop = n_hops + 1,
98 linkspecs = ?linkspecs,
99 "Extending circuit",
100 );
101 let extend_msg = Extend2::new(linkspecs, handshake_id, msg);
102 let cell = AnyRelayMsgOuter::new(None, extend_msg.into());
103 let cell = SendRelayCell {
105 hop,
106 early: true, cell,
108 };
109
110 trace!(circ_id = %unique_id, "waiting for EXTENDED2 cell");
111 let extender = Self {
113 peer_id,
114 state: Some(state),
115 settings,
116 unique_id,
117 expected_hop: hop,
118 operation_finished: None,
119 phantom: Default::default(),
120 };
121
122 Ok::<(CircuitExtender<_, _, _, _>, SendRelayCell), Error>((extender, cell))
123 })() {
124 Ok(mut result) => {
125 result.0.operation_finished = Some(done);
126 Ok(result)
127 }
128 Err(e) => {
129 let _ = done.send(Err(e.clone()));
131 Err(e)
132 }
133 }
134 }
135
136 fn extend_circuit(
140 &mut self,
141 msg: UnparsedRelayMsg,
142 circ: &mut Circuit,
143 ) -> Result<MetaCellDisposition> {
144 let msg = msg
145 .decode::<Extended2>()
146 .map_err(|e| Error::from_bytes_err(e, "extended2 message"))?
147 .into_msg();
148
149 let relay_handshake = msg.into_body();
150
151 trace!(
152 circ_id = %self.unique_id,
153 "Received EXTENDED2 cell; completing handshake.",
154 );
155 let (server_aux_data, keygen) = H::client2(
158 self.state
159 .take()
160 .expect("CircuitExtender::finish() called twice"),
161 relay_handshake,
162 )?;
163
164 H::handle_server_aux_data(&mut self.settings, &server_aux_data)?;
167
168 let layer = L::construct(keygen)?;
169
170 trace!(circ_id = %self.unique_id, "Handshake complete; circuit extended.");
171
172 let (layer_fwd, layer_back, binding) = layer.split_client_layer();
174 circ.add_hop(
175 path::HopDetail::Relay(self.peer_id.clone()),
176 Box::new(layer_fwd),
177 Box::new(layer_back),
178 Some(binding),
179 &self.settings,
180 )?;
181 Ok(MetaCellDisposition::ConversationFinished)
182 }
183}
184
185impl<H, L, FWD, REV> MetaCellHandler for CircuitExtender<H, L, FWD, REV>
186where
187 H: ClientHandshake + HandshakeAuxDataHandler,
188 H::StateType: Send,
189 H::KeyGen: KeyGenerator,
190 L: CryptInit + ClientLayer<FWD, REV> + Send,
191 FWD: OutboundClientLayer + 'static + Send,
192 REV: InboundClientLayer + 'static + Send,
193{
194 fn expected_hop(&self) -> HopLocation {
195 (self.unique_id.unique_id(), self.expected_hop).into()
196 }
197 fn handle_msg(
198 &mut self,
199 msg: UnparsedRelayMsg,
200 circ: &mut Circuit,
201 ) -> Result<MetaCellDisposition> {
202 let status = self.extend_circuit(msg, circ);
203
204 if let Some(done) = self.operation_finished.take() {
205 let _ = done.send(status.as_ref().map(|_| ()).map_err(Clone::clone));
207 status
208 } else {
209 Err(Error::from(internal!(
210 "Passed two messages to an CircuitExtender!"
211 )))
212 }
213 }
214}
215
216pub(crate) trait HandshakeAuxDataHandler: ClientHandshake {
233 fn handle_server_aux_data(
236 settings: &mut HopSettings,
237 data: &<Self as ClientHandshake>::ServerAuxData,
238 ) -> Result<()>;
239}
240
241impl HandshakeAuxDataHandler for NtorV3Client {
242 fn handle_server_aux_data(
243 settings: &mut HopSettings,
244 data: &Vec<CircResponseExt>,
245 ) -> Result<()> {
246 #[cfg_attr(not(feature = "flowctl-cc"), allow(clippy::never_loop))]
250 for ext in data {
251 match ext {
252 CircResponseExt::CcResponse(ack_ext) => {
253 cfg_if::cfg_if! {
254 if #[cfg(feature = "flowctl-cc")] {
255 if !settings.ccontrol.is_enabled() {
258 return Err(Error::HandshakeProto(
259 "Received unexpected ntorv3 CC ack extension".into(),
260 ));
261 }
262 let sendme_inc = ack_ext.sendme_inc();
263 if !congestion::params::is_sendme_inc_valid(sendme_inc, &settings.ccontrol) {
265 return Err(Error::HandshakeProto(
266 "Received invalid sendme increment in CC ntorv3 extension".into(),
267 ));
268 }
269 settings
271 .ccontrol
272 .cwnd_params_mut()
273 .set_sendme_inc(sendme_inc);
274 } else {
275 let _ = ack_ext;
276 return Err(Error::HandshakeProto(
277 "Received unexpected `AckCongestionControl` ntorv3 extension".into(),
278 ));
279 }
280 }
281 }
282 _ => {
284 return Err(Error::HandshakeProto(
285 "Received unexpected ntorv3 extension".into(),
286 ));
287 }
288 }
289 }
290 Ok(())
291 }
292}
293
294impl HandshakeAuxDataHandler for NtorClient {
295 fn handle_server_aux_data(_settings: &mut HopSettings, _data: &()) -> Result<()> {
296 Ok(())
298 }
299}
300
301impl HandshakeAuxDataHandler for CreateFastClient {
302 fn handle_server_aux_data(_settings: &mut HopSettings, _data: &()) -> Result<()> {
303 Ok(())
305 }
306}