add dnssec lookup to Lookup

This commit is contained in:
Benjamin Fry
2023-11-26 15:05:46 -08:00
parent 494fb8df45
commit 5c53fbcdad
3 changed files with 122 additions and 3 deletions

View File

@@ -339,7 +339,7 @@ impl fmt::Display for ProofError {
}
/// A wrapper type to ensure that the state of a DNSSEC proof is evaluated before use
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Proven<T> {
proof: Proof,
value: T,
@@ -393,4 +393,29 @@ impl<T> Proven<T> {
Err(self)
}
}
/// Map the value with the associated function, carrying forward the proof
pub fn map<U, F>(self, f: F) -> Proven<U>
where
F: FnOnce(T) -> U,
{
Proven {
proof: self.proof,
value: f(self.value),
}
}
}
impl<T> Proven<Option<T>> {
/// If the inner type is an Option this will transpose them so that it's an option wrapped Proven
pub fn transpose(self) -> Option<Proven<T>> {
if let Some(value) = self.value {
Some(Proven {
proof: self.proof,
value,
})
} else {
None
}
}
}

View File

@@ -19,7 +19,7 @@ use crate::{
};
#[cfg(feature = "dnssec")]
use crate::rr::dnssec::Proof;
use crate::rr::dnssec::{Proof, Proven};
#[allow(deprecated)]
use crate::rr::IntoRecordSet;
@@ -775,6 +775,22 @@ impl PartialOrd<Self> for Record {
}
}
#[cfg(feature = "dnssec")]
impl From<Record> for Proven<Record> {
fn from(record: Record) -> Self {
let proof = record.proof();
Self::new(proof, record)
}
}
#[cfg(feature = "dnssec")]
impl<'a> From<&'a Record> for Proven<&'a Record> {
fn from(record: &'a Record) -> Self {
let proof = record.proof();
Self::new(proof, record)
}
}
/// A Record where the RecordData type is already known
pub struct RecordRef<'a, R: RecordData> {
name_labels: &'a Name,

View File

@@ -41,7 +41,7 @@ use crate::{
};
#[cfg(feature = "dnssec")]
use proto::DnssecDnsHandle;
use proto::{rr::dnssec::Proven, DnssecDnsHandle};
/// Result of a DNS query when querying for any record type supported by the Hickory DNS Proto library.
///
@@ -89,6 +89,12 @@ impl Lookup {
LookupIter(self.records.iter())
}
/// Returns a borrowed iterator of the returned data wrapped in a dnssec Proven type
#[cfg(feature = "dnssec")]
pub fn dnssec_iter(&self) -> DnssecIter<'_> {
DnssecIter(self.dnssec_record_iter())
}
/// Returns an iterator over all records returned during the query.
///
/// It may include additional record types beyond the queried type, e.g. CNAME.
@@ -96,6 +102,12 @@ impl Lookup {
LookupRecordIter(self.records.iter())
}
/// Returns a borrowed iterator of the returned records wrapped in a dnssec Proven type
#[cfg(feature = "dnssec")]
pub fn dnssec_record_iter(&self) -> DnssecLookupRecordIter<'_> {
DnssecLookupRecordIter(self.records.iter())
}
/// Returns the `Instant` at which this `Lookup` is no longer valid.
pub fn valid_until(&self) -> Instant {
self.valid_until
@@ -139,6 +151,20 @@ impl<'a> Iterator for LookupIter<'a> {
}
}
/// An iterator over record data with all data wrapped in a Proven type for dnssec validation
#[cfg(feature = "dnssec")]
pub struct DnssecIter<'a>(DnssecLookupRecordIter<'a>);
#[cfg(feature = "dnssec")]
impl<'a> Iterator for DnssecIter<'a> {
type Item = Proven<&'a RData>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().and_then(|r| r.map(Record::data).transpose())
}
}
/// Borrowed view of set of [`Record`]s returned from a Lookup
pub struct LookupRecordIter<'a>(Iter<'a, Record>);
@@ -150,6 +176,20 @@ impl<'a> Iterator for LookupRecordIter<'a> {
}
}
/// An iterator over record data with all data wrapped in a Proven type for dnssec validation
#[cfg(feature = "dnssec")]
pub struct DnssecLookupRecordIter<'a>(Iter<'a, Record>);
#[cfg(feature = "dnssec")]
impl<'a> Iterator for DnssecLookupRecordIter<'a> {
type Item = Proven<&'a Record>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(Proven::from)
}
}
// TODO: consider removing this as it's not a zero-cost abstraction
impl IntoIterator for Lookup {
type Item = RData;
@@ -699,4 +739,42 @@ pub mod tests {
assert_eq!(lookup.next().unwrap(), RData::A(A::new(127, 0, 0, 2)));
assert_eq!(lookup.next(), None);
}
#[test]
#[cfg(feature = "dnssec")]
fn test_dnssec_lookup() {
use hickory_proto::rr::dnssec::Proof;
let mut a1 = Record::from_rdata(
Name::from_str("www.example.com.").unwrap(),
80,
RData::A(A::new(127, 0, 0, 1)),
);
a1.set_proof(Proof::Secure);
let mut a2 = Record::from_rdata(
Name::from_str("www.example.com.").unwrap(),
80,
RData::A(A::new(127, 0, 0, 2)),
);
a2.set_proof(Proof::Insecure);
let lookup = Lookup {
query: Query::default(),
records: Arc::from([a1.clone(), a2.clone()]),
valid_until: Instant::now(),
};
let mut lookup = lookup.dnssec_iter();
assert_eq!(
*lookup.next().unwrap().try_take(Proof::Secure).unwrap(),
*a1.data().unwrap()
);
assert_eq!(
*lookup.next().unwrap().try_take(Proof::Insecure).unwrap(),
*a2.data().unwrap()
);
assert_eq!(lookup.next(), None);
}
}