1
//! The stream reactor.
2

            
3
use crate::circuit::circhop::CircHopOutbound;
4
use crate::circuit::reactor::macros::derive_deftly_template_CircuitReactor;
5
use crate::circuit::{CircHopSyncView, UniqId};
6
use crate::congestion::{CongestionControl, sendme};
7
use crate::memquota::{CircuitAccount, SpecificAccount as _, StreamAccount};
8
use crate::stream::CloseStreamBehavior;
9
use crate::stream::cmdcheck::StreamStatus;
10
use crate::stream::flow_ctrl::state::WithSidechannelMitigations;
11
use crate::streammap;
12
use crate::util::err::ReactorError;
13
use crate::{Error, HopNum};
14

            
15
#[cfg(any(feature = "hs-service", feature = "relay"))]
16
use crate::stream::incoming::{
17
    InboundDataCmdChecker, IncomingStreamRequest, IncomingStreamRequestContext,
18
    IncomingStreamRequestDisposition, IncomingStreamRequestHandler, StreamReqInfo,
19
};
20

            
21
use tor_async_utils::{SinkTrySend as _, SinkTrySendError as _};
22
use tor_cell::chancell::CircId;
23
use tor_cell::relaycell::msg::{AnyRelayMsg, Begin, BeginDir, End, EndReason, Resolve};
24
use tor_cell::relaycell::{
25
    AnyRelayMsgOuter, RelayCellFormat, RelayCmd, StreamId, UnparsedRelayMsg,
26
};
27
use tor_error::{internal, into_internal};
28
use tor_log_ratelim::log_ratelim;
29
use tor_rtcompat::{DynTimeProvider, Runtime, SleepProvider as _};
30

            
31
use derive_deftly::Deftly;
32
use futures::SinkExt;
33
use futures::channel::mpsc;
34
use futures::{FutureExt as _, StreamExt as _, future, select_biased};
35
use tracing::debug;
36

            
37
use std::pin::Pin;
38
use std::result::Result as StdResult;
39
use std::sync::{Arc, Mutex};
40
use std::task::Poll;
41
use std::time::Duration;
42

            
43
/// Trait for customizing the behavior of the stream reactor.
44
///
45
/// Used for plugging in the implementation-dependent (client vs relay)
46
/// parts of the implementation into the generic one.
47
pub(crate) trait StreamHandler: Send + Sync + 'static {
48
    /// Return the amount of time a newly closed stream
49
    /// should be kept in the stream map for.
50
    ///
51
    /// This is the amount of time we are willing to wait for
52
    /// an END ack before removing the half-stream from the map.
53
    fn halfstream_expiry(&self, hop: &CircHopOutbound) -> Duration;
54

            
55
    /// Whether sidechannel mitigations should be enabled for incoming streams.
56
    fn flowctrl_sidechannel_mitigations(&self) -> WithSidechannelMitigations;
