hosts.rs 17 KB

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