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