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
12
    fn halfstream_expiry(&self, hop: &CircHopOutbound) -> Duration {
96
12
        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
12
        ccontrol
106
12
            .lock()
107
12
            .expect("poisoned lock")
108
12
            .rtt()
109
12
            .max_rtt_usec()
110
12
            .map(|rtt| Duration::from_millis(u64::from(rtt)))
111
            // TODO(relay): we should fallback to a non-zero default here
112
            // if we don't have any RTT measurements yet
113
12
            .unwrap_or_default()
114
12
    }
115

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

            
122
impl<R: Runtime> Reactor<R> {
123
    /// Create a new circuit reactor.
124
    ///
125
    /// Returns the [`Reactor`], a [`RelayCirc`] handle to it,
126
    /// and a [`Stream`](futures::Stream) of `IncomingStream`s.
127
    ///
128
    /// The reactor will send outbound messages on `channel`, receive incoming
129
    /// messages on `input`, and identify this circuit by the channel-local
130
    /// [`CircId`] provided.
131
    ///
132
    /// The internal unique identifier for this circuit will be `unique_id`.
133
    ///
134
    /// The returned `IncomingStream`s are exit, dns, or directory streams.
135
    /// An incoming stream is automatically rejected by the reactor
136
    /// if the provided `IncomingStreamRequestFilter` rejects it.
137
    /// You can also explicitly reject a stream by calling [`IncomingStream::reject`].
138
    /// If the `Stream` is dropped, the next incoming stream request
139
    /// (`BEGIN`, `BEGIN_DIR`, or RESOLVE`)
140
    /// on this circuit will cause the stream reactor to shut down,
141
    /// which will trigger a shutdown of all the circuit reactors (FWD, BWD),
142
    /// which causing the circuit to close.
143
    ///
144
    /// The streams not rejected by the `IncomingStreamRequestFilter` will
145
    /// get an entry in the circuit's stream map.
146
    /// Rejecting such a stream using [`IncomingStream::reject`] will remove the entry.
147
    ///
148
    /// The `IncomingStreamRequestFilter` should only perform inexpensive checks
149
    /// that won't block the reactor.
150
    /// More expensive, or blocking checks, should be handled outside of the circuit reactor,
151
    /// when processing new `IncomingStream`s from the returned Rust stream.
152
    ///
153
    /// Data and directory streams can be accepted by calling [`IncomingStream::accept_data`].
154
    /// The caller is responsible for proxying data between the resulting `DataStream`
155
    /// and the local application stream.
156
    ///
157
    // TODO(relay): say how RESOLVE streams should be handled
158
    //
159
    // TODO: declare a type-alias for the impl futures::Stream return type
160
    // when support for impl in type aliases gets stabilized.
161
    //
162
    // See issue #63063 <https://github.com/rust-lang/rust/issues/63063>
163
    //
164
    // TODO(DEDUP): the incoming stream handling is *very* similar
165
    // to the impll from ServiceOnionServiceDataTunnel::allow_stream_requests.
166
    // We should dedupe these someday, when we rewrite the client reactor
167
    // to use the new multi-reactor architecture
168
    #[allow(clippy::too_many_arguments)] // TODO
169
64
    pub(crate) fn new(
170
64
        runtime: R,
171
64
        channel: &Arc<Channel>,
172
64
        circ_id: CircId,
173
64
        unique_id: UniqId,
174
64
        input: CircuitRxReceiver,
175
64
        crypto_in: Box<dyn InboundRelayLayer + Send>,
176
64
        crypto_out: Box<dyn OutboundRelayLayer + Send>,
177
64
        settings: &HopSettings,
178
64
        chan_provider: Arc<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
179
64
        padding_ctrl: PaddingController,
180
64
        padding_event_stream: PaddingEventStream,
181
64
        incoming_filter: Box<dyn IncomingStreamRequestFilter>,
182
64
        allowed_stream_cmds: &[RelayCmd],
183
64
        memquota: &CircuitAccount,
184
64
    ) -> crate::Result<(
185
64
        Self,
186
64
        Arc<RelayCirc>,
187
64
        impl futures::Stream<Item = IncomingStream> + use<R>,
188
64
    )> {
189
        // NOTE: not registering this channel with the memquota subsystem is okay,
190
        // because it has no buffering (if ever decide to make the size of this buffer
191
        // non-zero for whatever reason, we must remember to register it with memquota
192
        // so that it counts towards the total memory usage for the circuit.
193
        #[allow(clippy::disallowed_methods)]
194
64
        let (stream_tx, stream_rx) = mpsc::channel(0);
195

            
196
        /// The size of the channel receiving IncomingStreamRequestContexts.
197
        ///
198
        // TODO(relay-tuning): buffer size
199
        //
200
        // This is currently set to 2x the initial receive window,
201
        // the same as the buffer size we use for onion services.
202
        // This value was picked arbitrarily,
203
        // and is not necessarily tuned for relay needs.
204
        const INCOMING_BUFFER: usize = crate::stream::STREAM_READER_BUFFER;
205

            
206
64
        let time_provider = DynTimeProvider::new(runtime.clone());
207
64
        let (incoming_sender, incoming_receiver) = MpscSpec::new(INCOMING_BUFFER)
208
64
            .new_mq(time_provider.clone(), memquota.as_raw_account())?;
209

            
210
        // Our IncomingCmdChecker does not reject BEGIN, BEGIN_DIR, RESOLVE cells,
211
        // but that doesn't necessarily mean the stream will be accepted.
212
        // An incoming stream can still be rejected at a later stage,
213
        // by the IncomingStreamRequestFilter, or directly by the consumer of the
214
        // futures::Stream<Item = IncomingStream> (by calling IncomingStream::reject()).
215
64
        let cmd_checker = IncomingCmdChecker::new_any(allowed_stream_cmds);
216
64
        let incoming_handler = IncomingStreamRequestHandler {
217
64
            incoming_sender,
218
64
            hop_num: None,
219
64
            cmd_checker,
220
64
            filter: incoming_filter,
221
64
        };
222
64
        let mut hop_mgr = HopMgr::new_with_incoming_handler(
223
64
            runtime.clone(),
224
64
            unique_id,
225
64
            circ_id,
226
64
            StreamHandler,
227
64
            stream_tx,
228
64
            incoming_handler,
229
64
            memquota.clone(),
230
        );
231

            
232
        // On the relay side, we always have one "hop" (ourselves).
233
        //
234
        // Clients will need to call this function in response to CtrlMsg::Create
235
        // (TODO: for clients, we probably will need to store a bunch more state here)
236
64
        hop_mgr.add_hop(settings.clone())?;
237

            
238
        // TODO(relay): currently we don't need buffering on this channel,
239
        // but we might need it if we start using it for more than just EXTENDED2 events
240
        #[allow(clippy::disallowed_methods)]
241
64
        let (fwd_ev_tx, fwd_ev_rx) = mpsc::channel(0);
242
64
        let forward = Forward::new(
243
64
            channel,
244
64
            circ_id,
245
64
            unique_id,
246
64
            crypto_out,
247
64
            chan_provider,
248
64
            fwd_ev_tx,
249
64
            memquota.clone(),
250
        );
251
64
        let backward = Backward::new(crypto_in);
252

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

            
269
64
        let reactor = Self(inner);
270
64
        let handle = Arc::new(RelayCirc(handle));
271

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

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

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

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

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

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

            
321
16
            IncomingStream::new(time_provider.clone(), req, components)
322
16
        });
