Skip to main content

tor_proto/circuit/reactor/
forward.rs

1//! A circuit's view of the forward state of the circuit.
2
3use crate::circuit::UniqId;
4use crate::circuit::reactor::backward::BackwardReactorCmd;
5use crate::circuit::reactor::hop_mgr::HopMgr;
6use crate::circuit::reactor::macros::derive_deftly_template_CircuitReactor;
7use crate::circuit::reactor::stream;
8use crate::circuit::reactor::{ControlHandler, ReactorResultChannel};
9use crate::congestion::sendme;
10use crate::stream::cmdcheck::AnyCmdChecker;
11use crate::stream::msg_streamid;
12use crate::util::err::ReactorError;
13use crate::{Error, HopNum, Result};
14
15#[cfg(any(feature = "hs-service", feature = "relay"))]
16use {
17    crate::stream::CloseStreamBehavior,
18    crate::stream::incoming::{
19        IncomingStreamRequestFilter, IncomingStreamRequestHandler, StreamReqSender,
20    },
21    tor_cell::relaycell::StreamId,
22};
23
24// TODO(circpad): once padding is stabilized, the padding module will be moved out of client.
25use crate::client::circuit::padding::PaddingController;
26
27use tor_cell::chancell::msg::AnyChanMsg;
28use tor_cell::relaycell::msg::{Sendme, SendmeTag};
29use tor_cell::relaycell::{
30    AnyRelayMsgOuter, RelayCellDecoderResult, RelayCellFormat, RelayCmd, UnparsedRelayMsg,
31};
32use tor_error::internal;
33use tor_linkspec::HasRelayIds;
34use tor_rtcompat::Runtime;
35
36use derive_deftly::Deftly;
37use futures::SinkExt;
38use futures::channel::mpsc;
39use futures::{FutureExt as _, StreamExt, select_biased};
40use tracing::debug;
41
42use std::result::Result as StdResult;
43
44use crate::circuit::CircuitRxReceiver;
45
46/// The forward circuit reactor.
47///
48/// See the [`reactor`](crate::circuit::reactor) module-level docs.
49///
50/// Shuts downs down if an error occurs, or if either the [`Reactor`](super::Reactor)
51/// or the [`BackwardReactor`](super::BackwardReactor) shuts down:
52///
53///   * if the `Reactor` shuts down, we are alerted via the ctrl/command mpsc channels
54///     (their sending ends will close, which causes run_once() to return ReactorError::Shutdown)
55///   * if `BackwardReactor` shuts down, the `Reactor` will notice and will itself shut down,
56///     which, in turn, causes the `ForwardReactor` to shut down as described above
57#[derive(Deftly)]
58#[derive_deftly(CircuitReactor)]
59#[deftly(reactor_name = "forward reactor")]
60#[deftly(run_inner_fn = "Self::run_once")]
61#[must_use = "If you don't call run() on a reactor, the circuit won't work."]
62pub(super) struct ForwardReactor<R: Runtime, F: ForwardHandler> {
63    /// A handle to the runtime.
64    runtime: R,
65    /// An identifier for logging about this reactor's circuit.
66    unique_id: UniqId,
67    /// Implementation-dependent part of the reactor.
68    ///
69    /// This enables us to customize the behavior of the reactor,
70    /// depending on whether we are a client or a relay.
71    inner: F,
72    /// Channel for receiving control commands.
73    command_rx: mpsc::UnboundedReceiver<CtrlCmd<F::CtrlCmd>>,
74    /// Channel for receiving control messages.
75    control_rx: mpsc::UnboundedReceiver<CtrlMsg<F::CtrlMsg>>,
76    /// The reading end of the inbound Tor channel.
77    ///
78    /// Yields cells moving from the client towards the exit, if we are a relay,
79    /// or cells moving towards *us*, if we are a client.
80    inbound_chan_rx: CircuitRxReceiver,
81    /// Sender for sending commands to the BackwardReactor.
82    ///
83    /// Used for sending:
84    ///
85    ///    * circuit-level SENDMEs received from the other endpoint
86    ///      (`[BackwardReactorCmd::HandleSendme]`)
87    ///    * circuit-level SENDMEs that need to be delivered to the other endpoint
88    ///      (using `[BackwardReactorCmd::SendRelayMsg]`)
89    ///
90    /// The receiver is in [`BackwardReactor`](super::BackwardReactor), which is responsible for
91    /// sending cell over the inbound channel.
92    backward_reactor_tx: mpsc::Sender<BackwardReactorCmd>,
93    /// Hop manager, storing per-hop state, and handles to the stream reactors.
94    ///
95    /// Contains the `CircHopList`.
96    hop_mgr: HopMgr<R>,
97    /// An implementation-specific event stream.
98    ///
99    /// Polled from the main loop of the reactor.
100    /// Each event is passed to [`ForwardHandler::handle_event`].
101    circ_events: mpsc::Receiver<F::CircEvent>,
102    /// A padding controller to which padding-related events should be reported.
103    padding_ctrl: PaddingController,
104}
105
106/// A control command aimed at the generic forward reactor.
107pub(crate) enum CtrlCmd<C> {
108    /// Begin accepting streams on this circuit.
109    //
110    // TODO(DEDUP): this is very similar to its client-side counterpart,
111    // except the hop is a Option<HopNum> instead of a TargetHop.
112    #[cfg(any(feature = "hs-service", feature = "relay"))]
113    #[expect(unused)] // TODO(dedup): this will be used by hs services
114    AwaitStreamRequests {
115        /// A channel for sending information about an incoming stream request.
116        incoming_sender: StreamReqSender,
117        /// A `CmdChecker` to keep track of which message types are acceptable.
118        cmd_checker: AnyCmdChecker,
119        /// Oneshot channel to notify on completion.
120        done: ReactorResultChannel<()>,
121        /// The hop that is allowed to create streams.
122        ///
123        /// Set to None if we are a relay wanting to accept stream requests.
124        hop: Option<HopNum>,
125        /// A filter used to check requests before passing them on.
126        filter: Box<dyn IncomingStreamRequestFilter>,
127    },
128
129    /// Close the specified pending incoming stream, sending the provided END message.
130    ///
131    /// A stream is said to be pending if the message for initiating the stream was received but
132    /// not has not been responded to yet.
133    ///
134    /// This should be used by responders for closing pending incoming streams initiated by the
135    /// other party on the circuit.
136    ///
137    /// TODO(dedup): this is almost identical to the ClosePendingStream control message
138    /// from the client-side. We can get rid of the duplication by rewriting
139    /// the client circuit reactor to use the new multi-reactor architecture
140    #[cfg(any(feature = "hs-service", feature = "relay"))]
141    ClosePendingStream {
142        /// The hop number the stream is on.
143        ///
144        /// Set to None if we are a relay.
145        hop: Option<HopNum>,
146        /// The stream ID to send the END for.
147        stream_id: StreamId,
148        /// The END message to send, if any.
149        message: CloseStreamBehavior,
150        /// Oneshot channel to notify on completion.
151        done: ReactorResultChannel<()>,
152    },
153    /// An implementation-dependent control command.
154    #[allow(unused)] // TODO(relay)
155    Custom(C),
156}
157
158/// A control message aimed at the generic forward reactor.
159pub(crate) enum CtrlMsg<M> {
160    /// An implementation-dependent control message.
161    #[allow(unused)] // TODO(relay)
162    Custom(M),
163}
164
165/// Trait for customizing the behavior of the forward reactor.
166///
167/// Used for plugging in the implementation-dependent (client vs relay)
168/// parts of the implementation into the generic one.
169pub(crate) trait ForwardHandler: ControlHandler {
170    /// Type that explains how to build an outgoing channel.
171    type BuildSpec: HasRelayIds;
172
173    /// The subclass of ChanMsg that can arrive on this type of circuit.
174    type CircChanMsg: TryFrom<AnyChanMsg, Error = crate::Error>;
175
176    /// An opaque event type.
177    ///
178    /// The [`ForwardReactor`] polls an MPSC stream yielding `CircEvent`s from the main loop.
179    /// Each event is passed to [`Self::handle_event`] for handling.
180    type CircEvent;
181
182    /// Handle a non-SENDME RELAY message on this circuit with stream ID 0.
183    async fn handle_meta_msg<R: Runtime>(
184        &mut self,
185        runtime: &R,
186        early: bool,
187        hopnum: Option<HopNum>,
188        msg: UnparsedRelayMsg,
189        relay_cell_format: RelayCellFormat,
190    ) -> StdResult<(), ReactorError>;
191
192    /// Handle a forward (TODO terminology) cell.
193    ///
194    /// The cell is
195    ///   - moving from the client towards the exit, if we're a relay
196    ///   - moving from the guard towards us, if we're a client
197    ///
198    /// Returns an error if the cell should cause the reactor to shut down,
199    /// or a [`ForwardCellDisposition`] specifying how it should be handled.
200    ///
201    /// Returns `None` if the cell was handled internally by this handler.
202    async fn handle_forward_cell<R: Runtime>(
203        &mut self,
204        hop_mgr: &mut HopMgr<R>,
205        cell: Self::CircChanMsg,
206    ) -> StdResult<Option<ForwardCellDisposition>, ReactorError>;
207
208    /// Handle an implementation-specific circuit event.
209    ///
210    /// Returns a command for the backward reactor.
211    fn handle_event(
212        &mut self,
213        event: Self::CircEvent,
214    ) -> StdResult<Option<BackwardReactorCmd>, ReactorError>;
215
216    /// Wait until the outbound channel, if there is one, is ready to accept more cells.
217    ///
218    /// Resolves immediately if there is no outbound channel.
219    /// Blocks if there is a pending outbound channel.
220    async fn outbound_chan_ready(&mut self) -> Result<()>;
221}
222
223/// What action to take in response to a cell arriving on our inbound Tor channel.
224pub(crate) enum ForwardCellDisposition {
225    /// Handle a decoded RELAY or RELAY_EARLY cell in the [`ForwardReactor`].
226    HandleRecognizedRelay {
227        /// The decoded cell.
228        cell: RelayCellDecoderResult,
229        /// Whether this was a RELAY_EARLY.
230        early: bool,
231        /// The hop this cell was for.
232        hopnum: Option<HopNum>,
233        /// The SENDME tag.
234        tag: SendmeTag,
235    },
236}
237
238impl<R: Runtime, F: ForwardHandler> ForwardReactor<R, F> {
239    /// Create a new [`ForwardReactor`].
240    #[allow(clippy::too_many_arguments)] // TODO
241    pub(super) fn new(
242        runtime: R,
243        unique_id: UniqId,
244        inner: F,
245        hop_mgr: HopMgr<R>,
246        inbound_chan_rx: CircuitRxReceiver,
247        control_rx: mpsc::UnboundedReceiver<CtrlMsg<F::CtrlMsg>>,
248        command_rx: mpsc::UnboundedReceiver<CtrlCmd<F::CtrlCmd>>,
249        backward_reactor_tx: mpsc::Sender<BackwardReactorCmd>,
250        circ_events: mpsc::Receiver<F::CircEvent>,
251        padding_ctrl: PaddingController,
252    ) -> Self {
253        Self {
254            runtime,
255            unique_id,
256            inbound_chan_rx,
257            control_rx,
258            command_rx,
259            inner,
260            backward_reactor_tx,
261            hop_mgr,
262            circ_events,
263            padding_ctrl,
264        }
265    }
266
267    /// Helper for [`run`](Self::run).
268    async fn run_once(&mut self) -> StdResult<(), ReactorError> {
269        let outbound_chan_ready = self.inner.outbound_chan_ready();
270
271        let inbound_chan_rx_fut = async {
272            // Avoid reading from the inbound_chan_rx Tor Channel if the outgoing sink is blocked
273            outbound_chan_ready.await?;
274            Ok(self.inbound_chan_rx.next().await)
275        };
276
277        select_biased! {
278            res = self.command_rx.next().fuse() => {
279                let cmd = res.ok_or_else(|| ReactorError::Shutdown)?;
280                self.handle_cmd(cmd).await
281            }
282            res = self.control_rx.next().fuse() => {
283                let msg = res.ok_or_else(|| ReactorError::Shutdown)?;
284                self.handle_msg(msg)
285            }
286            res = self.circ_events.next().fuse() => {
287                let ev = res.ok_or_else(|| ReactorError::Shutdown)?;
288                if let Some(cmd) = self.inner.handle_event(ev)? {
289                    self.send_reactor_cmd(cmd).await?;
290                }
291
292                Ok(())
293            }
294            res = inbound_chan_rx_fut.fuse() => {
295                let cell = res.map_err(ReactorError::Err)?;
296                let Some(cell) = cell else {
297                    debug!(
298                        circ_id = %self.unique_id,
299                        "Backward channel has closed, shutting down forward relay reactor",
300                    );
301
302                    return Err(ReactorError::Shutdown);
303                };
304
305                let cell: F::CircChanMsg = cell.try_into()?;
306                let Some(disp) = self.inner.handle_forward_cell(&mut self.hop_mgr, cell).await? else {
307                    return Ok(());
308                };
309
310                match disp {
311                    ForwardCellDisposition::HandleRecognizedRelay { cell, early, hopnum, tag } => {
312                        self.handle_relay_cell(cell, early, hopnum, tag).await
313                    }
314                }
315            },
316        }
317    }
318
319    /// Handle a control command.
320    #[allow(clippy::unused_async)] // used if any(feature = "hs-service", feature = "relay")
321    async fn handle_cmd(&mut self, cmd: CtrlCmd<F::CtrlCmd>) -> StdResult<(), ReactorError> {
322        match cmd {
323            #[cfg(any(feature = "hs-service", feature = "relay"))]
324            CtrlCmd::AwaitStreamRequests {
325                incoming_sender,
326                cmd_checker,
327                done,
328                hop,
329                filter,
330            } => {
331                let handler = IncomingStreamRequestHandler {
332                    incoming_sender,
333                    cmd_checker,
334                    hop_num: hop,
335                    filter,
336                };
337
338                // Update the HopMgr with the
339                let ret = self.hop_mgr.set_incoming_handler(handler);
340                let _ = done.send(ret); // don't care if the corresponding receiver goes away.
341                Ok(())
342            }
343            #[cfg(any(feature = "hs-service", feature = "relay"))]
344            CtrlCmd::ClosePendingStream {
345                hop,
346                stream_id,
347                message,
348                done,
349            } => {
350                let ret = self.hop_mgr.close_pending(hop, stream_id, message).await;
351                let _ = done.send(ret); // don't care if the corresponding receiver goes away.
352
353                Ok(())
354            }
355            CtrlCmd::Custom(c) => self.inner.handle_cmd(c),
356        }
357    }
358
359    /// Handle a control message.
360    fn handle_msg(&mut self, msg: CtrlMsg<F::CtrlMsg>) -> StdResult<(), ReactorError> {
361        match msg {
362            CtrlMsg::Custom(c) => self.inner.handle_msg(c),
363        }
364    }
365
366    /// Note that we have received a RELAY cell.
367    ///
368    /// Updates the padding and CC state.
369    fn note_relay_cell_received(
370        &self,
371        hopnum: Option<HopNum>,
372        c_t_w: bool,
373    ) -> Result<(RelayCellFormat, bool)> {
374        let mut hops = self.hop_mgr.hops().write().expect("poisoned lock");
375        let hop = hops
376            .get_mut(hopnum)
377            .ok_or_else(|| internal!("msg from non-existent hop???"))?;
378
379        // Check whether we are allowed to receive more data for this circuit hop.
380        hop.inbound.decrement_cell_limit()?;
381
382        // Decrement the circuit sendme windows, and see if we need to
383        // send a sendme cell.
384        let send_circ_sendme = if c_t_w {
385            hop.ccontrol
386                .lock()
387                .expect("poisoned lock")
388                .note_data_received()?
389        } else {
390            false
391        };
392
393        let relay_cell_format = hop.settings.relay_crypt_protocol().relay_cell_format();
394
395        Ok((relay_cell_format, send_circ_sendme))
396    }
397
398    /// Handle a RELAY cell.
399    ///
400    // TODO(DEDUP): very similar to Client::handle_relay_cell()
401    async fn handle_relay_cell(
402        &mut self,
403        decode_res: RelayCellDecoderResult,
404        early: bool,
405        hopnum: Option<HopNum>,
406        tag: SendmeTag,
407    ) -> StdResult<(), ReactorError> {
408        // For padding purposes, if we are a relay, we set the hopnum to 0
409        // TODO(relay): is this right?
410        let hopnum_padding = hopnum.unwrap_or_else(|| HopNum::from(0));
411        if decode_res.is_padding() {
412            self.padding_ctrl.decrypted_padding(hopnum_padding)?;
413        } else {
414            self.padding_ctrl.decrypted_data(hopnum_padding);
415        }
416
417        let c_t_w = decode_res.cmds().any(sendme::cmd_counts_towards_windows);
418        let (relay_cell_format, send_circ_sendme) = self.note_relay_cell_received(hopnum, c_t_w)?;
419
420        // If we do need to send a circuit-level SENDME cell, do so.
421        if send_circ_sendme {
422            // This always sends a V1 (tagged) sendme cell, and thereby assumes
423            // that SendmeEmitMinVersion is no more than 1.  If the authorities
424            // every increase that parameter to a higher number, this will
425            // become incorrect.  (Higher numbers are not currently defined.)
426            let sendme = Sendme::from(tag);
427            let msg = AnyRelayMsgOuter::new(None, sendme.into());
428            let forward = BackwardReactorCmd::SendRelayMsg { hop: hopnum, msg };
429
430            // NOTE: sending the SENDME to the backward reactor for handling
431            // might seem counterintuitive, given that we have access to
432            // the congestion control object right here (via hop_mgr).
433            //
434            // However, the forward reactor does not have access to the
435            // outbound_chan_tx part of the inbound (towards the client) Tor channel,
436            // and so it cannot handle the SENDME on its own
437            // (because it cannot obtain the congestion signals),
438            // so the SENDME needs to be handled in the backward reactor.
439            //
440            // NOTE: this will block if the backward reactor is not ready
441            // to send any more cells.
442            self.send_reactor_cmd(forward).await?;
443        }
444
445        let (mut msgs, incomplete) = decode_res.into_parts();
446        while let Some(msg) = msgs.next() {
447            match self
448                .handle_relay_msg(early, hopnum, msg, relay_cell_format, c_t_w)
449                .await
450            {
451                Ok(()) => continue,
452                Err(e) => {
453                    for m in msgs {
454                        debug!(
455                            circ_id = %self.unique_id,
456                            "Ignoring relay msg received after triggering shutdown: {m:?}",
457                        );
458                    }
459                    if let Some(incomplete) = incomplete {
460                        debug!(
461                            circ_id = %self.unique_id,
462                            "Ignoring partial relay msg received after triggering shutdown: {:?}",
463                            incomplete,
464                        );
465                    }
466
467                    return Err(e);
468                }
469            }
470        }
471
472        Ok(())
473    }
474
475    /// Handle a single incoming RELAY message.
476    async fn handle_relay_msg(
477        &mut self,
478        early: bool,
479        hop: Option<HopNum>,
480        msg: UnparsedRelayMsg,
481        relay_cell_format: RelayCellFormat,
482        cell_counts_toward_windows: bool,
483    ) -> StdResult<(), ReactorError> {
484        // If this msg wants/refuses to have a Stream ID, does it
485        // have/not have one?
486        let streamid = msg_streamid(&msg)?;
487
488        // If this doesn't have a StreamId, it's a meta cell,
489        // not meant for a particular stream.
490        let Some(sid) = streamid else {
491            return self
492                .handle_meta_msg(early, hop, msg, relay_cell_format)
493                .await;
494        };
495
496        let msg = stream::CtrlMsg::DeliverStreamMsg {
497            sid,
498            msg,
499            cell_counts_toward_windows,
500        };
501
502        // All messages on streams are handled in the stream reactor
503        // (because that's where the stream map is)
504        //
505        // Internally, this will spawn a StreamReactor for the target hop,
506        // if not already spawned.
507        self.hop_mgr.send(hop, msg).await
508    }
509
510    /// Handle a RELAY or RELAY_EARLY message on this circuit with stream ID 0.
511    async fn handle_meta_msg(
512        &mut self,
513        early: bool,
514        hopnum: Option<HopNum>,
515        msg: UnparsedRelayMsg,
516        relay_cell_format: RelayCellFormat,
517    ) -> StdResult<(), ReactorError> {
518        match msg.cmd() {
519            RelayCmd::SENDME => {
520                let sendme = msg
521                    .decode::<Sendme>()
522                    .map_err(|e| Error::from_bytes_err(e, "sendme message"))?
523                    .into_msg();
524
525                let cmd = BackwardReactorCmd::HandleSendme {
526                    hop: hopnum,
527                    sendme,
528                };
529
530                self.send_reactor_cmd(cmd).await
531            }
532            _ => {
533                self.inner
534                    .handle_meta_msg(&self.runtime, early, hopnum, msg, relay_cell_format)
535                    .await
536            }
537        }
538    }
539
540    /// Send a command to the backward reactor.
541    ///
542    /// Blocks if the `backward_reactor_tx` channel is full, i.e. if the backward reactor
543    /// is not ready to send any more cells.
544    ///
545    /// Returns an error if the backward reactor has shut down.
546    async fn send_reactor_cmd(
547        &mut self,
548        forward: BackwardReactorCmd,
549    ) -> StdResult<(), ReactorError> {
550        self.backward_reactor_tx.send(forward).await.map_err(|_| {
551            // The other reactor has shut down
552            ReactorError::Shutdown
553        })
554    }
555}