57
}
58

            
59
/// The stream reactor for a given hop.
60
///
61
/// Drives the application streams.
62
///
63
/// This reactor accepts [`CtrlMsg`]s from the forward reactor over its [`Self::cell_rx`]
64
/// MPSC channel, and delivers them to the corresponding stream entries in the stream map.
65
///
66
/// The local streams are polled from the main loop, and any ready messages are sent
67
/// to the backward reactor over the `bwd_tx` MPSC channel for packaging and delivery.
68
///
69
/// Shuts downs down if an error occurs, or if the sending end
70
/// of the `cell_rx` MPSC channel, i.e. the forward reactor, closes.
71
#[derive(Deftly)]
72
#[derive_deftly(CircuitReactor)]
73
#[deftly(reactor_name = "stream reactor")]
74
#[deftly(run_inner_fn = "Self::run_once")]
75
#[must_use = "If you don't call run() on a reactor, the circuit won't work."]
76
pub(crate) struct StreamReactor {
77
    /// The hop this stream reactor is for.
78
    ///
79
    /// This is `None` for relays.
80
    hopnum: Option<HopNum>,
81
    /// The state of this circuit hop.
82
    hop: CircHopOutbound,
83
    /// The time provider.
84
    time_provider: DynTimeProvider,
85
    /// An identifier for logging about this reactor's circuit.
86
    unique_id: UniqId,
87
    /// The circuit identifier on the inbound Tor channel.
88
    circ_id: CircId,
89
    /// Receiver for Tor stream data that need to be delivered to a Tor stream.
90
    ///
91
    /// The sender is in the [`HopMgr`](super::hop_mgr::HopMgr) of the
92
    /// [`ForwardReactor`](super::ForwardReactor), which will forward all cells
93
    /// carrying Tor stream data to us.
94
    ///
95
    /// This serves a dual purpose:
96
    ///
97
    ///   * it enables the `ForwardReactor` to deliver Tor stream data received from the client
98
    ///   * it lets the `StreamReactor` know if the `ForwardReactor` has shut down:
99
    ///     we select! on this MPSC channel in the main loop, so if the `ForwardReactor`
100
    ///     shuts down, we will get EOS upon calling `.next()`)
101
    cell_rx: mpsc::Receiver<CtrlMsg>,
102
    /// Sender for sending Tor stream data to [`BackwardReactor`](super::BackwardReactor).
103
    bwd_tx: mpsc::Sender<ReadyStreamMsg>,
104
    /// A handler for incoming streams.
105
    ///
106
    /// Set to `None` if incoming streams are not allowed on this circuit.
107
    ///
108
    /// This handler is shared with the [`HopMgr`](super::hop_mgr::HopMgr) of this reactor,
109
    /// which can install a new handler at runtime (for example, in response to a CtrlMsg).
110
    /// The ability to update the handler after the reactor is launched is needed
111
    /// for onion services, where the incoming stream request handler only gets installed
112
    /// after the virtual hop is created.
113
    #[cfg(any(feature = "hs-service", feature = "relay"))]
114
    incoming: Arc<Mutex<Option<IncomingStreamRequestHandler>>>,
115
    /// A handler for customizing the stream reactor behavior.
116
    inner: Arc<dyn StreamHandler>,
117
    /// Memory quota account
118
    memquota: CircuitAccount,
119
}
120

            
121
impl StreamReactor {
122
    /// Create a new [`StreamReactor`].
123
    #[allow(clippy::too_many_arguments)] // TODO
124
8
    pub(crate) fn new<R: Runtime>(
125
8
        runtime: R,
126
8
        hopnum: Option<HopNum>,
127
8
        hop: CircHopOutbound,
128
8
        unique_id: UniqId,
129
8
        circ_id: CircId,
130
8
        cell_rx: mpsc::Receiver<CtrlMsg>,
131
8
        bwd_tx: mpsc::Sender<ReadyStreamMsg>,
132
8
        inner: Arc<dyn StreamHandler>,
133
8
        #[cfg(any(feature = "hs-service", feature = "relay"))] //
134
8
        incoming: Arc<Mutex<Option<IncomingStreamRequestHandler>>>,
135
8
        memquota: CircuitAccount,
136
8
    ) -> Self {
137
8
        Self {
138
8
            hopnum,
139
8
            hop,
140
8
            time_provider: DynTimeProvider::new(runtime),
141
8
            unique_id,
142
8
            circ_id,
143
8
            #[cfg(any(feature = "hs-service", feature = "relay"))]
144
8
            incoming,
145
8
            cell_rx,
146
8
            bwd_tx,
147
8
            inner,
148
8
            memquota,
149
8
        }
150
8
    }
151

            
152
    /// Helper for [`run`](Self::run).
153
    ///
154
    /// Polls the stream map for messages
155
    /// that need to be delivered to the other endpoint,
156
    /// and the `cells_rx` MPSC stream for stream messages received
157
    /// from the `ForwardReactor` that need to be delivered to the application streams.
158
26
    async fn run_once(&mut self) -> StdResult<(), ReactorError> {
159
        // Garbage-collect all halfstreams that have expired.
160
        //
161
        // Note: this will iterate over the closed streams of this hop.
162
        // If we think this will cause perf issues, one idea would be to make
163
        // StreamMap::closed_streams into a min-heap, and add a branch to the
164
        // select_biased! below to sleep until the first expiry is due
165
        // (but my gut feeling is that iterating is cheaper)
166
26
        self.hop
167
26
            .stream_map()
168
26
            .lock()
169
26
            .expect("poisoned lock")
170
26
            .remove_expired_halfstreams(self.time_provider.now());
171

            
172
26
        let streams = Arc::clone(self.hop.stream_map());
173
26
        let can_send = self
174
26
            .hop
175
26
            .ccontrol()
176
26
            .lock()
177
26
            .expect("poisoned lock")
178
26
            .can_send();
179
26
        let ready_streams_fut = future::poll_fn(move |cx| {
180
21
            if !can_send {
181
                // We can't send anything on this hop that counts towards SENDME windows.
182
                //
183
                // Note: this does not block outgoing flow-control messages:
184
                //
185
                //   * circuit SENDMEs are initiated by the forward reactor,
186
                //     by sending a BackwardReactorCmd::SendRelayMsg to BWD,
187
                //   * stream SENDMEs will be initiated by StreamTarget::send_sendme(),
188
                //     by sending a control message to the reactor
189
                //     (TODO(relay): not yet implemented)
190
                //   * XOFFs are sent in response to messages on streams
191
                //     (i.e. RELAY messages with non-zero stream IDs).
192
                //     These messages are delivered to us by the forward reactor
193
                //     inside BackwardReactorCmd::HandleMsg
194
                //   * XON will be initiated by StreamTarget::drain_rate_update(),
195
                //     by sending a control message to the reactor
196
                //     (TODO(relay): not yet implemented)\
197
                return Poll::Pending;
198
21
            }
199

            
200
21
            let mut streams = streams.lock().expect("lock poisoned");
201
21
            let Some((sid, msg)) = streams.poll_ready_streams_iter(cx).next() else {
202
                // No ready streams
203
                //
204
                // TODO(flushing): if there are no ready Tor streams, we might want to defer
205
                // flushing until stream data becomes available (or until a timeout elapses).
206
                // The deferred flushing approach should enable us to send
207
                // more than one message at a time to the channel reactor.
208
17
                return Poll::Pending;
209
            };
210

            
211
4
            if msg.is_none() {
212
                // This means the local sender has been dropped,
213
                // which presumably can only happen if an error occurs,
214
                // or if the Tor stream ends. In both cases, we're going to
215
                // want to send an END to the client to let them know,
216
                // and to remove the stream from the stream map.
217
                //
218
                // TODO(relay): the local sender part is not implemented yet
219
2
                return Poll::Ready(StreamEvent::ApplicationStreamClosed(sid));
220
2
            };
221

            
222
2
            let msg = streams.take_ready_msg(sid).expect("msg disappeared");
223

            
224
2
            Poll::Ready(StreamEvent::ReadyMsg { sid, msg })
225
21
        });
226

            
227
26
        select_biased! {
228
26
            res = self.cell_rx.next().fuse() => {
229
22
                let Some(cmd) = res else {
230
                    // The forward reactor has shut down
231
4
                    return Err(ReactorError::Shutdown);
232
                };
233

            
234
18
                self.handle_reactor_cmd(cmd).await?;
235
            }
236
26
            event = ready_streams_fut.fuse() => {
237
4
                self.handle_stream_event(event).await?;
238
            }
239
        }
240

            
241
18
        Ok(())
242
26
    }
243

            
244
    /// Handle a stream message sent to us by the forward reactor.
245
    ///
246
    /// Delivers the message to its corresponding application stream.
247
18
    async fn handle_reactor_cmd(&mut self, msg: CtrlMsg) -> StdResult<(), ReactorError> {
248
18
        match msg {
249
            CtrlMsg::DeliverStreamMsg {
250
14
                sid,
251
14
                msg,
252
14
                cell_counts_toward_windows,
253
            } => {
254
14
                self.deliver_message_to_stream(sid, msg, cell_counts_toward_windows)
255
14
                    .await
256
            }
257
            #[cfg(any(feature = "hs-service", feature = "relay"))]
258
4
            CtrlMsg::ClosePendingStream { stream_id, behav } => {
259
4
                self.close_stream(stream_id, behav, streammap::TerminateReason::ExplicitEnd)
260
4
                    .await
261
            }
262
        }
263
18
    }
264

            
265
    /// Deliver `msg` to the specified stream
266
14
    async fn deliver_message_to_stream(
267
14
        &mut self,
268
14
        sid: StreamId,
269
14
        msg: UnparsedRelayMsg,
270
14
        cell_counts_toward_windows: bool,
271
14
    ) -> StdResult<(), ReactorError> {
272
        // We need to apply stream-level flow control *before* encoding the message.
273
        // May optionally return a message that needs to be sent back to the client.
274
14
        let bwd_msg = self.handle_msg(sid, msg, cell_counts_toward_windows)?;
275

            
276
10
        if let Some(bwd_msg) = bwd_msg {
277
            self.send_msg_to_bwd(bwd_msg).await?;
278
10
        }
279

            
280
10
        Ok(())
281
14
    }
282

            
283
    /// Handle a RELAY message that has a non-zero stream ID.
284
    ///
285
    /// A returned message is one that we need to send back to the client.
286
    //
287
    // TODO(relay): this is very similar to the client impl from
288
    // Circuit::handle_in_order_relay_msg()
289
14
    fn handle_msg(
290
14
        &mut self,
291
14
        streamid: StreamId,
292
14
        msg: UnparsedRelayMsg,
293
14
        cell_counts_toward_windows: bool,
294
14
    ) -> StdResult<Option<AnyRelayMsgOuter>, ReactorError> {
295
14
        let cmd = msg.cmd();
296
14
        let possible_proto_violation_err = move |streamid: StreamId| {
297
2
            Error::StreamProto(format!(
298
2
                "Unexpected {cmd:?} message on unknown stream {streamid}"
299
2
            ))
300
2
        };
301
14
        let now = self.time_provider.now();
302

            
303
        // Check if any of our already-open streams want this message
304
14
        let res = self.hop.handle_msg(
305
14
            possible_proto_violation_err,
306
14
            cell_counts_toward_windows,
307
14
            streamid,
308
14
            msg,
309
14
            now,
310
2
        )?;
311

            
312
        // If it was an incoming stream request, we don't need to worry about
313
        // sending an XOFF as there's no stream data within this message.
314
12
        if let Some(msg) = res {
315
            cfg_if::cfg_if! {
316
                if #[cfg(any(feature = "hs-service", feature = "relay"))] {
317
10
                    return self.handle_incoming_stream_request(streamid, msg);
318
                } else {
319
                    return Err(
320
                        Error::CircProto(format!("Cannot handle {} cells on this circuit", msg.cmd())).into(),
321
                    );
322
                }
323
            }
324
2
        }
325

            
326
        // We may want to send an XOFF if the incoming buffer is too large.
327
2
        if let Some(cell) = self.hop.maybe_send_xoff(streamid)? {
328
            let cell = AnyRelayMsgOuter::new(Some(streamid), cell.into());
329
            return Ok(Some(cell));
330
2
        }
331

            
332
2
        Ok(None)
333
14
    }