323

            
324
64
        Ok((reactor, handle, stream))
325
64
    }
326

            
327
    /// Launch the reactor, and run until the circuit closes or we
328
    /// encounter an error.
329
    ///
330
    /// Once this method returns, the circuit is dead and cannot be
331
    /// used again.
332
64
    pub(crate) async fn run(self) -> crate::Result<()> {
333
64
        self.0.run().await
334
64
    }
335
}
336

            
337
#[cfg(test)]
338
pub(crate) mod test {
339
    // @@ begin test lint list maintained by maint/add_warning @@
340
    #![allow(clippy::bool_assert_comparison)]
341
    #![allow(clippy::clone_on_copy)]
342
    #![allow(clippy::dbg_macro)]
343
    #![allow(clippy::mixed_attributes_style)]
344
    #![allow(clippy::print_stderr)]
345
    #![allow(clippy::print_stdout)]
346
    #![allow(clippy::single_char_pattern)]
347
    #![allow(clippy::unwrap_used)]
348
    #![allow(clippy::unchecked_time_subtraction)]
349
    #![allow(clippy::useless_vec)]
350
    #![allow(clippy::needless_pass_by_value)]
351
    #![allow(clippy::string_slice)] // See arti#2571
352
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
353

            
354
    use super::*;
355
    use crate::channel::ChannelMode;
356
    use crate::channel::CtrlMsg;
357
    use crate::channel::circmap::CircIdRange;
358
    use crate::channel::test_utils::DummyChan;
359
    use crate::circuit::CircParameters;
360
    use crate::circuit::circ_sender;
361
    use crate::circuit::reactor::test::{AllowAllStreamsFilter, rmsg_to_ccmsg};
362
    use crate::circuit::test::new_circ_net_params;
363
    use crate::client::circuit::padding::new_padding;
364
    use crate::congestion::test_utils::params::build_cc_vegas_params;
365
    use crate::crypto::cell::RelayCellBody;
366
    use crate::crypto::cell::{InboundRelayLayer, OutboundRelayLayer};
367
    use crate::relay::CreateRequestHandler;
368
    use crate::relay::channel::test::DummyChanProvider;
369
    use crate::stream::flow_ctrl::params::FlowCtrlParameters;
370
    use crate::stream::incoming::{IncomingStream, IncomingStreamRequest, NoOpRequestFilter};
371

            
372
    use futures::AsyncReadExt as _;
373
    use futures::SinkExt as _;
374
    use oneshot_fused_workaround as oneshot;
375
    use tracing_test::traced_test;
376

            
377
    use tor_basic_utils::test_rng::{TestingRng, testing_rng};
378
    use tor_cell::chancell::{ChanCell, ChanCmd, msg as chanmsg};
379
    use tor_cell::relaycell::{AnyRelayMsgOuter, RelayCellFormat, StreamId, msg as relaymsg};
380
    use tor_key_forge::Keygen;
381
    use tor_linkspec::{EncodedLinkSpec, HasRelayIds, LinkSpec};
382
    use tor_llcrypto::pk::curve25519::StaticKeypair;
383
    use tor_llcrypto::pk::ed25519::Ed25519Identity;
384
    use tor_llcrypto::pk::rsa::RsaIdentity;
385
    use tor_llcrypto::rng::FakeEntropicRng;
386
    use tor_protover::{Protocols, named};
387
    use tor_relay_crypto::pk::RelayNtorKeys;
388
    use tor_rtcompat::SpawnExt;
389
    use tor_rtcompat::{DynTimeProvider, Runtime};
390
    use tor_rtmock::MockRuntime;
391

            
392
    use chanmsg::{AnyChanMsg, Destroy, DestroyReason, HandshakeType};
393
    use relaymsg::SendmeTag;
394

            
395
    use std::net::IpAddr;
396
    use std::sync::{Arc, Mutex, Weak, mpsc};
397
    use std::task::{Context, Poll, Waker};
398

            
399
    // An inbound encryption layer that doesn't do any crypto.
400
    struct DummyInboundCrypto {}
401

            
402
    // An outbound encryption layer that doesn't do any crypto.
403
    struct DummyOutboundCrypto {
404
        /// Channel for controlling whether the current cell is meant for us or not.
405
        ///
406
        /// Useful for tests that check if recognized/unrecognized
407
        /// cells are handled/forwarded correctly.
408
        recognized_rx: mpsc::Receiver<Recognized>,
409
    }
410

            
411
    const DUMMY_TAG: [u8; 20] = [1; 20];
412

            
413
    impl InboundRelayLayer for DummyInboundCrypto {
414
        fn originate(&mut self, _cmd: ChanCmd, _cell: &mut RelayCellBody) -> SendmeTag {
415
            DUMMY_TAG.into()
416
        }
417

            
418
        fn encrypt_inbound(&mut self, _cmd: ChanCmd, _cell: &mut RelayCellBody) {}
419
    }
420

            
421
    impl OutboundRelayLayer for DummyOutboundCrypto {
422
        fn decrypt_outbound(
423
            &mut self,
424
            _cmd: ChanCmd,
425
            _cell: &mut RelayCellBody,
426
        ) -> Option<SendmeTag> {
427
            // Note: this should never block.
428
            let recognized = self.recognized_rx.recv().unwrap();
429

            
430
            match recognized {
431
                Recognized::Yes => Some(DUMMY_TAG.into()),
432
                Recognized::No => None,
433
            }
434
        }
435
    }
436

            
437
    /// A circuit reactor handle, for building circuits of the form
438
    /// A -> B, and A -> B -> C, where the circuit reactor under test
439
    /// "thinks" it is B.
440
    ///
441
    /// [`ReactorTestCtrl::new`] builds and spawns:
442
    ///
443
    ///   * a channel reactor for the A - B "Tor Channel"
444
    ///   * a circuit reactor for B's view of the circuit
445
    ///
446
    /// Some of the tests in this module extend the circuit by another dummy hop,
447
    /// to obtain an A -> B -> C circuit. This involves sending an EXTEND2
448
    /// cell over the A -> B channel, and calling [`ReactorTestCtrl::do_create2_handshake`]
449
    /// to finalize the handshake.
450
    struct ReactorTestCtrl {
451
        /// The relay circuit handle.
452
        relay_circ: Arc<RelayCirc>,
453
        /// The circuit id on our `inbound_chan`.
454
        circid: CircId,
455
        /// The inbound channel ("towards the client").
456
        ///
457
        /// This is the "Tor channel" between A and B in
458
        /// a circuit of the form A -> B or A -> B -> C.
459
        inbound_chan: DummyChan,
460
        /// The outbound channel ("away from the client"), if any.
461
        ///
462
        /// Shared with the DummyChanProvider, which initializes this
463
        /// when the relay reactor launches a channel to the next hop
464
        /// via `get_or_launch()`.
465
        ///
466
        /// This is the "Tor channel" between B and C,
467
        /// if our test circuit is of the form A -> B -> C
468
        /// (i.e. if we have extended the "base" circuit by another mock hop, to C).
469
        outbound_chan: Arc<Mutex<Option<DummyChan>>>,
470
        /// MPSC channel for telling the DummyOutboundCrypto that the next
471
        /// cell we're about to send to the reactor should be "recognized".
472
        recognized_tx: mpsc::Sender<Recognized>,
473
    }
474

            
475
    /// Whether a forward cell to send should be "recognized"
476
    /// or "unrecognized" by the relay under test.
477
    enum Recognized {
478
        /// Recognized
479
        Yes,
480
        /// Unrecognized
481
        No,
482
    }
483

            
484
    /// The direction we expect the reactor to have sent a DESTROY in
485
    #[allow(dead_code)] // we don't use all of these yet
486
    enum DestroyDirection {
487
        /// Forward ("towards the exit")
488
        Forward,
489
        /// Backward ("towards the client")
490
        Backward,
491
        /// Both forward and backward
492
        Both,
493
    }
494

            
495
    /// Decode a cell, extracting the underlying message of type `expect_msg`
496
    macro_rules! decode_relay_cell {
497
        ($cell:expr, $expect_msg:tt) => {{
498
            let rmsg = match $cell.msg() {
499
                chanmsg::AnyChanMsg::Relay(r) => AnyRelayMsgOuter::decode_singleton(
500
                    RelayCellFormat::V0,
501
                    r.clone().into_relay_body(),
502
                )
503
                .unwrap(),
504
                msg => panic!("unexpected forwarded {msg:?}"),
505
            };
506

            
507
            let msg = match rmsg.msg() {
508
                relaymsg::AnyRelayMsg::$expect_msg(inner) => inner.clone(),
509
                _ => panic!("unexpected relay message {rmsg:?}"),
510
            };
511

            
512
            (rmsg.stream_id(), msg)
513
        }};
514
    }
515

            
516
    const DUMMY_ED25519_KEY: [u8; 32] = *b"32 bytes pretending to be a key!";
517
    const DUMMY_RSA_KEY: [u8; 20] = *b"not really an RSA ky";
518

            
519
    /// Helper for building a [`ChannelMode::Relay`] for our test reactor
520
    fn build_channel_mode<R: Runtime>(
521
        chan_provider: Arc<DummyChanProvider<R>>,
522
        allowed_stream_cmds: &[RelayCmd],
523
    ) -> ChannelMode {
524
        let our_ed25519_id = Ed25519Identity::from_bytes(&DUMMY_ED25519_KEY).unwrap();
525
        let our_rsa_id = RsaIdentity::from_bytes(&DUMMY_RSA_KEY).unwrap();
526

            
527
        let mut rng = FakeEntropicRng::<TestingRng>(testing_rng());
528
        let relay_ntor_keys = StaticKeypair::generate(&mut rng).unwrap();
529

            
530
        // A handler that will process CREATE* requests on channels
531
        //
532
        // Note: in practice, this won't actually be used at all,
533
        // because for the purposes of these tests, the circuit reactor is spawned manually,
534
        // by ReactorTestCtrl::new(), which also hackily initializes the channel's circuit map
535
        // with a circuit entry for it.
536
        //
537
        // This should be fine for now, but we might want to rethink it in the future
538
        // (i.e. we might want to let the channel reactor spawn the circuit reactor under test,
539
        // in response to CREATE*).
540
        let (create_request_handler, _circuit_stream_rx) = CreateRequestHandler::new(
541
            Arc::downgrade(&chan_provider) as Weak<_>,
542
            new_circ_net_params(),
543
            RelayNtorKeys::new(relay_ntor_keys.into()),
544
            // Don't filter any stream requests.
545
            Box::new(|| Box::new(NoOpRequestFilter) as Box<_>),
546
            allowed_stream_cmds,
547
        );
548
        let create_request_handler = Arc::new(create_request_handler);
549

            
550
        ChannelMode::Relay {
551
            create_request_handler,
552
            our_ed25519_id,
553
            our_rsa_id,
554
            // This doesn't actually matter for these tests
555
            circ_id_range: CircIdRange::Low,
556
        }
557
    }
558

            
559
    /// Prepare our "inbound" channel,
560
    ///
561
    /// > Note: the concept of an "inbound" channel only really makes sense
562
    /// > if you think about it from a circuit perspective:
563
    /// > these tests essentially simulate circuits of the form A -> B
564
    /// > and A -> B -> C. The relay circuit reactor under test "thinks" it's relay B,
565
    /// > and its "inbound" and "outbound" channels are the A -> B and B -> C channels,
566
    /// > respectively.
567
    ///
568
    /// This spawns a channel reactor and creates a fake circuit entry in it,
569
    /// which is wired up to the circuit Reactor under test by [`ReactorTestCtrl::new`].
570
    async fn prepare_inbound_chan<R: Runtime>(
571
        rt: &R,
572
        mode: ChannelMode,
573
    ) -> (CircId, CircuitRxReceiver, DummyChan) {
574
        let mut inbound_chan = DummyChan::run(rt, mode);
575

            
576
        let memquota = CircuitAccount::new_noop();
577
        let time_provider = DynTimeProvider::new(rt.clone());
578

            
579
        let (sender, receiver) = MpscSpec::new(128)
580
            .new_mq(time_provider, memquota.as_raw_account())
581
            .unwrap();
582
        let (sender, receiver) = circ_sender::channel(sender, receiver);
583
        let (created_sender, created_receiver) = oneshot::channel();
584

            
585
        let (tx, rx) = oneshot::channel();
586

            
587
        // Note: we need to make sure the circuit is in the channel reactor's
588
        // circuit map, because otherwise we can't test the DESTROY behavior,
589
        // (the channel reactor conditionally sends DESTROY based on whether
590
        // the circuit entry is still in the circmap or not;
591
        // the presence of a circuit in the circmap is a proxy for
592
        // whether we have sent a DESTROY ourselves or not).
593
        inbound_chan
594
            .channel
595
            .send_control(CtrlMsg::AllocateCircuit {
596
                created_sender,
597
                sender,
598
                tx,
599
            })
600
            .unwrap();
601
        let (circid, _circ_unique_id, _padding_ctrl, _padding_stream) = rx.await.unwrap().unwrap();
602

            
603
        // Hack: AllocateCircuit puts the circuit in the "Opening" state,
604
        // but in order to actually be able to send anything on this channel,
605
        // we need to advance it to "Open". We do that by sending a CREATED2 cell on the channel,
606
        // which is nonsensical from the perspective of the relay-specific test setup
607
        // (it would make sense if this was a client channel, however).
608
        // Alas, it is the only way we can advance the circuit's state to "Open"
609
        // in the channel's circmap without introducing a test-only CtrlMsg for this,
610
        // or without surrendering the circuit Reactor setup to the channel impl
611
        // (the latter might not be so bad actually, because it would be closer to what
612
        // happens in reality).
613
        let handshake = vec![];
614
        let created2 = chanmsg::Created2::new(handshake.clone());
615
        let cell = ChanCell::new(Some(circid), created2.into());
616
        inbound_chan.tx.try_send(Ok(cell)).unwrap();
617

            
618
        // We **have** to read the CREATED2 (otherwise the channel reactor shuts down with an error)
619
        let _ = created_receiver.await;
620

            
621
        (circid, receiver, inbound_chan)
622
    }
623

            
624
    impl ReactorTestCtrl {
625
        /// Spawn a relay circuit reactor, returning a `ReactorTestCtrl` for
626
        /// controlling it.
627
        async fn spawn_reactor<R: Runtime>(
628
            rt: &R,
629
            allowed_stream_cmds: &[RelayCmd],
630
        ) -> (Self, impl futures::Stream<Item = IncomingStream>) {
631
            let outbound_chan = Arc::new(Mutex::new(None));
632
            let chan_provider = Arc::new(DummyChanProvider::new(
633
                rt.clone(),
634
                Arc::clone(&outbound_chan),
635
            ));
636

            
637
            let mode = build_channel_mode(Arc::clone(&chan_provider), allowed_stream_cmds);
638
            let (circid, receiver, inbound_chan) = prepare_inbound_chan(rt, mode).await;
639

            
640
            let unique_id = UniqId::new(8, 17);
641
            let (padding_ctrl, padding_stream) = new_padding(DynTimeProvider::new(rt.clone()));
642
            let params = CircParameters::new(
643
                true,
644
                build_cc_vegas_params(),
645
                FlowCtrlParameters::defaults_for_tests(),
646
            );
647
            let settings = HopSettings::from_params_and_caps(
648
                crate::circuit::circhop::HopNegotiationType::Full,
649
                &params,
650
                &[named::FLOWCTRL_CC].into_iter().collect::<Protocols>(),
651
            )
652
            .unwrap();
653

            
654
            let (recognized_tx, recognized_rx) = mpsc::channel();
655
            let (reactor, relay_circ, incoming_streams) = Reactor::new(
656
                rt.clone(),
657
                &Arc::clone(&inbound_chan.channel),
658
                circid,
659
                unique_id,
660
                receiver,
661
                Box::new(DummyInboundCrypto {}),
662
                Box::new(DummyOutboundCrypto { recognized_rx }),
663
                &settings,
664
                chan_provider,
665
                padding_ctrl,
666
                padding_stream,
667
                Box::new(AllowAllStreamsFilter),
668
                allowed_stream_cmds,
669
                &CircuitAccount::new_noop(),
670
            )
671
            .unwrap();
672

            
673
            rt.spawn(async {
674
                let _ = reactor.run().await;
675
            })
676
            .unwrap();
677

            
678
            let ctrl = Self {
679
                relay_circ,
680
                circid,
681
                recognized_tx,
682
                inbound_chan,
683
                outbound_chan,
684
            };
685

            
686
            (ctrl, incoming_streams)
687
        }
688

            
689
        /// Simulate the sending of a forward relay message through our relay.
690
        async fn send_fwd(
691
            &mut self,
692
            id: Option<StreamId>,
693
            msg: relaymsg::AnyRelayMsg,
694
            recognized: Recognized,
695
            early: bool,
696
        ) {
697
            // This a bit janky, but for each forward cell we send to the reactor
698
            // we need to send a bit of metadata to the DummyOutboundLayer
699
            // specifying whether the cell should be treated as recognized
700
            // or unrecognized
701
            self.recognized_tx.send(recognized).unwrap();
702
            self.send_fwd_cmsg(rmsg_to_ccmsg(id, msg, early)).await;
703
        }
704

            
705
        /// Simulate the sending of a forward channel message through our relay.
706
        async fn send_fwd_cmsg(&mut self, msg: chanmsg::AnyChanMsg) {
707
            let cell = ChanCell::new(Some(self.circid), msg);
708
            self.inbound_chan.tx.send(Ok(cell)).await.unwrap();
709
        }
710

            
711
        /// Whether the reactor opened an outbound channel
712
        /// (i.e. a channel to the next relay in the circuit).
713
        fn outbound_chan_launched(&self) -> bool {
714
            self.outbound_chan.lock().unwrap().is_some()
715
        }
716

            
717
        /// Perform the CREATE2 handshake.
718
        async fn do_create2_handshake(
719
            &mut self,
720
            rt: &MockRuntime,
721
            expected_hs_type: HandshakeType,
722
        ) -> Option<CircId> {
723
            // First, check that the reactor actually sent a CREATE2 to the next hop...
724
            let (circid, msg) = self.read_outbound().into_circid_and_msg();
725
            let _create2 = match msg {
726
                chanmsg::AnyChanMsg::Create2(c) => {
727
                    assert_eq!(c.handshake_type(), expected_hs_type);
728
                    c
729
                }
730
                _ => panic!("unexpected forwarded {msg:?}"),
731
            };
732

            
733
            let handshake = vec![];
734
            let created2 = chanmsg::Created2::new(handshake.clone());
735
            // ...and then finalize the handshake by pretending to be
736
            // the responding relay
737
            self.write_outbound(circid, chanmsg::AnyChanMsg::Created2(created2));
738
            rt.advance_until_stalled().await;
739

            
740
            // Make sure we actually did send an EXTENDED2 towards the client
741
            let msg = self.read_inbound();
742

            
743
            let (_sid, e) = decode_relay_cell!(msg, Extended2);
744
            assert_eq!(e.clone().into_body(), handshake);
745

            
746
            circid
747
        }
748

            
749
        /// Whether the circuit is closing (e.g. due to a proto violation).
750
        fn is_closing(&self) -> bool {
751
            self.relay_circ.is_closing()
752
        }
753

            
754
        /// Read a cell from the inbound channel
755
        /// (moving towards the client).
756
        ///
757
        /// See [`try_read_inbound`](Self::try_read_inbound).
758
        ///
759
        /// Panics if there are no ready cells on the inbound MPSC channel.
760
        fn read_inbound(&mut self) -> ChanCell<AnyChanMsg> {
761
            self.try_read_inbound().unwrap()
762
        }
763

            
764
        /// Try to read a cell from the inbound channel
765
        /// (moving towards the client).
766
        ///
767
        /// For example, for a circuit of the form A -> B -> C,
768
        /// where B is the relay whose circuit reactor we're testing,
769
        /// this function reads a channel message on the A <-> B channel,
770
        /// from the perspective of A (i.e. it reads a channel message sent by B).
771
        ///
772
        /// Returns None if there are no ready cells on the inbound MPSC channel.
773
        fn try_read_inbound(&mut self) -> Option<ChanCell<AnyChanMsg>> {
774
            #[allow(deprecated)] // TODO(#2386)
775
            self.inbound_chan.rx.try_next().ok().flatten()
776
        }
777

            
778
        /// Read a cell from the outbound channel
779
        /// (moving towards the next hop).
780
        ///
781
        /// See [`try_read_outbound`](Self::try_read_outbound).
782
        ///
783
        /// Panics if there are no ready cells on the outbound MPSC channel,
784
        /// or if there is no outbound channel.
785
        fn read_outbound(&mut self) -> ChanCell<AnyChanMsg> {
786
            self.try_read_outbound().unwrap()
787
        }
788

            
789
        /// Read a cell from the outbound channel
790
        /// (moving towards the next hop).
791
        ///
792
        /// For example, for a circuit of the form A -> B -> C,
793
        /// where B is the relay whose circuit reactor we're testing,
794
        /// this function reads a channel message on the B <-> C channel,
795
        /// from the perspective of C (i.e. it reads a channel message sent by B).
796
        ///
797
        /// Returns None if there are no ready cells on the outbound MPSC channel,
798
        /// or if there is no outbound channel.
799
        fn try_read_outbound(&mut self) -> Option<ChanCell<AnyChanMsg>> {
800
            let mut lock = self.outbound_chan.lock().unwrap();
801
            let chan = lock.as_mut()?;
802
            #[allow(deprecated)] // TODO(#2386)
803
            chan.rx.try_next().ok().flatten()
804
        }
805

            
806
        /// Write to the sending end of the outbound Tor channel.
807
        ///
808
        /// Simulates the receipt of a cell from the next hop.
809
        ///
810
        /// Panics if the outbound chan sender is full.
811
        fn write_outbound(&mut self, circid: Option<CircId>, msg: chanmsg::AnyChanMsg) {
812
            let mut lock = self.outbound_chan.lock().unwrap();
813
            let chan = lock.as_mut().unwrap();
814
            let cell = ChanCell::new(circid, msg);
815

            
816
            chan.tx.try_send(Ok(cell)).unwrap();
817
        }
818
    }
819

            
820
    fn dummy_linkspecs() -> Vec<EncodedLinkSpec> {
821
        vec![
822
            LinkSpec::Ed25519Id([43; 32].into()).encode().unwrap(),
823
            LinkSpec::RsaId([45; 20].into()).encode().unwrap(),
824
            LinkSpec::OrPort("127.0.0.1".parse::<IpAddr>().unwrap(), 999)
825
                .encode()
826
                .unwrap(),
827
        ]
828
    }
829

            
830
    macro_rules! assert_cell_is_destroy {
831
        ($cell:expr, $reason:expr) => {{
832
            match $cell.msg() {
833
                chanmsg::AnyChanMsg::Destroy(d) => {
834
                    assert_eq!(d.reason(), $reason);
835
                }
836
                _ => panic!("unexpected ending {:?}", $cell),
837
            }
838
        }};
839
    }
840

            
841
    /// Assert that we have sent a DESTROY cell with the specified `reason`
842
    /// towards the "client" and/or the "next hop".
843
    ///
844
    /// The test is expected to drain the inbound Tor "channel"
845
    /// of any non-ending cells it might be expecting before calling this function.
846
    fn assert_destroy_sent(
847
        ctrl: &mut ReactorTestCtrl,
848
        reason: DestroyReason,
849
        direction: DestroyDirection,
850
    ) {
851
        assert!(ctrl.is_closing());
852

            
853
        match direction {
854
            DestroyDirection::Backward => {
855
                assert_cell_is_destroy!(ctrl.read_inbound(), reason);
856
                assert!(ctrl.try_read_outbound().is_none());
857
            }
858
            DestroyDirection::Forward => {
859
                assert_cell_is_destroy!(ctrl.read_outbound(), reason);
860
                assert!(ctrl.try_read_inbound().is_none());
861
            }
862
            DestroyDirection::Both => {
863
                assert_cell_is_destroy!(ctrl.read_inbound(), reason);
864
                assert_cell_is_destroy!(ctrl.read_outbound(), reason);
865
            }
866
        }
867
    }
868

            
869
    macro_rules! expect_cell {
870
        ($cell:expr, $chanmsg:tt, $relaymsg:tt) => {{
871
            let msg = match $cell.msg() {
872
                chanmsg::AnyChanMsg::$chanmsg(m) => {
873
                    let body = m.clone().into_relay_body();
874
                    AnyRelayMsgOuter::decode_singleton(RelayCellFormat::V0, body).unwrap()
875
                }
876
                _ => panic!("unexpected forwarded {:?}", $cell),
877
            };
878

            
879
            match msg.msg() {
880
                relaymsg::AnyRelayMsg::$relaymsg(m) => m.clone(),
881
                _ => panic!("unexpected cell {msg:?}"),
882
            }
883
        }};
884
    }
885

            
886
    #[traced_test]
887
    #[test]
888
    fn reject_extend2_relay() {
889
        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
890
            let (mut ctrl, _incoming_streams) =
891
                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
892
            rt.advance_until_stalled().await;
893

            
894
            let linkspecs = dummy_linkspecs();
895
            let extend2 = relaymsg::Extend2::new(linkspecs, HandshakeType::NTOR_V3, vec![]).into();
896
            ctrl.send_fwd(None, extend2, Recognized::Yes, false).await;
897
            rt.advance_until_stalled().await;
898

            
899
            assert!(logs_contain("got EXTEND2 in a RELAY cell?!"));
900
            assert!(!ctrl.outbound_chan_launched());
901

            
902
            // There is no next hop because we haven't extended the circuit,
903
            // so only expect the DESTROY to be sent toward the client (Backward).
904
            assert_destroy_sent(&mut ctrl, DestroyReason::NONE, DestroyDirection::Backward);
905
        });
