server: use parking_lot::RwLock for addresses

This commit is contained in:
Luna Yao
2026-02-23 03:07:16 +01:00
parent b5a33a192b
commit 3fc030de48
+53 -73
View File
@@ -19,29 +19,28 @@ use hickory_server::{
server::{Request, RequestHandler, ResponseHandler, ResponseInfo}, server::{Request, RequestHandler, ResponseHandler, ResponseInfo},
ServerFuture, ServerFuture,
}; };
use itertools::Itertools; use parking_lot::{Mutex, RwLock};
use parking_lot::Mutex;
use pnet::packet::icmp::{IcmpTypes, MutableIcmpPacket}; use pnet::packet::icmp::{IcmpTypes, MutableIcmpPacket};
use pnet::packet::ip::IpNextHeaderProtocols; use pnet::packet::ip::IpNextHeaderProtocols;
use pnet::packet::ipv4::{Ipv4Packet, MutableIpv4Packet}; use pnet::packet::ipv4::{Ipv4Packet, MutableIpv4Packet};
use pnet::packet::udp::{MutableUdpPacket, UdpPacket}; use pnet::packet::udp::{MutableUdpPacket, UdpPacket};
use pnet::packet::{icmp, ipv4, udp, MutablePacket, Packet}; use pnet::packet::{icmp, ipv4, udp, MutablePacket, Packet};
use std::collections::HashSet; use std::collections::HashSet;
use std::io;
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
use std::{io, iter};
use std::{sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
use tokio::{sync::RwLock, task::JoinHandle}; use tokio::task::JoinHandle;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
#[derive(Clone)] #[derive(Clone)]
pub struct DynamicCatalog { pub struct DynamicCatalog {
inner: Arc<RwLock<Catalog>>, inner: Arc<tokio::sync::RwLock<Catalog>>,
} }
impl DynamicCatalog { impl DynamicCatalog {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
inner: Arc::new(RwLock::new(Catalog::new())), inner: Arc::new(tokio::sync::RwLock::new(Catalog::new())),
} }
} }
@@ -65,43 +64,6 @@ impl RequestHandler for DynamicCatalog {
} }
} }
struct DnsServerRuntime {
token: CancellationToken,
task: Option<JoinHandle<()>>,
}
impl DnsServerRuntime {
fn start<T: RequestHandler>(mut server: ServerFuture<T>) -> Self {
Self {
token: server.shutdown_token().clone(),
task: Some(tokio::spawn(async move {
server
.block_until_done()
.await
.unwrap_or_else(|e| tracing::error!("DNS server exited with error: {:?}", e));
})),
}
}
async fn stop(mut self) -> anyhow::Result<()> {
self.token.cancel();
if let Some(task) = self.task.take() {
task.await?;
}
Ok(())
}
}
impl Drop for DnsServerRuntime {
fn drop(&mut self) {
self.token.cancel();
if let Some(task) = self.task.take() {
task.abort();
tracing::warn!("DNS server runtime is leaked");
}
}
}
// ResponseWrapper for serializing DNS responses into a byte buffer. // ResponseWrapper for serializing DNS responses into a byte buffer.
// Used by the address hijacking NIC packet filter to produce DNS replies in-place. // Used by the address hijacking NIC packet filter to produce DNS replies in-place.
#[derive(Debug, Clone, From, Into, Deref, DerefMut)] #[derive(Debug, Clone, From, Into, Deref, DerefMut)]
@@ -148,6 +110,43 @@ impl ResponseHandler for Response {
} }
} }
struct DnsServerRuntime {
token: CancellationToken,
task: Option<JoinHandle<()>>,
}
impl DnsServerRuntime {
fn start<T: RequestHandler>(mut server: ServerFuture<T>) -> Self {
Self {
token: server.shutdown_token().clone(),
task: Some(tokio::spawn(async move {
server
.block_until_done()
.await
.unwrap_or_else(|e| tracing::error!("DNS server exited with error: {:?}", e));
})),
}
}
async fn stop(mut self) -> anyhow::Result<()> {
self.token.cancel();
if let Some(task) = self.task.take() {
task.await?;
}
Ok(())
}
}
impl Drop for DnsServerRuntime {
fn drop(&mut self) {
self.token.cancel();
if let Some(task) = self.task.take() {
task.abort();
tracing::warn!("DNS server runtime is leaked");
}
}
}
#[derive(Derivative)] #[derive(Derivative)]
#[derivative(Debug)] #[derivative(Debug)]
pub struct DnsServer { pub struct DnsServer {
@@ -178,20 +177,8 @@ impl DnsServer {
} }
} }
async fn reload_addresses(&self, addresses: impl IntoIterator<Item = NameServerAddr>) { pub fn routes(&self) -> HashSet<IpAddr> {
let addresses = addresses.into_iter().collect::<HashSet<_>>(); self.addresses.read().iter().map(|a| a.addr.ip()).collect()
let mut current = self.addresses.write().await; // TODO: read?
if *current == addresses {
return;
}
let added = addresses.difference(&*current).cloned().collect_vec();
let removed = current.difference(&addresses).cloned().collect_vec();
*current = addresses;
// TODO
} }
async fn reload_listeners( async fn reload_listeners(
@@ -239,7 +226,7 @@ impl DnsServer {
loop { loop {
dirty.addresses.notified().await; dirty.addresses.notified().await;
if dirty.addresses.reset() { if dirty.addresses.reset() {
self.reload_addresses(self.mgr.iter_addresses()).await; *self.addresses.write() = self.mgr.iter_addresses().collect();
} }
tokio::time::sleep(Duration::from_secs(1)).await; tokio::time::sleep(Duration::from_secs(1)).await;
} }
@@ -267,7 +254,7 @@ impl DnsServer {
_ = reload_listeners => {}, _ = reload_listeners => {},
); );
self.reload_addresses(iter::empty()).await; self.addresses.write().clear();
if let Some(runtime) = runtime.take() { if let Some(runtime) = runtime.take() {
let _ = runtime.stop().await; let _ = runtime.stop().await;
} }
@@ -295,19 +282,15 @@ impl NicPacketFilter for DnsServer {
} }
impl DnsServer { impl DnsServer {
async fn is_hijacked_ip(&self, ip: &IpAddr) -> bool { fn is_hijacked_ip(&self, ip: &IpAddr) -> bool {
self.addresses self.addresses.read().iter().any(|a| a.addr.ip() == *ip)
.read()
.await
.iter()
.any(|a| a.addr.ip() == *ip)
} }
async fn is_hijacked_addr(&self, addr: &NameServerAddr) -> bool { fn is_hijacked_addr(&self, addr: &NameServerAddr) -> bool {
self.addresses.read().await.contains(addr) self.addresses.read().contains(addr)
} }
/// Replace content of incoming UDP DNS request and ICMP echo request packet with reply data, /// Replace the content of an incoming UDP DNS request and ICMP echo request packet with reply data,
/// and swap source and destination IP addresses to send it back. /// and swap source and destination IP addresses to send it back.
async fn handle_ip_packet(&self, zc_packet: &mut ZCPacket) -> Option<()> { async fn handle_ip_packet(&self, zc_packet: &mut ZCPacket) -> Option<()> {
let (ip_header_length, ip_protocol, src_ip, dst_ip) = { let (ip_header_length, ip_protocol, src_ip, dst_ip) = {
@@ -325,7 +308,7 @@ impl DnsServer {
) )
}; };
if !self.is_hijacked_ip(&dst_ip.into()).await { if !self.is_hijacked_ip(&dst_ip.into()) {
return None; return None;
} }
@@ -383,10 +366,7 @@ impl DnsServer {
) )
}; };
if !self if !self.is_hijacked_addr(&SocketAddr::new(dst_ip.into(), dst_port).into()) {
.is_hijacked_addr(&SocketAddr::new(dst_ip.into(), dst_port).into())
.await
{
return None; return None;
} }