Skip to main content

tor_proto/relay/channel/
create_handler.rs

1//! Handler for CREATE* cells.
2
3use crate::FlowCtrlParameters;
4use crate::ccparams::{
5    AlgorithmDiscriminants, CongestionWindowParams, FixedWindowParams, RoundTripEstimatorParams,
6    VegasParams,
7};
8use crate::channel::Channel;
9use crate::circuit::celltypes::{CreateRequest, CreateResponse};
10use crate::circuit::circhop::{HandshakeParamsError, HopSettings};
11use crate::circuit::{
12    CircuitRxSender, HandshakeSubprotocols, InvalidHandshakeSubprotocolError, UniqId,
13};
14use crate::client::circuit::padding::PaddingController;
15use crate::crypto::binding::CircuitBinding;
16use crate::crypto::cell::CryptInit as _;
17use crate::crypto::cell::{
18    CgoRelayCrypto, InboundRelayLayer, OutboundRelayLayer, RelayLayer, Tor1RelayCrypto,
19};
20use crate::crypto::handshake::RelayHandshakeError;
21use crate::crypto::handshake::ServerHandshake as _;
22use crate::crypto::handshake::fast::CreateFastServer;
23use crate::crypto::handshake::ntor::{NtorSecretKey, NtorServer};
24use crate::crypto::handshake::ntor_v3::{NtorV3SecretKey, NtorV3Server};
25use crate::memquota::SpecificAccount as _;
26use crate::memquota::{ChannelAccount, CircuitAccount};
27use crate::relay::channel_provider::ChannelProvider;
28use crate::relay::reactor::Reactor;
29use crate::relay::{IncomingStreamRequestFilter, RelayCirc};
30use crate::stream::IncomingStream;
31use futures::channel::mpsc;
32use futures::{SinkExt, Stream};
33use smallvec::SmallVec;
34use std::sync::{Arc, RwLock, Weak};
35use tor_cell::chancell::ChanMsg as _;
36use tor_cell::chancell::CircId;
37use tor_cell::chancell::msg::{
38    CreateFast, Created2, CreatedFast, Destroy, DestroyReason, HandshakeType,
39};
40use tor_cell::relaycell::RelayCmd;
41use tor_cell::relaycell::extend::{
42    CcRequest, CcResponse, CircRequestExt, CircResponseExt, SubprotocolRequest,
43};
44use tor_error::{ErrorKind, HasKind, debug_report, internal, into_internal, warn_report};
45use tor_linkspec::OwnedChanTarget;
46use tor_llcrypto::pk::ed25519::Ed25519Identity;
47use tor_llcrypto::pk::rsa::RsaIdentity;
48use tor_memquota::mq_queue::ChannelSpec as _;
49use tor_memquota::mq_queue::MpscSpec;
50use tor_relay_crypto::pk::{RelayNtorKeypair, RelayNtorKeys};
51use tor_rtcompat::SpawnExt as _;
52use tor_rtcompat::{DynTimeProvider, Runtime};
53use tracing::{debug, trace};
54
55/// Everything needed to handle CREATE* messages on channels.
56#[derive(derive_more::Debug)]
57pub struct CreateRequestHandler {
58    /// Something that can launch channels. Typically the `ChanMgr`.
59    chan_provider: Weak<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
60    /// Circuit-related network parameters.
61    circ_net_params: RwLock<CircNetParameters>,
62    /// The circuit extension keys.
63    #[debug(skip)]
64    ntor_keys: RwLock<RelayNtorKeys>,
65    /// An [`IncomingStreamRequestFilter`] factory for checking whether the user wants
66    /// this request, or wants to reject it immediately.
67    ///
68    /// Used for obtaining a current [`IncomingStreamRequestFilter`]
69    /// for building a circuit reactor.
70    //
71    // TODO(relay): it's likely this will end up changing quite a bit once we start
72    // figuring out exactly how the config/reconfigure() logic and IncomingStreamRequestFilter
73    // should function for relays.
74    #[debug(skip)]
75    incoming_filter_factory: Box<dyn IncomingStreamRequestFilterFactory + Send + Sync>,
76    /// The allowed incoming stream commands.
77    ///
78    /// Used for rejecting BEGIN and RESOLVE if we are not configured to be an exit.
79    ///
80    // TODO(relay): we might use this for rejecting BEGIN_DIR too,
81    // if we decide to allow relays to opt out of being dir mirrors.
82    // See https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/4107/diffs#note_3426447
83    allowed_stream_cmds: SmallVec<[RelayCmd; 3]>,
84    /// A sender for the [`Stream`]s of `IncomingStream` of all circuits.
85    ///
86    /// The receiver will receive one [`Stream`] (of tor streams) per circuit.
87    ///
88    /// This being a bounded MPSC might seem a bit risky, because in theory,
89    /// if the receiver is not reading fast enough, sending will block.
90    /// In practice, however, it should never block (or buffer very much at all,
91    /// for that matter), because the user (arti-relay) is expected to read from
92    /// this in a tight loop, and spawn a task for handling each [`Stream`].
93    ///
94    /// Note: because this MPSC is not associated with any particular circuit or channel,
95    /// it does not participate in the memquota system (see [crate::memquota]).
96    #[debug(skip)]
97    circuit_stream_tx: mpsc::Sender<Box<dyn Stream<Item = IncomingStream> + Send + Sync + Unpin>>,
98}
99
100// We make the CREATE-handling methods of `CreateRequestHandler` async
101// since we expect that in the future we may want to offload the crypto to a worker thread.
102#[expect(clippy::unused_async)]
103impl CreateRequestHandler {
104    /// Build a new [`CreateRequestHandler`], and a [`CircuitIncomingStreamReceiver`]
105    /// for receiving new streams that are opened on any incoming circuits.
106    pub fn new(
107        chan_provider: Weak<dyn ChannelProvider<BuildSpec = OwnedChanTarget> + Send + Sync>,
108        circ_net_params: CircNetParameters,
109        ntor_keys: RelayNtorKeys,
110        incoming_filter_factory: Box<dyn IncomingStreamRequestFilterFactory + Send + Sync>,
111        allowed_stream_cmds: &[RelayCmd],
112    ) -> (Self, CircuitIncomingStreamReceiver) {
113        // TODO(relay-tuning): this MPSC can be a bottleneck,
114        // as all the channels on this relay will want to send one item on it
115        // each time a new circuit is created.
116        //
117        // The value set here is a guesstimate.
118        const CIRC_STREAM_BUF_SIZE: usize = 1024;
119
120        // This is not associated with any particular circuit
121        // (it is for *all* circuits), so it doesn't participate in memquota
122        // (see circuit_stream_tx docs)
123        #[allow(clippy::disallowed_methods)]
124        let (stream_tx, stream_rx) = mpsc::channel(CIRC_STREAM_BUF_SIZE);
125
126        let handler = Self {
127            chan_provider,
128            circ_net_params: RwLock::new(circ_net_params),
129            ntor_keys: RwLock::new(ntor_keys),
130            incoming_filter_factory,
131            allowed_stream_cmds: allowed_stream_cmds.into(),
132            circuit_stream_tx: stream_tx,
133        };
134
135        let circuit_stream_rx = CircuitIncomingStreamReceiver {
136            circuit_stream_rx: stream_rx,
137        };
138
139        (handler, circuit_stream_rx)
140    }
141
142    /// Update the circuit parameters from a network consensus.
143    pub fn update_params(&self, circ_net_params: CircNetParameters) {
144        *self.circ_net_params.write().expect("rwlock poisoned") = circ_net_params;
145    }
146
147    /// Update the handler with a new set of circuit extension keys.
148    ///
149    /// This is called periodically by the relay key rotation task.
150    pub fn update_ntor_keys(&self, ntor_keys: RelayNtorKeys) {
151        *self.ntor_keys.write().expect("rwlock poisoned") = ntor_keys;
152    }
153
154    /// Handle a CREATE* cell.
155    ///
156    /// This intentionally does not return a [`crate::Error`] so that we don't accidentally shut
157    /// down the channel reactor when we really should be returning a DESTROY. Shutting down a
158    /// channel may cause us to leak information about paths of circuits travelling through this
159    /// relay. This is especially important here since we're handling data that is controllable from
160    /// the other end of the circuit.
161    #[allow(clippy::too_many_arguments)]
162    pub(crate) async fn handle_create<R: Runtime>(
163        &self,
164        runtime: &R,
165        channel: &Arc<Channel>,
166        our_ed25519_id: &Ed25519Identity,
167        our_rsa_id: &RsaIdentity,
168        circ_id: CircId,
169        msg: &CreateRequest,
170        memquota: &ChannelAccount,
171        circ_unique_id: UniqId,
172    ) -> Result<(CreateResponse, RelayCircComponents), Destroy> {
173        let result = self
174            .handle_create_inner(
175                runtime,
176                channel,
177                our_ed25519_id,
178                our_rsa_id,
179                circ_id,
180                msg,
181                memquota,
182                circ_unique_id,
183            )
184            .await;
185
186        match result {
187            Ok(x) => Ok(x),
188            Err(e) => {
189                // TODO(relay): The log messages throughout could be very noisy, so should have rate limiting.
190                let cmd = msg.cmd();
191                debug_report!(&e, %cmd, "Failed to handle circuit create request");
192
193                // `tor-spec/tearing-down-circuits.md`:
194                //
195                // > Implementations SHOULD always use the NONE reason to avoid side channels: [...]
196                Err(Destroy::new(DestroyReason::NONE))
197            }
198        }
199    }
200
201    /// See [`Self::handle_create`].
202    #[allow(clippy::too_many_arguments)]
203    async fn handle_create_inner<R: Runtime>(
204        &self,
205        runtime: &R,
206        channel: &Arc<Channel>,
207        our_ed25519_id: &Ed25519Identity,
208        our_rsa_id: &RsaIdentity,
209        circ_id: CircId,
210        msg: &CreateRequest,
211        memquota: &ChannelAccount,
212        circ_unique_id: UniqId,
213    ) -> Result<(CreateResponse, RelayCircComponents), HandleCreateError> {
214        // Perform the handshake crypto and build the response.
215        let handshake_components = match msg {
216            CreateRequest::CreateFast(msg) => self.handle_create_fast(msg).await?,
217            CreateRequest::Create2(msg) => match msg.handshake_type() {
218                HandshakeType::NTOR_V3 => {
219                    self.handle_create2_ntorv3(msg.body(), our_ed25519_id)
220                        .await?
221                }
222                HandshakeType::NTOR => self.handle_create2_ntor(msg.body(), our_rsa_id).await?,
223                x @ HandshakeType::TAP | x => {
224                    return Err(HandleCreateError::Create2HandshakeType(x));
225                }
226            },
227        };
228
229        let memquota = CircuitAccount::new(memquota)?;
230
231        // We use a large mpsc queue here since a circuit should never block the channel,
232        // and we hope that memquota will help us if an attacker intentionally fills this buffer.
233        // We use `10_000_000` since `usize::MAX` causes `futures::channel::mpsc` to panic.
234        // TODO(relay): We should switch to an unbounded queue, but the circuit reactor is expecting
235        // a bounded queue.
236        let time_provider = DynTimeProvider::new(runtime.clone());
237        let account = memquota.as_raw_account();
238        let (sender, receiver) =
239            MpscSpec::new(10_000_000).new_mq(time_provider.clone(), account)?;
240        let (sender, receiver) = crate::circuit::circ_sender::channel(sender, receiver);
241
242        // TODO(relay): Do we really want a client padding machine here?
243        let (padding_ctrl, padding_stream) =
244            crate::client::circuit::padding::new_padding(DynTimeProvider::new(runtime.clone()));
245
246        // Upgrade the channel provider, which in practice is the `ChanMgr` so this should not fail.
247        let Some(chan_provider) = self.chan_provider.upgrade() else {
248            return Err(internal!("Unable to upgrade weak `ChannelProvider`").into());
249        };
250
251        // Create an IncomingStreamRequestFilter for this circuit.
252        // This will get applied to every stream request (BEGIN, BEGIN_DIR, RESOLVE)
253        // arriving on the circuit.
254        //
255        // Note: once built, a circuit reactor's IncomingStreamRequestFilter cannot be changed
256        // (it's fixed for the entire duration of the circuit).
257        let incoming_filter = self.incoming_filter_factory.current_filter();
258
259        // Build the relay circuit reactor.
260        let (reactor, circ, incoming_streams) = Reactor::new(
261            runtime.clone(),
262            channel,
263            circ_id,
264            circ_unique_id,
265            receiver,
266            handshake_components.crypto_in,
267            handshake_components.crypto_out,
268            &handshake_components.hop_settings,
269            chan_provider,
270            padding_ctrl.clone(),
271            padding_stream,
272            incoming_filter,
273            &self.allowed_stream_cmds,
274            &memquota,
275        )
276        .map_err(into_internal!("Failed to start circuit reactor"))?;
277
278        let mut circuit_stream_tx = self.circuit_stream_tx.clone();
279        // Start the reactor in a task.
280        let () = runtime.spawn(async move {
281            if let Err(e) = circuit_stream_tx.send(Box::new(incoming_streams)).await {
282                warn_report!(e, "IncomingStream handler disappeared?!");
283                // If we get here, it means the relay stream handler task has gone away,
284                // so there won't be anything handling the incoming streams.
285                //
286                // The reactor is dropped, making the RelayCirc returned below
287                // in the RelayCircComponents unusable
288                // (RelayCirc::is_closing() will return `true`).
289                drop(reactor);
290            } else {
291                // Only spawn the circuit reactor if the incoming stream handler was
292                // able to receive our message
293                match reactor.run().await {
294                    Ok(()) => {}
295                    Err(e) => {
296                        debug_report!(e, "Relay circuit reactor exited with an error");
297                    }
298                }
299            }
300        })?;
301
302        Ok((
303            handshake_components.response,
304            RelayCircComponents {
305                circ,
306                sender,
307                padding_ctrl,
308            },
309        ))
310    }
311
312    /// The handshake code for a CREATE_FAST request.
313    async fn handle_create_fast(
314        &self,
315        msg: &CreateFast,
316    ) -> Result<CompletedHandshakeComponents, HandleCreateError> {
317        // TODO(relay): We might want to offload this to a CPU worker in the future.
318        let (keygen, handshake_msg) = CreateFastServer::server(
319            &mut rand::rng(),
320            // The CREATE_FAST handshake doesn't accept or return extensions,
321            // so this `AuxDataReply` is a no-op.
322            &mut |_: &()| Some(()),
323            // The CREATE_FAST handshake doesn't use any keys.
324            &[()],
325            msg.handshake(),
326        )?;
327
328        let circ_net_params = self
329            .circ_net_params
330            .read()
331            .expect("rwlock poisoned")
332            .clone();
333
334        // No subprotocols are requested during a CREATE_FAST handshake.
335        let subprotos = HandshakeSubprotocols::default();
336
337        let hop_settings = HopSettings::from_handshake_params(
338            circ_net_params,
339            // CREATE_FAST always uses fixed-window flow control.
340            AlgorithmDiscriminants::FixedWindow,
341            subprotos,
342        )?;
343
344        let crypt = Tor1RelayCrypto::construct(keygen)
345            .map_err(into_internal!("Circuit crypt state construction failed"))?;
346
347        let (crypto_out, crypto_in, _binding) = split_relay_layer(crypt);
348
349        let response = CreatedFast::new(handshake_msg);
350        let response = CreateResponse::CreatedFast(response);
351
352        trace!("Completed CREATE_FAST handshake");
353
354        Ok(CompletedHandshakeComponents {
355            response,
356            hop_settings,
357            crypto_out,
358            crypto_in,
359        })
360    }
361
362    /// The handshake code for a CREATE2 ntor (non-v3) request.
363    async fn handle_create2_ntor(
364        &self,
365        msg_body: &[u8],
366        our_rsa_id: &RsaIdentity,
367    ) -> Result<CompletedHandshakeComponents, HandleCreateError> {
368        let ntor_keys = self.ntor_keys(|k| {
369            NtorSecretKey::new(k.secret().clone(), *k.public().inner(), *our_rsa_id)
370        });
371
372        // TODO(relay): We might want to offload this to a CPU worker in the future.
373        let (keygen, handshake_msg) = NtorServer::server(
374            &mut rand::rng(),
375            // The ntor (non-v3) handshake doesn't accept or return extensions,
376            // so this `AuxDataReply` is a no-op.
377            &mut |_: &()| Some(()),
378            ntor_keys.as_ref(),
379            msg_body,
380        )?;
381
382        let circ_net_params = self
383            .circ_net_params
384            .read()
385            .expect("rwlock poisoned")
386            .clone();
387
388        // No subprotocols are requested during an ntor (non-v3) handshake.
389        let subprotos = HandshakeSubprotocols::default();
390
391        let hop_settings = HopSettings::from_handshake_params(
392            circ_net_params,
393            // CREATE2 with ntor (non-v3) always uses fixed-window flow control.
394            AlgorithmDiscriminants::FixedWindow,
395            subprotos,
396        )?;
397
398        let crypt = Tor1RelayCrypto::construct(keygen)
399            .map_err(into_internal!("Circuit crypt state construction failed"))?;
400
401        let (crypto_out, crypto_in, _binding) = split_relay_layer(crypt);
402
403        let response = Created2::new(handshake_msg);
404        let response = CreateResponse::Created2(response);
405
406        trace!("Completed ntor handshake");
407
408        Ok(CompletedHandshakeComponents {
409            response,
410            hop_settings,
411            crypto_out,
412            crypto_in,
413        })
414    }
415
416    /// The handshake code for a CREATE2 ntor-v3 request.
417    async fn handle_create2_ntorv3(
418        &self,
419        msg_body: &[u8],
420        our_ed25519_id: &Ed25519Identity,
421    ) -> Result<CompletedHandshakeComponents, HandleCreateError> {
422        let ntor_keys = self.ntor_keys(|k| {
423            NtorV3SecretKey::new(k.secret().clone(), *k.public().inner(), *our_ed25519_id)
424        });
425
426        let circ_net_params = self
427            .circ_net_params
428            .read()
429            .expect("rwlock poisoned")
430            .clone();
431
432        // These extensions can be negotiated during the handshake.
433        let mut cc_algorithm = AlgorithmDiscriminants::FixedWindow;
434
435        // These subprotocols were requested during the handshake.
436        // They are not validated.
437        let mut subprotos = SubprotocolRequest::default();
438
439        // Helper which processes extension requests and returns any responses.
440        // Returns `None` if the handshake should fail.
441        let mut ext_reply_fn = |client_exts: &[CircRequestExt]| {
442            let mut response_exts = Vec::new();
443
444            // https://spec.torproject.org/tor-spec/create-created-cells.html#additional-data
445            //
446            // > Unless otherwise specified in the documentation for an extension type:
447            // > - [...]
448            // > - Parties MUST ignore any occurrence of an extension with a given type after the first such occurrence.
449            //
450            // TODO: Is there something nicer that we can do here?
451            // We could use accessors like `ExtList::get_cc_request()`
452            // which iterate over the extension list for each extension,
453            // but using an enum match like we do below is kind of nice.
454            let mut handled_cc_request = false;
455            let mut handled_subproto_request = false;
456
457            for ext in client_exts {
458                match ext {
459                    CircRequestExt::CcRequest(CcRequest { .. }) => {
460                        if handled_cc_request {
461                            continue;
462                        }
463                        handled_cc_request = true;
464
465                        cc_algorithm = AlgorithmDiscriminants::Vegas;
466
467                        let sendme_inc: u8 = circ_net_params.cc.cwnd.sendme_inc();
468                        let response = CcResponse::new(sendme_inc);
469                        response_exts.push(CircResponseExt::CcResponse(response));
470                    }
471                    // The given `SubprotocolRequest` stores a list of `NumberedSubver`,
472                    // but a circuit extension request is limited to 255 bytes (127 subprotocols).
473                    // So while a malicious client could send us a lot of invalid subprotocols,
474                    // this limit prevents this list from being excessively large.
475                    CircRequestExt::SubprotocolRequest(subproto_request) => {
476                        if handled_subproto_request {
477                            continue;
478                        }
479                        handled_subproto_request = true;
480
481                        // We don't check the requested subprotocols here.
482                        subprotos = subproto_request.clone();
483                    }
484                    CircRequestExt::Unrecognized(ext) => {
485                        // https://spec.torproject.org/tor-spec/create-created-cells.html#additional-data
486                        //
487                        // > Parties MUST ignore extensions with `EXT_FIELD_TYPE` bodies they do not recognize.
488                        debug!(
489                            ?ext,
490                            "CREATE2 ntor-v3 handshake requested unrecognized extension",
491                        );
492                    }
493                    ext => {
494                        // https://spec.torproject.org/tor-spec/create-created-cells.html#additional-data
495                        //
496                        // > Parties MUST ignore extensions with `EXT_FIELD_TYPE` bodies they do not recognize.
497                        //
498                        // We recognize this but don't know what to do with it.
499                        // We haven't implemented it, or it doesn't make sense
500                        // (for example `CircRequestExt::ProofOfWork`).
501                        // So we'll just behave as if we don't recognize it.
502                        debug!(
503                            ?ext,
504                            "CREATE2 ntor-v3 handshake requested unsupported extension",
505                        );
506                    }
507                }
508            }
509
510            Some(response_exts)
511        };
512
513        // TODO(relay): We might want to offload this to a CPU worker in the future.
514        let (keygen, handshake_msg) = NtorV3Server::server(
515            &mut rand::rng(),
516            &mut ext_reply_fn,
517            ntor_keys.as_ref(),
518            msg_body,
519        )?;
520
521        // Ensure that the client did not request invalid/unsupported subprotocols.
522        let subprotos = HandshakeSubprotocols::try_from_request(subprotos)?;
523
524        let hop_settings =
525            HopSettings::from_handshake_params(circ_net_params, cc_algorithm, subprotos)?;
526
527        let (crypto_out, crypto_in, _binding) = if subprotos.relay_crypt_cgo {
528            let crypt = CgoRelayCrypto::construct(keygen)
529                .map_err(into_internal!("Circuit crypt state construction failed"))?;
530            split_relay_layer(crypt)
531        } else {
532            let crypt = Tor1RelayCrypto::construct(keygen)
533                .map_err(into_internal!("Circuit crypt state construction failed"))?;
534            split_relay_layer(crypt)
535        };
536
537        let response = Created2::new(handshake_msg);
538        let response = CreateResponse::Created2(response);
539
540        trace!(?cc_algorithm, ?subprotos, "Completed ntor-v3 handshake");
541
542        Ok(CompletedHandshakeComponents {
543            response,
544            hop_settings,
545            crypto_out,
546            crypto_in,
547        })
548    }
549
550    /// Helper to get the ntor keypairs after some transformation `map`.
551    ///
552    /// The `map` transformation must be fast since it blocks a read lock.
553    /// The returned keys are sorted with the most recent key first.
554    ///
555    /// It would be nice if this just returned an iterator,
556    /// but the read lock prevents this.
557    fn ntor_keys<T>(&self, map: impl FnMut(&RelayNtorKeypair) -> T) -> impl AsRef<[T]> {
558        let ntor_keys = self.ntor_keys.read().expect("rwlock poisoned");
559        let ntor_keys = [Some(ntor_keys.latest()), ntor_keys.previous()];
560        ntor_keys
561            .into_iter()
562            .flatten()
563            .map(map)
564            .collect::<SmallVec<[T; 2]>>()
565    }
566}
567
568/// A receiver of [`Stream`]s (one for each incoming circuit),
569/// where each `Stream` produces [`IncomingStream`]s for that circuit.
570///
571// Note: in theory, it would be nice if we could get rid of this type altogether.
572// In an ideal world, I would've instead
573//
574//   * added a `RelayCirc::take_incoming_streams()` method for obtaining
575//     the futures::Stream of IncomingStream of that circuit
576//   * made the CreateRequestHandler send each Arc<RelayCirc> over to arti-relay for handling
577//   * made arti-relay obtain the futures::Stream<Item = IncomingStream> of each RelayCirc
578//     by calling `RelayCirc::take_incoming_streams()`
579//
580// However, that would involve adding some locking/interior mutability within RelayCirc
581// (which is always behind an Arc), or extending mq_queue::Receiver to be Clone,
582// which would be tricky to pull off (see the comment on mq_queue::Receiver about this).
583pub struct CircuitIncomingStreamReceiver {
584    /// The receiver for the [`Stream`]s of `IncomingStream` of all circuits.
585    ///
586    /// Receives one [`Stream`] (of tor streams) per circuit.
587    /// Each of these will be handled in a new task.
588    circuit_stream_rx: mpsc::Receiver<<Self as Stream>::Item>,
589}
590
591impl Stream for CircuitIncomingStreamReceiver {
592    // TODO: it would be nice if we could return a type-erased Stream here
593    // (impl Stream<...>), but impl Trait in associated types is unstable.
594    // See rust issue #63063 <https://github.com/rust-lang/rust/issues/63063>
595    type Item = Box<dyn Stream<Item = IncomingStream> + Send + Sync + Unpin>;
596
597    fn poll_next(
598        mut self: std::pin::Pin<&mut Self>,
599        cx: &mut std::task::Context<'_>,
600    ) -> std::task::Poll<Option<Self::Item>> {
601        use futures::StreamExt as _;
602
603        self.circuit_stream_rx.poll_next_unpin(cx)
604    }
605}
606
607/// Helper function to split a `RelayLayer` into forward and backward type-erased trait objects.
608fn split_relay_layer<F, B>(
609    crypt: impl RelayLayer<F, B>,
610) -> (
611    Box<dyn OutboundRelayLayer + Send>,
612    Box<dyn InboundRelayLayer + Send>,
613    CircuitBinding,
614)
615where
616    F: OutboundRelayLayer + Send + 'static,
617    B: InboundRelayLayer + Send + 'static,
618{
619    let (crypto_out, crypto_in, binding) = crypt.split_relay_layer();
620    let (crypto_out, crypto_in) = (Box::new(crypto_out), Box::new(crypto_in));
621
622    (crypto_out, crypto_in, binding)
623}
624
625/// An error that occurred while handling a CREATE* request.
626#[derive(Debug, thiserror::Error)]
627enum HandleCreateError {
628    /// Circuit relay handshake failed.
629    #[error("Circuit relay handshake failed")]
630    Handshake(#[from] RelayHandshakeError),
631    /// Circuit relay handshake failed.
632    #[error("Failed to process the circuit relay handshake parameters")]
633    HandshakeParameters(#[from] HandshakeParamsError),
634    /// Requested subprotocols which aren't supported.
635    #[error("Client requested subprotocol(s) which aren't supported")]
636    HandshakeSubprotocols(#[from] InvalidHandshakeSubprotocolError),
637    /// The requested handshake type is unsupported.
638    #[error("Unsupported handshake type {0}")]
639    Create2HandshakeType(HandshakeType),
640    /// A memquota error.
641    #[error("Memquota error")]
642    Memquota(#[from] tor_memquota::Error),
643    /// Error when spawning a task.
644    #[error("Runtime task spawn error")]
645    Spawn(#[from] futures::task::SpawnError),
646    /// An internal error.
647    ///
648    /// Note that other variants (such as `Handshake` containing a [`RelayHandshakeError`])
649    /// may themselves contain internal errors.
650    #[error("Internal error")]
651    Internal(#[from] tor_error::Bug),
652}
653
654impl HasKind for HandleCreateError {
655    fn kind(&self) -> ErrorKind {
656        match self {
657            Self::Handshake(e) => e.kind(),
658            Self::HandshakeParameters(e) => e.kind(),
659            Self::HandshakeSubprotocols(e) => e.kind(),
660            Self::Create2HandshakeType(_) => ErrorKind::NotImplemented,
661            Self::Memquota(e) => e.kind(),
662            Self::Spawn(e) => e.kind(),
663            Self::Internal(_) => ErrorKind::Internal,
664        }
665    }
666}
667
668/// The components of a completed CREATE* handshake.
669struct CompletedHandshakeComponents {
670    /// The message to send in response.
671    response: CreateResponse,
672    /// The negotiated hop settings.
673    hop_settings: HopSettings,
674    /// Outbound onion crypto.
675    crypto_out: Box<dyn OutboundRelayLayer + Send>,
676    /// Inbound onion crypto.
677    crypto_in: Box<dyn InboundRelayLayer + Send>,
678}
679
680/// A collection of objects built for a new relay circuit.
681pub(crate) struct RelayCircComponents {
682    /// The relay circuit handle.
683    pub(crate) circ: Arc<RelayCirc>,
684    /// Used to send data from the channel to the circuit reactor.
685    pub(crate) sender: CircuitRxSender,
686    /// The circuit's padding controller.
687    pub(crate) padding_ctrl: PaddingController,
688}
689
690/// Congestion control network parameters.
691#[derive(Debug, Clone)]
692#[allow(clippy::exhaustive_structs)]
693pub struct CongestionControlNetParams {
694    /// Fixed-window algorithm parameters.
695    pub fixed_window: FixedWindowParams,
696
697    /// Vegas algorithm parameters for exit circuits.
698    // NOTE: In this module we are handling CREATE* cells,
699    // which only happens for non-hs circuits.
700    // So we don't need to store the vegas hs parameters here.
701    pub vegas_exit: VegasParams,
702
703    /// Congestion window parameters.
704    pub cwnd: CongestionWindowParams,
705
706    /// RTT calculation parameters.
707    pub rtt: RoundTripEstimatorParams,
708
709    /// Flow control parameters to use for all streams on this circuit.
710    pub flow_ctrl: FlowCtrlParameters,
711}
712
713impl CongestionControlNetParams {
714    #[cfg(test)]
715    // These have been copied from C-tor.
716    pub(crate) fn defaults_for_tests() -> Self {
717        Self {
718            fixed_window: FixedWindowParams::defaults_for_tests(),
719            vegas_exit: VegasParams::defaults_for_tests(),
720            cwnd: CongestionWindowParams::defaults_for_tests(),
721            rtt: RoundTripEstimatorParams::defaults_for_tests(),
722            flow_ctrl: FlowCtrlParameters::defaults_for_tests(),
723        }
724    }
725}
726
727/// Network consensus parameters for handling incoming circuits.
728///
729/// Unlike `CircParameters`,
730/// this is unopinionated and contains all relevant consensus parameters,
731/// which is needed when handling an incoming CREATE* request where the
732/// circuit origin chooses the type/settings
733/// (for example congestion control type) of the circuit.
734#[derive(Debug, Clone)]
735#[allow(clippy::exhaustive_structs)]
736pub struct CircNetParameters {
737    /// Congestion control network parameters.
738    pub cc: CongestionControlNetParams,
739}
740
741/// An [`IncomingStreamRequestFilter`] factory for building [`IncomingStreamRequestFilter`]s.
742///
743/// Each time a new circuit is opened, the [`CreateRequestHandler`] calls
744/// [`IncomingStreamRequestFilterFactory::current_filter`] to build
745/// an [`IncomingStreamRequestFilter`] for the circuit.
746pub trait IncomingStreamRequestFilterFactory {
747    /// Return the [`IncomingStreamRequestFilter`] to apply to the incoming stream requests
748    /// arriving on a circuit.
749    fn current_filter(&self) -> Box<dyn IncomingStreamRequestFilter>;
750}
751
752impl<F> IncomingStreamRequestFilterFactory for F
753where
754    F: Fn() -> Box<dyn IncomingStreamRequestFilter>,
755{
756    fn current_filter(&self) -> Box<dyn IncomingStreamRequestFilter> {
757        (self)()
758    }
759}
760
761#[cfg(test)]
762mod test {
763    // @@ begin test lint list maintained by maint/add_warning @@
764    #![allow(clippy::bool_assert_comparison)]
765    #![allow(clippy::clone_on_copy)]
766    #![allow(clippy::dbg_macro)]
767    #![allow(clippy::mixed_attributes_style)]
768    #![allow(clippy::print_stderr)]
769    #![allow(clippy::print_stdout)]
770    #![allow(clippy::single_char_pattern)]
771    #![allow(clippy::unwrap_used)]
772    #![allow(clippy::unchecked_time_subtraction)]
773    #![allow(clippy::useless_vec)]
774    #![allow(clippy::needless_pass_by_value)]
775    #![allow(clippy::string_slice)] // See arti#2571
776    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
777
778    use tor_cell::chancell::msg::{AnyChanMsg, Create2, CreateFast, HandshakeType};
779    use tor_cell::chancell::{AnyChanCell, ChanCmd, ChanMsg as _};
780    use tor_rtcompat::test_with_one_runtime;
781
782    use crate::channel::test_utils;
783    use crate::circuit::CircParameters;
784
785    /// Test the CREATE_FAST handshake.
786    #[test]
787    fn create_fast() {
788        test_with_one_runtime!(|rt| async move {
789            let mut conn_inspector = test_utils::ConnInspector::new();
790
791            let (client_chan, relay_chan, _circuit_stream_rx, _target_builder) =
792                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
793
794            let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
795
796            let circ_params = CircParameters::default();
797
798            let tunnel = pending_tunnel
799                .create_firsthop_fast(circ_params)
800                .await
801                .unwrap();
802
803            // Client sent a CREATE_FAST, relay responded with a CREATED_FAST.
804            assert_eq!(
805                conn_inspector.try_client_cell().unwrap().msg().cmd(),
806                ChanCmd::CREATE_FAST,
807            );
808            assert_eq!(
809                conn_inspector.try_relay_cell().unwrap().msg().cmd(),
810                ChanCmd::CREATED_FAST,
811            );
812
813            drop(tunnel);
814
815            assert_eq!(
816                conn_inspector.client_cell().await.unwrap().msg().cmd(),
817                ChanCmd::DESTROY,
818            );
819
820            // Wait for both channels to close (ignoring any channel errors).
821            let wait_fut =
822                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
823            drop((client_chan, relay_chan));
824            let _ = wait_fut.await;
825
826            // We don't expect any other messages to have been sent.
827            assert!(conn_inspector.try_client_cell().is_none());
828            assert!(conn_inspector.try_relay_cell().is_none());
829        });
830    }
831
832    /// Test the CREATE_FAST handshake, but with a modified handshake payload.
833    #[test]
834    fn create_fast_fail() {
835        test_with_one_runtime!(|rt| async move {
836            let mut conn_inspector = test_utils::ConnInspector::new();
837
838            // Rewrite any client-sent CREATE_FAST messages to cause the client to fail the
839            // handshake when processing the CREATED_FAST.
840            conn_inspector.set_client_cell_modifier(|cell: &mut AnyChanCell| {
841                let circ_id = cell.circid();
842                if let AnyChanMsg::CreateFast(msg) = cell.msg() {
843                    let mut new_handshake = msg.handshake().to_vec();
844
845                    // Flip all bits.
846                    for byte in &mut new_handshake {
847                        *byte = !*byte;
848                    }
849
850                    // Reassemble the CREATE_FAST cell with the incorrect handshake body.
851                    let new_msg = CreateFast::new(new_handshake);
852                    *cell = AnyChanCell::new(circ_id, AnyChanMsg::CreateFast(new_msg));
853                }
854            });
855
856            let (client_chan, relay_chan, _circuit_stream_rx, _target_builder) =
857                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
858
859            let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
860
861            let circ_params = CircParameters::default();
862
863            // I don't think it's possible to modify the CREATE_FAST cell to make the relay fail the
864            // handshake (the CREATE_FAST payload consists of only random bytes),
865            // so the relay will respond successfully with a CREATED_FAST.
866            // But the client will fail since the CREATED_FAST will contain garbage bytes.
867
868            // The relay successfully processed the handshake,
869            // but the client fails to validate the handshake since the handshake data was modified.
870            assert!(matches!(
871                pending_tunnel.create_firsthop_fast(circ_params).await,
872                Err(crate::Error::BadCircHandshakeAuth),
873            ));
874
875            // Client sent a CREATE_FAST, relay responded with a CREATED_FAST.
876            assert_eq!(
877                conn_inspector.try_client_cell().unwrap().msg().cmd(),
878                ChanCmd::CREATE_FAST,
879            );
880            assert_eq!(
881                conn_inspector.try_relay_cell().unwrap().msg().cmd(),
882                ChanCmd::CREATED_FAST,
883            );
884
885            // Since the `create_firsthop_fast()` failed above,
886            // the client should have sent a DESTROY.
887            assert_eq!(
888                conn_inspector.client_cell().await.unwrap().msg().cmd(),
889                ChanCmd::DESTROY,
890            );
891
892            // Wait for both channels to close (ignoring any channel errors).
893            let wait_fut =
894                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
895            drop((client_chan, relay_chan));
896            let _ = wait_fut.await;
897
898            // We don't expect any other messages to have been sent.
899            assert!(conn_inspector.try_client_cell().is_none());
900            assert!(conn_inspector.try_relay_cell().is_none());
901        });
902    }
903
904    /// Test the client with the "Relay=1" subprotocol (corresponds to the TAP handshake),
905    /// which Arti doesn't support.
906    #[test]
907    fn tap() {
908        test_with_one_runtime!(|rt| async move {
909            let mut conn_inspector = test_utils::ConnInspector::new();
910
911            let (client_chan, _relay_chan, _circuit_stream_rx, mut target_builder) =
912                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
913
914            let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
915
916            let circ_params = CircParameters::default();
917
918            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
919            // 1 = RELAY_BASE
920            let protocols = "Relay=1".parse().unwrap();
921            let target = target_builder.protocols(protocols).build().unwrap();
922
923            // TODO: This should fail since we don't support TAP handshakes.
924            // But the channel will do an ntor handshake anyway even though it's not supported.
925            // https://gitlab.torproject.org/tpo/core/arti/-/work_items/2489
926            let _tunnel = pending_tunnel
927                .create_firsthop(&target, circ_params)
928                .await
929                .unwrap();
930
931            // TODO: As above, this is wrong.
932            assert_eq!(
933                conn_inspector.try_client_cell().unwrap().msg().cmd(),
934                ChanCmd::CREATE2,
935            );
936            assert_eq!(
937                conn_inspector.try_relay_cell().unwrap().msg().cmd(),
938                ChanCmd::CREATED2,
939            );
940        });
941    }
942
943    /// Test the CREATE2 ntor handshake.
944    #[test]
945    fn ntor() {
946        test_with_one_runtime!(|rt| async move {
947            let mut conn_inspector = test_utils::ConnInspector::new();
948
949            let (client_chan, relay_chan, _circuit_stream_rx, mut target_builder) =
950                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
951
952            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
953            // 2 = RELAY_NTOR
954            // 3 = RELAY_EXTEND_IPv6
955            for relay_version in [2, 3] {
956                let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
957
958                let circ_params = CircParameters::default();
959
960                let protocols = format!("Relay=2-{relay_version}").parse().unwrap();
961                let target = target_builder.protocols(protocols).build().unwrap();
962
963                let tunnel = pending_tunnel
964                    .create_firsthop(&target, circ_params)
965                    .await
966                    .unwrap();
967
968                let client_cell = conn_inspector.try_client_cell().unwrap().msg().clone();
969                let relay_cell = conn_inspector.try_relay_cell().unwrap().msg().clone();
970
971                // Client sent a CREATE2, relay responded with a CREATED2.
972                assert_eq!(client_cell.cmd(), ChanCmd::CREATE2);
973                assert_eq!(relay_cell.cmd(), ChanCmd::CREATED2);
974
975                // Check that it was an ntor handshake.
976                let AnyChanMsg::Create2(client_cell) = client_cell else {
977                    unreachable!("CREATE2 checked above");
978                };
979                assert_eq!(client_cell.handshake_type(), HandshakeType::NTOR);
980
981                drop(tunnel);
982
983                assert_eq!(
984                    conn_inspector.client_cell().await.unwrap().msg().cmd(),
985                    ChanCmd::DESTROY,
986                );
987
988                // We don't expect any other messages to have been sent.
989                assert!(conn_inspector.try_client_cell().is_none());
990                assert!(conn_inspector.try_relay_cell().is_none());
991            }
992
993            // Wait for both channels to close (ignoring any channel errors).
994            let wait_fut =
995                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
996            drop((client_chan, relay_chan));
997            let _ = wait_fut.await;
998
999            // We don't expect any other messages to have been sent.
1000            assert!(conn_inspector.try_client_cell().is_none());
1001            assert!(conn_inspector.try_relay_cell().is_none());
1002        });
1003    }
1004
1005    /// Test the CREATE2 ntor handshake, but with a modified handshake payload.
1006    #[test]
1007    fn ntor_fail() {
1008        test_with_one_runtime!(|rt| async move {
1009            let mut conn_inspector = test_utils::ConnInspector::new();
1010
1011            // Rewrite any client-sent CREATE2 messages to cause the relay to fail the handshake.
1012            conn_inspector.set_client_cell_modifier(|cell: &mut AnyChanCell| {
1013                let circ_id = cell.circid();
1014                if let AnyChanMsg::Create2(msg) = cell.msg() {
1015                    let mut new_body = msg.body().to_vec();
1016
1017                    // Flip some arbitrarily chosen byte.
1018                    new_body[10] = !new_body[10];
1019
1020                    // Reassemble the CREATE2 cell with the incorrect handshake body.
1021                    let new_msg = Create2::new(msg.handshake_type(), new_body);
1022                    *cell = AnyChanCell::new(circ_id, AnyChanMsg::Create2(new_msg));
1023                }
1024            });
1025
1026            let (client_chan, relay_chan, _circuit_stream_rx, mut target_builder) =
1027                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
1028
1029            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
1030            // 2 = RELAY_NTOR
1031            // 3 = RELAY_EXTEND_IPv6
1032            for relay_version in [2, 3] {
1033                let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
1034
1035                let circ_params = CircParameters::default();
1036
1037                let protocols = format!("Relay=2-{relay_version}").parse().unwrap();
1038                let target = target_builder.protocols(protocols).build().unwrap();
1039
1040                // The relay refuses the circuit handshake since the CREATED2 cell had some
1041                // intentional issue with the handshake body.
1042                assert!(matches!(
1043                    pending_tunnel.create_firsthop(&target, circ_params).await,
1044                    Err(crate::Error::CircRefused(_)),
1045                ));
1046
1047                // Client sent a CREATE2, relay responded with a DESTROY.
1048                assert_eq!(
1049                    conn_inspector.try_client_cell().unwrap().msg().cmd(),
1050                    ChanCmd::CREATE2,
1051                );
1052                assert_eq!(
1053                    conn_inspector.try_relay_cell().unwrap().msg().cmd(),
1054                    ChanCmd::DESTROY,
1055                );
1056
1057                // We don't expect any other messages to have been sent.
1058                assert!(conn_inspector.try_client_cell().is_none());
1059                assert!(conn_inspector.try_relay_cell().is_none());
1060            }
1061
1062            // Wait for both channels to close (ignoring any channel errors).
1063            let wait_fut =
1064                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
1065            drop((client_chan, relay_chan));
1066            let _ = wait_fut.await;
1067
1068            // We don't expect any other messages to have been sent.
1069            assert!(conn_inspector.try_client_cell().is_none());
1070            assert!(conn_inspector.try_relay_cell().is_none());
1071        });
1072    }
1073
1074    /// Test the CREATE2 ntor-v3 handshake.
1075    #[test]
1076    fn ntor_v3() {
1077        test_with_one_runtime!(|rt| async move {
1078            let mut conn_inspector = test_utils::ConnInspector::new();
1079
1080            let (client_chan, relay_chan, _circuit_stream_rx, mut target_builder) =
1081                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
1082
1083            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
1084            // 4 = RELAY_NTORV3
1085            // 5 = RELAY_NEGOTIATE_SUBPROTO
1086            // 6 = RELAY_CRYPT_CGO
1087            for relay_version in [4, 5, 6] {
1088                let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
1089
1090                let circ_params = CircParameters::default();
1091
1092                let protocols = format!("Relay=4-{relay_version}").parse().unwrap();
1093                let target = target_builder.protocols(protocols).build().unwrap();
1094
1095                let tunnel = pending_tunnel
1096                    .create_firsthop(&target, circ_params)
1097                    .await
1098                    .unwrap();
1099
1100                let client_cell = conn_inspector.try_client_cell().unwrap().msg().clone();
1101                let relay_cell = conn_inspector.try_relay_cell().unwrap().msg().clone();
1102
1103                // Client sent a CREATE2, relay responded with a CREATED2.
1104                assert_eq!(client_cell.cmd(), ChanCmd::CREATE2);
1105                assert_eq!(relay_cell.cmd(), ChanCmd::CREATED2);
1106
1107                // Check that it was an ntor-v3 handshake.
1108                let AnyChanMsg::Create2(client_cell) = client_cell else {
1109                    unreachable!("CREATE2 checked above");
1110                };
1111                assert_eq!(client_cell.handshake_type(), HandshakeType::NTOR_V3);
1112
1113                // TODO: It would be nice if we had a way to check that CGO was in use when
1114                // `relay_version` is >=6, but I don't see a nice way to do that.
1115
1116                drop(tunnel);
1117
1118                assert_eq!(
1119                    conn_inspector.client_cell().await.unwrap().msg().cmd(),
1120                    ChanCmd::DESTROY,
1121                );
1122
1123                // We don't expect any other messages to have been sent.
1124                assert!(conn_inspector.try_client_cell().is_none());
1125                assert!(conn_inspector.try_relay_cell().is_none());
1126            }
1127
1128            // Wait for both channels to close (ignoring any channel errors).
1129            let wait_fut =
1130                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
1131            drop((client_chan, relay_chan));
1132            let _ = wait_fut.await;
1133
1134            // We don't expect any other messages to have been sent.
1135            assert!(conn_inspector.try_client_cell().is_none());
1136            assert!(conn_inspector.try_relay_cell().is_none());
1137        });
1138    }
1139
1140    /// Test the CREATE2 ntor-v3 handshake, but with a modified handshake payload.
1141    #[test]
1142    fn ntor_v3_fail() {
1143        test_with_one_runtime!(|rt| async move {
1144            let mut conn_inspector = test_utils::ConnInspector::new();
1145
1146            // Rewrite any client-sent CREATE2 messages to cause the relay to fail the handshake.
1147            conn_inspector.set_client_cell_modifier(|cell: &mut AnyChanCell| {
1148                let circ_id = cell.circid();
1149                if let AnyChanMsg::Create2(msg) = cell.msg() {
1150                    let mut new_body = msg.body().to_vec();
1151
1152                    // Flip some arbitrarily chosen byte.
1153                    new_body[10] = !new_body[10];
1154
1155                    // Reassemble the CREATE2 cell with the incorrect handshake body.
1156                    let new_msg = Create2::new(msg.handshake_type(), new_body);
1157                    *cell = AnyChanCell::new(circ_id, AnyChanMsg::Create2(new_msg));
1158                }
1159            });
1160
1161            let (client_chan, relay_chan, _circuit_stream_rx, mut target_builder) =
1162                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
1163
1164            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
1165            // 4 = RELAY_NTORV3
1166            // 5 = RELAY_NEGOTIATE_SUBPROTO
1167            // 6 = RELAY_CRYPT_CGO
1168            for relay_version in [4, 5, 6] {
1169                let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
1170
1171                let circ_params = CircParameters::default();
1172
1173                let protocols = format!("Relay=4-{relay_version}").parse().unwrap();
1174                let target = target_builder.protocols(protocols).build().unwrap();
1175
1176                // The relay refuses the circuit handshake since the CREATED2 cell had some
1177                // intentional issue with the handshake body.
1178                assert!(matches!(
1179                    pending_tunnel.create_firsthop(&target, circ_params).await,
1180                    Err(crate::Error::CircRefused(_)),
1181                ));
1182
1183                // Client sent a CREATE2, relay responded with a DESTROY.
1184                assert_eq!(
1185                    conn_inspector.try_client_cell().unwrap().msg().cmd(),
1186                    ChanCmd::CREATE2,
1187                );
1188                assert_eq!(
1189                    conn_inspector.try_relay_cell().unwrap().msg().cmd(),
1190                    ChanCmd::DESTROY,
1191                );
1192
1193                // We don't expect any other messages to have been sent.
1194                assert!(conn_inspector.try_client_cell().is_none());
1195                assert!(conn_inspector.try_relay_cell().is_none());
1196            }
1197
1198            // Wait for both channels to close (ignoring any channel errors).
1199            let wait_fut =
1200                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
1201            drop((client_chan, relay_chan));
1202            let _ = wait_fut.await;
1203
1204            // We don't expect any other messages to have been sent.
1205            assert!(conn_inspector.try_client_cell().is_none());
1206            assert!(conn_inspector.try_relay_cell().is_none());
1207        });
1208    }
1209
1210    /// Test the CREATE2 handshake, but with an invalid handshake type.
1211    #[test]
1212    fn create2_invalid_handshake_fail() {
1213        test_with_one_runtime!(|rt| async move {
1214            let mut conn_inspector = test_utils::ConnInspector::new();
1215
1216            // Rewrite any client-sent CREATE2 messages to cause the relay to fail the handshake.
1217            conn_inspector.set_client_cell_modifier(|cell: &mut AnyChanCell| {
1218                let circ_id = cell.circid();
1219                if let AnyChanMsg::Create2(msg) = cell.msg() {
1220                    // Reassemble the CREATE2 cell with the incorrect handshake type.
1221                    let new_msg = tor_cell::chancell::msg::Create2::new(99.into(), msg.body());
1222                    *cell = AnyChanCell::new(circ_id, AnyChanMsg::Create2(new_msg));
1223                }
1224            });
1225
1226            let (client_chan, relay_chan, _circuit_stream_rx, mut target_builder) =
1227                test_utils::new_channel_pair_with_keys(&rt, &conn_inspector);
1228
1229            // https://spec.torproject.org/tor-spec/subprotocol-versioning.html
1230            // 2 = RELAY_NTOR
1231            // 3 = RELAY_EXTEND_IPv6
1232            // 4 = RELAY_NTORV3
1233            // 5 = RELAY_NEGOTIATE_SUBPROTO
1234            // 6 = RELAY_CRYPT_CGO
1235            for relay_version in [2, 6] {
1236                let pending_tunnel = test_utils::new_pending_tunnel(&rt, &client_chan).await;
1237
1238                let circ_params = CircParameters::default();
1239
1240                let protocols = format!("Relay=2-{relay_version}").parse().unwrap();
1241                let target = target_builder.protocols(protocols).build().unwrap();
1242
1243                // The relay refuses the circuit handshake since the CREATED2 cell had some
1244                // unrecognized handshake type.
1245                assert!(matches!(
1246                    pending_tunnel.create_firsthop(&target, circ_params).await,
1247                    Err(crate::Error::CircRefused(_)),
1248                ));
1249
1250                // Client sent a CREATE2, relay responded with a DESTROY.
1251                assert_eq!(
1252                    conn_inspector.try_client_cell().unwrap().msg().cmd(),
1253                    ChanCmd::CREATE2,
1254                );
1255                assert_eq!(
1256                    conn_inspector.try_relay_cell().unwrap().msg().cmd(),
1257                    ChanCmd::DESTROY,
1258                );
1259
1260                // We don't expect any other messages to have been sent.
1261                assert!(conn_inspector.try_client_cell().is_none());
1262                assert!(conn_inspector.try_relay_cell().is_none());
1263            }
1264
1265            // Wait for both channels to close (ignoring any channel errors).
1266            let wait_fut =
1267                futures::future::join(client_chan.wait_for_close(), relay_chan.wait_for_close());
1268            drop((client_chan, relay_chan));
1269            let _ = wait_fut.await;
1270
1271            // We don't expect any other messages to have been sent.
1272            assert!(conn_inspector.try_client_cell().is_none());
1273            assert!(conn_inspector.try_relay_cell().is_none());
1274        });
1275    }
1276}