Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/decode/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ pub enum DecodeError {
SSHFPAlgorithm(u8),
#[error("Could not decode SSHFPType: {0}")]
SSHFPType(u8),
#[error("The bitmap length must be between 1 and 32 bytes: {0}")]
NSECBitmapLength(u8),
#[error("Could not decode AlgorithmType: {0}")]
AlgorithmType(u8),
#[error("Could not decode DigestType: {0}")]
Expand Down
1 change: 1 addition & 0 deletions src/decode/rr/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ impl<'a, 'b: 'a> Decoder<'b, 'b> {
Type::URI => RR::URI(r_data.rr_uri(header)?),
Type::EID => RR::EID(r_data.rr_eid(header)?),
Type::NIMLOC => RR::NIMLOC(r_data.rr_nimloc(header)?),
Type::NSEC => RR::NSEC(r_data.rr_nsec(header)?),
Type::DNSKEY => RR::DNSKEY(r_data.rr_dnskey(header)?),
Type::DS => RR::DS(r_data.rr_ds(header)?),
Type::CAA => RR::CAA(r_data.rr_caa(header)?),
Expand Down
34 changes: 33 additions & 1 deletion src/decode/rr/rfc_4034.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::Header;
use crate::rr::{
AlgorithmType, DigestType, DNSKEY, DNSKEY_ZERO_MASK, DS, SECURE_ENTRY_POINT_FLAG, ZONE_KEY_FLAG,
AlgorithmType, DigestType, Type, DNSKEY, DNSKEY_ZERO_MASK, DS, NSEC, SECURE_ENTRY_POINT_FLAG,
ZONE_KEY_FLAG,
};
use crate::DecodeResult;
use crate::{decode::Decoder, DecodeError};
Expand Down Expand Up @@ -66,4 +67,35 @@ impl<'a, 'b: 'a> Decoder<'a, 'b> {
};
Ok(ds)
}