906
    }
907

            
908
    #[traced_test]
909
    #[test]
910
    fn reject_extend2_previous_hop() {
911
        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
912
            let (mut ctrl, _incoming_streams) =
913
                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
914
            rt.advance_until_stalled().await;
915

            
916
            // No outbound circuits yet
917
            assert!(!ctrl.outbound_chan_launched());
918

            
919
            // Build a linkspec with the identities of the dummy channel
920
            let mut linkspecs = ctrl
921
                .inbound_chan
922
                .channel
923
                .target()
924
                .identities()
925
                .map(|id| LinkSpec::from(id.to_owned()).encode())
926
                .collect::<Result<Vec<_>, _>>()
927
                .unwrap();
928

            
929
            // Make sure this channel actually has some identities
930
            // (i.e. that it's not a client channel or something)
931
            assert_eq!(linkspecs.len(), 2);
932

            
933
            // There must be at least one IPv4 OR port address
934
            linkspecs.push(
935
                LinkSpec::OrPort("127.0.0.1".parse::<IpAddr>().unwrap(), 999)
936
                    .encode()
937
                    .unwrap(),
938
            );
939
            let handshake_type = HandshakeType::NTOR_V3;
940
            let extend2 = relaymsg::Extend2::new(linkspecs, handshake_type, vec![]).into();
941
            ctrl.send_fwd(None, extend2, Recognized::Yes, true).await;
942
            rt.advance_until_stalled().await;
943

            
944
            // The reactor handled the EXTEND2 and launched an outbound channel
945
            assert!(logs_contain("Cannot extend circuit to previous hop"));
946
            assert!(!ctrl.outbound_chan_launched());
947
            assert!(ctrl.is_closing());
948
        });
