veilid_tools/resolver/non_windows/
mod.rs1use 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 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 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}