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 caret::caret_int;
9
use derive_deftly::Deftly;
10
use tor_bytes::{EncodeResult, Readable, Reader, Result, Writeable, Writer};
11
use tor_llcrypto::pk::ed25519;
12
use tor_llcrypto::pk::rsa::RsaIdentity;
13
use tor_memquota::derive_deftly_template_HasMemoryCost;
14

            
15
use crate::RelayId;
16

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

            
32
caret_int! {
33
    /// A numeric identifier for the type of a [`LinkSpec`].
34
    #[derive(Deftly)]
35
    #[derive_deftly(HasMemoryCost)]
36
    pub struct LinkSpecType(u8) {
37
        /// Indicates an IPv4 ORPORT link specifier.
38
        ORPORT_V4 = 0,
39
        /// Indicates an IPv6 ORPORT link specifier.
40
        ORPORT_V6 = 1,
41
        /// Indicates an RSA ID fingerprint link specifier
42
        RSAID = 2,
43
        /// Indicates an Ed25519 link specifier
44
        ED25519ID = 3,
45
    }
46
}
47

            
48
impl Readable for LinkSpec {
49
16
    fn take_from(b: &mut Reader<'_>) -> Result<Self> {
50
16
        let lstype = b.take_u8()?.into();
51
21
        b.read_nested_u8len(|r| Self::from_type_and_body(lstype, r))
52
16
    }
53
}
54
impl Writeable for LinkSpec {
55
10
    fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) -> EncodeResult<()> {
56
10
        w.write_u8(self.lstype().into());
57
10
        {
58
10
            let mut inner = w.write_nested_u8len();
59
10
            self.encode_body(&mut *inner)?;
60
10
            inner.finish()?;
61
        }
62
10
        Ok(())
63
10
    }
