trust_dns_proto/rr/
rr_set.rs

1// Copyright 2015-2019 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7use std::iter::Chain;
8use std::slice::Iter;
9use std::vec;
10
11use tracing::{info, warn};
12
13use crate::rr::{DNSClass, Name, RData, Record, RecordType};
14
15#[cfg(feature = "dnssec")]
16#[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
17use crate::rr::dnssec::SupportedAlgorithms;
18
19/// Set of resource records associated to a name and type
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub struct RecordSet {
22    name: Name,
23    record_type: RecordType,
24    dns_class: DNSClass,
25    ttl: u32,
26    records: Vec<Record>,
27    rrsigs: Vec<Record>,
28    serial: u32, // serial number at which this record was modified
29}
30
31impl RecordSet {
32    /// Creates a new Resource Record Set.
33    ///
34    /// # Arguments
35    ///
36    /// * `name` - The label for the `RecordSet`
37    /// * `record_type` - `RecordType` of this `RecordSet`, all records in the `RecordSet` must be of the
38    ///                   specified `RecordType`.
39    /// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
40    ///              signing for DNSSec after updates.
41    ///
42    /// # Return value
43    ///
44    /// The newly created Resource Record Set
45    /// TODO: make all cloned params pass by value
46    pub fn new(name: &Name, record_type: RecordType, serial: u32) -> Self {
47        Self {
48            name: name.clone(),
49            record_type,
50            dns_class: DNSClass::IN,
51            ttl: 0,
52            records: Vec::new(),
53            rrsigs: Vec::new(),
54            serial,
55        }
56    }
57
58    /// Creates a new Resource Record Set.
59    ///
60    /// # Arguments
61    ///
62    /// * `name` - The label for the `RecordSet`
63    /// * `record_type` - `RecordType` of this `RecordSet`, all records in the `RecordSet` must be of the
64    ///                   specified `RecordType`.
65    /// * `ttl` - time-to-live for the `RecordSet` in seconds.
66    ///
67    /// # Return value
68    ///
69    /// The newly created Resource Record Set
70    /// TODO: make all cloned params pass by value
71    pub fn with_ttl(name: Name, record_type: RecordType, ttl: u32) -> Self {
72        Self {
73            name,
74            record_type,
75            dns_class: DNSClass::IN,
76            ttl,
77            records: Vec::new(),
78            rrsigs: Vec::new(),
79            serial: 0,
80        }
81    }
82
83    /// # Return value
84    ///
85    /// Label of the Resource Record Set
86    pub fn name(&self) -> &Name {
87        &self.name
88    }
89
90    /// # Return value
91    ///
92    /// `RecordType` of the Resource Record Set
93    pub fn record_type(&self) -> RecordType {
94        self.record_type
95    }
96
97    /// Sets the DNSClass to the specified value
98    ///
99    /// This will traverse every record and associate with it the specified dns_class
100    pub fn set_dns_class(&mut self, dns_class: DNSClass) {
101        self.dns_class = dns_class;
102        for r in &mut self.records {
103            r.set_dns_class(dns_class);
104        }
105    }
106
107    /// Returns the `DNSClass` of the RecordSet
108    pub fn dns_class(&self) -> DNSClass {
109        self.dns_class
110    }
111
112    /// Sets the TTL, in seconds, to the specified value
113    ///
114    /// This will traverse every record and associate with it the specified ttl
115    pub fn set_ttl(&mut self, ttl: u32) {
116        self.ttl = ttl;
117        for r in &mut self.records {
118            r.set_ttl(ttl);
119        }
120    }
121
122    /// Returns the time-to-live for the record.
123    ///
124    /// # Return value
125    ///
126    /// TTL, time-to-live, of the Resource Record Set, this is the maximum length of time that an
127    /// RecordSet should be cached.
128    pub fn ttl(&self) -> u32 {
129        self.ttl
130    }
131
132    /// Returns a Vec of all records in the set.
133    ///
134    /// # Arguments
135    ///
136    /// * `and_rrsigs` - if true, RRSIGs will be returned if they exist
137    /// * `supported_algorithms` - the RRSIGs will be filtered by the set of supported_algorithms,
138    ///                            and then only the maximal RRSIG algorithm will be returned.
139    #[cfg(feature = "dnssec")]
140    #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
141    pub fn records(
142        &self,
143        and_rrsigs: bool,
144        supported_algorithms: SupportedAlgorithms,
145    ) -> RrsetRecords<'_> {
146        if and_rrsigs {
147            self.records_with_rrsigs(supported_algorithms)
148        } else {
149            self.records_without_rrsigs()
150        }
151    }
152
153    /// Returns a Vec of all records in the set, with RRSIGs, if present.
154    ///
155    /// # Arguments
156    ///
157    /// * `supported_algorithms` - the RRSIGs will be filtered by the set of supported_algorithms,
158    ///                            and then only the maximal RRSIG algorithm will be returned.
159    #[cfg(feature = "dnssec")]
160    #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
161    pub fn records_with_rrsigs(
162        &self,
163        supported_algorithms: SupportedAlgorithms,
164    ) -> RrsetRecords<'_> {
165        if self.records.is_empty() {
166            RrsetRecords::Empty
167        } else {
168            let rrsigs = RrsigsByAlgorithms {
169                rrsigs: self.rrsigs.iter(),
170                supported_algorithms,
171            };
172            RrsetRecords::RecordsAndRrsigs(RecordsAndRrsigsIter(self.records.iter().chain(rrsigs)))
173        }
174    }
175
176    /// Returns a Vec of all records in the set, without any RRSIGs.
177    pub fn records_without_rrsigs(&self) -> RrsetRecords<'_> {
178        if self.records.is_empty() {
179            RrsetRecords::Empty
180        } else {
181            RrsetRecords::RecordsOnly(self.records.iter())
182        }
183    }
184
185    /// Returns an iterator over the records in the set
186    #[deprecated(note = "see `records_without_rrsigs`")]
187    pub fn iter(&self) -> Iter<'_, Record> {
188        self.records.iter()
189    }
190
191    /// Returns true if there are no records in this set
192    pub fn is_empty(&self) -> bool {
193        self.records.is_empty()
194    }
195
196    /// Returns the serial number at which the record was updated.
197    pub fn serial(&self) -> u32 {
198        self.serial
199    }
200
201    /// Returns a slice of all the Records signatures in the RecordSet
202    pub fn rrsigs(&self) -> &[Record] {
203        &self.rrsigs
204    }
205
206    /// Inserts a Signature for the Record set
207    ///
208    /// Many can be associated with the RecordSet. Once added, the RecordSet should not be changed
209    ///
210    /// # Arguments
211    ///
212    /// * `rrsig` - A signature which covers the RecordSet.
213    pub fn insert_rrsig(&mut self, rrsig: Record) {
214        self.rrsigs.push(rrsig)
215    }
216
217    /// Useful for clearing all signatures when the RecordSet is updated, or keys are rotated.
218    pub fn clear_rrsigs(&mut self) {
219        self.rrsigs.clear()
220    }
221
222    fn updated(&mut self, serial: u32) {
223        self.serial = serial;
224        self.rrsigs.clear(); // on updates, the rrsigs are invalid
225    }
226
227    /// creates a new Record as part of this RecordSet, adding the associated RData
228    ///
229    /// this interface may be deprecated in the future.
230    pub fn new_record(&mut self, rdata: &RData) -> &Record {
231        self.add_rdata(rdata.clone());
232
233        self.records
234            .iter()
235            .find(|r| r.data().map(|r| r == rdata).unwrap_or(false))
236            .expect("insert failed")
237    }
238
239    /// creates a new Record as part of this RecordSet, adding the associated RData
240    pub fn add_rdata(&mut self, rdata: RData) -> bool {
241        debug_assert_eq!(self.record_type, rdata.to_record_type());
242
243        let mut record = Record::with(self.name.clone(), self.record_type, self.ttl);
244        record.set_data(Some(rdata));
245        self.insert(record, 0)
246    }
247
248    /// Inserts a new Resource Record into the Set.
249    ///
250    /// If the record is inserted, the ttl for the most recent record will be used for the ttl of
251    /// the entire resource record set.
252    ///
253    /// This abides by the following restrictions in RFC 2136, April 1997:
254    ///
255    /// ```text
256    /// 1.1.5. The following RR types cannot be appended to an RRset.  If the
257    ///  following comparison rules are met, then an attempt to add the new RR
258    ///  will result in the replacement of the previous RR:
259    ///
260    /// SOA    compare only NAME, CLASS and TYPE -- it is not possible to
261    ///         have more than one SOA per zone, even if any of the data
262    ///         fields differ.
263    ///
264    /// CNAME  compare only NAME, CLASS, and TYPE -- it is not possible
265    ///         to have more than one CNAME RR, even if their data fields
266    ///         differ.
267    /// ```
268    ///
269    /// # Arguments
270    ///
271    /// * `record` - `Record` asserts that the `name` and `record_type` match the `RecordSet`.
272    /// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
273    ///              signing for DNSSec after updates. The serial will only be updated if the
274    ///              record was added.
275    ///
276    /// # Return value
277    ///
278    /// True if the record was inserted.
279    ///
280    /// TODO: make a default add without serial number for basic usage
281    pub fn insert(&mut self, record: Record, serial: u32) -> bool {
282        assert_eq!(record.name(), &self.name);
283        assert_eq!(record.rr_type(), self.record_type);
284
285        // RFC 2136                       DNS Update                     April 1997
286        //
287        // 1.1.5. The following RR types cannot be appended to an RRset.  If the
288        //  following comparison rules are met, then an attempt to add the new RR
289        //  will result in the replacement of the previous RR:
290        match record.rr_type() {
291            // SOA    compare only NAME, CLASS and TYPE -- it is not possible to
292            //         have more than one SOA per zone, even if any of the data
293            //         fields differ.
294            RecordType::SOA => {
295                assert!(self.records.len() <= 1);
296
297                if let Some(soa_record) = self.records.get(0) {
298                    match soa_record.data() {
299                        Some(RData::SOA(ref existing_soa)) => {
300                            if let Some(RData::SOA(ref new_soa)) = record.data() {
301                                if new_soa.serial() <= existing_soa.serial() {
302                                    info!(
303                                        "update ignored serial out of data: {:?} <= {:?}",
304                                        new_soa, existing_soa
305                                    );
306                                    return false;
307                                }
308                            } else {
309                                // not panicking here, b/c this is a bad record from the client or something, ignore
310                                info!("wrong rdata for SOA update: {:?}", record.data());
311                                return false;
312                            }
313                        }
314                        rdata => {
315                            warn!("wrong rdata: {:?}, expected SOA", rdata);
316                            return false;
317                        }
318                    }
319                }
320
321                // if we got here, we're updating...
322                self.records.clear();
323            }
324            // RFC 1034/1035
325            // CNAME  compare only NAME, CLASS, and TYPE -- it is not possible
326            //         to have more than one CNAME RR, even if their data fields
327            //         differ.
328            //
329            // ANAME https://tools.ietf.org/html/draft-ietf-dnsop-aname-02
330            //    2.2.  Coexistence with other types
331            //
332            //   Only one ANAME <target> can be defined per <owner>.  An ANAME RRset
333            //   MUST NOT contain more than one resource record.
334            //
335            //   An ANAME's sibling address records are under the control of ANAME
336            //   processing (see Section 5) and are not first-class records in their
337            //   own right.  They MAY exist in zone files, but they can subsequently
338            //   be altered by ANAME processing.
339            //
340            //   ANAME records MAY freely coexist at the same owner name with other RR
341            //   types, except they MUST NOT coexist with CNAME or any other RR type
342            //   that restricts the types with which it can itself coexist.
343            //
344            //   Like other types, ANAME records can coexist with DNAME records at the
345            //   same owner name; in fact, the two can be used cooperatively to
346            //   redirect both the owner name address records (via ANAME) and
347            //   everything under it (via DNAME).
348            RecordType::CNAME | RecordType::ANAME => {
349                assert!(self.records.len() <= 1);
350                self.records.clear();
351            }
352            _ => (),
353        }
354
355        // collect any records to update based on rdata
356        let to_replace: Vec<usize> = self
357            .records
358            .iter()
359            .enumerate()
360            .filter(|&(_, rr)| rr.data() == record.data())
361            .map(|(i, _)| i)
362            .collect::<Vec<usize>>();
363
364        // if the Records are identical, ignore the update, update all that are not (ttl, etc.)
365        let mut replaced = false;
366        for i in to_replace {
367            if self.records[i] == record {
368                return false;
369            }
370
371            // TODO: this shouldn't really need a clone since there should only be one...
372            self.records.push(record.clone());
373            self.records.swap_remove(i);
374            self.ttl = record.ttl();
375            self.updated(serial);
376            replaced = true;
377        }
378
379        if !replaced {
380            self.ttl = record.ttl();
381            self.updated(serial);
382            self.records.push(record);
383            true
384        } else {
385            replaced
386        }
387    }
388
389    /// Removes the Resource Record if it exists.
390    ///
391    /// # Arguments
392    ///
393    /// * `record` - `Record` asserts that the `name` and `record_type` match the `RecordSet`. Removes
394    ///              any `record` if the record data, `RData`, match.
395    /// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
396    ///              signing for DNSSec after updates. The serial will only be updated if the
397    ///              record was added.
398    ///
399    /// # Return value
400    ///
401    /// True if a record was removed.
402    pub fn remove(&mut self, record: &Record, serial: u32) -> bool {
403        assert_eq!(record.name(), &self.name);
404        assert!(record.rr_type() == self.record_type || record.rr_type() == RecordType::ANY);
405
406        match record.rr_type() {
407            // never delete the last NS record
408            RecordType::NS => {
409                if self.records.len() <= 1 {
410                    info!("ignoring delete of last NS record: {:?}", record);
411                    return false;
412                }
413            }
414            // never delete SOA
415            RecordType::SOA => {
416                info!("ignored delete of SOA");
417                return false;
418            }
419            _ => (), // move on to the delete
420        }
421
422        // remove the records, first collect all the indexes, then remove the records
423        let to_remove: Vec<usize> = self
424            .records
425            .iter()
426            .enumerate()
427            .filter(|&(_, rr)| rr.data() == record.data())
428            .map(|(i, _)| i)
429            .collect::<Vec<usize>>();
430
431        let mut removed = false;
432        for i in to_remove {
433            self.records.remove(i);
434            removed = true;
435            self.updated(serial);
436        }
437
438        removed
439    }
440
441    /// Consumes `RecordSet` and returns its components
442    pub fn into_parts(self) -> RecordSetParts {
443        self.into()
444    }
445}
446
447/// Consumes `RecordSet` giving public access to fields of `RecordSet` so they can
448/// be destructured and taken by value
449#[derive(Clone, Debug, PartialEq, Eq)]
450pub struct RecordSetParts {
451    pub name: Name,
452    pub record_type: RecordType,
453    pub dns_class: DNSClass,
454    pub ttl: u32,
455    pub records: Vec<Record>,
456    pub rrsigs: Vec<Record>,
457    pub serial: u32, // serial number at which this record was modifie,
458}
459
460impl From<RecordSet> for RecordSetParts {
461    fn from(rset: RecordSet) -> Self {
462        let RecordSet {
463            name,
464            record_type,
465            dns_class,
466            ttl,
467            records,
468            rrsigs,
469            serial,
470        } = rset;
471        Self {
472            name,
473            record_type,
474            dns_class,
475            ttl,
476            records,
477            rrsigs,
478            serial,
479        }
480    }
481}
482
483impl From<Record> for RecordSet {
484    fn from(record: Record) -> Self {
485        Self {
486            name: record.name().clone(),
487            record_type: record.rr_type(),
488            dns_class: record.dns_class(),
489            ttl: record.ttl(),
490            records: vec![record],
491            rrsigs: vec![],
492            serial: 0,
493        }
494    }
495}
496
497/// Types which implement this can be converted into a RecordSet
498#[deprecated(note = "use From/Into")]
499pub trait IntoRecordSet: Sized {
500    /// Performs the conversion to a RecordSet
501    fn into_record_set(self) -> RecordSet;
502}
503
504#[allow(deprecated)]
505impl IntoRecordSet for RecordSet {
506    fn into_record_set(self) -> Self {
507        self
508    }
509}
510
511impl IntoIterator for RecordSet {
512    type Item = Record;
513    type IntoIter = Chain<vec::IntoIter<Record>, vec::IntoIter<Record>>;
514
515    fn into_iter(self) -> Self::IntoIter {
516        self.records.into_iter().chain(self.rrsigs.into_iter())
517    }
518}
519
520/// An iterator over all the records and their signatures
521#[cfg(feature = "dnssec")]
522#[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
523#[derive(Debug)]
524pub struct RecordsAndRrsigsIter<'r>(Chain<Iter<'r, Record>, RrsigsByAlgorithms<'r>>);
525
526#[cfg(feature = "dnssec")]
527#[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
528impl<'r> Iterator for RecordsAndRrsigsIter<'r> {
529    type Item = &'r Record;
530
531    fn next(&mut self) -> Option<Self::Item> {
532        self.0.next()
533    }
534}
535
536/// An iterator that limits the record signatures by SupportedAlgorithms
537#[cfg(feature = "dnssec")]
538#[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
539#[derive(Debug)]
540pub(crate) struct RrsigsByAlgorithms<'r> {
541    rrsigs: Iter<'r, Record>,
542    supported_algorithms: SupportedAlgorithms,
543}
544
545#[cfg(feature = "dnssec")]
546#[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
547impl<'r> Iterator for RrsigsByAlgorithms<'r> {
548    type Item = &'r Record;
549
550    fn next(&mut self) -> Option<Self::Item> {
551        use crate::rr::dnssec::rdata::DNSSECRData;
552        use crate::rr::dnssec::Algorithm;
553
554        let supported_algorithms = self.supported_algorithms;
555
556        // disable rfc 6975 when no supported_algorithms specified
557        if supported_algorithms.is_empty() {
558            self.rrsigs.next()
559        } else {
560            self.rrsigs
561                .by_ref()
562                .filter(|record| {
563                    if let Some(RData::DNSSEC(DNSSECRData::SIG(ref rrsig))) = record.data() {
564                        supported_algorithms.has(rrsig.algorithm())
565                    } else {
566                        false
567                    }
568                })
569                .max_by_key(|record| {
570                    if let Some(RData::DNSSEC(DNSSECRData::SIG(ref rrsig))) = record.data() {
571                        rrsig.algorithm()
572                    } else {
573                        #[allow(deprecated)]
574                        Algorithm::RSASHA1
575                    }
576                })
577        }
578    }
579}
580
581/// An iterator over the RecordSet data
582#[derive(Debug)]
583pub enum RrsetRecords<'r> {
584    /// There are no records in the record set
585    Empty,
586    /// The records associated with the record set
587    RecordsOnly(Iter<'r, Record>),
588    /// The records along with their signatures in the record set
589    #[cfg(feature = "dnssec")]
590    #[cfg_attr(docsrs, doc(cfg(feature = "dnssec")))]
591    RecordsAndRrsigs(RecordsAndRrsigsIter<'r>),
592}
593
594impl<'r> RrsetRecords<'r> {
595    /// This is a best effort emptyness check
596    pub fn is_empty(&self) -> bool {
597        matches!(*self, RrsetRecords::Empty)
598    }
599}
600
601impl<'r> Iterator for RrsetRecords<'r> {
602    type Item = &'r Record;
603
604    fn next(&mut self) -> Option<Self::Item> {
605        match self {
606            RrsetRecords::Empty => None,
607            RrsetRecords::RecordsOnly(i) => i.next(),
608            #[cfg(feature = "dnssec")]
609            RrsetRecords::RecordsAndRrsigs(i) => i.next(),
610        }
611    }
612}
613
614#[cfg(test)]
615mod test {
616    use std::net::Ipv4Addr;
617    use std::str::FromStr;
618
619    use crate::rr::rdata::SOA;
620    use crate::rr::*;
621
622    #[test]
623    fn test_insert() {
624        let name = Name::from_str("www.example.com.").unwrap();
625        let record_type = RecordType::A;
626        let mut rr_set = RecordSet::new(&name, record_type, 0);
627
628        let insert = Record::new()
629            .set_name(name.clone())
630            .set_ttl(86400)
631            .set_rr_type(record_type)
632            .set_dns_class(DNSClass::IN)
633            .set_data(Some(RData::A(Ipv4Addr::new(93, 184, 216, 24))))
634            .clone();
635
636        assert!(rr_set.insert(insert.clone(), 0));
637        assert_eq!(rr_set.records_without_rrsigs().count(), 1);
638        assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
639
640        // dups ignored
641        assert!(!rr_set.insert(insert.clone(), 0));
642        assert_eq!(rr_set.records_without_rrsigs().count(), 1);
643        assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
644
645        // add one
646        let insert1 = Record::new()
647            .set_name(name)
648            .set_ttl(86400)
649            .set_rr_type(record_type)
650            .set_dns_class(DNSClass::IN)
651            .set_data(Some(RData::A(Ipv4Addr::new(93, 184, 216, 25))))
652            .clone();
653        assert!(rr_set.insert(insert1.clone(), 0));
654        assert_eq!(rr_set.records_without_rrsigs().count(), 2);
655        assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
656        assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert1));
657    }
658
659    #[test]
660    #[allow(clippy::unreadable_literal)]
661    fn test_insert_soa() {
662        let name = Name::from_str("example.com.").unwrap();
663        let record_type = RecordType::SOA;
664        let mut rr_set = RecordSet::new(&name, record_type, 0);
665
666        let insert = Record::new()
667            .set_name(name.clone())
668            .set_ttl(3600)
669            .set_rr_type(RecordType::SOA)
670            .set_dns_class(DNSClass::IN)
671            .set_data(Some(RData::SOA(SOA::new(
672                Name::from_str("sns.dns.icann.org.").unwrap(),
673                Name::from_str("noc.dns.icann.org.").unwrap(),
674                2015082403,
675                7200,
676                3600,
677                1209600,
678                3600,
679            ))))
680            .clone();
681        let same_serial = Record::new()
682            .set_name(name.clone())
683            .set_ttl(3600)
684            .set_rr_type(RecordType::SOA)
685            .set_dns_class(DNSClass::IN)
686            .set_data(Some(RData::SOA(SOA::new(
687                Name::from_str("sns.dns.icann.net.").unwrap(),
688                Name::from_str("noc.dns.icann.net.").unwrap(),
689                2015082403,
690                7200,
691                3600,
692                1209600,
693                3600,
694            ))))
695            .clone();
696        let new_serial = Record::new()
697            .set_name(name)
698            .set_ttl(3600)
699            .set_rr_type(RecordType::SOA)
700            .set_dns_class(DNSClass::IN)
701            .set_data(Some(RData::SOA(SOA::new(
702                Name::from_str("sns.dns.icann.net.").unwrap(),
703                Name::from_str("noc.dns.icann.net.").unwrap(),
704                2015082404,
705                7200,
706                3600,
707                1209600,
708                3600,
709            ))))
710            .clone();
711
712        assert!(rr_set.insert(insert.clone(), 0));
713        assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
714        // same serial number
715        assert!(!rr_set.insert(same_serial.clone(), 0));
716        assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
717        assert!(!rr_set
718            .records_without_rrsigs()
719            .any(|ref x| x == &&same_serial));
720
721        assert!(rr_set.insert(new_serial.clone(), 0));
722        assert!(!rr_set.insert(same_serial.clone(), 0));
723        assert!(!rr_set.insert(insert.clone(), 0));
724
725        assert!(rr_set
726            .records_without_rrsigs()
727            .any(|ref x| x == &&new_serial));
728        assert!(!rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
729        assert!(!rr_set
730            .records_without_rrsigs()
731            .any(|ref x| x == &&same_serial));
732    }
733
734    #[test]
735    fn test_insert_cname() {
736        let name = Name::from_str("web.example.com.").unwrap();
737        let cname = Name::from_str("www.example.com.").unwrap();
738        let new_cname = Name::from_str("w2.example.com.").unwrap();
739
740        let record_type = RecordType::CNAME;
741        let mut rr_set = RecordSet::new(&name, record_type, 0);
742
743        let insert = Record::new()
744            .set_name(name.clone())
745            .set_ttl(3600)
746            .set_rr_type(RecordType::CNAME)
747            .set_dns_class(DNSClass::IN)
748            .set_data(Some(RData::CNAME(cname)))
749            .clone();
750        let new_record = Record::new()
751            .set_name(name)
752            .set_ttl(3600)
753            .set_rr_type(RecordType::CNAME)
754            .set_dns_class(DNSClass::IN)
755            .set_data(Some(RData::CNAME(new_cname)))
756            .clone();
757
758        assert!(rr_set.insert(insert.clone(), 0));
759        assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
760
761        // update the record
762        assert!(rr_set.insert(new_record.clone(), 0));
763        assert!(!rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
764        assert!(rr_set
765            .records_without_rrsigs()
766            .any(|ref x| x == &&new_record));
767    }
768
769    #[test]
770    fn test_remove() {
771        let name = Name::from_str("www.example.com.").unwrap();
772        let record_type = RecordType::A;
773        let mut rr_set = RecordSet::new(&name, record_type, 0);
774
775        let insert = Record::new()
776            .set_name(name.clone())
777            .set_ttl(86400)
778            .set_rr_type(record_type)
779            .set_dns_class(DNSClass::IN)
780            .set_data(Some(RData::A(Ipv4Addr::new(93, 184, 216, 24))))
781            .clone();
782        let insert1 = Record::new()
783            .set_name(name)
784            .set_ttl(86400)
785            .set_rr_type(record_type)
786            .set_dns_class(DNSClass::IN)
787            .set_data(Some(RData::A(Ipv4Addr::new(93, 184, 216, 25))))
788            .clone();
789
790        assert!(rr_set.insert(insert.clone(), 0));
791        assert!(rr_set.insert(insert1.clone(), 0));
792
793        assert!(rr_set.remove(&insert, 0));
794        assert!(!rr_set.remove(&insert, 0));
795        assert!(rr_set.remove(&insert1, 0));
796        assert!(!rr_set.remove(&insert1, 0));
797    }
798
799    #[test]
800    #[allow(clippy::unreadable_literal)]
801    fn test_remove_soa() {
802        let name = Name::from_str("www.example.com.").unwrap();
803        let record_type = RecordType::SOA;
804        let mut rr_set = RecordSet::new(&name, record_type, 0);
805
806        let insert = Record::new()
807            .set_name(name)
808            .set_ttl(3600)
809            .set_rr_type(RecordType::SOA)
810            .set_dns_class(DNSClass::IN)
811            .set_data(Some(RData::SOA(SOA::new(
812                Name::from_str("sns.dns.icann.org.").unwrap(),
813                Name::from_str("noc.dns.icann.org.").unwrap(),
814                2015082403,
815                7200,
816                3600,
817                1209600,
818                3600,
819            ))))
820            .clone();
821
822        assert!(rr_set.insert(insert.clone(), 0));
823        assert!(!rr_set.remove(&insert, 0));
824        assert!(rr_set.records_without_rrsigs().any(|ref x| x == &&insert));
825    }
826
827    #[test]
828    fn test_remove_ns() {
829        let name = Name::from_str("example.com.").unwrap();
830        let record_type = RecordType::NS;
831        let mut rr_set = RecordSet::new(&name, record_type, 0);
832
833        let ns1 = Record::new()
834            .set_name(name.clone())
835            .set_ttl(86400)
836            .set_rr_type(RecordType::NS)
837            .set_dns_class(DNSClass::IN)
838            .set_data(Some(RData::NS(
839                Name::from_str("a.iana-servers.net.").unwrap(),
840            )))
841            .clone();
842        let ns2 = Record::new()
843            .set_name(name)
844            .set_ttl(86400)
845            .set_rr_type(RecordType::NS)
846            .set_dns_class(DNSClass::IN)
847            .set_data(Some(RData::NS(
848                Name::from_str("b.iana-servers.net.").unwrap(),
849            )))
850            .clone();
851
852        assert!(rr_set.insert(ns1.clone(), 0));
853        assert!(rr_set.insert(ns2.clone(), 0));
854
855        // ok to remove one, but not two...
856        assert!(rr_set.remove(&ns1, 0));
857        assert!(!rr_set.remove(&ns2, 0));
858
859        // check that we can swap which ones are removed
860        assert!(rr_set.insert(ns1.clone(), 0));
861
862        assert!(rr_set.remove(&ns2, 0));
863        assert!(!rr_set.remove(&ns1, 0));
864    }
865
866    #[test]
867    #[cfg(feature = "dnssec")] // This tests RFC 6975, a DNSSEC-specific feature.
868    #[allow(clippy::blocks_in_if_conditions)]
869    fn test_get_filter() {
870        use crate::rr::dnssec::rdata::DNSSECRData;
871        use crate::rr::dnssec::rdata::SIG;
872        use crate::rr::dnssec::{Algorithm, SupportedAlgorithms};
873
874        let name = Name::root();
875        let rsasha256 = SIG::new(
876            RecordType::A,
877            Algorithm::RSASHA256,
878            0,
879            0,
880            0,
881            0,
882            0,
883            Name::root(),
884            vec![],
885        );
886        let ecp256 = SIG::new(
887            RecordType::A,
888            Algorithm::ECDSAP256SHA256,
889            0,
890            0,
891            0,
892            0,
893            0,
894            Name::root(),
895            vec![],
896        );
897        let ecp384 = SIG::new(
898            RecordType::A,
899            Algorithm::ECDSAP384SHA384,
900            0,
901            0,
902            0,
903            0,
904            0,
905            Name::root(),
906            vec![],
907        );
908        let ed25519 = SIG::new(
909            RecordType::A,
910            Algorithm::ED25519,
911            0,
912            0,
913            0,
914            0,
915            0,
916            Name::root(),
917            vec![],
918        );
919
920        let rrsig_rsa = Record::new()
921            .set_name(name.clone())
922            .set_ttl(3600)
923            .set_rr_type(RecordType::RRSIG)
924            .set_dns_class(DNSClass::IN)
925            .set_data(Some(RData::DNSSEC(DNSSECRData::SIG(rsasha256))))
926            .clone();
927        let rrsig_ecp256 = Record::new()
928            .set_name(name.clone())
929            .set_ttl(3600)
930            .set_rr_type(RecordType::RRSIG)
931            .set_dns_class(DNSClass::IN)
932            .set_data(Some(RData::DNSSEC(DNSSECRData::SIG(ecp256))))
933            .clone();
934        let rrsig_ecp384 = Record::new()
935            .set_name(name.clone())
936            .set_ttl(3600)
937            .set_rr_type(RecordType::RRSIG)
938            .set_dns_class(DNSClass::IN)
939            .set_data(Some(RData::DNSSEC(DNSSECRData::SIG(ecp384))))
940            .clone();
941        let rrsig_ed25519 = Record::new()
942            .set_name(name.clone())
943            .set_ttl(3600)
944            .set_rr_type(RecordType::RRSIG)
945            .set_dns_class(DNSClass::IN)
946            .set_data(Some(RData::DNSSEC(DNSSECRData::SIG(ed25519))))
947            .clone();
948
949        let a = Record::new()
950            .set_name(name)
951            .set_ttl(3600)
952            .set_rr_type(RecordType::A)
953            .set_dns_class(DNSClass::IN)
954            .set_data(Some(RData::A(Ipv4Addr::new(93, 184, 216, 24))))
955            .clone();
956
957        let mut rrset = RecordSet::from(a);
958        rrset.insert_rrsig(rrsig_rsa);
959        rrset.insert_rrsig(rrsig_ecp256);
960        rrset.insert_rrsig(rrsig_ecp384);
961        rrset.insert_rrsig(rrsig_ed25519);
962
963        assert!(rrset
964            .records_with_rrsigs(SupportedAlgorithms::all(),)
965            .any(
966                |r| if let Some(RData::DNSSEC(DNSSECRData::SIG(ref sig))) = r.data() {
967                    sig.algorithm() == Algorithm::ED25519
968                } else {
969                    false
970                },
971            ));
972
973        let mut supported_algorithms = SupportedAlgorithms::new();
974        supported_algorithms.set(Algorithm::ECDSAP384SHA384);
975        assert!(rrset.records_with_rrsigs(supported_algorithms).any(|r| {
976            if let Some(RData::DNSSEC(DNSSECRData::SIG(ref sig))) = r.data() {
977                sig.algorithm() == Algorithm::ECDSAP384SHA384
978            } else {
979                false
980            }
981        }));
982
983        let mut supported_algorithms = SupportedAlgorithms::new();
984        supported_algorithms.set(Algorithm::ED25519);
985        assert!(rrset.records_with_rrsigs(supported_algorithms).any(|r| {
986            if let Some(RData::DNSSEC(DNSSECRData::SIG(ref sig))) = r.data() {
987                sig.algorithm() == Algorithm::ED25519
988            } else {
989                false
990            }
991        }));
992    }
993}