Skip to main content

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