1
//! Link specifier objects
2
//!
3
//! (These are in a separate crate, since they get used both by
4
//! directory code and protocol code.)
5

            
6
use std::net::{IpAddr, SocketAddr};
7

            
8
use tor_bytes::{Error, Readable, Reader, Result, Writeable, Writer};
9
use tor_llcrypto::pk::ed25519;
10
use tor_llcrypto::pk::rsa::RsaIdentity;
11

            
12
/// A piece of information about a relay and how to connect to it.
13
#[non_exhaustive]
14
97
#[derive(Debug, Clone, PartialEq)]
15
pub enum LinkSpec {
16
    /// The TCP address of an OR Port for a relay
17
    OrPort(IpAddr, u16),
18
    /// The RSA identity fingerprint of the relay
19
    RsaId(RsaIdentity),
20
    /// The Ed25519 identity of the relay
21
    Ed25519Id(ed25519::Ed25519Identity),
22
    /// A link specifier that we didn't recognize
23
    Unrecognized(u8, Vec<u8>),
24
}
25

            
26
/// Indicates an IPv4 ORPORT link specifier.
27
const LSTYPE_ORPORT_V4: u8 = 0;
28
/// Indicates an IPv6 ORPORT link specifier.
29
const LSTYPE_ORPORT_V6: u8 = 1;
30
/// Indicates an RSA ID fingerprint link specifier
31
const LSTYPE_RSAID: u8 = 2;
32
/// Indicates an Ed25519 link specifier
33
const LSTYPE_ED25519ID: u8 = 3;
34

            
35
impl Readable for LinkSpec {
36
272
    fn take_from(r: &mut Reader<'_>) -> Result<Self> {
37
        /// Return the expected length of the link specifier whose type is tp.
38
272
        fn lstype_len(tp: u8) -> Option<usize> {
39
272
            match tp {
40
47
                LSTYPE_ORPORT_V4 => Some(6),
41
1
                LSTYPE_ORPORT_V6 => Some(18),
42
133
                LSTYPE_RSAID => Some(20),
43
89
                LSTYPE_ED25519ID => Some(32),
44
2
                _ => None,
45
            }
46
272
        }
47
272
        let lstype = r.take_u8()?;
48
272
        let lslen = r.take_u8()? as usize;
49
272
        if let Some(wantlen) = lstype_len(lstype) {
50
270
            if wantlen != lslen {
51
1
                return Err(Error::BadMessage("Wrong length for link specifier"));
52
269
            }
53
2
        }
54
271
        Ok(match lstype {
55
            LSTYPE_ORPORT_V4 => {
56
46
                let addr = IpAddr::V4(r.extract()?);
57
46
                LinkSpec::OrPort(addr, r.take_u16()?)
58
            }
59
            LSTYPE_ORPORT_V6 => {
60
1
                let addr = IpAddr::V6(r.extract()?);
61
1
                LinkSpec::OrPort(addr, r.take_u16()?)
62
            }
63
133
            LSTYPE_RSAID => LinkSpec::RsaId(r.extract()?),
64
89
            LSTYPE_ED25519ID => LinkSpec::Ed25519Id(r.extract()?),
65
2
            _ => LinkSpec::Unrecognized(lstype, r.take(lslen)?.into()),
66
        })
67
272
    }
68
}
69
impl Writeable for LinkSpec {
70
973
    fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) {
71
        use LinkSpec::*;
72
46
        match self {
73
45
            OrPort(IpAddr::V4(v4), port) => {
74
45
                w.write_u8(LSTYPE_ORPORT_V4);
75
45
                w.write_u8(6); // Length
76
45
                w.write(v4);
77
45
                w.write_u16(*port);
78
45
            }
79
1
            OrPort(IpAddr::V6(v6), port) => {
80
1
                w.write_u8(LSTYPE_ORPORT_V6);
81
1
                w.write_u8(18); // Length
82
1
                w.write(v6);
83
1
                w.write_u16(*port);
84
1
            }
85
485
            RsaId(r) => {
86
485
                w.write_u8(LSTYPE_RSAID);
87
485
                w.write_u8(20); // Length
88
485
                w.write(r);
89
485
            }
90
441
            Ed25519Id(e) => {
91
441
                w.write_u8(LSTYPE_ED25519ID);
92
441
                w.write_u8(32); // Length
93
441
                w.write(e);
94
441
            }
95
1
            Unrecognized(tp, vec) => {
96
1
                w.write_u8(*tp);
97
1
                assert!(vec.len() < std::u8::MAX as usize);
98
1
                w.write_u8(vec.len() as u8);
99
1
                w.write_all(&vec[..]);
100
            }
101
        }
102
973
    }
