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

            
153
    /// Helper for [`run`](Self::run).
154
    ///
155
    /// Polls the stream map for messages
156
    /// that need to be delivered to the other endpoint,
157
    /// and the `cells_rx` MPSC stream for stream messages received
158
    /// from the `ForwardReactor` that need to be delivered to the application streams.
159
78
    async fn run_once(&mut self) -> StdResult<(), ReactorError> {
160
        use postage::prelude::{Sink as _, Stream as _};
161

            
162
        // Garbage-collect all halfstreams that have expired.
163
        //
164
        // Note: this will iterate over the closed streams of this hop.
165
        // If we think this will cause perf issues, one idea would be to make
166
        // StreamMap::closed_streams into a min-heap, and add a branch to the
167
        // select_biased! below to sleep until the first expiry is due
168
        // (but my gut feeling is that iterating is cheaper)
169
52
        self.hop
170
52
            .stream_map()
171
52
            .lock()
172
52
            .expect("poisoned lock")
173
52
            .remove_expired_halfstreams(self.time_provider.now());
174

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

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

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

            
225
4
            let msg = streams.take_ready_msg(sid).expect("msg disappeared");
226

            
227
4
            Poll::Ready(StreamEvent::ReadyMsg { sid, msg })
228
38
        });
229

            
230
52
        select_biased! {
231
52
            res = self.cell_rx.next().fuse() => {
232
44
                let Some(cmd) = res else {
233
                    // The forward reactor has shut down
234
8
                    return Err(ReactorError::Shutdown);
235
                };
236

            
237
36
                self.handle_reactor_cmd(cmd).await?;
238
            }
239
52
            event = ready_streams_fut.fuse() => {
240
8
                self.handle_stream_event(event).await?;
241
            }
242
        }
243

            
244
36
        Ok(())
245
52
    }
246

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

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

            
279
        // TODO(DEDUP): this contains parts of Circuit::send_relay_cell_inner()
280
20
        if let Some(bwd_msg) = bwd_msg {
281
            // We might be out of capacity entirely; see if we are about to hit a limit.
282
            //
283
            // TODO: If we ever add a notion of _recoverable_ errors below, we'll
284
            // need a way to restore this limit, and similarly for about_to_send().
285
            self.hop.decrement_cell_limit()?;
286

            
287
            let c_t_w = sendme::cmd_counts_towards_windows(bwd_msg.cmd());
288

            
289
            // We need to apply stream-level flow control *before* encoding the message
290
            // (the BWD handles the encoding)
291
            if c_t_w {
292
                if let Some(stream_id) = bwd_msg.stream_id() {
293
                    self.hop.about_to_send(
294
                        self.unique_id,
295
                        self.circ_id,
296
                        stream_id,
297
                        bwd_msg.msg(),
298
                    )?;
299
                }
300
            }
301

            
302
            // NOTE: on the client side, we call note_data_sent()
303
            // just before writing the cell to the channel.
304
            // We can't do that here, because we're not the ones
305
            // encoding the cell, so we don't have the SENDME tag
306
            // which is needed for note_data_sent().
307
            //
308
            // Instead, we notify the CC algorithm in the BWD,
309
            // right after we've finished sending the cell.
310

            
311
            self.send_msg_to_bwd(bwd_msg).await?;
312
20
        }
313

            
314
20
        Ok(())
315
28
    }
316

            
317
    /// Handle a RELAY message that has a non-zero stream ID.
318
    ///
319
    /// A returned message is one that we need to send back to the client.
320
    //
321
    // TODO(relay): this is very similar to the client impl from
322
    // Circuit::handle_in_order_relay_msg()
323
28
    fn handle_msg(
324
28
        &mut self,
325
28
        streamid: StreamId,
326
28
        msg: UnparsedRelayMsg,
327
28
        cell_counts_toward_windows: bool,
328
28
    ) -> StdResult<Option<AnyRelayMsgOuter>, ReactorError> {
329
28
        let cmd = msg.cmd();
330
30
        let possible_proto_violation_err = move |streamid: StreamId| {
331
4
            Error::StreamProto(format!(
332
4
                "Unexpected {cmd:?} message on unknown stream {streamid}"
333
4
            ))
334
4
        };
335
28
        let now = self.time_provider.now();
336

            
337
        // Check if any of our already-open streams want this message
338
28
        let res = self.hop.handle_msg(
339
28
            possible_proto_violation_err,
340
28
            cell_counts_toward_windows,
341
28
            streamid,
342
28
            msg,
343
28
            now,
344
4
        )?;
345

            
346
        // If it was an incoming stream request, we don't need to worry about
347
        // sending an XOFF as there's no stream data within this message.
348
24
        if let Some(msg) = res {
349
            cfg_if::cfg_if! {
350
                if #[cfg(any(feature = "hs-service", feature = "relay"))] {
351
20
                    return self.handle_incoming_stream_request(streamid, msg);
352
                } else {
353
                    return Err(
354
                        Error::CircProto(format!("Cannot handle {} cells on this circuit", msg.cmd())).into(),
355
                    );
356
                }
357
            }
358
4
        }
