1
//! Code to handle incoming cells on a circuit.
2
//!
3
//! ## On message validation
4
//!
5
//! There are three steps for validating an incoming message on a stream:
6
//!
7
//! 1. Is the message contextually appropriate? (e.g., no more than one
8
//!    `CONNECTED` message per stream.) This is handled by calling
9
//!    [`CmdChecker::check_msg`](crate::stream::cmdcheck::CmdChecker::check_msg).
10
//! 2. Does the message comply with flow-control rules? (e.g., no more SENDMEs
11
//!    than we've sent data for.) This is handled within the reactor by the
12
//!    `StreamFlowCtrl`. For half-closed streams which don't send stream
13
//!    SENDMEs, an additional receive-window check is performed in the
14
//!    `halfstream` module.
15
//! 3. Does the message have an acceptable command type, and is the message
16
//!    well-formed? For open streams, the streams themselves handle this check.
17
//!    For half-closed streams, the reactor handles it by calling
18
//!    `consume_checked_msg()`.
19

            
20
pub(crate) mod circuit;
21
mod conflux;
22
mod control;
23

            
24
use crate::circuit::circhop::{ReactorStreamComponents, SendRelayCell};
25
use crate::circuit::{CircuitRxReceiver, UniqId};
26
use crate::client::circuit::ClientCircChanMsg;
27
use crate::client::circuit::padding::{PaddingController, PaddingEvent, PaddingEventStream};
28
use crate::client::{HopLocation, TargetHop};
29
use crate::crypto::cell::HopNum;
30
use crate::crypto::handshake::ntor_v3::NtorV3PublicKey;
31
use crate::memquota::CircuitAccount;
32
use crate::stream::CloseStreamBehavior;
33
use crate::streammap;
34
use crate::tunnel::{TunnelId, TunnelScopedCircId};
35
use crate::util::err::ReactorError;
36
use crate::util::skew::ClockSkew;
37
use crate::util::timeout::TimeoutEstimator;
38
use crate::{Error, Result};
39
use circuit::Circuit;
40
use conflux::ConfluxSet;
41
use control::ControlHandler;
42
use std::cmp::Ordering;
43
use std::collections::BinaryHeap;
44
use tor_basic_utils::onionperf_types::{OnionperfCircuitStatus, OnionperfEvent};
45
use tor_cell::relaycell::flow_ctrl::XonKBpsEwma;
46
use tor_cell::relaycell::msg::Sendme;
47
use tor_cell::relaycell::{AnyRelayMsgOuter, RelayCellFormat, StreamId, UnparsedRelayMsg};
48
use tor_error::{Bug, bad_api_usage, debug_report, internal, into_bad_api_usage};
49
use tor_rtcompat::{DynTimeProvider, SleepProvider};
50

            
51
use cfg_if::cfg_if;
52
use futures::StreamExt;
53
use futures::channel::mpsc;
54
use futures::{FutureExt as _, select_biased};
55
use oneshot_fused_workaround as oneshot;
56

            
57
use std::result::Result as StdResult;
58
use std::sync::Arc;
59
use std::time::Duration;
60

            
61
use crate::channel::Channel;
62
use crate::conflux::msghandler::RemoveLegReason;
63
use crate::crypto::handshake::ntor::{NtorClient, NtorPublicKey};
64
use circuit::CircuitCmd;
65
use derive_more::From;
66
use smallvec::smallvec;
67
use tor_cell::chancell::CircId;
68
use tor_llcrypto::pk;
69
use tracing::{debug, info, instrument, trace, warn};
70

            
71
use super::circuit::{MutableState, TunnelMutableState};
72
use crate::circuit::reactor::ReactorResultChannel;
73

            
74
#[cfg(feature = "hs-service")]
75
use crate::stream::incoming::IncomingStreamRequestHandler;
76

            
77
#[cfg(feature = "conflux")]
78
use {
79
    crate::conflux::msghandler::{ConfluxCmd, OooRelayMsg},
80
    crate::util::err::ConfluxHandshakeError,
81
};
82

            
83
pub(super) use control::{CtrlCmd, CtrlMsg, FlowCtrlMsg};
84

            
85
/// Contains a list of conflux handshake results.
86
#[cfg(feature = "conflux")]
87
pub(super) type ConfluxHandshakeResult = Vec<StdResult<(), ConfluxHandshakeError>>;
88

            
89
/// The type of oneshot channel used to inform reactor users of the outcome
90
/// of a client-side conflux handshake.
91
///
92
/// Contains a list of handshake results, one for each circuit that we were asked
93
/// to link in the tunnel.
94
#[cfg(feature = "conflux")]
95
pub(super) type ConfluxLinkResultChannel = ReactorResultChannel<ConfluxHandshakeResult>;
96

            
97
/// A handshake type, to be used when creating circuit hops.
98
#[derive(Clone, Debug)]
99
pub(crate) enum CircuitHandshake {
100
    /// Use the CREATE_FAST handshake.
101
    CreateFast,
102
    /// Use the ntor handshake.
103
    Ntor {
104
        /// The public key of the relay.
105
        public_key: NtorPublicKey,
106
        /// The Ed25519 identity of the relay, which is verified against the
107
        /// identity held in the circuit's channel.
108
        ed_identity: pk::ed25519::Ed25519Identity,
109
    },
110
    /// Use the ntor-v3 handshake.
111
    NtorV3 {
112
        /// The public key of the relay.
113
        public_key: NtorV3PublicKey,
114
    },
115
}
116

            
117
// TODO: the RunOnceCmd/RunOnceCmdInner/CircuitCmd/CircuitEvent enum
118
// proliferation is a bit bothersome, but unavoidable with the current design.
119
//
120
// We should consider getting rid of some of these enums (if possible),
121
// and coming up with more intuitive names.
122

            
123
/// One or more [`RunOnceCmdInner`] to run inside [`Reactor::run_once`].
124
#[derive(From, Debug)]
125
#[allow(clippy::large_enum_variant)] // TODO #2003: resolve this
126
enum RunOnceCmd {
127
    /// Run a single `RunOnceCmdInner` command.
128
    Single(RunOnceCmdInner),
129
    /// Run multiple `RunOnceCmdInner` commands.
130
    //
131
    // Note: this whole enum *could* be replaced with Vec<RunOnceCmdInner>,
132
    // but most of the time we're only going to have *one* RunOnceCmdInner
133
    // to run per run_once() loop. The enum enables us avoid the extra heap
134
    // allocation for the `RunOnceCmd::Single` case.
135
    Multiple(Vec<RunOnceCmdInner>),
136
}
137

            
138
/// Instructions for running something in the reactor loop.
139
///
140
/// Run at the end of [`Reactor::run_once`].
141
//
142
// TODO: many of the variants of this enum have an identical CtrlMsg counterpart.
143
// We should consider making each variant a tuple variant and deduplicating the fields.
144
#[derive(educe::Educe)]
145
#[educe(Debug)]
146
enum RunOnceCmdInner {
147
    /// Send a RELAY cell.
148
    Send {
149
        /// The leg the cell should be sent on.
150
        leg: UniqId,
151
        /// The cell to send.
152
        cell: SendRelayCell,
153
        /// A channel for sending completion notifications.
154
        done: Option<ReactorResultChannel<()>>,
155
    },
156
    /// Send a given control message on this circuit, and install a control-message handler to
157
    /// receive responses.
158
    #[cfg(feature = "send-control-msg")]
159
    SendMsgAndInstallHandler {
160
        /// The message to send, if any
161
        msg: Option<AnyRelayMsgOuter>,
162
        /// A message handler to install.
163
        ///
164
        /// If this is `None`, there must already be a message handler installed
165
        #[educe(Debug(ignore))]
166
        handler: Option<Box<dyn MetaCellHandler + Send + 'static>>,
167
        /// A sender that we use to tell the caller that the message was sent
168
        /// and the handler installed.
169
        done: oneshot::Sender<Result<()>>,
170
    },
171
    /// Handle a SENDME message.
172
    HandleSendMe {
173
        /// The leg the SENDME was received on.
174
        leg: UniqId,
175
        /// The hop number.
176
        hop: HopNum,
177
        /// The SENDME message to handle.
178
        sendme: Sendme,
179
    },
180
    /// Begin a stream with the provided hop in this circuit.
181
    ///
182
    /// Uses the provided stream ID, and sends the provided message to that hop.
183
    BeginStream {
184
        /// The cell to send.
185
        cell: SendRelayCell,
186
        /// The ID of the stream to return on the oneshot channel.
187
        stream_id: StreamId,
188
        /// The location of the hop on the tunnel. We don't use this (and `Circuit`s shouldn't need
189
        /// to worry about legs anyways), but need it so that we can pass it back in `done` to the
190
        /// caller.
191
        hop: HopLocation,
192
        /// The circuit leg to begin the stream on.
193
        leg: UniqId,
194
        /// Components that are needed to interact with the new stream.
195
        stream_components: ReactorStreamComponents,
196
        /// Oneshot channel to notify on completion, with the allocated stream ID.
197
        done: ReactorResultChannel<(
198
            StreamId,
199
            HopLocation,
200
            RelayCellFormat,
201
            ReactorStreamComponents,
202
        )>,
203
    },
204
    /// Consider sending an XON message with the given `rate`.
205
    MaybeSendXon {
206
        /// The drain rate to advertise in the XON message.
207
        rate: XonKBpsEwma,
208
        /// The ID of the stream to send the message on.
209
        stream_id: StreamId,
210
        /// The location of the hop on the tunnel.
211
        hop: HopLocation,
212
    },
213
    /// Close the specified stream.
214
    CloseStream {
215
        /// The hop number.
216
        hop: HopLocation,
217
        /// The ID of the stream to close.
218
        sid: StreamId,
219
        /// The stream-closing behavior.
220
        behav: CloseStreamBehavior,
221
        /// The reason for closing the stream.
222
        reason: streammap::TerminateReason,
223
        /// A channel for sending completion notifications.
224
        done: Option<ReactorResultChannel<()>>,
225
    },
226
    /// Get the clock skew claimed by the first hop of the circuit.
227
    FirstHopClockSkew {
228
        /// Oneshot channel to return the clock skew.
229
        answer: oneshot::Sender<StdResult<ClockSkew, Bug>>,
230
    },
231
    /// Remove a circuit leg from the conflux set.
232
    RemoveLeg {
233
        /// The circuit leg to remove.
234
        leg: UniqId,
235
        /// The reason for removal.
236
        ///
237
        /// This is only used for conflux circuits that get removed
238
        /// before the conflux handshake is complete.
239
        ///
240
        /// The [`RemoveLegReason`] is mapped by the reactor to a
241
        /// [`ConfluxHandshakeError`] that is sent to the initiator of the
242
        /// handshake to indicate the reason the handshake failed.
243
        reason: RemoveLegReason,
244
    },
245
    /// A circuit has completed the conflux handshake,
246
    /// and wants to send the specified cell.
247
    ///
248
    /// This is similar to [`RunOnceCmdInner::Send`],
249
    /// but needs to remain a separate variant,
250
    /// because in addition to instructing the reactor to send a cell,
251
    /// it also notifies it that the conflux handshake is complete on the specified `leg`.
252
    /// This enables the reactor to save the handshake result (`Ok(())`),
253
    /// and, if there are no other legs still in the handshake phase,
254
    /// send the result to the handshake initiator.
255
    #[cfg(feature = "conflux")]
256
    ConfluxHandshakeComplete {
257
        /// The circuit leg that has completed the handshake,
258
        /// This is the leg the cell should be sent on.
259
        leg: UniqId,
260
        /// The cell to send.
261
        cell: SendRelayCell,
262
    },
263
    /// Send a LINK cell on each of the unlinked circuit legs in the conflux set of this reactor.
264
    #[cfg(feature = "conflux")]
265
    Link {
266
        /// The circuits to link into the tunnel
267
        #[educe(Debug(ignore))]
268
        circuits: Vec<Circuit>,
269
        /// Oneshot channel for notifying of conflux handshake completion.
270
        answer: ConfluxLinkResultChannel,
271
    },
272
    /// Enqueue an out-of-order cell in ooo_msg.
273
    #[cfg(feature = "conflux")]
274
    Enqueue {
275
        /// The leg the entry originated from.
276
        leg: UniqId,
277
        /// The out-of-order message.
278
        msg: OooRelayMsg,
279
    },
280
    /// Take a padding-related event on a circuit leg.
281
    #[cfg(feature = "circ-padding")]
282
    PaddingAction {
283
        /// The leg to event on.
284
        leg: UniqId,
285
        /// The event to take.
286
        padding_event: PaddingEvent,
287
    },
288
    /// Perform a clean shutdown on this circuit.
289
    CleanShutdown,
290
}
291

            
292
impl RunOnceCmdInner {
293
    /// Create a [`RunOnceCmdInner`] out of a [`CircuitCmd`] and [`UniqId`].
294
4396
    fn from_circuit_cmd(leg: UniqId, cmd: CircuitCmd) -> Self {
295
92
        match cmd {
296
4180
            CircuitCmd::Send(cell) => Self::Send {
297
4180
                leg,
298
4180
                cell,
299
4180
                done: None,
300
4180
            },
301
44
            CircuitCmd::HandleSendMe { hop, sendme } => Self::HandleSendMe { leg, hop, sendme },
302
            CircuitCmd::CloseStream {
303
52
                hop,
304
52
                sid,
305
52
                behav,
306
52
                reason,
307
52
            } => Self::CloseStream {
308
52
                hop: HopLocation::Hop((leg, hop)),
309
52
                sid,
310
52
                behav,
311
52
                reason,
312
52
                done: None,
313
52
            },
314
            #[cfg(feature = "conflux")]
315
24
            CircuitCmd::Conflux(ConfluxCmd::RemoveLeg(reason)) => Self::RemoveLeg { leg, reason },
316
            #[cfg(feature = "conflux")]
317
68
            CircuitCmd::Conflux(ConfluxCmd::HandshakeComplete { hop, early, cell }) => {
318
68
                let cell = SendRelayCell {
319
68
                    hop: Some(hop),
320
68
                    early,
321
68
                    cell,
322
68
                };
323
68
                Self::ConfluxHandshakeComplete { leg, cell }
324
            }
325
            #[cfg(feature = "conflux")]
326
16
            CircuitCmd::Enqueue(msg) => Self::Enqueue { leg, msg },
327
12
            CircuitCmd::CleanShutdown => Self::CleanShutdown,
328
        }
329
4396
    }
330
}
331

            
332
/// A command to execute at the end of [`Reactor::run_once`].
333
#[derive(From, Debug)]
334
#[allow(clippy::large_enum_variant)] // TODO #2003: should we resolve this?
335
enum CircuitEvent {
336
    /// Run a single `CircuitCmd` command.
337
    RunCmd {
338
        /// The unique identifier of the circuit leg to run the command on
339
        leg: UniqId,
340
        /// The command to run.
341
        cmd: CircuitCmd,
342
    },
343
    /// Handle a control message
344
    HandleControl(CtrlMsg),
345
    /// Handle an input message.
346
    HandleCell {
347
        /// The unique identifier of the circuit leg the message was received on.
348
        leg: UniqId,
349
        /// The message to handle.
350
        cell: ClientCircChanMsg,
351
    },
352
    /// Remove the specified circuit leg from the conflux set.
353
    ///
354
    /// Returned whenever a single circuit leg needs to be removed
355
    /// from the reactor's conflux set, without necessarily tearing down
356
    /// the whole set or shutting down the reactor.
357
    ///
358
    /// Note: this event *can* cause the reactor to shut down
359
    /// (and the conflux set to be closed).
360
    ///
361
    /// See the [`ConfluxSet::remove`] docs for more on the exact behavior of this command.
362
    RemoveLeg {
363
        /// The leg to remove.
364
        leg: UniqId,
365
        /// The reason for removal.
366
        ///
367
        /// This is only used for conflux circuits that get removed
368
        /// before the conflux handshake is complete.
369
        ///
370
        /// The [`RemoveLegReason`] is mapped by the reactor to a
371
        /// [`ConfluxHandshakeError`] that is sent to the initiator of the
372
        /// handshake to indicate the reason the handshake failed.
373
        reason: RemoveLegReason,
374
    },
375
    /// Take some event (blocking or unblocking a circuit, or sending padding)
376
    /// based on the circuit padding backend code.
377
    PaddingAction {
378
        /// The leg on which to take the padding event .
379
        leg: UniqId,
380
        /// The event to take.
381
        padding_event: PaddingEvent,
382
    },
383
    /// Protocol violation. This leads for now to the close of the circuit reactor. The
384
    /// error causing the violation is set in err.
385
    ProtoViolation {
386
        /// The error that causes this protocol violation.
387
        err: crate::Error,
388
    },
389
}
390

            
391
impl CircuitEvent {
392
    /// Return the ordering with which we should handle this event
393
    /// within a list of events returned by a single call to next_circ_event().
394
    ///
395
    /// NOTE: Please do not make this any more complicated:
396
    /// It is a consequence of a kludge that we need this sorting at all.
397
    /// Assuming that eventually, we switch away from the current
398
    /// poll-oriented `next_circ_event` design,
399
    /// we may be able to get rid of this entirely.
400
88
    fn order_within_batch(&self) -> u8 {
401
        use CircuitEvent as CA;
402
        use PaddingEvent as PE;
403
        // This immediate state MUST NOT be used for events emitting cells. At the moment, it is
404
        // only used by the protocol violation event which leads to a shutdown of the reactor.
405
        const IMMEDIATE: u8 = 0;
406
        const EARLY: u8 = 1;
407
        const NORMAL: u8 = 2;
408
        const LATE: u8 = 3;
409

            
410
        // We use this ordering to move any "StartBlocking" to the _end_ of a batch and
411
        // "StopBlocking" to the start.
412
        //
413
        // This way, we can be sure that we will handle any "send data" operations
414
        // (and tell the Padder about them) _before_  we tell the Padder
415
        // that we have blocked the circuit.
416
        //
417
        // This keeps things a bit more logical.
418
88
        match self {
419
22
            CA::RunCmd { .. } => NORMAL,
420
            CA::HandleControl(..) => NORMAL,
421
64
            CA::HandleCell { .. } => NORMAL,
422
2
            CA::RemoveLeg { .. } => NORMAL,
423
            #[cfg(feature = "circ-padding")]
424
            CA::PaddingAction { padding_event, .. } => match padding_event {
425
                PE::StopBlocking => EARLY,
426
                PE::SendPadding(..) => NORMAL,
427
                PE::StartBlocking(..) => LATE,
428
            },
429
            #[cfg(not(feature = "circ-padding"))]
430
            CA::PaddingAction { .. } => NORMAL,
431
            CA::ProtoViolation { .. } => IMMEDIATE,
432
        }
433
88
    }
434
}
435

            
436
/// An object that's waiting for a meta cell (one not associated with a stream) in order to make
437
/// progress.
438
///
439
/// # Background
440
///
441
/// The `Reactor` can't have async functions that send and receive cells, because its job is to
442
/// send and receive cells: if one of its functions tried to do that, it would just hang forever.
443
///
444
/// To get around this problem, the reactor can send some cells, and then make one of these
445
/// `MetaCellHandler` objects, which will be run when the reply arrives.
446
pub(crate) trait MetaCellHandler: Send {
447
    /// The hop we're expecting the message to come from. This is compared against the hop
448
    /// from which we actually receive messages, and an error is thrown if the two don't match.
449
    fn expected_hop(&self) -> HopLocation;
450
    /// Called when the message we were waiting for arrives.
451
    ///
452
    /// Gets a copy of the `Reactor` in order to do anything it likes there.
453
    ///
454
    /// If this function returns an error, the reactor will shut down.
455
    fn handle_msg(
456
        &mut self,
457
        msg: UnparsedRelayMsg,
458
        reactor: &mut Circuit,
459
    ) -> Result<MetaCellDisposition>;
460
}
461

            
462
/// A possible successful outcome of giving a message to a [`MsgHandler`](super::msghandler::MsgHandler).
463
#[derive(Debug, Clone, PartialEq)]
464
#[cfg_attr(feature = "send-control-msg", visibility::make(pub))]
465
#[non_exhaustive]
466
pub(crate) enum MetaCellDisposition {
467
    /// The message was consumed; the handler should remain installed.
468
    #[cfg(feature = "send-control-msg")]
469
    Consumed,
470
    /// The message was consumed; the handler should be uninstalled.
471
    ConversationFinished,
472
    /// The message was consumed; the circuit should be closed.
473
    #[cfg(feature = "send-control-msg")]
474
    CloseCirc,
475
    // TODO: Eventually we might want the ability to have multiple handlers
476
    // installed, and to let them say "not for me, maybe for somebody else?".
477
    // But right now we don't need that.
478
}
479

            
480
/// Unwrap the specified [`Option`], returning a [`ReactorError::Shutdown`] if it is `None`.
481
///
482
/// This is a macro instead of a function to work around borrowck errors
483
/// in the select! from run_once().
484
macro_rules! unwrap_or_shutdown {
485
    ($self:expr, $res:expr, $reason:expr) => {{
486
        match $res {
487
            None => {
488
                trace!(
489
                    tunnel_id = %$self.tunnel_id,
490
                    reason = %$reason,
491
                    "reactor shutdown"
492
                );
493
                Err(ReactorError::Shutdown)
494
            }
495
            Some(v) => Ok(v),
496
        }
497
    }};
498
}
499

            
500
/// Object to handle incoming cells and background tasks on a circuit
501
///
502
/// This type is returned when you finish a circuit; you need to spawn a
503
/// new task that calls `run()` on it.
504
#[must_use = "If you don't call run() on a reactor, the circuit won't work."]
505
pub struct Reactor {
506
    /// Receiver for control messages for this reactor, sent by `ClientCirc` objects.
507
    ///
508
    /// This channel is polled in [`Reactor::run_once`], but only if the `chan_sender` sink
509
    /// is ready to accept cells.
510
    control: mpsc::UnboundedReceiver<CtrlMsg>,
511
    /// Receiver for command messages for this reactor, sent by `ClientCirc` objects.
512
    ///
513
    /// This channel is polled in [`Reactor::run_once`].
514
    ///
515
    /// NOTE: this is a separate channel from `control`, because some messages
516
    /// have higher priority and need to be handled even if the `chan_sender` is not
517
    /// ready (whereas `control` messages are not read until the `chan_sender` sink
518
    /// is ready to accept cells).
519
    command: mpsc::UnboundedReceiver<CtrlCmd>,
520
    /// A oneshot sender that is used to alert other tasks when this reactor is
521
    /// finally dropped.
522
    ///
523
    /// It is a sender for Void because we never actually want to send anything here;
524
    /// we only want to generate canceled events.
525
    #[allow(dead_code)] // the only purpose of this field is to be dropped.
526
    reactor_closed_tx: oneshot::Sender<void::Void>,
527
    /// A set of circuits that form a tunnel.
528
    ///
529
    /// Contains 1 or more circuits.
530
    ///
531
    /// Circuits may be added to this set throughout the lifetime of the reactor.
532
    ///
533
    /// Sometimes, the reactor will remove circuits from this set,
534
    /// for example if the `LINKED` message takes too long to arrive,
535
    /// or if congestion control negotiation fails.
536
    /// The reactor will continue running with the remaining circuits.
537
    /// It will shut down if *all* the circuits are removed.
538
    ///
539
    // TODO(conflux): document all the reasons why the reactor might
540
    // chose to tear down a circuit or tunnel (timeouts, protocol violations, etc.)
541
    circuits: ConfluxSet,
542
    /// An identifier for logging about this tunnel reactor.
543
    tunnel_id: TunnelId,
544
    /// Handlers, shared with `Circuit`.
545
    cell_handlers: CellHandlers,
546
    /// The time provider, used for conflux handshake timeouts.
547
    runtime: DynTimeProvider,
548
    /// The conflux handshake context, if there is an on-going handshake.
549
    ///
550
    /// Set to `None` if this is a single-path tunnel,
551
    /// or if none of the circuit legs from our conflux set
552
    /// are currently in the conflux handshake phase.
553
    #[cfg(feature = "conflux")]
554
    conflux_hs_ctx: Option<ConfluxHandshakeCtx>,
555
    /// A min-heap buffering all the out-of-order messages received so far.
556
    ///
557
    /// TODO(conflux): this becomes a DoS vector unless we impose a limit
558
    /// on its size. We should make this participate in the memquota memory
559
    /// tracking system, somehow.
560
    #[cfg(feature = "conflux")]
561
    ooo_msgs: BinaryHeap<ConfluxHeapEntry>,
562
}
563

            
564
/// The context for an on-going conflux handshake.
565
#[cfg(feature = "conflux")]
566
struct ConfluxHandshakeCtx {
567
    /// A channel for notifying the caller of the outcome of a CONFLUX_LINK request.
568
    answer: ConfluxLinkResultChannel,
569
    /// The number of legs that are currently doing the handshake.
570
    num_legs: usize,
571
    /// The handshake results we have collected so far.
572
    results: ConfluxHandshakeResult,
573
}
574

            
575
/// An out-of-order message buffered in [`Reactor::ooo_msgs`].
576
#[derive(Debug)]
577
#[cfg(feature = "conflux")]
578
struct ConfluxHeapEntry {
579
    /// The leg id this message came from.
580
    leg_id: UniqId,
581
    /// The out of order message
582
    msg: OooRelayMsg,
583
}
584

            
585
#[cfg(feature = "conflux")]
586
impl Ord for ConfluxHeapEntry {
587
4
    fn cmp(&self, other: &Self) -> Ordering {
588
4
        self.msg.cmp(&other.msg)
589
4
    }
590
}
591

            
592
#[cfg(feature = "conflux")]
593
impl PartialOrd for ConfluxHeapEntry {
594
4
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
595
4
        Some(self.cmp(other))
596
4
    }
