use std::iter::Chain;
use std::slice::Iter;
use std::vec;
use crate::rr::{DNSClass, Name, RData, Record, RecordType};
#[cfg(feature = "dnssec")]
use crate::rr::dnssec::SupportedAlgorithms;
#[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,
}
impl RecordSet {
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,
}
}
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,
}
}
pub fn name(&self) -> &Name {
&self.name
}
pub fn record_type(&self) -> RecordType {
self.record_type
}
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);
}
}
pub fn dns_class(&self) -> DNSClass {
self.dns_class
}
pub fn set_ttl(&mut self, ttl: u32) {
self.ttl = ttl;
for r in &mut self.records {
r.set_ttl(ttl);
}
}
pub fn ttl(&self) -> u32 {
self.ttl
}
#[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()
}
}
#[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)))
}
}
pub fn records_without_rrsigs(&self) -> RrsetRecords {
if self.records.is_empty() {
RrsetRecords::Empty
} else {
RrsetRecords::RecordsOnly(self.records.iter())
}
}
#[deprecated(note = "see `records_without_rrsigs`")]
pub fn iter(&self) -> Iter<Record> {
self.records.iter()
}
pub fn is_empty(&self) -> bool {
self.records.is_empty()
}
pub fn serial(&self) -> u32 {
self.serial
}
pub fn rrsigs(&self) -> &[Record] {
&self.rrsigs
}
pub fn insert_rrsig(&mut self, rrsig: Record) {
self.rrsigs.push(rrsig)
}
pub fn clear_rrsigs(&mut self) {
self.rrsigs.clear()
}
fn updated(&mut self, serial: u32) {
self.serial = serial;
self.rrsigs.clear();
}
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")
}
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)
}
pub fn insert(&mut self, record: Record, serial: u32) -> bool {
assert_eq!(record.name(), &self.name);
assert_eq!(record.rr_type(), self.record_type);
match record.rr_type() {
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 {
info!("wrong rdata for SOA update: {:?}", record.rdata());
return false;
}
}
rdata => panic!("wrong rdata: {:?}", rdata),
}
}
self.records.clear();
}
RecordType::CNAME | RecordType::ANAME => {
assert!(self.records.len() <= 1);
self.records.clear();
}
_ => (),
}
let to_replace: Vec<usize> = self
.records
.iter()
.enumerate()
.filter(|&(_, rr)| rr.rdata() == record.rdata())
.map(|(i, _)| i)
.collect::<Vec<usize>>();
let mut replaced = false;
for i in to_replace {
if self.records[i] == record {
return false;
}
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
}
}
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() {
RecordType::NS => {
if self.records.len() <= 1 {
info!("ignoring delete of last NS record: {:?}", record);
return false;
}
}
RecordType::SOA => {
info!("ignored delete of SOA");
return false;
}
_ => (),
}
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,
}
}
}
#[deprecated(note = "use From/Into")]
pub trait IntoRecordSet: Sized {
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())
}
}
#[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()
}
}
#[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;
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
}
})
}
}
}
#[derive(Debug)]
pub enum RrsetRecords<'r> {
Empty,
RecordsOnly(Iter<'r, Record>),
#[cfg(feature = "dnssec")]
RecordsAndRrsigs(RecordsAndRrsigsIter<'r>),
}
impl<'r> RrsetRecords<'r> {
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));
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));
let insert1 = 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, 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.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(),
2015082404,
7200,
3600,
1209600,
3600,
)))
.clone();
assert!(rr_set.insert(insert.clone(), 0));
assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
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()))
.clone();
let new_record = Record::new()
.set_name(name.clone())
.set_ttl(3600)
.set_rr_type(RecordType::CNAME)
.set_dns_class(DNSClass::IN)
.set_rdata(RData::CNAME(new_cname.clone()))
.clone();
assert!(rr_set.insert(insert.clone(), 0));
assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
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.clone())
.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.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();
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.clone())
.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));
assert!(rr_set.remove(&ns1, 0));
assert!(!rr_set.remove(&ns2, 0));
assert!(rr_set.insert(ns1.clone(), 0));
assert!(rr_set.remove(&ns2, 0));
assert!(!rr_set.remove(&ns1, 0));
}
#[test]
#[cfg(feature = "dnssec")]
#[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.clone())
.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
}
}));
}
}