Skip to main content

web_analyzer/
domain_dns_mobile.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize)]
4pub struct DnsRecords {
5    pub a: Vec<String>,
6    pub aaaa: Vec<String>,
7    pub mx: Vec<String>,
8    pub ns: Vec<String>,
9    pub soa: Vec<String>,
10    pub txt: Vec<String>,
11    pub cname: Vec<String>,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct DomainDnsResult {
16    pub timestamp: String,
17    pub domain: String,
18    pub records: DnsRecords,
19    pub response_time_ms: u128,
20}
21
22use chrono::Utc;
23use hickory_resolver::config::*;
24use hickory_resolver::AsyncResolver;
25use std::time::Instant;
26
27pub async fn get_dns_records(
28    domain: &str,
29    progress_tx: Option<tokio::sync::mpsc::Sender<crate::ScanProgress>>,
30) -> Result<DomainDnsResult, Box<dyn std::error::Error + Send + Sync>> {
31    let start_time = Instant::now();
32
33    if let Some(t) = &progress_tx {
34        let _ = t
35            .send(crate::ScanProgress {
36                module: "DNS Records".into(),
37                percentage: 10.0,
38                message: "Initializing native DNS resolver".into(),
39                status: "Info".into(),
40            })
41            .await;
42    }
43
44    // Using Google or Cloudflare as fallback
45    let resolver = AsyncResolver::tokio(ResolverConfig::cloudflare(), ResolverOpts::default());
46
47    let mut records_meta = DnsRecords {
48        a: vec![],
49        aaaa: vec![],
50        mx: vec![],
51        ns: vec![],
52        soa: vec![],
53        txt: vec![],
54        cname: vec![],
55    };
56
57    if let Some(t) = &progress_tx {
58        let _ = t
59            .send(crate::ScanProgress {
60                module: "DNS Records".into(),
61                percentage: 30.0,
62                message: "Looking up A and AAAA records".into(),
63                status: "Info".into(),
64            })
65            .await;
66    }
67
68    if let Ok(response) = resolver.ipv4_lookup(domain).await {
69        for ip in response.iter() {
70            records_meta.a.push(ip.to_string());
71        }
72    }
73
74    if let Ok(response) = resolver.ipv6_lookup(domain).await {
75        for ip in response.iter() {
76            records_meta.aaaa.push(ip.to_string());
77        }
78    }
79
80    if let Some(t) = &progress_tx {
81        let _ = t
82            .send(crate::ScanProgress {
83                module: "DNS Records".into(),
84                percentage: 50.0,
85                message: "Looking up MX and NS records".into(),
86                status: "Info".into(),
87            })
88            .await;
89    }
90
91    if let Ok(response) = resolver.mx_lookup(domain).await {
92        for mx in response.iter() {
93            records_meta.mx.push(format!("{} {}", mx.preference(), mx.exchange()));
94        }
95    }
96
97    if let Ok(response) = resolver.ns_lookup(domain).await {
98        for ns in response.iter() {
99            records_meta.ns.push(ns.to_string());
100        }
101    }
102
103    if let Some(t) = &progress_tx {
104        let _ = t
105            .send(crate::ScanProgress {
106                module: "DNS Records".into(),
107                percentage: 70.0,
108                message: "Looking up TXT records".into(),
109                status: "Info".into(),
110            })
111            .await;
112    }
113
114    if let Ok(response) = resolver.txt_lookup(domain).await {
115        for txt in response.iter() {
116            records_meta.txt.push(txt.to_string());
117        }
118    }
119
120    if let Some(t) = &progress_tx {
121        let _ = t
122            .send(crate::ScanProgress {
123                module: "DNS Records".into(),
124                percentage: 90.0,
125                message: "Looking up CNAME and SOA records".into(),
126                status: "Info".into(),
127            })
128            .await;
129    }
130
131    use hickory_resolver::proto::rr::RecordType;
132
133    if let Ok(response) = resolver.lookup(domain, RecordType::CNAME).await {
134        for record in response.iter() {
135            if let Some(cname) = record.as_cname() {
136                records_meta.cname.push(cname.to_string());
137            }
138        }
139    }
140
141    if let Ok(response) = resolver.lookup(domain, RecordType::SOA).await {
142        for record in response.iter() {
143            if let Some(soa) = record.as_soa() {
144                records_meta.soa.push(format!("{} {}", soa.mname(), soa.rname()));
145            }
146        }
147    }
148
149    let response_time_ms = start_time.elapsed().as_millis();
150
151    if let Some(t) = &progress_tx {
152        let _ = t
153            .send(crate::ScanProgress {
154                module: "DNS Records".into(),
155                percentage: 100.0,
156                message: "DNS analysis completed successfully".into(),
157                status: "Info".into(),
158            })
159            .await;
160    }
161
162    Ok(DomainDnsResult {
163        timestamp: Utc::now().to_rfc3339(),
164        domain: domain.to_string(),
165        records: records_meta,
166        response_time_ms,
167    })
168}