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-02
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 5) 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            //   ANAME records MAY freely coexist at the same owner name with other RR
318            //   types, except they MUST NOT coexist with CNAME or any other RR type
319            //   that restricts the types with which it can itself coexist.
320            //
321            //   Like other types, ANAME records can coexist with DNAME records at the
322            //   same owner name; in fact, the two can be used cooperatively to
323            //   redirect both the owner name address records (via ANAME) and
324            //   everything under it (via DNAME).
325            RecordType::CNAME | RecordType::ANAME => {
326                assert!(self.records.len() <= 1);
327                self.records.clear();
328            }
329            _ => (),
330        }
331
332        // collect any records to update based on rdata
333        let to_replace: Vec<usize> = self
334            .records
335            .iter()
336            .enumerate()
337            .filter(|&(_, rr)| rr.data() == record.data())
338            .map(|(i, _)| i)
339            .collect::<Vec<usize>>();
340
341        // if the Records are identical, ignore the update, update all that are not (ttl, etc.)
342        let mut replaced = false;
343        for i in to_replace {
344            if self.records[i] == record {
345                return false;
346            }
347
348            // TODO: this shouldn't really need a clone since there should only be one...
349            self.records.push(record.clone());
350            self.records.swap_remove(i);
351            self.ttl = record.ttl();
352            self.updated(serial);
353            replaced = true;
354        }
355
356        if !replaced {
357            self.ttl = record.ttl();
358            self.updated(serial);
359            self.records.push(record);
360            true
361        } else {
362            replaced
363        }
364    }
365
366    /// Removes the Resource Record if it exists.
367    ///
368    /// # Arguments
369    ///
370    /// * `record` - `Record` asserts that the `name` and `record_type` match the `RecordSet`. Removes
371    ///              any `record` if the record data, `RData`, match.
372    /// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
373    ///              signing for DNSSEC after updates. The serial will only be updated if the
374    ///              record was added.
375    ///
376    /// # Return value
377    ///
378    /// True if a record was removed.
379    pub fn remove(&mut self, record: &Record, serial: u32) -> bool {
380        assert_eq!(record.name(), &self.name);
381        assert!(
382            record.record_type() == self.record_type || record.record_type() == RecordType::ANY
383        );
384
385        match record.record_type() {
386            // never delete the last NS record
387            RecordType::NS => {
388                if self.records.len() <= 1 {
389                    info!("ignoring delete of last NS record: {:?}", record);
390                    return false;
391                }
392            }
393            // never delete SOA
394            RecordType::SOA => {
395                info!("ignored delete of SOA");
396                return false;
397            }
398            _ => (), // move on to the delete
399        }
400
401        // remove the records, first collect all the indexes, then remove the records
402        let to_remove: Vec<usize> = self
403            .records
404            .iter()
405            .enumerate()
406            .filter(|&(_, rr)| rr.data() == record.data())
407            .map(|(i, _)| i)
408            .collect::<Vec<usize>>();
409
410        let mut removed = false;
411        for i in to_remove {
412            self.records.remove(i);
413            removed = true;
414            self.updated(serial);
415        }
416
417        removed
418    }
419
420    /// Consumes `RecordSet` and returns its components
421    pub fn into_parts(self) -> RecordSetParts {
422        self.into()
423    }
424}
425
426/// Consumes `RecordSet` giving public access to fields of `RecordSet` so they can
427/// be destructured and taken by value
428#[derive(Clone, Debug, PartialEq, Eq)]
429pub struct RecordSetParts {
430    pub name: Name,
431    pub record_type: RecordType,
432    pub dns_class: DNSClass,
433    pub ttl: u32,
434    pub records: Vec<Record>,
435    pub rrsigs: Vec<Record>,
436    pub serial: u32, // serial number at which this record was modified,
437}
438
439impl From<RecordSet> for RecordSetParts {
440    fn from(rset: RecordSet) -> Self {
441        let RecordSet {
442            name,
443            record_type,
444            dns_class,
445            ttl,
446            records,
447            rrsigs,
448            serial,
449        } = rset;
450        Self {
451            name,
452            record_type,
453            dns_class,
454            ttl,
455            records,
456            rrsigs,
457            serial,
458        }
459    }
460}
461
462impl From<Record> for RecordSet {
463    fn from(record: Record) -> Self {
464        Self {
465            name: record.name().clone(),
466            record_type: record.record_type(),
467            dns_class: record.dns_class(),
468            ttl: record.ttl(),
469            records: vec![record],
470            rrsigs: vec![],
471            serial: 0,
472        }
473    }
474}
475
476/// Types which implement this can be converted into a RecordSet
477#[deprecated(note = "use From/Into")]
478pub trait IntoRecordSet: Sized {
479    /// Performs the conversion to a RecordSet
480    fn into_record_set(self) -> RecordSet;
481}
482
483#[allow(deprecated)]
484impl IntoRecordSet for RecordSet {
485    fn into_record_set(self) -> Self {
486        self
487    }
488}
489
490impl IntoIterator for RecordSet {
491    type Item = Record;
492    type IntoIter = Chain<vec::IntoIter<Record>, vec::IntoIter<Record>>;
493
494    fn into_iter(self) -> Self::IntoIter {
495        self.records.into_iter().chain(self.rrsigs)
496    }
497}
498
499/// An iterator over all the records and their signatures
500#[cfg(feature = "__dnssec")]
501#[derive(Debug)]
502pub struct RecordsAndRrsigsIter<'r>(Chain<Iter<'r, Record>, Iter<'r, Record>>);
503
504#[cfg(feature = "__dnssec")]
505impl<'r> Iterator for RecordsAndRrsigsIter<'r> {
506    type Item = &'r Record;
507
508    fn next(&mut self) -> Option<Self::Item> {
509        self.0.next()
510    }
511}
512
513/// An iterator over the RecordSet data
514#[derive(Debug)]
515pub enum RrsetRecords<'r> {
516    /// There are no records in the record set
517    Empty,
518    /// The records associated with the record set
519    RecordsOnly(Iter<'r, Record>),
520    /// The records along with their signatures in the record set
521    #[cfg(feature = "__dnssec")]
522    RecordsAndRrsigs(RecordsAndRrsigsIter<'r>),
523}
524
525impl RrsetRecords<'_> {
526    /// This is a best effort emptiness check
527    pub fn is_empty(&self) -> bool {
528        matches!(*self, RrsetRecords::Empty)
529    }
530}
531
532impl<'r> Iterator for RrsetRecords<'r> {
533    type Item = &'r Record;
534
535    fn next(&mut self) -> Option<Self::Item> {
536        match self {
537            RrsetRecords::Empty => None,
538            RrsetRecords::RecordsOnly(i) => i.next(),
539            #[cfg(feature = "__dnssec")]
540            RrsetRecords::RecordsAndRrsigs(i) => i.next(),
541        }
542    }
543}
544
545#[cfg(test)]
546mod test {
547    #[cfg(not(feature = "std"))]
548    use core::net::Ipv4Addr;
549    use core::str::FromStr;
550    #[cfg(feature = "std")]
551    use std::net::Ipv4Addr;
552
553    use crate::rr::rdata::{CNAME, NS, SOA};
554    use crate::rr::*;
555
556    #[test]
557    fn test_insert() {
558        let name = Name::from_str("www.example.com.").unwrap();
559        let record_type = RecordType::A;
560        let mut rr_set = RecordSet::new(name.clone(), record_type, 0);
561
562        let insert = Record::from_rdata(
563            name.clone(),
564            86400,
565            RData::A(Ipv4Addr::new(93, 184, 216, 24).into()),
566        )
567        .set_dns_class(DNSClass::IN)
568        .clone();
569
570        assert!(rr_set.insert(insert.clone(), 0));
571        assert_eq!(rr_set.records_without_rrsigs().count(), 1);
572        assert!(rr_set.records_without_rrsigs().any(|x| x == &insert));
573
574        // dups ignored
575        assert!(!rr_set.insert(insert.clone(), 0));
576        assert_eq!(rr_set.records_without_rrsigs().count(), 1);
577        assert!(rr_set.records_without_rrsigs().any(|x| x == &insert));
578
579        // add one
580        let insert1 = Record::from_rdata(
581            name,
582            86400,
583            RData::A(Ipv4Addr::new(93, 184, 216, 25).into()),
584        )
585        .set_dns_class(DNSClass::IN)
586        .clone();
587        assert!(rr_set.insert(insert1.clone(), 0));
588        assert_eq!(rr_set.records_without_rrsigs().count(), 2);
589        assert!(rr_set.records_without_rrsigs().any(|x| x == &insert));
590        assert!(rr_set.records_without_rrsigs().any(|x| x == &insert1));
591    }
592
593    #[test]
594    #[allow(clippy::unreadable_literal)]
595    fn test_insert_soa() {
596        let name = Name::from_str("example.com.").unwrap();
597        let record_type = RecordType::SOA;
598        let mut rr_set = RecordSet::new(name.clone(), record_type, 0);
599
600        let insert = Record::from_rdata(
601            name.clone(),
602            3600,
603            RData::SOA(SOA::new(
604                Name::from_str("sns.dns.icann.org.").unwrap(),
605                Name::from_str("noc.dns.icann.org.").unwrap(),
606                2015082403,
607                7200,
608                3600,
609                1209600,
610                3600,
611            )),
612        )
613        .set_dns_class(DNSClass::IN)
614        .clone();
615        let same_serial = Record::from_rdata(
616            name.clone(),
617            3600,
618            RData::SOA(SOA::new(
619                Name::from_str("sns.dns.icann.net.").unwrap(),
620                Name::from_str("noc.dns.icann.net.").unwrap(),
621                2015082403,
622                7200,
623                3600,
624                1209600,
625                3600,
626            )),
627        )
628        .set_dns_class(DNSClass::IN)
629        .clone();
630        let new_serial = Record::from_rdata(
631            name,
632            3600,
633            RData::SOA(SOA::new(
634                Name::from_str("sns.dns.icann.net.").unwrap(),
635                Name::from_str("noc.dns.icann.net.").unwrap(),
636                2015082404,
637                7200,
638                3600,
639                1209600,
640                3600,
641            )),
642        )
643        .set_dns_class(DNSClass::IN)
644        .clone();
645
646        assert!(rr_set.insert(insert.clone(), 0));
647        assert!(rr_set.records_without_rrsigs().any(|x| x == &insert));
648        // same serial number
649        assert!(!rr_set.insert(same_serial.clone(), 0));
650        assert!(rr_set.records_without_rrsigs().any(|x| x == &insert));
651        assert!(!rr_set.records_without_rrsigs().any(|x| x == &same_serial));
652
653        assert!(rr_set.insert(new_serial.clone(), 0));
654        assert!(!rr_set.insert(same_serial.clone(), 0));
655        assert!(!rr_set.insert(insert.clone(), 0));
656
657        assert!(rr_set.records_without_rrsigs().any(|x| x == &new_serial));
658        assert!(!rr_set.records_without_rrsigs().any(|x| x == &insert));
659        assert!(!rr_set.records_without_rrsigs().any(|x| x == &same_serial));
660    }
661
662    #[test]
663    fn test_insert_cname() {
664        let name = Name::from_str("web.example.com.").unwrap();
665        let cname = Name::from_str("www.example.com.").unwrap();
666        let new_cname = Name::from_str("w2.example.com.").unwrap();
667
668        let record_type = RecordType::CNAME;
669        let mut rr_set = RecordSet::new(name.clone(), record_type, 0);
670
671        let insert = Record::from_rdata(name.clone(), 3600, RData::CNAME(CNAME(cname)))
672            .set_dns_class(DNSClass::IN)
673            .clone();
674        let new_record = Record::from_rdata(name, 3600, RData::CNAME(CNAME(new_cname)))
675            .set_dns_class(DNSClass::IN)
676            .clone();
677
678        assert!(rr_set.insert(insert.clone(), 0));
679        assert!(rr_set.records_without_rrsigs().any(|x| x == &insert));
680
681        // update the record
682        assert!(rr_set.insert(new_record.clone(), 0));
683        assert!(!rr_set.records_without_rrsigs().any(|x| x == &insert));
684        assert!(rr_set.records_without_rrsigs().any(|x| x == &new_record));
685    }
686
687    #[test]
688    fn test_remove() {
689        let name = Name::from_str("www.example.com.").unwrap();
690        let record_type = RecordType::A;
691        let mut rr_set = RecordSet::new(name.clone(), record_type, 0);
692
693        let insert = Record::from_rdata(
694            name.clone(),
695            86400,
696            RData::A(Ipv4Addr::new(93, 184, 216, 24).into()),
697        )
698        .set_dns_class(DNSClass::IN)
699        .clone();
700        let insert1 = Record::from_rdata(
701            name,
702            86400,
703            RData::A(Ipv4Addr::new(93, 184, 216, 25).into()),
704        )
705        .set_dns_class(DNSClass::IN)
706        .clone();
707
708        assert!(rr_set.insert(insert.clone(), 0));
709        assert!(rr_set.insert(insert1.clone(), 0));
710
711        assert!(rr_set.remove(&insert, 0));
712        assert!(!rr_set.remove(&insert, 0));
713        assert!(rr_set.remove(&insert1, 0));
714        assert!(!rr_set.remove(&insert1, 0));
715    }
716
717    #[test]
718    #[allow(clippy::unreadable_literal)]
719    fn test_remove_soa() {
720        let name = Name::from_str("www.example.com.").unwrap();
721        let record_type = RecordType::SOA;
722        let mut rr_set = RecordSet::new(name.clone(), record_type, 0);
723
724        let insert = Record::from_rdata(
725            name,
726            3600,
727            RData::SOA(SOA::new(
728                Name::from_str("sns.dns.icann.org.").unwrap(),
729                Name::from_str("noc.dns.icann.org.").unwrap(),
730                2015082403,
731                7200,
732                3600,
733                1209600,
734                3600,
735            )),
736        )
737        .set_dns_class(DNSClass::IN)
738        .clone();
739
740        assert!(rr_set.insert(insert.clone(), 0));
741        assert!(!rr_set.remove(&insert, 0));
742        assert!(rr_set.records_without_rrsigs().any(|x| x == &insert));
743    }
744
745    #[test]
746    fn test_remove_ns() {
747        let name = Name::from_str("example.com.").unwrap();
748        let record_type = RecordType::NS;
749        let mut rr_set = RecordSet::new(name.clone(), record_type, 0);
750
751        let ns1 = Record::from_rdata(
752            name.clone(),
753            86400,
754            RData::NS(NS(Name::from_str("a.iana-servers.net.").unwrap())),
755        )
756        .set_dns_class(DNSClass::IN)
757        .clone();
758        let ns2 = Record::from_rdata(
759            name,
760            86400,
761            RData::NS(NS(Name::from_str("b.iana-servers.net.").unwrap())),
762        )
763        .set_dns_class(DNSClass::IN)
764        .clone();
765
766        assert!(rr_set.insert(ns1.clone(), 0));
767        assert!(rr_set.insert(ns2.clone(), 0));
768
769        // ok to remove one, but not two...
770        assert!(rr_set.remove(&ns1, 0));
771        assert!(!rr_set.remove(&ns2, 0));
772
773        // check that we can swap which ones are removed
774        assert!(rr_set.insert(ns1.clone(), 0));
775
776        assert!(rr_set.remove(&ns2, 0));
777        assert!(!rr_set.remove(&ns1, 0));
778    }
779
780    #[test]
781    #[cfg(feature = "__dnssec")] // This tests RFC 6975, a DNSSEC-specific feature.
782    #[allow(clippy::blocks_in_conditions)]
783    fn test_get_filter() {
784        use crate::dnssec::{
785            Algorithm,
786            rdata::{DNSSECRData, RRSIG},
787        };
788
789        let name = Name::root();
790        let rsasha256 = RRSIG::new(
791            RecordType::A,
792            Algorithm::RSASHA256,
793            0,
794            0,
795            0,
796            0,
797            0,
798            Name::root(),
799            vec![],
800        );
801        let ecp256 = RRSIG::new(
802            RecordType::A,
803            Algorithm::ECDSAP256SHA256,
804            0,
805            0,
806            0,
807            0,
808            0,
809            Name::root(),
810            vec![],
811        );
812        let ecp384 = RRSIG::new(
813            RecordType::A,
814            Algorithm::ECDSAP384SHA384,
815            0,
816            0,
817            0,
818            0,
819            0,
820            Name::root(),
821            vec![],
822        );
823        let ed25519 = RRSIG::new(
824            RecordType::A,
825            Algorithm::ED25519,
826            0,
827            0,
828            0,
829            0,
830            0,
831            Name::root(),
832            vec![],
833        );
834
835        let rrsig_rsa = Record::from_rdata(
836            name.clone(),
837            3600,
838            RData::DNSSEC(DNSSECRData::RRSIG(rsasha256)),
839        )
840        .set_dns_class(DNSClass::IN)
841        .clone();
842        let rrsig_ecp256 = Record::from_rdata(
843            name.clone(),
844            3600,
845            RData::DNSSEC(DNSSECRData::RRSIG(ecp256)),
846        )
847        .set_dns_class(DNSClass::IN)
848        .clone();
849        let rrsig_ecp384 = Record::from_rdata(
850            name.clone(),
851            3600,
852            RData::DNSSEC(DNSSECRData::RRSIG(ecp384)),
853        )
854        .set_dns_class(DNSClass::IN)
855        .clone();
856        let rrsig_ed25519 = Record::from_rdata(
857            name.clone(),
858            3600,
859            RData::DNSSEC(DNSSECRData::RRSIG(ed25519)),
860        )
861        .set_dns_class(DNSClass::IN)
862        .clone();
863
864        let a = Record::from_rdata(name, 3600, RData::A(Ipv4Addr::new(93, 184, 216, 24).into()))
865            .set_dns_class(DNSClass::IN)
866            .clone();
867
868        let mut rrset = RecordSet::from(a);
869        rrset.insert_rrsig(rrsig_rsa);
870        rrset.insert_rrsig(rrsig_ecp256);
871        rrset.insert_rrsig(rrsig_ecp384);
872        rrset.insert_rrsig(rrsig_ed25519);
873
874        assert!(rrset.records_with_rrsigs().any(|r| {
875            if let RData::DNSSEC(DNSSECRData::RRSIG(sig)) = r.data() {
876                sig.algorithm() == Algorithm::ED25519
877            } else {
878                false
879            }
880        },));
881
882        assert!(rrset.records_with_rrsigs().any(|r| {
883            if let RData::DNSSEC(DNSSECRData::RRSIG(sig)) = r.data() {
884                sig.algorithm() == Algorithm::ECDSAP384SHA384
885            } else {
886                false
887            }
888        }));
889
890        assert!(rrset.records_with_rrsigs().any(|r| {
891            if let RData::DNSSEC(DNSSECRData::RRSIG(sig)) = r.data() {
892                sig.algorithm() == Algorithm::ED25519
893            } else {
894                false
895            }
896        }));
897    }
898}