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
use super::CELL_DATA_LEN;
use crate::chancell::{msg, ChanCell, ChanCmd, CircId};
use crate::Error;
use arrayref::{array_mut_ref, array_ref};
use tor_bytes::{self, Reader, Writer};
use bytes::BytesMut;
pub struct ChannelCodec {
#[allow(dead_code)]
link_version: u16,
}
impl ChannelCodec {
pub fn new(link_version: u16) -> Self {
ChannelCodec { link_version }
}
pub fn write_cell(&mut self, item: ChanCell, dst: &mut BytesMut) -> crate::Result<()> {
let ChanCell { circid, msg } = item;
let cmd = msg.cmd();
dst.write_u32(circid.into());
dst.write_u8(cmd.into());
let pos = dst.len();
if cmd.is_var_cell() {
dst.write_u16(0);
msg.write_body_onto(dst);
let len = dst.len() - pos - 2;
if len > std::u16::MAX as usize {
return Err(Error::InternalError("ran out of space for varcell".into()));
}
*(array_mut_ref![&mut dst[pos..pos + 2], 0, 2]) = (len as u16).to_be_bytes();
} else {
msg.write_body_onto(dst);
let len = dst.len() - pos;
if len > CELL_DATA_LEN {
return Err(Error::InternalError("ran out of space for cell".into()));
}
dst.write_zeros(CELL_DATA_LEN - len);
}
Ok(())
}
pub fn decode_cell(&mut self, src: &mut BytesMut) -> crate::Result<Option<ChanCell>> {
if src.len() < 7 {
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(*array_ref![&src[5..7], 0, 2]);
msg_len as usize + 7
} else {
514
};
if src.len() < cell_len {
return Ok(None);
}
let cell = src.split_to(cell_len).freeze();
let mut r = Reader::from_bytes(&cell);
let circid: CircId = r.take_u32()?.into();
r.advance(if varcell { 3 } else { 1 })?;
let msg = msg::ChanMsg::take(&mut r, cmd)?;
if !cmd.accepts_circid_val(circid) {
return Err(Error::ChanProto(format!(
"Invalid circuit ID {} for cell command {}",
circid, cmd
)));
}
Ok(Some(ChanCell { circid, msg }))
}
}