Skip to main content

simple_dns/dns/rdata/
nsec.rs

1use crate::{
2    bytes_buffer::BytesBuffer,
3    dns::WireFormat,
4    lib::Write,
5    lib::{Cow, Vec},
6    Name,
7};
8
9use super::RR;
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 NSEC<'_> {
100    /// Transforms the inner data into its owned type
101    pub fn into_owned<'b>(self) -> NSEC<'b> {
102        let type_bit_maps = self
103            .type_bit_maps
104            .into_iter()
105            .map(|x| NsecTypeBitMap {
106                window_block: x.window_block,
107                bitmap: x.bitmap.into_owned().into(),
108            })
109            .collect();
110        NSEC {
111            next_name: self.next_name.into_owned(),
112            type_bit_maps,
113        }
114    }
115}
116
117#[cfg(test)]
118mod tests {
119
120    use super::*;
121    use crate::lib::vec;
122
123    #[test]
124    fn parse_and_write_nsec() {
125        let nsec = NSEC {
126            next_name: Name::new("host.example.com.").unwrap(),
127            type_bit_maps: vec![NsecTypeBitMap {
128                window_block: 0,
129                bitmap: vec![64, 1, 0, 0, 0, 1].into(),
130            }],
131        };
132        let mut data = Vec::new();
133        nsec.write_to(&mut data).unwrap();
134
135        let nsec = NSEC::parse(&mut data[..].into()).unwrap();
136        assert_eq!(nsec.next_name, Name::new("host.example.com.").unwrap());
137        assert_eq!(nsec.type_bit_maps.len(), 1);
138        assert_eq!(nsec.type_bit_maps[0].window_block, 0);
139        assert_eq!(nsec.type_bit_maps[0].bitmap, vec![64, 1, 0, 0, 0, 1]);
140    }
141
142    #[test]
143    fn nsec_reports_correct_len() {
144        let nsec = NSEC {
145            next_name: Name::new("host.example.com.").unwrap(),
146            type_bit_maps: vec![
147                NsecTypeBitMap {
148                    window_block: 0,
149                    bitmap: vec![0x40, 0x01, 0x00, 0x00, 0x00, 0x01].into(),
150                },
151                NsecTypeBitMap {
152                    window_block: 1,
153                    bitmap: vec![0x40, 0x01, 0x00, 0x00, 0x00, 0x01].into(),
154                },
155            ],
156        };
157
158        let mut buffer = Vec::new();
159        nsec.write_to(&mut buffer).unwrap();
160        assert_eq!(buffer.len(), nsec.len());
161    }
162
163    #[test]
164    #[cfg(feature = "std")]
165    fn parse_sample() -> Result<(), Box<dyn std::error::Error>> {
166        use crate::{rdata::RData, ResourceRecord};
167        let sample_file = std::fs::read("samples/zonefile/NSEC.sample")?;
168
169        let sample_rdata = match ResourceRecord::parse(&mut sample_file[..].into())?.rdata {
170            RData::NSEC(rdata) => rdata,
171            _ => unreachable!(),
172        };
173
174        assert_eq!(
175            sample_rdata.next_name,
176            Name::new("host.example.com.").unwrap()
177        );
178        assert_eq!(sample_rdata.type_bit_maps.len(), 1);
179        assert_eq!(sample_rdata.type_bit_maps[0].window_block, 0);
180        assert_eq!(
181            sample_rdata.type_bit_maps[0].bitmap,
182            vec![64, 1, 0, 0, 0, 1]
183        );
184
185        Ok(())
186    }
187}