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;
19#[cfg(feature = "hs")]
20pub mod hs;
21pub mod msg;
22#[cfg(feature = "experimental-udp")]
23pub mod udp;
24
25caret_int! {
26    /// A command that identifies the type of a relay cell
27    #[derive(Deftly)]
28    #[derive_deftly(HasMemoryCost)]
29    pub struct RelayCmd(u8) {
30        /// Start a new stream
31        BEGIN = 1,
32        /// Data on a stream
33        DATA = 2,
34        /// Close a stream
35        END = 3,
36        /// Acknowledge a BEGIN; stream is open
37        CONNECTED = 4,
38        /// Used for flow control
39        SENDME = 5,
40        /// Extend a circuit to a new hop; deprecated
41        EXTEND = 6,
42        /// Reply to EXTEND handshake; deprecated
43        EXTENDED = 7,
44        /// Partially close a circuit
45        TRUNCATE = 8,
46        /// Circuit has been partially closed
47        TRUNCATED = 9,
48        /// Padding cell
49        DROP = 10,
50        /// Start a DNS lookup
51        RESOLVE = 11,
52        /// Reply to a DNS lookup
53        RESOLVED = 12,
54        /// Start a directory stream
55        BEGIN_DIR = 13,
56        /// Extend a circuit to a new hop
57        EXTEND2 = 14,
58        /// Reply to an EXTEND2 cell.
59        EXTENDED2 = 15,
60
61        /// NOTE: UDP command are reserved but only used with experimental-udp feature
62
63        /// UDP: Start of a stream
64        CONNECT_UDP = 16,
65        /// UDP: Acknowledge a CONNECT_UDP. Stream is open.
66        CONNECTED_UDP = 17,
67        /// UDP: Data on a UDP stream.
68        DATAGRAM = 18,
69
70        /// CONFLUX: Link circuits together at the receiving endpoint.
71        CONFLUX_LINK = 19,
72        /// CONFLUX: Confirm that the circuits were linked.
73        CONFLUX_LINKED = 20,
74        /// CONFLUX: Acknowledge the linkage of the circuits, for RTT measurement.
75        CONFLUX_LINKED_ACK = 21,
76        /// CONFLUX: Switch to another leg in an already linked circuit construction.
77        CONFLUX_SWITCH = 22,
78
79        /// HS: establish an introduction point.
80        ESTABLISH_INTRO = 32,
81        /// HS: establish a rendezvous point.
82        ESTABLISH_RENDEZVOUS = 33,
83        /// HS: send introduction (client to introduction point)
84        INTRODUCE1 = 34,
85        /// HS: send introduction (introduction point to service)
86        INTRODUCE2 = 35,
87        /// HS: connect rendezvous point (service to rendezvous point)
88        RENDEZVOUS1 = 36,
89        /// HS: connect rendezvous point (rendezvous point to client)
90        RENDEZVOUS2 = 37,
91        /// HS: Response to ESTABLISH_INTRO
92        INTRO_ESTABLISHED = 38,
93        /// HS: Response to ESTABLISH_RENDEZVOUS
94        RENDEZVOUS_ESTABLISHED = 39,
95        /// HS: Response to INTRODUCE1 from introduction point to client
96        INTRODUCE_ACK = 40,
97
98        /// Padding: declare what kind of padding we want
99        PADDING_NEGOTIATE = 41,
100        /// Padding: reply to a PADDING_NEGOTIATE
101        PADDING_NEGOTIATED = 42,
102    }
103}
104
105/// Possible requirements on stream IDs for a relay command.
106enum StreamIdReq {
107    /// Can only be used with a stream ID of 0
108    WantNone,
109    /// Can only be used with a stream ID that isn't 0
110    WantSome,
111    /// Can be used with any stream ID
112    Any,
113}
114
115impl RelayCmd {
116    /// Check whether this command requires a certain kind of
117    /// StreamId, and return a corresponding StreamIdReq.
118    fn expects_streamid(self) -> StreamIdReq {
119        match self {
120            RelayCmd::BEGIN
121            | RelayCmd::DATA
122            | RelayCmd::END
123            | RelayCmd::CONNECTED
124            | RelayCmd::RESOLVE
125            | RelayCmd::RESOLVED
126            | RelayCmd::BEGIN_DIR => StreamIdReq::WantSome,
127            #[cfg(feature = "experimental-udp")]
128            RelayCmd::CONNECT_UDP | RelayCmd::CONNECTED_UDP | RelayCmd::DATAGRAM => {
129                StreamIdReq::WantSome
130            }
131            RelayCmd::EXTEND
132            | RelayCmd::EXTENDED
133            | RelayCmd::TRUNCATE
134            | RelayCmd::TRUNCATED
135            | RelayCmd::DROP
136            | RelayCmd::EXTEND2
137            | RelayCmd::EXTENDED2
138            | RelayCmd::CONFLUX_LINK
139            | RelayCmd::CONFLUX_LINKED
140            | RelayCmd::CONFLUX_LINKED_ACK
141            | RelayCmd::CONFLUX_SWITCH
142            | RelayCmd::ESTABLISH_INTRO
143            | RelayCmd::ESTABLISH_RENDEZVOUS
144            | RelayCmd::INTRODUCE1
145            | RelayCmd::INTRODUCE2
146            | RelayCmd::RENDEZVOUS1
147            | RelayCmd::RENDEZVOUS2
148            | RelayCmd::INTRO_ESTABLISHED
149            | RelayCmd::RENDEZVOUS_ESTABLISHED
150            | RelayCmd::INTRODUCE_ACK => StreamIdReq::WantNone,
151            RelayCmd::SENDME => StreamIdReq::Any,
152            _ => StreamIdReq::Any,
153        }
154    }
155    /// Return true if this command is one that accepts the particular
156    /// stream ID `id`
157    pub fn accepts_streamid_val(self, id: Option<StreamId>) -> bool {
158        match self.expects_streamid() {
159            StreamIdReq::WantNone => id.is_none(),
160            StreamIdReq::WantSome => id.is_some(),
161            StreamIdReq::Any => true,
162        }
163    }
164}
165
166/// Identify a single stream on a circuit.
167///
168/// These identifiers are local to each hop on a circuit.
169/// This can't be zero; if you need something that can be zero in the protocol,
170/// use `Option<StreamId>`.
171#[derive(Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Debug, Hash, Deftly)]
172#[derive_deftly(HasMemoryCost)]
173pub struct StreamId(NonZeroU16);
174
175impl From<NonZeroU16> for StreamId {
176    fn from(id: NonZeroU16) -> Self {
177        Self(id)
178    }
179}
180
181impl From<StreamId> for NonZeroU16 {
182    fn from(id: StreamId) -> NonZeroU16 {
183        id.0
184    }
185}
186
187impl From<StreamId> for u16 {
188    fn from(id: StreamId) -> u16 {
189        id.0.get()
190    }
191}
192
193impl std::fmt::Display for StreamId {
194    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
195        self.0.fmt(f)
196    }
197}
198
199impl StreamId {
200    /// Creates a `StreamId` for non-zero `stream_id`.
201    ///
202    /// Returns `None` when `stream_id` is zero. Messages with a zero/None stream ID
203    /// apply to the circuit as a whole instead of a particular stream.
204    pub fn new(stream_id: u16) -> Option<Self> {
205        NonZeroU16::new(stream_id).map(Self)
206    }
207
208    /// Convenience function to convert to a `u16`; `None` is mapped to 0.
209    pub fn get_or_zero(stream_id: Option<Self>) -> u16 {
210        match stream_id {
211            Some(stream_id) => stream_id.0.get(),
212            None => 0,
213        }
214    }
215}
216
217/// Specifies which encoding version of RelayCell to use.
218#[non_exhaustive]
219#[derive(Copy, Clone, Debug)]
220pub enum RelayCellFormat {
221    /// This is the "legacy" pre-prop340 format. No packing or fragmentation.
222    V0,
223}
224
225/// Specifies a relay cell format and associated types.
226pub trait RelayCellFormatTrait {
227    /// Which format this object is for.
228    const FORMAT: RelayCellFormat;
229    /// A `RelayCellFields` type for this format.
230    type FIELDS: RelayCellFields;
231    // TODO: Consider making a trait for the decoder as well and adding the
232    // corresponding associated type here.
233}
234
235/// Format type corresponding to `RelayCellFormat::V0`.
236#[non_exhaustive]
237pub struct RelayCellFormatV0;
238
239impl RelayCellFormatTrait for RelayCellFormatV0 {
240    const FORMAT: RelayCellFormat = RelayCellFormat::V0;
241    type FIELDS = RelayCellFieldsV0;
242}
243
244/// Specifies field layout for a particular relay cell format.
245pub trait RelayCellFields {
246    /// The range containing the `recognized` field, within a relay cell's body.
247    const RECOGNIZED_RANGE: std::ops::Range<usize>;
248    /// The range containing the `digest` field, within a relay cell's body.
249    const DIGEST_RANGE: std::ops::Range<usize>;
250    /// A static array of zeroes of the same size as this format uses for the
251    /// digest field. e.g. this enables updating a comparison-digest in one
252    /// hash-update method call, instead of having to loop over `DIGEST_RANGE`.
253    const EMPTY_DIGEST: &'static [u8];
254}
255
256/// Specifies fields for `RelayCellFormat::V0`.
257#[non_exhaustive]
258pub struct RelayCellFieldsV0;
259
260impl RelayCellFields for RelayCellFieldsV0 {
261    const RECOGNIZED_RANGE: std::ops::Range<usize> = 1..3;
262    const DIGEST_RANGE: std::ops::Range<usize> = 5..9;
263    const EMPTY_DIGEST: &'static [u8] = &[0, 0, 0, 0];
264}
265
266/// Internal decoder state.
267#[derive(Clone, Debug)]
268enum RelayCellDecoderInternal {
269    /// Internal state for `RelayCellFormat::V0`
270    V0,
271}
272
273// TODO prop340: We should also fuzz RelayCellDecoder, but not in this fuzzer.
274
275/// Decodes a stream of relay cell bodies into `UnparsedRelayMsg`s.
276#[derive(Clone, Debug)]
277pub struct RelayCellDecoder {
278    /// Internal representation.
279    internal: RelayCellDecoderInternal,
280}
281
282impl RelayCellDecoder {
283    /// Returns a new `Decoder`, handling a stream of relay cells
284    /// of the given `version`.
285    pub fn new(version: RelayCellFormat) -> Self {
286        match version {
287            RelayCellFormat::V0 => Self {
288                internal: RelayCellDecoderInternal::V0,
289            },
290        }
291    }
292    /// Parse a RELAY or RELAY_EARLY cell body.
293    ///
294    /// Requires that the cryptographic checks on the message have already been
295    /// performed
296    pub fn decode(&mut self, cell: BoxedCellBody) -> Result<RelayCellDecoderResult> {
297        match &self.internal {
298            RelayCellDecoderInternal::V0 => Ok(RelayCellDecoderResult {
299                msgs: smallvec![UnparsedRelayMsg {
300                    internal: UnparsedRelayMsgInternal::V0(cell)
301                }],
302                incomplete: None,
303            }),
304        }
305    }
306    /// Returns the `IncompleteRelayMsgInfo` describing the partial
307    /// (fragmented) relay message at the end of the so-far-processed relay cell
308    /// stream.
309    pub fn incomplete_info(&self) -> Option<IncompleteRelayMsgInfo> {
310        match &self.internal {
311            // V0 doesn't support fragmentation, so there is never a pending fragment.
312            RelayCellDecoderInternal::V0 => None,
313        }
314    }
315}
316
317/// Result of calling `RelayCellDecoder::decode`.
318#[derive(Debug)]
319pub struct RelayCellDecoderResult {
320    /// Complete messages obtained by decoding the cell. i.e. messages
321    /// that were completely contained within the cell, or for which the cell
322    /// contained the final fragment.
323    msgs: SmallVec<[UnparsedRelayMsg; 1]>,
324    /// Description of the partial message at the end of the cell, if any.
325    incomplete: Option<IncompleteRelayMsgInfo>,
326}
327
328impl RelayCellDecoderResult {
329    /// Returns a non-empty iterator over commands in relay messages that the
330    /// cell producing this result contained *any* part of. i.e. this includes
331    /// the command of "head", "middle", and/or "tail" message fragments that
332    /// were in the cell.
333    pub fn cmds(&self) -> impl Iterator<Item = RelayCmd> + '_ {
334        let complete_msg_cmds = self.msgs.iter().map(|m| m.cmd());
335        let partial_msg_cmd = self.incomplete.as_ref().map(|c| c.cmd());
336        complete_msg_cmds.chain(partial_msg_cmd)
337    }
338    /// Converts `self` to an iterator over the complete messages, and metadata
339    /// about the trailing incomplete message (as for `Self::incomplete_info`),
340    /// if any.
341    pub fn into_parts(
342        self,
343    ) -> (
344        impl Iterator<Item = UnparsedRelayMsg>,
345        Option<IncompleteRelayMsgInfo>,
346    ) {
347        (self.msgs.into_iter(), self.incomplete)
348    }
349    /// Returns the `IncompleteRelayMsgInfo` describing the incomplete
350    /// relay message that this cell contained a fragment of, if any.
351    ///
352    /// Note that:
353    /// * This does not describe a fragment that includes the end of the relay
354    ///   message, since the message is then complete.
355    /// * This *does* include a fragment that continues, but does not complete,
356    ///   a message started in an earlier relay cell.
357    /// * There is at most one such incomplete relay message, since no current
358    ///   relay cell format supports starting a new message before completing the
359    ///   previous one.
360    pub fn incomplete_info(&self) -> Option<IncompleteRelayMsgInfo> {
361        self.incomplete.clone()
362    }
363}
364
365/// Information about a relay message for which we don't yet have the complete body.
366#[derive(Clone, Debug)]
367pub struct IncompleteRelayMsgInfo {
368    /// The message's command.
369    cmd: RelayCmd,
370    /// The message's stream ID, if any.
371    stream_id: Option<StreamId>,
372    /// The total number of bytes in the body of the message.
373    total_msg_len: usize,
374    /// The number of bytes of the body of the message that we've decoded so
375    /// far.
376    num_bytes_present: usize,
377}
378
379impl IncompleteRelayMsgInfo {
380    /// Returns the message's command.
381    pub fn cmd(&self) -> RelayCmd {
382        self.cmd
383    }
384    /// Returns the message's `StreamId`, if any.
385    pub fn stream_id(&self) -> Option<StreamId> {
386        self.stream_id
387    }
388    /// Returns the total size of the complete message body.
389    pub fn total_msg_len(&self) -> usize {
390        self.total_msg_len
391    }
392    /// Returns the number of bytes of the message body that we have so far.
393    pub fn num_bytes_present(&self) -> usize {
394        self.num_bytes_present
395    }
396    /// Returns the number of bytes of the message body that we still need.
397    pub fn num_bytes_missing(&self) -> usize {
398        self.total_msg_len - self.num_bytes_present
399    }
400}
401
402/// Internal representation of an `UnparsedRelayMsg`.
403#[derive(Clone, Debug, Deftly)]
404#[derive_deftly(HasMemoryCost)]
405enum UnparsedRelayMsgInternal {
406    /// For `RelayCellFormat::V0` we can avoid copying data around by just
407    /// storing the original cell body here.
408    // NOTE: we could also have a separate command and stream ID field here, but
409    // we expect to be working with a TON of these, so we will be mildly
410    // over-optimized and just peek into the body.
411    //
412    // It *is* a bit ugly to have to encode so much knowledge about the format in
413    // different functions here, but that information shouldn't leak out of this module.
414    V0(BoxedCellBody),
415}
416
417/// An enveloped relay message that has not yet been fully parsed, but where we
418/// have access to the command and stream ID, for dispatching purposes.
419#[derive(Clone, Debug, Deftly)]
420#[derive_deftly(HasMemoryCost)]
421pub struct UnparsedRelayMsg {
422    /// The internal representation.
423    internal: UnparsedRelayMsgInternal,
424}
425
426/// Position of the stream ID within the cell body.
427const STREAM_ID_OFFSET: usize = 3;
428
429impl UnparsedRelayMsg {
430    /// Wrap a BoxedCellBody as an UnparsedRelayMsg.
431    ///
432    /// Fails if the body doesn't correspond to exactly one relay message, but
433    /// doesn't parse the message itself.
434    ///
435    /// Non-test code should generally use `RelayCellDecoder` instead.
436    // Ideally we'd make this `#[cfg(test)]`, but then we wouldn't be able
437    // to use it in integration tests.
438    // https://github.com/rust-lang/rust/issues/84629
439    pub fn from_singleton_body(version: RelayCellFormat, body: BoxedCellBody) -> Result<Self> {
440        let mut decoder = RelayCellDecoder::new(version);
441        let res = decoder.decode(body)?;
442        let (mut msgs, incomplete) = res.into_parts();
443        let Some(msg) = msgs.next() else {
444            // There was no complete message in the cell.
445            return Err(Error::MissingData);
446        };
447        if incomplete.is_some() {
448            // There was an incomplete message at the end of the cell.
449            return Err(Error::ExtraneousBytes);
450        }
451        if msgs.next().is_some() {
452            // There was more than one message in the cell.
453            return Err(Error::ExtraneousBytes);
454        }
455        Ok(msg)
456    }
457
458    /// Return the command for this cell.
459    pub fn cmd(&self) -> RelayCmd {
460        match &self.internal {
461            UnparsedRelayMsgInternal::V0(body) => {
462                /// Position of the command within the cell body.
463                const CMD_OFFSET: usize = 0;
464                body[CMD_OFFSET].into()
465            }
466        }
467    }
468    /// Return the stream ID for the stream that this msg corresponds to, if any.
469    pub fn stream_id(&self) -> Option<StreamId> {
470        match &self.internal {
471            UnparsedRelayMsgInternal::V0(body) => StreamId::new(u16::from_be_bytes(
472                body[STREAM_ID_OFFSET..STREAM_ID_OFFSET + 2]
473                    .try_into()
474                    .expect("two-byte slice was not two bytes long!?"),
475            )),
476        }
477    }
478    /// Decode this unparsed cell into a given cell type.
479    pub fn decode<M: RelayMsg>(self) -> Result<RelayMsgOuter<M>> {
480        match self.internal {
481            UnparsedRelayMsgInternal::V0(body) => {
482                let mut reader = Reader::from_slice(body.as_ref());
483                RelayMsgOuter::decode_v0_from_reader(&mut reader)
484            }
485        }
486    }
487}
488
489/// A decoded and parsed relay message of unrestricted type,
490/// with an accompanying optional Stream ID.
491pub type AnyRelayMsgOuter = RelayMsgOuter<msg::AnyRelayMsg>;
492
493/// A deprecated name for AnyRelayMsgOuter.
494#[deprecated(note = "Use AnyRelayMsgOuter instead.")]
495pub type AnyRelayCell = AnyRelayMsgOuter;
496
497/// Trait implemented by anything that can serve as a relay message.
498///
499/// Typically, this will be [`RelayMsg`] (to represent an unrestricted relay
500/// message), or a restricted subset of `RelayMsg`.
501pub trait RelayMsg {
502    /// Return the stream command associated with this message.
503    fn cmd(&self) -> RelayCmd;
504    /// Encode the body of this message, not including command or length
505    fn encode_onto<W: tor_bytes::Writer + ?Sized>(self, w: &mut W) -> tor_bytes::EncodeResult<()>;
506    /// Extract the body of a message with command `cmd` from reader `r`.
507    fn decode_from_reader(cmd: RelayCmd, r: &mut Reader<'_>) -> Result<Self>
508    where
509        Self: Sized;
510}
511
512/// A decoded and parsed relay message, along with an optional Stream ID.
513///
514/// This type represents a message that can be sent along a
515/// circuit, along with the ID for an associated stream that the
516/// message is meant for.
517///
518/// NOTE: This name is a placeholder; we intend to replace it once we have
519/// standardized our vocabulary in this area.
520#[derive(Debug)]
521pub struct RelayMsgOuter<M> {
522    /// The stream ID for the stream that this cell corresponds to.
523    streamid: Option<StreamId>,
524    /// The relay message for this cell.
525    msg: M,
526}
527
528/// A deprecated name for RelayMsgOuter.
529#[deprecated(note = "Use RelayMsgOuter instead.")]
530pub type RelayCell<M> = RelayMsgOuter<M>;
531
532impl<M: RelayMsg> RelayMsgOuter<M> {
533    /// Construct a new relay cell.
534    pub fn new(streamid: Option<StreamId>, msg: M) -> Self {
535        RelayMsgOuter { streamid, msg }
536    }
537    /// Consume this cell and return its components.
538    pub fn into_streamid_and_msg(self) -> (Option<StreamId>, M) {
539        (self.streamid, self.msg)
540    }
541    /// Return the command for this cell.
542    pub fn cmd(&self) -> RelayCmd {
543        self.msg.cmd()
544    }
545    /// Return the stream ID for the stream that this cell corresponds to.
546    pub fn stream_id(&self) -> Option<StreamId> {
547        self.streamid
548    }
549    /// Return the underlying message for this cell.
550    pub fn msg(&self) -> &M {
551        &self.msg
552    }
553    /// Consume this cell and return the underlying message.
554    pub fn into_msg(self) -> M {
555        self.msg
556    }
557    /// Consume this relay message and encode it as a 509-byte padded cell
558    /// body.
559    pub fn encode<R: Rng + CryptoRng>(self, rng: &mut R) -> crate::Result<BoxedCellBody> {
560        /// We skip this much space before adding any random padding to the
561        /// end of the cell
562        const MIN_SPACE_BEFORE_PADDING: usize = 4;
563
564        let (mut body, enc_len) = self.encode_to_cell()?;
565        debug_assert!(enc_len <= CELL_DATA_LEN);
566        if enc_len < CELL_DATA_LEN - MIN_SPACE_BEFORE_PADDING {
567            rng.fill_bytes(&mut body[enc_len + MIN_SPACE_BEFORE_PADDING..]);
568        }
569
570        Ok(body)
571    }
572
573    /// Consume a relay cell and return its contents, encoded for use
574    /// in a RELAY or RELAY_EARLY cell.
575    fn encode_to_cell(self) -> EncodeResult<(BoxedCellBody, usize)> {
576        // NOTE: This implementation is a bit optimized, since it happens to
577        // literally every relay cell that we produce.
578
579        // TODO -NM: Add a specialized implementation for making a DATA cell from
580        // a body?
581
582        /// Wrap a BoxedCellBody and implement AsMut<[u8]>
583        struct BodyWrapper(BoxedCellBody);
584        impl AsMut<[u8]> for BodyWrapper {
585            fn as_mut(&mut self) -> &mut [u8] {
586                self.0.as_mut()
587            }
588        }
589        /// The position of the length field within a relay cell.
590        const LEN_POS: usize = 9;
591        /// The position of the body a relay cell.
592        const BODY_POS: usize = 11;
593
594        let body = BodyWrapper(Box::new([0_u8; 509]));
595
596        let mut w = crate::slicewriter::SliceWriter::new(body);
597        w.write_u8(self.msg.cmd().into());
598        w.write_u16(0); // "Recognized"
599        debug_assert_eq!(
600            w.offset().expect("Overflowed a cell with just the header!"),
601            STREAM_ID_OFFSET
602        );
603        w.write_u16(StreamId::get_or_zero(self.streamid));
604        w.write_u32(0); // Digest
605                        // (It would be simpler to use NestedWriter at this point, but it uses an internal Vec that we are trying to avoid.)
606        debug_assert_eq!(
607            w.offset().expect("Overflowed a cell with just the header!"),
608            LEN_POS
609        );
610        w.write_u16(0); // Length.
611        debug_assert_eq!(
612            w.offset().expect("Overflowed a cell with just the header!"),
613            BODY_POS
614        );
615        self.msg.encode_onto(&mut w)?; // body
616        let (mut body, written) = w.try_unwrap().map_err(|_| {
617            EncodeError::Bug(internal!(
618                "Encoding of relay message was too long to fit into a cell!"
619            ))
620        })?;
621        let payload_len = written - BODY_POS;
622        debug_assert!(payload_len < u16::MAX as usize);
623        *(<&mut [u8; 2]>::try_from(&mut body.0[LEN_POS..LEN_POS + 2])
624            .expect("Two-byte slice was not two bytes long!?")) =
625            (payload_len as u16).to_be_bytes();
626        Ok((body.0, written))
627    }
628
629    /// Parse a RELAY or RELAY_EARLY cell body into a RelayMsgOuter.
630    /// Requires that the cryptographic checks on the message have already been
631    /// performed
632    ///
633    /// Fails if the cell doesn't contain exactly one message.
634    ///
635    /// Non-test code should generally use `RelayCellDecoder` instead.
636    // Ideally we'd make this `#[cfg(test)]`, but then we wouldn't be able
637    // to use it in integration tests.
638    // https://github.com/rust-lang/rust/issues/84629
639    #[allow(clippy::needless_pass_by_value)] // TODO this will go away soon.
640    pub fn decode_singleton(version: RelayCellFormat, body: BoxedCellBody) -> Result<Self> {
641        let unparsed_msg = UnparsedRelayMsg::from_singleton_body(version, body)?;
642        unparsed_msg.decode()
643    }
644    /// Parse a `RelayCellFormat::V0` RELAY or RELAY_EARLY cell body into a
645    /// RelayMsgOuter from a reader.
646    ///
647    /// Requires that the cryptographic checks on the message have already been
648    /// performed
649    fn decode_v0_from_reader(r: &mut Reader<'_>) -> Result<Self> {
650        let cmd = r.take_u8()?.into();
651        r.advance(2)?; // "recognized"
652        let streamid = StreamId::new(r.take_u16()?);
653        r.advance(4)?; // digest
654        let len = r.take_u16()? as usize;
655        if r.remaining() < len {
656            return Err(Error::InvalidMessage(
657                "Insufficient data in relay cell".into(),
658            ));
659        }
660        r.truncate(len);
661        let msg = M::decode_from_reader(cmd, r)?;
662        Ok(Self { streamid, msg })
663    }
664}