1
//! A relay's view of the forward (away from the client, towards the exit) state of a circuit.
2

            
3
mod extend_handler;
4

            
5
use extend_handler::ExtendRequestHandler;
6

            
7
use crate::channel::{Channel, ChannelSender};
8
use crate::circuit::CircuitRxReceiver;
9
use crate::circuit::UniqId;
10
use crate::circuit::celltypes::RelayMaybeEarlyChanMsg;
11
use crate::circuit::reactor::ControlHandler;
12
use crate::circuit::reactor::backward::BackwardReactorCmd;
13
use crate::circuit::reactor::forward::{ForwardCellDisposition, ForwardHandler};
14
use crate::circuit::reactor::hop_mgr::HopMgr;
15
use crate::crypto::cell::OutboundRelayLayer;
16
use crate::crypto::cell::RelayCellBody;
17
use crate::relay::RelayCircChanMsg;
18
use crate::util::err::ReactorError;
19
use crate::{Error, HopNum, Result};
20

            
21
// TODO(circpad): once padding is stabilized, the padding module will be moved out of client.
22
use crate::client::circuit::padding::QueuedCellPaddingInfo;
23

            
24
use crate::relay::channel_provider::ChannelProvider;
25
use crate::relay::reactor::CircuitAccount;
26
use tor_cell::chancell::msg::{AnyChanMsg, Destroy, PaddingNegotiate, Relay};
27
use tor_cell::chancell::{AnyChanCell, BoxedCellBody, ChanMsg, CircId};
28
use tor_cell::relaycell::msg::{Extended2, SendmeTag};
29
use tor_cell::relaycell::{RelayCellDecoderResult, RelayCellFormat, RelayCmd, UnparsedRelayMsg};
30
use tor_error::internal;
31
use tor_linkspec::OwnedChanTarget;
32
use tor_rtcompat::Runtime;
33

            
34
use futures::channel::mpsc;
35
use futures::{SinkExt as _, future};
36
use tracing::{debug, trace};
37

            
38
use std::result::Result as StdResult;
39
use std::sync::Arc;
40
use std::task::Poll;
41

            
42
/// Placeholder for our custom control message type.
43
type CtrlMsg = ();
44

            
45
/// Placeholder for our custom control command type.
46
type 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)
51
const MAX_RELAY_EARLY_CELLS_PER_CIRCUIT: usize = 8;
52

            
53
/// Relay-specific state for the forward reactor.
54
pub(crate) struct Forward {
55
    /// An identifier for logging about this reactor's circuit.
56
    unique_id: UniqId,
57
    /// The circuit identifier on the inbound Tor channel.
58
    circ_id: CircId,
59
    /// The outbound view of this circuit, if we are not the last hop.
60
    ///
61
    /// Delivers cells towards the exit.
62
    ///
63
    /// Only set for middle relays.
64
    outbound: Option<Outbound>,
65
    /// The cryptographic state for this circuit for inbound cells.
66
    crypto_out: Box<dyn OutboundRelayLayer + Send>,
67
    /// The number of RELAY_EARLY cells we have seen so far on this circuit.
68
    ///
69
    /// If we see more than [`MAX_RELAY_EARLY_CELLS_PER_CIRCUIT`] RELAY_EARLY cells, we tear down the circuit.
70
    relay_early_count: usize,
71
    /// Helper for handling circuit extension requests.
72
    ///
73
    /// Used for validating EXTEND2 cells.
74
    extend_handler: ExtendRequestHandler,
75
}
76

            
77
/// A type of event issued by the relay forward reactor.
78
pub(crate) enum CircEvent {
79
    /// The outcome of an EXTEND2 request.
80
    ExtendResult(StdResult<ExtendResult, ReactorError>),
81
}
82

            
83
/// A successful circuit extension result.
84
pub(crate) struct ExtendResult {
85
    /// The EXTENDED2 cell to send back to the client.
86
    extended2: Extended2,
87
    /// The outbound channel.
88
    outbound: Outbound,
89
    /// The reading end of the outbound Tor channel, if we are not the last hop.
90
    ///
91
    /// Yields cells moving from the exit towards the client, if we are a middle relay.
92
    outbound_chan_rx: CircuitRxReceiver,
93
}
94

            
95
/// The outbound view of a relay circuit.
96
struct Outbound {
97
    /// The circuit identifier on the outbound Tor channel.
98
    circ_id: CircId,
99
    /// The outbound Tor channel.
100
    channel: Arc<Channel>,
101
    /// The sending end of the outbound Tor channel.
102
    outbound_chan_tx: ChannelSender,
103
}
104

            
105
/// The outcome of `decode_relay_cell`.
106
enum CellDecodeResult {
107
    /// A decrypted cell.
108
    Recognized(SendmeTag, RelayCellDecoderResult),
109
    /// A cell we could not decrypt.
110
    Unrecognizd(RelayCellBody),
111
}
112

            
113
impl Forward {
114
    /// Create a new [`Forward`].
115
58
    pub(crate) fn new(
116
58
        inbound_chan: &Arc<Channel>,
117
58
        circ_id: CircId,
118
58
        unique_id: UniqId,
119
58
        crypto_out: Box<dyn OutboundRelayLayer + Send>,
120
58
        chan_provider: Arc<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
121
58
        event_tx: mpsc::Sender<CircEvent>,
122
58
        memquota: CircuitAccount,
123
58
    ) -> Self {
124
58
        let inbound_peer = Arc::clone(inbound_chan.peer_info());
125
58
        let extend_handler = ExtendRequestHandler::new(
126
58
            unique_id,
127
58
            circ_id,
128
58
            chan_provider,
129
58
            inbound_peer,
130
58
            event_tx,
131
58
            memquota,
132
        );
133

            
134
58
        Self {
135
58
            unique_id,
136
58
            circ_id,
137
58
            // Initially, we are the last hop in the circuit.
138
58
            outbound: None,
139
58
            crypto_out,
140
58
            relay_early_count: 0,
141
58
            extend_handler,
142
58
        }
143
58
    }
144

            
145
    /// Decode `cell`, returning its corresponding hop number, tag and decoded body.
146
64
    fn decode_relay_cell<R: Runtime>(
147
64
        &mut self,
148
64
        hop_mgr: &mut HopMgr<R>,
149
64
        cell: RelayMaybeEarlyChanMsg,
150
64
    ) -> Result<(Option<HopNum>, CellDecodeResult)> {
151
        // Note: the client reactor will return the actual source hopnum
152
64
        let hopnum = None;
153
64
        let cmd = cell.cmd();
154
64
        let mut body = cell.into_relay_body().into();
155
64
        let Some(tag) = self.crypto_out.decrypt_outbound(cmd, &mut body) else {
156
12
            return Ok((hopnum, CellDecodeResult::Unrecognizd(body)));
157
        };
158

            
159
        // The message is addressed to us! Now it's time to handle it...
160
52
        let mut hops = hop_mgr.hops().write().expect("poisoned lock");
161
52
        let decode_res = hops
162
52
            .get_mut(hopnum)
163
52
            .ok_or_else(|| internal!("msg from non-existent hop???"))?
164
            .inbound
165
52
            .decode(body.into())?;
166

            
167
52
        Ok((hopnum, CellDecodeResult::Recognized(tag, decode_res)))
168
64
    }
169

            
170
    /// Handle a DROP message.
171
    #[allow(clippy::unnecessary_wraps)] // Returns Err if circ-padding is enabled
172
    fn handle_drop(&mut self) -> StdResult<(), ReactorError> {
173
        cfg_if::cfg_if! {
174
            if #[cfg(feature = "circ-padding")] {
175
                Err(internal!("relay circuit padding not yet supported").into())
176
            } else {
177
                Ok(())
178
            }
179
        }
180
    }
