summaryrefslogtreecommitdiff
path: root/crates/tor-cell/tests/test_chancell.rs
blob: 6d4ee1e6bfca6a2d97d0d7870bb29949edfa78b3 (plain)
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// Test for encoding/decoding channel cells.
//
// Reminder: you can think of a cell as an message plus a circuitid.
#![allow(clippy::uninlined_format_args)]

use tor_cell::Error;
use tor_cell::chancell::msg::AnyChanMsg;
use tor_cell::chancell::{AnyChanCell, ChanCmd, ChanMsg, CircId, codec, msg};

use bytes::BytesMut;
use hex_literal::hex;

const FIXED_BODY_LEN: usize = 514;

fn decode(body: &str, pad_body: bool) -> Vec<u8> {
    let mut body = body.to_string();
    body.retain(|c| !c.is_whitespace());
    let mut body = hex::decode(body).unwrap();
    if pad_body {
        body.resize(FIXED_BODY_LEN, 0);
    }
    body
}

fn cell(body: &str, msg: msg::AnyChanMsg, id: Option<CircId>, pad_body: bool) {
    let body = decode(body, pad_body);

    let cell = AnyChanCell::new(id, msg);
    let mut codec = codec::ChannelCodec::new(4);

    let decoded = {
        let mut bm = BytesMut::new();
        bm.extend_from_slice(&body[..]);
        bm.extend_from_slice(&b"next thing"[..]);
        let decoded = codec.decode_cell::<AnyChanMsg>(&mut bm).unwrap();
        assert_eq!(bm.len(), 10);
        decoded.unwrap()
    };

    let decoded2 = {
        let mut bm = BytesMut::new();
        bm.extend_from_slice(&body[..]);
        // no extra bytes this time.
        let decoded = codec.decode_cell::<AnyChanMsg>(&mut bm).unwrap();
        assert_eq!(bm.len(), 0);
        decoded.unwrap()
    };

    assert_eq!(format!("{:?}", decoded), format!("{:?}", cell));
    assert_eq!(format!("{:?}", decoded2), format!("{:?}", cell));

    let mut encoded1 = BytesMut::new();
    let mut encoded2 = BytesMut::new();
    codec.write_cell(cell, &mut encoded1).unwrap();
    codec.write_cell(decoded, &mut encoded2).unwrap();

    assert_eq!(encoded1, encoded2);
    assert_eq!(encoded1, body);
}

fn fcell(body: &str, msg: msg::AnyChanMsg, id: Option<CircId>) {
    cell(body, msg, id, true);
}

fn vcell(body: &str, msg: msg::AnyChanMsg, id: Option<CircId>) {
    cell(body, msg, id, false);
}

#[test]
fn test_simple_cells() {
    fcell("", msg::Padding::new().into(), None);

    vcell(
        "12345678 ff 0019 7765206c697374656e20726f756e642074686520636c6f636b",
        msg::Unrecognized::new(255.into(), &b"we listen round the clock"[..]).into(),
        CircId::new(0x12345678),
    );

    fcell(
        "20201122 03 666f72206120636f64652063616c6c656420706561636574696d65",
        msg::Relay::new(b"for a code called peacetime").into(),
        CircId::new(0x20201122),
    );

    // Now try some accessors.
    let m = decode(
        "20201122 03 666f72206120636f64652063616c6c656420706561636574696d65",
        true,
    );
    let cell = {
        let mut bm = BytesMut::new();
        bm.extend_from_slice(&m);
        codec::ChannelCodec::new(4)
            .decode_cell::<AnyChanMsg>(&mut bm)
            .unwrap()
            .unwrap()
    };
    assert_eq!(cell.circid(), CircId::new(0x20201122));
    assert_eq!(cell.msg().cmd(), ChanCmd::RELAY);
    let (id, msg) = cell.into_circid_and_msg();
    assert_eq!(id, CircId::new(0x20201122));
    assert_eq!(msg.cmd(), ChanCmd::RELAY);
}

fn short_cell(body: &str) {
    let body = decode(body, false);

    let mut codec = codec::ChannelCodec::new(4);

    let mut bm = BytesMut::new();
    bm.extend_from_slice(&body[..]);
    let len_orig = bm.len();
    let d = codec.decode_cell::<AnyChanMsg>(&mut bm);
    assert!(d.unwrap().is_none()); // "Ok(None)" means truncated.
    assert_eq!(bm.len(), len_orig);
}

#[test]
fn test_truncated_cells() {
    // short relay (fixed)
    short_cell("00000001 03 1234");
    short_cell("00000001 03 12");
    short_cell("00000001 03 ");
    short_cell("00000001");
    short_cell("000000");
    short_cell("");

    // short certs (varlen)
    short_cell("00000000 81 0003 1234");
    short_cell("00000000 81 0003 12");
    short_cell("00000000 81 0003 ");
    short_cell("00000000 81 00 ");
    short_cell("00000000 81 ");
}

fn bad_cell(body: &str, err: Error, pad_body: bool) {
    let body = decode(body, pad_body);

    let mut codec = codec::ChannelCodec::new(4);

    let decoded = {
        let mut bm = BytesMut::new();
        bm.extend_from_slice(&body[..]);
        bm.extend_from_slice(&b"next thing"[..]);
        codec.decode_cell::<AnyChanMsg>(&mut bm).err().unwrap()
    };

    assert_eq!(format!("{:?}", decoded), format!("{:?}", err));
}

#[test]
fn bad_circid_cells() {
    // relay, no circid
    bad_cell(
        "00000000 03 123456",
        Error::ChanProto("Invalid circuit ID 0 for cell command RELAY".into()),
        true,
    );

    // relay, unexpected circid
    bad_cell(
        "00000010 08 123456",
        Error::ChanProto("Invalid circuit ID 16 for cell command NETINFO".into()),
        true,
    );
}

#[test]
fn versions() {
    // Test the special encoding of the versions cell.  (It's special
    // because it uses a 2-byte circid.
    let v = msg::Versions::new([4, 5, 6]).unwrap();
    let encoded = v.clone().encode_for_handshake().unwrap();
    assert_eq!(encoded, hex!("0000 07 0006 0004 0005 0006"));

    // Test the best_shared_protocol function.
    assert_eq!(v.best_shared_link_protocol(&[1, 2, 3, 77]), None);
    assert_eq!(v.best_shared_link_protocol(&[]), None);
    assert_eq!(v.best_shared_link_protocol(&[4, 5, 6, 7]), Some(6));
    assert_eq!(v.best_shared_link_protocol(&[4, 5, 11]), Some(5));
    assert_eq!(v.best_shared_link_protocol(&[4, 5]), Some(5));

    // Try converting into a ChanCell.
    let cc: AnyChanCell = v.into();
    assert_eq!(cc.circid(), None);
}