1use crate::{
2 bytes_buffer::BytesBuffer,
3 dns::WireFormat,
4 lib::Write,
5 lib::{BTreeMap, Cow, Vec},
6 Name,
7};
8
9use super::{RR, TYPE};
10
11#[derive(Debug, PartialEq, Eq, Hash, Clone)]
13pub struct NSEC<'a> {
14 pub next_name: Name<'a>,
16 pub type_bit_maps: Vec<NsecTypeBitMap<'a>>,
18}
19
20#[derive(Debug, PartialEq, Eq, Hash, Clone)]
22pub struct NsecTypeBitMap<'a> {
23 pub window_block: u8,
25 pub bitmap: Cow<'a, [u8]>,
27}
28
29impl RR for NSEC<'_> {
30 const TYPE_CODE: u16 = 47;
31}
32
33impl<'a> WireFormat<'a> for NSEC<'a> {
34 const MINIMUM_LEN: usize = 0;
35 fn parse(data: &mut BytesBuffer<'a>) -> crate::Result<Self>
36 where
37 Self: Sized,
38 {
39 let next_name = Name::parse(data)?;
40 let mut type_bit_maps = Vec::new();
41 let mut prev_window_block = None;
42
43 while data.has_remaining() {
44 let window_block = data.get_u8()?;
45 if let Some(prev_window_block) = prev_window_block {
46 if window_block <= prev_window_block {
47 return Err(crate::SimpleDnsError::InvalidDnsPacket);
48 }
49 }
50
51 prev_window_block = Some(window_block);
52
53 let bitmap_length = data.get_u8()? as usize;
54 if bitmap_length > 32 {
55 return Err(crate::SimpleDnsError::InvalidDnsPacket);
56 }
57
58 let bitmap = data.get_slice(bitmap_length)?;
59
60 type_bit_maps.push(NsecTypeBitMap {
61 window_block,
62 bitmap: Cow::Borrowed(bitmap),
63 });
64 }
65
66 Ok(Self {
67 next_name,
68 type_bit_maps,
69 })
70 }
71
72 fn write_to<T: Write>(&self, out: &mut T) -> crate::Result<()> {
73 self.next_name.write_to(out)?;
74
75 let mut sorted = self.type_bit_maps.clone();
76 sorted.sort_by_key(|a| a.window_block);
77
78 for record in sorted.iter() {
79 out.write_all(&[record.window_block])?;
80 out.write_all(&[record.bitmap.len() as u8])?;
81 out.write_all(&record.bitmap)?;
82 }
83
84 Ok(())
85 }
86
87 fn len(&self) -> usize {
88 self.next_name.len()
91 + self
92 .type_bit_maps
93 .iter()
94 .map(|el| el.bitmap.len() + 2) .sum::<usize>()
96 }
97}
98
99impl<'a> NSEC<'a> {
100 pub fn from_types(next_name: Name<'a>, types: &[TYPE]) -> Self {
104 let mut windows: BTreeMap<u8, [u8; 32]> = BTreeMap::new();
105 let mut max_bytes: BTreeMap<u8, usize> = BTreeMap::new();
106
107 for &ty in types {
108 let type_code = u16::from(ty);
109 let window = (type_code / 256) as u8;
110 let offset_in_window = (type_code % 256) as usize;
111 let byte_idx = offset_in_window / 8;
112 let bit_idx = offset_in_window % 8;
113
114 let bitmap = windows.entry(window).or_insert([0u8; 32]);
115 bitmap[byte_idx] |= 0x80 >> bit_idx;
116
117 let max_byte = max_bytes.entry(window).or_insert(0);
118 if byte_idx > *max_byte {
119 *max_byte = byte_idx;
120 }
121 }
122
123 let type_bit_maps = windows
124 .into_iter()
125 .map(|(window_block, bitmap)| {
126 let len = max_bytes.get(&window_block).copied().unwrap_or(0) + 1;
127 NsecTypeBitMap {
128 window_block,
129 bitmap: Cow::Owned(bitmap[..len].to_vec()),
130 }
131 })
132 .collect();
133
134 Self {
135 next_name,
136 type_bit_maps,
137 }
138 }
139
140 pub fn contains(&self, ty: TYPE) -> bool {
142 let type_code = u16::from(ty);
143 let window = (type_code / 256) as u8;
144 let offset_in_window = (type_code % 256) as usize;
145 let byte_idx = offset_in_window / 8;
146 let bit_mask = 0x80 >> (offset_in_window % 8);
147
148 for map in &self.type_bit_maps {
149 if map.window_block == window {
150 return map
151 .bitmap
152 .get(byte_idx)
153 .is_some_and(|byte| (byte & bit_mask) != 0);
154 }
155 }
156 false
157 }
158
159 pub fn types(&self) -> impl Iterator<Item = TYPE> + '_ {
161 self.type_bit_maps.iter().flat_map(|map| {
162 let window_base = (map.window_block as u16) * 256;
163 map.bitmap
164 .iter()
165 .enumerate()
166 .flat_map(move |(byte_idx, &byte)| {
167 (0..8).filter_map(move |bit_idx| {
168 if (byte & (0x80 >> bit_idx)) != 0 {
169 let type_code = window_base + (byte_idx as u16) * 8 + (bit_idx as u16);
170 Some(TYPE::from(type_code))
171 } else {
172 None
173 }
174 })
175 })
176 })
177 }
178
179 pub fn into_owned<'b>(self) -> NSEC<'b> {
181 let type_bit_maps = self
182 .type_bit_maps
183 .into_iter()
184 .map(|x| NsecTypeBitMap {
185 window_block: x.window_block,
186 bitmap: x.bitmap.into_owned().into(),
187 })
188 .collect();
189 NSEC {
190 next_name: self.next_name.into_owned(),
191 type_bit_maps,
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198
199 use super::*;
200 use crate::lib::vec;
201
202 #[test]
203 fn parse_and_write_nsec() {
204 let nsec = NSEC {
205 next_name: Name::new("host.example.com.").unwrap(),
206 type_bit_maps: vec![NsecTypeBitMap {
207 window_block: 0,
208 bitmap: vec![64, 1, 0, 0, 0, 1].into(),
209 }],
210 };
211 let mut data = Vec::new();
212 nsec.write_to(&mut data).unwrap();
213
214 let nsec = NSEC::parse(&mut data[..].into()).unwrap();
215 assert_eq!(nsec.next_name, Name::new("host.example.com.").unwrap());
216 assert_eq!(nsec.type_bit_maps.len(), 1);
217 assert_eq!(nsec.type_bit_maps[0].window_block, 0);
218 assert_eq!(nsec.type_bit_maps[0].bitmap, vec![64, 1, 0, 0, 0, 1]);
219 }
220
221 #[test]
222 fn nsec_reports_correct_len() {
223 let nsec = NSEC {
224 next_name: Name::new("host.example.com.").unwrap(),
225 type_bit_maps: vec![
226 NsecTypeBitMap {
227 window_block: 0,
228 bitmap: vec![0x40, 0x01, 0x00, 0x00, 0x00, 0x01].into(),
229 },
230 NsecTypeBitMap {
231 window_block: 1,
232 bitmap: vec![0x40, 0x01, 0x00, 0x00, 0x00, 0x01].into(),
233 },
234 ],
235 };
236
237 let mut buffer = Vec::new();
238 nsec.write_to(&mut buffer).unwrap();
239 assert_eq!(buffer.len(), nsec.len());
240 }
241
242 #[test]
243 #[cfg(feature = "std")]
244 fn parse_sample() -> Result<(), Box<dyn std::error::Error>> {
245 use crate::{rdata::RData, ResourceRecord};
246 let sample_file = std::fs::read("samples/zonefile/NSEC.sample")?;
247
248 let sample_rdata = match ResourceRecord::parse(&mut sample_file[..].into())?.rdata {
249 RData::NSEC(rdata) => rdata,
250 _ => unreachable!(),
251 };
252
253 assert_eq!(
254 sample_rdata.next_name,
255 Name::new("host.example.com.").unwrap()
256 );
257 assert_eq!(sample_rdata.type_bit_maps.len(), 1);
258 assert_eq!(sample_rdata.type_bit_maps[0].window_block, 0);
259 assert_eq!(
260 sample_rdata.type_bit_maps[0].bitmap,
261 vec![64, 1, 0, 0, 0, 1]
262 );
263
264 Ok(())
265 }
266
267 #[test]
268 fn test_nsec_from_types_and_helpers() {
269 let name = Name::new("host.example.com.").unwrap();
270 let types = [TYPE::A, TYPE::MX, TYPE::NSEC, TYPE::Unknown(300)];
271 let nsec = NSEC::from_types(name.clone(), &types);
272
273 assert_eq!(nsec.next_name, name);
274 assert_eq!(nsec.type_bit_maps.len(), 2);
275 assert_eq!(nsec.type_bit_maps[0].window_block, 0);
276 assert_eq!(nsec.type_bit_maps[0].bitmap, vec![64, 1, 0, 0, 0, 1]);
277 assert_eq!(nsec.type_bit_maps[1].window_block, 1);
278
279 assert!(nsec.contains(TYPE::A));
280 assert!(nsec.contains(TYPE::MX));
281 assert!(nsec.contains(TYPE::NSEC));
282 assert!(nsec.contains(TYPE::Unknown(300)));
283 assert!(!nsec.contains(TYPE::AAAA));
284 assert!(!nsec.contains(TYPE::TXT));
285
286 let recovered: Vec<_> = nsec.types().collect();
287 assert_eq!(recovered, types);
288
289 assert_eq!(u16::from(crate::CLASS::IN), 1);
290 assert_eq!(u16::from(crate::CLASS::CS), 2);
291 }
292}