use core::{borrow::Borrow, cmp::Ordering, fmt, ops::Deref};
use crate::utils::dst::UnsizedCopy;
use super::build::{BuildInMessage, NameCompressor};
use super::parse::{ParseMessageBytes, SplitMessageBytes};
use super::wire::{
AsBytes, BuildBytes, ParseBytes, ParseBytesZC, ParseError, SizePrefixed,
SplitBytes, SplitBytesZC, TruncationError, U16, U32,
};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Record<N, D> {
pub rname: N,
pub rtype: RType,
pub rclass: RClass,
pub ttl: TTL,
pub rdata: D,
}
impl<N, D> Record<N, D> {
pub fn new(
rname: N,
rtype: RType,
rclass: RClass,
ttl: TTL,
rdata: D,
) -> Self {
Self {
rname,
rtype,
rclass,
ttl,
rdata,
}
}
}
impl<N, D> Record<N, D> {
pub fn transform<NN, ND>(
self,
name_map: impl FnOnce(N) -> NN,
data_map: impl FnOnce(D) -> ND,
) -> Record<NN, ND> {
Record {
rname: (name_map)(self.rname),
rtype: self.rtype,
rclass: self.rclass,
ttl: self.ttl,
rdata: (data_map)(self.rdata),
}
}
pub fn transform_ref<'a, NN, ND>(
&'a self,
name_map: impl FnOnce(&'a N) -> NN,
data_map: impl FnOnce(&'a D) -> ND,
) -> Record<NN, ND> {
Record {
rname: (name_map)(&self.rname),
rtype: self.rtype,
rclass: self.rclass,
ttl: self.ttl,
rdata: (data_map)(&self.rdata),
}
}
}
impl<'a, N, D> SplitMessageBytes<'a> for Record<N, D>
where
N: SplitMessageBytes<'a>,
D: ParseRecordData<'a>,
{
fn split_message_bytes(
contents: &'a [u8],
start: usize,
) -> Result<(Self, usize), ParseError> {
let (rname, rest) = N::split_message_bytes(contents, start)?;
let (&rtype, rest) = <&RType>::split_message_bytes(contents, rest)?;
let (&rclass, rest) = <&RClass>::split_message_bytes(contents, rest)?;
let (&ttl, rest) = <&TTL>::split_message_bytes(contents, rest)?;
let rdata_start = rest;
let (_, rest) =
<&SizePrefixed<U16, [u8]>>::split_message_bytes(contents, rest)?;
let rdata =
D::parse_record_data(&contents[..rest], rdata_start + 2, rtype)?;
Ok((Self::new(rname, rtype, rclass, ttl, rdata), rest))
}
}
impl<'a, N, D> ParseMessageBytes<'a> for Record<N, D>
where
N: SplitMessageBytes<'a>,
D: ParseRecordData<'a>,
{
fn parse_message_bytes(
contents: &'a [u8],
start: usize,
) -> Result<Self, ParseError> {
match Self::split_message_bytes(contents, start) {
Ok((this, rest)) if rest == contents.len() => Ok(this),
_ => Err(ParseError),
}
}
}
impl<N, D> BuildInMessage for Record<N, D>
where
N: BuildInMessage,
D: BuildInMessage,
{
fn build_in_message(
&self,
contents: &mut [u8],
mut start: usize,
compressor: &mut NameCompressor,
) -> Result<usize, TruncationError> {
start = self.rname.build_in_message(contents, start, compressor)?;
let end = start + 8;
let bytes = contents.get_mut(start..end).ok_or(TruncationError)?;
bytes[0..2].copy_from_slice(self.rtype.as_bytes());
bytes[2..4].copy_from_slice(self.rclass.as_bytes());
bytes[4..8].copy_from_slice(self.ttl.as_bytes());
start = end;
start = SizePrefixed::<U16, _>::new(&self.rdata)
.build_in_message(contents, start, compressor)?;
Ok(start)
}
}
impl<'a, N, D> SplitBytes<'a> for Record<N, D>
where
N: SplitBytes<'a>,
D: ParseRecordDataBytes<'a>,
{
fn split_bytes(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), ParseError> {
let (rname, rest) = N::split_bytes(bytes)?;
let (rtype, rest) = RType::split_bytes(rest)?;
let (rclass, rest) = RClass::split_bytes(rest)?;
let (ttl, rest) = TTL::split_bytes(rest)?;
let (rdata, rest) = <&SizePrefixed<U16, [u8]>>::split_bytes(rest)?;
let rdata = D::parse_record_data_bytes(rdata, rtype)?;
Ok((Self::new(rname, rtype, rclass, ttl, rdata), rest))
}
}
impl<'a, N, D> ParseBytes<'a> for Record<N, D>
where
N: SplitBytes<'a>,
D: ParseRecordDataBytes<'a>,
{
fn parse_bytes(bytes: &'a [u8]) -> Result<Self, ParseError> {
match Self::split_bytes(bytes) {
Ok((this, &[])) => Ok(this),
_ => Err(ParseError),
}
}
}
impl<N, D> BuildBytes for Record<N, D>
where
N: BuildBytes,
D: BuildBytes,
{
fn build_bytes<'b>(
&self,
mut bytes: &'b mut [u8],
) -> Result<&'b mut [u8], TruncationError> {
bytes = self.rname.build_bytes(bytes)?;
bytes = self.rtype.as_bytes().build_bytes(bytes)?;
bytes = self.rclass.as_bytes().build_bytes(bytes)?;
bytes = self.ttl.as_bytes().build_bytes(bytes)?;
bytes =
SizePrefixed::<U16, _>::new(&self.rdata).build_bytes(bytes)?;
Ok(bytes)
}
fn built_bytes_size(&self) -> usize {
self.rname.built_bytes_size() + 10 + self.rdata.built_bytes_size()
}
}
#[derive(
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
AsBytes,
BuildBytes,
ParseBytes,
ParseBytesZC,
SplitBytes,
SplitBytesZC,
UnsizedCopy,
)]
#[repr(transparent)]
pub struct RType {
pub code: U16,
}
impl RType {
const fn new(value: u16) -> Self {
Self {
code: U16::new(value),
}
}
pub const A: Self = Self::new(1);
pub const NS: Self = Self::new(2);
pub const CNAME: Self = Self::new(5);
pub const SOA: Self = Self::new(6);
pub const PTR: Self = Self::new(12);
pub const HINFO: Self = Self::new(13);
pub const MX: Self = Self::new(15);
pub const TXT: Self = Self::new(16);
pub const RP: Self = Self::new(17);
pub const AAAA: Self = Self::new(28);
pub const DNAME: Self = Self::new(39);
pub const OPT: Self = Self::new(41);
pub const DS: Self = Self::new(43);
pub const RRSIG: Self = Self::new(46);
pub const NSEC: Self = Self::new(47);
pub const DNSKEY: Self = Self::new(48);
pub const NSEC3: Self = Self::new(50);
pub const NSEC3PARAM: Self = Self::new(51);
pub const CDS: Self = Self::new(59);
pub const CDNSKEY: Self = Self::new(60);
pub const ZONEMD: Self = Self::new(63);
pub const TSIG: Self = Self::new(250);
}
impl RType {
pub const fn uses_lowercase_canonical_form(&self) -> bool {
matches!(
*self,
Self::NS
| Self::CNAME
| Self::SOA
| Self::PTR
| Self::MX
| Self::RP
| Self::DNAME
| Self::RRSIG
)
}
}
impl From<u16> for RType {
fn from(value: u16) -> Self {
Self {
code: U16::new(value),
}
}
}
impl From<RType> for u16 {
fn from(value: RType) -> Self {
value.code.get()
}
}
impl fmt::Debug for RType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self {
Self::A => "RType::A",
Self::NS => "RType::NS",
Self::CNAME => "RType::CNAME",
Self::SOA => "RType::SOA",
Self::PTR => "RType::PTR",
Self::HINFO => "RType::HINFO",
Self::MX => "RType::MX",
Self::TXT => "RType::TXT",
Self::RP => "RType::RP",
Self::AAAA => "RType::AAAA",
Self::DNAME => "RType::DNAME",
Self::OPT => "RType::OPT",
Self::DS => "RType::DS",
Self::RRSIG => "RType::RRSIG",
Self::NSEC => "RType::NSEC",
Self::DNSKEY => "RType::DNSKEY",
Self::NSEC3 => "RType::NSEC3",
Self::NSEC3PARAM => "RType::NSEC3PARAM",
Self::CDS => "RType::CDS",
Self::CDNSKEY => "RType::CDNSKEY",
Self::ZONEMD => "RType::ZONEMD",
Self::TSIG => "RType::TSIG",
_ => return write!(f, "RType({})", self.code),
})
}
}
#[derive(
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
AsBytes,
BuildBytes,
ParseBytes,
ParseBytesZC,
SplitBytes,
SplitBytesZC,
UnsizedCopy,
)]
#[repr(transparent)]
pub struct RClass {
pub code: U16,
}
impl RClass {
const fn new(value: u16) -> Self {
Self {
code: U16::new(value),
}
}
pub const IN: Self = Self::new(1);
pub const CH: Self = Self::new(3);
}
impl fmt::Debug for RClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self {
Self::IN => "RClass::IN",
Self::CH => "RClass::CH",
_ => return write!(f, "RClass({})", self.code),
})
}
}
#[derive(
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
AsBytes,
BuildBytes,
ParseBytes,
ParseBytesZC,
SplitBytes,
SplitBytesZC,
UnsizedCopy,
)]
#[repr(transparent)]
pub struct TTL {
pub value: U32,
}
impl From<u32> for TTL {
fn from(value: u32) -> Self {
Self {
value: U32::new(value),
}
}
}
impl From<TTL> for u32 {
fn from(value: TTL) -> Self {
value.value.get()
}
}
impl fmt::Debug for TTL {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TTL({})", self.value)
}
}
pub trait ParseRecordData<'a>: ParseRecordDataBytes<'a> {
fn parse_record_data(
contents: &'a [u8],
start: usize,
rtype: RType,
) -> Result<Self, ParseError> {
Self::parse_record_data_bytes(&contents[start..], rtype)
}
}
pub trait ParseRecordDataBytes<'a>: Sized {
fn parse_record_data_bytes(
bytes: &'a [u8],
rtype: RType,
) -> Result<Self, ParseError>;
}
pub trait CanonicalRecordData: BuildBytes {
fn build_canonical_bytes<'b>(
&self,
bytes: &'b mut [u8],
) -> Result<&'b mut [u8], TruncationError> {
self.build_bytes(bytes)
}
fn cmp_canonical(&self, other: &Self) -> Ordering;
}
macro_rules! impl_canonical_record_data_for_deref {
{$(
$(#[$attr:meta])*
impl[$($args:tt)*] CanonicalRecordData for $subject:ty;
)*} => {$(
$(#[$attr])*
impl<$($args)*> CanonicalRecordData for $subject {
fn build_canonical_bytes<'b>(
&self,
bytes: &'b mut [u8],
) -> Result<&'b mut [u8], TruncationError> {
(**self).build_canonical_bytes(bytes)
}
fn cmp_canonical(&self, other: &Self) -> Ordering {
(**self).cmp_canonical(&**other)
}
}
)*};
}
impl_canonical_record_data_for_deref! {
impl[T: ?Sized + CanonicalRecordData] CanonicalRecordData for &T;
impl[T: ?Sized + CanonicalRecordData] CanonicalRecordData for &mut T;
#[cfg(feature = "alloc")]
impl[T: ?Sized + CanonicalRecordData] CanonicalRecordData for alloc::boxed::Box<T>;
#[cfg(feature = "alloc")]
impl[T: ?Sized + CanonicalRecordData] CanonicalRecordData for alloc::rc::Rc<T>;
#[cfg(feature = "alloc")]
impl[T: ?Sized + CanonicalRecordData] CanonicalRecordData for alloc::sync::Arc<T>;
}
#[derive(AsBytes, BuildBytes, UnsizedCopy)]
#[repr(transparent)]
pub struct UnparsedRecordData([u8]);
impl UnparsedRecordData {
pub const unsafe fn new_unchecked(bytes: &[u8]) -> &Self {
core::mem::transmute(bytes)
}
}
impl<'a> ParseRecordData<'a> for &'a UnparsedRecordData {}
impl<'a> ParseRecordDataBytes<'a> for &'a UnparsedRecordData {
fn parse_record_data_bytes(
bytes: &'a [u8],
_rtype: RType,
) -> Result<Self, ParseError> {
if bytes.len() > 65535 {
return Err(ParseError);
}
Ok(unsafe { UnparsedRecordData::new_unchecked(bytes) })
}
}
impl BuildInMessage for UnparsedRecordData {
fn build_in_message(
&self,
contents: &mut [u8],
start: usize,
_compressor: &mut NameCompressor,
) -> Result<usize, TruncationError> {
let end = start + self.0.len();
contents
.get_mut(start..end)
.ok_or(TruncationError)?
.copy_from_slice(&self.0);
Ok(end)
}
}
impl Deref for UnparsedRecordData {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Borrow<[u8]> for UnparsedRecordData {
fn borrow(&self) -> &[u8] {
self
}
}
impl AsRef<[u8]> for UnparsedRecordData {
fn as_ref(&self) -> &[u8] {
self
}
}
#[cfg(feature = "alloc")]
impl Clone for alloc::boxed::Box<UnparsedRecordData> {
fn clone(&self) -> Self {
(*self).unsized_copy_into()
}
}
#[cfg(test)]
mod test {
use super::{RClass, RType, Record, UnparsedRecordData, TTL};
use crate::new::base::{
name::Name,
wire::{AsBytes, BuildBytes, ParseBytes, SplitBytes},
};
#[test]
fn parse_build() {
type Subject<'a> = Record<&'a Name, &'a UnparsedRecordData>;
let bytes =
b"\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x2A\x00\x00\x54";
let (record, rest) = Subject::split_bytes(bytes).unwrap();
assert_eq!(record.rname.as_bytes(), b"\x03com\x00");
assert_eq!(record.rtype, RType::A);
assert_eq!(record.rclass, RClass::IN);
assert_eq!(record.ttl, TTL::from(42));
assert_eq!(record.rdata.as_bytes(), b"");
assert_eq!(rest, b"\x54");
assert!(Subject::parse_bytes(bytes).is_err());
assert!(Subject::parse_bytes(&bytes[..15]).is_ok());
let mut buffer = [0u8; 15];
assert_eq!(record.build_bytes(&mut buffer), Ok(&mut [] as &mut [u8]));
assert_eq!(buffer, &bytes[..15]);
}
}