1
#![cfg_attr(docsrs, feature(doc_cfg))]
2
#![doc = include_str!("../README.md")]
3
// @@ begin lint list maintained by maint/add_warning @@
4
#![allow(renamed_and_removed_lints)] // @@REMOVE_WHEN(ci_arti_stable)
5
#![allow(unknown_lints)] // @@REMOVE_WHEN(ci_arti_nightly)
6
#![warn(missing_docs)]
7
#![warn(noop_method_call)]
8
#![warn(unreachable_pub)]
9
#![warn(clippy::all)]
10
#![deny(clippy::await_holding_lock)]
11
#![deny(clippy::cargo_common_metadata)]
12
#![deny(clippy::cast_lossless)]
13
#![deny(clippy::checked_conversions)]
14
#![warn(clippy::cognitive_complexity)]
15
#![deny(clippy::debug_assert_with_mut_call)]
16
#![deny(clippy::exhaustive_enums)]
17
#![deny(clippy::exhaustive_structs)]
18
#![deny(clippy::expl_impl_clone_on_copy)]
19
#![deny(clippy::fallible_impl_from)]
20
#![deny(clippy::implicit_clone)]
21
#![deny(clippy::large_stack_arrays)]
22
#![warn(clippy::manual_ok_or)]
23
#![deny(clippy::missing_docs_in_private_items)]
24
#![warn(clippy::needless_borrow)]
25
#![warn(clippy::needless_pass_by_value)]
26
#![warn(clippy::option_option)]
27
#![deny(clippy::print_stderr)]
28
#![deny(clippy::print_stdout)]
29
#![warn(clippy::rc_buffer)]
30
#![deny(clippy::ref_option_ref)]
31
#![warn(clippy::semicolon_if_nothing_returned)]
32
#![warn(clippy::trait_duplication_in_bounds)]
33
#![deny(clippy::unchecked_time_subtraction)]
34
#![deny(clippy::unnecessary_wraps)]
35
#![warn(clippy::unseparated_literal_suffix)]
36
#![deny(clippy::unwrap_used)]
37
#![deny(clippy::mod_module_files)]
38
#![allow(clippy::let_unit_value)] // This can reasonably be done for explicitness
39
#![allow(clippy::uninlined_format_args)]
40
#![allow(clippy::significant_drop_in_scrutinee)] // arti/-/merge_requests/588/#note_2812945
41
#![allow(clippy::result_large_err)] // temporary workaround for arti#587
42
#![allow(clippy::needless_raw_string_hashes)] // complained-about code is fine, often best
43
#![allow(clippy::needless_lifetimes)] // See arti#1765
44
#![allow(mismatched_lifetime_syntaxes)] // temporary workaround for arti#2060
45
#![allow(clippy::collapsible_if)] // See arti#2342
46
#![deny(clippy::unused_async)]
47
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
48

            
49
#![allow(non_upper_case_globals)]
50
#![allow(clippy::upper_case_acronyms)]
51

            
52
use std::sync::Arc;
53

            
54
use caret::caret_int;
55

            
56
use thiserror::Error;
57
use tor_basic_utils::intern::InternCache;
58

            
59
pub mod named;
60

            
61
caret_int! {
62
    /// A recognized subprotocol.
63
    ///
64
    /// These names are kept in sync with the names used in consensus
65
    /// documents; the values are kept in sync with the values in the
66
    /// cbor document format in the walking onions proposal.
67
    ///
68
    /// For the full semantics of each subprotocol, see tor-spec.txt.
69
    #[derive(Hash,Ord,PartialOrd)]
70
    pub struct ProtoKind(u8) {
71
        /// Initiating and receiving channels, and getting cells on them.
72
        Link = 0,
73
        /// Different kinds of authenticate cells
74
        LinkAuth = 1,
75
        /// CREATE cells, CREATED cells, and the encryption that they
76
        /// create.
77
        Relay = 2,
78
        /// Serving and fetching network directory documents.
79
        DirCache = 3,
80
        /// Serving onion service descriptors
81
        HSDir = 4,
82
        /// Providing an onion service introduction point
83
        HSIntro = 5,
84
        /// Providing an onion service rendezvous point
85
        HSRend = 6,
86
        /// Describing a relay's functionality using router descriptors.
87
        Desc = 7,
88
        /// Describing a relay's functionality using microdescriptors.
89
        Microdesc = 8,
90
        /// Describing the network as a consensus directory document.
91
        Cons = 9,
92
        /// Sending and accepting circuit-level padding
93
        Padding = 10,
94
        /// Improved means of flow control on circuits.
95
        FlowCtrl = 11,
96
        /// Multi-path circuit support.
97
        Conflux = 12,
98
    }
99
}
100

            
101
/// How many recognized protocols are there?
102
const N_RECOGNIZED: usize = 13;
103

            
104
/// Maximum allowable value for a protocol's version field.
105
const MAX_VER: usize = 63;
106

            
107
/// A specific, named subversion of a protocol.
108
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
109
pub struct NamedSubver {
110
    /// The protocol in question
111
    ///
112
    /// Must be in-range for ProtoKind (0..N_RECOGNIZED).
113
    kind: ProtoKind,
114
    /// The version of the protocol
115
    ///
116
    /// Must be in 0..=MAX_VER
117
    version: u8,
118
}
119

            
120
impl NamedSubver {
121
    /// Create a new NamedSubver.
122
    ///
123
    /// # Panics
124
    ///
125
    /// Panics if `kind` is unrecognized or `version` is invalid.
126
    const fn new(kind: ProtoKind, version: u8) -> Self {
127
        assert!((kind.0 as usize) < N_RECOGNIZED);
128
        assert!((version as usize) <= MAX_VER);
129
        Self { kind, version }
130
    }
131
}
132

            
133
/// A subprotocol capability as represented by a (kind, version) tuple.
134
///
135
/// Does not necessarily represent a real subprotocol capability;
136
/// this type is meant for use in other pieces of the protocol.
137
///
138
/// # Ordering
139
///
140
/// Instances of `NumberedSubver` are sorted in lexicographic order by
141
/// their (kind, version) tuples.
142
//
143
// TODO: As with most other types in the crate, we should decide how to rename them as as part
144
// of #1934.
145
#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
146
pub struct NumberedSubver {
147
    /// The protocol in question
148
    kind: ProtoKind,
149
    /// The version of the protocol
150
    version: u8,
151
}
152

            
153
impl NumberedSubver {
154
    /// Construct a new [`NumberedSubver`]
155
432
    pub fn new(kind: impl Into<ProtoKind>, version: u8) -> Self {
156
432
        Self {
157
432
            kind: kind.into(),
158
432
            version,
159
432
        }
160
432
    }
161
    /// Return the ProtoKind and version for this [`NumberedSubver`].
162
    pub fn into_parts(self) -> (ProtoKind, u8) {
163
        (self.kind, self.version)
164
    }
165
}
166
impl From<NamedSubver> for NumberedSubver {
167
480
    fn from(value: NamedSubver) -> Self {
168
480
        Self {
169
480
            kind: value.kind,
170
480
            version: value.version,
171
480
        }
172
480
    }
173
}
174

            
175
#[cfg(feature = "tor-bytes")]
176
impl tor_bytes::Readable for NumberedSubver {
177
480
    fn take_from(b: &mut tor_bytes::Reader<'_>) -> tor_bytes::Result<Self> {
178
480
        let kind = b.take_u8()?;
179
480
        let version = b.take_u8()?;
180
420
        Ok(Self::new(kind, version))
181
480
    }
182
}
183

            
184
#[cfg(feature = "tor-bytes")]
185
impl tor_bytes::Writeable for NumberedSubver {
186
4
    fn write_onto<B: tor_bytes::Writer + ?Sized>(&self, b: &mut B) -> tor_bytes::EncodeResult<()> {
187
4
        b.write_u8(self.kind.into());
188
4
        b.write_u8(self.version);
189
4
        Ok(())
190
4
    }
191
}
192

            
193
/// Representation for a known or unknown protocol.
194
#[derive(Eq, PartialEq, Clone, Debug, Hash, Ord, PartialOrd)]
195
enum Protocol {
196
    /// A known protocol; represented by one of ProtoKind.
197
    ///
198
    /// ProtoKind must always be in the range 0..N_RECOGNIZED.
199
    Proto(ProtoKind),
200
    /// An unknown protocol; represented by its name.
201
    Unrecognized(String),
202
}
203

            
204
impl Protocol {
205
    /// Return true iff `s` is the name of a protocol we do not recognize.
206
638
    fn is_unrecognized(&self, s: &str) -> bool {
207
638
        match self {
208
638
            Protocol::Unrecognized(s2) => s2 == s,
209
            _ => false,
210
        }
211
638
    }
212
    /// Return a string representation of this protocol.
213
1144
    fn to_str(&self) -> &str {
214
1144
        match self {
215
            Protocol::Proto(k) => k.to_str().unwrap_or("<bug>"),
216
1144
            Protocol::Unrecognized(s) => s,
217
        }
218
1144
    }
219
}
220

            
221
/// Representation of a set of versions supported by a protocol.
222
///
223
/// For now, we only use this type for unrecognized protocols.
224
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
225
struct SubprotocolEntry {
226
    /// Which protocol's versions does this describe?
227
    proto: Protocol,
228
    /// A bit-vector defining which versions are supported.  If bit
229
    /// `(1<<i)` is set, then protocol version `i` is supported.
230
    supported: u64,
231
}
232

            
233
/// A set of supported or required subprotocol versions.
234
///
235
/// This type supports both recognized subprotocols (listed in ProtoKind),
236
/// and unrecognized subprotocols (stored by name).
237
///
238
/// To construct an instance, use the FromStr trait:
239
/// ```
240
/// use tor_protover::Protocols;
241
/// let p: Result<Protocols,_> = "Link=1-3 LinkAuth=2-3 Relay=1-2".parse();
242
/// ```
243
///
244
/// # Implementation notes
245
///
246
/// Because the number of distinct `Protocols` sets at any given time
247
/// is much smaller than the number of relays, this type is interned in order to
248
/// save memory and copying time.
249
///
250
/// This type is an Arc internally; it is cheap to clone.
251
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
252
#[cfg_attr(
253
    feature = "serde",
