From 795485951782da171bfb26f2aeb0334448fe34c1 Mon Sep 17 00:00:00 2001 From: Namilsk Date: Mon, 28 Sep 2026 18:45:28 +0300 Subject: [PATCH] refactor(fakedns): rewrite fakedns storage to hashmap; add cargo docs; impl(fakedns): secure dns servers bootstrapping --- Cargo.toml | 6 ++ src/sniffing/dns/dnsfaker.rs | 165 +++++++++++++++++++++++++++-------- 2 files changed, 136 insertions(+), 35 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7121300..cd5f4ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,15 +11,21 @@ prost-build = "0.14.3" [dependencies] arti-client = "0.40.0" +hickory-resolver = { version = "0.26.3", features = ["dnssec-ring", "https-ring", "quic-ring", "serde", "tls-ring", "toml"] } ipnet = "2.12.0" iptables = "0.6.0" maxminddb = "0.27.3" +memmap2 = "0.9.10" prost = "0.14.3" prost-types = "0.14.3" +rand = "0.10.3" +regex = "1.13.1" rtnetlink = "0.20.0" serde = { version = "1.0.228", features = ["derive"] } +smoltcp = "0.14.0" tokio = { version = "1.50.0", features = ["full"] } toml = { version = "1.0.6", features = ["serde"] } +trie = "0.3.0" tun = { version = "0.8.6", features = ["async", "tokio"] } [profile.release] diff --git a/src/sniffing/dns/dnsfaker.rs b/src/sniffing/dns/dnsfaker.rs index e05f0af..0f27a95 100644 --- a/src/sniffing/dns/dnsfaker.rs +++ b/src/sniffing/dns/dnsfaker.rs @@ -1,17 +1,18 @@ use hickory_resolver::{ - TokioResolver, + Resolver, TokioResolver, config::{NameServerConfig, ResolverConfig}, net::runtime::TokioRuntimeProvider, proto::rr::IntoName, }; use rand::RngExt; + +use std::collections::HashMap; use std::net::{IpAddr, Ipv4Addr}; use std::sync::Arc; -struct FakeIPEntry { - domain_name: String, - address: IpAddr, -} +pub type FakeIPStorage = HashMap; +// domain_name: String, +// address: IpAddr, #[derive(Debug, PartialEq, Eq)] pub enum IPStrategies { @@ -20,6 +21,7 @@ pub enum IPStrategies { Any, } +#[derive(Debug, Eq, PartialEq)] enum Protocols { DoH, DoU, @@ -27,19 +29,20 @@ enum Protocols { DoTCP, } -pub struct FakeIPStorage { - entries: Vec, - resolver: Option, +pub struct FakeIPServer { + entries: FakeIPStorage, + pub resolver: Option, } pub trait FakeIPOptions { - fn add_new(&mut self, domain: String) -> Ipv4Addr; + fn add(&mut self, domain: String) -> Ipv4Addr; async fn add_real_ip( &mut self, domain: String, ip_type: IPStrategies, ) -> Result; - fn init_resolver(&mut self, link: String, domain: String) -> Result<(), std::io::Error>; + async fn init_resolver(&mut self, link: String) -> Result<(), std::io::Error>; + fn get(&self, domain: &str) -> Result; } fn generate_ip_from(network: Ipv4Addr, prefix: u32) -> Ipv4Addr { @@ -47,34 +50,126 @@ fn generate_ip_from(network: Ipv4Addr, prefix: u32) -> Ipv4Addr { let network = u32::from(network); let host_bits = 32 - prefix; - let host = rng.random_range(0..(1u32 << host_bits)); + let host = rng.random_range(1..(1u32 << host_bits)); Ipv4Addr::from(network | host) } -impl FakeIPOptions for FakeIPStorage { - fn init_resolver(&mut self, link: String, domain: String) -> Result<(), std::io::Error> { + +// !TODO #[must_use]:FakeIpServer::new() builder AND FakeIPServer::start(), which unnecessary starts +// DNS server itself +impl FakeIPOptions for FakeIPServer { + /// Just gets an FakeIP addr from FakeIPStorage. + /// ```rust + /// x = FakeIPServer::new(/* params */); + /// x.add("nosystemd.org") // returns IpAddr, but if it dropped you can call + /// x.get("nosystemd.org").unwrap() // -> IpAddr + /// ``` + fn get(&self, domain: &str) -> Result { + self.entries.get(domain).copied().ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("No FakeIP entry found for domain: {domain}"), + ) + }) + } + + /// FakeIPServer function which init's integrated DNS resolver for + /// ```rust + /// let x: FakeIPServer = FakeIPServer::new( /* idk, params */ ).init_resolver("udp://8.8.8.8").add_real_ip("domain.com"); + /// // for tls:// and https:// allowed ONLY domain. + /// x.add_new("nosystemd.org"); + /// ``` + /// Doesn't call it if you wouldnt use `.add_real_ip()` - which needed ONLY for `AlwaysReal` + /// FakeIPServer strategy; + /// + /// P.S. FakeIPServer: AlwaysReal; Needed if you regularry uses `dnsutils` from your console + /// and dont like see Fake IP address. Or use strict antifrauded corporative software which + /// checking your DNS. (Like my ChatGPT app, before i stopped using xray core fakedns) + async fn init_resolver(&mut self, link: String) -> Result<(), std::io::Error> { + // just because i can let server_string = link.to_lowercase(); - let raw_protocol: Vec<&str> = server_string.split("://").collect(); + // parses dns url (ONLY with protocol) like `tls://domain.com` or `https://dns.google.com` etc. + // to + // [0]: protocol like `tls`/`udp`/`tcp` e.g. + // [1]: domain of the server + // overrides /dns-query path + let (protocol, server) = server_string.split_once("://").ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Resolver URL must contain `protocol://`", + ) + })?; - let result = match raw_protocol[0] { + // ts soooo shitty parsing + let result = match protocol { "tls" => Protocols::DoT, "tcp" => Protocols::DoTCP, "https" => Protocols::DoH, "udp" => Protocols::DoU, _ => { - panic!("Wrong resolver protocol"); + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "Wrong resolver protocol", + )); } }; - let ns: NameServerConfig = match result { - Protocols::DoT => { - NameServerConfig::tls(raw_protocol[1].to_ip().unwrap(), Arc::from(domain)) - } - Protocols::DoTCP => NameServerConfig::tcp(raw_protocol[1].to_ip().unwrap()), - Protocols::DoU => NameServerConfig::udp(raw_protocol[1].to_ip().unwrap()), - _ => panic!("Wrong IP/domain of resolver"), + // Bootstrapps secure DNS server via udp://8.8.8.8; + // Logic tls://some-resolver.com -> udp://8.8.8.8:53 (guaranteed real answer, hardcoded) -> tls://123.123.123.123:853 + // same with https:// + let secure_resolver_ip: Option = if result == Protocols::DoH + || result == Protocols::DoT + { + let bootstrap = TokioResolver::builder_with_config( + ResolverConfig::from_parts( + None, + vec![], + vec![NameServerConfig::udp(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))], + ), + TokioRuntimeProvider::default(), + ) + .build() + .map_err(|e| { + std::io::Error::other(format!("Failed to create bootstrap resolver: {e}")) + })?; + + Some( + bootstrap + .lookup_ip(server) + .await + .map_err(|e| std::io::Error::other(format!("Failed to resolve {server}: {e}")))? + .iter() + .next() + .ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("No IP addresses found for {server}"), + ) + })?, + ) + } else { + None + }; + let ip = secure_resolver_ip + .or_else(|| server.parse::().ok()) + .ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidInput, + format!("Resolver address is not an IP: {server}"), + ) + })?; + let ns = match result { + Protocols::DoT => NameServerConfig::tls(ip, Arc::from(server.to_owned())), + Protocols::DoTCP => NameServerConfig::tcp(ip), + Protocols::DoU => NameServerConfig::udp(ip), + Protocols::DoH => NameServerConfig::https( + ip, + Arc::from(server.to_owned()), + Some(Arc::from("/dns-query")), + ), + _ => unreachable!(), }; let config = ResolverConfig::from_parts(None, vec![], vec![ns]); @@ -87,18 +182,21 @@ impl FakeIPOptions for FakeIPStorage { Ok(()) } - - fn add_new(&mut self, domain: String) -> Ipv4Addr { + /// Generates new IPv4 addr from network `198.18.0.0/15` and inserts it to FakeIPServer.entries + /// ```rust + /// let x = FakeIPServer::new( /* params */ ); + /// x.add_new("nosystemd.org"); + /// println!("Given IP: {}: {}", "nosystemd.org", x.entries.get("nosystemd.org")); + /// ``` + fn add(&mut self, domain: String) -> Ipv4Addr { let gen_ip = generate_ip_from(Ipv4Addr::new(198, 18, 0, 0), 15); - let entry = FakeIPEntry { - domain_name: domain, - address: IpAddr::V4(gen_ip), - }; - - self.entries.push(entry); + self.entries.insert(domain, IpAddr::V4(gen_ip)); gen_ip } + /// Adds to FakeIPStorage REAL ip resolved by FakeIPServer.resolver; Configure it with calling + /// `x.init_resolver(/* "tls://example.com" || "https://dns.google.com/" etc. */).await` + /// Fuction will panic if you doesnt call `init_resolver` and using `add_real_ip` async fn add_real_ip( &mut self, domain: String, @@ -126,10 +224,7 @@ impl FakeIPOptions for FakeIPStorage { ) })?; - self.entries.push(FakeIPEntry { - domain_name: domain, - address: ip, - }); + self.entries.insert(domain, ip); Ok(ip) }