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
4
use crate::client::circuit::handshake::RelayCryptLayerProtocol;
5

            
6
use crate::ccparams::CongestionControlParams;
7
use crate::circuit::CircParameters;
8
use crate::congestion::{CongestionControl, sendme};
9
use crate::memquota::{SpecificAccount, StreamAccount};
10
use crate::stream::CloseStreamBehavior;
11
use crate::stream::SEND_WINDOW_INIT;
12
use crate::stream::StreamMpscSender;
13
use crate::stream::cmdcheck::{AnyCmdChecker, StreamStatus};
14
use crate::stream::flow_ctrl::params::FlowCtrlParameters;
15
use crate::stream::flow_ctrl::state::{
16
    FlowCtrlHooks, StreamFlowCtrl, StreamRateLimit, WithSidechannelMitigations,
17
};
18
use crate::stream::flow_ctrl::xon_xoff::reader::DrainRateRequest;
19
use crate::stream::queue::{StreamQueueReceiver, stream_queue};
20
use crate::streammap::{
21
    self, EndSentStreamEnt, OpenStreamEnt, ShouldSendEnd, StreamEntMut, StreamMap,
22
};
23
use crate::util::notify::{NotifyReceiver, NotifySender};
24
use crate::{Error, HopNum, Result};
25

            
26
use derive_deftly::Deftly;
27
use postage::watch;
28
use safelog::sensitive as sv;
29
use tracing::{debug, trace};
30

            
31
use tor_cell::chancell::{BoxedCellBody, CircId};
32
use tor_cell::relaycell::extend::{CcRequest, CircRequestExt};
33
use tor_cell::relaycell::flow_ctrl::{Xoff, Xon, XonKBpsEwma};
34
use tor_cell::relaycell::msg::AnyRelayMsg;
35
use tor_cell::relaycell::{
36
    AnyRelayMsgOuter, RelayCellDecoder, RelayCellDecoderResult, RelayCellFormat, RelayCmd,
37
    StreamId, UnparsedRelayMsg,
38
};
39
use tor_error::{Bug, ErrorKind, HasKind, internal, into_internal};
40
use tor_memquota::derive_deftly_template_HasMemoryCost;
41
use tor_memquota::mq_queue::{ChannelSpec as _, MpscSpec};
42
use tor_protover::named;
43
use tor_rtcompat::DynTimeProvider;
44

            
45
use std::num::NonZeroU32;
46
use std::pin::Pin;
47
use std::result::Result as StdResult;
48
use std::sync::{Arc, Mutex};
49
use web_time_compat::Instant;
50

            
51
#[cfg(test)]
52
use tor_cell::relaycell::msg::SendmeTag;
53

            
54
#[cfg(feature = "relay")]
55
use {
56
    crate::ccparams::{Algorithm, AlgorithmDiscriminants},
57
    crate::circuit::HandshakeSubprotocols,
58
    crate::relay::{CircNetParameters, CongestionControlNetParams},
59
};
60

            
61
use 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
65
const 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)]
75
pub(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)]
103
pub(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

            
120
impl 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
1126
    pub(crate) fn from_params_and_caps(
132
1126
        hoptype: HopNegotiationType,
133
1126
        params: &CircParameters,
134
1126
        caps: &tor_protover::Protocols,
135
1126
    ) -> Result<Self> {
136
1126
        let mut ccontrol = params.ccontrol.clone();
137
1126
        match ccontrol.alg() {
138
734
            crate::ccparams::Algorithm::FixedWindow(_) => {}
139
            crate::ccparams::Algorithm::Vegas(_) => {
140
                // If the target doesn't support FLOWCTRL_CC, we can't use Vegas.
141
392
                if !caps.supports_named_subver(named::FLOWCTRL_CC) {
142
                    ccontrol.use_fallback_alg();
143
392
                }
144
            }
145
        };
146
1126
        if hoptype == HopNegotiationType::None {
147
92
            ccontrol.use_fallback_alg();
148
1034
        }
149
1126
        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
1126
        let relay_crypt_protocol = match hoptype {
154
92
            HopNegotiationType::None => RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0),
155
            HopNegotiationType::HsV3 => {
156
                // TODO-CGO: Support CGO when available.
157
                cfg_if! {
158
                    if #[cfg(all(feature = "hs-common", feature = "flowctl-cc", feature = "counter-galois-onion"))] {
159
                        if ccontrol.alg().compatible_with_cgo() && caps.supports_named_subver(named::RELAY_CRYPT_CGO) {
160
                            RelayCryptLayerProtocol::Cgo
161
                        } else {
162
                            RelayCryptLayerProtocol::HsV3(RelayCellFormat::V0)
163
                        }
164
                    } else if #[cfg(feature = "hs-common")] {
165
                            RelayCryptLayerProtocol::HsV3(RelayCellFormat::V0)
166
                    } else {
167
                        return Err(
168
                            tor_error::internal!("Unexpectedly tried to negotiate HsV3 without support!").into(),
169
                        );
170
                    }
171
                }
172
            }
173
            HopNegotiationType::Full => {
174
                cfg_if! {
175
                    if #[cfg(all(feature = "flowctl-cc", feature = "counter-galois-onion"))] {
176
                        #[allow(clippy::overly_complex_bool_expr)]
177
1034
                        if  ccontrol.alg().compatible_with_cgo()
178
392
                            && caps.supports_named_subver(named::RELAY_NEGOTIATE_SUBPROTO)
179
                            && caps.supports_named_subver(named::RELAY_CRYPT_CGO)
180
                        {
181
                            RelayCryptLayerProtocol::Cgo
182
                        } else {
183
1034
                            RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0)
184
                        }
185
                    } else {
186
                        RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0)