597
}
598

            
599
#[cfg(feature = "conflux")]
600
impl PartialEq for ConfluxHeapEntry {
601
    fn eq(&self, other: &Self) -> bool {
602
        self.msg == other.msg
603
    }
604
}
605

            
606
#[cfg(feature = "conflux")]
607
impl Eq for ConfluxHeapEntry {}
608

            
609
/// Cell handlers, shared between the Reactor and its underlying `Circuit`s.
610
struct CellHandlers {
611
    /// A handler for a meta cell, together with a result channel to notify on completion.
612
    ///
613
    /// NOTE(prop349): this is part of Arti's "Base Circuit Hop Handler".
614
    ///
615
    /// Upon sending an EXTEND cell, the [`ControlHandler`] sets this handler
616
    /// to [`CircuitExtender`](circuit::extender::CircuitExtender).
617
    /// The handler is then used in [`Circuit::handle_meta_cell`] for handling
618
    /// all the meta cells received on the circuit that are not SENDMEs or TRUNCATE
619
    /// (which are handled separately) or conflux cells
620
    /// (which are handled by the conflux handlers).
621
    ///
622
    /// The handler is uninstalled after the receipt of the EXTENDED cell,
623
    /// so any subsequent EXTENDED cells will cause the circuit to be torn down.
624
    meta_handler: Option<Box<dyn MetaCellHandler + Send>>,
625
    /// A handler for incoming stream requests.
626
    #[cfg(feature = "hs-service")]
627
    incoming_stream_req_handler: Option<IncomingStreamRequestHandler>,
628
}
629

            
630
impl Reactor {
631
    /// Create a new circuit reactor.
632
    ///
633
    /// The reactor will send outbound messages on `channel`, receive incoming
634
    /// messages on `input`, and identify this circuit by the channel-local
635
    /// [`CircId`] provided.
636
    ///
637
    /// The internal unique identifier for this circuit will be `unique_id`.
638
    #[allow(clippy::type_complexity, clippy::too_many_arguments)] // TODO
639
406
    pub(super) fn new(
640
406
        channel: Arc<Channel>,
641
406
        circ_id: CircId,
642
406
        unique_id: UniqId,
643
406
        input: CircuitRxReceiver,
644
406
        runtime: DynTimeProvider,
645
406
        memquota: CircuitAccount,
646
406
        padding_ctrl: PaddingController,
647
406
        padding_stream: PaddingEventStream,
648
406
        timeouts: Arc<dyn TimeoutEstimator + Send>,
649
406
    ) -> (
650
406
        Self,
651
406
        mpsc::UnboundedSender<CtrlMsg>,
652
406
        mpsc::UnboundedSender<CtrlCmd>,
653
406
        oneshot::Receiver<void::Void>,
654
406
        Arc<TunnelMutableState>,
655
406
    ) {
656
406
        let tunnel_id = TunnelId::next();
657
406
        let (control_tx, control_rx) = mpsc::unbounded();
658
406
        let (command_tx, command_rx) = mpsc::unbounded();
659
406
        let mutable = Arc::new(MutableState::default());
660

            
661
406
        let (reactor_closed_tx, reactor_closed_rx) = oneshot::channel();
662

            
663
406
        let cell_handlers = CellHandlers {
664
406
            meta_handler: None,
665
406
            #[cfg(feature = "hs-service")]
666
406
            incoming_stream_req_handler: None,
667
406
        };
668

            
669
406
        let unique_id = TunnelScopedCircId::new(tunnel_id, unique_id);
670
406
        let circuit_leg = Circuit::new(
671
406
            runtime.clone(),
672
406
            channel,
673
406
            circ_id,
674
406
            unique_id,
675
406
            input,
676
406
            memquota,
677
406
            Arc::clone(&mutable),
678
406
            padding_ctrl,
679
406
            padding_stream,
680
406
            timeouts,
681
        );
682

            
683
406
        let (circuits, mutable) = ConfluxSet::new(tunnel_id, circuit_leg);
684

            
685
406
        let reactor = Reactor {
686
406
            circuits,
687
406
            control: control_rx,
688
406
            command: command_rx,
689
406
            reactor_closed_tx,
690
406
            tunnel_id,
691
406
            cell_handlers,
692
406
            runtime,
693
406
            #[cfg(feature = "conflux")]
694
406
            conflux_hs_ctx: None,
695
406
            #[cfg(feature = "conflux")]
696
406
            ooo_msgs: Default::default(),
697
406
        };
698

            
699
406
        (reactor, control_tx, command_tx, reactor_closed_rx, mutable)
700
406
    }
701

            
702
    /// Launch the reactor, and run until the circuit closes or we
703
    /// encounter an error.
704
    ///
705
    /// Once this method returns, the circuit is dead and cannot be
706
    /// used again.
707
    #[instrument(level = "trace", skip_all)]
708
609
    pub async fn run(mut self) -> Result<()> {
709
        trace!(tunnel_id = %self.tunnel_id, "Running tunnel reactor");
710
        let result: Result<()> = loop {
711
            match self.run_once().await {
712
                Ok(()) => (),
713
                Err(ReactorError::Shutdown) => break Ok(()),
714
                Err(ReactorError::Err(e)) => break Err(e),
715
            }
716
        };
717

            
718
        tracing::trace!(
719
            onionperf = true,
720
            tid = %self.tunnel_id,
721
            event = ?&OnionperfEvent::Circuit(OnionperfCircuitStatus::Closed)
722
        );
723

            
724
        // Log that the reactor stopped, possibly with the associated error as a report.
725
        // May log at a higher level depending on the error kind.
726
        const MSG: &str = "Tunnel reactor stopped";
727
        match &result {
728
            Ok(()) => trace!(tunnel_id = %self.tunnel_id, "{MSG}"),
729
            Err(e) => debug_report!(e, tunnel_id = %self.tunnel_id, "{MSG}"),
730
        }
731

            
732
        result
733
324
    }
734

            
735
    /// Helper for run: doesn't mark the circuit closed on finish.  Only
736
    /// processes one cell or control message.
737
    #[instrument(level = "trace", skip_all)]
738
9591
    async fn run_once(&mut self) -> StdResult<(), ReactorError> {
739
        // If all the circuits are closed, shut down the reactor
740
        if self.circuits.is_empty() {
741
            trace!(
742
                tunnel_id = %self.tunnel_id,
743
                "Tunnel reactor shutting down: all circuits have closed",
744
            );
745

            
746
            return Err(ReactorError::Shutdown);
747
        }
748

            
749
        // If this is a single path circuit, we need to wait until the first hop
750
        // is created before doing anything else
751
        let single_path_with_hops = self
752
            .circuits
753
            .single_leg_mut()
754
4944
            .is_ok_and(|leg| !leg.has_hops());
755
        if single_path_with_hops {
756
            self.wait_for_create().await?;
757

            
758
            return Ok(());
759
        }
760

            
761
        // Prioritize the buffered messages.
762
        //
763
        // Note: if any of the messages are ready to be handled,
764
        // this will block the reactor until we are done processing them
765
        //
766
        // TODO circpad: If this is a problem, we might want to re-order things so that we
767
        // prioritize padding instead.  On the other hand, this should be fixed by refactoring
768
        // circuit and tunnel reactors, so we might do well to just leave it alone for now.
769
        #[cfg(feature = "conflux")]
770
        self.try_dequeue_ooo_msgs().await?;
771

            
772
        let mut events = select_biased! {
773
            res = self.command.next() => {
774
                let cmd = unwrap_or_shutdown!(self, res, "command channel drop")?;
775
                return ControlHandler::new(self).handle_cmd(cmd);
776
            },
777
            // Check whether we've got a control message pending.
778
            //
779
            // Note: unfortunately, reading from control here means we might start
780
            // handling control messages before our chan_senders are ready.
781
            // With the current design, this is inevitable: we can't know which circuit leg
782
            // a control message is meant for without first reading the control message from
783
            // the channel, and at that point, we can't know for sure whether that particular
784
            // circuit is ready for sending.
785
            ret = self.control.next() => {
786
                let msg = unwrap_or_shutdown!(self, ret, "control drop")?;
787
                smallvec![CircuitEvent::HandleControl(msg)]
788
            },
789
            res = self.circuits.next_circ_event(&self.runtime).fuse() => res?,
790
        };
791

            
792
        // Put the events into the order that we need to execute them in.
793
        //
794
        // (Yes, this _does_ have to be a stable sort.  Not all events may be freely re-ordered
795
        // with respect to one another.)
796
88
        events.sort_by_key(|a| a.order_within_batch());
797

            
798
        for event in events {
799
            let cmd = match event {
800
                CircuitEvent::RunCmd { leg, cmd } => Some(RunOnceCmd::Single(
801
                    RunOnceCmdInner::from_circuit_cmd(leg, cmd),
802
                )),
803
                CircuitEvent::HandleControl(ctrl) => ControlHandler::new(self)
804
                    .handle_msg(ctrl)?
805
                    .map(RunOnceCmd::Single),
806
                CircuitEvent::HandleCell { leg, cell } => {
807
                    let circ = self
808
                        .circuits
809
                        .leg_mut(leg)
810
                        .ok_or_else(|| internal!("the circuit leg we just had disappeared?!"))?;
811

            
812
                    let circ_cmds = circ.handle_cell(&mut self.cell_handlers, leg, cell)?;
813
                    if circ_cmds.is_empty() {
814
                        None
815
                    } else {
816
                        // TODO: we return RunOnceCmd::Multiple even if there's a single command.
817
                        //
818
                        // See the TODO on `Circuit::handle_cell`.
819
                        let cmd = RunOnceCmd::Multiple(
820
                            circ_cmds
821
                                .into_iter()
822
164
                                .map(|cmd| RunOnceCmdInner::from_circuit_cmd(leg, cmd))
823
                                .collect(),
824
                        );
825

            
826
                        Some(cmd)
827
                    }
828
                }
829
                CircuitEvent::RemoveLeg { leg, reason } => {
830
                    Some(RunOnceCmdInner::RemoveLeg { leg, reason }.into())
831
                }
832
                CircuitEvent::PaddingAction { leg, padding_event } => {
833
                    cfg_if! {
834
                        if #[cfg(feature = "circ-padding")] {
835
                            Some(RunOnceCmdInner::PaddingAction { leg, padding_event }.into())
836
                        } else {
837
                            // If padding isn't enabled, we never generate a padding event,
838
                            // so we can be sure this case will never be called.
839
                            void::unreachable(padding_event.0);
840
                        }
841
                    }
842
                }
843
                CircuitEvent::ProtoViolation { err } => {
844
                    return Err(err.into());
845
                }
846
            };
847

            
848
            if let Some(cmd) = cmd {
849
                self.handle_run_once_cmd(cmd).await?;
850
            }
851
        }
852

            
853
        Ok(())
854
6342
    }