254
    derive(serde_with::DeserializeFromStr, serde_with::SerializeDisplay)
255
)]
256
pub struct Protocols(Arc<ProtocolsInner>);
257

            
258
/// Inner representation of Protocols.
259
///
260
/// We make this a separate type so that we can intern it inside an Arc.
261
#[derive(Default, Clone, Debug, Eq, PartialEq, Hash)]
262
struct ProtocolsInner {
263
    /// A mapping from protocols' integer encodings to bit-vectors.
264
    recognized: [u64; N_RECOGNIZED],
265
    /// A vector of unrecognized protocol versions,
266
    /// in sorted order.
267
    ///
268
    /// Every entry in this list has supported != 0.
269
    unrecognized: Vec<SubprotocolEntry>,
270
}
271

            
272
/// An InternCache of ProtocolsInner.
273
///
274
/// We intern ProtocolsInner objects because:
275
///  - There are very few _distinct_ values in any given set of relays.
276
///  - Every relay has one.
277
///  - We often want to copy them when we're remembering information about circuits.
278
static PROTOCOLS: InternCache<ProtocolsInner> = InternCache::new();
279

            
280
impl From<ProtocolsInner> for Protocols {
281
605480
    fn from(value: ProtocolsInner) -> Self {
282
605480
        Protocols(PROTOCOLS.intern(value))
283
605480
    }
284
}
285

            
286
impl Protocols {
287
    /// Return a new empty set of protocol versions.
288
    ///
289
    /// # Warning
290
    ///
291
    /// To the extend possible, avoid using empty lists to represent the capabilities
292
    /// of an unknown target.  Instead, if there is a consensus present, use the
293
    /// `required-relay-protocols` field of the consensus.
294
360
    pub fn new() -> Self {
295
360
        Protocols::default()
296
360
    }
297

            
298
    /// Helper: return true iff this protocol set contains the
299
    /// version `ver` of the protocol represented by the integer `proto`.
300
1462756
    fn supports_recognized_ver(&self, proto: usize, ver: u8) -> bool {
301
1462756
        if usize::from(ver) > MAX_VER {
302
2
            return false;
303
1462754
        }
304
1462754
        if proto >= self.0.recognized.len() {
305
            return false;
306
1462754
        }
307
1462754
        (self.0.recognized[proto] & (1 << ver)) != 0
308
1462756
    }
309
    /// Helper: return true iff this protocol set contains version
310
    /// `ver` of the unrecognized protocol represented by the string
311
    /// `proto`.
312
    ///
313
    /// Requires that `proto` is not the name of a recognized protocol.
314
10
    fn supports_unrecognized_ver(&self, proto: &str, ver: u8) -> bool {
315
10
        if usize::from(ver) > MAX_VER {
316
2
            return false;
317
8
        }
318
8
        let ent = self
319
8
            .0
320
8
            .unrecognized
321
8
            .iter()
322
12
            .find(|ent| ent.proto.is_unrecognized(proto));
323
8
        match ent {
324
4
            Some(e) => (e.supported & (1 << ver)) != 0,
325
4
            None => false,
326
        }
327
10
    }
328

            
329
    /// Return true if this list of protocols is empty.
330
1080
    pub fn is_empty(&self) -> bool {
331
10638
        self.0.recognized.iter().all(|v| *v == 0)
332
660
            && self.0.unrecognized.iter().all(|p| p.supported == 0)
333
1080
    }
334

            
335
    // TODO: Combine these next two functions into one by using a trait.
336
    /// Check whether a known protocol version is supported.
337
    ///
338
    /// ```
339
    /// use tor_protover::*;
340
    /// let protos: Protocols = "Link=1-3 HSDir=2,4-5".parse().unwrap();
341
    ///
342
    /// assert!(protos.supports_known_subver(ProtoKind::Link, 2));
343
    /// assert!(protos.supports_known_subver(ProtoKind::HSDir, 4));
344
    /// assert!(! protos.supports_known_subver(ProtoKind::HSDir, 3));
345
    /// assert!(! protos.supports_known_subver(ProtoKind::LinkAuth, 3));
346
    /// ```
347
1462632
    pub fn supports_known_subver(&self, proto: ProtoKind, ver: u8) -> bool {
348
1462632
        self.supports_recognized_ver(proto.get() as usize, ver)
349
1462632
    }
350
    /// Check whether a protocol version identified by a string is supported.
351
    ///
352
    /// ```
353
    /// use tor_protover::*;
354
    /// let protos: Protocols = "Link=1-3 Foobar=7".parse().unwrap();
355
    ///
356
    /// assert!(protos.supports_subver("Link", 2));
357
    /// assert!(protos.supports_subver("Foobar", 7));
358
    /// assert!(! protos.supports_subver("Link", 5));
359
    /// assert!(! protos.supports_subver("Foobar", 6));
360
    /// assert!(! protos.supports_subver("Wombat", 3));
361
    /// ```
362
134
    pub fn supports_subver(&self, proto: &str, ver: u8) -> bool {
363
134
        match ProtoKind::from_name(proto) {
364
124
            Some(p) => self.supports_recognized_ver(p.get() as usize, ver),
365
10
            None => self.supports_unrecognized_ver(proto, ver),
366
        }
367
134
    }
368

            
369
    /// Check whether a protocol version is supported.
370
    ///
371
    /// ```
372
    /// use tor_protover::*;
373
    /// let protos: Protocols = "Link=1-5 Desc=2-4".parse().unwrap();
374
    /// assert!(protos.supports_named_subver(named::DESC_FAMILY_IDS)); // Desc=4
375
    /// assert!(! protos.supports_named_subver(named::CONFLUX_BASE)); // Conflux=1
376
    /// ```
377
1462080
    pub fn supports_named_subver(&self, protover: NamedSubver) -> bool {
378
1462080
        self.supports_known_subver(protover.kind, protover.version)
379
1462080
    }
380

            
381
    /// Check whether a numbered subprotocol capability is supported.
382
    ///
383
    /// ```
384
    /// use tor_protover::*;
385
    /// let protos: Protocols = "Link=1-5 Desc=2-4".parse().unwrap();
386
    /// assert!(protos.supports_numbered_subver(NumberedSubver::new(ProtoKind::Desc, 4)));
387
    /// assert!(! protos.supports_numbered_subver(NumberedSubver::new(ProtoKind::Conflux, 1)));
388
    /// ```
389
540
    pub fn supports_numbered_subver(&self, protover: NumberedSubver) -> bool {
390
540
        self.supports_known_subver(protover.kind, protover.version)
391
540
    }
392

            
393
    /// Return a Protocols holding every protocol flag that is present in `self`
394
    /// but not `other`.
395
    ///
396
    /// ```
397
    /// use tor_protover::*;
398
    /// let protos: Protocols = "Desc=2-4 Microdesc=1-5".parse().unwrap();
399
    /// let protos2: Protocols = "Desc=3 Microdesc=3".parse().unwrap();
400
    /// assert_eq!(protos.difference(&protos2),
401
    ///            "Desc=2,4 Microdesc=1-2,4-5".parse().unwrap());
402
    /// ```
403
1152
    pub fn difference(&self, other: &Protocols) -> Protocols {
404
1152
        let mut r = ProtocolsInner::default();
405

            
406
16128
        for i in 0..N_RECOGNIZED {
407
14976
            r.recognized[i] = self.0.recognized[i] & !other.0.recognized[i];
408
14976
        }
409
        // This is not super efficient, but we don't have to do it often.
410
1156
        for ent in self.0.unrecognized.iter() {
411
76
            let mut ent = ent.clone();
412
83
            if let Some(other_ent) = other.0.unrecognized.iter().find(|e| e.proto == ent.proto) {
413
4
                ent.supported &= !other_ent.supported;
414
72
            }
415
76
            if ent.supported != 0 {
416
74
                r.unrecognized.push(ent);
417
74
            }
418
        }
419
1152
        Protocols::from(r)
420
1152
    }
421

            
422
    /// Return a Protocols holding every protocol flag that is present in `self`
423
    /// or `other` or both.
424
    ///
425
    /// ```
426
    /// use tor_protover::*;
427
    /// let protos: Protocols = "Desc=2-4 Microdesc=1-5".parse().unwrap();
428
    /// let protos2: Protocols = "Desc=3 Microdesc=10".parse().unwrap();
429
    /// assert_eq!(protos.union(&protos2),
430
    ///            "Desc=2-4 Microdesc=1-5,10".parse().unwrap());
431
    /// ```
432
3612
    pub fn union(&self, other: &Protocols) -> Protocols {
433
3612
        let mut r = (*self.0).clone();
434
50568
        for i in 0..N_RECOGNIZED {
435
46956
            r.recognized[i] |= other.0.recognized[i];
436
46956
        }
437
3616
        for ent in other.0.unrecognized.iter() {
438
26
            if let Some(my_ent) = r.unrecognized.iter_mut().find(|e| e.proto == ent.proto) {
439
4
                my_ent.supported |= ent.supported;
440
12
            } else {
441
12
                r.unrecognized.push(ent.clone());
442
12
            }
443
        }
444
3612
        r.unrecognized.sort();
445
3612
        Protocols::from(r)
446
3612
    }
447

            
448
    /// Return a Protocols holding every protocol flag that is present in both `self`
449
    /// and `other`.
450
    ///
451
    /// ```
452
    /// use tor_protover::*;
453
    /// let protos: Protocols = "Desc=2-4 Microdesc=1-5".parse().unwrap();
454
    /// let protos2: Protocols = "Desc=3 Microdesc=10".parse().unwrap();
455
    /// assert_eq!(protos.intersection(&protos2),
456
    ///            "Desc=3".parse().unwrap());
457
    /// ```
458
12
    pub fn intersection(&self, other: &Protocols) -> Protocols {
459
12
        let mut r = ProtocolsInner::default();
460
168
        for i in 0..N_RECOGNIZED {
461
156
            r.recognized[i] = self.0.recognized[i] & other.0.recognized[i];
462
156
        }
463
16
        for ent in self.0.unrecognized.iter() {
464
23
            if let Some(other_ent) = other.0.unrecognized.iter().find(|e| e.proto == ent.proto) {
465
4
                let supported = ent.supported & other_ent.supported;
466
4
                if supported != 0 {
467
4
                    r.unrecognized.push(SubprotocolEntry {
468
4
                        proto: ent.proto.clone(),
469
4
                        supported,
470
4
                    });
471
4
                }
472
12
            }
473
        }
474
12
        r.unrecognized.sort();
475
12
        Protocols::from(r)
476
12
    }
477
}
478

            
479
impl ProtocolsInner {
480
    /// Parsing helper: Try to add a new entry `ent` to this set of protocols.
481
    ///
482
    /// Uses `foundmask`, a bit mask saying which recognized protocols
483
    /// we've already found entries for.  Returns an error if `ent` is
484
    /// for a protocol we've already added.
485
    ///
486
    /// Does not preserve sorting order; the caller must call `self.unrecognized.sort()` before returning.
487
402936
    fn add(&mut self, foundmask: &mut u64, ent: SubprotocolEntry) -> Result<(), ParseError> {
488
402936
        match ent.proto {
489
401440
            Protocol::Proto(k) => {
490
401440
                let idx = k.get() as usize;
491
401440
                assert!(idx < N_RECOGNIZED); // guaranteed by invariant on Protocol::Proto
492
401440
                let bit = 1 << u64::from(k.get());
493
401440
                if (*foundmask & bit) != 0 {
494
4
                    return Err(ParseError::Duplicate);
495
401436
                }
496
401436
                *foundmask |= bit;
497
401436
                self.recognized[idx] = ent.supported;
498
            }
499
1496
            Protocol::Unrecognized(ref s) => {
500
1496
                if self
501
1496
                    .unrecognized
502
1496
                    .iter()
503
1521
                    .any(|ent| ent.proto.is_unrecognized(s))
504
                {
505
2
                    return Err(ParseError::Duplicate);
506
1494
                }
507
1494
                if ent.supported != 0 {
508
1494
                    self.unrecognized.push(ent);
509
1494
                }
510
            }
511
        }
512
402930
        Ok(())
513
402936
    }
514
}
515

            
516
/// An error representing a failure to parse a set of protocol versions.
517
#[derive(Error, Debug, PartialEq, Eq, Clone)]
518
#[non_exhaustive]
519
pub enum ParseError {
520
    /// A protocol version was not in the range 0..=63.
521
    #[error("Protocol version out of range")]
522
    OutOfRange,
523
    /// Some subprotocol or protocol version appeared more than once.
524
    #[error("Duplicate protocol entry")]
525
    Duplicate,
526
    /// The list of protocol versions was malformed in some other way.
527
    #[error("Malformed protocol entry")]
528
    Malformed,
529
}
530

            
531
/// Helper: return a new u64 in which bits `lo` through `hi` inclusive
532
/// are set to 1, and all the other bits are set to 0.
533
///
534
/// In other words, `bitrange(a,b)` is how we represent the range of
535
/// versions `a-b` in a protocol version bitmask.
536
///
537
/// ```ignore
538
/// # use tor_protover::bitrange;
539
/// assert_eq!(bitrange(0, 5), 0b111111);
540
/// assert_eq!(bitrange(2, 5), 0b111100);
541
/// assert_eq!(bitrange(2, 7), 0b11111100);
542
/// ```
543
411120
fn bitrange(lo: u64, hi: u64) -> u64 {
544
411120
    assert!(lo <= hi && lo <= 63 && hi <= 63);
545
411120
    let mut mask = !0;
546
411120
    mask <<= 63 - hi;
547
411120
    mask >>= 63 - hi + lo;
548
411120
    mask <<= lo;
549
411120
    mask
550
411120
}
551

            
552
/// Helper: return true if the provided string is a valid "integer"
553
/// in the form accepted by the protover spec.  This is stricter than
554
/// rust's integer parsing format.
555
822262
fn is_good_number(n: &str) -> bool {
556
839417
    n.chars().all(|ch| ch.is_ascii_digit()) && !n.starts_with('0')
557
822262
}
558

            
559
/// A single SubprotocolEntry is parsed from a string of the format
560
/// Name=Versions, where Versions is a comma-separated list of
561
/// integers or ranges of integers.
562
impl std::str::FromStr for SubprotocolEntry {
563
    type Err = ParseError;
564

            
565
402966
    fn from_str(s: &str) -> Result<Self, ParseError> {
566
        // split the string on the =.
567
402964
        let (name, versions) = {
568
402966
            let eq_idx = s.find('=').ok_or(ParseError::Malformed)?;
569
402964
            (&s[..eq_idx], &s[eq_idx + 1..])
570
        };
571
        // Look up the protocol by name.
572
402964
        let proto = match ProtoKind::from_name(name) {
573
401466
            Some(p) => Protocol::Proto(p),
574
1498
            None => Protocol::Unrecognized(name.to_string()),
575
        };
576
402964
        if versions.is_empty() {
577
            // We need to handle this case specially, since otherwise
578
            // it would be treated below as a single empty value, which
579
            // would be rejected.
580
2
            return Ok(SubprotocolEntry {
581
2
                proto,
582
2
                supported: 0,
583
2
            });
584
402962
        }
585
        // Construct a bitmask based on the comma-separated versions.
586
402962
        let mut supported = 0_u64;
587
411136
        for ent in versions.split(',') {
588
            // Find and parse lo and hi for a single range of versions.
589
            // (If this is not a range, but rather a single version v,
590
            // treat it as if it were a range v-v.)
591
411136
            let (lo_s, hi_s) = {
592
411136
                match ent.find('-') {
593
88188
                    Some(pos) => (&ent[..pos], &ent[pos + 1..]),
594
322948
                    None => (ent, ent),
595
                }
596
            };
597
411136
            if !is_good_number(lo_s) {
598
10
                return Err(ParseError::Malformed);
599
411126
            }
600
411126
            if !is_good_number(hi_s) {
601
2
                return Err(ParseError::Malformed);
602
411124
            }
603
411124
            let lo: u64 = lo_s.parse().map_err(|_| ParseError::Malformed)?;
604
411122
            let hi: u64 = hi_s.parse().map_err(|_| ParseError::Malformed)?;
605
            // Make sure that lo and hi are in-bounds and consistent.
606
411118
            if lo > (MAX_VER as u64) || hi > (MAX_VER as u64) {
607
6
                return Err(ParseError::OutOfRange);
608
411112
            }
609
411112
            if lo > hi {
610
2
                return Err(ParseError::Malformed);
611
411110
            }
612
411110
            let mask = bitrange(lo, hi);
613
            // Make sure that no version is included twice.
614
411110
            if (supported & mask) != 0 {
615
2
                return Err(ParseError::Duplicate);
616
411108
            }
617
            // Add the appropriate bits to the mask.
618
411108
            supported |= mask;
619
        }
620
402934
        Ok(SubprotocolEntry { proto, supported })
621
402966
    }
622
}
623

            
624
/// A Protocols set can be parsed from a string according to the
625
/// format used in Tor consensus documents.
626
///
627
/// A protocols set is represented by a space-separated list of
628
/// entries.  Each entry is of the form `Name=Versions`, where `Name`
629
/// is the name of a protocol, and `Versions` is a comma-separated
630
/// list of version numbers and version ranges.  Each version range is
631
/// a pair of integers separated by `-`.
632
///
633
/// No protocol name may be listed twice.  No version may be listed
634
/// twice for a single protocol.  All versions must be in range 0
635
/// through 63 inclusive.
636
impl std::str::FromStr for Protocols {
637
    type Err = ParseError;
638

            
639
567254
    fn from_str(s: &str) -> Result<Self, ParseError> {
640
567254
        let mut result = ProtocolsInner::default();
641
567254
        let mut foundmask = 0_u64;
642
681972
        for ent in s.split(' ') {
643
681972
            if ent.is_empty() {
644
279006
                continue;
645
402966
            }
646

            
647
402966
            let s: SubprotocolEntry = ent.parse()?;
648
402936
            result.add(&mut foundmask, s)?;
649
        }
650
567218
        result.unrecognized.sort();
651
567218
        Ok(result.into())
652
567254
    }
653
}
654

            
655
/// Given a bitmask, return a list of the bits set in the mask, as a
656
/// String in the format expected by Tor consensus documents.
657
///
658
/// This implementation constructs ranges greedily.  For example, the
659
/// bitmask `0b0111011` will be represented as `0-1,3-5`, and not
660
/// `0,1,3,4,5` or `0,1,3-5`.
661
///
662
/// ```ignore
663
/// # use tor_protover::dumpmask;
664
/// assert_eq!(dumpmask(0b111111), "0-5");
665
/// assert_eq!(dumpmask(0b111100), "2-5");
666
/// assert_eq!(dumpmask(0b11111100), "2-7");
667
/// ```
668
3624
fn dumpmask(mut mask: u64) -> String {
669
    /// Helper: push a range (which may be a singleton) onto `v`.
670
3690
    fn append(v: &mut Vec<String>, lo: u32, hi: u32) {
671
3690
        if lo == hi {
672
1574
            v.push(lo.to_string());
673
2116
        } else {
674
2116
            v.push(format!("{}-{}", lo, hi));
675
2116
        }
676
3690
    }
677
    // We'll be building up our result here, then joining it with
678
    // commas.
679
3624
    let mut result = Vec::new();
680
    // This implementation is a little tricky, but it should be more
681
    // efficient than a raw search.  Basically, we're using the
682
    // function u64::trailing_zeros to count how large each range of
683
    // 1s or 0s is, and then shifting by that amount.
684

            
685
    // How many bits have we already shifted `mask`?
686
3624
    let mut shift = 0;
687
7312
    while mask != 0 {
688
3690
        let zeros = mask.trailing_zeros();
689
3690
        mask >>= zeros;
690
3690
        shift += zeros;
691
3690
        let ones = mask.trailing_ones();
692
3690
        append(&mut result, shift, shift + ones - 1);
693
3690
        shift += ones;
694
3690
        if ones == 64 {
695
            // We have to do this check to avoid overflow when formatting
696
            // the range `0-63`.
697
2
            break;
698
3688
        }
699
3688
        mask >>= ones;
700
    }
701
3624
    result.join(",")
702
3624
}
703

            
704
/// The Display trait formats a protocol set in the format expected by Tor
705
/// consensus documents.
706
///
707
/// ```
708
/// use tor_protover::*;
709
/// let protos: Protocols = "Link=1,2,3 Foobar=7 Relay=2".parse().unwrap();
710
/// assert_eq!(format!("{}", protos),
711
///            "Foobar=7 Link=1-3 Relay=2");
712
/// ```
713
impl std::fmt::Display for Protocols {
714
1990
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
715
1990
        let mut entries = Vec::new();
716
25870
        for (idx, mask) in self.0.recognized.iter().enumerate() {
717
25870
            if *mask != 0 {
718
2470
                let pk: ProtoKind = (idx as u8).into();
719
2470
                entries.push(format!("{}={}", pk, dumpmask(*mask)));
720
23400
            }
721
        }
722
1990
        for ent in &self.0.unrecognized {
723
1144
            if ent.supported != 0 {
724
1144
                entries.push(format!(
725
1144
                    "{}={}",
726
1144
                    ent.proto.to_str(),
727
1144
                    dumpmask(ent.supported)
728
1144
                ));
729
1144
            }
730
        }
731
        // This sort is required.
732
1990
        entries.sort();
733
1990
        write!(f, "{}", entries.join(" "))
734
1990
    }