pub(super) fn rr_nsec(&mut self, header: Header) -> DecodeResult<NSEC> {
let class = header.get_class()?;
let next_domain_name = self.domain_name()?;
let mut type_bit_maps = Vec::new();
while self.remaining()? > 0 {
let window = self.u8()?;
let bitmap_length = self.u8()?;
if bitmap_length == 0 || bitmap_length > 32 {
return Err(DecodeError::NSECBitmapLength(bitmap_length));
}
let bitmap = self.read(bitmap_length as usize)?;
for (index, byte) in bitmap.iter().enumerate() {
for bit in 0..8 {
if byte & (0x80 >> bit) != 0 {
let type_number = u16::from(window) * 256 + (index as u16 * 8 + bit);
let type_ = Type::try_from(type_number).map_err(DecodeError::Type)?;
type_bit_maps.push(type_);
}
}
}
}
let nsec = NSEC {
domain_name: header.domain_name,
ttl: header.ttl,
class,
next_domain_name,
type_bit_maps,
};
Ok(nsec)
}
}
1 change: 1 addition & 0 deletions src/encode/rr/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ impl Encoder {
RR::URI(uri) => self.rr_uri(uri),
RR::EID(eid) => self.rr_eid(eid),
RR::NIMLOC(nimloc) => self.rr_nimloc(nimloc),
RR::NSEC(nsec) => self.rr_nsec(nsec),
RR::DNSKEY(dnskey) => self.rr_dnskey(dnskey),
RR::DS(ds) => self.rr_ds(ds),
RR::CAA(caa) => self.rr_caa(caa),
Expand Down
38 changes: 37 additions & 1 deletion src/encode/rr/rfc_4034.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::encode::Encoder;
use crate::rr::{AlgorithmType, DigestType, Type, DNSKEY, DS};
use crate::rr::{AlgorithmType, DigestType, Type, DNSKEY, DS, NSEC};
use crate::EncodeResult;
use std::collections::BTreeMap;

impl Encoder {
fn rr_algorithm_type(&mut self, algorithm_type: AlgorithmType) {
Expand Down Expand Up @@ -36,4 +37,39 @@ impl Encoder {
self.vec(&ds.digest);
self.set_length_index(length_index)
}

pub(super) fn rr_nsec(&mut self, nsec: &NSEC) -> EncodeResult<()> {
self.domain_name(&nsec.domain_name)?;
Comment thread
kgmyatthu marked this conversation as resolved.
self.rr_type(&Type::NSEC);
self.rr_class(&nsec.class);
self.u32(nsec.ttl);
let length_index = self.create_length_index();
self.domain_name(&nsec.next_domain_name)?;

let mut windows: BTreeMap<u8, [u8; 32]> = BTreeMap::new();
for type_ in &nsec.type_bit_maps {
let type_value = *type_ as u16;
let window = (type_value / 256) as u8;
let offset = (type_value % 256) as u8;
let bitmap = windows.entry(window).or_insert([0u8; 32]);
let byte_index = (offset / 8) as usize;
let bit_index = offset % 8;
bitmap[byte_index] |= 0x80 >> bit_index;
}

for (window, bitmap) in windows {
let mut length = bitmap.len();
while length > 0 && bitmap[length - 1] == 0 {
length -= 1;
}
if length == 0 {
continue;
}
self.u8(window);
self.u8(length as u8);
self.vec(&bitmap[..length]);
}

self.set_length_index(length_index)
}
}
16 changes: 10 additions & 6 deletions src/rr/enums.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pub use super::{
A, AAAA, AFSDB, APL, CAA, CNAME, DNAME, DNSKEY, DS, EID, EUI48, EUI64, GPOS, HINFO, ISDN, KX,
L32, L64, LOC, LP, MB, MD, MF, MG, MINFO, MR, MX, NID, NIMLOC, NS, NSAP, NULL, OPT, PTR, PX,
RP, RT, SOA, SRV, SSHFP, TXT, URI, WKS, X25,
L32, L64, LOC, LP, MB, MD, MF, MG, MINFO, MR, MX, NID, NIMLOC, NS, NSAP, NSEC, NULL, OPT, PTR,
PX, RP, RT, SOA, SRV, SSHFP, TXT, URI, WKS, X25,
};
use crate::rr::draft_ietf_dnsop_svcb_https::ServiceBinding;
use std::fmt::{Display, Formatter, Result as FmtResult};
Expand Down Expand Up @@ -176,10 +176,10 @@ try_from_enum_to_integer! {
/// The [DNSKEY] type.
///
/// [DNSKEY]: https://tools.ietf.org/html/rfc4034#section-2
DNSKEY = 48,
DHCID = 49,
NSEC3 = 50,
NSEC3PARAM = 51,
DNSKEY = 48,
DHCID = 49,
NSEC3 = 50,
NSEC3PARAM = 51,
TLSA = 52,
SMIMEA = 53,

Expand Down Expand Up @@ -280,6 +280,7 @@ pub enum RR {
EUI48(EUI48),
EUI64(EUI64),
DS(DS),
NSEC(NSEC),
DNSKEY(DNSKEY),
CAA(CAA),
SVCB(ServiceBinding),
Expand Down Expand Up @@ -331,6 +332,7 @@ impl RR {
RR::URI(uri) => Some(uri.ttl),
RR::EID(eid) => Some(eid.ttl),
RR::DS(ds) => Some(ds.ttl),
RR::NSEC(nsec) => Some(nsec.ttl),
RR::DNSKEY(dnskey) => Some(dnskey.ttl),
RR::CAA(caa) => Some(caa.ttl),
RR::SVCB(svcb) => Some(svcb.ttl),
Expand Down Expand Up @@ -382,6 +384,7 @@ impl RR {
RR::URI(uri) => Some(uri.class),
RR::EID(eid) => Some(eid.class),
RR::DS(ds) => Some(ds.class),
RR::NSEC(nsec) => Some(nsec.class),
RR::DNSKEY(dnskey) => Some(dnskey.class),
RR::CAA(caa) => Some(caa.class),
RR::SVCB(_) => Some(Class::IN),
Expand Down Expand Up @@ -435,6 +438,7 @@ impl Display for RR {
RR::URI(uri) => uri.fmt(f),
RR::EID(eid) => eid.fmt(f),
RR::DS(ds) => ds.fmt(f),
RR::NSEC(nsec) => nsec.fmt(f),
RR::DNSKEY(dnskey) => dnskey.fmt(f),
RR::CAA(caa) => caa.fmt(f),
RR::SVCB(svcb) => svcb.fmt(f),
Expand Down
3 changes: 2 additions & 1 deletion src/rr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ pub use rfc_3123::{APItem, APL, APL_NEGATION_MASK};
pub use rfc_3596::AAAA;
pub use rfc_3658::{SSHFPAlgorithm, SSHFPType, SSHFP};
pub use rfc_4034::{
AlgorithmType, DigestType, DNSKEY, DNSKEY_ZERO_MASK, DS, SECURE_ENTRY_POINT_FLAG, ZONE_KEY_FLAG,
AlgorithmType, DigestType, DNSKEY, DNSKEY_ZERO_MASK, DS, NSEC, SECURE_ENTRY_POINT_FLAG,
ZONE_KEY_FLAG,
};
pub use rfc_6672::DNAME;
pub use rfc_6742::{L32, L64, LP, NID};
Expand Down
28 changes: 28 additions & 0 deletions src/rr/rfc_4034.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::rr::Class;
use crate::rr::Type;
use crate::DomainName;
use hex::encode;
use std::fmt::{Display, Formatter, Result as FmtResult};
Expand Down Expand Up @@ -116,3 +117,30 @@ impl Display for DS {
)
}
}

#[derive(Debug, PartialEq, Clone, Eq, Hash)]
pub struct NSEC {
pub domain_name: DomainName,
pub ttl: u32,
pub class: Class,
pub next_domain_name: DomainName,
pub type_bit_maps: Vec<Type>,
Comment thread
kgmyatthu marked this conversation as resolved.
Outdated
}

impl Display for NSEC {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"{} {} {} NSEC {} {}",
self.domain_name,
self.ttl,
self.class,
self.next_domain_name,
self.type_bit_maps
.iter()
.map(|type_| format!("{:?}", type_))
.collect::<Vec<_>>()
.join(" ")
)
}
}
22 changes: 18 additions & 4 deletions tests/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ use dns_message_parser::{
ExtendedDNSErrors, Padding, ECS,
},
APItem, Address, AlgorithmType, Class, DigestType, ISDNAddress, PSDNAddress,
SSHFPAlgorithm, SSHFPType, ServiceBinding, ServiceParameter, Tag, A, AAAA, APL, CAA, CNAME,
DNAME, DNSKEY, DS, EID, EUI48, EUI64, GPOS, HINFO, ISDN, KX, L32, L64, LP, MB, MD, MF, MG,
MINFO, MR, MX, NID, NIMLOC, NS, OPT, PTR, PX, RP, RR, RT, SA, SOA, SRV, SSHFP, TXT, URI,
X25,
SSHFPAlgorithm, SSHFPType, ServiceBinding, ServiceParameter, Tag, Type, A, AAAA, APL, CAA,
CNAME, DNAME, DNSKEY, DS, EID, EUI48, EUI64, GPOS, HINFO, ISDN, KX, L32, L64, LP, MB, MD,
MF, MG, MINFO, MR, MX, NID, NIMLOC, NS, NSEC, OPT, PTR, PX, RP, RR, RT, SA, SOA, SRV,
SSHFP, TXT, URI, X25,
},
Dns, Flags, Opcode, RCode,
};
Expand Down Expand Up @@ -757,6 +757,20 @@ fn rr_ds() {
);
}

#[test]
fn rr_nsec() {
let domain_name = "example.org".parse().unwrap();
let next_domain_name = "ns.example.org".parse().unwrap();
let rr = RR::NSEC(NSEC {
domain_name,
ttl: 3600,
class: Class::IN,
next_domain_name,
type_bit_maps: vec![Type::A, Type::MX, Type::RRSIG],
});
check_output(&rr, "example.org. 3600 IN NSEC ns.example.org. A MX RRSIG");
}

#[test]
fn rr_caa() {
let domain_name = "caa.example.org".parse().unwrap();
Expand Down