hickory_server/store/sqlite/mod.rs
1// Copyright 2015-2018 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//! SQLite serving with Dynamic DNS and journaling support
9
10#[cfg(feature = "__dnssec")]
11use std::fs;
12use std::marker::PhantomData;
13#[cfg(feature = "__dnssec")]
14use std::str::FromStr;
15use std::{
16 ops::{Deref, DerefMut},
17 path::{Path, PathBuf},
18 sync::Arc,
19};
20
21use futures_util::lock::Mutex;
22use serde::Deserialize;
23use tracing::{debug, error, info, warn};
24
25#[cfg(feature = "metrics")]
26use crate::metrics::PersistentStoreMetrics;
27#[cfg(feature = "__dnssec")]
28use crate::proto::rr::{
29 TSigner,
30 rdata::tsig::{TSIG, TsigAlgorithm, TsigError},
31};
32#[cfg(feature = "__dnssec")]
33use crate::{
34 dnssec::NxProofKind,
35 proto::dnssec::{DnsSecResult, DnssecSigner},
36 zone_handler::{DnssecZoneHandler, Nsec3QueryInfo, UpdateRequest},
37};
38use crate::{
39 net::runtime::{RuntimeProvider, TokioRuntimeProvider},
40 proto::{
41 op::ResponseCode,
42 rr::{
43 DNSClass, LowerName, Name, RData, Record, RecordSet, RecordType, RrKey,
44 TSigResponseContext,
45 },
46 },
47 server::{Request, RequestInfo},
48 store::{
49 file::rooted,
50 in_memory::{InMemoryZoneHandler, zone_from_path},
51 },
52 zone_handler::{
53 AuthLookup, AxfrPolicy, LookupControlFlow, LookupError, LookupOptions, ZoneHandler,
54 ZoneTransfer, ZoneType,
55 },
56};
57
58pub mod persistence;
59pub use persistence::{Journal, PersistenceError};
60
61/// SqliteZoneHandler is responsible for storing the resource records for a particular zone.
62///
63/// Zone handlers default to DNSClass IN. The ZoneType specifies if this should be treated as the
64/// start of authority for the zone, is a Secondary, or a cached zone.
65#[allow(dead_code)]
66pub struct SqliteZoneHandler<P = TokioRuntimeProvider> {
67 in_memory: InMemoryZoneHandler<P>,
68 journal: Mutex<Option<Journal>>,
69 axfr_policy: AxfrPolicy,
70 allow_update: bool,
71 is_dnssec_enabled: bool,
72 #[cfg(feature = "metrics")]
73 metrics: PersistentStoreMetrics,
74 #[cfg(feature = "__dnssec")]
75 tsig_signers: Vec<TSigner>,
76 _phantom: PhantomData<P>,
77}
78
79impl<P: RuntimeProvider + Send + Sync> SqliteZoneHandler<P> {
80 /// Creates a new ZoneHandler.
81 ///
82 /// # Arguments
83 ///
84 /// * `in_memory` - InMemoryZoneHandler for all records.
85 /// * `axfr_policy` - A policy for determining if AXFR requests are allowed.
86 /// * `allow_update` - If true, then this zone accepts dynamic updates.
87 /// * `is_dnssec_enabled` - If true, then the zone will sign the zone with all registered keys,
88 /// (see `add_zone_signing_key()`)
89 ///
90 /// # Return value
91 ///
92 /// The new `ZoneHandler`.
93 pub fn new(
94 in_memory: InMemoryZoneHandler<P>,
95 axfr_policy: AxfrPolicy,
96 allow_update: bool,
97 is_dnssec_enabled: bool,
98 ) -> Self {
99 Self {
100 in_memory,
101 journal: Mutex::new(None),
102 axfr_policy,
103 allow_update,
104 is_dnssec_enabled,
105 #[cfg(feature = "metrics")]
106 metrics: PersistentStoreMetrics::new("sqlite"),
107 #[cfg(feature = "__dnssec")]
108 tsig_signers: Vec::new(),
109 _phantom: PhantomData,
110 }
111 }
112
113 /// load the zone handler from the configuration
114 pub async fn try_from_config(
115 origin: Name,
116 zone_type: ZoneType,
117 axfr_policy: AxfrPolicy,
118 enable_dnssec: bool,
119 root_dir: Option<&Path>,
120 config: &SqliteConfig,
121 #[cfg(feature = "__dnssec")] nx_proof_kind: Option<NxProofKind>,
122 ) -> Result<Self, String> {
123 let zone_name = origin;
124
125 // to be compatible with previous versions, the extension might be zone, not jrnl
126 let zone_path = rooted(&config.zone_path, root_dir);
127 let journal_path = rooted(&config.journal_path, root_dir);
128
129 #[cfg_attr(not(feature = "__dnssec"), allow(unused_mut))]
130 let mut handler = if journal_path.exists() {
131 // load the zone
132 info!("recovering zone from journal: {journal_path:?}",);
133 let journal = Journal::from_file(&journal_path)
134 .map_err(|e| format!("error opening journal: {journal_path:?}: {e}"))?;
135
136 let in_memory = InMemoryZoneHandler::empty(
137 zone_name.clone(),
138 zone_type,
139 AxfrPolicy::AllowAll, // We apply our own AXFR policy before invoking the InMemoryZoneHandler.
140 #[cfg(feature = "__dnssec")]
141 nx_proof_kind,
142 );
143 let mut handler = Self::new(in_memory, axfr_policy, config.allow_update, enable_dnssec);
144
145 handler
146 .recover_with_journal(&journal)
147 .await
148 .map_err(|e| format!("error recovering from journal: {e}"))?;
149
150 handler.set_journal(journal).await;
151 info!("recovered zone: {zone_name}");
152
153 handler
154 } else if zone_path.exists() {
155 // TODO: deprecate this portion of loading, instantiate the journal through a separate tool
156 info!("loading zone file: {zone_path:?}");
157
158 let records = zone_from_path(&zone_path, zone_name.clone())
159 .map_err(|e| format!("failed to load zone file: {e}"))?;
160
161 let in_memory = InMemoryZoneHandler::new(
162 zone_name.clone(),
163 records,
164 zone_type,
165 AxfrPolicy::AllowAll, // We apply our own AXFR policy before invoking the InMemoryZoneHandler.
166 #[cfg(feature = "__dnssec")]
167 nx_proof_kind,
168 )?;
169
170 let mut handler = Self::new(in_memory, axfr_policy, config.allow_update, enable_dnssec);
171
172 // if dynamic update is enabled, enable the journal
173 info!("creating new journal: {journal_path:?}");
174 let journal = Journal::from_file(&journal_path)
175 .map_err(|e| format!("error creating journal {journal_path:?}: {e}"))?;
176
177 handler.set_journal(journal).await;
178
179 // preserve to the new journal, i.e. we just loaded the zone from disk, start the journal
180 handler
181 .persist_to_journal()
182 .await
183 .map_err(|e| format!("error persisting to journal {journal_path:?}: {e}"))?;
184
185 info!("zone file loaded: {zone_name}");
186 handler
187 } else {
188 return Err(format!("no zone file or journal defined at: {zone_path:?}"));
189 };
190
191 #[cfg(feature = "__dnssec")]
192 for config in &config.tsig_keys {
193 handler
194 .tsig_signers
195 .push(config.to_signer(&zone_name, root_dir)?);
196 }
197
198 Ok(handler)
199 }
200
201 /// Recovers the zone from a Journal, returns an error on failure to recover the zone.
202 ///
203 /// # Arguments
204 ///
205 /// * `journal` - the journal from which to load the persisted zone.
206 pub async fn recover_with_journal(
207 &mut self,
208 journal: &Journal,
209 ) -> Result<(), PersistenceError> {
210 assert!(
211 self.in_memory.records_get_mut().is_empty(),
212 "records should be empty during a recovery"
213 );
214
215 info!("recovering from journal");
216 for record in journal.iter() {
217 // AXFR is special, it is used to mark the dump of a full zone.
218 // when recovering, if an AXFR is encountered, we should remove all the records in the
219 // zone.
220 if record.record_type() == RecordType::AXFR {
221 self.in_memory.clear();
222 } else {
223 match self.update_records(&[record], false).await {
224 Ok(_) => {
225 #[cfg(feature = "metrics")]
226 self.metrics.zone_records.increment(1);
227 }
228 Err(error) => return Err(PersistenceError::Recovery(error.to_str())),
229 }
230 }
231 }
232
233 Ok(())
234 }
235
236 /// Persist the state of the current zone to the journal, does nothing if there is no associated
237 /// Journal.
238 ///
239 /// Returns an error if there was an issue writing to the persistence layer.
240 pub async fn persist_to_journal(&self) -> Result<(), PersistenceError> {
241 if let Some(journal) = self.journal.lock().await.as_ref() {
242 let serial = self.in_memory.serial().await;
243
244 info!("persisting zone to journal at SOA.serial: {serial}");
245
246 // TODO: THIS NEEDS TO BE IN A TRANSACTION!!!
247 journal.insert_record(
248 serial,
249 &Record::update0(Name::new(), 0, RecordType::AXFR).into_record_of_rdata(),
250 )?;
251
252 for rr_set in self.in_memory.records().await.values() {
253 // TODO: should we preserve rr_sets or not?
254 for record in rr_set.records_without_rrsigs() {
255 journal.insert_record(serial, record)?;
256
257 #[cfg(feature = "metrics")]
258 self.metrics.zone_records.increment(1);
259 }
260 }
261
262 // TODO: COMMIT THE TRANSACTION!!!
263 }
264
265 Ok(())
266 }
267
268 /// Associate a backing Journal with this ZoneHandler for Updatable zones
269 pub async fn set_journal(&mut self, journal: Journal) {
270 *self.journal.lock().await = Some(journal);
271 }
272
273 /// Returns the associated Journal
274 #[cfg(any(test, feature = "testing"))]
275 pub async fn journal(&self) -> impl Deref<Target = Option<Journal>> + '_ {
276 self.journal.lock().await
277 }
278
279 /// Enables the zone for dynamic DNS updates
280 pub fn set_allow_update(&mut self, allow_update: bool) {
281 self.allow_update = allow_update;
282 }
283
284 /// Set the TSIG signers allowed to authenticate updates when `allow_update` is true
285 #[cfg(all(any(test, feature = "testing"), feature = "__dnssec"))]
286 pub fn set_tsig_signers(&mut self, signers: Vec<TSigner>) {
287 self.tsig_signers = signers;
288 }
289
290 /// Set the AXFR policy for testing purposes
291 #[cfg(feature = "testing")]
292 pub fn set_axfr_policy(&mut self, policy: AxfrPolicy) {
293 self.axfr_policy = policy;
294 }
295
296 /// Get serial
297 #[cfg(any(test, feature = "testing"))]
298 pub async fn serial(&self) -> u32 {
299 self.in_memory.serial().await
300 }
301
302 /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
303 ///
304 /// ```text
305 ///
306 /// 3.2 - Process Prerequisite Section
307 ///
308 /// Next, the Prerequisite Section is checked to see that all
309 /// prerequisites are satisfied by the current state of the zone. Using
310 /// the definitions expressed in Section 1.2, if any RR's NAME is not
311 /// within the zone specified in the Zone Section, signal NOTZONE to the
312 /// requestor.
313 ///
314 /// 3.2.1. For RRs in this section whose CLASS is ANY, test to see that
315 /// TTL and RDLENGTH are both zero (0), else signal FORMERR to the
316 /// requestor. If TYPE is ANY, test to see that there is at least one RR
317 /// in the zone whose NAME is the same as that of the Prerequisite RR,
318 /// else signal NXDOMAIN to the requestor. If TYPE is not ANY, test to
319 /// see that there is at least one RR in the zone whose NAME and TYPE are
320 /// the same as that of the Prerequisite RR, else signal NXRRSET to the
321 /// requestor.
322 ///
323 /// 3.2.2. For RRs in this section whose CLASS is NONE, test to see that
324 /// the TTL and RDLENGTH are both zero (0), else signal FORMERR to the
325 /// requestor. If the TYPE is ANY, test to see that there are no RRs in
326 /// the zone whose NAME is the same as that of the Prerequisite RR, else
327 /// signal YXDOMAIN to the requestor. If the TYPE is not ANY, test to
328 /// see that there are no RRs in the zone whose NAME and TYPE are the
329 /// same as that of the Prerequisite RR, else signal YXRRSET to the
330 /// requestor.
331 ///
332 /// 3.2.3. For RRs in this section whose CLASS is the same as the ZCLASS,
333 /// test to see that the TTL is zero (0), else signal FORMERR to the
334 /// requestor. Then, build an RRset for each unique <NAME,TYPE> and
335 /// compare each resulting RRset for set equality (same members, no more,
336 /// no less) with RRsets in the zone. If any Prerequisite RRset is not
337 /// entirely and exactly matched by a zone RRset, signal NXRRSET to the
338 /// requestor. If any RR in this section has a CLASS other than ZCLASS
339 /// or NONE or ANY, signal FORMERR to the requestor.
340 ///
341 /// 3.2.4 - Table Of Metavalues Used In Prerequisite Section
342 ///
343 /// CLASS TYPE RDATA Meaning
344 /// ------------------------------------------------------------
345 /// ANY ANY empty Name is in use
346 /// ANY rrset empty RRset exists (value independent)
347 /// NONE ANY empty Name is not in use
348 /// NONE rrset empty RRset does not exist
349 /// zone rrset rr RRset exists (value dependent)
350 /// ```
351 pub async fn verify_prerequisites(
352 &self,
353 pre_requisites: &[Record],
354 ) -> Result<(), ResponseCode> {
355 // 3.2.5 - Pseudocode for Prerequisite Section Processing
356 //
357 // for rr in prerequisites
358 // if (rr.ttl != 0)
359 // return (FORMERR)
360 // if (zone_of(rr.name) != ZNAME)
361 // return (NOTZONE);
362 // if (rr.class == ANY)
363 // if (rr.rdlength != 0)
364 // return (FORMERR)
365 // if (rr.type == ANY)
366 // if (!zone_name<rr.name>)
367 // return (NXDOMAIN)
368 // else
369 // if (!zone_rrset<rr.name, rr.type>)
370 // return (NXRRSET)
371 // if (rr.class == NONE)
372 // if (rr.rdlength != 0)
373 // return (FORMERR)
374 // if (rr.type == ANY)
375 // if (zone_name<rr.name>)
376 // return (YXDOMAIN)
377 // else
378 // if (zone_rrset<rr.name, rr.type>)
379 // return (YXRRSET)
380 // if (rr.class == zclass)
381 // temp<rr.name, rr.type> += rr
382 // else
383 // return (FORMERR)
384 //
385 // for rrset in temp
386 // if (zone_rrset<rrset.name, rrset.type> != rrset)
387 // return (NXRRSET)
388 for require in pre_requisites {
389 let required_name = LowerName::from(&require.name);
390
391 if require.ttl != 0 {
392 warn!("ttl must be 0 for: {require:?}");
393 return Err(ResponseCode::FormErr);
394 }
395
396 let origin = self.origin();
397 if !origin.zone_of(&(&require.name).into()) {
398 warn!("{} is not a zone_of {origin}", require.name);
399 return Err(ResponseCode::NotZone);
400 }
401
402 match require.dns_class {
403 DNSClass::ANY => {
404 if let RData::Update0(_) | RData::NULL(..) = require.data {
405 match require.record_type() {
406 // ANY ANY empty Name is in use
407 RecordType::ANY => {
408 if self
409 .lookup(
410 &required_name,
411 RecordType::ANY,
412 None,
413 LookupOptions::default(),
414 )
415 .await
416 .unwrap_or_default()
417 .was_empty()
418 {
419 return Err(ResponseCode::NXDomain);
420 } else {
421 continue;
422 }
423 }
424 // ANY rrset empty RRset exists (value independent)
425 rrset => {
426 if self
427 .lookup(&required_name, rrset, None, LookupOptions::default())
428 .await
429 .unwrap_or_default()
430 .was_empty()
431 {
432 return Err(ResponseCode::NXRRSet);
433 } else {
434 continue;
435 }
436 }
437 }
438 } else {
439 return Err(ResponseCode::FormErr);
440 }
441 }
442 DNSClass::NONE => {
443 if let RData::Update0(_) | RData::NULL(..) = require.data {
444 match require.record_type() {
445 // NONE ANY empty Name is not in use
446 RecordType::ANY => {
447 if !self
448 .lookup(
449 &required_name,
450 RecordType::ANY,
451 None,
452 LookupOptions::default(),
453 )
454 .await
455 .unwrap_or_default()
456 .was_empty()
457 {
458 return Err(ResponseCode::YXDomain);
459 } else {
460 continue;
461 }
462 }
463 // NONE rrset empty RRset does not exist
464 rrset => {
465 if !self
466 .lookup(&required_name, rrset, None, LookupOptions::default())
467 .await
468 .unwrap_or_default()
469 .was_empty()
470 {
471 return Err(ResponseCode::YXRRSet);
472 } else {
473 continue;
474 }
475 }
476 }
477 } else {
478 return Err(ResponseCode::FormErr);
479 }
480 }
481 class if class == self.in_memory.class() =>
482 // zone rrset rr RRset exists (value dependent)
483 {
484 if !self
485 .lookup(
486 &required_name,
487 require.record_type(),
488 None,
489 LookupOptions::default(),
490 )
491 .await
492 .unwrap_or_default()
493 .iter()
494 .any(|rr| rr == require)
495 {
496 return Err(ResponseCode::NXRRSet);
497 } else {
498 continue;
499 }
500 }
501 _ => return Err(ResponseCode::FormErr),
502 }
503 }
504
505 // if we didn't bail everything checked out...
506 Ok(())
507 }
508
509 /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
510 ///
511 /// ```text
512 ///
513 /// 3.3 - Check Requestor's Permissions
514 ///
515 /// 3.3.1. Next, the requestor's permission to update the RRs named in
516 /// the Update Section may be tested in an implementation dependent
517 /// fashion or using mechanisms specified in a subsequent Secure DNS
518 /// Update protocol. If the requestor does not have permission to
519 /// perform these updates, the server may write a warning message in its
520 /// operations log, and may either signal REFUSED to the requestor, or
521 /// ignore the permission problem and proceed with the update.
522 ///
523 /// 3.3.2. While the exact processing is implementation defined, if these
524 /// verification activities are to be performed, this is the point in the
525 /// server's processing where such performance should take place, since
526 /// if a REFUSED condition is encountered after an update has been
527 /// partially applied, it will be necessary to undo the partial update
528 /// and restore the zone to its original state before answering the
529 /// requestor.
530 /// ```
531 ///
532 #[cfg(feature = "__dnssec")]
533 pub async fn authorize_update(
534 &self,
535 request: &Request,
536 now: u64,
537 ) -> (Result<(), ResponseCode>, Option<TSigResponseContext>) {
538 // 3.3.3 - Pseudocode for Permission Checking
539 //
540 // if (security policy exists)
541 // if (this update is not permitted)
542 // if (local option)
543 // log a message about permission problem
544 // if (local option)
545 // return (REFUSED)
546
547 // does this zone handler allow_updates?
548 if !self.allow_update {
549 warn!(
550 "update attempted on non-updatable ZoneHandler: {}",
551 self.origin()
552 );
553 return (Err(ResponseCode::Refused), None);
554 }
555
556 match request.signature() {
557 Some(tsig) => {
558 let (resp, signer) = self.authorized_tsig(tsig, request, now).await;
559 (resp, Some(signer))
560 }
561 None => (Err(ResponseCode::Refused), None),
562 }
563 }
564
565 /// Checks that an AXFR `Request` has a valid signature, or returns an error
566 async fn authorize_axfr(
567 &self,
568 _request: &Request,
569 _now: u64,
570 ) -> (Result<(), ResponseCode>, Option<TSigResponseContext>) {
571 match self.axfr_policy {
572 // Deny without checking any signatures.
573 AxfrPolicy::Deny => (Err(ResponseCode::Refused), None),
574 // Allow without checking any signatures.
575 AxfrPolicy::AllowAll => (Ok(()), None),
576 // Allow only if a valid signature is present.
577 #[cfg(feature = "__dnssec")]
578 AxfrPolicy::AllowSigned => match _request.signature() {
579 Some(tsig) => {
580 let (resp, signer) = self.authorized_tsig(tsig, _request, _now).await;
581 (resp, Some(signer))
582 }
583 None => {
584 warn!("AXFR request was not signed");
585 (Err(ResponseCode::Refused), None)
586 }
587 },
588 }
589 }
590
591 /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
592 ///
593 /// ```text
594 ///
595 /// 3.4 - Process Update Section
596 ///
597 /// Next, the Update Section is processed as follows.
598 ///
599 /// 3.4.1 - Prescan
600 ///
601 /// The Update Section is parsed into RRs and each RR's CLASS is checked
602 /// to see if it is ANY, NONE, or the same as the Zone Class, else signal
603 /// a FORMERR to the requestor. Using the definitions in Section 1.2,
604 /// each RR's NAME must be in the zone specified by the Zone Section,
605 /// else signal NOTZONE to the requestor.
606 ///
607 /// 3.4.1.2. For RRs whose CLASS is not ANY, check the TYPE and if it is
608 /// ANY, AXFR, MAILA, MAILB, or any other QUERY metatype, or any
609 /// unrecognized type, then signal FORMERR to the requestor. For RRs
610 /// whose CLASS is ANY or NONE, check the TTL to see that it is zero (0),
611 /// else signal a FORMERR to the requestor. For any RR whose CLASS is
612 /// ANY, check the RDLENGTH to make sure that it is zero (0) (that is,
613 /// the RDATA field is empty), and that the TYPE is not AXFR, MAILA,
614 /// MAILB, or any other QUERY metatype besides ANY, or any unrecognized
615 /// type, else signal FORMERR to the requestor.
616 /// ```
617 pub async fn pre_scan(&self, records: &[Record]) -> Result<(), ResponseCode> {
618 // 3.4.1.3 - Pseudocode For Update Section Prescan
619 //
620 // [rr] for rr in updates
621 // if (zone_of(rr.name) != ZNAME)
622 // return (NOTZONE);
623 // if (rr.class == zclass)
624 // if (rr.type & ANY|AXFR|MAILA|MAILB)
625 // return (FORMERR)
626 // elsif (rr.class == ANY)
627 // if (rr.ttl != 0 || rr.rdlength != 0
628 // || rr.type & AXFR|MAILA|MAILB)
629 // return (FORMERR)
630 // elsif (rr.class == NONE)
631 // if (rr.ttl != 0 || rr.type & ANY|AXFR|MAILA|MAILB)
632 // return (FORMERR)
633 // else
634 // return (FORMERR)
635 for rr in records {
636 if !self.origin().zone_of(&(&rr.name).into()) {
637 return Err(ResponseCode::NotZone);
638 }
639
640 let class: DNSClass = rr.dns_class;
641 if class == self.in_memory.class() {
642 match rr.record_type() {
643 RecordType::ANY | RecordType::AXFR | RecordType::IXFR => {
644 return Err(ResponseCode::FormErr);
645 }
646 _ => (),
647 }
648 } else {
649 match class {
650 DNSClass::ANY => {
651 if rr.ttl != 0 {
652 return Err(ResponseCode::FormErr);
653 }
654
655 match rr.data {
656 RData::Update0(_) | RData::NULL(..) => {}
657 _ => return Err(ResponseCode::FormErr),
658 }
659
660 match rr.record_type() {
661 RecordType::AXFR | RecordType::IXFR => {
662 return Err(ResponseCode::FormErr);
663 }
664 _ => (),
665 }
666 }
667 DNSClass::NONE => {
668 if rr.ttl != 0 {
669 return Err(ResponseCode::FormErr);
670 }
671 match rr.record_type() {
672 RecordType::ANY | RecordType::AXFR | RecordType::IXFR => {
673 return Err(ResponseCode::FormErr);
674 }
675 _ => (),
676 }
677 }
678 _ => return Err(ResponseCode::FormErr),
679 }
680 }
681 }
682
683 Ok(())
684 }
685
686 /// Updates the specified records according to the update section.
687 ///
688 /// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
689 ///
690 /// ```text
691 ///
692 /// 3.4.2.6 - Table Of Metavalues Used In Update Section
693 ///
694 /// CLASS TYPE RDATA Meaning
695 /// ---------------------------------------------------------
696 /// ANY ANY empty Delete all RRsets from a name
697 /// ANY rrset empty Delete an RRset
698 /// NONE rrset rr Delete an RR from an RRset
699 /// zone rrset rr Add to an RRset
700 /// ```
701 ///
702 /// # Arguments
703 ///
704 /// * `records` - set of record instructions for update following above rules
705 /// * `auto_signing_and_increment` - if true, the zone will sign and increment the SOA, this
706 /// should be disabled during recovery.
707 pub async fn update_records(
708 &self,
709 records: &[Record],
710 auto_signing_and_increment: bool,
711 ) -> Result<bool, ResponseCode> {
712 let mut updated = false;
713 let serial: u32 = self.in_memory.serial().await;
714
715 // the persistence act as a write-ahead log. The WAL will also be used for recovery of a zone
716 // subsequent to a failure of the server.
717 if let Some(journal) = &*self.journal.lock().await {
718 if let Err(error) = journal.insert_records(serial, records) {
719 error!("could not persist update records: {error}");
720 return Err(ResponseCode::ServFail);
721 }
722 }
723
724 // 3.4.2.7 - Pseudocode For Update Section Processing
725 //
726 // [rr] for rr in updates
727 // if (rr.class == zclass)
728 // if (rr.type == CNAME)
729 // if (zone_rrset<rr.name, ~CNAME>)
730 // next [rr]
731 // elsif (zone_rrset<rr.name, CNAME>)
732 // next [rr]
733 // if (rr.type == SOA)
734 // if (!zone_rrset<rr.name, SOA> ||
735 // zone_rr<rr.name, SOA>.serial > rr.soa.serial)
736 // next [rr]
737 // for zrr in zone_rrset<rr.name, rr.type>
738 // if (rr.type == CNAME || rr.type == SOA ||
739 // (rr.type == WKS && rr.proto == zrr.proto &&
740 // rr.address == zrr.address) ||
741 // rr.rdata == zrr.rdata)
742 // zrr = rr
743 // next [rr]
744 // zone_rrset<rr.name, rr.type> += rr
745 // elsif (rr.class == ANY)
746 // if (rr.type == ANY)
747 // if (rr.name == zname)
748 // zone_rrset<rr.name, ~(SOA|NS)> = Nil
749 // else
750 // zone_rrset<rr.name, *> = Nil
751 // elsif (rr.name == zname &&
752 // (rr.type == SOA || rr.type == NS))
753 // next [rr]
754 // else
755 // zone_rrset<rr.name, rr.type> = Nil
756 // elsif (rr.class == NONE)
757 // if (rr.type == SOA)
758 // next [rr]
759 // if (rr.type == NS && zone_rrset<rr.name, NS> == rr)
760 // next [rr]
761 // zone_rr<rr.name, rr.type, rr.data> = Nil
762 // return (NOERROR)
763 for rr in records {
764 let rr_name = LowerName::from(&rr.name);
765 let rr_key = RrKey::new(rr_name.clone(), rr.record_type());
766
767 match rr.dns_class {
768 class if class == self.in_memory.class() => {
769 // RFC 2136 - 3.4.2.2. Any Update RR whose CLASS is the same as ZCLASS is added to
770 // the zone. In case of duplicate RDATAs (which for SOA RRs is always
771 // the case, and for WKS RRs is the case if the ADDRESS and PROTOCOL
772 // fields both match), the Zone RR is replaced by Update RR. If the
773 // TYPE is SOA and there is no Zone SOA RR, or the new SOA.SERIAL is
774 // lower (according to [RFC1982]) than or equal to the current Zone SOA
775 // RR's SOA.SERIAL, the Update RR is ignored. In the case of a CNAME
776 // Update RR and a non-CNAME Zone RRset or vice versa, ignore the CNAME
777 // Update RR, otherwise replace the CNAME Zone RR with the CNAME Update
778 // RR.
779
780 // zone rrset rr Add to an RRset
781 info!("upserting record: {rr:?}");
782 let upserted = self.in_memory.upsert(rr.clone(), serial).await;
783
784 #[cfg(all(feature = "metrics", feature = "__dnssec"))]
785 if auto_signing_and_increment {
786 if upserted {
787 self.metrics.added();
788 } else {
789 self.metrics.updated();
790 }
791 }
792
793 updated = upserted || updated
794 }
795 DNSClass::ANY => {
796 // This is a delete of entire RRSETs, either many or one. In either case, the spec is clear:
797 match rr.record_type() {
798 t @ RecordType::SOA | t @ RecordType::NS if rr_name == *self.origin() => {
799 // SOA and NS records are not to be deleted if they are the origin records
800 info!("skipping delete of {t:?} see RFC 2136 - 3.4.2.3");
801 continue;
802 }
803 RecordType::ANY => {
804 // RFC 2136 - 3.4.2.3. For any Update RR whose CLASS is ANY and whose TYPE is ANY,
805 // all Zone RRs with the same NAME are deleted, unless the NAME is the
806 // same as ZNAME in which case only those RRs whose TYPE is other than
807 // SOA or NS are deleted.
808
809 // ANY ANY empty Delete all RRsets from a name
810 info!(
811 "deleting all records at name (not SOA or NS at origin): {rr_name:?}"
812 );
813 let origin = self.origin();
814
815 let mut records = self.in_memory.records_mut().await;
816 let old_size = records.len();
817 records.retain(|k, _| {
818 k.name != rr_name
819 || ((k.record_type == RecordType::SOA
820 || k.record_type == RecordType::NS)
821 && k.name != *origin)
822 });
823 let new_size = records.len();
824 drop(records);
825
826 if new_size < old_size {
827 updated = true;
828 }
829
830 #[cfg(all(feature = "metrics", feature = "__dnssec"))]
831 for _ in 0..old_size - new_size {
832 if auto_signing_and_increment {
833 self.metrics.deleted()
834 }
835 }
836 }
837 _ => {
838 // RFC 2136 - 3.4.2.3. For any Update RR whose CLASS is ANY and
839 // whose TYPE is not ANY all Zone RRs with the same NAME and TYPE are
840 // deleted, unless the NAME is the same as ZNAME in which case neither
841 // SOA or NS RRs will be deleted.
842
843 // ANY rrset empty Delete an RRset
844 if let RData::Update0(_) | RData::NULL(..) = rr.data {
845 let deleted = self.in_memory.records_mut().await.remove(&rr_key);
846 info!("deleted rrset: {deleted:?}");
847 updated = updated || deleted.is_some();
848
849 #[cfg(all(feature = "metrics", feature = "__dnssec"))]
850 if auto_signing_and_increment {
851 self.metrics.deleted()
852 }
853 } else {
854 info!("expected empty rdata: {rr:?}");
855 return Err(ResponseCode::FormErr);
856 }
857 }
858 }
859 }
860 DNSClass::NONE => {
861 info!("deleting specific record: {rr:?}");
862 // NONE rrset rr Delete an RR from an RRset
863 if let Some(rrset) = self.in_memory.records_mut().await.get_mut(&rr_key) {
864 // b/c this is an Arc, we need to clone, then remove, and replace the node.
865 let mut rrset_clone: RecordSet = RecordSet::clone(&*rrset);
866 let deleted = rrset_clone.remove(rr, serial);
867 info!("deleted ({deleted}) specific record: {rr:?}");
868 updated = updated || deleted;
869
870 if deleted {
871 *rrset = Arc::new(rrset_clone);
872 }
873
874 #[cfg(all(feature = "metrics", feature = "__dnssec"))]
875 if auto_signing_and_increment {
876 self.metrics.deleted()
877 }
878 }
879 }
880 class => {
881 info!("unexpected DNS Class: {:?}", class);
882 return Err(ResponseCode::FormErr);
883 }
884 }
885 }
886
887 if !(updated && auto_signing_and_increment) {
888 return Ok(false);
889 }
890
891 let new_serial = if self.is_dnssec_enabled {
892 cfg_if::cfg_if! {
893 if #[cfg(feature = "__dnssec")] {
894 self.secure_zone().await.map_err(|error| {
895 error!(%error, "failure securing zone");
896 ResponseCode::ServFail
897 })?;
898 self.in_memory.serial().await
899 } else {
900 error!("failure securing zone, dnssec feature not enabled");
901 return Err(ResponseCode::ServFail)
902 }
903 }
904 } else {
905 // the secure_zone() function increments the SOA during it's operation, if we're not
906 // dnssec, then we need to do it here...
907 self.in_memory.increment_soa_serial().await
908 };
909
910 // Persist the post-update SOA record (including the incremented serial) so journal
911 // replay reconstructs the monotonic SOA serial across restarts.
912 //
913 // Note: `recover_with_journal()` replays with `auto_signing_and_increment = false`,
914 // so without journaling the updated SOA record, the in-memory serial bump would be
915 // lost after restart even though the updated RRsets are recovered.
916 let records = self.in_memory.records().await;
917 let Some(soa_record) = records
918 .get(&RrKey::new(self.origin().clone(), RecordType::SOA))
919 .and_then(|rrset| rrset.records_without_rrsigs().next())
920 else {
921 error!(origin = %self.origin(), "SOA record missing after serial increment");
922 return Err(ResponseCode::ServFail);
923 };
924
925 let journal_guard = self.journal.lock().await;
926 let Some(journal) = journal_guard.as_ref() else {
927 return Ok(updated);
928 };
929
930 if let Err(error) = journal.insert_record(new_serial, soa_record) {
931 error!("could not persist updated SOA record: {error}");
932 return Err(ResponseCode::ServFail);
933 }
934
935 Ok(true)
936 }
937
938 #[cfg(feature = "__dnssec")]
939 async fn authorized_tsig(
940 &self,
941 tsig: &Record<TSIG>,
942 request: &Request,
943 now: u64,
944 ) -> (Result<(), ResponseCode>, TSigResponseContext) {
945 let req_id = request.id();
946
947 debug!("authorizing with: {tsig:?}");
948 // RFC 8945 Section 5.5: "To prevent cross-algorithm attacks, there SHOULD only be
949 // one algorithm associated with any given key name." We rely on this and only check
950 // the key name when filtering TSIG keys.
951 let Some(tsigner) = self
952 .tsig_signers
953 .iter()
954 .find(|tsigner| tsigner.signer_name() == &tsig.name)
955 else {
956 warn!("no TSIG key name matched: id {req_id}");
957 return (
958 Err(ResponseCode::NotAuth),
959 TSigResponseContext::unknown_key(req_id, now, tsig.name.clone()),
960 );
961 };
962
963 let Ok((_, _, range)) = tsigner.verify_message_byte(request.as_slice(), None, true) else {
964 warn!("invalid TSIG signature: id {req_id}");
965 return (
966 Err(ResponseCode::NotAuth),
967 TSigResponseContext::bad_signature(req_id, now, tsigner.clone()),
968 );
969 };
970
971 let mut error = None;
972 let mut response = Ok(());
973
974 if !range.contains(&now) {
975 warn!("expired TSIG signature: id {req_id}");
976 // "A response indicating a BADTIME error MUST be signed by the same key as the request."
977 response = Err(ResponseCode::NotAuth);
978 error = Some(TsigError::BadTime);
979 }
980
981 (
982 response,
983 TSigResponseContext::new(req_id, now, tsigner.clone(), tsig.data.mac.clone(), error),
984 )
985 }
986}
987
988impl<P> Deref for SqliteZoneHandler<P> {
989 type Target = InMemoryZoneHandler<P>;
990
991 fn deref(&self) -> &Self::Target {
992 &self.in_memory
993 }
994}
995
996impl<P> DerefMut for SqliteZoneHandler<P> {
997 fn deref_mut(&mut self) -> &mut Self::Target {
998 &mut self.in_memory
999 }
1000}
1001
1002#[async_trait::async_trait]
1003impl<P: RuntimeProvider + Send + Sync> ZoneHandler for SqliteZoneHandler<P> {
1004 /// What type is this zone
1005 fn zone_type(&self) -> ZoneType {
1006 self.in_memory.zone_type()
1007 }
1008
1009 /// Return a policy that can be used to determine how AXFR requests should be handled.
1010 fn axfr_policy(&self) -> AxfrPolicy {
1011 self.axfr_policy
1012 }
1013
1014 /// Takes the UpdateMessage, extracts the Records, and applies the changes to the record set.
1015 ///
1016 /// # Arguments
1017 ///
1018 /// * `update` - The `UpdateMessage` records will be extracted and used to perform the update
1019 /// actions as specified in the above RFC.
1020 ///
1021 /// # Return value
1022 ///
1023 /// Always returns `Err(NotImp)` if DNSSEC is disabled. Returns `Ok(true)` if any of additions,
1024 /// updates or deletes were made to the zone, false otherwise. Err is returned in the case of
1025 /// bad data, etc.
1026 ///
1027 /// See [RFC 2136](https://datatracker.ietf.org/doc/html/rfc2136#section-3) section 3.4 for
1028 /// details.
1029 async fn update(
1030 &self,
1031 _request: &Request,
1032 _now: u64,
1033 ) -> (Result<bool, ResponseCode>, Option<TSigResponseContext>) {
1034 #[cfg(feature = "__dnssec")]
1035 {
1036 // the spec says to authorize after prereqs, seems better to auth first.
1037 let signer = match self.authorize_update(_request, _now).await {
1038 (Err(e), signer) => return (Err(e), signer),
1039 (_, signer) => signer,
1040 };
1041
1042 if let Err(code) = self.verify_prerequisites(_request.prerequisites()).await {
1043 return (Err(code), signer);
1044 }
1045
1046 if let Err(code) = self.pre_scan(_request.updates()).await {
1047 return (Err(code), signer);
1048 }
1049
1050 (self.update_records(_request.updates(), true).await, signer)
1051 }
1052 #[cfg(not(feature = "__dnssec"))]
1053 {
1054 // if we don't have dnssec, we can't do updates.
1055 (Err(ResponseCode::NotImp), None)
1056 }
1057 }
1058
1059 /// Get the origin of this zone, i.e. example.com is the origin for www.example.com
1060 fn origin(&self) -> &LowerName {
1061 self.in_memory.origin()
1062 }
1063
1064 /// Looks up all Resource Records matching the given `Name` and `RecordType`.
1065 ///
1066 /// # Arguments
1067 ///
1068 /// * `name` - The name to look up.
1069 /// * `rtype` - The `RecordType` to look up. `RecordType::ANY` will return all records matching
1070 /// `name`. `RecordType::AXFR` will return all record types except `RecordType::SOA`
1071 /// due to the requirements that on zone transfers the `RecordType::SOA` must both
1072 /// precede and follow all other records.
1073 /// * `lookup_options` - Query-related lookup options (e.g., DNSSEC DO bit, supported hash
1074 /// algorithms, etc.)
1075 ///
1076 /// # Return value
1077 ///
1078 /// A LookupControlFlow containing the lookup that should be returned to the client.
1079 async fn lookup(
1080 &self,
1081 name: &LowerName,
1082 rtype: RecordType,
1083 request_info: Option<&RequestInfo<'_>>,
1084 lookup_options: LookupOptions,
1085 ) -> LookupControlFlow<AuthLookup> {
1086 self.in_memory
1087 .lookup(name, rtype, request_info, lookup_options)
1088 .await
1089 }
1090
1091 async fn search(
1092 &self,
1093 request: &Request,
1094 lookup_options: LookupOptions,
1095 ) -> (LookupControlFlow<AuthLookup>, Option<TSigResponseContext>) {
1096 let request_info = match request.request_info() {
1097 Ok(info) => info,
1098 Err(e) => return (LookupControlFlow::Break(Err(e)), None),
1099 };
1100
1101 if request_info.query.query_type() == RecordType::AXFR {
1102 return (
1103 LookupControlFlow::Break(Err(LookupError::NetError(
1104 "AXFR must be handled with ZoneHandler::zone_transfer()".into(),
1105 ))),
1106 None,
1107 );
1108 }
1109
1110 let (search, _) = self.in_memory.search(request, lookup_options).await;
1111
1112 (search, None)
1113 }
1114
1115 async fn zone_transfer(
1116 &self,
1117 request: &Request,
1118 lookup_options: LookupOptions,
1119 now: u64,
1120 ) -> Option<(
1121 Result<ZoneTransfer, LookupError>,
1122 Option<TSigResponseContext>,
1123 )> {
1124 let (resp, signer) = self.authorize_axfr(request, now).await;
1125 if let Err(code) = resp {
1126 warn!(axfr_policy = ?self.axfr_policy, "rejected AXFR");
1127 return Some((Err(LookupError::ResponseCode(code)), signer));
1128 }
1129 debug!(axfr_policy = ?self.axfr_policy, "authorized AXFR");
1130
1131 let (zone_transfer, _) = self
1132 .in_memory
1133 .zone_transfer(request, lookup_options, now)
1134 .await?;
1135
1136 Some((zone_transfer, signer))
1137 }
1138
1139 /// Return the NSEC records based on the given name
1140 ///
1141 /// # Arguments
1142 ///
1143 /// * `name` - given this name (i.e. the lookup name), return the NSEC record that is less than
1144 /// this
1145 /// * `lookup_options` - Query-related lookup options (e.g., DNSSEC DO bit, supported hash
1146 /// algorithms, etc.)
1147 async fn nsec_records(
1148 &self,
1149 name: &LowerName,
1150 lookup_options: LookupOptions,
1151 ) -> LookupControlFlow<AuthLookup> {
1152 self.in_memory.nsec_records(name, lookup_options).await
1153 }
1154
1155 #[cfg(feature = "__dnssec")]
1156 async fn nsec3_records(
1157 &self,
1158 info: Nsec3QueryInfo<'_>,
1159 lookup_options: LookupOptions,
1160 ) -> LookupControlFlow<AuthLookup> {
1161 self.in_memory.nsec3_records(info, lookup_options).await
1162 }
1163
1164 #[cfg(feature = "__dnssec")]
1165 fn nx_proof_kind(&self) -> Option<&NxProofKind> {
1166 self.in_memory.nx_proof_kind()
1167 }
1168
1169 #[cfg(feature = "metrics")]
1170 fn metrics_label(&self) -> &'static str {
1171 "sqlite"
1172 }
1173}
1174
1175#[cfg(feature = "__dnssec")]
1176#[async_trait::async_trait]
1177impl<P: RuntimeProvider + Send + Sync> DnssecZoneHandler for SqliteZoneHandler<P> {
1178 /// By adding a secure key, this will implicitly enable dnssec for the zone.
1179 ///
1180 /// # Arguments
1181 ///
1182 /// * `signer` - Signer with associated private key
1183 async fn add_zone_signing_key(&self, signer: DnssecSigner) -> DnsSecResult<()> {
1184 self.in_memory.add_zone_signing_key(signer).await
1185 }
1186
1187 /// (Re)generates the nsec records, increments the serial number and signs the zone
1188 async fn secure_zone(&self) -> DnsSecResult<()> {
1189 self.in_memory.secure_zone().await
1190 }
1191}
1192
1193/// Configuration for zone file for sqlite based zones
1194#[derive(Deserialize, PartialEq, Eq, Debug)]
1195#[serde(deny_unknown_fields)]
1196pub struct SqliteConfig {
1197 /// path to initial zone file
1198 pub zone_path: PathBuf,
1199 /// path to the sqlite journal file
1200 pub journal_path: PathBuf,
1201 /// Are updates allowed to this zone
1202 #[serde(default)]
1203 pub allow_update: bool,
1204 /// TSIG keys allowed to authenticate updates if `allow_update` is true
1205 #[cfg(feature = "__dnssec")]
1206 #[serde(default)]
1207 pub tsig_keys: Vec<TsigKeyConfig>,
1208}
1209
1210/// Configuration for a TSIG authentication signer key
1211#[derive(Deserialize, PartialEq, Eq, Debug)]
1212#[serde(deny_unknown_fields)]
1213#[cfg(feature = "__dnssec")]
1214pub struct TsigKeyConfig {
1215 /// The key name
1216 pub name: String,
1217 /// A path to the unencoded symmetric HMAC key data
1218 pub key_file: PathBuf,
1219 /// The key algorithm
1220 pub algorithm: TsigAlgorithm,
1221 /// Allowed +/- difference (in seconds) between the time a TSIG request was signed
1222 /// and when it is verified.
1223 ///
1224 /// A fudge value that is too large may leave the server open to replay attacks.
1225 /// A fudge value that is too small may cause failures from latency and clock
1226 /// desynchronization.
1227 ///
1228 /// RFC 8945 recommends a fudge value of 300 seconds (the default if not specified).
1229 #[serde(default = "default_fudge")]
1230 pub fudge: u16,
1231}
1232
1233#[cfg(feature = "__dnssec")]
1234impl TsigKeyConfig {
1235 fn to_signer(&self, zone_name: &Name, root_dir: Option<&Path>) -> Result<TSigner, String> {
1236 let key_file = rooted(&self.key_file, root_dir);
1237 let key_data = fs::read(&key_file)
1238 .map_err(|e| format!("error reading TSIG key file: {}: {e}", key_file.display()))?;
1239 let signer_name = Name::from_str(&self.name).unwrap_or_else(|_| zone_name.clone());
1240
1241 TSigner::new(key_data, self.algorithm.clone(), signer_name, self.fudge)
1242 .map_err(|e| format!("invalid TSIG key configuration: {e}"))
1243 }
1244}
1245
1246/// Default TSIG fudge value (seconds).
1247///
1248/// Per RFC 8945 ยง10:
1249/// "The RECOMMENDED value in most situations is 300 seconds."
1250#[cfg(feature = "__dnssec")]
1251pub(crate) fn default_fudge() -> u16 {
1252 300
1253}
1254
1255#[cfg(test)]
1256#[allow(clippy::extra_unused_type_parameters)]
1257mod tests {
1258 use std::env::temp_dir;
1259 use std::fs::remove_file;
1260 use std::net::Ipv4Addr;
1261 use std::path::Path;
1262 use std::process;
1263 use std::str::FromStr;
1264 use std::time::SystemTime;
1265
1266 use crate::net::runtime::TokioRuntimeProvider;
1267 use crate::proto::rr::{Name, RData, Record};
1268 use crate::store::in_memory::{InMemoryZoneHandler, zone_from_path};
1269 use crate::store::sqlite::{Journal, SqliteZoneHandler};
1270 use crate::zone_handler::{AxfrPolicy, ZoneType};
1271
1272 #[test]
1273 fn test_is_send_sync() {
1274 fn send_sync<T: Send + Sync>() -> bool {
1275 true
1276 }
1277
1278 assert!(send_sync::<SqliteZoneHandler>());
1279 }
1280
1281 #[tokio::test]
1282 async fn test_soa_serial_is_monotonic_across_journal_recovery() {
1283 let origin = Name::from_str("example.com.").unwrap();
1284 let zone_path = Path::new(env!("CARGO_MANIFEST_DIR"))
1285 .join("../../tests/test-data/test_configs/example.com.zone");
1286
1287 let in_memory: InMemoryZoneHandler<TokioRuntimeProvider> = InMemoryZoneHandler::new(
1288 origin.clone(),
1289 zone_from_path(&zone_path, origin.clone()).unwrap(),
1290 ZoneType::Primary,
1291 AxfrPolicy::AllowAll,
1292 #[cfg(feature = "__dnssec")]
1293 None,
1294 )
1295 .unwrap();
1296
1297 // Use a file-backed journal so we can simulate a restart by reopening it.
1298 let journal_path = temp_dir().join(format!(
1299 "hickory-sqlite-journal-serial-test-{}-{}.sqlite",
1300 process::id(),
1301 SystemTime::now()
1302 .duration_since(std::time::UNIX_EPOCH)
1303 .unwrap()
1304 .as_nanos()
1305 ));
1306
1307 // Create a handler with journaling enabled and persist the initial zone snapshot.
1308 let mut handler = SqliteZoneHandler::new(
1309 in_memory,
1310 AxfrPolicy::AllowAll,
1311 true, // allow_update
1312 false, // dnssec disabled
1313 );
1314 handler
1315 .set_journal(Journal::from_file(&journal_path).unwrap())
1316 .await;
1317 handler.persist_to_journal().await.unwrap();
1318
1319 let s1 = handler.serial().await;
1320
1321 // Apply a dynamic update that modifies zone contents; this must increment SOA.
1322 let update_record = Record::from_rdata(
1323 Name::from_str("serialtest.example.com.").unwrap(),
1324 0,
1325 RData::A(Ipv4Addr::new(192, 0, 2, 55).into()),
1326 );
1327
1328 assert!(
1329 handler
1330 .update_records(&[update_record], true)
1331 .await
1332 .unwrap()
1333 );
1334 let s2 = handler.serial().await;
1335 assert_eq!(s2, s1 + 1);
1336
1337 // "Restart": recover into a new handler from the same journal.
1338 let in_memory_recovered: InMemoryZoneHandler<TokioRuntimeProvider> =
1339 InMemoryZoneHandler::empty(
1340 origin.clone(),
1341 ZoneType::Primary,
1342 AxfrPolicy::AllowAll,
1343 #[cfg(feature = "__dnssec")]
1344 None,
1345 );
1346 let mut recovered =
1347 SqliteZoneHandler::new(in_memory_recovered, AxfrPolicy::AllowAll, true, false);
1348 recovered
1349 .recover_with_journal(&Journal::from_file(&journal_path).unwrap())
1350 .await
1351 .unwrap();
1352
1353 let s3 = recovered.serial().await;
1354 assert_eq!(s3, s2);
1355
1356 let _ = remove_file(&journal_path);
1357 }
1358}