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
        {
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
5424
    fn from(sa: &SocketAddr) -> Self {
68
5424
        LinkSpec::OrPort(sa.ip(), sa.port())
69
5424
    }
70
}
71
impl From<SocketAddr> for LinkSpec {
72
5424
    fn from(sa: SocketAddr) -> Self {
73
5424
        (&sa).into()
74
5424
    }
75
}
76
impl From<RsaIdentity> for LinkSpec {
77
63
    fn from(id: RsaIdentity) -> Self {
78
63
        LinkSpec::RsaId(id)
79
63
    }
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
15506
    fn from(id: RelayId) -> Self {
93
15506
        match id {
94
7753
            RelayId::Ed25519(key) => LinkSpec::Ed25519Id(key),
95
7753
            RelayId::Rsa(key) => LinkSpec::RsaId(key),
96
        }
97
15506
    }
98
}
99

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

            
114
    /// Sort a slice of LinkSpec based on the order in which they should
115
    /// appear in an EXTEND cell.
116
7627
    pub fn sort_by_type(lst: &mut [Self]) {
117
7627
        lst.sort_by_key(LinkSpec::sort_pos);
118
7627
    }
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
11547
    fn from_type_and_body(lstype: LinkSpecType, r: &mut Reader<'_>) -> Result<Self> {
125
        use LinkSpecType as LST;
126
11547
        Ok(match lstype {
127
            LST::ORPORT_V4 => {
128
3217
                let addr = IpAddr::V4(r.extract()?);
129
3217
                LinkSpec::OrPort(addr, r.take_u16()?)
130
            }
131
            LST::ORPORT_V6 => {
132
1894
                let addr = IpAddr::V6(r.extract()?);
133
1894
                LinkSpec::OrPort(addr, r.take_u16()?)
134
            }
135
3217
            LST::RSAID => LinkSpec::RsaId(r.extract()?),
136
3217
            LST::ED25519ID => LinkSpec::Ed25519Id(r.extract()?),
137
2
            _ => LinkSpec::Unrecognized(lstype, r.take_rest().into()),
138
        })
139
11547
    }
140

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

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

            
154
    /// Try to encode the body of this linkspec onto a given writer.
155
38769
    fn encode_body<W: Writer + ?Sized>(&self, w: &mut W) -> EncodeResult<()> {
156
        use LinkSpec::*;
157
22438
        match self {
158
22434
            OrPort(IpAddr::V4(v4), port) => {
159
22434
                w.write(v4)?;
160
22434
                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
8196
            RsaId(r) => {
167
8196
                w.write(r)?;
168
            }
169
8133
            Ed25519Id(e) => {
170
8133
                w.write(e)?;
171
            }
172
2
            Unrecognized(_, vec) => {
173
2
                w.write_all(&vec[..]);
174
2
            }
175
        }
176
38769
        Ok(())
177
38769
    }
178

            
179
    /// Return an encoded version of this link specifier.
180
38759
    pub fn encode(&self) -> EncodeResult<EncodedLinkSpec> {
181
38759
        let tp = self.lstype();
182
38759
        let mut body = Vec::new();
183
38759
        self.encode_body(&mut body)?;
184
38759
        Ok(EncodedLinkSpec::new(tp, body))
185
38759
    }
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
38767
    pub fn new(lstype: LinkSpecType, body: impl Into<Vec<u8>>) -> Self {
203
38767
        EncodedLinkSpec {
204
38767
            lstype,
205
38767
            body: body.into(),
206
38767
        }
207
38767
    }
208

            
209
    /// Try to parse this into a `LinkSpec`, if it appears well-formed.
210
11537
    pub fn parse(&self) -> Result<LinkSpec> {
211
11537
        let mut r = Reader::from_slice(&self.body[..]);
212
11537
        let ls = LinkSpec::from_type_and_body(self.lstype, &mut r)?;
213
11537
        r.should_be_exhausted()?;
214
11537
        Ok(ls)
215
11537
    }
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
26220
    fn take_from(r: &mut Reader<'_>) -> Result<Self> {
225
26220
        let lstype = r.take_u8()?.into();
226
26639
        r.read_nested_u8len(|r| {
227
26214
            let body = r.take_rest().to_vec();
228
26214
            Ok(Self { lstype, body })
229
26214
        })
230
26220
    }
231
}
232
impl Writeable for EncodedLinkSpec {
233
2036
    fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) -> EncodeResult<()> {
234
2036
        w.write_u8(self.lstype.into());
235
2036
        let mut nested = w.write_nested_u8len();
236
2036
        nested.write_all(&self.body[..]);
237
2036
        nested.finish()
238
2036
    }
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_time_subtraction)]
253
    #![allow(clippy::useless_vec)]
254
    #![allow(clippy::needless_pass_by_value)]
255
    #![allow(clippy::string_slice)] // See arti#2571
256
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
257
    use super::*;
258
    use hex_literal::hex;
259
    use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
260
    use tor_bytes::{Reader, Writer};
261

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

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

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

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

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

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

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

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

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

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

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