1
//! Declare traits to be implemented by types that describe a place
2
//! that Tor can connect to, directly or indirectly.
3

            
4
use derive_deftly::derive_deftly_adhoc;
5
use itertools::Itertools;
6
use safelog::Redactable;
7
use std::{
8
    fmt,
9
    iter::FusedIterator,
10
    net::{IpAddr, SocketAddr},
11
};
12
use tor_llcrypto::pk;
13

            
14
use crate::{ChannelMethod, RelayIdRef, RelayIdType, RelayIdTypeIter};
15

            
16
#[cfg(feature = "pt-client")]
17
use crate::PtTargetAddr;
18

            
19
/// Legacy implementation helper for HasRelayIds.
20
///
21
/// Previously, we assumed that everything had these two identity types, which
22
/// is not an assumption we want to keep making in the future.
23
pub trait HasRelayIdsLegacy {
24
    /// Return the ed25519 identity for this relay.
25
    fn ed_identity(&self) -> &pk::ed25519::Ed25519Identity;
26
    /// Return the RSA identity for this relay.
27
    fn rsa_identity(&self) -> &pk::rsa::RsaIdentity;
28
}
29

            
30
/// An object containing information about a relay's identity keys.
31
///
32
/// This trait has a fairly large number of methods, most of which you're not
33
/// actually expected to implement.  The only one that you need to provide is
34
/// [`identity`](HasRelayIds::identity).
35
pub trait HasRelayIds {
36
    /// Return the identity of this relay whose type is `key_type`, or None if
37
    /// the relay has no such identity.
38
    ///
39
    /// (Currently all relays have all recognized identity types, but we might
40
    /// implement or deprecate an identity type in the future.)
41
    fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>>;
42

            
43
    /// Return an iterator over all of the identities held by this object.
44
44333262
    fn identities(&self) -> RelayIdIter<'_, Self> {
45
44333262
        RelayIdIter {
46
44333262
            info: self,
47
44333262
            next_key: RelayIdType::all_types(),
48
44333262
        }
49
44333262
    }
50

            
51
    /// Return the ed25519 identity for this relay if it has one.
52
15620792
    fn ed_identity(&self) -> Option<&pk::ed25519::Ed25519Identity> {
53
15620792
        self.identity(RelayIdType::Ed25519)
54
15620792
            .map(RelayIdRef::unwrap_ed25519)
55
15620792
    }
56

            
57
    /// Return the RSA identity for this relay if it has one.
58
15865352
    fn rsa_identity(&self) -> Option<&pk::rsa::RsaIdentity> {
59
15865352
        self.identity(RelayIdType::Rsa).map(RelayIdRef::unwrap_rsa)
60
15865352
    }
61

            
62
    /// Check whether the provided Id is a known identity of this relay.
63
    ///
64
    /// Remember that a given set of identity keys may be incomplete: some
65
    /// objects that represent a relay have only a subset of the relay's
66
    /// identities. Therefore, a "true" answer means that the relay has this
67
    /// identity,  but a "false" answer could mean that the relay has a
68
    /// different identity of this type, or that it has _no_ known identity of
69
    /// this type.
70
1282616
    fn has_identity(&self, id: RelayIdRef<'_>) -> bool {
71
1308300
        self.identity(id.id_type()).map(|my_id| my_id == id) == Some(true)
72
1282616
    }
73

            
74
    /// Return true if this object has any known identity.
75
4
    fn has_any_identity(&self) -> bool {
76
8
        RelayIdType::all_types().any(|id_type| self.identity(id_type).is_some())
77
4
    }
78

            
79
    /// Return true if this object has exactly the same relay IDs as `other`.
80
    //
81
    // TODO: Once we make it so particular identity key types are optional, we
82
    // should add a note saying that this function is usually not what you want
83
    // for many cases, since you might want to know "could this be the same
84
    // relay" vs "is this definitely the same relay."
85
    //
86
    // NOTE: We don't make this an `Eq` method, since we want to make callers
87
    // choose carefully among this method, `has_all_relay_ids_from`, and any
88
    // similar methods we add in the future.
89
    #[allow(clippy::nonminimal_bool)] // rust-clippy/issues/12627
