hickory_proto/rr/
rr_set.rs

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