refactor(fakedns): rewrite fakedns storage to hashmap; add cargo docs; impl(fakedns): secure dns servers bootstrapping
This commit is contained in:
parent
927299baad
commit
7954859517
2 changed files with 136 additions and 35 deletions
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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<String, IpAddr>;
|
||||
// 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<FakeIPEntry>,
|
||||
resolver: Option<TokioResolver>,
|
||||
pub struct FakeIPServer {
|
||||
entries: FakeIPStorage,
|
||||
pub resolver: Option<TokioResolver>,
|
||||
}
|
||||
|
||||
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<IpAddr, std::io::Error>;
|
||||
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<IpAddr, std::io::Error>;
|
||||
}
|
||||
|
||||
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<IpAddr, std::io::Error> {
|
||||
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<IpAddr> = 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::<IpAddr>().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)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue