Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix for ipv4 early return/search #467

Merged
merged 3 commits into from
May 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 8 additions & 17 deletions integration-tests/src/authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,7 @@ pub fn create_example() -> Authority {
.set_rr_type(RecordType::AAAA)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::AAAA(Ipv6Addr::new(
0x2606,
0x2800,
0x220,
0x1,
0x248,
0x1893,
0x25c8,
0x1946,
0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
)))
.clone(),
0,
Expand Down Expand Up @@ -159,14 +152,7 @@ pub fn create_example() -> Authority {
.set_rr_type(RecordType::AAAA)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::AAAA(Ipv6Addr::new(
0x2606,
0x2800,
0x220,
0x1,
0x248,
0x1893,
0x25c8,
0x1946,
0x2606, 0x2800, 0x220, 0x1, 0x248, 0x1893, 0x25c8, 0x1946,
)))
.clone(),
0,
Expand All @@ -191,7 +177,12 @@ pub fn create_secure_example() -> Authority {
let rsa = Rsa::generate(2048).unwrap();
let key = KeyPair::from_rsa(rsa).unwrap();
let dnskey = key.to_dnskey(Algorithm::RSASHA256).unwrap();
let signer = Signer::dnssec(dnskey, key, authority.origin().clone().into(), Duration::weeks(1));
let signer = Signer::dnssec(
dnskey,
key,
authority.origin().clone().into(),
Duration::weeks(1),
);

authority.add_secure_key(signer);
authority.secure_zone();
Expand Down
89 changes: 87 additions & 2 deletions integration-tests/tests/lookup_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,15 @@ use futures::{future, Future};
use tokio::runtime::current_thread::Runtime;

use trust_dns_proto::op::{NoopMessageFinalizer, Query};
use trust_dns_proto::rr::{Name, RData, RecordType};
use trust_dns_proto::rr::{DNSClass, Name, RData, Record, RecordType};
use trust_dns_proto::DnsFuture;
use trust_dns_resolver::config::LookupIpStrategy;
use trust_dns_resolver::error::ResolveError;
use trust_dns_resolver::lookup::{Lookup, LookupFuture};
use trust_dns_resolver::lookup_ip::LookupIpFuture;
use trust_dns_resolver::lookup_state::CachingClient;
use trust_dns_resolver::Hosts;
use trust_dns_server::authority::Catalog;
use trust_dns_server::authority::{Authority, Catalog};

use trust_dns_integration::authority::create_example;
use trust_dns_integration::mock_client::*;
Expand Down Expand Up @@ -90,13 +90,98 @@ fn test_lookup_hosts() {
CachingClient::new(0, client),
Default::default(),
Some(Arc::new(hosts)),
None,
)
});
let lookup = io_loop.block_on(lookup).unwrap();

assert_eq!(lookup.iter().next().unwrap(), Ipv4Addr::new(10, 0, 1, 104));
}

fn create_ip_like_example() -> Authority {
let mut authority = create_example();
authority.upsert(
Record::new()
.set_name(Name::from_str("1.2.3.4.example.com.").unwrap())
.set_ttl(86400)
.set_rr_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::A(Ipv4Addr::new(198, 51, 100, 35)))
.clone(),
0,
);

authority
}

#[test]
fn test_lookup_ipv4_like() {
let authority = create_ip_like_example();
let mut catalog = Catalog::new();
catalog.upsert(authority.origin().clone().into(), authority);

let mut io_loop = Runtime::new().unwrap();
let (stream, sender) = TestClientStream::new(Arc::new(Mutex::new(catalog)));
let client = future::lazy(|| {
future::ok(DnsFuture::new(
stream,
Box::new(sender),
NoopMessageFinalizer::new(),
))
});

let lookup = client.and_then(|client| {
LookupIpFuture::lookup(
vec![Name::from_str("1.2.3.4.example.com.").unwrap()],
LookupIpStrategy::default(),
CachingClient::new(0, client),
Default::default(),
Some(Arc::new(Hosts::default())),
Some(RData::A(Ipv4Addr::new(1, 2, 3, 4))),
)
});
let lookup = io_loop.block_on(lookup).unwrap();

assert_eq!(
lookup.iter().next().unwrap(),
Ipv4Addr::new(198, 51, 100, 35)
);
}

#[test]
fn test_lookup_ipv4_like_fall_through() {
let authority = create_ip_like_example();
let mut catalog = Catalog::new();
catalog.upsert(authority.origin().clone().into(), authority);

let mut io_loop = Runtime::new().unwrap();
let (stream, sender) = TestClientStream::new(Arc::new(Mutex::new(catalog)));
let client = future::lazy(|| {
future::ok(DnsFuture::new(
stream,
Box::new(sender),
NoopMessageFinalizer::new(),
))
});

let lookup = client.and_then(|client| {
LookupIpFuture::lookup(
vec![Name::from_str("198.51.100.35.example.com.").unwrap()],
LookupIpStrategy::default(),
CachingClient::new(0, client),
Default::default(),
Some(Arc::new(Hosts::default())),
Some(RData::A(Ipv4Addr::new(198, 51, 100, 35))),
)
});
let lookup = io_loop.block_on(lookup).unwrap();

assert_eq!(
lookup.iter().next().unwrap(),
Ipv4Addr::new(198, 51, 100, 35)
);
}