949
    }
950

            
951
    #[traced_test]
952
    #[test]
953
    fn extend_and_forward() {
954
        tor_rtmock::MockRuntime::test_with_various(|rt| async move {
955
            let (mut ctrl, _incoming_streams) =
956
                ReactorTestCtrl::spawn_reactor(&rt, &[RelayCmd::BEGIN]).await;
957
            rt.advance_until_stalled().await;
958

            
959
            // No outbound circuits yet
960
            assert!(!ctrl.outbound_chan_launched());
961

            
962
            let linkspecs = dummy_linkspecs();
963
            let handshake_type = HandshakeType::NTOR_V3;
964
            let extend2 = relaymsg::Extend2::new(linkspecs, handshake_type, vec![]).into();
965
            ctrl.send_fwd(None, extend2, Recognized::Yes, true).await;
966
            rt.advance_until_stalled().await;
967

            
968
            // The reactor handled the EXTEND2 and launched an outbound channel
969
            assert!(logs_contain(
970
                "Launched channel to the next hop circ_uniq_id=Circ 8.17"
971
            ));
972
            assert!(ctrl.outbound_chan_launched());
973
            assert!(!ctrl.is_closing());
974

            
975
            let _circid = ctrl.do_create2_handshake(&rt, handshake_type).await;
976
            assert!(logs_contain("Got CREATED2 response from next hop"));
977
            assert!(logs_contain("Extended circuit to the next hop"));
978

            
979
            // Time to forward a message to the next hop!
980
            let early = false;
981
            let begin = relaymsg::Begin::new("127.0.0.1", 1111, 0).unwrap();
982
            ctrl.send_fwd(None, begin.clone().into(), Recognized::No, early)
983
                .await;
984
            rt.advance_until_stalled().await;
985

            
986
            // Ensure the other end received the BEGIN cell
987
            let cell = ctrl.read_outbound();
988
            let recvd_begin = expect_cell!(cell, Relay, Begin);
989
            assert_eq!(begin, recvd_begin);
990

            
991
            // Now send the same message again, but this time in a RELAY_EARLY
992
            let early = true;
993
            let begin = relaymsg::Begin::new("127.0.0.1", 1111, 0).unwrap();
994
            ctrl.send_fwd(None, begin.clone().into(), Recognized::No, early)
995
                .await;
996
            rt.advance_until_stalled().await;
997
            let cell = ctrl.read_outbound();
998
            let recvd_begin = expect_cell!(cell, RelayEarly, Begin);
999
            assert_eq!(begin, recvd_begin);
        });
    }
    #[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"));
        });
    }
}