use core::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::error::*;
use crate::rr::{Name, RData, RecordData, RecordDataDecodable, RecordType, RecordTypeSet};
use crate::serialize::binary::*;
use super::DNSSECRData;
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct NSEC {
next_domain_name: Name,
type_bit_maps: RecordTypeSet,
}
impl NSEC {
pub fn new(
next_domain_name: Name,
type_bit_maps: impl IntoIterator<Item = RecordType>,
) -> Self {
Self {
next_domain_name,
type_bit_maps: RecordTypeSet::new(type_bit_maps),
}
}
pub fn new_cover_self(
next_domain_name: Name,
type_bit_maps: impl IntoIterator<Item = RecordType>,
) -> Self {
Self::new(
next_domain_name,
type_bit_maps
.into_iter()
.chain([RecordType::NSEC, RecordType::RRSIG]),
)
}
pub fn next_domain_name(&self) -> &Name {
&self.next_domain_name
}
pub fn type_bit_maps(&self) -> impl Iterator<Item = RecordType> + '_ {
self.type_bit_maps.iter()
}
pub fn type_set(&self) -> &RecordTypeSet {
&self.type_bit_maps
}
}
impl BinEncodable for NSEC {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
let mut encoder = encoder.with_rdata_behavior(RDataEncoding::Other);
self.next_domain_name().emit(&mut encoder)?;
self.type_bit_maps.emit(&mut encoder)?;
Ok(())
}
}
impl<'r> RecordDataDecodable<'r> for NSEC {
fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> Result<Self, DecodeError> {
let start_idx = decoder.index();
let next_domain_name = Name::read(decoder)?;
let offset = u16::try_from(decoder.index() - start_idx).map_err(|_| {
DecodeError::IncorrectRDataLengthRead {
read: decoder.index() - start_idx,
len: u16::MAX as usize,
}
})?;
let bit_map_len =
length
.checked_sub(offset)
.map_err(|len| DecodeError::IncorrectRDataLengthRead {
read: offset as usize,
len: len as usize,
})?;
let type_bit_maps = RecordTypeSet::read_data(decoder, bit_map_len)?;
Ok(Self {
next_domain_name,
type_bit_maps,
})
}
}
impl RecordData for NSEC {
fn try_borrow(data: &RData) -> Option<&Self> {
match data {
RData::DNSSEC(DNSSECRData::NSEC(csync)) => Some(csync),
_ => None,
}
}
fn record_type(&self) -> RecordType {
RecordType::NSEC
}
fn into_rdata(self) -> RData {
RData::DNSSEC(DNSSECRData::NSEC(self))
}
}
impl fmt::Display for NSEC {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(f, "{}", self.next_domain_name)?;
for ty in self.type_bit_maps.iter() {
write!(f, " {ty}")?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]
use std::println;
use alloc::vec::Vec;
use super::*;
#[test]
fn test() {
use crate::rr::RecordType;
use core::str::FromStr;
let rdata = NSEC::new(
Name::from_str("www.example.com.").unwrap(),
[
RecordType::A,
RecordType::AAAA,
RecordType::DS,
RecordType::RRSIG,
],
);
let mut bytes = Vec::new();
let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
assert!(rdata.emit(&mut encoder).is_ok());
let bytes = encoder.into_bytes();
println!("bytes: {bytes:?}");
let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
let restrict = Restrict::new(bytes.len() as u16);
let read_rdata = NSEC::read_data(&mut decoder, restrict).expect("Decoding error");
assert_eq!(rdata, read_rdata);
}
#[test]
fn rfc4034_example_rdata() {
let bytes = b"\x04host\
\x07example\
\x03com\x00\
\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 rdata = NSEC::new(
Name::parse("host.example.com.", None).unwrap(),
[
RecordType::A,
RecordType::MX,
RecordType::RRSIG,
RecordType::NSEC,
RecordType::Unknown(1234),
],
);
let mut buffer = Vec::new();
let mut encoder = BinEncoder::new(&mut buffer);
rdata.emit(&mut encoder).expect("Encoding error");
assert_eq!(encoder.into_bytes(), bytes);
let mut decoder = BinDecoder::new(bytes);
let decoded = NSEC::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
.expect("Decoding error");
assert_eq!(decoded, rdata);
}
}