tor_proto/client/reactor/circuit/
extender.rs1use super::{Circuit, ReactorResultChannel};
4use crate::circuit::circhop::HopSettings;
5use crate::client::circuit::handshake::HandshakeRole;
6use crate::client::reactor::MetaCellDisposition;
7use crate::crypto::cell::HopNum;
8use crate::crypto::handshake::fast::CreateFastClient;
9use crate::crypto::handshake::ntor_v3::NtorV3Client;
10use crate::tunnel::TunnelScopedCircId;
11use crate::{Error, Result};
12use crate::{HopLocation, congestion};
13use oneshot_fused_workaround as oneshot;
14use std::borrow::Borrow;
15use tor_basic_utils::onionperf_types::{OnionperfCircuitStatus, OnionperfEvent};
16use tor_cell::chancell::CircId;
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::circuit::circhop::SendRelayCell;
23use crate::client::circuit::path;
24use crate::client::reactor::MetaCellHandler;
25use crate::crypto::handshake::ntor::NtorClient;
26use crate::crypto::handshake::{ClientHandshake, KeyGenerator};
27use tor_cell::relaycell::extend::CircResponseExt;
28use tor_linkspec::{EncodedLinkSpec, OwnedChanTarget};
29use tracing::trace;
30
31pub(crate) struct CircuitExtender<H>
36where
37 H: ClientHandshake,
38{
39 peer_id: OwnedChanTarget,
43 state: Option<H::StateType>,
45 settings: HopSettings,
47 unique_id: TunnelScopedCircId,
49 circ_id: CircId,
51 expected_hop: HopNum,
53 operation_finished: Option<oneshot::Sender<Result<()>>>,
55}
56impl<H> CircuitExtender<H>
57where
58 H: ClientHandshake + HandshakeAuxDataHandler,
59 H::KeyGen: KeyGenerator,
60{
61 #[allow(clippy::too_many_arguments)]
70 #[allow(clippy::blocks_in_conditions)]
71 pub(crate) fn begin(
72 peer_id: OwnedChanTarget,
73 handshake_id: HandshakeType,
74 key: &H::KeyType,
75 linkspecs: Vec<EncodedLinkSpec>,
76 settings: HopSettings,
77 client_aux_data: &impl Borrow<H::ClientAuxData>,
78 circ: &mut Circuit,
79 done: ReactorResultChannel<()>,
80 ) -> Result<(Self, SendRelayCell)> {
81 match (|| {
82 let mut rng = rand::rng();
83 let unique_id = circ.unique_id;
84 let circ_id = circ.circ_id;
85
86 let (state, msg) = H::client1(&mut rng, key, client_aux_data)?;
87 let n_hops = circ.crypto_out.n_layers();
88 let hop = ((n_hops - 1) as u8).into();
89 trace!(
90 circ_uniq_id = %unique_id,
91 forward_circ_id = %circ_id,
92 target_hop = n_hops + 1,
93 linkspecs = ?linkspecs,
94 "Extending circuit",
95 );
96 let extend_msg = Extend2::new(linkspecs, handshake_id, msg);
97 let cell = AnyRelayMsgOuter::new(None, extend_msg.into());
98 let cell = SendRelayCell {
100 hop: Some(hop),
101 early: true, cell,
103 };
104
105 trace!(
106 circ_uniq_id = %unique_id,
107 forward_circ_id = %circ_id,
108 "waiting for EXTENDED2 cell"
109 );
110 let extender = Self {
112 peer_id,
113 state: Some(state),
114 settings,
115 unique_id,
116 circ_id,
117 expected_hop: hop,
118 operation_finished: None,
119 };
120
121 Ok::<(CircuitExtender<_>, SendRelayCell), Error>((extender, cell))
122 })() {
123 Ok(mut result) => {
124 result.0.operation_finished = Some(done);
125 Ok(result)
126 }
127 Err(e) => {
128 let _ = done.send(Err(e.clone()));
130 Err(e)
131 }
132 }
133 }
134
135 fn extend_circuit(
139 &mut self,
140 msg: UnparsedRelayMsg,
141 circ: &mut Circuit,
142 ) -> Result<MetaCellDisposition> {
143 let msg = msg
144 .decode::<Extended2>()
145 .map_err(|e| Error::from_bytes_err(e, "extended2 message"))?
146 .into_msg();
147
148 let relay_handshake = msg.into_body();
149
150 trace!(
151 circ_uniq_id = %self.unique_id,
152 forward_circ_id = %self.circ_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 = self
169 .settings
170 .relay_crypt_protocol()
171 .construct_client_layers(HandshakeRole::Initiator, keygen)?;
172
173 trace!(
174 circ_uniq_id = %self.unique_id,
175 forward_circ_id = %self.circ_id,
176 settings = ?self.settings,
177 "Handshake complete; circuit extended."
178 );
179
180 trace!(
181 onionperf = true,
182 circ_uniq_id = %self.unique_id,
183 forward_circ_id = %self.circ_id,
184 event = ?OnionperfEvent::Circuit(OnionperfCircuitStatus::Extended),
185 );
186
187 circ.add_hop(
189 path::HopDetail::Relay(self.peer_id.clone()),
190 layer.fwd,
191 layer.back,
192 layer.binding,
193 &self.settings,
194 )?;
195 Ok(MetaCellDisposition::ConversationFinished)
196 }
197}
198
199impl<H> MetaCellHandler for CircuitExtender<H>
200where
201 H: ClientHandshake + HandshakeAuxDataHandler,
202 H::StateType: Send,
203 H::KeyGen: KeyGenerator,
204{
205 fn expected_hop(&self) -> HopLocation {
206 (self.unique_id.unique_id(), self.expected_hop).into()
207 }
208 fn handle_msg(
209 &mut self,
210 msg: UnparsedRelayMsg,
211 circ: &mut Circuit,
212 ) -> Result<MetaCellDisposition> {
213 let status = self.extend_circuit(msg, circ);
214
215 if let Some(done) = self.operation_finished.take() {
216 let _ = done.send(status.as_ref().map(|_| ()).map_err(Clone::clone));
218 status
219 } else {
220 Err(Error::from(internal!(
221 "Passed two messages to an CircuitExtender!"
222 )))
223 }
224 }
225}
226
227pub(crate) trait HandshakeAuxDataHandler: ClientHandshake {
244 fn handle_server_aux_data(
247 settings: &mut HopSettings,
248 data: &<Self as ClientHandshake>::ServerAuxData,
249 ) -> Result<()>;
250}
251
252impl HandshakeAuxDataHandler for NtorV3Client {
253 fn handle_server_aux_data(
254 settings: &mut HopSettings,
255 data: &Vec<CircResponseExt>,
256 ) -> Result<()> {
257 let mut cc_response = false;
259
260 for ext in data {
262 match ext {
263 CircResponseExt::CcResponse(ack_ext) => {
264 cc_response = true;
265
266 if !settings.ccontrol.is_enabled() {
269 return Err(Error::HandshakeProto(
270 "Received unexpected ntorv3 CC ack extension".into(),
271 ));
272 }
273 let sendme_inc = ack_ext.sendme_inc();
274 if !congestion::params::is_sendme_inc_valid(sendme_inc, &settings.ccontrol) {
276 return Err(Error::HandshakeProto(
277 "Received invalid sendme increment in CC ntorv3 extension".into(),
278 ));
279 }
280 settings
282 .ccontrol
283 .cwnd_params_mut()
284 .set_sendme_inc(sendme_inc);
285 }
286 _ => {
288 return Err(Error::HandshakeProto(
289 "Received unexpected ntorv3 extension".into(),
290 ));
291 }
292 }
293 }
294
295 if settings.ccontrol.is_enabled() && !cc_response {
297 return Err(Error::HandshakeProto(
304 "Requested congestion control but did not receive an ntor-v3 cc response".into(),
305 ));
306 }
307
308 Ok(())
309 }
310}
311
312impl HandshakeAuxDataHandler for NtorClient {
313 fn handle_server_aux_data(_settings: &mut HopSettings, _data: &()) -> Result<()> {
314 Ok(())
316 }
317}
318
319impl HandshakeAuxDataHandler for CreateFastClient {
320 fn handle_server_aux_data(_settings: &mut HopSettings, _data: &()) -> Result<()> {
321 Ok(())
323 }
324}