90
54797768
    fn same_relay_ids<T: HasRelayIds + ?Sized>(&self, other: &T) -> bool {
91
        // We use derive-deftly to iterate over the id types, rather than strum
92
        //
93
        // Empirically, with rustc 1.77.0-beta.5, this arranges that
94
        //     <tor_netdir::Relay as HasRelayIds>::same_relay_ids
95
        // compiles to the same asm (on amd64) as the open-coded inherent
96
        //     tor_netdir::Relay::has_same_relay_ids
97
        //
98
        // The problem with the strum approach seems to be that the compiler doesn't inline
99
        //     <RelayIdTypeIter as Iterator>::next
100
        // and unroll the loop.
101
        // Adding `#[inline]` and even `#[inline(always)]` to the strum output didn't help.
102
        //
103
        // When `next()` isn't inlined and the loop unrolled,
104
        // the compiler can't inline the matching on the id type,
105
        // and generate the obvious simple function.
106
        //
107
        // Empirically, the same results with non-inlined next() and non-unrolled loop,
108
        // were obtained with:
109
        //   - a simpler hand-coded Iterator struct
110
        //   - that hand-coded Iterator struct locally present in tor-netdir,
111
        //   - using `<[RelayIdType; ] as IntoIterator>`
112
        //
113
        // I experimented to see if this was a general problem with `strum`'s iterator.
114
        // In a smaller test program the compiler *does* unroll and inline.
115
        // I suspect that the compiler is having trouble with the complexities
116
        // of disentangling `HasLegacyRelayIds` and/or comparing `Option<RelayIdRef>`.
117
        //
118
        // TODO: do we want to replace RelayIdType::all_types with derive-deftly
119
        // in RelayIdIter, has_all_relay_ids_from, has_any_relay_id_from, etc.?
120
        // If so, search this crate for all_types.
121
54797768
        derive_deftly_adhoc! {
122
            RelayIdType:
123
            $(
124
1773062
                self.identity($vtype) == other.identity($vtype) &&
125
            )
126
1773044
                true
127
        }
128
54797768
    }
129

            
130
    /// Return true if this object has every relay ID that `other` does.
131
    ///
132
    /// (It still returns true if there are some IDs in this object that are not
133
    /// present in `other`.)
134
10814088
    fn has_all_relay_ids_from<T: HasRelayIds + ?Sized>(&self, other: &T) -> bool {
135
21853229
        RelayIdType::all_types().all(|key_type| {
136
21609066
            match (self.identity(key_type), other.identity(key_type)) {
137
                // If we both have the same key for this type, great.
138
21608888
                (Some(mine), Some(theirs)) if mine == theirs => true,
139
                // Uh oh. They do have a key for his type, but it's not ours.
140
19142
                (_, Some(_theirs)) => false,
141
                // If they don't care what we have for this type, great.
142
150
                (_, None) => true,
143
            }
144
21609066
        })
145
10814088
    }
146

            
147
    /// Return true if this object has any relay ID that `other` has.
148
    ///
149
    /// This is symmetrical:
150
    /// it returns true if the two objects have any overlap in their identities.
151
76114
    fn has_any_relay_id_from<T: HasRelayIds + ?Sized>(&self, other: &T) -> bool {
152
76114
        RelayIdType::all_types()
153
190261
            .filter_map(|key_type| Some((self.identity(key_type)?, other.identity(key_type)?)))
154
190219
            .any(|(self_id, other_id)| self_id == other_id)
155
76114
    }
156

            
157
    /// Compare this object to another HasRelayIds.
158
    ///
159
    /// Objects are sorted by Ed25519 identities, with ties decided by RSA
160
    /// identities. An absent identity of a given type is sorted before a
161
    /// present identity of that type.
162
    ///
163
    /// If additional identities are added in the future, they may taken into
164
    /// consideration before _or_ after the current identity types.
