Wildcard support

This commit is contained in:
Xnoe 2026-04-12 03:26:25 +01:00
parent 79fee04aef
commit 8e7220f704
Signed by: xnoe
GPG Key ID: 45AC398F44F0DAFE
5 changed files with 76 additions and 10 deletions

2
Cargo.lock generated
View File

@ -254,7 +254,7 @@ dependencies = [
] ]
[[package]] [[package]]
name = "rust-dnsproxy-thing" name = "rust-dns-selective-routing"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",

View File

@ -1,5 +1,5 @@
[package] [package]
name = "rust-dnsproxy-thing" name = "rust-dns-selective-routing"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"

View File

@ -15,7 +15,7 @@ impl<'a, T> Cursor<'a, T> {
pub fn next(&mut self) -> Option<&T> { pub fn next(&mut self) -> Option<&T> {
let next_index = self.index + 1; let next_index = self.index + 1;
if next_index >= self.buf.len() { if next_index > self.buf.len() {
None None
} else { } else {
let v = &self.buf[self.index]; let v = &self.buf[self.index];
@ -25,7 +25,7 @@ impl<'a, T> Cursor<'a, T> {
} }
pub fn seek(&mut self, location: usize) -> Result<(), ()> { pub fn seek(&mut self, location: usize) -> Result<(), ()> {
if location >= self.buf.len() { if location > self.buf.len() {
Err(()) Err(())
} else { } else {
self.index = location; self.index = location;
@ -35,7 +35,7 @@ impl<'a, T> Cursor<'a, T> {
pub fn next_slice(&mut self, amount: usize) -> Option<&'a [T]> { pub fn next_slice(&mut self, amount: usize) -> Option<&'a [T]> {
let next_index = self.index + amount; let next_index = self.index + amount;
if next_index >= self.buf.len() { if next_index > self.buf.len() {
None None
} else { } else {
let slice = &self.buf[self.index..next_index]; let slice = &self.buf[self.index..next_index];
@ -50,7 +50,7 @@ impl<'a, T> Cursor<'a, T> {
pub fn forward(&mut self, amount: usize) -> Result<(), ()> { pub fn forward(&mut self, amount: usize) -> Result<(), ()> {
let next_index = self.index + amount; let next_index = self.index + amount;
if next_index >= self.buf.len() { if next_index > self.buf.len() {
Err(()) Err(())
} else { } else {
self.index = next_index; self.index = next_index;

View File

@ -17,7 +17,7 @@ impl IpPool {
return None; return None;
} }
let last_address_number = u32::MAX & !subnet_prefix_mask; let last_address_number = u32::MAX & !subnet_prefix_mask;
let mut pool = VecDeque::with_capacity(last_address_number as usize); let mut pool = VecDeque::with_capacity((last_address_number + 1) as usize);
for number in 0..=(last_address_number as usize) { for number in 0..=(last_address_number as usize) {
pool.push_back(Ipv4Addr::from(base_addr_int + (number as u32))); pool.push_back(Ipv4Addr::from(base_addr_int + (number as u32)));
} }

View File

@ -278,7 +278,8 @@ async fn handle_dns_response(buf: &[u8], reply_buf: &mut Vec<u8>, tcp: bool) ->
if !FwmarkConfigMap if !FwmarkConfigMap
.lock() .lock()
.await .await
.contains_key(&parts_to_dns_name(&qname_parts)) .get(&parts_to_dns_name(&qname_parts))
.is_some()
{ {
eprintln!("Not forging due to non-matching qname."); eprintln!("Not forging due to non-matching qname.");
forge = false; forge = false;
@ -401,8 +402,66 @@ struct Forwarding {
ttl: u32, ttl: u32,
} }
enum FwmarkDomainMatchType {
Exact,
Suffix
}
struct FwmarkConfig {
entries: Vec<(FwmarkDomainMatchType, Vec<Vec<u8>>, u32)>
}
impl FwmarkConfig {
fn new() -> Self {
Self {
entries: Vec::new()
}
}
fn insert(&mut self, dns_name: Vec<u8>, fwmark: u32) {
self.entries.push(
(
FwmarkDomainMatchType::Exact,
dns_name_to_parts(&mut Cursor::from(dns_name.as_slice())).unwrap(),
fwmark
)
);
}
fn insert_wildcard(&mut self, suffix_parts: Vec<Vec<u8>>, fwmark: u32) {
self.entries.push(
(
FwmarkDomainMatchType::Suffix,
suffix_parts,
fwmark
)
);
}
fn get(&self, dns_name: &Vec<u8>) -> Option<u32> {
let parts = dns_name_to_parts(&mut Cursor::from(dns_name.as_slice())).unwrap();
for (match_type, _parts, fwmark) in &self.entries {
match match_type {
FwmarkDomainMatchType::Exact => {
if parts == *_parts {
return Some(*fwmark)
}
},
FwmarkDomainMatchType::Suffix => {
if parts.ends_with(_parts.as_slice()) {
return Some(*fwmark)
}
}
}
}
None
}
}
lazy_static! { lazy_static! {
static ref FwmarkConfigMap: Mutex<HashMap<Vec<u8>, u32>> = Mutex::new(HashMap::new()); static ref FwmarkConfigMap: Mutex<FwmarkConfig> = Mutex::new(FwmarkConfig::new());
static ref ForwardingMap: Mutex<HashMap<Vec<u8>, Vec<Forwarding>>> = Mutex::new(HashMap::new()); static ref ForwardingMap: Mutex<HashMap<Vec<u8>, Vec<Forwarding>>> = Mutex::new(HashMap::new());
static ref IpAllocator: Mutex<IpPool> = static ref IpAllocator: Mutex<IpPool> =
Mutex::new(IpPool::new(Ipv4Addr::new(100, 64, 0, 0), 24).unwrap()); Mutex::new(IpPool::new(Ipv4Addr::new(100, 64, 0, 0), 24).unwrap());
@ -534,8 +593,15 @@ async fn main() -> anyhow::Result<()> {
if let Some(v) = map.get("domains") { if let Some(v) = map.get("domains") {
let mut fwmark_config = FwmarkConfigMap.lock().await; let mut fwmark_config = FwmarkConfigMap.lock().await;
for (domain, fwmark) in v.iter() { for (domain, fwmark) in v.iter() {
let dns_name = string_to_dns_name(domain.to_string());
let parts = dns_name_to_parts(&mut Cursor::from(&dns_name)).unwrap();
if let Ok(fwmark) = fwmark.parse::<u32>() { if let Ok(fwmark) = fwmark.parse::<u32>() {
let _ = fwmark_config.insert(string_to_dns_name(domain.to_string()), fwmark); if *parts.get(0).unwrap() == vec![b'*'] {
let _ = fwmark_config.insert_wildcard(parts[1..].to_vec(), fwmark);
} else {
let _ = fwmark_config.insert(dns_name, fwmark);
}
} }
} }
} }