359

            
360
        // We may want to send an XOFF if the incoming buffer is too large.
361
4
        if let Some(cell) = self.hop.maybe_send_xoff(streamid)? {
362
            let cell = AnyRelayMsgOuter::new(Some(streamid), cell.into());
363
            return Ok(Some(cell));
364
4
        }
365

            
366
4
        Ok(None)
367
28
    }
368

            
369
    /// A helper for handling incoming stream requests.
370
    ///
371
    /// Accepts the specified incoming stream request,
372
    /// by adding a new entry to our stream map.
373
    ///
374
    /// Returns the cell we need to send back to the client,
375
    /// if an error occurred and the stream cannot be opened.
376
    ///
377
    /// Returns None if everything went well
378
    /// (the CONNECTED response only comes if the external
379
    /// consumer of our [Stream](futures::Stream) of incoming Tor streams
380
    /// is able to actually establish the connection to the address
381
    /// specified in the BEGIN).
382
    ///
383
    /// Any error returned from this function will shut down the reactor.
384
    #[cfg(any(feature = "hs-service", feature = "relay"))]
385
20
    fn handle_incoming_stream_request(
386
20
        &mut self,
387
20
        sid: StreamId,
388
20
        msg: UnparsedRelayMsg,
389
20
    ) -> StdResult<Option<AnyRelayMsgOuter>, ReactorError> {
390
20
        let mut lock = self.incoming.lock().expect("poisoned lock");
391
20
        let Some(handler) = lock.as_mut() else {
392
            return Err(Error::CircProto(format!(
393
                "Cannot handle {} cells on this circuit",
394
                msg.cmd()
395
            ))
396
            .into());
397
        };
398

            
399
20
        if self.hopnum != handler.hop_num {
400
            let expected_hopnum = match handler.hop_num {
401
                Some(hopnum) => hopnum.display().to_string(),
402
                None => "client".to_string(),
403
            };
404

            
405
            let actual_hopnum = match self.hopnum {
406
                Some(hopnum) => hopnum.display().to_string(),
407
                None => "None".to_string(),
408
            };
409

            
410
            return Err(Error::CircProto(format!(
411
                "Expecting incoming streams from {}, but received {} cell from unexpected hop {}",
412
                expected_hopnum,
413
                msg.cmd(),
414
                actual_hopnum,
415
            ))
416
            .into());
417
20
        }
418

            
419
20
        let message_closes_stream = handler.cmd_checker.check_msg(&msg)? == StreamStatus::Closed;
420

            
421
16
        if message_closes_stream {
422
            self.hop
423
                .stream_map()
424
                .lock()
425
                .expect("poisoned lock")
426
                .ending_msg_received(sid)?;
427

            
428
            return Ok(None);
429
16
        }
430

            
431
16
        let req = parse_incoming_stream_req(msg)?;
432
16
        let view = CircHopSyncView::new(&self.hop);
433

            
434
16
        if let Some(reject) = Self::should_reject_incoming(handler, sid, &req, &view)? {
435
            // We can't honor this request, so we bail by sending an END.
436
            return Ok(Some(reject));
437
16
        };
438

            
439
16
        let memquota =
440
16
            StreamAccount::new(&self.memquota).map_err(|e| ReactorError::Err(e.into()))?;
441

            
442
16
        let cmd_checker = InboundDataCmdChecker::new_connected();
443
16
        let stream_components = self.hop.add_ent_with_id(
444
16
            &self.time_provider,
445
16
            sid,
446
16
            cmd_checker,
447
16
            self.inner.flowctrl_sidechannel_mitigations(),
448
16
            &memquota,
449
        )?;
450

            
451
16
        let outcome = Pin::new(&mut handler.incoming_sender).try_send(StreamReqInfo {
452
16
            req,
453
16
            stream_id: sid,
454
16
            hop: None,
455
16
            stream_components,
456
16
            memquota,
457
16
            relay_cell_format: self.hop.relay_cell_format(),
458
16
        });
459

            
460
16
        log_ratelim!("Delivering message to incoming stream handler"; outcome);
461

            
462
16
        if let Err(e) = outcome {
463
            if e.is_full() {
464
                // The IncomingStreamRequestHandler's stream is full; it isn't
465
                // handling requests fast enough. So instead, we reply with an
466
                // END cell.
467
                let end_msg = AnyRelayMsgOuter::new(
468
                    Some(sid),
469
                    End::new_with_reason(EndReason::RESOURCELIMIT).into(),
470
                );
471

            
472
                return Ok(Some(end_msg));
473
            } else if e.is_disconnected() {
474
                // The IncomingStreamRequestHandler's stream has been dropped.
475
                // In the Tor protocol as it stands, this always means that the
476
                // circuit itself is out-of-use and should be closed.
477
                //
478
                // Note that we will _not_ reach this point immediately after
479
                // the IncomingStreamRequestHandler is dropped; we won't hit it
480
                // until we next get an incoming request.  Thus, if we later
481
                // want to add early detection for a dropped
482
                // IncomingStreamRequestHandler, we need to do it elsewhere, in
483
                // a different way.
484
                debug!(
485
                    circ_uniq_id = %self.unique_id,
486
                    backward_circ_id = %self.circ_id,
487
                    "Incoming stream request receiver dropped",
488
                );
489
                // This will _cause_ the circuit to get closed.
490
                return Err(ReactorError::Err(Error::CircuitClosed));
491
            } else {
492
                // There are no errors like this with the current design of
493
                // futures::mpsc, but we shouldn't just ignore the possibility
494
                // that they'll be added later.
495
                return Err(
496
                    Error::from((into_internal!("try_send failed unexpectedly"))(e)).into(),
497
                );
498
            }
499
16
        }
500

            
501
16
        Ok(None)
502
20
    }
