use std::clone::Clone;
use std::collections::HashSet;
use std::mem;
use std::rc::Rc;
use futures::*;
use client::ClientHandle;
use error::*;
use op::{Message, OpCode, Query};
use rr::{domain, DNSClass, RData, Record, RecordType};
#[cfg(any(feature = "openssl", feature = "ring"))]
use rr::dnssec::Verifier;
use rr::dnssec::{Algorithm, SupportedAlgorithms, TrustAnchor};
use rr::rdata::{DNSKEY, SIG};
use rr::rdata::opt::EdnsOption;
#[derive(Debug)]
struct Rrset {
pub name: domain::Name,
pub record_type: RecordType,
pub record_class: DNSClass,
pub records: Vec<Record>,
}
#[derive(Clone)]
#[must_use = "queries can only be sent through a ClientHandle"]
pub struct SecureClientHandle<H: ClientHandle + 'static> {
client: H,
trust_anchor: Rc<TrustAnchor>,
request_depth: usize,
minimum_key_len: usize,
minimum_algorithm: Algorithm, }
impl<H> SecureClientHandle<H>
where H: ClientHandle + 'static
{
pub fn new(client: H) -> SecureClientHandle<H> {
Self::with_trust_anchor(client, TrustAnchor::default())
}
pub fn with_trust_anchor(client: H, trust_anchor: TrustAnchor) -> SecureClientHandle<H> {
SecureClientHandle {
client: client,
trust_anchor: Rc::new(trust_anchor),
request_depth: 0,
minimum_key_len: 0,
minimum_algorithm: Algorithm::RSASHA256,
}
}
fn clone_with_context(&self) -> Self {
SecureClientHandle {
client: self.client.clone(),
trust_anchor: self.trust_anchor.clone(),
request_depth: self.request_depth + 1,
minimum_key_len: self.minimum_key_len,
minimum_algorithm: self.minimum_algorithm,
}
}
}
impl<H> ClientHandle for SecureClientHandle<H>
where H: ClientHandle + 'static
{
fn send(&mut self, mut message: Message) -> Box<Future<Item = Message, Error = ClientError>> {
if self.request_depth > 20 {
return Box::new(failed(ClientErrorKind::Message("exceeded max validation depth")
.into()));
}
if let OpCode::Query = message.op_code() {
let query = message.queries().first().cloned().unwrap();
let client: SecureClientHandle<H> = self.clone_with_context();
#[cfg(any(feature = "openssl", feature = "ring"))]
{
let edns = message.edns_mut();
edns.set_dnssec_ok(true);
let mut algorithms = SupportedAlgorithms::new();
#[cfg(feature = "openssl")]
{
algorithms.set(Algorithm::RSASHA256);
algorithms.set(Algorithm::ECDSAP256SHA256);
algorithms.set(Algorithm::ECDSAP384SHA384);
}
#[cfg(feature = "ring")]
algorithms.set(Algorithm::ED25519);
let dau = EdnsOption::DAU(algorithms);
let dhu = EdnsOption::DHU(algorithms);
edns.set_option(dau);
edns.set_option(dhu);
}
message.set_authentic_data(true);
message.set_checking_disabled(false);
let dns_class = message
.queries()
.first()
.map_or(DNSClass::IN, |q| q.query_class());
return Box::new(self.client
.send(message)
.and_then(move |message_response| {
debug!("validating message_response: {}",
message_response.id());
verify_rrsets(client, message_response, dns_class)
})
.and_then(move |verified_message| {
if verified_message.answers().is_empty() {
let nsecs = verified_message
.name_servers()
.iter()
.filter(|rr| rr.rr_type() == RecordType::NSEC)
.collect::<Vec<_>>();
if !verify_nsec(&query, nsecs) {
return Err(ClientErrorKind::Message("could not validate nxdomain \
with NSEC")
.into());
}
}
Ok(verified_message)
}));
}
self.client.send(message)
}
}
struct VerifyRrsetsFuture {
message_result: Option<Message>,
rrsets: SelectAll<Box<Future<Item = Rrset, Error = ClientError>>>,
verified_rrsets: HashSet<(domain::Name, RecordType)>,
}
fn verify_rrsets<H>(client: SecureClientHandle<H>,
message_result: Message,
dns_class: DNSClass)
-> Box<Future<Item = Message, Error = ClientError>>
where H: ClientHandle
{
let mut rrset_types: HashSet<(domain::Name, RecordType)> = HashSet::new();
for rrset in message_result.answers()
.iter()
.chain(message_result.name_servers())
.filter(|rr| rr.rr_type() != RecordType::RRSIG &&
(client.request_depth <= 1 ||
rr.rr_type() == RecordType::DNSKEY ||
rr.rr_type() == RecordType::DS))
.map(|rr| (rr.name().clone(), rr.rr_type())) {
rrset_types.insert(rrset);
}
if rrset_types.is_empty() {
let mut message_result = message_result;
message_result.take_answers();
message_result.take_name_servers();
message_result.take_additionals();
return Box::new(failed(ClientErrorKind::Message("no results to verify").into()));
}
let mut rrsets: Vec<Box<Future<Item = Rrset, Error = ClientError>>> =
Vec::with_capacity(rrset_types.len());
for (name, record_type) in rrset_types {
let rrset: Vec<Record> = message_result
.answers()
.iter()
.chain(message_result.name_servers())
.chain(message_result.additionals())
.filter(|rr| rr.rr_type() == record_type && rr.name() == &name)
.cloned()
.collect();
let rrsigs: Vec<Record> = message_result
.answers()
.iter()
.chain(message_result.name_servers())
.chain(message_result.additionals())
.filter(|rr| rr.rr_type() == RecordType::RRSIG)
.filter(|rr| if let &RData::SIG(ref rrsig) = rr.rdata() {
rrsig.type_covered() == record_type
} else {
false
})
.cloned()
.collect();
let rrset = Rrset {
name: name,
record_type: record_type,
record_class: dns_class,
records: rrset,
};
debug!("verifying: {}, record_type: {:?}, rrsigs: {}",
rrset.name,
record_type,
rrsigs.len());
rrsets.push(verify_rrset(client.clone_with_context(), rrset, rrsigs));
}
let rrsets_to_verify = select_all(rrsets);
Box::new(VerifyRrsetsFuture {
message_result: Some(message_result),
rrsets: rrsets_to_verify,
verified_rrsets: HashSet::new(),
})
}
impl Future for VerifyRrsetsFuture {
type Item = Message;
type Error = ClientError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
if self.message_result.is_none() {
return Err(ClientErrorKind::Message("message is none").into());
}
loop {
let remaining = match self.rrsets.poll() {
Ok(Async::NotReady) => return Ok(Async::NotReady),
Ok(Async::Ready((rrset, _, remaining))) => {
debug!("an rrset was verified: {}, {:?}",
rrset.name,
rrset.record_type);
self.verified_rrsets
.insert((rrset.name, rrset.record_type));
remaining
}
Err((e, _, remaining)) => {
debug!("an rrset failed to verify: {}", e);
if remaining.is_empty() {
return Err(e);
}
remaining
}
};
if !remaining.is_empty() {
drop(mem::replace(&mut self.rrsets, select_all(remaining)));
} else {
let mut message_result = mem::replace(&mut self.message_result, None).unwrap();
let answers = message_result
.take_answers()
.into_iter()
.filter(|record| {
self.verified_rrsets
.contains(&(record.name().clone(), record.rr_type()))
})
.collect::<Vec<Record>>();
let name_servers = message_result
.take_name_servers()
.into_iter()
.filter(|record| {
self.verified_rrsets
.contains(&(record.name().clone(), record.rr_type()))
})
.collect::<Vec<Record>>();
let additionals = message_result
.take_additionals()
.into_iter()
.filter(|record| {
self.verified_rrsets
.contains(&(record.name().clone(), record.rr_type()))
})
.collect::<Vec<Record>>();
message_result.insert_answers(answers);
message_result.insert_name_servers(name_servers);
message_result.insert_additionals(additionals);
return Ok(Async::Ready(message_result));
}
}
}
}
fn verify_rrset<H>(client: SecureClientHandle<H>,
rrset: Rrset,
rrsigs: Vec<Record>)
-> Box<Future<Item = Rrset, Error = ClientError>>
where H: ClientHandle
{
if let RecordType::DNSKEY = rrset.record_type {
if rrsigs.is_empty() {
debug!("unsigned key: {}, {:?}", rrset.name, rrset.record_type);
return verify_dnskey_rrset(client.clone_with_context(), rrset);
}
}
Box::new(verify_default_rrset(client.clone_with_context(), rrset, rrsigs)
.and_then(|rrset|
match rrset.record_type {
RecordType::DNSKEY => verify_dnskey_rrset(client, rrset),
_ => Box::new(finished(rrset)),
}
)
.map_err(|e| {
debug!("rrset failed validation: {}", e);
e
})
)
}
fn verify_dnskey_rrset<H>(mut client: SecureClientHandle<H>,
rrset: Rrset)
-> Box<Future<Item = Rrset, Error = ClientError>>
where H: ClientHandle
{
debug!("dnskey validation {}, record_type: {:?}",
rrset.name,
rrset.record_type);
{
let anchored_keys = rrset
.records
.iter()
.enumerate()
.filter(|&(_, rr)| rr.rr_type() == RecordType::DNSKEY)
.filter_map(|(i, rr)| if let &RData::DNSKEY(ref rdata) = rr.rdata() {
Some((i, rdata))
} else {
None
})
.filter_map(|(i, rdata)| if client.trust_anchor.contains(rdata.public_key()) {
debug!("in trust_anchor");
Some(i)
} else {
None
})
.collect::<Vec<usize>>();
if !anchored_keys.is_empty() {
let mut rrset = rrset;
preserve(&mut rrset.records, anchored_keys);
debug!("validated dnskey with trust_anchor: {}, {}",
rrset.name,
rrset.records.len());
return Box::new(finished((rrset)));
}
}
let valid_dnskey = client
.query(rrset.name.clone(), rrset.record_class, RecordType::DS)
.and_then(move |ds_message| {
let valid_keys = rrset
.records
.iter()
.enumerate()
.filter(|&(_, rr)| rr.rr_type() == RecordType::DNSKEY)
.filter_map(|(i, rr)| if let &RData::DNSKEY(ref rdata) = rr.rdata() {
Some((i, rdata))
} else {
None
})
.filter(|&(_, key_rdata)| {
ds_message.answers()
.iter()
.filter(|ds| ds.rr_type() == RecordType::DS)
.filter_map(|ds| if let &RData::DS(ref ds_rdata) = ds.rdata() {
Some(ds_rdata)
} else {
None
})
.any(|ds_rdata| ds_rdata.covers(&rrset.name, key_rdata)
.unwrap_or(false))
})
.map(|(i, _)| i)
.collect::<Vec<usize>>();
if !valid_keys.is_empty() {
let mut rrset = rrset;
preserve(&mut rrset.records, valid_keys);
debug!("validated dnskey: {}, {}", rrset.name, rrset.records.len());
Ok(rrset)
} else {
Err(ClientErrorKind::Message("Could not validate all DNSKEYs").into())
}
});
Box::new(valid_dnskey)
}
fn preserve<T, I>(vec: &mut Vec<T>, indexes: I)
where I: IntoIterator<Item = usize>,
<I as IntoIterator>::IntoIter: DoubleEndedIterator
{
let mut indexes_iter = indexes.into_iter().rev();
let mut i = indexes_iter.next();
for j in (0..vec.len()).rev() {
if i.map_or(false, |i| i > j) {
i = indexes_iter.next();
}
if i.map_or(true, |i| i != j) {
vec.remove(j);
}
}
}
#[test]
fn test_preserve() {
let mut vec = vec![1, 2, 3];
let indexes = vec![];
preserve(&mut vec, indexes);
assert_eq!(vec, vec![]);
let mut vec = vec![1, 2, 3];
let indexes = vec![0];
preserve(&mut vec, indexes);
assert_eq!(vec, vec![1]);
let mut vec = vec![1, 2, 3];
let indexes = vec![1];
preserve(&mut vec, indexes);
assert_eq!(vec, vec![2]);
let mut vec = vec![1, 2, 3];
let indexes = vec![2];
preserve(&mut vec, indexes);
assert_eq!(vec, vec![3]);
let mut vec = vec![1, 2, 3];
let indexes = vec![0, 2];
preserve(&mut vec, indexes);
assert_eq!(vec, vec![1, 3]);
let mut vec = vec![1, 2, 3];
let indexes = vec![0, 1, 2];
preserve(&mut vec, indexes);
assert_eq!(vec, vec![1, 2, 3]);
}
fn verify_default_rrset<H>(client: SecureClientHandle<H>,
rrset: Rrset,
rrsigs: Vec<Record>)
-> Box<Future<Item = Rrset, Error = ClientError>>
where H: ClientHandle
{
let rrset = Rc::new(rrset);
debug!("default validation {}, record_type: {:?}",
rrset.name,
rrset.record_type);
if rrsigs
.iter()
.filter(|rrsig| rrsig.rr_type() == RecordType::RRSIG)
.any(|rrsig| if let &RData::SIG(ref sig) = rrsig.rdata() {
return RecordType::DNSKEY == rrset.record_type &&
sig.signer_name() == &rrset.name;
} else {
panic!("expected a SIG here");
}) {
return Box::new(done(
rrsigs.into_iter()
.filter(|rrsig| rrsig.rr_type() == RecordType::RRSIG)
.map(|rrsig|
if let RData::SIG(sig) = rrsig.unwrap_rdata() {
sig
} else {
panic!("expected a SIG here");
}
)
.filter_map(|sig| {
let rrset = rrset.clone();
if rrset.records.iter()
.any(|r| {
if let &RData::DNSKEY(ref dnskey) = r.rdata() {
verify_rrset_with_dnskey(dnskey, &sig, &rrset).is_ok()
} else {
panic!("expected a DNSKEY here: {:?}", r.rdata());
}
}) {
Some(rrset)
} else {
None
}
})
.next()
.ok_or(ClientErrorKind::Message("self-signed dnskey is invalid").into())
).map(move |rrset| Rc::try_unwrap(rrset).expect("unable to unwrap Rc"))
);
}
let verifications = rrsigs.into_iter()
.filter(|rrsig| rrsig.rr_type() == RecordType::RRSIG)
.map(|rrsig|
if let RData::SIG(sig) = rrsig.unwrap_rdata() {
sig
} else {
panic!("expected a SIG here");
}
)
.map(|sig| {
let rrset = rrset.clone();
let mut client = client.clone_with_context();
client.query(sig.signer_name().clone(), rrset.record_class, RecordType::DNSKEY)
.and_then(move |message|
message.answers()
.iter()
.filter(|r| r.rr_type() == RecordType::DNSKEY)
.find(|r|
if let &RData::DNSKEY(ref dnskey) = r.rdata() {
verify_rrset_with_dnskey(dnskey, &sig, &rrset).is_ok()
} else {
panic!("expected a DNSKEY here: {:?}", r.rdata());
}
)
.map(|_| rrset)
.ok_or(ClientErrorKind::Message("validation failed").into())
)
})
.collect::<Vec<_>>();
if verifications.is_empty() {
return Box::new(failed(ClientErrorKind::Msg(format!("no RRSIGs available for \
validation: {}, {:?}",
rrset.name,
rrset.record_type))
.into()));
}
let select =
select_ok(verifications)
.map(move |(rrset, rest)| {
drop(rest); Rc::try_unwrap(rrset).expect("unable to unwrap Rc")
});
Box::new(select)
}
#[cfg(any(feature = "openssl", feature = "ring"))]
fn verify_rrset_with_dnskey(dnskey: &DNSKEY, sig: &SIG, rrset: &Rrset) -> ClientResult<()> {
if dnskey.revoke() {
debug!("revoked");
return Err(ClientErrorKind::Message("revoked").into());
} if !dnskey.zone_key() {
return Err(ClientErrorKind::Message("is not a zone key").into());
}
if dnskey.algorithm() != sig.algorithm() {
return Err(ClientErrorKind::Message("mismatched algorithm").into());
}
dnskey.verify_rrsig(&rrset.name, rrset.record_class, sig, &rrset.records).map_err(Into::into)
}
#[cfg(not(any(feature = "openssl", feature = "ring")))]
fn verify_rrset_with_dnskey(_: &DNSKEY, _: &SIG, _: &Rrset) -> ClientResult<()> {
Err(ClientErrorKind::Message("openssl or ring feature(s) not enabled").into())
}
fn verify_nsec(query: &Query, nsecs: Vec<&Record>) -> bool {
if nsecs
.iter()
.any(|r| {
query.name() == r.name() &&
{
if let &RData::NSEC(ref rdata) = r.rdata() {
!rdata.type_bit_maps().contains(&query.query_type())
} else {
panic!("expected NSEC was {:?}", r.rr_type()) }
}
}) {
return true;
}
if nsecs
.iter()
.filter(|r| query.name() != r.name())
.any(|r| {
query.name() > r.name() &&
{
if let &RData::NSEC(ref rdata) = r.rdata() {
query.name() < rdata.next_domain_name()
} else {
panic!("expected NSEC was {:?}", r.rr_type()) }
}
}) {
return true;
}
false
}