Skip to main content

veilid_tools/resolver/non_windows/
mod.rs

1use super::*;
2
3cfg_if! {
4    if #[cfg(feature="rt-async-std")] {
5        mod async_std_providers;
6        use async_std_providers::{*, AsyncStdResolver as AsyncResolver};
7
8        use hickory_resolver::{config, system_conf::read_system_conf};
9
10        fn resolver(
11            config: config::ResolverConfig,
12            options: config::ResolverOpts,
13        ) -> AsyncResolver {
14            AsyncResolver::builder_with_config(config, AsyncStdConnectionProvider::default()).with_options(options).build()
15        }
16
17    } else if #[cfg(feature="rt-tokio")] {
18        use hickory_resolver::{config, name_server::TokioConnectionProvider, TokioResolver as AsyncResolver, system_conf::read_system_conf};
19
20        fn resolver(
21            config: config::ResolverConfig,
22            options: config::ResolverOpts,
23        ) -> AsyncResolver {
24            AsyncResolver::builder_with_config(config, TokioConnectionProvider::default()).with_options(options).build()
25        }
26    } else {
27        compile_error!("needs executor implementation");
28    }
29}
30
31struct Resolvers {
32    system: Option<Arc<AsyncResolver>>,
33    default: Arc<AsyncResolver>,
34}
35
36static RESOLVERS: LazyLock<AsyncMutex<Option<Arc<Resolvers>>>> =
37    LazyLock::new(|| AsyncMutex::new(None));
38
39async fn with_resolvers<R, F: FnOnce(Arc<Resolvers>) -> PinBoxFutureStatic<R>>(closure: F) -> R {
40    let mut resolvers_lock = RESOLVERS.lock().await;
41    if let Some(r) = &*resolvers_lock {
42        return closure(r.clone()).await;
43    }
44
45    let (config, mut options) = (
46        config::ResolverConfig::default(),
47        config::ResolverOpts::default(),
48    );
49    options.try_tcp_on_error = true;
50    let default = Arc::new(resolver(config, options));
51
52    let system = if let Ok((config, options)) = read_system_conf() {
53        Some(Arc::new(resolver(config, options)))
54    } else {
55        None
56    };
57    let resolvers = Arc::new(Resolvers { system, default });
58    *resolvers_lock = Some(resolvers.clone());
59    closure(resolvers).await
60}
61
62pub struct Resolver {}
63
64impl Resolver {
65    pub async fn txt_lookup<S: AsRef<str>>(host: S) -> Result<Vec<String>, ResolverError> {
66        let host = host.as_ref().to_string();
67        let txt_result = with_resolvers(|resolvers| {
68            Box::pin(async move {
69                // Try the system resolver config
70                if let Some(system_resolver) = &resolvers.system {
71                    match system_resolver.txt_lookup(&host).await {
72                        Ok(v) => {
73                            return Ok(v);
74                        }
75                        Err(e) => {
76                            debug!("system resolver txt_lookup error: {}", e);
77                        }
78                    }
79                }
80
81                match resolvers.default.txt_lookup(&host).await {
82                    Ok(v) => Ok(v),
83                    Err(e) => Err(ResolverError::Generic(format!(
84                        "default resolver txt_lookup error: {}",
85                        e
86                    ))),
87                }
88            })
89        })
90        .await?;
91
92        let mut out = Vec::new();
93        for x in txt_result.iter() {
94            let mut record_out = Vec::<u8>::new();
95            for txtd in x.txt_data() {
96                record_out.extend_from_slice(txtd);
97            }
98            if let Ok(s) = String::from_utf8(record_out) {
99                out.push(s);
100            }
101        }
102        Ok(out)
103    }
104
105    pub async fn ptr_lookup(ip_addr: IpAddr) -> Result<String, ResolverError> {
106        let ptr_result = with_resolvers(|resolvers| {
107            Box::pin(async move {
108                // Try the system resolver config
109                if let Some(system_resolver) = &resolvers.system {
110                    match system_resolver.reverse_lookup(ip_addr).await {
111                        Ok(v) => {
112                            return Ok(v);
113                        }
114                        Err(e) => {
115                            debug!("system resolver ptr_lookup error: {}", e);
116                        }
117                    }
118                }
119
120                match resolvers.default.reverse_lookup(ip_addr).await {
121                    Ok(v) => Ok(v),
122                    Err(e) => Err(ResolverError::Generic(format!(
123                        "default resolver ptr_lookup error: {}",
124                        e
125                    ))),
126                }
127            })
128        })
129        .await?;
130
131        if let Some(r) = ptr_result.iter().next() {
132            Ok(r.to_string().trim_end_matches('.').to_string())
133        } else {
134            Err(ResolverError::Generic(
135                "PTR lookup returned an empty string".to_string(),
136            ))
137        }
138    }
139}