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
#![deny(clippy::string_slice)] // See arti#2571
48
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
49

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

            
53
use std::sync::Arc;
54

            
55
use caret::caret_int;
56

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

            
60
pub mod named;
61

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
281
impl From<ProtocolsInner> for Protocols {
282
689232
    fn from(value: ProtocolsInner) -> Self {
283
        // TODO: Use Intern more natively.
284
689232
        Protocols(PROTOCOLS.intern(value).into())
285
689232
    }
286
}
287

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

            
300
    /// Construct a new [`Protocols`] from a single recognized kind and a list of associated versions.
301
    ///
302
    /// (This method should not usually be needed for new parts of Arti:
303
    /// its only use-case is a legacy piece of hsdesc parsing.)
304
1216
    pub fn from_kind_and_versions(kind: ProtoKind, versions: &str) -> Result<Self, ParseError> {
305
1216
        let versions = parse_version_mask(versions)?;
306
1216
        let mut protocols = ProtocolsInner::default();
307

            
308
1216
        if let Some(p) = protocols.recognized.get_mut(usize::from(kind.get())) {
309
1216
            *p = versions;
310
1216
        } else {
311
            return Err(ParseError::Malformed);
312
        }
313

            
314
1216
        Ok(protocols.into())
315
1216
    }
316

            
317
    /// Helper: return true iff this protocol set contains the
318
    /// version `ver` of the protocol represented by the integer `proto`.
319
1589008
    fn supports_recognized_ver(&self, proto: usize, ver: u8) -> bool {
320
1589008
        if usize::from(ver) > MAX_VER {
321
2
            return false;
322
1589006
        }
323
1589006
        if proto >= self.0.recognized.len() {
324
            return false;
325
1589006
        }
326
1589006
        (self.0.recognized[proto] & (1 << ver)) != 0
327
1589008
    }
328
    /// Helper: return true iff this protocol set contains version
329
    /// `ver` of the unrecognized protocol represented by the string
330
    /// `proto`.
331
    ///
332
    /// Requires that `proto` is not the name of a recognized protocol.
333
10
    fn supports_unrecognized_ver(&self, proto: &str, ver: u8) -> bool {
334
10
        if usize::from(ver) > MAX_VER {
335
2
            return false;
336
8
        }
337
8
        let ent = self
338
8
            .0
339
8
            .unrecognized
340
8
            .iter()
341
12
            .find(|ent| ent.proto.is_unrecognized(proto));
342
8
        match ent {
343
4
            Some(e) => (e.supported & (1 << ver)) != 0,
344
4
            None => false,
345
        }
346
10
    }
347

            
348
    /// Return true if this list of protocols is empty.
349
12224
    pub fn is_empty(&self) -> bool {
350
104767
        self.0.recognized.iter().all(|v| *v == 0)
351
6720
            && self.0.unrecognized.iter().all(|p| p.supported == 0)
352
12224
    }
353

            
354
    // TODO: Combine these next two functions into one by using a trait.
355
    /// Check whether a known protocol version is supported.
356
    ///
357
    /// ```
358
    /// use tor_protover::*;
359
    /// let protos: Protocols = "Link=1-3 HSDir=2,4-5".parse().unwrap();
360
    ///
361
    /// assert!(protos.supports_known_subver(ProtoKind::Link, 2));
362
    /// assert!(protos.supports_known_subver(ProtoKind::HSDir, 4));
363
    /// assert!(! protos.supports_known_subver(ProtoKind::HSDir, 3));
364
    /// assert!(! protos.supports_known_subver(ProtoKind::LinkAuth, 3));
365
    /// ```
366
1588876
    pub fn supports_known_subver(&self, proto: ProtoKind, ver: u8) -> bool {
367
1588876
        self.supports_recognized_ver(proto.get() as usize, ver)
368
1588876
    }
369
    /// Check whether a protocol version identified by a string is supported.
370
    ///
371
    /// ```
372
    /// use tor_protover::*;
373
    /// let protos: Protocols = "Link=1-3 Foobar=7".parse().unwrap();
