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::streammap;
11
use crate::util::err::ReactorError;
12
use crate::{Error, HopNum};
13

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

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

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

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

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

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

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

            
144
    /// Helper for [`run`](Self::run).
145
    ///
146
    /// Polls the stream map for messages
147
    /// that need to be delivered to the other endpoint,
148
    /// and the `cells_rx` MPSC stream for stream messages received
149
    /// from the `ForwardReactor` that need to be delivered to the application streams.
150
60
    async fn run_once(&mut self) -> StdResult<(), ReactorError> {
151
        use postage::prelude::{Sink as _, Stream as _};
152

            
153
        // Garbage-collect all halfstreams that have expired.
154
        //
155
        // Note: this will iterate over the closed streams of this hop.
156
        // If we think this will cause perf issues, one idea would be to make
157
        // StreamMap::closed_streams into a min-heap, and add a branch to the
158
        // select_biased! below to sleep until the first expiry is due
159
        // (but my gut feeling is that iterating is cheaper)
160
40
        self.hop
161
40
            .stream_map()
162
40
            .lock()
163
40
            .expect("poisoned lock")
164
40
            .remove_expired_halfstreams(self.time_provider.now());
165

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

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

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

            
216
4
            let msg = streams.take_ready_msg(sid).expect("msg disappeared");
217

            
218
4
            Poll::Ready(StreamEvent::ReadyMsg { sid, msg })
219
28
        });
220

            
221
40
        select_biased! {
222
40
            res = self.cell_rx.next().fuse() => {
223
32
                let Some(cmd) = res else {
224
                    // The forward reactor has shut down
225
4
                    return Err(ReactorError::Shutdown);
226
                };
227

            
228
28
                self.handle_reactor_cmd(cmd).await?;
229
            }
230
40
            event = ready_streams_fut.fuse() => {
231
8
                self.handle_stream_event(event).await?;
232
            }
233
        }
234

            
235
28
        Ok(())
236
40
    }
237

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

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

            
270
        // TODO(DEDUP): this contains parts of Circuit::send_relay_cell_inner()
271
16
        if let Some(bwd_msg) = bwd_msg {
272
            // We might be out of capacity entirely; see if we are about to hit a limit.
273
            //
274
            // TODO: If we ever add a notion of _recoverable_ errors below, we'll
275
            // need a way to restore this limit, and similarly for about_to_send().
276
            self.hop.decrement_cell_limit()?;
277

            
278
            let c_t_w = sendme::cmd_counts_towards_windows(bwd_msg.cmd());
279

            
280
            // We need to apply stream-level flow control *before* encoding the message
281
            // (the BWD handles the encoding)
282
            if c_t_w {
283
                if let Some(stream_id) = bwd_msg.stream_id() {
284
                    self.hop
285
                        .about_to_send(self.unique_id, stream_id, bwd_msg.msg())?;
286
                }
287
            }
288

            
289
            // NOTE: on the client side, we call note_data_sent()
290
            // just before writing the cell to the channel.
291
            // We can't do that here, because we're not the ones
292
            // encoding the cell, so we don't have the SENDME tag
293
            // which is needed for note_data_sent().
294
            //
295
            // Instead, we notify the CC algorithm in the BWD,
296
            // right after we've finished sending the cell.
297

            
298
            self.send_msg_to_bwd(bwd_msg).await?;
299
16
        }
300

            
301
16
        Ok(())
302
24
    }
303

            
304
    /// Handle a RELAY message that has a non-zero stream ID.
305
    ///
306
    /// A returned message is one that we need to send back to the client.
307
    //
308
    // TODO(relay): this is very similar to the client impl from
309
    // Circuit::handle_in_order_relay_msg()
