use core::fmt;
use core::str::FromStr;
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Rcode(u8);
impl Rcode {
pub const NOERROR: Self = Self(0);
pub const FORMERR: Self = Self(1);
pub const SERVFAIL: Self = Self(2);
pub const NXDOMAIN: Self = Self(3);
pub const NOTIMP: Self = Self(4);
pub const REFUSED: Self = Self(5);
pub const YXDOMAIN: Self = Self(6);
pub const YXRRSET: Self = Self(7);
pub const NXRRSET: Self = Self(8);
pub const NOTAUTH: Self = Self(9);
pub const NOTZONE: Self = Self(10);
}
impl Rcode {
#[must_use]
pub const fn checked_from_int(value: u8) -> Option<Self> {
if value & 0xF0 != 0 {
None
} else {
Some(Rcode(value))
}
}
#[must_use]
pub const fn masked_from_int(value: u8) -> Self {
Rcode(value & 0x0F)
}
#[must_use]
pub const fn to_int(self) -> u8 {
self.0
}
#[must_use]
pub const fn to_mnemonic(self) -> Option<&'static [u8]> {
match self {
Rcode::NOERROR => Some(b"NOERROR"),
Rcode::FORMERR => Some(b"FORMERR"),
Rcode::SERVFAIL => Some(b"SERVFAIL"),
Rcode::NXDOMAIN => Some(b"NXDOMAIN"),
Rcode::NOTIMP => Some(b"NOTIMP"),
Rcode::REFUSED => Some(b"REFUSED"),
Rcode::YXDOMAIN => Some(b"YXDOMAIN"),
Rcode::YXRRSET => Some(b"YXRRSET"),
Rcode::NXRRSET => Some(b"NXRRSET"),
Rcode::NOTAUTH => Some(b"NOTAUTH"),
Rcode::NOTZONE => Some(b"NOTZONE"),
_ => None,
}
}
}
impl FromStr for Rcode {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"NOERROR" => Ok(Rcode::NOERROR),
"FORMERR" => Ok(Rcode::FORMERR),
"SERVFAIL" => Ok(Rcode::SERVFAIL),
"NXDOMAIN" => Ok(Rcode::NXDOMAIN),
"NOTIMP" => Ok(Rcode::NOTIMP),
"REFUSED" => Ok(Rcode::REFUSED),
"YXDOMAIN" => Ok(Rcode::YXDOMAIN),
"YXRRSET" => Ok(Rcode::YXRRSET),
"NXRRSET" => Ok(Rcode::NXRRSET),
"NOTAUTH" => Ok(Rcode::NOTAUTH),
"NOTZONE" => Ok(Rcode::NOTZONE),
_ => Err(()),
}
}
}
impl TryFrom<u8> for Rcode {
type Error = InvalidRcode;
fn try_from(value: u8) -> Result<Self, Self::Error> {
Rcode::checked_from_int(value).ok_or(InvalidRcode(()))
}
}
impl From<Rcode> for u8 {
fn from(value: Rcode) -> u8 {
value.to_int()
}
}
impl fmt::Display for Rcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self
.to_mnemonic()
.and_then(|bytes| core::str::from_utf8(bytes).ok())
{
Some(mnemonic) => f.write_str(mnemonic),
None => self.0.fmt(f),
}
}
}
impl fmt::Debug for Rcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self
.to_mnemonic()
.and_then(|bytes| core::str::from_utf8(bytes).ok())
{
Some(mnemonic) => write!(f, "Rcode::{}", mnemonic),
None => f.debug_tuple("Rcode").field(&self.0).finish(),
}
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Rcode {
fn serialize<S: serde::Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
self.to_int().serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Rcode {
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
u8::deserialize(deserializer).and_then(|code| {
Rcode::try_from(code).map_err(serde::de::Error::custom)
})
}
}
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct OptRcode(u16);
impl OptRcode {
pub const NOERROR: Self = Self::from_rcode(Rcode::NOERROR);
pub const FORMERR: Self = Self::from_rcode(Rcode::FORMERR);
pub const SERVFAIL: Self = Self::from_rcode(Rcode::SERVFAIL);
pub const NXDOMAIN: Self = Self::from_rcode(Rcode::NXDOMAIN);
pub const NOTIMP: Self = Self::from_rcode(Rcode::NOTIMP);
pub const REFUSED: Self = Self::from_rcode(Rcode::REFUSED);
pub const YXDOMAIN: Self = Self::from_rcode(Rcode::YXDOMAIN);
pub const YXRRSET: Self = Self::from_rcode(Rcode::YXRRSET);
pub const NXRRSET: Self = Self::from_rcode(Rcode::NXRRSET);
pub const NOTAUTH: Self = Self::from_rcode(Rcode::NOTAUTH);
pub const NOTZONE: Self = Self::from_rcode(Rcode::NOTZONE);
pub const BADVERS: Self = Self(16);
pub const BADCOOKIE: Self = Self(23);
}
impl OptRcode {
#[must_use]
pub const fn checked_from_int(value: u16) -> Option<OptRcode> {
if value & 0x0FFF != 0 {
None
} else {
Some(Self(value))
}
}
#[must_use]
pub const fn masked_from_int(value: u16) -> OptRcode {
Self(value & 0x0FFF)
}
#[must_use]
pub const fn from_rcode(rcode: Rcode) -> Self {
Self(rcode.0 as u16)
}
#[must_use]
pub const fn to_int(self) -> u16 {
self.0
}
#[must_use]
pub fn from_parts(rcode: Rcode, ext: u8) -> OptRcode {
OptRcode((u16::from(ext) << 4) | u16::from(rcode.to_int()))
}
#[must_use]
pub fn to_parts(self) -> (Rcode, u8) {
(Rcode::masked_from_int(self.0 as u8), (self.0 >> 4) as u8)
}
#[must_use]
pub fn rcode(self) -> Rcode {
self.to_parts().0
}
#[must_use]
pub fn ext(self) -> u8 {
self.to_parts().1
}
#[must_use]
pub fn is_ext(&self) -> bool {
self.0 >> 4 != 0
}
#[must_use]
pub const fn to_mnemonic(self) -> Option<&'static [u8]> {
match self {
OptRcode::NOERROR => Some(b"NOERROR"),
OptRcode::FORMERR => Some(b"FORMERR"),
OptRcode::SERVFAIL => Some(b"SERVFAIL"),
OptRcode::NXDOMAIN => Some(b"NXDOMAIN"),
OptRcode::NOTIMP => Some(b"NOTIMP"),
OptRcode::REFUSED => Some(b"REFUSED"),
OptRcode::YXDOMAIN => Some(b"YXDOMAIN"),
OptRcode::YXRRSET => Some(b"YXRRSET"),
OptRcode::NXRRSET => Some(b"NXRRSET"),
OptRcode::NOTAUTH => Some(b"NOTAUTH"),
OptRcode::NOTZONE => Some(b"NOTZONE"),
OptRcode::BADVERS => Some(b"BADVERS"),
OptRcode::BADCOOKIE => Some(b"BADCOOKIE"),
_ => None,
}
}
}
impl FromStr for OptRcode {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"NOERROR" => Ok(OptRcode::NOERROR),
"FORMERR" => Ok(OptRcode::FORMERR),
"SERVFAIL" => Ok(OptRcode::SERVFAIL),
"NXDOMAIN" => Ok(OptRcode::NXDOMAIN),
"NOTIMP" => Ok(OptRcode::NOTIMP),
"REFUSED" => Ok(OptRcode::REFUSED),
"YXDOMAIN" => Ok(OptRcode::YXDOMAIN),
"YXRRSET" => Ok(OptRcode::YXRRSET),
"NXRRSET" => Ok(OptRcode::NXRRSET),
"NOTAUTH" => Ok(OptRcode::NOTAUTH),
"NOTZONE" => Ok(OptRcode::NOTZONE),
"BADVERS" => Ok(OptRcode::BADVERS),
"BADCOOKIE" => Ok(OptRcode::BADCOOKIE),
_ => Err(()),
}
}
}
impl TryFrom<u16> for OptRcode {
type Error = InvalidRcode;
fn try_from(value: u16) -> Result<Self, Self::Error> {
OptRcode::checked_from_int(value).ok_or(InvalidRcode(()))
}
}
impl From<OptRcode> for u16 {
fn from(value: OptRcode) -> u16 {
value.to_int()
}
}
impl From<Rcode> for OptRcode {
fn from(value: Rcode) -> OptRcode {
OptRcode::from_rcode(value)
}
}
impl fmt::Display for OptRcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self
.to_mnemonic()
.and_then(|bytes| core::str::from_utf8(bytes).ok())
{
Some(mnemonic) => f.write_str(mnemonic),
None => self.0.fmt(f),
}
}
}
impl fmt::Debug for OptRcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self
.to_mnemonic()
.and_then(|bytes| core::str::from_utf8(bytes).ok())
{
Some(mnemonic) => write!(f, "Rcode::{}", mnemonic),
None => f.debug_tuple("Rcode").field(&self.0).finish(),
}
}
}
int_enum! {
=>
TsigRcode, u16;
(NOERROR => 0, "NOERROR")
(FORMERR => 1, "FORMERR")
(SERVFAIL => 2, "SERVFAIL")
(NXDOMAIN => 3, "NXDOMAIN")
(NOTIMP => 4, "NOTIMPL")
(REFUSED => 5, "REFUSED")
(YXDOMAIN => 6, "YXDOMAIN")
(YXRRSET => 7, "YXRRSET")
(NXRRSET => 8, "NXRRSET")
(NOTAUTH => 9, "NOTAUTH")
(NOTZONE => 10, "NOTZONE")
(BADSIG => 16, "BADSIG")
(BADKEY => 17, "BADKEY")
(BADTIME => 18, "BADTIME")
(BADMODE => 19, "BADMODE")
(BADNAME => 20, "BADNAME")
(BADALG => 21, "BADALG")
(BADTRUNC => 22, "BADTRUNC")
(BADCOOKIE => 23, "BADCOOKIE")
}
impl From<Rcode> for TsigRcode {
fn from(value: Rcode) -> TsigRcode {
TsigRcode::from_int(u16::from(value.to_int()))
}
}
impl From<OptRcode> for TsigRcode {
fn from(value: OptRcode) -> TsigRcode {
TsigRcode::from_int(value.to_int())
}
}
int_enum_str_with_decimal!(TsigRcode, u16, "unknown TSIG error");
int_enum_zonefile_fmt_with_decimal!(TsigRcode);
#[derive(Clone, Copy, Debug)]
pub struct InvalidRcode(());
impl fmt::Display for InvalidRcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("invalid rcode value")
}
}
#[cfg(feature = "std")]
impl std::error::Error for InvalidRcode {}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn optrcode_parts() {
macro_rules! assert_opt_rcode_parts_eq {
($name:expr, $high_bits:expr, $low_bits:expr) => {
let u12 = (($high_bits as u16) << 4) | ($low_bits as u16);
assert_eq!($name.rcode().to_int(), $low_bits);
assert_eq!($name.ext(), $high_bits);
assert_eq!($name.to_parts().0.to_int(), $low_bits);
assert_eq!($name.to_parts().1, $high_bits);
assert_eq!($name.to_int(), u12);
};
}
assert_opt_rcode_parts_eq!(OptRcode::NOERROR, 0b000_0000, 0b0000);
assert_opt_rcode_parts_eq!(OptRcode::FORMERR, 0b000_0000, 0b0001);
assert_opt_rcode_parts_eq!(OptRcode::SERVFAIL, 0b000_0000, 0b0010);
assert_opt_rcode_parts_eq!(OptRcode::NXDOMAIN, 0b000_0000, 0b0011);
assert_opt_rcode_parts_eq!(OptRcode::NOTIMP, 0b000_0000, 0b0100);
assert_opt_rcode_parts_eq!(OptRcode::REFUSED, 0b000_0000, 0b0101);
assert_opt_rcode_parts_eq!(OptRcode::YXDOMAIN, 0b000_0000, 0b0110);
assert_opt_rcode_parts_eq!(OptRcode::YXRRSET, 0b000_0000, 0b0111);
assert_opt_rcode_parts_eq!(OptRcode::NXRRSET, 0b000_0000, 0b1000);
assert_opt_rcode_parts_eq!(OptRcode::NOTAUTH, 0b000_0000, 0b1001);
assert_opt_rcode_parts_eq!(OptRcode::NOTZONE, 0b000_0000, 0b1010);
assert_opt_rcode_parts_eq!(OptRcode(15), 0b0000_0000, 0b1111);
assert_opt_rcode_parts_eq!(OptRcode::BADVERS, 0b0000_0001, 0b0000);
assert_opt_rcode_parts_eq!(OptRcode(17), 0b0000_0001, 0b0001);
assert_opt_rcode_parts_eq!(OptRcode::BADCOOKIE, 0b000_0001, 0b0111);
assert_opt_rcode_parts_eq!(OptRcode(4094), 0b1111_1111, 0b1110);
assert_opt_rcode_parts_eq!(OptRcode(4095), 0b1111_1111, 0b1111);
}
#[test]
fn rcode_fromstr() {
assert_eq!(Ok(Rcode::NOERROR), "NOERROR".parse());
assert_eq!(Ok(Rcode::FORMERR), "FORMERR".parse());
assert_eq!(Ok(Rcode::SERVFAIL), "SERVFAIL".parse());
assert_eq!(Ok(Rcode::NXDOMAIN), "NXDOMAIN".parse());
assert_eq!(Ok(Rcode::NOTIMP), "NOTIMP".parse());
assert_eq!(Ok(Rcode::REFUSED), "REFUSED".parse());
assert_eq!(Ok(Rcode::YXDOMAIN), "YXDOMAIN".parse());
assert_eq!(Ok(Rcode::YXRRSET), "YXRRSET".parse());
assert_eq!(Ok(Rcode::NXRRSET), "NXRRSET".parse());
assert_eq!(Ok(Rcode::NOTAUTH), "NOTAUTH".parse());
assert_eq!(Ok(Rcode::NOTZONE), "NOTZONE".parse());
assert!("#$%!@".parse::<Rcode>().is_err());
}
#[test]
fn optrcode_fromstr() {
assert_eq!(Ok(OptRcode::NOERROR), "NOERROR".parse());
assert_eq!(Ok(OptRcode::FORMERR), "FORMERR".parse());
assert_eq!(Ok(OptRcode::SERVFAIL), "SERVFAIL".parse());
assert_eq!(Ok(OptRcode::NXDOMAIN), "NXDOMAIN".parse());
assert_eq!(Ok(OptRcode::NOTIMP), "NOTIMP".parse());
assert_eq!(Ok(OptRcode::REFUSED), "REFUSED".parse());
assert_eq!(Ok(OptRcode::YXDOMAIN), "YXDOMAIN".parse());
assert_eq!(Ok(OptRcode::YXRRSET), "YXRRSET".parse());
assert_eq!(Ok(OptRcode::NXRRSET), "NXRRSET".parse());
assert_eq!(Ok(OptRcode::NOTAUTH), "NOTAUTH".parse());
assert_eq!(Ok(OptRcode::NOTZONE), "NOTZONE".parse());
assert_eq!(Ok(OptRcode::BADVERS), "BADVERS".parse());
assert_eq!(Ok(OptRcode::BADCOOKIE), "BADCOOKIE".parse());
assert!("#$%!@".parse::<Rcode>().is_err());
}
#[test]
fn optrcode_isext() {
assert!(!OptRcode::NOERROR.is_ext());
assert!(!OptRcode::FORMERR.is_ext());
assert!(!OptRcode::SERVFAIL.is_ext());
assert!(!OptRcode::NXDOMAIN.is_ext());
assert!(!OptRcode::NOTIMP.is_ext());
assert!(!OptRcode::REFUSED.is_ext());
assert!(!OptRcode::YXDOMAIN.is_ext());
assert!(!OptRcode::YXRRSET.is_ext());
assert!(!OptRcode::NXRRSET.is_ext());
assert!(!OptRcode::NOTAUTH.is_ext());
assert!(!OptRcode::NOTZONE.is_ext());
assert!(OptRcode::BADVERS.is_ext());
assert!(OptRcode::BADCOOKIE.is_ext());
}
}