Skip to main content

nd_300/diagnostics/
reverse_dns.rs

1use serde::Serialize;
2
3use super::shared_cache::SharedCache;
4
5#[derive(Debug, Clone, Serialize)]
6pub struct ReverseDnsEntry {
7    pub ip: String,
8    pub hostname: Option<String>,
9    pub label: String,
10}
11
12pub async fn collect_with_cache(cache: &SharedCache) -> Option<Vec<ReverseDnsEntry>> {
13    let mut entries = Vec::new();
14
15    let gateway_ip = cache.gateway_ip.clone();
16
17    let mut ips_to_check: Vec<(String, String)> = Vec::new();
18
19    if let Some(ref gw) = gateway_ip {
20        ips_to_check.push((gw.clone(), "Gateway".to_string()));
21    }
22
23    ips_to_check.push(("1.1.1.1".to_string(), "Cloudflare DNS".to_string()));
24    ips_to_check.push(("8.8.8.8".to_string(), "Google DNS".to_string()));
25
26    for (ip, label) in ips_to_check {
27        let hostname = reverse_lookup(&ip).await;
28        entries.push(ReverseDnsEntry {
29            ip,
30            hostname,
31            label,
32        });
33    }
34
35    if entries.is_empty() {
36        None
37    } else {
38        Some(entries)
39    }
40}
41
42pub async fn collect() -> Option<Vec<ReverseDnsEntry>> {
43    let mut entries = Vec::new();
44
45    // Get gateway IP
46    let gateway_ip = default_net::get_default_gateway()
47        .ok()
48        .map(|gw| gw.ip_addr.to_string());
49
50    // Collect IPs to reverse-lookup
51    let mut ips_to_check: Vec<(String, String)> = Vec::new();
52
53    if let Some(ref gw) = gateway_ip {
54        ips_to_check.push((gw.clone(), "Gateway".to_string()));
55    }
56
57    ips_to_check.push(("1.1.1.1".to_string(), "Cloudflare DNS".to_string()));
58    ips_to_check.push(("8.8.8.8".to_string(), "Google DNS".to_string()));
59
60    for (ip, label) in ips_to_check {
61        let hostname = reverse_lookup(&ip).await;
62        entries.push(ReverseDnsEntry {
63            ip,
64            hostname,
65            label,
66        });
67    }
68
69    if entries.is_empty() {
70        None
71    } else {
72        Some(entries)
73    }
74}
75
76async fn reverse_lookup(ip: &str) -> Option<String> {
77    #[cfg(windows)]
78    {
79        let mut cmd = tokio::process::Command::new("nslookup");
80        cmd.args([ip]);
81        let output = super::util::run_with_timeout(cmd, super::util::SLOW).await?;
82
83        let text = String::from_utf8_lossy(&output.stdout);
84        for line in text.lines() {
85            if line.trim().starts_with("Name:") {
86                return line.split(':').nth(1).map(|s| s.trim().to_string());
87            }
88        }
89        None
90    }
91
92    #[cfg(unix)]
93    {
94        let mut cmd = tokio::process::Command::new("dig");
95        cmd.args(["-x", ip, "+short"]);
96        let output = super::util::run_with_timeout(cmd, super::util::SLOW).await?;
97
98        let text = String::from_utf8_lossy(&output.stdout).trim().to_string();
99        if text.is_empty() {
100            None
101        } else {
102            Some(text.trim_end_matches('.').to_string())
103        }
104    }
105}