1use rand::{prelude::IteratorRandom, rngs::OsRng, Rng};
58use smol::lock::RwLock as AsyncRwLock;
59use std::{
60 collections::HashMap,
61 fmt, fs,
62 fs::File,
63 net::{IpAddr, Ipv4Addr, Ipv6Addr},
64 sync::{
65 atomic::{AtomicBool, Ordering},
66 Arc, Mutex as SyncMutex, RwLock,
67 },
68 time::{Instant, UNIX_EPOCH},
69};
70use tracing::{debug, error, trace, warn};
71use url::{Host, Url};
72
73use super::{
74 session::{SESSION_REFINE, SESSION_SEED},
75 settings::Settings,
76 ChannelPtr,
77};
78use crate::{
79 system::{Publisher, PublisherPtr, Subscription},
80 util::{
81 file::{load_file, save_file},
82 logger::verbose,
83 most_frequent_or_any,
84 path::expand_path,
85 ringbuffer::RingBuffer,
86 },
87 Error, Result,
88};
89
90pub const LOCAL_HOST_STRS: [&str; 2] = ["localhost", "localhost.localdomain"];
93const WHITELIST_MAX_LEN: usize = 5000;
94const GREYLIST_MAX_LEN: usize = 2000;
95const DARKLIST_MAX_LEN: usize = 1000;
96
97pub type HostsPtr = Arc<Hosts>;
99
100pub(in crate::net) type HostRegistry = SyncMutex<HashMap<Url, HostState>>;
104
105#[derive(Clone, Debug)]
147pub(in crate::net) enum HostState {
148 Insert,
150 Refine,
153 Connect,
155 Suspend,
164 Connected(ChannelPtr),
166
167 Move,
170
171 Free(u64),
173}
174
175impl HostState {
176 fn try_insert(&self) -> Result<Self> {
180 let start = self.to_string();
181 let end = HostState::Insert.to_string();
182 match self {
183 HostState::Insert => Err(Error::HostStateBlocked(start, end)),
184 HostState::Refine => Err(Error::HostStateBlocked(start, end)),
185 HostState::Connect => Err(Error::HostStateBlocked(start, end)),
186 HostState::Suspend => Err(Error::HostStateBlocked(start, end)),
187 HostState::Connected(_) => Err(Error::HostStateBlocked(start, end)),
188 HostState::Move => Err(Error::HostStateBlocked(start, end)),
189 HostState::Free(_) => Ok(HostState::Insert),
190 }
191 }
192
193 fn try_refine(&self) -> Result<Self> {
196 let start = self.to_string();
197 let end = HostState::Refine.to_string();
198 match self {
199 HostState::Insert => Err(Error::HostStateBlocked(start, end)),
200 HostState::Refine => Err(Error::HostStateBlocked(start, end)),
201 HostState::Connect => Err(Error::HostStateBlocked(start, end)),
202 HostState::Suspend => Ok(HostState::Refine),
203 HostState::Connected(_) => Err(Error::HostStateBlocked(start, end)),
204 HostState::Move => Err(Error::HostStateBlocked(start, end)),
205 HostState::Free(_) => Ok(HostState::Refine),
206 }
207 }
208
209 fn try_connect(&self) -> Result<Self> {
212 let start = self.to_string();
213 let end = HostState::Connect.to_string();
214 match self {
215 HostState::Insert => Err(Error::HostStateBlocked(start, end)),
216 HostState::Refine => Err(Error::HostStateBlocked(start, end)),
217 HostState::Connect => Err(Error::HostStateBlocked(start, end)),
218 HostState::Suspend => Err(Error::HostStateBlocked(start, end)),
219 HostState::Connected(_) => Err(Error::HostStateBlocked(start, end)),
220 HostState::Move => Err(Error::HostStateBlocked(start, end)),
221 HostState::Free(_) => Ok(HostState::Connect),
222 }
223 }
224
225 fn try_connected(&self, channel: ChannelPtr) -> Result<Self> {
232 let start = self.to_string();
233 let end = HostState::Connected(channel.clone()).to_string();
234 match self {
235 HostState::Insert => Err(Error::HostStateBlocked(start, end)),
236 HostState::Refine => Ok(HostState::Connected(channel)),
237 HostState::Connect => Ok(HostState::Connected(channel)),
238 HostState::Suspend => Err(Error::HostStateBlocked(start, end)),
239 HostState::Connected(_) => Err(Error::HostStateBlocked(start, end)),
240 HostState::Move => Ok(HostState::Connected(channel)),
241 HostState::Free(_) => Ok(HostState::Connected(channel)),
242 }
243 }
244
245 fn try_move(&self) -> Result<Self> {
251 let start = self.to_string();
252 let end = HostState::Move.to_string();
253 match self {
254 HostState::Insert => Err(Error::HostStateBlocked(start, end)),
255 HostState::Refine => Ok(HostState::Move),
256 HostState::Connect => Ok(HostState::Move),
257 HostState::Suspend => Err(Error::HostStateBlocked(start, end)),
258 HostState::Connected(_) => Ok(HostState::Move),
259 HostState::Move => Err(Error::HostStateBlocked(start, end)),
260 HostState::Free(_) => Ok(HostState::Move),
261 }
262 }
263
264 fn try_suspend(&self) -> Result<Self> {
269 let start = self.to_string();
270 let end = HostState::Suspend.to_string();
271 match self {
272 HostState::Insert => Err(Error::HostStateBlocked(start, end)),
273 HostState::Refine => Err(Error::HostStateBlocked(start, end)),
274 HostState::Connect => Err(Error::HostStateBlocked(start, end)),
275 HostState::Suspend => Err(Error::HostStateBlocked(start, end)),
276 HostState::Connected(_) => Err(Error::HostStateBlocked(start, end)),
277 HostState::Move => Ok(HostState::Suspend),
278 HostState::Free(_) => Err(Error::HostStateBlocked(start, end)),
279 }
280 }
281
282 fn try_free(&self, age: u64) -> Result<Self> {
287 match self {
288 HostState::Insert => Ok(HostState::Free(age)),
289 HostState::Refine => Ok(HostState::Free(age)),
290 HostState::Connect => Ok(HostState::Free(age)),
291 HostState::Suspend => Ok(HostState::Free(age)),
292 HostState::Connected(_) => Ok(HostState::Free(age)),
293 HostState::Move => Ok(HostState::Free(age)),
294 HostState::Free(age) => Ok(HostState::Free(*age)),
295 }
296 }
297}
298
299impl fmt::Display for HostState {
300 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
301 fmt::Debug::fmt(self, f)
302 }
303}
304
305#[repr(u8)]
306#[derive(Clone, Debug)]
307pub enum HostColor {
308 Grey = 0,
311 White = 1,
313 Gold = 2,
316 Black = 3,
319 Dark = 4,
324}
325
326impl TryFrom<usize> for HostColor {
327 type Error = Error;
328
329 fn try_from(value: usize) -> Result<Self> {
330 match value {
331 0 => Ok(HostColor::Grey),
332 1 => Ok(HostColor::White),
333 2 => Ok(HostColor::Gold),
334 3 => Ok(HostColor::Black),
335 4 => Ok(HostColor::Dark),
336 _ => Err(Error::InvalidHostColor),
337 }
338 }
339}
340
341pub struct HostContainer {
345 pub(in crate::net) hostlists: [RwLock<Vec<(Url, u64)>>; 5],
346}
347
348impl HostContainer {
349 fn new() -> Self {
350 let hostlists: [RwLock<Vec<(Url, u64)>>; 5] = [
351 RwLock::new(Vec::new()),
352 RwLock::new(Vec::new()),
353 RwLock::new(Vec::new()),
354 RwLock::new(Vec::new()),
355 RwLock::new(Vec::new()),
356 ];
357
358 Self { hostlists }
359 }
360
361 fn store(&self, color: usize, addr: Url, last_seen: u64) {
363 trace!(target: "net::hosts::store", "[START] list={:?}",
364 HostColor::try_from(color).unwrap());
365
366 let mut list = self.hostlists[color].write().unwrap();
367 list.push((addr.clone(), last_seen));
368 debug!(target: "net::hosts::store", "Added [{addr}] to {:?} list",
369 HostColor::try_from(color).unwrap());
370
371 trace!(target: "net::hosts::store", "[END] list={:?}",
372 HostColor::try_from(color).unwrap());
373 }
374
375 fn store_or_update(&self, color: HostColor, addr: Url, last_seen: u64) {
378 trace!(target: "net::hosts::store_or_update", "[START]");
379 let color_code = color.clone() as usize;
380 let mut list = self.hostlists[color_code].write().unwrap();
381 if let Some(entry) = list.iter_mut().find(|(u, _)| *u == addr) {
382 entry.1 = last_seen;
383 debug!(target: "net::hosts::store_or_update", "Updated [{addr}] entry on {:?} list",
384 color.clone());
385 } else {
386 list.push((addr.clone(), last_seen));
387 debug!(target: "net::hosts::store_or_update", "Added [{addr}] to {color:?} list");
388 }
389 trace!(target: "net::hosts::store_or_update", "[STOP]");
390 }
391
392 pub fn update_last_seen(&self, color: usize, addr: Url, last_seen: u64) {
394 trace!(target: "net::hosts::update_last_seen", "[START] list={:?}",
395 HostColor::try_from(color).unwrap());
396
397 let mut list = self.hostlists[color].write().unwrap();
398 if let Some(entry) = list.iter_mut().find(|(u, _)| *u == addr) {
399 entry.1 = last_seen;
400 }
401 trace!(target: "net::hosts::update_last_seen", "[END] list={:?}",
402 HostColor::try_from(color).unwrap());
403 }
404
405 pub fn fetch_all(&self, color: HostColor) -> Vec<(Url, u64)> {
407 self.hostlists[color as usize].read().unwrap().iter().cloned().collect()
408 }
409
410 pub fn fetch_last(&self, color: HostColor) -> Option<(Url, u64)> {
412 let list = self.hostlists[color as usize].read().unwrap();
413 list.last().cloned()
414 }
415
416 pub(in crate::net) fn fetch_with_schemes(
419 &self,
420 color: usize,
421 schemes: &[String],
422 limit: Option<usize>,
423 ) -> Vec<(Url, u64)> {
424 trace!(target: "net::hosts::fetch_with_schemes", "[START] {:?}",
425 HostColor::try_from(color).unwrap());
426
427 let list = self.hostlists[color].read().unwrap();
428
429 let mut limit = match limit {
430 Some(l) => l.min(list.len()),
431 None => list.len(),
432 };
433 let mut ret = vec![];
434
435 if limit == 0 {
436 return ret
437 }
438
439 for (addr, last_seen) in list.iter() {
440 if schemes.contains(&addr.scheme().to_string()) {
441 ret.push((addr.clone(), *last_seen));
442 limit -= 1;
443 if limit == 0 {
444 debug!(target: "net::hosts::fetch_with_schemes",
445 "Found matching addr on list={:?}, returning {} addresses",
446 HostColor::try_from(color).unwrap(), ret.len());
447 return ret
448 }
449 }
450 }
451
452 if ret.is_empty() {
453 debug!(target: "net::hosts::fetch_with_schemes",
454 "No matching schemes found on list={:?}!", HostColor::try_from(color).unwrap())
455 }
456
457 ret
458 }
459
460 fn fetch_excluding_schemes(
464 &self,
465 color: usize,
466 schemes: &[String],
467 limit: Option<usize>,
468 ) -> Vec<(Url, u64)> {
469 trace!(target: "net::hosts::fetch_with_schemes", "[START] {:?}",
470 HostColor::try_from(color).unwrap());
471
472 let list = self.hostlists[color].read().unwrap();
473
474 let mut limit = match limit {
475 Some(l) => l.min(list.len()),
476 None => list.len(),
477 };
478 let mut ret = vec![];
479
480 if limit == 0 {
481 return ret
482 }
483
484 for (addr, last_seen) in list.iter() {
485 if !schemes.contains(&addr.scheme().to_string()) {
486 ret.push((addr.clone(), *last_seen));
487 limit -= 1;
488 if limit == 0 {
489 return ret
490 }
491 }
492 }
493
494 if ret.is_empty() {
495 debug!(target: "net::hosts::fetch_excluding_schemes", "No such schemes found!");
496 }
497
498 ret
499 }
500
501 pub(in crate::net) fn fetch_random_with_schemes(
504 &self,
505 color: HostColor,
506 schemes: &[String],
507 ) -> Option<((Url, u64), usize)> {
508 trace!(target: "net::hosts::fetch_random_with_schemes", "[START] {color:?}");
510 let list = self.fetch_with_schemes(color as usize, schemes, None);
511
512 if list.is_empty() {
513 return None
514 }
515
516 let position = rand::thread_rng().gen_range(0..list.len());
517 let entry = &list[position];
518 Some((entry.clone(), position))
519 }
520
521 pub(in crate::net) fn fetch_n_random(&self, color: HostColor, n: u32) -> Vec<(Url, u64)> {
523 trace!(target: "net::hosts::fetch_n_random", "[START] {color:?}");
524 let n = n as usize;
525 if n == 0 {
526 return vec![]
527 }
528 let mut hosts = vec![];
529
530 let list = self.hostlists[color as usize].read().unwrap();
531
532 for (addr, last_seen) in list.iter() {
533 hosts.push((addr.clone(), *last_seen));
534 }
535
536 if hosts.is_empty() {
537 debug!(target: "net::hosts::fetch_n_random", "No entries found!");
538 return hosts
539 }
540
541 let urls = hosts.iter().choose_multiple(&mut OsRng, n.min(hosts.len()));
543 urls.iter().map(|&url| url.clone()).collect()
544 }
545
546 pub(in crate::net) fn fetch_n_random_with_schemes(
548 &self,
549 color: HostColor,
550 schemes: &[String],
551 n: u32,
552 ) -> Vec<(Url, u64)> {
553 trace!(target: "net::hosts::fetch_n_random_with_schemes", "[START] {color:?}");
554 let index = color as usize;
555 let n = n as usize;
556 if n == 0 {
557 return vec![]
558 }
559
560 let hosts = self.fetch_with_schemes(index, schemes, None);
562 if hosts.is_empty() {
563 debug!(target: "net::hosts::fetch_n_random_with_schemes",
564 "No such schemes found!");
565 return hosts
566 }
567
568 let urls = hosts.iter().choose_multiple(&mut OsRng, n.min(hosts.len()));
570 urls.iter().map(|&url| url.clone()).collect()
571 }
572
573 pub(in crate::net) fn fetch_n_random_excluding_schemes(
576 &self,
577 color: HostColor,
578 schemes: &[String],
579 n: u32,
580 ) -> Vec<(Url, u64)> {
581 trace!(target: "net::hosts::fetch_excluding_schemes", "[START] {color:?}");
582 let index = color as usize;
583 let n = n as usize;
584 if n == 0 {
585 return vec![]
586 }
587 let hosts = self.fetch_excluding_schemes(index, schemes, None);
589
590 if hosts.is_empty() {
591 debug!(target: "net::hosts::fetch_n_random_excluding_schemes",
592 "No such schemes found!");
593 return hosts
594 }
595
596 let urls = hosts.iter().choose_multiple(&mut OsRng, n.min(hosts.len()));
598 urls.iter().map(|&url| url.clone()).collect()
599 }
600
601 pub fn remove_if_exists(&self, color: HostColor, addr: &Url) {
603 let color_code = color.clone() as usize;
604 let mut list = self.hostlists[color_code].write().unwrap();
605 if let Some(position) = list.iter().position(|(u, _)| u == addr) {
606 debug!(target: "net::hosts::remove_if_exists", "Removing addr={addr} list={color:?}");
607 list.remove(position);
608 }
609 }
610
611 pub fn is_empty(&self, color: HostColor) -> bool {
613 self.hostlists[color as usize].read().unwrap().is_empty()
614 }
615
616 pub fn contains(&self, color: usize, addr: &Url) -> bool {
618 self.hostlists[color].read().unwrap().iter().any(|(u, _t)| u == addr)
619 }
620
621 pub fn get_index_at_addr(&self, color: usize, addr: Url) -> Option<usize> {
623 self.hostlists[color].read().unwrap().iter().position(|a| a.0 == addr)
624 }
625
626 pub fn get_last_seen(&self, color: usize, addr: &Url) -> Option<u64> {
628 self.hostlists[color]
629 .read()
630 .unwrap()
631 .iter()
632 .find(|(url, _)| url == addr)
633 .map(|(_, last_seen)| *last_seen)
634 }
635
636 fn sort_by_last_seen(&self, color: usize) {
638 let mut list = self.hostlists[color].write().unwrap();
639 list.sort_by_key(|entry| entry.1);
640 list.reverse();
641 }
642
643 fn resize(&self, color: HostColor) {
645 let list = self.hostlists[color.clone() as usize].read().unwrap();
646 let size = list.len();
647
648 drop(list);
650
651 match color {
652 HostColor::Grey | HostColor::White | HostColor::Dark => {
653 let max_size = match color {
654 HostColor::Grey => GREYLIST_MAX_LEN,
655 HostColor::White => WHITELIST_MAX_LEN,
656 HostColor::Dark => DARKLIST_MAX_LEN,
657 _ => {
658 unreachable!()
659 }
660 };
661 if size == max_size {
662 let mut list = self.hostlists[color.clone() as usize].write().unwrap();
663 let last_entry = list.pop().unwrap();
664
665 debug!(
666 target: "net::hosts::resize",
667 "{color:?}list reached max size. Removed {last_entry:?}"
668 );
669 }
670 }
671 HostColor::Gold | HostColor::Black => (),
673 }
674 }
675
676 fn refresh(&self, color: HostColor, max_age: u64) {
679 let now = UNIX_EPOCH.elapsed().unwrap().as_secs();
680 let mut old_items = vec![];
681
682 let darklist = self.fetch_all(HostColor::Dark);
683 for (addr, last_seen) in darklist {
684 if now < last_seen {
690 debug!(target: "net::hosts::refresh",
691 "last_seen [{now}] is newer than current system time [{last_seen}]. Skipping");
692 continue
693 }
694 if (now - last_seen) > max_age {
695 old_items.push(addr);
696 }
697 }
698
699 for item in old_items {
700 debug!(target: "net::hosts::refresh", "Removing {item:?}");
701 self.remove_if_exists(color.clone(), &item);
702 }
703 }
704
705 pub(in crate::net) fn load_all(&self, path: &str) -> Result<()> {
707 let path = expand_path(path)?;
708
709 if !path.exists() {
710 if let Some(parent) = path.parent() {
711 fs::create_dir_all(parent)?;
712 }
713
714 File::create(path.clone())?;
715 }
716
717 let contents = load_file(&path);
718 if let Err(e) = contents {
719 warn!(target: "net::hosts::load_hosts", "Failed retrieving saved hosts: {e}");
720 return Ok(())
721 }
722
723 for line in contents.unwrap().lines() {
724 let data: Vec<&str> = line.split('\t').collect();
725
726 let url = match Url::parse(data[1]) {
727 Ok(u) => u,
728 Err(e) => {
729 debug!(target: "net::hosts::load_hosts", "Skipping malformed URL {e}");
730 continue
731 }
732 };
733
734 let last_seen = match data[2].parse::<u64>() {
735 Ok(t) => t,
736 Err(e) => {
737 debug!(target: "net::hosts::load_hosts", "Skipping malformed last seen {e}");
738 continue
739 }
740 };
741
742 match data[0] {
743 "gold" => {
744 self.store(HostColor::Gold as usize, url, last_seen);
745 self.sort_by_last_seen(HostColor::Gold as usize);
746 }
747 "white" => {
748 self.store(HostColor::White as usize, url, last_seen);
749 self.sort_by_last_seen(HostColor::White as usize);
750 self.resize(HostColor::White);
751 }
752 "grey" => {
753 self.store(HostColor::Grey as usize, url, last_seen);
754 self.sort_by_last_seen(HostColor::Grey as usize);
755 self.resize(HostColor::Grey);
756 }
757 "dark" => {
758 self.store(HostColor::Dark as usize, url, last_seen);
759 self.sort_by_last_seen(HostColor::Dark as usize);
760 self.resize(HostColor::Dark);
761
762 let day = 86400;
764 self.refresh(HostColor::Dark, day);
765 }
766 _ => {
767 debug!(target: "net::hosts::load_hosts", "Malformed list name...");
768 }
769 }
770 }
771
772 Ok(())
773 }
774
775 pub(in crate::net) fn save_all(&self, path: &str) -> Result<()> {
777 let path = expand_path(path)?;
778
779 let mut tsv = String::new();
780 let mut hostlist: HashMap<String, Vec<(Url, u64)>> = HashMap::new();
781
782 hostlist.insert("dark".to_string(), self.fetch_all(HostColor::Dark));
783 hostlist.insert("grey".to_string(), self.fetch_all(HostColor::Grey));
784 hostlist.insert("white".to_string(), self.fetch_all(HostColor::White));
785 hostlist.insert("gold".to_string(), self.fetch_all(HostColor::Gold));
786
787 for (name, list) in hostlist {
788 for (url, last_seen) in list {
789 tsv.push_str(&format!("{name}\t{url}\t{last_seen}\n"));
790 }
791 }
792
793 if !tsv.is_empty() {
794 verbose!(target: "net::hosts::save_hosts", "Saving hosts to: {path:?}");
795 if let Err(e) = save_file(&path, &tsv) {
796 error!(target: "net::hosts::save_hosts", "Failed saving hosts: {e}");
797 }
798 }
799
800 Ok(())
801 }
802
803 pub(in crate::net) fn mix_host(
809 addr: &Url,
810 transports: &[String],
811 mixed_transports: &[String],
812 tor_socks5_proxy: &Option<Url>,
813 nym_socks5_proxy: &Option<Url>,
814 ) -> Vec<Url> {
815 let mut hosts = vec![];
816
817 if !mixed_transports.contains(&addr.scheme().to_string()) {
818 return hosts;
819 }
820
821 macro_rules! mix_transport {
822 ($a:expr, $b:expr) => {
823 if transports.contains(&$a.to_string()) && addr.scheme() == $b {
824 let mut url = addr.clone();
825 url.set_scheme($a).unwrap();
826 hosts.push(url);
827 }
828 };
829 }
830 macro_rules! mix_socks5_transport {
831 ($a:expr, $b:expr, $proxies:expr) => {
832 if transports.contains(&$a.to_string()) && addr.scheme() == $b {
833 for proxy in $proxies {
834 if let Some(mut endpoint) = proxy {
835 endpoint.set_path(&format!(
836 "{}:{}",
837 addr.host().unwrap(),
838 addr.port().unwrap()
839 ));
840 endpoint.set_scheme($a).unwrap();
841 hosts.push(endpoint);
842 }
843 }
844 }
845 };
846 }
847
848 mix_transport!("tor", "tcp");
849 mix_transport!("tor+tls", "tcp+tls");
850 mix_transport!("nym", "tcp");
851 mix_transport!("nym+tls", "tcp+tls");
852 mix_socks5_transport!(
853 "socks5",
854 "tcp",
855 [tor_socks5_proxy.clone(), nym_socks5_proxy.clone()]
856 );
857 mix_socks5_transport!(
858 "socks5+tls",
859 "tcp+tls",
860 [tor_socks5_proxy.clone(), nym_socks5_proxy.clone()]
861 );
862 mix_socks5_transport!("socks5", "tor", [tor_socks5_proxy.clone()]);
863 mix_socks5_transport!("socks5+tls", "tor+tls", [tor_socks5_proxy.clone()]);
864
865 hosts
866 }
867}
868
869pub struct Hosts {
878 registry: HostRegistry,
880
881 pub container: HostContainer,
883
884 store_publisher: PublisherPtr<usize>,
886
887 pub(in crate::net) channel_publisher: PublisherPtr<Result<ChannelPtr>>,
889
890 pub(in crate::net) disconnect_publisher: PublisherPtr<Error>,
892
893 pub(in crate::net) last_connection: SyncMutex<Instant>,
895
896 pub(in crate::net) ipv6_available: AtomicBool,
898
899 auto_self_addrs: SyncMutex<RingBuffer<Ipv6Addr, 20>>,
901
902 settings: Arc<AsyncRwLock<Settings>>,
904}
905
906impl Hosts {
907 pub(in crate::net) fn new(settings: Arc<AsyncRwLock<Settings>>) -> HostsPtr {
909 Arc::new(Self {
910 registry: SyncMutex::new(HashMap::new()),
911 container: HostContainer::new(),
912 store_publisher: Publisher::new(),
913 channel_publisher: Publisher::new(),
914 disconnect_publisher: Publisher::new(),
915 last_connection: SyncMutex::new(Instant::now()),
916 ipv6_available: AtomicBool::new(true),
917 auto_self_addrs: SyncMutex::new(RingBuffer::new()),
918 settings,
919 })
920 }
921
922 pub(in crate::net) async fn insert(&self, color: HostColor, addrs: &[(Url, u64)]) {
925 trace!(target: "net::hosts:insert", "[START]");
926
927 let filtered_addrs = self.filter_addresses(addrs).await;
931 let mut addrs_len = 0;
932
933 if filtered_addrs.is_empty() {
934 debug!(target: "net::hosts::insert", "Filtered out all addresses");
935 }
936
937 for (i, (addr, last_seen)) in filtered_addrs.iter().enumerate() {
939 if let Err(e) = self.try_register(addr.clone(), HostState::Insert) {
940 debug!(target: "net::hosts::insert", "Cannot insert addr={}, err={e}",
941 addr.clone());
942
943 continue
944 }
945
946 addrs_len += i + 1;
947
948 self.container.store_or_update(color.clone(), addr.clone(), *last_seen);
949 self.container.sort_by_last_seen(color.clone() as usize);
950 self.container.resize(color.clone());
951
952 if let Err(e) = self.unregister(addr) {
953 warn!(target: "net::hosts::insert", "Error while unregistering addr={addr}, err={e}");
954 }
955 }
956
957 self.store_publisher.notify(addrs_len).await;
958 trace!(target: "net::hosts:insert", "[END]");
959 }
960
961 pub fn refinable(&self, addr: Url) -> bool {
964 self.try_register(addr.clone(), HostState::Refine).is_ok()
965 }
966
967 pub(in crate::net) fn try_register(
970 &self,
971 addr: Url,
972 new_state: HostState,
973 ) -> Result<HostState> {
974 let mut registry = self.registry.lock().unwrap();
975
976 trace!(target: "net::hosts::try_update_registry", "Try register addr={addr}, state={}",
977 &new_state);
978
979 if registry.contains_key(&addr) {
980 let current_state = registry.get(&addr).unwrap().clone();
981
982 let result: Result<HostState> = match new_state {
983 HostState::Insert => current_state.try_insert(),
984 HostState::Refine => current_state.try_refine(),
985 HostState::Connect => current_state.try_connect(),
986 HostState::Suspend => current_state.try_suspend(),
987 HostState::Connected(c) => current_state.try_connected(c),
988 HostState::Move => current_state.try_move(),
989 HostState::Free(a) => current_state.try_free(a),
990 };
991
992 if let Ok(state) = &result {
993 registry.insert(addr.clone(), state.clone());
994 }
995
996 trace!(target: "net::hosts::try_update_registry", "Returning result {result:?}");
997
998 result
999 } else {
1000 debug!(target: "net::hosts::try_update_registry", "Inserting addr={addr}, state={}",
1002 &new_state);
1003
1004 registry.insert(addr.clone(), new_state.clone());
1005
1006 Ok(new_state)
1007 }
1008 }
1009
1010 pub(in crate::net) async fn check_addrs(&self, hosts: Vec<(Url, u64)>) -> Option<(Url, u64)> {
1013 trace!(target: "net::hosts::check_addrs", "[START]");
1014
1015 let seeds = self.settings.read().await.seeds.clone();
1016 let external_addrs = self.external_addrs().await;
1017
1018 for (host, last_seen) in hosts {
1019 if seeds.contains(&host) {
1023 warn!(
1024 target: "net::hosts::check_addrs",
1025 "Seed addr={} has entered the hostlist! Skipping", host.clone(),
1026 );
1027 continue
1028 }
1029
1030 if external_addrs.contains(&host) {
1031 warn!(
1032 target: "net::hosts::check_addrs",
1033 "External addr={} has entered the hostlist! Skipping", host.clone(),
1034 );
1035 continue
1036 }
1037
1038 if let Err(e) = self.try_register(host.clone(), HostState::Connect) {
1039 trace!(
1040 target: "net::hosts::check_addrs",
1041 "Skipping addr={}, err={e}", host.clone(),
1042 );
1043 continue
1044 }
1045
1046 debug!(target: "net::hosts::check_addrs", "Found valid host {host}");
1047 return Some((host.clone(), last_seen))
1048 }
1049
1050 None
1051 }
1052
1053 pub(in crate::net) fn unregister(&self, addr: &Url) -> Result<()> {
1055 let age = UNIX_EPOCH.elapsed().unwrap().as_secs();
1056 self.try_register(addr.clone(), HostState::Free(age))?;
1057 debug!(target: "net::hosts::unregister", "Unregistered: {}", &addr);
1058 Ok(())
1059 }
1060
1061 pub fn channels(&self) -> Vec<ChannelPtr> {
1064 let registry = self.registry.lock().unwrap();
1065 let mut channels = Vec::new();
1066
1067 for (_, state) in registry.iter() {
1068 if let HostState::Connected(c) = state {
1069 channels.push(c.clone());
1070 }
1071 }
1072 channels
1073 }
1074
1075 pub fn get_channel(&self, id: u32) -> Option<ChannelPtr> {
1077 let mut channel = None;
1078
1079 let channels = self.channels();
1080 for c in channels {
1081 if c.info.id == id {
1082 channel = Some(c.clone());
1083 break
1084 }
1085 }
1086
1087 channel
1088 }
1089
1090 pub fn peers(&self) -> Vec<ChannelPtr> {
1093 let registry = self.registry.lock().unwrap();
1094 let mut channels = Vec::new();
1095
1096 for (_, state) in registry.iter() {
1097 if let HostState::Connected(c) = state {
1098 if c.session_type_id() & (SESSION_SEED | SESSION_REFINE) != 0 {
1100 continue
1101 }
1102 channels.push(c.clone());
1103 }
1104 }
1105 channels
1106 }
1107
1108 pub(in crate::net) fn suspended(&self) -> Vec<Url> {
1110 let registry = self.registry.lock().unwrap();
1111 let mut addrs = Vec::new();
1112
1113 for (url, state) in registry.iter() {
1114 if let HostState::Suspend = state {
1115 addrs.push(url.clone());
1116 }
1117 }
1118 addrs
1119 }
1120
1121 pub fn random_channel(&self) -> ChannelPtr {
1123 let channels = self.channels();
1124 let position = rand::thread_rng().gen_range(0..channels.len());
1125 channels[position].clone()
1126 }
1127
1128 pub(in crate::net) async fn register_channel(&self, channel: ChannelPtr) {
1130 let address = channel.address().clone();
1131
1132 if channel.p2p().settings().read().await.inbound_addrs.contains(&address) {
1134 return
1135 }
1136
1137 if let Err(e) = self.try_register(address, HostState::Connected(channel.clone())) {
1141 warn!(target: "net::hosts::register_channel", "Error while registering channel {channel:?}: {e:?}");
1142 return
1143 }
1144
1145 self.channel_publisher.notify(Ok(channel.clone())).await;
1147
1148 let mut last_online = self.last_connection.lock().unwrap();
1149 *last_online = Instant::now();
1150 }
1151
1152 pub async fn subscribe_store(&self) -> Subscription<usize> {
1154 self.store_publisher.clone().subscribe().await
1155 }
1156
1157 pub async fn subscribe_channel(&self) -> Subscription<Result<ChannelPtr>> {
1159 self.channel_publisher.clone().subscribe().await
1160 }
1161
1162 pub async fn subscribe_disconnect(&self) -> Subscription<Error> {
1164 self.disconnect_publisher.clone().subscribe().await
1165 }
1166
1167 pub fn is_local_host(&self, url: &Url) -> bool {
1174 if url.host_str().is_none() {
1176 return false
1177 }
1178
1179 match url.host().unwrap() {
1181 url::Host::Ipv4(ip) => {
1182 if !ip.unstable_is_global() {
1183 return true
1184 }
1185 }
1186 url::Host::Ipv6(ip) => {
1187 if !ip.unstable_is_global() {
1188 return true
1189 }
1190 }
1191 url::Host::Domain(d) => {
1192 if LOCAL_HOST_STRS.contains(&d) {
1193 return true
1194 }
1195 }
1196 }
1197 false
1198 }
1199
1200 pub fn is_ipv6(&self, url: &Url) -> bool {
1202 if url.host_str().is_none() {
1204 return false
1205 }
1206
1207 if let url::Host::Ipv6(_) = url.host().unwrap() {
1208 return true
1209 }
1210 false
1211 }
1212
1213 pub(in crate::net) async fn import_blacklist(&self) -> Result<()> {
1215 for (hostname, schemes, ports) in self.settings.read().await.blacklist.clone() {
1216 let schemes = if schemes.is_empty() { vec!["tcp+tls".to_string()] } else { schemes };
1218
1219 let ports = if ports.is_empty() { vec![0] } else { ports };
1221
1222 for scheme in schemes {
1223 for &port in &ports {
1224 let url_string = if port == 0 {
1225 format!("{scheme}://{hostname}")
1226 } else {
1227 format!("{scheme}://{hostname}:{port}")
1228 };
1229
1230 if let Ok(url) = Url::parse(&url_string) {
1231 self.container.store(HostColor::Black as usize, url, 0);
1232 }
1233 }
1234 }
1235 }
1236 Ok(())
1237 }
1238
1239 pub(in crate::net) fn block_all_ports(&self, url: &Url) -> bool {
1244 let host = url.host();
1245 if host.is_none() {
1246 return false
1248 }
1249
1250 let host = host.unwrap();
1251 self.container.hostlists[HostColor::Black as usize]
1252 .read()
1253 .unwrap()
1254 .iter()
1255 .any(|(u, _t)| u.host().unwrap() == host && u.port().is_none())
1256 }
1257
1258 async fn filter_addresses(&self, addrs: &[(Url, u64)]) -> Vec<(Url, u64)> {
1261 debug!(target: "net::hosts::filter_addresses", "Filtering addrs: {addrs:?}");
1262 let mut ret = vec![];
1263
1264 let settings = self.settings.read().await;
1266
1267 'addr_loop: for (addr_, last_seen) in addrs {
1268 if addr_.host_str().is_none() || addr_.port().is_none() || addr_.cannot_be_a_base() {
1270 debug!(
1271 target: "net::hosts::filter_addresses",
1272 "[{addr_}] has invalid addr format. Skipping"
1273 );
1274 continue
1275 }
1276
1277 if settings.seeds.contains(addr_) {
1279 debug!(
1280 target: "net::hosts::filter_addresses",
1281 "[{addr_}] is a configured seed. Skipping"
1282 );
1283 continue
1284 }
1285
1286 if settings.peers.contains(addr_) {
1288 debug!(
1289 target: "net::hosts::filter_addresses",
1290 "[{addr_}] is a configured peer. Skipping"
1291 );
1292 continue
1293 }
1294
1295 if self.container.contains(HostColor::Black as usize, addr_) ||
1297 self.block_all_ports(addr_)
1298 {
1299 debug!(
1300 target: "net::hosts::filter_addresses",
1301 "[{addr_}] is blacklisted"
1302 );
1303 continue
1304 }
1305
1306 let host = addr_.host().unwrap();
1307 let host_str = addr_.host_str().unwrap();
1308
1309 if !settings.localnet {
1310 for ext in self.external_addrs().await {
1312 if host == ext.host().unwrap() {
1313 debug!(
1314 target: "net::hosts::filter_addresses",
1315 "[{addr_}] is our own external addr. Skipping"
1316 );
1317 continue 'addr_loop
1318 }
1319 }
1320 } else {
1321 for ext in &settings.external_addrs {
1323 if addr_.port() == ext.port() {
1324 debug!(
1325 target: "net::hosts::filter_addresses",
1326 "[{addr_}] is our own localnet port. Skipping"
1327 );
1328 continue 'addr_loop
1329 }
1330 }
1331 }
1332
1333 if !settings.localnet && self.is_local_host(addr_) {
1337 debug!(
1338 target: "net::hosts::filter_addresses",
1339 "[{addr_}] Filtering non-global ranges"
1340 );
1341 continue
1342 }
1343
1344 match addr_.scheme() {
1345 #[cfg(feature = "p2p-tor")]
1347 "tor" | "tor+tls" => {
1348 use std::str::FromStr;
1349 if tor_hscrypto::pk::HsId::from_str(host_str).is_err() {
1350 continue
1351 }
1352 trace!(
1353 target: "net::hosts::filter_addresses",
1354 "[Tor] Valid: {host_str}"
1355 );
1356 }
1357
1358 #[cfg(feature = "p2p-nym")]
1359 "nym" | "nym+tls" => continue, "tcp" | "tcp+tls" => {
1362 trace!(
1363 target: "net::hosts::filter_addresses",
1364 "[TCP] Valid: {host_str}"
1365 );
1366 }
1367
1368 #[cfg(feature = "p2p-i2p")]
1369 "i2p" | "i2p+tls" => {
1370 if !Self::is_i2p_host(host_str) {
1371 continue
1372 }
1373 trace!(
1374 target: "net::hosts::filter_addresses",
1375 "[I2p] Valid: {host_str}"
1376 );
1377 }
1378
1379 _ => continue,
1380 }
1381
1382 if !settings.active_profiles.contains(&addr_.scheme().to_string()) ||
1387 (!self.ipv6_available.load(Ordering::SeqCst) && self.is_ipv6(addr_))
1388 {
1389 self.container.store_or_update(HostColor::Dark, addr_.clone(), *last_seen);
1390 self.container.sort_by_last_seen(HostColor::Dark as usize);
1391 self.container.resize(HostColor::Dark);
1392
1393 let day = 86400;
1395 self.container.refresh(HostColor::Dark, day);
1396
1397 if !settings.mixed_profiles.contains(&addr_.scheme().to_string()) {
1399 continue;
1400 }
1401 }
1402
1403 if self.container.contains(HostColor::Gold as usize, addr_) ||
1407 self.container.contains(HostColor::White as usize, addr_) ||
1408 self.container.contains(HostColor::Grey as usize, addr_)
1409 {
1410 debug!(target: "net::hosts::filter_addresses", "[{addr_}] exists! Skipping");
1411 continue
1412 }
1413
1414 ret.push((addr_.clone(), *last_seen));
1415 }
1416
1417 ret
1418 }
1419
1420 pub fn fetch_last_seen(&self, addr: &Url) -> Option<u64> {
1423 if self.container.contains(HostColor::Gold as usize, addr) {
1424 self.container.get_last_seen(HostColor::Gold as usize, addr)
1425 } else if self.container.contains(HostColor::White as usize, addr) {
1426 self.container.get_last_seen(HostColor::White as usize, addr)
1427 } else if self.container.contains(HostColor::Grey as usize, addr) {
1428 self.container.get_last_seen(HostColor::Grey as usize, addr)
1429 } else {
1430 None
1431 }
1432 }
1433
1434 pub async fn greylist_host(&self, addr: &Url, last_seen: u64) -> Result<()> {
1436 debug!(target: "net::hosts:greylist_host", "Downgrading addr={addr}");
1437 self.move_host(addr, last_seen, HostColor::Grey).await?;
1438
1439 self.unregister(addr)
1441 }
1442
1443 pub async fn whitelist_host(&self, addr: &Url, last_seen: u64) -> Result<()> {
1444 debug!(target: "net::hosts:whitelist_host", "Upgrading addr={addr}");
1445 self.move_host(addr, last_seen, HostColor::White).await?;
1446
1447 self.unregister(addr)
1449 }
1450
1451 pub(in crate::net) async fn move_host(
1465 &self,
1466 addr: &Url,
1467 last_seen: u64,
1468 destination: HostColor,
1469 ) -> Result<()> {
1470 debug!(target: "net::hosts::move_host", "Trying to move addr={addr} destination={destination:?}");
1471
1472 self.try_register(addr.clone(), HostState::Move)?;
1474
1475 debug!(target: "net::hosts::move_host", "Moving addr={} destination={destination:?}",
1476 addr.clone());
1477
1478 match destination {
1479 HostColor::Grey => {
1481 self.container.remove_if_exists(HostColor::Gold, addr);
1482 self.container.remove_if_exists(HostColor::White, addr);
1483
1484 self.container.store_or_update(HostColor::Grey, addr.clone(), last_seen);
1485 self.container.sort_by_last_seen(HostColor::Grey as usize);
1486 self.container.resize(HostColor::Grey);
1487 }
1488
1489 HostColor::White => {
1491 self.container.remove_if_exists(HostColor::Grey, addr);
1492
1493 self.container.store_or_update(HostColor::White, addr.clone(), last_seen);
1494 self.container.sort_by_last_seen(HostColor::White as usize);
1495 self.container.resize(HostColor::White);
1496 }
1497
1498 HostColor::Gold => {
1500 self.container.remove_if_exists(HostColor::Grey, addr);
1501 self.container.remove_if_exists(HostColor::White, addr);
1502
1503 self.container.store_or_update(HostColor::Gold, addr.clone(), last_seen);
1504 self.container.sort_by_last_seen(HostColor::Gold as usize);
1505 }
1506
1507 HostColor::Black => {
1509 if addr.host_str().is_some() {
1512 if !self.settings.read().await.localnet && self.is_local_host(addr) {
1515 return Ok(());
1516 }
1517
1518 self.container.remove_if_exists(HostColor::Grey, addr);
1519 self.container.remove_if_exists(HostColor::White, addr);
1520 self.container.remove_if_exists(HostColor::Gold, addr);
1521
1522 self.container.store_or_update(HostColor::Black, addr.clone(), last_seen);
1523 }
1524 }
1525
1526 HostColor::Dark => return Err(Error::InvalidHostColor),
1527 }
1528
1529 Ok(())
1530 }
1531
1532 pub(in crate::net) fn add_auto_addr(&self, addr: Ipv6Addr) {
1535 let mut auto_addrs = self.auto_self_addrs.lock().unwrap();
1536 auto_addrs.push(addr);
1537 }
1538
1539 pub fn guess_auto_addr(&self) -> Option<Ipv6Addr> {
1542 let mut auto_addrs = self.auto_self_addrs.lock().unwrap();
1543 let items = auto_addrs.make_contiguous();
1544 most_frequent_or_any(items)
1545 }
1546
1547 pub async fn external_addrs(&self) -> Vec<Url> {
1552 let mut external_addrs = self.settings.read().await.external_addrs.clone();
1553 for ext_addr in &mut external_addrs {
1554 let _ = self.patch_port(ext_addr);
1557 let _ = self.patch_auto_addr(ext_addr);
1558 }
1559 external_addrs
1560 }
1561
1562 fn patch_auto_addr(&self, ext_addr: &mut Url) -> Option<()> {
1565 if ext_addr.scheme() != "tcp" && ext_addr.scheme() != "tcp+tls" {
1566 return None
1567 }
1568
1569 let ext_host = ext_addr.host()?;
1570 let Host::Ipv6(ext_ip) = ext_host else { return None };
1572 if !ext_ip.is_unspecified() {
1574 return None
1575 }
1576
1577 let auto_addr = self.guess_auto_addr()?;
1579
1580 ext_addr.set_ip_host(IpAddr::V6(auto_addr)).ok()?;
1582 Some(())
1583 }
1584
1585 fn patch_port(&self, ext_addr: &mut Url) -> Option<()> {
1588 if ext_addr.port()? != 0 {
1590 return None
1591 }
1592
1593 None
1601 }
1602
1603 pub fn has_existing_connection(&self, url: &Url) -> bool {
1605 let host = url.host().unwrap();
1606 let colors = [HostColor::Gold, HostColor::White];
1607 colors.iter().any(|color| {
1608 self.container.hostlists[color.clone() as usize]
1609 .read()
1610 .unwrap()
1611 .iter()
1612 .any(|(u, _t)| u.host().unwrap() == host)
1613 })
1614 }
1615
1616 #[cfg(feature = "p2p-i2p")]
1617 fn is_i2p_host(host: &str) -> bool {
1618 if !host.ends_with(".i2p") {
1619 return false
1620 }
1621
1622 let name = host.trim_end_matches(".i2p");
1626
1627 if name.ends_with(".b32") {
1628 let b32 = name.trim_end_matches(".b32");
1629 let decoded = crate::util::encoding::base32::decode(b32);
1630 return decoded.is_some() && decoded.unwrap().len() == 32
1632 }
1633
1634 name.chars().all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.')
1635 }
1636}
1637
1638trait UnstableFeatureIp {
1641 fn unstable_is_global(&self) -> bool;
1642}
1643
1644impl UnstableFeatureIp for Ipv4Addr {
1645 #[inline]
1647 fn unstable_is_global(&self) -> bool {
1648 !(self.octets()[0] == 0 || self.is_private()
1650 || self.octets()[0] == 100 && (self.octets()[1] & 0b1100_0000 == 0b0100_0000)
1652 || self.is_loopback()
1653 || self.is_link_local()
1654 || (
1657 self.octets()[0] == 192 && self.octets()[1] == 0 && self.octets()[2] == 0
1658 && self.octets()[3] != 9 && self.octets()[3] != 10
1659 )
1660 || self.is_documentation()
1661 || self.octets()[0] == 198 && (self.octets()[1] & 0xfe) == 18
1663 || self.octets()[0] & 240 == 240 && !self.is_broadcast()
1665 || self.is_broadcast())
1666 }
1667}
1668
1669impl UnstableFeatureIp for Ipv6Addr {
1670 #[inline]
1672 fn unstable_is_global(&self) -> bool {
1673 !(self.is_unspecified()
1674 || self.is_loopback()
1675 || matches!(self.segments(), [0, 0, 0, 0, 0, 0xffff, _, _])
1677 || matches!(self.segments(), [0x64, 0xff9b, 1, _, _, _, _, _])
1679 || matches!(self.segments(), [0x100, 0, 0, 0, _, _, _, _])
1681 || (matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200)
1683 && !(
1684 u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001
1686 || u128::from_be_bytes(self.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002
1688 || matches!(self.segments(), [0x2001, 3, _, _, _, _, _, _])
1690 || matches!(self.segments(), [0x2001, 4, 0x112, _, _, _, _, _])
1692 || matches!(self.segments(), [0x2001, b, _, _, _, _, _, _] if (0x20..=0x3F).contains(&b))
1695 ))
1696 || matches!(self.segments(), [0x2002, _, _, _, _, _, _, _])
1699 || matches!(self.segments(), [0x2001, 0xdb8, ..] | [0x3fff, 0..=0x0fff, ..])
1701 || matches!(self.segments(), [0x5f00, ..])
1703 || self.is_unique_local()
1704 || self.is_unicast_link_local())
1705 }
1706}
1707
1708#[cfg(test)]
1709mod tests {
1710 use super::*;
1711 use crate::system::sleep;
1712
1713 #[test]
1714 fn test_is_local_host() {
1715 let settings = Settings {
1716 localnet: false,
1717 external_addrs: vec![
1718 Url::parse("tcp://foo.bar:123").unwrap(),
1719 Url::parse("tcp://lol.cat:321").unwrap(),
1720 ],
1721 ..Default::default()
1722 };
1723 let hosts = Hosts::new(Arc::new(AsyncRwLock::new(settings)));
1724
1725 let local_hosts: Vec<Url> = vec![
1726 Url::parse("tcp://localhost").unwrap(),
1727 Url::parse("tcp://127.0.0.1").unwrap(),
1728 Url::parse("tcp+tls://[::1]").unwrap(),
1729 Url::parse("tcp://localhost.localdomain").unwrap(),
1730 Url::parse("tcp://192.168.10.65").unwrap(),
1731 ];
1732 for host in local_hosts {
1733 eprintln!("{host}");
1734 assert!(hosts.is_local_host(&host));
1735 }
1736 let remote_hosts: Vec<Url> = vec![
1737 Url::parse("https://dyne.org").unwrap(),
1738 Url::parse("tcp://77.168.10.65:2222").unwrap(),
1739 Url::parse("tcp://[2345:0425:2CA1:0000:0000:0567:5673:23b5]").unwrap(),
1740 Url::parse("http://eweiibe6tdjsdprb4px6rqrzzcsi22m4koia44kc5pcjr7nec2rlxyad.onion")
1741 .unwrap(),
1742 ];
1743 for host in remote_hosts {
1744 assert!(!hosts.is_local_host(&host))
1745 }
1746 }
1747
1748 #[test]
1749 fn test_is_ipv6() {
1750 let settings = Settings { ..Default::default() };
1751 let hosts = Hosts::new(Arc::new(AsyncRwLock::new(settings)));
1752
1753 let ipv6_hosts: Vec<Url> = vec![
1754 Url::parse("tcp+tls://[::1]").unwrap(),
1755 Url::parse("tcp://[2001:0000:130F:0000:0000:09C0:876A:130B]").unwrap(),
1756 Url::parse("tcp://[2345:0425:2CA1:0000:0000:0567:5673:23b5]").unwrap(),
1757 ];
1758
1759 let ipv4_hosts: Vec<Url> = vec![
1760 Url::parse("tcp://192.168.10.65").unwrap(),
1761 Url::parse("https://dyne.org").unwrap(),
1762 Url::parse("tcp+tls://agorism.xyz").unwrap(),
1763 ];
1764
1765 for host in ipv6_hosts {
1766 assert!(hosts.is_ipv6(&host))
1767 }
1768
1769 for host in ipv4_hosts {
1770 assert!(!hosts.is_ipv6(&host))
1771 }
1772 }
1773
1774 #[test]
1775 fn test_block_all_ports() {
1776 let settings = Settings { ..Default::default() };
1777 let hosts = Hosts::new(Arc::new(AsyncRwLock::new(settings)));
1778
1779 let blacklist1 = Url::parse("tcp+tls://nietzsche.king:333").unwrap();
1780 let blacklist2 = Url::parse("tcp+tls://agorism.xyz").unwrap();
1781
1782 hosts.container.store(HostColor::Black as usize, blacklist1.clone(), 0);
1783 hosts.container.store(HostColor::Black as usize, blacklist2.clone(), 0);
1784
1785 assert!(hosts.block_all_ports(&blacklist2));
1786 assert!(!hosts.block_all_ports(&blacklist1));
1787 }
1788
1789 #[test]
1790 fn test_store() {
1791 let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
1792
1793 let settings = Settings { ..Default::default() };
1794 let hosts = Hosts::new(Arc::new(AsyncRwLock::new(settings)));
1795
1796 let grey_hosts = vec![
1797 Url::parse("tcp://localhost:3921").unwrap(),
1798 Url::parse("tor://[::1]:21481").unwrap(),
1799 Url::parse("tcp://192.168.10.65:311").unwrap(),
1800 Url::parse("tcp+tls://0.0.0.0:2312").unwrap(),
1801 Url::parse("tcp://255.255.255.255:2131").unwrap(),
1802 ];
1803
1804 for addr in &grey_hosts {
1805 hosts.container.store(HostColor::Grey as usize, addr.clone(), last_seen);
1806 }
1807 assert!(!hosts.container.is_empty(HostColor::Grey));
1808
1809 let white_hosts = vec![
1810 Url::parse("tcp://localhost:3921").unwrap(),
1811 Url::parse("tor://[::1]:21481").unwrap(),
1812 Url::parse("tcp://192.168.10.65:311").unwrap(),
1813 Url::parse("tcp+tls://0.0.0.0:2312").unwrap(),
1814 Url::parse("tcp://255.255.255.255:2131").unwrap(),
1815 ];
1816
1817 for host in &white_hosts {
1818 hosts.container.store(HostColor::White as usize, host.clone(), last_seen);
1819 }
1820 assert!(!hosts.container.is_empty(HostColor::White));
1821
1822 let gold_hosts = vec![
1823 Url::parse("tcp://dark.fi:80").unwrap(),
1824 Url::parse("tcp://http.cat:401").unwrap(),
1825 Url::parse("tcp://foo.bar:111").unwrap(),
1826 ];
1827
1828 for host in &gold_hosts {
1829 hosts.container.store(HostColor::Gold as usize, host.clone(), last_seen);
1830 }
1831
1832 assert!(hosts.container.contains(HostColor::Grey as usize, &grey_hosts[0]));
1833 assert!(hosts.container.contains(HostColor::White as usize, &white_hosts[1]));
1834 assert!(hosts.container.contains(HostColor::Gold as usize, &gold_hosts[2]));
1835 }
1836
1837 #[test]
1838 fn test_refresh() {
1839 smol::block_on(async {
1840 let settings = Settings { ..Default::default() };
1841 let hosts = Hosts::new(Arc::new(AsyncRwLock::new(settings)));
1842 let old_timestamp = 1720000000;
1843
1844 for i in 0..5 {
1846 let last_seen = old_timestamp + i;
1847 let url = Url::parse(&format!("tcp://old_darklist{i}:123")).unwrap();
1848 hosts.container.store(HostColor::Dark as usize, url.clone(), last_seen);
1849 }
1850
1851 for i in 0..5 {
1853 let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
1854 let url = Url::parse(&format!("tcp://new_darklist{i}:123")).unwrap();
1855 hosts.container.store(HostColor::Dark as usize, url.clone(), last_seen);
1856 }
1857
1858 let day = 86400;
1860 hosts.container.refresh(HostColor::Dark, day);
1861
1862 let darklist = hosts.container.hostlists[HostColor::Dark as usize].read().unwrap();
1863 assert!(darklist.len() == 5);
1864
1865 for (_, last_seen) in darklist.iter() {
1866 assert!(*last_seen > old_timestamp);
1867 }
1868
1869 drop(darklist);
1870 let now = UNIX_EPOCH.elapsed().unwrap().as_secs();
1871 let future_timestamp = now + 100;
1872
1873 for i in 0..5 {
1875 let last_seen = future_timestamp;
1876 let url = Url::parse(&format!("tcp://future_darklist{i}:123")).unwrap();
1877 hosts.container.store(HostColor::Dark as usize, url.clone(), last_seen);
1878 }
1879
1880 hosts.container.refresh(HostColor::Dark, day);
1881
1882 let darklist = hosts.container.hostlists[HostColor::Dark as usize].read().unwrap();
1884 assert!(darklist.len() == 10);
1885 });
1886 }
1887
1888 #[test]
1889 fn test_get_last() {
1890 smol::block_on(async {
1891 let settings = Settings { ..Default::default() };
1892 let hosts = Hosts::new(Arc::new(AsyncRwLock::new(settings)));
1893
1894 for i in 0..10 {
1896 sleep(1).await;
1897 let last_seen = UNIX_EPOCH.elapsed().unwrap().as_secs();
1898 let url = Url::parse(&format!("tcp://whitelist{i}:123")).unwrap();
1899 hosts.container.store(HostColor::White as usize, url.clone(), last_seen);
1900 }
1901
1902 for (url, last_seen) in
1903 hosts.container.hostlists[HostColor::White as usize].read().unwrap().iter()
1904 {
1905 println!("{url} {last_seen}");
1906 }
1907
1908 let entry = hosts.container.fetch_last(HostColor::White).unwrap();
1909 println!("last entry: {} {}", entry.0, entry.1);
1910 });
1911 }
1912
1913 #[test]
1914 fn test_is_p2p_host() {
1915 assert!(Hosts::is_i2p_host("tm7bz5qfh73id33yjpshxmesrqedoz2ckghd3levktqywcrramwq.b32.i2p"));
1916 assert!(!Hosts::is_i2p_host("randomstring.b32.i2p"));
1917 assert!(Hosts::is_i2p_host("node.dark.fi.i2p"));
1918 assert!(!Hosts::is_i2p_host("node.dark.fi"));
1919 }
1920
1921 #[test]
1924 fn test_transport_tor_mixed_with_tcp() {
1925 let mixed_hosts = HostContainer::mix_host(
1926 &Url::parse("tcp://dark.fi:28880").unwrap(),
1927 &["tor+tls".to_string(), "tcp".to_string(), "tor".to_string()],
1928 &["tcp".to_string()],
1929 &Url::parse("socks5://127.0.0.1:9050").ok(),
1930 &None,
1931 );
1932
1933 assert_eq!(mixed_hosts.len(), 1);
1934 assert_eq!(mixed_hosts[0].to_string(), "tor://dark.fi:28880/");
1935 }
1936
1937 #[test]
1941 fn test_transport_socks5_mixed_with_tcp_through_tor_and_nym_proxy() {
1942 let tor_socks5_proxy_url = Url::parse("socks5://127.0.0.1:9050").ok();
1943 let nym_socks5_proxy_url = Url::parse("socks5://127.0.0.1:1080").ok();
1944
1945 let fetched_hosts = HostContainer::mix_host(
1946 &Url::parse("tcp+tls://dark.fi:28880").unwrap(),
1947 &["socks5".to_string(), "socks5+tls".to_string()],
1948 &["tcp+tls".to_string()],
1949 &tor_socks5_proxy_url,
1950 &nym_socks5_proxy_url,
1951 );
1952
1953 assert_eq!(fetched_hosts.len(), 2);
1954 assert!(
1955 fetched_hosts[0].scheme() == "socks5+tls" && fetched_hosts[1].scheme() == "socks5+tls"
1956 );
1957 assert_eq!(
1958 fetched_hosts
1959 .iter()
1960 .filter(|h| h.port() == tor_socks5_proxy_url.as_ref().unwrap().port())
1961 .count(),
1962 1
1963 );
1964 assert_eq!(
1965 fetched_hosts
1966 .iter()
1967 .filter(|h| h.port() == nym_socks5_proxy_url.as_ref().unwrap().port())
1968 .count(),
1969 1
1970 );
1971 }
1972
1973 #[test]
1976 fn test_transport_socks5_mixed_with_tor() {
1977 let addr = "eweiibe6tdjsdprb4px6rqrzzcsi22m4koia44kc5pcjr7nec2rlxyad.onion:23330";
1978 let tor_socks5_proxy_url = Url::parse("socks5://127.0.0.1:9050").ok();
1979 let nym_socks5_proxy_url = Url::parse("socks5://127.0.0.1:1080").ok();
1980
1981 let fetched_hosts = HostContainer::mix_host(
1982 &Url::parse(&format!("tor://{addr}")).unwrap(),
1983 &["socks5".to_string(), "socks5+tls".to_string(), "tor".to_string()],
1984 &["tor".to_string()],
1985 &tor_socks5_proxy_url,
1986 &nym_socks5_proxy_url,
1987 );
1988
1989 assert_eq!(fetched_hosts.len(), 1);
1990 let mixed_url = fetched_hosts[0].clone();
1991 assert_eq!(mixed_url.scheme(), tor_socks5_proxy_url.as_ref().unwrap().scheme());
1992 assert_eq!(mixed_url.host(), tor_socks5_proxy_url.as_ref().unwrap().host());
1993 assert_eq!(mixed_url.port(), tor_socks5_proxy_url.as_ref().unwrap().port());
1994 assert_eq!(mixed_url.path_segments().unwrap().next(), Some(addr));
1995 }
1996
1997 #[test]
1999 fn test_transport_tor_and_socks5_mixed_with_tcp() {
2000 let fetched_hosts = HostContainer::mix_host(
2001 &Url::parse("tcp://dark.fi:28880").unwrap(),
2002 &[
2003 "tor".to_string(),
2004 "tor+tls".to_string(),
2005 "socks5".to_string(),
2006 "socks5+tls".to_string(),
2007 ],
2008 &["tcp".to_string()],
2009 &Url::parse("socks5://127.0.0.1:9050").ok(),
2010 &None,
2011 );
2012
2013 assert_eq!(fetched_hosts.len(), 2);
2014 let endpoints: Vec<_> = fetched_hosts.iter().map(|item| item.scheme()).collect();
2015 assert!(endpoints.iter().all(|&scheme| scheme == "tor" || scheme == "socks5"));
2016 }
2017}