855

            
856
    /// Try to process the previously-out-of-order messages we might have buffered.
857
    #[cfg(feature = "conflux")]
858
    #[instrument(level = "trace", skip_all)]
859
8958
    async fn try_dequeue_ooo_msgs(&mut self) -> StdResult<(), ReactorError> {
860
        // Check if we're ready to dequeue any of the previously out-of-order cells.
861
        while let Some(entry) = self.ooo_msgs.peek() {
862
            let should_pop = self.circuits.is_seqno_in_order(entry.msg.seqno);
863

            
864
            if !should_pop {
865
                break;
866
            }
867

            
868
            let entry = self.ooo_msgs.pop().expect("item just disappeared?!");
869

            
870
            let circ = self
871
                .circuits
872
                .leg_mut(entry.leg_id)
873
                .ok_or_else(|| internal!("the circuit leg we just had disappeared?!"))?;
874
            let handlers = &mut self.cell_handlers;
875
            let cmd = circ
876
                .handle_in_order_relay_msg(
877
                    handlers,
878
                    entry.msg.hopnum,
879
                    entry.leg_id,
880
                    entry.msg.cell_counts_towards_windows,
881
                    entry.msg.streamid,
882
                    entry.msg.msg,
883
                )?
884
                .map(|cmd| {
885
                    RunOnceCmd::Single(RunOnceCmdInner::from_circuit_cmd(entry.leg_id, cmd))
886
                });
887

            
888
            if let Some(cmd) = cmd {
889
                self.handle_run_once_cmd(cmd).await?;
890
            }
891
        }
892

            
893
        Ok(())
894
5972
    }