334

            
335
    /// A helper for handling incoming stream requests.
336
    ///
337
    /// Accepts the specified incoming stream request,
338
    /// by adding a new entry to our stream map.
339
    ///
340
    /// Returns the cell we need to send back to the client,
341
    /// if an error occurred and the stream cannot be opened.
342
    ///
343
    /// Returns None if everything went well
344
    /// (the CONNECTED response only comes if the external
345
    /// consumer of our [Stream](futures::Stream) of incoming Tor streams
346
    /// is able to actually establish the connection to the address
347
    /// specified in the BEGIN).
348
    ///
349
    /// Any error returned from this function will shut down the reactor.
350
    #[cfg(any(feature = "hs-service", feature = "relay"))]
351
10
    fn handle_incoming_stream_request(
352
10
        &mut self,
353
10
        sid: StreamId,
354
10
        msg: UnparsedRelayMsg,
355
10
    ) -> StdResult<Option<AnyRelayMsgOuter>, ReactorError> {
356
10
        let mut lock = self.incoming.lock().expect("poisoned lock");
357
10
        let Some(handler) = lock.as_mut() else {
358
            return Err(Error::CircProto(format!(
359
                "Cannot handle {} cells on this circuit",
360
                msg.cmd()
361
            ))
362
            .into());
363
        };
364

            
365
10
        if self.hopnum != handler.hop_num {
366
            let expected_hopnum = match handler.hop_num {
367
                Some(hopnum) => hopnum.display().to_string(),
368
                None => "client".to_string(),
369
            };
370

            
371
            let actual_hopnum = match self.hopnum {
372
                Some(hopnum) => hopnum.display().to_string(),
373
                None => "None".to_string(),
374
            };
375

            
376
            return Err(Error::CircProto(format!(
377
                "Expecting incoming streams from {}, but received {} cell from unexpected hop {}",
378
                expected_hopnum,
379
                msg.cmd(),
380
                actual_hopnum,
381
            ))
382
            .into());
383
10
        }
384

            
385
10
        let message_closes_stream = handler.cmd_checker.check_msg(&msg)? == StreamStatus::Closed;
386

            
387
8
        if message_closes_stream {
388
            self.hop
389
                .stream_map()
390
                .lock()
391
                .expect("poisoned lock")
392
                .ending_msg_received(sid)?;
393

            
394
            return Ok(None);
395
8
        }
396

            
397
8
        let req = parse_incoming_stream_req(msg)?;
398
8
        let view = CircHopSyncView::new(&self.hop);
399

            
400
8
        if let Some(reject) = Self::should_reject_incoming(handler, sid, &req, &view)? {
401
            // We can't honor this request, so we bail by sending an END.
402
            return Ok(Some(reject));
403
8
        };
404

            
405
8
        let memquota =
406
8
            StreamAccount::new(&self.memquota).map_err(|e| ReactorError::Err(e.into()))?;
407

            
408
8
        let cmd_checker = InboundDataCmdChecker::new_connected();
409
8
        let stream_components = self.hop.add_ent_with_id(
410
8
            &self.time_provider,
411
8
            sid,
412
8
            cmd_checker,
413
8
            self.inner.flowctrl_sidechannel_mitigations(),
414
8
            &memquota,
415
        )?;
416

            
417
8
        let outcome = Pin::new(&mut handler.incoming_sender).try_send(StreamReqInfo {
418
8
            req,
419
8
            stream_id: sid,
420
8
            hop: None,
421
8
            stream_components,
422
8
            memquota,
423
8
            relay_cell_format: self.hop.relay_cell_format(),
424
8
        });
425

            
426
8
        log_ratelim!("Delivering message to incoming stream handler"; outcome);
427

            
428
8
        if let Err(e) = outcome {
429
            if e.is_full() {
430
                // The IncomingStreamRequestHandler's stream is full; it isn't
431
                // handling requests fast enough. So instead, we reply with an
432
                // END cell.
433
                let end_msg = AnyRelayMsgOuter::new(
434
                    Some(sid),
435
                    End::new_with_reason(EndReason::RESOURCELIMIT).into(),
436
                );
437

            
438
                return Ok(Some(end_msg));
439
            } else if e.is_disconnected() {
440
                // The IncomingStreamRequestHandler's stream has been dropped.
441
                // In the Tor protocol as it stands, this always means that the
442
                // circuit itself is out-of-use and should be closed.
443
                //
444
                // Note that we will _not_ reach this point immediately after
445
                // the IncomingStreamRequestHandler is dropped; we won't hit it
446
                // until we next get an incoming request.  Thus, if we later
447
                // want to add early detection for a dropped
448
                // IncomingStreamRequestHandler, we need to do it elsewhere, in
449
                // a different way.
450
                debug!(
451
                    circ_uniq_id = %self.unique_id,
452
                    backward_circ_id = %self.circ_id,
453
                    "Incoming stream request receiver dropped",
454
                );
455
                // This will _cause_ the circuit to get closed.
456
                return Err(ReactorError::Err(Error::CircuitClosed));
457
            } else {
458
                // There are no errors like this with the current design of
459
                // futures::mpsc, but we shouldn't just ignore the possibility
460
                // that they'll be added later.
461
                return Err(
462
                    Error::from((into_internal!("try_send failed unexpectedly"))(e)).into(),
463
                );
464
            }
465
8
        }
466

            
467
8
        Ok(None)
468
10
    }
