simple_dns/dns/rdata/
nsec.rs1use std::borrow::Cow;
2
3use crate::{bytes_buffer::BytesBuffer, dns::WireFormat, Name};
4
5use super::RR;
6
7#[derive(Debug, PartialEq, Eq, Hash, Clone)]
9pub struct NSEC<'a> {
10 pub next_name: Name<'a>,
12 pub type_bit_maps: Vec<NsecTypeBitMap<'a>>,
14}
15
16#[derive(Debug, PartialEq, Eq, Hash, Clone)]
18pub struct NsecTypeBitMap<'a> {
19 pub window_block: u8,
21 pub bitmap: Cow<'a, [u8]>,
23}
24
25impl RR for NSEC<'_> {
26 const TYPE_CODE: u16 = 47;
27}
28
29impl<'a> WireFormat<'a> for NSEC<'a> {
30 const MINIMUM_LEN: usize = 0;
31 fn parse(data: &mut BytesBuffer<'a>) -> crate::Result<Self>
32 where
33 Self: Sized,
34 {
35 let next_name = Name::parse(data)?;
36 let mut type_bit_maps = Vec::new();
37 let mut prev_window_block = None;
38
39 while data.has_remaining() {
40 let window_block = data.get_u8()?;
41 if let Some(prev_window_block) = prev_window_block {
42 if window_block <= prev_window_block {
43 return Err(crate::SimpleDnsError::InvalidDnsPacket);
44 }
45 }
46
47 prev_window_block = Some(window_block);
48
49 let bitmap_length = data.get_u8()? as usize;
50 if bitmap_length > 32 {
51 return Err(crate::SimpleDnsError::InvalidDnsPacket);
52 }
53
54 let bitmap = data.get_slice(bitmap_length)?;
55
56 type_bit_maps.push(NsecTypeBitMap {
57 window_block,
58 bitmap: Cow::Borrowed(bitmap),
59 });
60 }
61
62 Ok(Self {
63 next_name,
64 type_bit_maps,
65 })
66 }
67
68 fn write_to<T: std::io::Write>(&self, out: &mut T) -> crate::Result<()> {
69 self.next_name.write_to(out)?;
70
71 let mut sorted = self.type_bit_maps.clone();
72 sorted.sort_by(|a, b| a.window_block.cmp(&b.window_block));
73
74 for record in sorted.iter() {
75 out.write_all(&[record.window_block])?;
76 out.write_all(&[record.bitmap.len() as u8])?;
77 out.write_all(&record.bitmap)?;
78 }
79
80 Ok(())
81 }
82
83 fn len(&self) -> usize {
84 self.next_name.len()
85 }
86}
87
88impl NSEC<'_> {
89 pub fn into_owned<'b>(self) -> NSEC<'b> {
91 let type_bit_maps = self
92 .type_bit_maps
93 .into_iter()
94 .map(|x| NsecTypeBitMap {
95 window_block: x.window_block,
96 bitmap: x.bitmap.into_owned().into(),
97 })
98 .collect();
99 NSEC {
100 next_name: self.next_name.into_owned(),
101 type_bit_maps,
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use crate::{rdata::RData, ResourceRecord};
109
110 use super::*;
111
112 #[test]
113 fn parse_and_write_nsec() {
114 let nsec = NSEC {
115 next_name: Name::new("host.example.com.").unwrap(),
116 type_bit_maps: vec![NsecTypeBitMap {
117 window_block: 0,
118 bitmap: vec![64, 1, 0, 0, 0, 1].into(),
119 }],
120 };
121 let mut data = Vec::new();
122 nsec.write_to(&mut data).unwrap();
123
124 let nsec = NSEC::parse(&mut data[..].into()).unwrap();
125 assert_eq!(nsec.next_name, Name::new("host.example.com.").unwrap());
126 assert_eq!(nsec.type_bit_maps.len(), 1);
127 assert_eq!(nsec.type_bit_maps[0].window_block, 0);
128 assert_eq!(nsec.type_bit_maps[0].bitmap, vec![64, 1, 0, 0, 0, 1]);
129 }
130
131 #[test]
132 fn parse_sample() -> Result<(), Box<dyn std::error::Error>> {
133 let sample_file = std::fs::read("samples/zonefile/NSEC.sample")?;
134
135 let sample_rdata = match ResourceRecord::parse(&mut sample_file[..].into())?.rdata {
136 RData::NSEC(rdata) => rdata,
137 _ => unreachable!(),
138 };
139
140 assert_eq!(
141 sample_rdata.next_name,
142 Name::new("host.example.com.").unwrap()
143 );
144 assert_eq!(sample_rdata.type_bit_maps.len(), 1);
145 assert_eq!(sample_rdata.type_bit_maps[0].window_block, 0);
146 assert_eq!(
147 sample_rdata.type_bit_maps[0].bitmap,
148 vec![64, 1, 0, 0, 0, 1]
149 );
150
151 Ok(())
152 }
153}