tor_cell/
relaycell.rs

1//! Implementation for parsing and encoding relay cells
2
3use std::num::NonZeroU16;
4
5use crate::chancell::{BoxedCellBody, CELL_DATA_LEN};
6use derive_deftly::Deftly;
7use smallvec::{SmallVec, smallvec};
8use tor_bytes::{EncodeError, EncodeResult, Error, Result};
9use tor_bytes::{Reader, Writer};
10use tor_error::internal;
11use tor_memquota::derive_deftly_template_HasMemoryCost;
12
13use caret::caret_int;
14use rand::{CryptoRng, Rng};
15
16#[cfg(feature = "conflux")]
17pub mod conflux;
18pub mod extend;
19mod extlist;
20pub mod flow_ctrl;
21#[cfg(feature = "hs")]
22pub mod hs;
23pub mod msg;
24#[cfg(feature = "experimental-udp")]
25pub mod udp;
26
27caret_int! {
28    /// A command that identifies the type of a relay cell
29    #[derive(Deftly)]
30    #[derive_deftly(HasMemoryCost)]
31    pub struct RelayCmd(u8) {
32        /// Start a new stream
33        BEGIN = 1,
34        /// Data on a stream
35        DATA = 2,
36        /// Close a stream
37        END = 3,
38        /// Acknowledge a BEGIN; stream is open
39        CONNECTED = 4,
40        /// Used for flow control
41        SENDME = 5,
42        /// Extend a circuit to a new hop; deprecated
43        EXTEND = 6,
44        /// Reply to EXTEND handshake; deprecated
45        EXTENDED = 7,
46        /// Partially close a circuit
47        TRUNCATE = 8,
48        /// Circuit has been partially closed
49        TRUNCATED = 9,
50        /// Padding cell
51        DROP = 10,
52        /// Start a DNS lookup
53        RESOLVE = 11,
54        /// Reply to a DNS lookup
55        RESOLVED = 12,
56        /// Start a directory stream
57        BEGIN_DIR = 13,
58        /// Extend a circuit to a new hop
59        EXTEND2 = 14,
60        /// Reply to an EXTEND2 cell.
61        EXTENDED2 = 15,
62
63        /// NOTE: UDP command are reserved but only used with experimental-udp feature
64
65        /// UDP: Start of a stream
66        CONNECT_UDP = 16,
67        /// UDP: Acknowledge a CONNECT_UDP. Stream is open.
68        CONNECTED_UDP = 17,
69        /// UDP: Data on a UDP stream.
70        DATAGRAM = 18,
71
72        /// CONFLUX: Link circuits together at the receiving endpoint.
73        CONFLUX_LINK = 19,
74        /// CONFLUX: Confirm that the circuits were linked.
75        CONFLUX_LINKED = 20,
76        /// CONFLUX: Acknowledge the linkage of the circuits, for RTT measurement.
77        CONFLUX_LINKED_ACK = 21,
78        /// CONFLUX: Switch to another leg in an already linked circuit construction.
79        CONFLUX_SWITCH = 22,
80
81        /// HS: establish an introduction point.
82        ESTABLISH_INTRO = 32,
83        /// HS: establish a rendezvous point.
84        ESTABLISH_RENDEZVOUS = 33,
85        /// HS: send introduction (client to introduction point)
86        INTRODUCE1 = 34,
87        /// HS: send introduction (introduction point to service)
88        INTRODUCE2 = 35,
89        /// HS: connect rendezvous point (service to rendezvous point)
90        RENDEZVOUS1 = 36,
91        /// HS: connect rendezvous point (rendezvous point to client)
92        RENDEZVOUS2 = 37,
93        /// HS: Response to ESTABLISH_INTRO
94        INTRO_ESTABLISHED = 38,
95        /// HS: Response to ESTABLISH_RENDEZVOUS
96        RENDEZVOUS_ESTABLISHED = 39,
97        /// HS: Response to INTRODUCE1 from introduction point to client
98        INTRODUCE_ACK = 40,
99
100        /// Padding: declare what kind of padding we want
101        PADDING_NEGOTIATE = 41,
102        /// Padding: reply to a PADDING_NEGOTIATE
103        PADDING_NEGOTIATED = 42,
104
105        /// Flow control: rate update (transmit off)
106        XOFF = 43,
107        /// Flow control: rate update (transmit on with rate limit)
108        XON = 44,
109    }
110}
111
112/// Possible requirements on stream IDs for a relay command.
113enum StreamIdReq {
114    /// Can only be used with a stream ID of 0
115    WantNone,
116    /// Can only be used with a stream ID that isn't 0
117    WantSome,
118    /// Can be used with any stream ID.
119    ///
120    /// This result is impossible with `RelayCellFormat::V1`.
121    Any,
122    /// Unrecognized; might be used with a stream ID or without.
123    Unrecognized,
124}
125
126impl RelayCmd {
127    /// Check whether this command requires a certain kind of
128    /// StreamId in the provided `format`, and return a corresponding StreamIdReq.
129    ///
130    /// If `format` is None, return a result that is correct for _any_ version.
131    fn expects_streamid(self, format: Option<RelayCellFormat>) -> StreamIdReq {
132        match self {
133            RelayCmd::BEGIN
134            | RelayCmd::DATA
135            | RelayCmd::END
136            | RelayCmd::CONNECTED
137            | RelayCmd::RESOLVE
138            | RelayCmd::RESOLVED
139            | RelayCmd::BEGIN_DIR
140            | RelayCmd::XOFF
141            | RelayCmd::XON => StreamIdReq::WantSome,
142            // NOTE: Even when a RelayCmd is not implemented (like these UDP-based commands),
143            // we need to implement expects_streamid() unconditionally.
144            // Otherwise we leak more information than necessary
145            // when parsing RelayCellFormat::V1 cells.
146            RelayCmd::CONNECT_UDP | RelayCmd::CONNECTED_UDP | RelayCmd::DATAGRAM => {
147                StreamIdReq::WantSome
148            }
149            RelayCmd::EXTEND
150            | RelayCmd::EXTENDED
151            | RelayCmd::TRUNCATE
152            | RelayCmd::TRUNCATED
153            | RelayCmd::DROP
154            | RelayCmd::EXTEND2
155            | RelayCmd::EXTENDED2
156            | RelayCmd::CONFLUX_LINK
157            | RelayCmd::CONFLUX_LINKED
158            | RelayCmd::CONFLUX_LINKED_ACK
159            | RelayCmd::CONFLUX_SWITCH
160            | RelayCmd::ESTABLISH_INTRO
161            | RelayCmd::ESTABLISH_RENDEZVOUS
162            | RelayCmd::INTRODUCE1
163            | RelayCmd::INTRODUCE2
164            | RelayCmd::RENDEZVOUS1
165            | RelayCmd::RENDEZVOUS2
166            | RelayCmd::INTRO_ESTABLISHED
167            | RelayCmd::RENDEZVOUS_ESTABLISHED
168            | RelayCmd::INTRODUCE_ACK => StreamIdReq::WantNone,
169            RelayCmd::SENDME => match format {
170                // There are no stream-level SENDMES in V1, since CC is mandatory.
171                // Further, the 'Any' result is not possible with V1, since
172                // we need be able to decide whether a stream ID is present
173                // from the value of the command.
174                Some(RelayCellFormat::V1) => StreamIdReq::WantNone,
175                // In V0, CC is not mandatory, so stream-level SENDMES are possible.
176                Some(RelayCellFormat::V0) => StreamIdReq::Any,
177                // When we're checking for general compatibility, we need to allow V0 or V1.
178                None => StreamIdReq::Any,
179            },
180            _ => StreamIdReq::Unrecognized,
181        }
182    }
183    /// Return true if this command is one that accepts the particular
184    /// stream ID `id`.
185    ///
186    /// Note that this method does not consider the [`RelayCellFormat`] in use:
187    /// it will return "true" for _any_ stream ID if the command is `SENDME`,
188    /// and if the command is unrecognized.
189    pub fn accepts_streamid_val(self, id: Option<StreamId>) -> bool {
190        match self.expects_streamid(None) {
191            StreamIdReq::WantNone => id.is_none(),
192            StreamIdReq::WantSome => id.is_some(),
193            StreamIdReq::Any => true,
194            StreamIdReq::Unrecognized => true,
195        }
196    }
197}
198
199/// Identify a single stream on a circuit.
200///
201/// These identifiers are local to each hop on a circuit.
202/// This can't be zero; if you need something that can be zero in the protocol,
203/// use `Option<StreamId>`.
204#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Debug, Hash, Deftly)]
205#[derive_deftly(HasMemoryCost)]
206pub struct StreamId(NonZeroU16);
207
208impl From<NonZeroU16> for StreamId {
209    fn from(id: NonZeroU16) -> Self {
210        Self(id)
211    }
212}
213
214impl From<StreamId> for NonZeroU16 {
215    fn from(id: StreamId) -> NonZeroU16 {
216        id.0
217    }
218}
219
220impl From<StreamId> for u16 {
221    fn from(id: StreamId) -> u16 {
222        id.0.get()
223    }
224}
225
226impl std::fmt::Display for StreamId {
227    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
228        self.0.fmt(f)
229    }
230}
231
232impl StreamId {
233    /// Creates a `StreamId` for non-zero `stream_id`.
234    ///
235    /// Returns `None` when `stream_id` is zero. Messages with a zero/None stream ID
236    /// apply to the circuit as a whole instead of a particular stream.
237    pub fn new(stream_id: u16) -> Option<Self> {
238        NonZeroU16::new(stream_id).map(Self)
239    }
240
241    /// Convenience function to convert to a `u16`; `None` is mapped to 0.
242    pub fn get_or_zero(stream_id: Option<Self>) -> u16 {
243        match stream_id {
244            Some(stream_id) => stream_id.0.get(),
245            None => 0,
246        }
247    }
248}
249
250/// Specifies which encoding version of RelayCell to use.
251#[non_exhaustive]
252#[derive(Copy, Clone, Debug)]
253pub enum RelayCellFormat {
254    /// This is the "legacy" pre-prop340 format. No packing or fragmentation.
255    V0,
256    /// A "transitional" format for use with Counter Galois Onion encryption.
257    ///
258    /// It provides a 16-byte tag field, and a simplified layout for the rest of
259    /// the cell.
260    V1,
261}
262
263/// Internal decoder state.
264#[derive(Clone, Debug)]
265enum RelayCellDecoderInternal {
266    /// Internal state for `RelayCellFormat::V0`
267    V0,
268    /// Internal state for `RelayCellFormat::V1`
269    V1,
270}
271
272// TODO prop340: We should also fuzz RelayCellDecoder, but not in this fuzzer.
273
274/// Decodes a stream of relay cell bodies into `UnparsedRelayMsg`s.
275#[derive(Clone, Debug)]
276pub struct RelayCellDecoder {
277    /// Internal representation.
278    internal: RelayCellDecoderInternal,
279}
280
281impl RelayCellDecoder {
282    /// Returns a new `Decoder`, handling a stream of relay cells
283    /// of the given `version`.
284    pub fn new(version: RelayCellFormat) -> Self {
285        match version {
286            RelayCellFormat::V0 => Self {
287                internal: RelayCellDecoderInternal::V0,
288            },
289            RelayCellFormat::V1 => Self {
290                internal: RelayCellDecoderInternal::V1,
291            },
292        }
293    }
294    /// Parse a RELAY or RELAY_EARLY cell body.
295    ///
296    /// Requires that the cryptographic checks on the message have already been
297    /// performed
298    pub fn decode(&mut self, cell: BoxedCellBody) -> Result<RelayCellDecoderResult> {
299        let msg_internal = match &self.internal {
300            RelayCellDecoderInternal::V0 => UnparsedRelayMsgInternal::V0(cell),
301            RelayCellDecoderInternal::V1 => UnparsedRelayMsgInternal::V1(cell),
302        };
303        Ok(RelayCellDecoderResult {
304            msgs: smallvec![UnparsedRelayMsg {
305                internal: msg_internal
306            }],
307            incomplete: None,
308        })
309    }
310    /// Returns the `IncompleteRelayMsgInfo` describing the partial
311    /// (fragmented) relay message at the end of the so-far-processed relay cell
312    /// stream.
313    pub fn incomplete_info(&self) -> Option<IncompleteRelayMsgInfo> {
314        match &self.internal {
315            // V0 and V1 don't support fragmentation, so there is never a pending fragment.
316            RelayCellDecoderInternal::V0 | RelayCellDecoderInternal::V1 => None,
317        }
318    }
319}
320
321/// Result of calling `RelayCellDecoder::decode`.
322#[derive(Debug)]
323pub struct RelayCellDecoderResult {
324    /// Complete messages obtained by decoding the cell. i.e. messages
325    /// that were completely contained within the cell, or for which the cell
326    /// contained the final fragment.
327    msgs: SmallVec<[UnparsedRelayMsg; 1]>,
328    /// Description of the partial message at the end of the cell, if any.
329    incomplete: Option<IncompleteRelayMsgInfo>,
330}
331
332impl RelayCellDecoderResult {
333    /// Returns a non-empty iterator over commands in relay messages that the
334    /// cell producing this result contained *any* part of. i.e. this includes
335    /// the command of "head", "middle", and/or "tail" message fragments that
336    /// were in the cell.
337    pub fn cmds(&self) -> impl Iterator<Item = RelayCmd> + '_ {
338        let complete_msg_cmds = self.msgs.iter().map(|m| m.cmd());
339        let partial_msg_cmd = self.incomplete.as_ref().map(|c| c.cmd());
340        complete_msg_cmds.chain(partial_msg_cmd)
341    }
342    /// Converts `self` to an iterator over the complete messages, and metadata
343    /// about the trailing incomplete message (as for `Self::incomplete_info`),
344    /// if any.
345    pub fn into_parts(
346        self,
347    ) -> (
348        impl Iterator<Item = UnparsedRelayMsg>,
349        Option<IncompleteRelayMsgInfo>,
350    ) {
351        (self.msgs.into_iter(), self.incomplete)
352    }
353    /// Returns the `IncompleteRelayMsgInfo` describing the incomplete
354    /// relay message that this cell contained a fragment of, if any.
355    ///
356    /// Note that:
357    /// * This does not describe a fragment that includes the end of the relay
358    ///   message, since the message is then complete.
359    /// * This *does* include a fragment that continues, but does not complete,
360    ///   a message started in an earlier relay cell.
361    /// * There is at most one such incomplete relay message, since no current
362    ///   relay cell format supports starting a new message before completing the
363    ///   previous one.
364    pub fn incomplete_info(&self) -> Option<IncompleteRelayMsgInfo> {
365        self.incomplete.clone()
366    }
367
368    /// Return true if this consists of nothing but padding.
369    pub fn is_padding(&self) -> bool {
370        // If all the messages we have are padding...
371        self.msgs.iter().all(|m| m.cmd() == RelayCmd::DROP) &&
372            // ... and any pending incomplete message is either absent or is padding...
373            self.incomplete
374                .as_ref()
375                .is_none_or(|incomplete| incomplete.cmd() == RelayCmd::DROP)
376        // ... then this is padding.
377    }
378}
379
380/// Information about a relay message for which we don't yet have the complete body.
381#[derive(Clone, Debug)]
382pub struct IncompleteRelayMsgInfo {
383    /// The message's command.
384    cmd: RelayCmd,
385    /// The message's stream ID, if any.
386    stream_id: Option<StreamId>,
387    /// The total number of bytes in the body of the message.
388    total_msg_len: usize,
389    /// The number of bytes of the body of the message that we've decoded so
390    /// far.
391    num_bytes_present: usize,
392}
393
394impl IncompleteRelayMsgInfo {
395    /// Returns the message's command.
396    pub fn cmd(&self) -> RelayCmd {
397        self.cmd
398    }
399    /// Returns the message's `StreamId`, if any.
400    pub fn stream_id(&self) -> Option<StreamId> {
401        self.stream_id
402    }
403    /// Returns the total size of the complete message body.
404    pub fn total_msg_len(&self) -> usize {
405        self.total_msg_len
406    }
407    /// Returns the number of bytes of the message body that we have so far.
408    pub fn num_bytes_present(&self) -> usize {
409        self.num_bytes_present
410    }
411    /// Returns the number of bytes of the message body that we still need.
412    pub fn num_bytes_missing(&self) -> usize {
413        self.total_msg_len - self.num_bytes_present
414    }
415}
416
417/// Internal representation of an `UnparsedRelayMsg`.
418#[derive(Clone, Debug, Deftly)]
419#[derive_deftly(HasMemoryCost)]
420enum UnparsedRelayMsgInternal {
421    /// For `RelayCellFormat::V0` we can avoid copying data around by just
422    /// storing the original cell body here.
423    // NOTE: we could also have a separate command and stream ID field here, but
424    // we expect to be working with a TON of these, so we will be mildly
425    // over-optimized and just peek into the body.
426    //
427    // It *is* a bit ugly to have to encode so much knowledge about the format in
428    // different functions here, but that information shouldn't leak out of this module.
429    V0(BoxedCellBody),
430
431    /// For `V1` we can also avoid copies, since there is still exactly one
432    /// relay message per cell.
433    V1(BoxedCellBody),
434}
435
436/// An enveloped relay message that has not yet been fully parsed, but where we
437/// have access to the command, stream ID, and payload data length for dispatching
438/// and congestion control purposes.
439#[derive(Clone, Debug, Deftly)]
440#[derive_deftly(HasMemoryCost)]
441pub struct UnparsedRelayMsg {
442    /// The internal representation.
443    internal: UnparsedRelayMsgInternal,
444}
445
446/// Const helper to find the min between three u16 values.
447const fn min(a: u16, b: u16, c: u16) -> u16 {
448    const fn min_2(a: u16, b: u16) -> u16 {
449        if a < b { a } else { b }
450    }
451    min_2(a, min_2(b, c))
452}
453
454/// Const helper to find the max between three u16 values.
455const fn max(a: u16, b: u16, c: u16) -> u16 {
456    const fn max_2(a: u16, b: u16) -> u16 {
457        if a > b { a } else { b }
458    }
459    max_2(a, max_2(b, c))
460}
461
462/// Position of the stream ID within the V0 cell body.
463const STREAM_ID_OFFSET_V0: usize = 3;
464
465/// Position of the stream ID within the V1 cell body, if it is present.
466const STREAM_ID_OFFSET_V1: usize = 16 + 1 + 2; // tag, command, length.
467
468/// Position of the payload data length within the V0 cell body.
469const LENGTH_OFFSET_V0: usize = 1 + 2 + 2 + 4; // command, recognized, stream_id, digest.
470
471/// Position of the payload data length within the V1 cell body.
472const LENGTH_OFFSET_V1: usize = 16 + 1; // tag, command.
473
474/// Position of the payload data within the V0 cell body.
475const PAYLOAD_OFFSET_V0: usize = LENGTH_OFFSET_V0 + 2; // (everything before length), length.
476
477/// Position of the payload data within the V1 cell body, when not including a stream ID.
478const PAYLOAD_OFFSET_V1_WITHOUT_STREAM_ID: usize = LENGTH_OFFSET_V1 + 2; // (everything before length), length.
479
480/// Position of the payload data within the V1 cell body, when including a stream ID.
481const PAYLOAD_OFFSET_V1_WITH_STREAM_ID: usize = LENGTH_OFFSET_V1 + 2 + 2; // (everything before length), length, stream_id.
482
483/// Max amount of payload data that can be stored in a V0 cell body.
484const PAYLOAD_MAX_SIZE_V0: u16 = BODY_MAX_LEN_V0 - (PAYLOAD_OFFSET_V0 as u16);
485
486/// Max amount of payload data that can be stored in a V1 cell body, when not including a stream ID.
487const PAYLOAD_MAX_SIZE_V1_WITHOUT_STREAM_ID: u16 =
488    BODY_MAX_LEN_V1 - (PAYLOAD_OFFSET_V1_WITHOUT_STREAM_ID as u16);
489
490/// Max amount of payload data that can be stored in a V1 cell body, when including a stream ID.
491const PAYLOAD_MAX_SIZE_V1_WITH_STREAM_ID: u16 =
492    BODY_MAX_LEN_V1 - (PAYLOAD_OFFSET_V1_WITH_STREAM_ID as u16);
493
494/// The maximum length of a V0 cell message body.
495const BODY_MAX_LEN_V0: u16 = 509;
496
497/// The maximum length of a V1 cell message body.
498const BODY_MAX_LEN_V1: u16 = 509;
499
500/// The maximum amount of payload data that can fit within all cell body types.
501pub const PAYLOAD_MAX_SIZE_ALL: u16 = min(
502    PAYLOAD_MAX_SIZE_V0,
503    PAYLOAD_MAX_SIZE_V1_WITH_STREAM_ID,
504    PAYLOAD_MAX_SIZE_V1_WITHOUT_STREAM_ID,
505);
506
507/// The maximum amount of payload data that can fit within any cell body type.
508pub const PAYLOAD_MAX_SIZE_ANY: u16 = max(
509    PAYLOAD_MAX_SIZE_V0,
510    PAYLOAD_MAX_SIZE_V1_WITH_STREAM_ID,
511    PAYLOAD_MAX_SIZE_V1_WITHOUT_STREAM_ID,
512);
513
514impl UnparsedRelayMsg {
515    /// Wrap a BoxedCellBody as an UnparsedRelayMsg.
516    ///
517    /// Fails if the body doesn't correspond to exactly one relay message, but
518    /// doesn't parse the message itself.
519    ///
520    /// Non-test code should generally use `RelayCellDecoder` instead.
521    // Ideally we'd make this `#[cfg(test)]`, but then we wouldn't be able
522    // to use it in integration tests.
523    // https://github.com/rust-lang/rust/issues/84629
524    pub fn from_singleton_body(version: RelayCellFormat, body: BoxedCellBody) -> Result<Self> {
525        let mut decoder = RelayCellDecoder::new(version);
526        let res = decoder.decode(body)?;
527        let (mut msgs, incomplete) = res.into_parts();
528        let Some(msg) = msgs.next() else {
529            // There was no complete message in the cell.
530            return Err(Error::MissingData);
531        };
532        if incomplete.is_some() {
533            // There was an incomplete message at the end of the cell.
534            return Err(Error::ExtraneousBytes);
535        }
536        if msgs.next().is_some() {
537            // There was more than one message in the cell.
538            return Err(Error::ExtraneousBytes);
539        }
540        Ok(msg)
541    }
542
543    /// Return the command for this cell.
544    pub fn cmd(&self) -> RelayCmd {
545        match &self.internal {
546            UnparsedRelayMsgInternal::V0(body) => {
547                /// Position of the command within the v0 cell body.
548                const CMD_OFFSET: usize = 0;
549                body[CMD_OFFSET].into()
550            }
551            UnparsedRelayMsgInternal::V1(body) => {
552                /// Position of the command within the v1 body.
553                const CMD_OFFSET: usize = 16;
554                body[CMD_OFFSET].into()
555            }
556        }
557    }
558    /// Return the stream ID for the stream that this msg corresponds to, if any.
559    pub fn stream_id(&self) -> Option<StreamId> {
560        match &self.internal {
561            UnparsedRelayMsgInternal::V0(body) => StreamId::new(u16::from_be_bytes(
562                body[STREAM_ID_OFFSET_V0..STREAM_ID_OFFSET_V0 + 2]
563                    .try_into()
564                    .expect("two-byte slice was not two bytes long!?"),
565            )),
566            UnparsedRelayMsgInternal::V1(body) => {
567                match self.cmd().expects_streamid(Some(RelayCellFormat::V1)) {
568                    StreamIdReq::WantNone => None,
569                    StreamIdReq::Unrecognized | StreamIdReq::Any => None,
570                    StreamIdReq::WantSome => StreamId::new(u16::from_be_bytes(
571                        body[STREAM_ID_OFFSET_V1..STREAM_ID_OFFSET_V1 + 2]
572                            .try_into()
573                            .expect("two-byte slice was not two bytes long!?"),
574                    )),
575                }
576            }
577        }
578    }
579    /// Return the "length" field of a data cell, or 0 if not a data cell.
580    ///
581    /// This is the size of the cell data (the "data" field), not the size of the cell.
582    ///
583    /// If the field value is invalid (for example >509 for V0 cells), an error will be returned.
584    pub fn data_len(&self) -> Result<u16> {
585        if self.cmd() != RelayCmd::DATA {
586            return Ok(0);
587        }
588
589        let bytes: [u8; 2] = match &self.internal {
590            UnparsedRelayMsgInternal::V0(body) => &body[LENGTH_OFFSET_V0..LENGTH_OFFSET_V0 + 2],
591            UnparsedRelayMsgInternal::V1(body) => &body[LENGTH_OFFSET_V1..LENGTH_OFFSET_V1 + 2],
592        }
593        .try_into()
594        .expect("two-byte slice was not two bytes long!?");
595
596        let len = u16::from_be_bytes(bytes);
597
598        let max = match &self.internal {
599            UnparsedRelayMsgInternal::V0(_body) => BODY_MAX_LEN_V0,
600            UnparsedRelayMsgInternal::V1(_body) => BODY_MAX_LEN_V1,
601        };
602
603        if len > max {
604            // TODO: This error value isn't quite right as it has the error message "object length
605            // too large to represent as usize", which isn't what we're checking here.
606            // But it wouldn't make sense to add a different but similar variant to `Error`.
607            return Err(Error::BadLengthValue);
608        }
609
610        Ok(len)
611    }
612    /// Decode this unparsed cell into a given cell type.
613    pub fn decode<M: RelayMsg>(self) -> Result<RelayMsgOuter<M>> {
614        match self.internal {
615            UnparsedRelayMsgInternal::V0(body) => {
616                let mut reader = Reader::from_slice(body.as_ref());
617                RelayMsgOuter::decode_v0_from_reader(&mut reader)
618            }
619            UnparsedRelayMsgInternal::V1(body) => {
620                let mut reader = Reader::from_slice(body.as_ref());
621                RelayMsgOuter::decode_v1_from_reader(&mut reader)
622            }
623        }
624    }
625}
626
627/// A decoded and parsed relay message of unrestricted type,
628/// with an accompanying optional Stream ID.
629pub type AnyRelayMsgOuter = RelayMsgOuter<msg::AnyRelayMsg>;
630
631/// A deprecated name for AnyRelayMsgOuter.
632#[deprecated(note = "Use AnyRelayMsgOuter instead.")]
633pub type AnyRelayCell = AnyRelayMsgOuter;
634
635/// Trait implemented by anything that can serve as a relay message.
636///
637/// Typically, this will be [`RelayMsg`] (to represent an unrestricted relay
638/// message), or a restricted subset of `RelayMsg`.
639pub trait RelayMsg {
640    /// Return the stream command associated with this message.
641    fn cmd(&self) -> RelayCmd;
642    /// Encode the body of this message, not including command or length
643    fn encode_onto<W: tor_bytes::Writer + ?Sized>(self, w: &mut W) -> tor_bytes::EncodeResult<()>;
644    /// Extract the body of a message with command `cmd` from reader `r`.
645    fn decode_from_reader(cmd: RelayCmd, r: &mut Reader<'_>) -> Result<Self>
646    where
647        Self: Sized;
648}
649
650/// A decoded and parsed relay message, along with an optional Stream ID.
651///
652/// This type represents a message that can be sent along a
653/// circuit, along with the ID for an associated stream that the
654/// message is meant for.
655///
656/// NOTE: This name is a placeholder; we intend to replace it once we have
657/// standardized our vocabulary in this area.
658#[derive(Debug)]
659pub struct RelayMsgOuter<M> {
660    /// The stream ID for the stream that this cell corresponds to.
661    streamid: Option<StreamId>,
662    /// The relay message for this cell.
663    msg: M,
664}
665
666/// A deprecated name for RelayMsgOuter.
667#[deprecated(note = "Use RelayMsgOuter instead.")]
668pub type RelayCell<M> = RelayMsgOuter<M>;
669
670impl<M: RelayMsg> RelayMsgOuter<M> {
671    /// Construct a new relay cell.
672    pub fn new(streamid: Option<StreamId>, msg: M) -> Self {
673        RelayMsgOuter { streamid, msg }
674    }
675    /// Consume this cell and return its components.
676    pub fn into_streamid_and_msg(self) -> (Option<StreamId>, M) {
677        (self.streamid, self.msg)
678    }
679    /// Return the command for this cell.
680    pub fn cmd(&self) -> RelayCmd {
681        self.msg.cmd()
682    }
683    /// Return the stream ID for the stream that this cell corresponds to.
684    pub fn stream_id(&self) -> Option<StreamId> {
685        self.streamid
686    }
687    /// Return the underlying message for this cell.
688    pub fn msg(&self) -> &M {
689        &self.msg
690    }
691    /// Consume this cell and return the underlying message.
692    pub fn into_msg(self) -> M {
693        self.msg
694    }
695    /// Consume this relay message and encode it as a 509-byte padded cell
696    /// body.
697    //
698    // TODO prop340: This API won't work for packed or fragmented messages.
699    pub fn encode<R: Rng + CryptoRng>(
700        self,
701        format: RelayCellFormat,
702        rng: &mut R,
703    ) -> crate::Result<BoxedCellBody> {
704        /// We skip this much space before adding any random padding to the
705        /// end of the cell
706        const MIN_SPACE_BEFORE_PADDING: usize = 4;
707
708        let (mut body, enc_len) = match format {
709            RelayCellFormat::V0 => self.encode_to_cell_v0()?,
710            RelayCellFormat::V1 => self.encode_to_cell_v1()?,
711        };
712        debug_assert!(enc_len <= CELL_DATA_LEN);
713        if enc_len < CELL_DATA_LEN - MIN_SPACE_BEFORE_PADDING {
714            rng.fill_bytes(&mut body[enc_len + MIN_SPACE_BEFORE_PADDING..]);
715        }
716
717        Ok(body)
718    }
719
720    /// Consume a relay cell and return its contents, encoded for use
721    /// in a RELAY or RELAY_EARLY cell.
722    fn encode_to_cell_v0(self) -> EncodeResult<(BoxedCellBody, usize)> {
723        // NOTE: This implementation is a bit optimized, since it happens to
724        // literally every relay cell that we produce.
725
726        // TODO -NM: Add a specialized implementation for making a DATA cell from
727        // a body?
728
729        /// The position of the length field within a relay cell.
730        const LEN_POS: usize = 9;
731        /// The position of the body a relay cell.
732        const BODY_POS: usize = 11;
733
734        let body = BodyWrapper(Box::new([0_u8; BODY_MAX_LEN_V0 as usize]));
735
736        let mut w = crate::slicewriter::SliceWriter::new(body);
737        w.write_u8(self.msg.cmd().into());
738        w.write_u16(0); // "Recognized"
739        w.assert_offset_is(STREAM_ID_OFFSET_V0);
740        w.write_u16(StreamId::get_or_zero(self.streamid));
741        w.write_u32(0); // Digest
742        // (It would be simpler to use NestedWriter at this point, but it uses an internal Vec that we are trying to avoid.)
743        w.assert_offset_is(LEN_POS);
744        w.write_u16(0); // Length.
745        w.assert_offset_is(BODY_POS);
746        self.msg.encode_onto(&mut w)?; // body
747        let (mut body, written) = w.try_unwrap().map_err(|_| {
748            EncodeError::Bug(internal!(
749                "Encoding of relay message was too long to fit into a cell!"
750            ))
751        })?;
752        let payload_len = written - BODY_POS;
753        debug_assert!(payload_len < u16::MAX as usize);
754        *(<&mut [u8; 2]>::try_from(&mut body.0[LEN_POS..LEN_POS + 2])
755            .expect("Two-byte slice was not two bytes long!?")) =
756            (payload_len as u16).to_be_bytes();
757        Ok((body.0, written))
758    }
759
760    /// Consume a relay cell and return its contents, encoded for use
761    /// in a RELAY or RELAY_EARLY cell.
762    fn encode_to_cell_v1(self) -> EncodeResult<(BoxedCellBody, usize)> {
763        // NOTE: This implementation is a bit optimized, since it happens to
764        // literally every relay cell that we produce.
765        // TODO -NM: Add a specialized implementation for making a DATA cell from
766        // a body?
767
768        /// Position of the length field within the cell.
769        const LEN_POS_V1: usize = 16 + 1; // Skipping tag, command.
770
771        let cmd = self.msg.cmd();
772        let body = BodyWrapper(Box::new([0_u8; BODY_MAX_LEN_V1 as usize]));
773        let mut w = crate::slicewriter::SliceWriter::new(body);
774        w.advance(16); // Tag: 16 bytes
775        w.write_u8(cmd.get()); // Command: 1 byte.
776        w.assert_offset_is(LEN_POS_V1);
777        w.advance(2); //  Length: 2 bytes.
778        let mut body_pos = 16 + 1 + 2;
779        match (
780            cmd.expects_streamid(Some(RelayCellFormat::V1)),
781            self.streamid,
782        ) {
783            (StreamIdReq::WantNone, None) => {}
784            (StreamIdReq::WantSome, Some(id)) => {
785                w.write_u16(id.into());
786                body_pos += 2;
787            }
788            (_, id) => {
789                return Err(EncodeError::Bug(internal!(
790                    "Tried to encode invalid stream ID {id:?} for {cmd}"
791                )));
792            }
793        }
794        w.assert_offset_is(body_pos);
795
796        self.msg.encode_onto(&mut w)?; // body
797        let (mut body, written) = w.try_unwrap().map_err(|_| {
798            EncodeError::Bug(internal!(
799                "Encoding of relay message was too long to fit into a cell!"
800            ))
801        })?;
802        let payload_len = written - body_pos;
803        debug_assert!(payload_len < u16::MAX as usize);
804        *(<&mut [u8; 2]>::try_from(&mut body.0[LEN_POS_V1..LEN_POS_V1 + 2])
805            .expect("Two-byte slice was not two bytes long!?")) =
806            (payload_len as u16).to_be_bytes();
807        Ok((body.0, written))
808    }
809
810    /// Parse a RELAY or RELAY_EARLY cell body into a RelayMsgOuter.
811    /// Requires that the cryptographic checks on the message have already been
812    /// performed
813    ///
814    /// Fails if the cell doesn't contain exactly one message.
815    ///
816    /// Non-test code should generally use `RelayCellDecoder` instead.
817    // Ideally we'd make this `#[cfg(test)]`, but then we wouldn't be able
818    // to use it in integration tests.
819    // https://github.com/rust-lang/rust/issues/84629
820    #[allow(clippy::needless_pass_by_value)] // TODO this will go away soon.
821    pub fn decode_singleton(version: RelayCellFormat, body: BoxedCellBody) -> Result<Self> {
822        let unparsed_msg = UnparsedRelayMsg::from_singleton_body(version, body)?;
823        unparsed_msg.decode()
824    }
825    /// Parse a `RelayCellFormat::V0` RELAY or RELAY_EARLY cell body into a
826    /// RelayMsgOuter from a reader.
827    ///
828    /// Requires that the cryptographic checks on the message have already been
829    /// performed
830    fn decode_v0_from_reader(r: &mut Reader<'_>) -> Result<Self> {
831        let cmd = r.take_u8()?.into();
832        r.advance(2)?; // "recognized"
833        let streamid = StreamId::new(r.take_u16()?);
834        r.advance(4)?; // digest
835        let len = r.take_u16()? as usize;
836        if r.remaining() < len {
837            return Err(Error::InvalidMessage(
838                "Insufficient data in relay cell".into(),
839            ));
840        }
841        r.truncate(len);
842        let msg = M::decode_from_reader(cmd, r)?;
843        Ok(Self { streamid, msg })
844    }
845
846    /// Parse a `RelayCellFormat::V1` RELAY or RELAY_EARLY cell body into a
847    /// RelayMsgOuter from a reader.
848    ///
849    /// Requires that the cryptographic checks on the message have already been
850    /// performed.
851    fn decode_v1_from_reader(r: &mut Reader<'_>) -> Result<Self> {
852        r.advance(16)?; // Tag
853        let cmd: RelayCmd = r.take_u8()?.into();
854        let len = r.take_u16()?.into();
855        let streamid = match cmd.expects_streamid(Some(RelayCellFormat::V1)) {
856            // If no stream ID is expected, then the body begins immediately.
857            StreamIdReq::WantNone => None,
858            // In this case, a stream ID _is_ expected.
859            //
860            // (If it happens to be zero, we will reject the message,
861            // since zero is never a stream ID.)
862            StreamIdReq::WantSome => Some(StreamId::new(r.take_u16()?).ok_or_else(|| {
863                Error::InvalidMessage(
864                    format!("Zero-valued stream ID with relay command {cmd}").into(),
865                )
866            })?),
867            // We treat an unrecognized command as having no stream ID.
868            //
869            // (Note: This command is truly unrecognized, and not one that we could parse
870            // differently under other circumstances.)
871            //
872            // Note that this enables a destructive fingerprinting opportunity,
873            // where an attacker can learn whether we have a version of Arti that recognizes this
874            // command, at the expense of our killing this circuit immediately if they are wrong.
875            // This is not a very bad attack.
876            //
877            // Note that StreamIdReq::Any should be impossible here, since we're using the V1
878            // format.
879            StreamIdReq::Unrecognized | StreamIdReq::Any => {
880                return Err(Error::InvalidMessage(
881                    format!("Unrecognized relay command {cmd}").into(),
882                ));
883            }
884        };
885        if r.remaining() < len {
886            //
887            return Err(Error::InvalidMessage(
888                "Insufficient data in relay cell".into(),
889            ));
890        }
891        r.truncate(len);
892        let msg = M::decode_from_reader(cmd, r)?;
893        Ok(Self { streamid, msg })
894    }
895}
896
897/// Wrap a BoxedCellBody and implement AsMut<[u8]>, so we can use it with `SliceWriter`.
898struct BodyWrapper(BoxedCellBody);
899impl AsMut<[u8]> for BodyWrapper {
900    fn as_mut(&mut self) -> &mut [u8] {
901        self.0.as_mut()
902    }
903}