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}