tor_cell/chancell/codec.rs
1//! Implementation for encoding and decoding of ChanCells.
2
3use super::{CELL_DATA_LEN, ChanCell};
4use crate::Error;
5use crate::chancell::{ChanCmd, ChanMsg, CircId};
6use tor_bytes::{self, Reader, Writer};
7use tor_error::internal;
8
9use bytes::BytesMut;
10
11/// This object can be used to encode and decode channel cells.
12///
13/// NOTE: only link protocol versions 3 and higher are supported.
14/// VERSIONS cells are not supported via the encoder/decoder, since
15/// VERSIONS cells always use a two-byte circuit-ID for backwards
16/// compatibility with protocol versions < 4.
17///
18/// The implemented format is one of the following:
19///
20/// Variable-length cells (since protocol versions 2 and 3 respectively):
21/// ```ignore
22/// u32 circid;
23/// u8 command;
24/// u16 len;
25/// u8 body[len];
26/// ```
27///
28/// Fixed-width cells (since protocol version 1 and 4 respectively):
29/// ```ignore
30/// u32 circid;
31/// u8 command;
32/// u8 body[509];
33/// ```
34pub struct ChannelCodec {
35 #[allow(dead_code)] // We don't support any link versions where this matters
36 /// The link protocol version being used for this channel.
37 ///
38 /// (We don't currently support any versions of the link protocol
39 /// where this version matters, but for protocol versions below 4, it would
40 /// affect the length of the circuit ID.)
41 link_version: u16,
42}
43
44impl ChannelCodec {
45 /// Create a new ChannelCodec with a given link protocol version
46 pub fn new(link_version: u16) -> Self {
47 ChannelCodec { link_version }
48 }
49
50 /// Return the link protocol version of this codec.
51 pub fn link_version(&self) -> u16 {
52 self.link_version
53 }
54
55 /// Write the given cell into the provided BytesMut object.
56 pub fn write_cell<M: ChanMsg>(
57 &mut self,
58 item: ChanCell<M>,
59 dst: &mut BytesMut,
60 ) -> crate::Result<()> {
61 let ChanCell { circid, msg } = item;
62 let cmd = msg.cmd();
63 dst.write_u32(CircId::get_or_zero(circid));
64 dst.write_u8(cmd.into());
65
66 // this is typically 5, but not always
67 // (for example if we were given a non-empty `dst`)
68 let pos = dst.len();
69
70 // now write the cell body and handle the length.
71 if cmd.is_var_cell() {
72 dst.write_u16(0);
73 msg.encode_onto(dst)?;
74 let len = dst.len() - pos - 2;
75 if len > u16::MAX as usize {
76 return Err(Error::Internal(internal!("ran out of space for varcell")));
77 }
78 // go back and set the length.
79 *(<&mut [u8; 2]>::try_from(&mut dst[pos..pos + 2])
80 .expect("two-byte slice was not two bytes!?")) = (len as u16).to_be_bytes();
81 } else {
82 msg.encode_onto(dst)?;
83 let len = dst.len() - pos;
84 if len > CELL_DATA_LEN {
85 return Err(Error::Internal(internal!("ran out of space for cell")));
86 }
87 // pad to end of fixed-length cell
88 dst.write_zeros(CELL_DATA_LEN - len);
89 }
90 Ok(())
91 }
92
93 /// Try to decode a cell from the provided BytesMut object.
94 ///
95 /// On a definite decoding error, return Err(_). On a cell that might
96 /// just be truncated, return Ok(None).
97 pub fn decode_cell<M: ChanMsg>(
98 &mut self,
99 src: &mut BytesMut,
100 ) -> crate::Result<Option<ChanCell<M>>> {
101 /// Wrap `be` as an appropriate type.
102 fn wrap_err(be: tor_bytes::Error) -> crate::Error {
103 crate::Error::BytesErr {
104 err: be,
105 parsed: "channel cell",
106 }
107 }
108
109 if src.len() < 7 {
110 // Smallest possible command: varcell with len 0
111 return Ok(None);
112 }
113 let cmd: ChanCmd = src[4].into();
114 let varcell = cmd.is_var_cell();
115 let cell_len: usize = if varcell {
116 let msg_len = u16::from_be_bytes(
117 src[5..7]
118 .try_into()
119 .expect("Two-byte slice was not two bytes long!?"),
120 );
121 msg_len as usize + 7
122 } else {
123 514
124 };
125 if src.len() < cell_len {
126 return Ok(None);
127 }
128
129 let cell = src.split_to(cell_len).freeze();
130 //trace!("{:?} cell body ({}) is {:?}", cmd, cell.len(), &cell[..]);
131 let mut r = Reader::from_bytes(&cell);
132 let circid: Option<CircId> = CircId::new(r.take_u32().map_err(wrap_err)?);
133 r.advance(if varcell { 3 } else { 1 }).map_err(wrap_err)?;
134 let msg = M::decode_from_reader(cmd, &mut r).map_err(wrap_err)?;
135
136 if !cmd.accepts_circid_val(circid) {
137 return Err(Error::ChanProto(format!(
138 "Invalid circuit ID {} for cell command {}",
139 CircId::get_or_zero(circid),
140 cmd
141 )));
142 }
143 Ok(Some(ChanCell { circid, msg }))
144 }
145}