|  | @@ -1,20 +1,20 @@
 | 
	
		
			
				|  |  |  // Copyright (c) 2016-2017, The Tor Project, Inc. */
 | 
	
		
			
				|  |  |  // See LICENSE for licensing information */
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -use std::str;
 | 
	
		
			
				|  |  | -use std::str::FromStr;
 | 
	
		
			
				|  |  | +use std::collections::HashMap;
 | 
	
		
			
				|  |  | +use std::collections::hash_map;
 | 
	
		
			
				|  |  |  use std::ffi::CStr;
 | 
	
		
			
				|  |  |  use std::fmt;
 | 
	
		
			
				|  |  | -use std::collections::{HashMap, HashSet};
 | 
	
		
			
				|  |  | -use std::ops::Range;
 | 
	
		
			
				|  |  | +use std::str;
 | 
	
		
			
				|  |  | +use std::str::FromStr;
 | 
	
		
			
				|  |  |  use std::string::String;
 | 
	
		
			
				|  |  | -use std::u32;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  use tor_log::{LogSeverity, LogDomain};
 | 
	
		
			
				|  |  |  use external::c_tor_version_as_new_as;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  use errors::ProtoverError;
 | 
	
		
			
				|  |  |  use protoset::Version;
 | 
	
		
			
				|  |  | +use protoset::ProtoSet;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /// The first version of Tor that included "proto" entries in its descriptors.
 | 
	
		
			
				|  |  |  /// Authorities should use this to decide whether to guess proto lines.
 | 
	
	
		
			
				|  | @@ -130,100 +130,90 @@ pub(crate) fn get_supported_protocols_cstr() -> &'static CStr {
 | 
	
		
			
				|  |  |             Relay=1-2")
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/// Get a string representation of current supported protocols.
 | 
	
		
			
				|  |  | -///
 | 
	
		
			
				|  |  | -/// # Returns
 | 
	
		
			
				|  |  | -///
 | 
	
		
			
				|  |  | -/// An `&'a str` whose value is the existing protocols supported by tor.
 | 
	
		
			
				|  |  | -/// Returned data is in the format as follows:
 | 
	
		
			
				|  |  | -///
 | 
	
		
			
				|  |  | -/// "HSDir=1-1 LinkAuth=1"
 | 
	
		
			
				|  |  | -pub fn get_supported_protocols<'a>() -> &'a str {
 | 
	
		
			
				|  |  | -    let supported_cstr: &'static CStr = get_supported_protocols_cstr();
 | 
	
		
			
				|  |  | -    let supported: &str = match supported_cstr.to_str() {
 | 
	
		
			
				|  |  | -        Ok(x)  => x,
 | 
	
		
			
				|  |  | -        Err(_) => "",
 | 
	
		
			
				|  |  | -    };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    supported
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +/// A map of protocol names to the versions of them which are supported.
 | 
	
		
			
				|  |  | +#[derive(Clone, Debug, PartialEq, Eq)]
 | 
	
		
			
				|  |  | +pub struct ProtoEntry(HashMap<Protocol, ProtoSet>);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -pub struct SupportedProtocols(HashMap<Proto, Versions>);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -impl SupportedProtocols {
 | 
	
		
			
				|  |  | -    pub fn from_proto_entries<I, S>(protocol_strs: I) -> Result<Self, &'static str>
 | 
	
		
			
				|  |  | -    where
 | 
	
		
			
				|  |  | -        I: Iterator<Item = S>,
 | 
	
		
			
				|  |  | -        S: AsRef<str>,
 | 
	
		
			
				|  |  | -    {
 | 
	
		
			
				|  |  | -        let mut parsed = HashMap::new();
 | 
	
		
			
				|  |  | -        for subproto in protocol_strs {
 | 
	
		
			
				|  |  | -            let (name, version) = get_proto_and_vers(subproto.as_ref())?;
 | 
	
		
			
				|  |  | -            parsed.insert(name, version);
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | -        Ok(SupportedProtocols(parsed))
 | 
	
		
			
				|  |  | +impl Default for ProtoEntry {
 | 
	
		
			
				|  |  | +    fn default() -> ProtoEntry {
 | 
	
		
			
				|  |  | +        ProtoEntry( HashMap::new() )
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    /// Translates a string representation of a protocol list to a
 | 
	
		
			
				|  |  | -    /// SupportedProtocols instance.
 | 
	
		
			
				|  |  | -    ///
 | 
	
		
			
				|  |  | -    /// # Examples
 | 
	
		
			
				|  |  | -    ///
 | 
	
		
			
				|  |  | -    /// ```
 | 
	
		
			
				|  |  | -    /// use protover::SupportedProtocols;
 | 
	
		
			
				|  |  | -    ///
 | 
	
		
			
				|  |  | -    /// let supported_protocols = SupportedProtocols::from_proto_entries_string(
 | 
	
		
			
				|  |  | -    ///     "HSDir=1-2 HSIntro=3-4"
 | 
	
		
			
				|  |  | -    /// );
 | 
	
		
			
				|  |  | -    /// ```
 | 
	
		
			
				|  |  | -    pub fn from_proto_entries_string(
 | 
	
		
			
				|  |  | -        proto_entries: &str,
 | 
	
		
			
				|  |  | -    ) -> Result<Self, &'static str> {
 | 
	
		
			
				|  |  | -        Self::from_proto_entries(proto_entries.split(" "))
 | 
	
		
			
				|  |  | +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
 | 
	
		
			
				|  |  | -    /// HashMap, which is useful when looking up a specific
 | 
	
		
			
				|  |  | +    /// ProtoEntry, which is useful when looking up a specific
 | 
	
		
			
				|  |  |      /// subprotocol.
 | 
	
		
			
				|  |  | -    ///
 | 
	
		
			
				|  |  | -    fn tor_supported() -> Result<Self, &'static str> {
 | 
	
		
			
				|  |  | -        Self::from_proto_entries_string(get_supported_protocols())
 | 
	
		
			
				|  |  | +    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 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()
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -/// Parse the subprotocol type and its version numbers.
 | 
	
		
			
				|  |  | -///
 | 
	
		
			
				|  |  | -/// # Inputs
 | 
	
		
			
				|  |  | -///
 | 
	
		
			
				|  |  | -/// * A `protocol_entry` string, comprised of a keyword, an "=" sign, and one
 | 
	
		
			
				|  |  | -/// or more version numbers.
 | 
	
		
			
				|  |  | -///
 | 
	
		
			
				|  |  | -/// # Returns
 | 
	
		
			
				|  |  | -///
 | 
	
		
			
				|  |  | -/// A `Result` whose `Ok` value is a tuple of `(Proto, HashSet<u32>)`, where the
 | 
	
		
			
				|  |  | -/// first element is the subprotocol type (see `protover::Proto`) and the last
 | 
	
		
			
				|  |  | -/// element is a(n unordered) set of unique version numbers which are supported.
 | 
	
		
			
				|  |  | -/// Otherwise, the `Err` value of this `Result` is a description of the error
 | 
	
		
			
				|  |  | -///
 | 
	
		
			
				|  |  | -fn get_proto_and_vers<'a>(
 | 
	
		
			
				|  |  | -    protocol_entry: &'a str,
 | 
	
		
			
				|  |  | -) -> Result<(Proto, Versions), &'static str> {
 | 
	
		
			
				|  |  | -    let mut parts = protocol_entry.splitn(2, "=");
 | 
	
		
			
				|  |  | +impl FromStr for ProtoEntry {
 | 
	
		
			
				|  |  | +    type Err = ProtoverError;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    let proto = match parts.next() {
 | 
	
		
			
				|  |  | -        Some(n) => n,
 | 
	
		
			
				|  |  | -        None => return Err("invalid protover entry"),
 | 
	
		
			
				|  |  | -    };
 | 
	
		
			
				|  |  | +    /// 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`, where the
 | 
	
		
			
				|  |  | +    /// first element is the subprotocol type (see `protover::Protocol`) and the last
 | 
	
		
			
				|  |  | +    /// element is an ordered set of `(low, high)` unique version numbers which are supported.
 | 
	
		
			
				|  |  | +    /// 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();
 | 
	
		
			
				|  |  | +        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("invalid protover entry"),
 | 
	
		
			
				|  |  | -    };
 | 
	
		
			
				|  |  | +            let vers = match parts.next() {
 | 
	
		
			
				|  |  | +                Some(n) => n,
 | 
	
		
			
				|  |  | +                None => return Err(ProtoverError::Unparseable),
 | 
	
		
			
				|  |  | +            };
 | 
	
		
			
				|  |  | +            let versions: ProtoSet = vers.parse()?;
 | 
	
		
			
				|  |  | +            let proto_name: Protocol = proto.parse()?;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    let versions = Versions::from_version_string(vers)?;
 | 
	
		
			
				|  |  | -    let proto_name = proto.parse()?;
 | 
	
		
			
				|  |  | +            proto_entry.insert(proto_name, versions);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    Ok((proto_name, versions))
 | 
	
		
			
				|  |  | +        Ok(proto_entry)
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /// Parses a single subprotocol entry string into subprotocol and version
 |