469

            
470
    /// Check if we should reject this incoming stream request or not.
471
    ///
472
    /// Returns a cell we need to send back to the client if we must reject the request,
473
    /// or `None` if we are allowed to accept it.
474
    ///`
475
    /// Any error returned from this function will shut down the reactor.
476
    #[cfg(any(feature = "hs-service", feature = "relay"))]
477
8
    fn should_reject_incoming<'a>(
478
8
        handler: &mut IncomingStreamRequestHandler,
479
8
        sid: StreamId,
480
8
        request: &IncomingStreamRequest,
481
8
        view: &CircHopSyncView<'a>,
482
8
    ) -> StdResult<Option<AnyRelayMsgOuter>, ReactorError> {
483
        use IncomingStreamRequestDisposition::*;
484

            
485
8
        let ctx = IncomingStreamRequestContext { request };
486

            
487
        // Run the externally provided filter to check if we should
488
        // open the stream or not.
489
8
        match handler.filter.as_mut().disposition(&ctx, view)? {
490
            Accept => {
491
                // All is well, we can accept the stream request
492
8
                Ok(None)
493
            }
494
            CloseCircuit => Err(ReactorError::Shutdown),
495
            RejectRequest(end) => {
496
                let end_msg = AnyRelayMsgOuter::new(Some(sid), end.into());
497

            
498
                Ok(Some(end_msg))
499
            }
500
        }
501
8
    }
