use std::fmt;
use std::net::IpAddr;
use std::sync::Arc;
use proto::error::ProtoResult;
use proto::op::Query;
use proto::rr::domain::usage::ONION;
use proto::rr::domain::TryParseIp;
use proto::rr::{IntoName, Name, Record, RecordType};
use proto::xfer::{DnsRequestOptions, RetryDnsHandle};
use proto::DnsHandle;
use tracing::{debug, trace};
use crate::caching_client::CachingClient;
use crate::config::{ResolverConfig, ResolverOpts};
use crate::dns_lru::{self, DnsLru};
use crate::error::*;
use crate::lookup::{self, Lookup, LookupEither, LookupFuture};
use crate::lookup_ip::{LookupIp, LookupIpFuture};
use crate::name_server::{
ConnectionProvider, GenericConnection, GenericConnectionProvider, NameServerPool,
RuntimeProvider,
};
#[cfg(feature = "tokio-runtime")]
use crate::name_server::{TokioConnection, TokioConnectionProvider, TokioHandle};
use crate::Hosts;
#[derive(Clone)]
pub struct AsyncResolver<C: DnsHandle<Error = ResolveError>, P: ConnectionProvider<Conn = C>> {
config: ResolverConfig,
options: ResolverOpts,
client_cache: CachingClient<LookupEither<C, P>, ResolveError>,
hosts: Option<Arc<Hosts>>,
}
#[cfg(feature = "tokio-runtime")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio-runtime")))]
pub type TokioAsyncResolver = AsyncResolver<TokioConnection, TokioConnectionProvider>;
macro_rules! lookup_fn {
($p:ident, $l:ty, $r:path) => {
pub async fn $p<N: IntoName>(&self, query: N) -> Result<$l, ResolveError> {
let name = match query.into_name() {
Ok(name) => name,
Err(err) => {
return Err(err.into());
}
};
self.inner_lookup(name, $r, self.request_options()).await
}
};
($p:ident, $l:ty, $r:path, $t:ty) => {
pub async fn $p(&self, query: $t) -> Result<$l, ResolveError> {
let name = Name::from(query);
self.inner_lookup(name, $r, self.request_options()).await
}
};
}
#[cfg(feature = "tokio-runtime")]
#[cfg_attr(docsrs, doc(cfg(feature = "tokio-runtime")))]
impl TokioAsyncResolver {
pub fn tokio(config: ResolverConfig, options: ResolverOpts) -> Result<Self, ResolveError> {
Self::new(config, options, TokioHandle)
}
#[cfg(any(unix, target_os = "windows"))]
#[cfg(feature = "system-config")]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "system-config", any(unix, target_os = "windows"))))
)]
pub fn tokio_from_system_conf() -> Result<Self, ResolveError> {
Self::from_system_conf(TokioHandle)
}
}
impl<R: RuntimeProvider> AsyncResolver<GenericConnection, GenericConnectionProvider<R>> {
pub fn new(
config: ResolverConfig,
options: ResolverOpts,
runtime: R::Handle,
) -> Result<Self, ResolveError> {
Self::new_with_conn(
config,
options,
GenericConnectionProvider::<R>::new(runtime),
)
}
#[cfg(any(unix, target_os = "windows"))]
#[cfg(feature = "system-config")]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "system-config", any(unix, target_os = "windows"))))
)]
pub fn from_system_conf(runtime: R::Handle) -> Result<Self, ResolveError> {
Self::from_system_conf_with_provider(GenericConnectionProvider::<R>::new(runtime))
}
pub fn clear_cache(&self) {
self.client_cache.clear_cache();
}
}
impl<C: DnsHandle<Error = ResolveError>, P: ConnectionProvider<Conn = C>> AsyncResolver<C, P> {
#[allow(clippy::unnecessary_wraps)]
pub fn new_with_conn(
config: ResolverConfig,
options: ResolverOpts,
conn_provider: P,
) -> Result<Self, ResolveError> {
let pool = NameServerPool::from_config_with_provider(&config, &options, conn_provider);
let either;
let client = RetryDnsHandle::new(pool, options.attempts);
if options.validate {
#[cfg(feature = "dnssec")]
{
use proto::xfer::DnssecDnsHandle;
either = LookupEither::Secure(DnssecDnsHandle::new(client));
}
#[cfg(not(feature = "dnssec"))]
{
tracing::warn!("validate option is only available with 'dnssec' feature");
either = LookupEither::Retry(client);
}
} else {
either = LookupEither::Retry(client);
}
let hosts = if options.use_hosts_file {
Some(Arc::new(Hosts::new()))
} else {
None
};
trace!("handle passed back");
let lru = DnsLru::new(options.cache_size, dns_lru::TtlConfig::from_opts(&options));
Ok(Self {
config,
options,
client_cache: CachingClient::with_cache(lru, either, options.preserve_intermediates),
hosts,
})
}
#[cfg(any(unix, target_os = "windows"))]
#[cfg(feature = "system-config")]
#[cfg_attr(
docsrs,
doc(cfg(all(feature = "system-config", any(unix, target_os = "windows"))))
)]
pub fn from_system_conf_with_provider(conn_provider: P) -> Result<Self, ResolveError> {
let (config, options) = super::system_conf::read_system_conf()?;
Self::new_with_conn(config, options, conn_provider)
}
pub(crate) fn request_options(&self) -> DnsRequestOptions {
let mut request_opts = DnsRequestOptions::default();
request_opts.recursion_desired = self.options.recursion_desired;
request_opts.use_edns = self.options.edns0;
request_opts
}
pub async fn lookup<N: IntoName>(
&self,
name: N,
record_type: RecordType,
) -> Result<Lookup, ResolveError> {
let name = match name.into_name() {
Ok(name) => name,
Err(err) => return Err(err.into()),
};
self.inner_lookup(name, record_type, self.request_options())
.await
}
fn push_name(name: Name, names: &mut Vec<Name>) {
if !names.contains(&name) {
names.push(name);
}
}
fn build_names(&self, name: Name) -> Vec<Name> {
if name.is_fqdn()
|| ONION.zone_of(&name)
&& name
.trim_to(2)
.iter()
.next()
.map(|name| name.len() == 56) .unwrap_or(false)
{
vec![name]
} else {
let mut names =
Vec::<Name>::with_capacity(1 + 1 + self.config.search().len());
let raw_name_first: bool =
name.num_labels() as usize > self.options.ndots || name.is_localhost();
if !raw_name_first {
names.push(name.clone());
}
for search in self.config.search().iter().rev() {
let name_search = name.clone().append_domain(search);
match name_search {
Ok(name_search) => Self::push_name(name_search, &mut names),
Err(e) => debug!(
"Not adding {} to {} for search due to error: {}",
search, name, e
),
}
}
if let Some(domain) = self.config.domain() {
let name_search = name.clone().append_domain(domain);
match name_search {
Ok(name_search) => Self::push_name(name_search, &mut names),
Err(e) => debug!(
"Not adding {} to {} for search due to error: {}",
domain, name, e
),
}
}
if raw_name_first {
names.push(name);
}
names
}
}
pub(crate) async fn inner_lookup<L>(
&self,
name: Name,
record_type: RecordType,
options: DnsRequestOptions,
) -> Result<L, ResolveError>
where
L: From<Lookup> + Send + 'static,
{
let names = self.build_names(name);
LookupFuture::lookup(names, record_type, options, self.client_cache.clone())
.await
.map(L::from)
}
pub async fn lookup_ip<N: IntoName + TryParseIp>(
&self,
host: N,
) -> Result<LookupIp, ResolveError> {
let mut finally_ip_addr: Option<Record> = None;
let maybe_ip = host.try_parse_ip();
let maybe_name: ProtoResult<Name> = host.into_name();
if let Some(ip_addr) = maybe_ip {
let name = maybe_name.clone().unwrap_or_default();
let record = Record::from_rdata(name.clone(), dns_lru::MAX_TTL, ip_addr.clone());
if self.options.ndots > 4 {
finally_ip_addr = Some(record);
} else {
let query = Query::query(name, ip_addr.to_record_type());
let lookup = Lookup::new_with_max_ttl(query, Arc::from([record]));
return Ok(lookup.into());
}
}
let name = match (maybe_name, finally_ip_addr.as_ref()) {
(Ok(name), _) => name,
(Err(_), Some(ip_addr)) => {
let query = Query::query(ip_addr.name().clone(), ip_addr.record_type());
let lookup = Lookup::new_with_max_ttl(query, Arc::from([ip_addr.clone()]));
return Ok(lookup.into());
}
(Err(err), None) => {
return Err(err.into());
}
};
let names = self.build_names(name);
let hosts = self.hosts.as_ref().cloned();
LookupIpFuture::lookup(
names,
self.options.ip_strategy,
self.client_cache.clone(),
self.request_options(),
hosts,
finally_ip_addr.and_then(Record::into_data),
)
.await
}
pub fn set_hosts(&mut self, hosts: Option<Hosts>) {
self.hosts = hosts.map(Arc::new);
}
lookup_fn!(
reverse_lookup,
lookup::ReverseLookup,
RecordType::PTR,
IpAddr
);
lookup_fn!(ipv4_lookup, lookup::Ipv4Lookup, RecordType::A);
lookup_fn!(ipv6_lookup, lookup::Ipv6Lookup, RecordType::AAAA);
lookup_fn!(mx_lookup, lookup::MxLookup, RecordType::MX);
lookup_fn!(ns_lookup, lookup::NsLookup, RecordType::NS);
lookup_fn!(soa_lookup, lookup::SoaLookup, RecordType::SOA);
lookup_fn!(srv_lookup, lookup::SrvLookup, RecordType::SRV);
lookup_fn!(tlsa_lookup, lookup::TlsaLookup, RecordType::TLSA);
lookup_fn!(txt_lookup, lookup::TxtLookup, RecordType::TXT);
}
impl<C: DnsHandle<Error = ResolveError>, P: ConnectionProvider<Conn = C>> fmt::Debug
for AsyncResolver<C, P>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AsyncResolver")
.field("request_tx", &"...")
.finish()
}
}
#[cfg(any(test, feature = "testing"))]
#[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
#[allow(dead_code, unreachable_pub)]
pub mod testing {
use std::{net::*, str::FromStr};
use crate::config::{LookupIpStrategy, NameServerConfig, ResolverConfig, ResolverOpts};
use crate::name_server::{GenericConnection, GenericConnectionProvider, RuntimeProvider};
use crate::AsyncResolver;
use proto::{rr::Name, Executor};
pub fn lookup_test<E: Executor, R: RuntimeProvider>(
config: ResolverConfig,
mut exec: E,
handle: R::Handle,
) {
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
config,
ResolverOpts::default(),
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("www.example.com."))
.expect("failed to run lookup");
assert_eq!(response.iter().count(), 1);
for address in response.iter() {
if address.is_ipv4() {
assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
} else {
assert_eq!(
address,
IpAddr::V6(Ipv6Addr::new(
0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
))
);
}
}
}
pub fn ip_lookup_test<E: Executor, R: RuntimeProvider>(mut exec: E, handle: R::Handle) {
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
ResolverConfig::default(),
ResolverOpts::default(),
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("10.1.0.2"))
.expect("failed to run lookup");
assert_eq!(
Some(IpAddr::V4(Ipv4Addr::new(10, 1, 0, 2))),
response.iter().next()
);
let response = exec
.block_on(resolver.lookup_ip("2606:2800:220:1:248:1893:25c8:1946"))
.expect("failed to run lookup");
assert_eq!(
Some(IpAddr::V6(Ipv6Addr::new(
0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
))),
response.iter().next()
);
}
pub fn ip_lookup_across_threads_test<E: Executor + Send + 'static, R: RuntimeProvider>(
handle: R::Handle,
) {
use std::thread;
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
ResolverConfig::default(),
ResolverOpts::default(),
handle,
)
.expect("failed to create resolver");
let resolver_one = resolver.clone();
let resolver_two = resolver;
let test_fn = |resolver: AsyncResolver<GenericConnection, GenericConnectionProvider<R>>| {
let mut exec = E::new();
let response = exec
.block_on(resolver.lookup_ip("10.1.0.2"))
.expect("failed to run lookup");
assert_eq!(
Some(IpAddr::V4(Ipv4Addr::new(10, 1, 0, 2))),
response.iter().next()
);
let response = exec
.block_on(resolver.lookup_ip("2606:2800:220:1:248:1893:25c8:1946"))
.expect("failed to run lookup");
assert_eq!(
Some(IpAddr::V6(Ipv6Addr::new(
0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
))),
response.iter().next()
);
};
let thread_one = thread::spawn(move || {
test_fn(resolver_one);
});
let thread_two = thread::spawn(move || {
test_fn(resolver_two);
});
thread_one.join().expect("thread_one failed");
thread_two.join().expect("thread_two failed");
}
#[cfg(feature = "dnssec")]
#[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
pub fn sec_lookup_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
ResolverConfig::default(),
ResolverOpts {
validate: true,
try_tcp_on_error: true,
..ResolverOpts::default()
},
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("www.example.com."))
.expect("failed to run lookup");
for address in response.iter() {
if address.is_ipv4() {
assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
} else {
assert_eq!(
address,
IpAddr::V6(Ipv6Addr::new(
0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
))
);
}
}
}
#[allow(deprecated)]
#[cfg(feature = "dnssec")]
#[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
pub fn sec_lookup_fails_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
use crate::error::*;
use proto::rr::RecordType;
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
ResolverConfig::default(),
ResolverOpts {
validate: true,
ip_strategy: LookupIpStrategy::Ipv4Only,
..ResolverOpts::default()
},
handle,
)
.expect("failed to create resolver");
let response = exec.block_on(resolver.lookup_ip("trust-dns.org."));
assert!(response.is_err());
let error = response.unwrap_err();
use proto::error::{ProtoError, ProtoErrorKind};
let error_str = format!("{}", error);
let name = Name::from_str("trust-dns.org.").unwrap();
let expected_str = format!(
"{}",
ResolveError::from(ProtoError::from(ProtoErrorKind::RrsigsNotPresent {
name,
record_type: RecordType::A
}))
);
assert_eq!(error_str, expected_str);
if let ResolveErrorKind::Proto(_) = *error.kind() {
} else {
panic!("wrong error")
}
}
#[cfg(feature = "system-config")]
#[cfg_attr(docsrs, doc(cfg(feature = "system-config")))]
pub fn system_lookup_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let resolver =
AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::from_system_conf(
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("www.example.com."))
.expect("failed to run lookup");
assert_eq!(response.iter().count(), 2);
for address in response.iter() {
if address.is_ipv4() {
assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
} else {
assert_eq!(
address,
IpAddr::V6(Ipv6Addr::new(
0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
))
);
}
}
}
#[cfg(feature = "system-config")]
#[cfg_attr(docsrs, doc(cfg(feature = "system-config")))]
pub fn hosts_lookup_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let resolver =
AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::from_system_conf(
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("a.com"))
.expect("failed to run lookup");
assert_eq!(response.iter().count(), 1);
for address in response.iter() {
if address.is_ipv4() {
assert_eq!(address, IpAddr::V4(Ipv4Addr::new(10, 1, 0, 104)));
} else {
panic!("failed to run lookup");
}
}
}
pub fn fqdn_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let domain = Name::from_str("incorrect.example.com.").unwrap();
let search = vec![
Name::from_str("bad.example.com.").unwrap(),
Name::from_str("wrong.example.com.").unwrap(),
];
let name_servers: Vec<NameServerConfig> =
ResolverConfig::default().name_servers().to_owned();
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
ResolverConfig::from_parts(Some(domain), search, name_servers),
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
..ResolverOpts::default()
},
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("www.example.com."))
.expect("failed to run lookup");
assert_eq!(response.iter().count(), 1);
for address in response.iter() {
if address.is_ipv4() {
assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
} else {
panic!("should only be looking up IPv4");
}
}
}
pub fn ndots_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let domain = Name::from_str("incorrect.example.com.").unwrap();
let search = vec![
Name::from_str("bad.example.com.").unwrap(),
Name::from_str("wrong.example.com.").unwrap(),
];
let name_servers: Vec<NameServerConfig> =
ResolverConfig::default().name_servers().to_owned();
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
ResolverConfig::from_parts(Some(domain), search, name_servers),
ResolverOpts {
ndots: 2,
ip_strategy: LookupIpStrategy::Ipv4Only,
..ResolverOpts::default()
},
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("www.example.com"))
.expect("failed to run lookup");
assert_eq!(response.iter().count(), 1);
for address in response.iter() {
if address.is_ipv4() {
assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
} else {
panic!("should only be looking up IPv4");
}
}
}
pub fn large_ndots_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let domain = Name::from_str("incorrect.example.com.").unwrap();
let search = vec![
Name::from_str("bad.example.com.").unwrap(),
Name::from_str("wrong.example.com.").unwrap(),
];
let name_servers: Vec<NameServerConfig> =
ResolverConfig::default().name_servers().to_owned();
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
ResolverConfig::from_parts(Some(domain), search, name_servers),
ResolverOpts {
ndots: 5,
ip_strategy: LookupIpStrategy::Ipv4Only,
..ResolverOpts::default()
},
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("www.example.com"))
.expect("failed to run lookup");
assert_eq!(response.iter().count(), 1);
for address in response.iter() {
if address.is_ipv4() {
assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
} else {
panic!("should only be looking up IPv4");
}
}
}
pub fn domain_search_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let domain = Name::from_str("example.com.").unwrap();
let search = vec![
Name::from_str("bad.example.com.").unwrap(),
Name::from_str("wrong.example.com.").unwrap(),
];
let name_servers: Vec<NameServerConfig> =
ResolverConfig::default().name_servers().to_owned();
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
ResolverConfig::from_parts(Some(domain), search, name_servers),
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
..ResolverOpts::default()
},
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("www"))
.expect("failed to run lookup");
assert_eq!(response.iter().count(), 1);
for address in response.iter() {
if address.is_ipv4() {
assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
} else {
panic!("should only be looking up IPv4");
}
}
}
pub fn search_list_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let domain = Name::from_str("incorrect.example.com.").unwrap();
let search = vec![
Name::from_str("bad.example.com.").unwrap(),
Name::from_str("example.com.").unwrap(),
];
let name_servers: Vec<NameServerConfig> =
ResolverConfig::default().name_servers().to_owned();
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
ResolverConfig::from_parts(Some(domain), search, name_servers),
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
..ResolverOpts::default()
},
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("www"))
.expect("failed to run lookup");
assert_eq!(response.iter().count(), 1);
for address in response.iter() {
if address.is_ipv4() {
assert_eq!(address, IpAddr::V4(Ipv4Addr::new(93, 184, 216, 34)));
} else {
panic!("should only be looking up IPv4");
}
}
}
pub fn idna_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
ResolverConfig::default(),
ResolverOpts::default(),
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("中国.icom.museum."))
.expect("failed to run lookup");
assert!(response.iter().next().is_some());
}
pub fn localhost_ipv4_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
ResolverConfig::default(),
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4thenIpv6,
..ResolverOpts::default()
},
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("localhost"))
.expect("failed to run lookup");
let mut iter = response.iter();
assert_eq!(
iter.next().expect("no A"),
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
);
}
pub fn localhost_ipv6_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
ResolverConfig::default(),
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv6thenIpv4,
..ResolverOpts::default()
},
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("localhost"))
.expect("failed to run lookup");
let mut iter = response.iter();
assert_eq!(
iter.next().expect("no AAAA"),
IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1,))
);
}
pub fn search_ipv4_large_ndots_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let mut config = ResolverConfig::default();
config.add_search(Name::from_str("example.com").unwrap());
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
config,
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
ndots: 5,
..ResolverOpts::default()
},
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("198.51.100.35"))
.expect("failed to run lookup");
let mut iter = response.iter();
assert_eq!(
iter.next().expect("no rdatas"),
IpAddr::V4(Ipv4Addr::new(198, 51, 100, 35))
);
}
pub fn search_ipv6_large_ndots_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let mut config = ResolverConfig::default();
config.add_search(Name::from_str("example.com").unwrap());
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
config,
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
ndots: 5,
..ResolverOpts::default()
},
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("2001:db8::c633:6423"))
.expect("failed to run lookup");
let mut iter = response.iter();
assert_eq!(
iter.next().expect("no rdatas"),
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc633, 0x6423))
);
}
pub fn search_ipv6_name_parse_fails_test<E: Executor + Send + 'static, R: RuntimeProvider>(
mut exec: E,
handle: R::Handle,
) {
let mut config = ResolverConfig::default();
config.add_search(Name::from_str("example.com").unwrap());
let resolver = AsyncResolver::<GenericConnection, GenericConnectionProvider<R>>::new(
config,
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
ndots: 5,
..ResolverOpts::default()
},
handle,
)
.expect("failed to create resolver");
let response = exec
.block_on(resolver.lookup_ip("2001:db8::198.51.100.35"))
.expect("failed to run lookup");
let mut iter = response.iter();
assert_eq!(
iter.next().expect("no rdatas"),
IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0xc633, 0x6423))
);
}
}
#[cfg(test)]
#[cfg(feature = "tokio-runtime")]
mod tests {
use proto::xfer::DnsRequest;
use tokio::runtime::Runtime;
use crate::config::{ResolverConfig, ResolverOpts};
use crate::name_server::{TokioConnection, TokioConnectionProvider, TokioRuntime};
use super::*;
fn is_send_t<T: Send>() -> bool {
true
}
fn is_sync_t<T: Sync>() -> bool {
true
}
#[test]
fn test_send_sync() {
assert!(is_send_t::<ResolverConfig>());
assert!(is_sync_t::<ResolverConfig>());
assert!(is_send_t::<ResolverOpts>());
assert!(is_sync_t::<ResolverOpts>());
assert!(is_send_t::<
AsyncResolver<TokioConnection, TokioConnectionProvider>,
>());
assert!(is_sync_t::<
AsyncResolver<TokioConnection, TokioConnectionProvider>,
>());
assert!(is_send_t::<DnsRequest>());
assert!(is_send_t::<LookupIpFuture<TokioConnection, ResolveError>>());
assert!(is_send_t::<LookupFuture<TokioConnection, ResolveError>>());
}
#[test]
fn test_lookup_google() {
use super::testing::lookup_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime");
let handle = TokioHandle;
lookup_test::<Runtime, TokioRuntime>(ResolverConfig::google(), io_loop, handle)
}
#[test]
fn test_lookup_cloudflare() {
use super::testing::lookup_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime");
let handle = TokioHandle;
lookup_test::<Runtime, TokioRuntime>(ResolverConfig::cloudflare(), io_loop, handle)
}
#[test]
fn test_lookup_quad9() {
use super::testing::lookup_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime");
let handle = TokioHandle;
lookup_test::<Runtime, TokioRuntime>(ResolverConfig::quad9(), io_loop, handle)
}
#[test]
fn test_ip_lookup() {
use super::testing::ip_lookup_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime");
let handle = TokioHandle;
ip_lookup_test::<Runtime, TokioRuntime>(io_loop, handle)
}
#[test]
fn test_ip_lookup_across_threads() {
use super::testing::ip_lookup_across_threads_test;
let _io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
ip_lookup_across_threads_test::<Runtime, TokioRuntime>(handle)
}
#[test]
#[cfg(feature = "dnssec")]
fn test_sec_lookup() {
use super::testing::sec_lookup_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
sec_lookup_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
#[cfg(feature = "dnssec")]
fn test_sec_lookup_fails() {
use super::testing::sec_lookup_fails_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
sec_lookup_fails_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
#[ignore]
#[cfg(any(unix, target_os = "windows"))]
#[cfg(feature = "system-config")]
fn test_system_lookup() {
use super::testing::system_lookup_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
system_lookup_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
#[ignore]
#[cfg(unix)]
fn test_hosts_lookup() {
use super::testing::hosts_lookup_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
hosts_lookup_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
fn test_fqdn() {
use super::testing::fqdn_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
fqdn_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
fn test_ndots() {
use super::testing::ndots_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
ndots_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
fn test_large_ndots() {
use super::testing::large_ndots_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
large_ndots_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
fn test_domain_search() {
use super::testing::domain_search_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
domain_search_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
fn test_search_list() {
use super::testing::search_list_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
search_list_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
fn test_idna() {
use super::testing::idna_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
idna_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
fn test_localhost_ipv4() {
use super::testing::localhost_ipv4_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
localhost_ipv4_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
fn test_localhost_ipv6() {
use super::testing::localhost_ipv6_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
localhost_ipv6_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
fn test_search_ipv4_large_ndots() {
use super::testing::search_ipv4_large_ndots_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
search_ipv4_large_ndots_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
fn test_search_ipv6_large_ndots() {
use super::testing::search_ipv6_large_ndots_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
search_ipv6_large_ndots_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
fn test_search_ipv6_name_parse_fails() {
use super::testing::search_ipv6_name_parse_fails_test;
let io_loop = Runtime::new().expect("failed to create tokio runtime io_loop");
let handle = TokioHandle;
search_ipv6_name_parse_fails_test::<Runtime, TokioRuntime>(io_loop, handle);
}
#[test]
fn test_build_names_onion() {
let handle = TokioHandle;
let mut config = ResolverConfig::default();
config.add_search(Name::from_ascii("example.com.").unwrap());
let resolver =
AsyncResolver::<GenericConnection, GenericConnectionProvider<TokioRuntime>>::new(
config,
ResolverOpts::default(),
handle,
)
.expect("failed to create resolver");
let tor_address = [
Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion")
.unwrap(),
Name::from_ascii("www.2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion")
.unwrap(), ];
let not_tor_address = [
Name::from_ascii("onion").unwrap(),
Name::from_ascii("www.onion").unwrap(),
Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.www.onion")
.unwrap(), Name::from_ascii("2gzyxa5ihm7nsggfxnu52rck2vv4rvmdlkiu3zzui5du4xyclen53wid.onion.to")
.unwrap(), ];
for name in &tor_address {
assert_eq!(resolver.build_names(name.clone()).len(), 1);
}
for name in ¬_tor_address {
assert_eq!(resolver.build_names(name.clone()).len(), 2);
}
}
}