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