187
                    }
188
                }
189
            }
190
        };
191

            
192
1126
        Ok(Self {
193
1126
            ccontrol,
194
1126
            flow_ctrl_params: params.flow_ctrl.clone(),
195
1126
            relay_crypt_protocol,
196
1126
            n_incoming_cells_permitted: params.n_incoming_cells_permitted,
197
1126
            n_outgoing_cells_permitted: params.n_outgoing_cells_permitted,
198
1126
        })
199
1126
    }
200

            
201
    /// Build a [`HopSettings`] from the parameters requested during a circuit handshake.
202
    //
203
    // We disable `unused` warnings at the root of tor-proto,
204
    // but it's nice to have here so we re-enable it.
205
    #[warn(unused)]
206
    #[cfg(feature = "relay")]
207
14
    pub(crate) fn from_handshake_params(
208
14
        circ_net_params: CircNetParameters,
209
14
        cc_algorithm: AlgorithmDiscriminants,
210
14
        subprotos_requested: HandshakeSubprotocols,
211
14
    ) -> StdResult<Self, HandshakeParamsError> {
212
        // Unpack everything to make sure that we aren't missing anything
213
        // (otherwise clippy would warn).
214
        let CircNetParameters {
215
            cc:
216
                CongestionControlNetParams {
217
14
                    fixed_window,
218
14
                    vegas_exit,
219
14
                    cwnd,
220
14
                    rtt,
221
14
                    flow_ctrl,
222
                },
223
14
        } = circ_net_params;
224

            
225
14
        let HandshakeSubprotocols { relay_crypt_cgo } = subprotos_requested;
226

            
227
        // TODO: We have similar logic in and around `HopSettings` that deals with determining the
228
        // crypt protocol and cc algorithm to use. We might want to try to dedup some of this, or
229
        // make it more self-contained. This is a bit tricky though since the code is used in
230
        // different situations and the inputs are not the same.
231
14
        let (cc_algorithm, relay_crypt_protocol) = match (cc_algorithm, relay_crypt_cgo) {
232
14
            (AlgorithmDiscriminants::FixedWindow, false) => (
233
14
                Algorithm::FixedWindow(fixed_window),
234
14
                RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0),
235
14
            ),
236
            (AlgorithmDiscriminants::FixedWindow, true) => {
237
                return Err(HandshakeParamsError::IncompatibleParams(
238
                    "requested CGO but not congestion control",
239
                ));
240
            }
241
            (AlgorithmDiscriminants::Vegas, false) => (
242
                Algorithm::Vegas(vegas_exit),
243
                RelayCryptLayerProtocol::Tor1(RelayCellFormat::V0),
244
            ),
245
            (AlgorithmDiscriminants::Vegas, true) => {
246
                (Algorithm::Vegas(vegas_exit), RelayCryptLayerProtocol::Cgo)
247
            }
248
        };
249

            
250
        // TODO(arti#2442): The builder pattern here seems like a footgun.
251
14
        let ccontrol = CongestionControlParams::builder()
252
14
            .alg(cc_algorithm)
253
14
            .fixed_window_params(fixed_window)
254
14
            .cwnd_params(cwnd)
255
14
            .rtt_params(rtt)
256
14
            .build()
257
14
            .map_err(into_internal!("Could not build `CongestionControlParams`"))?;
258

            
259
14
        Ok(Self {
260
14
            ccontrol,
261
14
            flow_ctrl_params: flow_ctrl,
262
14
            relay_crypt_protocol,
263
14
            n_incoming_cells_permitted: None,
264
14
            n_outgoing_cells_permitted: None,
265
14
        })
266
14
    }
267

            
268
    /// Return the negotiated relay crypto protocol.
269
1296
    pub(crate) fn relay_crypt_protocol(&self) -> RelayCryptLayerProtocol {
270
1296
        self.relay_crypt_protocol
271
1296
    }
272

            
273
    /// Return the client circuit-creation extensions that we should use in order to negotiate
274
    /// these circuit hop parameters.
275
    #[allow(clippy::unnecessary_wraps)]
276
42
    pub(crate) fn circuit_request_extensions(&self) -> Result<Vec<CircRequestExt>> {
277
        // allow 'unused_mut' because of the combinations of `cfg` conditions below
278
        #[allow(unused_mut)]
279
42
        let mut client_extensions = Vec::new();
280

            
281
        #[allow(unused, unused_mut)]
282
42
        let mut cc_extension_set = false;
283

            
284
42
        if self.ccontrol.is_enabled() {
285
12
            cfg_if::cfg_if! {
286
12
                if #[cfg(feature = "flowctl-cc")] {
287
12
                    client_extensions.push(CircRequestExt::CcRequest(CcRequest::default()));
288
12
                    cc_extension_set = true;
289
12
                } else {
290
12
                    return Err(
291
12
                        tor_error::internal!(
292
12
                            "Congestion control is enabled on this circuit, but 'flowctl-cc' feature is not enabled"
293
12
                        )
294
12
                        .into()
295
12
                    );
296
12
                }
297
12
            }
298
30
        }
299

            
300
        // See whether we need to send a list of required protocol capabilities.
301
        // These aren't "negotiated" per se; they're simply demanded.