181

            
182
    /// Handle the outcome of handling an EXTEND2.
183
12
    fn handle_extend_result(
184
12
        &mut self,
185
12
        res: StdResult<ExtendResult, ReactorError>,
186
12
    ) -> StdResult<Option<BackwardReactorCmd>, ReactorError> {
187
        let ExtendResult {
188
12
            extended2,
189
12
            outbound,
190
12
            outbound_chan_rx,
191
12
        } = res?;
192

            
193
12
        self.outbound = Some(outbound);
194

            
195
12
        Ok(Some(BackwardReactorCmd::HandleCircuitExtended {
196
12
            hop: None,
197
12
            extended2,
198
12
            outbound_chan_rx,
199
12
        }))
200
12
    }
201

            
202
    /// Handle a RELAY or RELAY_EARLY cell.
203
64
    fn handle_relay_cell<R: Runtime>(
204
64
        &mut self,
205
64
        hop_mgr: &mut HopMgr<R>,
206
64
        cell: RelayMaybeEarlyChanMsg,
207
64
    ) -> StdResult<Option<ForwardCellDisposition>, ReactorError> {
208
64
        let early = matches!(cell, RelayMaybeEarlyChanMsg::RelayEarly(_));
209

            
210
64
        if early {
211
24
            self.relay_early_count += 1;
212

            
213
24
            if self.relay_early_count > MAX_RELAY_EARLY_CELLS_PER_CIRCUIT {
214
                return Err(
215
                    Error::CircProto("Circuit received too many RELAY_EARLY cells".into()).into(),
216
                );
217
24
            }
218
40
        }
219

            
220
64
        let (hopnum, res) = self.decode_relay_cell(hop_mgr, cell)?;
221
64
        let (tag, decode_res) = match res {
222
12
            CellDecodeResult::Unrecognizd(body) => {
223
12
                self.handle_unrecognized_cell(body, None, early)?;
224
8
                return Ok(None);
225
            }
226
52
            CellDecodeResult::Recognized(tag, res) => (tag, res),
227
        };
228

            
229
52
        Ok(Some(ForwardCellDisposition::HandleRecognizedRelay {
230
52
            cell: decode_res,
231
52
            early,
232
52
            hopnum,
233
52
            tag,
234
52
        }))
235
64
    }
