aboutsummaryrefslogtreecommitdiff
path: root/src/rust/protover
diff options
context:
space:
mode:
Diffstat (limited to 'src/rust/protover')
-rw-r--r--src/rust/protover/Cargo.toml33
-rw-r--r--src/rust/protover/errors.rs57
-rw-r--r--src/rust/protover/ffi.rs247
-rw-r--r--src/rust/protover/lib.rs40
-rw-r--r--src/rust/protover/protoset.rs697
-rw-r--r--src/rust/protover/protover.rs984
-rw-r--r--src/rust/protover/tests/protover.rs365
7 files changed, 0 insertions, 2423 deletions
diff --git a/src/rust/protover/Cargo.toml b/src/rust/protover/Cargo.toml
deleted file mode 100644
index 84a7c71c1a..0000000000
--- a/src/rust/protover/Cargo.toml
+++ /dev/null
@@ -1,33 +0,0 @@
-[package]
-authors = ["The Tor Project"]
-version = "0.0.1"
-name = "protover"
-
-[features]
-# We have to define a feature here because doctests don't get cfg(test),
-# and we need to disable some C dependencies when running the doctests
-# because of the various linker issues. See
-# https://github.com/rust-lang/rust/issues/45599
-test_linking_hack = []
-
-[dependencies]
-libc = "=0.2.39"
-
-[dependencies.smartlist]
-path = "../smartlist"
-
-[dependencies.external]
-path = "../external"
-
-[dependencies.tor_util]
-path = "../tor_util"
-
-[dependencies.tor_allocate]
-path = "../tor_allocate"
-
-[dependencies.tor_log]
-path = "../tor_log"
-
-[lib]
-name = "protover"
-path = "lib.rs"
diff --git a/src/rust/protover/errors.rs b/src/rust/protover/errors.rs
deleted file mode 100644
index 04397ac4fe..0000000000
--- a/src/rust/protover/errors.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (c) 2018-2019, The Tor Project, Inc.
-// Copyright (c) 2018, isis agora lovecruft
-// See LICENSE for licensing information
-
-//! Various errors which may occur during protocol version parsing.
-
-use std::fmt;
-use std::fmt::Display;
-
-/// All errors which may occur during protover parsing routines.
-#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
-#[allow(missing_docs)] // See Display impl for error descriptions
-pub enum ProtoverError {
- Overlap,
- LowGreaterThanHigh,
- Unparseable,
- ExceedsMax,
- ExceedsExpansionLimit,
- UnknownProtocol,
- ExceedsNameLimit,
- InvalidProtocol,
-}
-
-/// Descriptive error messages for `ProtoverError` variants.
-impl Display for ProtoverError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- ProtoverError::Overlap => write!(
- f,
- "Two or more (low, high) protover ranges would overlap once expanded."
- ),
- ProtoverError::LowGreaterThanHigh => write!(
- f,
- "The low in a (low, high) protover range was greater than high."
- ),
- ProtoverError::Unparseable => write!(f, "The protover string was unparseable."),
- ProtoverError::ExceedsMax => write!(
- f,
- "The high in a (low, high) protover range exceeds 63."
- ),
- ProtoverError::ExceedsExpansionLimit => write!(
- f,
- "The protover string would exceed the maximum expansion limit."
- ),
- ProtoverError::UnknownProtocol => write!(
- f,
- "A protocol in the protover string we attempted to parse is unknown."
- ),
- ProtoverError::ExceedsNameLimit => {
- write!(f, "An unrecognised protocol name was too long.")
- }
- ProtoverError::InvalidProtocol => {
- write!(f, "A protocol name includes invalid characters.")
- }
- }
- }
-}
diff --git a/src/rust/protover/ffi.rs b/src/rust/protover/ffi.rs
deleted file mode 100644
index 2bf8d3a987..0000000000
--- a/src/rust/protover/ffi.rs
+++ /dev/null
@@ -1,247 +0,0 @@
-// Copyright (c) 2016-2019, The Tor Project, Inc. */
-// See LICENSE for licensing information */
-
-//! FFI functions, only to be called from C.
-//!
-//! Equivalent C versions of this api are in `protover.c`
-
-use libc::{c_char, c_int, uint32_t};
-use std::ffi::CStr;
-
-use smartlist::*;
-use tor_allocate::allocate_and_copy_string;
-
-use errors::ProtoverError;
-use protover::*;
-
-/// Translate C enums to Rust Proto enums, using the integer value of the C
-/// enum to map to its associated Rust enum.
-///
-/// C_RUST_COUPLED: protover.h `protocol_type_t`
-fn translate_to_rust(c_proto: uint32_t) -> Result<Protocol, ProtoverError> {
- match c_proto {
- 0 => Ok(Protocol::Link),
- 1 => Ok(Protocol::LinkAuth),
- 2 => Ok(Protocol::Relay),
- 3 => Ok(Protocol::DirCache),
- 4 => Ok(Protocol::HSDir),
- 5 => Ok(Protocol::HSIntro),
- 6 => Ok(Protocol::HSRend),
- 7 => Ok(Protocol::Desc),
- 8 => Ok(Protocol::Microdesc),
- 9 => Ok(Protocol::Cons),
- 10 => Ok(Protocol::Padding),
- 11 => Ok(Protocol::FlowCtrl),
- _ => Err(ProtoverError::UnknownProtocol),
- }
-}
-
-/// Provide an interface for C to translate arguments and return types for
-/// protover::all_supported
-#[no_mangle]
-pub extern "C" fn protover_all_supported(
- c_relay_version: *const c_char,
- missing_out: *mut *mut c_char,
-) -> c_int {
- if c_relay_version.is_null() {
- return 1;
- }
-
- // Require an unsafe block to read the version from a C string. The pointer
- // is checked above to ensure it is not null.
- let c_str: &CStr = unsafe { CStr::from_ptr(c_relay_version) };
-
- let relay_version = match c_str.to_str() {
- Ok(n) => n,
- Err(_) => return 1,
- };
-
- let relay_proto_entry: UnvalidatedProtoEntry =
- match UnvalidatedProtoEntry::from_str_any_len(relay_version) {
- Ok(n) => n,
- Err(_) => return 1,
- };
-
- if let Some(unsupported) = relay_proto_entry.all_supported() {
- if missing_out.is_null() {
- return 0;
- }
- let ptr = allocate_and_copy_string(&unsupported.to_string());
- unsafe { *missing_out = ptr };
-
- return 0;
- }
-
- 1
-}
-
-/// Provide an interface for C to translate arguments and return types for
-/// protover::list_supports_protocol
-#[no_mangle]
-pub extern "C" fn protocol_list_supports_protocol(
- c_protocol_list: *const c_char,
- c_protocol: uint32_t,
- version: uint32_t,
-) -> c_int {
- if c_protocol_list.is_null() {
- return 0;
- }
-
- // Require an unsafe block to read the version from a C string. The pointer
- // is checked above to ensure it is not null.
- let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) };
-
- let protocol_list = match c_str.to_str() {
- Ok(n) => n,
- Err(_) => return 0,
- };
- let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() {
- Ok(n) => n,
- Err(_) => return 0,
- };
- let protocol: UnknownProtocol = match translate_to_rust(c_protocol) {
- Ok(n) => n.into(),
- Err(_) => return 0,
- };
- if proto_entry.supports_protocol(&protocol, &version) {
- 1
- } else {
- 0
- }
-}
-
-#[no_mangle]
-pub extern "C" fn protover_contains_long_protocol_names_(c_protocol_list: *const c_char) -> c_int {
- if c_protocol_list.is_null() {
- return 1;
- }
-
- // Require an unsafe block to read the version from a C string. The pointer
- // is checked above to ensure it is not null.
- let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) };
-
- let protocol_list = match c_str.to_str() {
- Ok(n) => n,
- Err(_) => return 1,
- };
-
- match protocol_list.parse::<UnvalidatedProtoEntry>() {
- Ok(_) => 0,
- Err(_) => 1,
- }
-}
-
-/// Provide an interface for C to translate arguments and return types for
-/// protover::list_supports_protocol_or_later
-#[no_mangle]
-pub extern "C" fn protocol_list_supports_protocol_or_later(
- c_protocol_list: *const c_char,
- c_protocol: uint32_t,
- version: uint32_t,
-) -> c_int {
- if c_protocol_list.is_null() {
- return 0;
- }
-
- // Require an unsafe block to read the version from a C string. The pointer
- // is checked above to ensure it is not null.
- let c_str: &CStr = unsafe { CStr::from_ptr(c_protocol_list) };
-
- let protocol_list = match c_str.to_str() {
- Ok(n) => n,
- Err(_) => return 0,
- };
-
- let protocol = match translate_to_rust(c_protocol) {
- Ok(n) => n,
- Err(_) => return 0,
- };
-
- let proto_entry: UnvalidatedProtoEntry = match protocol_list.parse() {
- Ok(n) => n,
- Err(_) => return 0,
- };
-
- if proto_entry.supports_protocol_or_later(&protocol.into(), &version) {
- return 1;
- }
- 0
-}
-
-/// Provide an interface for C to translate arguments and return types for
-/// protover::get_supported_protocols
-#[no_mangle]
-pub extern "C" fn protover_get_supported_protocols() -> *const c_char {
- let supported: &'static CStr;
-
- supported = get_supported_protocols_cstr();
- supported.as_ptr()
-}
-
-/// Provide an interface for C to translate arguments and return types for
-/// protover::compute_vote
-//
-// Why is the threshold a signed integer? —isis
-#[no_mangle]
-pub extern "C" fn protover_compute_vote(list: *const Stringlist, threshold: c_int) -> *mut c_char {
- if list.is_null() {
- return allocate_and_copy_string("");
- }
-
- // Dereference of raw pointer requires an unsafe block. The pointer is
- // checked above to ensure it is not null.
- let data: Vec<String> = unsafe { (*list).get_list() };
- let hold: usize = threshold as usize;
- let mut proto_entries: Vec<UnvalidatedProtoEntry> = Vec::new();
-
- for datum in data {
- let entry: UnvalidatedProtoEntry = match datum.parse() {
- Ok(n) => n,
- Err(_) => continue,
- };
- proto_entries.push(entry);
- }
- let vote: UnvalidatedProtoEntry = ProtoverVote::compute(&proto_entries, &hold);
-
- allocate_and_copy_string(&vote.to_string())
-}
-
-/// Provide an interface for C to translate arguments and return types for
-/// protover::is_supported_here
-#[no_mangle]
-pub extern "C" fn protover_is_supported_here(c_protocol: uint32_t, version: uint32_t) -> c_int {
- let protocol = match translate_to_rust(c_protocol) {
- Ok(n) => n,
- Err(_) => return 0,
- };
-
- let is_supported = is_supported_here(&protocol, &version);
-
- return if is_supported { 1 } else { 0 };
-}
-
-/// Provide an interface for C to translate arguments and return types for
-/// protover::compute_for_old_tor
-#[no_mangle]
-pub extern "C" fn protover_compute_for_old_tor(version: *const c_char) -> *const c_char {
- let supported: &'static CStr;
- let empty: &'static CStr;
-
- empty = cstr!("");
-
- if version.is_null() {
- return empty.as_ptr();
- }
-
- // Require an unsafe block to read the version from a C string. The pointer
- // is checked above to ensure it is not null.
- let c_str: &CStr = unsafe { CStr::from_ptr(version) };
-
- let version = match c_str.to_str() {
- Ok(n) => n,
- Err(_) => return empty.as_ptr(),
- };
-
- supported = compute_for_old_tor_cstr(&version);
- supported.as_ptr()
-}
diff --git a/src/rust/protover/lib.rs b/src/rust/protover/lib.rs
deleted file mode 100644
index 35c4106ae5..0000000000
--- a/src/rust/protover/lib.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-//! Copyright (c) 2016-2019, The Tor Project, Inc. */
-//! See LICENSE for licensing information */
-
-//! Versioning information for different pieces of the Tor protocol.
-//!
-//! The below description is taken from src/rust/protover.c, which is currently
-//! enabled by default. We are in the process of experimenting with Rust in
-//! tor, and this protover module is implemented to help achieve this goal.
-//!
-//! Starting in version 0.2.9.3-alpha, Tor places separate version numbers on
-//! each of the different components of its protocol. Relays use these numbers
-//! to advertise what versions of the protocols they can support, and clients
-//! use them to find what they can ask a given relay to do. Authorities vote
-//! on the supported protocol versions for each relay, and also vote on the
-//! which protocols you should have to support in order to be on the Tor
-//! network. All Tor instances use these required/recommended protocol versions
-//! to tell what level of support for recent protocols each relay has, and
-//! to decide whether they should be running given their current protocols.
-//!
-//! The main advantage of these protocol versions numbers over using Tor
-//! version numbers is that they allow different implementations of the Tor
-//! protocols to develop independently, without having to claim compatibility
-//! with specific versions of Tor.
-
-// XXX: add missing docs
-//#![deny(missing_docs)]
-
-extern crate external;
-extern crate libc;
-extern crate smartlist;
-extern crate tor_allocate;
-#[macro_use]
-extern crate tor_util;
-
-pub mod errors;
-pub mod ffi;
-pub mod protoset;
-mod protover;
-
-pub use protover::*;
diff --git a/src/rust/protover/protoset.rs b/src/rust/protover/protoset.rs
deleted file mode 100644
index 0ab94457c5..0000000000
--- a/src/rust/protover/protoset.rs
+++ /dev/null
@@ -1,697 +0,0 @@
-// Copyright (c) 2018-2019, The Tor Project, Inc.
-// Copyright (c) 2018, isis agora lovecruft
-// See LICENSE for licensing information
-
-//! Sets for lazily storing ordered, non-overlapping ranges of integers.
-
-use std::cmp;
-use std::iter;
-use std::slice;
-use std::str::FromStr;
-use std::u32;
-
-use errors::ProtoverError;
-
-/// A single version number.
-pub type Version = u32;
-
-/// A `ProtoSet` stores an ordered `Vec<T>` of `(low, high)` pairs of ranges of
-/// non-overlapping protocol versions.
-///
-/// # Examples
-///
-/// ```
-/// use std::str::FromStr;
-///
-/// use protover::errors::ProtoverError;
-/// use protover::protoset::ProtoSet;
-/// use protover::protoset::Version;
-///
-/// # fn do_test() -> Result<ProtoSet, ProtoverError> {
-/// let protoset: ProtoSet = ProtoSet::from_str("3-5,8")?;
-///
-/// // We could also equivalently call:
-/// let protoset: ProtoSet = "3-5,8".parse()?;
-///
-/// assert!(protoset.contains(&4));
-/// assert!(!protoset.contains(&7));
-///
-/// let expanded: Vec<Version> = protoset.clone().into();
-///
-/// assert_eq!(&expanded[..], &[3, 4, 5, 8]);
-///
-/// let contracted: String = protoset.clone().to_string();
-///
-/// assert_eq!(contracted, "3-5,8".to_string());
-/// # Ok(protoset)
-/// # }
-/// # fn main() { do_test(); } // wrap the test so we can use the ? operator
-#[derive(Clone, Debug, Eq, PartialEq, Hash)]
-pub struct ProtoSet {
- pub(crate) pairs: Vec<(Version, Version)>,
-}
-
-impl Default for ProtoSet {
- fn default() -> Self {
- let pairs: Vec<(Version, Version)> = Vec::new();
-
- ProtoSet { pairs }
- }
-}
-
-impl<'a> ProtoSet {
- /// Create a new `ProtoSet` from a slice of `(low, high)` pairs.
- ///
- /// # Inputs
- ///
- /// We do not assume the input pairs are deduplicated or ordered.
- pub fn from_slice(low_high_pairs: &'a [(Version, Version)]) -> Result<Self, ProtoverError> {
- let mut pairs: Vec<(Version, Version)> = Vec::with_capacity(low_high_pairs.len());
-
- for &(low, high) in low_high_pairs {
- pairs.push((low, high));
- }
- // Sort the pairs without reallocation and remove all duplicate pairs.
- pairs.sort_unstable();
- pairs.dedup();
-
- ProtoSet { pairs }.is_ok()
- }
-}
-
-/// Expand this `ProtoSet` to a `Vec` of all its `Version`s.
-///
-/// # Examples
-///
-/// ```
-/// use std::str::FromStr;
-/// use protover::protoset::ProtoSet;
-/// use protover::protoset::Version;
-/// # use protover::errors::ProtoverError;
-///
-/// # fn do_test() -> Result<Vec<Version>, ProtoverError> {
-/// let protoset: ProtoSet = ProtoSet::from_str("3-5,21")?;
-/// let versions: Vec<Version> = protoset.into();
-///
-/// assert_eq!(&versions[..], &[3, 4, 5, 21]);
-/// #
-/// # Ok(versions)
-/// # }
-/// # fn main() { do_test(); } // wrap the test so we can use the ? operator
-/// ```
-impl Into<Vec<Version>> for ProtoSet {
- fn into(self) -> Vec<Version> {
- let mut versions: Vec<Version> = Vec::new();
-
- for &(low, high) in self.iter() {
- versions.extend(low..high + 1);
- }
- versions
- }
-}
-
-impl ProtoSet {
- /// Get an iterator over the `(low, high)` `pairs` in this `ProtoSet`.
- pub fn iter(&self) -> slice::Iter<(Version, Version)> {
- self.pairs.iter()
- }
-
- /// Expand this `ProtoSet` into a `Vec` of all its `Version`s.
- ///
- /// # Examples
- ///
- /// ```
- /// # use protover::errors::ProtoverError;
- /// use protover::protoset::ProtoSet;
- ///
- /// # fn do_test() -> Result<bool, ProtoverError> {
- /// let protoset: ProtoSet = "3-5,9".parse()?;
- ///
- /// assert_eq!(protoset.expand(), vec![3, 4, 5, 9]);
- ///
- /// let protoset: ProtoSet = "1,3,5-7".parse()?;
- ///
- /// assert_eq!(protoset.expand(), vec![1, 3, 5, 6, 7]);
- /// #
- /// # Ok(true)
- /// # }
- /// # fn main() { do_test(); } // wrap the test so we can use the ? operator
- /// ```
- pub fn expand(self) -> Vec<Version> {
- self.into()
- }
-
- pub fn len(&self) -> usize {
- let mut length: usize = 0;
-
- for &(low, high) in self.iter() {
- length += (high as usize - low as usize) + 1;
- }
-
- length
- }
-
- /// Check that this `ProtoSet` is well-formed.
- ///
- /// This is automatically called in `ProtoSet::from_str()`.
- ///
- /// # Errors
- ///
- /// * `ProtoverError::LowGreaterThanHigh`: if its `pairs` were not
- /// well-formed, i.e. a `low` in a `(low, high)` was higher than the
- /// previous `high`,
- /// * `ProtoverError::Overlap`: if one or more of the `pairs` are
- /// overlapping,
- /// * `ProtoverError::ExceedsMax`: if the number of versions when expanded
- /// would exceed `MAX_PROTOCOLS_TO_EXPAND`, and
- ///
- /// # Returns
- ///
- /// A `Result` whose `Ok` is this `Protoset`, and whose `Err` is one of the
- /// errors enumerated in the Errors section above.
- fn is_ok(self) -> Result<ProtoSet, ProtoverError> {
- let mut last_high: Version = 0;
-
- for &(low, high) in self.iter() {
- if low == u32::MAX || high == u32::MAX {
- return Err(ProtoverError::ExceedsMax);
- }
- if low <= last_high {
- return Err(ProtoverError::Overlap);
- } else if low > high {
- return Err(ProtoverError::LowGreaterThanHigh);
- }
- last_high = high;
- }
-
- Ok(self)
- }
-
- /// Determine if this `ProtoSet` contains no `Version`s.
- ///
- /// # Returns
- ///
- /// * `true` if this `ProtoSet`'s length is zero, and
- /// * `false` otherwise.
- ///
- /// # Examples
- ///
- /// ```
- /// use protover::protoset::ProtoSet;
- ///
- /// let protoset: ProtoSet = ProtoSet::default();
- ///
- /// assert!(protoset.is_empty());
- /// ```
- pub fn is_empty(&self) -> bool {
- self.pairs.len() == 0
- }
-
- /// Determine if `version` is included within this `ProtoSet`.
- ///
- /// # Inputs
- ///
- /// * `version`: a `Version`.
- ///
- /// # Returns
- ///
- /// `true` if the `version` is contained within this set; `false` otherwise.
- ///
- /// # Examples
- ///
- /// ```
- /// # use protover::errors::ProtoverError;
- /// use protover::protoset::ProtoSet;
- ///
- /// # fn do_test() -> Result<ProtoSet, ProtoverError> {
- /// let protoset: ProtoSet = ProtoSet::from_slice(&[(0, 5), (7, 9), (13, 14)])?;
- ///
- /// assert!(protoset.contains(&5));
- /// assert!(!protoset.contains(&10));
- /// #
- /// # Ok(protoset)
- /// # }
- /// # fn main() { do_test(); } // wrap the test so we can use the ? operator
- /// ```
- pub fn contains(&self, version: &Version) -> bool {
- for &(low, high) in self.iter() {
- if low <= *version && *version <= high {
- return true;
- }
- }
- false
- }
-
- /// Returns all the `Version`s in `self` which are not also in the `other`
- /// `ProtoSet`.
- ///
- /// # Examples
- ///
- /// ```
- /// # use protover::errors::ProtoverError;
- /// use protover::protoset::ProtoSet;
- ///
- /// # fn do_test() -> Result<bool, ProtoverError> {
- /// let protoset: ProtoSet = "1,3-6,10-12,15-16".parse()?;
- /// let other: ProtoSet = "2,5-7,9-11,14-20".parse()?;
- ///
- /// let subset: ProtoSet = protoset.and_not_in(&other);
- ///
- /// assert_eq!(subset.expand(), vec![1, 3, 4, 12]);
- /// #
- /// # Ok(true)
- /// # }
- /// # fn main() { do_test(); } // wrap the test so we can use the ? operator
- /// ```
- pub fn and_not_in(&self, other: &Self) -> Self {
- if self.is_empty() || other.is_empty() {
- return self.clone();
- }
-
- let pairs = self.iter().flat_map(|&(lo, hi)| {
- let the_end = (hi + 1, hi + 1); // special case to mark the end of the range.
- let excluded_ranges = other
- .iter()
- .cloned() // have to be owned tuples, to match iter::once(the_end).
- .skip_while(move|&(_, hi2)| hi2 < lo) // skip the non-overlapping ranges.
- .take_while(move|&(lo2, _)| lo2 <= hi) // take all the overlapping ones.
- .chain(iter::once(the_end));
-
- let mut nextlo = lo;
- excluded_ranges.filter_map(move |(excluded_lo, excluded_hi)| {
- let pair = if nextlo < excluded_lo {
- Some((nextlo, excluded_lo - 1))
- } else {
- None
- };
- nextlo = cmp::min(excluded_hi, u32::MAX - 1) + 1;
- pair
- })
- });
-
- let pairs = pairs.collect();
- ProtoSet::is_ok(ProtoSet { pairs }).expect("should be already sorted")
- }
-}
-
-/// Largest allowed protocol version.
-/// C_RUST_COUPLED: protover.c `MAX_PROTOCOL_VERSION`
-const MAX_PROTOCOL_VERSION: Version = 63;
-
-impl FromStr for ProtoSet {
- type Err = ProtoverError;
-
- /// Parse the unique version numbers supported by a subprotocol from a string.
- ///
- /// # Inputs
- ///
- /// * `version_string`, a string comprised of "[0-9,-]"
- ///
- /// # Returns
- ///
- /// A `Result` whose `Ok` value is a `ProtoSet` holding all of the unique
- /// version numbers.
- ///
- /// The returned `Result`'s `Err` value is an `ProtoverError` appropriate to
- /// the error.
- ///
- /// # Errors
- ///
- /// This function will error if:
- ///
- /// * the `version_string` is an equals (`"="`) sign,
- /// * the expansion of a version range produces an error (see
- /// `expand_version_range`),
- /// * any single version number is not parseable as an `u32` in radix 10, or
- /// * there are greater than 2^16 version numbers to expand.
- ///
- /// # Examples
- ///
- /// ```
- /// use std::str::FromStr;
- ///
- /// use protover::errors::ProtoverError;
- /// use protover::protoset::ProtoSet;
- ///
- /// # fn do_test() -> Result<ProtoSet, ProtoverError> {
- /// let protoset: ProtoSet = ProtoSet::from_str("2-5,8")?;
- ///
- /// assert!(protoset.contains(&5));
- /// assert!(!protoset.contains(&10));
- ///
- /// // We can also equivalently call `ProtoSet::from_str` by doing (all
- /// // implementations of `FromStr` can be called this way, this one isn't
- /// // special):
- /// let protoset: ProtoSet = "4-6,12".parse()?;
- ///
- /// // Calling it (either way) can take really large ranges (up to `u32::MAX`):
- /// let protoset: ProtoSet = "1-70000".parse()?;
- /// let protoset: ProtoSet = "1-4294967296".parse()?;
- ///
- /// // There are lots of ways to get an `Err` from this function. Here are
- /// // a few:
- /// assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("="));
- /// assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("-"));
- /// assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("not_an_int"));
- /// assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("3-"));
- /// assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1-,4"));
- ///
- /// // An empty string is, however, legal, and results in an
- /// // empty `ProtoSet`:
- /// assert_eq!(Ok(ProtoSet::default()), ProtoSet::from_str(""));
- /// #
- /// # Ok(protoset)
- /// # }
- /// # fn main() { do_test(); } // wrap the test so we can use the ? operator
- /// ```
- fn from_str(version_string: &str) -> Result<Self, Self::Err> {
- // If we were passed in an empty string, then return an empty ProtoSet.
- if version_string.is_empty() {
- return Ok(Self::default());
- }
-
- let mut pairs: Vec<(Version, Version)> = Vec::new();
- let pieces: ::std::str::Split<char> = version_string.split(',');
-
- for p in pieces {
- let (lo,hi) = if p.contains('-') {
- let mut pair = p.splitn(2, '-');
-
- let low = pair.next().ok_or(ProtoverError::Unparseable)?;
- let high = pair.next().ok_or(ProtoverError::Unparseable)?;
-
- let lo: Version = low.parse().or(Err(ProtoverError::Unparseable))?;
- let hi: Version = high.parse().or(Err(ProtoverError::Unparseable))?;
-
- (lo,hi)
- } else {
- let v: u32 = p.parse().or(Err(ProtoverError::Unparseable))?;
-
- (v, v)
- };
-
- if lo > MAX_PROTOCOL_VERSION || hi > MAX_PROTOCOL_VERSION {
- return Err(ProtoverError::ExceedsMax);
- }
- pairs.push((lo, hi));
- }
-
- ProtoSet::from_slice(&pairs[..])
- }
-}
-
-impl ToString for ProtoSet {
- /// Contracts a `ProtoSet` of versions into a string.
- ///
- /// # Returns
- ///
- /// A `String` representation of this `ProtoSet` in ascending order.
- fn to_string(&self) -> String {
- let mut final_output: Vec<String> = Vec::new();
-
- for &(lo, hi) in self.iter() {
- if lo != hi {
- debug_assert!(lo < hi);
- final_output.push(format!("{}-{}", lo, hi));
- } else {
- final_output.push(format!("{}", lo));
- }
- }
- final_output.join(",")
- }
-}
-
-/// Checks to see if there is a continuous range of integers, starting at the
-/// first in the list. Returns the last integer in the range if a range exists.
-///
-/// # Inputs
-///
-/// `list`, an ordered vector of `u32` integers of "[0-9,-]" representing the
-/// supported versions for a single protocol.
-///
-/// # Returns
-///
-/// A `bool` indicating whether the list contains a range, starting at the first
-/// in the list, a`Version` of the last integer in the range, and a `usize` of
-/// the index of that version.
-///
-/// For example, if given vec![1, 2, 3, 5], find_range will return true,
-/// as there is a continuous range, and 3, which is the last number in the
-/// continuous range, and 2 which is the index of 3.
-fn find_range(list: &Vec<Version>) -> (bool, Version, usize) {
- if list.len() == 0 {
- return (false, 0, 0);
- }
-
- let mut index: usize = 0;
- let mut iterable = list.iter().peekable();
- let mut range_end = match iterable.next() {
- Some(n) => *n,
- None => return (false, 0, 0),
- };
-
- let mut has_range = false;
-
- while iterable.peek().is_some() {
- let n = *iterable.next().unwrap();
- if n != range_end + 1 {
- break;
- }
-
- has_range = true;
- range_end = n;
- index += 1;
- }
-
- (has_range, range_end, index)
-}
-
-impl From<Vec<Version>> for ProtoSet {
- fn from(mut v: Vec<Version>) -> ProtoSet {
- let mut version_pairs: Vec<(Version, Version)> = Vec::new();
-
- v.sort_unstable();
- v.dedup();
-
- 'vector: while !v.is_empty() {
- let (has_range, end, index): (bool, Version, usize) = find_range(&v);
-
- if has_range {
- let first: Version = match v.first() {
- Some(x) => *x,
- None => continue,
- };
- let last: Version = match v.get(index) {
- Some(x) => *x,
- None => continue,
- };
- debug_assert!(last == end, format!("last = {}, end = {}", last, end));
-
- version_pairs.push((first, last));
- v = v.split_off(index + 1);
-
- if v.len() == 0 {
- break 'vector;
- }
- } else {
- let last: Version = match v.get(index) {
- Some(x) => *x,
- None => continue,
- };
- version_pairs.push((last, last));
- v.remove(index);
- }
- }
- ProtoSet::from_slice(&version_pairs[..]).unwrap_or(ProtoSet::default())
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn test_find_range() {
- assert_eq!((false, 0, 0), find_range(&vec![]));
- assert_eq!((false, 1, 0), find_range(&vec![1]));
- assert_eq!((true, 2, 1), find_range(&vec![1, 2]));
- assert_eq!((true, 3, 2), find_range(&vec![1, 2, 3]));
- assert_eq!((true, 3, 2), find_range(&vec![1, 2, 3, 5]));
- }
-
- macro_rules! assert_contains_each {
- ($protoset:expr, $versions:expr) => {
- for version in $versions {
- assert!($protoset.contains(version));
- }
- };
- }
-
- macro_rules! test_protoset_contains_versions {
- ($list:expr, $str:expr) => {
- let versions: &[Version] = $list;
- let protoset: Result<ProtoSet, ProtoverError> = ProtoSet::from_str($str);
-
- assert!(protoset.is_ok());
- let p = protoset.unwrap();
- assert_contains_each!(p, versions);
- };
- }
-
- #[test]
- fn test_versions_from_str() {
- test_protoset_contains_versions!(&[], "");
- test_protoset_contains_versions!(&[1], "1");
- test_protoset_contains_versions!(&[1, 2], "1,2");
- test_protoset_contains_versions!(&[1, 2, 3], "1-3");
- test_protoset_contains_versions!(&[1, 2, 5], "1-2,5");
- test_protoset_contains_versions!(&[1, 3, 4, 5], "1,3-5");
- test_protoset_contains_versions!(&[42, 55, 56, 57, 58], "42,55-58");
- }
-
- #[test]
- fn test_versions_from_str_ab() {
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("a,b"));
- }
-
- #[test]
- fn test_versions_from_str_negative_1() {
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("-1"));
- }
-
- #[test]
- fn test_versions_from_str_commas() {
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str(","));
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1,,2"));
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1,2,"));
- }
-
- #[test]
- fn test_versions_from_str_hyphens() {
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("--1"));
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("-1-2"));
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1--2"));
- }
-
- #[test]
- fn test_versions_from_str_triple() {
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1-2-3"));
- }
-
- #[test]
- fn test_versions_from_str_1exclam() {
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1,!"));
- }
-
- #[test]
- fn test_versions_from_str_percent_equal() {
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("%="));
- }
-
- #[test]
- fn test_versions_from_str_whitespace() {
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1,2\n"));
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1\r,2"));
- assert_eq!(Err(ProtoverError::Unparseable), ProtoSet::from_str("1,\t2"));
- }
-
- #[test]
- fn test_versions_from_str_overlap() {
- assert_eq!(Err(ProtoverError::Overlap), ProtoSet::from_str("1-3,2-4"));
- }
-
- #[test]
- fn test_versions_from_slice_overlap() {
- assert_eq!(
- Err(ProtoverError::Overlap),
- ProtoSet::from_slice(&[(1, 3), (2, 4)])
- );
- }
-
- #[test]
- fn test_versions_from_str_max() {
- assert_eq!(
- Err(ProtoverError::ExceedsMax),
- ProtoSet::from_str("4294967295")
- );
- }
-
- #[test]
- fn test_versions_from_slice_max() {
- assert_eq!(
- Err(ProtoverError::ExceedsMax),
- ProtoSet::from_slice(&[(4294967295, 4294967295)])
- );
- }
-
- #[test]
- fn test_protoset_contains() {
- let protoset: ProtoSet = ProtoSet::from_slice(&[(1, 5), (7, 9), (13, 14)]).unwrap();
-
- for x in 1..6 {
- assert!(protoset.contains(&x), format!("should contain {}", x));
- }
- for x in 7..10 {
- assert!(protoset.contains(&x), format!("should contain {}", x));
- }
- for x in 13..15 {
- assert!(protoset.contains(&x), format!("should contain {}", x));
- }
-
- for x in [6, 10, 11, 12, 15, 42, 43, 44, 45, 1234584].iter() {
- assert!(!protoset.contains(&x), format!("should not contain {}", x));
- }
- }
-
- #[test]
- fn test_protoset_contains_1_3() {
- let protoset: ProtoSet = ProtoSet::from_slice(&[(1, 3)]).unwrap();
-
- for x in 1..4 {
- assert!(protoset.contains(&x), format!("should contain {}", x));
- }
- }
-
- macro_rules! assert_protoset_from_vec_contains_all {
- ($($x:expr),*) => (
- let vec: Vec<Version> = vec!($($x),*);
- let protoset: ProtoSet = vec.clone().into();
-
- for x in vec.iter() {
- assert!(protoset.contains(&x));
- }
- )
- }
-
- #[test]
- fn test_protoset_from_vec_123() {
- assert_protoset_from_vec_contains_all!(1, 2, 3);
- }
-
- #[test]
- fn test_protoset_from_vec_1_315() {
- assert_protoset_from_vec_contains_all!(1, 2, 3, 15);
- }
-
- #[test]
- fn test_protoset_from_vec_unordered() {
- let v: Vec<Version> = vec![2, 3, 8, 4, 3, 9, 7, 2];
- let ps: ProtoSet = v.into();
-
- assert_eq!(ps.to_string(), "2-4,7-9");
- }
-
- #[test]
- fn test_protoset_into_vec() {
- let ps: ProtoSet = "1-13,42".parse().unwrap();
- let v: Vec<Version> = ps.into();
-
- assert!(v.contains(&7));
- assert!(v.contains(&42));
- }
-}
-
-#[cfg(all(test, feature = "bench"))]
-mod bench {
- use super::*;
-}
diff --git a/src/rust/protover/protover.rs b/src/rust/protover/protover.rs
deleted file mode 100644
index da87509ffa..0000000000
--- a/src/rust/protover/protover.rs
+++ /dev/null
@@ -1,984 +0,0 @@
-// Copyright (c) 2016-2019, The Tor Project, Inc. */
-// See LICENSE for licensing information */
-
-use std::collections::hash_map;
-use std::collections::HashMap;
-use std::ffi::CStr;
-use std::fmt;
-use std::str;
-use std::str::FromStr;
-use std::string::String;
-
-use external::c_tor_version_as_new_as;
-
-use errors::ProtoverError;
-use protoset::ProtoSet;
-use protoset::Version;
-
-/// The first version of Tor that included "proto" entries in its descriptors.
-/// Authorities should use this to decide whether to guess proto lines.
-///
-/// C_RUST_COUPLED:
-/// protover.h `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS`
-const FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS: &'static str = "0.2.9.3-alpha";
-
-/// The maximum number of subprotocol version numbers we will attempt to expand
-/// before concluding that someone is trying to DoS us
-///
-/// C_RUST_COUPLED: protover.c `MAX_PROTOCOLS_TO_EXPAND`
-const MAX_PROTOCOLS_TO_EXPAND: usize = 1 << 16;
-
-/// The maximum size an `UnknownProtocol`'s name may be.
-pub(crate) const MAX_PROTOCOL_NAME_LENGTH: usize = 100;
-
-/// Known subprotocols in Tor. Indicates which subprotocol a relay supports.
-///
-/// C_RUST_COUPLED: protover.h `protocol_type_t`
-#[derive(Clone, Hash, Eq, PartialEq, Debug)]
-pub enum Protocol {
- Cons,
- Desc,
- DirCache,
- HSDir,
- HSIntro,
- HSRend,
- Link,
- LinkAuth,
- Microdesc,
- Relay,
- Padding,
- FlowCtrl,
-}
-
-impl fmt::Display for Protocol {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{:?}", self)
- }
-}
-
-/// Translates a string representation of a protocol into a Proto type.
-/// Error if the string is an unrecognized protocol name.
-///
-/// C_RUST_COUPLED: protover.c `PROTOCOL_NAMES`
-impl FromStr for Protocol {
- type Err = ProtoverError;
-
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- match s {
- "Cons" => Ok(Protocol::Cons),
- "Desc" => Ok(Protocol::Desc),
- "DirCache" => Ok(Protocol::DirCache),
- "HSDir" => Ok(Protocol::HSDir),
- "HSIntro" => Ok(Protocol::HSIntro),
- "HSRend" => Ok(Protocol::HSRend),
- "Link" => Ok(Protocol::Link),
- "LinkAuth" => Ok(Protocol::LinkAuth),
- "Microdesc" => Ok(Protocol::Microdesc),
- "Relay" => Ok(Protocol::Relay),
- "Padding" => Ok(Protocol::Padding),
- "FlowCtrl" => Ok(Protocol::FlowCtrl),
- _ => Err(ProtoverError::UnknownProtocol),
- }
- }
-}
-
-/// A protocol string which is not one of the `Protocols` we currently know
-/// about.
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
-pub struct UnknownProtocol(String);
-
-impl fmt::Display for UnknownProtocol {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.0)
- }
-}
-
-fn is_valid_proto(s: &str) -> bool {
- s.chars().all(|c| c.is_ascii_alphanumeric() || c == '-')
-}
-
-impl FromStr for UnknownProtocol {
- type Err = ProtoverError;
-
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- if !is_valid_proto(s) {
- Err(ProtoverError::InvalidProtocol)
- } else if s.len() <= MAX_PROTOCOL_NAME_LENGTH {
- Ok(UnknownProtocol(s.to_string()))
- } else {
- Err(ProtoverError::ExceedsNameLimit)
- }
- }
-}
-
-impl UnknownProtocol {
- /// Create an `UnknownProtocol`, ignoring whether or not it
- /// exceeds MAX_PROTOCOL_NAME_LENGTH.
- fn from_str_any_len(s: &str) -> Result<Self, ProtoverError> {
- if !is_valid_proto(s) {
- return Err(ProtoverError::InvalidProtocol);
- }
- Ok(UnknownProtocol(s.to_string()))
- }
-}
-
-impl From<Protocol> for UnknownProtocol {
- fn from(p: Protocol) -> UnknownProtocol {
- UnknownProtocol(p.to_string())
- }
-}
-
-#[cfg(feature = "test_linking_hack")]
-fn have_linkauth_v1() -> bool {
- true
-}
-
-#[cfg(not(feature = "test_linking_hack"))]
-fn have_linkauth_v1() -> bool {
- use external::c_tor_is_using_nss;
- !c_tor_is_using_nss()
-}
-
-/// Get a CStr representation of current supported protocols, for
-/// passing to C, or for converting to a `&str` for Rust.
-///
-/// # Returns
-///
-/// An `&'static CStr` whose value is the existing protocols supported by tor.
-/// Returned data is in the format as follows:
-///
-/// "HSDir=1-1 LinkAuth=1"
-///
-/// # Note
-///
-/// Rust code can use the `&'static CStr` as a normal `&'a str` by
-/// calling `protover::get_supported_protocols`.
-///
-// C_RUST_COUPLED: protover.c `protover_get_supported_protocols`
-pub(crate) fn get_supported_protocols_cstr() -> &'static CStr {
- if !have_linkauth_v1() {
- cstr!(
- "Cons=1-2 \
- Desc=1-2 \
- DirCache=2 \
- FlowCtrl=1 \
- HSDir=1-2 \
- HSIntro=3-5 \
- HSRend=1-2 \
- Link=1-5 \
- LinkAuth=3 \
- Microdesc=1-2 \
- Padding=2 \
- Relay=1-3"
- )
- } else {
- cstr!(
- "Cons=1-2 \
- Desc=1-2 \
- DirCache=2 \
- FlowCtrl=1 \
- HSDir=1-2 \
- HSIntro=3-5 \
- HSRend=1-2 \
- Link=1-5 \
- LinkAuth=1,3 \
- Microdesc=1-2 \
- Padding=2 \
- Relay=1-3"
- )
- }
-}
-
-/// A map of protocol names to the versions of them which are supported.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct ProtoEntry(HashMap<Protocol, ProtoSet>);
-
-impl Default for ProtoEntry {
- fn default() -> ProtoEntry {
- ProtoEntry(HashMap::new())
- }
-}
-
-impl ProtoEntry {
- /// Get an iterator over the `Protocol`s and their `ProtoSet`s in this `ProtoEntry`.
- pub fn iter(&self) -> hash_map::Iter<Protocol, ProtoSet> {
- self.0.iter()
- }
-
- /// Translate the supported tor versions from a string into a
- /// ProtoEntry, which is useful when looking up a specific
- /// subprotocol.
- pub fn supported() -> Result<Self, ProtoverError> {
- let supported_cstr: &'static CStr = get_supported_protocols_cstr();
- let supported: &str = supported_cstr.to_str().unwrap_or("");
-
- supported.parse()
- }
-
- pub fn len(&self) -> usize {
- self.0.len()
- }
-
- pub fn get(&self, protocol: &Protocol) -> Option<&ProtoSet> {
- self.0.get(protocol)
- }
-
- pub fn insert(&mut self, key: Protocol, value: ProtoSet) {
- self.0.insert(key, value);
- }
-
- pub fn remove(&mut self, key: &Protocol) -> Option<ProtoSet> {
- self.0.remove(key)
- }
-
- pub fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-}
-
-impl FromStr for ProtoEntry {
- type Err = ProtoverError;
-
- /// Parse a string of subprotocol types and their version numbers.
- ///
- /// # Inputs
- ///
- /// * A `protocol_entry` string, comprised of a keywords, an "=" sign, and
- /// one or more version numbers, each separated by a space. For example,
- /// `"Cons=3-4 HSDir=1"`.
- ///
- /// # Returns
- ///
- /// A `Result` whose `Ok` value is a `ProtoEntry`.
- /// Otherwise, the `Err` value of this `Result` is a `ProtoverError`.
- fn from_str(protocol_entry: &str) -> Result<ProtoEntry, ProtoverError> {
- let mut proto_entry: ProtoEntry = ProtoEntry::default();
-
- if protocol_entry.is_empty() {
- return Ok(proto_entry);
- }
-
- let entries = protocol_entry.split(' ');
-
- for entry in entries {
- let mut parts = entry.splitn(2, '=');
-
- let proto = match parts.next() {
- Some(n) => n,
- None => return Err(ProtoverError::Unparseable),
- };
-
- let vers = match parts.next() {
- Some(n) => n,
- None => return Err(ProtoverError::Unparseable),
- };
- let versions: ProtoSet = vers.parse()?;
- let proto_name: Protocol = proto.parse()?;
-
- proto_entry.insert(proto_name, versions);
-
- if proto_entry.len() > MAX_PROTOCOLS_TO_EXPAND {
- return Err(ProtoverError::ExceedsMax);
- }
- }
- Ok(proto_entry)
- }
-}
-
-/// Generate an implementation of `ToString` for either a `ProtoEntry` or an
-/// `UnvalidatedProtoEntry`.
-macro_rules! impl_to_string_for_proto_entry {
- ($t:ty) => {
- impl ToString for $t {
- fn to_string(&self) -> String {
- let mut parts: Vec<String> = Vec::new();
-
- for (protocol, versions) in self.iter() {
- parts.push(format!("{}={}", protocol.to_string(), versions.to_string()));
- }
- parts.sort_unstable();
- parts.join(" ")
- }
- }
- };
-}
-
-impl_to_string_for_proto_entry!(ProtoEntry);
-impl_to_string_for_proto_entry!(UnvalidatedProtoEntry);
-
-/// A `ProtoEntry`, but whose `Protocols` can be any `UnknownProtocol`, not just
-/// the supported ones enumerated in `Protocols`. The protocol versions are
-/// validated, however.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct UnvalidatedProtoEntry(HashMap<UnknownProtocol, ProtoSet>);
-
-impl Default for UnvalidatedProtoEntry {
- fn default() -> UnvalidatedProtoEntry {
- UnvalidatedProtoEntry(HashMap::new())
- }
-}
-
-impl UnvalidatedProtoEntry {
- /// Get an iterator over the `Protocol`s and their `ProtoSet`s in this `ProtoEntry`.
- pub fn iter(&self) -> hash_map::Iter<UnknownProtocol, ProtoSet> {
- self.0.iter()
- }
-
- pub fn get(&self, protocol: &UnknownProtocol) -> Option<&ProtoSet> {
- self.0.get(protocol)
- }
-
- pub fn insert(&mut self, key: UnknownProtocol, value: ProtoSet) {
- self.0.insert(key, value);
- }
-
- pub fn remove(&mut self, key: &UnknownProtocol) -> Option<ProtoSet> {
- self.0.remove(key)
- }
-
- pub fn is_empty(&self) -> bool {
- self.0.is_empty()
- }
-
- pub fn len(&self) -> usize {
- let mut total: usize = 0;
-
- for (_, versions) in self.iter() {
- total += versions.len();
- }
- total
- }
-
- /// Determine if we support every protocol a client supports, and if not,
- /// determine which protocols we do not have support for.
- ///
- /// # Returns
- ///
- /// Optionally, return parameters which the client supports but which we do not.
- ///
- /// # Examples
- /// ```
- /// use protover::UnvalidatedProtoEntry;
- ///
- /// let protocols: UnvalidatedProtoEntry = "LinkAuth=1 Microdesc=1-2 Relay=2".parse().unwrap();
- /// let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- /// assert_eq!(true, unsupported.is_none());
- ///
- /// let protocols: UnvalidatedProtoEntry = "Link=1-2 Wombat=9".parse().unwrap();
- /// let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- /// assert_eq!(true, unsupported.is_some());
- /// assert_eq!("Wombat=9", &unsupported.unwrap().to_string());
- /// ```
- pub fn all_supported(&self) -> Option<UnvalidatedProtoEntry> {
- let mut unsupported: UnvalidatedProtoEntry = UnvalidatedProtoEntry::default();
- let supported: ProtoEntry = match ProtoEntry::supported() {
- Ok(x) => x,
- Err(_) => return None,
- };
-
- for (protocol, versions) in self.iter() {
- let is_supported: Result<Protocol, ProtoverError> = protocol.0.parse();
- let supported_protocol: Protocol;
-
- // If the protocol wasn't even in the enum, then we definitely don't
- // know about it and don't support any of its versions.
- if is_supported.is_err() {
- if !versions.is_empty() {
- unsupported.insert(protocol.clone(), versions.clone());
- }
- continue;
- } else {
- supported_protocol = is_supported.unwrap();
- }
-
- let maybe_supported_versions: Option<&ProtoSet> = supported.get(&supported_protocol);
- let supported_versions: &ProtoSet;
-
- // If the protocol wasn't in the map, then we don't know about it
- // and don't support any of its versions. Add its versions to the
- // map (if it has versions).
- if maybe_supported_versions.is_none() {
- if !versions.is_empty() {
- unsupported.insert(protocol.clone(), versions.clone());
- }
- continue;
- } else {
- supported_versions = maybe_supported_versions.unwrap();
- }
- let unsupported_versions = versions.and_not_in(supported_versions);
-
- if !unsupported_versions.is_empty() {
- unsupported.insert(protocol.clone(), unsupported_versions);
- }
- }
-
- if unsupported.is_empty() {
- return None;
- }
- Some(unsupported)
- }
-
- /// Determine if we have support for some protocol and version.
- ///
- /// # Inputs
- ///
- /// * `proto`, an `UnknownProtocol` to test support for
- /// * `vers`, a `Version` which we will go on to determine whether the
- /// specified protocol supports.
- ///
- /// # Return
- ///
- /// Returns `true` iff this `UnvalidatedProtoEntry` includes support for the
- /// indicated protocol and version, and `false` otherwise.
- ///
- /// # Examples
- ///
- /// ```
- /// # use std::str::FromStr;
- /// use protover::*;
- /// # use protover::errors::ProtoverError;
- ///
- /// # fn do_test () -> Result<UnvalidatedProtoEntry, ProtoverError> {
- /// let proto: UnvalidatedProtoEntry = "Link=3-4 Cons=1 Doggo=3-5".parse()?;
- /// assert_eq!(true, proto.supports_protocol(&Protocol::Cons.into(), &1));
- /// assert_eq!(false, proto.supports_protocol(&Protocol::Cons.into(), &5));
- /// assert_eq!(true, proto.supports_protocol(&UnknownProtocol::from_str("Doggo")?, &4));
- /// # Ok(proto)
- /// # } fn main () { do_test(); }
- /// ```
- pub fn supports_protocol(&self, proto: &UnknownProtocol, vers: &Version) -> bool {
- let supported_versions: &ProtoSet = match self.get(proto) {
- Some(n) => n,
- None => return false,
- };
- supported_versions.contains(&vers)
- }
-
- /// As `UnvalidatedProtoEntry::supports_protocol()`, but also returns `true`
- /// if any later version of the protocol is supported.
- ///
- /// # Examples
- /// ```
- /// use protover::*;
- /// # use protover::errors::ProtoverError;
- ///
- /// # fn do_test () -> Result<UnvalidatedProtoEntry, ProtoverError> {
- /// let proto: UnvalidatedProtoEntry = "Link=3-4 Cons=5".parse()?;
- ///
- /// assert_eq!(true, proto.supports_protocol_or_later(&Protocol::Cons.into(), &5));
- /// assert_eq!(true, proto.supports_protocol_or_later(&Protocol::Cons.into(), &4));
- /// assert_eq!(false, proto.supports_protocol_or_later(&Protocol::Cons.into(), &6));
- /// # Ok(proto)
- /// # } fn main () { do_test(); }
- /// ```
- pub fn supports_protocol_or_later(&self, proto: &UnknownProtocol, vers: &Version) -> bool {
- let supported_versions: &ProtoSet = match self.get(&proto) {
- Some(n) => n,
- None => return false,
- };
- supported_versions.iter().any(|v| v.1 >= *vers)
- }
-
- /// Split a string containing (potentially) several protocols and their
- /// versions into a `Vec` of tuples of string in `(protocol, versions)`
- /// form.
- ///
- /// # Inputs
- ///
- /// A &str in the form `"Link=3-4 Cons=5"`.
- ///
- /// # Returns
- ///
- /// A `Result` whose `Ok` variant is a `Vec<(&str, &str)>` of `(protocol,
- /// versions)`, or whose `Err` variant is a `ProtoverError`.
- ///
- /// # Errors
- ///
- /// This will error with a `ProtoverError::Unparseable` if any of the
- /// following are true:
- ///
- /// * If a protocol name is an empty string, e.g. `"Cons=1,3 =3-5"`.
- /// * If an entry has no equals sign, e.g. `"Cons=1,3 Desc"`.
- /// * If there is leading or trailing whitespace, e.g. `" Cons=1,3 Link=3"`.
- /// * If there is any other extra whitespice, e.g. `"Cons=1,3 Link=3"`.
- fn parse_protocol_and_version_str<'a>(
- protocol_string: &'a str,
- ) -> Result<Vec<(&'a str, &'a str)>, ProtoverError> {
- let mut protovers: Vec<(&str, &str)> = Vec::new();
-
- if protocol_string.is_empty() {
- return Ok(protovers);
- }
-
- for subproto in protocol_string.split(' ') {
- let mut parts = subproto.splitn(2, '=');
-
- let name = match parts.next() {
- Some("") => return Err(ProtoverError::Unparseable),
- Some(n) => n,
- None => return Err(ProtoverError::Unparseable),
- };
- let vers = match parts.next() {
- Some(n) => n,
- None => return Err(ProtoverError::Unparseable),
- };
- protovers.push((name, vers));
- }
- Ok(protovers)
- }
-}
-
-impl FromStr for UnvalidatedProtoEntry {
- type Err = ProtoverError;
-
- /// Parses a protocol list without validating the protocol names.
- ///
- /// # Inputs
- ///
- /// * `protocol_string`, a string comprised of keys and values, both which are
- /// strings. The keys are the protocol names while values are a string
- /// representation of the supported versions.
- ///
- /// The input is _not_ expected to be a subset of the Protocol types
- ///
- /// # Returns
- ///
- /// A `Result` whose `Ok` value is an `UnvalidatedProtoEntry`.
- ///
- /// The returned `Result`'s `Err` value is an `ProtoverError`.
- ///
- /// # Errors
- ///
- /// This function will error if:
- ///
- /// * The protocol string does not follow the "protocol_name=version_list"
- /// expected format, or
- /// * If the version string is malformed. See `impl FromStr for ProtoSet`.
- fn from_str(protocol_string: &str) -> Result<UnvalidatedProtoEntry, ProtoverError> {
- let mut parsed: UnvalidatedProtoEntry = UnvalidatedProtoEntry::default();
- let parts: Vec<(&str, &str)> =
- UnvalidatedProtoEntry::parse_protocol_and_version_str(protocol_string)?;
-
- for &(name, vers) in parts.iter() {
- let versions = ProtoSet::from_str(vers)?;
- let protocol = UnknownProtocol::from_str(name)?;
-
- parsed.insert(protocol, versions);
- }
- Ok(parsed)
- }
-}
-
-impl UnvalidatedProtoEntry {
- /// Create an `UnknownProtocol`, ignoring whether or not it
- /// exceeds MAX_PROTOCOL_NAME_LENGTH.
- pub(crate) fn from_str_any_len(
- protocol_string: &str,
- ) -> Result<UnvalidatedProtoEntry, ProtoverError> {
- let mut parsed: UnvalidatedProtoEntry = UnvalidatedProtoEntry::default();
- let parts: Vec<(&str, &str)> =
- UnvalidatedProtoEntry::parse_protocol_and_version_str(protocol_string)?;
-
- for &(name, vers) in parts.iter() {
- let versions = ProtoSet::from_str(vers)?;
- let protocol = UnknownProtocol::from_str_any_len(name)?;
-
- parsed.insert(protocol, versions);
- }
- Ok(parsed)
- }
-}
-
-/// Pretend a `ProtoEntry` is actually an `UnvalidatedProtoEntry`.
-impl From<ProtoEntry> for UnvalidatedProtoEntry {
- fn from(proto_entry: ProtoEntry) -> UnvalidatedProtoEntry {
- let mut unvalidated: UnvalidatedProtoEntry = UnvalidatedProtoEntry::default();
-
- for (protocol, versions) in proto_entry.iter() {
- unvalidated.insert(UnknownProtocol::from(protocol.clone()), versions.clone());
- }
- unvalidated
- }
-}
-
-/// A mapping of protocols to a count of how many times each of their `Version`s
-/// were voted for or supported.
-///
-/// # Warning
-///
-/// The "protocols" are *not* guaranteed to be known/supported `Protocol`s, in
-/// order to allow new subprotocols to be introduced even if Directory
-/// Authorities don't yet know of them.
-pub struct ProtoverVote(HashMap<UnknownProtocol, HashMap<Version, usize>>);
-
-impl Default for ProtoverVote {
- fn default() -> ProtoverVote {
- ProtoverVote(HashMap::new())
- }
-}
-
-impl IntoIterator for ProtoverVote {
- type Item = (UnknownProtocol, HashMap<Version, usize>);
- type IntoIter = hash_map::IntoIter<UnknownProtocol, HashMap<Version, usize>>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.0.into_iter()
- }
-}
-
-impl ProtoverVote {
- pub fn entry(
- &mut self,
- key: UnknownProtocol,
- ) -> hash_map::Entry<UnknownProtocol, HashMap<Version, usize>> {
- self.0.entry(key)
- }
-
- /// Protocol voting implementation.
- ///
- /// Given a slice of `UnvalidatedProtoEntry`s and a vote `threshold`, return
- /// a new `UnvalidatedProtoEntry` encoding all of the protocols that are
- /// listed by at least `threshold` of the inputs.
- ///
- /// # Examples
- ///
- /// ```
- /// use protover::ProtoverVote;
- /// use protover::UnvalidatedProtoEntry;
- ///
- /// let protos: &[UnvalidatedProtoEntry] = &["Link=3-4".parse().unwrap(),
- /// "Link=3".parse().unwrap()];
- /// let vote = ProtoverVote::compute(protos, &2);
- /// assert_eq!("Link=3", vote.to_string());
- /// ```
- // C_RUST_COUPLED: protover.c protover_compute_vote
- pub fn compute(
- proto_entries: &[UnvalidatedProtoEntry],
- threshold: &usize,
- ) -> UnvalidatedProtoEntry {
- let mut all_count: ProtoverVote = ProtoverVote::default();
- let mut final_output: UnvalidatedProtoEntry = UnvalidatedProtoEntry::default();
-
- if proto_entries.is_empty() {
- return final_output;
- }
-
- // parse and collect all of the protos and their versions and collect them
- for vote in proto_entries {
- // C_RUST_DIFFERS: This doesn't actually differ, bu this check on
- // the total is here to make it match. Because the C version calls
- // expand_protocol_list() which checks if there would be too many
- // subprotocols *or* individual version numbers, i.e. more than
- // MAX_PROTOCOLS_TO_EXPAND, and does this *per vote*, we need to
- // match it's behaviour and ensure we're not allowing more than it
- // would.
- if vote.len() > MAX_PROTOCOLS_TO_EXPAND {
- continue;
- }
-
- for (protocol, versions) in vote.iter() {
- let supported_vers: &mut HashMap<Version, usize> =
- all_count.entry(protocol.clone()).or_insert(HashMap::new());
-
- for version in versions.clone().expand() {
- let counter: &mut usize = supported_vers.entry(version).or_insert(0);
- *counter += 1;
- }
- }
- }
-
- for (protocol, mut versions) in all_count {
- // Go through and remove versions that are less than the threshold
- versions.retain(|_, count| *count as usize >= *threshold);
-
- if versions.len() > 0 {
- let voted_versions: Vec<Version> = versions.keys().cloned().collect();
- let voted_protoset: ProtoSet = ProtoSet::from(voted_versions);
-
- final_output.insert(protocol, voted_protoset);
- }
- }
- final_output
- }
-}
-
-/// Returns a boolean indicating whether the given protocol and version is
-/// supported in any of the existing Tor protocols
-///
-/// # Examples
-/// ```
-/// use protover::is_supported_here;
-/// use protover::Protocol;
-///
-/// let is_supported = is_supported_here(&Protocol::Link, &10);
-/// assert_eq!(false, is_supported);
-///
-/// let is_supported = is_supported_here(&Protocol::Link, &1);
-/// assert_eq!(true, is_supported);
-/// ```
-pub fn is_supported_here(proto: &Protocol, vers: &Version) -> bool {
- let currently_supported: ProtoEntry = match ProtoEntry::supported() {
- Ok(result) => result,
- Err(_) => return false,
- };
- let supported_versions = match currently_supported.get(proto) {
- Some(n) => n,
- None => return false,
- };
- supported_versions.contains(vers)
-}
-
-/// Since older versions of Tor cannot infer their own subprotocols,
-/// determine which subprotocols are supported by older Tor versions.
-///
-/// # Inputs
-///
-/// * `version`, a string comprised of "[0-9a-z.-]"
-///
-/// # Returns
-///
-/// A `&'static CStr` encoding a list of protocol names and supported
-/// versions. The string takes the following format:
-///
-/// "HSDir=1-1 LinkAuth=1"
-///
-/// This function returns the protocols that are supported by the version input,
-/// only for tor versions older than `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS`
-/// (but not older than 0.2.4.19). For newer tors (or older than 0.2.4.19), it
-/// returns an empty string.
-///
-/// # Note
-///
-/// This function is meant to be called for/within FFI code. If you'd
-/// like to use this code in Rust, please see `compute_for_old_tor()`.
-//
-// C_RUST_COUPLED: src/rust/protover.c `compute_for_old_tor`
-pub(crate) fn compute_for_old_tor_cstr(version: &str) -> &'static CStr {
- let empty: &'static CStr = cstr!("");
-
- if c_tor_version_as_new_as(version, FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS) {
- return empty;
- }
- if c_tor_version_as_new_as(version, "0.2.9.1-alpha") {
- return cstr!(
- "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1-2 \
- Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2"
- );
- }
- if c_tor_version_as_new_as(version, "0.2.7.5") {
- return cstr!(
- "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 \
- Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2"
- );
- }
- if c_tor_version_as_new_as(version, "0.2.4.19") {
- return cstr!(
- "Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 \
- Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2"
- );
- }
- empty
-}
-
-/// Since older versions of Tor cannot infer their own subprotocols,
-/// determine which subprotocols are supported by older Tor versions.
-///
-/// # Inputs
-///
-/// * `version`, a string comprised of "[0-9a-z.-]"
-///
-/// # Returns
-///
-/// A `Result` whose `Ok` value is an `&'static str` encoding a list of protocol
-/// names and supported versions. The string takes the following format:
-///
-/// "HSDir=1-1 LinkAuth=1"
-///
-/// This function returns the protocols that are supported by the version input,
-/// only for tor versions older than `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS`.
-/// (but not older than 0.2.4.19). For newer tors (or older than 0.2.4.19), its
-/// `Ok` `Result` contains an empty string.
-///
-/// Otherwise, its `Err` contains a `ProtoverError::Unparseable` if the
-/// `version` string was invalid utf-8.
-///
-/// # Note
-///
-/// This function is meant to be called for/within non-FFI Rust code.
-//
-// C_RUST_COUPLED: src/rust/protover.c `compute_for_old_tor`
-pub fn compute_for_old_tor(version: &str) -> Result<&'static str, ProtoverError> {
- // .to_str() fails with a Utf8Error if it couldn't validate the
- // utf-8, so convert that here into an Unparseable ProtoverError.
- compute_for_old_tor_cstr(version)
- .to_str()
- .or(Err(ProtoverError::Unparseable))
-}
-
-#[cfg(test)]
-mod test {
- use std::str::FromStr;
- use std::string::ToString;
-
- use super::*;
-
- macro_rules! parse_proto {
- ($e:expr) => {{
- let proto: Result<UnknownProtocol, _> = $e.parse();
- let proto2 = UnknownProtocol::from_str_any_len($e);
- assert_eq!(proto, proto2);
- proto
- }};
- }
-
- #[test]
- fn test_protocol_from_str() {
- assert!(parse_proto!("Cons").is_ok());
- assert!(parse_proto!("123").is_ok());
- assert!(parse_proto!("1-2-3").is_ok());
-
- let err = Err(ProtoverError::InvalidProtocol);
- assert_eq!(err, parse_proto!("a_b_c"));
- assert_eq!(err, parse_proto!("a b"));
- assert_eq!(err, parse_proto!("a,"));
- assert_eq!(err, parse_proto!("b."));
- assert_eq!(err, parse_proto!("é"));
- }
-
- macro_rules! assert_protoentry_is_parseable {
- ($e:expr) => {
- let protoentry: Result<ProtoEntry, ProtoverError> = $e.parse();
-
- assert!(protoentry.is_ok(), format!("{:?}", protoentry.err()));
- };
- }
-
- macro_rules! assert_protoentry_is_unparseable {
- ($e:expr) => {
- let protoentry: Result<ProtoEntry, ProtoverError> = $e.parse();
-
- assert!(protoentry.is_err());
- };
- }
-
- #[test]
- fn test_protoentry_from_str_multiple_protocols_multiple_versions() {
- assert_protoentry_is_parseable!("Cons=3-4 Link=1,3-5");
- }
-
- #[test]
- fn test_protoentry_from_str_empty() {
- assert_protoentry_is_parseable!("");
- assert!(UnvalidatedProtoEntry::from_str("").is_ok());
- }
-
- #[test]
- fn test_protoentry_from_str_single_protocol_single_version() {
- assert_protoentry_is_parseable!("HSDir=1");
- }
-
- #[test]
- fn test_protoentry_from_str_unknown_protocol() {
- assert_protoentry_is_unparseable!("Ducks=5-7,8");
- }
-
- #[test]
- fn test_protoentry_from_str_allowed_number_of_versions() {
- assert_protoentry_is_parseable!("Desc=1-63");
- }
-
- #[test]
- fn test_protoentry_from_str_too_many_versions() {
- assert_protoentry_is_unparseable!("Desc=1-64");
- }
-
- #[test]
- fn test_protoentry_all_supported_single_protocol_single_version() {
- let protocol: UnvalidatedProtoEntry = "Cons=1".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
- assert_eq!(true, unsupported.is_none());
- }
-
- #[test]
- fn test_protoentry_all_supported_multiple_protocol_multiple_versions() {
- let protocols: UnvalidatedProtoEntry = "Link=3-4 Desc=2".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_none());
- }
-
- #[test]
- fn test_protoentry_all_supported_three_values() {
- let protocols: UnvalidatedProtoEntry = "LinkAuth=1 Microdesc=1-2 Relay=2".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_none());
- }
-
- #[test]
- fn test_protoentry_all_supported_unknown_protocol() {
- let protocols: UnvalidatedProtoEntry = "Wombat=9".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_some());
- assert_eq!("Wombat=9", &unsupported.unwrap().to_string());
- }
-
- #[test]
- fn test_protoentry_all_supported_unsupported_high_version() {
- let protocols: UnvalidatedProtoEntry = "HSDir=12-60".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_some());
- assert_eq!("HSDir=12-60", &unsupported.unwrap().to_string());
- }
-
- #[test]
- fn test_protoentry_all_supported_unsupported_low_version() {
- let protocols: UnvalidatedProtoEntry = "HSIntro=2-3".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_some());
- assert_eq!("HSIntro=2", &unsupported.unwrap().to_string());
- }
-
- #[test]
- fn test_contract_protocol_list() {
- let mut versions = "";
- assert_eq!(
- String::from(versions),
- ProtoSet::from_str(&versions).unwrap().to_string()
- );
-
- versions = "1";
- assert_eq!(
- String::from(versions),
- ProtoSet::from_str(&versions).unwrap().to_string()
- );
-
- versions = "1-2";
- assert_eq!(
- String::from(versions),
- ProtoSet::from_str(&versions).unwrap().to_string()
- );
-
- versions = "1,3";
- assert_eq!(
- String::from(versions),
- ProtoSet::from_str(&versions).unwrap().to_string()
- );
-
- versions = "1-4";
- assert_eq!(
- String::from(versions),
- ProtoSet::from_str(&versions).unwrap().to_string()
- );
-
- versions = "1,3,5-7";
- assert_eq!(
- String::from(versions),
- ProtoSet::from_str(&versions).unwrap().to_string()
- );
-
- versions = "1-3,50";
- assert_eq!(
- String::from(versions),
- ProtoSet::from_str(&versions).unwrap().to_string()
- );
- }
-}
diff --git a/src/rust/protover/tests/protover.rs b/src/rust/protover/tests/protover.rs
deleted file mode 100644
index a6305ac39a..0000000000
--- a/src/rust/protover/tests/protover.rs
+++ /dev/null
@@ -1,365 +0,0 @@
-// Copyright (c) 2016-2019, The Tor Project, Inc. */
-// See LICENSE for licensing information */
-
-extern crate protover;
-
-use protover::errors::ProtoverError;
-use protover::ProtoEntry;
-use protover::ProtoverVote;
-use protover::UnvalidatedProtoEntry;
-
-#[test]
-fn parse_protocol_with_single_proto_and_single_version() {
- let _: ProtoEntry = "Cons=1".parse().unwrap();
-}
-
-#[test]
-fn parse_protocol_with_single_protocol_and_multiple_versions() {
- let _: ProtoEntry = "Cons=1-2".parse().unwrap();
-}
-
-#[test]
-fn parse_protocol_with_different_single_protocol_and_single_version() {
- let _: ProtoEntry = "HSDir=1".parse().unwrap();
-}
-
-#[test]
-fn parse_protocol_with_single_protocol_and_supported_version() {
- let _: ProtoEntry = "Desc=2".parse().unwrap();
-}
-
-#[test]
-fn parse_protocol_with_two_protocols_and_single_version() {
- let _: ProtoEntry = "Cons=1 HSDir=1".parse().unwrap();
-}
-
-#[test]
-fn parse_protocol_with_single_protocol_and_two_sequential_versions() {
- let _: ProtoEntry = "Desc=1-2".parse().unwrap();
-}
-
-#[test]
-fn parse_protocol_with_single_protocol_and_protocol_range() {
- let _: ProtoEntry = "Link=1-4".parse().unwrap();
-}
-
-#[test]
-fn parse_protocol_with_single_protocol_and_protocol_set() {
- let _: ProtoEntry = "Link=3-4 Desc=2".parse().unwrap();
-}
-
-#[test]
-fn protocol_all_supported_with_single_protocol_and_protocol_set() {
- let protocols: UnvalidatedProtoEntry = "Link=3-4 Desc=2".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_none());
-}
-
-#[test]
-fn protocol_all_supported_with_two_values() {
- let protocols: UnvalidatedProtoEntry = "Microdesc=1-2 Relay=2".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_none());
-}
-
-#[test]
-fn protocol_all_supported_with_one_value() {
- let protocols: UnvalidatedProtoEntry = "Microdesc=1-2".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_none());
-}
-
-#[test]
-fn protocol_all_supported_with_three_values() {
- let protocols: UnvalidatedProtoEntry = "LinkAuth=1 Microdesc=1-2 Relay=2".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_none());
-}
-
-#[test]
-fn protocol_all_supported_with_unsupported_protocol() {
- let protocols: UnvalidatedProtoEntry = "Wombat=9".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_some());
- assert_eq!("Wombat=9", &unsupported.unwrap().to_string());
-}
-
-#[test]
-fn protocol_all_supported_with_unsupported_versions() {
- let protocols: UnvalidatedProtoEntry = "Link=3-63".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_some());
- assert_eq!("Link=6-63", &unsupported.unwrap().to_string());
-}
-
-#[test]
-fn protocol_all_supported_with_unsupported_low_version() {
- let protocols: UnvalidatedProtoEntry = "HSIntro=2-3".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_some());
- assert_eq!("HSIntro=2", &unsupported.unwrap().to_string());
-}
-
-#[test]
-fn protocol_all_supported_with_unsupported_high_version() {
- let protocols: UnvalidatedProtoEntry = "Cons=1-2,60".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_some());
- assert_eq!("Cons=60", &unsupported.unwrap().to_string());
-}
-
-#[test]
-fn protocol_all_supported_with_mix_of_supported_and_unsupproted() {
- let protocols: UnvalidatedProtoEntry = "Link=3-4 Wombat=9".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_some());
- assert_eq!("Wombat=9", &unsupported.unwrap().to_string());
-}
-
-#[test]
-fn protover_string_supports_protocol_returns_true_for_single_supported() {
- let protocols: UnvalidatedProtoEntry = "Link=3-4 Cons=1".parse().unwrap();
- let is_supported = protocols.supports_protocol(&protover::Protocol::Cons.into(), &1);
- assert_eq!(true, is_supported);
-}
-
-#[test]
-fn protover_string_supports_protocol_returns_false_for_single_unsupported() {
- let protocols: UnvalidatedProtoEntry = "Link=3-4 Cons=1".parse().unwrap();
- let is_supported = protocols.supports_protocol(&protover::Protocol::Cons.into(), &2);
- assert_eq!(false, is_supported);
-}
-
-#[test]
-fn protover_string_supports_protocol_returns_false_for_unsupported() {
- let protocols: UnvalidatedProtoEntry = "Link=3-4".parse().unwrap();
- let is_supported = protocols.supports_protocol(&protover::Protocol::Cons.into(), &2);
- assert_eq!(false, is_supported);
-}
-
-#[test]
-#[should_panic]
-fn parse_protocol_with_unexpected_characters() {
- let _: UnvalidatedProtoEntry = "Cons=*-%".parse().unwrap();
-}
-
-#[test]
-fn protover_compute_vote_returns_empty_for_empty_string() {
- let protocols: &[UnvalidatedProtoEntry] = &["".parse().unwrap()];
- let listed = ProtoverVote::compute(protocols, &1);
- assert_eq!("", listed.to_string());
-}
-
-#[test]
-fn protover_compute_vote_returns_single_protocol_for_matching() {
- let protocols: &[UnvalidatedProtoEntry] = &["Cons=1".parse().unwrap()];
- let listed = ProtoverVote::compute(protocols, &1);
- assert_eq!("Cons=1", listed.to_string());
-}
-
-#[test]
-fn protover_compute_vote_returns_two_protocols_for_two_matching() {
- let protocols: &[UnvalidatedProtoEntry] = &["Link=1 Cons=1".parse().unwrap()];
- let listed = ProtoverVote::compute(protocols, &1);
- assert_eq!("Cons=1 Link=1", listed.to_string());
-}
-
-#[test]
-fn protover_compute_vote_returns_one_protocol_when_one_out_of_two_matches() {
- let protocols: &[UnvalidatedProtoEntry] =
- &["Cons=1 Link=2".parse().unwrap(), "Cons=1".parse().unwrap()];
- let listed = ProtoverVote::compute(protocols, &2);
- assert_eq!("Cons=1", listed.to_string());
-}
-
-#[test]
-fn protover_compute_vote_returns_protocols_that_it_doesnt_currently_support() {
- let protocols: &[UnvalidatedProtoEntry] =
- &["Foo=1 Cons=2".parse().unwrap(), "Bar=1".parse().unwrap()];
- let listed = ProtoverVote::compute(protocols, &1);
- assert_eq!("Bar=1 Cons=2 Foo=1", listed.to_string());
-}
-
-#[test]
-fn protover_compute_vote_returns_matching_for_mix() {
- let protocols: &[UnvalidatedProtoEntry] = &["Link=1-10,50 Cons=1,3-7,8".parse().unwrap()];
- let listed = ProtoverVote::compute(protocols, &1);
- assert_eq!("Cons=1,3-8 Link=1-10,50", listed.to_string());
-}
-
-#[test]
-fn protover_compute_vote_returns_matching_for_longer_mix() {
- let protocols: &[UnvalidatedProtoEntry] = &[
- "Desc=1-10,50 Cons=1,3-7,8".parse().unwrap(),
- "Link=12-45,8 Cons=2-6,8 Desc=9".parse().unwrap(),
- ];
-
- let listed = ProtoverVote::compute(protocols, &1);
- assert_eq!("Cons=1-8 Desc=1-10,50 Link=8,12-45", listed.to_string());
-}
-
-#[test]
-fn protover_compute_vote_returns_matching_for_longer_mix_with_threshold_two() {
- let protocols: &[UnvalidatedProtoEntry] = &[
- "Desc=1-10,50 Cons=1,3-7,8".parse().unwrap(),
- "Link=8,12-45 Cons=2-6,8 Desc=9".parse().unwrap(),
- ];
-
- let listed = ProtoverVote::compute(protocols, &2);
- assert_eq!("Cons=3-6,8 Desc=9", listed.to_string());
-}
-
-#[test]
-fn protover_compute_vote_handles_duplicated_versions() {
- let protocols: &[UnvalidatedProtoEntry] =
- &["Cons=1".parse().unwrap(), "Cons=1".parse().unwrap()];
- assert_eq!("Cons=1", ProtoverVote::compute(protocols, &2).to_string());
-
- let protocols: &[UnvalidatedProtoEntry] =
- &["Cons=1-2".parse().unwrap(), "Cons=1-2".parse().unwrap()];
- assert_eq!("Cons=1-2", ProtoverVote::compute(protocols, &2).to_string());
-}
-
-#[test]
-fn protover_compute_vote_handles_invalid_proto_entries() {
- let protocols: &[UnvalidatedProtoEntry] = &[
- "Cons=1".parse().unwrap(),
- "Cons=1".parse().unwrap(),
- "Dinosaur=1".parse().unwrap(),
- ];
- assert_eq!("Cons=1", ProtoverVote::compute(protocols, &2).to_string());
-}
-
-#[test]
-fn parse_protocol_with_single_protocol_and_two_nonsequential_versions() {
- let _: ProtoEntry = "Desc=1,2".parse().unwrap();
-}
-
-#[test]
-fn protover_is_supported_here_returns_true_for_supported_protocol() {
- assert_eq!(
- true,
- protover::is_supported_here(&protover::Protocol::Cons, &1)
- );
-}
-
-#[test]
-fn protover_is_supported_here_returns_false_for_unsupported_protocol() {
- assert_eq!(
- false,
- protover::is_supported_here(&protover::Protocol::Cons, &5)
- );
-}
-
-#[test]
-fn protocol_all_supported_with_single_proto_and_single_version() {
- let protocol: UnvalidatedProtoEntry = "Cons=1".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
- assert_eq!(true, unsupported.is_none());
-}
-
-#[test]
-fn protocol_all_supported_with_single_protocol_and_multiple_versions() {
- let protocol: UnvalidatedProtoEntry = "Cons=1-2".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
- assert_eq!(true, unsupported.is_none());
-}
-
-#[test]
-fn protocol_all_supported_with_different_single_protocol_and_single_version() {
- let protocol: UnvalidatedProtoEntry = "HSDir=1".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
- assert_eq!(true, unsupported.is_none());
-}
-
-#[test]
-fn protocol_all_supported_with_single_protocol_and_supported_version() {
- let protocol: UnvalidatedProtoEntry = "Desc=2".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
- assert_eq!(true, unsupported.is_none());
-}
-
-#[test]
-fn protocol_all_supported_with_two_protocols_and_single_version() {
- let protocols: UnvalidatedProtoEntry = "Cons=1 HSDir=1".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocols.all_supported();
- assert_eq!(true, unsupported.is_none());
-}
-
-#[test]
-fn protocol_all_supported_with_single_protocol_and_two_nonsequential_versions() {
- let protocol: UnvalidatedProtoEntry = "Desc=1,2".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
- assert_eq!(true, unsupported.is_none());
-}
-
-#[test]
-fn protocol_all_supported_with_single_protocol_and_two_sequential_versions() {
- let protocol: UnvalidatedProtoEntry = "Desc=1-2".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
- assert_eq!(true, unsupported.is_none());
-}
-
-#[test]
-fn protocol_all_supported_with_single_protocol_and_protocol_range() {
- let protocol: UnvalidatedProtoEntry = "Link=1-4".parse().unwrap();
- let unsupported: Option<UnvalidatedProtoEntry> = protocol.all_supported();
- assert_eq!(true, unsupported.is_none());
-}
-
-#[test]
-fn protover_all_supported_should_exclude_versions_we_actually_do_support() {
- let proto: UnvalidatedProtoEntry = "Link=3-63".parse().unwrap();
- let result: String = proto.all_supported().unwrap().to_string();
-
- assert_eq!(result, "Link=6-63".to_string());
-}
-
-#[test]
-fn protover_all_supported_should_exclude_versions_we_actually_do_support_complex1() {
- let proto: UnvalidatedProtoEntry = "Link=1-3,30-63".parse().unwrap();
- let result: String = proto.all_supported().unwrap().to_string();
-
- assert_eq!(result, "Link=30-63".to_string());
-}
-
-#[test]
-fn protover_all_supported_should_exclude_versions_we_actually_do_support_complex2() {
- let proto: UnvalidatedProtoEntry = "Link=1-3,5-12".parse().unwrap();
- let result: String = proto.all_supported().unwrap().to_string();
-
- assert_eq!(result, "Link=6-12".to_string());
-}
-
-#[test]
-fn protover_all_supported_should_exclude_some_versions_and_entire_protocols() {
- let proto: UnvalidatedProtoEntry = "Link=1-3,5-12 Quokka=50-51".parse().unwrap();
- let result: String = proto.all_supported().unwrap().to_string();
-
- assert_eq!(result, "Link=6-12 Quokka=50-51".to_string());
-}
-
-#[test]
-// C_RUST_DIFFERS: The C will return true (e.g. saying "yes, that's supported")
-// but set the msg to NULL (??? seems maybe potentially bad). The Rust will
-// simply return a None.
-fn protover_all_supported_should_return_empty_string_for_weird_thing() {
- let proto: UnvalidatedProtoEntry = "Fribble=".parse().unwrap();
- let result: Option<UnvalidatedProtoEntry> = proto.all_supported();
-
- assert!(result.is_none());
-}
-
-#[test]
-fn protover_unvalidatedprotoentry_should_err_entirely_unparseable_things() {
- let proto: Result<UnvalidatedProtoEntry, ProtoverError> = "Fribble".parse();
-
- assert_eq!(Err(ProtoverError::Unparseable), proto);
-}
-
-#[test]
-fn protover_all_supported_over_maximum_limit() {
- let proto: Result<UnvalidatedProtoEntry, ProtoverError> = "Sleen=1-4294967295".parse();
-
- assert_eq!(Err(ProtoverError::ExceedsMax), proto);
-}