From a4efde7db503b7e6a10bbdce4cd0605b5ce9db7e Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Fri, 30 Aug 2024 16:31:48 +0200 Subject: [PATCH] add Present trait for printing Records as zonefile --- src/base/iana/macros.rs | 49 ++++++++++++++++++++++- src/base/iana/secalg.rs | 2 +- src/base/opt/mod.rs | 11 ++++++ src/base/rdata.rs | 14 +++++++ src/base/record.rs | 29 ++++++++++++++ src/rdata/aaaa.rs | 10 +++++ src/rdata/cds.rs | 30 ++++++++++++++ src/rdata/dnssec.rs | 81 +++++++++++++++++++++++++++++++++++++- src/rdata/macros.rs | 50 +++++++++++++++++++++++ src/rdata/nsec3.rs | 41 +++++++++++++++++++ src/rdata/rfc1035/a.rs | 10 +++++ src/rdata/rfc1035/hinfo.rs | 10 +++++ src/rdata/rfc1035/minfo.rs | 10 +++++ src/rdata/rfc1035/mx.rs | 10 +++++ src/rdata/rfc1035/null.rs | 14 +++++++ src/rdata/rfc1035/soa.rs | 18 +++++++++ src/rdata/rfc1035/txt.rs | 19 +++++++++ src/rdata/srv.rs | 14 +++++++ src/rdata/svcb/params.rs | 31 +++++++++++++++ src/rdata/svcb/rdata.rs | 15 +++++++ src/rdata/tsig.rs | 18 +++++++++ src/rdata/zonemd.rs | 16 ++++++++ src/zonefile/mod.rs | 1 + src/zonefile/present.rs | 55 ++++++++++++++++++++++++++ 24 files changed, 554 insertions(+), 4 deletions(-) create mode 100644 src/zonefile/present.rs diff --git a/src/base/iana/macros.rs b/src/base/iana/macros.rs index 93c1c2b2..31d15e8e 100644 --- a/src/base/iana/macros.rs +++ b/src/base/iana/macros.rs @@ -201,6 +201,24 @@ macro_rules! int_enum_str_decimal { impl core::fmt::Display for $ianatype { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + use std::fmt::Write; + + write!(f, "{}", self.to_int())?; + + if let Some(m) = self.to_mnemonic() { + write!(f, "(")?; + for ch in m { + f.write_char(*ch as char)?; + } + write!(f, ")")?; + } + Ok(()) + } + } + + impl $crate::zonefile::present::Present for $ianatype { + fn present(&self, f: &mut $crate::zonefile::present::ZoneFileFormatter) -> core::fmt::Result { + use std::fmt::Write; write!(f, "{}", self.to_int()) } } @@ -275,6 +293,26 @@ macro_rules! int_enum_str_with_decimal { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { use core::fmt::Write; + match self.to_mnemonic() { + Some(m) => { + for ch in m { + f.write_char(*ch as char)? + } + write!(f, "({})", self.to_int()) + } + None => { + write!(f, "{}", self.to_int()) + } + } + } + } + + impl $crate::zonefile::present::Present for $ianatype { + fn present( + &self, + f: &mut $crate::zonefile::present::ZoneFileFormatter<'_>, + ) -> core::fmt::Result { + use std::fmt::Write; match self.to_mnemonic() { Some(m) => { for ch in m { @@ -389,7 +427,16 @@ macro_rules! int_enum_str_with_prefix { impl core::fmt::Display for $ianatype { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - use core::fmt::Write; + $crate::zonefile::present::Present::write_presentation(self, f) + } + } + + impl $crate::zonefile::present::Present for $ianatype { + fn present( + &self, + f: &mut $crate::zonefile::present::ZoneFileFormatter<'_>, + ) -> core::fmt::Result { + use std::fmt::Write; match self.to_mnemonic() { Some(m) => { diff --git a/src/base/iana/secalg.rs b/src/base/iana/secalg.rs index 15b77c33..59df9e46 100644 --- a/src/base/iana/secalg.rs +++ b/src/base/iana/secalg.rs @@ -116,4 +116,4 @@ int_enum! { (PRIVATEOID => 254, b"PRIVATEOID") } -int_enum_str_with_decimal!(SecAlg, u8, "unknown algorithm"); +int_enum_str_decimal!(SecAlg, u8); diff --git a/src/base/opt/mod.rs b/src/base/opt/mod.rs index 49c9929b..363b6112 100644 --- a/src/base/opt/mod.rs +++ b/src/base/opt/mod.rs @@ -47,6 +47,7 @@ use super::rdata::{ComposeRecordData, ParseRecordData, RecordData}; use super::record::{Record, Ttl}; use super::wire::{Compose, Composer, FormError, ParseError}; use crate::utils::base16; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::cmp::Ordering; use core::marker::PhantomData; use core::{fmt, hash, mem}; @@ -358,6 +359,16 @@ impl + ?Sized> fmt::Debug for Opt { } } +//--- Present + +impl + ?Sized> Present for Opt { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + // XXX TODO Print this properly. + use std::fmt::Write; + f.write_str("OPT ...") + } +} + //------------ OptHeader ----------------------------------------------------- /// The header of an OPT record. diff --git a/src/base/rdata.rs b/src/base/rdata.rs index 8dd5e18c..dbcbd303 100644 --- a/src/base/rdata.rs +++ b/src/base/rdata.rs @@ -20,6 +20,7 @@ use super::iana::Rtype; use super::scan::{Scan, Scanner, ScannerError, Symbol}; use super::wire::{Compose, Composer, ParseError}; use crate::utils::base16; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::cmp::Ordering; use core::fmt; use octseq::octets::{Octets, OctetsFrom}; @@ -477,6 +478,19 @@ impl> fmt::Debug for UnknownRecordData { } } +//--- Present + +impl> Present for UnknownRecordData { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "\\# {}", self.data.as_ref().len())?; + for ch in self.data.as_ref() { + write!(f, " {:02x}", *ch)? + } + Ok(()) + } +} + //============ Errors ======================================================== //------------ LongRecordData ------------------------------------------------ diff --git a/src/base/record.rs b/src/base/record.rs index 2b1ff589..17873739 100644 --- a/src/base/record.rs +++ b/src/base/record.rs @@ -15,6 +15,8 @@ //! [`RecordHeader`]: struct.RecordHeader.html //! [`ParsedRecord`]: struct.ParsedRecord.html +use crate::zonefile::present::{Present, ZoneFileFormatter}; + use super::cmp::CanonicalOrd; use super::iana::{Class, Rtype}; use super::name::{FlattenInto, ParsedName, ToName}; @@ -431,6 +433,26 @@ where } } +//--- Present +impl Present for Record +where + Name: fmt::Display, + Data: RecordData + Present, +{ + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + + write!(f, "{} ", self.owner)?; + self.ttl.present(f)?; + f.write_char(' ')?; + self.class.present(f)?; + f.write_char(' ')?; + self.data.rtype().present(f)?; + f.write_char(' ')?; + self.data.present(f) + } +} + //------------ ComposeRecord ------------------------------------------------- /// A helper trait allowing construction of records on the fly. @@ -1481,6 +1503,13 @@ impl Ttl { } } +impl Present for Ttl { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "{}", self.as_secs()) + } +} + impl core::ops::Add for Ttl { type Output = Ttl; diff --git a/src/rdata/aaaa.rs b/src/rdata/aaaa.rs index 3cd8bc0d..3aa0ea8a 100644 --- a/src/rdata/aaaa.rs +++ b/src/rdata/aaaa.rs @@ -10,6 +10,7 @@ use crate::base::net::Ipv6Addr; use crate::base::rdata::{ComposeRecordData, ParseRecordData, RecordData}; use crate::base::scan::{Scanner, ScannerError}; use crate::base::wire::{Composer, Parse, ParseError}; +use crate::zonefile::present::Present; use core::cmp::Ordering; use core::convert::Infallible; use core::str::FromStr; @@ -156,6 +157,15 @@ impl fmt::Display for Aaaa { } } +//--- Present + +impl Present for Aaaa { + fn present(&self, f: &mut crate::zonefile::present::ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "{}", self.addr) + } +} + //--- AsRef and AsMut impl AsRef for Aaaa { diff --git a/src/rdata/cds.rs b/src/rdata/cds.rs index 0386538c..cf876d81 100644 --- a/src/rdata/cds.rs +++ b/src/rdata/cds.rs @@ -9,6 +9,7 @@ use crate::base::rdata::{ use crate::base::scan::{Scan, Scanner, ScannerError}; use crate::base::wire::{Compose, Composer, Parse, ParseError}; use crate::utils::{base16, base64}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::cmp::Ordering; use core::{fmt, hash}; use octseq::octets::{Octets, OctetsFrom, OctetsInto}; @@ -319,6 +320,18 @@ impl> fmt::Debug for Cdnskey { } } +//--- Present + +impl> Present for Cdnskey { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "{} {} ", self.flags, self.protocol)?; + self.algorithm.present(f)?; + f.write_char(' ')?; + base64::display(&self.public_key, f) + } +} + //------------ Cds ----------------------------------------------------------- #[derive(Clone)] @@ -646,6 +659,23 @@ impl> fmt::Debug for Cds { } } +//--- Present + +impl> Present for Cds { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!( + f, + "{} {} {} ", + self.key_tag, self.algorithm, self.digest_type + )?; + for ch in self.digest.as_ref() { + write!(f, "{:02x}", ch)? + } + Ok(()) + } +} + //------------ parsed -------------------------------------------------------- pub mod parsed { diff --git a/src/rdata/dnssec.rs b/src/rdata/dnssec.rs index 76e98571..7edbd82e 100644 --- a/src/rdata/dnssec.rs +++ b/src/rdata/dnssec.rs @@ -15,6 +15,7 @@ use crate::base::serial::Serial; use crate::base::wire::{Compose, Composer, FormError, Parse, ParseError}; use crate::base::Ttl; use crate::utils::{base16, base64}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::cmp::Ordering; use core::convert::TryInto; use core::{cmp, fmt, hash, str}; @@ -431,6 +432,18 @@ impl> fmt::Debug for Dnskey { } } +//--- Present + +impl> Present for Dnskey { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "{} {} ", self.flags, self.protocol)?; + self.algorithm.present(f)?; + f.write_char(' ')?; + base64::display(&self.public_key, f) + } +} + //------------ ProtoRrsig ---------------------------------------------------- /// The RRSIG RDATA to be included when creating the signature. @@ -757,6 +770,15 @@ impl fmt::Display for Timestamp { } } +//--- Present + +impl Present for Timestamp { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "{}", self.0) + } +} + //--- PartialOrd and CanonicalOrd impl cmp::PartialOrd for Timestamp { @@ -1354,6 +1376,30 @@ where } } +//--- Present + +impl Present for Rrsig +where + Octs: AsRef<[u8]>, + Name: fmt::Display, +{ + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + self.type_covered.present(f)?; + f.write_char(' ')?; + self.algorithm.present(f)?; + f.write_char(' ')?; + self.original_ttl.present(f)?; + f.write_char(' ')?; + self.expiration.present(f)?; + f.write_char(' ')?; + self.inception.present(f)?; + f.write_char(' ')?; + write!(f, " {} {}. ", self.key_tag, self.signer_name)?; + base64::display(&self.signature, f) + } +} + //------------ Nsec ---------------------------------------------------------- #[derive(Clone)] @@ -1634,6 +1680,19 @@ where } } +//--- Present + +impl Present for Nsec +where + Octs: AsRef<[u8]>, + Name: fmt::Display, +{ + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "{}. {}", self.next_name, self.types) + } +} + //------------ Ds ----------------------------------------------------------- #[derive(Clone)] @@ -1945,7 +2004,7 @@ impl> fmt::Display for Ds { write!( f, "{} {} {} ", - self.key_tag, self.algorithm.to_int(), self.digest_type + self.key_tag, self.algorithm, self.digest_type )?; for ch in self.digest.as_ref() { write!(f, "{:02x}", ch)? @@ -1967,6 +2026,24 @@ impl> fmt::Debug for Ds { } } +//--- Present + +impl> Present for Ds { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + + write!(f, "{} ", self.key_tag)?; + self.algorithm.present(f)?; + f.write_char(' ')?; + self.digest_type.present(f)?; + f.write_char(' ')?; + for ch in self.digest.as_ref() { + write!(f, "{:02x}", ch)? + } + Ok(()) + } +} + //------------ RtypeBitmap --------------------------------------------------- #[derive(Clone)] @@ -2432,7 +2509,7 @@ where let buf_len = self.buf.as_ref().len(); for src_pos in (0..buf_len).step_by(34) { let chunk_len = (self.buf.as_ref()[src_pos + 1] as usize) + 2; - let buf = self.buf.as_mut(); + let buf = self.buf.as_mut(); buf.copy_within(src_pos..src_pos+chunk_len, dst_pos); dst_pos += chunk_len; } diff --git a/src/rdata/macros.rs b/src/rdata/macros.rs index 5f855701..7c46980b 100644 --- a/src/rdata/macros.rs +++ b/src/rdata/macros.rs @@ -499,6 +499,25 @@ macro_rules! rdata_types { } } + ///--- Present + + impl $crate::zonefile::present::Present for ZoneRecordData + where + O: AsRef<[u8]>, + N: fmt::Display + { + fn present(&self, f: &mut $crate::zonefile::present::ZoneFileFormatter) -> fmt::Result { + match *self { + $( $( $( + ZoneRecordData::$mtype(ref inner) => { + inner.present(f) + } + )* )* )* + ZoneRecordData::Unknown(ref inner) => inner.present(f), + } + } + } + //------------- AllRecordData ---------------------------------------- /// Record data for all record types. @@ -1102,6 +1121,28 @@ macro_rules! rdata_types { } } + //--- Present + impl $crate::zonefile::present::Present for AllRecordData + where O: Octets, N: fmt::Display { + fn present( + &self, f: &mut $crate::zonefile::present::ZoneFileFormatter + ) -> fmt::Result { + match *self { + $( $( $( + AllRecordData::$mtype(ref inner) => { + inner.present(f) + } + )* )* )* + $( $( $( + AllRecordData::$ptype(ref inner) => { + inner.present(f) + } + )* )* )* + AllRecordData::Opt(ref inner) => inner.present(f), + AllRecordData::Unknown(ref inner) => inner.present(f), + } + } + } } } @@ -1288,6 +1329,15 @@ macro_rules! name_type_base { write!(f, "{}.", self.$field) } } + + //--- Present + + impl $crate::zonefile::present::Present for $target { + fn present(&self, f: &mut $crate::zonefile::present::ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "{}.", self.$field) + } + } } } diff --git a/src/rdata/nsec3.rs b/src/rdata/nsec3.rs index 858af672..7ff7e789 100644 --- a/src/rdata/nsec3.rs +++ b/src/rdata/nsec3.rs @@ -13,6 +13,7 @@ use crate::base::scan::{ }; use crate::base::wire::{Compose, Composer, Parse, ParseError}; use crate::utils::{base16, base32}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; #[cfg(feature = "bytes")] use bytes::Bytes; use core::cmp::Ordering; @@ -369,6 +370,27 @@ impl> fmt::Debug for Nsec3 { } } + +//--- Present + +//--- Present + +impl> Present for Nsec3 +where + Octs: AsRef<[u8]>, +{ + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + + self.hash_algorithm.present(f)?; + write!(f, " {} {} ", self.flags, self.iterations)?; + self.salt.present(f)?; + f.write_char(' ')?; + base32::display_hex(&self.next_owner, f)?; + write!(f, " {}", self.types) + } +} + //------------ Nsec3Param ---------------------------------------------------- #[derive(Clone)] @@ -661,6 +683,17 @@ impl> fmt::Debug for Nsec3param { } } +//--- Present + +impl> Present for Nsec3param { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + self.hash_algorithm.present(f)?; + write!(f, " {} {} ", self.flags, self.iterations)?; + self.salt.present(f) + } +} + //------------ Nsec3Salt ----------------------------------------------------- /// The salt value of an NSEC3 record. @@ -970,6 +1003,14 @@ impl + ?Sized> fmt::Debug for Nsec3Salt { } } +//--- Present + +impl + ?Sized> Present for Nsec3Salt { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + base16::display(self.as_slice(), f) + } +} + //--- Serialize and Deserialize #[cfg(feature = "serde")] diff --git a/src/rdata/rfc1035/a.rs b/src/rdata/rfc1035/a.rs index 885555ee..4b872cfb 100644 --- a/src/rdata/rfc1035/a.rs +++ b/src/rdata/rfc1035/a.rs @@ -10,6 +10,7 @@ use crate::base::rdata::{ }; use crate::base::scan::{Scanner, ScannerError}; use crate::base::wire::{Composer, Parse, ParseError}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::{fmt, str}; use core::cmp::Ordering; use core::convert::Infallible; @@ -172,6 +173,15 @@ impl fmt::Display for A { } } +//--- Display + +impl Present for A { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "{}", self.addr) + } +} + //--- AsRef and AsMut impl AsRef for A { diff --git a/src/rdata/rfc1035/hinfo.rs b/src/rdata/rfc1035/hinfo.rs index 974cc504..de086c5b 100644 --- a/src/rdata/rfc1035/hinfo.rs +++ b/src/rdata/rfc1035/hinfo.rs @@ -10,6 +10,7 @@ use crate::base::rdata::{ }; use crate::base::scan::Scanner; use crate::base::wire::{Composer, ParseError}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::{fmt, hash}; use core::cmp::Ordering; #[cfg(feature = "serde")] @@ -236,6 +237,15 @@ impl> fmt::Debug for Hinfo { } } +//--- Present + +impl> Present for Hinfo { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "{} {}", self.cpu, self.os) + } +} + //============ Testing ======================================================= #[cfg(test)] diff --git a/src/rdata/rfc1035/minfo.rs b/src/rdata/rfc1035/minfo.rs index 7e24a717..69a6c2e4 100644 --- a/src/rdata/rfc1035/minfo.rs +++ b/src/rdata/rfc1035/minfo.rs @@ -10,6 +10,7 @@ use crate::base::rdata::{ }; use crate::base::scan::Scanner; use crate::base::wire::{Composer, ParseError}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::fmt; use core::cmp::Ordering; use octseq::octets::{Octets, OctetsFrom, OctetsInto}; @@ -242,6 +243,15 @@ impl fmt::Display for Minfo { } } +//--- Present + +impl Present for Minfo { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "{}. {}.", self.rmailbx, self.emailbx) + } +} + //============ Testing ======================================================= #[cfg(test)] diff --git a/src/rdata/rfc1035/mx.rs b/src/rdata/rfc1035/mx.rs index 15d0d972..c438563c 100644 --- a/src/rdata/rfc1035/mx.rs +++ b/src/rdata/rfc1035/mx.rs @@ -10,6 +10,7 @@ use crate::base::rdata::{ }; use crate::base::scan::{Scan, Scanner}; use crate::base::wire::{Compose, Composer, Parse, ParseError}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::fmt; use core::cmp::Ordering; use octseq::octets::{Octets, OctetsFrom, OctetsInto}; @@ -227,6 +228,15 @@ impl fmt::Display for Mx { } } +//--- Present + +impl Present for Mx { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "{} {}.", self.preference, self.exchange) + } +} + //============ Testing ======================================================= #[cfg(test)] diff --git a/src/rdata/rfc1035/null.rs b/src/rdata/rfc1035/null.rs index b7ce9110..960edede 100644 --- a/src/rdata/rfc1035/null.rs +++ b/src/rdata/rfc1035/null.rs @@ -8,6 +8,7 @@ use crate::base::rdata::{ ComposeRecordData, LongRecordData, ParseRecordData, RecordData, }; use crate::base::wire::{Composer, ParseError}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::{fmt, hash, mem}; use core::cmp::Ordering; use octseq::octets::{Octets, OctetsFrom, OctetsInto}; @@ -277,6 +278,19 @@ impl> fmt::Debug for Null { } } +//--- Present + +impl> Present for Null { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "\\# {}", self.data.as_ref().len())?; + for ch in self.data.as_ref().iter() { + write!(f, " {:02x}", ch)?; + } + Ok(()) + } +} + //============ Testing ======================================================= #[cfg(test)] diff --git a/src/rdata/rfc1035/soa.rs b/src/rdata/rfc1035/soa.rs index 45fa57b7..26c05b69 100644 --- a/src/rdata/rfc1035/soa.rs +++ b/src/rdata/rfc1035/soa.rs @@ -12,6 +12,7 @@ use crate::base::record::Ttl; use crate::base::scan::{Scan, Scanner}; use crate::base::serial::Serial; use crate::base::wire::{Compose, Composer, ParseError}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::fmt; use core::cmp::Ordering; use octseq::octets::{Octets, OctetsFrom, OctetsInto}; @@ -401,6 +402,23 @@ impl fmt::Display for Soa { } } +impl Present for Soa { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!( + f, + "{}. {}. {} {} {} {} {}", + self.mname, + self.rname, + self.serial, + self.refresh.as_secs(), + self.retry.as_secs(), + self.expire.as_secs(), + self.minimum.as_secs() + ) + } +} + //============ Testing ======================================================= #[cfg(test)] diff --git a/src/rdata/rfc1035/txt.rs b/src/rdata/rfc1035/txt.rs index faf12dcd..b78ef7d8 100644 --- a/src/rdata/rfc1035/txt.rs +++ b/src/rdata/rfc1035/txt.rs @@ -14,6 +14,7 @@ use crate::base::scan::Scanner; #[cfg(feature = "serde")] use crate::base::scan::Symbol; use crate::base::wire::{Composer, FormError, ParseError}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; #[cfg(feature = "bytes")] use bytes::BytesMut; use core::cmp::Ordering; @@ -464,6 +465,24 @@ impl> fmt::Debug for Txt { } } +//--- Present + +impl> Present for Txt { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + let mut first = true; + for slice in self.iter_charstrs() { + if !first { + f.write_str(" ")?; + } else { + first = false; + } + write!(f, "{}", slice.display_quoted())?; + } + Ok(()) + } +} + //--- Serialize and Deserialize #[cfg(feature = "serde")] diff --git a/src/rdata/srv.rs b/src/rdata/srv.rs index f0ce9dd8..f090e235 100644 --- a/src/rdata/srv.rs +++ b/src/rdata/srv.rs @@ -10,6 +10,7 @@ use crate::base::name::{FlattenInto, ParsedName, ToName}; use crate::base::rdata::{ComposeRecordData, ParseRecordData, RecordData}; use crate::base::scan::{Scan, Scanner}; use crate::base::wire::{Compose, Composer, Parse, ParseError}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::cmp::Ordering; use core::fmt; use octseq::octets::{Octets, OctetsFrom, OctetsInto}; @@ -282,6 +283,19 @@ impl fmt::Display for Srv { } } +//--- Present + +impl Present for Srv { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!( + f, + "{} {} {} {}", + self.priority, self.weight, self.port, self.target + ) + } +} + //============ Testing ====================================================== #[cfg(test)] diff --git a/src/rdata/svcb/params.rs b/src/rdata/svcb/params.rs index b6653940..bb25b7ce 100644 --- a/src/rdata/svcb/params.rs +++ b/src/rdata/svcb/params.rs @@ -7,6 +7,7 @@ use crate::base::cmp::CanonicalOrd; use crate::base::iana::SvcParamKey; use crate::base::scan::Symbol; use crate::base::wire::{Compose, Parse, ParseError}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use octseq::builder::{EmptyBuilder, FromBuilder, OctetsBuilder, ShortBuf}; use octseq::octets::{Octets, OctetsFrom, OctetsInto}; use octseq::parse::{Parser, ShortInput}; @@ -361,6 +362,36 @@ impl fmt::Debug for SvcParams { } } +//-- Present + +impl Present for SvcParams { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + let mut parser = Parser::from_ref(self.as_slice()); + let mut first = true; + while parser.remaining() > 0 { + let key = SvcParamKey::parse( + &mut parser + ).expect("invalid SvcbParam"); + let len = usize::from( + u16::parse(&mut parser).expect("invalid SvcParam") + ); + let mut parser = parser.parse_parser( + len + ).expect("invalid SvcParam"); + if first { + first = false; + } + else { + f.write_str(" ")?; + } + write!( + f, "{}", super::value::AllValues::parse_any(key, &mut parser) + )?; + }; + Ok(()) + } +} //------------ ValueIter ----------------------------------------------------- diff --git a/src/rdata/svcb/rdata.rs b/src/rdata/svcb/rdata.rs index 41a42280..6dd75531 100644 --- a/src/rdata/svcb/rdata.rs +++ b/src/rdata/svcb/rdata.rs @@ -10,6 +10,7 @@ use crate::base::rdata::{ ComposeRecordData, LongRecordData, ParseRecordData, RecordData, }; use crate::base::wire::{Compose, Composer, Parse, ParseError}; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use octseq::octets::{Octets, OctetsFrom, OctetsInto}; use octseq::parse::Parser; use core::{cmp, fmt, hash}; @@ -464,6 +465,20 @@ where } } +//--- Present + +impl Present for SvcbRdata +where + Octs: Octets, + Name: fmt::Display, +{ + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!(f, "{} {} ", self.priority, self.target)?; + self.params.present(f) + } +} + //============ Tests ========================================================= #[cfg(test)] diff --git a/src/rdata/tsig.rs b/src/rdata/tsig.rs index f0f4bcfc..5a8532b4 100644 --- a/src/rdata/tsig.rs +++ b/src/rdata/tsig.rs @@ -12,6 +12,7 @@ use crate::base::rdata::{ }; use crate::base::wire::{Compose, Composer, Parse, ParseError}; use crate::utils::base64; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::cmp::Ordering; use core::{fmt, hash}; use octseq::builder::OctetsBuilder; @@ -600,6 +601,23 @@ impl, N: fmt::Debug> fmt::Debug for Tsig { } } +//--- Present + +impl, N: fmt::Display> Present for Tsig { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!( + f, + "{}. {} {} ", + self.algorithm, self.time_signed, self.fudge + )?; + base64::display(&self.mac, f)?; + write!(f, " {} {} \"", self.original_id, self.error)?; + base64::display(&self.other, f)?; + write!(f, "\"") + } +} + //------------ Time48 -------------------------------------------------------- /// A 48-bit Unix timestamp. diff --git a/src/rdata/zonemd.rs b/src/rdata/zonemd.rs index b9414067..93d9fa54 100644 --- a/src/rdata/zonemd.rs +++ b/src/rdata/zonemd.rs @@ -11,6 +11,7 @@ use crate::base::scan::{Scan, Scanner}; use crate::base::serial::Serial; use crate::base::wire::{Composer, ParseError}; use crate::utils::base16; +use crate::zonefile::present::{Present, ZoneFileFormatter}; use core::cmp::Ordering; use core::{fmt, hash}; use octseq::octets::{Octets, OctetsFrom, OctetsInto}; @@ -223,6 +224,21 @@ impl> fmt::Debug for Zonemd { } } +impl> Present for Zonemd { + fn present(&self, f: &mut ZoneFileFormatter) -> fmt::Result { + use std::fmt::Write; + write!( + f, + "{} {} {} ( ", + self.serial, + u8::from(self.scheme), + u8::from(self.algo) + )?; + base16::display(&self.digest, f)?; + write!(f, " )") + } +} + impl PartialOrd> for Zonemd where Octs: AsRef<[u8]>, diff --git a/src/zonefile/mod.rs b/src/zonefile/mod.rs index c208be2a..bd3ccd65 100644 --- a/src/zonefile/mod.rs +++ b/src/zonefile/mod.rs @@ -3,3 +3,4 @@ #![cfg_attr(docsrs, doc(cfg(feature = "zonefile")))] pub mod inplace; +pub mod present; diff --git a/src/zonefile/present.rs b/src/zonefile/present.rs new file mode 100644 index 00000000..c32841f1 --- /dev/null +++ b/src/zonefile/present.rs @@ -0,0 +1,55 @@ +//! Modelled roughly after `std::fmt::Display` + +use core::fmt::{self, Write}; +use std::string::String; + +pub struct ZoneFileFormatter<'a> { + buf: &'a mut (dyn Write + 'a), +} + +pub struct Error; + +/// Print as presentation format +pub trait Present { + fn present(&self, w: &mut ZoneFileFormatter) -> fmt::Result; + + fn write_presentation(&self, mut w: W) -> fmt::Result { + let mut formatter = ZoneFileFormatter { + buf: &mut w, + }; + self.present(&mut formatter) + } + + fn to_presentation_string(&self) -> String { + let mut s = String::new(); + self.write_presentation(&mut s).unwrap(); + s + } +} + +impl ZoneFileFormatter<'_> { + pub fn format(&mut self, item: &P) -> fmt::Result { + item.present(self) + } + + pub fn write_bytes(&mut self, b: &[u8]) -> fmt::Result { + for byte in b { + self.write_char(*byte as char)?; + } + Ok(()) + } +} + +impl Write for ZoneFileFormatter<'_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.buf.write_str(s) + } + + fn write_char(&mut self, c: char) -> fmt::Result { + self.buf.write_char(c) + } + + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result { + self.buf.write_fmt(args) + } +}