use core::ops::Range;
use core::{
marker::PhantomData,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
};
use byteorder::{ByteOrder, NetworkEndian};
use self::error::{RdataError, RdataResultExt, SoaError};
use crate::view::{assert, BorrowedView, CharacterString, CharacterStrings, Name, Record, View};
use crate::{
core::{Class, Serial, Ttl, Type},
fmt::Format,
rdata::class::{GenericClass, InClass},
};
macro_rules! declare_rdata {
($($variant:ident($($ty:tt)+),)*) => {
#[non_exhaustive]
pub enum Rdata<'s> {
$($variant($($ty)+<'s>),)*
}
impl<'s> From<&Record<'s>> for Rdata<'s> {
fn from(record: &Record<'s>) -> Self {
let range = record.rdata_range();
if let Ok(result) = || -> Result<Self, ()> {Ok(match (record.class(), record.r#type()) {
(_, Type::NS) => Ns::view(record.source(), range).map_err(|_| ())?.0.into(),
(_, Type::CNAME) => Cname::view(record.source(), range).map_err(|_| ())?.0.into(),
(_, Type::SOA) => Soa::view(record.source(), range).map_err(|_| ())?.0.into(),
(_, Type::PTR) => Ptr::view(record.source(), range).map_err(|_| ())?.0.into(),
(_, Type::MX) => Mx::view(record.source(), range).map_err(|_| ())?.0.into(),
(_, Type::TXT) => Txt::view(record.source(), range).map_err(|_| ())?.0.into(),
(_, Type::CAA) => Caa::view(record.source(), range).map_err(|_| ())?.0.into(),
(Class::IN, Type::A) => A::view(record.source(), range).map_err(|_| ())?.0.into(),
(Class::IN, Type::AAAA) => Aaaa::view(record.source(), range).map_err(|_| ())?.0.into(),
_ => Unknown::view(record.source(), range).map_err(|_| ())?.0.into(),
})}() {
return result;
}
Malformed::view(record.source(), record.rdata_range())
.expect("guaranteed by impl View for Record")
.0
.into()
}
}
$(impl<'s> From<$($ty)+<'s>> for Rdata<'s> {
fn from(inner: $($ty)+<'s>) -> Self {
Self::$variant(inner)
}
})*
impl<'s> AsRef<dyn View<'s, Error = RdataError>> for Rdata<'s> {
fn as_ref(&self) -> &dyn View<'s, Error = RdataError> {
match self {
$(Self::$variant(x) => x,)*
}
}
}
impl<'s> AsRef<dyn Format + 's> for Rdata<'s> {
fn as_ref(&self) -> &(dyn Format + 's) {
match self {
$(Self::$variant(x) => x,)*
}
}
}
};
}
declare_rdata! {
Malformed(Malformed),
Unknown(Unknown),
Ns(Ns),
Cname(Cname),
Soa(Soa),
Ptr(Ptr),
Mx(Mx),
Txt(Txt),
Caa(Caa),
InAddress(A),
InAaaa(Aaaa),
}
pub struct Ns<'s, C: GenericClass = InClass> {
inner: Name<'s>,
_class: PhantomData<C>,
}
pub struct Cname<'s, C: GenericClass = InClass> {
inner: Name<'s>,
_class: PhantomData<C>,
}
pub struct Soa<'s, C: GenericClass = InClass> {
source: &'s [u8],
offset: usize,
mname_len: usize,
rname_len: usize,
_class: PhantomData<C>,
}
pub struct Ptr<'s, C: GenericClass = InClass> {
inner: Name<'s>,
_class: PhantomData<C>,
}
pub struct Mx<'s, C: GenericClass = InClass> {
source: &'s [u8],
offset: usize,
exchange_len: usize,
_class: PhantomData<C>,
}
pub struct Txt<'s, C: GenericClass = InClass> {
source: &'s [u8],
offset: usize,
len: usize,
_class: PhantomData<C>,
}
pub struct Caa<'s, C: GenericClass = InClass> {
source: &'s [u8],
offset: usize,
tag_len: usize,
value_len: usize,
_class: PhantomData<C>,
}
pub struct CaaTag<'s, C: GenericClass = InClass> {
source: &'s [u8],
offset: usize,
len: usize,
_class: PhantomData<C>,
}
pub struct CaaValue<'s, C: GenericClass = InClass> {
source: &'s [u8],
offset: usize,
len: usize,
_class: PhantomData<C>,
}
pub struct Malformed<'s, C: GenericClass = InClass> {
inner: Unknown<'s>,
_class: PhantomData<C>,
}
#[derive(Clone)]
pub struct Unknown<'s, C: GenericClass = InClass> {
source: &'s [u8],
offset: usize,
len: usize,
_class: PhantomData<C>,
}
impl<C: GenericClass> Ns<'_, C> {
pub fn name(&self) -> Name {
self.inner.clone()
}
}
impl<C: GenericClass> Cname<'_, C> {
pub fn name(&self) -> Name {
self.inner.clone()
}
}
impl<C: GenericClass> Soa<'_, C> {
pub fn mname(&self) -> Name {
let start = self.offset;
let end = start + self.mname_len;
Name::new_unchecked(self.source, start..end)
}
pub fn rname(&self) -> Name {
let start = self.offset + self.mname_len;
let end = start + self.rname_len;
Name::new_unchecked(self.source, start..end)
}
pub fn serial(&self) -> Serial {
let offset = self.mname_len + self.rname_len;
Serial::new(NetworkEndian::read_u32(
&self.source[self.offset..][offset..],
))
}
pub fn refresh(&self) -> Ttl {
let offset = self.mname_len + self.rname_len + 4;
Ttl::new(NetworkEndian::read_u32(
&self.source[self.offset..][offset..],
))
}
pub fn retry(&self) -> Ttl {
let offset = self.mname_len + self.rname_len + 8;
Ttl::new(NetworkEndian::read_u32(
&self.source[self.offset..][offset..],
))
}
pub fn expire(&self) -> Ttl {
let offset = self.mname_len + self.rname_len + 12;
Ttl::new(NetworkEndian::read_u32(
&self.source[self.offset..][offset..],
))
}
pub fn minimum(&self) -> Ttl {
let offset = self.mname_len + self.rname_len + 16;
Ttl::new(NetworkEndian::read_u32(
&self.source[self.offset..][offset..],
))
}
}
impl<C: GenericClass> Ptr<'_, C> {
pub fn name(&self) -> Name {
self.inner.clone()
}
}
impl Mx<'_, InClass> {
pub fn preference(&self) -> u16 {
NetworkEndian::read_u16(&self.source[self.offset..])
}
pub fn exchange(&self) -> Name {
let start = self.offset + 2;
let end = start + self.exchange_len;
Name::new_unchecked(self.source, start..end)
}
}
impl Txt<'_, InClass> {
pub fn data(&self) -> CharacterStrings<'_> {
let start = self.offset;
let stop = self.offset + self.len;
CharacterStrings::new(self.source, start..stop)
}
}
impl Caa<'_, InClass> {
pub fn flags(&self) -> u8 {
self.source[self.offset]
}
pub fn critical(&self) -> bool {
self.flags() >> 7 & 1 == 1
}
pub fn tag(&self) -> CaaTag {
CaaTag {
source: self.source,
offset: self.offset + 2,
len: self.tag_len,
_class: self._class,
}
}
pub fn value(&self) -> CaaValue {
CaaValue {
source: self.source,
offset: self.offset + 2 + self.tag_len,
len: self.value_len,
_class: self._class,
}
}
}
impl<C: GenericClass> Malformed<'_, C> {
pub fn as_unknown(&self) -> Unknown {
self.inner.clone()
}
}
impl<C: GenericClass> Unknown<'_, C> {
pub fn as_bytes(&self) -> &[u8] {
&self.source[self.offset..][..self.len()]
}
}
impl<'s, C: GenericClass + 's> View<'s> for Ns<'s, C> {
type Error = RdataError;
fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
let (name, rest) = Name::view(source, range.clone()).into_rdata_error::<Self>()?;
Ok((
Self {
inner: name,
_class: PhantomData,
},
rest,
))
}
}
impl<'s, C: GenericClass> BorrowedView<'s> for Ns<'s, C> {
fn source(&self) -> &'s [u8] {
self.inner.source()
}
fn offset(&self) -> usize {
self.inner.offset()
}
fn len(&self) -> usize {
self.inner.len()
}
}
impl<'s, C: GenericClass + 's> View<'s> for Cname<'s, C> {
type Error = RdataError;
fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
let (name, rest) = Name::view(source, range.clone()).into_rdata_error::<Self>()?;
Ok((
Self {
inner: name,
_class: PhantomData,
},
rest,
))
}
}
impl<'s, C: GenericClass> BorrowedView<'s> for Cname<'s, C> {
fn source(&self) -> &'s [u8] {
self.inner.source()
}
fn offset(&self) -> usize {
self.inner.offset()
}
fn len(&self) -> usize {
self.inner.len()
}
}
impl<'s, C: GenericClass + 's> View<'s> for Soa<'s, C> {
type Error = RdataError;
fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
let (_, rest) = Name::view(source, range.clone())
.map_err(SoaError::Name)
.into_rdata_error::<Self>()?;
let mname_len = rest.start - range.start;
let (_, mut rest) = Name::view(source, rest)
.map_err(SoaError::Name)
.into_rdata_error::<Self>()?;
let rname_len = rest.start - range.start - mname_len;
assert(source, &rest, 20)
.map_err(SoaError::Bounds)
.into_rdata_error::<Self>()?;
rest.start += 20;
Ok((
Soa {
source,
offset: range.start,
mname_len,
rname_len,
_class: PhantomData,
},
rest,
))
}
}
impl<'s, C: GenericClass> BorrowedView<'s> for Soa<'s, C> {
fn source(&self) -> &'s [u8] {
self.source
}
fn offset(&self) -> usize {
self.offset
}
fn len(&self) -> usize {
self.mname_len + self.rname_len + 20
}
}
impl<'s, C: GenericClass + 's> View<'s> for Ptr<'s, C> {
type Error = RdataError;
fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
let (name, rest) = Name::view(source, range.clone()).into_rdata_error::<Self>()?;
Ok((
Self {
inner: name,
_class: PhantomData,
},
rest,
))
}
}
impl<'s, C: GenericClass> BorrowedView<'s> for Ptr<'s, C> {
fn source(&self) -> &'s [u8] {
self.inner.source()
}
fn offset(&self) -> usize {
self.inner.offset()
}
fn len(&self) -> usize {
self.inner.len()
}
}
impl<'s> View<'s> for Mx<'s, InClass> {
type Error = RdataError;
fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
let mut rest = range.clone();
assert(source, &rest, 2).into_rdata_error::<Self>()?;
rest.start += 2;
let (_, rest) = Name::view(source, rest)
.map_err(SoaError::Name)
.into_rdata_error::<Self>()?;
let exchange_len = rest.start - range.start - 2;
Ok((
Mx {
source,
offset: range.start,
exchange_len,
_class: PhantomData,
},
rest,
))
}
}
impl<'s> BorrowedView<'s> for Mx<'s, InClass> {
fn source(&self) -> &'s [u8] {
self.source
}
fn offset(&self) -> usize {
self.offset
}
fn len(&self) -> usize {
2 + self.exchange_len
}
}
impl<'s> View<'s> for Txt<'s, InClass> {
type Error = RdataError;
fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
let mut next = range.clone();
while next.start != next.end {
let (_, rest) = CharacterString::view(source, next).into_rdata_error::<Self>()?;
next = rest;
}
let offset = range.start;
let len = next.start - offset;
Ok((
Txt {
source,
offset,
len,
_class: PhantomData,
},
next,
))
}
}
impl<'s> BorrowedView<'s> for Txt<'s, InClass> {
fn source(&self) -> &'s [u8] {
self.source
}
fn offset(&self) -> usize {
self.offset
}
fn len(&self) -> usize {
self.len
}
}
impl<'s> View<'s> for Caa<'s, InClass> {
type Error = RdataError;
fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
let mut rest = range.clone();
assert(source, &rest, 1).into_rdata_error::<Self>()?;
rest.start += 1;
let (octet, mut rest) = u8::view(source, rest).into_rdata_error::<Self>()?;
let tag_len: usize = octet.into();
assert(source, &rest, tag_len).into_rdata_error::<Self>()?;
rest.start += tag_len;
let value_len = rest.end - rest.start;
rest.start += value_len;
Ok((
Caa {
source,
offset: range.start,
tag_len,
value_len,
_class: PhantomData,
},
rest,
))
}
}
impl<'s> BorrowedView<'s> for Caa<'s, InClass> {
fn source(&self) -> &'s [u8] {
self.source
}
fn offset(&self) -> usize {
self.offset
}
fn len(&self) -> usize {
1 + 1 + self.tag_len + self.value_len
}
}
impl<'s> BorrowedView<'s> for CaaTag<'s, InClass> {
fn source(&self) -> &'s [u8] {
self.source
}
fn offset(&self) -> usize {
self.offset
}
fn len(&self) -> usize {
self.len
}
}
impl<'s> BorrowedView<'s> for CaaValue<'s, InClass> {
fn source(&self) -> &'s [u8] {
self.source
}
fn offset(&self) -> usize {
self.offset
}
fn len(&self) -> usize {
self.len
}
}
impl<'s, C: GenericClass + 's> View<'s> for Malformed<'s, C> {
type Error = RdataError;
fn view_range(source: &'s [u8], range: Range<usize>) -> crate::view::Result<Self, Self::Error> {
let (inner, range) = Unknown::view(source, range)?;
Ok((
Malformed {
inner,
_class: PhantomData,
},
range,
))
}
}
impl<'s, C: GenericClass> BorrowedView<'s> for Malformed<'s, C> {
fn source(&self) -> &'s [u8] {
self.inner.source()
}
fn offset(&self) -> usize {
self.inner.offset()
}
fn len(&self) -> usize {
self.inner.len()
}
}
impl<'s, C: GenericClass + 's> View<'s> for Unknown<'s, C> {
type Error = RdataError;
fn view_range(
source: &'s [u8],
mut range: Range<usize>,
) -> crate::view::Result<Self, Self::Error> {
let offset = range.start;
let len = range.end - offset;
assert(source, &range, len).into_rdata_error::<Self>()?;
range.start += len;
Ok((
Unknown {
source,
offset,
len,
_class: PhantomData,
},
range,
))
}
}
impl<'s, C: GenericClass> BorrowedView<'s> for Unknown<'s, C> {
fn source(&self) -> &'s [u8] {
self.source
}
fn offset(&self) -> usize {
self.offset
}
fn len(&self) -> usize {
self.len
}
}
pub struct A<'s, C: GenericClass = InClass> {
source: &'s [u8],
pub offset: usize,
_class: PhantomData<C>,
}
pub struct Aaaa<'s, C: GenericClass = InClass> {
source: &'s [u8],
pub offset: usize,
_class: PhantomData<C>,
}
impl A<'_, InClass> {
pub fn to_ipv4_addr(&self) -> Ipv4Addr {
let bits = NetworkEndian::read_u32(&self.source[self.offset..][..self.len()]);
Ipv4Addr::from_bits(bits)
}
pub fn to_ip_addr(&self) -> IpAddr {
self.to_ipv4_addr().into()
}
}
impl From<A<'_, InClass>> for Ipv4Addr {
fn from(value: A<'_>) -> Self {
value.to_ipv4_addr()
}
}
impl From<A<'_, InClass>> for IpAddr {
fn from(value: A<'_>) -> Self {
value.to_ip_addr()
}
}
impl Aaaa<'_, InClass> {
pub fn to_ipv6_addr(&self) -> Ipv6Addr {
let bits = NetworkEndian::read_u128(&self.source[self.offset..][..self.len()]);
Ipv6Addr::from_bits(bits)
}
pub fn to_ip_addr(&self) -> IpAddr {
self.to_ipv6_addr().into()
}
}
impl From<Aaaa<'_, InClass>> for Ipv6Addr {
fn from(value: Aaaa<'_>) -> Self {
value.to_ipv6_addr()
}
}
impl From<Aaaa<'_, InClass>> for IpAddr {
fn from(value: Aaaa<'_>) -> Self {
value.to_ip_addr()
}
}
impl<'s> View<'s> for A<'s, InClass> {
type Error = RdataError;
fn view_range(
source: &'s [u8],
mut range: Range<usize>,
) -> crate::view::Result<Self, Self::Error> {
let offset = range.start;
assert(source, &range, 4).into_rdata_error::<Self>()?;
range.start += 4;
Ok((
A {
source,
offset,
_class: PhantomData,
},
range,
))
}
}
impl<'s> BorrowedView<'s> for A<'s, InClass> {
fn source(&self) -> &'s [u8] {
self.source
}
fn offset(&self) -> usize {
self.offset
}
fn len(&self) -> usize {
4
}
}
impl<'s> View<'s> for Aaaa<'s, InClass> {
type Error = RdataError;
fn view_range(
source: &'s [u8],
mut range: Range<usize>,
) -> crate::view::Result<Self, Self::Error> {
let offset = range.start;
assert(source, &range, 16).into_rdata_error::<Self>()?;
range.start += 16;
Ok((
Aaaa {
source,
offset,
_class: PhantomData,
},
range,
))
}
}
impl<'s> BorrowedView<'s> for Aaaa<'s, InClass> {
fn source(&self) -> &'s [u8] {
self.source
}
fn offset(&self) -> usize {
self.offset
}
fn len(&self) -> usize {
16
}
}
pub mod error {
use crate::view::{BoundsError, NameError};
error!(TypedRdataError, View);
#[derive(Debug, displaydoc::Display)]
pub enum TypedRdataError {
View(RdataError),
WrongClassOrType,
}
#[cfg(feature = "eyre")]
error!(RdataError([s: s.as_ref()], __));
#[cfg(not(feature = "eyre"))]
error!(RdataError);
#[derive(Debug, displaydoc::Display)]
pub struct RdataError(RdataError0, &'static str);
#[cfg(feature = "eyre")]
type RdataError0 = eyre::Report;
#[cfg(not(feature = "eyre"))]
type RdataError0 = ();
error!(SoaError, Bounds, Name);
#[derive(Debug, displaydoc::Display)]
#[prefix_enum_doc_attributes]
pub enum SoaError {
Bounds(BoundsError),
Name(NameError),
}
impl RdataError {
#[cfg(feature = "eyre")]
fn new<E: 'static + Send + Sync + core::error::Error>(error: E, ty: &'static str) -> Self {
Self(error.into(), ty)
}
#[cfg(not(feature = "eyre"))]
fn new<E: 'static + Send + Sync + core::error::Error>(_: E, ty: &'static str) -> Self {
Self((), ty)
}
}
pub(crate) trait RdataResultExt<T> {
fn into_rdata_error<V>(self) -> Result<T, RdataError>;
}
#[cfg(feature = "eyre")]
impl<T, E: 'static + Send + Sync + core::error::Error> RdataResultExt<T> for Result<T, E> {
fn into_rdata_error<V>(self) -> Result<T, RdataError> {
self.map_err(|e| RdataError::new(e, core::any::type_name::<V>()))
}
}
#[cfg(not(feature = "eyre"))]
impl<T, E: 'static + Send + Sync + core::error::Error> RdataResultExt<T> for Result<T, E> {
fn into_rdata_error<V>(self) -> Result<T, RdataError> {
self.map_err(|e| RdataError::new(e, core::any::type_name::<V>()))
}
}
}
#[cfg(test)]
mod test {
use crate::{
core::Serial,
rdata::{
class::InClass,
view::{Caa, Mx, Soa},
},
view::{BorrowedView, View},
};
declare_any_error!(AnyError);
#[test]
fn soa() -> Result<(), AnyError> {
let soa = b"\x02ns\x07example\0\x0Ahostmaster\xC0\x03\0\0\0\x01\0\0\0\x02\0\0\0\x03\0\0\0\x04\0\0\0\x05";
let (soa, _) = Soa::<InClass>::view(soa, ..)?;
assert_eq!(soa.mname(), "ns.example.");
assert_eq!(soa.mname().len(), 12);
assert_eq!(soa.rname(), "hostmaster.example.");
assert_eq!(soa.rname().len(), 13);
assert_eq!(soa.serial(), Serial::new(1));
assert_eq!(soa.refresh().value(), 2);
assert_eq!(soa.retry().value(), 3);
assert_eq!(soa.expire().value(), 4);
assert_eq!(soa.minimum().value(), 5);
Ok(())
}
#[test]
fn mx() -> Result<(), AnyError> {
let mx = b"\xAA\x55\x04mail\x07example\0";
let (mx, _) = Mx::view(mx, ..)?;
assert_eq!(mx.preference(), 0xAA55);
assert_eq!(mx.exchange(), "mail.example.");
assert_eq!(mx.exchange().len(), 14);
Ok(())
}
#[test]
fn caa() -> Result<(), AnyError> {
let caa = b"\x80\x05issueletsencrypt.org";
let (caa, _) = Caa::view(caa, ..)?;
assert_eq!(caa.critical(), true);
assert_eq!(caa.tag().as_bytes(), b"issue");
assert_eq!(caa.value().as_bytes(), b"letsencrypt.org");
Ok(())
}
}