302
        // The relay will refuse the circuit if it doesn't support all of them,
303
        // and if any of them isn't supported in the SubprotocolRequest extension.
304
        //
305
        // (In other words, don't add capabilities here just because you want the
306
        // relay to have them! They must be explicitly listed as supported for use
307
        // with this extension. For the current list, see
308
        // https://spec.torproject.org/tor-spec/create-created-cells.html#subproto-request)
309
        //
310
        // TODO: Should this use `HandshakeSubprotocols` so that the above comment has some
311
        // compile-time checks?
312
        #[allow(unused_mut)]
313
42
        let mut required_protocol_capabilities: Vec<tor_protover::NamedSubver> = Vec::new();
314

            
315
        #[cfg(feature = "counter-galois-onion")]
316
42
        if matches!(self.relay_crypt_protocol(), RelayCryptLayerProtocol::Cgo) {
317
            if !cc_extension_set {
318
                return Err(tor_error::internal!("Tried to negotiate CGO without CC.").into());
319
            }
320
            required_protocol_capabilities.push(tor_protover::named::RELAY_CRYPT_CGO);
321
42
        }
322

            
323
42
        if !required_protocol_capabilities.is_empty() {
324
            client_extensions.push(CircRequestExt::SubprotocolRequest(
325
                required_protocol_capabilities.into_iter().collect(),
326
            ));
327
42
        }
328

            
329
42
        Ok(client_extensions)
330
42
    }