165
1544
    fn cmp_by_relay_ids<T: HasRelayIds + ?Sized>(&self, other: &T) -> std::cmp::Ordering {
166
2374
        for key_type in RelayIdType::all_types() {
167
2374
            let ordering = Ord::cmp(&self.identity(key_type), &other.identity(key_type));
168
2374
            if ordering.is_ne() {
169
742
                return ordering;
170
1632
            }
171
        }
172
802
        std::cmp::Ordering::Equal
173
1544
    }
174

            
175
    /// Return a reference to this object suitable for formatting its
176
    /// [`HasRelayIds`] members.
177
29380
    fn display_relay_ids(&self) -> DisplayRelayIds<'_, Self> {
178
29380
        DisplayRelayIds { inner: self }
179
29380
    }
180
}
181

            
182
impl<T: HasRelayIdsLegacy> HasRelayIds for T {
183
205528268
    fn identity(&self, key_type: RelayIdType) -> Option<RelayIdRef<'_>> {
184
205528268
        match key_type {
185
48059720
            RelayIdType::Rsa => Some(self.rsa_identity().into()),
186
157468548
            RelayIdType::Ed25519 => Some(self.ed_identity().into()),
187
        }
188
205528268
    }
189
}
190

            
191
/// A helper type used to format the [`RelayId`](crate::RelayId)s in a
192
/// [`HasRelayIds`].
193
#[derive(Clone)]
194
pub struct DisplayRelayIds<'a, T: HasRelayIds + ?Sized> {
195
    /// The HasRelayIds that we're displaying.
196
    inner: &'a T,
197
}
198
// Redactable must implement Debug.
199
impl<'a, T: HasRelayIds + ?Sized> fmt::Debug for DisplayRelayIds<'a, T> {
200
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201
        f.debug_struct("DisplayRelayIds").finish_non_exhaustive()
202
    }
203
}
204

            
205
impl<'a, T: HasRelayIds + ?Sized> DisplayRelayIds<'a, T> {
206
    /// Helper: output `self` in a possibly redacted way.
207
29380
    fn fmt_impl(&self, f: &mut fmt::Formatter<'_>, redact: bool) -> fmt::Result {
208
29380
        let mut iter = self.inner.identities();
209
29380
        if let Some(ident) = iter.next() {
210
29317
            write!(f, "{}", ident.maybe_redacted(redact))?;
211
63
        }
212
29380
        if redact {
213
2
            return Ok(());
214
29378
        }
215
29378
        for ident in iter {
216
29313
            write!(f, " {}", ident.maybe_redacted(redact))?;
217
        }
218
29378
        Ok(())
219
29380
    }
220
}
221
impl<'a, T: HasRelayIds + ?Sized> fmt::Display for DisplayRelayIds<'a, T> {
222
29374
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223
29374
        self.fmt_impl(f, false)
224
29374
    }
225
}
226
impl<'a, T: HasRelayIds + ?Sized> Redactable for DisplayRelayIds<'a, T> {
227
2
    fn display_redacted(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228
2
        self.fmt_impl(f, true)
229
2
    }
230
}
231

            
232
/// An iterator over all of the relay identities held by a [`HasRelayIds`]
233
#[derive(Clone)]
234
pub struct RelayIdIter<'a, T: HasRelayIds + ?Sized> {
235
    /// The object holding the keys
236
    info: &'a T,
237
    /// The next key type to yield
238
    next_key: RelayIdTypeIter,
