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_cell::chancell::msg::HandshakeType;
16use tor_cell::relaycell::msg::{Extend2, Extended2};
17use tor_cell::relaycell::{AnyRelayMsgOuter, UnparsedRelayMsg};
18use tor_error::internal;
19
20use crate::circuit::circhop::SendRelayCell;
21use crate::client::circuit::path;
22use crate::client::reactor::MetaCellHandler;
23use crate::crypto::handshake::ntor::NtorClient;
24use crate::crypto::handshake::{ClientHandshake, KeyGenerator};
25use tor_cell::relaycell::extend::CircResponseExt;
26use tor_linkspec::{EncodedLinkSpec, OwnedChanTarget};
27use tracing::trace;
28
29pub(crate) struct CircuitExtender<H>
34where
35 H: ClientHandshake,
36{
37 peer_id: OwnedChanTarget,
41 state: Option<H::StateType>,
43 settings: HopSettings,
45 unique_id: TunnelScopedCircId,
47 expected_hop: HopNum,
49 operation_finished: Option<oneshot::Sender<Result<()>>>,
51}
52impl<H> CircuitExtender<H>
53where
54 H: ClientHandshake + HandshakeAuxDataHandler,
55 H::KeyGen: KeyGenerator,
56{
57 #[allow(clippy::too_many_arguments)]
66 #[allow(clippy::blocks_in_conditions)]
67 pub(crate) fn begin(
68 peer_id: OwnedChanTarget,
69 handshake_id: HandshakeType,
70 key: &H::KeyType,
71 linkspecs: Vec<EncodedLinkSpec>,
72 settings: HopSettings,
73 client_aux_data: &impl Borrow<H::ClientAuxData>,
74 circ: &mut Circuit,
75 done: ReactorResultChannel<()>,
76 ) -> Result<(Self, SendRelayCell)> {
77 match (|| {
78 let mut rng = rand::rng();
79 let unique_id = circ.unique_id;
80
81 let (state, msg) = H::client1(&mut rng, key, client_aux_data)?;
82 let n_hops = circ.crypto_out.n_layers();
83 let hop = ((n_hops - 1) as u8).into();
84 trace!(
85 circ_id = %unique_id,
86 target_hop = n_hops + 1,
87 linkspecs = ?linkspecs,
88 "Extending circuit",
89 );
90 let extend_msg = Extend2::new(linkspecs, handshake_id, msg);
91 let cell = AnyRelayMsgOuter::new(None, extend_msg.into());
92 let cell = SendRelayCell {
94 hop: Some(hop),
95 early: true, cell,
97 };
98
99 trace!(circ_id = %unique_id, "waiting for EXTENDED2 cell");
100 let extender = Self {
102 peer_id,
103 state: Some(state),
104 settings,
105 unique_id,
106 expected_hop: hop,
107 operation_finished: None,
108 };
109
110 Ok::<(CircuitExtender<_>, SendRelayCell), Error>((extender, cell))
111 })() {
112 Ok(mut result) => {
113 result.0.operation_finished = Some(done);
114 Ok(result)
115 }
116 Err(e) => {
117 let _ = done.send(Err(e.clone()));
119 Err(e)
120 }
121 }
122 }
123
124 fn extend_circuit(
128 &mut self,
129 msg: UnparsedRelayMsg,
130 circ: &mut Circuit,
131 ) -> Result<MetaCellDisposition> {
132 let msg = msg
133 .decode::<Extended2>()
134 .map_err(|e| Error::from_bytes_err(e, "extended2 message"))?
135 .into_msg();
136
137 let relay_handshake = msg.into_body();
138
139 trace!(
140 circ_id = %self.unique_id,
141 "Received EXTENDED2 cell; completing handshake.",
142 );
143 let (server_aux_data, keygen) = H::client2(
146 self.state
147 .take()
148 .expect("CircuitExtender::finish() called twice"),
149 relay_handshake,
150 )?;
151
152 H::handle_server_aux_data(&mut self.settings, &server_aux_data)?;
155
156 let layer = self
157 .settings
158 .relay_crypt_protocol()
159 .construct_client_layers(HandshakeRole::Initiator, keygen)?;
160
161 trace!(circ_id = %self.unique_id, settings = ?self.settings,
162 "Handshake complete; circuit extended.");
163
164 circ.add_hop(
166 path::HopDetail::Relay(self.peer_id.clone()),
167 layer.fwd,
168 layer.back,
169 layer.binding,
170 &self.settings,
171 )?;
172 Ok(MetaCellDisposition::ConversationFinished)
173 }
174}
175
176impl<H> MetaCellHandler for CircuitExtender<H>
177where
178 H: ClientHandshake + HandshakeAuxDataHandler,
179 H::StateType: Send,
180 H::KeyGen: KeyGenerator,
181{
182 fn expected_hop(&self) -> HopLocation {
183 (self.unique_id.unique_id(), self.expected_hop).into()
184 }
185 fn handle_msg(
186 &mut self,
187 msg: UnparsedRelayMsg,
188 circ: &mut Circuit,
189 ) -> Result<MetaCellDisposition> {
190 let status = self.extend_circuit(msg, circ);
191
192 if let Some(done) = self.operation_finished.take() {
193 let _ = done.send(status.as_ref().map(|_| ()).map_err(Clone::clone));
195 status
196 } else {
197 Err(Error::from(internal!(
198 "Passed two messages to an CircuitExtender!"
199 )))
200 }
201 }
202}
203
204pub(crate) trait HandshakeAuxDataHandler: ClientHandshake {
221 fn handle_server_aux_data(
224 settings: &mut HopSettings,
225 data: &<Self as ClientHandshake>::ServerAuxData,
226 ) -> Result<()>;
227}
228
229impl HandshakeAuxDataHandler for NtorV3Client {
230 fn handle_server_aux_data(
231 settings: &mut HopSettings,
232 data: &Vec<CircResponseExt>,
233 ) -> Result<()> {
234 #[cfg_attr(not(feature = "flowctl-cc"), allow(clippy::never_loop))]
238 for ext in data {
239 match ext {
240 CircResponseExt::CcResponse(ack_ext) => {
241 cfg_if::cfg_if! {
242 if #[cfg(feature = "flowctl-cc")] {
243 if !settings.ccontrol.is_enabled() {
246 return Err(Error::HandshakeProto(
247 "Received unexpected ntorv3 CC ack extension".into(),
248 ));
249 }
250 let sendme_inc = ack_ext.sendme_inc();
251 if !congestion::params::is_sendme_inc_valid(sendme_inc, &settings.ccontrol) {
253 return Err(Error::HandshakeProto(
254 "Received invalid sendme increment in CC ntorv3 extension".into(),
255 ));
256 }
257 settings
259 .ccontrol
260 .cwnd_params_mut()
261 .set_sendme_inc(sendme_inc);
262 } else {
263 let _ = ack_ext;
264 return Err(Error::HandshakeProto(
265 "Received unexpected `AckCongestionControl` ntorv3 extension".into(),
266 ));
267 }
268 }
269 }
270 _ => {
272 return Err(Error::HandshakeProto(
273 "Received unexpected ntorv3 extension".into(),
274 ));
275 }
276 }
277 }
278 Ok(())
279 }
280}
281
282impl HandshakeAuxDataHandler for NtorClient {
283 fn handle_server_aux_data(_settings: &mut HopSettings, _data: &()) -> Result<()> {
284 Ok(())
286 }
287}
288
289impl HandshakeAuxDataHandler for CreateFastClient {
290 fn handle_server_aux_data(_settings: &mut HopSettings, _data: &()) -> Result<()> {
291 Ok(())
293 }
294}