503

            
504
    /// Check if we should reject this incoming stream request or not.
505
    ///
506
    /// Returns a cell we need to send back to the client if we must reject the request,
507
    /// or `None` if we are allowed to accept it.
508
    ///`
509
    /// Any error returned from this function will shut down the reactor.
510
    #[cfg(any(feature = "hs-service", feature = "relay"))]
511
16
    fn should_reject_incoming<'a>(
512
16
        handler: &mut IncomingStreamRequestHandler,
513
16
        sid: StreamId,
514
16
        request: &IncomingStreamRequest,
515
16
        view: &CircHopSyncView<'a>,
516
16
    ) -> StdResult<Option<AnyRelayMsgOuter>, ReactorError> {
517
        use IncomingStreamRequestDisposition::*;
518

            
519
16
        let ctx = IncomingStreamRequestContext { request };
520

            
521
        // Run the externally provided filter to check if we should
522
        // open the stream or not.
523
16
        match handler.filter.as_mut().disposition(&ctx, view)? {
524
            Accept => {
525
                // All is well, we can accept the stream request
526
16
                Ok(None)
527
            }
528
            CloseCircuit => Err(ReactorError::Shutdown),
529
            RejectRequest(end) => {
530
                let end_msg = AnyRelayMsgOuter::new(Some(sid), end.into());
531

            
532
                Ok(Some(end_msg))
533
            }
534
        }
535
16
    }
536

            
537
    /// Handle a [`StreamEvent`].
538
12
    async fn handle_stream_event(&mut self, event: StreamEvent) -> StdResult<(), ReactorError> {
539
8
        match event {
540
4
            StreamEvent::ApplicationStreamClosed(sid) => {
541
4
                self.close_stream(
542
4
                    sid,
543
4
                    CloseStreamBehavior::default(),
544
4
                    streammap::TerminateReason::StreamTargetClosed,
545
4
                )
546
4
                .await
547
            }
548
4
            StreamEvent::ReadyMsg { sid, msg } => {
549
4
                self.send_msg_to_bwd(AnyRelayMsgOuter::new(Some(sid), msg))
550
4
                    .await
551
            }
552
        }
553
8
    }
554

            
555
    /// Close the stream that has the specified `sid`.
556
    ///
557
    /// The `behav` controls whether an `END` will be sent or not.
558
    ///
559
    /// This calls [`CircHopOutbound::close_stream`] under the hood,
560
    /// which removes the stream from the stream map,
