Fix security issues.

This commit is contained in:
2025-08-11 00:11:18 +02:00
parent bc3aa0a5b9
commit b7755c9f49
8 changed files with 135 additions and 93 deletions
+2 -2
View File
@@ -17,8 +17,8 @@ path = "src/main.rs"
[dependencies]
clap = { version = "4.4", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
trust-dns-resolver = "0.23"
trust-dns-client = "0.23"
hickory-resolver = "0.24"
hickory-client = "0.24"
socket2 = "0.5"
pnet = "0.34"
anyhow = "1.0"
+16 -16
View File
@@ -181,24 +181,24 @@ pub enum RecordTypeArg {
}
impl RecordTypeArg {
pub fn to_record_type(&self) -> Vec<trust_dns_client::rr::RecordType> {
pub fn to_record_type(&self) -> Vec<hickory_client::rr::RecordType> {
match self {
RecordTypeArg::A => vec![trust_dns_client::rr::RecordType::A],
RecordTypeArg::AAAA => vec![trust_dns_client::rr::RecordType::AAAA],
RecordTypeArg::MX => vec![trust_dns_client::rr::RecordType::MX],
RecordTypeArg::NS => vec![trust_dns_client::rr::RecordType::NS],
RecordTypeArg::TXT => vec![trust_dns_client::rr::RecordType::TXT],
RecordTypeArg::CNAME => vec![trust_dns_client::rr::RecordType::CNAME],
RecordTypeArg::SOA => vec![trust_dns_client::rr::RecordType::SOA],
RecordTypeArg::PTR => vec![trust_dns_client::rr::RecordType::PTR],
RecordTypeArg::A => vec![hickory_client::rr::RecordType::A],
RecordTypeArg::AAAA => vec![hickory_client::rr::RecordType::AAAA],
RecordTypeArg::MX => vec![hickory_client::rr::RecordType::MX],
RecordTypeArg::NS => vec![hickory_client::rr::RecordType::NS],
RecordTypeArg::TXT => vec![hickory_client::rr::RecordType::TXT],
RecordTypeArg::CNAME => vec![hickory_client::rr::RecordType::CNAME],
RecordTypeArg::SOA => vec![hickory_client::rr::RecordType::SOA],
RecordTypeArg::PTR => vec![hickory_client::rr::RecordType::PTR],
RecordTypeArg::All => vec![
trust_dns_client::rr::RecordType::A,
trust_dns_client::rr::RecordType::AAAA,
trust_dns_client::rr::RecordType::MX,
trust_dns_client::rr::RecordType::NS,
trust_dns_client::rr::RecordType::TXT,
trust_dns_client::rr::RecordType::CNAME,
trust_dns_client::rr::RecordType::SOA,
hickory_client::rr::RecordType::A,
hickory_client::rr::RecordType::AAAA,
hickory_client::rr::RecordType::MX,
hickory_client::rr::RecordType::NS,
hickory_client::rr::RecordType::TXT,
hickory_client::rr::RecordType::CNAME,
hickory_client::rr::RecordType::SOA,
],
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
use super::DnsTest;
use crate::utils::TestResult;
use trust_dns_client::rr::RecordType;
use hickory_client::rr::RecordType;
#[derive(Clone)]
pub struct DomainCategory {
+89 -44
View File
@@ -1,13 +1,13 @@
use crate::utils::{measure_time, NetworkError, Result, TestResult};
use hickory_client::rr::{Name, RData, RecordData, RecordType};
use hickory_resolver::config::*;
use hickory_resolver::system_conf;
use hickory_resolver::TokioAsyncResolver;
use std::net::{IpAddr, SocketAddr};
use std::str::FromStr;
use std::time::Duration;
use tokio::net::TcpStream;
use tokio::time::timeout;
use trust_dns_client::rr::{Name, RData, RecordData, RecordType};
use trust_dns_resolver::config::*;
use trust_dns_resolver::system_conf;
use trust_dns_resolver::TokioAsyncResolver;
pub mod categories;
pub mod queries;
@@ -290,8 +290,8 @@ impl DnsTest {
}
async fn query_system_resolver(&self) -> Result<String> {
// Try to use system DNS configuration first, fall back to default if that fails
let (config, opts) = match system_conf::read_system_conf() {
// Try to use system DNS configuration but disable search domains for accurate testing
let (mut config, mut opts) = match system_conf::read_system_conf() {
Ok((config, opts)) => (config, opts),
Err(_) => {
// Fallback to default config if system config cannot be read
@@ -300,6 +300,18 @@ impl DnsTest {
}
};
// Clear search domains to prevent automatic domain expansion during DNS testing
// This ensures we query the exact domain name provided
// Create a new config with the same name servers but no search domains
let mut clean_config = ResolverConfig::new();
for name_server in config.name_servers() {
clean_config.add_name_server(name_server.clone());
}
config = clean_config;
// Ensure we don't use search domains
opts.ndots = 0;
let resolver = TokioAsyncResolver::tokio(config, opts);
let name = Name::from_str(&self.domain)
@@ -324,35 +336,52 @@ impl DnsTest {
Ok(format!("AAAA records: {}", ips.join(", ")))
}
RecordType::MX => {
let lookup = resolver
.mx_lookup(name.clone())
.await
.map_err(|e| format!("MX lookup failed: {}", e))?;
let lookup_result = resolver.mx_lookup(name.clone()).await;
handle_dns_lookup_result(
lookup_result,
"MX",
|lookup| {
let records: Vec<String> = lookup
.iter()
.map(|mx| format!("{} {}", mx.preference(), mx.exchange()))
.collect();
Ok(format!("MX records: {}", records.join(", ")))
format!("MX records: {}", records.join(", "))
},
"(none - no mail servers configured)",
)
}
RecordType::TXT => {
let lookup = resolver
.txt_lookup(name.clone())
.await
.map_err(|e| format!("TXT lookup failed: {}", e))?;
let records: Vec<String> = lookup.iter().map(|txt| txt.to_string()).collect();
Ok(format!("TXT records: {}", records.join(", ")))
let lookup_result = resolver.txt_lookup(name.clone()).await;
handle_dns_lookup_result(
lookup_result,
"TXT",
|lookup| {
let records: Vec<String> =
lookup.iter().map(|txt| txt.to_string()).collect();
format!("TXT records: {}", records.join(", "))
},
"(none - no text records found)",
)
}
RecordType::NS => {
let lookup = resolver
.ns_lookup(name.clone())
.await
.map_err(|e| format!("NS lookup failed: {}", e))?;
let records: Vec<String> = lookup.iter().map(|ns| ns.to_string()).collect();
Ok(format!("NS records: {}", records.join(", ")))
let lookup_result = resolver.ns_lookup(name.clone()).await;
handle_dns_lookup_result(
lookup_result,
"NS",
|lookup| {
let records: Vec<String> =
lookup.iter().map(|ns| ns.to_string()).collect();
format!("NS records: {}", records.join(", "))
},
"(none - no name servers found)",
)
}
RecordType::CNAME => {
match resolver.lookup(name.clone(), self.record_type).await {
Ok(lookup) => {
let lookup_result = resolver.lookup(name.clone(), self.record_type).await;
handle_dns_lookup_result(
lookup_result,
"CNAME",
|lookup| {
let records: Vec<String> = lookup
.iter()
.filter_map(|record| {
@@ -363,24 +392,17 @@ impl DnsTest {
}
})
.collect();
Ok(format!("CNAME records: {}", records.join(", ")))
}
Err(e) => {
// Check if this is just "no records found" which is normal for domains without CNAME
let error_str = e.to_string();
if error_str.contains("no record found") {
Ok("CNAME records: (none - domain is not an alias)".to_string())
} else {
Err(format!("CNAME lookup failed: {}", e))
}
}
}
format!("CNAME records: {}", records.join(", "))
},
"(none - domain is not an alias)",
)
}
RecordType::SOA => {
let lookup = resolver
.soa_lookup(name.clone())
.await
.map_err(|e| format!("SOA lookup failed: {}", e))?;
let lookup_result = resolver.soa_lookup(name.clone()).await;
handle_dns_lookup_result(
lookup_result,
"SOA",
|lookup| {
let records: Vec<String> = lookup
.iter()
.map(|soa| {
@@ -396,7 +418,10 @@ impl DnsTest {
)
})
.collect();
Ok(format!("SOA records: {}", records.join(", ")))
format!("SOA records: {}", records.join(", "))
},
"(none - no authority records found)",
)
}
RecordType::PTR => {
let lookup = resolver
@@ -435,9 +460,9 @@ impl DnsTest {
async fn query_specific_server(&self, server: SocketAddr) -> Result<String> {
// Create a resolver configuration that uses the specific server
let mut config = ResolverConfig::new();
config.add_name_server(trust_dns_resolver::config::NameServerConfig::new(
config.add_name_server(hickory_resolver::config::NameServerConfig::new(
server,
trust_dns_resolver::config::Protocol::Udp,
hickory_resolver::config::Protocol::Udp,
));
let opts = ResolverOpts::default();
@@ -696,3 +721,23 @@ pub fn debug_dns_config() -> String {
Err(e) => format!("❌ Could not read system DNS config: {}", e),
}
}
/// Helper function to handle DNS lookup results consistently across all record types
fn handle_dns_lookup_result<T>(
result: std::result::Result<T, hickory_resolver::error::ResolveError>,
record_type: &str,
format_success: impl FnOnce(T) -> String,
empty_message: &str,
) -> std::result::Result<String, String> {
match result {
Ok(lookup_result) => Ok(format_success(lookup_result)),
Err(e) => {
let error_str = e.to_string();
if error_str.contains("no record found") || error_str.contains("Name not found") {
Ok(format!("{} records: {}", record_type, empty_message))
} else {
Err(format!("{} lookup failed: {}", record_type, e))
}
}
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
use super::DnsTest;
use crate::utils::TestResult;
use trust_dns_client::rr::RecordType;
use hickory_client::rr::RecordType;
pub async fn comprehensive_dns_test(domain: &str) -> Vec<TestResult> {
let mut results = Vec::new();
+1 -1
View File
@@ -285,7 +285,7 @@ async fn handle_full_test(
// DNS servers test
let dns_servers =
dns::test_common_dns_servers(&target, trust_dns_client::rr::RecordType::A).await;
dns::test_common_dns_servers(&target, hickory_client::rr::RecordType::A).await;
all_results.extend(dns_servers);
pb.inc(1);
+2 -2
View File
@@ -78,8 +78,8 @@ impl NetworkTest {
}
async fn resolve_target(&self) -> Result<IpAddr> {
use trust_dns_resolver::config::*;
use trust_dns_resolver::TokioAsyncResolver;
use hickory_resolver::config::*;
use hickory_resolver::TokioAsyncResolver;
let resolver =
TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default());
+3 -6
View File
@@ -33,10 +33,7 @@ async fn test_network_udp_connectivity() {
#[tokio::test]
async fn test_dns_query() {
let test = dns::DnsTest::new(
"google.com".to_string(),
trust_dns_client::rr::RecordType::A,
)
let test = dns::DnsTest::new("google.com".to_string(), hickory_client::rr::RecordType::A)
.with_timeout(Duration::from_secs(10));
let result = test.run().await;
@@ -48,7 +45,7 @@ async fn test_dns_query() {
#[tokio::test]
async fn test_dns_servers() {
let results =
dns::test_common_dns_servers("google.com", trust_dns_client::rr::RecordType::A).await;
dns::test_common_dns_servers("google.com", hickory_client::rr::RecordType::A).await;
assert!(!results.is_empty());
let successful_results = results.iter().filter(|r| r.success).count();
@@ -83,7 +80,7 @@ async fn test_comprehensive_dns() {
async fn test_domain_categories() {
let results = dns::categories::test_domain_category(
&dns::categories::NORMAL_SITES,
trust_dns_client::rr::RecordType::A,
hickory_client::rr::RecordType::A,
)
.await;