895

            
896
    /// Handle a [`RunOnceCmd`].
897
    #[instrument(level = "trace", skip_all)]
898
6993
    async fn handle_run_once_cmd(&mut self, cmd: RunOnceCmd) -> StdResult<(), ReactorError> {
899
4662
        match cmd {
900
            RunOnceCmd::Single(cmd) => return self.handle_single_run_once_cmd(cmd).await,
901
            RunOnceCmd::Multiple(cmds) => {
902
                // While we know `sendable` is ready to accept *one* cell,
903
                // we can't be certain it will be able to accept *all* of the cells
904
                // that need to be sent here. This means we *may* end up buffering
905
                // in its underlying SometimesUnboundedSink! That is OK, because
906
                // RunOnceCmd::Multiple is only used for handling packed cells.
907
                for cmd in cmds {
908
                    self.handle_single_run_once_cmd(cmd).await?;
909
                }
910
            }
911
        }
912

            
913
        Ok(())
914
4662
    }
915

            
916
    /// Handle a [`RunOnceCmd`].
917
    #[instrument(level = "trace", skip_all)]
918
4662
    async fn handle_single_run_once_cmd(
919
4662
        &mut self,
920
4662
        cmd: RunOnceCmdInner,
921
6993
    ) -> StdResult<(), ReactorError> {
922
4662
        match cmd {
923
            RunOnceCmdInner::Send { leg, cell, done } => {
924
                // TODO: check the cc window
925
                let res = self.circuits.send_relay_cell_on_leg(cell, Some(leg)).await;
926
                if let Some(done) = done {
927
                    // Don't care if the receiver goes away
928
                    let _ = done.send(res.clone());
929
                }
930
                res?;
931
            }
932
            #[cfg(feature = "send-control-msg")]
933
            RunOnceCmdInner::SendMsgAndInstallHandler { msg, handler, done } => {
934
                let cell: Result<Option<SendRelayCell>> =
935
                    self.prepare_msg_and_install_handler(msg, handler);
936

            
937
                match cell {
938
                    Ok(Some(cell)) => {
939
                        // TODO(conflux): let the RunOnceCmdInner specify which leg to send the cell on
940
                        let outcome = self.circuits.send_relay_cell_on_leg(cell, None).await;
941
                        // don't care if receiver goes away.
942
                        let _ = done.send(outcome.clone());
943
                        outcome?;
944
                    }
945
                    Ok(None) => {
946
                        // don't care if receiver goes away.
947
                        let _ = done.send(Ok(()));
948
                    }
949
                    Err(e) => {
950
                        // don't care if receiver goes away.
951
                        let _ = done.send(Err(e.clone()));
952
                        return Err(e.into());
953
                    }
954
                }
955
            }
956
            RunOnceCmdInner::BeginStream {
957
                leg,
958
                cell,
959
                stream_id,
960
                hop,
961
                stream_components,
962
                done,
963
            } => {
964
                let circ = self
965
                    .circuits
966
                    .leg_mut(leg)
967
                    .ok_or_else(|| internal!("leg disappeared?!"))?;
968
                let cell_hop = cell.hop.expect("missing hop in client SendRelayCell?!");
969
                let relay_format = circ
970
                    .hop_mut(cell_hop)
971
                    // TODO: Is this the right error type here? Or should there be a "HopDisappeared"?
972
                    .ok_or(Error::NoSuchHop)?
973
                    .relay_cell_format();
974

            
975
                let outcome = self.circuits.send_relay_cell_on_leg(cell, Some(leg)).await;
976
                // don't care if receiver goes away.
977
                let _ = done.send(
978
                    outcome
979
                        .clone()
980
96
                        .map(|_| (stream_id, hop, relay_format, stream_components)),
981
                );
982
                outcome?;
983
            }
984
            RunOnceCmdInner::CloseStream {
985
                hop,
986
                sid,
987
                behav,
988
                reason,
989
                done,
990
            } => {
991
                let result = {
992
                    let (leg_id, hop_num) = self
993
                        .resolve_hop_location(hop)
994
                        .map_err(into_bad_api_usage!("Could not resolve {hop:?}"))?;
995
                    let leg = self
996
                        .circuits
997
                        .leg_mut(leg_id)
998
                        .ok_or(bad_api_usage!("No leg for id {:?}", leg_id))?;
999
                    Ok::<_, Bug>((leg, hop_num))
                };
                let (leg, hop_num) = match result {
                    Ok(x) => x,
                    Err(e) => {
                        if let Some(done) = done {
                            // don't care if the sender goes away
                            let e = into_bad_api_usage!("Could not resolve {hop:?}")(e);
                            let _ = done.send(Err(e.into()));
                        }
                        return Ok(());
                    }
                };
                let max_rtt = {
                    let hop = leg
                        .hop(hop_num)
                        .ok_or_else(|| internal!("the hop we resolved disappeared?!"))?;
                    let ccontrol = hop.ccontrol();
                    // Note: if we have no measurements for the RTT, this will be set to 0,
                    // and the timeout will be 2 * CBT.
                    ccontrol
                        .rtt()
                        .max_rtt_usec()
                        .map(|rtt| Duration::from_millis(u64::from(rtt)))
                        .unwrap_or_default()
                };
                // The length of the circuit up until the hop that has the half-streeam.
                //
                // +1, because HopNums are zero-based.
                let circ_len = usize::from(hop_num) + 1;
                // We double the CBT to account for rend circuits,
                // which are twice as long (otherwise we risk expiring
                // the rend half-streams too soon).
                let timeout = std::cmp::max(max_rtt, 2 * leg.estimate_cbt(circ_len));
                let expire_at = self.runtime.now() + timeout;
                let res: Result<()> = leg
                    .close_stream(hop_num, sid, behav, reason, expire_at)
                    .await;
                if let Some(done) = done {
                    // don't care if the sender goes away
                    let _ = done.send(res);
                }
            }
            RunOnceCmdInner::MaybeSendXon {
                rate,
                stream_id,
                hop,
            } => {
                let (leg_id, hop_num) = match self.resolve_hop_location(hop) {
                    Ok(x) => x,
                    Err(NoJoinPointError) => {
                        // A stream tried to send an XON message message to the join point of
                        // a tunnel that has never had a join point. Currently in arti, only a
                        // `StreamTarget` asks us to send an XON message, and this tunnel
                        // originally created the `StreamTarget` to begin with. So this is a
                        // legitimate bug somewhere in the tunnel code.
                        return Err(
                            internal!(
                                "Could not send an XON message to a join point on a tunnel without a join point",
                            )
                            .into()
                        );
                    }
                };
                let Some(leg) = self.circuits.leg_mut(leg_id) else {
                    // The leg has disappeared. This is fine since the stream may have ended and
                    // been cleaned up while this `CtrlMsg::MaybeSendXon` message was queued.
                    // It is possible that is a bug and this is an incorrect leg number, but
                    // it's not currently possible to differentiate between an incorrect leg
                    // number and a tunnel leg that has been closed.
                    debug!("Could not send an XON message on a leg that does not exist. Ignoring.");
                    return Ok(());
                };
                let Some(hop) = leg.hop_mut(hop_num) else {
                    // The hop has disappeared. This is fine since the circuit may have been
                    // been truncated while the `CtrlMsg::MaybeSendXon` message was queued.
                    // It is possible that is a bug and this is an incorrect hop number, but
                    // it's not currently possible to differentiate between an incorrect hop
                    // number and a circuit hop that has been removed.
                    debug!("Could not send an XON message on a hop that does not exist. Ignoring.");
                    return Ok(());
                };
                let Some(msg) = hop.maybe_send_xon(rate, stream_id)? else {
                    // Nothing to do.
                    return Ok(());
                };
                let cell = AnyRelayMsgOuter::new(Some(stream_id), msg.into());
                let cell = SendRelayCell {
                    hop: Some(hop_num),
                    early: false,
                    cell,
                };
                leg.send_relay_cell(cell).await?;
            }
            RunOnceCmdInner::HandleSendMe { leg, hop, sendme } => {
                let leg = self
                    .circuits
                    .leg_mut(leg)
                    .ok_or_else(|| internal!("leg disappeared?!"))?;
                // NOTE: it's okay to await. We are only awaiting on the congestion_signals
                // future which *should* resolve immediately
                let signals = leg.chan_sender.congestion_signals().await;
                leg.handle_sendme(hop, sendme, signals)?;
            }
            RunOnceCmdInner::FirstHopClockSkew { answer } => {
                let res = self.circuits.single_leg_mut().map(|leg| leg.clock_skew());
                // don't care if the sender goes away
                let _ = answer.send(res.map_err(Into::into));
            }
            RunOnceCmdInner::CleanShutdown => {
                trace!(tunnel_id = %self.tunnel_id, "reactor shutdown due to handled cell");
                return Err(ReactorError::Shutdown);
            }
            RunOnceCmdInner::RemoveLeg { leg, reason } => {
                debug!(tunnel_id = %self.tunnel_id, reason = %reason, "removing circuit leg");
                let circ = self.circuits.remove(leg)?;
                let is_conflux_pending = circ.is_conflux_pending();
                // Drop the removed leg. This will cause it to close if it's not already closed.
                drop(circ);
                // If we reach this point, it means we have more than one leg
                // (otherwise the .remove() would've returned a Shutdown error),
                // so we expect there to be a ConfluxHandshakeContext installed.
                #[cfg(feature = "conflux")]
                if is_conflux_pending {
                    let (error, proto_violation): (_, Option<Error>) = match &reason {
                        RemoveLegReason::ConfluxHandshakeTimeout => {
                            (ConfluxHandshakeError::Timeout, None)
                        }
                        RemoveLegReason::ConfluxHandshakeErr(e) => {
                            (ConfluxHandshakeError::Link(e.clone()), Some(e.clone()))
                        }
                        RemoveLegReason::ChannelClosed => {
                            (ConfluxHandshakeError::ChannelClosed, None)
                        }
                    };
                    self.note_conflux_handshake_result(Err(error), proto_violation.is_some())?;
                    if let Some(e) = proto_violation {
                        tor_error::warn_report!(
                            e,
                            tunnel_id = %self.tunnel_id,
                            "Malformed conflux handshake, tearing down tunnel",
                        );
                        return Err(e.into());
                    }
                }
            }
            #[cfg(feature = "conflux")]
            RunOnceCmdInner::ConfluxHandshakeComplete { leg, cell } => {
                // Note: on the client-side, the handshake is considered complete once the
                // RELAY_CONFLUX_LINKED_ACK is sent (roughly upon receipt of the LINKED cell).
                //
                // We're optimistic here, and declare the handshake a success *before*
                // sending the LINKED_ACK response. I think this is OK though,
                // because if the send_relay_cell() below fails, the reactor will shut
                // down anyway. OTOH, marking the handshake as complete slightly early
                // means that on the happy path, the circuit is marked as usable sooner,
                // instead of blocking on the sending of the LINKED_ACK.
                self.note_conflux_handshake_result(Ok(()), false)?;
                let res = self.circuits.send_relay_cell_on_leg(cell, Some(leg)).await;
                res?;
            }
            #[cfg(feature = "conflux")]
            RunOnceCmdInner::Link { circuits, answer } => {
                // Add the specified circuits to our conflux set,
                // and send a LINK cell down each unlinked leg.
                //
                // NOTE: this will block the reactor until all the cells are sent.
                self.handle_link_circuits(circuits, answer).await?;
            }
            #[cfg(feature = "conflux")]
            RunOnceCmdInner::Enqueue { leg, msg } => {
                let entry = ConfluxHeapEntry { leg_id: leg, msg };
                self.ooo_msgs.push(entry);
            }
            #[cfg(feature = "circ-padding")]
            RunOnceCmdInner::PaddingAction { leg, padding_event } => {
                // TODO: If we someday move back to having a per-circuit reactor,
                // this event would logically belong there, not on the tunnel reactor.
                self.circuits.run_padding_event(leg, padding_event).await?;
            }
        }
        Ok(())
4662
    }
    /// Wait for a [`CtrlMsg::Create`] to come along to set up the circuit.
    ///
    /// Returns an error if an unexpected `CtrlMsg` is received.
    #[instrument(level = "trace", skip_all)]
