Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ZonefileFmt trait for printing records as zonefiles #379

Merged
merged 22 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ae9e379
print algorithm of DS rdata as an integer instead of mnemonic
tertsdiepraam Aug 29, 2024
a4efde7
add Present trait for printing Records as zonefile
tertsdiepraam Aug 30, 2024
fceb571
print owner with dot in Present
tertsdiepraam Aug 30, 2024
fb2d573
clean some things up
tertsdiepraam Sep 2, 2024
955d129
show instead of present
tertsdiepraam Sep 4, 2024
d9eade9
Merge branch 'main' into ds-print-secalg-as-int
tertsdiepraam Sep 4, 2024
ac0fca8
write some tests for Show
tertsdiepraam Sep 4, 2024
3fe8911
add more info to show impls
tertsdiepraam Sep 6, 2024
62b1fbc
implement fmt_with_dot on ToName
tertsdiepraam Sep 6, 2024
6b2a677
remove some uses of std
tertsdiepraam Sep 6, 2024
48da400
rename Show to ZonefileFmt
tertsdiepraam Sep 27, 2024
a81e1bb
Merge branch 'main' into ds-print-secalg-as-int
tertsdiepraam Sep 27, 2024
52e2490
do not use return type impl trait for ZonefileFmt
tertsdiepraam Sep 27, 2024
930dde2
improve and document pretty TTL format
tertsdiepraam Sep 27, 2024
a2d02bd
small stylistic improvements
tertsdiepraam Sep 27, 2024
1f9e77d
rename ZonefileFmt::show to fmt
tertsdiepraam Sep 30, 2024
5be5419
rename more show occurrences to fmt
tertsdiepraam Oct 16, 2024
baec4e6
Merge branch 'main' into ds-print-secalg-as-int
tertsdiepraam Oct 16, 2024
c30ad83
fix rustfmt
tertsdiepraam Oct 16, 2024
4f5084f
fix rustfmt again
tertsdiepraam Oct 16, 2024
bbb002e
change manual write call to write! macro
tertsdiepraam Oct 22, 2024
9b63eda
Remove dyn object from SimpleWriter and MultilineWriter
tertsdiepraam Oct 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/base/iana/class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,34 @@ int_enum! {
///
/// This class is defined in RFC 1035 and really the only one relevant
/// at all.
(IN => 1, b"IN")
(IN => 1, "IN")

/// Chaosnet (CH).
///
/// A network protocol developed at MIT in the 1970s. Reused by BIND for
/// built-in server information zones.",
(CH => 3, b"CH")
(CH => 3, "CH")

/// Hesiod (HS).
///
/// A system information protocol part of MIT's Project Athena.",
(HS => 4, b"HS")
(HS => 4, "HS")

/// Query class None.
///
/// Defined in RFC 2136, this class is used in UPDATE queries to
/// require that an RRset does not exist prior to the update.",
(NONE => 0xFE, b"NONE")
(NONE => 0xFE, "NONE")

/// Query class * (ANY).
///
/// This class can be used in a query to indicate that records for the
/// given name from any class are requested.",
(ANY => 0xFF, b"*")
(ANY => 0xFF, "*")
}

int_enum_str_with_prefix!(Class, "CLASS", b"CLASS", u16, "unknown class");
int_enum_zonefile_fmt_with_prefix!(Class, "CLASS");

//============ Tests =========================================================

Expand Down
9 changes: 5 additions & 4 deletions src/base/iana/digestalg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,32 @@ int_enum! {
/// Specifies that the SHA-1 hash function is used.
///
/// Implementation of this function is currently mandatory.
(SHA1 => 1, b"SHA-1")
(SHA1 => 1, "SHA-1")

/// Specifies that the SHA-256 hash function is used.
///
/// Implementation of this function is currently mandatory.
(SHA256 => 2, b"SHA-256")
(SHA256 => 2, "SHA-256")

/// Specifies that the GOST R 34.11-94 hash function is used.
///
/// Use of this hash function is described in [RFC 5933]. Implementing
/// the function is optional.
///
/// [RFC 5933]: https://tools.ietf.org/html/rfc5933
(GOST => 3, b"GOST R 34.11-94")
(GOST => 3, "GOST R 34.11-94")

/// Specifies that the SHA-384 hash function is used.
///
/// Use of this hash function is described in [RFC 6605]. Implementing
/// the function is optional.
///
/// [RFC 6605]: https://tools.ietf.org/html/rfc6605
(SHA384 => 4, b"SHA-384")
(SHA384 => 4, "SHA-384")
}