236

            
237
    /// Handle a forward cell that we could not decrypt.
238
12
    fn handle_unrecognized_cell(
239
12
        &mut self,
240
12
        body: RelayCellBody,
241
12
        info: Option<QueuedCellPaddingInfo>,
242
12
        early: bool,
243
12
    ) -> StdResult<(), ReactorError> {
244
12
        let Some(chan) = self.outbound.as_mut() else {
245
            // The client shouldn't try to send us any cells before it gets
246
            // an EXTENDED2 cell from us
247
4
            return Err(Error::CircProto(
248
4
                "Asked to forward cell before the circuit was extended?!".into(),
249
4
            )
250
4
            .into());
251
        };
252

            
253
        // TODO(relay): remove this log once we add some tests
254
        // and confirm relaying cells works as expected
255
        // (in practice it will be too noisy to be useful, even at trace level).
256
8
        trace!(
257
            circ_uniq_id = %self.unique_id,
258
            forward_circ_id = %chan.circ_id,
259
            "Forwarding unrecognized cell"
260
        );
261

            
262
8
        let msg = Relay::from(BoxedCellBody::from(body));
263
8
        let relay = if early {
264
4
            AnyChanMsg::RelayEarly(msg.into())
265
        } else {
266
4
            AnyChanMsg::Relay(msg)
267
        };
268
8
        let cell = AnyChanCell::new(Some(chan.circ_id), relay);
269

            
270
        // Note: this future is always `Ready`, because we checked the sink for readiness
271
        // before polling the input channel, so await won't block.
272
8
        chan.outbound_chan_tx.start_send_unpin((cell, info))?;
273

            
274
8
        Ok(())
275
12
    }
276

            
277
    /// Handle a TRUNCATE cell.
278
4
    fn handle_truncate(&mut self) -> StdResult<(), ReactorError> {
279
        // This is not strictly spec compliant,
280
        // but since none of our implementations use TRUNCATE,
281
        // we deem it a proto violation and shut down the circuit.
282
        //
283
        // TODO(spec): codify this in the spec
284
4
        Err(Error::CircProto("TRUNCATE not allowed".into()).into())
285
4
    }
286

            
287
    /// Handle a DESTROY cell originating from the client.
288
10
    fn handle_destroy_cell(&mut self, cell: &Destroy) -> StdResult<(), ReactorError> {
289
10
        debug!(
290
            circ_uniq_id = %self.unique_id,
291
            backward_circ_id = %self.circ_id,
292
            reason = %cell.reason(),
293
            "Received outbound DESTROY, circuit shutting down",
294
        );
295

            
296
        // We don't need to send a DESTROY cell down the channel,
297
        // because that's handled implicitly by our Drop implementation
298
10
        Err(ReactorError::Shutdown)
299
10
    }
300

            
301
    /// Handle a PADDING_NEGOTIATE cell originating from the client.
302
    #[allow(clippy::needless_pass_by_value)] // TODO(relay)