633
    async fn wait_for_create(&mut self) -> StdResult<(), ReactorError> {
        let msg = select_biased! {
            res = self.command.next() => {
                let cmd = unwrap_or_shutdown!(self, res, "shutdown channel drop")?;
                match cmd {
                    CtrlCmd::Shutdown => return self.handle_shutdown().map(|_| ()),
                    #[cfg(test)]
                    CtrlCmd::AddFakeHop {
                        relay_cell_format: format,
                        fwd_lasthop,
                        rev_lasthop,
                        peer_id,
                        params,
                        done,
                    } => {
                        let leg = self.circuits.single_leg_mut()?;
                        leg.handle_add_fake_hop(format, fwd_lasthop, rev_lasthop, peer_id, &params, done);
                        return Ok(())
                    },
                    _ => {
                        trace!("reactor shutdown due to unexpected command: {:?}", cmd);
                        return Err(Error::CircProto(format!("Unexpected control {cmd:?} on client circuit")).into());
                    }
                }
            },
            res = self.control.next() => unwrap_or_shutdown!(self, res, "control drop")?,
        };
        match msg {
            CtrlMsg::Create {
                recv_created,
                handshake,
                settings,
                done,
            } => {
                // TODO(conflux): instead of crashing the reactor, it might be better
                // to send the error via the done channel instead
                let leg = self.circuits.single_leg_mut()?;
                leg.handle_create(recv_created, handshake, settings, done)
                    .await
            }
            _ => {
                trace!("reactor shutdown due to unexpected cell: {:?}", msg);
                Err(Error::CircProto(format!("Unexpected {msg:?} cell on client circuit")).into())
            }
        }
422
    }
    /// Add the specified handshake result to our `ConfluxHandshakeContext`.
    ///
    /// If all the circuits we were waiting on have finished the conflux handshake,
    /// the `ConfluxHandshakeContext` is consumed, and the results we have collected
    /// are sent to the handshake initiator.
    #[cfg(feature = "conflux")]
    #[instrument(level = "trace", skip_all)]
