fix: clear cargo audit vulnerabilities

This commit is contained in:
fanyang
2026-06-23 21:49:27 +08:00
parent af2e991df2
commit 34aa54b777
10 changed files with 1665 additions and 2073 deletions
+3
View File
@@ -0,0 +1,3 @@
[advisories]
# openidconnect 4.0.1 depends on rsa 0.9.10, and RUSTSEC-2023-0071 has no fixed upgrade.
ignore = ["RUSTSEC-2023-0071"]
Generated
+1495 -1938
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -33,7 +33,7 @@ sea-orm-migration = { version = "1.1" }
sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio-rustls", "chrono", "uuid"] } sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio-rustls", "chrono", "uuid"] }
# Validation # Validation
validator = { version = "0.18", features = ["derive"] } validator = { version = "0.20", features = ["derive"] }
thiserror = "1.0" thiserror = "1.0"
jsonwebtoken = "9.0" jsonwebtoken = "9.0"
+4 -5
View File
@@ -236,12 +236,11 @@ http_req = { git = "https://github.com/EasyTier/http_req.git", default-features
] } ] }
# for dns connector # for dns connector
hickory-resolver = "0.25.2" hickory-resolver = "0.26.1"
hickory-proto = "0.25.2" hickory-proto = "0.26.1"
# for magic dns # for magic dns
hickory-client = { version = "0.25.2", optional = true } hickory-server = { version = "0.26.1", features = [
hickory-server = { version = "0.25.2", features = [
"resolver", "resolver",
], optional = true } ], optional = true }
@@ -401,7 +400,7 @@ jemalloc-prof = [
"jemalloc-sys/stats", "jemalloc-sys/stats",
] ]
tracing = ["tokio/tracing", "dep:console-subscriber"] tracing = ["tokio/tracing", "dep:console-subscriber"]
magic-dns = ["dep:hickory-client", "dep:hickory-server"] magic-dns = ["dep:hickory-server"]
faketcp = ["dep:flume"] faketcp = ["dep:flume"]
zstd = ["dep:zstd"] zstd = ["dep:zstd"]
# For Network Extension on macOS # For Network Extension on macOS
+35 -22
View File
@@ -3,33 +3,40 @@ use std::sync::Arc;
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use anyhow::Context; use anyhow::Context;
use hickory_proto::runtime::TokioRuntimeProvider; use hickory_proto::rr::RData;
use hickory_proto::xfer::Protocol; use hickory_resolver::config::{
use hickory_resolver::config::{LookupIpStrategy, NameServerConfig, ResolverConfig, ResolverOpts}; ConnectionConfig, LookupIpStrategy, NameServerConfig, ResolverConfig, ResolverOpts,
use hickory_resolver::name_server::{GenericConnector, TokioConnectionProvider}; };
use hickory_resolver::net::runtime::TokioRuntimeProvider;
use hickory_resolver::system_conf::read_system_conf; use hickory_resolver::system_conf::read_system_conf;
use hickory_resolver::{Resolver, TokioResolver}; use hickory_resolver::TokioResolver;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use tokio::net::lookup_host; use tokio::net::lookup_host;
use super::error::Error; use super::error::Error;
pub fn get_default_resolver_config() -> ResolverConfig { pub fn get_default_resolver_config() -> ResolverConfig {
let mut default_resolve_config = ResolverConfig::new(); ResolverConfig::from_parts(
default_resolve_config.add_name_server(NameServerConfig::new( None,
"223.5.5.5:53".parse().unwrap(), vec![],
Protocol::Udp, vec![
)); NameServerConfig::new(
default_resolve_config.add_name_server(NameServerConfig::new( "223.5.5.5".parse().unwrap(),
"180.184.1.1:53".parse().unwrap(), true,
Protocol::Udp, vec![ConnectionConfig::udp()],
)); ),
default_resolve_config NameServerConfig::new(
"180.184.1.1".parse().unwrap(),
true,
vec![ConnectionConfig::udp()],
),
],
)
} }
pub static ALLOW_USE_SYSTEM_DNS_RESOLVER: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(true)); pub static ALLOW_USE_SYSTEM_DNS_RESOLVER: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(true));
pub static RESOLVER: Lazy<Arc<Resolver<GenericConnector<TokioRuntimeProvider>>>> = pub static RESOLVER: Lazy<Arc<TokioResolver>> =
Lazy::new(|| { Lazy::new(|| {
let system_cfg = read_system_conf(); let system_cfg = read_system_conf();
let mut cfg = get_default_resolver_config(); let mut cfg = get_default_resolver_config();
@@ -41,9 +48,11 @@ pub static RESOLVER: Lazy<Arc<Resolver<GenericConnector<TokioRuntimeProvider>>>>
opt = s.1; opt = s.1;
} }
opt.ip_strategy = LookupIpStrategy::Ipv4AndIpv6; opt.ip_strategy = LookupIpStrategy::Ipv4AndIpv6;
let builder = TokioResolver::builder_with_config(cfg, TokioConnectionProvider::default()) let resolver = TokioResolver::builder_with_config(cfg, TokioRuntimeProvider::default())
.with_options(opt); .with_options(opt)
Arc::new(builder.build()) .build()
.expect("failed to build DNS resolver");
Arc::new(resolver)
}); });
pub async fn resolve_txt_record(domain_name: &str) -> Result<String, Error> { pub async fn resolve_txt_record(domain_name: &str) -> Result<String, Error> {
@@ -53,12 +62,16 @@ pub async fn resolve_txt_record(domain_name: &str) -> Result<String, Error> {
.await .await
.with_context(|| format!("txt_lookup failed, domain_name: {}", domain_name))?; .with_context(|| format!("txt_lookup failed, domain_name: {}", domain_name))?;
let txt_record = response let Some(RData::TXT(txt_record)) = response
.answers()
.iter() .iter()
.next() .next()
.with_context(|| format!("no txt record found, domain_name: {}", domain_name))?; .map(|record| &record.data)
else {
return Err(anyhow::anyhow!("no txt record found, domain_name: {}", domain_name).into());
};
let txt_data = String::from_utf8_lossy(&txt_record.txt_data()[0]); let txt_data = String::from_utf8_lossy(&txt_record.txt_data[0]);
tracing::info!(?txt_data, ?domain_name, "get txt record"); tracing::info!(?txt_data, ?domain_name, "get txt record");
Ok(txt_data.to_string()) Ok(txt_data.to_string())
+10 -7
View File
@@ -13,7 +13,7 @@ use crate::{
}; };
use anyhow::Context; use anyhow::Context;
use dashmap::DashSet; use dashmap::DashSet;
use hickory_resolver::proto::rr::rdata::SRV; use hickory_resolver::proto::rr::{RData, rdata::SRV};
use rand::{Rng as _, seq::SliceRandom}; use rand::{Rng as _, seq::SliceRandom};
use strum::VariantArray; use strum::VariantArray;
@@ -85,12 +85,12 @@ impl DnsTunnelConnector {
fn handle_one_srv_record(record: &SRV, protocol: IpScheme) -> Result<(url::Url, u64), Error> { fn handle_one_srv_record(record: &SRV, protocol: IpScheme) -> Result<(url::Url, u64), Error> {
// port must be non-zero // port must be non-zero
if record.port() == 0 { if record.port == 0 {
return Err(anyhow::anyhow!("port must be non-zero").into()); return Err(anyhow::anyhow!("port must be non-zero").into());
} }
let connector_dst = record.target().to_utf8(); let connector_dst = record.target.to_utf8();
let dst_url = format!("{}://{}:{}", protocol, connector_dst, record.port()); let dst_url = format!("{}://{}:{}", protocol, connector_dst, record.port);
Ok(( Ok((
dst_url.parse().with_context(|| { dst_url.parse().with_context(|| {
@@ -98,11 +98,11 @@ impl DnsTunnelConnector {
"parse dst_url failed, protocol: {}, connector_dst: {}, port: {}, dst_url: {}", "parse dst_url failed, protocol: {}, connector_dst: {}, port: {}, dst_url: {}",
protocol, protocol,
connector_dst, connector_dst,
record.port(), record.port,
dst_url dst_url
) )
})?, })?,
record.priority() as _, record.priority as _,
)) ))
} }
@@ -129,7 +129,10 @@ impl DnsTunnelConnector {
format!("srv_lookup failed, srv_domain: {}", srv_domain) format!("srv_lookup failed, srv_domain: {}", srv_domain)
})?; })?;
tracing::info!(?response, ?srv_domain, "srv_lookup response"); tracing::info!(?response, ?srv_domain, "srv_lookup response");
for record in response.iter() { for record in response.answers() {
let RData::SRV(record) = &record.data else {
continue;
};
let parsed_record = Self::handle_one_srv_record(record, **protocol); let parsed_record = Self::handle_one_srv_record(record, **protocol);
tracing::info!(?parsed_record, ?srv_domain, "parsed_record"); tracing::info!(?parsed_record, ?srv_domain, "parsed_record");
if let Err(e) = &parsed_record { if let Err(e) = &parsed_record {
+4 -6
View File
@@ -92,12 +92,11 @@ impl TryFrom<&Record> for rr::Record {
fn try_from(value: &Record) -> Result<Self, Self::Error> { fn try_from(value: &Record) -> Result<Self, Self::Error> {
let name = value.name()?; let name = value.name()?;
let mut record = Self::update0(name, value.ttl.as_secs() as u32, value.rr_type()); let ttl = value.ttl.as_secs() as u32;
record.set_dns_class(rr::DNSClass::IN);
match value.rr_type { match value.rr_type {
RecordType::A => { RecordType::A => {
let addr: Ipv4Addr = value.value.parse()?; let addr: Ipv4Addr = value.value.parse()?;
record.set_data(RData::A(rr::rdata::a::A(addr))); Ok(Self::from_rdata(name, ttl, RData::A(rr::rdata::a::A(addr))))
} }
RecordType::SOA => { RecordType::SOA => {
let soa = value.value.split_whitespace().collect::<Vec<_>>(); let soa = value.value.split_whitespace().collect::<Vec<_>>();
@@ -111,7 +110,7 @@ impl TryFrom<&Record> for rr::Record {
let retry: u32 = soa[4].parse()?; let retry: u32 = soa[4].parse()?;
let expire: u32 = soa[5].parse()?; let expire: u32 = soa[5].parse()?;
let minimum: u32 = soa[6].parse()?; let minimum: u32 = soa[6].parse()?;
record.set_data(RData::SOA(rr::rdata::soa::SOA::new( Ok(Self::from_rdata(name, ttl, RData::SOA(rr::rdata::soa::SOA::new(
mname, mname,
rname, rname,
serial, serial,
@@ -119,11 +118,10 @@ impl TryFrom<&Record> for rr::Record {
retry.try_into().unwrap(), retry.try_into().unwrap(),
expire.try_into().unwrap(), expire.try_into().unwrap(),
minimum, minimum,
))); ))))
} }
_ => todo!(), _ => todo!(),
} }
Ok(record)
} }
} }
+58 -36
View File
@@ -3,14 +3,14 @@ use hickory_proto::op::Edns;
use hickory_proto::rr; use hickory_proto::rr;
use hickory_proto::rr::LowerName; use hickory_proto::rr::LowerName;
use hickory_resolver::config::ResolverOpts; use hickory_resolver::config::ResolverOpts;
use hickory_resolver::name_server::TokioConnectionProvider; use hickory_resolver::net::runtime::TokioRuntimeProvider;
use hickory_resolver::system_conf::read_system_conf; use hickory_resolver::system_conf::read_system_conf;
use hickory_server::ServerFuture; use hickory_server::net::runtime::{Time, TokioTime};
use hickory_server::authority::{AuthorityObject, Catalog, ZoneType}; use hickory_server::server::Server as HickoryServer;
use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo}; use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo};
use hickory_server::store::forwarder::ForwardConfig; use hickory_server::store::forwarder::ForwardConfig;
use hickory_server::store::{forwarder::ForwardAuthority, in_memory::InMemoryAuthority}; use hickory_server::store::{forwarder::ForwardZoneHandler, in_memory::InMemoryZoneHandler};
use std::io; use hickory_server::zone_handler::{AxfrPolicy, Catalog, ZoneHandler, ZoneType};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
@@ -24,7 +24,7 @@ use crate::common::dns::get_default_resolver_config;
use super::config::{GeneralConfig, Record, RunConfig}; use super::config::{GeneralConfig, Record, RunConfig};
pub struct Server { pub struct Server {
server: ServerFuture<CatalogRequestHandler>, server: HickoryServer<CatalogRequestHandler>,
catalog: Arc<RwLock<Catalog>>, catalog: Arc<RwLock<Catalog>>,
general_config: GeneralConfig, general_config: GeneralConfig,
udp_local_addr: Option<SocketAddr>, udp_local_addr: Option<SocketAddr>,
@@ -52,7 +52,7 @@ impl CatalogRequestHandler {
#[async_trait::async_trait] #[async_trait::async_trait]
impl RequestHandler for CatalogRequestHandler { impl RequestHandler for CatalogRequestHandler {
async fn handle_request<R: ResponseHandler>( async fn handle_request<R: ResponseHandler, T: hickory_server::net::runtime::Time>(
&self, &self,
request: &Request, request: &Request,
response_handle: R, response_handle: R,
@@ -60,14 +60,14 @@ impl RequestHandler for CatalogRequestHandler {
self.catalog self.catalog
.read() .read()
.await .await
.handle_request(request, response_handle) .handle_request::<R, T>(request, response_handle)
.await .await
} }
} }
pub fn build_authority(domain: &str, records: &[Record]) -> Result<InMemoryAuthority> { pub fn build_authority(domain: &str, records: &[Record]) -> Result<InMemoryZoneHandler> {
let zone = rr::Name::from_str(domain)?; let zone = rr::Name::from_str(domain)?;
let mut authority = InMemoryAuthority::empty(zone, ZoneType::Primary, false); let mut authority = InMemoryZoneHandler::empty(zone, ZoneType::Primary, AxfrPolicy::Deny);
for record in records.iter() { for record in records.iter() {
let r = record.try_into()?; let r = record.try_into()?;
authority.upsert_mut(r, 0); authority.upsert_mut(r, 0);
@@ -97,18 +97,16 @@ impl Server {
.name_servers() .name_servers()
.iter() .iter()
.filter(|&x| { .filter(|&x| {
!config !config.excluded_forward_nameservers().contains(&x.ip)
.excluded_forward_nameservers()
.contains(&x.socket_addr.ip())
}) })
.cloned() .cloned()
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into(), .into(),
options: Some(system_conf.1), options: Some(system_conf.1),
}; };
let auth = ForwardAuthority::builder_with_config( let auth = ForwardZoneHandler::builder_with_config(
forward_config, forward_config,
TokioConnectionProvider::default(), TokioRuntimeProvider::default(),
) )
.build() .build()
.unwrap(); .unwrap();
@@ -117,7 +115,7 @@ impl Server {
let catalog = Arc::new(RwLock::new(catalog)); let catalog = Arc::new(RwLock::new(catalog));
let handler = CatalogRequestHandler::new(catalog.clone()); let handler = CatalogRequestHandler::new(catalog.clone());
let server = ServerFuture::new(handler); let server = HickoryServer::new(handler);
Ok(Self { Ok(Self {
server, server,
@@ -187,7 +185,7 @@ impl Server {
.with_context(|| format!("DNS Server failed to bind TCP address {}", address))?; .with_context(|| format!("DNS Server failed to bind TCP address {}", address))?;
self.tcp_local_addr = Some(tcp_listener.local_addr()?); self.tcp_local_addr = Some(tcp_listener.local_addr()?);
self.server self.server
.register_listener(tcp_listener, Duration::from_secs(5)); .register_listener(tcp_listener, Duration::from_secs(5), 1024);
} }
if let Some(address) = self.general_config.listen_udp() { if let Some(address) = self.general_config.listen_udp() {
@@ -203,11 +201,11 @@ impl Server {
Ok(()) Ok(())
} }
pub async fn upsert(&self, name: LowerName, authority: Arc<dyn AuthorityObject>) { pub async fn upsert(&self, name: LowerName, authority: Arc<dyn ZoneHandler>) {
self.catalog.write().await.upsert(name, vec![authority]); self.catalog.write().await.upsert(name, vec![authority]);
} }
pub async fn remove(&self, name: &LowerName) -> Option<Vec<Arc<dyn AuthorityObject>>> { pub async fn remove(&self, name: &LowerName) -> Option<Vec<Arc<dyn ZoneHandler>>> {
self.catalog.write().await.remove(name) self.catalog.write().await.remove(name)
} }
@@ -216,11 +214,16 @@ impl Server {
update: &Request, update: &Request,
response_edns: Option<Edns>, response_edns: Option<Edns>,
response_handle: R, response_handle: R,
) -> io::Result<ResponseInfo> { ) -> ResponseInfo {
self.catalog self.catalog
.write() .write()
.await .await
.update(update, response_edns, response_handle) .update(
update,
response_edns.as_ref(),
TokioTime::current_time(),
response_handle,
)
.await .await
} }
@@ -237,7 +240,12 @@ impl Server {
self.catalog self.catalog
.read() .read()
.await .await
.lookup(request, response_edns, response_handle) .lookup(
request,
response_edns.as_ref(),
TokioTime::current_time(),
response_handle,
)
.await .await
} }
@@ -257,11 +265,14 @@ mod tests {
GeneralConfigBuilder, RecordBuilder, RecordType, RunConfigBuilder, GeneralConfigBuilder, RecordBuilder, RecordType, RunConfigBuilder,
}; };
use anyhow::Result; use anyhow::Result;
use hickory_client::client::{Client, ClientHandle};
use hickory_proto::rr; use hickory_proto::rr;
use hickory_proto::runtime::TokioRuntimeProvider; use hickory_resolver::TokioResolver;
use hickory_proto::udp::UdpClientStream; use hickory_resolver::config::{
ConnectionConfig, NameServerConfig, ResolverConfig, ResolverOpts,
};
use hickory_resolver::net::runtime::TokioRuntimeProvider;
use maplit::hashmap; use maplit::hashmap;
use std::net::Ipv4Addr;
use std::time::Duration; use std::time::Duration;
#[tokio::test] #[tokio::test]
@@ -314,23 +325,34 @@ mod tests {
server.run().await?; server.run().await?;
let local_addr = server.udp_local_addr().unwrap(); let local_addr = server.udp_local_addr().unwrap();
let stream = UdpClientStream::builder(local_addr, TokioRuntimeProvider::default()).build(); let mut connection = ConnectionConfig::udp();
let (mut client, background) = Client::connect(stream).await?; connection.port = local_addr.port();
let background_task = tokio::spawn(background); let resolver_config = ResolverConfig::from_parts(
let response = client None,
.query( vec![],
rr::Name::from_str("www.et.internal")?, vec![NameServerConfig::new(
rr::DNSClass::IN, local_addr.ip(),
rr::RecordType::A, true,
vec![connection],
)],
);
let resolver = TokioResolver::builder_with_config(
resolver_config,
TokioRuntimeProvider::default(),
) )
.with_options(ResolverOpts::default())
.build()?;
let response = resolver
.lookup(rr::Name::from_str("www.et.internal")?, rr::RecordType::A)
.await?; .await?;
drop(background_task);
println!("Response: {:?}", response); println!("Response: {:?}", response);
assert_eq!(response.answers().len(), 1); assert_eq!(response.answers().len(), 1);
let expected_record: rr::Record = configured_record.try_into()?; let Some(rr::RData::A(ip)) = response.answers().first().map(|record| &record.data) else {
assert_eq!(response.answers().first().unwrap(), &expected_record); panic!("unexpected DNS response: {response:?}");
};
assert_eq!(ip.0, Ipv4Addr::new(123, 123, 123, 123));
server.shutdown().await?; server.shutdown().await?;
Ok(()) Ok(())
@@ -39,9 +39,10 @@ use anyhow::Context;
use cidr::Ipv4Inet; use cidr::Ipv4Inet;
use dashmap::DashMap; use dashmap::DashMap;
use hickory_proto::rr::LowerName; use hickory_proto::rr::LowerName;
use hickory_proto::serialize::binary::{BinDecodable, BinEncoder}; use hickory_proto::serialize::binary::BinEncoder;
use hickory_server::authority::{MessageRequest, MessageResponse}; use hickory_server::net::{NetError, udp as dns_udp, xfer::Protocol};
use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo}; use hickory_server::server::{Request, RequestHandler, ResponseHandler, ResponseInfo};
use hickory_server::zone_handler::MessageResponse;
use multimap::MultiMap; use multimap::MultiMap;
use pnet::packet::icmp::{IcmpTypes, MutableIcmpPacket}; use pnet::packet::icmp::{IcmpTypes, MutableIcmpPacket};
use pnet::packet::ipv4::Ipv4Packet; use pnet::packet::ipv4::Ipv4Packet;
@@ -54,7 +55,7 @@ use pnet::packet::{
}; };
use std::net::{SocketAddr, SocketAddrV4}; use std::net::{SocketAddr, SocketAddrV4};
use std::sync::Mutex; use std::sync::Mutex;
use std::{collections::BTreeMap, io, net::Ipv4Addr, str::FromStr, sync::Arc, time::Duration}; use std::{collections::BTreeMap, net::Ipv4Addr, str::FromStr, sync::Arc, time::Duration};
static NIC_PIPELINE_NAME: &str = "magic_dns_server"; static NIC_PIPELINE_NAME: &str = "magic_dns_server";
@@ -266,25 +267,21 @@ impl ResponseHandler for ResponseWrapper {
impl RecordIter<'a>, impl RecordIter<'a>,
impl RecordIter<'a>, impl RecordIter<'a>,
>, >,
) -> io::Result<ResponseInfo> { ) -> Result<ResponseInfo, NetError> {
let mut buffer = self let mut buffer = self
.response .response
.lock() .lock()
.map_err(|_| io::Error::other("lock poisoned"))?; .map_err(|_| NetError::Msg("lock poisoned".to_string()))?;
let mut encoder = BinEncoder::new(&mut buffer); let mut encoder = BinEncoder::new(&mut buffer);
// `max_size` should be u16::MAX for protocol other than UDP. // `max_size` should be u16::MAX for protocol other than UDP.
let max_size = if let Some(edns) = response.get_edns() { let max_size = dns_udp::MAX_RECEIVE_BUFFER_SIZE as u16;
edns.max_payload()
} else {
hickory_proto::udp::MAX_RECEIVE_BUFFER_SIZE as u16
};
encoder.set_max_size(max_size); encoder.set_max_size(max_size);
response response
.destructive_emit(&mut encoder) .destructive_emit(&mut encoder)
.map_err(io::Error::other) .map_err(NetError::from)
} }
} }
@@ -360,11 +357,12 @@ impl MagicDnsServerInstanceData {
( (
src_port, src_port,
dst_port, dst_port,
Request::new( Request::from_bytes(
MessageRequest::from_bytes(request_payload).ok()?, request_payload.to_vec(),
SocketAddr::from(SocketAddrV4::new(src_ip, src_port)), SocketAddr::from(SocketAddrV4::new(src_ip, src_port)),
hickory_proto::xfer::Protocol::Udp, Protocol::Udp,
), )
.ok()?,
request_payload.len(), request_payload.len(),
) )
}; };
@@ -375,7 +373,7 @@ impl MagicDnsServerInstanceData {
self.dns_server self.dns_server
.read_catalog() .read_catalog()
.await .await
.handle_request( .handle_request::<ResponseWrapper, hickory_server::net::runtime::TokioTime>(
&request, &request,
ResponseWrapper { ResponseWrapper {
response: response_payload_arc.clone(), response: response_payload_arc.clone(),
+40 -41
View File
@@ -1,13 +1,15 @@
use std::net::{Ipv4Addr, SocketAddr}; use std::net::Ipv4Addr;
use std::str::FromStr as _; use std::str::FromStr as _;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use cidr::Ipv4Inet; use cidr::Ipv4Inet;
use hickory_client::client::{Client, ClientHandle as _};
use hickory_proto::rr; use hickory_proto::rr;
use hickory_proto::runtime::TokioRuntimeProvider; use hickory_resolver::TokioResolver;
use hickory_proto::udp::UdpClientStream; use hickory_resolver::config::{
ConnectionConfig, NameServerConfig, ResolverConfig, ResolverOpts,
};
use hickory_resolver::net::runtime::TokioRuntimeProvider;
use tokio::sync::Notify; use tokio::sync::Notify;
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
@@ -66,55 +68,52 @@ pub async fn prepare_env_with_tld_dns_zone(
} }
pub async fn check_dns_record(fake_ip: &Ipv4Addr, domain: &str, expected_ip: &str) { pub async fn check_dns_record(fake_ip: &Ipv4Addr, domain: &str, expected_ip: &str) {
let stream = UdpClientStream::builder( let resolver = build_test_resolver(fake_ip);
SocketAddr::new((*fake_ip).into(), 53), let response = resolver
TokioRuntimeProvider::default(), .lookup(rr::Name::from_str(domain).unwrap(), rr::RecordType::A)
)
.build();
let (mut client, background) = Client::connect(stream).await.unwrap();
let background_task = tokio::spawn(background);
let response = client
.query(
rr::Name::from_str(domain).unwrap(),
rr::DNSClass::IN,
rr::RecordType::A,
)
.await .await
.unwrap_or_else(|e| panic!("DNS query failed unexpectedly for domain '{domain}': {e}")); .unwrap_or_else(|e| panic!("DNS query failed unexpectedly for domain '{domain}': {e}"));
background_task.abort();
let _ = background_task.await;
println!("Response: {:?}", response); println!("Response: {:?}", response);
assert_eq!(response.answers().len(), 1, "{:?}", response.answers()); assert_eq!(response.answers().len(), 1, "{:?}", response);
let resp = response.answers().first().unwrap(); let resp = response.answers().first().unwrap();
let rr::RData::A(ip) = &resp.data else {
panic!("unexpected DNS response: {response:?}");
};
assert_eq!( assert_eq!(
resp.clone().into_parts().rdata.into_a().unwrap().0, ip.0,
expected_ip.parse::<Ipv4Addr>().unwrap() expected_ip.parse::<Ipv4Addr>().unwrap()
); );
} }
pub async fn check_dns_record_missing(fake_ip: &Ipv4Addr, domain: &str) { pub async fn check_dns_record_missing(fake_ip: &Ipv4Addr, domain: &str) {
let stream = UdpClientStream::builder( let resolver = build_test_resolver(fake_ip);
SocketAddr::new((*fake_ip).into(), 53), let response = resolver
TokioRuntimeProvider::default(), .lookup(rr::Name::from_str(domain).unwrap(), rr::RecordType::A)
) .await;
.build(); if let Ok(response) = response {
let (mut client, background) = Client::connect(stream).await.unwrap(); assert!(response.answers().is_empty(), "{:?}", response);
let background_task = tokio::spawn(background); }
let response = client }
.query(
rr::Name::from_str(domain).unwrap(), fn build_test_resolver(fake_ip: &Ipv4Addr) -> TokioResolver {
rr::DNSClass::IN, let mut connection = ConnectionConfig::udp();
rr::RecordType::A, connection.port = 53;
) let config = ResolverConfig::from_parts(
.await None,
.unwrap_or_else(|e| { vec![],
panic!("DNS query for missing record failed unexpectedly for domain '{domain}': {e}") vec![NameServerConfig::new(
}); (*fake_ip).into(),
background_task.abort(); true,
let _ = background_task.await; vec![connection],
assert!(response.answers().is_empty(), "{:?}", response.answers()); )],
);
TokioResolver::builder_with_config(config, TokioRuntimeProvider::default())
.with_options(ResolverOpts::default())
.build()
.unwrap()
} }
#[tokio::test] #[tokio::test]