Skip to main content

simple_dns/dns/rdata/
nsec.rs

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/// A NSEC record see [rfc4034](https://datatracker.ietf.org/doc/html/rfc4034#section-4)
12#[derive(Debug, PartialEq, Eq, Hash, Clone)]
13pub struct NSEC<'a> {
14    /// The next owner name in the canonical ordering of the zone
15    pub next_name: Name<'a>,
16    /// The type bit maps representing the RR types present at the NSEC RR's owner name
17    pub type_bit_maps: Vec<NsecTypeBitMap<'a>>,
18}
19
20/// A Type bit map entry in a NSEC record see [rfc4034](https://datatracker.ietf.org/doc/html/rfc4034#section-4.1.2)
21#[derive(Debug, PartialEq, Eq, Hash, Clone)]
22pub struct NsecTypeBitMap<'a> {
23    /// The window block number of this bit map
24    pub window_block: u8,
25    /// The bitmap containing the RR types present in this window block
26    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        // To calculate the len it is necessary to get the len of all the bitmaps in the nsec record
89        // Type Bit Maps Field = ( Window Block # | Bitmap Length | Bitmap )+
90        self.next_name.len()
91            + self
92                .type_bit_maps
93                .iter()
94                .map(|el| el.bitmap.len() + 2) // each type window has 2 bytes + bitmap data
95                .sum::<usize>()
96    }
97}
98
99impl<'a> NSEC<'a> {
100    /// Constructs a new `NSEC` record with the given `next_name` and list of [`TYPE`]s.
101    ///
102    /// Bitmaps are constructed and windowed according to [RFC 4034 Section 4.1.2](https://datatracker.ietf.org/doc/html/rfc4034#section-4.1.2).
103    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    /// Returns `true` if the type bit maps indicate the presence of `ty`.
141    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    /// Iterates over all [`TYPE`]s represented in this record's type bit maps.
160    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    /// Transforms the inner data into its owned type
180    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}