64
}
65

            
66
impl From<&SocketAddr> for LinkSpec {
67
55
    fn from(sa: &SocketAddr) -> Self {
68
55
        LinkSpec::OrPort(sa.ip(), sa.port())
69
55
    }
70
}
71
impl From<SocketAddr> for LinkSpec {
72
55
    fn from(sa: SocketAddr) -> Self {
73
55
        (&sa).into()
74
55
    }
75
}
76
impl From<RsaIdentity> for LinkSpec {
77
49
    fn from(id: RsaIdentity) -> Self {
78
49
        LinkSpec::RsaId(id)
79
49
    }
80
}
81
impl From<ed25519::Ed25519Identity> for LinkSpec {
82
    fn from(id: ed25519::Ed25519Identity) -> Self {
83
        LinkSpec::Ed25519Id(id)
84
    }
85
}
86
impl From<ed25519::PublicKey> for LinkSpec {
87
    fn from(pk: ed25519::PublicKey) -> Self {
88
        LinkSpec::Ed25519Id(pk.into())
89
    }
90
}
91
impl From<RelayId> for LinkSpec {
92
2360
    fn from(id: RelayId) -> Self {
93
2360
        match id {
94
1180
            RelayId::Ed25519(key) => LinkSpec::Ed25519Id(key),
95
1180
            RelayId::Rsa(key) => LinkSpec::RsaId(key),
96
        }
97
2360
    }
98
}
99

            
100
impl LinkSpec {
101
    /// Helper: return the position in the list of identifiers
102
    /// in which a given linkspec should occur.
103
2380
    fn sort_pos(&self) -> u8 {
104
        use LinkSpec::*;
105
10
        match self {
106
8
            OrPort(IpAddr::V4(_), _) => 0,
107
1184
            RsaId(_) => 1,
108
1186
            Ed25519Id(_) => 2,
109
2
            OrPort(IpAddr::V6(_), _) => 3,
110
            Unrecognized(n, _) => (*n).into(),
111
        }
112
2380
    }
113

            
114
    /// Sort a slice of LinkSpec based on the order in which they should
115
    /// appear in an EXTEND cell.
116
1180
    pub fn sort_by_type(lst: &mut [Self]) {
117
1180
        lst.sort_by_key(LinkSpec::sort_pos);
118
1180
    }
119

            
120
    /// Try to create a LinkSpec of encoded type `lstype`, taking its body from a
121
    /// given reader `r`.
122
    ///
123
    /// Does not check whether `r` is exhausted at the end of the operation or not.
124
557
    fn from_type_and_body(lstype: LinkSpecType, r: &mut Reader<'_>) -> Result<Self> {
125
        use LinkSpecType as LST;
126
557
        Ok(match lstype {
127
            LST::ORPORT_V4 => {
128
151
                let addr = IpAddr::V4(r.extract()?);
129
151
                LinkSpec::OrPort(addr, r.take_u16()?)
130
            }
131
            LST::ORPORT_V6 => {
132
102
                let addr = IpAddr::V6(r.extract()?);
133
102
                LinkSpec::OrPort(addr, r.take_u16()?)
134
            }
135
151
            LST::RSAID => LinkSpec::RsaId(r.extract()?),
136
151
            LST::ED25519ID => LinkSpec::Ed25519Id(r.extract()?),
137
2
            _ => LinkSpec::Unrecognized(lstype, r.take_rest().into()),
138
        })
139
557
    }
140

            
141
    /// Return the command for this linkspec.
142
15312
    fn lstype(&self) -> LinkSpecType {
143
        use LinkSpecType as LST;
144
12897
        match self {
145
12893
            LinkSpec::OrPort(IpAddr::V4(_), _) => LST::ORPORT_V4,
146
4
            LinkSpec::OrPort(IpAddr::V6(_), _) => LST::ORPORT_V6,
147

            
148
1231
            LinkSpec::RsaId(_) => LST::RSAID,
149
1182
            LinkSpec::Ed25519Id(_) => LST::ED25519ID,
150
2
            LinkSpec::Unrecognized(lstype, _) => *lstype,
151
        }
152
15312
    }
153

            
154
    /// Try to encode the body of this linkspec onto a given writer.
155
15312
    fn encode_body<W: Writer + ?Sized>(&self, w: &mut W) -> EncodeResult<()> {
156
        use LinkSpec::*;
157
12897
        match self {
158
12893
            OrPort(IpAddr::V4(v4), port) => {
159
12893
                w.write(v4)?;
160
12893
                w.write_u16(*port);
161
            }
162
4
            OrPort(IpAddr::V6(v6), port) => {
163
4
                w.write(v6)?;
164
4
                w.write_u16(*port);
165
            }
166
1231
            RsaId(r) => {
167
1231
                w.write(r)?;
168
            }
169
1182
            Ed25519Id(e) => {
170
1182
                w.write(e)?;
171
            }
172
2
            Unrecognized(_, vec) => {
173
2
                w.write_all(&vec[..]);
174
2
            }
175
        }
176
15312
        Ok(())
177
15312
    }
178

            
179
    /// Return an encoded version of this link specifier.
180
15302
    pub fn encode(&self) -> EncodeResult<EncodedLinkSpec> {
181
15302
        let tp = self.lstype();
182
15302
        let mut body = Vec::new();
183
15302
        self.encode_body(&mut body)?;
184
15302
        Ok(EncodedLinkSpec::new(tp, body))
185
15302
    }
186
}
187

            
188
/// An unparsed piece of information about a relay and how to connect to it.
189
///
190
/// Unlike [`LinkSpec`], this can't be used directly; we only pass it on.
191
#[derive(Debug, Clone, PartialEq, Eq, Deftly)]
192
#[derive_deftly(HasMemoryCost)]
193
pub struct EncodedLinkSpec {
194
    /// The link specifier type.
195
    lstype: LinkSpecType,
196
    /// The body of the link speciier.
197
    body: Vec<u8>,
198
}
199

            
200
impl EncodedLinkSpec {
201
    /// Create a new `EncodedLinkSpec`.
202
15310
    pub fn new(lstype: LinkSpecType, body: impl Into<Vec<u8>>) -> Self {
203
15310
        EncodedLinkSpec {
204
15310
            lstype,
205
15310
            body: body.into(),
206
15310
        }
207
15310
    }
208

            
209
    /// Try to parse this into a `LinkSpec`, if it appears well-formed.
210
547
    pub fn parse(&self) -> Result<LinkSpec> {
211
547
        let mut r = Reader::from_slice(&self.body[..]);
212
547
        let ls = LinkSpec::from_type_and_body(self.lstype, &mut r)?;
213
547
        r.should_be_exhausted()?;
214
547
        Ok(ls)
215
547
    }
216

            
217
    /// Return the link spec type for this `EncodedLinkSpec`.
218
    pub fn lstype(&self) -> LinkSpecType {
219
        self.lstype
220
    }
221
}
222

            
223
impl Readable for EncodedLinkSpec {
224
15888
    fn take_from(r: &mut Reader<'_>) -> Result<Self> {
225
15888
        let lstype = r.take_u8()?.into();
226
16215
        r.read_nested_u8len(|r| {
227
15882
            let body = r.take_rest().to_vec();
228
15882
            Ok(Self { lstype, body })
229
16215
        })
230
15888
    }
231
}
232
impl Writeable for EncodedLinkSpec {
233
1430
    fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) -> EncodeResult<()> {
234
1430
        w.write_u8(self.lstype.into());
235
1430
        let mut nested = w.write_nested_u8len();
236
1430
        nested.write_all(&self.body[..]);
237
1430
        nested.finish()
238
1430
    }