561
    /// and returns an optional `END` cell to send back to the other party.
562
12
    async fn close_stream(
563
12
        &mut self,
564
12
        sid: StreamId,
565
12
        behav: CloseStreamBehavior,
566
12
        reason: streammap::TerminateReason,
567
18
    ) -> StdResult<(), ReactorError> {
568
12
        let timeout = self.inner.halfstream_expiry(&self.hop);
569
12
        let expire_at = self.time_provider.now() + timeout;
570
12
        let res = self.hop.close_stream(
571
12
            self.unique_id,
572
12
            self.circ_id,
573
12
            sid,
574
12
            None,
575
12
            behav,
576
12
            reason,
577
12
            expire_at,
578
        )?;
579
12
        let Some(msg) = res else {
580
            // We may not need to send anything at all...
581
            return Ok(());
582
        };
583

            
584
12
        self.send_msg_to_bwd(msg.cell).await
585
12
    }
586

            
587
    /// Wrap `msg` in [`ReadyStreamMsg`], and send it to the backward reactor.
588
24
    async fn send_msg_to_bwd(&mut self, msg: AnyRelayMsgOuter) -> StdResult<(), ReactorError> {
589
16
        let msg = ReadyStreamMsg {
590
16
            hop: self.hopnum,
591
16
            relay_cell_format: self.hop.relay_cell_format(),
592
16
            ccontrol: Arc::clone(self.hop.ccontrol()),
593
16
            msg,
594
16
        };
595

            
596
16
        self.bwd_tx
597
16
            .send(msg)
598
16
            .await
599
16
            .map_err(|_| ReactorError::Shutdown)?;
600

            
601
16
        Ok(())
602
16
    }
603
}
604

            
605
/// A Tor stream-related event.
606
enum StreamEvent {
607
    /// An application stream was closed.
608
    ///
609
    /// The corresponding entry needs to be removed from the reactor's stream map.
610
    ApplicationStreamClosed(StreamId),
611
    /// A stream has a ready message.
612
    ReadyMsg {
613
        /// The ID of the stream to close.
614
        sid: StreamId,
615
        /// The message.
616
        msg: AnyRelayMsg,
617
    },
618
}
619

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

            
645
            IncomingStreamRequest::$type(req)
646
        }};
647
    }
648

            
649
16
    let req = match msg.cmd() {
650
8
        RelayCmd::BEGIN => parse_stream_req!(msg, Begin),
651
4
        RelayCmd::BEGIN_DIR => parse_stream_req!(msg, BeginDir),
652
4
        RelayCmd::RESOLVE => parse_stream_req!(msg, Resolve),
653
        cmd => {
654
            // It's a bug if we reach this point, because CircHopOutbound::handle_msg()
655
            // should have consumed the message (by forwarding it to the appropriate stream
656
            // in its stream map)
657
            return Err(internal!("{cmd} is not an incoming stream request").into());
658
        }
659
    };
660

            
661
16
    Ok(req)
662
16
}
663

            
664
/// A stream message to be sent to the backward reactor for delivery.
665
pub(crate) struct ReadyStreamMsg {
666
    /// The hop number, or `None` if we are a relay.
667
    pub(crate) hop: Option<HopNum>,
668
    /// The message to send.
669
    pub(crate) msg: AnyRelayMsgOuter,
670
    /// The cell format used with the hop the message should be sent to.
671
    pub(crate) relay_cell_format: RelayCellFormat,
672
    /// The CC object to use.
673
    pub(crate) ccontrol: Arc<Mutex<CongestionControl>>,
674
}
675

            
676
/// A control message
677
/// that needs to be handled by [`StreamReactor`].
678
pub(crate) enum CtrlMsg {
679
    /// Stream data received from the other endpoint
680
    /// that needs to be delivered to a Tor stream
681
    DeliverStreamMsg {
682
        /// The ID of the stream this message is for.
683
        sid: StreamId,
684
        /// The message.
685
        msg: UnparsedRelayMsg,
686
        /// Whether the cell this message came from counts towards flow-control windows.
687
        cell_counts_toward_windows: bool,
688
    },
689

            
690
    /// Close the specified pending incoming stream, sending the provided END message.
691
    #[cfg(any(feature = "hs-service", feature = "relay"))]
692
    ClosePendingStream {
693
        /// The stream ID to send the END for.
694
        stream_id: StreamId,
695
        /// The END message to send, if any.
696
        behav: CloseStreamBehavior,
697
    },
698
}