hosts.rs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. use async_std::sync::{Arc, Mutex};
  2. use std::net::IpAddr;
  3. use fxhash::{FxHashMap, FxHashSet};
  4. use ipnet::{Ipv4Net, Ipv6Net};
  5. use iprange::IpRange;
  6. use log::{debug, error, warn};
  7. use url::Url;
  8. use super::constants::{IP4_PRIV_RANGES, IP6_PRIV_RANGES, LOCALNET};
  9. use crate::util::encoding::base32;
  10. /// Pointer to hosts class.
  11. pub type HostsPtr = Arc<Hosts>;
  12. /// Manages a store of network addresses.
  13. pub struct Hosts {
  14. addrs: Mutex<FxHashSet<Url>>,
  15. localnet: bool,
  16. ipv4_range: IpRange<Ipv4Net>,
  17. ipv6_range: IpRange<Ipv6Net>,
  18. }
  19. impl Hosts {
  20. /// Create a new host list.
  21. pub fn new(localnet: bool) -> Arc<Self> {
  22. // Initialize ipv4_range and ipv6_range if needed
  23. let mut ipv4_range: IpRange<Ipv4Net> =
  24. IP4_PRIV_RANGES.iter().map(|s| s.parse().unwrap()).collect();
  25. let mut ipv6_range: IpRange<Ipv6Net> =
  26. IP6_PRIV_RANGES.iter().map(|s| s.parse().unwrap()).collect();
  27. // These will make the trie potentially smaller
  28. ipv4_range.simplify();
  29. ipv6_range.simplify();
  30. Arc::new(Self { addrs: Mutex::new(FxHashSet::default()), localnet, ipv4_range, ipv6_range })
  31. }
  32. /// Add a new host to the host list, after filtering.
  33. pub async fn store(&self, input_addrs: Vec<Url>) {
  34. debug!(target: "net", "hosts::store() [Start]");
  35. let addrs = if !self.localnet {
  36. let filtered = filter_localnet(input_addrs);
  37. let filtered = filter_invalid(&self.ipv4_range, &self.ipv6_range, filtered);
  38. filtered.into_iter().map(|(k, _)| k).collect()
  39. } else {
  40. debug!(target: "net", "hosts::store() [Localnet mode, skipping filterring.]");
  41. input_addrs
  42. };
  43. let mut addrs_map = self.addrs.lock().await;
  44. for addr in addrs {
  45. addrs_map.insert(addr);
  46. }
  47. debug!(target: "net", "hosts::store() [End]");
  48. }
  49. /// Add a new hosts external adders to the host list, after filtering and verifying
  50. /// the address url resolves to the provided connection address.
  51. pub async fn store_ext(&self, connection_addr: Url, input_addrs: Vec<Url>) {
  52. debug!(target: "net", "hosts::store_ext() [Start]");
  53. let addrs = if !self.localnet {
  54. let filtered = filter_localnet(input_addrs);
  55. let filtered = filter_invalid(&self.ipv4_range, &self.ipv6_range, filtered);
  56. filter_non_resolving(connection_addr, filtered)
  57. } else {
  58. debug!(target: "net", "hosts::store_ext() [Localnet mode, skipping filterring.]");
  59. input_addrs
  60. };
  61. let mut addrs_map = self.addrs.lock().await;
  62. for addr in addrs {
  63. addrs_map.insert(addr);
  64. }
  65. debug!(target: "net", "hosts::store_ext() [End]");
  66. }
  67. /// Return the list of hosts.
  68. pub async fn load_all(&self) -> Vec<Url> {
  69. self.addrs.lock().await.iter().cloned().collect()
  70. }
  71. /// Remove an Url from the list
  72. pub async fn remove(&self, url: &Url) -> bool {
  73. self.addrs.lock().await.remove(url)
  74. }
  75. /// Check if the host list is empty.
  76. pub async fn is_empty(&self) -> bool {
  77. self.addrs.lock().await.is_empty()
  78. }
  79. }
  80. /// Auxiliary function to filter localnet hosts.
  81. fn filter_localnet(input_addrs: Vec<Url>) -> Vec<Url> {
  82. debug!(target: "net", "hosts::filter_localnet() [Input addresses: {:?}]", input_addrs);
  83. let mut filtered = vec![];
  84. for addr in &input_addrs {
  85. if let Some(host_str) = addr.host_str() {
  86. if !LOCALNET.contains(&host_str) {
  87. filtered.push(addr.clone());
  88. continue
  89. }
  90. debug!(target: "net", "hosts::filter_localnet() [Filtered localnet addr: {}]", addr);
  91. continue
  92. }
  93. warn!(target: "net", "hosts::filter_localnet() [{} addr.host_str is empty, skipping.]", addr);
  94. }
  95. debug!(target: "net", "hosts::filter_localnet() [Filtered addresses: {:?}]", filtered);
  96. filtered
  97. }
  98. /// Auxiliary function to filter invalid(unresolvable) hosts.
  99. fn filter_invalid(
  100. ipv4_range: &IpRange<Ipv4Net>,
  101. ipv6_range: &IpRange<Ipv6Net>,
  102. input_addrs: Vec<Url>,
  103. ) -> FxHashMap<Url, Vec<IpAddr>> {
  104. debug!(target: "net", "hosts::filter_invalid() [Input addresses: {:?}]", input_addrs);
  105. let mut filtered = FxHashMap::default();
  106. for addr in &input_addrs {
  107. // Discard domainless Urls
  108. let domain = match addr.domain() {
  109. Some(d) => d,
  110. None => {
  111. debug!(target: "net", "hosts::filter_invalid() [Filtered domainless url: {}]", addr);
  112. continue
  113. }
  114. };
  115. // Validate onion domain
  116. if domain.ends_with("onion") {
  117. match is_valid_onion(domain) {
  118. true => {
  119. filtered.insert(addr.clone(), vec![]);
  120. }
  121. false => {
  122. warn!(target: "net", "hosts::filter_invalid() [Got invalid onion address: {}]", addr)
  123. }
  124. }
  125. continue
  126. }
  127. // Validate Internet domains and IPs. socket_addrs() does a resolution
  128. // with the local DNS resolver (i.e. /etc/resolv.conf), so the admin has
  129. // to take care of any DNS leaks by properly configuring their system for
  130. // DNS resolution.
  131. if let Ok(socket_addrs) = addr.socket_addrs(|| None) {
  132. // Check if domain resolved to anything
  133. if socket_addrs.is_empty() {
  134. debug!(target: "net", "hosts::filter_invalid() [Filtered unresolvable URL: {}]", addr);
  135. continue
  136. }
  137. // Checking resolved IP validity
  138. let mut resolves = vec![];
  139. for i in socket_addrs {
  140. let ip = i.ip();
  141. match ip {
  142. IpAddr::V4(a) => {
  143. if ipv4_range.contains(&a) {
  144. debug!(target: "net", "hosts::filter_invalid() [Filtered private-range IPv4: {}]", a);
  145. continue
  146. }
  147. }
  148. IpAddr::V6(a) => {
  149. if ipv6_range.contains(&a) {
  150. debug!(target: "net", "hosts::filter_invalid() [Filtered private range IPv6: {}]", a);
  151. continue
  152. }
  153. }
  154. }
  155. resolves.push(ip);
  156. }
  157. if resolves.is_empty() {
  158. debug!(target: "net", "hosts::filter_invalid() [Filtered unresolvable URL: {}]", addr);
  159. continue
  160. }
  161. filtered.insert(addr.clone(), resolves);
  162. } else {
  163. warn!(target: "net", "hosts::filter_invalid() [Failed resolving socket_addrs for {}]", addr);
  164. continue
  165. }
  166. }
  167. debug!(target: "net", "hosts::filter_invalid() [Filtered addresses: {:?}]", filtered);
  168. filtered
  169. }
  170. /// Filters `input_addrs` keys to whatever has at least one `IpAddr` that is
  171. /// the same as `connection_addr`'s IP address.
  172. /// Skips .onion domains.
  173. fn filter_non_resolving(
  174. connection_addr: Url,
  175. input_addrs: FxHashMap<Url, Vec<IpAddr>>,
  176. ) -> Vec<Url> {
  177. debug!(target: "net", "hosts::filter_non_resolving() [Input addresses: {:?}]", input_addrs);
  178. debug!(target: "net", "hosts::filter_non_resolving() [Connection address: {}]", connection_addr);
  179. // Retrieve connection IPs
  180. let mut ipv4_range = vec![];
  181. let mut ipv6_range = vec![];
  182. match connection_addr.socket_addrs(|| None) {
  183. Ok(v) => {
  184. for i in v {
  185. match i.ip() {
  186. IpAddr::V4(a) => ipv4_range.push(a),
  187. IpAddr::V6(a) => ipv6_range.push(a),
  188. }
  189. }
  190. }
  191. Err(e) => {
  192. error!(target: "net", "hosts::filter_non_resolving() [Failed resolving connection_addr {}: {}]", connection_addr, e);
  193. return vec![]
  194. }
  195. };
  196. debug!(target: "net", "hosts::filter_non_resolving() [{} IPv4: {:?}]", connection_addr, ipv4_range);
  197. debug!(target: "net", "hosts::filter_non_resolving() [{} IPv6: {:?}]", connection_addr, ipv6_range);
  198. let mut filtered = vec![];
  199. for (addr, resolves) in &input_addrs {
  200. // Keep onion domains. It's assumed that the .onion addresses
  201. // have already been validated.
  202. let addr_domain = addr.domain().unwrap();
  203. if addr_domain.ends_with(".onion") {
  204. filtered.push(addr.clone());
  205. continue
  206. }
  207. // Checking IP validity. If at least one IP matches, we consider it fine.
  208. let mut valid = false;
  209. for ip in resolves {
  210. match ip {
  211. IpAddr::V4(a) => {
  212. if ipv4_range.contains(&a) {
  213. valid = true;
  214. break
  215. }
  216. }
  217. IpAddr::V6(a) => {
  218. if ipv6_range.contains(&a) {
  219. valid = true;
  220. break
  221. }
  222. }
  223. }
  224. }
  225. if !valid {
  226. debug!(target: "net", "hosts::filter_non_resolving() [Filtered unresolvable url: {}]", addr);
  227. continue
  228. }
  229. filtered.push(addr.clone());
  230. }
  231. debug!(target: "net", "hosts::filter_non_resolving() [Filtered addresses: {:?}]", filtered);
  232. filtered
  233. }
  234. /// Validate a given .onion address. Currently it just checks that the
  235. /// length and encoding are ok, and does not do any deeper check. Should
  236. /// be fixed in the future.
  237. fn is_valid_onion(onion: &str) -> bool {
  238. let onion = match onion.strip_suffix(".onion") {
  239. Some(s) => s,
  240. None => onion,
  241. };
  242. if onion.len() != 56 {
  243. return false
  244. }
  245. base32::decode(&onion.to_uppercase()).is_some()
  246. }
  247. #[cfg(test)]
  248. mod tests {
  249. use std::net::{IpAddr, Ipv4Addr};
  250. use fxhash::{FxHashMap, FxHashSet};
  251. use ipnet::{Ipv4Net, Ipv6Net};
  252. use iprange::IpRange;
  253. // Uncomment for inner logging
  254. //use simplelog::{ColorChoice, Config, LevelFilter, TermLogger, TerminalMode};
  255. use url::Url;
  256. use crate::net::{
  257. constants::{IP4_PRIV_RANGES, IP6_PRIV_RANGES},
  258. hosts::{filter_invalid, filter_localnet, filter_non_resolving, is_valid_onion},
  259. };
  260. #[test]
  261. fn test_filter_localnet() {
  262. // Uncomment for inner logging
  263. /*
  264. TermLogger::init(
  265. LevelFilter::Debug,
  266. Config::default(),
  267. TerminalMode::Mixed,
  268. ColorChoice::Auto,
  269. )
  270. .unwrap();
  271. */
  272. // Create addresses to test
  273. let valid = Url::parse("tls://facebook.com:13333").unwrap();
  274. let onion = Url::parse(
  275. "tor://facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion:13333",
  276. )
  277. .unwrap();
  278. let localhost = Url::parse("tls://localhost:13333").unwrap();
  279. let localip = Url::parse("tls://127.0.0.1:13333").unwrap();
  280. // Create input addresses vector
  281. let input_addrs = vec![valid.clone(), onion.clone(), localhost, localip];
  282. // Create expected output addresses vector
  283. let output_addrs = vec![valid, onion];
  284. let output_addrs = FxHashSet::from_iter(output_addrs.iter());
  285. // Execute filtering for v4 addr
  286. let filtered = filter_localnet(input_addrs);
  287. let filtered = FxHashSet::from_iter(filtered.iter());
  288. // Validate filtered addresses
  289. assert_eq!(output_addrs, filtered);
  290. }
  291. #[test]
  292. fn test_filter_invalid() {
  293. // Uncomment for inner logging
  294. /*
  295. TermLogger::init(
  296. LevelFilter::Debug,
  297. Config::default(),
  298. TerminalMode::Mixed,
  299. ColorChoice::Auto,
  300. )
  301. .unwrap();
  302. */
  303. // Initialize ipv4_range and ipv6_range if needed
  304. let mut ipv4_range: IpRange<Ipv4Net> =
  305. IP4_PRIV_RANGES.iter().map(|s| s.parse().unwrap()).collect();
  306. let mut ipv6_range: IpRange<Ipv6Net> =
  307. IP6_PRIV_RANGES.iter().map(|s| s.parse().unwrap()).collect();
  308. // These will make the trie potentially smaller
  309. ipv4_range.simplify();
  310. ipv6_range.simplify();
  311. // Create addresses to test
  312. let valid = Url::parse("tls://facebook.com:13333").unwrap();
  313. let domainless = Url::parse("unix:/run/foo.socket").unwrap();
  314. let mut hostless = Url::parse("tls://185.60.216.35:13333").unwrap();
  315. hostless.set_host(None).unwrap();
  316. let onion = Url::parse(
  317. "tor://facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion:13333",
  318. )
  319. .unwrap();
  320. let invalid_onion =
  321. Url::parse("tor://facebookwemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion:13333")
  322. .unwrap();
  323. // Create input addresses vector
  324. let input_addrs = vec![valid.clone(), domainless, hostless, onion.clone(), invalid_onion];
  325. // Create expected output addresses vector
  326. let output_addrs = vec![valid, onion];
  327. let output_addrs = FxHashSet::from_iter(output_addrs.iter());
  328. // Execute filtering for v4 addr
  329. let filtered = filter_invalid(&ipv4_range, &ipv6_range, input_addrs);
  330. let filtered: Vec<Url> = filtered.into_iter().map(|(k, _)| k).collect();
  331. let filtered = FxHashSet::from_iter(filtered.iter());
  332. // Validate filtered addresses
  333. assert_eq!(output_addrs, filtered);
  334. }
  335. #[test]
  336. fn test_filter_non_resolving() {
  337. // Uncomment for inner logging
  338. /*
  339. TermLogger::init(
  340. LevelFilter::Debug,
  341. Config::default(),
  342. TerminalMode::Mixed,
  343. ColorChoice::Auto,
  344. )
  345. .unwrap();
  346. */
  347. // Create addresses to test
  348. let connection_url_v4 = Url::parse("tls://185.60.216.35:13333").unwrap();
  349. let connection_url_v6 =
  350. Url::parse("tls://[2a03:2880:f12d:83:face:b00c:0:25de]:13333").unwrap();
  351. let fake_connection_url = Url::parse("tls://185.199.109.153:13333").unwrap();
  352. let resolving_url = Url::parse("tls://facebook.com:13333").unwrap();
  353. let random_url = Url::parse("tls://facebookkk.com:13333").unwrap();
  354. let onion = Url::parse(
  355. "tor://facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion:13333",
  356. )
  357. .unwrap();
  358. // Create input addresses hashmap, containing created addresses, excluding connection url
  359. let mut input_addrs = FxHashMap::default();
  360. input_addrs.insert(
  361. resolving_url.clone(),
  362. vec![
  363. IpAddr::V4(Ipv4Addr::new(185, 60, 216, 35)),
  364. "2a03:2880:f12d:83:face:b00c:0:25de".parse().unwrap(),
  365. ],
  366. );
  367. input_addrs.insert(random_url, vec![]);
  368. input_addrs.insert(onion.clone(), vec![]);
  369. // Create expected output addresses hashset
  370. let mut output_addrs = FxHashMap::default();
  371. output_addrs.insert(
  372. resolving_url,
  373. vec![
  374. IpAddr::V4(Ipv4Addr::new(185, 60, 216, 35)),
  375. "2a03:2880:f12d:83:face:b00c:0:25de".parse().unwrap(),
  376. ],
  377. );
  378. output_addrs.insert(onion.clone(), vec![]);
  379. // Convert hashmap to Vec<Url and then to hashset, to ignore shuffling
  380. let output_addrs: Vec<Url> = output_addrs.into_iter().map(|(k, _)| k).collect();
  381. let output_addrs = FxHashSet::from_iter(output_addrs.iter());
  382. let mut fake_output_addrs: FxHashMap<Url, Vec<Url>> = FxHashMap::default();
  383. // Onion addresses don't get filtered, as we can't resolve them
  384. fake_output_addrs.insert(onion, vec![]);
  385. let fake_output_addrs: Vec<Url> = fake_output_addrs.into_iter().map(|(k, _)| k).collect();
  386. let fake_output_addrs = FxHashSet::from_iter(fake_output_addrs.iter());
  387. // Execute filtering for v4 addr
  388. let filtered = filter_non_resolving(connection_url_v4, input_addrs.clone());
  389. let filtered = FxHashSet::from_iter(filtered.iter());
  390. // Validate filtered addresses
  391. assert_eq!(output_addrs, filtered);
  392. // Execute filtering for v6 addr
  393. let filtered = filter_non_resolving(connection_url_v6, input_addrs.clone());
  394. let filtered = FxHashSet::from_iter(filtered.iter());
  395. assert_eq!(output_addrs, filtered);
  396. // Execute filtering for fake addr
  397. let filtered = filter_non_resolving(fake_connection_url, input_addrs);
  398. let filtered = FxHashSet::from_iter(filtered.iter());
  399. assert_eq!(fake_output_addrs, filtered);
  400. }
  401. #[test]
  402. fn test_is_valid_onion() {
  403. // Valid onion
  404. assert_eq!(
  405. is_valid_onion("facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion"),
  406. true
  407. );
  408. // Valid onion without .onion suffix
  409. assert_eq!(
  410. is_valid_onion("facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd"),
  411. true
  412. );
  413. // Invalid onion
  414. assert_eq!(is_valid_onion("facebook.com"), false);
  415. }
  416. }