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