hickory_proto/rr/dnssec/rdata/
nsec.rs

1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! NSEC record types
9use std::fmt;
10
11#[cfg(feature = "serde-config")]
12use serde::{Deserialize, Serialize};
13
14use crate::error::*;
15use crate::rr::type_bit_map::{decode_type_bit_maps, encode_type_bit_maps};
16use crate::rr::{Name, RData, RecordData, RecordDataDecodable, RecordType};
17use crate::serialize::binary::*;
18
19use super::DNSSECRData;
20
21/// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-4), DNSSEC Resource Records, March 2005
22///
23/// ```text
24/// 4.1.  NSEC RDATA Wire Format
25///
26///    The RDATA of the NSEC RR is as shown below:
27///
28///                         1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
29///     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
30///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31///    /                      Next Domain Name                         /
32///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33///    /                       Type Bit Maps                           /
34///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35///
36/// 4.1.3.  Inclusion of Wildcard Names in NSEC RDATA
37///
38///    If a wildcard owner name appears in a zone, the wildcard label ("*")
39///    is treated as a literal symbol and is treated the same as any other
40///    owner name for the purposes of generating NSEC RRs.  Wildcard owner
41///    names appear in the Next Domain Name field without any wildcard
42///    expansion.  [RFC4035] describes the impact of wildcards on
43///    authenticated denial of existence.
44/// ```
45#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
46#[derive(Debug, PartialEq, Eq, Hash, Clone)]
47pub struct NSEC {
48    next_domain_name: Name,
49    type_bit_maps: Vec<RecordType>,
50}
51
52impl NSEC {
53    /// Constructs a new NSEC RData, warning this won't guarantee that the NSEC covers itself
54    ///  which it should at it's own name.
55    ///
56    /// # Arguments
57    ///
58    /// * `next_domain_name` - the name labels of the next ordered name in the zone
59    /// * `type_bit_maps` - a bit map of the types that exist at this name
60    ///
61    /// # Returns
62    ///
63    /// An NSEC RData for use in a Resource Record
64    pub fn new(next_domain_name: Name, type_bit_maps: Vec<RecordType>) -> Self {
65        Self {
66            next_domain_name,
67            type_bit_maps,
68        }
69    }
70
71    /// Constructs a new NSEC RData, this will add the NSEC itself as covered, generally
72    ///   correct for NSEC records generated at their own name
73    ///
74    /// # Arguments
75    ///
76    /// * `next_domain_name` - the name labels of the next ordered name in the zone
77    /// * `type_bit_maps` - a bit map of the types that exist at this name
78    ///
79    /// # Returns
80    ///
81    /// An NSEC RData for use in a Resource Record
82    pub fn new_cover_self(next_domain_name: Name, mut type_bit_maps: Vec<RecordType>) -> Self {
83        type_bit_maps.push(RecordType::NSEC);
84
85        Self::new(next_domain_name, type_bit_maps)
86    }
87
88    /// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-4.1.1), DNSSEC Resource Records, March 2005
89    ///
90    /// ```text
91    /// 4.1.1.  The Next Domain Name Field
92    ///
93    ///    The Next Domain field contains the next owner name (in the canonical
94    ///    ordering of the zone) that has authoritative data or contains a
95    ///    delegation point NS RRset; see Section 6.1 for an explanation of
96    ///    canonical ordering.  The value of the Next Domain Name field in the
97    ///    last NSEC record in the zone is the name of the zone apex (the owner
98    ///    name of the zone's SOA RR).  This indicates that the owner name of
99    ///    the NSEC RR is the last name in the canonical ordering of the zone.
100    ///
101    ///    A sender MUST NOT use DNS name compression on the Next Domain Name
102    ///    field when transmitting an NSEC RR.
103    ///
104    ///    Owner names of RRsets for which the given zone is not authoritative
105    ///    (such as glue records) MUST NOT be listed in the Next Domain Name
106    ///    unless at least one authoritative RRset exists at the same owner
107    ///    name.
108    /// ```
109    pub fn next_domain_name(&self) -> &Name {
110        &self.next_domain_name
111    }
112
113    /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-4.1.2)
114    ///
115    /// ```text
116    /// 4.1.2.  The Type Bit Maps Field
117    ///
118    ///    The Type Bit Maps field identifies the RRset types that exist at the
119    ///    NSEC RR's owner name.
120    ///
121    ///    A zone MUST NOT include an NSEC RR for any domain name that only
122    ///    holds glue records.
123    /// ```
124    pub fn type_bit_maps(&self) -> &[RecordType] {
125        &self.type_bit_maps
126    }
127}
128
129impl BinEncodable for NSEC {
130    /// [RFC 6840](https://tools.ietf.org/html/rfc6840#section-6)
131    ///
132    /// ```text
133    /// 5.1.  Errors in Canonical Form Type Code List
134    ///
135    ///   When canonicalizing DNS names (for both ordering and signing), DNS
136    ///   names in the RDATA section of NSEC resource records are not converted
137    ///   to lowercase.  DNS names in the RDATA section of RRSIG resource
138    ///   records are converted to lowercase.
139    /// ```
140    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
141        encoder.with_canonical_names(|encoder| {
142            self.next_domain_name().emit(encoder)?;
143            encode_type_bit_maps(encoder, self.type_bit_maps())
144        })
145    }
146}
147
148impl<'r> RecordDataDecodable<'r> for NSEC {
149    fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> ProtoResult<Self> {
150        let start_idx = decoder.index();
151
152        let next_domain_name = Name::read(decoder)?;
153
154        let bit_map_len = length
155            .map(|u| u as usize)
156            .checked_sub(decoder.index() - start_idx)
157            .map_err(|_| ProtoError::from("invalid rdata length in NSEC"))?;
158        let record_types = decode_type_bit_maps(decoder, bit_map_len)?;
159
160        Ok(Self::new(next_domain_name, record_types))
161    }
162}
163
164impl RecordData for NSEC {
165    fn try_from_rdata(data: RData) -> Result<Self, RData> {
166        match data {
167            RData::DNSSEC(DNSSECRData::NSEC(csync)) => Ok(csync),
168            _ => Err(data),
169        }
170    }
171
172    fn try_borrow(data: &RData) -> Option<&Self> {
173        match data {
174            RData::DNSSEC(DNSSECRData::NSEC(csync)) => Some(csync),
175            _ => None,
176        }
177    }
178
179    fn record_type(&self) -> RecordType {
180        RecordType::NSEC
181    }
182
183    fn into_rdata(self) -> RData {
184        RData::DNSSEC(DNSSECRData::NSEC(self))
185    }
186}
187
188/// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-4.2), DNSSEC Resource Records, March 2005
189///
190/// ```text
191/// 4.2.  The NSEC RR Presentation Format
192///
193///    The presentation format of the RDATA portion is as follows:
194///
195///    The Next Domain Name field is represented as a domain name.
196///
197///    The Type Bit Maps field is represented as a sequence of RR type
198///    mnemonics.  When the mnemonic is not known, the TYPE representation
199///    described in [RFC3597], Section 5, MUST be used.
200///
201/// 4.3.  NSEC RR Example
202///
203///    The following NSEC RR identifies the RRsets associated with
204///    alfa.example.com. and identifies the next authoritative name after
205///    alfa.example.com.
206///
207///    alfa.example.com. 86400 IN NSEC host.example.com. (
208///                                    A MX RRSIG NSEC TYPE1234 )
209///
210///    The first four text fields specify the name, TTL, Class, and RR type
211///    (NSEC).  The entry host.example.com. is the next authoritative name
212///    after alfa.example.com. in canonical order.  The A, MX, RRSIG, NSEC,
213///    and TYPE1234 mnemonics indicate that there are A, MX, RRSIG, NSEC,
214///    and TYPE1234 RRsets associated with the name alfa.example.com.
215///
216///    Assuming that the validator can authenticate this NSEC record, it
217///    could be used to prove that beta.example.com does not exist, or to
218///    prove that there is no AAAA record associated with alfa.example.com.
219///    Authenticated denial of existence is discussed in [RFC4035].
220/// ```
221impl fmt::Display for NSEC {
222    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
223        write!(f, "{}", self.next_domain_name)?;
224
225        for ty in &self.type_bit_maps {
226            write!(f, " {ty}")?;
227        }
228
229        Ok(())
230    }
231}
232
233#[cfg(test)]
234mod tests {
235    #![allow(clippy::dbg_macro, clippy::print_stdout)]
236
237    use super::*;
238
239    #[test]
240    fn test() {
241        use crate::rr::RecordType;
242        use std::str::FromStr;
243
244        let rdata = NSEC::new(
245            Name::from_str("www.example.com").unwrap(),
246            vec![
247                RecordType::A,
248                RecordType::AAAA,
249                RecordType::DS,
250                RecordType::RRSIG,
251            ],
252        );
253
254        let mut bytes = Vec::new();
255        let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
256        assert!(rdata.emit(&mut encoder).is_ok());
257        let bytes = encoder.into_bytes();
258
259        println!("bytes: {bytes:?}");
260
261        let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
262        let restrict = Restrict::new(bytes.len() as u16);
263        let read_rdata = NSEC::read_data(&mut decoder, restrict).expect("Decoding error");
264        assert_eq!(rdata, read_rdata);
265    }
266}