84
    fn note_conflux_handshake_result(
84
        &mut self,
84
        res: StdResult<(), ConfluxHandshakeError>,
84
        reactor_is_closing: bool,
84
    ) -> StdResult<(), ReactorError> {
84
        let tunnel_complete = match self.conflux_hs_ctx.as_mut() {
84
            Some(conflux_ctx) => {
84
                conflux_ctx.results.push(res);
                // Whether all the legs have finished linking:
84
                conflux_ctx.results.len() == conflux_ctx.num_legs
            }
            None => {
                return Err(internal!("no conflux handshake context").into());
            }
        };
84
        if tunnel_complete || reactor_is_closing {
            // Time to remove the conflux handshake context
            // and extract the results we have collected
48
            let conflux_ctx = self.conflux_hs_ctx.take().expect("context disappeared?!");
108
            let success_count = conflux_ctx.results.iter().filter(|res| res.is_ok()).count();
48
            let leg_count = conflux_ctx.results.len();
48
            info!(
                tunnel_id = %self.tunnel_id,
                "conflux tunnel ready ({success_count}/{leg_count} circuits successfully linked)",
            );
48
            send_conflux_outcome(conflux_ctx.answer, Ok(conflux_ctx.results))?;
            // We don't expect to receive any more handshake results,
            // at least not until we get another LinkCircuits control message,
            // which will install a new ConfluxHandshakeCtx with a channel
            // for us to send updates on
36
        }
80
        Ok(())
84
    }
    /// Prepare a `SendRelayCell` request, and install the given meta-cell handler.
    fn prepare_msg_and_install_handler(
        &mut self,
        msg: Option<AnyRelayMsgOuter>,
        handler: Option<Box<dyn MetaCellHandler + Send + 'static>>,
    ) -> Result<Option<SendRelayCell>> {
        let msg = msg
            .map(|msg| {
                let handlers = &mut self.cell_handlers;
                let handler = handler
                    .as_ref()
                    .or(handlers.meta_handler.as_ref())
                    .ok_or_else(|| internal!("tried to use an ended Conversation"))?;
                // We should always have a precise HopLocation here so this should never fails but
                // in case we have a ::JointPoint, we'll notice.
                let hop = handler.expected_hop().hop_num().ok_or(bad_api_usage!(
                    "MsgHandler doesn't have a precise HopLocation"
                ))?;
                Ok::<_, crate::Error>(SendRelayCell {
                    hop: Some(hop),
                    early: false,
                    cell: msg,
                })
            })
            .transpose()?;
        if let Some(handler) = handler {
            self.cell_handlers.set_meta_handler(handler)?;
        }
        Ok(msg)
    }
    /// Handle a shutdown request.
