blob: e69fe0f1264294c5d6092fb7d7e01fea591d032a [file] [log] [blame]
// Copyright 2015-2019 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
use std::iter::Chain;
use std::slice::Iter;
use std::vec;
use log::info;
use crate::rr::{DNSClass, Name, RData, Record, RecordType};
#[cfg(feature = "dnssec")]
use crate::rr::dnssec::SupportedAlgorithms;
/// Set of resource records associated to a name and type
#[derive(Clone, Debug, PartialEq)]
pub struct RecordSet {
name: Name,
record_type: RecordType,
dns_class: DNSClass,
ttl: u32,
records: Vec<Record>,
rrsigs: Vec<Record>,
serial: u32, // serial number at which this record was modified
}
impl RecordSet {
/// Creates a new Resource Record Set.
///
/// # Arguments
///
/// * `name` - The label for the `RecordSet`
/// * `record_type` - `RecordType` of this `RecordSet`, all records in the `RecordSet` must be of the
/// specified `RecordType`.
/// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
/// signing for DNSSec after updates.
///
/// # Return value
///
/// The newly created Resource Record Set
/// TODO: make all cloned params pass by value
pub fn new(name: &Name, record_type: RecordType, serial: u32) -> Self {
RecordSet {
name: name.clone(),
record_type,
dns_class: DNSClass::IN,
ttl: 0,
records: Vec::new(),
rrsigs: Vec::new(),
serial,
}
}
/// Creates a new Resource Record Set.
///
/// # Arguments
///
/// * `name` - The label for the `RecordSet`
/// * `record_type` - `RecordType` of this `RecordSet`, all records in the `RecordSet` must be of the
/// specified `RecordType`.
/// * `ttl` - time-to-live for the `RecordSet` in seconds.
///
/// # Return value
///
/// The newly created Resource Record Set
/// TODO: make all cloned params pass by value
pub fn with_ttl(name: Name, record_type: RecordType, ttl: u32) -> Self {
RecordSet {
name,
record_type,
dns_class: DNSClass::IN,
ttl,
records: Vec::new(),
rrsigs: Vec::new(),
serial: 0,
}
}
/// # Return value
///
/// Label of the Resource Record Set
pub fn name(&self) -> &Name {
&self.name
}
/// # Return value
///
/// `RecordType` of the Resource Record Set
pub fn record_type(&self) -> RecordType {
self.record_type
}
/// Sets the DNSClass to the specified value
///
/// This will traverse every record and associate with it the specified dns_class
pub fn set_dns_class(&mut self, dns_class: DNSClass) {
self.dns_class = dns_class;
for r in &mut self.records {
r.set_dns_class(dns_class);
}
}
/// Returns the `DNSClass` of the RecordSet
pub fn dns_class(&self) -> DNSClass {
self.dns_class
}
/// Sets the TTL, in seconds, to the specified value
///
/// This will traverse every record and associate with it the specified ttl
pub fn set_ttl(&mut self, ttl: u32) {
self.ttl = ttl;
for r in &mut self.records {
r.set_ttl(ttl);
}
}
/// Returns the time-to-live for the record.
///
/// # Return value
///
/// TTL, time-to-live, of the Resource Record Set, this is the maximum length of time that an
/// RecordSet should be cached.
pub fn ttl(&self) -> u32 {
self.ttl
}
/// Returns a Vec of all records in the set.
///
/// # Arguments
///
/// * `and_rrsigs` - if true, RRSIGs will be returned if they exist
/// * `supported_algorithms` - the RRSIGs will be filtered by the set of supported_algorithms,
/// and then only the maximal RRSIG algorithm will be returned.
#[cfg(feature = "dnssec")]
pub fn records(
&self,
and_rrsigs: bool,
supported_algorithms: SupportedAlgorithms,
) -> RrsetRecords {
if and_rrsigs {
self.records_with_rrsigs(supported_algorithms)
} else {
self.records_without_rrsigs()
}
}
/// Returns a Vec of all records in the set, with RRSIGs, if present.
///
/// # Arguments
///
/// * `supported_algorithms` - the RRSIGs will be filtered by the set of supported_algorithms,
/// and then only the maximal RRSIG algorithm will be returned.
#[cfg(feature = "dnssec")]
pub fn records_with_rrsigs(&self, supported_algorithms: SupportedAlgorithms) -> RrsetRecords {
if self.records.is_empty() {
RrsetRecords::Empty
} else {
let rrsigs = RrsigsByAlgorithms {
rrsigs: self.rrsigs.iter(),
supported_algorithms,
};
RrsetRecords::RecordsAndRrsigs(RecordsAndRrsigsIter(self.records.iter().chain(rrsigs)))
}
}
/// Returns a Vec of all records in the set, without any RRSIGs.
pub fn records_without_rrsigs(&self) -> RrsetRecords {
if self.records.is_empty() {
RrsetRecords::Empty
} else {
RrsetRecords::RecordsOnly(self.records.iter())
}
}
/// Returns an iterator over the records in the set
#[deprecated(note = "see `records_without_rrsigs`")]
pub fn iter(&self) -> Iter<Record> {
self.records.iter()
}
/// Returns true if there are no records in this set
pub fn is_empty(&self) -> bool {
self.records.is_empty()
}
/// Returns the serial number at which the record was updated.
pub fn serial(&self) -> u32 {
self.serial
}
/// Returns a slice of all the Records signatures in the RecordSet
pub fn rrsigs(&self) -> &[Record] {
&self.rrsigs
}
/// Inserts a Signature for the Record set
///
/// Many can be associated with the RecordSet. Once added, the RecordSet should not be changed
///
/// # Arguments
///
/// * `rrsig` - A signature which covers the RecordSet.
pub fn insert_rrsig(&mut self, rrsig: Record) {
self.rrsigs.push(rrsig)
}
/// Useful for clearing all signatures when the RecordSet is updated, or keys are rotated.
pub fn clear_rrsigs(&mut self) {
self.rrsigs.clear()
}
fn updated(&mut self, serial: u32) {
self.serial = serial;
self.rrsigs.clear(); // on updates, the rrsigs are invalid
}
/// creates a new Record as part of this RecordSet, adding the associated RData
///
/// this interface may be deprecated in the future.
pub fn new_record(&mut self, rdata: &RData) -> &Record {
self.add_rdata(rdata.clone());
self.records
.iter()
.find(|r| r.rdata() == rdata)
.expect("insert failed")
}
/// creates a new Record as part of this RecordSet, adding the associated RData
pub fn add_rdata(&mut self, rdata: RData) -> bool {
debug_assert_eq!(self.record_type, rdata.to_record_type());
let mut record = Record::with(self.name.clone(), self.record_type, self.ttl);
record.set_rdata(rdata);
self.insert(record, 0)
}
/// Inserts a new Resource Record into the Set.
///
/// If the record is inserted, the ttl for the most recent record will be used for the ttl of
/// the entire resource record set.
///
/// This abides by the following restrictions in RFC 2136, April 1997:
///
/// ```text
/// 1.1.5. The following RR types cannot be appended to an RRset. If the
/// following comparison rules are met, then an attempt to add the new RR
/// will result in the replacement of the previous RR:
///
/// SOA compare only NAME, CLASS and TYPE -- it is not possible to
/// have more than one SOA per zone, even if any of the data
/// fields differ.
///
/// CNAME compare only NAME, CLASS, and TYPE -- it is not possible
/// to have more than one CNAME RR, even if their data fields
/// differ.
/// ```
///
/// # Arguments
///
/// * `record` - `Record` asserts that the `name` and `record_type` match the `RecordSet`.
/// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
/// signing for DNSSec after updates. The serial will only be updated if the
/// record was added.
///
/// # Return value
///
/// True if the record was inserted.
///
/// TODO: make a default add without serial number for basic usage
pub fn insert(&mut self, record: Record, serial: u32) -> bool {
assert_eq!(record.name(), &self.name);
assert_eq!(record.rr_type(), self.record_type);
// RFC 2136 DNS Update April 1997
//
// 1.1.5. The following RR types cannot be appended to an RRset. If the
// following comparison rules are met, then an attempt to add the new RR
// will result in the replacement of the previous RR:
match record.rr_type() {
// SOA compare only NAME, CLASS and TYPE -- it is not possible to
// have more than one SOA per zone, even if any of the data
// fields differ.
RecordType::SOA => {
assert!(self.records.len() <= 1);
if let Some(soa_record) = self.records.iter().next() {
match soa_record.rdata() {
&RData::SOA(ref existing_soa) => {
if let RData::SOA(ref new_soa) = *record.rdata() {
if new_soa.serial() <= existing_soa.serial() {
info!(
"update ignored serial out of data: {:?} <= {:?}",
new_soa, existing_soa
);
return false;
}
} else {
// not panicking here, b/c this is a bad record from the client or something, ignore
info!("wrong rdata for SOA update: {:?}", record.rdata());
return false;
}
}
rdata => panic!("wrong rdata: {:?}", rdata), // valid panic, never should happen
}
}
// if we got here, we're updating...
self.records.clear();
}
// RFC 1034/1035
// CNAME compare only NAME, CLASS, and TYPE -- it is not possible
// to have more than one CNAME RR, even if their data fields
// differ.
//
// ANAME https://tools.ietf.org/html/draft-ietf-dnsop-aname-02
// 2.2. Coexistence with other types
//
// Only one ANAME <target> can be defined per <owner>. An ANAME RRset
// MUST NOT contain more than one resource record.
//
// An ANAME's sibling address records are under the control of ANAME
// processing (see Section 5) and are not first-class records in their
// own right. They MAY exist in zone files, but they can subsequently
// be altered by ANAME processing.
//
// ANAME records MAY freely coexist at the same owner name with other RR
// types, except they MUST NOT coexist with CNAME or any other RR type
// that restricts the types with which it can itself coexist.
//
// Like other types, ANAME records can coexist with DNAME records at the
// same owner name; in fact, the two can be used cooperatively to
// redirect both the owner name address records (via ANAME) and
// everything under it (via DNAME).
RecordType::CNAME | RecordType::ANAME => {
assert!(self.records.len() <= 1);
self.records.clear();
}
_ => (),
}
// collect any records to update based on rdata
let to_replace: Vec<usize> = self
.records
.iter()
.enumerate()
.filter(|&(_, rr)| rr.rdata() == record.rdata())
.map(|(i, _)| i)
.collect::<Vec<usize>>();
// if the Records are identical, ignore the update, update all that are not (ttl, etc.)
let mut replaced = false;
for i in to_replace {
if self.records[i] == record {
return false;
}
// TODO: this shouldn't really need a clone since there should only be one...
self.records.push(record.clone());
self.records.swap_remove(i);
self.ttl = record.ttl();
self.updated(serial);
replaced = true;
}
if !replaced {
self.ttl = record.ttl();
self.updated(serial);
self.records.push(record);
true
} else {
replaced
}
}
/// Removes the Resource Record if it exists.
///
/// # Arguments
///
/// * `record` - `Record` asserts that the `name` and `record_type` match the `RecordSet`. Removes
/// any `record` if the record data, `RData`, match.
/// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
/// signing for DNSSec after updates. The serial will only be updated if the
/// record was added.
///
/// # Return value
///
/// True if a record was removed.
pub fn remove(&mut self, record: &Record, serial: u32) -> bool {
assert_eq!(record.name(), &self.name);
assert!(record.rr_type() == self.record_type || record.rr_type() == RecordType::ANY);
match record.rr_type() {
// never delete the last NS record
RecordType::NS => {
if self.records.len() <= 1 {
info!("ignoring delete of last NS record: {:?}", record);
return false;
}
}
// never delete SOA
RecordType::SOA => {
info!("ignored delete of SOA");
return false;
}
_ => (), // move on to the delete
}
// remove the records, first collect all the indexes, then remove the records
let to_remove: Vec<usize> = self
.records
.iter()
.enumerate()
.filter(|&(_, rr)| rr.rdata() == record.rdata())
.map(|(i, _)| i)
.collect::<Vec<usize>>();
let mut removed = false;
for i in to_remove {
self.records.remove(i);
removed = true;
self.updated(serial);
}
removed
}
}
impl From<Record> for RecordSet {
fn from(record: Record) -> Self {
RecordSet {
name: record.name().clone(),
record_type: record.rr_type(),
dns_class: record.dns_class(),
ttl: record.ttl(),
records: vec![record],
rrsigs: vec![],
serial: 0,
}
}
}
/// Types which implement this can be converted into a RecordSet
#[deprecated(note = "use From/Into")]
pub trait IntoRecordSet: Sized {
/// Performs the conversion to a RecordSet
fn into_record_set(self) -> RecordSet;
}
#[allow(deprecated)]
impl IntoRecordSet for RecordSet {
fn into_record_set(self) -> Self {
self
}
}
impl IntoIterator for RecordSet {
type Item = Record;
type IntoIter = Chain<vec::IntoIter<Record>, vec::IntoIter<Record>>;
fn into_iter(self) -> Self::IntoIter {
self.records.into_iter().chain(self.rrsigs.into_iter())
}
}
/// An iterator over all the records and their signatures
#[cfg(feature = "dnssec")]
#[derive(Debug)]
pub struct RecordsAndRrsigsIter<'r>(Chain<Iter<'r, Record>, RrsigsByAlgorithms<'r>>);
#[cfg(feature = "dnssec")]
impl<'r> Iterator for RecordsAndRrsigsIter<'r> {
type Item = &'r Record;
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
/// An iterator that limits the record signatures by SupportedAlgorithms
#[cfg(feature = "dnssec")]
#[derive(Debug)]
pub struct RrsigsByAlgorithms<'r> {
rrsigs: Iter<'r, Record>,
supported_algorithms: SupportedAlgorithms,
}
#[cfg(feature = "dnssec")]
impl<'r> Iterator for RrsigsByAlgorithms<'r> {
type Item = &'r Record;
fn next(&mut self) -> Option<Self::Item> {
use crate::rr::dnssec::rdata::DNSSECRData;
use crate::rr::dnssec::Algorithm;
let supported_algorithms = self.supported_algorithms;
// disable rfc 6975 when no supported_algorithms specified
if supported_algorithms.is_empty() {
self.rrsigs.next()
} else {
self.rrsigs
.by_ref()
.filter(|record| {
if let RData::DNSSEC(DNSSECRData::SIG(ref rrsig)) = *record.rdata() {
supported_algorithms.has(rrsig.algorithm())
} else {
false
}
})
.max_by_key(|record| {
if let RData::DNSSEC(DNSSECRData::SIG(ref rrsig)) = *record.rdata() {
rrsig.algorithm()
} else {
Algorithm::RSASHA1
}
})
}
}
}
/// An iterator over the RecordSet data
#[derive(Debug)]
pub enum RrsetRecords<'r> {
/// There are no records in the record set
Empty,
/// The records associated with the record set
RecordsOnly(Iter<'r, Record>),
/// The records along with their signatures in the record set
#[cfg(feature = "dnssec")]
RecordsAndRrsigs(RecordsAndRrsigsIter<'r>),
}
impl<'r> RrsetRecords<'r> {
/// This is a best effort emptyness check
pub fn is_empty(&self) -> bool {
match *self {
RrsetRecords::Empty => true,
_ => false,
}
}
}
impl<'r> Iterator for RrsetRecords<'r> {
type Item = &'r Record;
fn next(&mut self) -> Option<Self::Item> {
match self {
RrsetRecords::Empty => None,
RrsetRecords::RecordsOnly(i) => i.next(),
#[cfg(feature = "dnssec")]
RrsetRecords::RecordsAndRrsigs(i) => i.next(),
}
}
}
#[cfg(test)]
mod test {
use std::net::Ipv4Addr;
use std::str::FromStr;
use crate::rr::rdata::SOA;
use crate::rr::*;
#[test]
fn test_insert() {
let name = Name::from_str("www.example.com.").unwrap();
let record_type = RecordType::A;
let mut rr_set = RecordSet::new(&name, record_type, 0);
let insert = Record::new()
.set_name(name.clone())
.set_ttl(86400)
.set_rr_type(record_type)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::A(Ipv4Addr::new(93, 184, 216, 24)))
.clone();
assert!(rr_set.insert(insert.clone(), 0));
assert_eq!(rr_set.records_without_rrsigs().count(), 1);
assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
// dups ignored
assert!(!rr_set.insert(insert.clone(), 0));
assert_eq!(rr_set.records_without_rrsigs().count(), 1);
assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
// add one
let insert1 = Record::new()
.set_name(name)
.set_ttl(86400)
.set_rr_type(record_type)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::A(Ipv4Addr::new(93, 184, 216, 25)))
.clone();
assert!(rr_set.insert(insert1.clone(), 0));
assert_eq!(rr_set.records_without_rrsigs().count(), 2);
assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert1));
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_insert_soa() {
let name = Name::from_str("example.com.").unwrap();
let record_type = RecordType::SOA;
let mut rr_set = RecordSet::new(&name, record_type, 0);
let insert = Record::new()
.set_name(name.clone())
.set_ttl(3600)
.set_rr_type(RecordType::SOA)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::SOA(SOA::new(
Name::from_str("sns.dns.icann.org.").unwrap(),
Name::from_str("noc.dns.icann.org.").unwrap(),
2015082403,
7200,
3600,
1209600,
3600,
)))
.clone();
let same_serial = Record::new()
.set_name(name.clone())
.set_ttl(3600)
.set_rr_type(RecordType::SOA)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::SOA(SOA::new(
Name::from_str("sns.dns.icann.net.").unwrap(),
Name::from_str("noc.dns.icann.net.").unwrap(),
2015082403,
7200,
3600,
1209600,
3600,
)))
.clone();
let new_serial = Record::new()
.set_name(name)
.set_ttl(3600)
.set_rr_type(RecordType::SOA)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::SOA(SOA::new(
Name::from_str("sns.dns.icann.net.").unwrap(),
Name::from_str("noc.dns.icann.net.").unwrap(),
2015082404,
7200,
3600,
1209600,
3600,
)))
.clone();
assert!(rr_set.insert(insert.clone(), 0));
assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
// same serial number
assert!(!rr_set.insert(same_serial.clone(), 0));
assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
assert!(!rr_set
.records_without_rrsigs()
.any(|ref x| x == &&same_serial));
assert!(rr_set.insert(new_serial.clone(), 0));
assert!(!rr_set.insert(same_serial.clone(), 0));
assert!(!rr_set.insert(insert.clone(), 0));
assert!(rr_set
.records_without_rrsigs()
.any(|ref x| x == &&new_serial));
assert!(!rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
assert!(!rr_set
.records_without_rrsigs()
.any(|ref x| x == &&same_serial));
}
#[test]
fn test_insert_cname() {
let name = Name::from_str("web.example.com.").unwrap();
let cname = Name::from_str("www.example.com.").unwrap();
let new_cname = Name::from_str("w2.example.com.").unwrap();
let record_type = RecordType::CNAME;
let mut rr_set = RecordSet::new(&name, record_type, 0);
let insert = Record::new()
.set_name(name.clone())
.set_ttl(3600)
.set_rr_type(RecordType::CNAME)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::CNAME(cname))
.clone();
let new_record = Record::new()
.set_name(name)
.set_ttl(3600)
.set_rr_type(RecordType::CNAME)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::CNAME(new_cname))
.clone();
assert!(rr_set.insert(insert.clone(), 0));
assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
// update the record
assert!(rr_set.insert(new_record.clone(), 0));
assert!(!rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
assert!(rr_set
.records_without_rrsigs()
.any(|ref x| x == &&new_record));
}
#[test]
fn test_remove() {
let name = Name::from_str("www.example.com.").unwrap();
let record_type = RecordType::A;
let mut rr_set = RecordSet::new(&name, record_type, 0);
let insert = Record::new()
.set_name(name.clone())
.set_ttl(86400)
.set_rr_type(record_type)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::A(Ipv4Addr::new(93, 184, 216, 24)))
.clone();
let insert1 = Record::new()
.set_name(name)
.set_ttl(86400)
.set_rr_type(record_type)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::A(Ipv4Addr::new(93, 184, 216, 25)))
.clone();
assert!(rr_set.insert(insert.clone(), 0));
assert!(rr_set.insert(insert1.clone(), 0));
assert!(rr_set.remove(&insert, 0));
assert!(!rr_set.remove(&insert, 0));
assert!(rr_set.remove(&insert1, 0));
assert!(!rr_set.remove(&insert1, 0));
}
#[test]
#[allow(clippy::unreadable_literal)]
fn test_remove_soa() {
let name = Name::from_str("www.example.com.").unwrap();
let record_type = RecordType::SOA;
let mut rr_set = RecordSet::new(&name, record_type, 0);
let insert = Record::new()
.set_name(name)
.set_ttl(3600)
.set_rr_type(RecordType::SOA)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::SOA(SOA::new(
Name::from_str("sns.dns.icann.org.").unwrap(),
Name::from_str("noc.dns.icann.org.").unwrap(),
2015082403,
7200,
3600,
1209600,
3600,
)))
.clone();
assert!(rr_set.insert(insert.clone(), 0));
assert!(!rr_set.remove(&insert, 0));
assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
}
#[test]
fn test_remove_ns() {
let name = Name::from_str("example.com.").unwrap();
let record_type = RecordType::NS;
let mut rr_set = RecordSet::new(&name, record_type, 0);
let ns1 = Record::new()
.set_name(name.clone())
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::NS(Name::from_str("a.iana-servers.net.").unwrap()))
.clone();
let ns2 = Record::new()
.set_name(name)
.set_ttl(86400)
.set_rr_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::NS(Name::from_str("b.iana-servers.net.").unwrap()))
.clone();
assert!(rr_set.insert(ns1.clone(), 0));
assert!(rr_set.insert(ns2.clone(), 0));
// ok to remove one, but not two...
assert!(rr_set.remove(&ns1, 0));
assert!(!rr_set.remove(&ns2, 0));
// check that we can swap which ones are removed
assert!(rr_set.insert(ns1.clone(), 0));
assert!(rr_set.remove(&ns2, 0));
assert!(!rr_set.remove(&ns1, 0));
}
#[test]
#[cfg(feature = "dnssec")] // This tests RFC 6975, a DNSSEC-specific feature.
#[allow(clippy::block_in_if_condition_stmt)]
fn test_get_filter() {
use crate::rr::dnssec::rdata::SIG;
use crate::rr::dnssec::rdata::{DNSSECRData, DNSSECRecordType};
use crate::rr::dnssec::{Algorithm, SupportedAlgorithms};
let name = Name::root();
let rsasha256 = SIG::new(
RecordType::A,
Algorithm::RSASHA256,
0,
0,
0,
0,
0,
Name::root(),
vec![],
);
let ecp256 = SIG::new(
RecordType::A,
Algorithm::ECDSAP256SHA256,
0,
0,
0,
0,
0,
Name::root(),
vec![],
);
let ecp384 = SIG::new(
RecordType::A,
Algorithm::ECDSAP384SHA384,
0,
0,
0,
0,
0,
Name::root(),
vec![],
);
let ed25519 = SIG::new(
RecordType::A,
Algorithm::ED25519,
0,
0,
0,
0,
0,
Name::root(),
vec![],
);
let rrsig_rsa = Record::new()
.set_name(name.clone())
.set_ttl(3600)
.set_rr_type(RecordType::DNSSEC(DNSSECRecordType::RRSIG))
.set_dns_class(DNSClass::IN)
.set_rdata(RData::DNSSEC(DNSSECRData::SIG(rsasha256)))
.clone();
let rrsig_ecp256 = Record::new()
.set_name(name.clone())
.set_ttl(3600)
.set_rr_type(RecordType::DNSSEC(DNSSECRecordType::RRSIG))
.set_dns_class(DNSClass::IN)
.set_rdata(RData::DNSSEC(DNSSECRData::SIG(ecp256)))
.clone();
let rrsig_ecp384 = Record::new()
.set_name(name.clone())
.set_ttl(3600)
.set_rr_type(RecordType::DNSSEC(DNSSECRecordType::RRSIG))
.set_dns_class(DNSClass::IN)
.set_rdata(RData::DNSSEC(DNSSECRData::SIG(ecp384)))
.clone();
let rrsig_ed25519 = Record::new()
.set_name(name.clone())
.set_ttl(3600)
.set_rr_type(RecordType::DNSSEC(DNSSECRecordType::RRSIG))
.set_dns_class(DNSClass::IN)
.set_rdata(RData::DNSSEC(DNSSECRData::SIG(ed25519)))
.clone();
let a = Record::new()
.set_name(name)
.set_ttl(3600)
.set_rr_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::A(Ipv4Addr::new(93, 184, 216, 24)))
.clone();
let mut rrset = RecordSet::from(a);
rrset.insert_rrsig(rrsig_rsa);
rrset.insert_rrsig(rrsig_ecp256);
rrset.insert_rrsig(rrsig_ecp384);
rrset.insert_rrsig(rrsig_ed25519);
assert!(rrset
.records_with_rrsigs(SupportedAlgorithms::all(),)
.any(
|r| if let RData::DNSSEC(DNSSECRData::SIG(ref sig)) = *r.rdata() {
sig.algorithm() == Algorithm::ED25519
} else {
false
},
));
let mut supported_algorithms = SupportedAlgorithms::new();
supported_algorithms.set(Algorithm::ECDSAP384SHA384);
assert!(rrset.records_with_rrsigs(supported_algorithms).any(|r| {
if let RData::DNSSEC(DNSSECRData::SIG(ref sig)) = *r.rdata() {
sig.algorithm() == Algorithm::ECDSAP384SHA384
} else {
false
}
}));
let mut supported_algorithms = SupportedAlgorithms::new();
supported_algorithms.set(Algorithm::ED25519);
assert!(rrset.records_with_rrsigs(supported_algorithms).any(|r| {
if let RData::DNSSEC(DNSSECRData::SIG(ref sig)) = *r.rdata() {
sig.algorithm() == Algorithm::ED25519
} else {
false
}
}));
}
}