use anyhow::Result;
use hickory_resolver::config::{ResolverConfig, ResolverOpts};
use hickory_resolver::TokioAsyncResolver;
use serde::{Deserialize, Serialize};
use std::time::Instant;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DnsResult {
pub domain: String,
pub resolution_time_ms: f64,
pub resolved_ips: Vec<String>,
}
pub async fn measure_dns(domain: &str) -> Result<DnsResult> {
let resolver = TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default());
let start = Instant::now();
let response = resolver.lookup_ip(domain).await?;
let elapsed = start.elapsed();
let resolved_ips: Vec<String> = response.iter().map(|ip| ip.to_string()).collect();
Ok(DnsResult {
domain: domain.to_string(),
resolution_time_ms: elapsed.as_secs_f64() * 1000.0,
resolved_ips,
})
}
#[allow(dead_code)]
pub async fn measure_dns_with_server(domain: &str, dns_server: &str) -> Result<DnsResult> {
use hickory_resolver::config::{NameServerConfig, Protocol};
use std::net::SocketAddr;
let socket_addr: SocketAddr = format!("{}:53", dns_server).parse()?;
let name_server = NameServerConfig::new(socket_addr, Protocol::Udp);
let mut config = ResolverConfig::new();
config.add_name_server(name_server);
let resolver = TokioAsyncResolver::tokio(config, ResolverOpts::default());
let start = Instant::now();
let response = resolver.lookup_ip(domain).await?;
let elapsed = start.elapsed();
let resolved_ips: Vec<String> = response.iter().map(|ip| ip.to_string()).collect();
Ok(DnsResult {
domain: domain.to_string(),
resolution_time_ms: elapsed.as_secs_f64() * 1000.0,
resolved_ips,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_dns_lookup() {
let result = measure_dns("google.com").await;
assert!(result.is_ok());
let dns = result.unwrap();
assert!(dns.resolution_time_ms > 0.0);
assert!(!dns.resolved_ips.is_empty());
}
}