735
}
736

            
737
impl FromIterator<NamedSubver> for Protocols {
738
3903
    fn from_iter<T: IntoIterator<Item = NamedSubver>>(iter: T) -> Self {
739
3903
        let mut r = ProtocolsInner::default();
740
19904
        for named_subver in iter {
741
16001
            let proto_idx = usize::from(named_subver.kind.get());
742
16001
            let proto_ver = named_subver.version;
743

            
744
            // These are guaranteed by invariants on NamedSubver.
745
16001
            assert!(proto_idx < N_RECOGNIZED);
746
16001
            assert!(usize::from(proto_ver) <= MAX_VER);
747
16001
            r.recognized[proto_idx] |= 1_u64 << proto_ver;
748
        }
749
3903
        Protocols::from(r)
750
3903
    }
751
}
752

            
753
/// Documentation: when is a protocol "supported"?
754
///
755
/// Arti should consider itself to "support" a protocol if, _as built_,
756
/// it implements the protocol completely.
757
///
758
/// Just having the protocol listed among the [`named`]
759
/// protocols is not enough, and neither is an incomplete
760
/// or uncompliant implementation.
761
///
762
/// Similarly, if the protocol is not compiled in,
763
/// it is not technically _supported_.
764
///
765
/// When in doubt, ask yourself:
766
/// - If another Tor implementation believed that we implemented this protocol,
767
///   and began to speak it to us, would we be able to do so?
768
/// - If the protocol were required,
769
///   would this software as built actually meet that requirement?
770
///
771
/// If either answer is no, the protocol is not supported.
772
pub mod doc_supported {}
773

            
774
/// Documentation about changing lists of supported versions.
775
///
776
/// # Warning
777
///
778
/// You need to be extremely careful when removing
779
/// _any_ entry from a list of supported protocols.
780
///
781
/// If you remove an entry while it still appears as "recommended" in the consensus,
782
/// you'll cause all the instances without it to warn.
783
///
784
/// If you remove an entry while it still appears as "required" in the
785
///  consensus, you'll cause all the instances without it to refuse to connect
786
/// to the network, and shut down.
787
///
788
/// If you need to remove a version from a list of supported protocols,
789
/// you need to make sure that it is not listed in the _current consensuses_:
790
/// just removing it from the list that the authorities vote for is NOT ENOUGH.
791
/// You need to remove it from the required list,
792
/// and THEN let the authorities upgrade and vote on new
793
/// consensuses without it. Only once those consensuses are out is it safe to
794
/// remove from the list of required protocols.
795
///
796
/// ## Example
797
///
798
/// One concrete example of a very dangerous race that could occur:
799
///
800
/// Suppose that the client supports protocols "HsDir=1-2" and the consensus
801
/// requires protocols "HsDir=1-2".  If the client supported protocol list is
802
/// then changed to "HSDir=2", while the consensus stills lists "HSDir=1-2",
803
/// then these clients, even very recent ones, will shut down because they
804
/// don't support "HSDir=1".
805
///
806
/// And so, changes need to be done in strict sequence as described above.
807
pub mod doc_changing {}
808

            
809
#[cfg(test)]
810
mod test {
811
    // @@ begin test lint list maintained by maint/add_warning @@
812
    #![allow(clippy::bool_assert_comparison)]
813
    #![allow(clippy::clone_on_copy)]
814
    #![allow(clippy::dbg_macro)]
815
    #![allow(clippy::mixed_attributes_style)]
816
    #![allow(clippy::print_stderr)]
817
    #![allow(clippy::print_stdout)]
818
    #![allow(clippy::single_char_pattern)]
819
    #![allow(clippy::unwrap_used)]
820
    #![allow(clippy::unchecked_time_subtraction)]
821
    #![allow(clippy::useless_vec)]
822
    #![allow(clippy::needless_pass_by_value)]
823
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
824
    use std::str::FromStr;
825

            
826
    use super::*;
827

            
828
    #[test]
829
    fn test_bitrange() {
830
        assert_eq!(0b1, bitrange(0, 0));
831
        assert_eq!(0b10, bitrange(1, 1));
832
        assert_eq!(0b11, bitrange(0, 1));
833
        assert_eq!(0b1111110000000, bitrange(7, 12));
834
        assert_eq!(!0, bitrange(0, 63));
835
    }
836

            
837
    #[test]
838
    fn test_dumpmask() {
839
        assert_eq!("", dumpmask(0));
840
        assert_eq!("0-5", dumpmask(0b111111));
841
        assert_eq!("4-5", dumpmask(0b110000));
842
        assert_eq!("1,4-5", dumpmask(0b110010));
843
        assert_eq!("0-63", dumpmask(!0));
844
    }
845

            
846
    #[test]
847
    fn test_canonical() -> Result<(), ParseError> {
848
        fn t(orig: &str, canonical: &str) -> Result<(), ParseError> {
849
            let protos: Protocols = orig.parse()?;
850
            let enc = format!("{}", protos);
851
            assert_eq!(enc, canonical);
852
            Ok(())
853
        }
854

            
855
        t("", "")?;
856
        t(" ", "")?;
857
        t("Link=5,6,7,9 Relay=4-7,2", "Link=5-7,9 Relay=2,4-7")?;
858
        t("FlowCtrl= Padding=8,7 Desc=1-5,6-8", "Desc=1-8 Padding=7-8")?;
859
        t("Zelda=7 Gannon=3,6 Link=4", "Gannon=3,6 Link=4 Zelda=7")?;
860

            
861
        Ok(())
862
    }
863

            
864
    #[test]
865
    fn test_invalid() {
866
        fn t(s: &str) -> ParseError {
867
            let protos: Result<Protocols, ParseError> = s.parse();
868
            assert!(protos.is_err());
869
            protos.err().unwrap()
870
        }
871

            
872
        assert_eq!(t("Link=1-100"), ParseError::OutOfRange);
873
        assert_eq!(t("Zelda=100"), ParseError::OutOfRange);
874
        assert_eq!(t("Link=100-200"), ParseError::OutOfRange);
875

            
876
        assert_eq!(t("Link=1,1"), ParseError::Duplicate);
877
        assert_eq!(t("Link=1 Link=1"), ParseError::Duplicate);
878
        assert_eq!(t("Link=1 Link=3"), ParseError::Duplicate);
879
        assert_eq!(t("Zelda=1 Zelda=3"), ParseError::Duplicate);
880

            
881
        assert_eq!(t("Link=Zelda"), ParseError::Malformed);
882
        assert_eq!(t("Link=6-2"), ParseError::Malformed);
883
        assert_eq!(t("Link=6-"), ParseError::Malformed);
884
        assert_eq!(t("Link=6-,2"), ParseError::Malformed);
885
        assert_eq!(t("Link=1,,2"), ParseError::Malformed);
886
        assert_eq!(t("Link=6-frog"), ParseError::Malformed);
887
        assert_eq!(t("Link=gannon-9"), ParseError::Malformed);
888
        assert_eq!(t("Link Zelda"), ParseError::Malformed);
889

            
890
        assert_eq!(t("Link=01"), ParseError::Malformed);
891
        assert_eq!(t("Link=waffle"), ParseError::Malformed);
892
        assert_eq!(t("Link=1_1"), ParseError::Malformed);
893
    }
894

            
895
    #[test]
896
    fn test_supports() -> Result<(), ParseError> {
897
        let p: Protocols = "Link=4,5-7 Padding=2 Lonk=1-3,5".parse()?;
898

            
899
        assert!(p.supports_known_subver(ProtoKind::Padding, 2));
900
        assert!(!p.supports_known_subver(ProtoKind::Padding, 1));
901
        assert!(p.supports_known_subver(ProtoKind::Link, 6));
902
        assert!(!p.supports_known_subver(ProtoKind::Link, 255));
903
        assert!(!p.supports_known_subver(ProtoKind::Cons, 1));
904
        assert!(!p.supports_known_subver(ProtoKind::Cons, 0));
905
        assert!(p.supports_subver("Link", 6));
906
        assert!(!p.supports_subver("link", 6));
907
        assert!(!p.supports_subver("Cons", 0));
908
        assert!(p.supports_subver("Lonk", 3));
909
        assert!(!p.supports_subver("Lonk", 4));
910
        assert!(!p.supports_subver("lonk", 3));
911
        assert!(!p.supports_subver("Lonk", 64));
912

            
913
        Ok(())
914
    }
915

            
916
    #[test]
917
    fn test_difference() -> Result<(), ParseError> {
918
        let p1: Protocols = "Link=1-10 Desc=5-10 Relay=1,3,5,7,9 Other=7-60 Mine=1-20".parse()?;
919
        let p2: Protocols = "Link=3-4 Desc=1-6 Relay=2-6 Other=8 Theirs=20".parse()?;
920

            
921
        assert_eq!(
922
            p1.difference(&p2),
923
            Protocols::from_str("Link=1-2,5-10 Desc=7-10 Relay=1,7,9 Other=7,9-60 Mine=1-20")?
924
        );
925
        assert_eq!(
926
            p2.difference(&p1),
927
            Protocols::from_str("Desc=1-4 Relay=2,4,6 Theirs=20")?,
928
        );
929

            
930
        let nil = Protocols::default();
931
        assert_eq!(p1.difference(&nil), p1);
932
        assert_eq!(p2.difference(&nil), p2);
933
        assert_eq!(nil.difference(&p1), nil);
934
        assert_eq!(nil.difference(&p2), nil);
935

            
936
        Ok(())
937
    }
938

            
939
    #[test]
940
    fn test_union() -> Result<(), ParseError> {
941
        let p1: Protocols = "Link=1-10 Desc=5-10 Relay=1,3,5,7,9 Other=7-60 Mine=1-20".parse()?;
942
        let p2: Protocols = "Link=3-4 Desc=1-6 Relay=2-6 Other=2,8 Theirs=20".parse()?;
943

            
944
        assert_eq!(
945
            p1.union(&p2),
946
            Protocols::from_str(
947
                "Link=1-10 Desc=1-10 Relay=1-7,9 Other=2,7-60 Theirs=20 Mine=1-20"
948
            )?
949
        );
950
        assert_eq!(
951
            p2.union(&p1),
952
            Protocols::from_str(
953
                "Link=1-10 Desc=1-10 Relay=1-7,9 Other=2,7-60 Theirs=20 Mine=1-20"
954
            )?
955
        );
956

            
957
        let nil = Protocols::default();
958
        assert_eq!(p1.union(&nil), p1);
959
        assert_eq!(p2.union(&nil), p2);
960
        assert_eq!(nil.union(&p1), p1);
961
        assert_eq!(nil.union(&p2), p2);
962

            
963
        Ok(())
964
    }
965

            
966
    #[test]
967
    fn test_intersection() -> Result<(), ParseError> {
968
        let p1: Protocols = "Link=1-10 Desc=5-10 Relay=1,3,5,7,9 Other=7-60 Mine=1-20".parse()?;
969
        let p2: Protocols = "Link=3-4 Desc=1-6 Relay=2-6 Other=2,8 Theirs=20".parse()?;
970

            
971
        assert_eq!(
972
            p1.intersection(&p2),
973
            Protocols::from_str("Link=3-4 Desc=5-6 Relay=3,5 Other=8")?
974
        );
975
        assert_eq!(
976
            p2.intersection(&p1),
977
            Protocols::from_str("Link=3-4 Desc=5-6 Relay=3,5 Other=8")?
978
        );
979

            
980
        let nil = Protocols::default();
981
        assert_eq!(p1.intersection(&nil), nil);
982
        assert_eq!(p2.intersection(&nil), nil);
983
        assert_eq!(nil.intersection(&p1), nil);
984
        assert_eq!(nil.intersection(&p2), nil);
985

            
986
        Ok(())
987
    }
988

            
989
    #[test]
990
    fn from_iter() {
991
        use named as n;
992
        let empty: [NamedSubver; 0] = [];
993
        let prs: Protocols = empty.iter().copied().collect();
994
        assert_eq!(prs, Protocols::default());
995
        let prs: Protocols = empty.into_iter().collect();
996
        assert_eq!(prs, Protocols::default());
997

            
998
        let prs = [
999
            n::LINK_V3,
            n::HSDIR_V3,
            n::LINK_V4,
            n::LINK_V5,
            n::CONFLUX_BASE,
        ]
        .into_iter()
        .collect::<Protocols>();
        assert_eq!(prs, "Link=3-5 HSDir=2 Conflux=1".parse().unwrap());
    }
    #[test]
    fn order_numbered_subvers() {
        // We rely on this sort order elsewhere in our protocol.
        assert!(NumberedSubver::new(5, 7) < NumberedSubver::new(7, 5));
        assert!(NumberedSubver::new(7, 5) < NumberedSubver::new(7, 6));
        assert!(NumberedSubver::new(7, 6) < NumberedSubver::new(8, 6));
    }
}