502

            
503
    /// Handle a [`StreamEvent`].
504
4
    async fn handle_stream_event(&mut self, event: StreamEvent) -> StdResult<(), ReactorError> {
505
4
        match event {
506
2
            StreamEvent::ApplicationStreamClosed(sid) => {
507
2
                self.close_stream(
508
2
                    sid,
509
2
                    CloseStreamBehavior::default(),
510
2
                    streammap::TerminateReason::StreamTargetClosed,
511
2
                )
512
2
                .await
513
            }
514
2
            StreamEvent::ReadyMsg { sid, msg } => {
515
2
                self.send_msg_to_bwd(AnyRelayMsgOuter::new(Some(sid), msg))
516
2
                    .await
517
            }
518
        }
519
4
    }
520

            
521
    /// Close the stream that has the specified `sid`.
522
    ///
523
    /// The `behav` controls whether an `END` will be sent or not.
524
    ///
525
    /// This calls [`CircHopOutbound::close_stream`] under the hood,
526
    /// which removes the stream from the stream map,
527
    /// and returns an optional `END` cell to send back to the other party.
528
6
    async fn close_stream(
529
6
        &mut self,
530
6
        sid: StreamId,
531
6
        behav: CloseStreamBehavior,
532
6
        reason: streammap::TerminateReason,
533
6
    ) -> StdResult<(), ReactorError> {
534
6
        let timeout = self.inner.halfstream_expiry(&self.hop);
535
6
        let expire_at = self.time_provider.now() + timeout;
536
6
        let res = self.hop.close_stream(
537
6
            self.unique_id,
538
6
            self.circ_id,
539
6
            sid,
540
6
            None,
541
6
            behav,
542
6
            reason,
543
6
            expire_at,
544
        )?;
545
6
        let Some(msg) = res else {
546
            // We may not need to send anything at all...
547
            return Ok(());
548
        };
549

            
550
6
        self.send_msg_to_bwd(msg.cell).await
551
6
    }
