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