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