use std::cmp::min;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::slice::Iter;
use std::sync::Arc;
use std::time::{Duration, Instant};
use futures::{future, Async, Future, Poll};
use proto::error::ProtoError;
use proto::op::Query;
use proto::rr::rdata;
use proto::rr::{Name, RData, RecordType};
use proto::xfer::{DnsRequest, DnsRequestOptions, DnsResponse};
#[cfg(feature = "dnssec")]
use proto::SecureDnsHandle;
use proto::{DnsHandle, RetryDnsHandle};
use dns_lru::MAX_TTL;
use error::*;
use lookup_ip::LookupIpIter;
use lookup_state::CachingClient;
use name_server_pool::{ConnectionHandle, ConnectionProvider, NameServerPool, StandardConnection};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Lookup {
rdatas: Arc<Vec<RData>>,
valid_until: Instant,
}
impl Lookup {
pub fn new_with_max_ttl(rdatas: Arc<Vec<RData>>) -> Self {
let valid_until = Instant::now() + Duration::from_secs(MAX_TTL as u64);
Lookup {
rdatas,
valid_until,
}
}
pub fn new_with_deadline(rdatas: Arc<Vec<RData>>, valid_until: Instant) -> Self {
Lookup {
rdatas,
valid_until,
}
}
pub fn iter(&self) -> LookupIter {
LookupIter(self.rdatas.iter())
}
pub fn valid_until(&self) -> Instant {
self.valid_until
}
pub(crate) fn is_empty(&self) -> bool {
self.rdatas.is_empty()
}
pub(crate) fn len(&self) -> usize {
self.rdatas.len()
}
pub(crate) fn append(&self, other: Lookup) -> Self {
let mut rdatas = Vec::with_capacity(self.len() + other.len());
rdatas.extend_from_slice(&*self.rdatas);
rdatas.extend_from_slice(&*other.rdatas);
let valid_until = min(self.valid_until(), other.valid_until());
Self::new_with_deadline(Arc::new(rdatas), valid_until)
}
}
impl From<RData> for Lookup {
fn from(data: RData) -> Self {
Lookup::new_with_max_ttl(Arc::new(vec![data]))
}
}
pub struct LookupIter<'a>(Iter<'a, RData>);
impl<'a> Iterator for LookupIter<'a> {
type Item = &'a RData;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[derive(Clone)]
#[doc(hidden)]
pub enum LookupEither<C: DnsHandle + 'static, P: ConnectionProvider<ConnHandle = C> + 'static> {
Retry(RetryDnsHandle<NameServerPool<C, P>>),
#[cfg(feature = "dnssec")]
Secure(SecureDnsHandle<RetryDnsHandle<NameServerPool<C, P>>>),
}
impl<C: DnsHandle, P: ConnectionProvider<ConnHandle = C>> DnsHandle for LookupEither<C, P> {
type Response = Box<Future<Item = DnsResponse, Error = ProtoError> + Send>;
fn is_verifying_dnssec(&self) -> bool {
match *self {
LookupEither::Retry(ref c) => c.is_verifying_dnssec(),
#[cfg(feature = "dnssec")]
LookupEither::Secure(ref c) => c.is_verifying_dnssec(),
}
}
fn send<R: Into<DnsRequest>>(&mut self, request: R) -> Self::Response {
match *self {
LookupEither::Retry(ref mut c) => c.send(request),
#[cfg(feature = "dnssec")]
LookupEither::Secure(ref mut c) => c.send(request),
}
}
}
#[doc(hidden)]
pub struct LookupFuture<C = LookupEither<ConnectionHandle, StandardConnection>>
where
C: DnsHandle + 'static,
{
client_cache: CachingClient<C>,
names: Vec<Name>,
record_type: RecordType,
options: DnsRequestOptions,
query: Box<Future<Item = Lookup, Error = ResolveError> + Send>,
}
impl<C: DnsHandle + 'static> LookupFuture<C> {
#[doc(hidden)]
pub fn lookup(
mut names: Vec<Name>,
record_type: RecordType,
options: DnsRequestOptions,
mut client_cache: CachingClient<C>,
) -> Self {
let name = names.pop().ok_or_else(|| {
ResolveError::from(ResolveErrorKind::Message("can not lookup for no names"))
});
let query: Box<Future<Item = Lookup, Error = ResolveError> + Send> = match name {
Ok(name) => {
Box::new(client_cache.lookup(Query::query(name, record_type), options.clone()))
}
Err(err) => Box::new(future::err(err)),
};
LookupFuture {
client_cache: client_cache,
names,
record_type,
options,
query,
}
}
}
impl<C: DnsHandle + 'static> Future for LookupFuture<C> {
type Item = Lookup;
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.rdatas.len() == 0,
Err(_) => true,
};
if should_retry {
if let Some(name) = self.names.pop() {
self.query = self
.client_cache
.lookup(Query::query(name, self.record_type), self.options.clone());
continue;
}
}
return query;
}
}
}
#[derive(Debug, Clone)]
pub struct SrvLookup(Lookup);
impl SrvLookup {
pub fn iter(&self) -> SrvLookupIter {
SrvLookupIter(self.0.iter())
}
pub fn ip_iter(&self) -> LookupIpIter {
LookupIpIter(self.0.iter())
}
}
impl From<Lookup> for SrvLookup {
fn from(lookup: Lookup) -> Self {
SrvLookup(lookup)
}
}
pub struct SrvLookupIter<'i>(LookupIter<'i>);
impl<'i> Iterator for SrvLookupIter<'i> {
type Item = &'i rdata::SRV;
fn next(&mut self) -> Option<Self::Item> {
let iter: &mut _ = &mut self.0;
iter.filter_map(|rdata| match *rdata {
RData::SRV(ref data) => Some(data),
_ => None,
})
.next()
}
}
pub struct SrvLookupFuture(LookupFuture);
impl From<LookupFuture> for SrvLookupFuture {
fn from(lookup_future: LookupFuture) -> Self {
SrvLookupFuture(lookup_future)
}
}
impl Future for SrvLookupFuture {
type Item = SrvLookup;
type Error = ResolveError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.0.poll() {
Ok(Async::Ready(lookup)) => Ok(Async::Ready(SrvLookup(lookup))),
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(e) => Err(e),
}
}
}
macro_rules! lookup_type {
($l:ident, $i:ident, $f:ident, $r:path, $t:path) => {
#[derive(Debug, Clone)]
pub struct $l(Lookup);
impl $l {
pub fn iter(&self) -> $i {
$i(self.0.iter())
}
}
impl From<Lookup> for $l {
fn from(lookup: Lookup) -> Self {
$l(lookup)
}
}
pub struct $i<'i>(LookupIter<'i>);
impl<'i> Iterator for $i<'i> {
type Item = &'i $t;
fn next(&mut self) -> Option<Self::Item> {
let iter: &mut _ = &mut self.0;
iter.filter_map(|rdata| match *rdata {
$r(ref data) => Some(data),
_ => None,
})
.next()
}
}
pub struct $f(LookupFuture);
impl From<LookupFuture> for $f {
fn from(lookup_future: LookupFuture) -> Self {
$f(lookup_future)
}
}
impl Future for $f {
type Item = $l;
type Error = ResolveError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.0.poll() {
Ok(Async::Ready(lookup)) => Ok(Async::Ready($l(lookup))),
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(e) => Err(e),
}
}
}
};
}
lookup_type!(
ReverseLookup,
ReverseLookupIter,
ReverseLookupFuture,
RData::PTR,
Name
);
lookup_type!(
Ipv4Lookup,
Ipv4LookupIter,
Ipv4LookupFuture,
RData::A,
Ipv4Addr
);
lookup_type!(
Ipv6Lookup,
Ipv6LookupIter,
Ipv6LookupFuture,
RData::AAAA,
Ipv6Addr
);
lookup_type!(MxLookup, MxLookupIter, MxLookupFuture, RData::MX, rdata::MX);
lookup_type!(
TxtLookup,
TxtLookupIter,
TxtLookupFuture,
RData::TXT,
rdata::TXT
);
#[cfg(test)]
pub mod tests {
use std::net::{IpAddr, Ipv4Addr};
use std::sync::{Arc, Mutex};
use futures::{future, Future};
use proto::error::{ProtoErrorKind, ProtoResult};
use proto::op::Message;
use proto::rr::{Name, RData, Record, RecordType};
use proto::xfer::{DnsRequest, DnsRequestOptions};
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 empty() -> ProtoResult<DnsResponse> {
Ok(Message::new().into())
}
pub fn error() -> ProtoResult<DnsResponse> {
Err(ProtoErrorKind::Io.into())
}
pub fn mock(messages: Vec<ProtoResult<DnsResponse>>) -> MockDnsHandle {
MockDnsHandle {
messages: Arc::new(Mutex::new(messages)),
}
}
#[test]
fn test_lookup() {
assert_eq!(
LookupFuture::lookup(
vec![Name::root()],
RecordType::A,
DnsRequestOptions::default(),
CachingClient::new(0, mock(vec![v4_message()])),
)
.wait()
.unwrap()
.iter()
.map(|r| r.to_ip_addr().unwrap())
.collect::<Vec<IpAddr>>(),
vec![Ipv4Addr::new(127, 0, 0, 1)]
);
}
#[test]
fn test_error() {
assert!(LookupFuture::lookup(
vec![Name::root()],
RecordType::A,
DnsRequestOptions::default(),
CachingClient::new(0, mock(vec![error()])),
)
.wait()
.is_err());
}
#[test]
fn test_empty_no_response() {
assert_eq!(
*LookupFuture::lookup(
vec![Name::root()],
RecordType::A,
DnsRequestOptions::default(),
CachingClient::new(0, mock(vec![empty()])),
)
.wait()
.unwrap_err()
.kind(),
ResolveErrorKind::NoRecordsFound {
query: Query::query(Name::root(), RecordType::A),
valid_until: None,
}
);
}
}