hickory_server/store/in_memory/
mod.rs

1// Copyright 2015-2019 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//! Zone file based serving with Dynamic DNS and journaling support
9
10#[cfg(all(feature = "__dnssec", feature = "testing"))]
11use std::ops::Deref;
12use std::{collections::BTreeMap, ops::DerefMut, sync::Arc};
13
14use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
15use tracing::debug;
16#[cfg(feature = "__dnssec")]
17use tracing::warn;
18
19use crate::{
20    authority::{
21        AnyRecords, AuthLookup, Authority, LookupControlFlow, LookupError, LookupOptions,
22        LookupRecords, MessageRequest, UpdateResult, ZoneType,
23    },
24    proto::{
25        op::ResponseCode,
26        rr::{DNSClass, LowerName, Name, RData, Record, RecordSet, RecordType, RrKey, rdata::SOA},
27    },
28    server::RequestInfo,
29};
30#[cfg(feature = "__dnssec")]
31use crate::{
32    authority::{DnssecAuthority, Nsec3QueryInfo},
33    dnssec::NxProofKind,
34    proto::dnssec::{
35        DnsSecResult, SigSigner,
36        rdata::{DNSKEY, DNSSECRData, key::KEY},
37    },
38};
39
40mod inner;
41use inner::InnerInMemory;
42
43/// InMemoryAuthority is responsible for storing the resource records for a particular zone.
44///
45/// Authorities default to DNSClass IN. The ZoneType specifies if this should be treated as the
46/// start of authority for the zone, is a Secondary, or a cached zone.
47pub struct InMemoryAuthority {
48    origin: LowerName,
49    class: DNSClass,
50    zone_type: ZoneType,
51    allow_axfr: bool,
52    inner: RwLock<InnerInMemory>,
53    #[cfg(feature = "__dnssec")]
54    nx_proof_kind: Option<NxProofKind>,
55}
56
57impl InMemoryAuthority {
58    /// Creates a new Authority.
59    ///
60    /// # Arguments
61    ///
62    /// * `origin` - The zone `Name` being created, this should match that of the `RecordType::SOA`
63    ///   record.
64    /// * `records` - The map of the initial set of records in the zone.
65    /// * `zone_type` - The type of zone, i.e. is this authoritative?
66    /// * `allow_axfr` - Whether AXFR is allowed.
67    /// * `nx_proof_kind` - The kind of non-existence proof to be used by the server.
68    ///
69    /// # Return value
70    ///
71    /// The new `Authority`.
72    pub fn new(
73        origin: Name,
74        records: BTreeMap<RrKey, RecordSet>,
75        zone_type: ZoneType,
76        allow_axfr: bool,
77        #[cfg(feature = "__dnssec")] nx_proof_kind: Option<NxProofKind>,
78    ) -> Result<Self, String> {
79        let mut this = Self::empty(
80            origin.clone(),
81            zone_type,
82            allow_axfr,
83            #[cfg(feature = "__dnssec")]
84            nx_proof_kind,
85        );
86        let inner = this.inner.get_mut();
87
88        // SOA must be present
89        let serial = records
90            .iter()
91            .find(|(key, _)| key.record_type == RecordType::SOA)
92            .and_then(|(_, rrset)| rrset.records_without_rrsigs().next())
93            .map(Record::data)
94            .and_then(RData::as_soa)
95            .map(SOA::serial)
96            .ok_or_else(|| format!("SOA record must be present: {origin}"))?;
97
98        let iter = records.into_values();
99
100        // add soa to the records
101        for rrset in iter {
102            let name = rrset.name().clone();
103            let rr_type = rrset.record_type();
104
105            for record in rrset.records_without_rrsigs() {
106                if !inner.upsert(record.clone(), serial, this.class) {
107                    return Err(format!(
108                        "Failed to insert {name} {rr_type} to zone: {origin}"
109                    ));
110                };
111            }
112        }
113
114        Ok(this)
115    }
116
117    /// Creates an empty Authority
118    ///
119    /// # Warning
120    ///
121    /// This is an invalid zone, SOA must be added
122    pub fn empty(
123        origin: Name,
124        zone_type: ZoneType,
125        allow_axfr: bool,
126        #[cfg(feature = "__dnssec")] nx_proof_kind: Option<NxProofKind>,
127    ) -> Self {
128        Self {
129            origin: LowerName::new(&origin),
130            class: DNSClass::IN,
131            zone_type,
132            allow_axfr,
133            inner: RwLock::new(InnerInMemory::default()),
134
135            #[cfg(feature = "__dnssec")]
136            nx_proof_kind,
137        }
138    }
139
140    /// The DNSClass of this zone
141    pub fn class(&self) -> DNSClass {
142        self.class
143    }
144
145    /// Allow AXFR's (zone transfers)
146    #[cfg(any(test, feature = "testing"))]
147    pub fn set_allow_axfr(&mut self, allow_axfr: bool) {
148        self.allow_axfr = allow_axfr;
149    }
150
151    /// Clears all records (including SOA, etc)
152    pub fn clear(&mut self) {
153        self.inner.get_mut().records.clear()
154    }
155
156    /// Retrieve the Signer, which contains the private keys, for this zone
157    #[cfg(all(feature = "__dnssec", feature = "testing"))]
158    pub async fn secure_keys(&self) -> impl Deref<Target = [SigSigner]> + '_ {
159        RwLockWriteGuard::map(self.inner.write().await, |i| i.secure_keys.as_mut_slice())
160    }
161
162    /// Get all the records
163    pub async fn records(&self) -> BTreeMap<RrKey, Arc<RecordSet>> {
164        let records = RwLockReadGuard::map(self.inner.read().await, |i| &i.records);
165        records.clone()
166    }
167
168    /// Get a mutable reference to the records
169    pub async fn records_mut(
170        &self,
171    ) -> impl DerefMut<Target = BTreeMap<RrKey, Arc<RecordSet>>> + '_ {
172        RwLockWriteGuard::map(self.inner.write().await, |i| &mut i.records)
173    }
174
175    /// Get a mutable reference to the records
176    pub fn records_get_mut(&mut self) -> &mut BTreeMap<RrKey, Arc<RecordSet>> {
177        &mut self.inner.get_mut().records
178    }
179
180    /// Returns the minimum ttl (as used in the SOA record)
181    pub async fn minimum_ttl(&self) -> u32 {
182        self.inner.read().await.minimum_ttl(self.origin())
183    }
184
185    /// get the current serial number for the zone.
186    pub async fn serial(&self) -> u32 {
187        self.inner.read().await.serial(self.origin())
188    }
189
190    #[cfg(any(feature = "__dnssec", feature = "sqlite"))]
191    #[allow(unused)]
192    pub(crate) async fn increment_soa_serial(&self) -> u32 {
193        self.inner
194            .write()
195            .await
196            .increment_soa_serial(self.origin(), self.class)
197    }
198
199    /// Inserts or updates a `Record` depending on it's existence in the authority.
200    ///
201    /// Guarantees that SOA, CNAME only has one record, will implicitly update if they already exist.
202    ///
203    /// # Arguments
204    ///
205    /// * `record` - The `Record` to be inserted or updated.
206    /// * `serial` - Current serial number to be recorded against updates.
207    ///
208    /// # Return value
209    ///
210    /// true if the value was inserted, false otherwise
211    pub async fn upsert(&self, record: Record, serial: u32) -> bool {
212        self.inner.write().await.upsert(record, serial, self.class)
213    }
214
215    /// Non-async version of upsert when behind a mutable reference.
216    pub fn upsert_mut(&mut self, record: Record, serial: u32) -> bool {
217        self.inner.get_mut().upsert(record, serial, self.class)
218    }
219
220    /// Add a (Sig0) key that is authorized to perform updates against this authority
221    #[cfg(feature = "__dnssec")]
222    fn inner_add_update_auth_key(
223        inner: &mut InnerInMemory,
224
225        name: Name,
226        key: KEY,
227        origin: &LowerName,
228        dns_class: DNSClass,
229    ) -> DnsSecResult<()> {
230        let rdata = RData::DNSSEC(DNSSECRData::KEY(key));
231        // TODO: what TTL?
232        let record = Record::from_rdata(name, 86400, rdata);
233
234        let serial = inner.serial(origin);
235        if inner.upsert(record, serial, dns_class) {
236            Ok(())
237        } else {
238            Err("failed to add auth key".into())
239        }
240    }
241
242    /// Non-async method of add_update_auth_key when behind a mutable reference
243    #[cfg(feature = "__dnssec")]
244    pub fn add_update_auth_key_mut(&mut self, name: Name, key: KEY) -> DnsSecResult<()> {
245        let Self {
246            origin,
247            inner,
248            class,
249            ..
250        } = self;
251
252        Self::inner_add_update_auth_key(inner.get_mut(), name, key, origin, *class)
253    }
254
255    /// By adding a secure key, this will implicitly enable dnssec for the zone.
256    ///
257    /// # Arguments
258    ///
259    /// * `signer` - Signer with associated private key
260    #[cfg(feature = "__dnssec")]
261    fn inner_add_zone_signing_key(
262        inner: &mut InnerInMemory,
263        signer: SigSigner,
264        origin: &LowerName,
265        dns_class: DNSClass,
266    ) -> DnsSecResult<()> {
267        // also add the key to the zone
268        let zone_ttl = inner.minimum_ttl(origin);
269        let dnskey = DNSKEY::from_key(&signer.key().to_public_key()?);
270        let dnskey = Record::from_rdata(
271            origin.clone().into(),
272            zone_ttl,
273            RData::DNSSEC(DNSSECRData::DNSKEY(dnskey)),
274        );
275
276        // TODO: also generate the CDS and CDNSKEY
277        let serial = inner.serial(origin);
278        inner.upsert(dnskey, serial, dns_class);
279        inner.secure_keys.push(signer);
280        Ok(())
281    }
282
283    /// Non-async method of add_zone_signing_key when behind a mutable reference
284    #[cfg(feature = "__dnssec")]
285    pub fn add_zone_signing_key_mut(&mut self, signer: SigSigner) -> DnsSecResult<()> {
286        let Self {
287            origin,
288            inner,
289            class,
290            ..
291        } = self;
292
293        Self::inner_add_zone_signing_key(inner.get_mut(), signer, origin, *class)
294    }
295
296    /// (Re)generates the nsec records, increments the serial number and signs the zone
297    #[cfg(feature = "__dnssec")]
298    pub fn secure_zone_mut(&mut self) -> DnsSecResult<()> {
299        let Self { origin, inner, .. } = self;
300        inner
301            .get_mut()
302            .secure_zone_mut(origin, self.class, self.nx_proof_kind.as_ref())
303    }
304
305    /// (Re)generates the nsec records, increments the serial number and signs the zone
306    #[cfg(not(feature = "__dnssec"))]
307    pub fn secure_zone_mut(&mut self) -> Result<(), &str> {
308        Err("DNSSEC was not enabled during compilation.")
309    }
310}
311
312#[async_trait::async_trait]
313impl Authority for InMemoryAuthority {
314    type Lookup = AuthLookup;
315
316    /// What type is this zone
317    fn zone_type(&self) -> ZoneType {
318        self.zone_type
319    }
320
321    /// Return true if AXFR is allowed
322    fn is_axfr_allowed(&self) -> bool {
323        self.allow_axfr
324    }
325
326    /// Takes the UpdateMessage, extracts the Records, and applies the changes to the record set.
327    ///
328    /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
329    ///
330    /// ```text
331    ///
332    /// 3.4 - Process Update Section
333    ///
334    ///   Next, the Update Section is processed as follows.
335    ///
336    /// 3.4.2 - Update
337    ///
338    ///   The Update Section is parsed into RRs and these RRs are processed in
339    ///   order.
340    ///
341    /// 3.4.2.1. If any system failure (such as an out of memory condition,
342    ///   or a hardware error in persistent storage) occurs during the
343    ///   processing of this section, signal SERVFAIL to the requestor and undo
344    ///   all updates applied to the zone during this transaction.
345    ///
346    /// 3.4.2.2. Any Update RR whose CLASS is the same as ZCLASS is added to
347    ///   the zone.  In case of duplicate RDATAs (which for SOA RRs is always
348    ///   the case, and for WKS RRs is the case if the ADDRESS and PROTOCOL
349    ///   fields both match), the Zone RR is replaced by Update RR.  If the
350    ///   TYPE is SOA and there is no Zone SOA RR, or the new SOA.SERIAL is
351    ///   lower (according to [RFC1982]) than or equal to the current Zone SOA
352    ///   RR's SOA.SERIAL, the Update RR is ignored.  In the case of a CNAME
353    ///   Update RR and a non-CNAME Zone RRset or vice versa, ignore the CNAME
354    ///   Update RR, otherwise replace the CNAME Zone RR with the CNAME Update
355    ///   RR.
356    ///
357    /// 3.4.2.3. For any Update RR whose CLASS is ANY and whose TYPE is ANY,
358    ///   all Zone RRs with the same NAME are deleted, unless the NAME is the
359    ///   same as ZNAME in which case only those RRs whose TYPE is other than
360    ///   SOA or NS are deleted.  For any Update RR whose CLASS is ANY and
361    ///   whose TYPE is not ANY all Zone RRs with the same NAME and TYPE are
362    ///   deleted, unless the NAME is the same as ZNAME in which case neither
363    ///   SOA or NS RRs will be deleted.
364    ///
365    /// 3.4.2.4. For any Update RR whose class is NONE, any Zone RR whose
366    ///   NAME, TYPE, RDATA and RDLENGTH are equal to the Update RR is deleted,
367    ///   unless the NAME is the same as ZNAME and either the TYPE is SOA or
368    ///   the TYPE is NS and the matching Zone RR is the only NS remaining in
369    ///   the RRset, in which case this Update RR is ignored.
370    ///
371    /// 3.4.2.5. Signal NOERROR to the requestor.
372    /// ```
373    ///
374    /// # Arguments
375    ///
376    /// * `update` - The `UpdateMessage` records will be extracted and used to perform the update
377    ///              actions as specified in the above RFC.
378    ///
379    /// # Return value
380    ///
381    /// true if any of additions, updates or deletes were made to the zone, false otherwise. Err is
382    ///  returned in the case of bad data, etc.
383    async fn update(&self, _update: &MessageRequest) -> UpdateResult<bool> {
384        Err(ResponseCode::NotImp)
385    }
386
387    /// Get the origin of this zone, i.e. example.com is the origin for www.example.com
388    fn origin(&self) -> &LowerName {
389        &self.origin
390    }
391
392    /// Looks up all Resource Records matching the given `Name` and `RecordType`.
393    ///
394    /// # Arguments
395    ///
396    /// * `name` - The name to look up.
397    /// * `query_type` - The `RecordType` to look up. `RecordType::ANY` will return all records
398    ///                  matching `name`. `RecordType::AXFR` will return all record types except
399    ///                  `RecordType::SOA` due to the requirements that on zone transfers the
400    ///                  `RecordType::SOA` must both precede and follow all other records.
401    /// * `lookup_options` - Query-related lookup options (e.g., DNSSEC DO bit, supported hash
402    ///                      algorithms, etc.)
403    ///
404    /// # Return value
405    ///
406    /// A LookupControlFlow containing the lookup that should be returned to the client.
407    async fn lookup(
408        &self,
409        name: &LowerName,
410        query_type: RecordType,
411        lookup_options: LookupOptions,
412    ) -> LookupControlFlow<Self::Lookup> {
413        let inner = self.inner.read().await;
414
415        // Collect the records from each rr_set
416        let (result, additionals): (LookupControlFlow<LookupRecords, _>, Option<LookupRecords>) =
417            match query_type {
418                RecordType::AXFR | RecordType::ANY => {
419                    let result = AnyRecords::new(
420                        lookup_options,
421                        inner.records.values().cloned().collect(),
422                        query_type,
423                        name.clone(),
424                    );
425                    (
426                        LookupControlFlow::Continue(Ok(LookupRecords::AnyRecords(result))),
427                        None,
428                    )
429                }
430                _ => {
431                    // perform the lookup
432                    let answer = inner.inner_lookup(name, query_type, lookup_options);
433
434                    // evaluate any cnames for additional inclusion
435                    let additionals_root_chain_type: Option<(_, _)> = answer
436                        .as_ref()
437                        .and_then(|a| maybe_next_name(a, query_type))
438                        .and_then(|(search_name, search_type)| {
439                            inner
440                                .additional_search(
441                                    name,
442                                    query_type,
443                                    search_name,
444                                    search_type,
445                                    lookup_options,
446                                )
447                                .map(|adds| (adds, search_type))
448                        });
449
450                    // if the chain started with an ANAME, take the A or AAAA record from the list
451                    let (additionals, answer) =
452                        match (additionals_root_chain_type, answer, query_type) {
453                            (
454                                Some((additionals, RecordType::ANAME)),
455                                Some(answer),
456                                RecordType::A,
457                            )
458                            | (
459                                Some((additionals, RecordType::ANAME)),
460                                Some(answer),
461                                RecordType::AAAA,
462                            ) => {
463                                // This should always be true...
464                                debug_assert_eq!(answer.record_type(), RecordType::ANAME);
465
466                                // in the case of ANAME the final record should be the A or AAAA record
467                                let (rdatas, a_aaaa_ttl) = {
468                                    let last_record = additionals.last();
469                                    let a_aaaa_ttl = last_record.map_or(u32::MAX, |r| r.ttl());
470
471                                    // grap the rdatas
472                                    let rdatas: Option<Vec<RData>> = last_record
473                                        .and_then(|record| match record.record_type() {
474                                            RecordType::A | RecordType::AAAA => {
475                                                // the RRSIGS will be useless since we're changing the record type
476                                                Some(record.records_without_rrsigs())
477                                            }
478                                            _ => None,
479                                        })
480                                        .map(|records| {
481                                            records.map(Record::data).cloned().collect::<Vec<_>>()
482                                        });
483
484                                    (rdatas, a_aaaa_ttl)
485                                };
486
487                                // now build up a new RecordSet
488                                //   the name comes from the ANAME record
489                                //   according to the rfc the ttl is from the ANAME
490                                //   TODO: technically we should take the min of the potential CNAME chain
491                                let ttl = answer.ttl().min(a_aaaa_ttl);
492                                let mut new_answer =
493                                    RecordSet::new(answer.name().clone(), query_type, ttl);
494
495                                for rdata in rdatas.into_iter().flatten() {
496                                    new_answer.add_rdata(rdata);
497                                }
498
499                                // if DNSSEC is enabled, and the request had the DO set, sign the recordset
500                                #[cfg(feature = "__dnssec")]
501                                // ANAME's are constructed on demand, so need to be signed before return
502                                if lookup_options.dnssec_ok() {
503                                    InnerInMemory::sign_rrset(
504                                        &mut new_answer,
505                                        &inner.secure_keys,
506                                        inner.minimum_ttl(self.origin()),
507                                        self.class(),
508                                    )
509                                    // rather than failing the request, we'll just warn
510                                    .map_err(|e| warn!("failed to sign ANAME record: {}", e))
511                                    .ok();
512                                }
513
514                                // prepend answer to additionals here (answer is the ANAME record)
515                                let additionals =
516                                    std::iter::once(answer).chain(additionals).collect();
517
518                                // return the new answer
519                                //   because the searched set was an Arc, we need to arc too
520                                (Some(additionals), Some(Arc::new(new_answer)))
521                            }
522                            (Some((additionals, _)), answer, _) => (Some(additionals), answer),
523                            (None, answer, _) => (None, answer),
524                        };
525
526                    // map the answer to a result
527                    let answer = answer.map_or(
528                        LookupControlFlow::Continue(Err(LookupError::from(ResponseCode::NXDomain))),
529                        |rr_set| {
530                            LookupControlFlow::Continue(Ok(LookupRecords::new(
531                                lookup_options,
532                                rr_set,
533                            )))
534                        },
535                    );
536
537                    let additionals = additionals.map(|a| LookupRecords::many(lookup_options, a));
538
539                    (answer, additionals)
540                }
541            };
542
543        // This is annoying. The 1035 spec literally specifies that most DNS authorities would want to store
544        //   records in a list except when there are a lot of records. But this makes indexed lookups by name+type
545        //   always return empty sets. This is only important in the negative case, where other DNS authorities
546        //   generally return NoError and no results when other types exist at the same name. bah.
547        // TODO: can we get rid of this?
548        use LookupControlFlow::*;
549        let result = match result {
550            Continue(Err(LookupError::ResponseCode(ResponseCode::NXDomain))) => {
551                if inner
552                    .records
553                    .keys()
554                    .any(|key| key.name() == name || name.zone_of(key.name()))
555                {
556                    return Continue(Err(LookupError::NameExists));
557                } else {
558                    let code = if self.origin().zone_of(name) {
559                        ResponseCode::NXDomain
560                    } else {
561                        ResponseCode::Refused
562                    };
563                    return Continue(Err(LookupError::from(code)));
564                }
565            }
566            Continue(Err(e)) => return Continue(Err(e)),
567            o => o,
568        };
569
570        result.map(|answers| AuthLookup::answers(answers, additionals))
571    }
572
573    async fn search(
574        &self,
575        request_info: RequestInfo<'_>,
576        lookup_options: LookupOptions,
577    ) -> LookupControlFlow<Self::Lookup> {
578        debug!("searching InMemoryAuthority for: {}", request_info.query);
579
580        let lookup_name = request_info.query.name();
581        let record_type: RecordType = request_info.query.query_type();
582
583        // if this is an AXFR zone transfer, verify that this is either the Secondary or Primary
584        //  for AXFR the first and last record must be the SOA
585        if RecordType::AXFR == record_type {
586            // TODO: support more advanced AXFR options
587            if !self.is_axfr_allowed() {
588                return LookupControlFlow::Continue(Err(LookupError::from(ResponseCode::Refused)));
589            }
590
591            #[allow(deprecated)]
592            match self.zone_type() {
593                ZoneType::Primary | ZoneType::Secondary | ZoneType::Master | ZoneType::Slave => (),
594                // TODO: Forward?
595                _ => {
596                    return LookupControlFlow::Continue(Err(LookupError::from(
597                        ResponseCode::NXDomain,
598                    )));
599                }
600            }
601        }
602
603        // perform the actual lookup
604        match record_type {
605            RecordType::SOA => {
606                self.lookup(self.origin(), record_type, lookup_options)
607                    .await
608            }
609            RecordType::AXFR => {
610                // TODO: shouldn't these SOA's be secure? at least the first, perhaps not the last?
611                use LookupControlFlow::Continue;
612                let start_soa = if let Continue(Ok(res)) = self.soa_secure(lookup_options).await {
613                    res.unwrap_records()
614                } else {
615                    LookupRecords::Empty
616                };
617                let end_soa = if let Continue(Ok(res)) = self.soa().await {
618                    res.unwrap_records()
619                } else {
620                    LookupRecords::Empty
621                };
622
623                let records = if let Continue(Ok(res)) =
624                    self.lookup(lookup_name, record_type, lookup_options).await
625                {
626                    res.unwrap_records()
627                } else {
628                    LookupRecords::Empty
629                };
630
631                LookupControlFlow::Continue(Ok(AuthLookup::AXFR {
632                    start_soa,
633                    end_soa,
634                    records,
635                }))
636            }
637            // A standard Lookup path
638            _ => self.lookup(lookup_name, record_type, lookup_options).await,
639        }
640    }
641
642    /// Return the NSEC records based on the given name
643    ///
644    /// # Arguments
645    ///
646    /// * `name` - given this name (i.e. the lookup name), return the NSEC record that is less than
647    ///            this
648    /// * `lookup_options` - Query-related lookup options (e.g., DNSSEC DO bit, supported hash
649    ///                      algorithms, etc.)
650    #[cfg(feature = "__dnssec")]
651    async fn get_nsec_records(
652        &self,
653        name: &LowerName,
654        lookup_options: LookupOptions,
655    ) -> LookupControlFlow<Self::Lookup> {
656        let inner = self.inner.read().await;
657
658        // TODO: need a BorrowdRrKey
659        let rr_key = RrKey::new(name.clone(), RecordType::NSEC);
660        let no_data = inner
661            .records
662            .get(&rr_key)
663            .map(|rr_set| LookupRecords::new(lookup_options, rr_set.clone()));
664
665        if let Some(no_data) = no_data {
666            return LookupControlFlow::Continue(Ok(no_data.into()));
667        }
668
669        let closest_proof = inner.closest_nsec(name);
670
671        // we need the wildcard proof, but make sure that it's still part of the zone.
672        let wildcard = name.base_name();
673        let origin = self.origin();
674        let wildcard = if origin.zone_of(&wildcard) {
675            wildcard
676        } else {
677            origin.clone()
678        };
679
680        // don't duplicate the record...
681        let wildcard_proof = if wildcard != *name {
682            inner.closest_nsec(&wildcard)
683        } else {
684            None
685        };
686
687        let proofs = match (closest_proof, wildcard_proof) {
688            (Some(closest_proof), Some(wildcard_proof)) => {
689                // dedup with the wildcard proof
690                if wildcard_proof != closest_proof {
691                    vec![wildcard_proof, closest_proof]
692                } else {
693                    vec![closest_proof]
694                }
695            }
696            (None, Some(proof)) | (Some(proof), None) => vec![proof],
697            (None, None) => vec![],
698        };
699
700        LookupControlFlow::Continue(Ok(LookupRecords::many(lookup_options, proofs).into()))
701    }
702
703    #[cfg(not(feature = "__dnssec"))]
704    async fn get_nsec_records(
705        &self,
706        _name: &LowerName,
707        _lookup_options: LookupOptions,
708    ) -> LookupControlFlow<Self::Lookup> {
709        LookupControlFlow::Continue(Ok(AuthLookup::default()))
710    }
711
712    #[cfg(feature = "__dnssec")]
713    async fn get_nsec3_records(
714        &self,
715        info: Nsec3QueryInfo<'_>,
716        lookup_options: LookupOptions,
717    ) -> LookupControlFlow<Self::Lookup> {
718        let inner = self.inner.read().await;
719        LookupControlFlow::Continue(
720            inner
721                .proof(info, self.origin())
722                .map(|proof| LookupRecords::many(lookup_options, proof).into()),
723        )
724    }
725
726    #[cfg(feature = "__dnssec")]
727    fn nx_proof_kind(&self) -> Option<&NxProofKind> {
728        self.nx_proof_kind.as_ref()
729    }
730}
731
732#[cfg(feature = "__dnssec")]
733#[async_trait::async_trait]
734impl DnssecAuthority for InMemoryAuthority {
735    /// Add a (Sig0) key that is authorized to perform updates against this authority
736    async fn add_update_auth_key(&self, name: Name, key: KEY) -> DnsSecResult<()> {
737        let mut inner = self.inner.write().await;
738
739        Self::inner_add_update_auth_key(&mut inner, name, key, self.origin(), self.class)
740    }
741
742    /// By adding a secure key, this will implicitly enable dnssec for the zone.
743    ///
744    /// # Arguments
745    ///
746    /// * `signer` - Signer with associated private key
747    async fn add_zone_signing_key(&self, signer: SigSigner) -> DnsSecResult<()> {
748        let mut inner = self.inner.write().await;
749
750        Self::inner_add_zone_signing_key(&mut inner, signer, self.origin(), self.class)
751    }
752
753    /// Sign the zone for DNSSEC
754    async fn secure_zone(&self) -> DnsSecResult<()> {
755        let mut inner = self.inner.write().await;
756
757        inner.secure_zone_mut(self.origin(), self.class, self.nx_proof_kind.as_ref())
758    }
759}
760
761/// Gets the next search name, and returns the RecordType that it originated from
762fn maybe_next_name(
763    record_set: &RecordSet,
764    query_type: RecordType,
765) -> Option<(LowerName, RecordType)> {
766    match (record_set.record_type(), query_type) {
767        // ANAME is similar to CNAME,
768        //  unlike CNAME, it is only something that continue to additional processing if the
769        //  the query was for address (A, AAAA, or ANAME itself) record types.
770        (t @ RecordType::ANAME, RecordType::A)
771        | (t @ RecordType::ANAME, RecordType::AAAA)
772        | (t @ RecordType::ANAME, RecordType::ANAME) => record_set
773            .records_without_rrsigs()
774            .next()
775            .map(Record::data)
776            .and_then(RData::as_aname)
777            .map(|aname| LowerName::from(&aname.0))
778            .map(|name| (name, t)),
779        (t @ RecordType::NS, RecordType::NS) => record_set
780            .records_without_rrsigs()
781            .next()
782            .map(Record::data)
783            .and_then(RData::as_ns)
784            .map(|ns| LowerName::from(&ns.0))
785            .map(|name| (name, t)),
786        // CNAME will continue to additional processing for any query type
787        (t @ RecordType::CNAME, _) => record_set
788            .records_without_rrsigs()
789            .next()
790            .map(Record::data)
791            .and_then(RData::as_cname)
792            .map(|cname| LowerName::from(&cname.0))
793            .map(|name| (name, t)),
794        (t @ RecordType::MX, RecordType::MX) => record_set
795            .records_without_rrsigs()
796            .next()
797            .map(Record::data)
798            .and_then(RData::as_mx)
799            .map(|mx| mx.exchange().clone())
800            .map(LowerName::from)
801            .map(|name| (name, t)),
802        (t @ RecordType::SRV, RecordType::SRV) => record_set
803            .records_without_rrsigs()
804            .next()
805            .map(Record::data)
806            .and_then(RData::as_srv)
807            .map(|srv| srv.target().clone())
808            .map(LowerName::from)
809            .map(|name| (name, t)),
810        // other additional collectors can be added here can be added here
811        _ => None,
812    }
813}