Skip to main content

tor_proto/client/reactor/
circuit.rs

1//! Module exposing types for representing circuits in the tunnel reactor.
2
3pub(crate) mod circhop;
4pub(super) mod extender;
5
6use crate::channel::Channel;
7use crate::circuit::cell_sender::CircuitCellSender;
8use crate::circuit::celltypes::CreateResponse;
9use crate::circuit::circhop::{HopSettings, ReactorStreamComponents};
10use crate::circuit::create::{Create2Wrap, CreateFastWrap, CreateHandshakeWrap};
11use crate::circuit::padding::CircPaddingDisposition;
12use crate::circuit::{CircuitRxReceiver, UniqId};
13use crate::client::circuit::handshake::{BoxedClientLayer, HandshakeRole};
14use crate::client::circuit::padding::{
15    self, PaddingController, PaddingEventStream, QueuedCellPaddingInfo,
16};
17use crate::client::circuit::{ClientCircChanMsg, MutableState, path};
18use crate::client::reactor::MetaCellDisposition;
19use crate::congestion::CongestionSignals;
20use crate::congestion::sendme;
21use crate::crypto::binding::CircuitBinding;
22use crate::crypto::cell::{
23    HopNum, InboundClientCrypt, InboundClientLayer, OutboundClientCrypt, OutboundClientLayer,
24    RelayCellBody,
25};
26use crate::crypto::handshake::fast::CreateFastClient;
27use crate::crypto::handshake::ntor::{NtorClient, NtorPublicKey};
28use crate::crypto::handshake::ntor_v3::{NtorV3Client, NtorV3PublicKey};
29use crate::crypto::handshake::{ClientHandshake, KeyGenerator};
30use crate::memquota::{CircuitAccount, SpecificAccount as _, StreamAccount};
31use crate::stream::cmdcheck::{AnyCmdChecker, StreamStatus};
32use crate::stream::msg_streamid;
33use crate::streammap;
34use crate::tunnel::TunnelScopedCircId;
35use crate::util::err::ReactorError;
36use crate::util::timeout::TimeoutEstimator;
37use crate::{ClockSkew, Error, Result};
38
39use tor_async_utils::{SinkTrySend as _, SinkTrySendError as _};
40use tor_cell::chancell::msg::{AnyChanMsg, HandshakeType, Relay};
41use tor_cell::chancell::{AnyChanCell, ChanCmd, CircId};
42use tor_cell::chancell::{BoxedCellBody, ChanMsg};
43use tor_cell::relaycell::msg::{AnyRelayMsg, End, Sendme, SendmeTag, Truncated};
44use tor_cell::relaycell::{
45    AnyRelayMsgOuter, RelayCellDecoderResult, RelayCellFormat, RelayCmd, StreamId, UnparsedRelayMsg,
46};
47use tor_error::{Bug, internal};
48use tor_linkspec::RelayIds;
49use tor_llcrypto::pk;
50use web_time_compat::{Duration, Instant, SystemTime};
51
52use futures::SinkExt as _;
53use oneshot_fused_workaround as oneshot;
54use tor_rtcompat::{DynTimeProvider, SleepProvider as _};
55use tracing::{debug, instrument, trace, warn};
56
57use super::{
58    CellHandlers, CircuitHandshake, CloseStreamBehavior, ReactorResultChannel, SendRelayCell,
59};
60
61use crate::conflux::msghandler::ConfluxStatus;
62
63use std::borrow::Borrow;
64use std::pin::Pin;
65use std::result::Result as StdResult;
66use std::sync::Arc;
67
68use extender::HandshakeAuxDataHandler;
69
70#[cfg(feature = "hs-service")]
71use {
72    crate::circuit::CircHopSyncView,
73    crate::stream::{InboundDataCmdChecker, IncomingStreamRequest},
74    tor_cell::relaycell::msg::Begin,
75};
76
77#[cfg(feature = "conflux")]
78use {
79    crate::conflux::msghandler::{ConfluxAction, ConfluxCmd, ConfluxMsgHandler, OooRelayMsg},
80    crate::tunnel::TunnelId,
81};
82
83#[cfg(not(feature = "flowctl-cc"))]
84use crate::stream::STREAM_READER_BUFFER;
85
86pub(super) use circhop::{CircHop, CircHopList};
87
88/// A circuit "leg" from a tunnel.
89///
90/// Regular (non-multipath) circuits have a single leg.
91/// Conflux (multipath) circuits have `N` (usually, `N = 2`).
92pub(crate) struct Circuit {
93    /// The time provider.
94    runtime: DynTimeProvider,
95    /// The channel this circuit is attached to.
96    channel: Arc<Channel>,
97    /// Sender object used to actually send cells.
98    ///
99    /// NOTE: Control messages could potentially add unboundedly to this, although that's
100    ///       not likely to happen (and isn't triggereable from the network, either).
101    pub(super) chan_sender: CircuitCellSender,
102    /// Input stream, on which we receive ChanMsg objects from this circuit's
103    /// channel.
104    ///
105    // TODO: could use a SPSC channel here instead.
106    pub(super) input: CircuitRxReceiver,
107    /// The cryptographic state for this circuit for inbound cells.
108    /// This object is divided into multiple layers, each of which is
109    /// shared with one hop of the circuit.
110    crypto_in: InboundClientCrypt,
111    /// The cryptographic state for this circuit for outbound cells.
112    crypto_out: OutboundClientCrypt,
113    /// List of hops state objects used by the reactor
114    pub(super) hops: CircHopList,
115    /// Mutable information about this circuit,
116    /// shared with the reactor's `ConfluxSet`.
117    mutable: Arc<MutableState>,
118    /// This circuit's identifier on the upstream channel.
119    channel_id: CircId,
120    /// An identifier for logging about this reactor's circuit.
121    unique_id: TunnelScopedCircId,
122    /// A handler for conflux cells.
123    ///
124    /// Set once the conflux handshake is initiated by the reactor
125    /// using [`Reactor::handle_link_circuits`](super::Reactor::handle_link_circuits).
126    #[cfg(feature = "conflux")]
127    conflux_handler: Option<ConfluxMsgHandler>,
128    /// A padding controller to which padding-related events should be reported.
129    padding_ctrl: PaddingController,
130    /// An event stream telling us about padding-related events.
131    //
132    // TODO: it would be nice to have all of these streams wrapped in a single
133    // SelectAll, but we can't really do that, since we need the ability to move them
134    // from one conflux set to another, and a SelectAll doesn't let you actually
135    // remove one of its constituent streams.  This issue might get solved along
136    // with the rest of the next reactor refactoring.
137    pub(super) padding_event_stream: PaddingEventStream,
138    /// Current rules for blocking traffic, according to the padding controller.
139    #[cfg(feature = "circ-padding")]
140    padding_block: Option<padding::StartBlocking>,
141    /// The circuit timeout estimator.
142    ///
143    /// Used for computing half-stream expiration.
144    timeouts: Arc<dyn TimeoutEstimator>,
145    /// Memory quota account
146    #[allow(dead_code)] // Partly here to keep it alive as long as the circuit
147    memquota: CircuitAccount,
148}
149
150/// A command to run in response to a circuit event.
151///
152/// Unlike `RunOnceCmdInner`, doesn't know anything about `UniqId`s.
153/// The user of the `CircuitCmd`s is supposed to know the `UniqId`
154/// of the circuit the `CircuitCmd` came from.
155///
156/// This type gets mapped to a `RunOnceCmdInner` in the circuit reactor.
157#[derive(Debug, derive_more::From)]
158pub(super) enum CircuitCmd {
159    /// Send a RELAY cell on the circuit leg this command originates from.
160    Send(SendRelayCell),
161    /// Handle a SENDME message received on the circuit leg this command originates from.
162    HandleSendMe {
163        /// The hop number.
164        hop: HopNum,
165        /// The SENDME message to handle.
166        sendme: Sendme,
167    },
168    /// Close the specified stream on the circuit leg this command originates from.
169    CloseStream {
170        /// The hop number.
171        hop: HopNum,
172        /// The ID of the stream to close.
173        sid: StreamId,
174        /// The stream-closing behavior.
175        behav: CloseStreamBehavior,
176        /// The reason for closing the stream.
177        reason: streammap::TerminateReason,
178    },
179    /// Perform an action resulting from handling a conflux cell.
180    #[cfg(feature = "conflux")]
181    Conflux(ConfluxCmd),
182    /// Perform a clean shutdown on this circuit.
183    CleanShutdown,
184    /// Enqueue an out-of-order cell in the reactor.
185    #[cfg(feature = "conflux")]
186    Enqueue(OooRelayMsg),
187}
188
189/// Return a `CircProto` error for the specified unsupported cell.
190///
191/// This error will shut down the reactor.
192///
193/// Note: this is a macro to simplify usage (this way the caller doesn't
194/// need to .map() the result to the appropriate type)
195macro_rules! unsupported_client_cell {
196    ($msg:expr) => {{
197        unsupported_client_cell!(@ $msg, "")
198    }};
199
200    ($msg:expr, $hopnum:expr) => {{
201        let hop: HopNum = $hopnum;
202        let hop_display = format!(" from hop {}", hop.display());
203        unsupported_client_cell!(@ $msg, hop_display)
204    }};
205
206    (@ $msg:expr, $hopnum_display:expr) => {
207        Err(crate::Error::CircProto(format!(
208            "Unexpected {} cell{} on client circuit",
209            $msg.cmd(),
210            $hopnum_display,
211        )))
212    };
213}
214
215pub(super) use unsupported_client_cell;
216
217impl Circuit {
218    /// Create a new non-multipath circuit.
219    #[allow(clippy::too_many_arguments)]
220    pub(super) fn new(
221        runtime: DynTimeProvider,
222        channel: Arc<Channel>,
223        channel_id: CircId,
224        unique_id: TunnelScopedCircId,
225        input: CircuitRxReceiver,
226        memquota: CircuitAccount,
227        mutable: Arc<MutableState>,
228        padding_ctrl: PaddingController,
229        padding_event_stream: PaddingEventStream,
230        timeouts: Arc<dyn TimeoutEstimator>,
231    ) -> Self {
232        let chan_sender = CircuitCellSender::from_channel_sender(channel.sender());
233
234        let crypto_out = OutboundClientCrypt::new();
235        Circuit {
236            runtime,
237            channel,
238            chan_sender,
239            input,
240            crypto_in: InboundClientCrypt::new(),
241            hops: CircHopList::default(),
242            unique_id,
243            channel_id,
244            crypto_out,
245            mutable,
246            #[cfg(feature = "conflux")]
247            conflux_handler: None,
248            padding_ctrl,
249            padding_event_stream,
250            #[cfg(feature = "circ-padding")]
251            padding_block: None,
252            timeouts,
253            memquota,
254        }
255    }
256
257    /// Return the process-unique identifier of this circuit.
258    pub(super) fn unique_id(&self) -> UniqId {
259        self.unique_id.unique_id()
260    }
261
262    /// Return the shared mutable state of this circuit.
263    pub(super) fn mutable(&self) -> &Arc<MutableState> {
264        &self.mutable
265    }
266
267    /// Add this circuit to a multipath tunnel, by associating it with a new [`TunnelId`],
268    /// and installing a [`ConfluxMsgHandler`] on this circuit.
269    ///
270    /// Once this is called, the circuit will be able to handle conflux cells.
271    #[cfg(feature = "conflux")]
272    pub(super) fn add_to_conflux_tunnel(
273        &mut self,
274        tunnel_id: TunnelId,
275        conflux_handler: ConfluxMsgHandler,
276    ) {
277        self.unique_id = TunnelScopedCircId::new(tunnel_id, self.unique_id.unique_id());
278        self.conflux_handler = Some(conflux_handler);
279    }
280
281    /// Send a LINK cell to the specified hop.
282    ///
283    /// This must be called *after* a [`ConfluxMsgHandler`] is installed
284    /// on the circuit with [`add_to_conflux_tunnel`](Self::add_to_conflux_tunnel).
285    #[cfg(feature = "conflux")]
286    pub(super) async fn begin_conflux_link(
287        &mut self,
288        hop: HopNum,
289        cell: AnyRelayMsgOuter,
290        runtime: &tor_rtcompat::DynTimeProvider,
291    ) -> Result<()> {
292        use tor_rtcompat::SleepProvider as _;
293
294        if self.conflux_handler.is_none() {
295            return Err(internal!(
296                "tried to send LINK cell before installing a ConfluxMsgHandler?!"
297            )
298            .into());
299        }
300
301        let cell = SendRelayCell {
302            hop: Some(hop),
303            early: false,
304            cell,
305        };
306        self.send_relay_cell(cell).await?;
307
308        let Some(conflux_handler) = self.conflux_handler.as_mut() else {
309            return Err(internal!("ConfluxMsgHandler disappeared?!").into());
310        };
311
312        Ok(conflux_handler.note_link_sent(runtime.wallclock())?)
313    }
314
315    /// Get the wallclock time when the handshake on this circuit is supposed to time out.
316    ///
317    /// Returns `None` if the handshake is not currently in progress.
318    pub(super) fn conflux_hs_timeout(&self) -> Option<SystemTime> {
319        cfg_if::cfg_if! {
320            if #[cfg(feature = "conflux")] {
321                self.conflux_handler.as_ref().map(|handler| handler.handshake_timeout())?
322            } else {
323                None
324            }
325        }
326    }
327
328    /// Handle a [`CtrlMsg::AddFakeHop`](super::CtrlMsg::AddFakeHop) message.
329    #[cfg(test)]
330    pub(super) fn handle_add_fake_hop(
331        &mut self,
332        format: RelayCellFormat,
333        fwd_lasthop: bool,
334        rev_lasthop: bool,
335        dummy_peer_id: path::HopDetail,
336        // TODO-CGO: Take HopSettings instead of CircParams.
337        // (Do this after we've got the virtual-hop refactorings done for
338        // virtual extending.)
339        params: &crate::client::circuit::CircParameters,
340        done: ReactorResultChannel<()>,
341    ) {
342        use tor_protover::{Protocols, named};
343
344        use crate::client::circuit::test::DummyCrypto;
345
346        assert!(matches!(format, RelayCellFormat::V0));
347        let _ = format; // TODO-CGO: remove this once we have CGO+hs implemented.
348
349        let fwd = Box::new(DummyCrypto::new(fwd_lasthop));
350        let rev = Box::new(DummyCrypto::new(rev_lasthop));
351        let binding = None;
352
353        let settings = HopSettings::from_params_and_caps(
354            // This is for testing only, so we'll assume full negotiation took place.
355            crate::circuit::circhop::HopNegotiationType::Full,
356            params,
357            &[named::FLOWCTRL_CC].into_iter().collect::<Protocols>(),
358        )
359        .expect("Can't construct HopSettings");
360        self.add_hop(dummy_peer_id, fwd, rev, binding, &settings)
361            .expect("could not add hop to circuit");
362        let _ = done.send(Ok(()));
363    }
364
365    /// Encode `msg` and encrypt it, returning the resulting cell
366    /// and tag that should be expected for an authenticated SENDME sent
367    /// in response to that cell.
368    fn encode_relay_cell(
369        crypto_out: &mut OutboundClientCrypt,
370        relay_format: RelayCellFormat,
371        hop: HopNum,
372        early: bool,
373        msg: AnyRelayMsgOuter,
374    ) -> Result<(AnyChanMsg, SendmeTag)> {
375        let mut body: RelayCellBody = msg
376            .encode(relay_format, &mut rand::rng())
377            .map_err(|e| Error::from_cell_enc(e, "relay cell body"))?
378            .into();
379        let cmd = if early {
380            ChanCmd::RELAY_EARLY
381        } else {
382            ChanCmd::RELAY
383        };
384        let tag = crypto_out.encrypt(cmd, &mut body, hop)?;
385        let msg = Relay::from(BoxedCellBody::from(body));
386        let msg = if early {
387            AnyChanMsg::RelayEarly(msg.into())
388        } else {
389            AnyChanMsg::Relay(msg)
390        };
391
392        Ok((msg, tag))
393    }
394
395    /// Encode `msg`, encrypt it, and send it to the 'hop'th hop.
396    ///
397    /// If there is insufficient outgoing *circuit-level* or *stream-level*
398    /// SENDME window, an error is returned instead.
399    ///
400    /// Does not check whether the cell is well-formed or reasonable.
401    ///
402    /// NOTE: the reactor should not call this function directly, only via
403    /// [`ConfluxSet::send_relay_cell_on_leg`](super::conflux::ConfluxSet::send_relay_cell_on_leg),
404    /// which will reroute the message, if necessary to the primary leg.
405    #[instrument(level = "trace", skip_all)]
406    pub(super) async fn send_relay_cell(&mut self, msg: SendRelayCell) -> Result<()> {
407        self.send_relay_cell_inner(msg, None).await
408    }
409
410    /// As [`send_relay_cell`](Self::send_relay_cell), but takes an optional
411    /// [`QueuedCellPaddingInfo`] in `padding_info`.
412    ///
413    /// If `padding_info` is None, `msg` must be non-padding: we report it as such to the
414    /// padding controller.
415    #[instrument(level = "trace", skip_all)]
416    async fn send_relay_cell_inner(
417        &mut self,
418        msg: SendRelayCell,
419        padding_info: Option<QueuedCellPaddingInfo>,
420    ) -> Result<()> {
421        let SendRelayCell {
422            hop,
423            early,
424            cell: msg,
425        } = msg;
426
427        let is_conflux_link = msg.cmd() == RelayCmd::CONFLUX_LINK;
428        if !is_conflux_link && self.is_conflux_pending() {
429            // Note: it is the responsibility of the reactor user to wait until
430            // at least one of the legs completes the handshake.
431            return Err(internal!("tried to send cell on unlinked circuit").into());
432        }
433
434        trace!(circ_id = %self.unique_id, cell = ?msg, "sending relay cell");
435
436        // Cloned, because we borrow mutably from self when we get the circhop.
437        let runtime = self.runtime.clone();
438        let c_t_w = sendme::cmd_counts_towards_windows(msg.cmd());
439        let stream_id = msg.stream_id();
440        let hop = hop.expect("missing hop in client SendRelayCell?!");
441        let circhop = self.hops.get_mut(hop).ok_or(Error::NoSuchHop)?;
442
443        // We might be out of capacity entirely; see if we are about to hit a limit.
444        //
445        // TODO: If we ever add a notion of _recoverable_ errors below, we'll
446        // need a way to restore this limit, and similarly for about_to_send().
447        circhop.decrement_outbound_cell_limit()?;
448
449        // We need to apply stream-level flow control *before* encoding the message.
450        if c_t_w {
451            if let Some(stream_id) = stream_id {
452                circhop.about_to_send(stream_id, msg.msg())?;
453            }
454        }
455
456        // Save the RelayCmd of the message before it gets consumed below.
457        // We need this to tell our ConfluxMsgHandler about the cell we've just sent,
458        // so that it can update its counters.
459        let relay_cmd = msg.cmd();
460
461        // NOTE(eta): Now that we've encrypted the cell, we *must* either send it or abort
462        //            the whole circuit (e.g. by returning an error).
463        let (msg, tag) = Self::encode_relay_cell(
464            &mut self.crypto_out,
465            circhop.relay_cell_format(),
466            hop,
467            early,
468            msg,
469        )?;
470        // The cell counted for congestion control, inform our algorithm of such and pass down the
471        // tag for authenticated SENDMEs.
472        if c_t_w {
473            circhop.ccontrol().note_data_sent(&runtime, &tag)?;
474        }
475
476        // Remember that we've enqueued this cell.
477        let padding_info = padding_info.or_else(|| self.padding_ctrl.queued_data(hop));
478
479        self.send_msg(msg, padding_info).await?;
480
481        #[cfg(feature = "conflux")]
482        if let Some(conflux) = self.conflux_handler.as_mut() {
483            conflux.note_cell_sent(relay_cmd);
484        }
485
486        Ok(())
487    }
488
489    /// Helper: process a cell on a channel.  Most cells get ignored
490    /// or rejected; a few get delivered to circuits.
491    ///
492    /// Return `CellStatus::CleanShutdown` if we should exit.
493    ///
494    // TODO: returning `Vec<CircuitCmd>` means we're unnecessarily
495    // allocating a `Vec` here. Generally, the number of commands is going to be small
496    // (usually 1, but > 1 when we start supporting packed cells).
497    //
498    // We should consider using smallvec instead. It might also be a good idea to have a
499    // separate higher-level type splitting this out into Single(CircuitCmd),
500    // and Multiple(SmallVec<[CircuitCmd; <capacity>]>).
501    pub(super) fn handle_cell(
502        &mut self,
503        handlers: &mut CellHandlers,
504        leg: UniqId,
505        cell: ClientCircChanMsg,
506    ) -> Result<Vec<CircuitCmd>> {
507        trace!(circ_id = %self.unique_id, cell = ?cell, "handling cell");
508        use ClientCircChanMsg::*;
509        match cell {
510            Relay(r) => self.handle_relay_cell(handlers, leg, r),
511            Destroy(d) => {
512                let reason = d.reason();
513                debug!(
514                    circ_id = %self.unique_id,
515                    "Received DESTROY cell. Reason: {} [{}]",
516                    reason.human_str(),
517                    reason
518                );
519
520                self.handle_destroy_cell().map(|c| vec![c])
521            }
522        }
523    }
524
525    /// Decode `cell`, returning its corresponding hop number, tag,
526    /// and decoded body.
527    fn decode_relay_cell(
528        &mut self,
529        cell: Relay,
530    ) -> Result<(HopNum, SendmeTag, RelayCellDecoderResult)> {
531        // This is always RELAY, not RELAY_EARLY, so long as this code is client-only.
532        let cmd = cell.cmd();
533        let mut body = cell.into_relay_body().into();
534
535        // Decrypt the cell. If it's recognized, then find the
536        // corresponding hop.
537        let (hopnum, tag) = self.crypto_in.decrypt(cmd, &mut body)?;
538
539        // Decode the cell.
540        let decode_res = self
541            .hop_mut(hopnum)
542            .ok_or_else(|| {
543                Error::from(internal!(
544                    "Trying to decode cell from nonexistent hop {:?}",
545                    hopnum
546                ))
547            })?
548            .decode(body.into())?;
549
550        Ok((hopnum, tag, decode_res))
551    }
552
553    /// React to a Relay or RelayEarly cell.
554    fn handle_relay_cell(
555        &mut self,
556        handlers: &mut CellHandlers,
557        leg: UniqId,
558        cell: Relay,
559    ) -> Result<Vec<CircuitCmd>> {
560        let (hopnum, tag, decode_res) = self.decode_relay_cell(cell)?;
561
562        if decode_res.is_padding() {
563            self.padding_ctrl.decrypted_padding(hopnum)?;
564        } else {
565            self.padding_ctrl.decrypted_data(hopnum);
566        }
567
568        // Check whether we are allowed to receive more data for this circuit hop.
569        self.hop_mut(hopnum)
570            .ok_or_else(|| internal!("nonexistent hop {:?}", hopnum))?
571            .decrement_inbound_cell_limit()?;
572
573        let c_t_w = decode_res.cmds().any(sendme::cmd_counts_towards_windows);
574
575        // Decrement the circuit sendme windows, and see if we need to
576        // send a sendme cell.
577        let send_circ_sendme = if c_t_w {
578            self.hop_mut(hopnum)
579                .ok_or_else(|| Error::CircProto("Sendme from nonexistent hop".into()))?
580                .ccontrol()
581                .note_data_received()?
582        } else {
583            false
584        };
585
586        let mut circ_cmds = vec![];
587        // If we do need to send a circuit-level SENDME cell, do so.
588        if send_circ_sendme {
589            // This always sends a V1 (tagged) sendme cell, and thereby assumes
590            // that SendmeEmitMinVersion is no more than 1.  If the authorities
591            // every increase that parameter to a higher number, this will
592            // become incorrect.  (Higher numbers are not currently defined.)
593            let sendme = Sendme::from(tag);
594            let cell = AnyRelayMsgOuter::new(None, sendme.into());
595            circ_cmds.push(CircuitCmd::Send(SendRelayCell {
596                hop: Some(hopnum),
597                early: false,
598                cell,
599            }));
600
601            // Inform congestion control of the SENDME we are sending. This is a circuit level one.
602            self.hop_mut(hopnum)
603                .ok_or_else(|| {
604                    Error::from(internal!(
605                        "Trying to send SENDME to nonexistent hop {:?}",
606                        hopnum
607                    ))
608                })?
609                .ccontrol()
610                .note_sendme_sent()?;
611        }
612
613        let (mut msgs, incomplete) = decode_res.into_parts();
614        while let Some(msg) = msgs.next() {
615            let msg_status = self.handle_relay_msg(handlers, hopnum, leg, c_t_w, msg)?;
616
617            match msg_status {
618                None => continue,
619                Some(msg @ CircuitCmd::CleanShutdown) => {
620                    for m in msgs {
621                        debug!(
622                            "{id}: Ignoring relay msg received after triggering shutdown: {m:?}",
623                            id = self.unique_id
624                        );
625                    }
626                    if let Some(incomplete) = incomplete {
627                        debug!(
628                            "{id}: Ignoring partial relay msg received after triggering shutdown: {:?}",
629                            incomplete,
630                            id = self.unique_id,
631                        );
632                    }
633                    circ_cmds.push(msg);
634                    return Ok(circ_cmds);
635                }
636                Some(msg) => {
637                    circ_cmds.push(msg);
638                }
639            }
640        }
641
642        Ok(circ_cmds)
643    }
644
645    /// Handle a single incoming relay message.
646    fn handle_relay_msg(
647        &mut self,
648        handlers: &mut CellHandlers,
649        hopnum: HopNum,
650        leg: UniqId,
651        cell_counts_toward_windows: bool,
652        msg: UnparsedRelayMsg,
653    ) -> Result<Option<CircuitCmd>> {
654        // If this msg wants/refuses to have a Stream ID, does it
655        // have/not have one?
656        let streamid = msg_streamid(&msg)?;
657
658        // If this doesn't have a StreamId, it's a meta cell,
659        // not meant for a particular stream.
660        let Some(streamid) = streamid else {
661            return self.handle_meta_cell(handlers, hopnum, msg);
662        };
663
664        #[cfg(feature = "conflux")]
665        let msg = if let Some(conflux) = self.conflux_handler.as_mut() {
666            match conflux.action_for_msg(hopnum, cell_counts_toward_windows, streamid, msg)? {
667                ConfluxAction::Deliver(msg) => {
668                    // The message either doesn't count towards the sequence numbers
669                    // or is already well-ordered, so we're ready to handle it.
670
671                    // It's possible that some of our buffered messages are now ready to be
672                    // handled. We don't check that here, however, because that's handled
673                    // by the reactor main loop.
674                    msg
675                }
676                ConfluxAction::Enqueue(msg) => {
677                    // Tell the reactor to enqueue this msg
678                    return Ok(Some(CircuitCmd::Enqueue(msg)));
679                }
680            }
681        } else {
682            // If we don't have a conflux_handler, it means this circuit is not part of
683            // a conflux tunnel, so we can just process the message.
684            msg
685        };
686
687        self.handle_in_order_relay_msg(
688            handlers,
689            hopnum,
690            leg,
691            cell_counts_toward_windows,
692            streamid,
693            msg,
694        )
695    }
696
697    /// Handle a single incoming relay message that is known to be in order.
698    pub(super) fn handle_in_order_relay_msg(
699        &mut self,
700        handlers: &mut CellHandlers,
701        hopnum: HopNum,
702        leg: UniqId,
703        cell_counts_toward_windows: bool,
704        streamid: StreamId,
705        msg: UnparsedRelayMsg,
706    ) -> Result<Option<CircuitCmd>> {
707        let now = self.runtime.now();
708
709        #[cfg(feature = "conflux")]
710        if let Some(conflux) = self.conflux_handler.as_mut() {
711            conflux.inc_last_seq_delivered(&msg);
712        }
713
714        let path = self.mutable.path();
715
716        let nonexistent_hop_err = || Error::CircProto("Cell from nonexistent hop!".into());
717        let hop = self.hop_mut(hopnum).ok_or_else(nonexistent_hop_err)?;
718
719        let hop_detail = path
720            .iter()
721            .nth(usize::from(hopnum))
722            .ok_or_else(nonexistent_hop_err)?;
723
724        // Returns the original message if it's an incoming stream request
725        // that we need to handle.
726        let res = hop.handle_msg(hop_detail, cell_counts_toward_windows, streamid, msg, now)?;
727
728        // If it was an incoming stream request, we don't need to worry about
729        // sending an XOFF as there's no stream data within this message.
730        if let Some(msg) = res {
731            cfg_if::cfg_if! {
732                if #[cfg(feature = "hs-service")] {
733                    return self.handle_incoming_stream_request(handlers, msg, streamid, hopnum, leg);
734                } else {
735                    return Err(internal!("incoming stream not rejected, but hs-service feature is disabled?!").into());
736                }
737            }
738        }
739
740        // We may want to send an XOFF if the incoming buffer is too large.
741        if let Some(cell) = hop.maybe_send_xoff(streamid)? {
742            let cell = AnyRelayMsgOuter::new(Some(streamid), cell.into());
743            let cell = SendRelayCell {
744                hop: Some(hopnum),
745                early: false,
746                cell,
747            };
748            return Ok(Some(CircuitCmd::Send(cell)));
749        }
750
751        Ok(None)
752    }
753
754    /// Handle a conflux message coming from the specified hop.
755    ///
756    /// Returns an error if
757    ///
758    ///   * this is not a conflux circuit (i.e. it doesn't have a [`ConfluxMsgHandler`])
759    ///   * this is a client circuit and the conflux message originated an unexpected hop
760    ///   * the cell was sent in violation of the handshake protocol
761    #[cfg(feature = "conflux")]
762    fn handle_conflux_msg(
763        &mut self,
764        hop: HopNum,
765        msg: UnparsedRelayMsg,
766    ) -> Result<Option<ConfluxCmd>> {
767        let Some(conflux_handler) = self.conflux_handler.as_mut() else {
768            // If conflux is not enabled, tear down the circuit
769            // (see 4.2.1. Cell Injection Side Channel Mitigations in prop329)
770            return Err(Error::CircProto(format!(
771                "Received {} cell from hop {} on non-conflux client circuit?!",
772                msg.cmd(),
773                hop.display(),
774            )));
775        };
776
777        Ok(conflux_handler.handle_conflux_msg(msg, hop))
778    }
779
780    /// For conflux: return the sequence number of the last cell sent on this leg.
781    ///
782    /// Returns an error if this circuit is not part of a conflux set.
783    #[cfg(feature = "conflux")]
784    pub(super) fn last_seq_sent(&self) -> Result<u64> {
785        let handler = self
786            .conflux_handler
787            .as_ref()
788            .ok_or_else(|| internal!("tried to get last_seq_sent of non-conflux circ"))?;
789
790        Ok(handler.last_seq_sent())
791    }
792
793    /// For conflux: set the sequence number of the last cell sent on this leg.
794    ///
795    /// Returns an error if this circuit is not part of a conflux set.
796    #[cfg(feature = "conflux")]
797    pub(super) fn set_last_seq_sent(&mut self, n: u64) -> Result<()> {
798        let handler = self
799            .conflux_handler
800            .as_mut()
801            .ok_or_else(|| internal!("tried to get last_seq_sent of non-conflux circ"))?;
802
803        handler.set_last_seq_sent(n);
804        Ok(())
805    }
806
807    /// For conflux: return the sequence number of the last cell received on this leg.
808    ///
809    /// Returns an error if this circuit is not part of a conflux set.
810    #[cfg(feature = "conflux")]
811    pub(super) fn last_seq_recv(&self) -> Result<u64> {
812        let handler = self
813            .conflux_handler
814            .as_ref()
815            .ok_or_else(|| internal!("tried to get last_seq_recv of non-conflux circ"))?;
816
817        Ok(handler.last_seq_recv())
818    }
819
820    /// A helper for handling incoming stream requests.
821    ///
822    // TODO: can we make this a method on CircHop to avoid the double HopNum lookup?
823    #[cfg(feature = "hs-service")]
824    fn handle_incoming_stream_request(
825        &mut self,
826        handlers: &mut CellHandlers,
827        msg: UnparsedRelayMsg,
828        stream_id: StreamId,
829        hop_num: HopNum,
830        leg: UniqId,
831    ) -> Result<Option<CircuitCmd>> {
832        use tor_cell::relaycell::msg::EndReason;
833        use tor_error::into_internal;
834        use tor_log_ratelim::log_ratelim;
835
836        use crate::stream::incoming::StreamReqInfo;
837
838        // We need to construct this early so that we don't double-borrow &mut self
839
840        let Some(handler) = handlers.incoming_stream_req_handler.as_mut() else {
841            return Err(Error::CircProto(
842                "Cannot handle BEGIN cells on this circuit".into(),
843            ));
844        };
845
846        // The handler's hop_num is only ever set to None for relays.
847        let expected_hop_num = handler
848            .hop_num
849            .ok_or_else(|| internal!("Handler HopNum is None in client impl?!"))?;
850
851        if hop_num != expected_hop_num {
852            return Err(Error::CircProto(format!(
853                "Expecting incoming streams from {}, but received {} cell from unexpected hop {}",
854                expected_hop_num.display(),
855                msg.cmd(),
856                hop_num.display()
857            )));
858        }
859
860        let message_closes_stream = handler.cmd_checker.check_msg(&msg)? == StreamStatus::Closed;
861
862        // TODO: we've already looked up the `hop` in handle_relay_cell, so we shouldn't
863        // have to look it up again! However, we can't pass the `&mut hop` reference from
864        // `handle_relay_cell` to this function, because that makes Rust angry (we'd be
865        // borrowing self as mutable more than once).
866        //
867        // TODO: we _could_ use self.hops.get_mut(..) instead self.hop_mut(..) inside
868        // handle_relay_cell to work around the problem described above
869        let hop = self.hops.get_mut(hop_num).ok_or(Error::CircuitClosed)?;
870
871        if message_closes_stream {
872            hop.ending_msg_received(stream_id)?;
873
874            return Ok(None);
875        }
876
877        let begin = msg
878            .decode::<Begin>()
879            .map_err(|e| Error::from_bytes_err(e, "Invalid Begin message"))?
880            .into_msg();
881
882        let req = IncomingStreamRequest::Begin(begin);
883
884        {
885            use crate::stream::IncomingStreamRequestDisposition::*;
886
887            let ctx = crate::stream::IncomingStreamRequestContext { request: &req };
888            // IMPORTANT: super::syncview::CircHopSyncView::n_open_streams() (called via disposition() below)
889            // accesses the stream map mutexes!
890            //
891            // This means it's very important not to call this function while any of the hop's
892            // stream map mutex is held.
893            let view = CircHopSyncView::new(hop.outbound());
894
895            match handler.filter.as_mut().disposition(&ctx, &view)? {
896                Accept => {}
897                CloseCircuit => return Ok(Some(CircuitCmd::CleanShutdown)),
898                RejectRequest(end) => {
899                    let end_msg = AnyRelayMsgOuter::new(Some(stream_id), end.into());
900                    let cell = SendRelayCell {
901                        hop: Some(hop_num),
902                        early: false,
903                        cell: end_msg,
904                    };
905                    return Ok(Some(CircuitCmd::Send(cell)));
906                }
907            }
908        }
909
910        // TODO: Sadly, we need to look up `&mut hop` yet again,
911        // since we needed to pass `&self.hops` by reference to our filter above. :(
912        let hop = self.hops.get_mut(hop_num).ok_or(Error::CircuitClosed)?;
913        let relay_cell_format = hop.relay_cell_format();
914
915        let memquota = StreamAccount::new(&self.memquota)?;
916
917        let cmd_checker = InboundDataCmdChecker::new_connected();
918        let stream_components = hop.add_ent_with_id(
919            self.chan_sender.time_provider(),
920            stream_id,
921            cmd_checker,
922            &memquota,
923        )?;
924
925        let outcome = Pin::new(&mut handler.incoming_sender).try_send(StreamReqInfo {
926            req,
927            stream_id,
928            hop: Some((leg, hop_num).into()),
929            stream_components,
930            memquota,
931            relay_cell_format,
932        });
933
934        log_ratelim!("Delivering message to incoming stream handler"; outcome);
935
936        if let Err(e) = outcome {
937            if e.is_full() {
938                // The IncomingStreamRequestHandler's stream is full; it isn't
939                // handling requests fast enough. So instead, we reply with an
940                // END cell.
941                let end_msg = AnyRelayMsgOuter::new(
942                    Some(stream_id),
943                    End::new_with_reason(EndReason::RESOURCELIMIT).into(),
944                );
945
946                let cell = SendRelayCell {
947                    hop: Some(hop_num),
948                    early: false,
949                    cell: end_msg,
950                };
951                return Ok(Some(CircuitCmd::Send(cell)));
952            } else if e.is_disconnected() {
953                // The IncomingStreamRequestHandler's stream has been dropped.
954                // In the Tor protocol as it stands, this always means that the
955                // circuit itself is out-of-use and should be closed. (See notes
956                // on `allow_stream_requests.`)
957                //
958                // Note that we will _not_ reach this point immediately after
959                // the IncomingStreamRequestHandler is dropped; we won't hit it
960                // until we next get an incoming request.  Thus, if we do later
961                // want to add early detection for a dropped
962                // IncomingStreamRequestHandler, we need to do it elsewhere, in
963                // a different way.
964                debug!(
965                    circ_id = %self.unique_id,
966                    "Incoming stream request receiver dropped",
967                );
968                // This will _cause_ the circuit to get closed.
969                return Err(Error::CircuitClosed);
970            } else {
971                // There are no errors like this with the current design of
972                // futures::mpsc, but we shouldn't just ignore the possibility
973                // that they'll be added later.
974                return Err(Error::from((into_internal!(
975                    "try_send failed unexpectedly"
976                ))(e)));
977            }
978        }
979
980        Ok(None)
981    }
982
983    /// Helper: process a destroy cell.
984    #[allow(clippy::unnecessary_wraps)]
985    fn handle_destroy_cell(&mut self) -> Result<CircuitCmd> {
986        // I think there is nothing more to do here.
987        Ok(CircuitCmd::CleanShutdown)
988    }
989
990    /// Handle a [`CtrlMsg::Create`](super::CtrlMsg::Create) message.
991    pub(super) async fn handle_create(
992        &mut self,
993        recv_created: oneshot::Receiver<CreateResponse>,
994        handshake: CircuitHandshake,
995        settings: HopSettings,
996        done: ReactorResultChannel<()>,
997    ) -> StdResult<(), ReactorError> {
998        let ret = match handshake {
999            CircuitHandshake::CreateFast => self.create_firsthop_fast(recv_created, settings).await,
1000            CircuitHandshake::Ntor {
1001                public_key,
1002                ed_identity,
1003            } => {
1004                self.create_firsthop_ntor(recv_created, ed_identity, public_key, settings)
1005                    .await
1006            }
1007            CircuitHandshake::NtorV3 { public_key } => {
1008                self.create_firsthop_ntor_v3(recv_created, public_key, settings)
1009                    .await
1010            }
1011        };
1012        let _ = done.send(ret); // don't care if sender goes away
1013
1014        // TODO: maybe we don't need to flush here?
1015        // (we could let run_once() handle all the flushing)
1016        self.chan_sender.flush().await?;
1017
1018        Ok(())
1019    }
1020
1021    /// Helper: create the first hop of a circuit.
1022    ///
1023    /// This is parameterized not just on the RNG, but a wrapper object to
1024    /// build the right kind of create cell, and a handshake object to perform
1025    /// the cryptographic handshake.
1026    async fn create_impl<H, W, M>(
1027        &mut self,
1028        recvcreated: oneshot::Receiver<CreateResponse>,
1029        wrap: &W,
1030        key: &H::KeyType,
1031        mut settings: HopSettings,
1032        msg: &M,
1033    ) -> Result<()>
1034    where
1035        H: ClientHandshake + HandshakeAuxDataHandler,
1036        W: CreateHandshakeWrap,
1037        H::KeyGen: KeyGenerator,
1038        M: Borrow<H::ClientAuxData>,
1039    {
1040        // We don't need to shut down the circuit on failure here, since this
1041        // function consumes the PendingClientCirc and only returns
1042        // a ClientCirc on success.
1043
1044        let (state, msg) = H::client1(&mut rand::rng(), key, msg)?;
1045        let create_cell = wrap.to_chanmsg(msg);
1046        trace!(
1047            circ_id = %self.unique_id,
1048            create = %create_cell.cmd(),
1049            "Extending to hop 1",
1050        );
1051        self.send_msg(create_cell, None).await?;
1052
1053        let reply = recvcreated
1054            .await
1055            .map_err(|_| Error::CircProto("Circuit closed while waiting".into()))?;
1056
1057        let relay_handshake = wrap.decode_chanmsg(reply)?;
1058        let (server_msg, keygen) = H::client2(state, relay_handshake)?;
1059
1060        H::handle_server_aux_data(&mut settings, &server_msg)?;
1061
1062        let BoxedClientLayer { fwd, back, binding } = settings
1063            .relay_crypt_protocol()
1064            .construct_client_layers(HandshakeRole::Initiator, keygen)?;
1065
1066        trace!(circ_id = %self.unique_id, "Handshake complete; circuit created.");
1067
1068        let peer_id = self.channel.target().clone();
1069
1070        self.add_hop(
1071            path::HopDetail::Relay(peer_id),
1072            fwd,
1073            back,
1074            binding,
1075            &settings,
1076        )?;
1077        Ok(())
1078    }
1079
1080    /// Use the (questionable!) CREATE_FAST handshake to connect to the
1081    /// first hop of this circuit.
1082    ///
1083    /// There's no authentication in CREATE_FAST,
1084    /// so we don't need to know whom we're connecting to: we're just
1085    /// connecting to whichever relay the channel is for.
1086    async fn create_firsthop_fast(
1087        &mut self,
1088        recvcreated: oneshot::Receiver<CreateResponse>,
1089        settings: HopSettings,
1090    ) -> Result<()> {
1091        // In a CREATE_FAST handshake, we can't negotiate a format other than this.
1092        let wrap = CreateFastWrap;
1093        self.create_impl::<CreateFastClient, _, _>(recvcreated, &wrap, &(), settings, &())
1094            .await
1095    }
1096
1097    /// Use the ntor handshake to connect to the first hop of this circuit.
1098    ///
1099    /// Note that the provided keys must match the channel's target,
1100    /// or the handshake will fail.
1101    async fn create_firsthop_ntor(
1102        &mut self,
1103        recvcreated: oneshot::Receiver<CreateResponse>,
1104        ed_identity: pk::ed25519::Ed25519Identity,
1105        pubkey: NtorPublicKey,
1106        settings: HopSettings,
1107    ) -> Result<()> {
1108        // Exit now if we have an Ed25519 or RSA identity mismatch.
1109        let target = RelayIds::builder()
1110            .ed_identity(ed_identity)
1111            .rsa_identity(pubkey.id)
1112            .build()
1113            .expect("Unable to build RelayIds");
1114        self.channel.check_match(&target)?;
1115
1116        let wrap = Create2Wrap {
1117            handshake_type: HandshakeType::NTOR,
1118        };
1119        self.create_impl::<NtorClient, _, _>(recvcreated, &wrap, &pubkey, settings, &())
1120            .await
1121    }
1122
1123    /// Use the ntor-v3 handshake to connect to the first hop of this circuit.
1124    ///
1125    /// Note that the provided key must match the channel's target,
1126    /// or the handshake will fail.
1127    async fn create_firsthop_ntor_v3(
1128        &mut self,
1129        recvcreated: oneshot::Receiver<CreateResponse>,
1130        pubkey: NtorV3PublicKey,
1131        settings: HopSettings,
1132    ) -> Result<()> {
1133        // Exit now if we have a mismatched key.
1134        let target = RelayIds::builder()
1135            .ed_identity(pubkey.id)
1136            .build()
1137            .expect("Unable to build RelayIds");
1138        self.channel.check_match(&target)?;
1139
1140        // Set the client extensions.
1141        let client_extensions = settings.circuit_request_extensions()?;
1142        let wrap = Create2Wrap {
1143            handshake_type: HandshakeType::NTOR_V3,
1144        };
1145
1146        self.create_impl::<NtorV3Client, _, _>(
1147            recvcreated,
1148            &wrap,
1149            &pubkey,
1150            settings,
1151            &client_extensions,
1152        )
1153        .await
1154    }
1155
1156    /// Add a hop to the end of this circuit.
1157    ///
1158    /// Will return an error if the circuit already has [`u8::MAX`] hops.
1159    pub(super) fn add_hop(
1160        &mut self,
1161        peer_id: path::HopDetail,
1162        fwd: Box<dyn OutboundClientLayer + 'static + Send>,
1163        rev: Box<dyn InboundClientLayer + 'static + Send>,
1164        binding: Option<CircuitBinding>,
1165        settings: &HopSettings,
1166    ) -> StdResult<(), Bug> {
1167        let hop_num = self.hops.len();
1168        debug_assert_eq!(hop_num, usize::from(self.num_hops()));
1169
1170        // There are several places in the code that assume that a `usize` hop number
1171        // can be cast or converted to a `u8` hop number,
1172        // so this check is important to prevent panics or incorrect behaviour.
1173        if hop_num == usize::from(u8::MAX) {
1174            return Err(internal!(
1175                "cannot add more hops to a circuit with `u8::MAX` hops"
1176            ));
1177        }
1178
1179        let hop_num = (hop_num as u8).into();
1180
1181        let hop = CircHop::new(self.unique_id, hop_num, settings);
1182        self.hops.push(hop);
1183        self.crypto_in.add_layer(rev);
1184        self.crypto_out.add_layer(fwd);
1185        self.mutable.add_hop(peer_id, binding);
1186
1187        Ok(())
1188    }
1189
1190    /// Handle a RELAY cell on this circuit with stream ID 0.
1191    ///
1192    /// NOTE(prop349): this is part of Arti's "Base Circuit Hop Handler".
1193    /// This function returns a `CircProto` error if `msg` is an unsupported,
1194    /// unexpected, or otherwise invalid message:
1195    ///
1196    ///   * unexpected messages are rejected by returning an error using
1197    ///     [`unsupported_client_cell`]
1198    ///   * SENDME/TRUNCATED messages are rejected if they don't parse
1199    ///   * SENDME authentication tags are validated inside [`Circuit::handle_sendme`]
1200    ///   * conflux cells are handled in the client [`ConfluxMsgHandler`]
1201    ///
1202    /// The error is propagated all the way up to [`Circuit::handle_cell`],
1203    /// and eventually ends up being returned from the reactor's `run_once` function,
1204    /// causing it to shut down.
1205    #[allow(clippy::cognitive_complexity)]
1206    fn handle_meta_cell(
1207        &mut self,
1208        handlers: &mut CellHandlers,
1209        hopnum: HopNum,
1210        msg: UnparsedRelayMsg,
1211    ) -> Result<Option<CircuitCmd>> {
1212        // SENDME cells and TRUNCATED get handled internally by the circuit.
1213
1214        // TODO: This pattern (Check command, try to decode, map error) occurs
1215        // several times, and would be good to extract simplify. Such
1216        // simplification is obstructed by a couple of factors: First, that
1217        // there is not currently a good way to get the RelayCmd from _type_ of
1218        // a RelayMsg.  Second, that decode() [correctly] consumes the
1219        // UnparsedRelayMsg.  I tried a macro-based approach, and didn't care
1220        // for it. -nickm
1221        if msg.cmd() == RelayCmd::SENDME {
1222            let sendme = msg
1223                .decode::<Sendme>()
1224                .map_err(|e| Error::from_bytes_err(e, "sendme message"))?
1225                .into_msg();
1226
1227            return Ok(Some(CircuitCmd::HandleSendMe {
1228                hop: hopnum,
1229                sendme,
1230            }));
1231        }
1232        if msg.cmd() == RelayCmd::TRUNCATED {
1233            let truncated = msg
1234                .decode::<Truncated>()
1235                .map_err(|e| Error::from_bytes_err(e, "truncated message"))?
1236                .into_msg();
1237            let reason = truncated.reason();
1238            debug!(
1239                circ_id = %self.unique_id,
1240                "Truncated from hop {}. Reason: {} [{}]",
1241                hopnum.display(),
1242                reason.human_str(),
1243                reason
1244            );
1245
1246            return Ok(Some(CircuitCmd::CleanShutdown));
1247        }
1248
1249        if msg.cmd() == RelayCmd::DROP {
1250            cfg_if::cfg_if! {
1251                if #[cfg(feature = "circ-padding")] {
1252                    return Ok(None);
1253                } else {
1254                    use crate::util::err::ExcessPadding;
1255                    return Err(Error::ExcessPadding(ExcessPadding::NoPaddingNegotiated, hopnum));
1256                }
1257            }
1258        }
1259
1260        trace!(circ_id = %self.unique_id, cell = ?msg, "Received meta-cell");
1261
1262        #[cfg(feature = "conflux")]
1263        if matches!(
1264            msg.cmd(),
1265            RelayCmd::CONFLUX_LINK
1266                | RelayCmd::CONFLUX_LINKED
1267                | RelayCmd::CONFLUX_LINKED_ACK
1268                | RelayCmd::CONFLUX_SWITCH
1269        ) {
1270            let cmd = self.handle_conflux_msg(hopnum, msg)?;
1271            return Ok(cmd.map(CircuitCmd::from));
1272        }
1273
1274        if self.is_conflux_pending() {
1275            warn!(
1276                circ_id = %self.unique_id,
1277                "received unexpected cell {msg:?} on unlinked conflux circuit",
1278            );
1279            return Err(Error::CircProto(
1280                "Received unexpected cell on unlinked circuit".into(),
1281            ));
1282        }
1283
1284        // For all other command types, we'll only get them in response
1285        // to another command, which should have registered a responder.
1286        //
1287        // TODO: should the conflux state machine be a meta cell handler?
1288        // We'd need to add support for multiple meta handlers, and change the
1289        // MetaCellHandler API to support returning Option<RunOnceCmdInner>
1290        // (because some cells will require sending a response)
1291        if let Some(mut handler) = handlers.meta_handler.take() {
1292            // The handler has a TargetHop so we do a quick convert for equality check.
1293            if handler.expected_hop() == (self.unique_id(), hopnum).into() {
1294                // Somebody was waiting for a message -- maybe this message
1295                let ret = handler.handle_msg(msg, self);
1296                trace!(
1297                    circ_id = %self.unique_id,
1298                    result = ?ret,
1299                    "meta handler completed",
1300                );
1301                match ret {
1302                    #[cfg(feature = "send-control-msg")]
1303                    Ok(MetaCellDisposition::Consumed) => {
1304                        handlers.meta_handler = Some(handler);
1305                        Ok(None)
1306                    }
1307                    Ok(MetaCellDisposition::ConversationFinished) => Ok(None),
1308                    #[cfg(feature = "send-control-msg")]
1309                    Ok(MetaCellDisposition::CloseCirc) => Ok(Some(CircuitCmd::CleanShutdown)),
1310                    Err(e) => Err(e),
1311                }
1312            } else {
1313                // Somebody wanted a message from a different hop!  Put this
1314                // one back.
1315                handlers.meta_handler = Some(handler);
1316
1317                unsupported_client_cell!(msg, hopnum)
1318            }
1319        } else {
1320            // No need to call shutdown here, since this error will
1321            // propagate to the reactor shut it down.
1322            unsupported_client_cell!(msg)
1323        }
1324    }
1325
1326    /// Handle a RELAY_SENDME cell on this circuit with stream ID 0.
1327    #[instrument(level = "trace", skip_all)]
1328    pub(super) fn handle_sendme(
1329        &mut self,
1330        hopnum: HopNum,
1331        msg: Sendme,
1332        signals: CongestionSignals,
1333    ) -> Result<Option<CircuitCmd>> {
1334        // Cloned, because we borrow mutably from self when we get the circhop.
1335        let runtime = self.runtime.clone();
1336
1337        // No need to call "shutdown" on errors in this function;
1338        // it's called from the reactor task and errors will propagate there.
1339        let hop = self
1340            .hop_mut(hopnum)
1341            .ok_or_else(|| Error::CircProto(format!("Couldn't find hop {}", hopnum.display())))?;
1342
1343        let tag = msg.into_sendme_tag().ok_or_else(||
1344                // Versions of Tor <=0.3.5 would omit a SENDME tag in this case;
1345                // but we don't support those any longer.
1346                 Error::CircProto("missing tag on circuit sendme".into()))?;
1347        // Update the CC object that we received a SENDME along with possible congestion signals.
1348        hop.ccontrol()
1349            .note_sendme_received(&runtime, tag, signals)?;
1350        Ok(None)
1351    }
1352
1353    /// Send a message onto the circuit's channel.
1354    ///
1355    /// If the channel is ready to accept messages, it will be sent immediately. If not, the message
1356    /// will be enqueued for sending at a later iteration of the reactor loop.
1357    ///
1358    /// `info` is the status returned from the padding controller when we told it we were queueing
1359    /// this data.  It should be provided whenever possible.
1360    ///
1361    /// # Note
1362    ///
1363    /// Making use of the enqueuing capabilities of this function is discouraged! You should first
1364    /// check whether the channel is ready to receive messages (`self.channel.poll_ready`), and
1365    /// ideally use this to implement backpressure (such that you do not read from other sources
1366    /// that would send here while you know you're unable to forward the messages on).
1367    #[instrument(level = "trace", skip_all)]
1368    async fn send_msg(
1369        &mut self,
1370        msg: AnyChanMsg,
1371        info: Option<QueuedCellPaddingInfo>,
1372    ) -> Result<()> {
1373        let cell = AnyChanCell::new(Some(self.channel_id), msg);
1374        // Note: this future is always `Ready`, so await won't block.
1375        Pin::new(&mut self.chan_sender)
1376            .send_unbounded((cell, info))
1377            .await?;
1378        Ok(())
1379    }
1380
1381    /// Remove all halfstreams that are expired at `now`.
1382    pub(super) fn remove_expired_halfstreams(&mut self, now: Instant) {
1383        self.hops.remove_expired_halfstreams(now);
1384    }
1385
1386    /// Return a reference to the hop corresponding to `hopnum`, if there is one.
1387    pub(super) fn hop(&self, hopnum: HopNum) -> Option<&CircHop> {
1388        self.hops.hop(hopnum)
1389    }
1390
1391    /// Return a mutable reference to the hop corresponding to `hopnum`, if there is one.
1392    pub(super) fn hop_mut(&mut self, hopnum: HopNum) -> Option<&mut CircHop> {
1393        self.hops.get_mut(hopnum)
1394    }
1395
1396    /// Begin a stream with the provided hop in this circuit.
1397    // TODO: see if there's a way that we can clean this up
1398    #[allow(clippy::too_many_arguments)]
1399    pub(super) fn begin_stream(
1400        &mut self,
1401        hop_num: HopNum,
1402        message: AnyRelayMsg,
1403        time_prov: &DynTimeProvider,
1404        cmd_checker: AnyCmdChecker,
1405        memquota: &StreamAccount,
1406    ) -> Result<(SendRelayCell, StreamId, ReactorStreamComponents)> {
1407        let Some(hop) = self.hop_mut(hop_num) else {
1408            return Err(internal!(
1409                "{}: Attempting to send a BEGIN cell to an unknown hop {hop_num:?}",
1410                self.unique_id,
1411            )
1412            .into());
1413        };
1414
1415        hop.begin_stream(message, time_prov, cmd_checker, memquota)
1416    }
1417
1418    /// Close the specified stream
1419    #[instrument(level = "trace", skip_all)]
1420    pub(super) async fn close_stream(
1421        &mut self,
1422        hop_num: HopNum,
1423        sid: StreamId,
1424        behav: CloseStreamBehavior,
1425        reason: streammap::TerminateReason,
1426        expiry: Instant,
1427    ) -> Result<()> {
1428        if let Some(hop) = self.hop_mut(hop_num) {
1429            let res = hop.close_stream(sid, behav, reason, expiry)?;
1430            if let Some(cell) = res {
1431                self.send_relay_cell(cell).await?;
1432            }
1433        }
1434        Ok(())
1435    }
1436
1437    /// Returns true if there are any streams on this circuit
1438    ///
1439    /// Important: this function locks the stream map of its each of the [`CircHop`]s
1440    /// in this circuit, so it must **not** be called from any function where the
1441    /// stream map lock is held.
1442    pub(super) fn has_streams(&self) -> bool {
1443        self.hops.has_streams()
1444    }
1445
1446    /// The number of hops in this circuit.
1447    pub(super) fn num_hops(&self) -> u8 {
1448        // `Circuit::add_hop` checks to make sure that we never have more than `u8::MAX` hops,
1449        // so `self.hops.len()` should be safe to cast to a `u8`.
1450        // If that assumption is violated,
1451        // we choose to panic rather than silently use the wrong hop due to an `as` cast.
1452        self.hops
1453            .len()
1454            .try_into()
1455            .expect("`hops.len()` has more than `u8::MAX` hops")
1456    }
1457
1458    /// Check whether this circuit has any hops.
1459    pub(super) fn has_hops(&self) -> bool {
1460        !self.hops.is_empty()
1461    }
1462
1463    /// Get the `HopNum` of the last hop, if this circuit is non-empty.
1464    ///
1465    /// Returns `None` if the circuit has no hops.
1466    pub(super) fn last_hop_num(&self) -> Option<HopNum> {
1467        let num_hops = self.num_hops();
1468        if num_hops == 0 {
1469            // asked for the last hop, but there are no hops
1470            return None;
1471        }
1472        Some(HopNum::from(num_hops - 1))
1473    }
1474
1475    /// Get the path of the circuit.
1476    ///
1477    /// **Warning:** Do not call while already holding the [`Self::mutable`] lock.
1478    pub(super) fn path(&self) -> Arc<path::Path> {
1479        self.mutable.path()
1480    }
1481
1482    /// Return a ClockSkew declaring how much clock skew the other side of this channel
1483    /// claimed that we had when we negotiated the connection.
1484    pub(super) fn clock_skew(&self) -> ClockSkew {
1485        self.channel.clock_skew()
1486    }
1487
1488    /// Does congestion control use stream SENDMEs for the given `hop`?
1489    ///
1490    /// Returns `None` if `hop` doesn't exist.
1491    pub(super) fn uses_stream_sendme(&self, hop: HopNum) -> Option<bool> {
1492        let hop = self.hop(hop)?;
1493        Some(hop.ccontrol().uses_stream_sendme())
1494    }
1495
1496    /// Returns whether this is a conflux circuit that is not linked yet.
1497    pub(super) fn is_conflux_pending(&self) -> bool {
1498        let Some(status) = self.conflux_status() else {
1499            return false;
1500        };
1501
1502        status != ConfluxStatus::Linked
1503    }
1504
1505    /// Returns the conflux status of this circuit.
1506    ///
1507    /// Returns `None` if this is not a conflux circuit.
1508    pub(super) fn conflux_status(&self) -> Option<ConfluxStatus> {
1509        cfg_if::cfg_if! {
1510            if #[cfg(feature = "conflux")] {
1511                self.conflux_handler
1512                    .as_ref()
1513                    .map(|handler| handler.status())
1514            } else {
1515                None
1516            }
1517        }
1518    }
1519
1520    /// Returns initial RTT on this leg, measured in the conflux handshake.
1521    #[cfg(feature = "conflux")]
1522    pub(super) fn init_rtt(&self) -> Option<Duration> {
1523        self.conflux_handler
1524            .as_ref()
1525            .map(|handler| handler.init_rtt())?
1526    }
1527
1528    /// Start or stop padding at the given hop.
1529    ///
1530    /// Replaces any previous padder at that hop.
1531    ///
1532    /// Return an error if that hop doesn't exist.
1533    #[cfg(feature = "circ-padding-manual")]
1534    pub(super) fn set_padding_at_hop(
1535        &self,
1536        hop: HopNum,
1537        padder: Option<padding::CircuitPadder>,
1538    ) -> Result<()> {
1539        if self.hop(hop).is_none() {
1540            return Err(Error::NoSuchHop);
1541        }
1542        self.padding_ctrl.install_padder_padding_at_hop(hop, padder);
1543        Ok(())
1544    }
1545
1546    /// Determine how exactly to handle a request to handle padding.
1547    ///
1548    /// This is fairly complicated; see the maybenot documentation for more information.
1549    ///
1550    /// ## Limitations
1551    ///
1552    /// In our current padding implementation, a circuit is either blocked or not blocked:
1553    /// we do not keep track of which hop is actually doing the blocking.
1554    #[cfg(feature = "circ-padding")]
1555    fn padding_disposition(&self, send_padding: &padding::SendPadding) -> CircPaddingDisposition {
1556        crate::circuit::padding::padding_disposition(
1557            send_padding,
1558            &self.chan_sender,
1559            self.padding_block.as_ref(),
1560        )
1561    }
1562
1563    /// Handle a request from our padding subsystem to send a padding packet.
1564    #[cfg(feature = "circ-padding")]
1565    pub(super) async fn send_padding(&mut self, send_padding: padding::SendPadding) -> Result<()> {
1566        use CircPaddingDisposition::*;
1567
1568        let target_hop = send_padding.hop;
1569
1570        match self.padding_disposition(&send_padding) {
1571            QueuePaddingNormally => {
1572                let queue_info = self.padding_ctrl.queued_padding(target_hop, send_padding);
1573                self.queue_padding_cell_for_hop(target_hop, queue_info)
1574                    .await?;
1575            }
1576            QueuePaddingAndBypass => {
1577                let queue_info = self.padding_ctrl.queued_padding(target_hop, send_padding);
1578                self.queue_padding_cell_for_hop(target_hop, queue_info)
1579                    .await?;
1580            }
1581            TreatQueuedCellAsPadding => {
1582                self.padding_ctrl
1583                    .replaceable_padding_already_queued(target_hop, send_padding);
1584            }
1585        }
1586        Ok(())
1587    }
1588
1589    /// Generate and encrypt a padding cell, and send it to a targeted hop.
1590    ///
1591    /// Ignores any padding-based blocking.
1592    #[cfg(feature = "circ-padding")]
1593    async fn queue_padding_cell_for_hop(
1594        &mut self,
1595        target_hop: HopNum,
1596        queue_info: Option<QueuedCellPaddingInfo>,
1597    ) -> Result<()> {
1598        use tor_cell::relaycell::msg::Drop as DropMsg;
1599        let msg = SendRelayCell {
1600            hop: Some(target_hop),
1601            // TODO circpad: we will probably want padding machines that can send EARLY cells.
1602            early: false,
1603            cell: AnyRelayMsgOuter::new(None, DropMsg::default().into()),
1604        };
1605        self.send_relay_cell_inner(msg, queue_info).await
1606    }
1607
1608    /// Enable padding-based blocking,
1609    /// or change the rule for padding-based blocking to the one in `block`.
1610    #[cfg(feature = "circ-padding")]
1611    pub(super) fn start_blocking_for_padding(&mut self, block: padding::StartBlocking) {
1612        self.chan_sender.start_blocking();
1613        self.padding_block = Some(block);
1614    }
1615
1616    /// Disable padding-based blocking.
1617    #[cfg(feature = "circ-padding")]
1618    pub(super) fn stop_blocking_for_padding(&mut self) {
1619        self.chan_sender.stop_blocking();
1620        self.padding_block = None;
1621    }
1622
1623    /// The estimated circuit build timeout for a circuit of the specified length.
1624    pub(super) fn estimate_cbt(&self, length: usize) -> Duration {
1625        self.timeouts.circuit_build_timeout(length)
1626    }
1627}
1628
1629impl Drop for Circuit {
1630    fn drop(&mut self) {
1631        let _ = self.channel.close_circuit(self.channel_id);
1632    }
1633}