#![recursion_limit = "128"]
#[cfg(all(feature = "tokio-runtime", feature = "system-config"))]
use {
futures_util::future,
once_cell::sync::Lazy,
std::fmt::Display,
std::io,
std::net::SocketAddr,
std::task::Poll,
trust_dns_resolver::name_server::TokioConnectionProvider,
trust_dns_resolver::TokioAsyncResolver,
trust_dns_resolver::{IntoName, TryParseIp},
};
#[cfg(all(feature = "tokio-runtime", feature = "system-config"))]
static GLOBAL_DNS_RESOLVER: Lazy<TokioAsyncResolver> = Lazy::new(|| {
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
let pair = Arc::new((Mutex::new(None::<TokioAsyncResolver>), Condvar::new()));
let pair2 = pair.clone();
thread::spawn(move || {
let runtime = tokio::runtime::Runtime::new().expect("failed to launch Runtime");
let resolver = {
#[cfg(any(unix, windows))]
{
TokioAsyncResolver::from_system_conf(TokioConnectionProvider::default())
}
#[cfg(not(any(unix, windows)))]
{
use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
TokioAsyncResolver::new(
ResolverConfig::google(),
ResolverOpts::default(),
runtime.handle().clone(),
)
}
};
let (lock, cvar) = &*pair2;
let mut started = lock.lock().unwrap();
let resolver = resolver.expect("failed to create trust-dns-resolver");
*started = Some(resolver);
cvar.notify_one();
drop(started);
runtime.block_on(future::poll_fn(|_cx| Poll::<()>::Pending))
});
let (lock, cvar) = &*pair;
let mut resolver = lock.lock().unwrap();
while resolver.is_none() {
resolver = cvar.wait(resolver).unwrap();
}
let resolver = resolver.take();
resolver.expect("resolver should not be none")
});
#[cfg(all(feature = "tokio-runtime", feature = "system-config"))]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "tokio-runtime", feature = "system-config")))
)]
pub async fn resolve<N: IntoName + Display + TryParseIp + 'static>(
host: N,
port: u16,
) -> io::Result<Vec<SocketAddr>> {
let name = host.to_string();
let result = GLOBAL_DNS_RESOLVER.lookup_ip(host).await;
result
.map_err(move |err| {
io::Error::new(
io::ErrorKind::AddrNotAvailable,
format!("dns resolution error for {name}: {err}"),
)
})
.map(move |lookup_ip| {
lookup_ip
.iter()
.map(|ip| SocketAddr::new(ip, port))
.collect::<Vec<_>>()
})
}
#[cfg(all(feature = "tokio-runtime", feature = "system-config"))]
fn main() {
use std::thread;
let names = &["www.google.com", "www.reddit.com", "www.wikipedia.org"];
let threads = names
.iter()
.map(|name| {
let join = thread::spawn(move || {
let runtime = tokio::runtime::Runtime::new().expect("failed to launch Runtime");
runtime.block_on(resolve(*name, 443))
});
(name, join)
})
.collect::<Vec<_>>();
for (name, join) in threads {
let result = join
.join()
.expect("resolution thread failed")
.expect("resolution failed");
println!("{name} resolved to {result:?}");
}
}
#[cfg(not(all(feature = "tokio-runtime", feature = "system-config")))]
fn main() {
println!("tokio-runtime and system-config feature must be enabled")
}
#[test]
fn test_global_resolver() {
main()
}