protover.rs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. // Copyright (c) 2016-2017, The Tor Project, Inc. */
  2. // See LICENSE for licensing information */
  3. use external::c_tor_version_as_new_as;
  4. use std::str::FromStr;
  5. use std::fmt;
  6. use std::collections::{HashMap, HashSet};
  7. use std::ops::Range;
  8. use std::string::String;
  9. /// The first version of Tor that included "proto" entries in its descriptors.
  10. /// Authorities should use this to decide whether to guess proto lines.
  11. ///
  12. /// C_RUST_COUPLED:
  13. /// src/or/protover.h `FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS`
  14. const FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS: &'static str = "0.2.9.3-alpha";
  15. /// The maximum number of subprotocol version numbers we will attempt to expand
  16. /// before concluding that someone is trying to DoS us
  17. ///
  18. /// C_RUST_COUPLED: src/or/protover.c `MAX_PROTOCOLS_TO_EXPAND`
  19. const MAX_PROTOCOLS_TO_EXPAND: u32 = 500;
  20. /// Currently supported protocols and their versions
  21. ///
  22. /// C_RUST_COUPLED: src/or/protover.c `protover_get_supported_protocols`
  23. const SUPPORTED_PROTOCOLS: &'static [&'static str] = &[
  24. "Cons=1-2",
  25. "Desc=1-2",
  26. "DirCache=1-2",
  27. "HSDir=1-2",
  28. "HSIntro=3-4",
  29. "HSRend=1-2",
  30. "Link=1-4",
  31. "LinkAuth=1,3",
  32. "Microdesc=1-2",
  33. "Relay=1-2",
  34. ];
  35. /// Known subprotocols in Tor. Indicates which subprotocol a relay supports.
  36. ///
  37. /// C_RUST_COUPLED: src/or/protover.h `protocol_type_t`
  38. #[derive(Hash, Eq, PartialEq, Debug)]
  39. pub enum Proto {
  40. Cons,
  41. Desc,
  42. DirCache,
  43. HSDir,
  44. HSIntro,
  45. HSRend,
  46. Link,
  47. LinkAuth,
  48. Microdesc,
  49. Relay,
  50. }
  51. impl fmt::Display for Proto {
  52. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  53. write!(f, "{:?}", self)
  54. }
  55. }
  56. /// Translates a string representation of a protocol into a Proto type.
  57. /// Error if the string is an unrecognized protocol name.
  58. ///
  59. /// C_RUST_COUPLED: src/or/protover.c `PROTOCOL_NAMES`
  60. impl FromStr for Proto {
  61. type Err = &'static str;
  62. fn from_str(s: &str) -> Result<Self, Self::Err> {
  63. match s {
  64. "Cons" => Ok(Proto::Cons),
  65. "Desc" => Ok(Proto::Desc),
  66. "DirCache" => Ok(Proto::DirCache),
  67. "HSDir" => Ok(Proto::HSDir),
  68. "HSIntro" => Ok(Proto::HSIntro),
  69. "HSRend" => Ok(Proto::HSRend),
  70. "Link" => Ok(Proto::Link),
  71. "LinkAuth" => Ok(Proto::LinkAuth),
  72. "Microdesc" => Ok(Proto::Microdesc),
  73. "Relay" => Ok(Proto::Relay),
  74. _ => Err("Not a valid protocol type"),
  75. }
  76. }
  77. }
  78. /// Get the string representation of current supported protocols
  79. ///
  80. /// # Returns
  81. ///
  82. /// A `String` whose value is the existing protocols supported by tor.
  83. /// Returned data is in the format as follows:
  84. ///
  85. /// "HSDir=1-1 LinkAuth=1"
  86. ///
  87. pub fn get_supported_protocols() -> String {
  88. SUPPORTED_PROTOCOLS.join(" ")
  89. }
  90. pub struct SupportedProtocols(HashMap<Proto, Versions>);
  91. impl SupportedProtocols {
  92. /// # Examples
  93. ///
  94. /// ```
  95. /// use protover::SupportedProtocols;
  96. ///
  97. /// let supported_protocols = SupportedProtocols::from_proto_entries_string(
  98. /// "HSDir=1-2 HSIntro=3-4"
  99. /// );
  100. /// ```
  101. pub fn from_proto_entries_string(
  102. proto_entries: &str,
  103. ) -> Result<Self, &'static str> {
  104. Self::from_proto_entries(proto_entries.split(" "))
  105. }
  106. /// ```
  107. /// use protover::SupportedProtocols;
  108. ///
  109. /// let supported_protocols = SupportedProtocols::from_proto_entries([
  110. /// "HSDir=1-2",
  111. /// "HSIntro=3-4",
  112. /// ].iter());
  113. /// ```
  114. pub fn from_proto_entries<I, S>(protocol_strs: I) -> Result<Self, &'static str>
  115. where
  116. I: Iterator<Item = S>,
  117. S: AsRef<str>,
  118. {
  119. let mut parsed = HashMap::new();
  120. for subproto in protocol_strs {
  121. let (name, version) = get_proto_and_vers(subproto.as_ref())?;
  122. parsed.insert(name, version);
  123. }
  124. Ok(SupportedProtocols(parsed))
  125. }
  126. /// Translates supported tor versions from a string into a HashMap, which
  127. /// is useful when looking up a specific subprotocol.
  128. ///
  129. /// # Returns
  130. ///
  131. /// A `Result` whose `Ok` value is a `HashMap<Proto, <Version>>` holding all
  132. /// subprotocols and versions currently supported by tor.
  133. ///
  134. /// The returned `Result`'s `Err` value is an `&'static str` with a
  135. /// description of the error.
  136. ///
  137. fn tor_supported() -> Result<Self, &'static str> {
  138. Self::from_proto_entries(SUPPORTED_PROTOCOLS.iter().map(|n| *n))
  139. }
  140. }
  141. type Version = u32;
  142. /// Set of versions for a protocol.
  143. #[derive(Debug, PartialEq, Eq)]
  144. pub struct Versions(HashSet<Version>);
  145. impl Versions {
  146. /// Get the unique version numbers supported by a subprotocol.
  147. ///
  148. /// # Inputs
  149. ///
  150. /// * `version_string`, a string comprised of "[0-9,-]"
  151. ///
  152. /// # Returns
  153. ///
  154. /// A `Result` whose `Ok` value is a `HashSet<u32>` holding all of the unique
  155. /// version numbers. If there were ranges in the `version_string`, then these
  156. /// are expanded, i.e. `"1-3"` would expand to `HashSet<u32>::new([1, 2, 3])`.
  157. /// The returned HashSet is *unordered*.
  158. ///
  159. /// The returned `Result`'s `Err` value is an `&'static str` with a description
  160. /// of the error.
  161. ///
  162. /// # Errors
  163. ///
  164. /// This function will error if:
  165. ///
  166. /// * the `version_string` is empty or contains an equals (`"="`) sign,
  167. /// * the expansion of a version range produces an error (see
  168. /// `expand_version_range`),
  169. /// * any single version number is not parseable as an `u32` in radix 10, or
  170. /// * there are greater than 2^16 version numbers to expand.
  171. ///
  172. fn from_version_string(
  173. version_string: &str,
  174. ) -> Result<Self, &'static str> {
  175. if version_string.is_empty() {
  176. return Err("version string is empty");
  177. }
  178. let mut versions = HashSet::<Version>::new();
  179. for piece in version_string.split(",") {
  180. if piece.contains("-") {
  181. for p in expand_version_range(piece)? {
  182. versions.insert(p);
  183. }
  184. } else {
  185. versions.insert(u32::from_str(piece).or(
  186. Err("invalid protocol entry"),
  187. )?);
  188. }
  189. if versions.len() > MAX_PROTOCOLS_TO_EXPAND as usize {
  190. return Err("Too many versions to expand");
  191. }
  192. }
  193. Ok(Versions(versions))
  194. }
  195. }
  196. /// Parse the subprotocol type and its version numbers.
  197. ///
  198. /// # Inputs
  199. ///
  200. /// * A `protocol_entry` string, comprised of a keyword, an "=" sign, and one
  201. /// or more version numbers.
  202. ///
  203. /// # Returns
  204. ///
  205. /// A `Result` whose `Ok` value is a tuple of `(Proto, HashSet<u32>)`, where the
  206. /// first element is the subprotocol type (see `protover::Proto`) and the last
  207. /// element is a(n unordered) set of unique version numbers which are supported.
  208. /// Otherwise, the `Err` value of this `Result` is a description of the error
  209. ///
  210. fn get_proto_and_vers<'a>(
  211. protocol_entry: &'a str,
  212. ) -> Result<(Proto, Versions), &'static str> {
  213. let mut parts = protocol_entry.splitn(2, "=");
  214. let proto = match parts.next() {
  215. Some(n) => n,
  216. None => return Err("invalid protover entry"),
  217. };
  218. let vers = match parts.next() {
  219. Some(n) => n,
  220. None => return Err("invalid protover entry"),
  221. };
  222. let versions = Versions::from_version_string(vers)?;
  223. let proto_name = proto.parse()?;
  224. Ok((proto_name, versions))
  225. }
  226. /// Parses a single subprotocol entry string into subprotocol and version
  227. /// parts, and then checks whether any of those versions are unsupported.
  228. /// Helper for protover::all_supported
  229. ///
  230. /// # Inputs
  231. ///
  232. /// Accepted data is in the string format as follows:
  233. ///
  234. /// "HSDir=1-1"
  235. ///
  236. /// # Returns
  237. ///
  238. /// Returns `true` if the protocol entry is well-formatted and only contains
  239. /// versions that are also supported in tor. Otherwise, returns false
  240. ///
  241. fn contains_only_supported_protocols(proto_entry: &str) -> bool {
  242. let (name, mut vers) = match get_proto_and_vers(proto_entry) {
  243. Ok(n) => n,
  244. Err(_) => return false,
  245. };
  246. let currently_supported = match SupportedProtocols::tor_supported() {
  247. Ok(n) => n.0,
  248. Err(_) => return false,
  249. };
  250. let supported_versions = match currently_supported.get(&name) {
  251. Some(n) => n,
  252. None => return false,
  253. };
  254. vers.0.retain(|x| !supported_versions.0.contains(x));
  255. vers.0.is_empty()
  256. }
  257. /// Determine if we support every protocol a client supports, and if not,
  258. /// determine which protocols we do not have support for.
  259. ///
  260. /// # Inputs
  261. ///
  262. /// Accepted data is in the string format as follows:
  263. ///
  264. /// "HSDir=1-1 LinkAuth=1-2"
  265. ///
  266. /// # Returns
  267. ///
  268. /// Return `true` if every protocol version is one that we support.
  269. /// Otherwise, return `false`.
  270. /// Optionally, return parameters which the client supports but which we do not
  271. ///
  272. /// # Examples
  273. /// ```
  274. /// use protover::all_supported;
  275. ///
  276. /// let (is_supported, unsupported) = all_supported("Link=1");
  277. /// assert_eq!(true, is_supported);
  278. ///
  279. /// let (is_supported, unsupported) = all_supported("Link=5-6");
  280. /// assert_eq!(false, is_supported);
  281. /// assert_eq!("Link=5-6", unsupported);
  282. ///
  283. pub fn all_supported(protocols: &str) -> (bool, String) {
  284. let unsupported = protocols
  285. .split_whitespace()
  286. .filter(|v| !contains_only_supported_protocols(v))
  287. .collect::<Vec<&str>>();
  288. (unsupported.is_empty(), unsupported.join(" "))
  289. }
  290. /// Return true iff the provided protocol list includes support for the
  291. /// indicated protocol and version.
  292. /// Otherwise, return false
  293. ///
  294. /// # Inputs
  295. ///
  296. /// * `list`, a string representation of a list of protocol entries.
  297. /// * `proto`, a `Proto` to test support for
  298. /// * `vers`, a `Version` version which we will go on to determine whether the
  299. /// specified protocol supports.
  300. ///
  301. /// # Examples
  302. /// ```
  303. /// use protover::*;
  304. ///
  305. /// let is_supported = protover_string_supports_protocol("Link=3-4 Cons=1",
  306. /// Proto::Cons,1);
  307. /// assert_eq!(true, is_supported);
  308. ///
  309. /// let is_not_supported = protover_string_supports_protocol("Link=3-4 Cons=1",
  310. /// Proto::Cons,5);
  311. /// assert_eq!(false, is_not_supported)
  312. /// ```
  313. pub fn protover_string_supports_protocol(
  314. list: &str,
  315. proto: Proto,
  316. vers: Version,
  317. ) -> bool {
  318. let supported = match SupportedProtocols::from_proto_entries_string(list) {
  319. Ok(result) => result.0,
  320. Err(_) => return false,
  321. };
  322. let supported_versions = match supported.get(&proto) {
  323. Some(n) => n,
  324. None => return false,
  325. };
  326. supported_versions.0.contains(&vers)
  327. }
  328. /// As protover_string_supports_protocol(), but also returns True if
  329. /// any later version of the protocol is supported.
  330. ///
  331. /// # Examples
  332. /// ```
  333. /// use protover::*;
  334. ///
  335. /// let is_supported = protover_string_supports_protocol_or_later(
  336. /// "Link=3-4 Cons=5", Proto::Cons, 5);
  337. ///
  338. /// assert_eq!(true, is_supported);
  339. ///
  340. /// let is_supported = protover_string_supports_protocol_or_later(
  341. /// "Link=3-4 Cons=5", Proto::Cons, 4);
  342. ///
  343. /// assert_eq!(true, is_supported);
  344. ///
  345. /// let is_supported = protover_string_supports_protocol_or_later(
  346. /// "Link=3-4 Cons=5", Proto::Cons, 6);
  347. ///
  348. /// assert_eq!(false, is_supported);
  349. /// ```
  350. pub fn protover_string_supports_protocol_or_later(
  351. list: &str,
  352. proto: Proto,
  353. vers: u32,
  354. ) -> bool {
  355. let supported = match SupportedProtocols::from_proto_entries_string(list) {
  356. Ok(result) => result.0,
  357. Err(_) => return false,
  358. };
  359. let supported_versions = match supported.get(&proto) {
  360. Some(n) => n,
  361. None => return false,
  362. };
  363. supported_versions.0.iter().any(|v| v >= &vers)
  364. }
  365. /// Fully expand a version range. For example, 1-3 expands to 1,2,3
  366. /// Helper for Versions::from_version_string
  367. ///
  368. /// # Inputs
  369. ///
  370. /// `range`, a string comprised of "[0-9,-]"
  371. ///
  372. /// # Returns
  373. ///
  374. /// A `Result` whose `Ok` value a vector of unsigned integers representing the
  375. /// expanded range of supported versions by a single protocol.
  376. /// Otherwise, the `Err` value of this `Result` is a description of the error
  377. ///
  378. /// # Errors
  379. ///
  380. /// This function will error if:
  381. ///
  382. /// * the specified range is empty
  383. /// * the version range does not contain both a valid lower and upper bound.
  384. ///
  385. fn expand_version_range(range: &str) -> Result<Range<u32>, &'static str> {
  386. if range.is_empty() {
  387. return Err("version string empty");
  388. }
  389. let mut parts = range.split("-");
  390. let lower_string = parts.next().ok_or(
  391. "cannot parse protocol range lower bound",
  392. )?;
  393. let lower = u32::from_str_radix(lower_string, 10).or(Err(
  394. "cannot parse protocol range lower bound",
  395. ))?;
  396. let higher_string = parts.next().ok_or(
  397. "cannot parse protocol range upper bound",
  398. )?;
  399. let higher = u32::from_str_radix(higher_string, 10).or(Err(
  400. "cannot parse protocol range upper bound",
  401. ))?;
  402. // We can use inclusive range syntax when it becomes stable.
  403. Ok(lower..higher + 1)
  404. }
  405. /// Checks to see if there is a continuous range of integers, starting at the
  406. /// first in the list. Returns the last integer in the range if a range exists.
  407. /// Helper for compute_vote
  408. ///
  409. /// # Inputs
  410. ///
  411. /// `list`, an ordered vector of `u32` integers of "[0-9,-]" representing the
  412. /// supported versions for a single protocol.
  413. ///
  414. /// # Returns
  415. ///
  416. /// A `bool` indicating whether the list contains a range, starting at the
  417. /// first in the list, and an `u32` of the last integer in the range.
  418. ///
  419. /// For example, if given vec![1, 2, 3, 5], find_range will return true,
  420. /// as there is a continuous range, and 3, which is the last number in the
  421. /// continuous range.
  422. ///
  423. fn find_range(list: &Vec<u32>) -> (bool, u32) {
  424. if list.len() == 0 {
  425. return (false, 0);
  426. }
  427. let mut iterable = list.iter().peekable();
  428. let mut range_end = match iterable.next() {
  429. Some(n) => *n,
  430. None => return (false, 0),
  431. };
  432. let mut has_range = false;
  433. while iterable.peek().is_some() {
  434. let n = *iterable.next().unwrap();
  435. if n != range_end + 1 {
  436. break;
  437. }
  438. has_range = true;
  439. range_end = n;
  440. }
  441. (has_range, range_end)
  442. }
  443. /// Contracts a HashSet representation of supported versions into a string.
  444. /// Helper for compute_vote
  445. ///
  446. /// # Inputs
  447. ///
  448. /// `supported_set`, a set of integers of "[0-9,-]" representing the
  449. /// supported versions for a single protocol.
  450. ///
  451. /// # Returns
  452. ///
  453. /// A `String` representation of this set in ascending order.
  454. ///
  455. fn contract_protocol_list<'a>(supported_set: &'a HashSet<Version>) -> String {
  456. let mut supported: Vec<Version> =
  457. supported_set.iter().map(|x| *x).collect();
  458. supported.sort();
  459. let mut final_output: Vec<String> = Vec::new();
  460. while supported.len() != 0 {
  461. let (has_range, end) = find_range(&supported);
  462. let current = supported.remove(0);
  463. if has_range {
  464. final_output.push(format!(
  465. "{}-{}",
  466. current.to_string(),
  467. &end.to_string(),
  468. ));
  469. supported.retain(|&x| x > end);
  470. } else {
  471. final_output.push(current.to_string());
  472. }
  473. }
  474. final_output.join(",")
  475. }
  476. /// Parses a protocol list without validating the protocol names
  477. ///
  478. /// # Inputs
  479. ///
  480. /// * `protocol_string`, a string comprised of keys and values, both which are
  481. /// strings. The keys are the protocol names while values are a string
  482. /// representation of the supported versions.
  483. ///
  484. /// The input is _not_ expected to be a subset of the Proto types
  485. ///
  486. /// # Returns
  487. ///
  488. /// A `Result` whose `Ok` value is a `HashSet<Version>` holding all of the
  489. /// unique version numbers.
  490. ///
  491. /// The returned `Result`'s `Err` value is an `&'static str` with a description
  492. /// of the error.
  493. ///
  494. /// # Errors
  495. ///
  496. /// This function will error if:
  497. ///
  498. /// * The protocol string does not follow the "protocol_name=version_list"
  499. /// expected format
  500. /// * If the version string is malformed. See `Versions::from_version_string`.
  501. ///
  502. fn parse_protocols_from_string_with_no_validation<'a>(
  503. protocol_string: &'a str,
  504. ) -> Result<HashMap<String, Versions>, &'static str> {
  505. let mut parsed: HashMap<String, Versions> = HashMap::new();
  506. for subproto in protocol_string.split(" ") {
  507. let mut parts = subproto.splitn(2, "=");
  508. let name = match parts.next() {
  509. Some(n) => n,
  510. None => return Err("invalid protover entry"),
  511. };
  512. let vers = match parts.next() {
  513. Some(n) => n,
  514. None => return Err("invalid protover entry"),
  515. };
  516. let versions = Versions::from_version_string(vers)?;
  517. parsed.insert(String::from(name), versions);
  518. }
  519. Ok(parsed)
  520. }
  521. /// Protocol voting implementation.
  522. ///
  523. /// Given a list of strings describing protocol versions, return a new
  524. /// string encoding all of the protocols that are listed by at
  525. /// least threshold of the inputs.
  526. ///
  527. /// The string is sorted according to the following conventions:
  528. /// - Protocols names are alphabetized
  529. /// - Protocols are in order low to high
  530. /// - Individual and ranges are listed together. For example,
  531. /// "3, 5-10,13"
  532. /// - All entries are unique
  533. ///
  534. /// # Examples
  535. /// ```
  536. /// use protover::compute_vote;
  537. ///
  538. /// let protos = vec![String::from("Link=3-4"), String::from("Link=3")];
  539. /// let vote = compute_vote(protos, 2);
  540. /// assert_eq!("Link=3", vote)
  541. /// ```
  542. pub fn compute_vote(
  543. list_of_proto_strings: Vec<String>,
  544. threshold: i32,
  545. ) -> String {
  546. let empty = String::from("");
  547. if list_of_proto_strings.is_empty() {
  548. return empty;
  549. }
  550. // all_count is a structure to represent the count of the number of
  551. // supported versions for a specific protocol. For example, in JSON format:
  552. // {
  553. // "FirstSupportedProtocol": {
  554. // "1": "3",
  555. // "2": "1"
  556. // }
  557. // }
  558. // means that FirstSupportedProtocol has three votes which support version
  559. // 1, and one vote that supports version 2
  560. let mut all_count: HashMap<String, HashMap<Version, usize>> =
  561. HashMap::new();
  562. // parse and collect all of the protos and their versions and collect them
  563. for vote in list_of_proto_strings {
  564. let this_vote: HashMap<String, Versions> =
  565. match parse_protocols_from_string_with_no_validation(&vote) {
  566. Ok(result) => result,
  567. Err(_) => continue,
  568. };
  569. for (protocol, versions) in this_vote {
  570. let supported_vers: &mut HashMap<Version, usize> =
  571. all_count.entry(protocol).or_insert(HashMap::new());
  572. for version in versions.0 {
  573. let counter: &mut usize =
  574. supported_vers.entry(version).or_insert(0);
  575. *counter += 1;
  576. }
  577. }
  578. }
  579. let mut final_output: HashMap<String, String> =
  580. HashMap::with_capacity(SUPPORTED_PROTOCOLS.len());
  581. // Go through and remove verstions that are less than the threshold
  582. for (protocol, versions) in all_count {
  583. let mut meets_threshold = HashSet::new();
  584. for (version, count) in versions {
  585. if count >= threshold as usize {
  586. meets_threshold.insert(version);
  587. }
  588. }
  589. // For each protocol, compress its version list into the expected
  590. // protocol version string format
  591. let contracted = contract_protocol_list(&meets_threshold);
  592. if !contracted.is_empty() {
  593. final_output.insert(protocol, contracted);
  594. }
  595. }
  596. write_vote_to_string(&final_output)
  597. }
  598. /// Return a String comprised of protocol entries in alphabetical order
  599. ///
  600. /// # Inputs
  601. ///
  602. /// * `vote`, a `HashMap` comprised of keys and values, both which are strings.
  603. /// The keys are the protocol names while values are a string representation of
  604. /// the supported versions.
  605. ///
  606. /// # Returns
  607. ///
  608. /// A `String` whose value is series of pairs, comprising of the protocol name
  609. /// and versions that it supports. The string takes the following format:
  610. ///
  611. /// "first_protocol_name=1,2-5, second_protocol_name=4,5"
  612. ///
  613. /// Sorts the keys in alphabetical order and creates the expected subprotocol
  614. /// entry format.
  615. ///
  616. fn write_vote_to_string(vote: &HashMap<String, String>) -> String {
  617. let mut keys: Vec<&String> = vote.keys().collect();
  618. keys.sort();
  619. let mut output = Vec::new();
  620. for key in keys {
  621. // TODO error in indexing here?
  622. output.push(format!("{}={}", key, vote[key]));
  623. }
  624. output.join(" ")
  625. }
  626. /// Returns a boolean indicating whether the given protocol and version is
  627. /// supported in any of the existing Tor protocols
  628. ///
  629. /// # Examples
  630. /// ```
  631. /// use protover::*;
  632. ///
  633. /// let is_supported = is_supported_here(Proto::Link, 5);
  634. /// assert_eq!(false, is_supported);
  635. ///
  636. /// let is_supported = is_supported_here(Proto::Link, 1);
  637. /// assert_eq!(true, is_supported);
  638. /// ```
  639. pub fn is_supported_here(proto: Proto, vers: Version) -> bool {
  640. let currently_supported = match SupportedProtocols::tor_supported() {
  641. Ok(result) => result.0,
  642. Err(_) => return false,
  643. };
  644. let supported_versions = match currently_supported.get(&proto) {
  645. Some(n) => n,
  646. None => return false,
  647. };
  648. supported_versions.0.contains(&vers)
  649. }
  650. /// Older versions of Tor cannot infer their own subprotocols
  651. /// Used to determine which subprotocols are supported by older Tor versions.
  652. ///
  653. /// # Inputs
  654. ///
  655. /// * `version`, a string comprised of "[0-9,-]"
  656. ///
  657. /// # Returns
  658. ///
  659. /// A `String` whose value is series of pairs, comprising of the protocol name
  660. /// and versions that it supports. The string takes the following format:
  661. ///
  662. /// "HSDir=1-1 LinkAuth=1"
  663. ///
  664. /// This function returns the protocols that are supported by the version input,
  665. /// only for tor versions older than FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS.
  666. ///
  667. /// C_RUST_COUPLED: src/rust/protover.c `compute_for_old_tor`
  668. pub fn compute_for_old_tor(version: &str) -> String {
  669. if c_tor_version_as_new_as(
  670. version,
  671. FIRST_TOR_VERSION_TO_ADVERTISE_PROTOCOLS,
  672. )
  673. {
  674. return String::new();
  675. }
  676. if c_tor_version_as_new_as(version, "0.2.9.1-alpha") {
  677. let ret = "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1-2 \
  678. Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2";
  679. return String::from(ret);
  680. }
  681. if c_tor_version_as_new_as(version, "0.2.7.5") {
  682. let ret = "Cons=1-2 Desc=1-2 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 \
  683. Link=1-4 LinkAuth=1 Microdesc=1-2 Relay=1-2";
  684. return String::from(ret);
  685. }
  686. if c_tor_version_as_new_as(version, "0.2.4.19") {
  687. let ret = "Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 \
  688. Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2";
  689. return String::from(ret);
  690. }
  691. String::new()
  692. }
  693. #[cfg(test)]
  694. mod test {
  695. use super::Version;
  696. #[test]
  697. fn test_versions_from_version_string() {
  698. use std::collections::HashSet;
  699. use super::Versions;
  700. assert_eq!(Err("version string is empty"), Versions::from_version_string(""));
  701. assert_eq!(Err("invalid protocol entry"), Versions::from_version_string("a,b"));
  702. assert_eq!(Err("invalid protocol entry"), Versions::from_version_string("1,!"));
  703. {
  704. let mut versions: HashSet<Version> = HashSet::new();
  705. versions.insert(1);
  706. assert_eq!(versions, Versions::from_version_string("1").unwrap().0);
  707. }
  708. {
  709. let mut versions: HashSet<Version> = HashSet::new();
  710. versions.insert(1);
  711. versions.insert(2);
  712. assert_eq!(versions, Versions::from_version_string("1,2").unwrap().0);
  713. }
  714. {
  715. let mut versions: HashSet<Version> = HashSet::new();
  716. versions.insert(1);
  717. versions.insert(2);
  718. versions.insert(3);
  719. assert_eq!(versions, Versions::from_version_string("1-3").unwrap().0);
  720. }
  721. {
  722. let mut versions: HashSet<Version> = HashSet::new();
  723. versions.insert(1);
  724. versions.insert(2);
  725. versions.insert(5);
  726. assert_eq!(versions, Versions::from_version_string("1-2,5").unwrap().0);
  727. }
  728. {
  729. let mut versions: HashSet<Version> = HashSet::new();
  730. versions.insert(1);
  731. versions.insert(3);
  732. versions.insert(4);
  733. versions.insert(5);
  734. assert_eq!(versions, Versions::from_version_string("1,3-5").unwrap().0);
  735. }
  736. }
  737. #[test]
  738. fn test_contains_only_supported_protocols() {
  739. use super::contains_only_supported_protocols;
  740. assert_eq!(false, contains_only_supported_protocols(""));
  741. assert_eq!(false, contains_only_supported_protocols("Cons="));
  742. assert_eq!(true, contains_only_supported_protocols("Cons=1"));
  743. assert_eq!(false, contains_only_supported_protocols("Cons=0"));
  744. assert_eq!(false, contains_only_supported_protocols("Cons=0-1"));
  745. assert_eq!(false, contains_only_supported_protocols("Cons=5"));
  746. assert_eq!(false, contains_only_supported_protocols("Cons=1-5"));
  747. assert_eq!(false, contains_only_supported_protocols("Cons=1,5"));
  748. assert_eq!(false, contains_only_supported_protocols("Cons=5,6"));
  749. assert_eq!(false, contains_only_supported_protocols("Cons=1,5,6"));
  750. assert_eq!(true, contains_only_supported_protocols("Cons=1,2"));
  751. assert_eq!(true, contains_only_supported_protocols("Cons=1-2"));
  752. }
  753. #[test]
  754. fn test_find_range() {
  755. use super::find_range;
  756. assert_eq!((false, 0), find_range(&vec![]));
  757. assert_eq!((false, 1), find_range(&vec![1]));
  758. assert_eq!((true, 2), find_range(&vec![1, 2]));
  759. assert_eq!((true, 3), find_range(&vec![1, 2, 3]));
  760. assert_eq!((true, 3), find_range(&vec![1, 2, 3, 5]));
  761. }
  762. #[test]
  763. fn test_expand_version_range() {
  764. use super::expand_version_range;
  765. assert_eq!(Err("version string empty"), expand_version_range(""));
  766. assert_eq!(Ok(1..3), expand_version_range("1-2"));
  767. assert_eq!(Ok(1..5), expand_version_range("1-4"));
  768. assert_eq!(
  769. Err("cannot parse protocol range lower bound"),
  770. expand_version_range("a")
  771. );
  772. assert_eq!(
  773. Err("cannot parse protocol range upper bound"),
  774. expand_version_range("1-a")
  775. );
  776. }
  777. #[test]
  778. fn test_contract_protocol_list() {
  779. use std::collections::HashSet;
  780. use super::contract_protocol_list;
  781. {
  782. let mut versions = HashSet::<Version>::new();
  783. assert_eq!(String::from(""), contract_protocol_list(&versions));
  784. versions.insert(1);
  785. assert_eq!(String::from("1"), contract_protocol_list(&versions));
  786. versions.insert(2);
  787. assert_eq!(String::from("1-2"), contract_protocol_list(&versions));
  788. }
  789. {
  790. let mut versions = HashSet::<Version>::new();
  791. versions.insert(1);
  792. versions.insert(3);
  793. assert_eq!(String::from("1,3"), contract_protocol_list(&versions));
  794. }
  795. {
  796. let mut versions = HashSet::<Version>::new();
  797. versions.insert(1);
  798. versions.insert(2);
  799. versions.insert(3);
  800. versions.insert(4);
  801. assert_eq!(String::from("1-4"), contract_protocol_list(&versions));
  802. }
  803. {
  804. let mut versions = HashSet::<Version>::new();
  805. versions.insert(1);
  806. versions.insert(3);
  807. versions.insert(5);
  808. versions.insert(6);
  809. versions.insert(7);
  810. assert_eq!(
  811. String::from("1,3,5-7"),
  812. contract_protocol_list(&versions)
  813. );
  814. }
  815. {
  816. let mut versions = HashSet::<Version>::new();
  817. versions.insert(1);
  818. versions.insert(2);
  819. versions.insert(3);
  820. versions.insert(500);
  821. assert_eq!(
  822. String::from("1-3,500"),
  823. contract_protocol_list(&versions)
  824. );
  825. }
  826. }
  827. }