374
    ///
375
    /// assert!(protos.supports_subver("Link", 2));
376
    /// assert!(protos.supports_subver("Foobar", 7));
377
    /// assert!(! protos.supports_subver("Link", 5));
378
    /// assert!(! protos.supports_subver("Foobar", 6));
379
    /// assert!(! protos.supports_subver("Wombat", 3));
380
    /// ```
381
142
    pub fn supports_subver(&self, proto: &str, ver: u8) -> bool {
382
142
        match ProtoKind::from_name(proto) {
383
132
            Some(p) => self.supports_recognized_ver(p.get() as usize, ver),
384
10
            None => self.supports_unrecognized_ver(proto, ver),
385
        }
386
142
    }
387

            
388
    /// Check whether a protocol version is supported.
389
    ///
390
    /// ```
391
    /// use tor_protover::*;
392
    /// let protos: Protocols = "Link=1-5 Desc=2-4".parse().unwrap();
393
    /// assert!(protos.supports_named_subver(named::DESC_FAMILY_IDS)); // Desc=4
394
    /// assert!(! protos.supports_named_subver(named::CONFLUX_BASE)); // Conflux=1
395
    /// ```
396
1588288
    pub fn supports_named_subver(&self, protover: NamedSubver) -> bool {
397
1588288
        self.supports_known_subver(protover.kind, protover.version)
398
1588288
    }
399

            
400
    /// Check whether a numbered subprotocol capability is supported.
401
    ///
402
    /// ```
403
    /// use tor_protover::*;
404
    /// let protos: Protocols = "Link=1-5 Desc=2-4".parse().unwrap();
405
    /// assert!(protos.supports_numbered_subver(NumberedSubver::new(ProtoKind::Desc, 4)));
406
    /// assert!(! protos.supports_numbered_subver(NumberedSubver::new(ProtoKind::Conflux, 1)));
407
    /// ```
408
576
    pub fn supports_numbered_subver(&self, protover: NumberedSubver) -> bool {
409
576
        self.supports_known_subver(protover.kind, protover.version)
410
576
    }
411

            
412
    /// Return a Protocols holding every protocol flag that is present in `self`
413
    /// but not `other`.
414
    ///
415
    /// ```
416
    /// use tor_protover::*;
417
    /// let protos: Protocols = "Desc=2-4 Microdesc=1-5".parse().unwrap();
418
    /// let protos2: Protocols = "Desc=3 Microdesc=3".parse().unwrap();
419
    /// assert_eq!(protos.difference(&protos2),
420
    ///            "Desc=2,4 Microdesc=1-2,4-5".parse().unwrap());
421
    /// ```
422
1036
    pub fn difference(&self, other: &Protocols) -> Protocols {
423
1036
        let mut r = ProtocolsInner::default();
424

            
425
13468
        for i in 0..N_RECOGNIZED {
426
13468
            r.recognized[i] = self.0.recognized[i] & !other.0.recognized[i];
427
13468
        }
428
        // This is not super efficient, but we don't have to do it often.
429
1040
        for ent in self.0.unrecognized.iter() {
430
80
            let mut ent = ent.clone();
431
87
            if let Some(other_ent) = other.0.unrecognized.iter().find(|e| e.proto == ent.proto) {
432
4
                ent.supported &= !other_ent.supported;
433
76
            }
434
80
            if ent.supported != 0 {
435
78
                r.unrecognized.push(ent);
436
78
            }
437
        }
438
1036
        Protocols::from(r)
439
1036
    }
440

            
441
    /// Return a Protocols holding every protocol flag that is present in `self`
442
    /// or `other` or both.
443
    ///
444
    /// ```
445
    /// use tor_protover::*;
446
    /// let protos: Protocols = "Desc=2-4 Microdesc=1-5".parse().unwrap();
447
    /// let protos2: Protocols = "Desc=3 Microdesc=10".parse().unwrap();
448
    /// assert_eq!(protos.union(&protos2),
449
    ///            "Desc=2-4 Microdesc=1-5,10".parse().unwrap());
