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