64
    fn handle_shutdown(&self) -> StdResult<Option<RunOnceCmdInner>, ReactorError> {
64
        trace!(
            tunnel_id = %self.tunnel_id,
            "reactor shutdown due to explicit request",
        );
64
        Err(ReactorError::Shutdown)
64
    }
    /// Handle a request to shutdown the reactor and return the only [`Circuit`] in this tunnel.
    ///
    /// Returns an error over the `answer` channel if the reactor has no circuits,
    /// or more than one circuit. The reactor will shut down regardless.
    #[cfg(feature = "conflux")]
64
    fn handle_shutdown_and_return_circuit(
64
        &mut self,
64
        answer: oneshot::Sender<StdResult<Circuit, Bug>>,
64
    ) -> StdResult<(), ReactorError> {
        // Don't care if the receiver goes away
64
        let _ = answer.send(self.circuits.take_single_leg());
64
        self.handle_shutdown().map(|_| ())
64
    }
    /// Resolves a [`TargetHop`] to a [`HopLocation`].
    ///
    /// After resolving a `TargetHop::LastHop`,
    /// the `HopLocation` can become stale if a single-path circuit is later extended or truncated.
    /// This means that the `HopLocation` can become stale from one reactor iteration to the next.
    ///
    /// It's generally okay to hold on to a (possibly stale) `HopLocation`
    /// if you need a fixed hop position in the tunnel.
    /// For example if we open a stream to `TargetHop::LastHop`,
    /// we would want to store the stream position as a `HopLocation` and not a `TargetHop::LastHop`
    /// as we don't want the stream position to change as the tunnel is extended or truncated.
    ///
    /// Returns [`NoHopsBuiltError`] if trying to resolve `TargetHop::LastHop`
    /// and the tunnel has no hops
    /// (either has no legs, or has legs which contain no hops).