303
    fn handle_padding_negotiate(&mut self, _cell: PaddingNegotiate) -> StdResult<(), ReactorError> {
304
        Err(internal!("PADDING_NEGOTIATE is not implemented").into())
305
    }
306
}
307

            
308
impl ForwardHandler for Forward {
309
    type BuildSpec = OwnedChanTarget;
310
    type CircChanMsg = RelayCircChanMsg;
311
    type CircEvent = CircEvent;
312

            
313
24
    async fn handle_meta_msg<R: Runtime>(
314
24
        &mut self,
315
24
        runtime: &R,
316
24
        early: bool,
317
24
        _hopnum: Option<HopNum>,
318
24
        msg: UnparsedRelayMsg,
319
24
        _relay_cell_format: RelayCellFormat,
320
24
    ) -> StdResult<(), ReactorError> {
321
24
        match msg.cmd() {
322
            RelayCmd::DROP => self.handle_drop(),
323
20
            RelayCmd::EXTEND2 => self.extend_handler.handle_extend2(runtime, early, msg),
324
4
            RelayCmd::TRUNCATE => self.handle_truncate(),
325
            cmd => Err(internal!("relay cmd {cmd} not supported").into()),
326
        }
327
24
    }
328

            
329
74
    async fn handle_forward_cell<R: Runtime>(
330
74
        &mut self,
331
74
        hop_mgr: &mut HopMgr<R>,
332
74
        cell: RelayCircChanMsg,
333
74
    ) -> StdResult<Option<ForwardCellDisposition>, ReactorError> {
334
        use RelayCircChanMsg::*;
335

            
336
74
        match cell {
337
40
            Relay(r) => self.handle_relay_cell(hop_mgr, r.into()),
338
24
            RelayEarly(r) => self.handle_relay_cell(hop_mgr, r.into()),
339
10
            Destroy(d) => {
340
10
                self.handle_destroy_cell(&d)?;
341
                Ok(None)
342
            }
343
            PaddingNegotiate(p) => {
344
                self.handle_padding_negotiate(p)?;
345
                Ok(None)
346
            }
347
        }
348
74
    }
349

            
350
12
    fn handle_event(
351
12
        &mut self,
352
12
        event: Self::CircEvent,
353
12
    ) -> StdResult<Option<BackwardReactorCmd>, ReactorError> {
354
12
        match event {
355
12
            CircEvent::ExtendResult(res) => self.handle_extend_result(res),
356
        }
357
12
    }
358

            
359
174
    async fn outbound_chan_ready(&mut self) -> Result<()> {
360
116
        future::poll_fn(|cx| match &mut self.outbound {
361
20
            Some(chan) => {
362
20
                let _ = chan.outbound_chan_tx.poll_flush_unpin(cx);
363

            
364
20
                chan.outbound_chan_tx.poll_ready_unpin(cx)
365
            }
366
            None => {
367
                // Pedantically, if the channel doesn't exist, it can't be ready,
368
                // but we have no choice here than to return Ready
369
                // (returning Pending would cause the reactor to lock up).
370
                //
371
                // Returning ready here means the base reactor is allowed to read
372
                // from its inbound channel. This is OK, because if we *do*
373
                // read a cell from that channel and find ourselves needing to
374
                // forward it to the next hop, we simply return a proto violation error,
375
                // shutting down the reactor.
376
96
                Poll::Ready(Ok(()))
377
            }
378
116
        })
379
116
        .await
380
116
    }
381
}
382

            
383
impl ControlHandler for Forward {
384
    type CtrlMsg = CtrlMsg;
385
    type CtrlCmd = CtrlCmd;
386

            
387
    fn handle_cmd(&mut self, cmd: Self::CtrlCmd) -> StdResult<(), ReactorError> {
388
        let () = cmd;
389
        Ok(())
390
    }
391

            
392
    fn handle_msg(&mut self, msg: Self::CtrlMsg) -> StdResult<(), ReactorError> {
393
        let () = msg;
394
        Ok(())
395
    }
396
}
397

            
398
impl Drop for Forward {
399
58
    fn drop(&mut self) {
400
58
        if let Some(outbound) = self.outbound.as_mut() {
401
12
            // This will send a DESTROY down the outbound channel
402
12
            let _ = outbound.channel.close_circuit(outbound.circ_id);
403
46
        }
404
58
    }
405
}