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

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

            
10
use bytes::BytesMut;
11

            
12
/// This object can be used to encode and decode channel cells.
13
///
14
/// NOTE: only link protocol versions 3 and higher are supported.
15
/// VERSIONS cells are not supported via the encoder/decoder, since it
16
/// VERSIONS always uses a two-byte circuit-ID.
17
///
18
/// The implemented format is one of the following:
19
///
20
/// ```ignore
21
///     u32 circid;
22
///     u8 command;
23
///     u16 len;
24
///     u8 body[len];
25
/// ```
26
///
27
/// ```ignore
28
///     u32 circid;
29
///     u8 command;
30
///     u8 body[509];
31
/// ```
32
pub struct ChannelCodec {
33
    #[allow(dead_code)] // We don't support any link versions where this matters
34
    /// The link protocol version being used for this channel.
35
    ///
36
    /// (We don't currently support any versions of the link protocol
37
    /// where this version matters, but for some older versions, it would
38
    /// affect the length of the circuit ID.)
39
    link_version: u16,
40
}
41

            
42
impl ChannelCodec {
43
    /// Create a new ChannelCodec with a given link protocol version
44
855
    pub fn new(link_version: u16) -> Self {
45
855
        ChannelCodec { link_version }
46
855
    }
47

            
48
    /// Write the given cell into the provided BytesMut object.
49
304
    pub fn write_cell(&mut self, item: ChanCell, dst: &mut BytesMut) -> crate::Result<()> {
50
304
        let ChanCell { circid, msg } = item;
51
304
        let cmd = msg.cmd();
52
304
        dst.write_u32(circid.into());
53
304
        dst.write_u8(cmd.into());
54
304

            
55
304
        let pos = dst.len(); // always 5?
56
304

            
57
304
        // now write the cell body and handle the length.
58
304
        if cmd.is_var_cell() {
59
114
            dst.write_u16(0);
60
114
            msg.write_body_onto(dst);
61
114
            let len = dst.len() - pos - 2;
62
114
            if len > std::u16::MAX as usize {
63
                return Err(Error::Internal(internal!("ran out of space for varcell")));
64
114
            }
65
114
            // go back and set the length.
66
114
            *(array_mut_ref![&mut dst[pos..pos + 2], 0, 2]) = (len as u16).to_be_bytes();
67
        } else {
68
190
            msg.write_body_onto(dst);
69
190
            let len = dst.len() - pos;
70
190
            if len > CELL_DATA_LEN {
71
                return Err(Error::Internal(internal!("ran out of space for cell")));
72
190
            }
73
190
            // pad to end of fixed-length cell
74
190
            dst.write_zeros(CELL_DATA_LEN - len);
75
        }
76
304
        Ok(())
77
304
    }
78

            
79
    /// Try to decode a cell from the provided BytesMut object.
80
    ///
81
    /// On a definite decoding error, return Err(_).  On a cell that might
82
    /// just be truncated, return Ok(None).
83
1292
    pub fn decode_cell(&mut self, src: &mut BytesMut) -> crate::Result<Option<ChanCell>> {
84
1292
        if src.len() < 7 {
85
            // Smallest possible command: varcell with len 0
86
380
            return Ok(None);
87
912
        }
88
912
        let cmd: ChanCmd = src[4].into();
89
912
        let varcell = cmd.is_var_cell();
90
912
        let cell_len: usize = if varcell {
91
532
            let msg_len = u16::from_be_bytes(*array_ref![&src[5..7], 0, 2]);
92
532
            msg_len as usize + 7
93
        } else {
94
380
            514
95
        };
96
912
        if src.len() < cell_len {
97
266
            return Ok(None);
98
646
        }
99
646

            
100
646
        let cell = src.split_to(cell_len).freeze();
101
646
        //trace!("{:?} cell body ({}) is {:?}", cmd, cell.len(), &cell[..]);
102
646
        let mut r = Reader::from_bytes(&cell);
103
646
        let circid: CircId = r.take_u32()?.into();
104
646
        r.advance(if varcell { 3 } else { 1 })?;
105
646
        let msg = msg::ChanMsg::take(&mut r, cmd)?;
106

            
107
627
        if !cmd.accepts_circid_val(circid) {
108
38
            return Err(Error::ChanProto(format!(
109
38
                "Invalid circuit ID {} for cell command {}",
110
38
                circid, cmd
111
38
            )));
112
589
        }
113
589
        Ok(Some(ChanCell { circid, msg }))
114
1292
    }
115
}