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}