Skip to main content

tor_proto/circuit/
circhop.rs

1//! Module exposing structures relating to a reactor's view of a circuit hop.
2
3// TODO(relay): don't import from the client module
4use crate::client::circuit::handshake::RelayCryptLayerProtocol;
5
6use crate::ccparams::CongestionControlParams;
7use crate::circuit::CircParameters;
8use crate::congestion::{CongestionControl, sendme};
9use crate::memquota::{SpecificAccount, StreamAccount};
10use crate::stream::CloseStreamBehavior;
11use crate::stream::SEND_WINDOW_INIT;
12use crate::stream::StreamMpscSender;
13use crate::stream::cmdcheck::{AnyCmdChecker, StreamStatus};
14use crate::stream::flow_ctrl::params::FlowCtrlParameters;
15use crate::stream::flow_ctrl::state::{
16    FlowCtrlHooks, StreamFlowCtrl, StreamRateLimit, WithSidechannelMitigations,
17};
18use crate::stream::flow_ctrl::xon_xoff::reader::DrainRateRequest;
19use crate::stream::queue::{StreamQueueReceiver, stream_queue};
20use crate::streammap::{
21    self, EndSentStreamEnt, OpenStreamEnt, ShouldSendEnd, StreamEntMut, StreamMap,
22};
23use crate::util::notify::{NotifyReceiver, NotifySender};
24use crate::{Error, HopNum, Result};
25
26use derive_deftly::Deftly;
27use postage::watch;
28use safelog::sensitive as sv;
29use tracing::{debug, trace};
30
31use tor_cell::chancell::{BoxedCellBody, CircId};
32use tor_cell::relaycell::extend::{CcRequest, CircRequestExt};
33use tor_cell::relaycell::flow_ctrl::{Xoff, Xon, XonKBpsEwma};
34use tor_cell::relaycell::msg::AnyRelayMsg;
35use tor_cell::relaycell::{
36    AnyRelayMsgOuter, RelayCellDecoder, RelayCellDecoderResult, RelayCellFormat, RelayCmd,
37    StreamId, UnparsedRelayMsg,
38};
39use tor_error::{Bug, ErrorKind, HasKind, internal, into_internal};
40use tor_memquota::derive_deftly_template_HasMemoryCost;
41use tor_memquota::mq_queue::{ChannelSpec as _, MpscSpec};
42use tor_protover::named;
43use tor_rtcompat::DynTimeProvider;
44
45use std::num::NonZeroU32;
46use std::pin::Pin;
47use std::result::Result as StdResult;
48use std::sync::{Arc, Mutex};
49use web_time_compat::Instant;
50
51#[cfg(test)]
52use tor_cell::relaycell::msg::SendmeTag;
53
54#[cfg(feature = "relay")]
55use {
56    crate::ccparams::{Algorithm, AlgorithmDiscriminants},
57    crate::circuit::HandshakeSubprotocols,
58    crate::relay::{CircNetParameters, CongestionControlNetParams},
59};
60
61use cfg_if::cfg_if;
62
63/// The size of the stream's outbound RELAY message queue.
64// TODO(tuning): figure out if this is a good size for this buffer
65const CIRCUIT_BUFFER_SIZE: usize = 128;
66
67/// Type of negotiation that we'll be performing as we establish a hop.
68///
69/// Determines what flavor of extensions we can send and receive, which in turn
70/// limits the hop settings we can negotiate.
71///
72// TODO-CGO: This is likely to be refactored when we finally add support for
73// HsV3+CGO, which will require refactoring
74#[derive(Debug, Clone, Copy, Eq, PartialEq)]
75pub(crate) enum HopNegotiationType {
76    /// We're using a handshake in which extension-based negotiation cannot occur.
77    None,
78    /// We're using the HsV3-ntor handshake, in which the client can send extensions,
79    /// but the server cannot.
80    ///
81    /// As a special case, the default relay encryption protocol is the hsv3
82    /// variant of Tor1.
83    //
84    // We would call this "HalfDuplex" or something, but we do not expect to add
85    // any more handshakes of this type.
86    HsV3,
87    /// We're using a handshake in which both client and relay can send extensions.
88    Full,
89}
90
91/// The settings we use for single hop of a circuit.
92///
93/// Unlike [`CircParameters`], this type is crate-internal.
94/// We construct it based on our settings from the circuit,
95/// and from the hop's actual capabilities.
96/// Then, we negotiate with the hop as part of circuit
97/// creation/extension to determine the actual settings that will be in use.
98/// Finally, we use those settings to construct the negotiated circuit hop.
99//
100// TODO: Relays should probably derive an instance of this type too, as
101// part of the circuit creation handshake.
102#[derive(Clone, Debug)]
103pub(crate) struct HopSettings {
104    /// The negotiated congestion control settings for this hop .
105    pub(crate) ccontrol: CongestionControlParams,
106
107    /// Flow control parameters that will be used for streams on this hop.
108    pub(crate) flow_ctrl_params: FlowCtrlParameters,
109
110    /// Maximum number of permitted incoming relay cells for this hop.
111    pub(crate) n_incoming_cells_permitted: Option<u32>,
112
113    /// Maximum number of permitted outgoing relay cells for this hop.
114    pub(crate) n_outgoing_cells_permitted: Option<u32>,
115
116    /// The relay cell encryption algorithm and cell format for this hop.
117    relay_crypt_protocol: RelayCryptLayerProtocol,
118}
119
120impl HopSettings {
121    /// Construct a new `HopSettings` based on `params` (a set of circuit parameters)
122    /// and `caps` (a set of protocol capabilities for a circuit target).
123    ///
124    /// The resulting settings will represent what the client would prefer to negotiate
125    /// (determined by `params`),
126    /// as modified by what the target relay is believed to support (represented by `caps`).
127    ///
128    /// This represents the `HopSettings` in a pre-negotiation state:
129    /// the circuit negotiation process will modify it.
130    #[allow(clippy::unnecessary_wraps)] // likely to become fallible in the future.
131    pub(crate) fn from_params_and_caps(
132        hoptype: HopNegotiationType,
133        params: &CircParameters,
134        caps: &tor_protover::Protocols,
135    ) -> Result<Self> {
136        let mut ccontrol = params.ccontrol.clone();
137        match ccontrol.alg() {
138            crate::ccparams::Algorithm::FixedWindow(_) => {}
139            crate::ccparams::Algorithm::Vegas(_) => {
140                // If the target doesn't support FLOWCTRL_CC, we can't use Vegas.
141                if !caps.supports_named_subver(named::FLOWCTRL_CC) {
142                    ccontrol.use_fallback_alg();
143                }
144            }
145        };
146        if hoptype == HopNegotiationType::None {
147            ccontrol.use_fallback_alg();
148        }
149        let ccontrol = ccontrol; // drop mut
150
151        // Negotiate CGO if it is supported, if CC is also supported,
152        // and if CGO is available on this relay.
153        let relay_crypt_protocol = match hoptype {
154            HopNegotiationType::None => RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0),
155            HopNegotiationType::HsV3 => {
156                cfg_if! {
157                    if #[cfg(feature = "hs-common")] {
158                        if ccontrol.alg().compatible_with_cgo() && caps.supports_named_subver(named::RELAY_CRYPT_CGO) {
159                            RelayCryptLayerProtocol::Cgo
160                        } else {
161                            RelayCryptLayerProtocol::HsV3(RelayCellFormat::V0)
162                        }
163                    } else {
164                        return Err(
165                            tor_error::internal!("Unexpectedly tried to negotiate HsV3 without support!").into(),
166                        );
167                    }
168                }
169            }
170            HopNegotiationType::Full => {
171                #[allow(clippy::overly_complex_bool_expr)]
172                if ccontrol.alg().compatible_with_cgo()
173                    && caps.supports_named_subver(named::RELAY_NEGOTIATE_SUBPROTO)
174                    && caps.supports_named_subver(named::RELAY_CRYPT_CGO)
175                {
176                    RelayCryptLayerProtocol::Cgo
177                } else {
178                    RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0)
179                }
180            }
181        };
182
183        Ok(Self {
184            ccontrol,
185            flow_ctrl_params: params.flow_ctrl.clone(),
186            relay_crypt_protocol,
187            n_incoming_cells_permitted: params.n_incoming_cells_permitted,
188            n_outgoing_cells_permitted: params.n_outgoing_cells_permitted,
189        })
190    }
191
192    /// Build a [`HopSettings`] from the parameters requested during a circuit handshake.
193    //
194    // We disable `unused` warnings at the root of tor-proto,
195    // but it's nice to have here so we re-enable it.
196    #[warn(unused)]
197    #[cfg(feature = "relay")]
198    pub(crate) fn from_handshake_params(
199        circ_net_params: CircNetParameters,
200        cc_algorithm: AlgorithmDiscriminants,
201        subprotos_requested: HandshakeSubprotocols,
202    ) -> StdResult<Self, HandshakeParamsError> {
203        // Unpack everything to make sure that we aren't missing anything
204        // (otherwise clippy would warn).
205        let CircNetParameters {
206            cc:
207                CongestionControlNetParams {
208                    fixed_window,
209                    vegas_exit,
210                    cwnd,
211                    rtt,
212                    flow_ctrl,
213                },
214        } = circ_net_params;
215
216        let HandshakeSubprotocols { relay_crypt_cgo } = subprotos_requested;
217
218        // TODO: We have similar logic in and around `HopSettings` that deals with determining the
219        // crypt protocol and cc algorithm to use. We might want to try to dedup some of this, or
220        // make it more self-contained. This is a bit tricky though since the code is used in
221        // different situations and the inputs are not the same.
222        let (cc_algorithm, relay_crypt_protocol) = match (cc_algorithm, relay_crypt_cgo) {
223            (AlgorithmDiscriminants::FixedWindow, false) => (
224                Algorithm::FixedWindow(fixed_window),
225                RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0),
226            ),
227            (AlgorithmDiscriminants::FixedWindow, true) => {
228                return Err(HandshakeParamsError::IncompatibleParams(
229                    "requested CGO but not congestion control",
230                ));
231            }
232            (AlgorithmDiscriminants::Vegas, false) => (
233                Algorithm::Vegas(vegas_exit),
234                RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0),
235            ),
236            (AlgorithmDiscriminants::Vegas, true) => {
237                (Algorithm::Vegas(vegas_exit), RelayCryptLayerProtocol::Cgo)
238            }
239        };
240
241        // TODO(arti#2442): The builder pattern here seems like a footgun.
242        let ccontrol = CongestionControlParams::builder()
243            .alg(cc_algorithm)
244            .fixed_window_params(fixed_window)
245            .cwnd_params(cwnd)
246            .rtt_params(rtt)
247            .build()
248            .map_err(into_internal!("Could not build `CongestionControlParams`"))?;
249
250        Ok(Self {
251            ccontrol,
252            flow_ctrl_params: flow_ctrl,
253            relay_crypt_protocol,
254            n_incoming_cells_permitted: None,
255            n_outgoing_cells_permitted: None,
256        })
257    }
258
259    /// Return the negotiated relay crypto protocol.
260    pub(crate) fn relay_crypt_protocol(&self) -> RelayCryptLayerProtocol {
261        self.relay_crypt_protocol
262    }
263
264    /// Return the client circuit-creation extensions that we should use in order to negotiate
265    /// these circuit hop parameters.
266    #[allow(clippy::unnecessary_wraps)]
267    pub(crate) fn circuit_request_extensions(&self) -> Result<Vec<CircRequestExt>> {
268        // allow 'unused_mut' because of the combinations of `cfg` conditions below
269        #[allow(unused_mut)]
270        let mut client_extensions = Vec::new();
271
272        #[allow(unused, unused_mut)]
273        let mut cc_extension_set = false;
274
275        if self.ccontrol.is_enabled() {
276            client_extensions.push(CircRequestExt::CcRequest(CcRequest::default()));
277            cc_extension_set = true;
278        }
279
280        // See whether we need to send a list of required protocol capabilities.
281        // These aren't "negotiated" per se; they're simply demanded.
282        // The relay will refuse the circuit if it doesn't support all of them,
283        // and if any of them isn't supported in the SubprotocolRequest extension.
284        //
285        // (In other words, don't add capabilities here just because you want the
286        // relay to have them! They must be explicitly listed as supported for use
287        // with this extension. For the current list, see
288        // https://spec.torproject.org/tor-spec/create-created-cells.html#subproto-request)
289        //
290        // TODO: Should this use `HandshakeSubprotocols` so that the above comment has some
291        // compile-time checks?
292        #[allow(unused_mut)]
293        let mut required_protocol_capabilities: Vec<tor_protover::NamedSubver> = Vec::new();
294
295        if matches!(self.relay_crypt_protocol(), RelayCryptLayerProtocol::Cgo) {
296            if !cc_extension_set {
297                return Err(tor_error::internal!("Tried to negotiate CGO without CC.").into());
298            }
299            required_protocol_capabilities.push(tor_protover::named::RELAY_CRYPT_CGO);
300        }
301
302        if !required_protocol_capabilities.is_empty() {
303            client_extensions.push(CircRequestExt::SubprotocolRequest(
304                required_protocol_capabilities.into_iter().collect(),
305            ));
306        }
307
308        Ok(client_extensions)
309    }
310}
311
312#[cfg(test)]
313impl std::default::Default for CircParameters {
314    fn default() -> Self {
315        Self {
316            extend_by_ed25519_id: true,
317            ccontrol: crate::congestion::test_utils::params::build_cc_fixed_params(),
318            flow_ctrl: FlowCtrlParameters::defaults_for_tests(),
319            n_incoming_cells_permitted: None,
320            n_outgoing_cells_permitted: None,
321        }
322    }
323}
324
325/// An error that can occur when building a [`HopSettings`] using parameters requested during a
326/// circuit handshake.
327#[derive(Clone, Debug, thiserror::Error)]
328pub(crate) enum HandshakeParamsError {
329    /// The provided parameters are incompatible with each other.
330    #[error("The provided handshake parameters are incompatible with each other: {0}")]
331    IncompatibleParams(&'static str),
332    /// An internal error.
333    #[error("Internal error")]
334    Internal(#[from] tor_error::Bug),
335}
336
337impl HasKind for HandshakeParamsError {
338    fn kind(&self) -> ErrorKind {
339        match self {
340            Self::IncompatibleParams(_) => ErrorKind::TorProtocolViolation,
341            Self::Internal(_) => ErrorKind::Internal,
342        }
343    }
344}
345
346impl CircParameters {
347    /// Constructor
348    pub fn new(
349        extend_by_ed25519_id: bool,
350        ccontrol: CongestionControlParams,
351        flow_ctrl: FlowCtrlParameters,
352    ) -> Self {
353        Self {
354            extend_by_ed25519_id,
355            ccontrol,
356            flow_ctrl,
357            n_incoming_cells_permitted: None,
358            n_outgoing_cells_permitted: None,
359        }
360    }
361}
362
363/// Instructions for sending a RELAY cell.
364///
365/// This instructs a circuit reactor to send a RELAY cell to a given target
366/// (a hop, if we are a client, or the client, if we are a relay).
367#[derive(educe::Educe)]
368#[educe(Debug)]
369pub(crate) struct SendRelayCell {
370    /// The hop number, or `None` if we are a relay.
371    pub(crate) hop: Option<HopNum>,
372    /// Whether to use a RELAY_EARLY cell.
373    pub(crate) early: bool,
374    /// The cell to send.
375    pub(crate) cell: AnyRelayMsgOuter,
376}
377
378/// The inbound state of a hop.
379pub(crate) struct CircHopInbound {
380    /// Decodes relay cells received from this hop.
381    decoder: RelayCellDecoder,
382    /// Remaining permitted incoming relay cells from this hop, plus 1.
383    ///
384    /// (In other words, `None` represents no limit,
385    /// `Some(1)` represents an exhausted limit,
386    /// and `Some(n)` means that n-1 more cells may be received.)
387    ///
388    /// If this ever decrements from Some(1), then the circuit must be torn down with an error.
389    n_incoming_cells_permitted: Option<NonZeroU32>,
390}
391
392/// The outbound state of a hop.
393pub(crate) struct CircHopOutbound {
394    /// Congestion control object.
395    ///
396    /// This object is also in charge of handling circuit level SENDME logic for this hop.
397    ccontrol: Arc<Mutex<CongestionControl>>,
398    /// Map from stream IDs to streams.
399    ///
400    /// We store this with the reactor instead of the circuit, since the
401    /// reactor needs it for every incoming cell on a stream, whereas
402    /// the circuit only needs it when allocating new streams.
403    ///
404    /// NOTE: this is behind a mutex because the client reactor polls the `StreamMap`s
405    /// of all hops concurrently, in a `FuturesUnordered`. Without the mutex,
406    /// this wouldn't be possible, because it would mean holding multiple
407    /// mutable references to `self` (the reactor). Note, however,
408    /// that there should never be any contention on this mutex:
409    /// we never create more than one
410    /// `CircHopList::ready_streams_iterator()` stream
411    /// at a time, and we never clone/lock the hop's `StreamMap` outside of it.
412    ///
413    /// Additionally, the stream map of the last hop (join point) of a conflux tunnel
414    /// is shared with all the circuits in the tunnel.
415    map: Arc<Mutex<StreamMap>>,
416    /// Format to use for relay cells.
417    //
418    // When we have packed/fragmented cells, this may be replaced by a RelayCellEncoder.
419    relay_format: RelayCellFormat,
420    /// Flow control parameters for new streams.
421    flow_ctrl_params: Arc<FlowCtrlParameters>,
422    /// Remaining permitted outgoing relay cells from this hop, plus 1.
423    ///
424    /// If this ever decrements from Some(1), then the circuit must be torn down with an error.
425    n_outgoing_cells_permitted: Option<NonZeroU32>,
426}
427
428impl CircHopInbound {
429    /// Create a new [`CircHopInbound`].
430    pub(crate) fn new(decoder: RelayCellDecoder, settings: &HopSettings) -> Self {
431        Self {
432            decoder,
433            n_incoming_cells_permitted: settings.n_incoming_cells_permitted.map(cvt),
434        }
435    }
436
437    /// Parse a RELAY or RELAY_EARLY cell body.
438    ///
439    /// Requires that the cryptographic checks on the message have already been
440    /// performed
441    pub(crate) fn decode(&mut self, cell: BoxedCellBody) -> Result<RelayCellDecoderResult> {
442        self.decoder
443            .decode(cell)
444            .map_err(|e| Error::from_bytes_err(e, "relay cell"))
445    }
446
447    /// Decrement the limit of inbound cells that may be received from this hop; give
448    /// an error if it would reach zero.
449    pub(crate) fn decrement_cell_limit(&mut self) -> Result<()> {
450        try_decrement_cell_limit(&mut self.n_incoming_cells_permitted)
451            .map_err(|_| Error::ExcessInboundCells)
452    }
453}
454
455impl CircHopOutbound {
456    /// Create a new [`CircHopOutbound`].
457    pub(crate) fn new(
458        ccontrol: Arc<Mutex<CongestionControl>>,
459        relay_format: RelayCellFormat,
460        flow_ctrl_params: Arc<FlowCtrlParameters>,
461        settings: &HopSettings,
462    ) -> Self {
463        Self {
464            ccontrol,
465            map: Arc::new(Mutex::new(StreamMap::new())),
466            relay_format,
467            flow_ctrl_params,
468            n_outgoing_cells_permitted: settings.n_outgoing_cells_permitted.map(cvt),
469        }
470    }
471
472    /// Start a stream. Creates an entry in the stream map with the given channels, and sends the
473    /// `message` to the provided hop.
474    pub(crate) fn begin_stream(
475        &mut self,
476        hop: Option<HopNum>,
477        message: AnyRelayMsg,
478        time_prov: &DynTimeProvider,
479        cmd_checker: AnyCmdChecker,
480        memquota: &StreamAccount,
481    ) -> Result<(SendRelayCell, StreamId, ReactorStreamComponents)> {
482        // TODO: This has a lot of duplicated code with `Self::add_ent_with_id()`.
483
484        // A channel for the reactor to inform the writer of a new rate limit.
485        let (rate_limit_tx, rate_limit_rx) = watch::channel_with(StreamRateLimit::MAX);
486
487        // A channel for the reactor to request a new drain rate from the reader.
488        // Typically this notification will be sent after an XOFF is sent so that the reader can
489        // send us a new drain rate when the stream data queue becomes empty.
490        let mut drain_rate_request_tx = NotifySender::new_typed();
491        let drain_rate_request_rx = drain_rate_request_tx.subscribe();
492
493        let flow_ctrl = self.build_flow_ctrl(
494            // We are starting the stream,
495            // so we're a client and want flow control sidechannel mitigations.
496            WithSidechannelMitigations::Enabled,
497            rate_limit_tx,
498            drain_rate_request_tx,
499        )?;
500
501        let stream_queue_max_len = flow_ctrl.inbound_queue_max_len();
502
503        // A queue for inbound RELAY messages.
504        let (sender, receiver) = stream_queue(stream_queue_max_len, memquota, time_prov)?;
505
506        // A queue for outbound RELAY messages.
507        let (msg_tx, msg_rx) = MpscSpec::new(CIRCUIT_BUFFER_SIZE)
508            .new_mq(time_prov.clone(), memquota.as_raw_account())?;
509
510        let r = self.map.lock().expect("lock poisoned").add_ent(
511            sender,
512            msg_rx,
513            flow_ctrl,
514            cmd_checker,
515        )?;
516        let cell = AnyRelayMsgOuter::new(Some(r), message);
517
518        let stream_components = ReactorStreamComponents {
519            stream_inbound_rx: receiver,
520            stream_outbound_tx: msg_tx,
521            rate_limit_rx,
522            drain_rate_request_rx,
523        };
524
525        Ok((
526            SendRelayCell {
527                hop,
528                early: false,
529                cell,
530            },
531            r,
532            stream_components,
533        ))
534    }
535
536    /// Close the stream associated with `id` because the stream was dropped.
537    ///
538    /// If we have not already received an END cell on this stream, send one.
539    /// If no END cell is specified, an END cell with the reason byte set to
540    /// REASON_MISC will be sent.
541    ///
542    // Note(relay): `circ_uniq_id` is an opaque displayable type
543    // because relays use a different circuit ID type
544    // than clients. Eventually, we should probably make
545    // them both use the same ID type, or have a nicer approach here
546    #[allow(clippy::too_many_arguments)]
547    pub(crate) fn close_stream(
548        &mut self,
549        circ_uniq_id: impl std::fmt::Display,
550        circ_id: CircId,
551        id: StreamId,
552        hop: Option<HopNum>,
553        message: CloseStreamBehavior,
554        why: streammap::TerminateReason,
555        expiry: Instant,
556    ) -> Result<Option<SendRelayCell>> {
557        let should_send_end = self
558            .map
559            .lock()
560            .expect("lock poisoned")
561            .terminate(id, why, expiry)?;
562        trace!(
563            circ_uniq_id = %circ_uniq_id,
564            circ_id = %circ_id,
565            stream_id = %id,
566            should_send_end = ?should_send_end,
567            "Ending stream",
568        );
569        // TODO: I am about 80% sure that we only send an END cell if
570        // we didn't already get an END cell.  But I should double-check!
571        let end_message = match should_send_end {
572            ShouldSendEnd::Send => match message {
573                CloseStreamBehavior::SendEnd(end_message) => end_message.into(),
574                CloseStreamBehavior::SendResolved(resolved_message) => resolved_message.into(),
575                CloseStreamBehavior::SendNothing => return Ok(None),
576            },
577            ShouldSendEnd::DontSend => return Ok(None),
578        };
579
580        let end_cell = AnyRelayMsgOuter::new(Some(id), end_message);
581        let cell = SendRelayCell {
582            hop,
583            early: false,
584            cell: end_cell,
585        };
586
587        Ok(Some(cell))
588    }
589
590    /// Check if we should send an XON message.
591    ///
592    /// If we should, then returns the XON message that should be sent.
593    pub(crate) fn maybe_send_xon(
594        &mut self,
595        rate: XonKBpsEwma,
596        id: StreamId,
597    ) -> Result<Option<Xon>> {
598        // the call below will return an error if XON/XOFF aren't supported,
599        // so we check for support here
600        if !self
601            .ccontrol()
602            .lock()
603            .expect("poisoned lock")
604            .uses_xon_xoff()
605        {
606            return Ok(None);
607        }
608
609        let mut map = self.map.lock().expect("lock poisoned");
610        let Some(StreamEntMut::Open(ent)) = map.get_mut(id) else {
611            // stream went away
612            return Ok(None);
613        };
614
615        ent.maybe_send_xon(rate)
616    }
617
618    /// Check if we should send an XOFF message.
619    ///
620    /// If we should, then returns the XOFF message that should be sent.
621    pub(crate) fn maybe_send_xoff(&mut self, id: StreamId) -> Result<Option<Xoff>> {
622        // the call below will return an error if XON/XOFF aren't supported,
623        // so we check for support here
624        if !self
625            .ccontrol()
626            .lock()
627            .expect("poisoned lock")
628            .uses_xon_xoff()
629        {
630            return Ok(None);
631        }
632
633        let mut map = self.map.lock().expect("lock poisoned");
634        let Some(StreamEntMut::Open(ent)) = map.get_mut(id) else {
635            // stream went away
636            return Ok(None);
637        };
638
639        ent.maybe_send_xoff()
640    }
641
642    /// Return the format that is used for relay cells sent to this hop.
643    ///
644    /// For the most part, this format isn't necessary to interact with a CircHop;
645    /// it becomes relevant when we are deciding _what_ we can encode for the hop.
646    pub(crate) fn relay_cell_format(&self) -> RelayCellFormat {
647        self.relay_format
648    }
649
650    /// Delegate to CongestionControl, for testing purposes
651    #[cfg(test)]
652    pub(crate) fn send_window_and_expected_tags(&self) -> (u32, Vec<SendmeTag>) {
653        self.ccontrol()
654            .lock()
655            .expect("poisoned lock")
656            .send_window_and_expected_tags()
657    }
658
659    /// Return the number of open streams on this hop.
660    ///
661    /// WARNING: because this locks the stream map mutex,
662    /// it should never be called from a context where that mutex is already locked.
663    pub(crate) fn n_open_streams(&self) -> usize {
664        self.map.lock().expect("lock poisoned").n_open_streams()
665    }
666
667    /// Return a reference to our CongestionControl object.
668    pub(crate) fn ccontrol(&self) -> &Arc<Mutex<CongestionControl>> {
669        &self.ccontrol
670    }
671
672    /// We're about to send `msg`.
673    ///
674    /// See [`OpenStreamEnt::about_to_send`](crate::streammap::OpenStreamEnt::about_to_send).
675    //
676    // TODO prop340: This should take a cell or similar, not a message.
677    //
678    // Note(relay): `circ_uniq_id` is an opaque displayable type
679    // because relays use a different circuit ID type
680    // than clients. Eventually, we should probably make
681    // them both use the same ID type, or have a nicer approach here
682    pub(crate) fn about_to_send(
683        &mut self,
684        circ_uniq_id: impl std::fmt::Display,
685        circ_id: CircId,
686        stream_id: StreamId,
687        msg: &AnyRelayMsg,
688    ) -> Result<()> {
689        let mut hop_map = self.map.lock().expect("lock poisoned");
690        let Some(StreamEntMut::Open(ent)) = hop_map.get_mut(stream_id) else {
691            // This can happen when we have outgoing data queued when we received an END.
692            // We shouldn't return an error here since it would close the circuit along with all
693            // other streams, and instead we just let the caller send this message anyways.
694            // Also the caller only calls `about_to_send()` for DATA cells,
695            // which means that other non-DATA cells don't hit this code path and are always sent,
696            // and so we should handle all cell types consistently.
697            // TODO: We should drop the message and not send it,
698            // but the caller of `about_to_send()` isn't designed to handle fallible sends
699            // so it would need some refactoring to handle this.
700            debug!(
701                circ_uniq_id = %circ_uniq_id,
702                circ_id = %circ_id,
703                stream_id = %stream_id,
704                "sending a relay cell for non-existent or non-open stream!",
705            );
706            return Ok(());
707        };
708
709        ent.about_to_send(msg)
710    }
711
712    /// Add an entry to this map using the specified StreamId.
713    #[cfg(any(feature = "hs-service", feature = "relay"))]
714    pub(crate) fn add_ent_with_id(
715        &self,
716        time_prov: &DynTimeProvider,
717        stream_id: StreamId,
718        cmd_checker: AnyCmdChecker,
719        with_sidechannel_mitigations: WithSidechannelMitigations,
720        memquota: &StreamAccount,
721    ) -> Result<ReactorStreamComponents> {
722        // TODO: This has a lot of duplicated code with `Self::begin_stream()`.
723
724        // A channel for the reactor to inform the writer of a new rate limit.
725        let (rate_limit_tx, rate_limit_rx) = watch::channel_with(StreamRateLimit::MAX);
726
727        // A channel for the reactor to request a new drain rate from the reader.
728        // Typically this notification will be sent after an XOFF is sent so that the reader can
729        // send us a new drain rate when the stream data queue becomes empty.
730        let mut drain_rate_request_tx = NotifySender::new_typed();
731        let drain_rate_request_rx = drain_rate_request_tx.subscribe();
732
733        let flow_ctrl = self.build_flow_ctrl(
734            with_sidechannel_mitigations,
735            rate_limit_tx,
736            drain_rate_request_tx,
737        )?;
738
739        let stream_queue_max_len = flow_ctrl.inbound_queue_max_len();
740
741        // A queue for inbound RELAY messages.
742        let (sender, receiver) = stream_queue(stream_queue_max_len, memquota, time_prov)?;
743
744        // A queue for outbound RELAY messages.
745        let (msg_tx, msg_rx) = MpscSpec::new(CIRCUIT_BUFFER_SIZE)
746            .new_mq(time_prov.clone(), memquota.as_raw_account())?;
747
748        let mut hop_map = self.map.lock().expect("lock poisoned");
749        hop_map.add_ent_with_id(sender, msg_rx, flow_ctrl, stream_id, cmd_checker)?;
750
751        Ok(ReactorStreamComponents {
752            stream_inbound_rx: receiver,
753            stream_outbound_tx: msg_tx,
754            rate_limit_rx,
755            drain_rate_request_rx,
756        })
757    }
758
759    /// Builds the reactor's flow control handler for a new stream.
760    // TODO: remove the `Result` once we remove the "flowctl-cc" feature
761    #[expect(clippy::unnecessary_wraps)]
762    fn build_flow_ctrl(
763        &self,
764        with_sidechannel_mitigations: WithSidechannelMitigations,
765        rate_limit_updater: watch::Sender<StreamRateLimit>,
766        drain_rate_requester: NotifySender<DrainRateRequest>,
767    ) -> Result<StreamFlowCtrl> {
768        let params = Arc::clone(&self.flow_ctrl_params);
769
770        if self
771            .ccontrol()
772            .lock()
773            .expect("poisoned lock")
774            .uses_stream_sendme()
775        {
776            let window = sendme::StreamSendWindow::new(SEND_WINDOW_INIT);
777            Ok(StreamFlowCtrl::new_window(window))
778        } else {
779            Ok(StreamFlowCtrl::new_xon_xoff(
780                params,
781                with_sidechannel_mitigations,
782                rate_limit_updater,
783                drain_rate_requester,
784            ))
785        }
786    }
787
788    /// Deliver `msg` to the specified open stream entry `ent`.
789    fn deliver_msg_to_stream(
790        streamid: StreamId,
791        ent: &mut OpenStreamEnt,
792        cell_counts_toward_windows: bool,
793        msg: UnparsedRelayMsg,
794    ) -> Result<bool> {
795        use tor_async_utils::SinkTrySend as _;
796        use tor_async_utils::SinkTrySendError as _;
797
798        // The stream for this message exists, and is open.
799
800        // We need to handle SENDME/XON/XOFF messages here, not in the stream's recv() method, or
801        // else we'd never notice them if the stream isn't reading.
802        match msg.cmd() {
803            RelayCmd::SENDME => {
804                ent.put_for_incoming_sendme(msg)?;
805                return Ok(false);
806            }
807            RelayCmd::XON => {
808                ent.handle_incoming_xon(msg)?;
809                return Ok(false);
810            }
811            RelayCmd::XOFF => {
812                ent.handle_incoming_xoff(msg)?;
813                return Ok(false);
814            }
815            _ => {}
816        }
817
818        let message_closes_stream = ent.cmd_checker.check_msg(&msg)? == StreamStatus::Closed;
819
820        if let Err(e) = Pin::new(&mut ent.sink).try_send(msg) {
821            if e.is_full() {
822                return Err(internal!(
823                    "Stream (ID {}) uses an unbounded queue, but apparently it's full?",
824                    sv(streamid),
825                )
826                .into());
827            }
828            if e.is_disconnected() && cell_counts_toward_windows {
829                // the other side of the stream has gone away; remember
830                // that we received a cell that we couldn't queue for it.
831                //
832                // Later this value will be recorded in a half-stream.
833                ent.dropped += 1;
834            }
835        }
836
837        Ok(message_closes_stream)
838    }
839
840    /// Note that we received an END message (or other message indicating the end of
841    /// the stream) on the stream with `id`.
842    ///
843    /// See [`StreamMap::ending_msg_received`](crate::streammap::StreamMap::ending_msg_received).
844    #[cfg(feature = "hs-service")]
845    pub(crate) fn ending_msg_received(&self, stream_id: StreamId) -> Result<()> {
846        let mut hop_map = self.map.lock().expect("lock poisoned");
847
848        hop_map.ending_msg_received(stream_id)?;
849
850        Ok(())
851    }
852
853    /// Handle `msg`, delivering it to the stream with the specified `streamid` if appropriate.
854    ///
855    /// Returns back the provided `msg`, if the message is an incoming stream request
856    /// that needs to be handled by the calling code.
857    ///
858    // TODO: the above is a bit of a code smell -- we should try to avoid passing the msg
859    // back and forth like this.
860    pub(crate) fn handle_msg<F>(
861        &self,
862        possible_proto_violation_err: F,
863        cell_counts_toward_windows: bool,
864        streamid: StreamId,
865        msg: UnparsedRelayMsg,
866        now: Instant,
867    ) -> Result<Option<UnparsedRelayMsg>>
868    where
869        F: FnOnce(StreamId) -> Error,
870    {
871        let mut hop_map = self.map.lock().expect("lock poisoned");
872
873        match hop_map.get_mut(streamid) {
874            Some(StreamEntMut::Open(ent)) => {
875                // Can't have a stream level SENDME when congestion control is enabled.
876                let message_closes_stream =
877                    Self::deliver_msg_to_stream(streamid, ent, cell_counts_toward_windows, msg)?;
878
879                if message_closes_stream {
880                    hop_map.ending_msg_received(streamid)?;
881                }
882            }
883            Some(StreamEntMut::EndSent(EndSentStreamEnt { expiry, .. })) if now >= *expiry => {
884                return Err(possible_proto_violation_err(streamid));
885            }
886            Some(StreamEntMut::EndSent(_))
887                if matches!(
888                    msg.cmd(),
889                    RelayCmd::BEGIN | RelayCmd::BEGIN_DIR | RelayCmd::RESOLVE
890                ) =>
891            {
892                // If the other side is sending us a BEGIN but hasn't yet acknowledged our END
893                // message, just remove the old stream from the map and stop waiting for a
894                // response
895                hop_map.ending_msg_received(streamid)?;
896                return Ok(Some(msg));
897            }
898            Some(StreamEntMut::EndSent(EndSentStreamEnt { half_stream, .. })) => {
899                // We sent an end but maybe the other side hasn't heard.
900
901                match half_stream.handle_msg(msg)? {
902                    StreamStatus::Open => {}
903                    StreamStatus::Closed => {
904                        hop_map.ending_msg_received(streamid)?;
905                    }
906                }
907            }
908            None if matches!(
909                msg.cmd(),
910                RelayCmd::BEGIN | RelayCmd::BEGIN_DIR | RelayCmd::RESOLVE
911            ) =>
912            {
913                return Ok(Some(msg));
914            }
915            _ => {
916                // No stream wants this message, or ever did.
917                return Err(possible_proto_violation_err(streamid));
918            }
919        }
920
921        Ok(None)
922    }
923
924    /// Get the stream map of this hop.
925    pub(crate) fn stream_map(&self) -> &Arc<Mutex<StreamMap>> {
926        &self.map
927    }
928
929    /// Set the stream map of this hop to `map`.
930    ///
931    /// Returns an error if the existing stream map of the hop has any open stream.
932    pub(crate) fn set_stream_map(&mut self, map: Arc<Mutex<StreamMap>>) -> StdResult<(), Bug> {
933        if self.n_open_streams() != 0 {
934            return Err(internal!("Tried to discard existing open streams?!"));
935        }
936
937        self.map = map;
938
939        Ok(())
940    }
941
942    /// Decrement the limit of outbound cells that may be sent to this hop; give
943    /// an error if it would reach zero.
944    pub(crate) fn decrement_cell_limit(&mut self) -> Result<()> {
945        try_decrement_cell_limit(&mut self.n_outgoing_cells_permitted)
946            .map_err(|_| Error::ExcessOutboundCells)
947    }
948}
949
950/// If `val` is `Some(1)`, return Err(());
951/// otherwise decrement it (if it is Some) and return Ok(()).
952#[inline]
953fn try_decrement_cell_limit(val: &mut Option<NonZeroU32>) -> StdResult<(), ()> {
954    // This is a bit verbose, but I've confirmed that it optimizes nicely.
955    match val {
956        Some(x) => {
957            let z = u32::from(*x);
958            if z == 1 {
959                Err(())
960            } else {
961                *x = (z - 1).try_into().expect("NonZeroU32 was zero?!");
962                Ok(())
963            }
964        }
965        None => Ok(()),
966    }
967}
968
969/// Convert a limit from the form used in a HopSettings to that used here.
970/// (The format we use here is more compact.)
971fn cvt(limit: u32) -> NonZeroU32 {
972    // See "known limitations" comment on n_incoming_cells_permitted.
973    limit
974        .saturating_add(1)
975        .try_into()
976        .expect("Adding one left it as zero?")
977}
978
979/// A collection of components that can be used to interact with the reactor's view of a Tor stream.
980//
981// TODO: We also have a `StreamComponents` type that is used and built outside of the reactor.
982// It's maybe confusing to have these similar type names, so a better name would be nice.
983//
984// TODO(arti#2068): The components we return should maybe depend on what type of flow control is
985// used, so in the future we might want to make some of these fields optional.
986#[derive(Debug, Deftly)]
987#[derive_deftly(HasMemoryCost)]
988pub(crate) struct ReactorStreamComponents {
989    /// An MPSC receiver for inbound messages that arrive on the stream.
990    #[deftly(has_memory_cost(indirect_size = "0"))] // estimate
991    pub(crate) stream_inbound_rx: StreamQueueReceiver,
992
993    /// An MPSC sender for outbound messages to be sent on the stream.
994    #[deftly(has_memory_cost(indirect_size = "size_of::<AnyRelayMsg>()"))] // estimate
995    pub(crate) stream_outbound_tx: StreamMpscSender<AnyRelayMsg>,
996
997    /// A mechanism to allow the stream's writer to receive rate limit updates from the reactor.
998    // The `watch::Sender` owns the indirect data.
999    #[deftly(has_memory_cost(indirect_size = "0"))]
1000    pub(crate) rate_limit_rx: watch::Receiver<StreamRateLimit>,
1001
1002    /// A mechanism to allow the stream's reader to receive drain rate update requests from the
1003    /// reactor.
1004    #[deftly(has_memory_cost(indirect_size = "0"))]
1005    pub(crate) drain_rate_request_rx: NotifyReceiver<DrainRateRequest>,
1006}