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}