Skip to main content

tor_proto/relay/reactor/
forward.rs

1//! A relay's view of the forward (away from the client, towards the exit) state of a circuit.
2
3mod 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
21// TODO(circpad): once padding is stabilized, the padding module will be moved out of client.
22use 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
42/// Placeholder for our custom control message type.
43type CtrlMsg = ();
44
45/// Placeholder for our custom control command type.
46type CtrlCmd = ();
47
48/// The maximum number of RELAY_EARLY cells allowed on a circuit.
49///
50// TODO(relay): should we come up with a consensus parameter for this? (arti#2349)
51const MAX_RELAY_EARLY_CELLS_PER_CIRCUIT: usize = 8;
52
53/// Relay-specific state for the forward reactor.
54pub(crate) struct Forward {
55    /// An identifier for logging about this reactor's circuit.
56    unique_id: UniqId,
57    /// The outbound view of this circuit, if we are not the last hop.
58    ///
59    /// Delivers cells towards the exit.
60    ///
61    /// Only set for middle relays.
62    outbound: Option<Outbound>,
63    /// The cryptographic state for this circuit for inbound cells.
64    crypto_out: Box<dyn OutboundRelayLayer + Send>,
65    /// The number of RELAY_EARLY cells we have seen so far on this circuit.
66    ///
67    /// If we see more than [`MAX_RELAY_EARLY_CELLS_PER_CIRCUIT`] RELAY_EARLY cells, we tear down the circuit.
68    relay_early_count: usize,
69    /// Helper for handling circuit extension requests.
70    ///
71    /// Used for validating EXTEND2 cells.
72    extend_handler: ExtendRequestHandler,
73}
74
75/// A type of event issued by the relay forward reactor.
76pub(crate) enum CircEvent {
77    /// The outcome of an EXTEND2 request.
78    ExtendResult(StdResult<ExtendResult, ReactorError>),
79}
80
81/// A successful circuit extension result.
82pub(crate) struct ExtendResult {
83    /// The EXTENDED2 cell to send back to the client.
84    extended2: Extended2,
85    /// The outbound channel.
86    outbound: Outbound,
87    /// The reading end of the outbound Tor channel, if we are not the last hop.
88    ///
89    /// Yields cells moving from the exit towards the client, if we are a middle relay.
90    outbound_chan_rx: CircuitRxReceiver,
91}
92
93/// The outbound view of a relay circuit.
94struct Outbound {
95    /// The circuit identifier on the outbound Tor channel.
96    circ_id: CircId,
97    /// The outbound Tor channel.
98    channel: Arc<Channel>,
99    /// The sending end of the outbound Tor channel.
100    outbound_chan_tx: ChannelSender,
101}
102
103/// The outcome of `decode_relay_cell`.
104enum CellDecodeResult {
105    /// A decrypted cell.
106    Recognized(SendmeTag, RelayCellDecoderResult),
107    /// A cell we could not decrypt.
108    Unrecognizd(RelayCellBody),
109}
110
111impl Forward {
112    /// Create a new [`Forward`].
113    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            // Initially, we are the last hop in the circuit.
128            outbound: None,
129            crypto_out,
130            relay_early_count: 0,
131            extend_handler,
132        }
133    }
134
135    /// Decode `cell`, returning its corresponding hop number, tag and decoded body.
136    fn decode_relay_cell<R: Runtime>(
137        &mut self,
138        hop_mgr: &mut HopMgr<R>,
139        cell: RelayMaybeEarlyChanMsg,
140    ) -> Result<(Option<HopNum>, CellDecodeResult)> {
141        // Note: the client reactor will return the actual source hopnum
142        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        // The message is addressed to us! Now it's time to handle it...
150        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    /// Handle a DROP message.
161    #[allow(clippy::unnecessary_wraps)] // Returns Err if circ-padding is enabled
162    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    /// Handle the outcome of handling an EXTEND2.
173    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    /// Handle a RELAY or RELAY_EARLY cell.
193    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    /// Handle a forward cell that we could not decrypt.
228    fn handle_unrecognized_cell(
229        &mut self,
230        body: RelayCellBody,
231        info: Option<QueuedCellPaddingInfo>,
232        early: bool,
233    ) -> StdResult<(), ReactorError> {
234        // TODO(relay): remove this log once we add some tests
235        // and confirm relaying cells works as expected
236        // (in practice it will be too noisy to be useful, even at trace level).
237        trace!(
238            circ_id = %self.unique_id,
239            "Forwarding unrecognized cell"
240        );
241
242        let Some(chan) = self.outbound.as_mut() else {
243            // The client shouldn't try to send us any cells before it gets
244            // an EXTENDED2 cell from us
245            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        // Note: this future is always `Ready`, because we checked the sink for readiness
260        // before polling the input channel, so await won't block.
261        chan.outbound_chan_tx.start_send_unpin((cell, info))?;
262
263        Ok(())
264    }
265
266    /// Handle a TRUNCATE cell.
267    fn handle_truncate(&mut self) -> StdResult<(), ReactorError> {
268        // This is not strictly spec compliant,
269        // but since none of our implementations use TRUNCATE,
270        // we deem it a proto violation and shut down the circuit.
271        //
272        // TODO(spec): codify this in the spec
273        Err(Error::CircProto("TRUNCATE not allowed".into()).into())
274    }
275
276    /// Handle a DESTROY cell originating from the client.
277    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        // We don't need to send a DESTROY cell down the channel,
285        // because that's handled implicitly by our Drop implementation
286        Err(ReactorError::Shutdown)
287    }
288
289    /// Handle a PADDING_NEGOTIATE cell originating from the client.
290    #[allow(clippy::needless_pass_by_value)] // TODO(relay)
291    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                // Pedantically, if the channel doesn't exist, it can't be ready,
356                // but we have no choice here than to return Ready
357                // (returning Pending would cause the reactor to lock up).
358                //
359                // Returning ready here means the base reactor is allowed to read
360                // from its inbound channel. This is OK, because if we *do*
361                // read a cell from that channel and find ourselves needing to
362                // forward it to the next hop, we simply return a proto violation error,
363                // shutting down the reactor.
364                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            // This will send a DESTROY down the outbound channel
390            let _ = outbound.channel.close_circuit(outbound.circ_id);
391        }
392    }
393}