use trust_dns_resolver::config::*;
use trust_dns_resolver::TokioAsyncResolver;
use trust_dns_resolver::proto::rr::{RecordType, RData};
use std::net::IpAddr;
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone)]
pub enum DnsRecord {
A(String), AAAA(String), CNAME(String), NS(String), MX(u16, String), TXT(String), SOA(String), PTR(String), }
#[derive(Debug, Clone)]
pub struct DnsResolveResult {
pub domain: String,
pub records: HashMap<String, Vec<DnsRecord>>,
pub has_records: bool,
}
pub struct DnsResolver {
resolver: TokioAsyncResolver,
}
impl DnsResolver {
pub async fn new() -> Result<Self, Box<dyn std::error::Error>> {
let resolver = TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default());
Ok(DnsResolver { resolver })
}
pub async fn resolve_all_records(&self, domain: &str) -> DnsResolveResult {
let mut records = HashMap::new();
let mut has_records = false;
if let Ok(response) = self.resolver.lookup_ip(domain).await {
let mut a_records = Vec::new();
for ip in response.iter() {
match ip {
IpAddr::V4(ipv4) => {
a_records.push(DnsRecord::A(ipv4.to_string()));
has_records = true;
}
IpAddr::V6(ipv6) => {
a_records.push(DnsRecord::AAAA(ipv6.to_string()));
has_records = true;
}
}
}
if !a_records.is_empty() {
records.insert("A/AAAA".to_string(), a_records);
}
}
if let Ok(response) = self.resolver.lookup(domain, RecordType::CNAME).await {
let mut cname_records = Vec::new();
for record in response.iter() {
match record {
RData::CNAME(cname) => {
cname_records.push(DnsRecord::CNAME(cname.to_string()));
has_records = true;
}
_ => {}
}
}
if !cname_records.is_empty() {
records.insert("CNAME".to_string(), cname_records);
}
}
if let Ok(response) = self.resolver.lookup(domain, RecordType::NS).await {
let mut ns_records = Vec::new();
for record in response.iter() {
match record {
RData::NS(ns) => {
ns_records.push(DnsRecord::NS(ns.to_string()));
has_records = true;
}
_ => {}
}
}
if !ns_records.is_empty() {
records.insert("NS".to_string(), ns_records);
}
}
if let Ok(response) = self.resolver.lookup(domain, RecordType::MX).await {
let mut mx_records = Vec::new();
for record in response.iter() {
match record {
RData::MX(mx) => {
mx_records.push(DnsRecord::MX(mx.preference(), mx.exchange().to_string()));
has_records = true;
}
_ => {}
}
}
if !mx_records.is_empty() {
records.insert("MX".to_string(), mx_records);
}
}
if let Ok(response) = self.resolver.lookup(domain, RecordType::TXT).await {
let mut txt_records = Vec::new();
for record in response.iter() {
match record {
RData::TXT(txt) => {
let txt_data = txt.iter()
.map(|bytes| String::from_utf8_lossy(bytes).to_string())
.collect::<Vec<_>>()
.join("");
txt_records.push(DnsRecord::TXT(txt_data));
has_records = true;
}
_ => {}
}
}
if !txt_records.is_empty() {
records.insert("TXT".to_string(), txt_records);
}
}
if let Ok(response) = self.resolver.lookup(domain, RecordType::SOA).await {
let mut soa_records = Vec::new();
for record in response.iter() {
match record {
RData::SOA(soa) => {
soa_records.push(DnsRecord::SOA(format!("{} {}",
soa.mname(), soa.rname())));
has_records = true;
}
_ => {}
}
}
if !soa_records.is_empty() {
records.insert("SOA".to_string(), soa_records);
}
}
DnsResolveResult {
domain: domain.to_string(),
records,
has_records,
}
}
pub async fn resolve_domains(&self, domains: Vec<String>) -> Vec<DnsResolveResult> {
let mut results = Vec::new();
let semaphore = Arc::new(tokio::sync::Semaphore::new(20));
let mut tasks = Vec::new();
for domain in domains {
let permit = Arc::clone(&semaphore);
let resolver = self.new_resolver().await;
let task = tokio::spawn(async move {
match permit.acquire().await {
Ok(_permit) => resolver.resolve_all_records(&domain).await,
Err(e) => {
eprintln!("错误: 获取信号量失败: {}", e);
DnsResolveResult {
domain,
records: HashMap::new(),
has_records: false,
}
}
}
});
tasks.push(task);
}
for task in tasks {
if let Ok(result) = task.await {
results.push(result);
}
}
results
}
pub fn display_results(&self, results: &[DnsResolveResult]) {
println!("=== DNS解析结果 ===");
for result in results {
if result.has_records {
println!("域名: {}", result.domain);
for (record_type, records) in &result.records {
println!(" {}:", record_type);
for record in records {
match record {
DnsRecord::A(ip) => println!(" {}", ip),
DnsRecord::AAAA(ip) => println!(" {}", ip),
DnsRecord::CNAME(cname) => println!(" {}", cname),
DnsRecord::NS(ns) => println!(" {}", ns),
DnsRecord::MX(priority, host) => println!(" {} {}", priority, host),
DnsRecord::TXT(txt) => println!(" \"{}\"", txt),
DnsRecord::SOA(soa) => println!(" {}", soa),
DnsRecord::PTR(ptr) => println!(" {}", ptr),
}
}
}
println!();
}
}
}
async fn new_resolver(&self) -> DnsResolver {
DnsResolver {
resolver: TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default()),
}
}
pub async fn resolve_a_record(&self, domain: &str) -> Option<String> {
if let Ok(response) = self.resolver.lookup_ip(domain).await {
for ip in response.iter() {
if let IpAddr::V4(ipv4) = ip {
return Some(ipv4.to_string());
}
}
}
None
}
}