use std::fmt;
use std::future::Future;
use std::net::IpAddr;
use std::sync::Arc;
use futures::{self, future, lock::Mutex};
use proto::error::ProtoResult;
use proto::op::Query;
use proto::rr::domain::TryParseIp;
use proto::rr::{IntoName, Name, Record, RecordType};
use proto::xfer::{DnsRequestOptions, RetryDnsHandle};
use proto::DnsHandle;
#[cfg(feature = "tokio-runtime")]
use tokio::runtime::Handle;
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::lookup_state::CachingClient;
use crate::name_server::{ConnectionProvider, NameServerPool};
#[cfg(feature = "tokio-runtime")]
use crate::name_server::{TokioConnection, TokioConnectionProvider};
use crate::Hosts;
#[derive(Clone)]
pub struct AsyncResolver<C: DnsHandle, P: ConnectionProvider<Conn = C>> {
config: ResolverConfig,
options: ResolverOpts,
client_cache: CachingClient<LookupEither<C, P>>,
hosts: Option<Arc<Hosts>>,
}
#[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, DnsRequestOptions::default()).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, DnsRequestOptions::default()).await
}
};
}
#[cfg(feature = "tokio-runtime")]
impl AsyncResolver<TokioConnection, TokioConnectionProvider> {
pub async fn new(
config: ResolverConfig,
options: ResolverOpts,
runtime: Handle,
) -> Result<Self, ResolveError> {
AsyncResolver::<TokioConnection, TokioConnectionProvider>::new_with_conn(
config,
options,
TokioConnectionProvider::new(runtime),
)
.await
}
#[cfg(any(unix, target_os = "windows"))]
pub async fn from_system_conf(runtime: Handle) -> Result<Self, ResolveError> {
Self::from_system_conf_with_provider(TokioConnectionProvider::new(runtime)).await
}
}
impl<C: DnsHandle, P: ConnectionProvider<Conn = C>> AsyncResolver<C, P> {
pub async fn new_with_conn(
config: ResolverConfig,
options: ResolverOpts,
conn_provider: P,
) -> Result<Self, ResolveError> {
let lru = DnsLru::new(options.cache_size, dns_lru::TtlConfig::from_opts(&options));
let lru = Arc::new(Mutex::new(lru));
Self::with_cache_with_provider(config, options, lru, conn_provider).await
}
pub(crate) async fn with_cache_with_provider(
config: ResolverConfig,
options: ResolverOpts,
lru: Arc<Mutex<DnsLru>>,
conn_provider: P,
) -> Result<Self, ResolveError> {
debug!("trust-dns resolver running");
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"))]
{
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");
Ok(AsyncResolver {
config,
options,
client_cache: CachingClient::with_cache(lru, either),
hosts,
})
}
#[cfg(any(unix, target_os = "windows"))]
pub async 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).await
}
pub fn lookup<N: IntoName>(
&self,
name: N,
record_type: RecordType,
options: DnsRequestOptions,
) -> impl Future<Output = Result<Lookup, ResolveError>> + Send + Unpin + 'static {
let name = match name.into_name() {
Ok(name) => name,
Err(err) => return future::Either::Left(future::err(err.into())),
};
let names = self.build_names(name);
future::Either::Right(LookupFuture::lookup(
names,
record_type,
options,
self.client_cache.clone(),
))
}
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() {
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);
Self::push_name(name_search, &mut names);
}
if let Some(domain) = self.config.domain() {
let name_search = name.clone().append_domain(domain);
Self::push_name(name_search, &mut names);
}
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,
{
self.lookup(name, record_type, options).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::new(vec![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::new(vec![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(),
DnsRequestOptions::default(),
hosts,
finally_ip_addr.map(Record::unwrap_rdata),
)
.await
}
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!(txt_lookup, lookup::TxtLookup, RecordType::TXT);
}
impl<C: DnsHandle, 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(test)]
#[cfg(feature = "tokio-runtime")]
mod tests {
extern crate env_logger;
extern crate tokio;
use failure::Fail;
use std::net::*;
use std::str::FromStr;
use self::tokio::runtime::Runtime;
use proto::xfer::DnsRequest;
use crate::config::{LookupIpStrategy, NameServerConfig};
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_send_t::<ResolverConfig>());
assert!(is_send_t::<ResolverOpts>());
assert!(is_sync_t::<ResolverOpts>());
#[cfg(feature = "tokio-runtime")]
assert!(is_send_t::<
AsyncResolver<TokioConnection, TokioConnectionProvider>,
>());
#[cfg(feature = "tokio-runtime")]
assert!(is_sync_t::<
AsyncResolver<TokioConnection, TokioConnectionProvider>,
>());
assert!(is_send_t::<DnsRequest>());
#[cfg(feature = "tokio-runtime")]
assert!(is_send_t::<LookupIpFuture<TokioConnection>>());
#[cfg(feature = "tokio-runtime")]
assert!(is_send_t::<LookupFuture<TokioConnection>>());
}
fn lookup_test(config: ResolverConfig) {
let mut io_loop = Runtime::new().unwrap();
let resolver =
AsyncResolver::new(config, ResolverOpts::default(), io_loop.handle().clone());
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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,
))
);
}
}
}
#[test]
fn test_lookup_google() {
lookup_test(ResolverConfig::google())
}
#[test]
fn test_lookup_cloudflare() {
lookup_test(ResolverConfig::cloudflare())
}
#[test]
fn test_lookup_quad9() {
lookup_test(ResolverConfig::quad9())
}
#[test]
fn test_ip_lookup() {
let mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::new(
ResolverConfig::default(),
ResolverOpts::default(),
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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 = io_loop
.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()
);
}
#[test]
fn test_ip_lookup_across_threads() {
use std::thread;
let mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::new(
ResolverConfig::default(),
ResolverOpts::default(),
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let resolver_one = resolver.clone();
let resolver_two = resolver;
let test_fn = |resolver: AsyncResolver<TokioConnection, TokioConnectionProvider>| {
let mut io_loop = Runtime::new().unwrap();
let response = io_loop
.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 = io_loop
.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");
}
#[test]
#[ignore]
fn test_sec_lookup() {
let mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::new(
ResolverConfig::default(),
ResolverOpts {
validate: true,
..ResolverOpts::default()
},
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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,
))
);
}
}
}
#[test]
#[ignore]
#[allow(deprecated)]
fn test_sec_lookup_fails() {
let mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::new(
ResolverConfig::default(),
ResolverOpts {
validate: true,
ip_strategy: LookupIpStrategy::Ipv4Only,
..ResolverOpts::default()
},
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let name = Name::from_str("www.trust-dns.org.").unwrap();
let response = io_loop.block_on(resolver.lookup_ip("www.trust-dns.org."));
assert!(response.is_err());
let error = response.unwrap_err();
use proto::error::{ProtoError, ProtoErrorKind};
let error_str = format!("{}", error.root_cause());
let expected_str = format!(
"{}",
ProtoError::from(ProtoErrorKind::RrsigsNotPresent {
name,
record_type: RecordType::A
})
);
assert_eq!(error_str, expected_str);
assert_eq!(*error.kind(), ResolveErrorKind::Proto);
}
#[test]
#[ignore]
#[cfg(any(unix, target_os = "windows"))]
fn test_system_lookup() {
let mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::from_system_conf(io_loop.handle().clone());
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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,
))
);
}
}
}
#[test]
#[ignore]
#[cfg(unix)]
fn test_hosts_lookup() {
let mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::from_system_conf(io_loop.handle().clone());
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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");
}
}
}
#[test]
fn test_fqdn() {
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 mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::new(
ResolverConfig::from_parts(Some(domain), search, name_servers),
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
..ResolverOpts::default()
},
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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");
}
}
}
#[test]
fn test_ndots() {
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 mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::new(
ResolverConfig::from_parts(Some(domain), search, name_servers),
ResolverOpts {
ndots: 2,
ip_strategy: LookupIpStrategy::Ipv4Only,
..ResolverOpts::default()
},
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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");
}
}
}
#[test]
fn test_large_ndots() {
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 mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::new(
ResolverConfig::from_parts(Some(domain), search, name_servers),
ResolverOpts {
ndots: 5,
ip_strategy: LookupIpStrategy::Ipv4Only,
..ResolverOpts::default()
},
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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");
}
}
}
#[test]
fn test_domain_search() {
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 mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::new(
ResolverConfig::from_parts(Some(domain), search, name_servers),
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
..ResolverOpts::default()
},
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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");
}
}
}
#[test]
fn test_search_list() {
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 mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::new(
ResolverConfig::from_parts(Some(domain), search, name_servers),
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
..ResolverOpts::default()
},
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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");
}
}
}
#[test]
fn test_idna() {
let mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::new(
ResolverConfig::default(),
ResolverOpts::default(),
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.block_on(resolver.lookup_ip("中国.icom.museum."))
.expect("failed to run lookup");
assert!(response.iter().next().is_some());
}
#[test]
fn test_localhost_ipv4() {
let mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::new(
ResolverConfig::default(),
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4thenIpv6,
..ResolverOpts::default()
},
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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))
);
}
#[test]
fn test_localhost_ipv6() {
let mut io_loop = Runtime::new().unwrap();
let resolver = AsyncResolver::new(
ResolverConfig::default(),
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv6thenIpv4,
..ResolverOpts::default()
},
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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,))
);
}
#[test]
fn test_search_ipv4_large_ndots() {
let mut io_loop = Runtime::new().unwrap();
let mut config = ResolverConfig::default();
config.add_search(Name::from_str("example.com").unwrap());
let resolver = AsyncResolver::new(
config,
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
ndots: 5,
..ResolverOpts::default()
},
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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))
);
}
#[test]
fn test_search_ipv6_large_ndots() {
let mut io_loop = Runtime::new().unwrap();
let mut config = ResolverConfig::default();
config.add_search(Name::from_str("example.com").unwrap());
let resolver = AsyncResolver::new(
config,
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
ndots: 5,
..ResolverOpts::default()
},
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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))
);
}
#[test]
fn test_search_ipv6_name_parse_fails() {
let mut io_loop = Runtime::new().unwrap();
let mut config = ResolverConfig::default();
config.add_search(Name::from_str("example.com").unwrap());
let resolver = AsyncResolver::new(
config,
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
ndots: 5,
..ResolverOpts::default()
},
io_loop.handle().clone(),
);
let resolver = io_loop
.block_on(resolver)
.expect("failed to create resolver");
let response = io_loop
.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))
);
}
}