tor_protover/
lib.rs

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//! <!-- @@ end lint list maintained by maint/add_warning @@ -->
46
47#![allow(non_upper_case_globals)]
48#![allow(clippy::upper_case_acronyms)]
49
50use std::sync::Arc;
51
52use caret::caret_int;
53
54use thiserror::Error;
55use tor_basic_utils::intern::InternCache;
56
57pub mod named;
58
59caret_int! {
60    /// A recognized subprotocol.
61    ///
62    /// These names are kept in sync with the names used in consensus
63    /// documents; the values are kept in sync with the values in the
64    /// cbor document format in the walking onions proposal.
65    ///
66    /// For the full semantics of each subprotocol, see tor-spec.txt.
67    #[derive(Hash,Ord,PartialOrd)]
68    pub struct ProtoKind(u8) {
69        /// Initiating and receiving channels, and getting cells on them.
70        Link = 0,
71        /// Different kinds of authenticate cells
72        LinkAuth = 1,
73        /// CREATE cells, CREATED cells, and the encryption that they
74        /// create.
75        Relay = 2,
76        /// Serving and fetching network directory documents.
77        DirCache = 3,
78        /// Serving onion service descriptors
79        HSDir = 4,
80        /// Providing an onion service introduction point
81        HSIntro = 5,
82        /// Providing an onion service rendezvous point
83        HSRend = 6,
84        /// Describing a relay's functionality using router descriptors.
85        Desc = 7,
86        /// Describing a relay's functionality using microdescriptors.
87        Microdesc = 8,
88        /// Describing the network as a consensus directory document.
89        Cons = 9,
90        /// Sending and accepting circuit-level padding
91        Padding = 10,
92        /// Improved means of flow control on circuits.
93        FlowCtrl = 11,
94        /// Multi-path circuit support.
95        Conflux = 12,
96    }
97}
98
99/// How many recognized protocols are there?
100const N_RECOGNIZED: usize = 13;
101
102/// Maximum allowable value for a protocol's version field.
103const MAX_VER: usize = 63;
104
105/// A specific, named subversion of a protocol.
106#[derive(Eq, PartialEq, Copy, Clone, Debug)]
107pub struct NamedSubver {
108    /// The protocol in question
109    ///
110    /// Must be in-range for ProtoKind (0..N_RECOGNIZED).
111    kind: ProtoKind,
112    /// The version of the protocol
113    ///
114    /// Must be in 0..=MAX_VER
115    version: u8,
116}
117
118impl NamedSubver {
119    /// Create a new NamedSubver.
120    ///
121    /// # Panics
122    ///
123    /// Panics if `kind` is unrecognized or `version` is invalid.
124    const fn new(kind: ProtoKind, version: u8) -> Self {
125        assert!((kind.0 as usize) < N_RECOGNIZED);
126        assert!((version as usize) <= MAX_VER);
127        Self { kind, version }
128    }
129}
130
131/// A subprotocol capability as represented by a (kind, version) tuple.
132///
133/// Does not necessarily represent a real subprotocol capability;
134/// this type is meant for use in other pieces of the protocol.
135///
136/// # Ordering
137///
138/// Instances of `NumberedSubver` are sorted in lexicographic order by
139/// their (kind, version) tuples.
140//
141// TODO: As with most other types in the crate, we should decide how to rename them as as part
142// of #1934.
143#[derive(Clone, Copy, Debug, Ord, PartialOrd, Eq, PartialEq)]
144pub struct NumberedSubver {
145    /// The protocol in question
146    kind: ProtoKind,
147    /// The version of the protocol
148    version: u8,
149}
150
151impl NumberedSubver {
152    /// Construct a new [`NumberedSubver`]
153    pub fn new(kind: impl Into<ProtoKind>, version: u8) -> Self {
154        Self {
155            kind: kind.into(),
156            version,
157        }
158    }
159    /// Return the ProtoKind and version for this [`NumberedSubver`].
160    pub fn into_parts(self) -> (ProtoKind, u8) {
161        (self.kind, self.version)
162    }
163}
164impl From<NamedSubver> for NumberedSubver {
165    fn from(value: NamedSubver) -> Self {
166        Self {
167            kind: value.kind,
168            version: value.version,
169        }
170    }
171}
172
173#[cfg(feature = "tor-bytes")]
174impl tor_bytes::Readable for NumberedSubver {
175    fn take_from(b: &mut tor_bytes::Reader<'_>) -> tor_bytes::Result<Self> {
176        let kind = b.take_u8()?;
177        let version = b.take_u8()?;
178        Ok(Self::new(kind, version))
179    }
180}
181
182#[cfg(feature = "tor-bytes")]
183impl tor_bytes::Writeable for NumberedSubver {
184    fn write_onto<B: tor_bytes::Writer + ?Sized>(&self, b: &mut B) -> tor_bytes::EncodeResult<()> {
185        b.write_u8(self.kind.into());
186        b.write_u8(self.version);
187        Ok(())
188    }
189}
190
191/// Representation for a known or unknown protocol.
192#[derive(Eq, PartialEq, Clone, Debug, Hash, Ord, PartialOrd)]
193enum Protocol {
194    /// A known protocol; represented by one of ProtoKind.
195    ///
196    /// ProtoKind must always be in the range 0..N_RECOGNIZED.
197    Proto(ProtoKind),
198    /// An unknown protocol; represented by its name.
199    Unrecognized(String),
200}
201
202impl Protocol {
203    /// Return true iff `s` is the name of a protocol we do not recognize.
204    fn is_unrecognized(&self, s: &str) -> bool {
205        match self {
206            Protocol::Unrecognized(s2) => s2 == s,
207            _ => false,
208        }
209    }
210    /// Return a string representation of this protocol.
211    fn to_str(&self) -> &str {
212        match self {
213            Protocol::Proto(k) => k.to_str().unwrap_or("<bug>"),
214            Protocol::Unrecognized(s) => s,
215        }
216    }
217}
218
219/// Representation of a set of versions supported by a protocol.
220///
221/// For now, we only use this type for unrecognized protocols.
222#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
223struct SubprotocolEntry {
224    /// Which protocol's versions does this describe?
225    proto: Protocol,
226    /// A bit-vector defining which versions are supported.  If bit
227    /// `(1<<i)` is set, then protocol version `i` is supported.
228    supported: u64,
229}
230
231/// A set of supported or required subprotocol versions.
232///
233/// This type supports both recognized subprotocols (listed in ProtoKind),
234/// and unrecognized subprotocols (stored by name).
235///
236/// To construct an instance, use the FromStr trait:
237/// ```
238/// use tor_protover::Protocols;
239/// let p: Result<Protocols,_> = "Link=1-3 LinkAuth=2-3 Relay=1-2".parse();
240/// ```
241///
242/// # Implementation notes
243///
244/// Because the number of distinct `Protocols` sets at any given time
245/// is much smaller than the number of relays, this type is interned in order to
246/// save memory and copying time.
247///
248/// This type is an Arc internally; it is cheap to clone.
249#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
250#[cfg_attr(
251    feature = "serde",
252    derive(serde_with::DeserializeFromStr, serde_with::SerializeDisplay)
253)]
254pub struct Protocols(Arc<ProtocolsInner>);
255
256/// Inner representation of Protocols.
257///
258/// We make this a separate type so that we can intern it inside an Arc.
259#[derive(Default, Clone, Debug, Eq, PartialEq, Hash)]
260struct ProtocolsInner {
261    /// A mapping from protocols' integer encodings to bit-vectors.
262    recognized: [u64; N_RECOGNIZED],
263    /// A vector of unrecognized protocol versions,
264    /// in sorted order.
265    ///
266    /// Every entry in this list has supported != 0.
267    unrecognized: Vec<SubprotocolEntry>,
268}
269
270/// An InternCache of ProtocolsInner.
271///
272/// We intern ProtocolsInner objects because:
273///  - There are very few _distinct_ values in any given set of relays.
274///  - Every relay has one.
275///  - We often want to copy them when we're remembering information about circuits.
276static PROTOCOLS: InternCache<ProtocolsInner> = InternCache::new();
277
278impl From<ProtocolsInner> for Protocols {
279    fn from(value: ProtocolsInner) -> Self {
280        Protocols(PROTOCOLS.intern(value))
281    }
282}
283
284impl Protocols {
285    /// Return a new empty set of protocol versions.
286    ///
287    /// # Warning
288    ///
289    /// To the extend possible, avoid using empty lists to represent the capabilities
290    /// of an unknown target.  Instead, if there is a consensus present, use the
291    /// `required-relay-protocols` field of the consensus.
292    pub fn new() -> Self {
293        Protocols::default()
294    }
295
296    /// Helper: return true iff this protocol set contains the
297    /// version `ver` of the protocol represented by the integer `proto`.
298    fn supports_recognized_ver(&self, proto: usize, ver: u8) -> bool {
299        if usize::from(ver) > MAX_VER {
300            return false;
301        }
302        if proto >= self.0.recognized.len() {
303            return false;
304        }
305        (self.0.recognized[proto] & (1 << ver)) != 0
306    }
307    /// Helper: return true iff this protocol set contains version
308    /// `ver` of the unrecognized protocol represented by the string
309    /// `proto`.
310    ///
311    /// Requires that `proto` is not the name of a recognized protocol.
312    fn supports_unrecognized_ver(&self, proto: &str, ver: u8) -> bool {
313        if usize::from(ver) > MAX_VER {
314            return false;
315        }
316        let ent = self
317            .0
318            .unrecognized
319            .iter()
320            .find(|ent| ent.proto.is_unrecognized(proto));
321        match ent {
322            Some(e) => (e.supported & (1 << ver)) != 0,
323            None => false,
324        }
325    }
326
327    /// Return true if this list of protocols is empty.
328    pub fn is_empty(&self) -> bool {
329        self.0.recognized.iter().all(|v| *v == 0)
330            && self.0.unrecognized.iter().all(|p| p.supported == 0)
331    }
332
333    // TODO: Combine these next two functions into one by using a trait.
334    /// Check whether a known protocol version is supported.
335    ///
336    /// ```
337    /// use tor_protover::*;
338    /// let protos: Protocols = "Link=1-3 HSDir=2,4-5".parse().unwrap();
339    ///
340    /// assert!(protos.supports_known_subver(ProtoKind::Link, 2));
341    /// assert!(protos.supports_known_subver(ProtoKind::HSDir, 4));
342    /// assert!(! protos.supports_known_subver(ProtoKind::HSDir, 3));
343    /// assert!(! protos.supports_known_subver(ProtoKind::LinkAuth, 3));
344    /// ```
345    pub fn supports_known_subver(&self, proto: ProtoKind, ver: u8) -> bool {
346        self.supports_recognized_ver(proto.get() as usize, ver)
347    }
348    /// Check whether a protocol version identified by a string is supported.
349    ///
350    /// ```
351    /// use tor_protover::*;
352    /// let protos: Protocols = "Link=1-3 Foobar=7".parse().unwrap();
353    ///
354    /// assert!(protos.supports_subver("Link", 2));
355    /// assert!(protos.supports_subver("Foobar", 7));
356    /// assert!(! protos.supports_subver("Link", 5));
357    /// assert!(! protos.supports_subver("Foobar", 6));
358    /// assert!(! protos.supports_subver("Wombat", 3));
359    /// ```
360    pub fn supports_subver(&self, proto: &str, ver: u8) -> bool {
361        match ProtoKind::from_name(proto) {
362            Some(p) => self.supports_recognized_ver(p.get() as usize, ver),
363            None => self.supports_unrecognized_ver(proto, ver),
364        }
365    }
366
367    /// Check whether a protocol version is supported.
368    ///
369    /// ```
370    /// use tor_protover::*;
371    /// let protos: Protocols = "Link=1-5 Desc=2-4".parse().unwrap();
372    /// assert!(protos.supports_named_subver(named::DESC_FAMILY_IDS)); // Desc=4
373    /// assert!(! protos.supports_named_subver(named::CONFLUX_BASE)); // Conflux=1
374    /// ```
375    pub fn supports_named_subver(&self, protover: NamedSubver) -> bool {
376        self.supports_known_subver(protover.kind, protover.version)
377    }
378
379    /// Check whether a numbered subprotocol capability is supported.
380    ///
381    /// ```
382    /// use tor_protover::*;
383    /// let protos: Protocols = "Link=1-5 Desc=2-4".parse().unwrap();
384    /// assert!(protos.supports_numbered_subver(NumberedSubver::new(ProtoKind::Desc, 4)));
385    /// assert!(! protos.supports_numbered_subver(NumberedSubver::new(ProtoKind::Conflux, 1)));
386    /// ```
387    pub fn supports_numbered_subver(&self, protover: NumberedSubver) -> bool {
388        self.supports_known_subver(protover.kind, protover.version)
389    }
390
391    /// Return a Protocols holding every protocol flag that is present in `self`
392    /// but not `other`.
393    ///
394    /// ```
395    /// use tor_protover::*;
396    /// let protos: Protocols = "Desc=2-4 Microdesc=1-5".parse().unwrap();
397    /// let protos2: Protocols = "Desc=3 Microdesc=3".parse().unwrap();
398    /// assert_eq!(protos.difference(&protos2),
399    ///            "Desc=2,4 Microdesc=1-2,4-5".parse().unwrap());
400    /// ```
401    pub fn difference(&self, other: &Protocols) -> Protocols {
402        let mut r = ProtocolsInner::default();
403
404        for i in 0..N_RECOGNIZED {
405            r.recognized[i] = self.0.recognized[i] & !other.0.recognized[i];
406        }
407        // This is not super efficient, but we don't have to do it often.
408        for ent in self.0.unrecognized.iter() {
409            let mut ent = ent.clone();
410            if let Some(other_ent) = other.0.unrecognized.iter().find(|e| e.proto == ent.proto) {
411                ent.supported &= !other_ent.supported;
412            }
413            if ent.supported != 0 {
414                r.unrecognized.push(ent);
415            }
416        }
417        Protocols::from(r)
418    }
419
420    /// Return a Protocols holding every protocol flag that is present in `self`
421    /// or `other` or both.
422    ///
423    /// ```
424    /// use tor_protover::*;
425    /// let protos: Protocols = "Desc=2-4 Microdesc=1-5".parse().unwrap();
426    /// let protos2: Protocols = "Desc=3 Microdesc=10".parse().unwrap();
427    /// assert_eq!(protos.union(&protos2),
428    ///            "Desc=2-4 Microdesc=1-5,10".parse().unwrap());
429    /// ```
430    pub fn union(&self, other: &Protocols) -> Protocols {
431        let mut r = (*self.0).clone();
432        for i in 0..N_RECOGNIZED {
433            r.recognized[i] |= other.0.recognized[i];
434        }
435        for ent in other.0.unrecognized.iter() {
436            if let Some(my_ent) = r.unrecognized.iter_mut().find(|e| e.proto == ent.proto) {
437                my_ent.supported |= ent.supported;
438            } else {
439                r.unrecognized.push(ent.clone());
440            }
441        }
442        r.unrecognized.sort();
443        Protocols::from(r)
444    }
445
446    /// Return a Protocols holding every protocol flag that is present in both `self`
447    /// and `other`.
448    ///
449    /// ```
450    /// use tor_protover::*;
451    /// let protos: Protocols = "Desc=2-4 Microdesc=1-5".parse().unwrap();
452    /// let protos2: Protocols = "Desc=3 Microdesc=10".parse().unwrap();
453    /// assert_eq!(protos.intersection(&protos2),
454    ///            "Desc=3".parse().unwrap());
455    /// ```
456    pub fn intersection(&self, other: &Protocols) -> Protocols {
457        let mut r = ProtocolsInner::default();
458        for i in 0..N_RECOGNIZED {
459            r.recognized[i] = self.0.recognized[i] & other.0.recognized[i];
460        }
461        for ent in self.0.unrecognized.iter() {
462            if let Some(other_ent) = other.0.unrecognized.iter().find(|e| e.proto == ent.proto) {
463                let supported = ent.supported & other_ent.supported;
464                if supported != 0 {
465                    r.unrecognized.push(SubprotocolEntry {
466                        proto: ent.proto.clone(),
467                        supported,
468                    });
469                }
470            }
471        }
472        r.unrecognized.sort();
473        Protocols::from(r)
474    }
475}
476
477impl ProtocolsInner {
478    /// Parsing helper: Try to add a new entry `ent` to this set of protocols.
479    ///
480    /// Uses `foundmask`, a bit mask saying which recognized protocols
481    /// we've already found entries for.  Returns an error if `ent` is
482    /// for a protocol we've already added.
483    ///
484    /// Does not preserve sorting order; the caller must call `self.unrecognized.sort()` before returning.
485    fn add(&mut self, foundmask: &mut u64, ent: SubprotocolEntry) -> Result<(), ParseError> {
486        match ent.proto {
487            Protocol::Proto(k) => {
488                let idx = k.get() as usize;
489                assert!(idx < N_RECOGNIZED); // guaranteed by invariant on Protocol::Proto
490                let bit = 1 << u64::from(k.get());
491                if (*foundmask & bit) != 0 {
492                    return Err(ParseError::Duplicate);
493                }
494                *foundmask |= bit;
495                self.recognized[idx] = ent.supported;
496            }
497            Protocol::Unrecognized(ref s) => {
498                if self
499                    .unrecognized
500                    .iter()
501                    .any(|ent| ent.proto.is_unrecognized(s))
502                {
503                    return Err(ParseError::Duplicate);
504                }
505                if ent.supported != 0 {
506                    self.unrecognized.push(ent);
507                }
508            }
509        }
510        Ok(())
511    }
512}
513
514/// An error representing a failure to parse a set of protocol versions.
515#[derive(Error, Debug, PartialEq, Eq, Clone)]
516#[non_exhaustive]
517pub enum ParseError {
518    /// A protocol version was not in the range 0..=63.
519    #[error("Protocol version out of range")]
520    OutOfRange,
521    /// Some subprotocol or protocol version appeared more than once.
522    #[error("Duplicate protocol entry")]
523    Duplicate,
524    /// The list of protocol versions was malformed in some other way.
525    #[error("Malformed protocol entry")]
526    Malformed,
527}
528
529/// Helper: return a new u64 in which bits `lo` through `hi` inclusive
530/// are set to 1, and all the other bits are set to 0.
531///
532/// In other words, `bitrange(a,b)` is how we represent the range of
533/// versions `a-b` in a protocol version bitmask.
534///
535/// ```ignore
536/// # use tor_protover::bitrange;
537/// assert_eq!(bitrange(0, 5), 0b111111);
538/// assert_eq!(bitrange(2, 5), 0b111100);
539/// assert_eq!(bitrange(2, 7), 0b11111100);
540/// ```
541fn bitrange(lo: u64, hi: u64) -> u64 {
542    assert!(lo <= hi && lo <= 63 && hi <= 63);
543    let mut mask = !0;
544    mask <<= 63 - hi;
545    mask >>= 63 - hi + lo;
546    mask <<= lo;
547    mask
548}
549
550/// Helper: return true if the provided string is a valid "integer"
551/// in the form accepted by the protover spec.  This is stricter than
552/// rust's integer parsing format.
553fn is_good_number(n: &str) -> bool {
554    n.chars().all(|ch| ch.is_ascii_digit()) && !n.starts_with('0')
555}
556
557/// A single SubprotocolEntry is parsed from a string of the format
558/// Name=Versions, where Versions is a comma-separated list of
559/// integers or ranges of integers.
560impl std::str::FromStr for SubprotocolEntry {
561    type Err = ParseError;
562
563    fn from_str(s: &str) -> Result<Self, ParseError> {
564        // split the string on the =.
565        let (name, versions) = {
566            let eq_idx = s.find('=').ok_or(ParseError::Malformed)?;
567            (&s[..eq_idx], &s[eq_idx + 1..])
568        };
569        // Look up the protocol by name.
570        let proto = match ProtoKind::from_name(name) {
571            Some(p) => Protocol::Proto(p),
572            None => Protocol::Unrecognized(name.to_string()),
573        };
574        if versions.is_empty() {
575            // We need to handle this case specially, since otherwise
576            // it would be treated below as a single empty value, which
577            // would be rejected.
578            return Ok(SubprotocolEntry {
579                proto,
580                supported: 0,
581            });
582        }
583        // Construct a bitmask based on the comma-separated versions.
584        let mut supported = 0_u64;
585        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            let (lo_s, hi_s) = {
590                match ent.find('-') {
591                    Some(pos) => (&ent[..pos], &ent[pos + 1..]),
592                    None => (ent, ent),
593                }
594            };
595            if !is_good_number(lo_s) {
596                return Err(ParseError::Malformed);
597            }
598            if !is_good_number(hi_s) {
599                return Err(ParseError::Malformed);
600            }
601            let lo: u64 = lo_s.parse().map_err(|_| ParseError::Malformed)?;
602            let hi: u64 = hi_s.parse().map_err(|_| ParseError::Malformed)?;
603            // Make sure that lo and hi are in-bounds and consistent.
604            if lo > (MAX_VER as u64) || hi > (MAX_VER as u64) {
605                return Err(ParseError::OutOfRange);
606            }
607            if lo > hi {
608                return Err(ParseError::Malformed);
609            }
610            let mask = bitrange(lo, hi);
611            // Make sure that no version is included twice.
612            if (supported & mask) != 0 {
613                return Err(ParseError::Duplicate);
614            }
615            // Add the appropriate bits to the mask.
616            supported |= mask;
617        }
618        Ok(SubprotocolEntry { proto, supported })
619    }
620}
621
622/// A Protocols set can be parsed from a string according to the
623/// format used in Tor consensus documents.
624///
625/// A protocols set is represented by a space-separated list of
626/// entries.  Each entry is of the form `Name=Versions`, where `Name`
627/// is the name of a protocol, and `Versions` is a comma-separated
628/// list of version numbers and version ranges.  Each version range is
629/// a pair of integers separated by `-`.
630///
631/// No protocol name may be listed twice.  No version may be listed
632/// twice for a single protocol.  All versions must be in range 0
633/// through 63 inclusive.
634impl std::str::FromStr for Protocols {
635    type Err = ParseError;
636
637    fn from_str(s: &str) -> Result<Self, ParseError> {
638        let mut result = ProtocolsInner::default();
639        let mut foundmask = 0_u64;
640        for ent in s.split(' ') {
641            if ent.is_empty() {
642                continue;
643            }
644
645            let s: SubprotocolEntry = ent.parse()?;
646            result.add(&mut foundmask, s)?;
647        }
648        result.unrecognized.sort();
649        Ok(result.into())
650    }
651}
652
653/// Given a bitmask, return a list of the bits set in the mask, as a
654/// String in the format expected by Tor consensus documents.
655///
656/// This implementation constructs ranges greedily.  For example, the
657/// bitmask `0b0111011` will be represented as `0-1,3-5`, and not
658/// `0,1,3,4,5` or `0,1,3-5`.
659///
660/// ```ignore
661/// # use tor_protover::dumpmask;
662/// assert_eq!(dumpmask(0b111111), "0-5");
663/// assert_eq!(dumpmask(0b111100), "2-5");
664/// assert_eq!(dumpmask(0b11111100), "2-7");
665/// ```
666fn dumpmask(mut mask: u64) -> String {
667    /// Helper: push a range (which may be a singleton) onto `v`.
668    fn append(v: &mut Vec<String>, lo: u32, hi: u32) {
669        if lo == hi {
670            v.push(lo.to_string());
671        } else {
672            v.push(format!("{}-{}", lo, hi));
673        }
674    }
675    // We'll be building up our result here, then joining it with
676    // commas.
677    let mut result = Vec::new();
678    // This implementation is a little tricky, but it should be more
679    // efficient than a raw search.  Basically, we're using the
680    // function u64::trailing_zeros to count how large each range of
681    // 1s or 0s is, and then shifting by that amount.
682
683    // How many bits have we already shifted `mask`?
684    let mut shift = 0;
685    while mask != 0 {
686        let zeros = mask.trailing_zeros();
687        mask >>= zeros;
688        shift += zeros;
689        let ones = mask.trailing_ones();
690        append(&mut result, shift, shift + ones - 1);
691        shift += ones;
692        if ones == 64 {
693            // We have to do this check to avoid overflow when formatting
694            // the range `0-63`.
695            break;
696        }
697        mask >>= ones;
698    }
699    result.join(",")
700}
701
702/// The Display trait formats a protocol set in the format expected by Tor
703/// consensus documents.
704///
705/// ```
706/// use tor_protover::*;
707/// let protos: Protocols = "Link=1,2,3 Foobar=7 Relay=2".parse().unwrap();
708/// assert_eq!(format!("{}", protos),
709///            "Foobar=7 Link=1-3 Relay=2");
710/// ```
711impl std::fmt::Display for Protocols {
712    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
713        let mut entries = Vec::new();
714        for (idx, mask) in self.0.recognized.iter().enumerate() {
715            if *mask != 0 {
716                let pk: ProtoKind = (idx as u8).into();
717                entries.push(format!("{}={}", pk, dumpmask(*mask)));
718            }
719        }
720        for ent in &self.0.unrecognized {
721            if ent.supported != 0 {
722                entries.push(format!(
723                    "{}={}",
724                    ent.proto.to_str(),
725                    dumpmask(ent.supported)
726                ));
727            }
728        }
729        // This sort is required.
730        entries.sort();
731        write!(f, "{}", entries.join(" "))
732    }
733}
734
735impl FromIterator<NamedSubver> for Protocols {
736    fn from_iter<T: IntoIterator<Item = NamedSubver>>(iter: T) -> Self {
737        let mut r = ProtocolsInner::default();
738        for named_subver in iter {
739            let proto_idx = usize::from(named_subver.kind.get());
740            let proto_ver = named_subver.version;
741
742            // These are guaranteed by invariants on NamedSubver.
743            assert!(proto_idx < N_RECOGNIZED);
744            assert!(usize::from(proto_ver) <= MAX_VER);
745            r.recognized[proto_idx] |= 1_u64 << proto_ver;
746        }
747        Protocols::from(r)
748    }
749}
750
751/// Documentation: when is a protocol "supported"?
752///
753/// Arti should consider itself to "support" a protocol if, _as built_,
754/// it implements the protocol completely.
755///
756/// Just having the protocol listed among the [`named`]
757/// protocols is not enough, and neither is an incomplete
758/// or uncompliant implementation.
759///
760/// Similarly, if the protocol is not compiled in,
761/// it is not technically _supported_.
762///
763/// When in doubt, ask yourself:
764/// - If another Tor implementation believed that we implemented this protocol,
765///   and began to speak it to us, would we be able to do so?
766/// - If the protocol were required,
767///   would this software as built actually meet that requirement?
768///
769/// If either answer is no, the protocol is not supported.
770pub mod doc_supported {}
771
772/// Documentation about changing lists of supported versions.
773///
774/// # Warning
775///
776/// You need to be extremely careful when removing
777/// _any_ entry from a list of supported protocols.
778///
779/// If you remove an entry while it still appears as "recommended" in the consensus,
780/// you'll cause all the instances without it to warn.
781///
782/// If you remove an entry while it still appears as "required" in the
783///  consensus, you'll cause all the instances without it to refuse to connect
784/// to the network, and shut down.
785///
786/// If you need to remove a version from a list of supported protocols,
787/// you need to make sure that it is not listed in the _current consensuses_:
788/// just removing it from the list that the authorities vote for is NOT ENOUGH.
789/// You need to remove it from the required list,
790/// and THEN let the authorities upgrade and vote on new
791/// consensuses without it. Only once those consensuses are out is it safe to
792/// remove from the list of required protocols.
793///
794/// ## Example
795///
796/// One concrete example of a very dangerous race that could occur:
797///
798/// Suppose that the client supports protocols "HsDir=1-2" and the consensus
799/// requires protocols "HsDir=1-2".  If the client supported protocol list is
800/// then changed to "HSDir=2", while the consensus stills lists "HSDir=1-2",
801/// then these clients, even very recent ones, will shut down because they
802/// don't support "HSDir=1".
803///
804/// And so, changes need to be done in strict sequence as described above.
805pub mod doc_changing {}
806
807#[cfg(test)]
808mod test {
809    // @@ begin test lint list maintained by maint/add_warning @@
810    #![allow(clippy::bool_assert_comparison)]
811    #![allow(clippy::clone_on_copy)]
812    #![allow(clippy::dbg_macro)]
813    #![allow(clippy::mixed_attributes_style)]
814    #![allow(clippy::print_stderr)]
815    #![allow(clippy::print_stdout)]
816    #![allow(clippy::single_char_pattern)]
817    #![allow(clippy::unwrap_used)]
818    #![allow(clippy::unchecked_time_subtraction)]
819    #![allow(clippy::useless_vec)]
820    #![allow(clippy::needless_pass_by_value)]
821    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
822    use std::str::FromStr;
823
824    use super::*;
825
826    #[test]
827    fn test_bitrange() {
828        assert_eq!(0b1, bitrange(0, 0));
829        assert_eq!(0b10, bitrange(1, 1));
830        assert_eq!(0b11, bitrange(0, 1));
831        assert_eq!(0b1111110000000, bitrange(7, 12));
832        assert_eq!(!0, bitrange(0, 63));
833    }
834
835    #[test]
836    fn test_dumpmask() {
837        assert_eq!("", dumpmask(0));
838        assert_eq!("0-5", dumpmask(0b111111));
839        assert_eq!("4-5", dumpmask(0b110000));
840        assert_eq!("1,4-5", dumpmask(0b110010));
841        assert_eq!("0-63", dumpmask(!0));
842    }
843
844    #[test]
845    fn test_canonical() -> Result<(), ParseError> {
846        fn t(orig: &str, canonical: &str) -> Result<(), ParseError> {
847            let protos: Protocols = orig.parse()?;
848            let enc = format!("{}", protos);
849            assert_eq!(enc, canonical);
850            Ok(())
851        }
852
853        t("", "")?;
854        t(" ", "")?;
855        t("Link=5,6,7,9 Relay=4-7,2", "Link=5-7,9 Relay=2,4-7")?;
856        t("FlowCtrl= Padding=8,7 Desc=1-5,6-8", "Desc=1-8 Padding=7-8")?;
857        t("Zelda=7 Gannon=3,6 Link=4", "Gannon=3,6 Link=4 Zelda=7")?;
858
859        Ok(())
860    }
861
862    #[test]
863    fn test_invalid() {
864        fn t(s: &str) -> ParseError {
865            let protos: Result<Protocols, ParseError> = s.parse();
866            assert!(protos.is_err());
867            protos.err().unwrap()
868        }
869
870        assert_eq!(t("Link=1-100"), ParseError::OutOfRange);
871        assert_eq!(t("Zelda=100"), ParseError::OutOfRange);
872        assert_eq!(t("Link=100-200"), ParseError::OutOfRange);
873
874        assert_eq!(t("Link=1,1"), ParseError::Duplicate);
875        assert_eq!(t("Link=1 Link=1"), ParseError::Duplicate);
876        assert_eq!(t("Link=1 Link=3"), ParseError::Duplicate);
877        assert_eq!(t("Zelda=1 Zelda=3"), ParseError::Duplicate);
878
879        assert_eq!(t("Link=Zelda"), ParseError::Malformed);
880        assert_eq!(t("Link=6-2"), ParseError::Malformed);
881        assert_eq!(t("Link=6-"), ParseError::Malformed);
882        assert_eq!(t("Link=6-,2"), ParseError::Malformed);
883        assert_eq!(t("Link=1,,2"), ParseError::Malformed);
884        assert_eq!(t("Link=6-frog"), ParseError::Malformed);
885        assert_eq!(t("Link=gannon-9"), ParseError::Malformed);
886        assert_eq!(t("Link Zelda"), ParseError::Malformed);
887
888        assert_eq!(t("Link=01"), ParseError::Malformed);
889        assert_eq!(t("Link=waffle"), ParseError::Malformed);
890        assert_eq!(t("Link=1_1"), ParseError::Malformed);
891    }
892
893    #[test]
894    fn test_supports() -> Result<(), ParseError> {
895        let p: Protocols = "Link=4,5-7 Padding=2 Lonk=1-3,5".parse()?;
896
897        assert!(p.supports_known_subver(ProtoKind::Padding, 2));
898        assert!(!p.supports_known_subver(ProtoKind::Padding, 1));
899        assert!(p.supports_known_subver(ProtoKind::Link, 6));
900        assert!(!p.supports_known_subver(ProtoKind::Link, 255));
901        assert!(!p.supports_known_subver(ProtoKind::Cons, 1));
902        assert!(!p.supports_known_subver(ProtoKind::Cons, 0));
903        assert!(p.supports_subver("Link", 6));
904        assert!(!p.supports_subver("link", 6));
905        assert!(!p.supports_subver("Cons", 0));
906        assert!(p.supports_subver("Lonk", 3));
907        assert!(!p.supports_subver("Lonk", 4));
908        assert!(!p.supports_subver("lonk", 3));
909        assert!(!p.supports_subver("Lonk", 64));
910
911        Ok(())
912    }
913
914    #[test]
915    fn test_difference() -> Result<(), ParseError> {
916        let p1: Protocols = "Link=1-10 Desc=5-10 Relay=1,3,5,7,9 Other=7-60 Mine=1-20".parse()?;
917        let p2: Protocols = "Link=3-4 Desc=1-6 Relay=2-6 Other=8 Theirs=20".parse()?;
918
919        assert_eq!(
920            p1.difference(&p2),
921            Protocols::from_str("Link=1-2,5-10 Desc=7-10 Relay=1,7,9 Other=7,9-60 Mine=1-20")?
922        );
923        assert_eq!(
924            p2.difference(&p1),
925            Protocols::from_str("Desc=1-4 Relay=2,4,6 Theirs=20")?,
926        );
927
928        let nil = Protocols::default();
929        assert_eq!(p1.difference(&nil), p1);
930        assert_eq!(p2.difference(&nil), p2);
931        assert_eq!(nil.difference(&p1), nil);
932        assert_eq!(nil.difference(&p2), nil);
933
934        Ok(())
935    }
936
937    #[test]
938    fn test_union() -> Result<(), ParseError> {
939        let p1: Protocols = "Link=1-10 Desc=5-10 Relay=1,3,5,7,9 Other=7-60 Mine=1-20".parse()?;
940        let p2: Protocols = "Link=3-4 Desc=1-6 Relay=2-6 Other=2,8 Theirs=20".parse()?;
941
942        assert_eq!(
943            p1.union(&p2),
944            Protocols::from_str(
945                "Link=1-10 Desc=1-10 Relay=1-7,9 Other=2,7-60 Theirs=20 Mine=1-20"
946            )?
947        );
948        assert_eq!(
949            p2.union(&p1),
950            Protocols::from_str(
951                "Link=1-10 Desc=1-10 Relay=1-7,9 Other=2,7-60 Theirs=20 Mine=1-20"
952            )?
953        );
954
955        let nil = Protocols::default();
956        assert_eq!(p1.union(&nil), p1);
957        assert_eq!(p2.union(&nil), p2);
958        assert_eq!(nil.union(&p1), p1);
959        assert_eq!(nil.union(&p2), p2);
960
961        Ok(())
962    }
963
964    #[test]
965    fn test_intersection() -> 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.intersection(&p2),
971            Protocols::from_str("Link=3-4 Desc=5-6 Relay=3,5 Other=8")?
972        );
973        assert_eq!(
974            p2.intersection(&p1),
975            Protocols::from_str("Link=3-4 Desc=5-6 Relay=3,5 Other=8")?
976        );
977
978        let nil = Protocols::default();
979        assert_eq!(p1.intersection(&nil), nil);
980        assert_eq!(p2.intersection(&nil), nil);
981        assert_eq!(nil.intersection(&p1), nil);
982        assert_eq!(nil.intersection(&p2), nil);
983
984        Ok(())
985    }
986
987    #[test]
988    fn from_iter() {
989        use named as n;
990        let empty: [NamedSubver; 0] = [];
991        let prs: Protocols = empty.iter().copied().collect();
992        assert_eq!(prs, Protocols::default());
993        let prs: Protocols = empty.into_iter().collect();
994        assert_eq!(prs, Protocols::default());
995
996        let prs = [
997            n::LINK_V3,
998            n::HSDIR_V3,
999            n::LINK_V4,
1000            n::LINK_V5,
1001            n::CONFLUX_BASE,
1002        ]
1003        .into_iter()
1004        .collect::<Protocols>();
1005        assert_eq!(prs, "Link=3-5 HSDir=2 Conflux=1".parse().unwrap());
1006    }
1007
1008    #[test]
1009    fn order_numbered_subvers() {
1010        // We rely on this sort order elsewhere in our protocol.
1011        assert!(NumberedSubver::new(5, 7) < NumberedSubver::new(7, 5));
1012        assert!(NumberedSubver::new(7, 5) < NumberedSubver::new(7, 6));
1013        assert!(NumberedSubver::new(7, 6) < NumberedSubver::new(8, 6));
1014    }
1015}