239
}
240

            
241
impl<'a, T: HasRelayIds + ?Sized> Iterator for RelayIdIter<'a, T> {
242
    type Item = RelayIdRef<'a>;
243

            
244
112042806
    fn next(&mut self) -> Option<Self::Item> {
245
112042806
        for key_type in &mut self.next_key {
246
78195849
            if let Some(key) = self.info.identity(key_type) {
247
78191039
                return Some(key);
248
4810
            }
249
        }
250
33851767
        None
251
112042806
    }
252
}
253
// RelayIdIter is fused since next_key is fused.
254
impl<'a, T: HasRelayIds + ?Sized> FusedIterator for RelayIdIter<'a, T> {}
255

            
256
/// An object that represents a host on the network which may have known IP addresses.
257
pub trait HasAddrs {
258
    /// Return the addresses listed for this server.
259
    ///
260
    /// NOTE that these addresses are not necessarily ones that we should
261
    /// connect to directly!  They can be useful for telling where a server is
262
    /// located, or whether it is "close" to another server, but without knowing
263
    /// the associated protocols you cannot use these to launch a connection.
264
    ///
265
    /// Also, for some servers, we may not actually have any relevant addresses;
266
    /// in that case, the returned slice is empty.
267
    ///
268
    /// To see how to _connect_ to a relay, use [`HasChanMethod::chan_method`]
269
    //
270
    // TODO: This is a questionable API. I'd rather return an iterator
271
    // of addresses or references to addresses, but both of those options
272
    // make defining the right associated types rather tricky.
273
    fn addrs(&self) -> impl Iterator<Item = SocketAddr>;
274
}
275

            
276
impl<T: HasAddrs> HasAddrs for &T {
277
    fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
278
        // Be explicit about the type here so that we don't end up in an infinite loop by accident.
279
        <T as HasAddrs>::addrs(self)
280
    }
281
}
282

            
283
/// An object that can be connected to via [`ChannelMethod`]s.
284
pub trait HasChanMethod {
285
    /// Return the known ways to contact this
286
    // TODO: See notes on HasAddrs above.
287
    // TODO: I don't like having this return a new ChannelMethod, but I
288
    // don't see a great alternative. Let's revisit that.-nickm.
289
    fn chan_method(&self) -> ChannelMethod;
290
}
291

            
292
/// Implement `HasChanMethods` for an object with `HasAddr` whose addresses
293
/// _all_ represent a host we can connect to by a direct Tor connection at its
294
/// IP addresses.
295
pub trait DirectChanMethodsHelper: HasAddrs {}
296

            
297
impl<D: DirectChanMethodsHelper> HasChanMethod for D {
298
1809392
    fn chan_method(&self) -> ChannelMethod {
299
1809392
        ChannelMethod::Direct(self.addrs().collect_vec())
300
1809392
    }
301
}
302

            
303
/// Information about a Tor relay used to connect to it.
304
///
305
/// Anything that implements 'ChanTarget' can be used as the
306
/// identity of a relay for the purposes of launching a new
307
/// channel.
308
pub trait ChanTarget: HasRelayIds + HasAddrs + HasChanMethod {
309
    /// Return a reference to this object suitable for formatting its
310
    /// [`ChanTarget`]-specific members.
311
    ///
312
    /// The display format is not exhaustive, but tries to give enough
313
    /// information to identify which channel target we're talking about.
314
4
    fn display_chan_target(&self) -> DisplayChanTarget<'_, Self>
315
4
    where
316
4
        Self: Sized,
317
    {
318
4
        DisplayChanTarget { inner: self }
319
4
    }
320

            
321
    /// Return true if we think all addresses are allowed to be used for a relay outgoing channel.
322
    ///
323
    /// If no address are found, true is returned.
324
    ///
325
    /// NOTE: The set of RFCs checked here are not expected to change over time and so this should
326
    /// be a check that yields the same result regardless of the Rust library version. HOWEVER, it
327
    /// doesn't mean that each relay/client on the network uses the same set of checks.
328
    fn all_addrs_allowed_for_outgoing_channels(&self) -> bool {
329
        self.addrs().all(|addr| match addr.ip() {
330
            IpAddr::V4(v4) => {
331
                !(v4.is_loopback() // RFC 1122 (127.0.0.0/8)
332
                    || v4.is_private() // RFC1918
333
                    || v4.is_unspecified() // 0.0.0.0
334
                    || v4.is_documentation() // RFC 5737
335
                    || v4.is_multicast() // RFC 5771 (224.0.0.0/4)
336
                    || v4.is_link_local()) // RFC 3927 (169.254.0.0/16)
337
            }
338
            IpAddr::V6(v6) => {
339
                !(v6.is_loopback() // RFC 4291 (::1)
340
                    || v6.is_multicast() // RFC 4291 (ff00::/8)
341
                    || v6.is_unspecified() // RFC 4291 (::)
342
                    || v6.is_unique_local() // RFC 4193 (fc00::/7)
343
                    || v6.is_unicast_link_local()) // RFC 4291 (2001:db8::/32, fe80::/10)
344
            }
345
        })
346
    }
347

            
348
    /// Return true iff all addresses' ports are non-zero, or there are no addresses.
349
2
    fn has_all_nonzero_port(&self) -> bool {
350
3
        self.addrs().all(|addr| addr.port() != 0)
351
2
    }
352
}
353

            
354
/// Information about a Tor relay used to extend a circuit to it.
355
///
356
/// Anything that implements 'CircTarget' can be used as the
357
/// identity of a relay for the purposes of extending a circuit.
358
pub trait CircTarget: ChanTarget {
359
    /// Return a new vector of encoded link specifiers for this relay.
360
    ///
361
    /// Note that, outside of this method, nothing in Arti should be re-ordering
362
    /// the link specifiers returned by this method.  It is this method's
363
    /// responsibility to return them in the correct order.
364
    ///
365
    /// The default implementation for this method builds a list of link
366
    /// specifiers from this object's identities and IP addresses, and sorts
367
    /// them into the order specified in tor-spec to avoid implementation
368
    /// fingerprinting attacks.
369
    //
370
    // TODO: This is a questionable API. I'd rather return an iterator
371
    // of link specifiers, but that's not so easy to do, since it seems
372
    // doing so correctly would require default associated types.
373
246
    fn linkspecs(&self) -> tor_bytes::EncodeResult<Vec<crate::EncodedLinkSpec>> {
374
615
        let mut result: Vec<_> = self.identities().map(|id| id.to_owned().into()).collect();
375
        #[allow(irrefutable_let_patterns)]
376
246
        if let ChannelMethod::Direct(addrs) = self.chan_method() {
377
246
            result.extend(addrs.into_iter().map(crate::LinkSpec::from));
378
246
        }
379
246
        crate::LinkSpec::sort_by_type(&mut result[..]);
380
791
        result.into_iter().map(|ls| ls.encode()).collect()
381
246
    }
382
    /// Return the ntor onion key for this relay
383
    fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey;
384
    /// Return the subprotocols implemented by this relay.
385
    fn protovers(&self) -> &tor_protover::Protocols;
386
}
387

            
388
/// A reference to a ChanTarget that implements Display using a hopefully useful
389
/// format.
390
#[derive(Debug, Clone)]
391
pub struct DisplayChanTarget<'a, T> {
392
    /// The ChanTarget that we're formatting.