310
24
    fn handle_msg(
311
24
        &mut self,
312
24
        streamid: StreamId,
313
24
        msg: UnparsedRelayMsg,
314
24
        cell_counts_toward_windows: bool,
315
24
    ) -> StdResult<Option<AnyRelayMsgOuter>, ReactorError> {
316
24
        let cmd = msg.cmd();
317
26
        let possible_proto_violation_err = move |streamid: StreamId| {
318
4
            Error::StreamProto(format!(
319
4
                "Unexpected {cmd:?} message on unknown stream {streamid}"
320
4
            ))
321
4
        };
322
24
        let now = self.time_provider.now();
323

            
324
        // Check if any of our already-open streams want this message
325
24
        let res = self.hop.handle_msg(
326
24
            possible_proto_violation_err,
327
24
            cell_counts_toward_windows,
328
24
            streamid,
329
24
            msg,
330
24
            now,
331
4
        )?;
332

            
333
        // If it was an incoming stream request, we don't need to worry about
334
        // sending an XOFF as there's no stream data within this message.
335
20
        if let Some(msg) = res {
336
            cfg_if::cfg_if! {
337
                if #[cfg(any(feature = "hs-service", feature = "relay"))] {
338
16
                    return self.handle_incoming_stream_request(streamid, msg);
339
                } else {
340
                    return Err(
341
                        tor_error::internal!(
342
                            "incoming stream not rejected, but relay and hs-service features are disabled?!"
343
                            ).into()
344
                    );
345
                }
346
            }
347
4
        }
348

            
349
        // We may want to send an XOFF if the incoming buffer is too large.
350
4
        if let Some(cell) = self.hop.maybe_send_xoff(streamid)? {
351
            let cell = AnyRelayMsgOuter::new(Some(streamid), cell.into());
352
            return Ok(Some(cell));
353
4
        }
354

            
355
4
        Ok(None)
356
24
    }
357

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

            
386
16
        if self.hopnum != handler.hop_num {
387
            let expected_hopnum = match handler.hop_num {
388
                Some(hopnum) => hopnum.display().to_string(),
389
                None => "client".to_string(),
390
            };
391

            
392
            let actual_hopnum = match self.hopnum {
393
                Some(hopnum) => hopnum.display().to_string(),
394
                None => "None".to_string(),
395
            };
396

            
397
            return Err(Error::CircProto(format!(
398
                "Expecting incoming streams from {}, but received {} cell from unexpected hop {}",
399
                expected_hopnum,
400
                msg.cmd(),
401
                actual_hopnum,
402
            ))
403
            .into());
404
16
        }
405

            
406
16
        let message_closes_stream = handler.cmd_checker.check_msg(&msg)? == StreamStatus::Closed;
407

            
408
12
        if message_closes_stream {
409
            self.hop
410
                .stream_map()
411
                .lock()
412
                .expect("poisoned lock")
413
                .ending_msg_received(sid)?;
414

            
415
            return Ok(None);
416
12
        }
417

            
418
12
        let req = parse_incoming_stream_req(msg)?;
419
12
        let view = CircHopSyncView::new(&self.hop);
420

            
421
12
        if let Some(reject) = Self::should_reject_incoming(handler, sid, &req, &view)? {
422
            // We can't honor this request, so we bail by sending an END.
423
            return Ok(Some(reject));
424
12
        };
425

            
426
12
        let memquota =
427
12
            StreamAccount::new(&self.memquota).map_err(|e| ReactorError::Err(e.into()))?;
428

            
429
12
        let cmd_checker = InboundDataCmdChecker::new_connected();
430
12
        let stream_components =
431
12
            self.hop
432
12
                .add_ent_with_id(&self.time_provider, sid, cmd_checker, &memquota)?;
433

            
434
12
        let outcome = Pin::new(&mut handler.incoming_sender).try_send(StreamReqInfo {
435
12
            req,
436
12
            stream_id: sid,
437
12
            hop: None,
438
12
            stream_components,
439
12
            memquota,
440
12
            relay_cell_format: self.hop.relay_cell_format(),
441
12
        });
442

            
443
12
        log_ratelim!("Delivering message to incoming stream handler"; outcome);
444

            
445
12
        if let Err(e) = outcome {
446
            if e.is_full() {
447
                // The IncomingStreamRequestHandler's stream is full; it isn't
448
                // handling requests fast enough. So instead, we reply with an
449
                // END cell.
450
                let end_msg = AnyRelayMsgOuter::new(
451
                    Some(sid),
452
                    End::new_with_reason(EndReason::RESOURCELIMIT).into(),
453
                );
454

            
455
                return Ok(Some(end_msg));
456
            } else if e.is_disconnected() {
457
                // The IncomingStreamRequestHandler's stream has been dropped.
458
                // In the Tor protocol as it stands, this always means that the
459
                // circuit itself is out-of-use and should be closed.
460
                //
461
                // Note that we will _not_ reach this point immediately after
462
                // the IncomingStreamRequestHandler is dropped; we won't hit it
463
                // until we next get an incoming request.  Thus, if we later
464
                // want to add early detection for a dropped
465
                // IncomingStreamRequestHandler, we need to do it elsewhere, in
466
                // a different way.
467
                debug!(
468
                    circ_id = %self.unique_id,
469
                    "Incoming stream request receiver dropped",
470
                );
471
                // This will _cause_ the circuit to get closed.
472
                return Err(ReactorError::Err(Error::CircuitClosed));
473
            } else {
474
                // There are no errors like this with the current design of
475
                // futures::mpsc, but we shouldn't just ignore the possibility
476
                // that they'll be added later.
477
                return Err(
478
                    Error::from((into_internal!("try_send failed unexpectedly"))(e)).into(),
479
                );
480
            }
481
12
        }