#[test]
fn test_mock_lookup() {
let resp_query = Query::query(Name::from_str("www.example.com.").unwrap(), RecordType::A);
Expand Down
9 changes: 9 additions & 0 deletions resolver/src/lookup_ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ where
options: DnsRequestOptions,
future: Box<Future<Item = Lookup, Error = ResolveError> + Send>,
hosts: Option<Arc<Hosts>>,
finally_ip_addr: Option<RData>,
}

impl<C: DnsHandle<Error = ResolveError> + 'static> LookupIpFuture<C> {
Expand All @@ -98,6 +99,7 @@ impl<C: DnsHandle<Error = ResolveError> + 'static> LookupIpFuture<C> {
client_cache: CachingClient<C>,
options: DnsRequestOptions,
hosts: Option<Arc<Hosts>>,
finally_ip_addr: Option<RData>,
) -> Self {
let name = names.pop().ok_or_else(|| {
ResolveError::from(ResolveErrorKind::Message("can not lookup IPs for no names"))
Expand All @@ -121,6 +123,7 @@ impl<C: DnsHandle<Error = ResolveError> + 'static> LookupIpFuture<C> {
options,
future: query,
hosts: hosts,
finally_ip_addr,
}
}

Expand All @@ -142,6 +145,10 @@ impl<C: DnsHandle<Error = ResolveError> + 'static> LookupIpFuture<C> {
// guarantee that we get scheduled for the next turn...
task::current().notify();
Ok(Async::NotReady)
} else if let Some(ip_addr) = mem::replace(&mut self.finally_ip_addr, None) {
Ok(Async::Ready(
Lookup::new_with_max_ttl(Arc::new(vec![ip_addr])).into(),
))
} else {
otherwise()
}
Expand All @@ -158,6 +165,7 @@ impl<C: DnsHandle<Error = ResolveError> + 'static> LookupIpFuture<C> {
ResolveErrorKind::Msg(format!("{}", error)).into(),
)),
hosts: None,
finally_ip_addr: None,
};
}

Expand All @@ -169,6 +177,7 @@ impl<C: DnsHandle<Error = ResolveError> + 'static> LookupIpFuture<C> {
options: DnsRequestOptions::default(),
future: Box::new(future::ok(lp)),
hosts: None,
finally_ip_addr: None,
};
}
}
Expand Down
113 changes: 105 additions & 8 deletions resolver/src/resolver_future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,17 +260,34 @@ impl ResolverFuture {
/// # Arguments
/// * `host` - string hostname, if this is an invalid hostname, an error will be returned.
pub fn lookup_ip<N: IntoName + TryParseIp>(&self, host: N) -> LookupIpFuture {
let mut finally_ip_addr = None;

// if host is a ip address, return directly.
if let Some(addr) = host.try_parse_ip() {
return LookupIpFuture::ok(
self.client_cache.clone(),
Lookup::new_with_max_ttl(Arc::new(vec![addr])),
);
if let Some(ip_addr) = host.try_parse_ip() {
// if ndots are greater than 4, then we can't assume the name is an IpAddr
// this accepts IPv6 as well, b/c IPv6 can take the form: 2001:db8::198.51.100.35
// but `:` is not a valid DNS character, so techinically this will fail parsing.
// TODO: should we always do search before returning this?
if self.options.ndots > 4 {
finally_ip_addr = Some(ip_addr);
} else {
return LookupIpFuture::ok(
self.client_cache.clone(),
Lookup::new_with_max_ttl(Arc::new(vec![ip_addr])),
);
}
}

let name = match host.into_name() {
Ok(name) => name,
Err(err) => {
let name = match (host.into_name(), finally_ip_addr.as_ref()) {
(Ok(name), _) => name,
(Err(_), Some(ip_addr)) => {
// it was a valid IP, return that...
return LookupIpFuture::ok(
self.client_cache.clone(),
Lookup::new_with_max_ttl(Arc::new(vec![ip_addr.clone()])),
);
}
(Err(err), None) => {
return LookupIpFuture::error(self.client_cache.clone(), err);
}
};
Expand All @@ -281,12 +298,14 @@ impl ResolverFuture {
} else {
None
};

LookupIpFuture::lookup(
names,
self.options.ip_strategy,
self.client_cache.clone(),
DnsRequestOptions::default(),
hosts,
finally_ip_addr,
)
}

Expand Down Expand Up @@ -782,4 +801,82 @@ mod tests {
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 = ResolverFuture::new(
config,
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
ndots: 5,
..ResolverOpts::default()
},
);

let response = io_loop
.block_on(resolver.and_then(|resolver| 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 = ResolverFuture::new(
config,
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
ndots: 5,
..ResolverOpts::default()
},
);

let response = io_loop
.block_on(resolver.and_then(|resolver| 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 = ResolverFuture::new(
config,
ResolverOpts {
ip_strategy: LookupIpStrategy::Ipv4Only,
ndots: 5,
..ResolverOpts::default()
},
);

let response = io_loop
.block_on(resolver.and_then(|resolver| 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))
);
}
}