Skip to main content

tor_proto/relay/
reactor.rs

1//! Module exposing the relay circuit reactor subsystem.
2//!
3//! See [`reactor`](crate::circuit::reactor) for a description of the overall architecture.
4//!
5//! All cells moving in the forward direction (i.e. away from the client)
6//! are handled by the forward reactor, which deals with
7//!
8//!  * unrecognized RELAY* cells, by moving them in the forward direction (towards the exit)
9//!  * recognized RELAY* cells, by splitting each cell into messages, and handling
10//!    each message individually as described in the table below
11//!    (Note: since prop340 is not yet implemented, in practice there is only 1 message per cell).
12//!  * DESTROY cells, by tearing down the circuit, and causing a DESTROY to be sent forward,
13//!    to the next hop, if there is one
14//!  * PADDING_NEGOTIATE cells (**not yet implemented**)
15//!
16//! ```text
17//!
18//! Legend: `F` = "forward reactor", `B` = "backward reactor", `S` = "stream reactor"
19//! `FH` = `ForwardHandler`
20//!
21//! | RELAY cmd  | Received in | Handled in            | Description                            |
22//! |------------|-------------|-----------------------|----------------------------------------|
23//! | DROP       | F           | FH::handle_meta_msg() | Passed to PaddingController for        |
24//! |            |             |                       | validation                             |
25//! |------------|-------------|-----------------------|----------------------------------------|
26//! | EXTEND2    | F           | FH::handle_meta_msg() | Handled by the ExtendRequestHandler    |
27//! |            |             |                       | See [forward::extend_handler].         |
28//! |------------|-------------|-----------------------|----------------------------------------|
29//! | TRUNCATE   | F           | FH::handle_meta_msg() | Not supported: TRUNCATE is considered  |
30//! |            |             |                       | a protocol violation, because none of  |
31//! |            |             |                       | of our implementations send it.        |
32//! |------------|-------------|-----------------------|----------------------------------------|
33//! | SENDME     | F           | B                     | Sent to BackwardReactor for handling.  |
34//! | (sid = 0)  |             |                       | See the [crate::circuit::reactor] docs |
35//! |------------|-------------|-----------------------|----------------------------------------|
36//! | Other      | F           | FH::handle_meta_msg() | Rejected as unrecognized               |
37//! | (sid = 0)  |             |                       |                                        |
38//! |------------|-------------|-----------------------|----------------------------------------|
39//! | Other      | F           | S                     | Handled in the `StreamReactor`         |
40//! | (sid != 0) |             |                       |                                        |
41//! ```
42
43pub(crate) mod backward;
44pub(crate) mod forward;
45
46use std::sync::Arc;
47use std::time::Duration;
48
49use futures::StreamExt as _;
50use futures::channel::mpsc;
51
52use tor_cell::chancell::CircId;
53use tor_cell::relaycell::RelayCmd;
54use tor_linkspec::OwnedChanTarget;
55use tor_memquota::mq_queue::{ChannelSpec, MpscSpec};
56use tor_rtcompat::{DynTimeProvider, Runtime};
57
58use crate::channel::Channel;
59use crate::circuit::circhop::ReactorStreamComponents;
60use crate::circuit::circhop::{CircHopOutbound, HopSettings};
61use crate::circuit::reactor::Reactor as BaseReactor;
62use crate::circuit::reactor::hop_mgr::HopMgr;
63use crate::circuit::reactor::stream;
64use crate::circuit::{CircuitRxReceiver, UniqId};
65use crate::congestion::sendme::StreamRecvWindow;
66use crate::crypto::cell::{InboundRelayLayer, OutboundRelayLayer};
67use crate::memquota::{CircuitAccount, SpecificAccount};
68use crate::relay::RelayCirc;
69use crate::relay::channel_provider::ChannelProvider;
70use crate::relay::reactor::backward::Backward;
71use crate::relay::reactor::forward::Forward;
72use crate::stream::flow_ctrl::state::WithSidechannelMitigations;
73use crate::stream::flow_ctrl::xon_xoff::reader::XonXoffReaderCtrl;
74use crate::stream::incoming::{
75    IncomingCmdChecker, IncomingStream, IncomingStreamRequestFilter, IncomingStreamRequestHandler,
76    StreamReqInfo,
77};
78use crate::stream::raw::StreamReceiver;
79use crate::stream::{RECV_WINDOW_INIT, StreamComponents, StreamTarget, Tunnel};
80
81// TODO(circpad): once padding is stabilized, the padding module will be moved out of client.
82use crate::client::circuit::padding::{PaddingController, PaddingEventStream};
83
84/// Type-alias for the relay base reactor type.
85type RelayBaseReactor<R> = BaseReactor<R, Forward, Backward>;
86
87/// The entry point of the circuit reactor subsystem.
88#[must_use = "If you don't call run() on a reactor, the circuit won't work."]
89pub(crate) struct Reactor<R: Runtime>(RelayBaseReactor<R>);
90
91/// A handler customizing the relay stream reactor.
92struct StreamHandler;
93
94impl stream::StreamHandler for StreamHandler {
95    fn halfstream_expiry(&self, hop: &CircHopOutbound) -> Duration {
96        let ccontrol = hop.ccontrol();
97
98        // Note: if we have no measurements for the RTT, this will be set to 0,
99        // so the stream will be removed from the stream map immediately,
100        // and any subsequent messages arriving on it will trigger
101        // a proto violation causing the circuit to close.
102        //
103        // TODO(relay-tuning): we should make sure that this doesn't cause us to
104        // wrongly close legitimate circuits that still have in-flight stream data
105        ccontrol
106            .lock()
107            .expect("poisoned lock")
108            .rtt()
109            .max_rtt()
110            // TODO(relay): we should fallback to a non-zero default here
111            // if we don't have any RTT measurements yet
112            .unwrap_or_default()
113    }
114
115    fn flowctrl_sidechannel_mitigations(&self) -> WithSidechannelMitigations {
116        // We're a relay, so we don't want sidechannel mitigations for flow control.
117        WithSidechannelMitigations::Disabled
118    }
119}
120
121impl<R: Runtime> Reactor<R> {
122    /// Create a new circuit reactor.
123    ///
124    /// Returns the [`Reactor`], a [`RelayCirc`] handle to it,
125    /// and a [`Stream`](futures::Stream) of `IncomingStream`s.
126    ///
127    /// The reactor will send outbound messages on `channel`, receive incoming
128    /// messages on `input`, and identify this circuit by the channel-local
129    /// [`CircId`] provided.
130    ///
131    /// The internal unique identifier for this circuit will be `unique_id`.
132    ///
133    /// The returned `IncomingStream`s are exit, dns, or directory streams.
134    /// An incoming stream is automatically rejected by the reactor
135    /// if the provided `IncomingStreamRequestFilter` rejects it.
136    /// You can also explicitly reject a stream by calling [`IncomingStream::reject`].
137    /// If the `Stream` is dropped, the next incoming stream request
138    /// (`BEGIN`, `BEGIN_DIR`, or RESOLVE`)
139    /// on this circuit will cause the stream reactor to shut down,
140    /// which will trigger a shutdown of all the circuit reactors (FWD, BWD),
141    /// which causing the circuit to close.
142    ///
143    /// The streams not rejected by the `IncomingStreamRequestFilter` will
144    /// get an entry in the circuit's stream map.
145    /// Rejecting such a stream using [`IncomingStream::reject`] will remove the entry.
146    ///
147    /// The `IncomingStreamRequestFilter` should only perform inexpensive checks
148    /// that won't block the reactor.
149    /// More expensive, or blocking checks, should be handled outside of the circuit reactor,
150    /// when processing new `IncomingStream`s from the returned Rust stream.
151    ///
152    /// Data and directory streams can be accepted by calling [`IncomingStream::accept_data`].
153    /// The caller is responsible for proxying data between the resulting `DataStream`
154    /// and the local application stream.
155    ///
156    // TODO(relay): say how RESOLVE streams should be handled
157    //
158    // TODO: declare a type-alias for the impl futures::Stream return type
159    // when support for impl in type aliases gets stabilized.
160    //
161    // See issue #63063 <https://github.com/rust-lang/rust/issues/63063>
162    //
163    // TODO(DEDUP): the incoming stream handling is *very* similar
164    // to the impll from ServiceOnionServiceDataTunnel::allow_stream_requests.
165    // We should dedupe these someday, when we rewrite the client reactor
166    // to use the new multi-reactor architecture
167    #[allow(clippy::too_many_arguments)] // TODO
168    pub(crate) fn new(
169        runtime: R,
170        channel: &Arc<Channel>,
171        circ_id: CircId,
172        unique_id: UniqId,
173        input: CircuitRxReceiver,
174        crypto_in: Box<dyn InboundRelayLayer + Send>,
175        crypto_out: Box<dyn OutboundRelayLayer + Send>,
176        settings: &HopSettings,
177        chan_provider: Arc<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
178        padding_ctrl: PaddingController,
179        padding_event_stream: PaddingEventStream,
180        incoming_filter: Box<dyn IncomingStreamRequestFilter>,
181        allowed_stream_cmds: &[RelayCmd],
182        memquota: &CircuitAccount,
183    ) -> crate::Result<(
184        Self,
185        Arc<RelayCirc>,
186        impl futures::Stream<Item = IncomingStream> + use<R>,
187    )> {
188        // NOTE: not registering this channel with the memquota subsystem is okay,
189        // because it has no buffering (if ever decide to make the size of this buffer
190        // non-zero for whatever reason, we must remember to register it with memquota
191        // so that it counts towards the total memory usage for the circuit.
192        #[allow(clippy::disallowed_methods)]
193        let (stream_tx, stream_rx) = mpsc::channel(0);
194
195        /// The size of the channel receiving IncomingStreamRequestContexts.
196        ///
197        // TODO(relay-tuning): buffer size
198        //
199        // This is currently set to 2x the initial receive window,
200        // the same as the buffer size we use for onion services.
201        // This value was picked arbitrarily,
202        // and is not necessarily tuned for relay needs.
203        const INCOMING_BUFFER: usize = crate::stream::STREAM_READER_BUFFER;
204
205        let time_provider = DynTimeProvider::new(runtime.clone());
206        let (incoming_sender, incoming_receiver) = MpscSpec::new(INCOMING_BUFFER)
207            .new_mq(time_provider.clone(), memquota.as_raw_account())?;
208
209        // Our IncomingCmdChecker does not reject BEGIN, BEGIN_DIR, RESOLVE cells,
210        // but that doesn't necessarily mean the stream will be accepted.
211        // An incoming stream can still be rejected at a later stage,
212        // by the IncomingStreamRequestFilter, or directly by the consumer of the
213        // futures::Stream<Item = IncomingStream> (by calling IncomingStream::reject()).
214        let cmd_checker = IncomingCmdChecker::new_any(allowed_stream_cmds);
215        let incoming_handler = IncomingStreamRequestHandler {
216            incoming_sender,
217            hop_num: None,
218            cmd_checker,
219            filter: incoming_filter,
220        };
221        let mut hop_mgr = HopMgr::new_with_incoming_handler(
222            runtime.clone(),
223            unique_id,
224            circ_id,
225            StreamHandler,
226            stream_tx,
227            incoming_handler,
228            memquota.clone(),
229        );
230
231        // On the relay side, we always have one "hop" (ourselves).
232        //
233        // Clients will need to call this function in response to CtrlMsg::Create
234        // (TODO: for clients, we probably will need to store a bunch more state here)
235        hop_mgr.add_hop(settings.clone())?;
236
237        // TODO(relay): currently we don't need buffering on this channel,
238        // but we might need it if we start using it for more than just EXTENDED2 events
239        #[allow(clippy::disallowed_methods)]
240        let (fwd_ev_tx, fwd_ev_rx) = mpsc::channel(0);
241        let forward = Forward::new(
242            channel,
243            circ_id,
244            unique_id,
245            crypto_out,
246            chan_provider,
247            fwd_ev_tx,
248            memquota.clone(),
249        );
250        let backward = Backward::new(crypto_in);
251
252        let (inner, handle) = BaseReactor::new(
253            runtime,
254            channel,
255            circ_id,
256            unique_id,
257            input,
258            forward,
259            backward,
260            hop_mgr,
261            padding_ctrl,
262            padding_event_stream,
263            stream_rx,
264            fwd_ev_rx,
265            memquota,
266        );
267
268        let reactor = Self(inner);
269        let handle = Arc::new(RelayCirc(handle));
270
271        // Note: tunnel is a bit of a misnomer for relays
272        let tunnel = Arc::clone(&handle);
273        // TODO(relay): this is more or less copy-pasta from client code
274        let stream = incoming_receiver.map(move |req_ctx| {
275            let StreamReqInfo {
276                req,
277                stream_id,
278                hop,
279                stream_components:
280                    ReactorStreamComponents {
281                        stream_inbound_rx,
282                        stream_outbound_tx,
283                        rate_limit_rx,
284                        drain_rate_request_rx,
285                    },
286                memquota,
287                relay_cell_format,
288            } = req_ctx;
289
290            // There is no originating hop if we're a relay
291            debug_assert!(hop.is_none());
292
293            let target = StreamTarget {
294                tunnel: Tunnel::Relay(Arc::clone(&tunnel)),
295                tx: stream_outbound_tx,
296                hop: None,
297                stream_id,
298                relay_cell_format,
299                rate_limit_stream: rate_limit_rx,
300            };
301
302            // can be used to build a reader that supports XON/XOFF flow control
303            let xon_xoff_reader_ctrl =
304                XonXoffReaderCtrl::new(drain_rate_request_rx, target.clone());
305
306            let reader = StreamReceiver {
307                target: target.clone(),
308                receiver: stream_inbound_rx,
309                recv_window: StreamRecvWindow::new(RECV_WINDOW_INIT),
310                ended: false,
311            };
312
313            let components = StreamComponents {
314                stream_receiver: reader,
315                target,
316                memquota,
317                xon_xoff_reader_ctrl,
318            };
319
320            IncomingStream::new(time_provider.clone(), req, components)
321        });
322
323        Ok((reactor, handle, stream))
324    }
325
326    /// Launch the reactor, and run until the circuit closes or we
327    /// encounter an error.
328    ///
329    /// Once this method returns, the circuit is dead and cannot be
330    /// used again.
331    pub(crate) async fn run(self) -> crate::Result<()> {
332        self.0.run().await
333    }
334}
335
336#[cfg(test)]
337pub(crate) mod test {
338    // @@ begin test lint list maintained by maint/add_warning @@
339    #![allow(clippy::bool_assert_comparison)]
340    #![allow(clippy::clone_on_copy)]
341    #![allow(clippy::dbg_macro)]
342    #![allow(clippy::mixed_attributes_style)]
343    #![allow(clippy::print_stderr)]
344    #![allow(clippy::print_stdout)]
345    #![allow(clippy::single_char_pattern)]
346    #![allow(clippy::unwrap_used)]
347    #![allow(clippy::unchecked_time_subtraction)]
348    #![allow(clippy::useless_vec)]
349    #![allow(clippy::needless_pass_by_value)]
350    #![allow(clippy::string_slice)] // See arti#2571
351    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
352
353    use super::*;
354    use crate::channel::ChannelMode;
355    use crate::channel::CtrlMsg;
356    use crate::channel::circmap::CircIdRange;
357    use crate::channel::test_utils::DummyChan;
358    use crate::circuit::CircParameters;
359    use crate::circuit::circ_sender;
360    use crate::circuit::reactor::test::{AllowAllStreamsFilter, rmsg_to_ccmsg};
361    use crate::circuit::test::new_circ_net_params;
362    use crate::client::circuit::padding::new_padding;
363    use crate::congestion::test_utils::params::build_cc_vegas_params;
364    use crate::crypto::cell::RelayCellBody;
365    use crate::crypto::cell::{InboundRelayLayer, OutboundRelayLayer};
366    use crate::relay::CreateRequestHandler;
367    use crate::relay::channel::test::DummyChanProvider;
368    use crate::stream::flow_ctrl::params::FlowCtrlParameters;
369    use crate::stream::incoming::{IncomingStream, IncomingStreamRequest, NoOpRequestFilter};
370
371    use futures::AsyncReadExt as _;
372    use futures::SinkExt as _;
373    use oneshot_fused_workaround as oneshot;
374    use tracing_test::traced_test;
375
376    use tor_basic_utils::test_rng::{TestingRng, testing_rng};
377    use tor_cell::chancell::{ChanCell, ChanCmd, msg as chanmsg};
378    use tor_cell::relaycell::{AnyRelayMsgOuter, RelayCellFormat, StreamId, msg as relaymsg};
379    use tor_key_forge::Keygen;
380    use tor_linkspec::{EncodedLinkSpec, HasRelayIds, LinkSpec};
381    use tor_llcrypto::pk::curve25519::StaticKeypair;
382    use tor_llcrypto::pk::ed25519::Ed25519Identity;
383    use tor_llcrypto::pk::rsa::RsaIdentity;
384    use tor_llcrypto::rng::FakeEntropicRng;
385    use tor_protover::{Protocols, named};
386    use tor_relay_crypto::pk::RelayNtorKeys;
387    use tor_rtcompat::SpawnExt;
388    use tor_rtcompat::{DynTimeProvider, Runtime};
389    use tor_rtmock::MockRuntime;
390
391    use chanmsg::{AnyChanMsg, Destroy, DestroyReason, HandshakeType};
392    use relaymsg::SendmeTag;
393
394    use std::net::IpAddr;
395    use std::sync::{Arc, Mutex, Weak, mpsc};
396    use std::task::{Context, Poll, Waker};
397
398    // An inbound encryption layer that doesn't do any crypto.
399    struct DummyInboundCrypto {}
400
401    // An outbound encryption layer that doesn't do any crypto.
402    struct DummyOutboundCrypto {
403        /// Channel for controlling whether the current cell is meant for us or not.
404        ///
405        /// Useful for tests that check if recognized/unrecognized
406        /// cells are handled/forwarded correctly.
407        recognized_rx: mpsc::Receiver<Recognized>,
408    }
409
410    const DUMMY_TAG: [u8; 20] = [1; 20];
411
412    impl InboundRelayLayer for DummyInboundCrypto {
413        fn originate(&mut self, _cmd: ChanCmd, _cell: &mut RelayCellBody) -> SendmeTag {
414            DUMMY_TAG.into()
415        }
416
417        fn encrypt_inbound(&mut self, _cmd: ChanCmd, _cell: &mut RelayCellBody) {}
418    }
419
420    impl OutboundRelayLayer for DummyOutboundCrypto {
421        fn decrypt_outbound(
422            &mut self,
423            _cmd: ChanCmd,
424            _cell: &mut RelayCellBody,
425        ) -> Option<SendmeTag> {
426            // Note: this should never block.
427            let recognized = self.recognized_rx.recv().unwrap();
428
429            match recognized {
430                Recognized::Yes => Some(DUMMY_TAG.into()),
431                Recognized::No => None,
432            }
433        }
434    }
435
436    /// A circuit reactor handle, for building circuits of the form
437    /// A -> B, and A -> B -> C, where the circuit reactor under test
438    /// "thinks" it is B.
439    ///
440    /// [`ReactorTestCtrl::new`] builds and spawns:
441    ///
442    ///   * a channel reactor for the A - B "Tor Channel"
443    ///   * a circuit reactor for B's view of the circuit
444    ///
445    /// Some of the tests in this module extend the circuit by another dummy hop,
446    /// to obtain an A -> B -> C circuit. This involves sending an EXTEND2
447    /// cell over the A -> B channel, and calling [`ReactorTestCtrl::do_create2_handshake`]
448    /// to finalize the handshake.
449    struct ReactorTestCtrl {
450        /// The relay circuit handle.
451        relay_circ: Arc<RelayCirc>,
452        /// The circuit id on our `inbound_chan`.
453        circid: CircId,
454        /// The inbound channel ("towards the client").
455        ///
456        /// This is the "Tor channel" between A and B in
457        /// a circuit of the form A -> B or A -> B -> C.
458        inbound_chan: DummyChan,
459        /// The outbound channel ("away from the client"), if any.
460        ///
461        /// Shared with the DummyChanProvider, which initializes this
462        /// when the relay reactor launches a channel to the next hop
463        /// via `get_or_launch()`.
464        ///
465        /// This is the "Tor channel" between B and C,
466        /// if our test circuit is of the form A -> B -> C
467        /// (i.e. if we have extended the "base" circuit by another mock hop, to C).
468        outbound_chan: Arc<Mutex<Option<DummyChan>>>,
469        /// MPSC channel for telling the DummyOutboundCrypto that the next
470        /// cell we're about to send to the reactor should be "recognized".
471        recognized_tx: mpsc::Sender<Recognized>,
472    }
473
474    /// Whether a forward cell to send should be "recognized"
475    /// or "unrecognized" by the relay under test.
476    enum Recognized {
477        /// Recognized
478        Yes,
479        /// Unrecognized
480        No,
481    }
482
483    /// The direction we expect the reactor to have sent a DESTROY in
484    #[allow(dead_code)] // we don't use all of these yet
485    enum DestroyDirection {
486        /// Forward ("towards the exit")
487        Forward,
488        /// Backward ("towards the client")
489        Backward,
490        /// Both forward and backward
491        Both,
492    }
493
494    /// Decode a cell, extracting the underlying message of type `expect_msg`
495    macro_rules! decode_relay_cell {
496        ($cell:expr, $expect_msg:tt) => {{
497            let rmsg = match $cell.msg() {
498                chanmsg::AnyChanMsg::Relay(r) => AnyRelayMsgOuter::decode_singleton(
499                    RelayCellFormat::V0,
500                    r.clone().into_relay_body(),
501                )
502                .unwrap(),
503                msg => panic!("unexpected forwarded {msg:?}"),
504            };
505
506            let msg = match rmsg.msg() {
507                relaymsg::AnyRelayMsg::$expect_msg(inner) => inner.clone(),
508                _ => panic!("unexpected relay message {rmsg:?}"),
509            };
510
511            (rmsg.stream_id(), msg)
512        }};
513    }
514
515    const DUMMY_ED25519_KEY: [u8; 32] = *b"32 bytes pretending to be a key!";
516    const DUMMY_RSA_KEY: [u8; 20] = *b"not really an RSA ky";
517
518    /// Helper for building a [`ChannelMode::Relay`] for our test reactor
519    fn build_channel_mode<R: Runtime>(
520        chan_provider: Arc<DummyChanProvider<R>>,
521        allowed_stream_cmds: &[RelayCmd],
522    ) -> ChannelMode {
523        let our_ed25519_id = Ed25519Identity::from_bytes(&DUMMY_ED25519_KEY).unwrap();
524        let our_rsa_id = RsaIdentity::from_bytes(&DUMMY_RSA_KEY).unwrap();
525
526        let mut rng = FakeEntropicRng::<TestingRng>(testing_rng());
527        let relay_ntor_keys = StaticKeypair::generate(&mut rng).unwrap();
528
529        // A handler that will process CREATE* requests on channels
530        //
531        // Note: in practice, this won't actually be used at all,
532        // because for the purposes of these tests, the circuit reactor is spawned manually,
533        // by ReactorTestCtrl::new(), which also hackily initializes the channel's circuit map
534        // with a circuit entry for it.
535        //
536        // This should be fine for now, but we might want to rethink it in the future
537        // (i.e. we might want to let the channel reactor spawn the circuit reactor under test,
538        // in response to CREATE*).
539        let (create_request_handler, _circuit_stream_rx) = CreateRequestHandler::new(
540            Arc::downgrade(&chan_provider) as Weak<_>,
541            new_circ_net_params(),
542            RelayNtorKeys::new(relay_ntor_keys.into()),
543            // Don't filter any stream requests.
544            Box::new(|| Box::new(NoOpRequestFilter) as Box<_>),
545            allowed_stream_cmds,
546        );
547        let create_request_handler = Arc::new(create_request_handler);
548
549        ChannelMode::Relay {
550            create_request_handler,
551            our_ed25519_id,
552            our_rsa_id,
553            // This doesn't actually matter for these tests
554            circ_id_range: CircIdRange::Low,
555        }
556    }
557
558    /// Prepare our "inbound" channel,
559    ///
560    /// > Note: the concept of an "inbound" channel only really makes sense
561    /// > if you think about it from a circuit perspective:
562    /// > these tests essentially simulate circuits of the form A -> B
563    /// > and A -> B -> C. The relay circuit reactor under test "thinks" it's relay B,
564    /// > and its "inbound" and "outbound" channels are the A -> B and B -> C channels,
565    /// > respectively.
566    ///
567    /// This spawns a channel reactor and creates a fake circuit entry in it,
568    /// which is wired up to the circuit Reactor under test by [`ReactorTestCtrl::new`].
569    async fn prepare_inbound_chan<R: Runtime>(
570        rt: &R,
571        mode: ChannelMode,
572    ) -> (CircId, CircuitRxReceiver, DummyChan) {
573        let mut inbound_chan = DummyChan::run(rt, mode);
574
575        let memquota = CircuitAccount::new_noop();
576        let time_provider = DynTimeProvider::new(rt.clone());
577
578        let (sender, receiver) = MpscSpec::new(128)
579            .new_mq(time_provider, memquota.as_raw_account())
580            .unwrap();
581        let (sender, receiver) = circ_sender::channel(sender, receiver);
582        let (created_sender, created_receiver) = oneshot::channel();
583
584        let (tx, rx) = oneshot::channel();
585
586        // Note: we need to make sure the circuit is in the channel reactor's
587        // circuit map, because otherwise we can't test the DESTROY behavior,
588        // (the channel reactor conditionally sends DESTROY based on whether
589        // the circuit entry is still in the circmap or not;
590        // the presence of a circuit in the circmap is a proxy for
591        // whether we have sent a DESTROY ourselves or not).
592        inbound_chan
593            .channel
594            .send_control(CtrlMsg::AllocateCircuit {
595                created_sender,
596                sender,
597                tx,
598            })
599            .unwrap();
600        let (circid, _circ_unique_id, _padding_ctrl, _padding_stream) = rx.await.unwrap().unwrap();
601
602        // Hack: AllocateCircuit puts the circuit in the "Opening" state,
603        // but in order to actually be able to send anything on this channel,
604        // we need to advance it to "Open". We do that by sending a CREATED2 cell on the channel,
605        // which is nonsensical from the perspective of the relay-specific test setup
606        // (it would make sense if this was a client channel, however).
607        // Alas, it is the only way we can advance the circuit's state to "Open"
608        // in the channel's circmap without introducing a test-only CtrlMsg for this,
609        // or without surrendering the circuit Reactor setup to the channel impl
610        // (the latter might not be so bad actually, because it would be closer to what
611        // happens in reality).
612        let handshake = vec![];
613        let created2 = chanmsg::Created2::new(handshake.clone());
614        let cell = ChanCell::new(Some(circid), created2.into());
615        inbound_chan.tx.try_send(Ok(cell)).unwrap();
616
617        // We **have** to read the CREATED2 (otherwise the channel reactor shuts down with an error)
618        let _ = created_receiver.await;
619
620        (circid, receiver, inbound_chan)
621    }
622
623    impl ReactorTestCtrl {
624        /// Spawn a relay circuit reactor, returning a `ReactorTestCtrl` for
625        /// controlling it.
626        async fn spawn_reactor<R: Runtime>(
627            rt: &R,
628            allowed_stream_cmds: &[RelayCmd],
629        ) -> (Self, impl futures::Stream<Item = IncomingStream>) {
630            let outbound_chan = Arc::new(Mutex::new(None));
631            let chan_provider = Arc::new(DummyChanProvider::new(
632                rt.clone(),
633                Arc::clone(&outbound_chan),
634            ));
635
636            let mode = build_channel_mode(Arc::clone(&chan_provider), allowed_stream_cmds);
637            let (circid, receiver, inbound_chan) = prepare_inbound_chan(rt, mode).await;
638
639            let unique_id = UniqId::new(8, 17);
640            let (padding_ctrl, padding_stream) = new_padding(DynTimeProvider::new(rt.clone()));
641            let params = CircParameters::new(
642                true,
643                build_cc_vegas_params(),
644                FlowCtrlParameters::defaults_for_tests(),
645            );
646            let settings = HopSettings::from_params_and_caps(
647                crate::circuit::circhop::HopNegotiationType::Full,
648                &params,
649                &[named::FLOWCTRL_CC].into_iter().collect::<Protocols>(),
650            )
651            .unwrap();
652
653            let (recognized_tx, recognized_rx) = mpsc::channel();
654            let (reactor, relay_circ, incoming_streams) = Reactor::new(
655                rt.clone(),
656                &Arc::clone(&inbound_chan.channel),
657                circid,
658                unique_id,
659                receiver,
660                Box::new(DummyInboundCrypto {}),
661                Box::new(DummyOutboundCrypto { recognized_rx }),
662                &settings,
663                chan_provider,
664                padding_ctrl,
665                padding_stream,
666                Box::new(AllowAllStreamsFilter),
667                allowed_stream_cmds,
668                &CircuitAccount::new_noop(),
669            )
670            .unwrap();
671
672            rt.spawn(async {
673                let _ = reactor.run().await;
674            })
675            .unwrap();
676
677            let ctrl = Self {
678                relay_circ,
679                circid,
680                recognized_tx,
681                inbound_chan,
682                outbound_chan,
683            };
684
685            (ctrl, incoming_streams)
686        }
687
688        /// Simulate the sending of a forward relay message through our relay.
689        async fn send_fwd(
690            &mut self,
691            id: Option<StreamId>,
692            msg: relaymsg::AnyRelayMsg,
693            recognized: Recognized,
694            early: bool,
695        ) {
696            // This a bit janky, but for each forward cell we send to the reactor
697            // we need to send a bit of metadata to the DummyOutboundLayer
698            // specifying whether the cell should be treated as recognized
699            // or unrecognized
700            self.recognized_tx.send(recognized).unwrap();
701            self.send_fwd_cmsg(rmsg_to_ccmsg(id, msg, early)).await;
702        }
703
704        /// Simulate the sending of a forward channel message through our relay.
705        async fn send_fwd_cmsg(&mut self, msg: chanmsg::AnyChanMsg) {
706            let cell = ChanCell::new(Some(self.circid), msg);
707            self.inbound_chan.tx.send(Ok(cell)).await.unwrap();
708        }
709
710        /// Whether the reactor opened an outbound channel
711        /// (i.e. a channel to the next relay in the circuit).
712        fn outbound_chan_launched(&self) -> bool {
713            self.outbound_chan.lock().unwrap().is_some()
714        }
715
716        /// Perform the CREATE2 handshake.
717        async fn do_create2_handshake(
718            &mut self,
719            rt: &MockRuntime,
720            expected_hs_type: HandshakeType,
721        ) -> Option<CircId> {
722            // First, check that the reactor actually sent a CREATE2 to the next hop...
723            let (circid, msg) = self.read_outbound().into_circid_and_msg();
724            let _create2 = match msg {
725                chanmsg::AnyChanMsg::Create2(c) => {
726                    assert_eq!(c.handshake_type(), expected_hs_type);
727                    c
728                }
729                _ => panic!("unexpected forwarded {msg:?}"),
730            };
731
732            let handshake = vec![];
733            let created2 = chanmsg::Created2::new(handshake.clone());
734            // ...and then finalize the handshake by pretending to be
735            // the responding relay
736            self.write_outbound(circid, chanmsg::AnyChanMsg::Created2(created2));
737            rt.advance_until_stalled().await;
738
739            // Make sure we actually did send an EXTENDED2 towards the client
740            let msg = self.read_inbound();
741
742            let (_sid, e) = decode_relay_cell!(msg, Extended2);
743            assert_eq!(e.clone().into_body(), handshake);
744
745            circid
746        }
747
748        /// Whether the circuit is closing (e.g. due to a proto violation).
749        fn is_closing(&self) -> bool {
750            self.relay_circ.is_closing()
751        }
752
753        /// Read a cell from the inbound channel
754        /// (moving towards the client).
755        ///
756        /// See [`try_read_inbound`](Self::try_read_inbound).
757        ///
758        /// Panics if there are no ready cells on the inbound MPSC channel.
759        fn read_inbound(&mut self) -> ChanCell<AnyChanMsg> {
760            self.try_read_inbound().unwrap()
761        }
762
763        /// Try to read a cell from the inbound channel
764        /// (moving towards the client).
765        ///
766        /// For example, for a circuit of the form A -> B -> C,
767        /// where B is the relay whose circuit reactor we're testing,
768        /// this function reads a channel message on the A <-> B channel,
769        /// from the perspective of A (i.e. it reads a channel message sent by B).
770        ///
771        /// Returns None if there are no ready cells on the inbound MPSC channel.
772        fn try_read_inbound(&mut self) -> Option<ChanCell<AnyChanMsg>> {
773            #[allow(deprecated)] // TODO(#2386)
774            self.inbound_chan.rx.try_next().ok().flatten()
775        }
776
777        /// Read a cell from the outbound channel
778        /// (moving towards the next hop).
779        ///
780        /// See [`try_read_outbound`](Self::try_read_outbound).
781        ///
782        /// Panics if there are no ready cells on the outbound MPSC channel,
783        /// or if there is no outbound channel.
784        fn read_outbound(&mut self) -> ChanCell<AnyChanMsg> {
785            self.try_read_outbound().unwrap()
786        }
787
788        /// Read a cell from the outbound channel
789        /// (moving towards the next hop).
790        ///
791        /// For example, for a circuit of the form A -> B -> C,
792        /// where B is the relay whose circuit reactor we're testing,
793        /// this function reads a channel message on the B <-> C channel,
794        /// from the perspective of C (i.e. it reads a channel message sent by B).
795        ///
796        /// Returns None if there are no ready cells on the outbound MPSC channel,
797        /// or if there is no outbound channel.
798        fn try_read_outbound(&mut self) -> Option<ChanCell<AnyChanMsg>> {
799            let mut lock = self.outbound_chan.lock().unwrap();
800            let chan = lock.as_mut()?;
801            #[allow(deprecated)] // TODO(#2386)
802            chan.rx.try_next().ok().flatten()
803        }
804
805        /// Write to the sending end of the outbound Tor channel.
806        ///
807        /// Simulates the receipt of a cell from the next hop.
808        ///
809        /// Panics if the outbound chan sender is full.
810        fn write_outbound(&mut self, circid: Option<CircId>, msg: chanmsg::AnyChanMsg) {
811            let mut lock = self.outbound_chan.lock().unwrap();
812            let chan = lock.as_mut().unwrap();
813            let cell = ChanCell::new(circid, msg);
814
815            chan.tx.try_send(Ok(cell)).unwrap();
816        }
817    }
818
819    fn dummy_linkspecs() -> Vec<EncodedLinkSpec> {
820        vec![
821            LinkSpec::Ed25519Id([43; 32].into()).encode().unwrap(),
822            LinkSpec::RsaId([45; 20].into()).encode().unwrap(),
823            LinkSpec::OrPort("127.0.0.1".parse::<IpAddr>().unwrap(), 999)
824                .encode()
825                .unwrap(),
826        ]
827    }
828
829    macro_rules! assert_cell_is_destroy {
830        ($cell:expr, $reason:expr) => {{
831            match $cell.msg() {
832                chanmsg::AnyChanMsg::Destroy(d) => {
833                    assert_eq!(d.reason(), $reason);
834                }
835                _ => panic!("unexpected ending {:?}", $cell),
836            }
837        }};
838    }
839
840    /// Assert that we have sent a DESTROY cell with the specified `reason`
841    /// towards the "client" and/or the "next hop".
842    ///
843    /// The test is expected to drain the inbound Tor "channel"
844    /// of any non-ending cells it might be expecting before calling this function.
845    fn assert_destroy_sent(
846        ctrl: &mut ReactorTestCtrl,
847        reason: DestroyReason,
848        direction: DestroyDirection,
849    ) {
850        assert!(ctrl.is_closing());
851
852        match direction {
853            DestroyDirection::Backward => {
854                assert_cell_is_destroy!(ctrl.read_inbound(), reason);
855                assert!(ctrl.try_read_outbound().is_none());
856            }
857            DestroyDirection::Forward => {
858                assert_cell_is_destroy!(ctrl.read_outbound(), reason);
859                assert!(ctrl.try_read_inbound().is_none());
860            }
861            DestroyDirection::Both => {
862                assert_cell_is_destroy!(ctrl.read_inbound(), reason);
863                assert_cell_is_destroy!(ctrl.read_outbound(), reason);
864            }
865        }
866    }
867
868    macro_rules! expect_cell {
869        ($cell:expr, $chanmsg:tt, $relaymsg:tt) => {{
870            let msg = match $cell.msg() {
871                chanmsg::AnyChanMsg::$chanmsg(m) => {
872                    let body = m.clone().into_relay_body();
873                    AnyRelayMsgOuter::decode_singleton(RelayCellFormat::V0, body).unwrap()
874                }
875                _ => panic!("unexpected forwarded {:?}", $cell),
876            };
877
878            match msg.msg() {
879                relaymsg::AnyRelayMsg::$relaymsg(m) => m.clone(),
880                _ => panic!("unexpected cell {msg:?}"),
881            }
882        }};
883    }
884
885    #[traced_test]
886    #[test]
887    fn reject_extend2_relay() {
888        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
889            let (mut ctrl, _incoming_streams) =
890                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
891            rt.advance_until_stalled().await;
892
893            let linkspecs = dummy_linkspecs();
894            let extend2 = relaymsg::Extend2::new(linkspecs, HandshakeType::NTOR_V3, vec![]).into();
895            ctrl.send_fwd(None, extend2, Recognized::Yes, false).await;
896            rt.advance_until_stalled().await;
897
898            assert!(logs_contain("got EXTEND2 in a RELAY cell?!"));
899            assert!(!ctrl.outbound_chan_launched());
900
901            // There is no next hop because we haven't extended the circuit,
902            // so only expect the DESTROY to be sent toward the client (Backward).
903            assert_destroy_sent(&mut ctrl, DestroyReason::NONE, DestroyDirection::Backward);
904        });
905    }
906
907    #[traced_test]
908    #[test]
909    fn reject_extend2_previous_hop() {
910        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
911            let (mut ctrl, _incoming_streams) =
912                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
913            rt.advance_until_stalled().await;
914
915            // No outbound circuits yet
916            assert!(!ctrl.outbound_chan_launched());
917
918            // Build a linkspec with the identities of the dummy channel
919            let mut linkspecs = ctrl
920                .inbound_chan
921                .channel
922                .target()
923                .identities()
924                .map(|id| LinkSpec::from(id.to_owned()).encode())
925                .collect::<Result<Vec<_>, _>>()
926                .unwrap();
927
928            // Make sure this channel actually has some identities
929            // (i.e. that it's not a client channel or something)
930            assert_eq!(linkspecs.len(), 2);
931
932            // There must be at least one IPv4 OR port address
933            linkspecs.push(
934                LinkSpec::OrPort("127.0.0.1".parse::<IpAddr>().unwrap(), 999)
935                    .encode()
936                    .unwrap(),
937            );
938            let handshake_type = HandshakeType::NTOR_V3;
939            let extend2 = relaymsg::Extend2::new(linkspecs, handshake_type, vec![]).into();
940            ctrl.send_fwd(None, extend2, Recognized::Yes, true).await;
941            rt.advance_until_stalled().await;
942
943            // The reactor handled the EXTEND2 and launched an outbound channel
944            assert!(logs_contain("Cannot extend circuit to previous hop"));
945            assert!(!ctrl.outbound_chan_launched());
946            assert!(ctrl.is_closing());
947        });
948    }
949
950    #[traced_test]
951    #[test]
952    fn extend_and_forward() {
953        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
954            let (mut ctrl, _incoming_streams) =
955                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
956            rt.advance_until_stalled().await;
957
958            // No outbound circuits yet
959            assert!(!ctrl.outbound_chan_launched());
960
961            let linkspecs = dummy_linkspecs();
962            let handshake_type = HandshakeType::NTOR_V3;
963            let extend2 = relaymsg::Extend2::new(linkspecs, handshake_type, vec![]).into();
964            ctrl.send_fwd(None, extend2, Recognized::Yes, true).await;
965            rt.advance_until_stalled().await;
966
967            // The reactor handled the EXTEND2 and launched an outbound channel
968            assert!(logs_contain(
969                "Launched channel to the next hop circ_uniq_id=Circ 8.17"
970            ));
971            assert!(ctrl.outbound_chan_launched());
972            assert!(!ctrl.is_closing());
973
974            let _circid = ctrl.do_create2_handshake(&rt, handshake_type).await;
975            assert!(logs_contain("Got CREATED2 response from next hop"));
976            assert!(logs_contain("Extended circuit to the next hop"));
977
978            // Time to forward a message to the next hop!
979            let early = false;
980            let begin = relaymsg::Begin::new("127.0.0.1", 1111, 0).unwrap();
981            ctrl.send_fwd(None, begin.clone().into(), Recognized::No, early)
982                .await;
983            rt.advance_until_stalled().await;
984
985            // Ensure the other end received the BEGIN cell
986            let cell = ctrl.read_outbound();
987            let recvd_begin = expect_cell!(cell, Relay, Begin);
988            assert_eq!(begin, recvd_begin);
989
990            // Now send the same message again, but this time in a RELAY_EARLY
991            let early = true;
992            let begin = relaymsg::Begin::new("127.0.0.1", 1111, 0).unwrap();
993            ctrl.send_fwd(None, begin.clone().into(), Recognized::No, early)
994                .await;
995            rt.advance_until_stalled().await;
996            let cell = ctrl.read_outbound();
997            let recvd_begin = expect_cell!(cell, RelayEarly, Begin);
998            assert_eq!(begin, recvd_begin);
999        });
1000    }
1001
1002    #[traced_test]
1003    #[test]
1004    fn forward_before_extend() {
1005        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
1006            let (mut ctrl, _incoming_streams) =
1007                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
1008            rt.advance_until_stalled().await;
1009
1010            // Send an arbitrary unrecognized cell. The reactor should flag this as
1011            // a protocol violation, because we don't have an outbound channel to forward it on.
1012            let end = relaymsg::End::new_misc().into();
1013            ctrl.send_fwd(None, end, Recognized::No, true).await;
1014            rt.advance_until_stalled().await;
1015
1016            assert!(logs_contain(
1017                "Asked to forward cell before the circuit was extended?!"
1018            ));
1019
1020            // There is no next hop because we haven't extended the circuit,
1021            // so only expect the DESTROY to be sent toward the client (Backward).
1022            assert_destroy_sent(&mut ctrl, DestroyReason::NONE, DestroyDirection::Backward);
1023        });
1024    }
1025
1026    #[traced_test]
1027    #[test]
1028    fn reject_invalid_begin() {
1029        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
1030            let (mut ctrl, _incoming_streams) =
1031                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
1032            rt.advance_until_stalled().await;
1033
1034            let begin = relaymsg::Begin::new("127.0.0.1", 1111, 0).unwrap().into();
1035
1036            // BEGIN cells *must* have a stream ID, so expect the reactor to reject this
1037            // and close the circuit
1038            ctrl.send_fwd(None, begin, Recognized::Yes, false).await;
1039            rt.advance_until_stalled().await;
1040
1041            assert!(logs_contain(
1042                "Invalid stream ID [scrubbed] for relay command BEGIN"
1043            ));
1044
1045            // There is no next hop because we haven't extended the circuit,
1046            // so only expect the DESTROY to be sent toward the client (Backward).
1047            assert_destroy_sent(&mut ctrl, DestroyReason::NONE, DestroyDirection::Backward);
1048        });
1049    }
1050
1051    #[traced_test]
1052    #[test]
1053    fn destroy_from_client() {
1054        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
1055            let (mut ctrl, _incoming_streams) =
1056                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
1057            rt.advance_until_stalled().await;
1058
1059            // Extend the circuit by another hop
1060            let linkspecs = dummy_linkspecs();
1061            let handshake_type = HandshakeType::NTOR_V3;
1062            let extend2 = relaymsg::Extend2::new(linkspecs, handshake_type, vec![]).into();
1063            ctrl.send_fwd(None, extend2, Recognized::Yes, true).await;
1064            rt.advance_until_stalled().await;
1065            let _circid = ctrl.do_create2_handshake(&rt, handshake_type).await;
1066            assert!(logs_contain("Extended circuit to the next hop"));
1067            assert!(ctrl.outbound_chan_launched());
1068
1069            // Simulate the client sending us a DESTROY cell
1070            let destroy = Destroy::new(DestroyReason::PROTOCOL);
1071            ctrl.send_fwd_cmsg(destroy.into()).await;
1072            rt.advance_until_stalled().await;
1073
1074            assert!(logs_contain(
1075                "Received outbound DESTROY, circuit shutting down"
1076            ));
1077
1078            // Since this is a circuit of the form A -> B -> C,
1079            // and A sent us a DESTROY, we expect our relay (B) to forward
1080            // the DESTROY to C.
1081            assert_destroy_sent(&mut ctrl, DestroyReason::NONE, DestroyDirection::Forward);
1082        });
1083    }
1084
1085    #[traced_test]
1086    #[test]
1087    fn destroy_from_next_hop() {
1088        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
1089            let (mut ctrl, _incoming_streams) =
1090                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
1091            rt.advance_until_stalled().await;
1092
1093            // Extend the circuit by another hop
1094            let linkspecs = dummy_linkspecs();
1095            let handshake_type = HandshakeType::NTOR_V3;
1096            let extend2 = relaymsg::Extend2::new(linkspecs, handshake_type, vec![]).into();
1097            ctrl.send_fwd(None, extend2, Recognized::Yes, true).await;
1098            rt.advance_until_stalled().await;
1099            let circid = ctrl.do_create2_handshake(&rt, handshake_type).await;
1100            assert!(logs_contain("Extended circuit to the next hop"));
1101            assert!(ctrl.outbound_chan_launched());
1102
1103            // Simulate the next hop sending us a DESTROY cell
1104            let destroy = Destroy::new(DestroyReason::PROTOCOL);
1105            ctrl.write_outbound(circid, destroy.into());
1106            rt.advance_until_stalled().await;
1107
1108            // We have *not* received an outbound destroy
1109            assert!(!logs_contain(
1110                "Received outbound DESTROY, circuit shutting down"
1111            ));
1112
1113            // We received an inbound one (from the next hop)
1114            assert!(logs_contain(
1115                "Received inbound DESTROY, circuit shutting down"
1116            ));
1117
1118            // There is no next hop because we haven't extended the circuit,
1119            // so only expect the DESTROY to be sent toward the client (Backward).
1120            // This also ensures the destroy reason (PROTOCOL) is not propagated.
1121            assert_destroy_sent(&mut ctrl, DestroyReason::NONE, DestroyDirection::Backward);
1122        });
1123    }
1124
1125    #[traced_test]
1126    #[test]
1127    fn truncate() {
1128        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
1129            let (mut ctrl, _incoming_streams) =
1130                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
1131            rt.advance_until_stalled().await;
1132
1133            // Simulate the client sending us a TRUNCATE cell
1134            let truncate = relaymsg::Truncate::default().into();
1135            ctrl.send_fwd(None, truncate, Recognized::Yes, false).await;
1136            rt.advance_until_stalled().await;
1137
1138            assert!(logs_contain(
1139                "Circuit protocol violation: TRUNCATE not allowed"
1140            ));
1141
1142            // There is no next hop because we haven't extended the circuit,
1143            // so only expect the DESTROY to be sent toward the client (Backward).
1144            assert_destroy_sent(&mut ctrl, DestroyReason::NONE, DestroyDirection::Backward);
1145        });
1146    }
1147
1148    #[traced_test]
1149    #[test]
1150    fn data_stream() {
1151        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
1152            const TO_SEND: &[u8] = b"The bells were musical in the silvery sun";
1153
1154            let (mut ctrl, mut incoming_streams) =
1155                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
1156            rt.advance_until_stalled().await;
1157
1158            let begin = relaymsg::Begin::new("127.0.0.1", 1111, 0).unwrap().into();
1159            ctrl.send_fwd(StreamId::new(1), begin, Recognized::Yes, false)
1160                .await;
1161            rt.advance_until_stalled().await;
1162
1163            let data = relaymsg::Data::new(TO_SEND).unwrap().into();
1164            ctrl.send_fwd(StreamId::new(1), data, Recognized::Yes, false)
1165                .await;
1166
1167            // We should have a pending incoming stream
1168            let pending = incoming_streams.next().await.unwrap();
1169
1170            // Accept it, and let's see what we have!
1171            let mut stream = pending
1172                .accept_data(relaymsg::Connected::new_empty())
1173                .await
1174                .unwrap();
1175
1176            let mut recv_buf = [0_u8; TO_SEND.len()];
1177            stream.read_exact(&mut recv_buf).await.unwrap();
1178            assert_eq!(recv_buf, TO_SEND);
1179        });
1180    }
1181
1182    #[traced_test]
1183    #[test]
1184    fn reject_stream() {
1185        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
1186            let (mut ctrl, mut incoming_streams) =
1187                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
1188            rt.advance_until_stalled().await;
1189
1190            let begin = relaymsg::Begin::new("127.0.0.1", 1111, 0).unwrap().into();
1191            ctrl.send_fwd(StreamId::new(1), begin, Recognized::Yes, false)
1192                .await;
1193            rt.advance_until_stalled().await;
1194
1195            // We should have a pending incoming stream
1196            let pending = incoming_streams.next().await.unwrap();
1197
1198            // Reject the stream, and wait for the reactor to finish sending the END
1199            let end = relaymsg::End::new_misc();
1200            pending.reject(end.clone()).await.unwrap();
1201            rt.advance_until_stalled().await;
1202
1203            // The END cell written to the Tor channel should be the same as
1204            // the one we sent above, in reject().
1205            let cell = ctrl.read_inbound();
1206            let actual_end = expect_cell!(cell, Relay, End);
1207            assert_eq!(end.reason(), actual_end.reason());
1208
1209            // Sending another message on this stream results is flagged
1210            // as a proto violation
1211            let data = relaymsg::Data::new(b"no dice").unwrap().into();
1212            ctrl.send_fwd(StreamId::new(1), data, Recognized::Yes, false)
1213                .await;
1214            rt.advance_until_stalled().await;
1215
1216            assert!(logs_contain("Stream protocol violation"));
1217            assert!(logs_contain(
1218                "Unexpected RelayCmd(DATA) message on unknown stream 1"
1219            ));
1220        });
1221    }
1222
1223    #[traced_test]
1224    #[test]
1225    fn only_allow_begin_dir() {
1226        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
1227            let (mut ctrl, mut incoming_streams) = ReactorTestCtrl::spawn_reactor(
1228                &rt,
1229                // The stream reactor will only accept BEGIN_DIR streams
1230                &[RelayCmd::BEGIN_DIR],
1231            )
1232            .await;
1233            rt.advance_until_stalled().await;
1234
1235            // Directory streams should be allowed (because BEGIN_DIR is allowed)...
1236            let begin_dir = relaymsg::BeginDir::default().into();
1237            ctrl.send_fwd(StreamId::new(1), begin_dir, Recognized::Yes, false)
1238                .await;
1239            rt.advance_until_stalled().await;
1240
1241            let pending_dir_stream = incoming_streams.next().await.unwrap();
1242            assert!(matches!(
1243                pending_dir_stream.request(),
1244                IncomingStreamRequest::BeginDir(_)
1245            ));
1246
1247            let begin = relaymsg::Begin::new("127.0.0.1", 1111, 0).unwrap().into();
1248            ctrl.send_fwd(StreamId::new(2), begin, Recognized::Yes, false)
1249                .await;
1250            rt.advance_until_stalled().await;
1251
1252            // ... but the exit stream is not
1253            assert!(logs_contain("stream reactor shut down"));
1254            assert!(logs_contain(
1255                "Stream protocol violation: Unexpected BEGIN on incoming stream circ_uniq_id=Circ 8.17"
1256            ));
1257
1258            // The reactor won't create an IncomingStream,
1259            // because the stream request is rejected right away
1260            let mut noop_cx = Context::from_waker(Waker::noop());
1261            assert_eq!(
1262                incoming_streams.poll_next_unpin(&mut noop_cx).map(|_| ()),
1263                Poll::Pending
1264            );
1265        });
1266    }
1267
1268    #[traced_test]
1269    #[test]
1270    fn resolve_stream() {
1271        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
1272            let (mut ctrl, mut incoming_streams) =
1273                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::RESOLVE]).await;
1274            rt.advance_until_stalled().await;
1275
1276            let resolve = relaymsg::Resolve::new("example.com");
1277            let resolve_sid = StreamId::new(1337);
1278            ctrl.send_fwd(resolve_sid, resolve.into(), Recognized::Yes, false)
1279                .await;
1280            rt.advance_until_stalled().await;
1281
1282            // We should have a pending incoming stream
1283            let pending = incoming_streams.next().await.unwrap();
1284
1285            let mut resolved = relaymsg::Resolved::new_empty();
1286            let resolved_val = relaymsg::ResolvedVal::Ip(IpAddr::from([1, 2, 3, 4]));
1287            resolved.add_answer(resolved_val.clone(), 1337);
1288
1289            // We expect the client to receive this cell
1290            let expected_resolved = resolved.clone();
1291
1292            // Respond with RESOLVED
1293            pending.resolve(resolved).await.unwrap();
1294
1295            rt.advance_until_stalled().await;
1296            let (sid, resolved) = decode_relay_cell!(ctrl.read_inbound(), Resolved);
1297
1298            // Make sure the RESOLVED cell sent towards the client
1299            // matches what we sent via the IncomingStream::resolve() call above
1300            assert_eq!(resolved.into_answers(), expected_resolved.into_answers());
1301            assert_eq!(sid, resolve_sid);
1302            assert!(logs_contain("Ending stream"));
1303        });
1304    }
1305}