450
    /// ```
451
5772
    pub fn union(&self, other: &Protocols) -> Protocols {
452
5772
        let mut r = (*self.0).clone();
453
75036
        for i in 0..N_RECOGNIZED {
454
75036
            r.recognized[i] |= other.0.recognized[i];
455
75036
        }
456
5776
        for ent in other.0.unrecognized.iter() {
457
26
            if let Some(my_ent) = r.unrecognized.iter_mut().find(|e| e.proto == ent.proto) {
458
4
                my_ent.supported |= ent.supported;
459
12
            } else {
460
12
                r.unrecognized.push(ent.clone());
461
12
            }
462
        }
463
5772
        r.unrecognized.sort();
464
5772
        Protocols::from(r)
465
5772
    }
466

            
467
    /// Return a Protocols holding every protocol flag that is present in both `self`
468
    /// and `other`.
469
    ///
470
    /// ```
471
    /// use tor_protover::*;
472
    /// let protos: Protocols = "Desc=2-4 Microdesc=1-5".parse().unwrap();
473
    /// let protos2: Protocols = "Desc=3 Microdesc=10".parse().unwrap();
474
    /// assert_eq!(protos.intersection(&protos2),
475
    ///            "Desc=3".parse().unwrap());
476
    /// ```
477
20876
    pub fn intersection(&self, other: &Protocols) -> Protocols {
478
20876
        let mut r = ProtocolsInner::default();
479
271388
        for i in 0..N_RECOGNIZED {
480
271388
            r.recognized[i] = self.0.recognized[i] & other.0.recognized[i];
481
271388
        }
482
20880
        for ent in self.0.unrecognized.iter() {
483
23
            if let Some(other_ent) = other.0.unrecognized.iter().find(|e| e.proto == ent.proto) {
484
4
                let supported = ent.supported & other_ent.supported;
485
4
                if supported != 0 {
486
4
                    r.unrecognized.push(SubprotocolEntry {
487
4
                        proto: ent.proto.clone(),
488
4
                        supported,
489
4
                    });
490
4
                }
491
12
            }
492
        }
493
20876
        r.unrecognized.sort();
494
20876
        Protocols::from(r)
495
20876
    }
