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
#![allow(clippy::cognitive_complexity)] // See arti#2556
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
#![allow(recursion_depth_exceeding_limit)] // arti#2715, rust/issues/159228
49
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
50

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

            
54
use caret::caret_int;
55

            
56
use derive_deftly::Deftly;
57
use thiserror::Error;
58
use tor_basic_utils::intern::{GloballyInternable as _, Intern};
59

            
60
pub mod named;
61

            
62
/// Types we export for macros.
63
#[doc(hidden)]
64
pub mod macro_export {
65
    pub use paste;
66
}
67

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

            
108
/// How many recognized protocols are there?
109
const N_RECOGNIZED: usize = 13;
110

            
111
/// Maximum allowable value for a protocol's version field.
112
const MAX_VER: usize = 63;
113

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

            
127
impl NamedSubver {
128
    /// Create a new NamedSubver.
129
    ///
130
    /// # Panics
131
    ///
132
    /// Panics if `kind` is unrecognized or `version` is invalid.
133
    const fn new(kind: ProtoKind, version: u8) -> Self {
134
        assert!((kind.0 as usize) < N_RECOGNIZED);
135
        assert!((version as usize) <= MAX_VER);
136
        Self { kind, version }
137
    }
138
}
139

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

            
160
impl NumberedSubver {
161
    /// Construct a new [`NumberedSubver`]
162
474
    pub fn new(kind: impl Into<ProtoKind>, version: u8) -> Self {
163
474
        Self {
164
474
            kind: kind.into(),
165
474
            version,
166
474
        }
167
474
    }
168
    /// Return the ProtoKind and version for this [`NumberedSubver`].
169
    pub fn into_parts(self) -> (ProtoKind, u8) {
170
        (self.kind, self.version)
171
    }
172
}
173
impl From<NamedSubver> for NumberedSubver {
174
1122
    fn from(value: NamedSubver) -> Self {
175
1122
        Self {
176
1122
            kind: value.kind,
177
1122
            version: value.version,
178
1122
        }
179
1122
    }
180
}
181

            
182
impl From<NamedSubver> for Protocols {
183
    fn from(value: NamedSubver) -> Self {
184
        Self::from_iter([value])
185
    }
186
}
187

            
188
#[cfg(feature = "tor-bytes")]
189
impl tor_bytes::Readable for NumberedSubver {
190
528
    fn take_from(b: &mut tor_bytes::Reader<'_>) -> tor_bytes::Result<Self> {
191
528
        let kind = b.take_u8()?;
192
528
        let version = b.take_u8()?;
193
462
        Ok(Self::new(kind, version))
194
528
    }
195
}
196

            
197
#[cfg(feature = "tor-bytes")]
198
impl tor_bytes::Writeable for NumberedSubver {
199
4
    fn write_onto<B: tor_bytes::Writer + ?Sized>(&self, b: &mut B) -> tor_bytes::EncodeResult<()> {
200
4
        b.write_u8(self.kind.into());
201
4
        b.write_u8(self.version);
202
4
        Ok(())
203
4
    }
204
}
205

            
206
/// Representation for a known or unknown protocol.
207
#[derive(Eq, PartialEq, Clone, Debug, Hash, Ord, PartialOrd)]
208
enum Protocol {
209
    /// A known protocol; represented by one of ProtoKind.
210
    ///
211
    /// ProtoKind must always be in the range 0..N_RECOGNIZED.
212
    Proto(ProtoKind),
213
    /// An unknown protocol; represented by its name.
214
    Unrecognized(String),
215
}
216

            
217
impl Protocol {
218
    /// Return true iff `s` is the name of a protocol we do not recognize.
219
8
    fn is_unrecognized(&self, s: &str) -> bool {
220
8
        match self {
221
8
            Protocol::Unrecognized(s2) => s2 == s,
222
            _ => false,
223
        }
224
8
    }
225
    /// Return a string representation of this protocol.
226
1324
    fn to_str(&self) -> &str {
227
1324
        match self {
228
            Protocol::Proto(k) => k.to_str().unwrap_or("<bug>"),
229
1324
            Protocol::Unrecognized(s) => s,
230
        }
231
1324
    }
232
}
233

            
234
/// Representation of a set of versions supported by a protocol.
235
///
236
/// For now, we only use this type for unrecognized protocols.
237
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
238
struct SubprotocolEntry {
239
    /// Which protocol's versions does this describe?
240
    proto: Protocol,
241
    /// A bit-vector defining which versions are supported.  If bit
242
    /// `(1<<i)` is set, then protocol version `i` is supported.
243
    supported: u64,
244
}
245

            
246
/// A set of supported or required subprotocol versions.
247
///
248
/// This type supports both recognized subprotocols (listed in ProtoKind),
249
/// and unrecognized subprotocols (stored by name).
250
///
251
/// To construct an instance, use the FromStr trait:
252
/// ```
253
/// use tor_protover::Protocols;
254
/// let p: Result<Protocols,_> = "Link=1-3 LinkAuth=2-3 Relay=1-2".parse();
255
/// ```
256
///
257
/// # Implementation notes
258
///
259
/// Because the number of distinct `Protocols` sets at any given time
260
/// is much smaller than the number of relays, this type is interned in order to
261
/// save memory and copying time.
262
///
263
/// This type is an Arc internally; it is cheap to clone.
264
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
265
#[cfg_attr(
266
    feature = "serde",
