From 47e003e4fe7049033845d2cc8307846bbde9de33 Mon Sep 17 00:00:00 2001 From: Ximon Eighteen <3304436+ximon18@users.noreply.github.com> Date: Tue, 26 Mar 2024 10:24:31 +0100 Subject: [PATCH] Put related things together (in-memory store, errors, traits, types) and don't use the internal version types of the in-memory store in public APIs. --- Cargo.toml | 18 +- examples/other/mysql-zone.rs | 17 +- examples/query-zone.rs | 4 +- examples/read-zone.rs | 10 +- examples/serve-zone.rs | 10 +- src/zonefile/error.rs | 53 +++ src/zonefile/mod.rs | 2 + src/zonefile/parsed.rs | 20 +- src/zonetree/answer.rs | 178 ++++++++++ src/zonetree/{ => in_memory}/builder.rs | 75 +---- src/zonetree/in_memory/mod.rs | 7 + src/zonetree/{ => in_memory}/nodes.rs | 86 ++--- src/zonetree/{ => in_memory}/read.rs | 377 +++------------------- src/zonetree/{ => in_memory}/versioned.rs | 5 + src/zonetree/{ => in_memory}/write.rs | 141 ++++---- src/zonetree/mod.rs | 42 +-- src/zonetree/traits.rs | 128 ++++++++ src/zonetree/{set.rs => tree.rs} | 10 +- src/zonetree/{rrset.rs => types.rs} | 10 + src/zonetree/walk.rs | 68 ++++ src/zonetree/zone.rs | 199 +++++------- 21 files changed, 788 insertions(+), 672 deletions(-) create mode 100644 src/zonefile/error.rs create mode 100644 src/zonetree/answer.rs rename src/zonetree/{ => in_memory}/builder.rs (61%) create mode 100644 src/zonetree/in_memory/mod.rs rename src/zonetree/{ => in_memory}/nodes.rs (89%) rename src/zonetree/{ => in_memory}/read.rs (55%) rename src/zonetree/{ => in_memory}/versioned.rs (93%) rename src/zonetree/{ => in_memory}/write.rs (82%) create mode 100644 src/zonetree/traits.rs rename src/zonetree/{set.rs => tree.rs} (96%) rename src/zonetree/{rrset.rs => types.rs} (94%) create mode 100644 src/zonetree/walk.rs diff --git a/Cargo.toml b/Cargo.toml index b51bf286b..94c2eb092 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,14 +96,6 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } all-features = true rustdoc-args = ["--cfg", "docsrs"] -[[example]] -name = "read-zone" -required-features = ["zonefile"] - -[[example]] -name = "query-zone" -required-features = ["unstable-zonetree"] - [[example]] name = "download-rust-lang" required-features = ["resolv"] @@ -128,9 +120,17 @@ required-features = ["net", "unstable-client-transport"] name = "server-transports" required-features = ["net", "unstable-server-transport"] +[[example]] +name = "read-zone" +required-features = ["zonefile"] + +[[example]] +name = "query-zone" +required-features = ["zonefile", "unstable-zonetree"] + [[example]] name = "serve-zone" -required-features = ["net", "unstable-server-transport", "unstable-zonetree"] +required-features = ["zonefile", "net", "unstable-server-transport", "unstable-zonetree"] # This example is commented out because it is difficult, if not impossible, # when including the sqlx dependency, to make the dependency tree compatible diff --git a/examples/other/mysql-zone.rs b/examples/other/mysql-zone.rs index f7a8fec62..819164c06 100644 --- a/examples/other/mysql-zone.rs +++ b/examples/other/mysql-zone.rs @@ -1,6 +1,6 @@ //! MySQL backed zone serving minimal proof of concept. // -// This example extends `domain` with a new `ZoneData` impl adding support for +// This example extends `domain` with a new `ZoneStore` impl adding support for // MySQL backed zones. This demonstration only implements the `ReadableZone` // trait, it doesn't implement the `WritableZone` trait, so database access is // read-only. Write access could be implemented, it just isn't in this @@ -123,8 +123,8 @@ use domain::base::scan::IterScanner; use domain::base::{Dname, Rtype, Ttl}; use domain::rdata::ZoneRecordData; use domain::zonetree::{ - Answer, OutOfZone, ReadableZone, Rrset, SharedRrset, StoredDname, - Version, VersionMarker, WalkOp, WriteableZone, Zone, ZoneData, ZoneSet, + Answer, OutOfZone, ReadableZone, Rrset, SharedRrset, StoredDname, WalkOp, + WriteableZone, Zone, ZoneSet, ZoneStore, }; use parking_lot::RwLock; use sqlx::Row; @@ -195,9 +195,9 @@ impl DatabaseNode { } } -//--- impl ZoneData +//--- impl ZoneStore -impl ZoneData for DatabaseNode { +impl ZoneStore for DatabaseNode { fn class(&self) -> Class { Class::In } @@ -206,10 +206,7 @@ impl ZoneData for DatabaseNode { &self.apex_name } - fn read( - self: Arc, - _current: (Version, Arc), - ) -> Box { + fn read(self: Arc) -> Box { Box::new(DatabaseReadZone::new( self.db_pool.clone(), self.apex_name.clone(), @@ -218,8 +215,6 @@ impl ZoneData for DatabaseNode { fn write( self: Arc, - _version: Version, - _zone_versions: Arc>, ) -> Pin>>> { todo!() } diff --git a/examples/query-zone.rs b/examples/query-zone.rs index 289914c25..a1eb18a86 100644 --- a/examples/query-zone.rs +++ b/examples/query-zone.rs @@ -13,7 +13,7 @@ use domain::base::{ParsedRecord, Record}; use domain::rdata::ZoneRecordData; use domain::zonefile::inplace; use domain::zonetree::{Answer, Rrset}; -use domain::zonetree::{Zone, ZoneSet}; +use domain::zonetree::{Zone, ZoneTree}; use octseq::Parser; use tracing_subscriber::EnvFilter; @@ -52,7 +52,7 @@ fn main() { }); // Go! - let mut zones = ZoneSet::new(); + let mut zones = ZoneTree::new(); for (zone_file_path, mut zone_file) in zone_files { if verbosity != Verbosity::Quiet { diff --git a/examples/read-zone.rs b/examples/read-zone.rs index e0150900f..110aaa804 100644 --- a/examples/read-zone.rs +++ b/examples/read-zone.rs @@ -1,15 +1,14 @@ //! Reads a zone file. +use std::env; +use std::fs::File; use std::process::exit; +use std::time::SystemTime; use domain::zonefile::inplace::Entry; +use domain::zonefile::inplace::Zonefile; fn main() { - use domain::zonefile::inplace::Zonefile; - use std::env; - use std::fs::File; - use std::time::SystemTime; - let mut args = env::args(); let prog_name = args.next().unwrap(); // SAFETY: O/S always passes our name as the first argument. let zone_files: Vec<_> = args.collect(); @@ -44,7 +43,6 @@ fn main() { eprintln!(" Error: {err}"); if let Some(entry) = &last_entry { if let Entry::Record(record) = &entry { - // let record = record.unwrap().into_record::>>().unwrap().unwrap(); eprintln!( "\nThe last record read was:\n{record}." ); diff --git a/examples/serve-zone.rs b/examples/serve-zone.rs index d7bf298b5..0314fd9a1 100644 --- a/examples/serve-zone.rs +++ b/examples/serve-zone.rs @@ -29,7 +29,7 @@ use domain::net::server::stream::StreamServer; use domain::net::server::util::{mk_builder_for_target, service_fn}; use domain::zonefile::inplace; use domain::zonetree::{Answer, Rrset}; -use domain::zonetree::{Zone, ZoneSet}; +use domain::zonetree::{Zone, ZoneTree}; use octseq::OctetsBuilder; use std::future::{pending, ready, Future}; use std::io::BufReader; @@ -49,7 +49,7 @@ async fn main() { .ok(); // Populate a zone tree with test data - let mut zones = ZoneSet::new(); + let mut zones = ZoneTree::new(); let zone_bytes = include_bytes!("../test-data/zonefiles/nsd-example.txt"); let mut zone_bytes = BufReader::new(&zone_bytes[..]); @@ -79,7 +79,7 @@ async fn main() { #[allow(clippy::type_complexity)] fn my_service( msg: Request>>, - zones: Arc, + zones: Arc, ) -> Result< Transaction< Vec, @@ -105,7 +105,7 @@ fn my_service( async fn handle_non_axfr_request( msg: Request>>, - zones: Arc, + zones: Arc, ) -> Result, Vec>, ServiceError> { let question = msg.message().sole_question().unwrap(); let zone = zones @@ -127,7 +127,7 @@ async fn handle_non_axfr_request( async fn handle_axfr_request( msg: Request>>, - zones: Arc, + zones: Arc, ) -> TransactionStream, Vec>, ServiceError>> { let mut stream = TransactionStream::default(); diff --git a/src/zonefile/error.rs b/src/zonefile/error.rs new file mode 100644 index 000000000..69806b15d --- /dev/null +++ b/src/zonefile/error.rs @@ -0,0 +1,53 @@ +//------------ ZoneCutError -------------------------------------------------- + +use std::fmt::Display; + +#[derive(Clone, Copy, Debug)] +pub enum ZoneCutError { + OutOfZone, + ZoneCutAtApex, +} + +impl From for ZoneCutError { + fn from(_: OutOfZone) -> ZoneCutError { + ZoneCutError::OutOfZone + } +} + +impl Display for ZoneCutError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + ZoneCutError::OutOfZone => write!(f, "Out of zone"), + ZoneCutError::ZoneCutAtApex => write!(f, "Zone cut at apex"), + } + } +} + +//----------- CnameError ----------------------------------------------------- + +#[derive(Clone, Copy, Debug)] +pub enum CnameError { + OutOfZone, + CnameAtApex, +} + +impl From for CnameError { + fn from(_: OutOfZone) -> CnameError { + CnameError::OutOfZone + } +} + +impl Display for CnameError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + CnameError::OutOfZone => write!(f, "Out of zone"), + CnameError::CnameAtApex => write!(f, "CNAME at apex"), + } + } +} + +//----------- OutOfZone ------------------------------------------------------ + +/// A domain name is not under the zone’s apex. +#[derive(Clone, Copy, Debug)] +pub struct OutOfZone; diff --git a/src/zonefile/mod.rs b/src/zonefile/mod.rs index fbc360245..6cf90f22b 100644 --- a/src/zonefile/mod.rs +++ b/src/zonefile/mod.rs @@ -2,5 +2,7 @@ #![cfg(feature = "zonefile")] #![cfg_attr(docsrs, doc(cfg(feature = "zonefile")))] +pub mod error; pub mod inplace; +#[cfg(feature = "unstable-zonetree")] pub mod parsed; diff --git a/src/zonefile/parsed.rs b/src/zonefile/parsed.rs index 071558c99..be4c98740 100644 --- a/src/zonefile/parsed.rs +++ b/src/zonefile/parsed.rs @@ -1,22 +1,22 @@ //! Importing from and exporting to a zonefiles. -use tracing::trace; - -use crate::base::iana::{Class, Rtype}; -use crate::base::name::FlattenInto; -use crate::base::ToDname; -use crate::rdata::ZoneRecordData; -use crate::zonetree::{ - CnameError, Rrset, SharedRr, StoredDname, StoredRecord, ZoneBuilder, - ZoneCutError, -}; use core::convert::Infallible; use std::collections::{BTreeMap, HashMap}; use std::fmt::Display; use std::vec::Vec; +use tracing::trace; + +use super::error::{CnameError, ZoneCutError}; use super::inplace::{self, Entry}; +use crate::base::iana::{Class, Rtype}; +use crate::base::name::FlattenInto; +use crate::base::ToDname; +use crate::rdata::ZoneRecordData; +use crate::zonetree::in_memory::ZoneBuilder; +use crate::zonetree::{Rrset, SharedRr, StoredDname, StoredRecord}; + //------------ Zonefile ------------------------------------------------------ /// The content of a zonefile. diff --git a/src/zonetree/answer.rs b/src/zonetree/answer.rs new file mode 100644 index 000000000..c57138b6b --- /dev/null +++ b/src/zonetree/answer.rs @@ -0,0 +1,178 @@ +//------------ Answer -------------------------------------------------------- + +use super::{SharedRr, SharedRrset, StoredDname}; +use crate::base::iana::Rcode; +use crate::base::message_builder::AdditionalBuilder; +use crate::base::wire::Composer; +use crate::base::Message; +use crate::base::MessageBuilder; +use octseq::Octets; + +#[derive(Clone)] +pub struct Answer { + /// The response code of the answer. + rcode: Rcode, + + /// The content of the answer. + content: AnswerContent, + + /// The optional authority section to be included in the answer. + authority: Option, +} + +impl Answer { + pub fn new(rcode: Rcode) -> Self { + Answer { + rcode, + content: AnswerContent::NoData, + authority: Default::default(), + } + } + + pub fn with_authority(rcode: Rcode, authority: AnswerAuthority) -> Self { + Answer { + rcode, + content: AnswerContent::NoData, + authority: Some(authority), + } + } + + pub fn other(rcode: Rcode) -> Self { + Answer::new(rcode) + } + + pub fn refused() -> Self { + Answer::new(Rcode::Refused) + } + + pub fn add_cname(&mut self, cname: SharedRr) { + self.content = AnswerContent::Cname(cname); + } + + pub fn add_answer(&mut self, answer: SharedRrset) { + self.content = AnswerContent::Data(answer); + } + + pub fn add_authority(&mut self, authority: AnswerAuthority) { + self.authority = Some(authority) + } + + pub fn to_message( + &self, + message: &Message, + builder: MessageBuilder, + ) -> AdditionalBuilder { + let question = message.sole_question().unwrap(); + let qname = question.qname(); + let qclass = question.qclass(); + let mut builder = builder.start_answer(message, self.rcode).unwrap(); + + match self.content { + AnswerContent::Data(ref answer) => { + for item in answer.data() { + // TODO: This will panic if too many answers were given, + // rather than give the caller a way to push the rest into + // another message. + builder + .push((qname, qclass, answer.ttl(), item)) + .unwrap(); + } + } + AnswerContent::Cname(ref cname) => builder + .push((qname, qclass, cname.ttl(), cname.data())) + .unwrap(), + AnswerContent::NoData => {} + } + + let mut builder = builder.authority(); + if let Some(authority) = self.authority.as_ref() { + if let Some(soa) = authority.soa.as_ref() { + builder + .push(( + authority.owner.clone(), + qclass, + soa.ttl(), + soa.data(), + )) + .unwrap(); + } + if let Some(ns) = authority.ns.as_ref() { + for item in ns.data() { + builder + .push(( + authority.owner.clone(), + qclass, + ns.ttl(), + item, + )) + .unwrap() + } + } + if let Some(ref ds) = authority.ds { + for item in ds.data() { + builder + .push(( + authority.owner.clone(), + qclass, + ds.ttl(), + item, + )) + .unwrap() + } + } + } + + builder.additional() + } + + pub fn rcode(&self) -> Rcode { + self.rcode + } + + pub fn content(&self) -> &AnswerContent { + &self.content + } + + pub fn authority(&self) -> Option<&AnswerAuthority> { + self.authority.as_ref() + } +} + +//------------ AnswerContent ------------------------------------------------- + +/// The content of the answer. +#[derive(Clone)] +pub enum AnswerContent { + Data(SharedRrset), + Cname(SharedRr), + NoData, +} + +//------------ AnswerAuthority ----------------------------------------------- + +/// The authority section of a query answer. +#[derive(Clone)] +pub struct AnswerAuthority { + /// The owner name of the record sets in the authority section. + owner: StoredDname, + + /// The SOA record if it should be included. + soa: Option, + + /// The NS record set if it should be included. + ns: Option, + + /// The DS record set if it should be included.. + ds: Option, +} + +impl AnswerAuthority { + pub fn new( + owner: StoredDname, + soa: Option, + ns: Option, + ds: Option, + ) -> Self { + AnswerAuthority { owner, soa, ns, ds } + } +} diff --git a/src/zonetree/builder.rs b/src/zonetree/in_memory/builder.rs similarity index 61% rename from src/zonetree/builder.rs rename to src/zonetree/in_memory/builder.rs index 1f7703224..620acdeb3 100644 --- a/src/zonetree/builder.rs +++ b/src/zonetree/in_memory/builder.rs @@ -3,15 +3,18 @@ use std::sync::Arc; use std::vec::Vec; -use super::nodes::{OutOfZone, Special, ZoneApex, ZoneCut, ZoneNode}; -use super::rrset::{SharedRr, SharedRrset, StoredDname, StoredRecord}; -use super::set::{InsertZoneError, ZoneSet}; -use super::versioned::Version; -use super::zone::Zone; use crate::base::iana::Class; use crate::base::name::{Label, ToDname}; +use crate::zonefile::error::{CnameError, OutOfZone, ZoneCutError}; use crate::zonefile::parsed; -use std::fmt::Display; +use crate::zonetree::tree::InsertZoneError; +use crate::zonetree::types::ZoneCut; +use crate::zonetree::{ + SharedRr, SharedRrset, StoredDname, StoredRecord, Zone, ZoneTree, +}; + +use super::nodes::{Special, ZoneApex, ZoneNode}; +use super::versioned::Version; //------------ ZoneBuilder --------------------------------------------------- @@ -32,7 +35,7 @@ impl ZoneBuilder { pub fn finalize_into_set( self, - zone_set: &mut ZoneSet, + zone_set: &mut ZoneTree, ) -> Result<(), InsertZoneError> { zone_set.insert_zone(self.finalize()) } @@ -107,61 +110,3 @@ impl TryFrom for ZoneBuilder { source.into_zone_builder() } } - -//------------ ZoneCutError -------------------------------------------------- - -#[derive(Clone, Copy, Debug)] -pub enum ZoneCutError { - OutOfZone, - ZoneCutAtApex, -} - -impl From for ZoneCutError { - fn from(_: OutOfZone) -> ZoneCutError { - ZoneCutError::OutOfZone - } -} - -impl<'a> From<&'a ZoneApex> for ZoneCutError { - fn from(_: &'a ZoneApex) -> ZoneCutError { - ZoneCutError::ZoneCutAtApex - } -} - -impl Display for ZoneCutError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - ZoneCutError::OutOfZone => write!(f, "Out of zone"), - ZoneCutError::ZoneCutAtApex => write!(f, "Zone cut at apex"), - } - } -} - -//----------- CnameError ----------------------------------------------------- - -#[derive(Clone, Copy, Debug)] -pub enum CnameError { - OutOfZone, - CnameAtApex, -} - -impl From for CnameError { - fn from(_: OutOfZone) -> CnameError { - CnameError::OutOfZone - } -} - -impl<'a> From<&'a ZoneApex> for CnameError { - fn from(_: &'a ZoneApex) -> CnameError { - CnameError::CnameAtApex - } -} - -impl Display for CnameError { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - match self { - CnameError::OutOfZone => write!(f, "Out of zone"), - CnameError::CnameAtApex => write!(f, "CNAME at apex"), - } - } -} diff --git a/src/zonetree/in_memory/mod.rs b/src/zonetree/in_memory/mod.rs new file mode 100644 index 000000000..d7b50db5b --- /dev/null +++ b/src/zonetree/in_memory/mod.rs @@ -0,0 +1,7 @@ +mod builder; +mod nodes; +mod read; +mod versioned; +mod write; + +pub use builder::ZoneBuilder; diff --git a/src/zonetree/nodes.rs b/src/zonetree/in_memory/nodes.rs similarity index 89% rename from src/zonetree/nodes.rs rename to src/zonetree/in_memory/nodes.rs index 4e7ae80d7..69944c235 100644 --- a/src/zonetree/nodes.rs +++ b/src/zonetree/in_memory/nodes.rs @@ -1,17 +1,5 @@ //! The nodes in a zone tree. -use super::read::{ReadableZone, WalkState}; -use super::rrset::{SharedRr, SharedRrset, StoredDname, StoredRecord}; -use super::versioned::{Version, Versioned}; -use super::write::{WriteZone, WriteableZone}; -use super::zone::{VersionMarker, ZoneData, ZoneVersions}; -use super::ReadZone; -use crate::base::iana::{Class, Rtype}; -use crate::base::name::{Label, OwnedLabel, ToDname, ToLabelIter}; -use parking_lot::{ - RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard, -}; -use serde::{Deserialize, Serialize}; use std::borrow::Cow; use std::boxed::Box; use std::collections::{hash_map, HashMap}; @@ -20,9 +8,26 @@ use std::pin::Pin; use std::string::String; use std::string::ToString; use std::sync::Arc; -use std::vec::Vec; + +use parking_lot::{ + RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard, +}; use tokio::sync::Mutex; +use crate::base::iana::{Class, Rtype}; +use crate::base::name::{Label, OwnedLabel, ToDname, ToLabelIter}; +use crate::zonefile::error::{CnameError, OutOfZone, ZoneCutError}; +use crate::zonetree::types::ZoneCut; +use crate::zonetree::walk::WalkState; +use crate::zonetree::{ + ReadableZone, SharedRr, SharedRrset, StoredDname, WriteableZone, + ZoneStore, +}; + +use super::read::ReadZone; +use super::versioned::{Version, Versioned}; +use super::write::{WriteZone, ZoneVersions}; + //------------ ZoneApex ------------------------------------------------------ #[derive(Debug)] @@ -33,6 +38,7 @@ pub struct ZoneApex { rrsets: NodeRrsets, children: NodeChildren, update_lock: Arc>, + versions: Arc>, } impl ZoneApex { @@ -45,6 +51,7 @@ impl ZoneApex { rrsets: Default::default(), children: Default::default(), update_lock: Default::default(), + versions: Default::default(), } } @@ -54,6 +61,7 @@ impl ZoneApex { class: Class, rrsets: NodeRrsets, children: NodeChildren, + versions: ZoneVersions, ) -> Self { ZoneApex { apex_name_display: format!("{}", apex_name), @@ -62,6 +70,7 @@ impl ZoneApex { rrsets, children, update_lock: Default::default(), + versions: Arc::new(RwLock::new(versions)), } } @@ -118,11 +127,15 @@ impl ZoneApex { self.rrsets.clean(version); self.children.clean(version); } + + pub fn versions(&self) -> &RwLock { + &self.versions + } } -//--- impl ZoneData +//--- impl ZoneStore -impl ZoneData for ZoneApex { +impl ZoneStore for ZoneApex { fn class(&self) -> Class { self.class } @@ -131,27 +144,40 @@ impl ZoneData for ZoneApex { &self.apex_name } - fn read( - self: Arc, - current: (Version, Arc), - ) -> Box { - let (version, marker) = current; + fn read(self: Arc) -> Box { + let (version, marker) = self.versions().read().current().clone(); Box::new(ReadZone::new(self, version, marker)) } fn write( self: Arc, - version: Version, - zone_versions: Arc>, ) -> Pin>>> { Box::pin(async move { let lock = self.update_lock.clone().lock_owned().await; + let version = self.versions().read().current().0.next(); + let zone_versions = self.versions.clone(); Box::new(WriteZone::new(self, lock, version, zone_versions)) as Box }) } } +//--- impl From<&'a ZoneApex> + +impl<'a> From<&'a ZoneApex> for CnameError { + fn from(_: &'a ZoneApex) -> CnameError { + CnameError::CnameAtApex + } +} + +//--- impl From<&'a ZoneApex> + +impl<'a> From<&'a ZoneApex> for ZoneCutError { + fn from(_: &'a ZoneApex) -> ZoneCutError { + ZoneCutError::ZoneCutAtApex + } +} + //------------ ZoneNode ------------------------------------------------------ #[derive(Default, Debug)] @@ -333,16 +359,6 @@ pub enum Special { NxDomain, } -//------------ ZoneCut ------------------------------------------------------- - -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct ZoneCut { - pub name: StoredDname, - pub ns: SharedRrset, - pub ds: Option, - pub glue: Vec, -} - //------------ NodeChildren -------------------------------------------------- #[derive(Debug, Default)] @@ -402,9 +418,3 @@ impl NodeChildren { } } } - -//============ Error Types ================================================== - -/// A domain name is not under the zone’s apex. -#[derive(Clone, Copy, Debug)] -pub struct OutOfZone; diff --git a/src/zonetree/read.rs b/src/zonetree/in_memory/read.rs similarity index 55% rename from src/zonetree/read.rs rename to src/zonetree/in_memory/read.rs index 7bd25a82d..707751b1e 100644 --- a/src/zonetree/read.rs +++ b/src/zonetree/in_memory/read.rs @@ -1,71 +1,24 @@ //! Quering for zone data. - -use core::future::ready; use core::iter; -use std::boxed::Box; -use std::future::Future; -use std::pin::Pin; -use std::sync::{Arc, Mutex}; -use std::vec::Vec; + +use std::sync::Arc; use bytes::Bytes; -use super::nodes::{ - NodeChildren, NodeRrsets, OutOfZone, Special, ZoneApex, ZoneCut, ZoneNode, -}; -use super::rrset::{SharedRr, SharedRrset, StoredDname}; -use super::versioned::Version; -use super::zone::{VersionMarker, ZoneData}; -use super::Rrset; use crate::base::iana::{Rcode, Rtype}; -use crate::base::message::Message; -use crate::base::message_builder::{AdditionalBuilder, MessageBuilder}; -use crate::base::name::{Label, OwnedLabel}; -use crate::base::wire::Composer; -use crate::base::{Dname, DnameBuilder}; -use crate::dep::octseq::Octets; - -pub type WalkOp = Box, &Rrset) + Send + Sync>; - -//------------ ReadableZone -------------------------------------------------- - -pub trait ReadableZone: Send { - fn is_async(&self) -> bool { - true - } - - //--- Sync variants - - fn query( - &self, - _qname: Dname, - _qtype: Rtype, - ) -> Result { - unimplemented!() - } - - fn walk(&self, _op: WalkOp) { - unimplemented!() - } - - //--- Async variants - - fn query_async( - &self, - qname: Dname, - qtype: Rtype, - ) -> Pin> + Send>> { - Box::pin(ready(self.query(qname, qtype))) - } +use crate::base::name::Label; +use crate::base::Dname; +use crate::zonefile::error::OutOfZone; +use crate::zonetree::answer::{Answer, AnswerAuthority}; +use crate::zonetree::types::ZoneCut; +use crate::zonetree::walk::WalkState; +use crate::zonetree::{ + ReadableZone, Rrset, SharedRr, SharedRrset, WalkOp, ZoneStore, +}; - fn walk_async( - &self, - op: WalkOp, - ) -> Pin + Send>> { - self.walk(op); - Box::pin(ready(())) - } -} +use super::nodes::{NodeChildren, NodeRrsets, Special, ZoneApex, ZoneNode}; +use super::versioned::Version; +use super::versioned::VersionMarker; //------------ ReadZone ------------------------------------------------------ @@ -90,99 +43,6 @@ impl ReadZone { } } -struct WalkStateInner { - op: WalkOp, - label_stack: Mutex>, -} - -impl WalkStateInner { - fn new(op: WalkOp) -> Self { - Self { - op, - label_stack: Default::default(), - } - } -} - -#[derive(Clone)] -pub(super) struct WalkState { - inner: Option>, -} - -impl WalkState { - pub const DISABLED: WalkState = WalkState { inner: None }; - - fn new(op: WalkOp) -> Self { - Self { - inner: Some(Arc::new(WalkStateInner::new(op))), - } - } - - fn enabled(&self) -> bool { - self.inner.is_some() - } - - fn op(&self, rrset: &Rrset) { - if let Some(inner) = &self.inner { - let labels = inner.label_stack.lock().unwrap(); - let mut dname = DnameBuilder::new_bytes(); - for label in labels.iter().rev() { - dname.append_label(label.as_slice()).unwrap(); - } - let owner = dname.into_dname().unwrap(); - (inner.op)(owner, rrset); - } - } - - fn push(&self, label: OwnedLabel) { - if let Some(inner) = &self.inner { - inner.label_stack.lock().unwrap().push(label); - } - } - - fn pop(&self) { - if let Some(inner) = &self.inner { - inner.label_stack.lock().unwrap().pop(); - } - } -} - -impl ReadableZone for ReadZone { - fn is_async(&self) -> bool { - false - } - - fn query( - &self, - qname: Dname, - qtype: Rtype, - ) -> Result { - let mut qname = self.apex.prepare_name(&qname)?; - - let answer = if let Some(label) = qname.next() { - self.query_below_apex(label, qname, qtype, WalkState::DISABLED) - } else { - self.query_rrsets(self.apex.rrsets(), qtype, WalkState::DISABLED) - }; - - Ok(answer.into_answer(self)) - } - - fn walk(&self, op: WalkOp) { - // https://datatracker.ietf.org/doc/html/rfc8482 notes that the ANY - // query type is problematic and should be answered as minimally as - // possible. Rather than use ANY internally here to achieve a walk, as - // specific behaviour may actually be wanted for ANY we instead use - // the presence of a callback `op` to indicate that walking mode is - // requested. We still have to pass an Rtype but it won't be used for - // matching when in walk mode, so we set it to Any as it most closely - // matches our intent and will be ignored anyway. - let walk = WalkState::new(op); - self.query_rrsets(self.apex.rrsets(), Rtype::Any, walk.clone()); - self.query_below_apex(Label::root(), iter::empty(), Rtype::Any, walk); - } -} - impl ReadZone { fn query_below_apex<'l>( &self, @@ -367,6 +227,44 @@ impl ReadZone { } } +//--- impl ReadableZone + +impl ReadableZone for ReadZone { + fn is_async(&self) -> bool { + false + } + + fn query( + &self, + qname: Dname, + qtype: Rtype, + ) -> Result { + let mut qname = self.apex.prepare_name(&qname)?; + + let answer = if let Some(label) = qname.next() { + self.query_below_apex(label, qname, qtype, WalkState::DISABLED) + } else { + self.query_rrsets(self.apex.rrsets(), qtype, WalkState::DISABLED) + }; + + Ok(answer.into_answer(self)) + } + + fn walk(&self, op: WalkOp) { + // https://datatracker.ietf.org/doc/html/rfc8482 notes that the ANY + // query type is problematic and should be answered as minimally as + // possible. Rather than use ANY internally here to achieve a walk, as + // specific behaviour may actually be wanted for ANY we instead use + // the presence of a callback `op` to indicate that walking mode is + // requested. We still have to pass an Rtype but it won't be used for + // matching when in walk mode, so we set it to Any as it most closely + // matches our intent and will be ignored anyway. + let walk = WalkState::new(op); + self.query_rrsets(self.apex.rrsets(), Rtype::Any, walk.clone()); + self.query_below_apex(Label::root(), iter::empty(), Rtype::Any, walk); + } +} + //------------ NodeAnswer ---------------------------------------------------- /// An answer that includes instructions to the apex on what it needs to do. @@ -433,174 +331,3 @@ impl NodeAnswer { self.answer } } - -//------------ Answer -------------------------------------------------------- - -#[derive(Clone)] -pub struct Answer { - /// The response code of the answer. - rcode: Rcode, - - /// The content of the answer. - content: AnswerContent, - - /// The optional authority section to be included in the answer. - authority: Option, -} - -impl Answer { - pub fn new(rcode: Rcode) -> Self { - Answer { - rcode, - content: AnswerContent::NoData, - authority: Default::default(), - } - } - - pub fn with_authority(rcode: Rcode, authority: AnswerAuthority) -> Self { - Answer { - rcode, - content: AnswerContent::NoData, - authority: Some(authority), - } - } - - pub fn other(rcode: Rcode) -> Self { - Answer::new(rcode) - } - - pub fn refused() -> Self { - Answer::new(Rcode::Refused) - } - - pub fn add_cname(&mut self, cname: SharedRr) { - self.content = AnswerContent::Cname(cname); - } - - pub fn add_answer(&mut self, answer: SharedRrset) { - self.content = AnswerContent::Data(answer); - } - - pub fn add_authority(&mut self, authority: AnswerAuthority) { - self.authority = Some(authority) - } - - pub fn to_message( - &self, - message: &Message, - builder: MessageBuilder, - ) -> AdditionalBuilder { - let question = message.sole_question().unwrap(); - let qname = question.qname(); - let qclass = question.qclass(); - let mut builder = builder.start_answer(message, self.rcode).unwrap(); - - match self.content { - AnswerContent::Data(ref answer) => { - for item in answer.data() { - // TODO: This will panic if too many answers were given, - // rather than give the caller a way to push the rest into - // another message. - builder - .push((qname, qclass, answer.ttl(), item)) - .unwrap(); - } - } - AnswerContent::Cname(ref cname) => builder - .push((qname, qclass, cname.ttl(), cname.data())) - .unwrap(), - AnswerContent::NoData => {} - } - - let mut builder = builder.authority(); - if let Some(authority) = self.authority.as_ref() { - if let Some(soa) = authority.soa.as_ref() { - builder - .push(( - authority.owner.clone(), - qclass, - soa.ttl(), - soa.data(), - )) - .unwrap(); - } - if let Some(ns) = authority.ns.as_ref() { - for item in ns.data() { - builder - .push(( - authority.owner.clone(), - qclass, - ns.ttl(), - item, - )) - .unwrap() - } - } - if let Some(ref ds) = authority.ds { - for item in ds.data() { - builder - .push(( - authority.owner.clone(), - qclass, - ds.ttl(), - item, - )) - .unwrap() - } - } - } - - builder.additional() - } - - pub fn rcode(&self) -> Rcode { - self.rcode - } - - pub fn content(&self) -> &AnswerContent { - &self.content - } - - pub fn authority(&self) -> Option<&AnswerAuthority> { - self.authority.as_ref() - } -} - -//------------ AnswerContent ------------------------------------------------- - -/// The content of the answer. -#[derive(Clone)] -pub enum AnswerContent { - Data(SharedRrset), - Cname(SharedRr), - NoData, -} - -//------------ AnswerAuthority ----------------------------------------------- - -/// The authority section of a query answer. -#[derive(Clone)] -pub struct AnswerAuthority { - /// The owner name of the record sets in the authority section. - owner: StoredDname, - - /// The SOA record if it should be included. - soa: Option, - - /// The NS record set if it should be included. - ns: Option, - - /// The DS record set if it should be included.. - ds: Option, -} - -impl AnswerAuthority { - pub fn new( - owner: StoredDname, - soa: Option, - ns: Option, - ds: Option, - ) -> Self { - AnswerAuthority { owner, soa, ns, ds } - } -} diff --git a/src/zonetree/versioned.rs b/src/zonetree/in_memory/versioned.rs similarity index 93% rename from src/zonetree/versioned.rs rename to src/zonetree/in_memory/versioned.rs index 08ba0d263..ce4ce2b38 100644 --- a/src/zonetree/versioned.rs +++ b/src/zonetree/in_memory/versioned.rs @@ -78,3 +78,8 @@ impl Default for Versioned { Self::new() } } + +//------------ VersionMarker ------------------------------------------------- + +#[derive(Debug)] +pub struct VersionMarker; diff --git a/src/zonetree/write.rs b/src/zonetree/in_memory/write.rs similarity index 82% rename from src/zonetree/write.rs rename to src/zonetree/in_memory/write.rs index fc9f0a699..681794481 100644 --- a/src/zonetree/write.rs +++ b/src/zonetree/in_memory/write.rs @@ -1,77 +1,26 @@ //! Write access to zones. -use super::nodes::{Special, ZoneApex, ZoneCut, ZoneNode}; -use super::rrset::{SharedRr, SharedRrset}; -use super::versioned::Version; -use super::zone::ZoneVersions; -use crate::base::iana::Rtype; -use crate::base::name::Label; use core::future::ready; -use futures::future::Either; -use parking_lot::RwLock; use std::boxed::Box; use std::future::Future; use std::pin::Pin; use std::sync::Arc; +use std::sync::Weak; +use std::vec::Vec; use std::{fmt, io}; -use tokio::sync::OwnedMutexGuard; - -//------------ WritableZone -------------------------------------------------- - -pub trait WriteableZoneNode { - #[allow(clippy::type_complexity)] - fn update_child( - &self, - label: &Label, - ) -> Pin< - Box< - dyn Future< - Output = Result, io::Error>, - >, - >, - >; - fn update_rrset( - &self, - rrset: SharedRrset, - ) -> Pin>>>; - - fn remove_rrset( - &self, - rtype: Rtype, - ) -> Pin>>>; - - fn make_regular( - &self, - ) -> Pin>>>; - - fn make_zone_cut( - &self, - cut: ZoneCut, - ) -> Pin>>>; - - fn make_cname( - &self, - cname: SharedRr, - ) -> Pin>>>; -} +use futures::future::Either; +use parking_lot::RwLock; +use tokio::sync::OwnedMutexGuard; -pub trait WriteableZone { - #[allow(clippy::type_complexity)] - fn open( - &self, - ) -> Pin< - Box< - dyn Future< - Output = Result, io::Error>, - >, - >, - >; +use crate::base::iana::Rtype; +use crate::base::name::Label; +use crate::zonetree::types::ZoneCut; +use crate::zonetree::SharedRr; +use crate::zonetree::{SharedRrset, WriteableZone, WriteableZoneNode}; - fn commit( - &mut self, - ) -> Pin>>>; -} +use super::nodes::{Special, ZoneApex, ZoneNode}; +use super::versioned::{Version, VersionMarker}; //------------ WriteZone ----------------------------------------------------- @@ -100,6 +49,8 @@ impl WriteZone { } } +//--- impl Clone + impl Clone for WriteZone { fn clone(&self) -> Self { Self { @@ -112,6 +63,8 @@ impl Clone for WriteZone { } } +//--- impl Drop + impl Drop for WriteZone { fn drop(&mut self) { if self.dirty { @@ -396,3 +349,65 @@ impl fmt::Display for WriteApexError { } } } + +//------------ ZoneVersions -------------------------------------------------- + +#[derive(Debug)] +pub struct ZoneVersions { + current: (Version, Arc), + all: Vec<(Version, Weak)>, +} + +impl ZoneVersions { + #[allow(unused)] + pub fn update_current(&mut self, version: Version) -> Arc { + let marker = Arc::new(VersionMarker); + self.current = (version, marker.clone()); + marker + } + + #[allow(unused)] + pub fn push_version( + &mut self, + version: Version, + marker: Arc, + ) { + self.all.push((version, Arc::downgrade(&marker))) + } + + #[allow(unused)] + pub fn clean_versions(&mut self) -> Option { + let mut max_version = None; + self.all.retain(|item| { + if item.1.strong_count() > 0 { + true + } else { + match max_version { + Some(old) => { + if item.0 > old { + max_version = Some(item.0) + } + } + None => max_version = Some(item.0), + } + false + } + }); + max_version + } + + pub fn current(&self) -> &(Version, Arc) { + &self.current + } +} + +impl Default for ZoneVersions { + fn default() -> Self { + let marker = Arc::new(VersionMarker); + let weak_marker = Arc::downgrade(&marker); + ZoneVersions { + current: (Version::default(), marker), + all: vec![(Version::default(), weak_marker)], + } + } +} diff --git a/src/zonetree/mod.rs b/src/zonetree/mod.rs index 6e0288a8c..bc4970cb4 100644 --- a/src/zonetree/mod.rs +++ b/src/zonetree/mod.rs @@ -1,23 +1,25 @@ -#![cfg(feature = "zonefile")] -#![cfg_attr(docsrs, doc(cfg(feature = "zonefile")))] +//! Building, querying and iterating over zone trees. +//! +//! +#![cfg(feature = "unstable-zonetree")] +#![cfg_attr(docsrs, doc(cfg(feature = "unstable-zonetree")))] -pub use self::builder::{CnameError, ZoneBuilder, ZoneCutError}; -pub use self::nodes::OutOfZone; -pub(crate) use self::read::ReadZone; -pub use self::read::{Answer, AnswerContent, ReadableZone, WalkOp}; -pub use self::rrset::{ +mod tree; +mod types; +mod walk; +mod zone; + +pub mod answer; +pub mod in_memory; +pub mod traits; + +pub use self::answer::Answer; +pub use self::traits::{ + ReadableZone, WriteableZone, WriteableZoneNode, ZoneStore, +}; +pub use self::tree::{ZoneExists, ZoneTree}; +pub use self::types::{ Rrset, SharedRr, SharedRrset, StoredDname, StoredRecord, }; -pub use self::set::{ZoneExists, ZoneSet}; -pub use self::versioned::Version; -pub use self::write::WriteableZone; -pub use self::zone::{VersionMarker, Zone, ZoneData, ZoneVersions}; - -mod builder; -mod nodes; -mod read; -mod rrset; -mod set; -mod versioned; -mod write; -mod zone; +pub use self::walk::WalkOp; +pub use self::zone::Zone; diff --git a/src/zonetree/traits.rs b/src/zonetree/traits.rs new file mode 100644 index 000000000..707f11d9a --- /dev/null +++ b/src/zonetree/traits.rs @@ -0,0 +1,128 @@ +use bytes::Bytes; +use core::future::ready; +use core::pin::Pin; +use std::boxed::Box; +use std::fmt::Debug; +use std::future::Future; +use std::io; +use std::sync::Arc; + +use crate::base::iana::Class; +use crate::base::name::Label; +use crate::base::{Dname, Rtype}; +use crate::zonefile::error::OutOfZone; + +use super::answer::Answer; +use super::types::ZoneCut; +use super::{SharedRr, SharedRrset, StoredDname, WalkOp}; + +//------------ ZoneStore ----------------------------------------------------- + +pub trait ZoneStore: Debug + Sync + Send { + /// Returns the class of the zone. + fn class(&self) -> Class; + + /// Returns the apex name of the zone. + fn apex_name(&self) -> &StoredDname; + + fn read(self: Arc) -> Box; + + fn write( + self: Arc, + ) -> Pin>>>; +} + +//------------ ReadableZone -------------------------------------------------- + +pub trait ReadableZone: Send { + fn is_async(&self) -> bool { + true + } + + //--- Sync variants + + fn query( + &self, + _qname: Dname, + _qtype: Rtype, + ) -> Result; + + fn walk(&self, _op: WalkOp); + + //--- Async variants + + fn query_async( + &self, + qname: Dname, + qtype: Rtype, + ) -> Pin> + Send>> { + Box::pin(ready(self.query(qname, qtype))) + } + + fn walk_async( + &self, + op: WalkOp, + ) -> Pin + Send>> { + self.walk(op); + Box::pin(ready(())) + } +} + +//------------ WritableZone -------------------------------------------------- + +pub trait WriteableZone { + #[allow(clippy::type_complexity)] + fn open( + &self, + ) -> Pin< + Box< + dyn Future< + Output = Result, io::Error>, + >, + >, + >; + + fn commit( + &mut self, + ) -> Pin>>>; +} + +//------------ WriteableZoneNode ---------------------------------------------- + +pub trait WriteableZoneNode { + #[allow(clippy::type_complexity)] + fn update_child( + &self, + label: &Label, + ) -> Pin< + Box< + dyn Future< + Output = Result, io::Error>, + >, + >, + >; + + fn update_rrset( + &self, + rrset: SharedRrset, + ) -> Pin>>>; + + fn remove_rrset( + &self, + rtype: Rtype, + ) -> Pin>>>; + + fn make_regular( + &self, + ) -> Pin>>>; + + fn make_zone_cut( + &self, + cut: ZoneCut, + ) -> Pin>>>; + + fn make_cname( + &self, + cname: SharedRr, + ) -> Pin>>>; +} diff --git a/src/zonetree/set.rs b/src/zonetree/tree.rs similarity index 96% rename from src/zonetree/set.rs rename to src/zonetree/tree.rs index 5d33d4b3e..b0142f619 100644 --- a/src/zonetree/set.rs +++ b/src/zonetree/tree.rs @@ -9,15 +9,15 @@ use std::fmt::Display; use std::io; use std::vec::Vec; -//------------ ZoneSet ------------------------------------------------------- +//------------ ZoneTree ------------------------------------------------------ -/// The set of zones we are authoritative for. +/// The hierarchy of zones we are authoritative for. #[derive(Default)] -pub struct ZoneSet { +pub struct ZoneTree { roots: Roots, } -impl ZoneSet { +impl ZoneTree { pub fn new() -> Self { Default::default() } @@ -138,7 +138,7 @@ pub struct ZoneSetIter<'a> { } impl<'a> ZoneSetIter<'a> { - fn new(set: &'a ZoneSet) -> Self { + fn new(set: &'a ZoneTree) -> Self { ZoneSetIter { roots: set.roots.others.values(), nodes: NodesIter::new(&set.roots.in_), diff --git a/src/zonetree/rrset.rs b/src/zonetree/types.rs similarity index 94% rename from src/zonetree/rrset.rs rename to src/zonetree/types.rs index 2e7773e6c..7bc0b9612 100644 --- a/src/zonetree/rrset.rs +++ b/src/zonetree/types.rs @@ -182,3 +182,13 @@ impl Serialize for SharedRrset { self.as_rrset().serialize(serializer) } } + +//------------ ZoneCut ------------------------------------------------------- + +#[derive(Clone, Debug)] +pub struct ZoneCut { + pub name: StoredDname, + pub ns: SharedRrset, + pub ds: Option, + pub glue: Vec, +} diff --git a/src/zonetree/walk.rs b/src/zonetree/walk.rs new file mode 100644 index 000000000..94743d779 --- /dev/null +++ b/src/zonetree/walk.rs @@ -0,0 +1,68 @@ +use std::boxed::Box; +use std::sync::{Arc, Mutex}; +use std::vec::Vec; + +use bytes::Bytes; + +use super::Rrset; +use crate::base::name::OwnedLabel; +use crate::base::{Dname, DnameBuilder}; + +pub type WalkOp = Box, &Rrset) + Send + Sync>; + +struct WalkStateInner { + op: WalkOp, + label_stack: Mutex>, +} + +impl WalkStateInner { + fn new(op: WalkOp) -> Self { + Self { + op, + label_stack: Default::default(), + } + } +} + +#[derive(Clone)] +pub(super) struct WalkState { + inner: Option>, +} + +impl WalkState { + pub(super) const DISABLED: WalkState = WalkState { inner: None }; + + pub(super) fn new(op: WalkOp) -> Self { + Self { + inner: Some(Arc::new(WalkStateInner::new(op))), + } + } + + pub(super) fn enabled(&self) -> bool { + self.inner.is_some() + } + + pub(super) fn op(&self, rrset: &Rrset) { + if let Some(inner) = &self.inner { + let labels = inner.label_stack.lock().unwrap(); + let mut dname = DnameBuilder::new_bytes(); + for label in labels.iter().rev() { + dname.append_label(label.as_slice()).unwrap(); + } + let owner = dname.into_dname().unwrap(); + (inner.op)(owner, rrset); + } + } + + pub(super) fn push(&self, label: OwnedLabel) { + if let Some(inner) = &self.inner { + inner.label_stack.lock().unwrap().push(label); + } + } + + pub(super) fn pop(&self) { + if let Some(inner) = &self.inner { + inner.label_stack.lock().unwrap().pop(); + } + } +} diff --git a/src/zonetree/zone.rs b/src/zonetree/zone.rs index 4f4e00656..1bb34e3d6 100644 --- a/src/zonetree/zone.rs +++ b/src/zonetree/zone.rs @@ -1,92 +1,47 @@ -use super::read::ReadableZone; -use super::rrset::StoredDname; -use super::versioned::Version; -use super::write::WriteableZone; -use super::ZoneBuilder; -use crate::base::iana::Class; -use crate::zonefile::{inplace, parsed}; -use parking_lot::RwLock; use std::boxed::Box; use std::fmt::Debug; use std::future::Future; use std::pin::Pin; -use std::sync::{Arc, Weak}; -use std::vec::Vec; - -//------------ ZoneMeta ------------------------------------------------------ +use std::sync::Arc; -pub trait ZoneData: Debug + Sync + Send { - /// Returns the class of the zone. - fn class(&self) -> Class; - - /// Returns the apex name of the zone. - fn apex_name(&self) -> &StoredDname; - - fn read( - self: Arc, - current: (Version, Arc), - ) -> Box; +use crate::base::iana::Class; +use crate::zonefile::{inplace, parsed}; - fn write( - self: Arc, - version: Version, - zone_versions: Arc>, - ) -> Pin>>>; -} +use super::in_memory::ZoneBuilder; +use super::traits::WriteableZone; +use super::{ReadableZone, StoredDname, ZoneStore}; +// TODO: Delete Zone, rename ZoneStore to Zone (and make ZoneSetNode generic?) //------------ Zone ---------------------------------------------------------- #[derive(Debug)] pub struct Zone { - data: Arc, - versions: Arc>, + store: Arc, } impl Zone { - pub fn new(data: impl ZoneData + 'static) -> Self { + pub fn new(data: impl ZoneStore + 'static) -> Self { Zone { - data: Arc::new(data), - versions: Default::default(), + store: Arc::new(data), } } pub fn class(&self) -> Class { - self.data.class() + self.store.class() } pub fn apex_name(&self) -> &StoredDname { - self.data.apex_name() + self.store.apex_name() } pub fn read(&self) -> Box { - let current = self.versions.read().current.clone(); - self.data.clone().read(current) + self.store.clone().read() } pub fn write( &self, ) -> Pin>>> { - let version = self.versions.read().current.0.next(); - let zone_versions = self.versions.clone(); - self.data.clone().write(version, zone_versions) - } -} - -//--- TryFrom - -impl From for Zone { - fn from(builder: ZoneBuilder) -> Self { - builder.finalize() - } -} - -//--- TryFrom - -impl TryFrom for Zone { - type Error = parsed::ZoneError; - - fn try_from(source: parsed::Zonefile) -> Result { - Ok(Zone::from(ZoneBuilder::try_from(source)?)) + self.store.clone().write() } } @@ -102,65 +57,83 @@ impl TryFrom for Zone { } } -//------------ ZoneVersions -------------------------------------------------- - -#[derive(Debug)] -pub struct ZoneVersions { - current: (Version, Arc), - all: Vec<(Version, Weak)>, -} +//--- TryFrom -impl ZoneVersions { - #[allow(unused)] - pub fn update_current(&mut self, version: Version) -> Arc { - let marker = Arc::new(VersionMarker); - self.current = (version, marker.clone()); - marker +impl From for Zone { + fn from(builder: ZoneBuilder) -> Self { + builder.finalize() } +} - #[allow(unused)] - pub fn push_version( - &mut self, - version: Version, - marker: Arc, - ) { - self.all.push((version, Arc::downgrade(&marker))) - } +//--- TryFrom - #[allow(unused)] - pub fn clean_versions(&mut self) -> Option { - let mut max_version = None; - self.all.retain(|item| { - if item.1.strong_count() > 0 { - true - } else { - match max_version { - Some(old) => { - if item.0 > old { - max_version = Some(item.0) - } - } - None => max_version = Some(item.0), - } - false - } - }); - max_version - } -} +impl TryFrom for Zone { + type Error = parsed::ZoneError; -impl Default for ZoneVersions { - fn default() -> Self { - let marker = Arc::new(VersionMarker); - let weak_marker = Arc::downgrade(&marker); - ZoneVersions { - current: (Version::default(), marker), - all: vec![(Version::default(), weak_marker)], - } + fn try_from(source: parsed::Zonefile) -> Result { + Ok(Zone::from(ZoneBuilder::try_from(source)?)) } } -//------------ VersionMarker ------------------------------------------------- - -#[derive(Debug)] -pub struct VersionMarker; +// //------------ ZoneVersions -------------------------------------------------- + +// #[derive(Debug)] +// pub struct ZoneVersions { +// current: (Version, Arc), +// all: Vec<(Version, Weak)>, +// } + +// impl ZoneVersions { +// #[allow(unused)] +// pub fn update_current(&mut self, version: Version) -> Arc { +// let marker = Arc::new(VersionMarker); +// self.current = (version, marker.clone()); +// marker +// } + +// #[allow(unused)] +// pub fn push_version( +// &mut self, +// version: Version, +// marker: Arc, +// ) { +// self.all.push((version, Arc::downgrade(&marker))) +// } + +// #[allow(unused)] +// pub fn clean_versions(&mut self) -> Option { +// let mut max_version = None; +// self.all.retain(|item| { +// if item.1.strong_count() > 0 { +// true +// } else { +// match max_version { +// Some(old) => { +// if item.0 > old { +// max_version = Some(item.0) +// } +// } +// None => max_version = Some(item.0), +// } +// false +// } +// }); +// max_version +// } +// } + +// impl Default for ZoneVersions { +// fn default() -> Self { +// let marker = Arc::new(VersionMarker); +// let weak_marker = Arc::downgrade(&marker); +// ZoneVersions { +// current: (Version::default(), marker), +// all: vec![(Version::default(), weak_marker)], +// } +// } +// } + +// //------------ VersionMarker ------------------------------------------------- + +// #[derive(Debug)] +// pub struct VersionMarker;