simple_dns/dns/rdata/
nsec.rs

1use std::borrow::Cow;
2
3use crate::{bytes_buffer::BytesBuffer, dns::WireFormat, Name};
4
5use super::RR;
6
7/// A NSEC record see [rfc4034](https://datatracker.ietf.org/doc/html/rfc4034#section-4)
8#[derive(Debug, PartialEq, Eq, Hash, Clone)]
9pub struct NSEC<'a> {
10    /// The next owner name in the canonical ordering of the zone
11    pub next_name: Name<'a>,
12    /// The type bit maps representing the RR types present at the NSEC RR's owner name
13    pub type_bit_maps: Vec<NsecTypeBitMap<'a>>,
14}
15
16/// A Type bit map entry in a NSEC record see [rfc4034](https://datatracker.ietf.org/doc/html/rfc4034#section-4.1.2)
17#[derive(Debug, PartialEq, Eq, Hash, Clone)]
18pub struct NsecTypeBitMap<'a> {
19    /// The window block number of this bit map
20    pub window_block: u8,
21    /// The bitmap containing the RR types present in this window block
22    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    /// Transforms the inner data into its owned type
90    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}