267
    derive(serde_with::DeserializeFromStr, serde_with::SerializeDisplay)
268
)]
269
pub struct Protocols(
270
    /// We intern ProtocolsInner objects because:
271
    ///  - There are very few _distinct_ values in any given set of relays.
272
    ///  - Every relay has one.
273
    ///  - We often want to copy them when we're remembering information about circuits.
274
    Intern<ProtocolsInner>,
275
);
276

            
277
/// Inner representation of Protocols.
278
///
279
/// We make this a separate type so that we can intern it inside an `Intern`
280
#[derive(Default, Clone, Debug, Eq, PartialEq, Hash, Deftly)]
281
#[derive_deftly(tor_basic_utils::GloballyInternable)]
282
struct ProtocolsInner {
283
    /// A mapping from protocols' integer encodings to bit-vectors.
284
    recognized: [u64; N_RECOGNIZED],
285
    /// A vector of unrecognized protocol versions,
286
    /// in sorted order.
287
    ///
288
    /// Every entry in this list has supported != 0.
289
    unrecognized: Vec<SubprotocolEntry>,
290
}
291

            
292
impl From<ProtocolsInner> for Protocols {
293
1128090
    fn from(value: ProtocolsInner) -> Self {
294
1128090
        Protocols(value.into_intern())
295
1128090
    }
296
}
297

            
298
impl Protocols {
299
    /// Return a new empty set of protocol versions.
300
    ///
301
    /// # Warning
302
    ///
303
    /// To the extend possible, avoid using empty lists to represent the capabilities
304
    /// of an unknown target.  Instead, if there is a consensus present, use the
305
    /// `required-relay-protocols` field of the consensus.
306
2310
    pub fn new() -> Self {
307
2310
        Protocols::default()
308
2310
    }
309

            
310
    /// Construct a new [`Protocols`] from a single recognized kind and a list of associated versions.
311
    ///
312
    /// (This method should not usually be needed for new parts of Arti:
313
    /// its only use-case is a legacy piece of hsdesc parsing.)
314
1254
    pub fn from_kind_and_versions(kind: ProtoKind, versions: &str) -> Result<Self, ParseError> {
315
1254
        let versions = parse_version_mask(versions)?;
316
1254
        let mut protocols = ProtocolsInner::default();
317

            
318
1254
        if let Some(p) = protocols.recognized.get_mut(usize::from(kind.get())) {
319
1254
            *p = versions;
320
1254
        } else {
321
            return Err(ParseError::Malformed);
322
        }
323

            
324
1254
        Ok(protocols.into())
325
1254
    }
326

            
327
    /// Helper: return true iff this protocol set contains the
328
    /// version `ver` of the protocol represented by the integer `proto`.
329
1634308
    fn supports_recognized_ver(&self, proto: usize, ver: u8) -> bool {
330
1634308
        if usize::from(ver) > MAX_VER {
331
2
            return false;
332
1634306
        }
333
1634306
        if proto >= self.0.recognized.len() {
334
            return false;
335
1634306
        }
336
1634306
        (self.0.recognized[proto] & (1 << ver)) != 0
337
1634308
    }
338
    /// Helper: return true iff this protocol set contains version
339
    /// `ver` of the unrecognized protocol represented by the string
340
    /// `proto`.
341
    ///
342
    /// Requires that `proto` is not the name of a recognized protocol.
343
10
    fn supports_unrecognized_ver(&self, proto: &str, ver: u8) -> bool {
344
10
        if usize::from(ver) > MAX_VER {
345
2
            return false;
346
8
        }
347
8
        let ent = self
348
8
            .0
349
8
            .unrecognized
350
8
            .iter()
351
12
            .find(|ent| ent.proto.is_unrecognized(proto));
352
8
        match ent {
353
4
            Some(e) => (e.supported & (1 << ver)) != 0,
354
4
            None => false,
355
        }
356
10
    }
357

            
358
    /// Return true if this list of protocols is empty.
359
11550
    pub fn is_empty(&self) -> bool {
360
104851
        self.0.recognized.iter().all(|v| *v == 0)
361
6930
            && self.0.unrecognized.iter().all(|p| p.supported == 0)
362
11550
    }
363

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

            
398
    /// Check whether a protocol version is supported.
399
    ///
400
    /// ```
401
    /// use tor_protover::*;
402
    /// let protos: Protocols = "Link=1-5 Desc=2-4".parse().unwrap();
403
    /// assert!(protos.supports_named_subver(named::DESC_FAMILY_IDS)); // Desc=4
404
    /// assert!(! protos.supports_named_subver(named::CONFLUX_BASE)); // Conflux=1
405
    /// ```
406
1633368
    pub fn supports_named_subver(&self, protover: NamedSubver) -> bool {
407
1633368
        self.supports_known_subver(protover.kind, protover.version)
408
1633368
    }
409

            
410
    /// Check whether a numbered subprotocol capability is supported.
411
    ///
412
    /// ```
413
    /// use tor_protover::*;
414
    /// let protos: Protocols = "Link=1-5 Desc=2-4".parse().unwrap();
415
    /// assert!(protos.supports_numbered_subver(NumberedSubver::new(ProtoKind::Desc, 4)));
416
    /// assert!(! protos.supports_numbered_subver(NumberedSubver::new(ProtoKind::Conflux, 1)));
417
    /// ```
418
792
    pub fn supports_numbered_subver(&self, protover: NumberedSubver) -> bool {
419
792
        self.supports_known_subver(protover.kind, protover.version)
420
792
    }
421

            
422
    /// Return a Protocols holding every protocol flag that is present in `self`
423
    /// but not `other`.
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=3".parse().unwrap();
429
    /// assert_eq!(protos.difference(&protos2),
430
    ///            "Desc=2,4 Microdesc=1-2,4-5".parse().unwrap());
431
    /// ```
432
1068
    pub fn difference(&self, other: &Protocols) -> Protocols {
433
1068
        let mut r = ProtocolsInner::default();
434

            
435
13884
        for i in 0..N_RECOGNIZED {
436
13884
            r.recognized[i] = self.0.recognized[i] & !other.0.recognized[i];
437
13884
        }
438
        // This is not super efficient, but we don't have to do it often.
439
1072
        for ent in self.0.unrecognized.iter() {
440
82
            let mut ent = ent.clone();
441
89
            if let Some(other_ent) = other.0.unrecognized.iter().find(|e| e.proto == ent.proto) {
442
4
                ent.supported &= !other_ent.supported;
443
78
            }
444
82
            if ent.supported != 0 {
445
80
                r.unrecognized.push(ent);
446
80
            }
447
        }
448
1068
        Protocols::from(r)
449
1068
    }
450

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

            
477
    /// Return a Protocols holding every protocol flag that is present in both `self`
478
    /// and `other`.
479
    ///
480
    /// ```
481
    /// use tor_protover::*;
482
    /// let protos: Protocols = "Desc=2-4 Microdesc=1-5".parse().unwrap();
483
    /// let protos2: Protocols = "Desc=3 Microdesc=10".parse().unwrap();
484
    /// assert_eq!(protos.intersection(&protos2),
485
    ///            "Desc=3".parse().unwrap());
486
    /// ```
487
18360
    pub fn intersection(&self, other: &Protocols) -> Protocols {
488
18360
        let mut r = ProtocolsInner::default();
489
238680
        for i in 0..N_RECOGNIZED {
490
238680
            r.recognized[i] = self.0.recognized[i] & other.0.recognized[i];
491
238680
        }
492
18364
        for ent in self.0.unrecognized.iter() {
493
23
            if let Some(other_ent) = other.0.unrecognized.iter().find(|e| e.proto == ent.proto) {
494
4
                let supported = ent.supported & other_ent.supported;
495
4
                if supported != 0 {
496
4
                    r.unrecognized.push(SubprotocolEntry {
497
4
                        proto: ent.proto.clone(),
498
4
                        supported,
499
4
                    });
500
4
                }
501
12
            }
502
        }
503
18360
        r.unrecognized.sort();
504
18360
        Protocols::from(r)
505
18360
    }
506
}
507

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

            
541
/// An error representing a failure to parse a set of protocol versions.
542
#[derive(Error, Debug, PartialEq, Eq, Clone)]
543
#[non_exhaustive]
544
pub enum ParseError {
545
    /// A protocol version was not in the range 1..=63.
546
    #[error("Protocol version out of range")]
547
    OutOfRange,
548
    /// Some subprotocol or protocol version appeared more than once.
549
    #[error("Duplicate protocol entry")]
550
    Duplicate,
551
    /// The list of protocol versions was malformed in some other way.
552
    #[error("Malformed protocol entry")]
553
    Malformed,
554
}
555

            
556
/// Helper: return a new u64 in which bits `lo` through `hi` inclusive
557
/// are set to 1, and all the other bits are set to 0.
558
///
559
/// In other words, `bitrange(a,b)` is how we represent the range of
560
/// versions `a-b` in a protocol version bitmask.
561
///
562
/// ```ignore
563
/// # use tor_protover::bitrange;
564
/// assert_eq!(bitrange(0, 5), 0b111111);
565
/// assert_eq!(bitrange(2, 5), 0b111100);
566
/// assert_eq!(bitrange(2, 7), 0b11111100);
567
/// ```
568
5473754
fn bitrange(lo: u64, hi: u64) -> u64 {
569
5473754
    assert!(lo <= hi && lo <= 63 && hi <= 63);
570
5473754
    let mut mask = !0;
571
5473754
    mask <<= 63 - hi;
572
5473754
    mask >>= 63 - hi + lo;
573
5473754
    mask <<= lo;
574
5473754
    mask
575
5473754
}
576

            
577
/// Helper: return true if the provided string is a valid "integer"
578
/// in the form accepted by the protover spec.  This is stricter than
579
/// rust's integer parsing format.
580
10947530
fn is_good_number(n: &str) -> bool {
581
11117365
    n.chars().all(|ch| ch.is_ascii_digit()) && !n.starts_with('0')
582
10947530
}
583

            
584
/// Parse a version-list in `versions` into a bitmask.
585
#[allow(clippy::string_slice)] // TODO
586
5466372
fn parse_version_mask(versions: &str) -> Result<u64, ParseError> {
587
5466372
    if versions.is_empty() {
588
        // We need to handle this case specially, since otherwise
589
        // it would be treated below as a single empty value, which
590
        // would be rejected.
591
2
        return Ok(0);
592
5466370
    }
593
    // Construct a bitmask based on the comma-separated versions.
594
5466370
    let mut supported = 0_u64;
595
5473770
    for ent in versions.split(',') {
596
        // Find and parse lo and hi for a single range of versions.
597
        // (If this is not a range, but rather a single version v,
598
        // treat it as if it were a range v-v.)
599
5473770
        let (lo_s, hi_s) = ent.split_once('-').unwrap_or((ent, ent));
600

            
601
5473770
        if !is_good_number(lo_s) {
602
10
            return Err(ParseError::Malformed);
603
5473760
        }
604
5473760
        if !is_good_number(hi_s) {
605
2
            return Err(ParseError::Malformed);
606
5473758
        }
607
5473758
        let lo: u64 = lo_s.parse().map_err(|_| ParseError::Malformed)?;
608
5473756
        let hi: u64 = hi_s.parse().map_err(|_| ParseError::Malformed)?;
609
        // Make sure that lo and hi are in-bounds and consistent.
610
5473752
        if lo > (MAX_VER as u64) || hi > (MAX_VER as u64) {
611
6
            return Err(ParseError::OutOfRange);
612
5473746
        }
613
5473746
        if lo > hi {
614
2
            return Err(ParseError::Malformed);
615
5473744
        }
616
5473744
        let mask = bitrange(lo, hi);
617
        // Make sure that no version is included twice.
618
5473744
        if (supported & mask) != 0 {
619
2
            return Err(ParseError::Duplicate);
620
5473742
        }
621
        // Add the appropriate bits to the mask.
622
5473742
        supported |= mask;
623
    }
624

            
625
5466342
    Ok(supported)
626
5466372
}
627

            
628
/// A single SubprotocolEntry is parsed from a string of the format
629
/// Name=Versions, where Versions is a comma-separated list of
630
/// integers or ranges of integers.
631
impl std::str::FromStr for SubprotocolEntry {
632
    type Err = ParseError;
633

            
634
5465120
    fn from_str(s: &str) -> Result<Self, ParseError> {
635
        // split the string on the =.
636
5465120
        let (name, versions) = s.split_once('=').ok_or(ParseError::Malformed)?;
637

            
638
        // Look up the protocol by name.
639
5465118
        let proto = match ProtoKind::from_name(name) {
640
5463410
            Some(p) => Protocol::Proto(p),
641
1708
            None => Protocol::Unrecognized(name.to_string()),
642
        };
643
        Ok(SubprotocolEntry {
644
5465118
            proto,
645
5465118
            supported: parse_version_mask(versions)?,
646
        })
647
5465120
    }
648
}
649

            
650
/// A Protocols set can be parsed from a string according to the
651
/// format used in Tor consensus documents.
652
///
653
/// A protocols set is represented by a space-separated list of
654
/// entries.  Each entry is of the form `Name=Versions`, where `Name`
655
/// is the name of a protocol, and `Versions` is a comma-separated
656
/// list of version numbers and version ranges.  Each version range is
657
/// a pair of integers separated by `-`.
658
///
659
/// No protocol name may be listed twice.  No version may be listed
660
/// twice for a single protocol.  All versions must be in range 0
661
/// through 63 inclusive.
662
impl std::str::FromStr for Protocols {
663
    type Err = ParseError;
664

            
665
1041952
    fn from_str(s: &str) -> Result<Self, ParseError> {
666
1041952
        let mut result = ProtocolsInner::default();
667
1041952
        let mut foundmask = 0_u64;
668
5762786
        for ent in s.split(' ') {
669
5762786
            if ent.is_empty() {
670
297666
                continue;
671
5465120
            }
672

            
673
5465120
            let s: SubprotocolEntry = ent.parse()?;
674
5465090
            result.add(&mut foundmask, s)?;
675
        }
676
1041918
        result.unrecognized.sort();
677
1041918
        if result
678
1041918
            .unrecognized
679
1041918
            .windows(2)
680
1041941
            .any(|w| w[0].proto == w[1].proto)
681
        {
682
2
            return Err(ParseError::Duplicate);
683
1041916
        }
684

            
685
1041916
        Ok(result.into())
686
1041952
    }
687
}
688

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

            
719
    // How many bits have we already shifted `mask`?