482

            
483
12
        Ok(None)
484
16
    }
485

            
486
    /// Check if we should reject this incoming stream request or not.
487
    ///
488
    /// Returns a cell we need to send back to the client if we must reject the request,
489
    /// or `None` if we are allowed to accept it.
490
    ///`
491
    /// Any error returned from this function will shut down the reactor.
492
    #[cfg(any(feature = "hs-service", feature = "relay"))]
493
12
    fn should_reject_incoming<'a>(
494
12
        handler: &mut IncomingStreamRequestHandler,
495
12
        sid: StreamId,
496
12
        request: &IncomingStreamRequest,
497
12
        view: &CircHopSyncView<'a>,
498
12
    ) -> StdResult<Option<AnyRelayMsgOuter>, ReactorError> {
499
        use IncomingStreamRequestDisposition::*;
500

            
501
12
        let ctx = IncomingStreamRequestContext { request };
502

            
503
        // Run the externally provided filter to check if we should
504
        // open the stream or not.
505
12
        match handler.filter.as_mut().disposition(&ctx, view)? {
506
            Accept => {
507
                // All is well, we can accept the stream request
508
12
                Ok(None)
509
            }
510
            CloseCircuit => Err(ReactorError::Shutdown),
511
            RejectRequest(end) => {
512
                let end_msg = AnyRelayMsgOuter::new(Some(sid), end.into());
513

            
514
                Ok(Some(end_msg))
515
            }
516
        }
517
12
    }
518

            
519
    /// Handle a [`StreamEvent`].
520
12
    async fn handle_stream_event(&mut self, event: StreamEvent) -> StdResult<(), ReactorError> {
521
8
        match event {
522
4
            StreamEvent::ApplicationStreamClosed(sid) => {
523
4
                self.close_stream(
524
4
                    sid,
525
4
                    CloseStreamBehavior::default(),
526
4
                    streammap::TerminateReason::StreamTargetClosed,
527
4
                )
528
4
                .await
529
            }
530
4
            StreamEvent::ReadyMsg { sid, msg } => {
531
4
                self.send_msg_to_bwd(AnyRelayMsgOuter::new(Some(sid), msg))
532
4
                    .await
533
            }
534
        }
535
8
    }
536

            
537
    /// Close the stream that has the specified `sid`.
538
    ///
539
    /// The `behav` controls whether an `END` will be sent or not.
540
    ///
541
    /// This calls [`CircHopOutbound::close_stream`] under the hood,
542
    /// which removes the stream from the stream map,
543
    /// and returns an optional `END` cell to send back to the other party.
544
8
    async fn close_stream(
545
8
        &mut self,
546
8
        sid: StreamId,
547
8
        behav: CloseStreamBehavior,
548
8
        reason: streammap::TerminateReason,
549
12
    ) -> StdResult<(), ReactorError> {
550
8
        let timeout = self.inner.halfstream_expiry(&self.hop);
551
8
        let expire_at = self.time_provider.now() + timeout;
552
8
        let res = self
553
8
            .hop
554
8
            .close_stream(self.unique_id, sid, None, behav, reason, expire_at)?;
555
8
        let Some(msg) = res else {
556
            // We may not need to send anything at all...
557
            return Ok(());
558
        };
559

            
560
8
        self.send_msg_to_bwd(msg.cell).await
561
8
    }
562

            
563
    /// Wrap `msg` in [`ReadyStreamMsg`], and send it to the backward reactor.