496
}
497

            
498
impl ProtocolsInner {
499
    /// Parsing helper: Try to add a new entry `ent` to this set of protocols.
500
    ///
501
    /// Uses `foundmask`, a bit mask saying which recognized protocols
502
    /// we've already found entries for.  Returns an error if `ent` is
503
    /// for a recognized protocol we've already added.
504
    ///
505
    /// WARNING: This method DOES NOT enforce uniqueness for unrecognized protocols.
506
    /// The caller is responsible for doing that.
507
    ///
508
    /// Does not preserve sorting order; the caller must call `self.unrecognized.sort()` before returning.
509
497244
    fn add(&mut self, foundmask: &mut u64, ent: SubprotocolEntry) -> Result<(), ParseError> {
510
497244
        match ent.proto {
511
495588
            Protocol::Proto(k) => {
512
495588
                let idx = k.get() as usize;
513
495588
                assert!(idx < N_RECOGNIZED); // guaranteed by invariant on Protocol::Proto
514
495588
                let bit = 1 << u64::from(k.get());
515
495588
                if (*foundmask & bit) != 0 {
516
4
                    return Err(ParseError::Duplicate);
517
495584
                }
518
495584
                *foundmask |= bit;
519
495584
                self.recognized[idx] = ent.supported;
520
            }
521
            Protocol::Unrecognized(_) => {
522
1656
                if ent.supported != 0 {
523
1656
                    self.unrecognized.push(ent);
524
1656
                }
525
            }
526
        }
527
497240
        Ok(())
528
497244
    }
529
}
530

            
531
/// An error representing a failure to parse a set of protocol versions.
532
#[derive(Error, Debug, PartialEq, Eq, Clone)]
533
#[non_exhaustive]
534
pub enum ParseError {
535
    /// A protocol version was not in the range 1..=63.
536
    #[error("Protocol version out of range")]
537
    OutOfRange,
538
    /// Some subprotocol or protocol version appeared more than once.
539
    #[error("Duplicate protocol entry")]
540
    Duplicate,
541
    /// The list of protocol versions was malformed in some other way.
542
    #[error("Malformed protocol entry")]
543
    Malformed,
544
}
545

            
546
/// Helper: return a new u64 in which bits `lo` through `hi` inclusive
547
/// are set to 1, and all the other bits are set to 0.
548
///
549
/// In other words, `bitrange(a,b)` is how we represent the range of
550
/// versions `a-b` in a protocol version bitmask.
551
///
552
/// ```ignore
553
/// # use tor_protover::bitrange;
554
/// assert_eq!(bitrange(0, 5), 0b111111);
555
/// assert_eq!(bitrange(2, 5), 0b111100);
556
/// assert_eq!(bitrange(2, 7), 0b11111100);
557
/// ```
558
506160
fn bitrange(lo: u64, hi: u64) -> u64 {
559
506160
    assert!(lo <= hi && lo <= 63 && hi <= 63);
560
506160
    let mut mask = !0;
561
506160
    mask <<= 63 - hi;
562
506160
    mask >>= 63 - hi + lo;
563
506160
    mask <<= lo;
564
506160
    mask
565
506160
}
566

            
567
/// Helper: return true if the provided string is a valid "integer"
568
/// in the form accepted by the protover spec.  This is stricter than
569
/// rust's integer parsing format.
570
1012342
fn is_good_number(n: &str) -> bool {
571
1032011
    n.chars().all(|ch| ch.is_ascii_digit()) && !n.starts_with('0')
572
1012342
}
573

            
574
/// Parse a version-list in `versions` into a bitmask.
575
#[allow(clippy::string_slice)] // TODO
576
498488
fn parse_version_mask(versions: &str) -> Result<u64, ParseError> {
577
498488
    if versions.is_empty() {
578
        // We need to handle this case specially, since otherwise
579
        // it would be treated below as a single empty value, which
580
        // would be rejected.
581
2
        return Ok(0);
582
498486
    }
583
    // Construct a bitmask based on the comma-separated versions.
584
498486
    let mut supported = 0_u64;
585
506176
    for ent in versions.split(',') {
586
        // Find and parse lo and hi for a single range of versions.
587
        // (If this is not a range, but rather a single version v,
588
        // treat it as if it were a range v-v.)
589
506176
        let (lo_s, hi_s) = ent.split_once('-').unwrap_or((ent, ent));
590

            
591
506176
        if !is_good_number(lo_s) {
592
10
            return Err(ParseError::Malformed);
593
506166
        }
594
506166
        if !is_good_number(hi_s) {
595
2
            return Err(ParseError::Malformed);
596
506164
        }
597
506164
        let lo: u64 = lo_s.parse().map_err(|_| ParseError::Malformed)?;
598
506162
        let hi: u64 = hi_s.parse().map_err(|_| ParseError::Malformed)?;
599
        // Make sure that lo and hi are in-bounds and consistent.
600
506158
        if lo > (MAX_VER as u64) || hi > (MAX_VER as u64) {
601
6
            return Err(ParseError::OutOfRange);
602
506152
        }
603
506152
        if lo > hi {
604
2
            return Err(ParseError::Malformed);
605
506150
        }
606
506150
        let mask = bitrange(lo, hi);
607
        // Make sure that no version is included twice.
608
506150
        if (supported & mask) != 0 {
609
2
            return Err(ParseError::Duplicate);
610
506148
        }
611
        // Add the appropriate bits to the mask.
612
506148
        supported |= mask;
613
    }
614

            
615
498458
    Ok(supported)
616
498488
}
617

            
618
/// A single SubprotocolEntry is parsed from a string of the format
619
/// Name=Versions, where Versions is a comma-separated list of
620
/// integers or ranges of integers.
621
impl std::str::FromStr for SubprotocolEntry {
622
    type Err = ParseError;
623

            
624
497274
    fn from_str(s: &str) -> Result<Self, ParseError> {
625
        // split the string on the =.
626
497274
        let (name, versions) = s.split_once('=').ok_or(ParseError::Malformed)?;
627

            
628
        // Look up the protocol by name.
629
497272
        let proto = match ProtoKind::from_name(name) {
630
495614
            Some(p) => Protocol::Proto(p),
631
1658
            None => Protocol::Unrecognized(name.to_string()),
632
        };
633
        Ok(SubprotocolEntry {
634
497272
            proto,
635
497272
            supported: parse_version_mask(versions)?,
636
        })
637
497274
    }
638
}
639

            
640
/// A Protocols set can be parsed from a string according to the
641
/// format used in Tor consensus documents.
642
///
643
/// A protocols set is represented by a space-separated list of
644
/// entries.  Each entry is of the form `Name=Versions`, where `Name`
645
/// is the name of a protocol, and `Versions` is a comma-separated
646
/// list of version numbers and version ranges.  Each version range is
647
/// a pair of integers separated by `-`.
648
///
649
/// No protocol name may be listed twice.  No version may be listed
650
/// twice for a single protocol.  All versions must be in range 0
651
/// through 63 inclusive.
652
impl std::str::FromStr for Protocols {
653
    type Err = ParseError;
654

            
655
600778
    fn from_str(s: &str) -> Result<Self, ParseError> {
656
600778
        let mut result = ProtocolsInner::default();
657
600778
        let mut foundmask = 0_u64;
658
785088
        for ent in s.split(' ') {
659
785088
            if ent.is_empty() {
660
287814
                continue;
661
497274
            }
662

            
663
497274
            let s: SubprotocolEntry = ent.parse()?;
664
497244
            result.add(&mut foundmask, s)?;
665
        }
666
600744
        result.unrecognized.sort();
667
600744
        if result
668
600744
            .unrecognized
669
600744
            .windows(2)
670
600767
            .any(|w| w[0].proto == w[1].proto)
671
        {
672
2
            return Err(ParseError::Duplicate);
673
600742
        }
674

            
675
600742
        Ok(result.into())
676
600778
    }
677
}
678

            
679
/// Given a bitmask, return a list of the bits set in the mask, as a
680
/// String in the format expected by Tor consensus documents.
681
///
682
/// This implementation constructs ranges greedily.  For example, the
683
/// bitmask `0b0111011` will be represented as `0-1,3-5`, and not
684
/// `0,1,3,4,5` or `0,1,3-5`.
685
///
686
/// ```ignore
687
/// # use tor_protover::dumpmask;
688
/// assert_eq!(dumpmask(0b111111), "0-5");
689
/// assert_eq!(dumpmask(0b111100), "2-5");
690
/// assert_eq!(dumpmask(0b11111100), "2-7");
691
/// ```
692
55704
fn dumpmask(mut mask: u64) -> String {
693
    /// Helper: push a range (which may be a singleton) onto `v`.
694
55774
    fn append(v: &mut Vec<String>, lo: u32, hi: u32) {
695
55774
        if lo == hi {
696
20046
            v.push(lo.to_string());
697
35728
        } else {
698
35728
            v.push(format!("{}-{}", lo, hi));
699
35728
        }
700
55774
    }
701
    // We'll be building up our result here, then joining it with
702
    // commas.
703
55704
    let mut result = Vec::new();
704
    // This implementation is a little tricky, but it should be more
705
    // efficient than a raw search.  Basically, we're using the
706
    // function u64::trailing_zeros to count how large each range of
707
    // 1s or 0s is, and then shifting by that amount.
708

            
709
    // How many bits have we already shifted `mask`?
710
55704
    let mut shift = 0;
711
111476
    while mask != 0 {
712
55774
        let zeros = mask.trailing_zeros();
713
55774
        mask >>= zeros;
714
55774
        shift += zeros;
715
55774
        let ones = mask.trailing_ones();
716
55774
        append(&mut result, shift, shift + ones - 1);
717
55774
        shift += ones;
718
55774
        if ones == 64 {
719
            // We have to do this check to avoid overflow when formatting
720
            // the range `0-63`.
721
2
            break;
722
55772
        }
723
55772
        mask >>= ones;
724
    }
725
55704
    result.join(",")
726
55704
}
727

            
728
/// The Display trait formats a protocol set in the format expected by Tor
729
/// consensus documents.
730
///
731
/// ```
732
/// use tor_protover::*;
733
/// let protos: Protocols = "Link=1,2,3 Foobar=7 Relay=2".parse().unwrap();
734
/// assert_eq!(format!("{}", protos),
735
///            "Foobar=7 Link=1-3 Relay=2");
736
/// ```
737
impl std::fmt::Display for Protocols {
738
15818
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
739
15818
        let mut entries = Vec::new();
740
205634
        for (idx, mask) in self.0.recognized.iter().enumerate() {
741
205634
            if *mask != 0 {
742
54410
                let pk: ProtoKind = (idx as u8).into();
743
54410
                entries.push(format!("{}={}", pk, dumpmask(*mask)));
744
151224
            }
745
        }
746
15818
        for ent in &self.0.unrecognized {
747
1284
            if ent.supported != 0 {
748
1284
                entries.push(format!(
749
1284
                    "{}={}",
750
1284
                    ent.proto.to_str(),
751
1284
                    dumpmask(ent.supported)
752
1284
                ));
753
1284
            }
754
        }
755
        // This sort is required.
756
15818
        entries.sort();
757
15818
        write!(f, "{}", entries.join(" "))
758
15818
    }
