hosts.rs 18 KB

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