web_analyzer/
domain_dns.rs1use serde::{Deserialize, Serialize};
2use std::time::Instant;
3use tokio::process::Command;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct DnsRecords {
9 pub a: Vec<String>,
10 pub aaaa: Vec<String>,
11 pub mx: Vec<String>,
12 pub ns: Vec<String>,
13 pub soa: Vec<String>,
14 pub txt: Vec<String>,
15 pub cname: Vec<String>,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct DomainDnsResult {
20 pub timestamp: String,
21 pub domain: String,
22 pub records: DnsRecords,
23 pub response_time_ms: u128,
24}
25
26async fn resolve_record(domain: &str, record_type: &str) -> Vec<String> {
29 if let Ok(output) = Command::new("dig")
30 .arg("+short")
31 .arg(record_type)
32 .arg(domain)
33 .output()
34 .await
35 {
36 if let Ok(text) = String::from_utf8(output.stdout) {
37 return text
38 .lines()
39 .filter_map(|s| {
40 let s = s.trim();
41 if !s.is_empty() && !s.starts_with(';') {
42 Some(s.to_string())
43 } else {
44 None
45 }
46 })
47 .collect();
48 }
49 }
50 vec![]
51}
52
53pub async fn get_dns_records(
56 domain: &str,
57 progress_tx: Option<tokio::sync::mpsc::Sender<crate::ScanProgress>>,
58) -> Result<DomainDnsResult, Box<dyn std::error::Error + Send + Sync>> {
59 let start_time = Instant::now();
60
61 if let Some(t) = &progress_tx { let _ = t.send(crate::ScanProgress { module: "Domain DNS".into(), percentage: 5.0, message: "Starting parallel DNS resolution...".into(), status: "Info".into() }).await; }
62
63 let (a, aaaa, mx, ns, soa, txt, cname) = tokio::join!(
65 async {
66 let res = resolve_record(domain, "A").await;
67 if let Some(t) = &progress_tx { let _ = t.send(crate::ScanProgress { module: "Domain DNS".into(), percentage: 15.0, message: "Resolved A records".into(), status: "Success".into() }).await; }
68 res
69 },
70 async {
71 let res = resolve_record(domain, "AAAA").await;
72 if let Some(t) = &progress_tx { let _ = t.send(crate::ScanProgress { module: "Domain DNS".into(), percentage: 30.0, message: "Resolved AAAA records".into(), status: "Success".into() }).await; }
73 res
74 },
75 async {
76 let res = resolve_record(domain, "MX").await;
77 if let Some(t) = &progress_tx { let _ = t.send(crate::ScanProgress { module: "Domain DNS".into(), percentage: 45.0, message: "Resolved MX records".into(), status: "Success".into() }).await; }
78 res
79 },
80 async {
81 let res = resolve_record(domain, "NS").await;
82 if let Some(t) = &progress_tx { let _ = t.send(crate::ScanProgress { module: "Domain DNS".into(), percentage: 60.0, message: "Resolved NS records".into(), status: "Success".into() }).await; }
83 res
84 },
85 async {
86 let res = resolve_record(domain, "SOA").await;
87 if let Some(t) = &progress_tx { let _ = t.send(crate::ScanProgress { module: "Domain DNS".into(), percentage: 75.0, message: "Resolved SOA records".into(), status: "Success".into() }).await; }
88 res
89 },
90 async {
91 let res = resolve_record(domain, "TXT").await;
92 if let Some(t) = &progress_tx { let _ = t.send(crate::ScanProgress { module: "Domain DNS".into(), percentage: 90.0, message: "Resolved TXT records".into(), status: "Success".into() }).await; }
93 res
94 },
95 async {
96 let res = resolve_record(domain, "CNAME").await;
97 if let Some(t) = &progress_tx { let _ = t.send(crate::ScanProgress { module: "Domain DNS".into(), percentage: 100.0, message: "Resolved CNAME records".into(), status: "Success".into() }).await; }
98 res
99 },
100 );
101
102 let duration = start_time.elapsed().as_millis();
103
104 Ok(DomainDnsResult {
105 timestamp: chrono::Utc::now().to_rfc3339(),
106 domain: domain.to_string(),
107 records: DnsRecords {
108 a,
109 aaaa,
110 mx,
111 ns,
112 soa,
113 txt,
114 cname,
115 },
116 response_time_ms: duration,
117 })
118}