103
}
104

            
105
impl From<&SocketAddr> for LinkSpec {
106
24
    fn from(sa: &SocketAddr) -> Self {
107
24
        LinkSpec::OrPort(sa.ip(), sa.port())
108
24
    }
109
}
110
impl From<SocketAddr> for LinkSpec {
111
22
    fn from(sa: SocketAddr) -> Self {
112
22
        (&sa).into()
113
22
    }
114
}
115
impl From<RsaIdentity> for LinkSpec {
116
463
    fn from(id: RsaIdentity) -> Self {
117
463
        LinkSpec::RsaId(id)
118
463
    }
119
}
120
impl From<ed25519::Ed25519Identity> for LinkSpec {
121
441
    fn from(id: ed25519::Ed25519Identity) -> Self {
122
441
        LinkSpec::Ed25519Id(id)
123
441
    }
124
}
125
impl From<ed25519::PublicKey> for LinkSpec {
126
    fn from(pk: ed25519::PublicKey) -> Self {
127
        LinkSpec::Ed25519Id(pk.into())
128
    }
129
}
130

            
131
impl LinkSpec {
132
    /// Helper: return the position in the list of identifiers
133
    /// in which a given linkspec should occur.
134
    fn sort_pos(&self) -> u8 {
135
        use LinkSpec::*;
136
22
        match self {
137
22
            OrPort(IpAddr::V4(_), _) => 0,
138
462
            RsaId(_) => 1,
139
440
            Ed25519Id(_) => 2,
140
            OrPort(IpAddr::V6(_), _) => 3,
141
            Unrecognized(n, _) => *n,
142
        }
143
924
    }
144

            
145
    /// Sort a slice of LinkSpec based on the order in which they should
146
    /// appear in an EXTEND cell.
147
462
    pub fn sort_by_type(lst: &mut [Self]) {
148
462
        lst.sort_by_key(LinkSpec::sort_pos);
149
462
    }
150
}
151

            
152
#[cfg(test)]
153
mod test {
154
    #![allow(clippy::unwrap_used)]
155
    use super::*;
156
    use hex_literal::hex;
157
    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
158
    use tor_bytes::{Reader, Writer};
159

            
160
    #[test]
161
    fn test_parse_enc() {
162
        fn t(b: &[u8], val: &LinkSpec) {
163
            let mut r = Reader::from_slice(b);
164
            let got: LinkSpec = r.extract().unwrap();
165
            assert_eq!(r.remaining(), 0);
166
            assert_eq!(&got, val);
167
            let mut v = Vec::new();
168
            v.write(val);
169
            assert_eq!(&v[..], b);
170
        }
171

            
172
        t(
173
            &hex!("00 06 01020304 0050"),
174
            &LinkSpec::OrPort(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 80),
175
        );
176
        t(
177
            &hex!("01 12 0001 0002 0003 0004 0005 0006 0007 0008 01bb"),
178
            &LinkSpec::OrPort(IpAddr::V6(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), 443),
179
        );
180
        t(
181
            &[
182
                2, 20, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 33, 33, 33, 33,
183
                33, 33, 33, 33,
184
            ],
185
            &LinkSpec::RsaId(RsaIdentity::from_bytes(b"hello world!!!!!!!!!").unwrap()),
186
        );
187
        let key = ed25519::PublicKey::from_bytes(&hex!(
188
            "B440EEDB32D5C89EF21D6B16BE85A658774CE5992355737411678EE1041BDFBA"
189
        ))
190
        .unwrap()
191
        .into();
192
        t(
193
            &hex!("03 20 B440EEDB32D5C89EF21D6B16BE85A658774CE5992355737411678EE1041BDFBA"),
194
            &LinkSpec::Ed25519Id(key),
195
        );
196

            
197
        t(
198
            &[77, 7, 115, 116, 114, 97, 110, 103, 101],
199
            &LinkSpec::Unrecognized(77, (&b"strange"[..]).into()),
200
        );
201
    }
202

            
203
    #[test]
204
    fn test_parse_bad() {
205
        use tor_bytes::Error;
206

            
207
        fn t(b: &[u8]) -> Error {
208
            let mut r = Reader::from_slice(b);
209
            let got: Result<LinkSpec> = r.extract();
210
            got.err().unwrap()
211
        }
212

            
213
        assert!(matches!(t(&hex!("00 03")), Error::BadMessage(_)));
214
        assert!(matches!(t(&hex!("00 06 01020304")), Error::Truncated));
215
        assert!(matches!(t(&hex!("99 07 010203")), Error::Truncated));
216
    }
217
}