tor_cell/chancell/
codec.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! Implementation for encoding and decoding of ChanCells.

use super::{ChanCell, CELL_DATA_LEN};
use crate::chancell::{ChanCmd, ChanMsg, CircId};
use crate::Error;
use tor_bytes::{self, Reader, Writer};
use tor_error::internal;

use bytes::BytesMut;

/// This object can be used to encode and decode channel cells.
///
/// NOTE: only link protocol versions 3 and higher are supported.
/// VERSIONS cells are not supported via the encoder/decoder, since
/// VERSIONS cells always use a two-byte circuit-ID for backwards
/// compatibility with protocol versions < 4.
///
/// The implemented format is one of the following:
///
/// Variable-length cells (since protocol versions 2 and 3 respectively):
/// ```ignore
///     u32 circid;
///     u8 command;
///     u16 len;
///     u8 body[len];
/// ```
///
/// Fixed-width cells (since protocol version 1 and 4 respectively):
/// ```ignore
///     u32 circid;
///     u8 command;
///     u8 body[509];
/// ```
pub struct ChannelCodec {
    #[allow(dead_code)] // We don't support any link versions where this matters
    /// The link protocol version being used for this channel.
    ///
    /// (We don't currently support any versions of the link protocol
    /// where this version matters, but for protocol versions below 4, it would
    /// affect the length of the circuit ID.)
    link_version: u16,
}

impl ChannelCodec {
    /// Create a new ChannelCodec with a given link protocol version
    pub fn new(link_version: u16) -> Self {
        ChannelCodec { link_version }
    }

    /// Write the given cell into the provided BytesMut object.
    pub fn write_cell<M: ChanMsg>(
        &mut self,
        item: ChanCell<M>,
        dst: &mut BytesMut,
    ) -> crate::Result<()> {
        let ChanCell { circid, msg } = item;
        let cmd = msg.cmd();
        dst.write_u32(CircId::get_or_zero(circid));
        dst.write_u8(cmd.into());

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

        // now write the cell body and handle the length.
        if cmd.is_var_cell() {
            dst.write_u16(0);
            msg.encode_onto(dst)?;
            let len = dst.len() - pos - 2;
            if len > u16::MAX as usize {
                return Err(Error::Internal(internal!("ran out of space for varcell")));
            }
            // go back and set the length.
            *(<&mut [u8; 2]>::try_from(&mut dst[pos..pos + 2])
                .expect("two-byte slice was not two bytes!?")) = (len as u16).to_be_bytes();
        } else {
            msg.encode_onto(dst)?;
            let len = dst.len() - pos;
            if len > CELL_DATA_LEN {
                return Err(Error::Internal(internal!("ran out of space for cell")));
            }
            // pad to end of fixed-length cell
            dst.write_zeros(CELL_DATA_LEN - len);
        }
        Ok(())
    }

    /// Try to decode a cell from the provided BytesMut object.
    ///
    /// On a definite decoding error, return Err(_).  On a cell that might
    /// just be truncated, return Ok(None).
    pub fn decode_cell<M: ChanMsg>(
        &mut self,
        src: &mut BytesMut,
    ) -> crate::Result<Option<ChanCell<M>>> {
        /// Wrap `be` as an appropriate type.
        fn wrap_err(be: tor_bytes::Error) -> crate::Error {
            crate::Error::BytesErr {
                err: be,
                parsed: "channel cell",
            }
        }

        if src.len() < 7 {
            // Smallest possible command: varcell with len 0
            return Ok(None);
        }
        let cmd: ChanCmd = src[4].into();
        let varcell = cmd.is_var_cell();
        let cell_len: usize = if varcell {
            let msg_len = u16::from_be_bytes(
                src[5..7]
                    .try_into()
                    .expect("Two-byte slice was not two bytes long!?"),
            );
            msg_len as usize + 7
        } else {
            514
        };
        if src.len() < cell_len {
            return Ok(None);
        }

        let cell = src.split_to(cell_len).freeze();
        //trace!("{:?} cell body ({}) is {:?}", cmd, cell.len(), &cell[..]);
        let mut r = Reader::from_bytes(&cell);
        let circid: Option<CircId> = CircId::new(r.take_u32().map_err(wrap_err)?);
        r.advance(if varcell { 3 } else { 1 }).map_err(wrap_err)?;
        let msg = M::decode_from_reader(cmd, &mut r).map_err(wrap_err)?;

        if !cmd.accepts_circid_val(circid) {
            return Err(Error::ChanProto(format!(
                "Invalid circuit ID {} for cell command {}",
                CircId::get_or_zero(circid),
                cmd
            )));
        }
        Ok(Some(ChanCell { circid, msg }))
    }
}