393
    inner: &'a T,
394
}
395

            
396
impl<'a, T: ChanTarget> DisplayChanTarget<'a, T> {
397
    /// helper: output `self` in a possibly redacted way.
398
4
    fn fmt_impl(&self, f: &mut fmt::Formatter<'_>, redact: bool) -> fmt::Result {
399
4
        write!(f, "[")?;
400
        // We look at the chan_method() (where we would connect to) rather than
401
        // the addrs() (where the relay is, nebulously, "located").  This lets us
402
        // give a less surprising description.
403
4
        match self.inner.chan_method() {
404
2
            ChannelMethod::Direct(v) if v.is_empty() => write!(f, "?")?,
405
2
            ChannelMethod::Direct(v) if v.len() == 1 => {
406
                write!(f, "{}", v[0].maybe_redacted(redact))?;
407
            }
408
2
            ChannelMethod::Direct(v) => write!(f, "{}+", v[0].maybe_redacted(redact))?,
409
            #[cfg(feature = "pt-client")]
410
2
            ChannelMethod::Pluggable(target) => {
411
2
                match target.addr() {
412
                    PtTargetAddr::None => {}
413
2
                    other => write!(f, "{} ", other.maybe_redacted(redact))?,
414
                }
415
2
                write!(f, "via {}", target.transport())?;
416
                // This deliberately doesn't include the PtTargetSettings, since
417
                // they can be large, and they're typically unnecessary.
418
            }
419
        }
420

            
421
4
        write!(f, " ")?;
422
4
        self.inner.display_relay_ids().fmt_impl(f, redact)?;
423

            
424
4
        write!(f, "]")
425
4
    }
426
}
427

            
428
impl<'a, T: ChanTarget> fmt::Display for DisplayChanTarget<'a, T> {
429
4
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430
4
        self.fmt_impl(f, false)
431
4
    }
