1mod extend_handler;
4
5use extend_handler::ExtendRequestHandler;
6
7use crate::channel::{Channel, ChannelSender};
8use crate::circuit::CircuitRxReceiver;
9use crate::circuit::UniqId;
10use crate::circuit::celltypes::RelayMaybeEarlyChanMsg;
11use crate::circuit::reactor::ControlHandler;
12use crate::circuit::reactor::backward::BackwardReactorCmd;
13use crate::circuit::reactor::forward::{ForwardCellDisposition, ForwardHandler};
14use crate::circuit::reactor::hop_mgr::HopMgr;
15use crate::crypto::cell::OutboundRelayLayer;
16use crate::crypto::cell::RelayCellBody;
17use crate::relay::RelayCircChanMsg;
18use crate::util::err::ReactorError;
19use crate::{Error, HopNum, Result};
20
21use crate::client::circuit::padding::QueuedCellPaddingInfo;
23
24use crate::relay::channel_provider::ChannelProvider;
25use crate::relay::reactor::CircuitAccount;
26use tor_cell::chancell::msg::{AnyChanMsg, Destroy, PaddingNegotiate, Relay};
27use tor_cell::chancell::{AnyChanCell, BoxedCellBody, ChanMsg, CircId};
28use tor_cell::relaycell::msg::{Extended2, SendmeTag};
29use tor_cell::relaycell::{RelayCellDecoderResult, RelayCellFormat, RelayCmd, UnparsedRelayMsg};
30use tor_error::internal;
31use tor_linkspec::OwnedChanTarget;
32use tor_rtcompat::Runtime;
33
34use futures::channel::mpsc;
35use futures::{SinkExt as _, future};
36use tracing::{debug, trace};
37
38use std::result::Result as StdResult;
39use std::sync::Arc;
40use std::task::Poll;
41
42type CtrlMsg = ();
44
45type CtrlCmd = ();
47
48const MAX_RELAY_EARLY_CELLS_PER_CIRCUIT: usize = 8;
52
53pub(crate) struct Forward {
55 unique_id: UniqId,
57 outbound: Option<Outbound>,
63 crypto_out: Box<dyn OutboundRelayLayer + Send>,
65 relay_early_count: usize,
69 extend_handler: ExtendRequestHandler,
73}
74
75pub(crate) enum CircEvent {
77 ExtendResult(StdResult<ExtendResult, ReactorError>),
79}
80
81pub(crate) struct ExtendResult {
83 extended2: Extended2,
85 outbound: Outbound,
87 outbound_chan_rx: CircuitRxReceiver,
91}
92
93struct Outbound {
95 circ_id: CircId,
97 channel: Arc<Channel>,
99 outbound_chan_tx: ChannelSender,
101}
102
103enum CellDecodeResult {
105 Recognized(SendmeTag, RelayCellDecoderResult),
107 Unrecognizd(RelayCellBody),
109}
110
111impl Forward {
112 pub(crate) fn new(
114 inbound_chan: &Arc<Channel>,
115 unique_id: UniqId,
116 crypto_out: Box<dyn OutboundRelayLayer + Send>,
117 chan_provider: Arc<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
118 event_tx: mpsc::Sender<CircEvent>,
119 memquota: CircuitAccount,
120 ) -> Self {
121 let inbound_peer = Arc::clone(inbound_chan.peer_info());
122 let extend_handler =
123 ExtendRequestHandler::new(unique_id, chan_provider, inbound_peer, event_tx, memquota);
124
125 Self {
126 unique_id,
127 outbound: None,
129 crypto_out,
130 relay_early_count: 0,
131 extend_handler,
132 }
133 }
134
135 fn decode_relay_cell<R: Runtime>(
137 &mut self,
138 hop_mgr: &mut HopMgr<R>,
139 cell: RelayMaybeEarlyChanMsg,
140 ) -> Result<(Option<HopNum>, CellDecodeResult)> {
141 let hopnum = None;
143 let cmd = cell.cmd();
144 let mut body = cell.into_relay_body().into();
145 let Some(tag) = self.crypto_out.decrypt_outbound(cmd, &mut body) else {
146 return Ok((hopnum, CellDecodeResult::Unrecognizd(body)));
147 };
148
149 let mut hops = hop_mgr.hops().write().expect("poisoned lock");
151 let decode_res = hops
152 .get_mut(hopnum)
153 .ok_or_else(|| internal!("msg from non-existent hop???"))?
154 .inbound
155 .decode(body.into())?;
156
157 Ok((hopnum, CellDecodeResult::Recognized(tag, decode_res)))
158 }
159
160 #[allow(clippy::unnecessary_wraps)] fn handle_drop(&mut self) -> StdResult<(), ReactorError> {
163 cfg_if::cfg_if! {
164 if #[cfg(feature = "circ-padding")] {
165 Err(internal!("relay circuit padding not yet supported").into())
166 } else {
167 Ok(())
168 }
169 }
170 }
171
172 fn handle_extend_result(
174 &mut self,
175 res: StdResult<ExtendResult, ReactorError>,
176 ) -> StdResult<Option<BackwardReactorCmd>, ReactorError> {
177 let ExtendResult {
178 extended2,
179 outbound,
180 outbound_chan_rx,
181 } = res?;
182
183 self.outbound = Some(outbound);
184
185 Ok(Some(BackwardReactorCmd::HandleCircuitExtended {
186 hop: None,
187 extended2,
188 outbound_chan_rx,
189 }))
190 }
191
192 fn handle_relay_cell<R: Runtime>(
194 &mut self,
195 hop_mgr: &mut HopMgr<R>,
196 cell: RelayMaybeEarlyChanMsg,
197 ) -> StdResult<Option<ForwardCellDisposition>, ReactorError> {
198 let early = matches!(cell, RelayMaybeEarlyChanMsg::RelayEarly(_));
199
200 if early {
201 self.relay_early_count += 1;
202
203 if self.relay_early_count > MAX_RELAY_EARLY_CELLS_PER_CIRCUIT {
204 return Err(
205 Error::CircProto("Circuit received too many RELAY_EARLY cells".into()).into(),
206 );
207 }
208 }
209
210 let (hopnum, res) = self.decode_relay_cell(hop_mgr, cell)?;
211 let (tag, decode_res) = match res {
212 CellDecodeResult::Unrecognizd(body) => {
213 self.handle_unrecognized_cell(body, None, early)?;
214 return Ok(None);
215 }
216 CellDecodeResult::Recognized(tag, res) => (tag, res),
217 };
218
219 Ok(Some(ForwardCellDisposition::HandleRecognizedRelay {
220 cell: decode_res,
221 early,
222 hopnum,
223 tag,
224 }))
225 }
226
227 fn handle_unrecognized_cell(
229 &mut self,
230 body: RelayCellBody,
231 info: Option<QueuedCellPaddingInfo>,
232 early: bool,
233 ) -> StdResult<(), ReactorError> {
234 trace!(
238 circ_id = %self.unique_id,
239 "Forwarding unrecognized cell"
240 );
241
242 let Some(chan) = self.outbound.as_mut() else {
243 return Err(Error::CircProto(
246 "Asked to forward cell before the circuit was extended?!".into(),
247 )
248 .into());
249 };
250
251 let msg = Relay::from(BoxedCellBody::from(body));
252 let relay = if early {
253 AnyChanMsg::RelayEarly(msg.into())
254 } else {
255 AnyChanMsg::Relay(msg)
256 };
257 let cell = AnyChanCell::new(Some(chan.circ_id), relay);
258
259 chan.outbound_chan_tx.start_send_unpin((cell, info))?;
262
263 Ok(())
264 }
265
266 fn handle_truncate(&mut self) -> StdResult<(), ReactorError> {
268 Err(Error::CircProto("TRUNCATE not allowed".into()).into())
274 }
275
276 fn handle_destroy_cell(&mut self, cell: &Destroy) -> StdResult<(), ReactorError> {
278 debug!(
279 circ_id = %self.unique_id,
280 reason = %cell.reason(),
281 "Received outbound DESTROY, circuit shutting down",
282 );
283
284 Err(ReactorError::Shutdown)
287 }
288
289 #[allow(clippy::needless_pass_by_value)] fn handle_padding_negotiate(&mut self, _cell: PaddingNegotiate) -> StdResult<(), ReactorError> {
292 Err(internal!("PADDING_NEGOTIATE is not implemented").into())
293 }
294}
295
296impl ForwardHandler for Forward {
297 type BuildSpec = OwnedChanTarget;
298 type CircChanMsg = RelayCircChanMsg;
299 type CircEvent = CircEvent;
300
301 async fn handle_meta_msg<R: Runtime>(
302 &mut self,
303 runtime: &R,
304 early: bool,
305 _hopnum: Option<HopNum>,
306 msg: UnparsedRelayMsg,
307 _relay_cell_format: RelayCellFormat,
308 ) -> StdResult<(), ReactorError> {
309 match msg.cmd() {
310 RelayCmd::DROP => self.handle_drop(),
311 RelayCmd::EXTEND2 => self.extend_handler.handle_extend2(runtime, early, msg),
312 RelayCmd::TRUNCATE => self.handle_truncate(),
313 cmd => Err(internal!("relay cmd {cmd} not supported").into()),
314 }
315 }
316
317 async fn handle_forward_cell<R: Runtime>(
318 &mut self,
319 hop_mgr: &mut HopMgr<R>,
320 cell: RelayCircChanMsg,
321 ) -> StdResult<Option<ForwardCellDisposition>, ReactorError> {
322 use RelayCircChanMsg::*;
323
324 match cell {
325 Relay(r) => self.handle_relay_cell(hop_mgr, r.into()),
326 RelayEarly(r) => self.handle_relay_cell(hop_mgr, r.into()),
327 Destroy(d) => {
328 self.handle_destroy_cell(&d)?;
329 Ok(None)
330 }
331 PaddingNegotiate(p) => {
332 self.handle_padding_negotiate(p)?;
333 Ok(None)
334 }
335 }
336 }
337
338 fn handle_event(
339 &mut self,
340 event: Self::CircEvent,
341 ) -> StdResult<Option<BackwardReactorCmd>, ReactorError> {
342 match event {
343 CircEvent::ExtendResult(res) => self.handle_extend_result(res),
344 }
345 }
346
347 async fn outbound_chan_ready(&mut self) -> Result<()> {
348 future::poll_fn(|cx| match &mut self.outbound {
349 Some(chan) => {
350 let _ = chan.outbound_chan_tx.poll_flush_unpin(cx);
351
352 chan.outbound_chan_tx.poll_ready_unpin(cx)
353 }
354 None => {
355 Poll::Ready(Ok(()))
365 }
366 })
367 .await
368 }
369}
370
371impl ControlHandler for Forward {
372 type CtrlMsg = CtrlMsg;
373 type CtrlCmd = CtrlCmd;
374
375 fn handle_cmd(&mut self, cmd: Self::CtrlCmd) -> StdResult<(), ReactorError> {
376 let () = cmd;
377 Ok(())
378 }
379
380 fn handle_msg(&mut self, msg: Self::CtrlMsg) -> StdResult<(), ReactorError> {
381 let () = msg;
382 Ok(())
383 }
384}
385
386impl Drop for Forward {
387 fn drop(&mut self) {
388 if let Some(outbound) = self.outbound.as_mut() {
389 let _ = outbound.channel.close_circuit(outbound.circ_id);
391 }
392 }
393}