720
75726
    let mut shift = 0;
721
151522
    while mask != 0 {
722
75798
        let zeros = mask.trailing_zeros();
723
75798
        mask >>= zeros;
724
75798
        shift += zeros;
725
75798
        let ones = mask.trailing_ones();
726
75798
        append(&mut result, shift, shift + ones - 1);
727
75798
        shift += ones;
728
75798
        if ones == 64 {
729
            // We have to do this check to avoid overflow when formatting
730
            // the range `0-63`.
731
2
            break;
732
75796
        }
733
75796
        mask >>= ones;
734
    }
735
75726
    result.join(",")
736
75726
}
737

            
738
/// The Display trait formats a protocol set in the format expected by Tor
739
/// consensus documents.
740
///
741
/// ```
742
/// use tor_protover::*;
743
/// let protos: Protocols = "Link=1,2,3 Foobar=7 Relay=2".parse().unwrap();
744
/// assert_eq!(format!("{}", protos),
745
///            "Foobar=7 Link=1-3 Relay=2");
746
/// ```
747
impl std::fmt::Display for Protocols {
748
16840
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
749
16840
        let mut entries = Vec::new();
750
218920
        for (idx, mask) in self.0.recognized.iter().enumerate() {
751
218920
            if *mask != 0 {
752
74392
                let pk: ProtoKind = (idx as u8).into();
753
74392
                entries.push(format!("{}={}", pk, dumpmask(*mask)));
754
144528
            }
755
        }
756
16840
        for ent in &self.0.unrecognized {
757
1324
            if ent.supported != 0 {
758
1324
                entries.push(format!(
759
1324
                    "{}={}",
760
1324
                    ent.proto.to_str(),
761
1324
                    dumpmask(ent.supported)
762
1324
                ));
763
1324
            }
764
        }
765
        // This sort is required.
766
16840
        entries.sort();
767
16840
        write!(f, "{}", entries.join(" "))
768
16840
    }