759
}
760

            
761
impl FromIterator<NamedSubver> for Protocols {
762
18304
    fn from_iter<T: IntoIterator<Item = NamedSubver>>(iter: T) -> Self {
763
18304
        let mut r = ProtocolsInner::default();
764
127519
        for named_subver in iter {
765
127518
            let proto_idx = usize::from(named_subver.kind.get());
766
127518
            let proto_ver = named_subver.version;
767

            
768
            // These are guaranteed by invariants on NamedSubver.
769
127518
            assert!(proto_idx < N_RECOGNIZED);
770
127518
            assert!(usize::from(proto_ver) <= MAX_VER);
771
127518
            r.recognized[proto_idx] |= 1_u64 << proto_ver;
772
        }
773
18304
        Protocols::from(r)
774
18304
    }
775
}
776

            
777
/// Documentation: when is a protocol "supported"?
778
///
779
/// Arti should consider itself to "support" a protocol if, _as built_,
780
/// it implements the protocol completely.
781
///
782
/// Just having the protocol listed among the [`named`]
783
/// protocols is not enough, and neither is an incomplete
784
/// or uncompliant implementation.
785
///
786
/// Similarly, if the protocol is not compiled in,
787
/// it is not technically _supported_.
788
///
789
/// When in doubt, ask yourself:
790
/// - If another Tor implementation believed that we implemented this protocol,
791
///   and began to speak it to us, would we be able to do so?
792
/// - If the protocol were required,
793
///   would this software as built actually meet that requirement?
794
///
795
/// If either answer is no, the protocol is not supported.
796
pub mod doc_supported {}
797

            
798
/// Documentation about changing lists of supported versions.
799
///
800
/// # Warning
801
///
802
/// You need to be extremely careful when removing
803
/// _any_ entry from a list of supported protocols.
804
///
805
/// If you remove an entry while it still appears as "recommended" in the consensus,
806
/// you'll cause all the instances without it to warn.
807
///
808
/// If you remove an entry while it still appears as "required" in the
809
///  consensus, you'll cause all the instances without it to refuse to connect
810
/// to the network, and shut down.
811
///
812
/// If you need to remove a version from a list of supported protocols,
813
/// you need to make sure that it is not listed in the _current consensuses_:
814
/// just removing it from the list that the authorities vote for is NOT ENOUGH.
815
/// You need to remove it from the required list,
816
/// and THEN let the authorities upgrade and vote on new
817
/// consensuses without it. Only once those consensuses are out is it safe to
818
/// remove from the list of required protocols.
819
///
820
/// ## Example
821
///
822
/// One concrete example of a very dangerous race that could occur:
823
///
824
/// Suppose that the client supports protocols "HsDir=1-2" and the consensus
825
/// requires protocols "HsDir=1-2".  If the client supported protocol list is
826
/// then changed to "HSDir=2", while the consensus stills lists "HSDir=1-2",
827
/// then these clients, even very recent ones, will shut down because they
828
/// don't support "HSDir=1".
829
///
830
/// And so, changes need to be done in strict sequence as described above.
831
pub mod doc_changing {}
832

            
833
#[cfg(test)]
834
mod test {
835
    // @@ begin test lint list maintained by maint/add_warning @@
836
    #![allow(clippy::bool_assert_comparison)]
837
    #![allow(clippy::clone_on_copy)]
838
    #![allow(clippy::dbg_macro)]
839
    #![allow(clippy::mixed_attributes_style)]
840
    #![allow(clippy::print_stderr)]
841
    #![allow(clippy::print_stdout)]
842
    #![allow(clippy::single_char_pattern)]
843
    #![allow(clippy::unwrap_used)]
844
    #![allow(clippy::unchecked_time_subtraction)]
845
    #![allow(clippy::useless_vec)]
846
    #![allow(clippy::needless_pass_by_value)]
847
    #![allow(clippy::string_slice)] // See arti#2571
848
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
849
    use std::str::FromStr;
850

            
851
    use super::*;
852

            
853
    #[test]
854
    fn test_bitrange() {
855
        assert_eq!(0b1, bitrange(0, 0));
856
        assert_eq!(0b10, bitrange(1, 1));
857
        assert_eq!(0b11, bitrange(0, 1));
858
        assert_eq!(0b1111110000000, bitrange(7, 12));
859
        assert_eq!(!0, bitrange(0, 63));
860
    }
861

            
862
    #[test]
863
    fn test_dumpmask() {
864
        assert_eq!("", dumpmask(0));
865
        assert_eq!("0-5", dumpmask(0b111111));
866
        assert_eq!("4-5", dumpmask(0b110000));
867
        assert_eq!("1,4-5", dumpmask(0b110010));
868
        assert_eq!("0-63", dumpmask(!0));
869
    }
870

            
871
    #[test]
872
    fn test_canonical() -> Result<(), ParseError> {
873
        fn t(orig: &str, canonical: &str) -> Result<(), ParseError> {
874
            let protos: Protocols = orig.parse()?;
875
            let enc = format!("{}", protos);
876
            assert_eq!(enc, canonical);
877
            Ok(())
878
        }
879

            
880
        t("", "")?;
881
        t(" ", "")?;
882
        t("Link=5,6,7,9 Relay=4-7,2", "Link=5-7,9 Relay=2,4-7")?;
883
        t("FlowCtrl= Padding=8,7 Desc=1-5,6-8", "Desc=1-8 Padding=7-8")?;
884
        t("Zelda=7 Gannon=3,6 Link=4", "Gannon=3,6 Link=4 Zelda=7")?;
885

            
886
        Ok(())
887
    }
888

            
889
    #[test]
890
    fn test_invalid() {
891
        fn t(s: &str) -> ParseError {
892
            let protos: Result<Protocols, ParseError> = s.parse();
893
            assert!(protos.is_err());
894
            protos.err().unwrap()
895
        }
896

            
897
        assert_eq!(t("Link=1-100"), ParseError::OutOfRange);
898
        assert_eq!(t("Zelda=100"), ParseError::OutOfRange);
899
        assert_eq!(t("Link=100-200"), ParseError::OutOfRange);
900

            
901
        assert_eq!(t("Link=1,1"), ParseError::Duplicate);
902
        assert_eq!(t("Link=1 Link=1"), ParseError::Duplicate);
903
        assert_eq!(t("Link=1 Link=3"), ParseError::Duplicate);
904
        assert_eq!(t("Zelda=1 Zelda=3"), ParseError::Duplicate);
905

            
906
        assert_eq!(t("Link=Zelda"), ParseError::Malformed);
907
        assert_eq!(t("Link=6-2"), ParseError::Malformed);
908
        assert_eq!(t("Link=6-"), ParseError::Malformed);
909
        assert_eq!(t("Link=6-,2"), ParseError::Malformed);
910
        assert_eq!(t("Link=1,,2"), ParseError::Malformed);
911
        assert_eq!(t("Link=6-frog"), ParseError::Malformed);
912
        assert_eq!(t("Link=gannon-9"), ParseError::Malformed);
913
        assert_eq!(t("Link Zelda"), ParseError::Malformed);
914

            
915
        assert_eq!(t("Link=01"), ParseError::Malformed);
916
        assert_eq!(t("Link=waffle"), ParseError::Malformed);
917
        assert_eq!(t("Link=1_1"), ParseError::Malformed);
918
    }
919

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

            
924
        assert!(p.supports_known_subver(ProtoKind::Padding, 2));
925
        assert!(!p.supports_known_subver(ProtoKind::Padding, 1));
926
        assert!(p.supports_known_subver(ProtoKind::Link, 6));
927
        assert!(!p.supports_known_subver(ProtoKind::Link, 255));
928
        assert!(!p.supports_known_subver(ProtoKind::Cons, 1));
929
        assert!(!p.supports_known_subver(ProtoKind::Cons, 0));
930
        assert!(p.supports_subver("Link", 6));
931
        assert!(!p.supports_subver("link", 6));
932
        assert!(!p.supports_subver("Cons", 0));
933
        assert!(p.supports_subver("Lonk", 3));
934
        assert!(!p.supports_subver("Lonk", 4));
935
        assert!(!p.supports_subver("lonk", 3));
936
        assert!(!p.supports_subver("Lonk", 64));
937

            
938
        Ok(())
939
    }
