use core::hash::{Hash, Hasher};
use core::{cmp::Ordering, fmt};
use crate::new::base::build::BuildInMessage;
use crate::new::base::name::{CanonicalName, Name, NameCompressor};
use crate::new::base::wire::*;
use crate::new::base::{CanonicalRecordData, RType};
use crate::utils::dst::UnsizedCopy;
#[derive(Clone, Debug, PartialEq, Eq, Hash, BuildBytes)]
pub struct Nsec<'a> {
pub next: &'a Name,
pub types: &'a TypeBitmaps,
}
impl Nsec<'_> {
#[cfg(feature = "bumpalo")]
pub fn clone_to_bump<'r>(&self, bump: &'r bumpalo::Bump) -> Nsec<'r> {
use crate::utils::dst::copy_to_bump;
Nsec {
next: copy_to_bump(self.next, bump),
types: copy_to_bump(self.types, bump),
}
}
}
impl CanonicalRecordData for Nsec<'_> {
fn cmp_canonical(&self, other: &Self) -> Ordering {
self.next
.cmp_composed(other.next)
.then_with(|| self.types.as_bytes().cmp(other.types.as_bytes()))
}
}
impl BuildInMessage for Nsec<'_> {
fn build_in_message(
&self,
contents: &mut [u8],
start: usize,
_compressor: &mut NameCompressor,
) -> Result<usize, TruncationError> {
let bytes = contents.get_mut(start..).ok_or(TruncationError)?;
let rest = self.build_bytes(bytes)?.len();
Ok(contents.len() - rest)
}
}
impl<'a> ParseBytes<'a> for Nsec<'a> {
fn parse_bytes(bytes: &'a [u8]) -> Result<Self, ParseError> {
let (next, bytes) = <&Name>::split_bytes(bytes)?;
if bytes.is_empty() {
return Err(ParseError);
}
let types = <&TypeBitmaps>::parse_bytes(bytes)?;
Ok(Self { next, types })
}
}
#[derive(PartialEq, Eq, AsBytes, BuildBytes, UnsizedCopy)]
#[repr(transparent)]
pub struct TypeBitmaps {
octets: [u8],
}
impl TypeBitmaps {
pub fn types(&self) -> impl Iterator<Item = RType> + '_ {
fn split_window(octets: &[u8]) -> Option<(u8, &[u8], &[u8])> {
let &[num, len, ref rest @ ..] = octets else {
return None;
};
let (bits, rest) = rest.split_at(len as usize);
Some((num, bits, rest))
}
core::iter::successors(split_window(&self.octets), |(_, _, rest)| {
split_window(rest)
})
.flat_map(move |(num, bits, _)| {
bits.iter().enumerate().flat_map(move |(i, &b)| {
(0..8).filter(move |&j| ((b >> (7 - j)) & 1) != 0).map(
move |j| {
RType::from(u16::from_be_bytes([
num,
(i * 8 + j) as u8,
]))
},
)
})
})
}
}
impl fmt::Debug for TypeBitmaps {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_set().entries(self.types()).finish()
}
}
impl TypeBitmaps {
fn validate_bytes(mut octets: &[u8]) -> Result<(), ParseError> {
let mut num = None;
while let Some(&next) = octets.first() {
if num.replace(next) > Some(next) {
return Err(ParseError);
}
octets = Self::validate_window_bytes(octets)?;
}
Ok(())
}
fn validate_window_bytes(octets: &[u8]) -> Result<&[u8], ParseError> {
let &[_num, len, ref rest @ ..] = octets else {
return Err(ParseError);
};
if !(1..=32).contains(&len) || rest.len() < len as usize {
return Err(ParseError);
}
let (bits, rest) = rest.split_at(len as usize);
if bits.last() == Some(&0) {
return Err(ParseError);
}
Ok(rest)
}
}
unsafe impl ParseBytesZC for TypeBitmaps {
fn parse_bytes_by_ref(bytes: &[u8]) -> Result<&Self, ParseError> {
Self::validate_bytes(bytes)?;
unsafe { core::mem::transmute(bytes) }
}
}
#[cfg(feature = "alloc")]
impl Clone for alloc::boxed::Box<TypeBitmaps> {
fn clone(&self) -> Self {
(*self).unsized_copy_into()
}
}
impl Hash for TypeBitmaps {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.octets)
}
}
impl<'a> Nsec<'a> {
pub fn new(next: &'a Name, types: &'a TypeBitmaps) -> Self {
Self { next, types }
}
pub fn types(&self) -> &TypeBitmaps {
self.types
}
pub fn next_name(&self) -> &Name {
self.next
}
}
impl TypeBitmaps {
pub fn iter(&self) -> impl Iterator<Item = RType> {
self.types()
}
pub fn contains(&self, rtype: RType) -> bool {
self.types().any(|t| t == rtype)
}
pub fn is_empty(&self) -> bool {
self.types().next().is_none()
}
}
#[cfg(test)]
mod tests {
use crate::new::base::{RType, wire::ParseBytesZC};
use super::TypeBitmaps;
#[test]
fn type_bitmaps_parse() {
let bytes = b"\x00\x06\x40\x01\x00\x00\x00\x03\
\x04\x1b\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x20";
let bitmaps = TypeBitmaps::parse_bytes_by_ref(bytes).unwrap();
let expected = [
RType::A, RType::MX, RType::RRSIG, RType::NSEC, RType::from(0x04D2),
];
assert!(
bitmaps.types().eq(expected),
"{bitmaps:?} did not match expectation {expected:?}"
);
}
}