769
}
770

            
771
impl FromIterator<NamedSubver> for Protocols {
772
16686
    fn from_iter<T: IntoIterator<Item = NamedSubver>>(iter: T) -> Self {
773
16686
        let mut r = ProtocolsInner::default();
774
111229
        for named_subver in iter {
775
111228
            let proto_idx = usize::from(named_subver.kind.get());
776
111228
            let proto_ver = named_subver.version;
777

            
778
            // These are guaranteed by invariants on NamedSubver.
779
111228
            assert!(proto_idx < N_RECOGNIZED);
780
111228
            assert!(usize::from(proto_ver) <= MAX_VER);
781
111228
            r.recognized[proto_idx] |= 1_u64 << proto_ver;
782
        }
783
16686
        Protocols::from(r)
784
16686
    }
785
}
786

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

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

            
843
#[cfg(test)]
844
mod test {
845
    // @@ begin test lint list maintained by maint/add_warning @@
846
    #![allow(clippy::bool_assert_comparison)]
847
    #![allow(clippy::clone_on_copy)]
848
    #![allow(clippy::dbg_macro)]
849
    #![allow(clippy::mixed_attributes_style)]
850
    #![allow(clippy::print_stderr)]
851
    #![allow(clippy::print_stdout)]
852
    #![allow(clippy::single_char_pattern)]
853
    #![allow(clippy::unwrap_used)]
854
    #![allow(clippy::unchecked_time_subtraction)]
855
    #![allow(clippy::useless_vec)]
856
    #![allow(clippy::needless_pass_by_value)]
857
    #![allow(clippy::string_slice)] // See arti#2571
858
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
859
    use std::str::FromStr;
860

            
861
    use super::*;
862

            
863
    #[test]
864
    fn test_bitrange() {
865
        assert_eq!(0b1, bitrange(0, 0));
866
        assert_eq!(0b10, bitrange(1, 1));
867
        assert_eq!(0b11, bitrange(0, 1));
868
        assert_eq!(0b1111110000000, bitrange(7, 12));
869
        assert_eq!(!0, bitrange(0, 63));
870
    }
871

            
872
    #[test]
873
    fn test_dumpmask() {
874
        assert_eq!("", dumpmask(0));
875
        assert_eq!("0-5", dumpmask(0b111111));
876
        assert_eq!("4-5", dumpmask(0b110000));
877
        assert_eq!("1,4-5", dumpmask(0b110010));
878
        assert_eq!("0-63", dumpmask(!0));
879
    }
880

            
881
    #[test]
882
    fn test_canonical() -> Result<(), ParseError> {
883
        fn t(orig: &str, canonical: &str) -> Result<(), ParseError> {
884
            let protos: Protocols = orig.parse()?;
885
            let enc = format!("{}", protos);
886
            assert_eq!(enc, canonical);
887
            Ok(())
888
        }
889

            
890
        t("", "")?;
891
        t(" ", "")?;
892
        t("Link=5,6,7,9 Relay=4-7,2", "Link=5-7,9 Relay=2,4-7")?;
893
        t("FlowCtrl= Padding=8,7 Desc=1-5,6-8", "Desc=1-8 Padding=7-8")?;
894
        t("Zelda=7 Gannon=3,6 Link=4", "Gannon=3,6 Link=4 Zelda=7")?;
895

            
896
        Ok(())
897
    }
898

            
899
    #[test]
900
    fn test_invalid() {
901
        fn t(s: &str) -> ParseError {
902
            let protos: Result<Protocols, ParseError> = s.parse();
903
            assert!(protos.is_err());
904
            protos.err().unwrap()
905
        }
906

            
907
        assert_eq!(t("Link=1-100"), ParseError::OutOfRange);
908
        assert_eq!(t("Zelda=100"), ParseError::OutOfRange);
909
        assert_eq!(t("Link=100-200"), ParseError::OutOfRange);
910

            
911
        assert_eq!(t("Link=1,1"), ParseError::Duplicate);
912
        assert_eq!(t("Link=1 Link=1"), ParseError::Duplicate);
913
        assert_eq!(t("Link=1 Link=3"), ParseError::Duplicate);
914
        assert_eq!(t("Zelda=1 Zelda=3"), ParseError::Duplicate);
915

            
916
        assert_eq!(t("Link=Zelda"), ParseError::Malformed);
917
        assert_eq!(t("Link=6-2"), ParseError::Malformed);
918
        assert_eq!(t("Link=6-"), ParseError::Malformed);
919
        assert_eq!(t("Link=6-,2"), ParseError::Malformed);
920
        assert_eq!(t("Link=1,,2"), ParseError::Malformed);
921
        assert_eq!(t("Link=6-frog"), ParseError::Malformed);
922
        assert_eq!(t("Link=gannon-9"), ParseError::Malformed);
923
        assert_eq!(t("Link Zelda"), ParseError::Malformed);
924

            
925
        assert_eq!(t("Link=01"), ParseError::Malformed);
926
        assert_eq!(t("Link=waffle"), ParseError::Malformed);
927
        assert_eq!(t("Link=1_1"), ParseError::Malformed);
928
    }
929

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

            
934
        assert!(p.supports_known_subver(ProtoKind::Padding, 2));
935
        assert!(!p.supports_known_subver(ProtoKind::Padding, 1));
936
        assert!(p.supports_known_subver(ProtoKind::Link, 6));
937
        assert!(!p.supports_known_subver(ProtoKind::Link, 255));
938
        assert!(!p.supports_known_subver(ProtoKind::Cons, 1));
939
        assert!(!p.supports_known_subver(ProtoKind::Cons, 0));
940
        assert!(p.supports_subver("Link", 6));
941
        assert!(!p.supports_subver("link", 6));
942
        assert!(!p.supports_subver("Cons", 0));
943
        assert!(p.supports_subver("Lonk", 3));
944
        assert!(!p.supports_subver("Lonk", 4));
945
        assert!(!p.supports_subver("lonk", 3));
946
        assert!(!p.supports_subver("Lonk", 64));
947

            
948
        Ok(())
949
    }