204
    fn resolve_target_hop(&self, hop: TargetHop) -> StdResult<HopLocation, NoHopsBuiltError> {
204
        match hop {
60
            TargetHop::Hop(hop) => Ok(hop),
            TargetHop::LastHop => {
144
                if let Ok(leg) = self.circuits.single_leg() {
132
                    let leg_id = leg.unique_id();
                    // single-path tunnel
132
                    let hop = leg.last_hop_num().ok_or(NoHopsBuiltError)?;
132
                    Ok(HopLocation::Hop((leg_id, hop)))
12
                } else if !self.circuits.is_empty() {
                    // multi-path tunnel
12
                    Ok(HopLocation::JoinPoint)
                } else {
                    // no legs
                    Err(NoHopsBuiltError)
                }
            }
        }
204
    }
    /// Resolves a [`HopLocation`] to a [`UniqId`] and [`HopNum`].
    ///
    /// After resolving a `HopLocation::JoinPoint`,
    /// the [`UniqId`] and [`HopNum`] can become stale if the primary leg changes.
    ///
    /// You should try to only resolve to a specific [`UniqId`] and [`HopNum`] immediately before you
    /// need them,
    /// and you should not hold on to the resolved [`UniqId`] and [`HopNum`] between reactor
    /// iterations as the primary leg may change from one iteration to the next.
    ///
    /// Returns [`NoJoinPointError`] if trying to resolve `HopLocation::JoinPoint`
    /// but it does not have a join point.
    #[instrument(level = "trace", skip_all)]
220
    fn resolve_hop_location(
220
        &self,
220
        hop: HopLocation,
220
    ) -> StdResult<(UniqId, HopNum), NoJoinPointError> {
220
        match hop {
208
            HopLocation::Hop((leg_id, hop_num)) => Ok((leg_id, hop_num)),
            HopLocation::JoinPoint => {
12
                if let Some((leg_id, hop_num)) = self.circuits.primary_join_point() {
12
                    Ok((leg_id, hop_num))
                } else {
                    // Attempted to get the join point of a non-multipath tunnel.
                    Err(NoJoinPointError)
                }
            }
        }
220
    }
    /// Resolve a [`TargetHop`] directly into a [`UniqId`] and [`HopNum`].
    ///
    /// This is a helper function that basically calls both resolve_target_hop and
    /// resolve_hop_location back to back.
    ///
    /// It returns None on failure to resolve meaning that if you want more detailed error on why
    /// it failed, explicitly use the resolve_hop_location() and resolve_target_hop() functions.
60
    pub(crate) fn target_hop_to_hopnum_id(&self, hop: TargetHop) -> Option<(UniqId, HopNum)> {
60
        self.resolve_target_hop(hop)
60
            .ok()
90
            .and_then(|resolved| self.resolve_hop_location(resolved).ok())
60
    }
    /// Install or remove a padder at a given hop.
    #[cfg(feature = "circ-padding-manual")]
    fn set_padding_at_hop(
        &self,
        hop: HopLocation,
        padder: Option<super::circuit::padding::CircuitPadder>,
    ) -> Result<()> {
        let HopLocation::Hop((leg_id, hop_num)) = hop else {
            return Err(bad_api_usage!("Padding to the join point is not supported.").into());
        };
        let circ = self.circuits.leg(leg_id).ok_or(Error::NoSuchHop)?;
        circ.set_padding_at_hop(hop_num, padder)?;
        Ok(())
    }
    /// Does congestion control use stream SENDMEs for the given hop?
    ///
    /// Returns `None` if either the `leg` or `hop` don't exist.
    fn uses_stream_sendme(&self, leg: UniqId, hop: HopNum) -> Option<bool> {
        self.circuits.uses_stream_sendme(leg, hop)
    }
    /// Handle a request to link some extra circuits in the reactor's conflux set.
    ///
    /// The circuits are validated, and if they do not have the same length,
    /// or if they do not all have the same last hop, an error is returned on
    /// the `answer` channel, and the conflux handshake is *not* initiated.
    ///
    /// If validation succeeds, the circuits are added to this reactor's conflux set,
    /// and the conflux handshake is initiated (by sending a LINK cell on each leg).
    ///
    /// NOTE: this blocks the reactor main loop until all the cells are sent.
    #[cfg(feature = "conflux")]
    #[instrument(level = "trace", skip_all)]
60
    async fn handle_link_circuits(
60
        &mut self,
60
        circuits: Vec<Circuit>,
60
        answer: ConfluxLinkResultChannel,
90
    ) -> StdResult<(), ReactorError> {
        use tor_error::warn_report;
        if self.conflux_hs_ctx.is_some() {
            let err = internal!("conflux linking already in progress");
            send_conflux_outcome(answer, Err(err.into()))?;
            return Ok(());
        }
        let unlinked_legs = self.circuits.num_unlinked();
        // We need to send the LINK cell on each of the new circuits
        // and on each of the existing, unlinked legs from self.circuits.
        //
        // In reality, there can only be one such circuit
        // (the "initial" one from the previously single-path tunnel),
        // because any circuits that to complete the conflux handshake
        // get removed from the set.
        let num_legs = circuits.len() + unlinked_legs;
        // Note: add_legs validates `circuits`
60
        let res = async {
60
            self.circuits.add_legs(circuits, &self.runtime)?;
52
            self.circuits.link_circuits(&self.runtime).await
60
        }
        .await;
        if let Err(e) = res {
            warn_report!(e, "Failed to link conflux circuits");
            send_conflux_outcome(answer, Err(e))?;
        } else {
            // Save the channel, to notify the user of completion.
            self.conflux_hs_ctx = Some(ConfluxHandshakeCtx {
                answer,
                num_legs,
                results: Default::default(),
            });
        }
        Ok(())
60
    }
}
/// Notify the conflux handshake initiator of the handshake outcome.
///
/// Returns an error if the initiator has done away.
#[cfg(feature = "conflux")]
56
fn send_conflux_outcome(
56
    tx: ConfluxLinkResultChannel,
56
    res: Result<ConfluxHandshakeResult>,
56
) -> StdResult<(), ReactorError> {
56
    if tx.send(res).is_err() {
4
        tracing::warn!("conflux initiator went away before handshake completed?");
4
        return Err(ReactorError::Shutdown);
52
    }
52
    Ok(())
56
}
/// The tunnel does not have any hops.
#[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
#[error("no hops have been built for this tunnel")]
pub(crate) struct NoHopsBuiltError;
/// The tunnel does not have a join point.
#[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
#[error("the tunnel does not have a join point")]
pub(crate) struct NoJoinPointError;
impl CellHandlers {
    /// Try to install a given meta-cell handler to receive any unusual cells on
    /// this circuit, along with a result channel to notify on completion.
72
    fn set_meta_handler(&mut self, handler: Box<dyn MetaCellHandler + Send>) -> Result<()> {
72
        if self.meta_handler.is_none() {
72
            self.meta_handler = Some(handler);
72
            Ok(())
        } else {
            Err(Error::from(internal!(
                "Tried to install a meta-cell handler before the old one was gone."
            )))
        }
72
    }
    /// Try to install a given cell handler on this circuit.
    #[cfg(feature = "hs-service")]
60
    fn set_incoming_stream_req_handler(
60
        &mut self,
60
        handler: IncomingStreamRequestHandler,
60
    ) -> Result<()> {
60
        if self.incoming_stream_req_handler.is_none() {
48
            self.incoming_stream_req_handler = Some(handler);
48
            Ok(())
        } else {
12
            Err(Error::from(internal!(
12
                "Tried to install a BEGIN cell handler before the old one was gone."
12
            )))
        }
60
    }
}
#[cfg(test)]
mod test {
    // Tested in [`crate::client::circuit::test`].
}