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

            
43
pub(crate) mod backward;
44
pub(crate) mod forward;
45

            
46
use std::sync::Arc;
47
use std::time::Duration;
48

            
49
use futures::StreamExt as _;
50
use futures::channel::mpsc;
51

            
52
use tor_cell::chancell::CircId;
53
use tor_cell::relaycell::RelayCmd;
54
use tor_linkspec::OwnedChanTarget;
55
use tor_memquota::mq_queue::{ChannelSpec, MpscSpec};
56
use tor_rtcompat::{DynTimeProvider, Runtime};
57

            
58
use crate::channel::Channel;
59
use crate::circuit::circhop::ReactorStreamComponents;
60
use crate::circuit::circhop::{CircHopOutbound, HopSettings};
61
use crate::circuit::reactor::Reactor as BaseReactor;
62
use crate::circuit::reactor::hop_mgr::HopMgr;
63
use crate::circuit::reactor::stream;
64
use crate::circuit::{CircuitRxReceiver, UniqId};
65
use crate::congestion::sendme::StreamRecvWindow;
66
use crate::crypto::cell::{InboundRelayLayer, OutboundRelayLayer};
67
use crate::memquota::{CircuitAccount, SpecificAccount};
68
use crate::relay::RelayCirc;
69
use crate::relay::channel_provider::ChannelProvider;
70
use crate::relay::reactor::backward::Backward;
71
use crate::relay::reactor::forward::Forward;
72
use crate::stream::flow_ctrl::state::WithSidechannelMitigations;
73
use crate::stream::flow_ctrl::xon_xoff::reader::XonXoffReaderCtrl;
74
use crate::stream::incoming::{
75
    IncomingCmdChecker, IncomingStream, IncomingStreamRequestFilter, IncomingStreamRequestHandler,
76
    StreamReqInfo,
77
};
78
use crate::stream::raw::StreamReceiver;
79
use 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.
82
use crate::client::circuit::padding::{PaddingController, PaddingEventStream};
83

            
84
/// Type-alias for the relay base reactor type.
85
type 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."]
89
pub(crate) struct Reactor<R: Runtime>(RelayBaseReactor<R>);
90

            
91
/// A handler customizing the relay stream reactor.
92
struct StreamHandler;
93

            
94
impl stream::StreamHandler for StreamHandler {
95
6
    fn halfstream_expiry(&self, hop: &CircHopOutbound) -> Duration {
96
6
        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
6
        ccontrol
106
6
            .lock()
107
6
            .expect("poisoned lock")
108
6
            .rtt()
109
6
            .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
6
            .unwrap_or_default()
113
6
    }
114

            
115
8
    fn flowctrl_sidechannel_mitigations(&self) -> WithSidechannelMitigations {
116
        // We're a relay, so we don't want sidechannel mitigations for flow control.
117
8
        WithSidechannelMitigations::Disabled
118
8
    }
119
}
120

            
121
impl<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
32
    pub(crate) fn new(
169
32
        runtime: R,
170
32
        channel: &Arc<Channel>,
171
32
        circ_id: CircId,
172
32
        unique_id: UniqId,
173
32
        input: CircuitRxReceiver,
174
32
        crypto_in: Box<dyn InboundRelayLayer + Send>,
175
32
        crypto_out: Box<dyn OutboundRelayLayer + Send>,
176
32
        settings: &HopSettings,
177
32
        chan_provider: Arc<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
178
32
        padding_ctrl: PaddingController,
179
32
        padding_event_stream: PaddingEventStream,
180
32
        incoming_filter: Box<dyn IncomingStreamRequestFilter>,
181
32
        allowed_stream_cmds: &[RelayCmd],
182
32
        memquota: &CircuitAccount,
183
32
    ) -> crate::Result<(
184
32
        Self,
185
32
        Arc<RelayCirc>,
186
32
        impl futures::Stream<Item = IncomingStream> + use<R>,
187
32
    )> {
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
32
        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
32
        let time_provider = DynTimeProvider::new(runtime.clone());
206
32
        let (incoming_sender, incoming_receiver) = MpscSpec::new(INCOMING_BUFFER)
207
32
            .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
32
        let cmd_checker = IncomingCmdChecker::new_any(allowed_stream_cmds);
215
32
        let incoming_handler = IncomingStreamRequestHandler {
216
32
            incoming_sender,
217
32
            hop_num: None,
218
32
            cmd_checker,
219
32
            filter: incoming_filter,
220
32
        };
221
32
        let mut hop_mgr = HopMgr::new_with_incoming_handler(
222
32
            runtime.clone(),
223
32
            unique_id,
224
32
            circ_id,
225
32
            StreamHandler,
226
32
            stream_tx,
227
32
            incoming_handler,
228
32
            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
32
        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
32
        let (fwd_ev_tx, fwd_ev_rx) = mpsc::channel(0);
241
32
        let forward = Forward::new(
242
32
            channel,
243
32
            circ_id,
244
32
            unique_id,
245
32
            crypto_out,
246
32
            chan_provider,
247
32
            fwd_ev_tx,
248
32
            memquota.clone(),
249
        );
250
32
        let backward = Backward::new(crypto_in);
251

            
252
32
        let (inner, handle) = BaseReactor::new(
253
32
            runtime,
254
32
            channel,
255
32
            circ_id,
256
32
            unique_id,
257
32
            input,
258
32
            forward,
259
32
            backward,
260
32
            hop_mgr,
261
32
            padding_ctrl,
262
32
            padding_event_stream,
263
32
            stream_rx,
264
32
            fwd_ev_rx,
265
32
            memquota,
266
32
        );
267

            
268
32
        let reactor = Self(inner);
269
32
        let handle = Arc::new(RelayCirc(handle));
270

            
271
        // Note: tunnel is a bit of a misnomer for relays
272
32
        let tunnel = Arc::clone(&handle);
273
        // TODO(relay): this is more or less copy-pasta from client code
274
32
        let stream = incoming_receiver.map(move |req_ctx| {
275
            let StreamReqInfo {
276
8
                req,
277
8
                stream_id,
278
8
                hop,
279
                stream_components:
280
                    ReactorStreamComponents {
281
8
                        stream_inbound_rx,
282
8
                        stream_outbound_tx,
283
8
                        rate_limit_rx,
284
8
                        drain_rate_request_rx,
285
                    },
286
8
                memquota,
287
8
                relay_cell_format,
288
8
            } = req_ctx;
289

            
290
            // There is no originating hop if we're a relay
291
8
            debug_assert!(hop.is_none());
292

            
293
8
            let target = StreamTarget {
294
8
                tunnel: Tunnel::Relay(Arc::clone(&tunnel)),
295
8
                tx: stream_outbound_tx,
296
8
                hop: None,
297
8
                stream_id,
298
8
                relay_cell_format,
299
8
                rate_limit_stream: rate_limit_rx,
300
8
            };
301

            
302
            // can be used to build a reader that supports XON/XOFF flow control
303
8
            let xon_xoff_reader_ctrl =
304
8
                XonXoffReaderCtrl::new(drain_rate_request_rx, target.clone());
305

            
306
8
            let reader = StreamReceiver {
307
8
                target: target.clone(),
308
8
                receiver: stream_inbound_rx,
309
8
                recv_window: StreamRecvWindow::new(RECV_WINDOW_INIT),
310
8
                ended: false,
311
8
            };
312

            
313
8
            let components = StreamComponents {
314
8
                stream_receiver: reader,
315
8
                target,
316
8
                memquota,
317
8
                xon_xoff_reader_ctrl,
318
8
            };
319

            
320
8
            IncomingStream::new(time_provider.clone(), req, components)
321
8
        });
322

            
323
32
        Ok((reactor, handle, stream))
324
32
    }
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
32
    pub(crate) async fn run(self) -> crate::Result<()> {
332
32
        self.0.run().await
333
32
    }
334
}
335

            
336
#[cfg(test)]
337
pub(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
        });
    }
    #[traced_test]
    #[test]
    fn forward_before_extend() {
        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
            let (mut ctrl, _incoming_streams) =
                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
            rt.advance_until_stalled().await;
            // Send an arbitrary unrecognized cell. The reactor should flag this as
            // a protocol violation, because we don't have an outbound channel to forward it on.
            let end = relaymsg::End::new_misc().into();
            ctrl.send_fwd(None, end, Recognized::No, true).await;
            rt.advance_until_stalled().await;
            assert!(logs_contain(
                "Asked to forward cell before the circuit was extended?!"
            ));
            // There is no next hop because we haven't extended the circuit,
            // so only expect the DESTROY to be sent toward the client (Backward).
            assert_destroy_sent(&mut ctrl, DestroyReason::NONE, DestroyDirection::Backward);
        });
    }
    #[traced_test]
    #[test]
    fn reject_invalid_begin() {
        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
            let (mut ctrl, _incoming_streams) =
                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
            rt.advance_until_stalled().await;
            let begin = relaymsg::Begin::new("127.0.0.1", 1111, 0).unwrap().into();
            // BEGIN cells *must* have a stream ID, so expect the reactor to reject this
            // and close the circuit
            ctrl.send_fwd(None, begin, Recognized::Yes, false).await;
            rt.advance_until_stalled().await;
            assert!(logs_contain(
                "Invalid stream ID [scrubbed] for relay command BEGIN"
            ));
            // There is no next hop because we haven't extended the circuit,
            // so only expect the DESTROY to be sent toward the client (Backward).
            assert_destroy_sent(&mut ctrl, DestroyReason::NONE, DestroyDirection::Backward);
        });
    }
    #[traced_test]
    #[test]
    fn destroy_from_client() {
        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
            let (mut ctrl, _incoming_streams) =
                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
            rt.advance_until_stalled().await;
            // Extend the circuit by another hop
            let linkspecs = dummy_linkspecs();
            let handshake_type = HandshakeType::NTOR_V3;
            let extend2 = relaymsg::Extend2::new(linkspecs, handshake_type, vec![]).into();
            ctrl.send_fwd(None, extend2, Recognized::Yes, true).await;
            rt.advance_until_stalled().await;
            let _circid = ctrl.do_create2_handshake(&rt, handshake_type).await;
            assert!(logs_contain("Extended circuit to the next hop"));
            assert!(ctrl.outbound_chan_launched());
            // Simulate the client sending us a DESTROY cell
            let destroy = Destroy::new(DestroyReason::PROTOCOL);
            ctrl.send_fwd_cmsg(destroy.into()).await;
            rt.advance_until_stalled().await;
            assert!(logs_contain(
                "Received outbound DESTROY, circuit shutting down"
            ));
            // Since this is a circuit of the form A -> B -> C,
            // and A sent us a DESTROY, we expect our relay (B) to forward
            // the DESTROY to C.
            assert_destroy_sent(&mut ctrl, DestroyReason::NONE, DestroyDirection::Forward);
        });
    }
    #[traced_test]
    #[test]
    fn destroy_from_next_hop() {
        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
            let (mut ctrl, _incoming_streams) =
                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
            rt.advance_until_stalled().await;
            // Extend the circuit by another hop
            let linkspecs = dummy_linkspecs();
            let handshake_type = HandshakeType::NTOR_V3;
            let extend2 = relaymsg::Extend2::new(linkspecs, handshake_type, vec![]).into();
            ctrl.send_fwd(None, extend2, Recognized::Yes, true).await;
            rt.advance_until_stalled().await;
            let circid = ctrl.do_create2_handshake(&rt, handshake_type).await;
            assert!(logs_contain("Extended circuit to the next hop"));
            assert!(ctrl.outbound_chan_launched());
            // Simulate the next hop sending us a DESTROY cell
            let destroy = Destroy::new(DestroyReason::PROTOCOL);
            ctrl.write_outbound(circid, destroy.into());
            rt.advance_until_stalled().await;
            // We have *not* received an outbound destroy
            assert!(!logs_contain(
                "Received outbound DESTROY, circuit shutting down"
            ));
            // We received an inbound one (from the next hop)
            assert!(logs_contain(
                "Received inbound DESTROY, circuit shutting down"
            ));
            // There is no next hop because we haven't extended the circuit,
            // so only expect the DESTROY to be sent toward the client (Backward).
            // This also ensures the destroy reason (PROTOCOL) is not propagated.
            assert_destroy_sent(&mut ctrl, DestroyReason::NONE, DestroyDirection::Backward);
        });
    }
    #[traced_test]
    #[test]
    fn truncate() {
        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
            let (mut ctrl, _incoming_streams) =
                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
            rt.advance_until_stalled().await;
            // Simulate the client sending us a TRUNCATE cell
            let truncate = relaymsg::Truncate::default().into();
            ctrl.send_fwd(None, truncate, Recognized::Yes, false).await;
            rt.advance_until_stalled().await;
            assert!(logs_contain(
                "Circuit protocol violation: TRUNCATE not allowed"
            ));
            // There is no next hop because we haven't extended the circuit,
            // so only expect the DESTROY to be sent toward the client (Backward).
            assert_destroy_sent(&mut ctrl, DestroyReason::NONE, DestroyDirection::Backward);
        });
    }
    #[traced_test]
    #[test]
    fn data_stream() {
        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
            const TO_SEND: &[u8] = b"The bells were musical in the silvery sun";
            let (mut ctrl, mut incoming_streams) =
                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
            rt.advance_until_stalled().await;
            let begin = relaymsg::Begin::new("127.0.0.1", 1111, 0).unwrap().into();
            ctrl.send_fwd(StreamId::new(1), begin, Recognized::Yes, false)
                .await;
            rt.advance_until_stalled().await;
            let data = relaymsg::Data::new(TO_SEND).unwrap().into();
            ctrl.send_fwd(StreamId::new(1), data, Recognized::Yes, false)
                .await;
            // We should have a pending incoming stream
            let pending = incoming_streams.next().await.unwrap();
            // Accept it, and let's see what we have!
            let mut stream = pending
                .accept_data(relaymsg::Connected::new_empty())
                .await
                .unwrap();
            let mut recv_buf = [0_u8; TO_SEND.len()];
            stream.read_exact(&mut recv_buf).await.unwrap();
            assert_eq!(recv_buf, TO_SEND);
        });
    }
    #[traced_test]
    #[test]
    fn reject_stream() {
        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
            let (mut ctrl, mut incoming_streams) =
                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
            rt.advance_until_stalled().await;
            let begin = relaymsg::Begin::new("127.0.0.1", 1111, 0).unwrap().into();
            ctrl.send_fwd(StreamId::new(1), begin, Recognized::Yes, false)
                .await;
            rt.advance_until_stalled().await;
            // We should have a pending incoming stream
            let pending = incoming_streams.next().await.unwrap();
            // Reject the stream, and wait for the reactor to finish sending the END
            let end = relaymsg::End::new_misc();
            pending.reject(end.clone()).await.unwrap();
            rt.advance_until_stalled().await;
            // The END cell written to the Tor channel should be the same as
            // the one we sent above, in reject().
            let cell = ctrl.read_inbound();
            let actual_end = expect_cell!(cell, Relay, End);
            assert_eq!(end.reason(), actual_end.reason());
            // Sending another message on this stream results is flagged
            // as a proto violation
            let data = relaymsg::Data::new(b"no dice").unwrap().into();
            ctrl.send_fwd(StreamId::new(1), data, Recognized::Yes, false)
                .await;
            rt.advance_until_stalled().await;
            assert!(logs_contain("Stream protocol violation"));
            assert!(logs_contain(
                "Unexpected RelayCmd(DATA) message on unknown stream 1"
            ));
        });
    }
    #[traced_test]
    #[test]
    fn only_allow_begin_dir() {
        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
            let (mut ctrl, mut incoming_streams) = ReactorTestCtrl::spawn_reactor(
                &rt,
                // The stream reactor will only accept BEGIN_DIR streams
                &[RelayCmd::BEGIN_DIR],
            )
            .await;
            rt.advance_until_stalled().await;
            // Directory streams should be allowed (because BEGIN_DIR is allowed)...
            let begin_dir = relaymsg::BeginDir::default().into();
            ctrl.send_fwd(StreamId::new(1), begin_dir, Recognized::Yes, false)
                .await;
            rt.advance_until_stalled().await;
            let pending_dir_stream = incoming_streams.next().await.unwrap();
            assert!(matches!(
                pending_dir_stream.request(),
                IncomingStreamRequest::BeginDir(_)
            ));
            let begin = relaymsg::Begin::new("127.0.0.1", 1111, 0).unwrap().into();
            ctrl.send_fwd(StreamId::new(2), begin, Recognized::Yes, false)
                .await;
            rt.advance_until_stalled().await;
            // ... but the exit stream is not
            assert!(logs_contain("stream reactor shut down"));
            assert!(logs_contain(
                "Stream protocol violation: Unexpected BEGIN on incoming stream circ_uniq_id=Circ 8.17"
            ));
            // The reactor won't create an IncomingStream,
            // because the stream request is rejected right away
            let mut noop_cx = Context::from_waker(Waker::noop());
            assert_eq!(
                incoming_streams.poll_next_unpin(&mut noop_cx).map(|_| ()),
                Poll::Pending
            );
        });
    }
    #[traced_test]
    #[test]
    fn resolve_stream() {
        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
            let (mut ctrl, mut incoming_streams) =
                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::RESOLVE]).await;
            rt.advance_until_stalled().await;
            let resolve = relaymsg::Resolve::new("example.com");
            let resolve_sid = StreamId::new(1337);
            ctrl.send_fwd(resolve_sid, resolve.into(), Recognized::Yes, false)
                .await;
            rt.advance_until_stalled().await;
            // We should have a pending incoming stream
            let pending = incoming_streams.next().await.unwrap();
            let mut resolved = relaymsg::Resolved::new_empty();
            let resolved_val = relaymsg::ResolvedVal::Ip(IpAddr::from([1, 2, 3, 4]));
            resolved.add_answer(resolved_val.clone(), 1337);
            // We expect the client to receive this cell
            let expected_resolved = resolved.clone();
            // Respond with RESOLVED
            pending.resolve(resolved).await.unwrap();
            rt.advance_until_stalled().await;
            let (sid, resolved) = decode_relay_cell!(ctrl.read_inbound(), Resolved);
            // Make sure the RESOLVED cell sent towards the client
            // matches what we sent via the IncomingStream::resolve() call above
            assert_eq!(resolved.into_answers(), expected_resolved.into_answers());
            assert_eq!(sid, resolve_sid);
            assert!(logs_contain("Ending stream"));
        });
    }
}