hickory_proto/dnssec/dnssec_dns_handle/
mod.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
8//! The `DnssecDnsHandle` is used to validate all DNS responses for correct DNSSEC signatures.
9
10use alloc::{borrow::ToOwned, boxed::Box, string::ToString, sync::Arc, vec::Vec};
11use core::{clone::Clone, pin::Pin};
12use std::{
13    collections::{HashMap, HashSet},
14    time::{SystemTime, UNIX_EPOCH},
15};
16
17use futures_util::{
18    future::{self, TryFutureExt},
19    stream::{self, Stream, TryStreamExt},
20};
21use tracing::{debug, error, trace, warn};
22
23use crate::{
24    dnssec::{
25        Algorithm, Proof, ProofError, ProofErrorKind, TrustAnchors, Verifier,
26        rdata::{DNSKEY, DS, RRSIG},
27    },
28    error::{ProtoError, ProtoErrorKind},
29    op::{Edns, Message, OpCode, Query},
30    rr::{Name, Record, RecordData, RecordType, SerialNumber, resource::RecordRef},
31    xfer::{DnsRequest, DnsRequestOptions, DnsResponse, FirstAnswer, dns_handle::DnsHandle},
32};
33
34use self::rrset::Rrset;
35
36mod nsec3_validation;
37use nsec3_validation::verify_nsec3;
38
39use super::rdata::NSEC;
40
41/// Performs DNSSEC validation of all DNS responses from the wrapped DnsHandle
42///
43/// This wraps a DnsHandle, changing the implementation `send()` to validate all
44///  message responses for Query operations. Update operation responses are not validated by
45///  this process.
46#[derive(Clone)]
47#[must_use = "queries can only be sent through a DnsHandle"]
48pub struct DnssecDnsHandle<H>
49where
50    H: DnsHandle + Unpin + 'static,
51{
52    handle: H,
53    trust_anchor: Arc<TrustAnchors>,
54    request_depth: usize,
55    minimum_key_len: usize,
56    minimum_algorithm: Algorithm, // used to prevent down grade attacks...
57}
58
59impl<H> DnssecDnsHandle<H>
60where
61    H: DnsHandle + Unpin + 'static,
62{
63    /// Create a new DnssecDnsHandle wrapping the specified handle.
64    ///
65    /// This uses the compiled in TrustAnchor default trusted keys.
66    ///
67    /// # Arguments
68    /// * `handle` - handle to use for all connections to a remote server.
69    pub fn new(handle: H) -> Self {
70        Self::with_trust_anchor(handle, Arc::new(TrustAnchors::default()))
71    }
72
73    /// Create a new DnssecDnsHandle wrapping the specified handle.
74    ///
75    /// This allows a custom TrustAnchor to be define.
76    ///
77    /// # Arguments
78    /// * `handle` - handle to use for all connections to a remote server.
79    /// * `trust_anchor` - custom DNSKEYs that will be trusted, can be used to pin trusted keys.
80    pub fn with_trust_anchor(handle: H, trust_anchor: Arc<TrustAnchors>) -> Self {
81        Self {
82            handle,
83            trust_anchor,
84            request_depth: 0,
85            minimum_key_len: 0,
86            minimum_algorithm: Algorithm::RSASHA256,
87        }
88    }
89
90    /// An internal function used to clone the handle, but maintain some information back to the
91    ///  original handle, such as the request_depth such that infinite recursion does
92    ///  not occur.
93    fn clone_with_context(&self) -> Self {
94        Self {
95            handle: self.handle.clone(),
96            trust_anchor: Arc::clone(&self.trust_anchor),
97            request_depth: self.request_depth + 1,
98            minimum_key_len: self.minimum_key_len,
99            minimum_algorithm: self.minimum_algorithm,
100        }
101    }
102}
103
104#[cfg(any(feature = "std", feature = "no-std-rand"))]
105impl<H> DnsHandle for DnssecDnsHandle<H>
106where
107    H: DnsHandle + Sync + Unpin,
108{
109    type Response = Pin<Box<dyn Stream<Item = Result<DnsResponse, ProtoError>> + Send>>;
110
111    fn is_verifying_dnssec(&self) -> bool {
112        // This handler is always verifying...
113        true
114    }
115
116    fn send<R: Into<DnsRequest>>(&self, request: R) -> Self::Response {
117        let mut request = request.into();
118
119        // backstop
120        if self.request_depth > request.options().max_request_depth {
121            error!("exceeded max validation depth");
122            return Box::pin(stream::once(future::err(ProtoError::from(
123                "exceeded max validation depth",
124            ))));
125        }
126
127        // dnssec only matters on queries.
128        match request.op_code() {
129            OpCode::Query => {}
130            _ => return Box::pin(self.handle.send(request)),
131        }
132
133        // This will fail on no queries, that is a very odd type of request, isn't it?
134        // TODO: with mDNS there can be multiple queries
135        let query = if let Some(query) = request.queries().first().cloned() {
136            query
137        } else {
138            return Box::pin(stream::once(future::err(ProtoError::from(
139                "no query in request",
140            ))));
141        };
142
143        let handle = self.clone_with_context();
144        request
145            .extensions_mut()
146            .get_or_insert_with(Edns::new)
147            .enable_dnssec();
148
149        request.set_authentic_data(true);
150        request.set_checking_disabled(false);
151        let options = *request.options();
152
153        Box::pin(
154            self.handle
155                .send(request)
156                .or_else(move |res| {
157                    // Translate NoRecordsFound errors into a DnsResponse message so the rest of the
158                    // DNSSEC handler chain can validate negative responses.
159                    match res.kind() {
160                        ProtoErrorKind::NoRecordsFound {
161                            query,
162                            authorities,
163                            response_code,
164                            ..
165                        } => {
166                            let mut msg = Message::new();
167
168                            debug!("translating NoRecordsFound to DnsResponse for {query}");
169
170                            msg.add_query(*query.clone());
171
172                            msg.set_response_code(*response_code);
173
174                            if let Some(authorities) = authorities {
175                                for ns in authorities.iter() {
176                                    msg.add_name_server(ns.clone());
177                                }
178                            }
179
180                            match DnsResponse::from_message(msg) {
181                                Ok(res) => future::ok(res),
182                                Err(_e) => future::err(ProtoError::from(
183                                    "unable to construct DnsResponse: {_e:?}",
184                                )),
185                            }
186                        }
187                        _ => future::err(ProtoError::from(res.to_string())),
188                    }
189                })
190                .and_then(move |message_response| {
191                    verify_response(handle.clone(), message_response, options)
192                })
193                .and_then(move |verified_message| {
194                    future::ready(check_nsec(verified_message, &query))
195                }),
196        )
197    }
198}
199
200/// TODO: I've noticed upstream resolvers don't always return NSEC responses
201///   this causes bottom up evaluation to fail
202///
203/// at this point all of the message is verified.
204/// This is where NSEC and NSEC3 validation occurs
205fn check_nsec(verified_message: DnsResponse, query: &Query) -> Result<DnsResponse, ProtoError> {
206    if !verified_message.answers().is_empty() {
207        return Ok(verified_message);
208    }
209
210    if !verified_message.name_servers().is_empty()
211        && verified_message
212            .name_servers()
213            .iter()
214            .all(|x| x.proof() == Proof::Insecure)
215    {
216        return Ok(verified_message);
217    }
218
219    // get SOA name
220    let soa_name = if let Some(soa_name) = verified_message
221        .name_servers()
222        .iter()
223        // there should only be one
224        .find(|rr| rr.record_type() == RecordType::SOA)
225        .map(Record::name)
226    {
227        soa_name
228    } else {
229        return Err(ProtoError::from(
230            "could not validate negative response missing SOA",
231        ));
232    };
233
234    let nsec3s = verified_message
235        .name_servers()
236        .iter()
237        .filter_map(|rr| {
238            rr.data()
239                .as_dnssec()?
240                .as_nsec3()
241                .map(|data| (rr.name(), data))
242        })
243        .collect::<Vec<_>>();
244
245    let nsecs = verified_message
246        .name_servers()
247        .iter()
248        .filter_map(|rr| {
249            rr.data()
250                .as_dnssec()?
251                .as_nsec()
252                .map(|data| (rr.name(), data))
253        })
254        .collect::<Vec<_>>();
255
256    // Both NSEC and NSEC3 records cannot coexist during
257    // transition periods, as per RFC 5515 10.4.3 and
258    // 10.5.2
259    let nsec_proof = match (!nsec3s.is_empty(), !nsecs.is_empty()) {
260        (true, false) => verify_nsec3(
261            query,
262            soa_name,
263            verified_message.response_code(),
264            verified_message.answers(),
265            &nsec3s,
266        ),
267        (false, true) => verify_nsec(query, soa_name, nsecs.as_slice()),
268        (true, true) => {
269            warn!(
270                "response contains both NSEC and NSEC3 records\nQuery:\n{query:?}\nResponse:\n{verified_message:?}"
271            );
272            Proof::Bogus
273        }
274        (false, false) => {
275            warn!(
276                "response does not contain NSEC or NSEC3 records. Query: {query:?} response: {verified_message:?}"
277            );
278            Proof::Bogus
279        }
280    };
281
282    if !nsec_proof.is_secure() {
283        debug!("returning Nsec error for {} {nsec_proof}", query.name());
284        // TODO change this to remove the NSECs, like we do for the others?
285        return Err(ProtoError::from(ProtoErrorKind::Nsec {
286            query: Box::new(query.clone()),
287            proof: nsec_proof,
288        }));
289    }
290
291    Ok(verified_message)
292}
293
294/// Extracts the different sections of a message and verifies the RRSIGs
295async fn verify_response<H>(
296    handle: DnssecDnsHandle<H>,
297    mut message: DnsResponse,
298    options: DnsRequestOptions,
299) -> Result<DnsResponse, ProtoError>
300where
301    H: DnsHandle + Sync + Unpin,
302{
303    debug!(
304        "validating message_response: {}, with {} trust_anchors",
305        message.id(),
306        handle.trust_anchor.len(),
307    );
308
309    // group the record sets by name and type
310    //  each rrset type needs to validated independently
311    let answers = message.take_answers();
312    let nameservers = message.take_name_servers();
313    let additionals = message.take_additionals();
314
315    let answers = verify_rrsets(&handle, answers, options).await;
316    let nameservers = verify_rrsets(&handle, nameservers, options).await;
317    let additionals = verify_rrsets(&handle, additionals, options).await;
318
319    message.insert_answers(answers);
320    message.insert_name_servers(nameservers);
321    message.insert_additionals(additionals);
322
323    Ok(message)
324}
325
326/// This pulls all answers returned in a Message response and returns a future which will
327///  validate all of them.
328#[allow(clippy::type_complexity)]
329async fn verify_rrsets<H>(
330    handle: &DnssecDnsHandle<H>,
331    records: Vec<Record>,
332    options: DnsRequestOptions,
333) -> Vec<Record>
334where
335    H: DnsHandle + Sync + Unpin,
336{
337    let mut rrset_types: HashSet<(Name, RecordType)> = HashSet::new();
338
339    for rrset in records
340        .iter()
341        .filter(|rr| {
342            !is_dnssec(rr, RecordType::RRSIG) &&
343                             // if we are at a depth greater than 1, we are only interested in proving evaluation chains
344                             //   this means that only DNSKEY and DS are interesting at that point.
345                             //   this protects against looping over things like NS records and DNSKEYs in responses.
346                             // TODO: is there a cleaner way to prevent cycles in the evaluations?
347                                          (handle.request_depth <= 1 ||
348                                           is_dnssec(rr, RecordType::DNSKEY) ||
349                                           is_dnssec(rr, RecordType::DS))
350        })
351        .map(|rr| (rr.name().clone(), rr.record_type()))
352    {
353        rrset_types.insert(rrset);
354    }
355
356    // there were no records to verify
357    if rrset_types.is_empty() {
358        return records;
359    }
360
361    // Records for return, eventually, all records will be returned in here
362    let mut return_records = Vec::with_capacity(records.len());
363
364    // Removing the RRSIGs from the original records, the rest of the records will be mutable to remove those evaluated
365    //    and the remainder after all evalutions will be returned.
366    let (mut rrsigs, mut records) = records
367        .into_iter()
368        .partition::<Vec<_>, _>(|r| r.record_type().is_rrsig());
369
370    for (name, record_type) in rrset_types {
371        // collect all the rrsets to verify
372        let current_rrset;
373        (current_rrset, records) = records
374            .into_iter()
375            .partition::<Vec<_>, _>(|rr| rr.record_type() == record_type && rr.name() == &name);
376
377        let current_rrsigs;
378        (current_rrsigs, rrsigs) = rrsigs.into_iter().partition::<Vec<_>, _>(|rr| {
379            rr.try_borrow::<RRSIG>()
380                .map(|rr| rr.name() == &name && rr.data().type_covered() == record_type)
381                .unwrap_or_default()
382        });
383
384        // TODO: we can do a better job here, no need for all the vec creation and clones in the Rrset.
385        let mut rrs_to_verify = current_rrset.iter();
386        let mut rrset = Rrset::new(rrs_to_verify.next().unwrap());
387        rrs_to_verify.for_each(|rr| rrset.add(rr));
388
389        // RRSIGS are never modified after this point
390        let rrsigs: Vec<_> = current_rrsigs
391            .iter()
392            .filter_map(|rr| rr.try_borrow::<RRSIG>())
393            .filter(|rr| rr.name() == &name)
394            .filter(|rrsig| rrsig.data().type_covered() == record_type)
395            .collect();
396
397        // if there is already an active validation going on, assume the other validation will
398        //  complete properly or error if it is invalid
399
400        // TODO: support non-IN classes?
401        debug!(
402            "verifying: {name} record_type: {record_type}, rrsigs: {rrsig_len}",
403            rrsig_len = rrsigs.len()
404        );
405
406        // verify this rrset
407        let proof = verify_rrset(handle.clone(), &rrset, rrsigs, options).await;
408
409        let proof = match proof {
410            Ok(proof) => {
411                debug!("verified: {name} record_type: {record_type}",);
412                proof
413            }
414            Err(ProofError { proof, kind }) => {
415                match kind {
416                    ProofErrorKind::DsResponseNsec { .. } => {
417                        debug!("verified insecure {name}/{record_type}")
418                    }
419                    _ => debug!("failed to verify: {name} record_type: {record_type}: {kind}"),
420                }
421                (proof, None, None)
422            }
423        };
424
425        let (proof, adjusted_ttl, rrsig_idx) = proof;
426        for mut record in current_rrset {
427            record.set_proof(proof);
428            if let (Proof::Secure, Some(ttl)) = (proof, adjusted_ttl) {
429                record.set_ttl(ttl);
430            }
431
432            return_records.push(record);
433        }
434
435        // only mark the RRSIG used for the proof
436        let mut current_rrsigs = current_rrsigs;
437        if let Some(rrsig_idx) = rrsig_idx {
438            if let Some(rrsig) = current_rrsigs.get_mut(rrsig_idx) {
439                rrsig.set_proof(proof);
440                if let (Proof::Secure, Some(ttl)) = (proof, adjusted_ttl) {
441                    rrsig.set_ttl(ttl);
442                }
443            } else {
444                warn!(
445                    "bad rrsig index {rrsig_idx} rrsigs.len = {}",
446                    current_rrsigs.len()
447                );
448            }
449        }
450
451        // push all the RRSIGs back to the return
452        return_records.extend(current_rrsigs);
453    }
454
455    // Add back all the RRSIGs and any records that were not verified
456    return_records.extend(rrsigs);
457    return_records.extend(records);
458    return_records
459}
460
461// TODO: is this method useful/necessary?
462fn is_dnssec<D: RecordData>(rr: &Record<D>, dnssec_type: RecordType) -> bool {
463    rr.record_type().is_dnssec() && dnssec_type.is_dnssec() && rr.record_type() == dnssec_type
464}
465
466/// Generic entrypoint to verify any RRSET against the provided signatures.
467///
468/// Generally, the RRSET will be validated by `verify_default_rrset()`. There are additional
469///  checks that happen after the RRSET is successfully validated. In the case of DNSKEYs this
470///  triggers `verify_dnskey_rrset()`. If it's an NSEC record, then the NSEC record will be
471///  validated to prove it's correctness. There is a special case for DNSKEY, where if the RRSET
472///  is unsigned, `rrsigs` is empty, then an immediate `verify_dnskey_rrset()` is triggered. In
473///  this case, it's possible the DNSKEY is a trust_anchor and is not self-signed.
474///
475/// # Returns
476///
477/// If Ok, the set of (Proof, AdjustedTTL, and IndexOfRRSIG) is returned, where the index is the one of the RRSIG that validated
478///   the Rrset
479async fn verify_rrset<H>(
480    handle: DnssecDnsHandle<H>,
481    rrset: &Rrset<'_>,
482    rrsigs: Vec<RecordRef<'_, RRSIG>>,
483    options: DnsRequestOptions,
484) -> Result<(Proof, Option<u32>, Option<usize>), ProofError>
485where
486    H: DnsHandle + Sync + Unpin,
487{
488    // use the same current time value for all rrsig + rrset pairs.
489    let current_time = current_time();
490
491    // DNSKEYS have different logic for their verification
492    if matches!(rrset.record_type(), RecordType::DNSKEY) {
493        let proof = verify_dnskey_rrset(handle, rrset, &rrsigs, current_time, options).await?;
494
495        return Ok(proof);
496    }
497
498    verify_default_rrset(&handle, rrset, &rrsigs, current_time, options).await
499}
500
501/// DNSKEY-specific verification
502///
503/// A DNSKEY needs to be checked against a DS record provided by the parent zone.
504///
505/// A DNSKEY that's part of the trust anchor does not need to have its DS record (which may
506/// not exist as it's the case of the root zone) nor its RRSIG validated. If an RRSIG is present
507/// it will be validated.
508///
509/// # Return
510///
511/// If Ok, the set of (Proof, AdjustedTTL, and IndexOfRRSIG) is returned, where the index is the one of the RRSIG that validated
512///   the Rrset
513///
514/// # Panics
515///
516/// This method should only be called to validate DNSKEYs, see `verify_default_rrset` for other record types.
517///  if a non-DNSKEY RRSET is passed into this method it will always panic.
518async fn verify_dnskey_rrset<H>(
519    handle: DnssecDnsHandle<H>,
520    rrset: &Rrset<'_>,
521    rrsigs: &Vec<RecordRef<'_, RRSIG>>,
522    current_time: u32,
523    options: DnsRequestOptions,
524) -> Result<(Proof, Option<u32>, Option<usize>), ProofError>
525where
526    H: DnsHandle + Sync + Unpin,
527{
528    // Ensure that this method is not misused
529    if RecordType::DNSKEY != rrset.record_type() {
530        panic!("All other RRSETs must use verify_default_rrset");
531    }
532
533    debug!(
534        "dnskey validation {}, record_type: {:?}",
535        rrset.name(),
536        rrset.record_type()
537    );
538
539    let mut dnskey_proofs =
540        Vec::<(Proof, Option<u32>, Option<usize>)>::with_capacity(rrset.records().len());
541    dnskey_proofs.resize(rrset.records().len(), (Proof::Bogus, None, None));
542
543    // check if the DNSKEYs are in the root store
544    for (r, proof) in rrset.records().iter().zip(dnskey_proofs.iter_mut()) {
545        let Some(dnskey) = r.try_borrow::<DNSKEY>() else {
546            continue;
547        };
548
549        proof.0 = is_dnskey_in_root_store(&handle, &dnskey);
550    }
551
552    // if not all of the DNSKEYs are in the root store, then we need to look for DS records to verify
553    let ds_records = if !dnskey_proofs.iter().all(|p| p.0.is_secure()) && !rrset.name().is_root() {
554        // Need to get DS records for each DNSKEY.
555        // Every DNSKEY other than the root zone's keys may have a corresponding DS record.
556        fetch_ds_records(&handle, rrset.name().clone(), options).await?
557    } else {
558        debug!("ignoring DS lookup for root zone or registered keys");
559        Vec::default()
560    };
561
562    // if the DS records are not empty and they also have no supported algorithms, then this is INSECURE
563    // for secure DS records the BOGUS check happens after DNSKEYs are evaluated against the DS
564    if ds_records
565        .iter()
566        .filter(|ds| ds.proof().is_secure() || ds.proof().is_insecure())
567        .all(|ds| !ds.data().algorithm().is_supported() || !ds.data().digest_type().is_supported())
568        && !ds_records.is_empty()
569    {
570        debug!(
571            "all dnskeys use unsupported algorithms and there are no supported DS records in the parent zone"
572        );
573        // cannot validate; mark as insecure
574        return Err(ProofError::new(
575            Proof::Insecure,
576            ProofErrorKind::UnsupportedKeyAlgorithm,
577        ));
578    }
579
580    // verify all dnskeys individually against the DS records
581    for (r, proof) in rrset.records().iter().zip(dnskey_proofs.iter_mut()) {
582        let Some(dnskey) = r.try_borrow() else {
583            continue;
584        };
585
586        if proof.0.is_secure() {
587            continue;
588        }
589
590        // need to track each proof on each dnskey to ensure they are all validated
591        match verify_dnskey(&dnskey, &ds_records) {
592            Ok(pf) => {
593                *proof = (pf, None, None);
594            }
595            Err(err) => {
596                *proof = (err.proof, None, None);
597            }
598        }
599    }
600
601    // There may have been a key-signing key for the zone,
602    //   we need to verify all the other DNSKEYS in the zone against it (i.e. the rrset)
603    for (i, rrsig) in rrsigs.iter().enumerate() {
604        // These should all match, but double checking...
605        let signer_name = rrsig.data().signer_name();
606
607        let rrset_proof = rrset
608            .records()
609            .iter()
610            .zip(dnskey_proofs.iter())
611            .filter(|(_, (proof, ..))| proof.is_secure())
612            .filter(|(r, _)| r.name() == signer_name)
613            .filter_map(|(r, (proof, ..))| {
614                RecordRef::<'_, DNSKEY>::try_from(*r)
615                    .ok()
616                    .map(|r| (r, proof))
617            })
618            .find_map(|(dnskey, proof)| {
619                verify_rrset_with_dnskey(dnskey, *proof, rrsig, rrset, current_time).ok()
620            });
621
622        if let Some(rrset_proof) = rrset_proof {
623            return Ok((rrset_proof.0, rrset_proof.1, Some(i)));
624        }
625    }
626
627    // if it was just the root DNSKEYS with no RRSIG, we'll accept the entire set, or none
628    if dnskey_proofs.iter().all(|(proof, ..)| proof.is_secure()) {
629        return Ok(dnskey_proofs.pop().unwrap(/* This can not happen due to above test */));
630    }
631
632    if !ds_records.is_empty() {
633        // there were DS records, but no DNSKEYs, we're in a bogus state
634        trace!("bogus dnskey: {}", rrset.name());
635        return Err(ProofError::new(
636            Proof::Bogus,
637            ProofErrorKind::DsRecordsButNoDnskey {
638                name: rrset.name().clone(),
639            },
640        ));
641    }
642
643    // There were DS records or RRSIGs, but none of the signatures could be validated, so we're in a
644    // bogus state. If there was no DS record, it should have gotten an NSEC upstream, and returned
645    // early above.
646    trace!("no dnskey found: {}", rrset.name());
647    Err(ProofError::new(
648        Proof::Bogus,
649        ProofErrorKind::DnskeyNotFound {
650            name: rrset.name().clone(),
651        },
652    ))
653}
654
655/// Verifies that the key is a trust anchor.
656///
657/// # Returns
658///
659/// Proof::Secure if registered in the root store, Proof::Bogus if not
660fn is_dnskey_in_root_store<H>(handle: &DnssecDnsHandle<H>, rr: &RecordRef<'_, DNSKEY>) -> Proof
661where
662    H: DnsHandle + Sync + Unpin,
663{
664    let dns_key = rr.data();
665    let pub_key = dns_key.public_key();
666
667    // Checks to see if the key is valid against the registered root certificates
668    if handle.trust_anchor.contains(pub_key) {
669        debug!(
670            "validated dnskey with trust_anchor: {}, {dns_key}",
671            rr.name(),
672        );
673
674        Proof::Secure
675    } else {
676        Proof::Bogus
677    }
678}
679
680/// This verifies a DNSKEY record against DS records from a secure delegation.
681fn verify_dnskey(
682    rr: &RecordRef<'_, DNSKEY>,
683    ds_records: &[Record<DS>],
684) -> Result<Proof, ProofError> {
685    let key_rdata = rr.data();
686    let key_tag = key_rdata.calculate_key_tag().map_err(|_| {
687        ProofError::new(
688            Proof::Insecure,
689            ProofErrorKind::ErrorComputingKeyTag {
690                name: rr.name().clone(),
691            },
692        )
693    })?;
694    let key_algorithm = key_rdata.algorithm();
695
696    if !key_algorithm.is_supported() {
697        return Err(ProofError::new(
698            Proof::Insecure,
699            ProofErrorKind::UnsupportedKeyAlgorithm,
700        ));
701    }
702
703    // DS check if covered by DS keys
704    let mut key_authentication_attempts = 0;
705    for r in ds_records.iter().filter(|ds| ds.proof().is_secure()) {
706        if r.data().algorithm() != key_algorithm {
707            trace!(
708                "skipping DS record due to algorithm mismatch, expected algorithm {}: ({}, {})",
709                key_algorithm,
710                r.name(),
711                r.data(),
712            );
713
714            continue;
715        }
716
717        if r.data().key_tag() != key_tag {
718            trace!(
719                "skipping DS record due to key tag mismatch, expected tag {key_tag}: ({}, {})",
720                r.name(),
721                r.data(),
722            );
723
724            continue;
725        }
726
727        // Count the number of DS records with the same algorithm and key tag as this DNSKEY.
728        // Ignore remaining DS records if there are too many key tag collisions. Doing so before
729        // checking hashes or signatures protects us from KeyTrap denial of service attacks.
730        key_authentication_attempts += 1;
731        if key_authentication_attempts > MAX_KEY_TAG_COLLISIONS {
732            warn!(
733                key_tag,
734                attempts = key_authentication_attempts,
735                "too many DS records with same key tag; skipping"
736            );
737            continue;
738        }
739
740        if !r.data().covers(rr.name(), key_rdata).unwrap_or(false) {
741            continue;
742        }
743
744        debug!(
745            "validated dnskey ({}, {key_rdata}) with {} {}",
746            rr.name(),
747            r.name(),
748            r.data(),
749        );
750
751        // If this key is valid, then it is secure
752        return Ok(Proof::Secure);
753    }
754
755    trace!("bogus dnskey: {}", rr.name());
756    Err(ProofError::new(
757        Proof::Bogus,
758        ProofErrorKind::DnsKeyHasNoDs {
759            name: rr.name().clone(),
760        },
761    ))
762}
763
764/// Retrieves DS records for the given zone.
765async fn fetch_ds_records<H>(
766    handle: &DnssecDnsHandle<H>,
767    zone: Name,
768    options: DnsRequestOptions,
769) -> Result<Vec<Record<DS>>, ProofError>
770where
771    H: DnsHandle + Sync + Unpin,
772{
773    let ds_message = handle
774        .lookup(Query::query(zone.clone(), RecordType::DS), options)
775        .first_answer()
776        .await;
777
778    let error = match ds_message {
779        Ok(mut ds_message)
780            if ds_message
781                .answers()
782                .iter()
783                .filter(|r| r.record_type() == RecordType::DS)
784                .any(|r| r.proof().is_secure()) =>
785        {
786            // This is a secure DS RRset.
787
788            let all_records = ds_message
789                .take_answers()
790                .into_iter()
791                .filter_map(|r| Record::<DS>::try_from(r).ok());
792
793            let mut supported_records = vec![];
794            let mut all_unknown = None;
795            for record in all_records {
796                // A chain can be either SECURE or INSECURE, but we should not trust BOGUS or other
797                // records.
798                if (!record.data().algorithm().is_supported()
799                    || !record.data().digest_type().is_supported())
800                    && (record.proof().is_secure() || record.proof().is_insecure())
801                {
802                    all_unknown.get_or_insert(true);
803                    continue;
804                }
805                all_unknown = Some(false);
806
807                supported_records.push(record);
808            }
809
810            if all_unknown.unwrap_or(false) {
811                return Err(ProofError::new(
812                    Proof::Insecure,
813                    ProofErrorKind::UnknownKeyAlgorithm,
814                ));
815            } else if !supported_records.is_empty() {
816                return Ok(supported_records);
817            } else {
818                ProtoError::from(ProtoErrorKind::NoError)
819            }
820        }
821        Ok(response) => {
822            let any_ds_rr = response
823                .answers()
824                .iter()
825                .any(|r| r.record_type() == RecordType::DS);
826            if any_ds_rr {
827                ProtoError::from(ProtoErrorKind::NoError)
828            } else {
829                // If the response was an authenticated proof of nonexistence, then we have an
830                // insecure zone.
831                debug!("marking {zone} as insecure based on secure NSEC/NSEC3 proof");
832                return Err(ProofError::new(
833                    Proof::Insecure,
834                    ProofErrorKind::DsResponseNsec { name: zone },
835                ));
836            }
837        }
838        Err(error) => error,
839    };
840
841    // If the response was an empty DS RRset that was itself insecure, then we have another insecure zone.
842    if let Some((query, _proof)) = error
843        .kind()
844        .as_nsec()
845        .filter(|(_query, proof)| proof.is_insecure())
846    {
847        debug!(
848            "marking {} as insecure based on insecure NSEC/NSEC3 proof",
849            query.name()
850        );
851        return Err(ProofError::new(
852            Proof::Insecure,
853            ProofErrorKind::DsResponseNsec {
854                name: query.name().to_owned(),
855            },
856        ));
857    }
858
859    Err(ProofError::ds_should_exist(zone))
860}
861
862/// Checks whether a DS RRset exists for the given name or an ancestor of it.
863async fn find_ds_records<H>(
864    handle: &DnssecDnsHandle<H>,
865    zone: Name,
866    options: DnsRequestOptions,
867) -> Result<(), ProofError>
868where
869    H: DnsHandle + Sync + Unpin,
870{
871    match fetch_ds_records(handle, zone.clone(), options).await {
872        Ok(_) => return Ok(()),
873        Err(ProofError {
874            proof: _,
875            kind: ProofErrorKind::DsRecordShouldExist { .. },
876        }) => {}
877        Err(err) => return Err(err),
878    }
879
880    // Otherwise, we need to discover the status of DS RRsets up the chain. If we find a valid DS
881    // RRset, then we're in a Bogus state. If we get a ProofError, our result is the same.
882    let mut parent = zone.base_name();
883    loop {
884        match fetch_ds_records(handle, parent.clone(), options).await {
885            Ok(_) => {
886                return Err(ProofError::ds_should_exist(zone));
887            }
888            Err(ProofError {
889                proof: _,
890                kind: ProofErrorKind::DsRecordShouldExist { .. },
891            }) => {}
892            Err(err) => return Err(err),
893        }
894        parent = match parent.is_root() {
895            true => return Err(ProofError::ds_should_exist(zone)),
896            false => parent.base_name(),
897        };
898    }
899}
900
901/// Verifies that a given RRSET is validly signed by any of the specified RRSIGs.
902///
903/// Invalid RRSIGs will be ignored. RRSIGs will only be validated against DNSKEYs which can
904///  be validated through a chain back to the `trust_anchor`. As long as one RRSIG is valid,
905///  then the RRSET will be valid.
906///
907/// # Returns
908///
909/// On Ok, the set of (Proof, AdjustedTTL, and IndexOfRRSIG) is returned, where the index is the one of the RRSIG that validated
910///   the Rrset
911///
912/// # Panics
913///
914/// This method should never be called to validate DNSKEYs, see `verify_dnskey_rrset` instead.
915///  if a DNSKEY RRSET is passed into this method it will always panic.
916#[allow(clippy::blocks_in_conditions)]
917async fn verify_default_rrset<H>(
918    handle: &DnssecDnsHandle<H>,
919    rrset: &Rrset<'_>,
920    rrsigs: &Vec<RecordRef<'_, RRSIG>>,
921    current_time: u32,
922    options: DnsRequestOptions,
923) -> Result<(Proof, Option<u32>, Option<usize>), ProofError>
924where
925    H: DnsHandle + Sync + Unpin,
926{
927    // Ensure that this method is not misused
928    if RecordType::DNSKEY == rrset.record_type() {
929        panic!("DNSKEYs must be validated with verify_dnskey_rrset");
930    }
931
932    if rrsigs.is_empty() {
933        // Decide if we're:
934        //    1) "insecure", the zone has a valid NSEC for the DS record in the parent zone
935        //    2) "bogus", the parent zone has a valid DS record, but the child zone didn't have the RRSIGs/DNSKEYs
936        find_ds_records(handle, rrset.name().clone(), options).await?; // insecure will return early here
937
938        return Err(ProofError::new(
939            Proof::Bogus,
940            ProofErrorKind::RrsigsNotPresent {
941                name: rrset.name().clone(),
942                record_type: rrset.record_type(),
943            },
944        ));
945    }
946
947    // the record set is going to be shared across a bunch of futures, Arc for that.
948    trace!(
949        "default validation {}, record_type: {:?}",
950        rrset.name(),
951        rrset.record_type()
952    );
953
954    // we can validate with any of the rrsigs...
955    //  i.e. the first that validates is good enough
956    //  TODO: could there be a cert downgrade attack here with a MITM stripping stronger RRSIGs?
957    //         we could check for the strongest RRSIG and only use that...
958    //         though, since the entire package isn't signed any RRSIG could have been injected,
959    //         right? meaning if there is an attack on any of the acceptable algorithms, we'd be
960    //         susceptible until that algorithm is removed as an option.
961    //        dns over TLS will mitigate this.
962    //  TODO: strip RRSIGS to accepted algorithms and make algorithms configurable.
963    let verifications = rrsigs
964        .iter()
965        .enumerate()
966        .filter_map(|(i, rrsig)| {
967            let query = Query::query(rrsig.data().signer_name().clone(), RecordType::DNSKEY);
968
969            if i > MAX_RRSIGS_PER_RRSET {
970                warn!("too many ({i}) RRSIGs for rrset {rrset:?}; skipping");
971                return None;
972            }
973
974            // TODO: Should this sig.signer_name should be confirmed to be in the same zone as the rrsigs and rrset?
975            Some(handle
976                .lookup(query.clone(), options)
977                .first_answer()
978                .map_err(|proto| {
979                    ProofError::new(Proof::Bogus, ProofErrorKind::Proto { query, proto })
980                })
981                .map_ok(move |message| {
982                    let mut tag_count = HashMap::<u16, usize>::new();
983
984                    // DNSKEYs were already validated by the inner query in the above lookup
985                    let dnskeys = message
986                        .answers()
987                        .iter()
988                        .filter_map(|r| {
989                            let dnskey = r.try_borrow::<DNSKEY>()?;
990
991                            let tag = match dnskey.data().calculate_key_tag() {
992                                Ok(tag) => tag,
993                                Err(e) => {
994                                    warn!("unable to calculate key tag: {e:?}; skipping key");
995                                    return None;
996                                }
997                            };
998
999                            match tag_count.get_mut(&tag) {
1000                                Some(n_keys) => {
1001                                    *n_keys += 1;
1002                                    if *n_keys > MAX_KEY_TAG_COLLISIONS {
1003                                        warn!("too many ({n_keys}) DNSKEYs with key tag {tag}; skipping");
1004                                        return None;
1005                                    }
1006                                }
1007                                None => _ = tag_count.insert(tag, 1),
1008                            }
1009
1010                            Some(dnskey)
1011                        });
1012
1013                    let mut all_insecure = None;
1014                    for dnskey in dnskeys {
1015                        match dnskey.proof() {
1016                            Proof::Secure => {
1017                                all_insecure = Some(false);
1018                                if let Ok(proof) =
1019                                    verify_rrset_with_dnskey(dnskey, dnskey.proof(), rrsig, rrset, current_time)
1020                                {
1021                                    return Some((proof.0, proof.1, Some(i)));
1022                                }
1023                            }
1024                            Proof::Insecure => {
1025                                all_insecure.get_or_insert(true);
1026                            }
1027                            _ => all_insecure = Some(false),
1028                        }
1029                    }
1030
1031                    if all_insecure.unwrap_or(false) {
1032                        // inherit Insecure state
1033                        Some((Proof::Insecure, None, None))
1034                    } else {
1035                        None
1036                    }
1037                }))
1038        })
1039        .collect::<Vec<_>>();
1040
1041    // if there are no available verifications, then we are in a failed state.
1042    if verifications.is_empty() {
1043        return Err(ProofError::new(
1044            Proof::Bogus,
1045            ProofErrorKind::RrsigsNotPresent {
1046                name: rrset.name().clone(),
1047                record_type: rrset.record_type(),
1048            },
1049        ));
1050    }
1051
1052    // as long as any of the verifications is good, then the RRSET is valid.
1053    let select = future::select_ok(verifications);
1054
1055    // this will return either a good result or the errors
1056    let (proof, rest) = select.await?;
1057    drop(rest);
1058
1059    proof.ok_or_else(||
1060        // we are in a bogus state, DS records were available (see beginning of function), but RRSIGs couldn't be verified
1061        ProofError::new(Proof::Bogus, ProofErrorKind::RrsigsUnverified{name: rrset.name().clone(), record_type: rrset.record_type()})
1062    )
1063}
1064
1065/// Verifies the given SIG of the RRSET with the DNSKEY.
1066fn verify_rrset_with_dnskey(
1067    dnskey: RecordRef<'_, DNSKEY>,
1068    dnskey_proof: Proof,
1069    rrsig: &RecordRef<'_, RRSIG>,
1070    rrset: &Rrset<'_>,
1071    current_time: u32,
1072) -> Result<(Proof, Option<u32>), ProofError> {
1073    match dnskey_proof {
1074        Proof::Secure => (),
1075        proof => {
1076            debug!("insecure dnskey {} {}", dnskey.name(), dnskey.data());
1077            return Err(ProofError::new(
1078                proof,
1079                ProofErrorKind::InsecureDnsKey {
1080                    name: dnskey.name().clone(),
1081                    key_tag: rrsig.data().key_tag(),
1082                },
1083            ));
1084        }
1085    }
1086
1087    if dnskey.data().revoke() {
1088        debug!("revoked dnskey {} {}", dnskey.name(), dnskey.data());
1089        return Err(ProofError::new(
1090            Proof::Bogus,
1091            ProofErrorKind::DnsKeyRevoked {
1092                name: dnskey.name().clone(),
1093                key_tag: rrsig.data().key_tag(),
1094            },
1095        ));
1096    } // TODO: does this need to be validated? RFC 5011
1097    if !dnskey.data().zone_key() {
1098        return Err(ProofError::new(
1099            Proof::Bogus,
1100            ProofErrorKind::NotZoneDnsKey {
1101                name: dnskey.name().clone(),
1102                key_tag: rrsig.data().key_tag(),
1103            },
1104        ));
1105    }
1106    if dnskey.data().algorithm() != rrsig.data().algorithm() {
1107        return Err(ProofError::new(
1108            Proof::Bogus,
1109            ProofErrorKind::AlgorithmMismatch {
1110                rrsig: rrsig.data().algorithm(),
1111                dnskey: dnskey.data().algorithm(),
1112            },
1113        ));
1114    }
1115
1116    let validity = check_rrsig_validity(*rrsig, rrset, dnskey, current_time);
1117    if !matches!(validity, RrsigValidity::ValidRrsig) {
1118        // TODO better error handling when the error payload is not immediately discarded by
1119        // the caller
1120        return Err(ProofError::new(
1121            Proof::Bogus,
1122            ProofErrorKind::Msg(format!("{:?}", validity)),
1123        ));
1124    }
1125
1126    dnskey
1127        .data()
1128        .verify_rrsig(
1129            rrset.name(),
1130            rrset.record_class(),
1131            rrsig.data(),
1132            rrset.records().iter().copied(),
1133        )
1134        .map(|_| {
1135            debug!(
1136                "validated ({}, {:?}) with ({}, {})",
1137                rrset.name(),
1138                rrset.record_type(),
1139                dnskey.name(),
1140                dnskey.data()
1141            );
1142            (
1143                Proof::Secure,
1144                Some(rrsig.data().authenticated_ttl(rrset.record(), current_time)),
1145            )
1146        })
1147        .map_err(|e| {
1148            debug!(
1149                "failed validation of ({}, {:?}) with ({}, {})",
1150                rrset.name(),
1151                rrset.record_type(),
1152                dnskey.name(),
1153                dnskey.data()
1154            );
1155            ProofError::new(
1156                Proof::Bogus,
1157                ProofErrorKind::DnsKeyVerifyRrsig {
1158                    name: dnskey.name().clone(),
1159                    key_tag: rrsig.data().key_tag(),
1160                    error: e,
1161                },
1162            )
1163        })
1164}
1165
1166// see section 5.3.1 of RFC4035 "Checking the RRSIG RR Validity"
1167fn check_rrsig_validity(
1168    rrsig: RecordRef<'_, RRSIG>,
1169    rrset: &Rrset<'_>,
1170    dnskey: RecordRef<'_, DNSKEY>,
1171    current_time: u32,
1172) -> RrsigValidity {
1173    let current_time = SerialNumber(current_time);
1174    let expiration = rrsig.data().sig_expiration();
1175    let inception = rrsig.data().sig_inception();
1176
1177    let Ok(dnskey_key_tag) = dnskey.data().calculate_key_tag() else {
1178        return RrsigValidity::WrongDnskey;
1179    };
1180
1181    if !(
1182        // "The RRSIG RR and the RRset MUST have the same owner name and the same class"
1183        rrsig.name() == rrset.name() &&
1184        rrsig.dns_class() == rrset.record_class() &&
1185
1186        // "The RRSIG RR's Signer's Name field MUST be the name of the zone that contains the RRset"
1187        // TODO(^) the zone name is in the SOA record, which is not accessible from here
1188
1189        // "The RRSIG RR's Type Covered field MUST equal the RRset's type"
1190        rrsig.data().type_covered() == rrset.record_type() &&
1191
1192        // "The number of labels in the RRset owner name MUST be greater than or equal to the value
1193        // in the RRSIG RR's Labels field"
1194        rrset.name().num_labels() >= rrsig.data().num_labels()
1195    ) {
1196        return RrsigValidity::WrongRrsig;
1197    }
1198
1199    // Section 3.1.5 of RFC4034 states that 'all comparisons involving these fields MUST use
1200    // "Serial number arithmetic", as defined in RFC1982'
1201    if !(
1202        // "The validator's notion of the current time MUST be less than or equal to the time listed
1203        // in the RRSIG RR's Expiration field"
1204        current_time <= expiration &&
1205
1206        // "The validator's notion of the current time MUST be greater than or equal to the time
1207        // listed in the RRSIG RR's Inception field"
1208        current_time >= inception
1209    ) {
1210        return RrsigValidity::ExpiredRrsig;
1211    }
1212
1213    if !(
1214        // "The RRSIG RR's Signer's Name, Algorithm, and Key Tag fields MUST match the owner name,
1215        // algorithm, and key tag for some DNSKEY RR in the zone's apex DNSKEY RRset"
1216        rrsig.data().signer_name() == dnskey.name() &&
1217        rrsig.data().algorithm() == dnskey.data().algorithm() &&
1218        rrsig.data().key_tag() == dnskey_key_tag &&
1219
1220        // "The matching DNSKEY RR MUST be present in the zone's apex DNSKEY RRset, and MUST have the
1221        // Zone Flag bit (DNSKEY RDATA Flag bit 7) set"
1222        dnskey.data().zone_key()
1223    ) {
1224        return RrsigValidity::WrongDnskey;
1225    }
1226
1227    RrsigValidity::ValidRrsig
1228}
1229
1230#[derive(Clone, Copy, Debug)]
1231enum RrsigValidity {
1232    /// RRSIG has already expired
1233    ExpiredRrsig,
1234    /// RRSIG is valid
1235    ValidRrsig,
1236    /// DNSKEY does not match RRSIG
1237    WrongDnskey,
1238    /// RRSIG does not match RRset
1239    WrongRrsig,
1240}
1241
1242/// Verifies NSEC records
1243///
1244/// ```text
1245/// RFC 4035             DNSSEC Protocol Modifications            March 2005
1246///
1247/// 5.4.  Authenticated Denial of Existence
1248///
1249///  A resolver can use authenticated NSEC RRs to prove that an RRset is
1250///  not present in a signed zone.  Security-aware name servers should
1251///  automatically include any necessary NSEC RRs for signed zones in
1252///  their responses to security-aware resolvers.
1253///
1254///  Denial of existence is determined by the following rules:
1255///
1256///  o  If the requested RR name matches the owner name of an
1257///     authenticated NSEC RR, then the NSEC RR's type bit map field lists
1258///     all RR types present at that owner name, and a resolver can prove
1259///     that the requested RR type does not exist by checking for the RR
1260///     type in the bit map.  If the number of labels in an authenticated
1261///     NSEC RR's owner name equals the Labels field of the covering RRSIG
1262///     RR, then the existence of the NSEC RR proves that wildcard
1263///     expansion could not have been used to match the request.
1264///
1265///  o  If the requested RR name would appear after an authenticated NSEC
1266///     RR's owner name and before the name listed in that NSEC RR's Next
1267///     Domain Name field according to the canonical DNS name order
1268///     defined in [RFC4034], then no RRsets with the requested name exist
1269///     in the zone.  However, it is possible that a wildcard could be
1270///     used to match the requested RR owner name and type, so proving
1271///     that the requested RRset does not exist also requires proving that
1272///     no possible wildcard RRset exists that could have been used to
1273///     generate a positive response.
1274///
1275///  In addition, security-aware resolvers MUST authenticate the NSEC
1276///  RRsets that comprise the non-existence proof as described in Section
1277///  5.3.
1278///
1279///  To prove the non-existence of an RRset, the resolver must be able to
1280///  verify both that the queried RRset does not exist and that no
1281///  relevant wildcard RRset exists.  Proving this may require more than
1282///  one NSEC RRset from the zone.  If the complete set of necessary NSEC
1283///  RRsets is not present in a response (perhaps due to message
1284///  truncation), then a security-aware resolver MUST resend the query in
1285///  order to attempt to obtain the full collection of NSEC RRs necessary
1286///  to verify the non-existence of the requested RRset.  As with all DNS
1287///  operations, however, the resolver MUST bound the work it puts into
1288///  answering any particular query.
1289///
1290///  Since a validated NSEC RR proves the existence of both itself and its
1291///  corresponding RRSIG RR, a validator MUST ignore the settings of the
1292///  NSEC and RRSIG bits in an NSEC RR.
1293/// ```
1294#[allow(clippy::blocks_in_conditions)]
1295#[doc(hidden)]
1296pub fn verify_nsec(query: &Query, soa_name: &Name, nsecs: &[(&Name, &NSEC)]) -> Proof {
1297    // TODO: consider converting this to Result, and giving explicit reason for the failure
1298
1299    // first look for a record with the same name
1300    //  if they are, then the query_type should not exist in the NSEC record.
1301    //  if we got an NSEC record of the same name, but it is listed in the NSEC types,
1302    //    WTF? is that bad server, bad record
1303    if let Some((_, nsec_data)) = nsecs.iter().find(|(name, _)| query.name() == *name) {
1304        if !nsec_data.type_set().contains(query.query_type()) {
1305            return proof_log_yield(Proof::Secure, query.name(), "nsec1", "direct match");
1306        } else {
1307            return proof_log_yield(Proof::Bogus, query.name(), "nsec1", "direct match");
1308        }
1309    }
1310
1311    let verify_nsec_coverage = |query_name: &Name| -> bool {
1312        nsecs.iter().any(|(nsec_name, nsec_data)| {
1313            // the query name must be greater than nsec's label (or equal in the case of wildcard)
1314            query_name >= nsec_name && {
1315                // the query name is less than the next name
1316                // or this record wraps the end, i.e. is the last record
1317                query_name < nsec_data.next_domain_name()
1318                    || nsec_data.next_domain_name() < nsec_name
1319            }
1320        })
1321    };
1322
1323    // continue to validate there is no wildcard
1324    if !verify_nsec_coverage(query.name()) {
1325        return proof_log_yield(Proof::Bogus, query.name(), "nsec1", "no wildcard");
1326    }
1327
1328    // validate ANY or *.domain record existence
1329
1330    // we need the wildcard proof, but make sure that it's still part of the zone.
1331    let wildcard = query.name().base_name();
1332    let wildcard = if soa_name.zone_of(&wildcard) {
1333        wildcard
1334    } else {
1335        soa_name.clone()
1336    };
1337
1338    // don't need to validate the same name again
1339    if wildcard == *query.name() {
1340        // this was validated by the nsec coverage over the query.name()
1341        proof_log_yield(
1342            Proof::Secure,
1343            query.name(),
1344            "nsec1",
1345            "direct wildcard match",
1346        )
1347    } else {
1348        // this is the final check, return it's value
1349        //  if there is wildcard coverage, we're good.
1350        if verify_nsec_coverage(&wildcard) {
1351            proof_log_yield(
1352                Proof::Secure,
1353                query.name(),
1354                "nsec1",
1355                "covering wildcard match",
1356            )
1357        } else {
1358            proof_log_yield(
1359                Proof::Bogus,
1360                query.name(),
1361                "nsec1",
1362                "covering wildcard match",
1363            )
1364        }
1365    }
1366}
1367
1368/// Returns the current system time as Unix timestamp in seconds.
1369fn current_time() -> u32 {
1370    SystemTime::now()
1371        .duration_since(UNIX_EPOCH)
1372        .unwrap_or_default()
1373        .as_secs() as u32
1374}
1375
1376/// Logs a debug message and yields a Proof type for return
1377fn proof_log_yield(proof: Proof, name: &Name, nsec_type: &str, msg: &str) -> Proof {
1378    debug!("{nsec_type} proof for {name}, returning {proof}: {msg}");
1379    proof
1380}
1381
1382mod rrset {
1383    use alloc::vec::Vec;
1384
1385    use crate::rr::{DNSClass, Name, Record, RecordType};
1386
1387    // TODO: combine this with crate::rr::RecordSet?
1388    #[derive(Debug)]
1389    pub(super) struct Rrset<'r> {
1390        name: Name,
1391        record_class: DNSClass,
1392        record_type: RecordType,
1393        records: Vec<&'r Record>,
1394    }
1395
1396    impl<'r> Rrset<'r> {
1397        pub(super) fn new(record: &'r Record) -> Self {
1398            Self {
1399                name: record.name().clone(),
1400                record_class: record.dns_class(),
1401                record_type: record.record_type(),
1402                records: vec![record],
1403            }
1404        }
1405
1406        /// Adds `record` to this RRset IFF it belongs to it
1407        pub(super) fn add(&mut self, record: &'r Record) {
1408            if self.name == *record.name()
1409                && self.record_type == record.record_type()
1410                && self.record_class == record.dns_class()
1411            {
1412                self.records.push(record);
1413            }
1414        }
1415
1416        /// Returns the first (main) record.
1417        pub(super) fn record(&self) -> &Record {
1418            self.records[0]
1419        }
1420
1421        pub(super) fn name(&self) -> &Name {
1422            &self.name
1423        }
1424
1425        pub(super) fn record_class(&self) -> DNSClass {
1426            self.record_class
1427        }
1428
1429        pub(super) fn record_type(&self) -> RecordType {
1430            self.record_type
1431        }
1432
1433        pub(super) fn records(&self) -> &[&Record] {
1434            &self.records
1435        }
1436    }
1437}
1438
1439/// The maximum number of key tag collisions to accept when:
1440///
1441/// 1) Retrieving DNSKEY records for a zone
1442/// 2) Retrieving DS records from a parent zone
1443///
1444/// Any colliding records encountered beyond this limit will be discarded.
1445const MAX_KEY_TAG_COLLISIONS: usize = 2;
1446
1447/// The maximum number of RRSIGs to attempt to validate for each RRSET.
1448const MAX_RRSIGS_PER_RRSET: usize = 8;