tor_cell/chancell/codec.rs
1//! Implementation for encoding and decoding of ChanCells.
2
3use super::{ChanCell, CELL_DATA_LEN};
4use crate::chancell::{ChanCmd, ChanMsg, CircId};
5use crate::Error;
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 /// Write the given cell into the provided BytesMut object.
51 pub fn write_cell<M: ChanMsg>(
52 &mut self,
53 item: ChanCell<M>,
54 dst: &mut BytesMut,
55 ) -> crate::Result<()> {
56 let ChanCell { circid, msg } = item;
57 let cmd = msg.cmd();
58 dst.write_u32(CircId::get_or_zero(circid));
59 dst.write_u8(cmd.into());
60
61 let pos = dst.len(); // always 5?
62
63 // now write the cell body and handle the length.
64 if cmd.is_var_cell() {
65 dst.write_u16(0);
66 msg.encode_onto(dst)?;
67 let len = dst.len() - pos - 2;
68 if len > u16::MAX as usize {
69 return Err(Error::Internal(internal!("ran out of space for varcell")));
70 }
71 // go back and set the length.
72 *(<&mut [u8; 2]>::try_from(&mut dst[pos..pos + 2])
73 .expect("two-byte slice was not two bytes!?")) = (len as u16).to_be_bytes();
74 } else {
75 msg.encode_onto(dst)?;
76 let len = dst.len() - pos;
77 if len > CELL_DATA_LEN {
78 return Err(Error::Internal(internal!("ran out of space for cell")));
79 }
80 // pad to end of fixed-length cell
81 dst.write_zeros(CELL_DATA_LEN - len);
82 }
83 Ok(())
84 }
85
86 /// Try to decode a cell from the provided BytesMut object.
87 ///
88 /// On a definite decoding error, return Err(_). On a cell that might
89 /// just be truncated, return Ok(None).
90 pub fn decode_cell<M: ChanMsg>(
91 &mut self,
92 src: &mut BytesMut,
93 ) -> crate::Result<Option<ChanCell<M>>> {
94 /// Wrap `be` as an appropriate type.
95 fn wrap_err(be: tor_bytes::Error) -> crate::Error {
96 crate::Error::BytesErr {
97 err: be,
98 parsed: "channel cell",
99 }
100 }
101
102 if src.len() < 7 {
103 // Smallest possible command: varcell with len 0
104 return Ok(None);
105 }
106 let cmd: ChanCmd = src[4].into();
107 let varcell = cmd.is_var_cell();
108 let cell_len: usize = if varcell {
109 let msg_len = u16::from_be_bytes(
110 src[5..7]
111 .try_into()
112 .expect("Two-byte slice was not two bytes long!?"),
113 );
114 msg_len as usize + 7
115 } else {
116 514
117 };
118 if src.len() < cell_len {
119 return Ok(None);
120 }
121
122 let cell = src.split_to(cell_len).freeze();
123 //trace!("{:?} cell body ({}) is {:?}", cmd, cell.len(), &cell[..]);
124 let mut r = Reader::from_bytes(&cell);
125 let circid: Option<CircId> = CircId::new(r.take_u32().map_err(wrap_err)?);
126 r.advance(if varcell { 3 } else { 1 }).map_err(wrap_err)?;
127 let msg = M::decode_from_reader(cmd, &mut r).map_err(wrap_err)?;
128
129 if !cmd.accepts_circid_val(circid) {
130 return Err(Error::ChanProto(format!(
131 "Invalid circuit ID {} for cell command {}",
132 CircId::get_or_zero(circid),
133 cmd
134 )));
135 }
136 Ok(Some(ChanCell { circid, msg }))
137 }
138}