use super::cmp::CanonicalOrd;
use super::iana::Rtype;
use super::scan::{Scan, Scanner, ScannerError, Symbol};
use super::wire::{Compose, Composer, ParseError};
use super::zonefile_fmt::{self, Formatter, ZonefileFmt};
use crate::utils::base16;
use core::cmp::Ordering;
use core::fmt;
use octseq::octets::{Octets, OctetsFrom};
use octseq::parse::Parser;
pub trait RecordData {
fn rtype(&self) -> Rtype;
}
impl<T: RecordData> RecordData for &T {
fn rtype(&self) -> Rtype {
(*self).rtype()
}
}
pub trait ComposeRecordData: RecordData {
fn rdlen(&self, compress: bool) -> Option<u16>;
fn compose_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError>;
fn compose_canonical_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError>;
fn compose_len_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
if let Some(rdlen) = self.rdlen(target.can_compress()) {
rdlen.compose(target)?;
self.compose_rdata(target)
} else {
compose_prefixed(target, |target| self.compose_rdata(target))
}
}
fn compose_canonical_len_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
if let Some(rdlen) = self.rdlen(false) {
rdlen.compose(target)?;
self.compose_canonical_rdata(target)
} else {
compose_prefixed(target, |target| {
self.compose_canonical_rdata(target)
})
}
}
}
fn compose_prefixed<Target: Composer + ?Sized, F>(
target: &mut Target,
op: F,
) -> Result<(), Target::AppendError>
where
F: FnOnce(&mut Target) -> Result<(), Target::AppendError>,
{
target.append_slice(&[0; 2])?;
let pos = target.as_ref().len();
match op(target) {
Ok(_) => {
let len = u16::try_from(target.as_ref().len() - pos)
.expect("long data");
target.as_mut()[pos - 2..pos]
.copy_from_slice(&(len).to_be_bytes());
Ok(())
}
Err(err) => {
target.truncate(pos);
Err(err)
}
}
}
impl<T: ComposeRecordData> ComposeRecordData for &T {
fn rdlen(&self, compress: bool) -> Option<u16> {
(*self).rdlen(compress)
}
fn compose_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
(*self).compose_rdata(target)
}
fn compose_canonical_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
(*self).compose_canonical_rdata(target)
}
}
pub trait ParseRecordData<'a, Octs: ?Sized>: RecordData + Sized {
fn parse_rdata(
rtype: Rtype,
parser: &mut Parser<'a, Octs>,
) -> Result<Option<Self>, ParseError>;
}
pub trait ParseAnyRecordData<'a, Octs: ?Sized>: RecordData + Sized {
fn parse_any_rdata(
rtype: Rtype,
parser: &mut Parser<'a, Octs>,
) -> Result<Self, ParseError>;
}
#[derive(Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct UnknownRecordData<Octs> {
rtype: Rtype,
#[cfg_attr(
feature = "serde",
serde(
serialize_with = "crate::utils::base16::serde::serialize",
deserialize_with = "crate::utils::base16::serde::deserialize",
bound(
serialize = "Octs: AsRef<[u8]> + octseq::serde::SerializeOctets",
deserialize = "\
Octs: \
octseq::builder::FromBuilder + \
octseq::serde::DeserializeOctets<'de>, \
<Octs as octseq::builder::FromBuilder>::Builder: \
octseq::builder::EmptyBuilder, \
",
)
)
)]
data: Octs,
}
impl<Octs> UnknownRecordData<Octs> {
pub fn from_octets(
rtype: Rtype,
data: Octs,
) -> Result<Self, LongRecordData>
where
Octs: AsRef<[u8]>,
{
LongRecordData::check_len(data.as_ref().len())?;
Ok(UnknownRecordData { rtype, data })
}
pub fn rtype(&self) -> Rtype {
self.rtype
}
pub fn data(&self) -> &Octs {
&self.data
}
pub fn scan<S: Scanner<Octets = Octs>>(
rtype: Rtype,
scanner: &mut S,
) -> Result<Self, S::Error>
where
Octs: AsRef<[u8]>,
{
let mut first = true;
scanner.scan_symbols(|symbol| {
if first {
first = false;
match symbol {
Symbol::SimpleEscape(b'#') => Ok(()),
_ => Err(S::Error::custom("'\\#' expected")),
}
} else {
Err(S::Error::custom("'\\#' expected"))
}
})?;
Self::scan_without_marker(rtype, scanner)
}
pub fn scan_without_marker<S: Scanner<Octets = Octs>>(
rtype: Rtype,
scanner: &mut S,
) -> Result<Self, S::Error>
where
Octs: AsRef<[u8]>,
{
let len = u16::scan(scanner)?;
let data = scanner.convert_entry(base16::SymbolConverter::new())?;
if data.as_ref().len() != usize::from(len) {
return Err(S::Error::custom(
"generic data has incorrect length",
));
}
Ok(UnknownRecordData { rtype, data })
}
pub fn parse_any_rdata<'a, SrcOcts>(
rtype: Rtype,
parser: &mut Parser<'a, SrcOcts>,
) -> Result<Self, ParseError>
where
SrcOcts: Octets<Range<'a> = Octs> + ?Sized + 'a,
{
let rdlen = parser.remaining();
parser
.parse_octets(rdlen)
.map(|data| Self { rtype, data })
.map_err(Into::into)
}
}
impl<Octs, SrcOcts> OctetsFrom<UnknownRecordData<SrcOcts>>
for UnknownRecordData<Octs>
where
Octs: OctetsFrom<SrcOcts>,
{
type Error = Octs::Error;
fn try_octets_from(
source: UnknownRecordData<SrcOcts>,
) -> Result<Self, Self::Error> {
Ok(UnknownRecordData {
rtype: source.rtype,
data: Octs::try_octets_from(source.data)?,
})
}
}
impl<Octs, Other> PartialEq<UnknownRecordData<Other>>
for UnknownRecordData<Octs>
where
Octs: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn eq(&self, other: &UnknownRecordData<Other>) -> bool {
self.data.as_ref().eq(other.data.as_ref())
}
}
impl<Octs: AsRef<[u8]>> Eq for UnknownRecordData<Octs> {}
impl<Octs, Other> PartialOrd<UnknownRecordData<Other>>
for UnknownRecordData<Octs>
where
Octs: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn partial_cmp(
&self,
other: &UnknownRecordData<Other>,
) -> Option<Ordering> {
self.data.as_ref().partial_cmp(other.data.as_ref())
}
}
impl<Octs, Other> CanonicalOrd<UnknownRecordData<Other>>
for UnknownRecordData<Octs>
where
Octs: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn canonical_cmp(&self, other: &UnknownRecordData<Other>) -> Ordering {
self.data.as_ref().cmp(other.data.as_ref())
}
}
impl<Octs: AsRef<[u8]>> Ord for UnknownRecordData<Octs> {
fn cmp(&self, other: &Self) -> Ordering {
self.data.as_ref().cmp(other.data.as_ref())
}
}
impl<Octs: AsRef<[u8]>> ComposeRecordData for UnknownRecordData<Octs> {
fn rdlen(&self, _compress: bool) -> Option<u16> {
Some(u16::try_from(self.data.as_ref().len()).expect("long rdata"))
}
fn compose_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
target.append_slice(self.data.as_ref())
}
fn compose_canonical_rdata<Target: Composer + ?Sized>(
&self,
target: &mut Target,
) -> Result<(), Target::AppendError> {
self.compose_rdata(target)
}
}
impl<Octs: AsRef<[u8]>> RecordData for UnknownRecordData<Octs> {
fn rtype(&self) -> Rtype {
self.rtype
}
}
impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs>
for UnknownRecordData<Octs::Range<'a>>
{
fn parse_rdata(
rtype: Rtype,
parser: &mut Parser<'a, Octs>,
) -> Result<Option<Self>, ParseError> {
let rdlen = parser.remaining();
parser
.parse_octets(rdlen)
.map(|data| Some(Self { rtype, data }))
.map_err(Into::into)
}
}
impl<Octs: AsRef<[u8]>> fmt::Display for UnknownRecordData<Octs> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\\# {}", self.data.as_ref().len())?;
for ch in self.data.as_ref() {
write!(f, " {:02x}", *ch)?
}
Ok(())
}
}
impl<Octs: AsRef<[u8]>> fmt::Debug for UnknownRecordData<Octs> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("UnknownRecordData(")?;
fmt::Display::fmt(self, f)?;
f.write_str(")")
}
}
impl<Octs: AsRef<[u8]>> ZonefileFmt for UnknownRecordData<Octs> {
fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
struct Data<'a>(&'a [u8]);
impl fmt::Display for Data<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\\# {}", self.0.len())?;
for ch in self.0 {
write!(f, " {:02x}", *ch)?
}
Ok(())
}
}
p.write_token(Data(self.data.as_ref()))
}
}
#[derive(Clone, Copy, Debug)]
pub struct LongRecordData(());
impl LongRecordData {
#[must_use]
pub fn as_str(self) -> &'static str {
"record data too long"
}
pub fn check_len(len: usize) -> Result<(), Self> {
if len > usize::from(u16::MAX) {
Err(Self(()))
} else {
Ok(())
}
}
pub fn check_append_len(
len: usize,
extra_len: usize,
) -> Result<(), Self> {
Self::check_len(len.checked_add(extra_len).ok_or(Self(()))?)
}
}
impl fmt::Display for LongRecordData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[cfg(feature = "std")]
impl std::error::Error for LongRecordData {}
#[cfg(test)]
#[cfg(all(feature = "std", feature = "bytes"))]
pub(crate) mod test {
use super::super::scan::IterScanner;
use super::*;
use bytes::{Bytes, BytesMut};
use core::fmt::Debug;
use octseq::builder::infallible;
use std::vec::Vec;
pub fn test_rdlen<R: ComposeRecordData>(data: R) {
let mut buf = Vec::new();
infallible(data.compose_rdata(&mut buf));
assert_eq!(buf.len(), usize::from(data.rdlen(false).unwrap()));
buf.clear();
infallible(data.compose_canonical_rdata(&mut buf));
assert_eq!(buf.len(), usize::from(data.rdlen(false).unwrap()));
}
pub fn test_compose_parse<In, F, Out>(data: &In, parse: F)
where
In: ComposeRecordData + PartialEq<Out> + Debug,
F: FnOnce(&mut Parser<'_, Bytes>) -> Result<Out, ParseError>,
Out: Debug,
{
let mut buf = BytesMut::new();
infallible(data.compose_rdata(&mut buf));
let buf = buf.freeze();
let mut parser = Parser::from_ref(&buf);
let parsed = (parse)(&mut parser).unwrap();
assert_eq!(parser.remaining(), 0);
assert_eq!(*data, parsed);
}
type TestScanner =
IterScanner<std::vec::IntoIter<std::string::String>, Vec<u8>>;
pub fn test_scan<F, T, X>(input: &[&str], scan: F, expected: &X)
where
F: FnOnce(
&mut TestScanner,
) -> Result<T, <TestScanner as Scanner>::Error>,
T: Debug,
X: Debug + PartialEq<T>,
{
let mut scanner = IterScanner::new(
input
.iter()
.map(|s| std::string::String::from(*s))
.collect::<Vec<_>>(),
);
assert_eq!(*expected, scan(&mut scanner).unwrap(),);
assert!(scanner.is_exhausted());
}
}