552

            
553
    /// Wrap `msg` in [`ReadyStreamMsg`], and send it to the backward reactor.
554
8
    async fn send_msg_to_bwd(&mut self, msg: AnyRelayMsgOuter) -> StdResult<(), ReactorError> {
555
        // TODO(DEDUP): this contains parts of Circuit::send_relay_cell_inner()
556

            
557
        // We might be out of capacity entirely; see if we are about to hit a limit.
558
        //
559
        // TODO: If we ever add a notion of _recoverable_ errors below, we'll
560
        // need a way to restore this limit, and similarly for about_to_send().
561
8
        self.hop.decrement_cell_limit()?;
562

            
563
        // We need to apply stream-level flow control *before* encoding the message
564
        // (the BWD handles the encoding)
565
8
        if sendme::cmd_counts_towards_windows(msg.cmd()) {
566
            if let Some(stream_id) = msg.stream_id() {
567
                self.hop
568
                    .about_to_send(self.unique_id, self.circ_id, stream_id, msg.msg())?;
569
            }
570
8
        }
571

            
572
        // NOTE: on the client side, we call note_data_sent()
573
        // just before writing the cell to the channel.
574
        // We can't do that here, because we're not the ones
575
        // encoding the cell, so we don't have the SENDME tag
576
        // which is needed for note_data_sent().
577
        //
578
        // Instead, we notify the CC algorithm in the BWD,
579
        // right after we've finished sending the cell.
580

            
581
8
        let msg = ReadyStreamMsg {
582
8
            hop: self.hopnum,
583
8
            relay_cell_format: self.hop.relay_cell_format(),
584
8
            ccontrol: Arc::clone(self.hop.ccontrol()),
585
8
            msg,
586
8
        };
587

            
588
8
        self.bwd_tx
589
8
            .send(msg)
590
8
            .await
591
8
            .map_err(|_| ReactorError::Shutdown)?;
592

            
593
8
        Ok(())
594
8
    }
