Skip to main content

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