940

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

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

            
955
        let nil = Protocols::default();
956
        assert_eq!(p1.difference(&nil), p1);
957
        assert_eq!(p2.difference(&nil), p2);
958
        assert_eq!(nil.difference(&p1), nil);
959
        assert_eq!(nil.difference(&p2), nil);
960

            
961
        Ok(())
962
    }
963

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

            
969
        assert_eq!(
970
            p1.union(&p2),
971
            Protocols::from_str(
972
                "Link=1-10 Desc=1-10 Relay=1-7,9 Other=2,7-60 Theirs=20 Mine=1-20"
973
            )?
974
        );
975
        assert_eq!(
976
            p2.union(&p1),
977
            Protocols::from_str(
978
                "Link=1-10 Desc=1-10 Relay=1-7,9 Other=2,7-60 Theirs=20 Mine=1-20"
979
            )?
980
        );
981

            
982
        let nil = Protocols::default();
983
        assert_eq!(p1.union(&nil), p1);
984
        assert_eq!(p2.union(&nil), p2);
985
        assert_eq!(nil.union(&p1), p1);
986
        assert_eq!(nil.union(&p2), p2);
987

            
988
        Ok(())
989
    }
990

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

            
996
        assert_eq!(
997
            p1.intersection(&p2),
998
            Protocols::from_str("Link=3-4 Desc=5-6 Relay=3,5 Other=8")?
999
        );
        assert_eq!(
            p2.intersection(&p1),
            Protocols::from_str("Link=3-4 Desc=5-6 Relay=3,5 Other=8")?
        );
        let nil = Protocols::default();
        assert_eq!(p1.intersection(&nil), nil);
        assert_eq!(p2.intersection(&nil), nil);
        assert_eq!(nil.intersection(&p1), nil);
        assert_eq!(nil.intersection(&p2), nil);
        Ok(())
    }
    #[test]
    fn from_iter() {
        use named as n;
        let empty: [NamedSubver; 0] = [];
        let prs: Protocols = empty.iter().copied().collect();
        assert_eq!(prs, Protocols::default());
        let prs: Protocols = empty.into_iter().collect();
        assert_eq!(prs, Protocols::default());
        let prs = [
            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));
    }
}