595
}
596

            
597
/// A Tor stream-related event.
598
enum StreamEvent {
599
    /// An application stream was closed.
600
    ///
601
    /// The corresponding entry needs to be removed from the reactor's stream map.
602
    ApplicationStreamClosed(StreamId),
603
    /// A stream has a ready message.
604
    ReadyMsg {
605
        /// The ID of the stream to close.
606
        sid: StreamId,
607
        /// The message.
608
        msg: AnyRelayMsg,
609
    },
610
}
611

            
612
/// Convert an incoming stream request message (BEGIN, BEGIN_DIR, RESOLVE, etc.)
613
/// to an [`IncomingStreamRequest`]
614
///
615
// TODO(dedup): when we rewrite the client reactor in the multi-reactor register,
616
// we should rethink this part a bit: ideally, onion services shouldn't even
617
// try to parse BEGIN_DIR, RESOLVE.
618
//
619
// We will likely need an implementation-specific hook for this,
620
// similar to the `{Forward,Backward}Handler` implementation-specific handlers
621
// we have for the FWD and BWD reactors.
622
//
623
// See https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/4188#note_3432579
624
#[cfg(any(feature = "hs-service", feature = "relay"))]
625
8
fn parse_incoming_stream_req(msg: UnparsedRelayMsg) -> crate::Result<IncomingStreamRequest> {
626
    /// Helper for parsing an incoming stream request
627
    /// (BEGIN, BEGIN_DIR, or RESOLVE)
628
    macro_rules! parse_stream_req {
629
        ($msg:expr, $type:tt) => {{
630
            let req = $msg
631
                .decode::<$type>()
632
                .map_err(|e| {
633
                    Error::from_bytes_err(e, concat!("Invalid ", stringify!($type), " message"))
634
                })?
635
                .into_msg();
636

            
637
            IncomingStreamRequest::$type(req)
638
        }};
639
    }
640

            
641
8
    let req = match msg.cmd() {
642
4
        RelayCmd::BEGIN => parse_stream_req!(msg, Begin),
643
2
        RelayCmd::BEGIN_DIR => parse_stream_req!(msg, BeginDir),
644
2
        RelayCmd::RESOLVE => parse_stream_req!(msg, Resolve),
645
        cmd => {
646
            // It's a bug if we reach this point, because CircHopOutbound::handle_msg()
647
            // should have consumed the message (by forwarding it to the appropriate stream
648
            // in its stream map)
649
            return Err(internal!("{cmd} is not an incoming stream request").into());
650
        }
651
    };
652

            
653
8
    Ok(req)
654
8
}
655

            
656
/// A stream message to be sent to the backward reactor for delivery.
657
pub(crate) struct ReadyStreamMsg {
658
    /// The hop number, or `None` if we are a relay.
659
    pub(crate) hop: Option<HopNum>,
660
    /// The message to send.
661
    pub(crate) msg: AnyRelayMsgOuter,
662
    /// The cell format used with the hop the message should be sent to.
663
    pub(crate) relay_cell_format: RelayCellFormat,
664
    /// The CC object to use.
665
    pub(crate) ccontrol: Arc<Mutex<CongestionControl>>,
666
}
667

            
668
/// A control message
669
/// that needs to be handled by [`StreamReactor`].
670
pub(crate) enum CtrlMsg {
671
    /// Stream data received from the other endpoint
672
    /// that needs to be delivered to a Tor stream
673
    DeliverStreamMsg {
674
        /// The ID of the stream this message is for.
675
        sid: StreamId,
676
        /// The message.
677
        msg: UnparsedRelayMsg,
678
        /// Whether the cell this message came from counts towards flow-control windows.
679
        cell_counts_toward_windows: bool,
680
    },
681

            
682
    /// Close the specified pending incoming stream, sending the provided END message.
683
    #[cfg(any(feature = "hs-service", feature = "relay"))]
684
    ClosePendingStream {
685
        /// The stream ID to send the END for.
686
        stream_id: StreamId,
687
        /// The END message to send, if any.
688
        behav: CloseStreamBehavior,
689
    },
690
}