239
}
240

            
241
#[cfg(test)]
242
mod test {
243
    // @@ begin test lint list maintained by maint/add_warning @@
244
    #![allow(clippy::bool_assert_comparison)]
245
    #![allow(clippy::clone_on_copy)]
246
    #![allow(clippy::dbg_macro)]
247
    #![allow(clippy::mixed_attributes_style)]
248
    #![allow(clippy::print_stderr)]
249
    #![allow(clippy::print_stdout)]
250
    #![allow(clippy::single_char_pattern)]
251
    #![allow(clippy::unwrap_used)]
252
    #![allow(clippy::unchecked_duration_subtraction)]
253
    #![allow(clippy::useless_vec)]
254
    #![allow(clippy::needless_pass_by_value)]
255
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
256
    use super::*;
257
    use hex_literal::hex;
258
    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
259
    use tor_bytes::{Reader, Writer};
260

            
261
    #[test]
262
    fn test_parse_enc() {
263
        fn t(b: &[u8], val: &LinkSpec) {
264
            let mut r = Reader::from_slice_for_test(b);
265
            let got: LinkSpec = r.extract().unwrap();
266
            assert_eq!(r.remaining(), 0);
267
            assert_eq!(&got, val);
268
            let mut v = Vec::new();
269
            v.write(val).expect("Encoding failure");
270
            assert_eq!(&v[..], b);
271
        }
272

            
273
        t(
274
            &hex!("00 06 01020304 0050"),
275
            &LinkSpec::OrPort(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 80),
276
        );
277
        t(
278
            &hex!("01 12 0001 0002 0003 0004 0005 0006 0007 0008 01bb"),
279
            &LinkSpec::OrPort(IpAddr::V6(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), 443),
280
        );
281
        t(
282
            &[
283
                2, 20, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 33, 33, 33, 33,
284
                33, 33, 33, 33,
285
            ],
286
            &LinkSpec::RsaId(RsaIdentity::from_bytes(b"hello world!!!!!!!!!").unwrap()),
287
        );
288
        let key = ed25519::PublicKey::from_bytes(&hex!(
289
            "B440EEDB32D5C89EF21D6B16BE85A658774CE5992355737411678EE1041BDFBA"
290
        ))
291
        .unwrap()
292
        .into();
293
        t(
294
            &hex!("03 20 B440EEDB32D5C89EF21D6B16BE85A658774CE5992355737411678EE1041BDFBA"),
295
            &LinkSpec::Ed25519Id(key),
296
        );
297

            
298
        t(
299
            &[77, 7, 115, 116, 114, 97, 110, 103, 101],
300
            &LinkSpec::Unrecognized(77.into(), (&b"strange"[..]).into()),
301
        );
302
    }
303

            
304
    #[test]
305
    fn test_parse_bad() {
306
        use tor_bytes::Error;
307

            
308
        fn t(b: &[u8]) -> Error {
309
            let mut r = Reader::from_slice_for_test(b);
310
            let got: Result<LinkSpec> = r.extract();
311
            got.err().unwrap()
312
        }
313

            
314
        assert_eq!(t(&hex!("00 03")), Error::new_incomplete_for_test(3));
315
        assert_eq!(
316
            t(&hex!("00 06 01020304")),
317
            Error::new_incomplete_for_test(2)
318
        );
319
        assert_eq!(t(&hex!("99 07 010203")), Error::new_incomplete_for_test(4));
320
    }
321

            
322
    #[test]
323
    fn test_unparsed() {
324
        fn t(b: &[u8], val: &EncodedLinkSpec) {
325
            let mut r = Reader::from_slice_for_test(b);
326
            let got: EncodedLinkSpec = r.extract().unwrap();
327
            assert_eq!(r.remaining(), 0);
328
            assert_eq!(&got, val);
329
            let mut v = Vec::new();
330
            v.write(val).expect("Encoding failure");
331
            assert_eq!(&v[..], b);
332
        }
333

            
334
        // Note that these are not valid linkspecs, but we accept them here.
335
        t(
336
            &hex!("00 00"),
337
            &EncodedLinkSpec {
338
                lstype: 0.into(),
339
                body: vec![],
340
            },
341
        );
342
        t(
343
            &hex!("00 03 010203"),
344
            &EncodedLinkSpec {
345
                lstype: 0.into(),
346
                body: vec![1, 2, 3],
347
            },
348
        );
349

            
350
        t(
351
            &hex!("99 10 000102030405060708090a0b0c0d0e0f"),
352
            &EncodedLinkSpec {
353
                lstype: 0x99.into(),
354
                body: (0..=15).collect(),
355
            },
356
        );
357
    }
358

            
359
    #[test]
360
    fn test_unparsed_bad() {
361
        use tor_bytes::Error;
362
        fn t(b: &[u8]) -> Error {
363
            let mut r = Reader::from_slice_for_test(b);
364
            let got: Result<EncodedLinkSpec> = r.extract();
365
            got.err().unwrap()
366
        }
367

            
368
        assert_eq!(t(&hex!("00")), Error::new_incomplete_for_test(1));
369
        assert_eq!(t(&hex!("00 04 010203")), Error::new_incomplete_for_test(1));
370
        assert_eq!(
371
            t(&hex!("00 05 01020304")),
372
            Error::new_incomplete_for_test(1)
373
        );
374
    }
375
}