int_enum_str_decimal!(DigestAlg, u8);
int_enum_zonefile_fmt_decimal!(DigestAlg, "digest type");

//============ Tests =========================================================

Expand Down
60 changes: 27 additions & 33 deletions src/base/iana/exterr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,103 +19,103 @@ int_enum! {
/// match known extended error codes. Implementations SHOULD
/// include an EXTRA-TEXT value to augment this error code with
/// additional information.
(OTHER => 0, b"Other Error")
(OTHER => 0, "Other Error")

/// The resolver attempted to perform DNSSEC validation, but a DNSKEY
/// RRset contained only unsupported DNSSEC algorithms.
(UNSUPPORTED_DNSKEY_ALGORITHM => 1, b"Unsupported DNSKEY Algorithm")
(UNSUPPORTED_DNSKEY_ALGORITHM => 1, "Unsupported DNSKEY Algorithm")

/// The resolver attempted to perform DNSSEC validation, but a DS
/// RRset contained only unsupported Digest Types.
(UNSUPPORTED_DS_DIGEST_TYPE => 2, b"Unsupported DS Digest Type")
(UNSUPPORTED_DS_DIGEST_TYPE => 2, "Unsupported DS Digest Type")

/// The resolver was unable to resolve the answer within its time
/// limits and decided to answer with previously cached data
/// instead of answering with an error. This is typically caused
/// by problems communicating with an authoritative server,
/// possibly as result of a denial of service (DoS) attack against
/// another network. (See also Code 19.)
(STALE_ANSWER => 3, b"Stale Answer")
(STALE_ANSWER => 3, "Stale Answer")

/// For policy reasons (legal obligation or malware filtering, for
/// instance), an answer was forged. Note that this should be
/// used when an answer is still provided, not when failure
/// codes are returned instead. See Blocked (15), Censored
/// (16), and Filtered (17) for use when returning other
/// response codes.
(FORGED_ANSWER => 4, b"Forged Answer")
(FORGED_ANSWER => 4, "Forged Answer")

/// The resolver attempted to perform DNSSEC validation, but
/// validation ended in the Indeterminate state [RFC 4035].
///
/// [RFC 4035]: https://tools.ietf.org/html/rfc4035
(DNSSEC_INDETERMINATE => 5, b"DNSSEC Indeterminate")
(DNSSEC_INDETERMINATE => 5, "DNSSEC Indeterminate")

/// The resolver attempted to perform DNSSEC validation, but
/// validation ended in the Bogus state.
(DNSSEC_BOGUS => 6, b"DNSSEC Bogus")
(DNSSEC_BOGUS => 6, "DNSSEC Bogus")

/// The resolver attempted to perform DNSSEC validation, but no
/// signatures are presently valid and some (often all) are
/// expired.
(SIGNATURE_EXPIRED => 7, b"Signature Expired")
(SIGNATURE_EXPIRED => 7, "Signature Expired")

/// The resolver attempted to perform DNSSEC validation, but no
/// signatures are presently valid and at least some are not yet
/// valid.
(SIGNATURE_NOT_YET_VALID => 8, b"Signature Not Yet Valid")
(SIGNATURE_NOT_YET_VALID => 8, "Signature Not Yet Valid")

/// A DS record existed at a parent, but no supported matching
/// DNSKEY record could be found for the child.
(DNSKEY_MISSING => 9, b"DNSKEY Missing")
(DNSKEY_MISSING => 9, "DNSKEY Missing")

/// The resolver attempted to perform DNSSEC validation, but no
/// RRSIGs could be found for at least one RRset where RRSIGs were
/// expected.
(RRSIGS_MISSING => 10, b"RRSIGs Missing")
(RRSIGS_MISSING => 10, "RRSIGs Missing")

/// The resolver attempted to perform DNSSEC validation, but no
/// Zone Key Bit was set in a DNSKEY.
(NO_ZONE_KEY_BIT_SET => 11, b"No Zone Key Bit Set")
(NO_ZONE_KEY_BIT_SET => 11, "No Zone Key Bit Set")

/// The resolver attempted to perform DNSSEC validation, but the
/// requested data was missing and a covering NSEC or NSEC3 was
/// not provided.
(NSEC_MISSING => 12, b"NSEC Missing")
(NSEC_MISSING => 12, "NSEC Missing")

/// The resolver is returning the SERVFAIL RCODE from its cache.
(CACHED_ERROR => 13, b"Cached Error")
(CACHED_ERROR => 13, "Cached Error")

/// The server is unable to answer the query, as it was not fully
/// functional when the query was received.
(NOT_READY => 14, b"Not Ready")
(NOT_READY => 14, "Not Ready")

/// The server is unable to respond to the request because the
/// domain is on a blocklist due to an internal security policy
/// imposed by the operator of the server resolving or forwarding
/// the query.
(BLOCKED => 15, b"Blocked")
(BLOCKED => 15, "Blocked")

/// The server is unable to respond to the request because the
/// domain is on a blocklist due to an external requirement
/// imposed by an entity other than the operator of the server
/// resolving or forwarding the query. Note that how the imposed
/// policy is applied is irrelevant (in-band DNS filtering, court
/// order, etc.).
(CENSORED => 16, b"Censored")
(CENSORED => 16, "Censored")

/// The server is unable to respond to the request because the
/// domain is on a blocklist as requested by the client.
/// Functionally, this amounts to "you requested that we filter
/// domains like this one."
(FILTERED => 17, b"Filtered")
(FILTERED => 17, "Filtered")

/// An authoritative server or recursive resolver that receives a
/// query from an "unauthorized" client can annotate its REFUSED
/// message with this code. Examples of "unauthorized" clients are
/// recursive queries from IP addresses outside the network,
/// blocklisted IP addresses, local policy, etc.
(PROHIBITED => 18, b"Prohibited")
(PROHIBITED => 18, "Prohibited")

/// The resolver was unable to resolve an answer within its
/// configured time limits and decided to answer with a previously
Expand All @@ -124,31 +124,31 @@ int_enum! {
/// with an authoritative server, possibly as result of a denial
/// of service (DoS) attack against another network. (See also
/// Code 3.)
(STALE_NXDOMAIN_ANSWER => 19, b"Stale NXDomain Answer")
(STALE_NXDOMAIN_ANSWER => 19, "Stale NXDomain Answer")

/// An authoritative server that receives a query with the
/// Recursion Desired (RD) bit clear, or when it is not configured
/// for recursion for a domain for which it is not authoritative,
/// SHOULD include this EDE code in the REFUSED response. A
/// resolver that receives a query with the RD bit clear SHOULD
/// include this EDE code in the REFUSED response.
(NOT_AUTHORITATIVE => 20, b"Not Authoritative")
(NOT_AUTHORITATIVE => 20, "Not Authoritative")

/// The requested operation or query is not supported.
(NOT_SUPPORTED => 21, b"Not Supported")
(NOT_SUPPORTED => 21, "Not Supported")

/// The resolver could not reach any of the authoritative name
/// servers (or they potentially refused to reply).
(NO_REACHABLE_AUTHORITY => 22, b"No Reachable Authority")
(NO_REACHABLE_AUTHORITY => 22, "No Reachable Authority")

/// An unrecoverable error occurred while communicating with
/// another server.
(NETWORK_ERROR => 23, b"Network Error")
(NETWORK_ERROR => 23, "Network Error")

/// The authoritative server cannot answer with data for a zone it
/// is otherwise configured to support. Examples of this include
/// its most recent zone being too old or having expired.
(INVALID_DATA => 24, b"Invalid Data")
(INVALID_DATA => 24, "Invalid Data")
}

/// Start of the private range for EDE codes.
Expand All @@ -164,14 +164,8 @@ pub const EDE_PRIVATE_RANGE_BEGIN: u16 = 49152;
// bundled by the `int_enum_*` macros is not very useful.
impl core::fmt::Display for ExtendedErrorCode {
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)?
}
Ok(())
}
match self.to_mnemonic_str() {
Some(m) => f.write_str(m),
None => write!(f, "EDE{}", self.to_int()),
}
}
Expand Down
90 changes: 72 additions & 18 deletions src/base/iana/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ macro_rules! int_enum {
#[must_use]
pub fn from_mnemonic(m: &[u8]) -> Option<Self> {
$(
if m.eq_ignore_ascii_case($mnemonic) {
if m.eq_ignore_ascii_case($mnemonic.as_bytes()) {
return Some($ianatype::$variant)
}
)*
Expand All @@ -52,6 +52,14 @@ macro_rules! int_enum {
/// is hidden in a `Int` variant.
#[must_use]
pub const fn to_mnemonic(self) -> Option<&'static [u8]> {
match self.to_mnemonic_str() {
Some(m) => Some(m.as_bytes()),
None => None,
bal-e marked this conversation as resolved.
Show resolved Hide resolved
}
}

/// Returns the mnemonic as a `&str` for this value if there is one
pub const fn to_mnemonic_str(self) -> Option<&'static str> {
match self {
$(
$ianatype::$variant => {
Expand Down Expand Up @@ -201,7 +209,14 @@ macro_rules! int_enum_str_decimal {

impl core::fmt::Display for $ianatype {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", self.to_int())
write!(f, "{}", self.to_int())?;

if let Some(m) = self.to_mnemonic_str() {
write!(f, "(")?;
f.write_str(m)?;
write!(f, ")")?;
tertsdiepraam marked this conversation as resolved.
Show resolved Hide resolved
}
Ok(())
}
}

Expand Down Expand Up @@ -273,14 +288,10 @@ macro_rules! int_enum_str_with_decimal {

impl core::fmt::Display for $ianatype {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
use core::fmt::Write;

match self.to_mnemonic() {
match self.to_mnemonic_str() {
Some(m) => {
for ch in m {
f.write_char(*ch as char)?
}
Ok(())
f.write_str(m)?;
write!(f, "({})", self.to_int())
tertsdiepraam marked this conversation as resolved.
Show resolved Hide resolved
}
None => {
write!(f, "{}", self.to_int())
Expand Down Expand Up @@ -389,15 +400,8 @@ 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;

match self.to_mnemonic() {
Some(m) => {
for ch in m {
f.write_char(*ch as char)?
}
Ok(())
}
match self.to_mnemonic_str() {
Some(m) => f.write_str(m),
None => {
write!(f, "{}{}", $str_prefix, self.to_int())
}
Expand Down Expand Up @@ -436,6 +440,56 @@ macro_rules! int_enum_str_with_prefix {
};
}

macro_rules! int_enum_zonefile_fmt_decimal {
($ianatype:ident, $name:expr) => {
impl $crate::base::zonefile_fmt::ZonefileFmt for $ianatype {
fn show(
&self,
p: &mut $crate::base::zonefile_fmt::Presenter,
) -> $crate::base::zonefile_fmt::Result {
p.write_token(self.to_int())?;
p.write_comment(format_args!("{}: {}", $name, self))
}
}
};
}

macro_rules! int_enum_zonefile_fmt_with_decimal {
($ianatype:ident) => {
impl $crate::base::zonefile_fmt::ZonefileFmt for $ianatype {
fn show(
&self,
p: &mut $crate::base::zonefile_fmt::Presenter,
) -> $crate::base::zonefile_fmt::Result {
match self.to_mnemonic_str() {
Some(m) => p.write_token(m),
None => p.write_token(self.to_int()),
}
}
}
};
}

macro_rules! int_enum_zonefile_fmt_with_prefix {
($ianatype:ident, $str_prefix:expr) => {
impl $crate::base::zonefile_fmt::ZonefileFmt for $ianatype {
fn show(
&self,
p: &mut $crate::base::zonefile_fmt::Presenter,
) -> $crate::base::zonefile_fmt::Result {
match self.to_mnemonic_str() {
Some(m) => p.write_token(m),
None => p.write_token(format_args!(
"{}{}",
$str_prefix,
self.to_int()
)),
}
}
}
};
}

macro_rules! scan_impl {
($ianatype:ident) => {
impl $ianatype {
Expand Down
3 changes: 2 additions & 1 deletion src/base/iana/nsec3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ int_enum! {
Nsec3HashAlg, u8;

/// Specifies that the SHA-1 hash function is used.
(SHA1 => 1, b"SHA-1")
(SHA1 => 1, "SHA-1")
}

int_enum_str_decimal!(Nsec3HashAlg, u8);
int_enum_zonefile_fmt_decimal!(Nsec3HashAlg, "hash algorithm");
Loading