store.rs 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2024 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,
  20. fmt, fs,
  21. fs::File,
  22. sync::Arc,
  23. time::{Instant, UNIX_EPOCH},
  24. };
  25. use log::{debug, error, info, trace, warn};
  26. use rand::{prelude::IteratorRandom, rngs::OsRng, Rng};
  27. use smol::lock::RwLock;
  28. use url::Url;
  29. use super::super::{settings::SettingsPtr, ChannelPtr};
  30. use crate::{
  31. system::{Subscriber, SubscriberPtr, Subscription},
  32. util::{
  33. file::{load_file, save_file},
  34. path::expand_path,
  35. },
  36. Error, Result,
  37. };
  38. // An array containing all possible local host strings
  39. // TODO: This could perhaps be more exhaustive?
  40. pub const LOCAL_HOST_STRS: [&str; 2] = ["localhost", "localhost.localdomain"];
  41. const WHITELIST_MAX_LEN: usize = 5000;
  42. const GREYLIST_MAX_LEN: usize = 2000;
  43. /// Atomic pointer to hosts object
  44. pub type HostsPtr = Arc<Hosts>;
  45. /// Keeps track of hosts and their current state. Prevents race conditions
  46. /// where multiple threads are simultaenously trying to change the state of
  47. /// a given host.
  48. pub type HostRegistry = RwLock<HashMap<Url, HostState>>;
  49. /// HostState is a set of mutually exclusive states that can be Pending,
  50. /// Connected, Disconnected or Refining. The state is `None` when the
  51. /// corresponding host has been removed from the HostRegistry.
  52. ///
  53. /// +----------+
  54. /// +-- | refining | --+
  55. /// | +----------+ |
  56. /// | |
  57. /// v v
  58. /// +---------+ +-----------+ +------+
  59. /// | pending | -> | connected | -> | None |
  60. /// +---------+ +-----------+ +------+
  61. /// | ^
  62. /// | |
  63. /// | +-------------+ |
  64. /// +-----> | downgrading | ------+
  65. /// +-------------+
  66. ///
  67. #[derive(Clone, Debug)]
  68. pub enum HostState {
  69. /// Hosts that are being connected to in Outbound and Manual Session.
  70. Pending,
  71. /// Hosts that have been successfully connected to.
  72. Connected(ChannelPtr),
  73. /// Hosts that we have repeatedly failed to connect to, and that are being
  74. /// removed from the anchorlist and whitelist and added to the greylist.
  75. Downgrading,
  76. /// Hosts that are migrating from the greylist to the whitelist or being
  77. /// removed from the greylist, as defined in `refinery.rs`.
  78. Refining,
  79. }
  80. impl HostState {
  81. // Try to change state to Downgrading. Only possible if this
  82. // connection is pending i.e. if we are trying to connect to this
  83. // host.
  84. fn try_downgrade(&self) -> Result<Self> {
  85. match self {
  86. HostState::Pending => Ok(HostState::Downgrading),
  87. HostState::Connected(_) => Err(Error::StateBlocked(self.to_string())),
  88. HostState::Downgrading => Err(Error::StateBlocked(self.to_string())),
  89. HostState::Refining => Err(Error::StateBlocked(self.to_string())),
  90. }
  91. }
  92. // Try to change state to Refining. Only possible if we are not yet
  93. // tracking this host in the HostRegistry.
  94. fn try_refine(&self) -> Result<Self> {
  95. match self {
  96. HostState::Pending => Err(Error::StateBlocked(self.to_string())),
  97. HostState::Connected(_) => Err(Error::StateBlocked(self.to_string())),
  98. HostState::Downgrading => Err(Error::StateBlocked(self.to_string())),
  99. HostState::Refining => Err(Error::StateBlocked(self.to_string())),
  100. }
  101. }
  102. // Try to change state to Connected. Possible if this peer is
  103. // currently Pending or being Refined. The latter is necessary since
  104. // the refinery process requires us to establish a connection to
  105. // a peer.
  106. fn try_connect(&self, channel: ChannelPtr) -> Result<Self> {
  107. match self {
  108. HostState::Pending => Ok(HostState::Connected(channel)),
  109. HostState::Connected(_) => Err(Error::StateBlocked(self.to_string())),
  110. HostState::Downgrading => Err(Error::StateBlocked(self.to_string())),
  111. HostState::Refining => Ok(HostState::Connected(channel)),
  112. }
  113. }
  114. // Try to change state to Pending. Only possible if we are not yet
  115. // tracking this host in the HostRegistry.
  116. fn try_pending(&self) -> Result<Self> {
  117. match self {
  118. HostState::Pending => Err(Error::StateBlocked(self.to_string())),
  119. HostState::Connected(_) => Err(Error::StateBlocked(self.to_string())),
  120. HostState::Downgrading => Err(Error::StateBlocked(self.to_string())),
  121. HostState::Refining => Err(Error::StateBlocked(self.to_string())),
  122. }
  123. }
  124. }
  125. impl fmt::Display for HostState {
  126. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  127. fmt::Debug::fmt(self, f)
  128. }
  129. }
  130. #[repr(u8)]
  131. pub enum HostColor {
  132. /// Intermediary nodes that are periodically probed and updated to White.
  133. Grey = 0,
  134. /// Recently seen hosts. Shared with other nodes.
  135. White = 1,
  136. /// Nodes to which we have already been able to establish a connection.
  137. Gold = 2,
  138. /// Hostile peers that can neither be connected to nor establish
  139. /// connections to us for the duration of the program.
  140. Black = 3,
  141. }
  142. /// A Container for managing Grey, White, Gold and Black
  143. /// hostlists. Exposes a common interface for writing to and querying
  144. /// hostlists.
  145. // TODO: Currently hosts (aside from hosts on the Black list) are on
  146. // multiple lists at once. This needs to be reconsidered.
  147. // Rethink upgrade/ downgrade methods and consider a single method move() which
  148. // removes from one hostlist and places on another.
  149. // TODO: Verify the performance overhead of using vectors for hostlists.
  150. // TODO: Check whether anchorlist (Gold) has a max size in Monero.
  151. pub struct HostContainer {
  152. pub hostlists: [RwLock<Vec<(Url, u64)>>; 4],
  153. }
  154. impl HostContainer {
  155. fn new() -> Self {
  156. let hostlists: [RwLock<Vec<(Url, u64)>>; 4] = [
  157. RwLock::new(Vec::new()),
  158. RwLock::new(Vec::new()),
  159. RwLock::new(Vec::new()),
  160. RwLock::new(Vec::new()),
  161. ];
  162. Self { hostlists }
  163. }
  164. /// Append host to a hostlist.
  165. pub async fn store(&self, color: usize, addr: Url, last_seen: u64) {
  166. trace!(target: "net::hosts::store()", "[START]");
  167. let mut list = self.hostlists[color].write().await;
  168. list.push((addr, last_seen));
  169. if color == 0 {
  170. if list.len() == GREYLIST_MAX_LEN {
  171. let last_entry = list.pop().unwrap();
  172. debug!(target: "net::hosts::store()",
  173. "Greylist reached max size. Removed {:?}", last_entry);
  174. }
  175. }
  176. if color == 1 {
  177. if list.len() == WHITELIST_MAX_LEN {
  178. let last_entry = list.pop().unwrap();
  179. debug!(target: "net::hosts::store()",
  180. "Whitelist reached max size. Removed {:?}", last_entry);
  181. }
  182. }
  183. // Sort the list by last_seen.
  184. list.sort_by_key(|entry| entry.1);
  185. list.reverse();
  186. trace!(target: "net::hosts::store()", "[END]");
  187. }
  188. /// Stores an address on a hostlist or updates its last_seen field if we already
  189. /// have the address.
  190. pub async fn store_or_update(&self, color: HostColor, addrs: &[(Url, u64)]) {
  191. trace!(target: "net::hosts::store_or_update()", "[START]");
  192. let parent_index = color as usize;
  193. for (addr, last_seen) in addrs {
  194. if !self.contains(parent_index, &addr).await {
  195. debug!(target: "net::hosts::store_or_update()",
  196. "We do not have this entry in the hostlist. Adding to store...");
  197. self.store(parent_index, addr.clone(), *last_seen).await;
  198. } else {
  199. debug!(target: "net::hosts::store_or_update()",
  200. "We have this entry in the hostlist. Updating last seen...");
  201. let child_index = self
  202. .get_index_at_addr(parent_index, addr.clone())
  203. .await
  204. .expect("Expected entry to exist");
  205. debug!(target: "net::hosts::store_or_update()",
  206. "Selected index, updating last seen...");
  207. self.update_last_seen(parent_index, &addr, *last_seen, child_index).await;
  208. }
  209. }
  210. }
  211. /// Update the last_seen field of a peer on a hostlist.
  212. pub async fn update_last_seen(&self, color: usize, addr: &Url, last_seen: u64, index: usize) {
  213. trace!(target: "net::hosts::update_last_seen()", "[START]");
  214. let mut list = self.hostlists[color].write().await;
  215. list[index] = (addr.clone(), last_seen);
  216. list.sort_by_key(|entry| entry.1);
  217. list.reverse();
  218. trace!(target: "net::hosts::update_last_seen()", "[END]");
  219. }
  220. /// Return all known hosts on a hostlist.
  221. pub async fn fetch_all(&self, color: HostColor) -> Vec<(Url, u64)> {
  222. self.hostlists[color as usize].read().await.iter().cloned().collect()
  223. }
  224. /// Get the oldest entry from a hostlist.
  225. pub async fn fetch_last(&self, color: HostColor) -> ((Url, u64), usize) {
  226. let list = self.hostlists[color as usize].read().await;
  227. let position = list.len() - 1;
  228. let entry = &list[position];
  229. (entry.clone(), position)
  230. }
  231. /// TODO: documentation
  232. pub async fn fetch_address(
  233. &self,
  234. color: HostColor,
  235. transports: &[String],
  236. transport_mixing: bool,
  237. ) -> Vec<(Url, u64)> {
  238. trace!(target: "net::hosts::fetch_address()", "[START]");
  239. let mut hosts = vec![];
  240. let index = color as usize;
  241. // If transport mixing is enabled, then for example we're allowed to
  242. // use tor:// to connect to tcp:// and tor+tls:// to connect to tcp+tls://.
  243. // However, **do not** mix tor:// and tcp+tls://, nor tor+tls:// and tcp://.
  244. macro_rules! mix_transport {
  245. ($a:expr, $b:expr) => {
  246. if transports.contains(&$a.to_string()) && transport_mixing {
  247. let mut a_to_b = self.fetch_with_schemes(index, &[$b.to_string()], None).await;
  248. for (addr, last_seen) in a_to_b.iter_mut() {
  249. addr.set_scheme($a).unwrap();
  250. hosts.push((addr.clone(), last_seen.clone()));
  251. }
  252. }
  253. };
  254. }
  255. mix_transport!("tor", "tcp");
  256. mix_transport!("tor+tls", "tcp+tls");
  257. mix_transport!("nym", "tcp");
  258. mix_transport!("nym+tls", "tcp+tls");
  259. // And now the actual requested transports
  260. for (addr, last_seen) in self.fetch_with_schemes(index, transports, None).await {
  261. hosts.push((addr, last_seen));
  262. }
  263. trace!(target: "net::hosts::fetch_address()", "Grabbed hosts, length: {}", hosts.len());
  264. hosts
  265. }
  266. /// Get up to limit peers that match the given transport schemes from a hostlist.
  267. /// If limit was not provided, return all matching peers.
  268. async fn fetch_with_schemes(
  269. &self,
  270. color: usize,
  271. schemes: &[String],
  272. limit: Option<usize>,
  273. ) -> Vec<(Url, u64)> {
  274. trace!(target: "net::hosts::fetch_with_schemes()", "[START]");
  275. let list = self.hostlists[color].read().await;
  276. let mut limit = match limit {
  277. Some(l) => l.min(list.len()),
  278. None => list.len(),
  279. };
  280. let mut ret = vec![];
  281. if limit == 0 {
  282. return ret
  283. }
  284. for (addr, last_seen) in list.iter() {
  285. if schemes.contains(&addr.scheme().to_string()) {
  286. ret.push((addr.clone(), *last_seen));
  287. limit -= 1;
  288. if limit == 0 {
  289. debug!(target: "net::hosts::fetch_with_schemes()",
  290. "Found matching scheme, returning {} addresses",
  291. ret.len());
  292. return ret
  293. }
  294. }
  295. }
  296. if ret.is_empty() {
  297. debug!(target: "net::hosts::fetch_with_schemes()",
  298. "No such schemes found!")
  299. }
  300. ret
  301. }
  302. /// Get up to limit peers that don't match the given transport schemes from a hostlist.
  303. /// If limit was not provided, return all matching peers.
  304. pub async fn fetch_excluding_schemes(
  305. &self,
  306. color: usize,
  307. schemes: &[String],
  308. limit: Option<usize>,
  309. ) -> Vec<(Url, u64)> {
  310. let list = self.hostlists[color].read().await;
  311. let mut limit = match limit {
  312. Some(l) => l.min(list.len()),
  313. None => list.len(),
  314. };
  315. let mut ret = vec![];
  316. if limit == 0 {
  317. return ret
  318. }
  319. for (addr, last_seen) in list.iter() {
  320. if !schemes.contains(&addr.scheme().to_string()) {
  321. ret.push((addr.clone(), *last_seen));
  322. limit -= 1;
  323. if limit == 0 {
  324. return ret
  325. }
  326. }
  327. }
  328. if ret.is_empty() {
  329. debug!(target: "net::hosts::fetch_excluding_schemes()",
  330. "No such schemes found!")
  331. }
  332. ret
  333. }
  334. /// Get a random peer from a hostlist.
  335. pub async fn fetch_random(&self, color: HostColor) -> ((Url, u64), usize) {
  336. let list = self.hostlists[color as usize].read().await;
  337. let position = rand::thread_rng().gen_range(0..list.len());
  338. let entry = &list[position];
  339. (entry.clone(), position)
  340. }
  341. /// Get a random peer from a hostlist that matches the given transport schemes.
  342. pub async fn fetch_random_with_schemes(
  343. &self,
  344. color: HostColor,
  345. schemes: &[String],
  346. ) -> Option<((Url, u64), usize)> {
  347. // Retrieve all peers corresponding to that transport schemes
  348. trace!(target: "net::hosts::fetch_random_with_schemes()", "[START]");
  349. let list = self.fetch_with_schemes(color as usize, schemes, None).await;
  350. if list.is_empty() {
  351. return None
  352. }
  353. let position = rand::thread_rng().gen_range(0..list.len());
  354. let entry = &list[position];
  355. Some((entry.clone(), position))
  356. }
  357. /// Get up to n random peers. Schemes are not taken into account.
  358. pub async fn fetch_n_random(&self, color: HostColor, n: u32) -> Vec<(Url, u64)> {
  359. trace!(target: "net::hosts::fetch_n_random()", "[START]");
  360. let n = n as usize;
  361. if n == 0 {
  362. return vec![]
  363. }
  364. let mut hosts = vec![];
  365. let list = self.hostlists[color as usize].read().await;
  366. for (addr, last_seen) in list.iter() {
  367. hosts.push((addr.clone(), *last_seen));
  368. }
  369. if hosts.is_empty() {
  370. debug!(target: "net::hosts::fetch_n_random()",
  371. "No entries found!");
  372. return hosts
  373. }
  374. // Grab random ones
  375. let urls = hosts.iter().choose_multiple(&mut OsRng, n.min(hosts.len()));
  376. urls.iter().map(|&url| url.clone()).collect()
  377. }
  378. /// Get up to n random peers that match the given transport schemes.
  379. pub async fn fetch_n_random_with_schemes(
  380. &self,
  381. color: HostColor,
  382. schemes: &[String],
  383. n: u32,
  384. ) -> Vec<(Url, u64)> {
  385. trace!(target: "net::hosts::fetch_n_random_with_schemes()", "[START]");
  386. let index = color as usize;
  387. let n = n as usize;
  388. if n == 0 {
  389. return vec![]
  390. }
  391. // Retrieve all peers corresponding to that transport schemes
  392. let hosts = self.fetch_with_schemes(index, schemes, None).await;
  393. if hosts.is_empty() {
  394. debug!(target: "net::hosts::fetch_n_random_with_schemes()",
  395. "No such schemes found!");
  396. return hosts
  397. }
  398. // Grab random ones
  399. let urls = hosts.iter().choose_multiple(&mut OsRng, n.min(hosts.len()));
  400. urls.iter().map(|&url| url.clone()).collect()
  401. }
  402. /// Get up to n random peers that don't match the given transport schemes from
  403. /// a hostlist.
  404. pub async fn fetch_n_random_excluding_schemes(
  405. &self,
  406. color: HostColor,
  407. schemes: &[String],
  408. n: u32,
  409. ) -> Vec<(Url, u64)> {
  410. trace!(target: "net::hosts::fetch_excluding_schemes()", "[START]");
  411. let index = color as usize;
  412. let n = n as usize;
  413. if n == 0 {
  414. return vec![]
  415. }
  416. // Retrieve all peers not corresponding to that transport schemes
  417. let hosts = self.fetch_excluding_schemes(index, schemes, None).await;
  418. if hosts.is_empty() {
  419. debug!(target: "net::hosts::fetch_n_random_excluding_schemes()",
  420. "No such schemes found!");
  421. return hosts
  422. }
  423. // Grab random ones
  424. let urls = hosts.iter().choose_multiple(&mut OsRng, n.min(hosts.len()));
  425. urls.iter().map(|&url| url.clone()).collect()
  426. }
  427. /// Upgrade a connection to the anchorlist. Called after a connection has been successfully
  428. /// established in Outbound and Manual sessions.
  429. pub async fn upgrade_host(&self, addr: &Url) {
  430. let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
  431. self.store_or_update(HostColor::Gold, &[(addr.clone(), last_seen)]).await;
  432. }
  433. /// Remove an entry from a hostlist.
  434. pub async fn remove(&self, color: HostColor, addr: &Url, index: usize) {
  435. debug!(target: "net::hosts::remove()", "Removing peer {} from hostlist", addr);
  436. let mut list = self.hostlists[color as usize].write().await;
  437. list.remove(index);
  438. }
  439. /// Check if a hostlist is empty.
  440. pub async fn is_empty(&self, color: HostColor) -> bool {
  441. self.hostlists[color as usize].read().await.is_empty()
  442. }
  443. /// Check if host is in a hostlist
  444. pub async fn contains(&self, color: usize, addr: &Url) -> bool {
  445. self.hostlists[color].read().await.iter().any(|(u, _t)| u == addr)
  446. }
  447. /// Get the index for a given addr on a hostlist.
  448. pub async fn get_index_at_addr(&self, color: usize, addr: Url) -> Option<usize> {
  449. self.hostlists[color].read().await.iter().position(|a| a.0 == addr)
  450. }
  451. /// Get the entry for a given addr on the hostlist.
  452. pub async fn get_entry_at_addr(&self, color: usize, addr: &Url) -> Option<(Url, u64)> {
  453. self.hostlists[color]
  454. .read()
  455. .await
  456. .iter()
  457. .find(|(url, _)| url == addr)
  458. .map(|(url, time)| (url.clone(), *time))
  459. }
  460. /// Load the hostlists from a file.
  461. pub async fn load_all(&self, path: &String) -> Result<()> {
  462. let path = expand_path(path)?;
  463. if !path.exists() {
  464. if let Some(parent) = path.parent() {
  465. fs::create_dir_all(parent)?;
  466. }
  467. File::create(path.clone())?;
  468. }
  469. let contents = load_file(&path);
  470. if let Err(e) = contents {
  471. warn!(target: "net::hosts::load_hosts()", "Failed retrieving saved hosts: {}", e);
  472. return Ok(())
  473. }
  474. for line in contents.unwrap().lines() {
  475. let data: Vec<&str> = line.split('\t').collect();
  476. let url = match Url::parse(data[1]) {
  477. Ok(u) => u,
  478. Err(e) => {
  479. debug!(target: "net::hosts::load_hosts()", "Skipping malformed URL {}", e);
  480. continue
  481. }
  482. };
  483. let last_seen = match data[2].parse::<u64>() {
  484. Ok(t) => t,
  485. Err(e) => {
  486. debug!(target: "net::hosts::load_hosts()", "Skipping malformed last seen {}", e);
  487. continue
  488. }
  489. };
  490. match data[0] {
  491. "greylist" => {
  492. self.store(HostColor::Grey as usize, url, last_seen).await;
  493. }
  494. "whitelist" => {
  495. self.store(HostColor::White as usize, url, last_seen).await;
  496. }
  497. "anchorlist" => {
  498. self.store(HostColor::Gold as usize, url, last_seen).await;
  499. }
  500. _ => {
  501. debug!(target: "net::hosts::load_hosts()", "Malformed list name...");
  502. }
  503. }
  504. }
  505. Ok(())
  506. }
  507. /// Save the hostlist to a file. Whitelist gets written to the greylist to force
  508. /// whitelist entries through the refinery on start.
  509. pub async fn save_all(&self, path: &String) -> Result<()> {
  510. let path = expand_path(path)?;
  511. let mut tsv = String::new();
  512. let mut white = vec![];
  513. let mut greygold: HashMap<String, Vec<(Url, u64)>> = HashMap::new();
  514. // First gather all the whitelist entries we don't have in greylist.
  515. for (url, last_seen) in self.fetch_all(HostColor::White).await {
  516. if !self.contains(HostColor::Grey as usize, &url).await {
  517. white.push((url, last_seen))
  518. }
  519. }
  520. // Then gather the greylist and anchorlist entries.
  521. greygold.insert("anchorlist".to_string(), self.fetch_all(HostColor::Gold).await);
  522. greygold.insert("greylist".to_string(), self.fetch_all(HostColor::Grey).await);
  523. // We write whitelist entries to the greylist on p2p.stop() to force
  524. // them through the refinery on start().
  525. for (name, mut list) in greygold {
  526. if name == *"greylist".to_string() {
  527. list.append(&mut white)
  528. }
  529. for (url, last_seen) in list {
  530. tsv.push_str(&format!("{}\t{}\t{}\n", name, url, last_seen));
  531. }
  532. }
  533. if !tsv.eq("") {
  534. info!(target: "net::hosts::save_hosts()", "Saving hosts to: {:?}",
  535. path);
  536. if let Err(e) = save_file(&path, &tsv) {
  537. error!(target: "net::hosts::save_hosts()", "Failed saving hosts: {}", e);
  538. }
  539. }
  540. Ok(())
  541. }
  542. }
  543. /// TODO: documentation
  544. pub struct Hosts {
  545. /// Subscriber for notifications of new channels
  546. channel_subscriber: SubscriberPtr<Result<ChannelPtr>>,
  547. /// Set of stored addresses that are quarantined.
  548. /// We quarantine peers we've been unable to connect to, but we keep them
  549. /// around so we can potentially try them again, up to n tries. This should
  550. /// be helpful in order to self-heal the p2p connections in case we have an
  551. /// Internet interrupt (goblins unplugging cables)
  552. quarantine: RwLock<HashMap<Url, usize>>,
  553. /// A registry that tracks hosts and their current state.
  554. registry: HostRegistry,
  555. /// Subscriber listening for store updates
  556. store_subscriber: SubscriberPtr<usize>,
  557. /// Pointer to configured P2P settings
  558. settings: SettingsPtr,
  559. pub container: HostContainer,
  560. }
  561. impl Hosts {
  562. /// Create a new hosts list>
  563. pub fn new(settings: SettingsPtr) -> HostsPtr {
  564. Arc::new(Self {
  565. channel_subscriber: Subscriber::new(),
  566. quarantine: RwLock::new(HashMap::new()),
  567. registry: RwLock::new(HashMap::new()),
  568. store_subscriber: Subscriber::new(),
  569. settings,
  570. container: HostContainer::new(),
  571. })
  572. }
  573. /// Safely insert into the HostContainer. Filters the addresses first before storing and
  574. /// notifies the subscriber. Must be called when first receiving greylist addresses.
  575. pub async fn insert(&self, color: HostColor, addrs: &[(Url, u64)]) {
  576. trace!(target: "net::hosts:insert()", "[START]");
  577. let filtered_addrs = self.filter_addresses(self.settings.clone(), addrs).await;
  578. let filtered_addrs_len = filtered_addrs.len();
  579. if filtered_addrs.is_empty() {
  580. debug!(target: "net::hosts::insert()", "Filtered out all addresses");
  581. }
  582. self.container.store_or_update(color, &filtered_addrs).await;
  583. self.store_subscriber.notify(filtered_addrs_len).await;
  584. }
  585. /// Try to update the registry. If the host already exists, try to update its state.
  586. /// Otherwise add the host to the registry along with its state.
  587. pub async fn try_register(&self, addr: Url, new_state: HostState) -> Result<HostState> {
  588. let mut registry = self.registry.write().await;
  589. if registry.contains_key(&addr) {
  590. let current_state = registry.get(&addr).unwrap().clone();
  591. debug!(target: "net::hosts::try_update_registry()",
  592. "Attempting to update addr={} current_state={}, new_state={}",
  593. addr, current_state, new_state.to_string());
  594. let result: Result<HostState> = match new_state {
  595. HostState::Pending => current_state.try_pending(),
  596. HostState::Connected(c) => current_state.try_connect(c),
  597. HostState::Downgrading => current_state.try_downgrade(),
  598. HostState::Refining => current_state.try_refine(),
  599. };
  600. if let Ok(state) = &result {
  601. registry.insert(addr.clone(), state.clone());
  602. }
  603. result
  604. } else {
  605. // We don't know this peer. We can safely update the state.
  606. registry.insert(addr.clone(), new_state.clone());
  607. Ok(new_state)
  608. }
  609. }
  610. pub async fn check_address(&self, hosts: Vec<(Url, u64)>) -> Option<(Url, u64)> {
  611. // Try to find an unused host in the set.
  612. for (host, last_seen) in hosts {
  613. debug!(target: "net::hosts::check_address()", "Starting checks");
  614. if let Err(_) = self.try_register(host.clone(), HostState::Pending).await {
  615. continue
  616. }
  617. debug!(
  618. target: "net::hosts::check_address()",
  619. "Found valid host {}",
  620. host
  621. );
  622. return Some((host.clone(), last_seen))
  623. }
  624. None
  625. }
  626. /// Remove a host from the HostRegistry. Must be called after downgrade(), when the refinery
  627. /// process fails, or when a channel stops. Prevents hosts from getting trapped in the
  628. /// HostState logical machinery.
  629. pub async fn unregister(&self, addr: &Url) {
  630. debug!(target: "net::hosts::unregister()", "Removing {} from HostRegistry", addr);
  631. self.registry.write().await.remove(addr);
  632. }
  633. /// Returns the list of connected channels.
  634. pub async fn channels(&self) -> Vec<ChannelPtr> {
  635. let registry = self.registry.read().await;
  636. let mut channels = Vec::new();
  637. for (_, value) in registry.iter() {
  638. if let HostState::Connected(c) = value {
  639. channels.push(c.clone());
  640. }
  641. }
  642. channels
  643. }
  644. /// Retrieve a random connected channel
  645. pub async fn random_channel(&self) -> ChannelPtr {
  646. let channels = self.channels().await;
  647. let position = rand::thread_rng().gen_range(0..channels.len());
  648. channels[position].clone()
  649. }
  650. /// Add a channel to the set of connected channels
  651. pub async fn register_channel(&self, channel: ChannelPtr) -> Result<()> {
  652. let address = channel.address().clone();
  653. if let Err(e) =
  654. self.try_register(address.clone(), HostState::Connected(channel.clone())).await
  655. {
  656. return Err(e)
  657. }
  658. self.channel_subscriber.notify(Ok(channel)).await;
  659. Ok(())
  660. }
  661. pub async fn subscribe_store(&self) -> Result<Subscription<usize>> {
  662. let sub = self.store_subscriber.clone().subscribe().await;
  663. Ok(sub)
  664. }
  665. // Verify whether a URL is local.
  666. // NOTE: This function is stateless and not specific to
  667. // `Hosts`. For this reason, it might make more sense
  668. // to move this function to a more appropriate location
  669. // in the codebase.
  670. /// Check whether a URL is local host
  671. pub async fn is_local_host(&self, url: Url) -> bool {
  672. // Reject Urls without host strings.
  673. if url.host_str().is_none() {
  674. return false
  675. }
  676. // We do this hack in order to parse IPs properly.
  677. // https://github.com/whatwg/url/issues/749
  678. let addr = Url::parse(&url.as_str().replace(url.scheme(), "http")).unwrap();
  679. // Filter private IP ranges
  680. match addr.host().unwrap() {
  681. url::Host::Ipv4(ip) => {
  682. if !ip.is_global() {
  683. return true
  684. }
  685. }
  686. url::Host::Ipv6(ip) => {
  687. if !ip.is_global() {
  688. return true
  689. }
  690. }
  691. url::Host::Domain(d) => {
  692. if LOCAL_HOST_STRS.contains(&d) {
  693. return true
  694. }
  695. }
  696. }
  697. false
  698. }
  699. /// Filter given addresses based on certain rulesets and validity.
  700. async fn filter_addresses(
  701. &self,
  702. settings: SettingsPtr,
  703. addrs: &[(Url, u64)],
  704. ) -> Vec<(Url, u64)> {
  705. trace!(target: "net::hosts::filter_addresses()", "Filtering addrs: {:?}", addrs);
  706. let mut ret = vec![];
  707. let localnet = self.settings.localnet;
  708. 'addr_loop: for (addr_, last_seen) in addrs {
  709. // Validate that the format is `scheme://host_str:port`
  710. if addr_.host_str().is_none() ||
  711. addr_.port().is_none() ||
  712. addr_.cannot_be_a_base() ||
  713. addr_.path_segments().is_some()
  714. {
  715. continue
  716. }
  717. if self.container.contains(HostColor::Black as usize, addr_).await {
  718. warn!(target: "net::hosts::filter_addresses()",
  719. "Peer {} is blacklisted", addr_);
  720. continue
  721. }
  722. let host_str = addr_.host_str().unwrap();
  723. if !localnet {
  724. // Our own external addresses should never enter the hosts set.
  725. for ext in &settings.external_addrs {
  726. if host_str == ext.host_str().unwrap() {
  727. continue 'addr_loop
  728. }
  729. }
  730. }
  731. // On localnet, make sure ours ports don't enter the host set.
  732. for ext in &settings.external_addrs {
  733. if addr_.port() == ext.port() {
  734. continue 'addr_loop
  735. }
  736. }
  737. // We do this hack in order to parse IPs properly.
  738. // https://github.com/whatwg/url/issues/749
  739. let addr = Url::parse(&addr_.as_str().replace(addr_.scheme(), "http")).unwrap();
  740. // Filter non-global ranges if we're not allowing localnet.
  741. // Should never be allowed in production, so we don't really care
  742. // about some of them (e.g. 0.0.0.0, or broadcast, etc.).
  743. if !localnet && self.is_local_host(addr).await {
  744. continue
  745. }
  746. match addr_.scheme() {
  747. // Validate that the address is an actual onion.
  748. #[cfg(feature = "p2p-tor")]
  749. "tor" | "tor+tls" => {
  750. use std::str::FromStr;
  751. if tor_hscrypto::pk::HsId::from_str(host_str).is_err() {
  752. continue
  753. }
  754. trace!(target: "net::hosts::filter_addresses()",
  755. "[Tor] Valid: {}", host_str);
  756. }
  757. #[cfg(feature = "p2p-nym")]
  758. "nym" | "nym+tls" => continue, // <-- Temp skip
  759. #[cfg(feature = "p2p-tcp")]
  760. "tcp" | "tcp+tls" => {
  761. trace!(target: "net::hosts::filter_addresses()",
  762. "[TCP] Valid: {}", host_str);
  763. }
  764. _ => continue,
  765. }
  766. ret.push((addr_.clone(), *last_seen));
  767. }
  768. ret
  769. }
  770. /// Downgrade a host to greylist. If the host is on the anchorlist or whitelist, remove it.
  771. /// If it's already on the greylist we can't do anything here.
  772. pub async fn downgrade_host(&self, addr: &Url, last_seen: u64) {
  773. if let Err(_) = self.try_register(addr.clone(), HostState::Downgrading).await {
  774. return
  775. }
  776. debug!(target: "net::hosts::downgrade_host()", "Downgrading host {}", addr);
  777. if self.container.contains(HostColor::Grey as usize, addr).await {
  778. warn!(target: "net::hosts::downgrade_host()",
  779. "Cannot downgrade a host that is already on the greylist! {}", addr);
  780. }
  781. if self.container.contains(HostColor::Gold as usize, addr).await {
  782. debug!(target: "net::hosts::downgrade_host()", "Removing from anchorlist {}", addr);
  783. let index = self
  784. .container
  785. .get_index_at_addr(HostColor::Gold as usize, addr.clone())
  786. .await
  787. .expect("Expected anchorlist index to exist");
  788. self.container.remove(HostColor::Gold, addr, index).await;
  789. self.container.store_or_update(HostColor::Grey, &[(addr.clone(), last_seen)]).await;
  790. }
  791. if self.container.contains(HostColor::White as usize, addr).await {
  792. debug!(target: "net::hosts::downgrade_host()", "Removing from whitelist {}", addr);
  793. let index = self
  794. .container
  795. .get_index_at_addr(HostColor::White as usize, addr.clone())
  796. .await
  797. .expect("Expected whitelist index to exist");
  798. self.container.remove(HostColor::White, addr, index).await;
  799. self.container.store_or_update(HostColor::Grey, &[(addr.clone(), last_seen)]).await;
  800. }
  801. // Remove this entry from HostRegistry to avoid this host getting
  802. // stuck in the Downgrading state.
  803. self.unregister(&addr).await;
  804. }
  805. /// Quarantine a peer.
  806. /// If they've been quarantined for more than a configured limit, downgrade to greylist.
  807. pub async fn quarantine(&self, addr: &Url, last_seen: u64) {
  808. debug!(target: "net::hosts::quarantine()", "Quarantining peer {}", addr);
  809. let timer = Instant::now();
  810. let mut q = self.quarantine.write().await;
  811. if let Some(retries) = q.get_mut(addr) {
  812. *retries += 1;
  813. debug!(target: "net::hosts::quarantine()",
  814. "Peer {} quarantined {} times", addr, retries);
  815. if *retries == self.settings.hosts_quarantine_limit {
  816. debug!(target: "net::hosts::quarantine()",
  817. "Reached quarantine limited after {:?}", timer.elapsed());
  818. debug!(target: "net::hosts::quarantine()",
  819. "Removing from hostlist {}", addr);
  820. drop(q);
  821. self.downgrade_host(addr, last_seen).await;
  822. }
  823. } else {
  824. debug!(target: "net::hosts::quarantine()", "Added peer {} to quarantine", addr);
  825. q.insert(addr.clone(), 0);
  826. }
  827. }
  828. /// Mark a peer as blacklist.
  829. pub async fn blacklist(&self, peer: &Url) {
  830. // We ignore UNIX sockets here so we will just work
  831. // with stuff that has host_str().
  832. if let Some(_) = peer.host_str() {
  833. // Localhost connections should never enter the blacklist
  834. // This however allows any Tor and Nym connections.
  835. if self.is_local_host(peer.clone()).await {
  836. return
  837. }
  838. // Insert into the blacklist. We set last_seen to 0 (we don't care about this
  839. // field).
  840. self.container.hostlists[HostColor::Black as usize]
  841. .write()
  842. .await
  843. .push((peer.clone(), 0));
  844. }
  845. }
  846. }
  847. #[cfg(test)]
  848. mod tests {
  849. use super::{
  850. super::super::{settings::Settings, P2p},
  851. *,
  852. };
  853. use crate::{net::hosts::refinery::ping_node, system::sleep};
  854. use smol::Executor;
  855. #[test]
  856. fn test_ping_node() {
  857. smol::block_on(async {
  858. let settings = Settings {
  859. localnet: false,
  860. external_addrs: vec![
  861. Url::parse("tcp://foo.bar:123").unwrap(),
  862. Url::parse("tcp://lol.cat:321").unwrap(),
  863. ],
  864. ..Default::default()
  865. };
  866. let ex = Arc::new(Executor::new());
  867. let p2p = P2p::new(settings, ex.clone()).await;
  868. let url = Url::parse("tcp://xeno.systems.wtf").unwrap();
  869. println!("Pinging node...");
  870. let task = ex.spawn(ping_node(url.clone(), p2p));
  871. ex.run(task).await;
  872. println!("Ping node complete!");
  873. });
  874. }
  875. #[test]
  876. fn test_is_local_host() {
  877. smol::block_on(async {
  878. let settings = Settings {
  879. localnet: false,
  880. external_addrs: vec![
  881. Url::parse("tcp://foo.bar:123").unwrap(),
  882. Url::parse("tcp://lol.cat:321").unwrap(),
  883. ],
  884. ..Default::default()
  885. };
  886. let hosts = Hosts::new(Arc::new(settings.clone()));
  887. let local_hosts: Vec<Url> = vec![
  888. Url::parse("tcp://localhost").unwrap(),
  889. Url::parse("tcp://127.0.0.1").unwrap(),
  890. Url::parse("tcp+tls://[::1]").unwrap(),
  891. Url::parse("tcp://localhost.localdomain").unwrap(),
  892. Url::parse("tcp://192.168.10.65").unwrap(),
  893. ];
  894. for host in local_hosts {
  895. eprintln!("{}", host);
  896. assert!(hosts.is_local_host(host).await);
  897. }
  898. let remote_hosts: Vec<Url> = vec![
  899. Url::parse("https://dyne.org").unwrap(),
  900. Url::parse("tcp://77.168.10.65:2222").unwrap(),
  901. Url::parse("tcp://[2345:0425:2CA1:0000:0000:0567:5673:23b5]").unwrap(),
  902. Url::parse("http://eweiibe6tdjsdprb4px6rqrzzcsi22m4koia44kc5pcjr7nec2rlxyad.onion")
  903. .unwrap(),
  904. ];
  905. for host in remote_hosts {
  906. assert!(!hosts.is_local_host(host).await)
  907. }
  908. });
  909. }
  910. #[test]
  911. fn test_store() {
  912. let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
  913. smol::block_on(async {
  914. let settings = Settings { ..Default::default() };
  915. let hosts = Hosts::new(Arc::new(settings.clone()));
  916. let grey_hosts = vec![
  917. Url::parse("tcp://localhost:3921").unwrap(),
  918. Url::parse("tor://[::1]:21481").unwrap(),
  919. Url::parse("tcp://192.168.10.65:311").unwrap(),
  920. Url::parse("tcp+tls://0.0.0.0:2312").unwrap(),
  921. Url::parse("tcp://255.255.255.255:2131").unwrap(),
  922. ];
  923. for addr in &grey_hosts {
  924. hosts.container.store(HostColor::Grey as usize, addr.clone(), last_seen).await;
  925. }
  926. assert!(!hosts.container.is_empty(HostColor::Grey).await);
  927. let white_hosts = vec![
  928. Url::parse("tcp://localhost:3921").unwrap(),
  929. Url::parse("tor://[::1]:21481").unwrap(),
  930. Url::parse("tcp://192.168.10.65:311").unwrap(),
  931. Url::parse("tcp+tls://0.0.0.0:2312").unwrap(),
  932. Url::parse("tcp://255.255.255.255:2131").unwrap(),
  933. ];
  934. for host in &white_hosts {
  935. hosts.container.store(HostColor::White as usize, host.clone(), last_seen).await;
  936. }
  937. assert!(!hosts.container.is_empty(HostColor::White).await);
  938. let gold_hosts = vec![
  939. Url::parse("tcp://dark.fi:80").unwrap(),
  940. Url::parse("tcp://http.cat:401").unwrap(),
  941. Url::parse("tcp://foo.bar:111").unwrap(),
  942. ];
  943. for host in &gold_hosts {
  944. hosts.container.store(HostColor::Gold as usize, host.clone(), last_seen).await;
  945. }
  946. assert!(hosts.container.contains(HostColor::Grey as usize, &grey_hosts[0]).await);
  947. assert!(hosts.container.contains(HostColor::White as usize, &white_hosts[1]).await);
  948. assert!(hosts.container.contains(HostColor::Gold as usize, &gold_hosts[2]).await);
  949. });
  950. }
  951. #[test]
  952. fn test_get_last() {
  953. smol::block_on(async {
  954. let settings = Settings { ..Default::default() };
  955. let hosts = Hosts::new(Arc::new(settings.clone()));
  956. // Build up a hostlist
  957. for i in 0..10 {
  958. sleep(1).await;
  959. let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
  960. let url = Url::parse(&format!("tcp://whitelist{}:123", i)).unwrap();
  961. hosts.container.store(HostColor::White as usize, url.clone(), last_seen).await;
  962. }
  963. for (url, last_seen) in
  964. hosts.container.hostlists[HostColor::White as usize].read().await.iter()
  965. {
  966. println!("{} {}", url, last_seen);
  967. }
  968. let (entry, _position) = hosts.container.fetch_last(HostColor::White).await;
  969. println!("last entry: {} {}", entry.0, entry.1);
  970. });
  971. }
  972. #[test]
  973. fn test_get_entry() {
  974. smol::block_on(async {
  975. let settings = Settings { ..Default::default() };
  976. let hosts = Hosts::new(Arc::new(settings.clone()));
  977. let url = Url::parse("tcp://dark.renaissance:333").unwrap();
  978. let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
  979. hosts.container.store(HostColor::White as usize, url.clone(), last_seen).await;
  980. hosts.container.store(HostColor::Gold as usize, url.clone(), last_seen).await;
  981. assert!(hosts
  982. .container
  983. .get_entry_at_addr(HostColor::White as usize, &url)
  984. .await
  985. .is_some());
  986. assert!(hosts
  987. .container
  988. .get_entry_at_addr(HostColor::Gold as usize, &url)
  989. .await
  990. .is_some());
  991. });
  992. }
  993. #[test]
  994. fn test_remove() {
  995. smol::block_on(async {
  996. let settings = Settings { ..Default::default() };
  997. let hosts = Hosts::new(Arc::new(settings.clone()));
  998. let url = Url::parse("tcp://dark.renaissance:333").unwrap();
  999. let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
  1000. hosts.container.store(HostColor::White as usize, url.clone(), last_seen).await;
  1001. sleep(1).await;
  1002. let url = Url::parse("tcp://milady:333").unwrap();
  1003. let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
  1004. hosts.container.store(HostColor::White as usize, url.clone(), last_seen).await;
  1005. sleep(1).await;
  1006. let url = Url::parse("tcp://king-ted:333").unwrap();
  1007. let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
  1008. hosts.container.store(HostColor::White as usize, url.clone(), last_seen).await;
  1009. for (url, last_seen) in
  1010. hosts.container.hostlists[HostColor::White as usize].read().await.iter()
  1011. {
  1012. println!("{}, {}", url, last_seen);
  1013. }
  1014. let position = hosts
  1015. .container
  1016. .get_index_at_addr(HostColor::White as usize, url.clone())
  1017. .await
  1018. .unwrap();
  1019. hosts.container.remove(HostColor::White, &url, position).await;
  1020. for (url, last_seen) in
  1021. hosts.container.hostlists[HostColor::White as usize].read().await.iter()
  1022. {
  1023. println!("{}, {}", url, last_seen);
  1024. }
  1025. });
  1026. }
  1027. #[test]
  1028. fn test_fetch_address() {
  1029. smol::block_on(async {
  1030. let mut hostlist = vec![];
  1031. let mut grey_urls = vec![];
  1032. let mut white_urls = vec![];
  1033. let mut anchor_urls = vec![];
  1034. let ex = Arc::new(Executor::new());
  1035. let settings = Settings { ..Default::default() };
  1036. let p2p = P2p::new(settings, ex.clone()).await;
  1037. let hosts = &p2p.hosts().container;
  1038. // Build up a hostlist
  1039. for i in 0..5 {
  1040. let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
  1041. hosts
  1042. .store(
  1043. HostColor::Grey as usize,
  1044. Url::parse(&format!("tcp://greylist{}:123", i)).unwrap(),
  1045. last_seen,
  1046. )
  1047. .await;
  1048. hosts
  1049. .store(
  1050. HostColor::White as usize,
  1051. Url::parse(&format!("tcp://whitelist{}:123", i)).unwrap(),
  1052. last_seen,
  1053. )
  1054. .await;
  1055. hosts
  1056. .store(
  1057. HostColor::Gold as usize,
  1058. Url::parse(&format!("tcp://anchorlist{}:123", i)).unwrap(),
  1059. last_seen,
  1060. )
  1061. .await;
  1062. grey_urls
  1063. .push((Url::parse(&format!("tcp://greylist{}:123", i)).unwrap(), last_seen));
  1064. white_urls
  1065. .push((Url::parse(&format!("tcp://whitelist{}:123", i)).unwrap(), last_seen));
  1066. anchor_urls
  1067. .push((Url::parse(&format!("tcp://anchorlist{}:123", i)).unwrap(), last_seen));
  1068. }
  1069. assert!(!hosts.is_empty(HostColor::Grey).await);
  1070. assert!(!hosts.is_empty(HostColor::White).await);
  1071. assert!(!hosts.is_empty(HostColor::Gold).await);
  1072. let transports = &vec!["tcp".to_string()];
  1073. let white_count =
  1074. p2p.settings().outbound_connections * p2p.settings().white_connection_percent / 100;
  1075. let localnet = true;
  1076. // Simulate the address selection logic found in outbound_session::fetch_address()
  1077. for i in 0..8 {
  1078. if i < p2p.settings().anchor_connection_count {
  1079. if !hosts.fetch_address(HostColor::Gold, transports, localnet).await.is_empty()
  1080. {
  1081. let addrs =
  1082. hosts.fetch_address(HostColor::Gold, transports, localnet).await;
  1083. hostlist.push(addrs);
  1084. }
  1085. if !hosts.fetch_address(HostColor::White, transports, localnet).await.is_empty()
  1086. {
  1087. let addrs =
  1088. hosts.fetch_address(HostColor::White, transports, localnet).await;
  1089. hostlist.push(addrs);
  1090. }
  1091. if !hosts.fetch_address(HostColor::Grey, transports, localnet).await.is_empty()
  1092. {
  1093. let addrs =
  1094. hosts.fetch_address(HostColor::Grey, transports, localnet).await;
  1095. hostlist.push(addrs);
  1096. }
  1097. } else if i < white_count {
  1098. if !hosts.fetch_address(HostColor::White, transports, localnet).await.is_empty()
  1099. {
  1100. let addrs =
  1101. hosts.fetch_address(HostColor::White, transports, localnet).await;
  1102. hostlist.push(addrs);
  1103. }
  1104. if !hosts.fetch_address(HostColor::Grey, transports, localnet).await.is_empty()
  1105. {
  1106. let addrs =
  1107. hosts.fetch_address(HostColor::Grey, transports, localnet).await;
  1108. hostlist.push(addrs);
  1109. }
  1110. } else if !hosts
  1111. .fetch_address(HostColor::Grey, transports, localnet)
  1112. .await
  1113. .is_empty()
  1114. {
  1115. let addrs = hosts.fetch_address(HostColor::Grey, transports, localnet).await;
  1116. hostlist.push(addrs);
  1117. }
  1118. }
  1119. // Check we're returning the correct addresses.
  1120. anchor_urls.sort();
  1121. white_urls.sort();
  1122. grey_urls.sort();
  1123. hostlist[0].sort();
  1124. hostlist[4].sort();
  1125. hostlist[7].sort();
  1126. assert!(anchor_urls == hostlist[0]);
  1127. assert!(white_urls == hostlist[4]);
  1128. assert!(grey_urls == hostlist[7]);
  1129. })
  1130. }
  1131. }