564
18
    async fn send_msg_to_bwd(&mut self, msg: AnyRelayMsgOuter) -> StdResult<(), ReactorError> {
565
12
        let msg = ReadyStreamMsg {
566
12
            hop: self.hopnum,
567
12
            relay_cell_format: self.hop.relay_cell_format(),
568
12
            ccontrol: Arc::clone(self.hop.ccontrol()),
569
12
            msg,
570
12
        };
571

            
572
12
        self.bwd_tx
573
12
            .send(msg)
574
12
            .await
575
12
            .map_err(|_| ReactorError::Shutdown)?;
576

            
577
12
        Ok(())
578
12
    }
579
}
580

            
581
/// A Tor stream-related event.
582
enum StreamEvent {
583
    /// An application stream was closed.
584
    ///
585
    /// The corresponding entry needs to be removed from the reactor's stream map.
586
    ApplicationStreamClosed(StreamId),
587
    /// A stream has a ready message.
588
    ReadyMsg {
589
        /// The ID of the stream to close.
590
        sid: StreamId,
591
        /// The message.
592
        msg: AnyRelayMsg,
593
    },
594
}
595

            
596
/// Convert an incoming stream request message (BEGIN, BEGIN_DIR, RESOLVE, etc.)
597
/// to an [`IncomingStreamRequest`]
598
///
599
// TODO(dedup): when we rewrite the client reactor in the multi-reactor register,
600
// we should rethink this part a bit: ideally, onion services shouldn't even
601
// try to parse BEGIN_DIR, RESOLVE.
602
//
603
// We will likely need an implementation-specific hook for this,
604
// similar to the `{Forward,Backward}Handler` implementation-specific handlers
605
// we have for the FWD and BWD reactors.
606
//
607
// See https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/4188#note_3432579
608
#[cfg(any(feature = "hs-service", feature = "relay"))]
609
12
fn parse_incoming_stream_req(msg: UnparsedRelayMsg) -> crate::Result<IncomingStreamRequest> {
610
    /// Helper for parsing an incoming stream request
611
    /// (BEGIN, BEGIN_DIR, or RESOLVE)
612
    macro_rules! parse_stream_req {
613
        ($msg:expr, $type:tt) => {{
614
            let req = $msg
615
                .decode::<$type>()
616
                .map_err(|e| {
617
                    Error::from_bytes_err(e, concat!("Invalid ", stringify!($type), " message"))
618
                })?
619
                .into_msg();
620

            
621
            IncomingStreamRequest::$type(req)
622
        }};
623
    }
624

            
625
12
    let req = match msg.cmd() {
626
8
        RelayCmd::BEGIN => parse_stream_req!(msg, Begin),
627
4
        RelayCmd::BEGIN_DIR => parse_stream_req!(msg, BeginDir),
628
        RelayCmd::RESOLVE => parse_stream_req!(msg, Resolve),
629
        cmd => {
630
            // It's a bug if we reach this point, because CircHopOutbound::handle_msg()
631
            // should have consumed the message (by forwarding it to the appropriate stream
632
            // in its stream map)
633
            return Err(internal!("{cmd} is not an incoming stream request").into());
634
        }
635
    };
636

            
637
12
    Ok(req)
638
12
}
639

            
640
/// A stream message to be sent to the backward reactor for delivery.
641
pub(crate) struct ReadyStreamMsg {
642
    /// The hop number, or `None` if we are a relay.
643
    pub(crate) hop: Option<HopNum>,
644
    /// The message to send.
645
    pub(crate) msg: AnyRelayMsgOuter,
646
    /// The cell format used with the hop the message should be sent to.
647
    pub(crate) relay_cell_format: RelayCellFormat,
648
    /// The CC object to use.
649
    pub(crate) ccontrol: Arc<Mutex<CongestionControl>>,
650
}
651

            
652
/// A control message
653
/// that needs to be handled by [`StreamReactor`].
654
pub(crate) enum CtrlMsg {
655
    /// Stream data received from the other endpoint
656
    /// that needs to be delivered to a Tor stream
657
    DeliverStreamMsg {
658
        /// The ID of the stream this message is for.
659
        sid: StreamId,
660
        /// The message.
661
        msg: UnparsedRelayMsg,
662
        /// Whether the cell this message came from counts towards flow-control windows.
663
        cell_counts_toward_windows: bool,
664
    },
665

            
666
    /// Close the specified pending incoming stream, sending the provided END message.
667
    #[cfg(any(feature = "hs-service", feature = "relay"))]
668
    ClosePendingStream {
669
        /// The stream ID to send the END for.
670
        stream_id: StreamId,
671
        /// The END message to send, if any.
672
        behav: CloseStreamBehavior,
673
    },
674
}