432
}
433

            
434
impl<'a, T: ChanTarget + fmt::Debug> safelog::Redactable for DisplayChanTarget<'a, T> {
435
    fn display_redacted(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
436
        self.fmt_impl(f, true)
437
    }
438
    fn debug_redacted(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
439
        write!(f, "ChanTarget({:?})", self.redacted().to_string())
440
    }
441
}
442

            
443
#[cfg(test)]
444
mod test {
445
    // @@ begin test lint list maintained by maint/add_warning @@
446
    #![allow(clippy::bool_assert_comparison)]
447
    #![allow(clippy::clone_on_copy)]
448
    #![allow(clippy::dbg_macro)]
449
    #![allow(clippy::mixed_attributes_style)]
450
    #![allow(clippy::print_stderr)]
451
    #![allow(clippy::print_stdout)]
452
    #![allow(clippy::single_char_pattern)]
453
    #![allow(clippy::unwrap_used)]
454
    #![allow(clippy::unchecked_time_subtraction)]
455
    #![allow(clippy::useless_vec)]
456
    #![allow(clippy::needless_pass_by_value)]
457
    #![allow(clippy::string_slice)] // See arti#2571
458
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
459
    use super::*;
460
    use crate::RelayIds;
461
    use hex_literal::hex;
462
    use std::net::IpAddr;
463
    use tor_llcrypto::pk::{self, ed25519::Ed25519Identity, rsa::RsaIdentity};
464

            
465
    struct Example {
466
        addrs: Vec<SocketAddr>,
467
        ed_id: pk::ed25519::Ed25519Identity,
468
        rsa_id: pk::rsa::RsaIdentity,
469
        ntor: pk::curve25519::PublicKey,
470
        pv: tor_protover::Protocols,
471
    }
472
    impl HasAddrs for Example {
473
        fn addrs(&self) -> impl Iterator<Item = SocketAddr> {
474
            self.addrs.iter().copied()
475
        }
476
    }
477
    impl DirectChanMethodsHelper for Example {}
478
    impl HasRelayIdsLegacy for Example {
479
        fn ed_identity(&self) -> &pk::ed25519::Ed25519Identity {
480
            &self.ed_id
481
        }
482
        fn rsa_identity(&self) -> &pk::rsa::RsaIdentity {
483
            &self.rsa_id
484
        }
485
    }
486
    impl ChanTarget for Example {}
487
    impl CircTarget for Example {
488
        fn ntor_onion_key(&self) -> &pk::curve25519::PublicKey {
489
            &self.ntor
490
        }
491
        fn protovers(&self) -> &tor_protover::Protocols {
492
            &self.pv
493
        }
494
    }
495

            
496
    /// Return an `Example` object, for use in tests below.
497
    fn example() -> Example {
498
        Example {
499
            addrs: vec![
500
                "127.0.0.1:99".parse::<SocketAddr>().unwrap(),
501
                "[::1]:909".parse::<SocketAddr>().unwrap(),
502
            ],
503
            ed_id: pk::ed25519::PublicKey::from_bytes(&hex!(
504
                "fc51cd8e6218a1a38da47ed00230f058
505
                 0816ed13ba3303ac5deb911548908025"
506
            ))
507
            .unwrap()
508
            .into(),
509
            rsa_id: pk::rsa::RsaIdentity::from_bytes(&hex!(
510
                "1234567890abcdef12341234567890abcdef1234"
511
            ))
512
            .unwrap(),
513
            ntor: pk::curve25519::PublicKey::from(hex!(
514
                "e6db6867583030db3594c1a424b15f7c
515
                 726624ec26b3353b10a903a6d0ab1c4c"
516
            )),
517
            pv: tor_protover::Protocols::default(),
518
        }
519
    }
520

            
521
    #[test]
522
    fn test_linkspecs() {
523
        let ex = example();
524
        let specs = ex
525
            .linkspecs()
526
            .unwrap()
527
            .into_iter()
528
            .map(|ls| ls.parse())
529
            .collect::<Result<Vec<_>, _>>()
530
            .unwrap();
531
        assert_eq!(4, specs.len());
532

            
533
        use crate::ls::LinkSpec;
534
        assert_eq!(
535
            specs[0],
536
            LinkSpec::OrPort("127.0.0.1".parse::<IpAddr>().unwrap(), 99)
537
        );
538
        assert_eq!(
539
            specs[1],
540
            LinkSpec::RsaId(
541
                pk::rsa::RsaIdentity::from_bytes(&hex!("1234567890abcdef12341234567890abcdef1234"))
542
                    .unwrap()
543
            )
544
        );
545
        assert_eq!(
546
            specs[2],
547
            LinkSpec::Ed25519Id(
548
                pk::ed25519::PublicKey::from_bytes(&hex!(
549
                    "fc51cd8e6218a1a38da47ed00230f058
550
                     0816ed13ba3303ac5deb911548908025"
551
                ))
552
                .unwrap()
553
                .into()
554
            )
555
        );
556
        assert_eq!(
557
            specs[3],
558
            LinkSpec::OrPort("::1".parse::<IpAddr>().unwrap(), 909)
559
        );
560
    }
561

            
562
    #[test]
563
    fn cmp_by_ids() {
564
        use crate::RelayIds;
565
        use std::cmp::Ordering;
566
        fn b(ed: Option<Ed25519Identity>, rsa: Option<RsaIdentity>) -> RelayIds {
567
            let mut b = RelayIds::builder();
568
            if let Some(ed) = ed {
569
                b.ed_identity(ed);
570
            }
571
            if let Some(rsa) = rsa {
572
                b.rsa_identity(rsa);
573
            }
574
            b.build().unwrap()
575
        }
576
        // Assert that v is strictly ascending.
577
        fn assert_sorted(v: &[RelayIds]) {
578
            for slice in v.windows(2) {
579
                assert_eq!(slice[0].cmp_by_relay_ids(&slice[1]), Ordering::Less);
580
                assert_eq!(slice[1].cmp_by_relay_ids(&slice[0]), Ordering::Greater);
581
                assert_eq!(slice[0].cmp_by_relay_ids(&slice[0]), Ordering::Equal);
582
            }
583
        }
584

            
585
        let ed1 = hex!("0a54686973206973207468652043656e7472616c205363727574696e697a6572").into();
586
        let ed2 = hex!("6962696c69747920746f20656e666f72636520616c6c20746865206c6177730a").into();
587
        let ed3 = hex!("73736564207965740a497420697320616c736f206d7920726573706f6e736962").into();
588
        let rsa1 = hex!("2e2e2e0a4974206973206d7920726573706f6e73").into();
589
        let rsa2 = hex!("5468617420686176656e2774206265656e207061").into();
590
        let rsa3 = hex!("696c69747920746f20616c65727420656163680a").into();
591

            
592
        assert_sorted(&[
593
            b(Some(ed1), None),
594
            b(Some(ed2), None),
595
            b(Some(ed3), None),
596
            b(Some(ed3), Some(rsa1)),
597
        ]);
598
        assert_sorted(&[
599
            b(Some(ed1), Some(rsa3)),
600
            b(Some(ed2), Some(rsa2)),
601
            b(Some(ed3), Some(rsa1)),
602
            b(Some(ed3), Some(rsa2)),
603
        ]);
604
        assert_sorted(&[
605
            b(Some(ed1), Some(rsa1)),
606
            b(Some(ed1), Some(rsa2)),
607
            b(Some(ed1), Some(rsa3)),
608
        ]);
609
        assert_sorted(&[
610
            b(None, Some(rsa1)),
611
            b(None, Some(rsa2)),
612
            b(None, Some(rsa3)),
613
        ]);
614
        assert_sorted(&[
615
            b(None, Some(rsa1)),
616
            b(Some(ed1), None),
617
            b(Some(ed1), Some(rsa1)),
618
        ]);
619
    }
620

            
621
    #[test]
622
    fn compare_id_sets() {
623
        // TODO somehow nicely unify these repeated predefined examples
624
        let ed1 = hex!("0a54686973206973207468652043656e7472616c205363727574696e697a6572").into();
625
        let rsa1 = hex!("2e2e2e0a4974206973206d7920726573706f6e73").into();
626
        let rsa2 = RsaIdentity::from(hex!("5468617420686176656e2774206265656e207061"));
627

            
628
        let both1 = RelayIds::builder()
629
            .ed_identity(ed1)
630
            .rsa_identity(rsa1)
631
            .build()
632
            .unwrap();
633
        let mixed = RelayIds::builder()
634
            .ed_identity(ed1)
635
            .rsa_identity(rsa2)
636
            .build()
637
            .unwrap();
638
        let ed1 = RelayIds::builder().ed_identity(ed1).build().unwrap();
639
        let rsa1 = RelayIds::builder().rsa_identity(rsa1).build().unwrap();
640
        let rsa2 = RelayIds::builder().rsa_identity(rsa2).build().unwrap();
641

            
642
        fn chk_equal(v: &impl HasRelayIds) {
643
            assert!(v.same_relay_ids(v));
644
            assert!(v.has_all_relay_ids_from(v));
645
            assert!(v.has_any_relay_id_from(v));
646
        }
647
        fn chk_strict_subset(bigger: &impl HasRelayIds, smaller: &impl HasRelayIds) {
648
            assert!(!bigger.same_relay_ids(smaller));
649
            assert!(bigger.has_all_relay_ids_from(smaller));
650
            assert!(bigger.has_any_relay_id_from(smaller));
651
            assert!(!smaller.same_relay_ids(bigger));
652
            assert!(!smaller.has_all_relay_ids_from(bigger));
653
            assert!(smaller.has_any_relay_id_from(bigger));
654
        }
655
        fn chk_nontrivially_overlapping_one_way(a: &impl HasRelayIds, b: &impl HasRelayIds) {
656
            assert!(!a.same_relay_ids(b));
657
            assert!(!a.has_all_relay_ids_from(b));
658
            assert!(a.has_any_relay_id_from(b));
659
        }
660
        fn chk_nontrivially_overlapping(a: &impl HasRelayIds, b: &impl HasRelayIds) {
661
            chk_nontrivially_overlapping_one_way(a, b);
662
            chk_nontrivially_overlapping_one_way(b, a);
663
        }
664

            
665
        chk_equal(&ed1);
666
        chk_equal(&rsa1);
667
        chk_equal(&both1);
668

            
669
        chk_strict_subset(&both1, &ed1);
670
        chk_strict_subset(&both1, &rsa1);
671
        chk_strict_subset(&mixed, &ed1);
672
        chk_strict_subset(&mixed, &rsa2);
673

            
674
        chk_nontrivially_overlapping(&both1, &mixed);
675
    }
676

            
677
    #[test]
678
    fn display() {
679
        let e1 = example();
680
        assert_eq!(
681
            e1.display_chan_target().to_string(),
682
            "[127.0.0.1:99+ ed25519:/FHNjmIYoaONpH7QAjDwWAgW7RO6MwOsXeuRFUiQgCU \
683
              $1234567890abcdef12341234567890abcdef1234]"
684
        );
685

            
686
        #[cfg(feature = "pt-client")]
687
        {
688
            use crate::PtTarget;
689

            
690
            let rsa = hex!("234461644a6f6b6523436f726e794f6e4d61696e").into();
691
            let mut b = crate::OwnedChanTarget::builder();
692
            b.ids().rsa_identity(rsa);
693
            let e2 = b
694
                .method(ChannelMethod::Pluggable(PtTarget::new(
695
                    "obfs4".parse().unwrap(),
696
                    "127.0.0.1:99".parse().unwrap(),
697
                )))
698
                .build()
699
                .unwrap();
700
            assert_eq!(
701
                e2.to_string(),
702
                "[127.0.0.1:99 via obfs4 $234461644a6f6b6523436f726e794f6e4d61696e]"
703
            );
704
        }
705
    }
706

            
707
    #[test]
708
    fn has_id() {
709
        use crate::RelayIds;
710
        assert!(example().has_any_identity());
711
        assert!(!RelayIds::empty().has_any_identity());
712
    }
713
}