#![allow(static_mut_refs)]
use std::io::{Result, Error};
use std::net::SocketAddr;
use hickory_resolver as resolver;
use resolver::TokioResolver;
use resolver::system_conf::read_system_conf;
use resolver::lookup_ip::{LookupIp, LookupIpIter};
use resolver::config::{ResolverOpts, ResolverConfig};
#[cfg(not(feature = "multi-thread"))]
use once_cell::unsync::{OnceCell, Lazy};
#[cfg(feature = "multi-thread")]
use once_cell::{unsync::OnceCell, sync::Lazy};
use crate::endpoint::RemoteAddr;
pub mod config {
use super::resolver;
pub use resolver::config::*;
pub use resolver::proto::xfer::Protocol;
}
#[derive(Debug, Clone)]
pub struct DnsConf {
pub conf: ResolverConfig,
pub opts: ResolverOpts,
}
impl Default for DnsConf {
fn default() -> Self {
#[cfg(any(all(unix, not(target_os = "android")), windows))]
let (conf, opts) = read_system_conf().unwrap_or_default();
#[cfg(not(any(all(unix, not(target_os = "android")), windows)))]
let (conf, opts) = Default::default();
Self { conf, opts }
}
}
static mut DNS_CONF: OnceCell<DnsConf> = OnceCell::new();
static mut DNS: Lazy<TokioResolver> = Lazy::new(|| {
use resolver::proto::runtime::TokioRuntimeProvider as Tokio;
use resolver::name_server::GenericConnector as Connect;
let DnsConf { conf, opts } = unsafe { DNS_CONF.take().unwrap() };
TokioResolver::builder_with_config(conf, Connect::new(Tokio::new()))
.with_options(opts)
.build()
});
pub fn force_init() {
use std::ptr;
unsafe {
Lazy::force(&*ptr::addr_of!(DNS));
}
}
pub fn build(conf: Option<ResolverConfig>, opts: Option<ResolverOpts>) {
build_lazy(conf, opts);
force_init();
}
pub fn build_lazy(conf: Option<ResolverConfig>, opts: Option<ResolverOpts>) {
let mut dns_conf = DnsConf::default();
if let Some(conf) = conf {
dns_conf.conf = conf;
}
if let Some(opts) = opts {
dns_conf.opts = opts;
}
unsafe {
DNS_CONF.set(dns_conf).unwrap();
}
}
pub async fn resolve_ip(ip: &str) -> Result<LookupIp> {
unsafe { DNS.lookup_ip(ip).await.map_or_else(|e| Err(Error::other(e)), Ok) }
}
pub async fn resolve_addr(addr: &RemoteAddr) -> Result<LookupRemoteAddr<'_>> {
use RemoteAddr::*;
use LookupRemoteAddr::*;
match addr {
SocketAddr(addr) => Ok(NoLookup(addr)),
DomainName(ip, port) => resolve_ip(ip).await.map(|ip| Dolookup(ip, *port)),
}
}
pub enum LookupRemoteAddr<'a> {
NoLookup(&'a SocketAddr),
Dolookup(LookupIp, u16),
}
impl LookupRemoteAddr<'_> {
pub fn iter(&self) -> LookupRemoteAddrIter<'_> {
use LookupRemoteAddr::*;
match self {
NoLookup(addr) => LookupRemoteAddrIter::NoLookup(std::iter::once(addr)),
Dolookup(ip, port) => LookupRemoteAddrIter::DoLookup(ip.iter(), *port),
}
}
}
pub enum LookupRemoteAddrIter<'a> {
NoLookup(std::iter::Once<&'a SocketAddr>),
DoLookup(LookupIpIter<'a>, u16),
}
impl Iterator for LookupRemoteAddrIter<'_> {
type Item = SocketAddr;
fn next(&mut self) -> Option<Self::Item> {
use LookupRemoteAddrIter::*;
match self {
NoLookup(addr) => addr.next().copied(),
DoLookup(ip, port) => ip.next().map(|ip| SocketAddr::new(ip, *port)),
}
}
}