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
//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
49

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

            
53
use caret::caret_int;
54

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

            
59
pub mod named;
60

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
323
1235
        Ok(protocols.into())
324
1235
    }
325

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

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

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

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

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

            
421
    /// Return a Protocols holding every protocol flag that is present in `self`
422
    /// but not `other`.
423
    ///
424
    /// ```
425
    /// use tor_protover::*;
426
    /// let protos: Protocols = "Desc=2-4 Microdesc=1-5".parse().unwrap();
427
    /// let protos2: Protocols = "Desc=3 Microdesc=3".parse().unwrap();
428
    /// assert_eq!(protos.difference(&protos2),
429
    ///            "Desc=2,4 Microdesc=1-2,4-5".parse().unwrap());
430
    /// ```
431
1052
    pub fn difference(&self, other: &Protocols) -> Protocols {
432
1052
        let mut r = ProtocolsInner::default();
433

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

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

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

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

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

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

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

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

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

            
624
570271
    Ok(supported)
625
570301
}
626

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

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

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

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

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

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

            
684
616890
        Ok(result.into())
685
616926
    }
686
}
687

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

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

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

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

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

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

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

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

            
860
    use super::*;
861

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

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

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

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

            
895
        Ok(())
896
    }
897

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

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

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

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

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

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

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

            
947
        Ok(())
948
    }
949

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

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

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

            
970
        Ok(())
971
    }
972

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

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

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

            
997
        Ok(())
998
    }
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));
    }
}