#![cfg_attr(feature = "alloc", doc = " - [`BoxedRecordData`] ")]
#![cfg_attr(not(feature = "alloc"), doc = " - `BoxedRecordData` ")]
#![deny(missing_docs)]
#![deny(clippy::missing_docs_in_private_items)]
use core::cmp::Ordering;
#[cfg(feature = "alloc")]
use core::{
fmt,
hash::{Hash, Hasher},
};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
use crate::{
new::base::{
build::{BuildInMessage, NameCompressor},
name::CanonicalName,
parse::{ParseMessageBytes, SplitMessageBytes},
wire::{
AsBytes, BuildBytes, ParseBytes, ParseError, SplitBytes,
TruncationError,
},
CanonicalRecordData, ParseRecordData, ParseRecordDataBytes, RType,
},
utils::dst::UnsizedCopy,
};
#[cfg(feature = "alloc")]
use crate::new::base::name::{Name, NameBuf};
mod basic;
pub use basic::{CName, HInfo, Mx, Ns, Ptr, Soa, Txt, A};
mod dname;
pub use dname::DName;
mod ipv6;
pub use ipv6::Aaaa;
mod edns;
pub use edns::{EdnsOptionsIter, Opt};
mod rp;
pub use rp::Rp;
mod dnssec;
pub use dnssec::{
DNSKey, DNSKeyFlags, DigestType, Ds, NSec, NSec3, NSec3Flags,
NSec3HashAlg, NSec3Param, RRSig, SecAlg, TypeBitmaps,
};
mod zonemd;
pub use zonemd::{ZoneMD, ZoneMDHashAlg, ZoneMDScheme};
use super::base::wire::ParseBytesZC;
macro_rules! define_record_data {
{
$(#[$attr:meta])*
$vis:vis enum $name:ident<$a:lifetime, $N:ident> {
$(
$(#[$v_attr:meta])*
$v_name:ident ($v_type:ty) = $v_disc:ident
),*;
$(#[$u_attr:meta])*
Unknown(RType, $u_type:ty)
}
} => {
$(#[$attr])*
$vis enum $name<$a, $N> {
$($(#[$v_attr])* $v_name ($v_type),)*
$(#[$u_attr])* Unknown(RType, $u_type),
}
impl<$N> $name<'_, $N> {
pub const fn rtype(&self) -> RType {
match *self {
$(Self::$v_name(_) => RType::$v_disc,)*
Self::Unknown(rtype, _) => rtype,
}
}
}
$(impl<$a, $N> From<$v_type> for $name<$a, $N> {
fn from(value: $v_type) -> Self {
Self::$v_name(value)
}
})*
impl<$N: CanonicalName> CanonicalRecordData for $name<'_, $N> {
fn build_canonical_bytes<'b>(
&self,
bytes: &'b mut [u8],
) -> Result<&'b mut [u8], TruncationError> {
match self {
$(Self::$v_name(r) => r.build_canonical_bytes(bytes),)*
Self::Unknown(_, rd) => rd.build_canonical_bytes(bytes),
}
}
fn cmp_canonical(&self, other: &Self) -> Ordering {
if self.rtype() != other.rtype() {
return self.rtype().cmp(&other.rtype());
}
match (self, other) {
$((Self::$v_name(l), Self::$v_name(r))
=> l.cmp_canonical(r),)*
(Self::Unknown(_, l), Self::Unknown(_, r))
=> l.cmp_canonical(r),
_ => unreachable!("'self' and 'other' had the same rtype but were different enum variants"),
}
}
}
impl<$a, $N: SplitBytes<$a>> ParseRecordDataBytes<$a> for $name<$a, $N> {
fn parse_record_data_bytes(
bytes: &$a [u8],
rtype: RType,
) -> Result<Self, ParseError> {
Ok(match rtype {
$(RType::$v_disc => Self::$v_name(ParseBytes::parse_bytes(bytes)?),)*
_ => Self::Unknown(rtype, ParseBytes::parse_bytes(bytes)?),
})
}
}
impl<$N: BuildInMessage> BuildInMessage for $name<'_, $N> {
fn build_in_message(
&self,
contents: &mut [u8],
start: usize,
name: &mut NameCompressor,
) -> Result<usize, TruncationError> {
match *self {
$(Self::$v_name(ref r) => r.build_in_message(contents, start, name),)*
Self::Unknown(_, r) => r.build_in_message(contents, start, name),
}
}
}
impl<$N: BuildBytes> BuildBytes for $name<'_, $N> {
fn build_bytes<'b>(
&self,
bytes: &'b mut [u8],
) -> Result<&'b mut [u8], TruncationError> {
match self {
$(Self::$v_name(r) => r.build_bytes(bytes),)*
Self::Unknown(_, r) => r.build_bytes(bytes),
}
}
fn built_bytes_size(&self) -> usize {
match self {
$(Self::$v_name(r) => r.built_bytes_size(),)*
Self::Unknown(_, r) => r.built_bytes_size(),
}
}
}
};
}
define_record_data! {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum RecordData<'a, N> {
A(A) = A,
Ns(Ns<N>) = NS,
CName(CName<N>) = CNAME,
Soa(Soa<N>) = SOA,
Ptr(Ptr<N>) = PTR,
HInfo(HInfo<'a>) = HINFO,
Mx(Mx<N>) = MX,
Txt(&'a Txt) = TXT,
Rp(Rp<N>) = RP,
Aaaa(Aaaa) = AAAA,
DName(&'a DName) = DNAME,
Opt(&'a Opt) = OPT,
Ds(&'a Ds) = DS,
RRSig(RRSig<'a>) = RRSIG,
NSec(NSec<'a>) = NSEC,
DNSKey(&'a DNSKey) = DNSKEY,
NSec3(NSec3<'a>) = NSEC3,
NSec3Param(&'a NSec3Param) = NSEC3PARAM,
ZoneMD(&'a ZoneMD) = ZONEMD;
Unknown(RType, &'a UnknownRecordData)
}
}
impl<'a, N> RecordData<'a, N> {
pub fn map_names<R, F: FnMut(N) -> R>(self, f: F) -> RecordData<'a, R> {
match self {
Self::A(r) => RecordData::A(r),
Self::Ns(r) => RecordData::Ns(r.map_name(f)),
Self::CName(r) => RecordData::CName(r.map_name(f)),
Self::Soa(r) => RecordData::Soa(r.map_names(f)),
Self::Ptr(r) => RecordData::Ptr(r.map_name(f)),
Self::HInfo(r) => RecordData::HInfo(r),
Self::Mx(r) => RecordData::Mx(r.map_name(f)),
Self::Txt(r) => RecordData::Txt(r),
Self::Rp(r) => RecordData::Rp(r.map_names(f)),
Self::Aaaa(r) => RecordData::Aaaa(r),
Self::DName(r) => RecordData::DName(r),
Self::Opt(r) => RecordData::Opt(r),
Self::Ds(r) => RecordData::Ds(r),
Self::RRSig(r) => RecordData::RRSig(r),
Self::NSec(r) => RecordData::NSec(r),
Self::DNSKey(r) => RecordData::DNSKey(r),
Self::NSec3(r) => RecordData::NSec3(r),
Self::NSec3Param(r) => RecordData::NSec3Param(r),
Self::ZoneMD(r) => RecordData::ZoneMD(r),
Self::Unknown(rt, rd) => RecordData::Unknown(rt, rd),
}
}
pub fn map_names_by_ref<'r, R, F: FnMut(&'r N) -> R>(
&'r self,
f: F,
) -> RecordData<'r, R> {
match self {
Self::A(r) => RecordData::A(*r),
Self::Ns(r) => RecordData::Ns(r.map_name_by_ref(f)),
Self::CName(r) => RecordData::CName(r.map_name_by_ref(f)),
Self::Soa(r) => RecordData::Soa(r.map_names_by_ref(f)),
Self::Ptr(r) => RecordData::Ptr(r.map_name_by_ref(f)),
Self::HInfo(r) => RecordData::HInfo(*r),
Self::Mx(r) => RecordData::Mx(r.map_name_by_ref(f)),
Self::Txt(r) => RecordData::Txt(r),
Self::Rp(r) => RecordData::Rp(r.map_names_by_ref(f)),
Self::Aaaa(r) => RecordData::Aaaa(*r),
Self::DName(r) => RecordData::DName(r),
Self::Opt(r) => RecordData::Opt(r),
Self::Ds(r) => RecordData::Ds(r),
Self::RRSig(r) => RecordData::RRSig(r.clone()),
Self::NSec(r) => RecordData::NSec(r.clone()),
Self::DNSKey(r) => RecordData::DNSKey(r),
Self::NSec3(r) => RecordData::NSec3(r.clone()),
Self::NSec3Param(r) => RecordData::NSec3Param(r),
Self::ZoneMD(r) => RecordData::ZoneMD(r),
Self::Unknown(rt, rd) => RecordData::Unknown(*rt, rd),
}
}
#[cfg(feature = "bumpalo")]
pub fn clone_to_bump<'r>(
&self,
bump: &'r bumpalo::Bump,
) -> RecordData<'r, N>
where
N: Clone,
{
use crate::utils::dst::copy_to_bump;
match self {
Self::A(r) => RecordData::A(*r),
Self::Ns(r) => RecordData::Ns(r.clone()),
Self::CName(r) => RecordData::CName(r.clone()),
Self::Soa(r) => RecordData::Soa(r.clone()),
Self::Ptr(r) => RecordData::Ptr(r.clone()),
Self::HInfo(r) => RecordData::HInfo(r.clone_to_bump(bump)),
Self::Mx(r) => RecordData::Mx(r.clone()),
Self::Txt(r) => RecordData::Txt(copy_to_bump(*r, bump)),
Self::Rp(r) => RecordData::Rp(r.clone()),
Self::Aaaa(r) => RecordData::Aaaa(*r),
Self::DName(r) => RecordData::DName(copy_to_bump(*r, bump)),
Self::Opt(r) => RecordData::Opt(copy_to_bump(*r, bump)),
Self::Ds(r) => RecordData::Ds(copy_to_bump(*r, bump)),
Self::RRSig(r) => RecordData::RRSig(r.clone_to_bump(bump)),
Self::NSec(r) => RecordData::NSec(r.clone_to_bump(bump)),
Self::DNSKey(r) => RecordData::DNSKey(copy_to_bump(*r, bump)),
Self::NSec3(r) => RecordData::NSec3(r.clone_to_bump(bump)),
Self::NSec3Param(r) => {
RecordData::NSec3Param(copy_to_bump(*r, bump))
}
Self::ZoneMD(r) => RecordData::ZoneMD(copy_to_bump(*r, bump)),
Self::Unknown(rt, rd) => {
RecordData::Unknown(*rt, rd.clone_to_bump(bump))
}
}
}
}
impl<'a, N: SplitMessageBytes<'a>> ParseRecordData<'a> for RecordData<'a, N> {
fn parse_record_data(
contents: &'a [u8],
start: usize,
rtype: RType,
) -> Result<Self, ParseError> {
match rtype {
RType::A => A::parse_bytes(&contents[start..]).map(Self::A),
RType::NS => {
Ns::parse_message_bytes(contents, start).map(Self::Ns)
}
RType::CNAME => {
CName::parse_message_bytes(contents, start).map(Self::CName)
}
RType::SOA => {
Soa::parse_message_bytes(contents, start).map(Self::Soa)
}
RType::PTR => {
Ptr::parse_message_bytes(contents, start).map(Self::Ptr)
}
RType::HINFO => {
HInfo::parse_bytes(&contents[start..]).map(Self::HInfo)
}
RType::MX => {
Mx::parse_message_bytes(contents, start).map(Self::Mx)
}
RType::TXT => {
<&Txt>::parse_bytes(&contents[start..]).map(Self::Txt)
}
RType::RP => {
Rp::parse_message_bytes(contents, start).map(Self::Rp)
}
RType::AAAA => {
Aaaa::parse_bytes(&contents[start..]).map(Self::Aaaa)
}
RType::DNAME => {
<&DName>::parse_bytes(&contents[start..]).map(Self::DName)
}
RType::OPT => {
<&Opt>::parse_bytes(&contents[start..]).map(Self::Opt)
}
RType::DS => <&Ds>::parse_bytes(&contents[start..]).map(Self::Ds),
RType::RRSIG => {
RRSig::parse_bytes(&contents[start..]).map(Self::RRSig)
}
RType::NSEC => {
NSec::parse_bytes(&contents[start..]).map(Self::NSec)
}
RType::DNSKEY => {
<&DNSKey>::parse_bytes(&contents[start..]).map(Self::DNSKey)
}
RType::NSEC3 => {
NSec3::parse_bytes(&contents[start..]).map(Self::NSec3)
}
RType::NSEC3PARAM => {
<&NSec3Param>::parse_bytes(&contents[start..])
.map(Self::NSec3Param)
}
RType::ZONEMD => {
<&ZoneMD>::parse_bytes(&contents[start..]).map(Self::ZoneMD)
}
_ => <&UnknownRecordData>::parse_bytes(&contents[start..])
.map(|data| Self::Unknown(rtype, data)),
}
}
}
#[cfg(feature = "alloc")]
pub struct BoxedRecordData {
data: *mut u8,
rtype: RType,
size: u16,
}
#[cfg(feature = "alloc")]
impl BoxedRecordData {
pub const fn rtype(&self) -> RType {
self.rtype
}
pub const fn bytes(&self) -> &[u8] {
unsafe { core::slice::from_raw_parts(self.data, self.size as usize) }
}
pub fn get(&self) -> RecordData<'_, &'_ Name> {
let (rtype, bytes) = (self.rtype, self.bytes());
unsafe {
RecordData::parse_record_data_bytes(bytes, rtype)
.unwrap_unchecked()
}
}
}
#[cfg(feature = "alloc")]
impl fmt::Debug for BoxedRecordData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Boxed")?;
self.get().fmt(f)
}
}
#[cfg(feature = "alloc")]
impl PartialEq for BoxedRecordData {
fn eq(&self, other: &Self) -> bool {
self.get().eq(&other.get())
}
}
#[cfg(feature = "alloc")]
impl Eq for BoxedRecordData {}
#[cfg(feature = "alloc")]
impl Hash for BoxedRecordData {
fn hash<H: Hasher>(&self, state: &mut H) {
self.get().hash(state)
}
}
#[cfg(feature = "alloc")]
impl Clone for BoxedRecordData {
fn clone(&self) -> Self {
let bytes: Box<[u8]> = self.bytes().into();
let data = Box::into_raw(bytes).cast::<u8>();
let (rtype, size) = (self.rtype, self.size);
Self { data, rtype, size }
}
}
#[cfg(feature = "alloc")]
impl Drop for BoxedRecordData {
fn drop(&mut self) {
let slice = core::ptr::slice_from_raw_parts_mut(
self.data,
self.size as usize,
);
let _ = unsafe { Box::from_raw(slice) };
}
}
#[cfg(feature = "alloc")]
unsafe impl Send for BoxedRecordData {}
#[cfg(feature = "alloc")]
unsafe impl Sync for BoxedRecordData {}
#[cfg(feature = "alloc")]
impl<N: BuildBytes> From<RecordData<'_, N>> for BoxedRecordData {
fn from(value: RecordData<'_, N>) -> Self {
let mut buffer = vec![0u8; 65535];
let rest_len = value
.build_bytes(&mut buffer)
.expect("A 'RecordData' could not be built into a 64KiB buffer")
.len();
let len = buffer.len() - rest_len;
buffer.truncate(len);
let buffer: Box<[u8]> = buffer.into_boxed_slice();
let _rdata: RecordData<'_, &Name> =
RecordData::parse_record_data_bytes(&buffer, value.rtype())
.expect("A serialized 'RecordData' could not be parsed back");
let size = buffer.len() as u16;
let data = Box::into_raw(buffer).cast::<u8>();
let rtype = value.rtype();
Self { data, rtype, size }
}
}
#[cfg(feature = "alloc")]
impl CanonicalRecordData for BoxedRecordData {
fn build_canonical_bytes<'b>(
&self,
bytes: &'b mut [u8],
) -> Result<&'b mut [u8], TruncationError> {
if self.rtype.uses_lowercase_canonical_form() {
self.get().build_canonical_bytes(bytes)
} else {
self.bytes().build_bytes(bytes)
}
}
fn cmp_canonical(&self, other: &Self) -> Ordering {
if self.rtype != other.rtype {
return self.rtype.cmp(&other.rtype);
}
if self.rtype.uses_lowercase_canonical_form() {
self.get().cmp_canonical(&other.get())
} else {
self.bytes().cmp(other.bytes())
}
}
}
#[cfg(feature = "alloc")]
impl ParseRecordData<'_> for BoxedRecordData {
fn parse_record_data(
contents: &'_ [u8],
start: usize,
rtype: RType,
) -> Result<Self, ParseError> {
RecordData::<'_, NameBuf>::parse_record_data(contents, start, rtype)
.map(BoxedRecordData::from)
}
}
#[cfg(feature = "alloc")]
impl ParseRecordDataBytes<'_> for BoxedRecordData {
fn parse_record_data_bytes(
bytes: &'_ [u8],
rtype: RType,
) -> Result<Self, ParseError> {
let _rdata: RecordData<'_, &Name> =
RecordData::parse_record_data_bytes(bytes, rtype)?;
let size = u16::try_from(bytes.len()).map_err(|_| ParseError)?;
let bytes: Box<[u8]> = bytes.into();
let data = Box::into_raw(bytes).cast::<u8>();
Ok(Self { data, rtype, size })
}
}
#[cfg(feature = "alloc")]
impl BuildBytes for BoxedRecordData {
fn build_bytes<'b>(
&self,
bytes: &'b mut [u8],
) -> Result<&'b mut [u8], TruncationError> {
self.bytes().build_bytes(bytes)
}
fn built_bytes_size(&self) -> usize {
self.bytes().len()
}
}
#[derive(
Debug, PartialEq, Eq, Hash, AsBytes, BuildBytes, ParseBytesZC, UnsizedCopy,
)]
#[repr(transparent)]
pub struct UnknownRecordData {
pub octets: [u8],
}
impl UnknownRecordData {
#[cfg(feature = "bumpalo")]
#[allow(clippy::mut_from_ref)] pub fn clone_to_bump<'r>(&self, bump: &'r bumpalo::Bump) -> &'r mut Self {
use crate::new::base::wire::{AsBytes, ParseBytesZC};
let bytes = bump.alloc_slice_copy(self.as_bytes());
unsafe { Self::parse_bytes_in(bytes).unwrap_unchecked() }
}
}
impl CanonicalRecordData for UnknownRecordData {
fn cmp_canonical(&self, other: &Self) -> Ordering {
self.octets.cmp(&other.octets)
}
}
impl BuildInMessage for UnknownRecordData {
fn build_in_message(
&self,
contents: &mut [u8],
start: usize,
_compressor: &mut NameCompressor,
) -> Result<usize, TruncationError> {
let end = start + self.octets.len();
contents
.get_mut(start..end)
.ok_or(TruncationError)?
.copy_from_slice(&self.octets);
Ok(end)
}
}
#[cfg(feature = "alloc")]
impl Clone for alloc::boxed::Box<UnknownRecordData> {
fn clone(&self) -> Self {
(*self).unsized_copy_into()
}
}