use std::net::IpAddr;
use std::sync::Arc;
use std::time::Instant;
use failure::Fail;
use futures::{future, Async, Future, Poll};
use proto::op::Query;
use proto::rr::{Name, RData, RecordType};
use proto::xfer::{DnsHandle, DnsRequestOptions};
use config::LookupIpStrategy;
use error::*;
use hosts::Hosts;
use lookup::{Lookup, LookupEither, LookupIter};
use lookup_state::CachingClient;
use name_server_pool::{ConnectionHandle, StandardConnection};
#[derive(Debug, Clone)]
pub struct LookupIp(Lookup);
impl LookupIp {
pub fn iter(&self) -> LookupIpIter {
LookupIpIter(self.0.iter())
}
pub fn valid_until(&self) -> Instant {
self.0.valid_until()
}
}
impl From<Lookup> for LookupIp {
fn from(lookup: Lookup) -> Self {
LookupIp(lookup)
}
}
pub struct LookupIpIter<'i>(pub(crate) LookupIter<'i>);
impl<'i> Iterator for LookupIpIter<'i> {
type Item = IpAddr;
fn next(&mut self) -> Option<Self::Item> {
let iter: &mut _ = &mut self.0;
iter.filter_map(|rdata| match *rdata {
RData::A(ip) => Some(IpAddr::from(ip)),
RData::AAAA(ip) => Some(IpAddr::from(ip)),
_ => None,
}).next()
}
}
pub struct LookupIpFuture<C = LookupEither<ConnectionHandle, StandardConnection>>
where
C: DnsHandle + 'static,
{
client_cache: CachingClient<C>,
names: Vec<Name>,
strategy: LookupIpStrategy,
options: DnsRequestOptions,
query: Box<Future<Item = Lookup, Error = ResolveError> + Send>,
hosts: Option<Arc<Hosts>>,
finally_ip_addr: Option<RData>,
}
impl<C: DnsHandle + 'static> Future for LookupIpFuture<C> {
type Item = LookupIp;
type Error = ResolveError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
loop {
let query = self.query.poll();
let should_retry = match query {
Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready(ref lookup)) => lookup.is_empty(),
Err(_) => true,
};
if should_retry {
if let Some(name) = self.names.pop() {
self.query = strategic_lookup(
name,
self.strategy,
self.client_cache.clone(),
self.options.clone(),
self.hosts.clone(),
);
continue;
} else if let Some(ip_addr) = self.finally_ip_addr.take() {
return Ok(Async::Ready(
Lookup::new_with_max_ttl(Arc::new(vec![ip_addr])).into(),
));
}
};
return query.map(|async| async.map(LookupIp::from));
}
}
}
impl<C> LookupIpFuture<C>
where
C: DnsHandle + 'static,
{
pub fn lookup(
names: Vec<Name>,
strategy: LookupIpStrategy,
client_cache: CachingClient<C>,
options: DnsRequestOptions,
hosts: Option<Arc<Hosts>>,
finally_ip_addr: Option<RData>,
) -> Self {
let empty =
ResolveError::from(ResolveErrorKind::Message("can not lookup IPs for no names"));
LookupIpFuture {
names,
strategy,
client_cache,
query: Box::new(future::err(empty)),
options,
hosts,
finally_ip_addr,
}
}
pub(crate) fn error<E: Fail>(client_cache: CachingClient<C>, error: E) -> Self {
return LookupIpFuture {
client_cache,
names: vec![],
strategy: LookupIpStrategy::default(),
options: DnsRequestOptions::default(),
query: Box::new(future::err(
ResolveErrorKind::Msg(format!("{}", error)).into(),
)),
hosts: None,
finally_ip_addr: None,
};
}
pub(crate) fn ok(client_cache: CachingClient<C>, lp: Lookup) -> Self {
return LookupIpFuture {
client_cache,
names: vec![],
strategy: LookupIpStrategy::default(),
options: DnsRequestOptions::default(),
query: Box::new(future::ok(lp)),
hosts: None,
finally_ip_addr: None,
};
}
}
fn strategic_lookup<C: DnsHandle + 'static>(
name: Name,
strategy: LookupIpStrategy,
client: CachingClient<C>,
options: DnsRequestOptions,
hosts: Option<Arc<Hosts>>,
) -> Box<Future<Item = Lookup, Error = ResolveError> + Send> {
match strategy {
LookupIpStrategy::Ipv4Only => ipv4_only(name, client, options, hosts),
LookupIpStrategy::Ipv6Only => ipv6_only(name, client, options, hosts),
LookupIpStrategy::Ipv4AndIpv6 => ipv4_and_ipv6(name, client, options, hosts),
LookupIpStrategy::Ipv6thenIpv4 => ipv6_then_ipv4(name, client, options, hosts),
LookupIpStrategy::Ipv4thenIpv6 => ipv4_then_ipv6(name, client, options, hosts),
}
}
fn hosts_lookup<C: DnsHandle + 'static>(
query: Query,
mut client: CachingClient<C>,
options: DnsRequestOptions,
hosts: Option<Arc<Hosts>>,
) -> Box<Future<Item = Lookup, Error = ResolveError> + Send> {
if let Some(hosts) = hosts {
if let Some(lookup) = hosts.lookup_static_host(&query) {
return Box::new(future::ok(lookup));
};
}
client.lookup(query, options)
}
fn ipv4_only<C: DnsHandle + 'static>(
name: Name,
client: CachingClient<C>,
options: DnsRequestOptions,
hosts: Option<Arc<Hosts>>,
) -> Box<Future<Item = Lookup, Error = ResolveError> + Send> {
hosts_lookup(Query::query(name, RecordType::A), client, options, hosts)
}
fn ipv6_only<C: DnsHandle + 'static>(
name: Name,
client: CachingClient<C>,
options: DnsRequestOptions,
hosts: Option<Arc<Hosts>>,
) -> Box<Future<Item = Lookup, Error = ResolveError> + Send> {
hosts_lookup(Query::query(name, RecordType::AAAA), client, options, hosts)
}
fn ipv4_and_ipv6<C: DnsHandle + 'static>(
name: Name,
client: CachingClient<C>,
options: DnsRequestOptions,
hosts: Option<Arc<Hosts>>,
) -> Box<Future<Item = Lookup, Error = ResolveError> + Send> {
Box::new(
hosts_lookup(
Query::query(name.clone(), RecordType::A),
client.clone(),
options.clone(),
hosts.clone(),
).select(hosts_lookup(
Query::query(name, RecordType::AAAA),
client,
options,
hosts,
))
.then(|sel_res| {
match sel_res {
Ok((ips, remaining_query)) => {
Box::new(remaining_query.then(move |query_res| match query_res {
Ok(rem_ips) => {
let ips = ips.append(rem_ips);
future::ok(ips)
}
Err(_) => future::ok(ips),
})) as
Box<Future<Item = Lookup, Error = ResolveError> + Send>
}
Err((_, remaining_query)) => Box::new(remaining_query),
}
}),
)
}
fn ipv6_then_ipv4<C: DnsHandle + 'static>(
name: Name,
client: CachingClient<C>,
options: DnsRequestOptions,
hosts: Option<Arc<Hosts>>,
) -> Box<Future<Item = Lookup, Error = ResolveError> + Send> {
rt_then_swap(
name,
client,
RecordType::AAAA,
RecordType::A,
options,
hosts,
)
}
fn ipv4_then_ipv6<C: DnsHandle + 'static>(
name: Name,
client: CachingClient<C>,
options: DnsRequestOptions,
hosts: Option<Arc<Hosts>>,
) -> Box<Future<Item = Lookup, Error = ResolveError> + Send> {
rt_then_swap(
name,
client,
RecordType::A,
RecordType::AAAA,
options,
hosts,
)
}
fn rt_then_swap<C: DnsHandle + 'static>(
name: Name,
client: CachingClient<C>,
first_type: RecordType,
second_type: RecordType,
options: DnsRequestOptions,
hosts: Option<Arc<Hosts>>,
) -> Box<Future<Item = Lookup, Error = ResolveError> + Send> {
let or_client = client.clone();
Box::new(
hosts_lookup(
Query::query(name.clone(), first_type),
client,
options.clone(),
hosts.clone(),
).then(move |res| {
match res {
Ok(ips) => {
if ips.is_empty() {
Box::new(hosts_lookup(
Query::query(name.clone(), second_type),
or_client,
options,
hosts,
))
as Box<Future<Item = Lookup, Error = ResolveError> + Send>
} else {
Box::new(future::ok(ips))
as Box<Future<Item = Lookup, Error = ResolveError> + Send>
}
}
Err(_) => Box::new(hosts_lookup(
Query::query(name.clone(), second_type),
or_client,
options,
hosts,
)),
}
}),
)
}
#[cfg(test)]
pub mod tests {
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::sync::{Arc, Mutex};
use futures::{future, Future};
use proto::error::{ProtoError, ProtoResult};
use proto::op::Message;
use proto::rr::{Name, RData, Record, RecordType};
use proto::xfer::{DnsHandle, DnsRequest, DnsResponse};
use super::*;
#[derive(Clone)]
pub struct MockDnsHandle {
messages: Arc<Mutex<Vec<ProtoResult<DnsResponse>>>>,
}
impl DnsHandle for MockDnsHandle {
type Response = Box<Future<Item = DnsResponse, Error = ProtoError> + Send>;
fn send<R: Into<DnsRequest>>(&mut self, _: R) -> Self::Response {
Box::new(future::result(
self.messages.lock().unwrap().pop().unwrap_or(empty()),
))
}
}
pub fn v4_message() -> ProtoResult<DnsResponse> {
let mut message = Message::new();
message.insert_answers(vec![Record::from_rdata(
Name::root(),
86400,
RecordType::A,
RData::A(Ipv4Addr::new(127, 0, 0, 1)),
)]);
Ok(message.into())
}
pub fn v6_message() -> ProtoResult<DnsResponse> {
let mut message = Message::new();
message.insert_answers(vec![Record::from_rdata(
Name::root(),
86400,
RecordType::AAAA,
RData::AAAA(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
)]);
Ok(message.into())
}
pub fn empty() -> ProtoResult<DnsResponse> {
Ok(Message::new().into())
}
pub fn error() -> ProtoResult<DnsResponse> {
Err(ProtoError::from("forced test failure"))
}
pub fn mock(messages: Vec<ProtoResult<DnsResponse>>) -> MockDnsHandle {
MockDnsHandle {
messages: Arc::new(Mutex::new(messages)),
}
}
#[test]
fn test_ipv4_only_strategy() {
assert_eq!(
ipv4_only(
Name::root(),
CachingClient::new(0, mock(vec![v4_message()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![Ipv4Addr::new(127, 0, 0, 1)]
);
}
#[test]
fn test_ipv6_only_strategy() {
assert_eq!(
ipv6_only(
Name::root(),
CachingClient::new(0, mock(vec![v6_message()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)]
);
}
#[test]
fn test_ipv4_and_ipv6_strategy() {
assert_eq!(
ipv4_and_ipv6(
Name::root(),
CachingClient::new(0, mock(vec![v6_message(), v4_message()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![
IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)),
IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
]
);
assert_eq!(
ipv4_and_ipv6(
Name::root(),
CachingClient::new(0, mock(vec![empty(), v4_message()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))]
);
assert_eq!(
ipv4_and_ipv6(
Name::root(),
CachingClient::new(0, mock(vec![error(), v4_message()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))]
);
assert_eq!(
ipv4_and_ipv6(
Name::root(),
CachingClient::new(0, mock(vec![v6_message(), empty()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))]
);
assert_eq!(
ipv4_and_ipv6(
Name::root(),
CachingClient::new(0, mock(vec![v6_message(), error()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))]
);
}
#[test]
fn test_ipv6_then_ipv4_strategy() {
assert_eq!(
ipv6_then_ipv4(
Name::root(),
CachingClient::new(0, mock(vec![v6_message()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)]
);
assert_eq!(
ipv6_then_ipv4(
Name::root(),
CachingClient::new(0, mock(vec![v4_message(), empty()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![Ipv4Addr::new(127, 0, 0, 1)]
);
assert_eq!(
ipv6_then_ipv4(
Name::root(),
CachingClient::new(0, mock(vec![v4_message(), error()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![Ipv4Addr::new(127, 0, 0, 1)]
);
}
#[test]
fn test_ipv4_then_ipv6_strategy() {
assert_eq!(
ipv4_then_ipv6(
Name::root(),
CachingClient::new(0, mock(vec![v4_message()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![Ipv4Addr::new(127, 0, 0, 1)]
);
assert_eq!(
ipv4_then_ipv6(
Name::root(),
CachingClient::new(0, mock(vec![v6_message(), empty()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)]
);
assert_eq!(
ipv4_then_ipv6(
Name::root(),
CachingClient::new(0, mock(vec![v6_message(), error()])),
Default::default(),
None,
).wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)]
);
}
}