1
//! Implementation for encoding and decoding of ChanCells.
2

            
3
use super::{CELL_DATA_LEN, ChanCell};
4
use crate::Error;
5
use crate::chancell::{ChanCmd, ChanMsg, CircId};
6
use tor_bytes::{self, Reader, Writer};
7
use tor_error::internal;
8

            
9
use 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
/// ```
36
pub 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

            
46
impl ChannelCodec {
47
    /// Create a new ChannelCodec with a given link protocol version
48
5481
    pub fn new(link_version: u16) -> Self {
49
5481
        ChannelCodec { link_version }
50
5481
    }
51

            
52
    /// Return the link protocol version of this codec.
53
882
    pub fn link_version(&self) -> u16 {
54
882
        self.link_version
55
882
    }
56

            
57
    /// Write the given cell into the provided BytesMut object.
58
68
    pub fn write_cell<M: ChanMsg>(
59
68
        &mut self,
60
68
        item: ChanCell<M>,
61
68
        dst: &mut BytesMut,
62
68
    ) -> crate::Result<()> {
63
68
        let ChanCell { circid, msg } = item;
64
68
        let cmd = msg.cmd();
65
68
        dst.write_u32(CircId::get_or_zero(circid));
66
68
        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
68
        let pos = dst.len();
71

            
72
        // now write the cell body and handle the length.
73
68
        if cmd.is_var_cell() {
74
8
            dst.write_u16(0);
75
8
            msg.encode_onto(dst)?;
76
8
            let len = dst.len() - pos - 2;
77
8
            if len > u16::MAX as usize {
78
                return Err(Error::Internal(internal!("ran out of space for varcell")));
79
8
            }
80
            // go back and set the length.
81
8
            *(<&mut [u8; 2]>::try_from(&mut dst[pos..pos + 2])
82
8
                .expect("two-byte slice was not two bytes!?")) = (len as u16).to_be_bytes();
83
        } else {
84
60
            msg.encode_onto(dst)?;
85
60
            let len = dst.len() - pos;
86
60
            if len > CELL_DATA_LEN {
87
                return Err(Error::Internal(internal!("ran out of space for cell")));
88
60
            }
89
            // pad to end of fixed-length cell
90
60
            dst.write_zeros(CELL_DATA_LEN - len);
91
        }
92
68
        Ok(())
93
68
    }
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
892
    pub fn decode_cell<M: ChanMsg>(
103
892
        &mut self,
104
892
        src: &mut BytesMut,
105
892
    ) -> crate::Result<Option<(ChanCell<M>, Bytes)>> {
106
        /// Wrap `be` as an appropriate type.
107
252
        fn wrap_err(be: tor_bytes::Error) -> crate::Error {
108
252
            crate::Error::BytesErr {
109
252
                err: be,
110
252
                parsed: "channel cell",
111
252
            }
112
252
        }
113

            
114
892
        if src.len() < 7 {
115
            // Smallest possible command: varcell with len 0
116
173
            return Ok(None);
117
719
        }
118
719
        let cmd: ChanCmd = src[4].into();
119
719
        let varcell = cmd.is_var_cell();
120
719
        let cell_len: usize = if varcell {
121
495
            let msg_len = u16::from_be_bytes(
122
495
                src[5..7]
123
495
                    .try_into()
124
495
                    .expect("Two-byte slice was not two bytes long!?"),
125
            );
126
495
            msg_len as usize + 7
127
        } else {
128
224
            514
129
        };
130
719
        if src.len() < cell_len {
131
504
            return Ok(None);
132
215
        }
133

            
134
215
        let cell = src.split_to(cell_len).freeze();
135
215
        let mut r = Reader::from_bytes(&cell);
136
215
        let circid: Option<CircId> = CircId::new(r.take_u32().map_err(wrap_err)?);
137
215
        r.advance(if varcell { 3 } else { 1 }).map_err(wrap_err)?;
138
215
        let msg = M::decode_from_reader(cmd, &mut r).map_err(wrap_err)?;
139

            
140
211
        if !cmd.accepts_circid_val(circid) {
141
2
            return Err(Error::ChanProto(format!(
142
2
                "Invalid circuit ID {} for cell command {}",
143
2
                CircId::get_or_zero(circid),
144
2
                cmd
145
2
            )));
146
209
        }
147
209
        Ok(Some((ChanCell { circid, msg }, cell)))
148
892
    }
149
}