331
}
332

            
333
#[cfg(test)]
334
impl std::default::Default for CircParameters {
335
336
    fn default() -> Self {
336
336
        Self {
337
336
            extend_by_ed25519_id: true,
338
336
            ccontrol: crate::congestion::test_utils::params::build_cc_fixed_params(),
339
336
            flow_ctrl: FlowCtrlParameters::defaults_for_tests(),
340
336
            n_incoming_cells_permitted: None,
341
336
            n_outgoing_cells_permitted: None,
342
336
        }
343
336
    }
344
}
345

            
346
/// An error that can occur when building a [`HopSettings`] using parameters requested during a
347
/// circuit handshake.
348
#[derive(Clone, Debug, thiserror::Error)]
349
pub(crate) enum HandshakeParamsError {
350
    /// The provided parameters are incompatible with each other.
351
    #[error("The provided handshake parameters are incompatible with each other: {0}")]
352
    IncompatibleParams(&'static str),
353
    /// An internal error.
354
    #[error("Internal error")]
355
    Internal(#[from] tor_error::Bug),
356
}
357

            
358
impl HasKind for HandshakeParamsError {
359
    fn kind(&self) -> ErrorKind {
360
        match self {
361
            Self::IncompatibleParams(_) => ErrorKind::TorProtocolViolation,
362
            Self::Internal(_) => ErrorKind::Internal,
363
        }
364
    }
365
}
366

            
367
impl CircParameters {
368
    /// Constructor
369
775
    pub fn new(
370
775
        extend_by_ed25519_id: bool,
371
775
        ccontrol: CongestionControlParams,
372
775
        flow_ctrl: FlowCtrlParameters,
373
775
    ) -> Self {
374
775
        Self {
375
775
            extend_by_ed25519_id,
376
775
            ccontrol,
377
775
            flow_ctrl,
378
775
            n_incoming_cells_permitted: None,
379
775
            n_outgoing_cells_permitted: None,
380
775
        }
381
775
    }
382
}
383

            
384
/// Instructions for sending a RELAY cell.
385
///
386
/// This instructs a circuit reactor to send a RELAY cell to a given target
387
/// (a hop, if we are a client, or the client, if we are a relay).
388
#[derive(educe::Educe)]
389
#[educe(Debug)]
390
pub(crate) struct SendRelayCell {
391
    /// The hop number, or `None` if we are a relay.
392
    pub(crate) hop: Option<HopNum>,
393
    /// Whether to use a RELAY_EARLY cell.
394
    pub(crate) early: bool,
395
    /// The cell to send.
396
    pub(crate) cell: AnyRelayMsgOuter,
397
}
398

            
399
/// The inbound state of a hop.
400
pub(crate) struct CircHopInbound {
401
    /// Decodes relay cells received from this hop.
402
    decoder: RelayCellDecoder,
403
    /// Remaining permitted incoming relay cells from this hop, plus 1.
404
    ///
405
    /// (In other words, `None` represents no limit,
406
    /// `Some(1)` represents an exhausted limit,
407
    /// and `Some(n)` means that n-1 more cells may be received.)
408
    ///
409
    /// If this ever decrements from Some(1), then the circuit must be torn down with an error.
410
    n_incoming_cells_permitted: Option<NonZeroU32>,
411
}
412

            
413
/// The outbound state of a hop.
414
pub(crate) struct CircHopOutbound {
415
    /// Congestion control object.
416
    ///
417
    /// This object is also in charge of handling circuit level SENDME logic for this hop.
418
    ccontrol: Arc<Mutex<CongestionControl>>,
419
    /// Map from stream IDs to streams.
420
    ///
421
    /// We store this with the reactor instead of the circuit, since the
422
    /// reactor needs it for every incoming cell on a stream, whereas
423
    /// the circuit only needs it when allocating new streams.
424
    ///
425
    /// NOTE: this is behind a mutex because the client reactor polls the `StreamMap`s
426
    /// of all hops concurrently, in a `FuturesUnordered`. Without the mutex,
427
    /// this wouldn't be possible, because it would mean holding multiple
428
    /// mutable references to `self` (the reactor). Note, however,
429
    /// that there should never be any contention on this mutex:
430
    /// we never create more than one
431
    /// `CircHopList::ready_streams_iterator()` stream
432
    /// at a time, and we never clone/lock the hop's `StreamMap` outside of it.
433
    ///
434
    /// Additionally, the stream map of the last hop (join point) of a conflux tunnel
435
    /// is shared with all the circuits in the tunnel.
436
    map: Arc<Mutex<StreamMap>>,
437
    /// Format to use for relay cells.
438
    //
439
    // When we have packed/fragmented cells, this may be replaced by a RelayCellEncoder.
440
    relay_format: RelayCellFormat,
441
    /// Flow control parameters for new streams.
442
    flow_ctrl_params: Arc<FlowCtrlParameters>,
443
    /// Remaining permitted outgoing relay cells from this hop, plus 1.
444
    ///
445
    /// If this ever decrements from Some(1), then the circuit must be torn down with an error.
446
    n_outgoing_cells_permitted: Option<NonZeroU32>,
447
}
448

            
449
impl CircHopInbound {
450
    /// Create a new [`CircHopInbound`].
451
1092
    pub(crate) fn new(decoder: RelayCellDecoder, settings: &HopSettings) -> Self {
452
1092
        Self {
453
1092
            decoder,
454
1092
            n_incoming_cells_permitted: settings.n_incoming_cells_permitted.map(cvt),
455
1092
        }
456
1092
    }
457

            
458
    /// Parse a RELAY or RELAY_EARLY cell body.
459
    ///
460
    /// Requires that the cryptographic checks on the message have already been
461
    /// performed
462
552
    pub(crate) fn decode(&mut self, cell: BoxedCellBody) -> Result<RelayCellDecoderResult> {
463
552
        self.decoder
464
552
            .decode(cell)
465
552
            .map_err(|e| Error::from_bytes_err(e, "relay cell"))
466
552
    }
467

            
468
    /// Decrement the limit of inbound cells that may be received from this hop; give
469
    /// an error if it would reach zero.
470
552
    pub(crate) fn decrement_cell_limit(&mut self) -> Result<()> {
471
552
        try_decrement_cell_limit(&mut self.n_incoming_cells_permitted)
472
552
            .map_err(|_| Error::ExcessInboundCells)
473
552
    }
474
}
475

            
476
impl CircHopOutbound {
477
    /// Create a new [`CircHopOutbound`].
478
1046
    pub(crate) fn new(
479
1046
        ccontrol: Arc<Mutex<CongestionControl>>,
480
1046
        relay_format: RelayCellFormat,
481
1046
        flow_ctrl_params: Arc<FlowCtrlParameters>,
482
1046
        settings: &HopSettings,
483
1046
    ) -> Self {
484
1046
        Self {
485
1046
            ccontrol,
486
1046
            map: Arc::new(Mutex::new(StreamMap::new())),
487
1046
            relay_format,
488
1046
            flow_ctrl_params,
489
1046
            n_outgoing_cells_permitted: settings.n_outgoing_cells_permitted.map(cvt),
490
1046
        }
491
1046
    }
492

            
493
    /// Start a stream. Creates an entry in the stream map with the given channels, and sends the
494
    /// `message` to the provided hop.
495
96
    pub(crate) fn begin_stream(
496
96
        &mut self,
497
96
        hop: Option<HopNum>,
498
96
        message: AnyRelayMsg,
499
96
        time_prov: &DynTimeProvider,
500
96
        cmd_checker: AnyCmdChecker,
501
96
        memquota: &StreamAccount,
502
96
    ) -> Result<(SendRelayCell, StreamId, ReactorStreamComponents)> {
503
        // TODO: This has a lot of duplicated code with `Self::add_ent_with_id()`.
504

            
505
        // A channel for the reactor to inform the writer of a new rate limit.
506
96
        let (rate_limit_tx, rate_limit_rx) = watch::channel_with(StreamRateLimit::MAX);
507

            
508
        // A channel for the reactor to request a new drain rate from the reader.
509
        // Typically this notification will be sent after an XOFF is sent so that the reader can
510
        // send us a new drain rate when the stream data queue becomes empty.
511
96
        let mut drain_rate_request_tx = NotifySender::new_typed();
512
96
        let drain_rate_request_rx = drain_rate_request_tx.subscribe();
513

            
514
96
        let flow_ctrl = self.build_flow_ctrl(
515
            // We are starting the stream,
516
            // so we're a client and want flow control sidechannel mitigations.
517
96
            WithSidechannelMitigations::Enabled,
518
96
            rate_limit_tx,
519
96
            drain_rate_request_tx,
520
        )?;
521

            
522
96
        let stream_queue_max_len = flow_ctrl.inbound_queue_max_len();
523

            
524
        // A queue for inbound RELAY messages.
525
96
        let (sender, receiver) = stream_queue(stream_queue_max_len, memquota, time_prov)?;
526

            
527
        // A queue for outbound RELAY messages.
528
96
        let (msg_tx, msg_rx) = MpscSpec::new(CIRCUIT_BUFFER_SIZE)
529
96
            .new_mq(time_prov.clone(), memquota.as_raw_account())?;
530

            
531
96
        let r = self.map.lock().expect("lock poisoned").add_ent(
532
96
            sender,
533
96
            msg_rx,
534
96
            flow_ctrl,
535
96
            cmd_checker,
536
96
        )?;
537
96
        let cell = AnyRelayMsgOuter::new(Some(r), message);
538

            
539
96
        let stream_components = ReactorStreamComponents {
540
96
            stream_inbound_rx: receiver,
541
96
            stream_outbound_tx: msg_tx,
542
96
            rate_limit_rx,
543
96
            drain_rate_request_rx,
544
96
        };
545

            
546
96
        Ok((
547
96
            SendRelayCell {
548
96
                hop,
549
96
                early: false,
550
96
                cell,
551
96
            },
552
96
            r,
553
96
            stream_components,
554
96
        ))
555
96
    }
556

            
557
    /// Close the stream associated with `id` because the stream was dropped.
558
    ///
559
    /// If we have not already received an END cell on this stream, send one.
560
    /// If no END cell is specified, an END cell with the reason byte set to
561
    /// REASON_MISC will be sent.
562
    ///
563
    // Note(relay): `circ_uniq_id` is an opaque displayable type
564
    // because relays use a different circuit ID type
565
    // than clients. Eventually, we should probably make
566
    // them both use the same ID type, or have a nicer approach here
567
    #[allow(clippy::too_many_arguments)]
568
74
    pub(crate) fn close_stream(
569
74
        &mut self,
570
74
        circ_uniq_id: impl std::fmt::Display,
571
74
        circ_id: CircId,
572
74
        id: StreamId,
573
74
        hop: Option<HopNum>,
574
74
        message: CloseStreamBehavior,
575
74
        why: streammap::TerminateReason,
576
74
        expiry: Instant,
577
74
    ) -> Result<Option<SendRelayCell>> {
578
74
        let should_send_end = self
579
74
            .map
580
74
            .lock()
581
74
            .expect("lock poisoned")
582
74
            .terminate(id, why, expiry)?;
583
74
        trace!(
584
            circ_uniq_id = %circ_uniq_id,
585
            circ_id = %circ_id,
586
            stream_id = %id,
587
            should_send_end = ?should_send_end,
588
            "Ending stream",
589
        );
590
        // TODO: I am about 80% sure that we only send an END cell if
591
        // we didn't already get an END cell.  But I should double-check!
592
74
        if let (ShouldSendEnd::Send, CloseStreamBehavior::SendEnd(end_message)) =
593
74
            (should_send_end, message)
594
        {
595
74
            let end_cell = AnyRelayMsgOuter::new(Some(id), end_message.into());
596
74
            let cell = SendRelayCell {
597
74
                hop,
598
74
                early: false,
599
74
                cell: end_cell,
600
74
            };
601

            
602
74
            return Ok(Some(cell));
603
        }
604
        Ok(None)
605
74
    }
606

            
607
    /// Check if we should send an XON message.
608
    ///
609
    /// If we should, then returns the XON message that should be sent.
610
    pub(crate) fn maybe_send_xon(
611
        &mut self,
612
        rate: XonKBpsEwma,
613
        id: StreamId,
614
    ) -> Result<Option<Xon>> {
615
        // the call below will return an error if XON/XOFF aren't supported,
616
        // so we check for support here
617
        if !self
618
            .ccontrol()
619
            .lock()
620
            .expect("poisoned lock")
621
            .uses_xon_xoff()
622
        {
623
            return Ok(None);
624
        }
625

            
626
        let mut map = self.map.lock().expect("lock poisoned");
627
        let Some(StreamEntMut::Open(ent)) = map.get_mut(id) else {
628
            // stream went away
629
            return Ok(None);
630
        };
631

            
632
        ent.maybe_send_xon(rate)
633
    }
634

            
635
    /// Check if we should send an XOFF message.
636
    ///
637
    /// If we should, then returns the XOFF message that should be sent.
638
220
    pub(crate) fn maybe_send_xoff(&mut self, id: StreamId) -> Result<Option<Xoff>> {
639
        // the call below will return an error if XON/XOFF aren't supported,
640
        // so we check for support here
641
220
        if !self
642
220
            .ccontrol()
643
220
            .lock()
644
220
            .expect("poisoned lock")
645
220
            .uses_xon_xoff()
646
        {
647
140
            return Ok(None);
648
80
        }
649

            
650
80
        let mut map = self.map.lock().expect("lock poisoned");
651
80
        let Some(StreamEntMut::Open(ent)) = map.get_mut(id) else {
652
            // stream went away
653
12
            return Ok(None);
654
        };
655

            
656
68
        ent.maybe_send_xoff()
657
220
    }
658

            
659
    /// Return the format that is used for relay cells sent to this hop.
660
    ///
661
    /// For the most part, this format isn't necessary to interact with a CircHop;
662
    /// it becomes relevant when we are deciding _what_ we can encode for the hop.
663
4748
    pub(crate) fn relay_cell_format(&self) -> RelayCellFormat {
664
4748
        self.relay_format
665
4748
    }
666

            
667
    /// Delegate to CongestionControl, for testing purposes
668
    #[cfg(test)]
669
20
    pub(crate) fn send_window_and_expected_tags(&self) -> (u32, Vec<SendmeTag>) {
670
20
        self.ccontrol()
671
20
            .lock()
672
20
            .expect("poisoned lock")
673
20
            .send_window_and_expected_tags()
674
20
    }
675

            
676
    /// Return the number of open streams on this hop.
677
    ///
678
    /// WARNING: because this locks the stream map mutex,
679
    /// it should never be called from a context where that mutex is already locked.
680
104
    pub(crate) fn n_open_streams(&self) -> usize {
681
104
        self.map.lock().expect("lock poisoned").n_open_streams()
682
104
    }
683

            
684
    /// Return a reference to our CongestionControl object.
685
27932
    pub(crate) fn ccontrol(&self) -> &Arc<Mutex<CongestionControl>> {
686
27932
        &self.ccontrol
687
27932
    }
688

            
689
    /// We're about to send `msg`.
690
    ///
691
    /// See [`OpenStreamEnt::about_to_send`](crate::streammap::OpenStreamEnt::about_to_send).
692
    //
693
    // TODO prop340: This should take a cell or similar, not a message.
694
    //
695
    // Note(relay): `circ_uniq_id` is an opaque displayable type
696
    // because relays use a different circuit ID type
697
    // than clients. Eventually, we should probably make
698
    // them both use the same ID type, or have a nicer approach here
699
4158
    pub(crate) fn about_to_send(
700
4158
        &mut self,
701
4158
        circ_uniq_id: impl std::fmt::Display,
702
4158
        circ_id: CircId,
703
4158
        stream_id: StreamId,
704
4158
        msg: &AnyRelayMsg,
705
4158
    ) -> Result<()> {
706
4158
        let mut hop_map = self.map.lock().expect("lock poisoned");
707
4158
        let Some(StreamEntMut::Open(ent)) = hop_map.get_mut(stream_id) else {
708
            // This can happen when we have outgoing data queued when we received an END.
709
            // We shouldn't return an error here since it would close the circuit along with all
710
            // other streams, and instead we just let the caller send this message anyways.
711
            // Also the caller only calls `about_to_send()` for DATA cells,
712
            // which means that other non-DATA cells don't hit this code path and are always sent,
713
            // and so we should handle all cell types consistently.
714
            // TODO: We should drop the message and not send it,
715
            // but the caller of `about_to_send()` isn't designed to handle fallible sends
716
            // so it would need some refactoring to handle this.
717
            debug!(
718
                circ_uniq_id = %circ_uniq_id,
719
                circ_id = %circ_id,
720
                stream_id = %stream_id,
721
                "sending a relay cell for non-existent or non-open stream!",
722
            );
723
            return Ok(());
724
        };
725

            
726
4158
        ent.about_to_send(msg)
727
4158
    }
728

            
729
    /// Add an entry to this map using the specified StreamId.
730
    #[cfg(any(feature = "hs-service", feature = "relay"))]
731
48
    pub(crate) fn add_ent_with_id(
732
48
        &self,
733
48
        time_prov: &DynTimeProvider,
734
48
        stream_id: StreamId,
735
48
        cmd_checker: AnyCmdChecker,
736
48
        with_sidechannel_mitigations: WithSidechannelMitigations,
737
48
        memquota: &StreamAccount,
738
48
    ) -> Result<ReactorStreamComponents> {
739
        // TODO: This has a lot of duplicated code with `Self::begin_stream()`.
740

            
741
        // A channel for the reactor to inform the writer of a new rate limit.
742
48
        let (rate_limit_tx, rate_limit_rx) = watch::channel_with(StreamRateLimit::MAX);
743

            
744
        // A channel for the reactor to request a new drain rate from the reader.
745
        // Typically this notification will be sent after an XOFF is sent so that the reader can
746
        // send us a new drain rate when the stream data queue becomes empty.
747
48
        let mut drain_rate_request_tx = NotifySender::new_typed();
748
48
        let drain_rate_request_rx = drain_rate_request_tx.subscribe();
749

            
750
48
        let flow_ctrl = self.build_flow_ctrl(
751
48
            with_sidechannel_mitigations,
752
48
            rate_limit_tx,
753
48
            drain_rate_request_tx,
754
        )?;
755

            
756
48
        let stream_queue_max_len = flow_ctrl.inbound_queue_max_len();
757

            
758
        // A queue for inbound RELAY messages.
759
48
        let (sender, receiver) = stream_queue(stream_queue_max_len, memquota, time_prov)?;
760

            
761
        // A queue for outbound RELAY messages.
762
48
        let (msg_tx, msg_rx) = MpscSpec::new(CIRCUIT_BUFFER_SIZE)
763
48
            .new_mq(time_prov.clone(), memquota.as_raw_account())?;
764

            
765
48
        let mut hop_map = self.map.lock().expect("lock poisoned");
766
48
        hop_map.add_ent_with_id(sender, msg_rx, flow_ctrl, stream_id, cmd_checker)?;
767

            
768
48
        Ok(ReactorStreamComponents {
769
48
            stream_inbound_rx: receiver,
770
48
            stream_outbound_tx: msg_tx,
771
48
            rate_limit_rx,
772
48
            drain_rate_request_rx,
773
48
        })
774
48
    }
775

            
776
    /// Builds the reactor's flow control handler for a new stream.
777
    // TODO: remove the `Result` once we remove the "flowctl-cc" feature
778
    #[cfg_attr(feature = "flowctl-cc", expect(clippy::unnecessary_wraps))]
779
144
    fn build_flow_ctrl(
780
144
        &self,
781
144
        with_sidechannel_mitigations: WithSidechannelMitigations,
782
144
        rate_limit_updater: watch::Sender<StreamRateLimit>,
783
144
        drain_rate_requester: NotifySender<DrainRateRequest>,
784
144
    ) -> Result<StreamFlowCtrl> {
785
144
        let params = Arc::clone(&self.flow_ctrl_params);
786

            
787
144
        if self
788
144
            .ccontrol()
789
144
            .lock()
790
144
            .expect("poisoned lock")
791
144
            .uses_stream_sendme()
792
        {
793
120
            let window = sendme::StreamSendWindow::new(SEND_WINDOW_INIT);
794
120
            Ok(StreamFlowCtrl::new_window(window))
795
        } else {
796
            cfg_if::cfg_if! {
797
                if #[cfg(feature = "flowctl-cc")] {
798
24
                    Ok(StreamFlowCtrl::new_xon_xoff(
799
24
                        params,
800
24
                        with_sidechannel_mitigations,
801
24
                        rate_limit_updater,
802
24
                        drain_rate_requester,
803
24
                    ))
804
                } else {
805
                    drop(params);
806
                    drop(rate_limit_updater);
807
                    drop(drain_rate_requester);
808
                    Err(internal!(
809
                        "`CongestionControl` doesn't use sendmes, but 'flowctl-cc' feature not enabled",
810
                    ).into())
811
                }
812
            }
813
        }
814
144
    }
815

            
816
    /// Deliver `msg` to the specified open stream entry `ent`.
817
216
    fn deliver_msg_to_stream(
818
216
        streamid: StreamId,
819
216
        ent: &mut OpenStreamEnt,
820
216
        cell_counts_toward_windows: bool,
821
216
        msg: UnparsedRelayMsg,
822
216
    ) -> Result<bool> {
823
        use tor_async_utils::SinkTrySend as _;
824
        use tor_async_utils::SinkTrySendError as _;
825

            
826
        // The stream for this message exists, and is open.
827

            
828
        // We need to handle SENDME/XON/XOFF messages here, not in the stream's recv() method, or
829
        // else we'd never notice them if the stream isn't reading.
830
216
        match msg.cmd() {
831
            RelayCmd::SENDME => {
832
4
                ent.put_for_incoming_sendme(msg)?;
833
4
                return Ok(false);
834
            }
835
            RelayCmd::XON => {
836
                ent.handle_incoming_xon(msg)?;
837
                return Ok(false);
838
            }
839
            RelayCmd::XOFF => {
840
                ent.handle_incoming_xoff(msg)?;
841
                return Ok(false);
842
            }
843
212
            _ => {}
844
        }
845

            
846
212
        let message_closes_stream = ent.cmd_checker.check_msg(&msg)? == StreamStatus::Closed;
847

            
848
212
        if let Err(e) = Pin::new(&mut ent.sink).try_send(msg) {
849
            if e.is_full() {
850
                cfg_if::cfg_if! {
851
                    if #[cfg(not(feature = "flowctl-cc"))] {
852
                        // If we get here, we either have a logic bug (!), or an attacker
853
                        // is sending us more cells than we asked for via congestion control.
854
                        return Err(Error::CircProto(format!(
855
                            "Stream sink would block; received too many cells on stream ID {}",
856
                            sv(streamid),
857
                        )));
858
                    } else {
859
                        return Err(internal!(
860
                            "Stream (ID {}) uses an unbounded queue, but apparently it's full?",
861
                            sv(streamid),
862
                        )
863
                        .into());
864
                    }
865
                }
866
            }
867
            if e.is_disconnected() && cell_counts_toward_windows {
868
                // the other side of the stream has gone away; remember
869
                // that we received a cell that we couldn't queue for it.
870
                //
871
                // Later this value will be recorded in a half-stream.
872
                ent.dropped += 1;
873
            }
874
212
        }
875

            
876
212
        Ok(message_closes_stream)
877
216
    }
878

            
879
    /// Note that we received an END message (or other message indicating the end of
880
    /// the stream) on the stream with `id`.
881
    ///
882
    /// See [`StreamMap::ending_msg_received`](crate::streammap::StreamMap::ending_msg_received).
883
    #[cfg(feature = "hs-service")]
884
    pub(crate) fn ending_msg_received(&self, stream_id: StreamId) -> Result<()> {
885
        let mut hop_map = self.map.lock().expect("lock poisoned");
886

            
887
        hop_map.ending_msg_received(stream_id)?;
888

            
889
        Ok(())
890
    }
891

            
892
    /// Handle `msg`, delivering it to the stream with the specified `streamid` if appropriate.
893
    ///
894
    /// Returns back the provided `msg`, if the message is an incoming stream request
895
    /// that needs to be handled by the calling code.
896
    ///
897
    // TODO: the above is a bit of a code smell -- we should try to avoid passing the msg
898
    // back and forth like this.
899
292
    pub(crate) fn handle_msg<F>(
900
292
        &self,
901
292
        possible_proto_violation_err: F,
902
292
        cell_counts_toward_windows: bool,
903
292
        streamid: StreamId,
904
292
        msg: UnparsedRelayMsg,
905
292
        now: Instant,
906
292
    ) -> Result<Option<UnparsedRelayMsg>>
907
292
    where
908
292
        F: FnOnce(StreamId) -> Error,
909
    {
910
292
        let mut hop_map = self.map.lock().expect("lock poisoned");
911

            
912
292
        match hop_map.get_mut(streamid) {
913
216
            Some(StreamEntMut::Open(ent)) => {
914
                // Can't have a stream level SENDME when congestion control is enabled.
915
216
                let message_closes_stream =
916
216
                    Self::deliver_msg_to_stream(streamid, ent, cell_counts_toward_windows, msg)?;
917

            
918
216
                if message_closes_stream {
919
24
                    hop_map.ending_msg_received(streamid)?;
920
192
                }
921
            }
922
20
            Some(StreamEntMut::EndSent(EndSentStreamEnt { expiry, .. })) if now >= *expiry => {
923
4
                return Err(possible_proto_violation_err(streamid));
924
            }
925
            Some(StreamEntMut::EndSent(_))
926
4
                if matches!(
927
16
                    msg.cmd(),
928
                    RelayCmd::BEGIN | RelayCmd::BEGIN_DIR | RelayCmd::RESOLVE
929
                ) =>
930
            {
931
                // If the other side is sending us a BEGIN but hasn't yet acknowledged our END
932
                // message, just remove the old stream from the map and stop waiting for a
933
                // response
934
12
                hop_map.ending_msg_received(streamid)?;
935
12
                return Ok(Some(msg));
936
            }
937
4
            Some(StreamEntMut::EndSent(EndSentStreamEnt { half_stream, .. })) => {
938
                // We sent an end but maybe the other side hasn't heard.
939

            
940
4
                match half_stream.handle_msg(msg)? {
941
4
                    StreamStatus::Open => {}
942
                    StreamStatus::Closed => {
943
                        hop_map.ending_msg_received(streamid)?;
944
                    }
945
                }
946
            }
947
4
            None if matches!(
948
56
                msg.cmd(),
949
                RelayCmd::BEGIN | RelayCmd::BEGIN_DIR | RelayCmd::RESOLVE
950
            ) =>
951
            {
952
52
                return Ok(Some(msg));
953
            }
954
            _ => {
955
                // No stream wants this message, or ever did.
956
4
                return Err(possible_proto_violation_err(streamid));
957
            }
958
        }
959

            
960
220
        Ok(None)
961
292
    }
962

            
963
    /// Get the stream map of this hop.
964
40058
    pub(crate) fn stream_map(&self) -> &Arc<Mutex<StreamMap>> {
965
40058
        &self.map
966
40058
    }
967

            
968
    /// Set the stream map of this hop to `map`.
969
    ///
970
    /// Returns an error if the existing stream map of the hop has any open stream.
971
104
    pub(crate) fn set_stream_map(&mut self, map: Arc<Mutex<StreamMap>>) -> StdResult<(), Bug> {
972
104
        if self.n_open_streams() != 0 {
973
            return Err(internal!("Tried to discard existing open streams?!"));
974
104
        }
975

            
976
104
        self.map = map;
977

            
978
104
        Ok(())
979
104
    }
980

            
981
    /// Decrement the limit of outbound cells that may be sent to this hop; give
982
    /// an error if it would reach zero.
983
4592
    pub(crate) fn decrement_cell_limit(&mut self) -> Result<()> {
984
4592
        try_decrement_cell_limit(&mut self.n_outgoing_cells_permitted)
985
4592
            .map_err(|_| Error::ExcessOutboundCells)
986
4592
    }
987
}
988

            
989
/// If `val` is `Some(1)`, return Err(());
990
/// otherwise decrement it (if it is Some) and return Ok(()).
991
#[inline]
992
5144
fn try_decrement_cell_limit(val: &mut Option<NonZeroU32>) -> StdResult<(), ()> {
993
    // This is a bit verbose, but I've confirmed that it optimizes nicely.
994
5144
    match val {
995
        Some(x) => {
996
            let z = u32::from(*x);
997
            if z == 1 {
998
                Err(())
999
            } else {
                *x = (z - 1).try_into().expect("NonZeroU32 was zero?!");
                Ok(())
            }
        }
5144
        None => Ok(()),
    }
5144
}
/// Convert a limit from the form used in a HopSettings to that used here.
/// (The format we use here is more compact.)
fn cvt(limit: u32) -> NonZeroU32 {
    // See "known limitations" comment on n_incoming_cells_permitted.
    limit
        .saturating_add(1)
        .try_into()
        .expect("Adding one left it as zero?")
}
/// A collection of components that can be used to interact with the reactor's view of a Tor stream.
//
// TODO: We also have a `StreamComponents` type that is used and built outside of the reactor.
// It's maybe confusing to have these similar type names, so a better name would be nice.
//
// TODO(arti#2068): The components we return should maybe depend on what type of flow control is
// used, so in the future we might want to make some of these fields optional.
#[derive(Debug, Deftly)]
#[derive_deftly(HasMemoryCost)]
pub(crate) struct ReactorStreamComponents {
    /// An MPSC receiver for inbound messages that arrive on the stream.
    #[deftly(has_memory_cost(indirect_size = "0"))] // estimate
    pub(crate) stream_inbound_rx: StreamQueueReceiver,
    /// An MPSC sender for outbound messages to be sent on the stream.
    #[deftly(has_memory_cost(indirect_size = "size_of::<AnyRelayMsg>()"))] // estimate
    pub(crate) stream_outbound_tx: StreamMpscSender<AnyRelayMsg>,
    /// A mechanism to allow the stream's writer to receive rate limit updates from the reactor.
    // The `watch::Sender` owns the indirect data.
    #[deftly(has_memory_cost(indirect_size = "0"))]
    pub(crate) rate_limit_rx: watch::Receiver<StreamRateLimit>,
    /// A mechanism to allow the stream's reader to receive drain rate update requests from the
    /// reactor.
    #[deftly(has_memory_cost(indirect_size = "0"))]
    pub(crate) drain_rate_request_rx: NotifyReceiver<DrainRateRequest>,
}