950

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

            
956
        assert_eq!(
957
            p1.difference(&p2),
958
            Protocols::from_str("Link=1-2,5-10 Desc=7-10 Relay=1,7,9 Other=7,9-60 Mine=1-20")?
959
        );
960
        assert_eq!(
961
            p2.difference(&p1),
962
            Protocols::from_str("Desc=1-4 Relay=2,4,6 Theirs=20")?,
963
        );
964

            
965
        let nil = Protocols::default();
966
        assert_eq!(p1.difference(&nil), p1);
967
        assert_eq!(p2.difference(&nil), p2);
968
        assert_eq!(nil.difference(&p1), nil);
969
        assert_eq!(nil.difference(&p2), nil);
970

            
971
        Ok(())
972
    }
973

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

            
979
        assert_eq!(
980
            p1.union(&p2),
981
            Protocols::from_str(
982
                "Link=1-10 Desc=1-10 Relay=1-7,9 Other=2,7-60 Theirs=20 Mine=1-20"
983
            )?
984
        );
985
        assert_eq!(
986
            p2.union(&p1),
987
            Protocols::from_str(
988
                "Link=1-10 Desc=1-10 Relay=1-7,9 Other=2,7-60 Theirs=20 Mine=1-20"
989
            )?
990
        );
991

            
992
        let nil = Protocols::default();
993
        assert_eq!(p1.union(&nil), p1);
994
        assert_eq!(p2.union(&nil), p2);
995
        assert_eq!(nil.union(&p1), p1);
996
        assert_eq!(nil.union(&p2), p2);
997

            
998
        Ok(())
999
    }
    #[test]
    fn test_intersection() -> Result<(), ParseError> {
        let p1: Protocols = "Link=1-10 Desc=5-10 Relay=1,3,5,7,9 Other=7-60 Mine=1-20".parse()?;
        let p2: Protocols = "Link=3-4 Desc=1-6 Relay=2-6 Other=2,8 Theirs=20".parse()?;
        assert_eq!(
            p1.intersection(&p2),
            Protocols::from_str("Link=3-4 Desc=5-6 Relay=3,5 Other=8")?
        );
        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));
    }
}