domain/rdata/
nsec3.rs

1//! Record data from [RFC 5155]: NSEC3 and NSEC3PARAM records.
2//!
3//! This RFC defines the NSEC3 and NSEC3PARAM resource records.
4//!
5//! [RFC 5155]: https://tools.ietf.org/html/rfc5155
6
7use super::dnssec::RtypeBitmap;
8use crate::base::cmp::CanonicalOrd;
9use crate::base::iana::{Nsec3HashAlgorithm, Rtype};
10use crate::base::rdata::{ComposeRecordData, ParseRecordData, RecordData};
11use crate::base::scan::{
12    ConvertSymbols, EntrySymbol, Scan, Scanner, ScannerError,
13};
14use crate::base::wire::{Compose, Composer, Parse, ParseError};
15use crate::base::zonefile_fmt::{self, Formatter, ZonefileFmt};
16use crate::utils::{base16, base32};
17#[cfg(feature = "bytes")]
18use bytes::Bytes;
19use core::cmp::Ordering;
20use core::fmt::Write;
21use core::{fmt, hash, mem, str};
22use octseq::builder::{
23    EmptyBuilder, FreezeBuilder, FromBuilder, OctetsBuilder,
24};
25use octseq::octets::{Octets, OctetsFrom, OctetsInto};
26use octseq::parse::Parser;
27#[cfg(feature = "serde")]
28use octseq::serde::{DeserializeOctets, SerializeOctets};
29
30//------------ Nsec3 ---------------------------------------------------------
31
32#[derive(Clone)]
33#[cfg_attr(
34    feature = "serde",
35    derive(serde::Serialize, serde::Deserialize),
36    serde(bound(
37        serialize = "
38            Octs: octseq::serde::SerializeOctets + AsRef<[u8]>,
39        ",
40        deserialize = "
41            Octs: FromBuilder + octseq::serde::DeserializeOctets<'de>,
42            <Octs as FromBuilder>::Builder:
43                EmptyBuilder + octseq::builder::Truncate
44                + AsRef<[u8]> + AsMut<[u8]>,
45        ",
46    ))
47)]
48pub struct Nsec3<Octs> {
49    hash_algorithm: Nsec3HashAlgorithm,
50    flags: u8,
51    iterations: u16,
52    salt: Nsec3Salt<Octs>,
53    next_owner: OwnerHash<Octs>,
54    types: RtypeBitmap<Octs>,
55}
56
57impl Nsec3<()> {
58    /// The rtype of this record data type.
59    pub(crate) const RTYPE: Rtype = Rtype::NSEC3;
60}
61
62impl<Octs> Nsec3<Octs> {
63    pub fn new(
64        hash_algorithm: Nsec3HashAlgorithm,
65        flags: u8,
66        iterations: u16,
67        salt: Nsec3Salt<Octs>,
68        next_owner: OwnerHash<Octs>,
69        types: RtypeBitmap<Octs>,
70    ) -> Self {
71        Nsec3 {
72            hash_algorithm,
73            flags,
74            iterations,
75            salt,
76            next_owner,
77            types,
78        }
79    }
80
81    pub fn hash_algorithm(&self) -> Nsec3HashAlgorithm {
82        self.hash_algorithm
83    }
84
85    pub fn flags(&self) -> u8 {
86        self.flags
87    }
88
89    pub fn opt_out(&self) -> bool {
90        self.flags & 0x01 != 0
91    }
92
93    pub fn iterations(&self) -> u16 {
94        self.iterations
95    }
96
97    pub fn salt(&self) -> &Nsec3Salt<Octs> {
98        &self.salt
99    }
100
101    pub fn next_owner(&self) -> &OwnerHash<Octs> {
102        &self.next_owner
103    }
104
105    pub fn set_next_owner(&mut self, next_owner: OwnerHash<Octs>) {
106        self.next_owner = next_owner;
107    }
108
109    pub fn types(&self) -> &RtypeBitmap<Octs> {
110        &self.types
111    }
112
113    pub fn set_types(&mut self, types: RtypeBitmap<Octs>) {
114        self.types = types;
115    }
116
117    pub(super) fn convert_octets<Target>(
118        self,
119    ) -> Result<Nsec3<Target>, Target::Error>
120    where
121        Target: OctetsFrom<Octs>,
122    {
123        Ok(Nsec3::new(
124            self.hash_algorithm,
125            self.flags,
126            self.iterations,
127            self.salt.try_octets_into()?,
128            self.next_owner.try_octets_into()?,
129            self.types.try_octets_into()?,
130        ))
131    }
132
133    pub(super) fn flatten<Target: OctetsFrom<Octs>>(
134        self,
135    ) -> Result<Nsec3<Target>, Target::Error> {
136        self.convert_octets()
137    }
138
139    pub fn scan<S: Scanner<Octets = Octs>>(
140        scanner: &mut S,
141    ) -> Result<Self, S::Error> {
142        Ok(Self::new(
143            Nsec3HashAlgorithm::scan(scanner)?,
144            u8::scan(scanner)?,
145            u16::scan(scanner)?,
146            Nsec3Salt::scan(scanner)?,
147            OwnerHash::scan(scanner)?,
148            RtypeBitmap::scan(scanner)?,
149        ))
150    }
151}
152
153impl<Octs: AsRef<[u8]>> Nsec3<Octs> {
154    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
155        parser: &mut Parser<'a, Src>,
156    ) -> Result<Self, ParseError> {
157        let hash_algorithm = Nsec3HashAlgorithm::parse(parser)?;
158        let flags = u8::parse(parser)?;
159        let iterations = u16::parse(parser)?;
160        let salt = Nsec3Salt::parse(parser)?;
161        let next_owner = OwnerHash::parse(parser)?;
162        let types = RtypeBitmap::parse(parser)?;
163        Ok(Self::new(
164            hash_algorithm,
165            flags,
166            iterations,
167            salt,
168            next_owner,
169            types,
170        ))
171    }
172}
173
174//--- OctetsFrom
175
176impl<Octs, SrcOcts> OctetsFrom<Nsec3<SrcOcts>> for Nsec3<Octs>
177where
178    Octs: OctetsFrom<SrcOcts>,
179{
180    type Error = Octs::Error;
181
182    fn try_octets_from(source: Nsec3<SrcOcts>) -> Result<Self, Self::Error> {
183        Ok(Nsec3::new(
184            source.hash_algorithm,
185            source.flags,
186            source.iterations,
187            Nsec3Salt::try_octets_from(source.salt)?,
188            OwnerHash::try_octets_from(source.next_owner)?,
189            RtypeBitmap::try_octets_from(source.types)?,
190        ))
191    }
192}
193
194//--- PartialEq and Eq
195
196impl<Octs, Other> PartialEq<Nsec3<Other>> for Nsec3<Octs>
197where
198    Octs: AsRef<[u8]>,
199    Other: AsRef<[u8]>,
200{
201    fn eq(&self, other: &Nsec3<Other>) -> bool {
202        self.hash_algorithm == other.hash_algorithm
203            && self.flags == other.flags
204            && self.iterations == other.iterations
205            && self.salt == other.salt
206            && self.next_owner == other.next_owner
207            && self.types == other.types
208    }
209}
210
211impl<Octs: AsRef<[u8]>> Eq for Nsec3<Octs> {}
212
213//--- PartialOrd, CanonicalOrd, and Ord
214
215impl<Octs, Other> PartialOrd<Nsec3<Other>> for Nsec3<Octs>
216where
217    Octs: AsRef<[u8]>,
218    Other: AsRef<[u8]>,
219{
220    fn partial_cmp(&self, other: &Nsec3<Other>) -> Option<Ordering> {
221        match self.hash_algorithm.partial_cmp(&other.hash_algorithm) {
222            Some(Ordering::Equal) => {}
223            other => return other,
224        }
225        match self.flags.partial_cmp(&other.flags) {
226            Some(Ordering::Equal) => {}
227            other => return other,
228        }
229        match self.iterations.partial_cmp(&other.iterations) {
230            Some(Ordering::Equal) => {}
231            other => return other,
232        }
233        match self.salt.partial_cmp(&other.salt) {
234            Some(Ordering::Equal) => {}
235            other => return other,
236        }
237        match self.next_owner.partial_cmp(&other.next_owner) {
238            Some(Ordering::Equal) => {}
239            other => return other,
240        }
241        self.types.partial_cmp(&other.types)
242    }
243}
244
245impl<Octs, Other> CanonicalOrd<Nsec3<Other>> for Nsec3<Octs>
246where
247    Octs: AsRef<[u8]>,
248    Other: AsRef<[u8]>,
249{
250    fn canonical_cmp(&self, other: &Nsec3<Other>) -> Ordering {
251        match self.hash_algorithm.cmp(&other.hash_algorithm) {
252            Ordering::Equal => {}
253            other => return other,
254        }
255        match self.flags.cmp(&other.flags) {
256            Ordering::Equal => {}
257            other => return other,
258        }
259        match self.iterations.cmp(&other.iterations) {
260            Ordering::Equal => {}
261            other => return other,
262        }
263        match self.salt.canonical_cmp(&other.salt) {
264            Ordering::Equal => {}
265            other => return other,
266        }
267        match self.next_owner.canonical_cmp(&other.next_owner) {
268            Ordering::Equal => {}
269            other => return other,
270        }
271        self.types.canonical_cmp(&other.types)
272    }
273}
274
275impl<Octs: AsRef<[u8]>> Ord for Nsec3<Octs> {
276    fn cmp(&self, other: &Self) -> Ordering {
277        self.canonical_cmp(other)
278    }
279}
280
281//--- Hash
282
283impl<Octs: AsRef<[u8]>> hash::Hash for Nsec3<Octs> {
284    fn hash<H: hash::Hasher>(&self, state: &mut H) {
285        self.hash_algorithm.hash(state);
286        self.flags.hash(state);
287        self.iterations.hash(state);
288        self.salt.hash(state);
289        self.next_owner.hash(state);
290        self.types.hash(state);
291    }
292}
293
294//--- RecordData
295
296impl<Octs> RecordData for Nsec3<Octs> {
297    fn rtype(&self) -> Rtype {
298        Nsec3::RTYPE
299    }
300}
301
302impl<'a, Octs> ParseRecordData<'a, Octs> for Nsec3<Octs::Range<'a>>
303where
304    Octs: Octets + ?Sized,
305{
306    fn parse_rdata(
307        rtype: Rtype,
308        parser: &mut Parser<'a, Octs>,
309    ) -> Result<Option<Self>, ParseError> {
310        if rtype == Nsec3::RTYPE {
311            Self::parse(parser).map(Some)
312        } else {
313            Ok(None)
314        }
315    }
316}
317
318impl<Octs: AsRef<[u8]>> ComposeRecordData for Nsec3<Octs> {
319    fn rdlen(&self, _compress: bool) -> Option<u16> {
320        Some(
321            u16::checked_add(
322                Nsec3HashAlgorithm::COMPOSE_LEN
323                    + u8::COMPOSE_LEN
324                    + u16::COMPOSE_LEN,
325                self.salt.compose_len(),
326            )
327            .expect("long NSEC3")
328            .checked_add(self.next_owner.compose_len())
329            .expect("long NSEC3")
330            .checked_add(self.types.compose_len())
331            .expect("long NSEC3"),
332        )
333    }
334
335    fn compose_rdata<Target: Composer + ?Sized>(
336        &self,
337        target: &mut Target,
338    ) -> Result<(), Target::AppendError> {
339        self.hash_algorithm.compose(target)?;
340        self.flags.compose(target)?;
341        self.iterations.compose(target)?;
342        self.salt.compose(target)?;
343        self.next_owner.compose(target)?;
344        self.types.compose(target)
345    }
346
347    fn compose_canonical_rdata<Target: Composer + ?Sized>(
348        &self,
349        target: &mut Target,
350    ) -> Result<(), Target::AppendError> {
351        self.compose_rdata(target)
352    }
353}
354
355//--- Display, and Debug
356
357impl<Octs: AsRef<[u8]>> fmt::Display for Nsec3<Octs> {
358    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
359        write!(
360            f,
361            "{} {} {} {} ",
362            self.hash_algorithm, self.flags, self.iterations, self.salt
363        )?;
364        base32::display_hex(&self.next_owner, f)?;
365        if !self.types.is_empty() {
366            write!(f, " {}", self.types)?;
367        }
368        Ok(())
369    }
370}
371
372impl<Octs: AsRef<[u8]>> fmt::Debug for Nsec3<Octs> {
373    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
374        f.debug_struct("Nsec3")
375            .field("hash_algorithm", &self.hash_algorithm)
376            .field("flags", &self.flags)
377            .field("iterations", &self.iterations)
378            .field("salt", &self.salt)
379            .field("next_owner", &self.next_owner)
380            .field("types", &self.types)
381            .finish()
382    }
383}
384
385//--- ZonefileFmt
386
387impl<Octs: AsRef<[u8]>> ZonefileFmt for Nsec3<Octs>
388where
389    Octs: AsRef<[u8]>,
390{
391    fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
392        p.block(|p| {
393            p.write_show(self.hash_algorithm)?;
394            p.write_token(self.flags)?;
395            p.write_comment(format_args!(
396                "flags: {}",
397                if self.opt_out() { "opt-out" } else { "<none>" }
398            ))?;
399            p.write_token(self.iterations)?;
400            p.write_comment("iterations")?;
401            p.write_show(&self.salt)?;
402            p.write_token(base32::encode_display_hex(&self.next_owner))?;
403            p.write_show(&self.types)
404        })
405    }
406}
407
408//------------ Nsec3Param ----------------------------------------------------
409
410// https://datatracker.ietf.org/doc/html/rfc5155#section-3.2
411// 3.2.  NSEC3 RDATA Wire Format
412//   "Flags field is a single octet, the Opt-Out flag is the least significant
413//    bit"
414const NSEC3_OPT_OUT_FLAG_MASK: u8 = 0b0000_0001;
415
416#[derive(Clone)]
417#[cfg_attr(
418    feature = "serde",
419    derive(serde::Serialize, serde::Deserialize),
420    serde(bound(
421        serialize = "
422            Octs: octseq::serde::SerializeOctets + AsRef<[u8]>,
423        ",
424        deserialize = "
425            Octs: FromBuilder + octseq::serde::DeserializeOctets<'de>,
426            <Octs as FromBuilder>::Builder: OctetsBuilder + EmptyBuilder,
427        ",
428    ))
429)]
430pub struct Nsec3param<Octs> {
431    /// https://www.rfc-editor.org/rfc/rfc5155.html#section-3.1.1
432    /// 3.1.1.  Hash Algorithm
433    ///   "The Hash Algorithm field identifies the cryptographic hash
434    ///    algorithm used to construct the hash-value."
435    hash_algorithm: Nsec3HashAlgorithm,
436
437    /// https://www.rfc-editor.org/rfc/rfc5155.html#section-3.1.2
438    /// 3.1.2.  Flags
439    ///   "The Flags field contains 8 one-bit flags that can be used to
440    ///   indicate different processing.  All undefined flags must be zero.
441    ///   The only flag defined by this specification is the Opt-Out flag."
442    ///
443    /// 3.1.2.1.  Opt-Out Flag
444    ///   "If the Opt-Out flag is set, the NSEC3 record covers zero or more
445    ///    unsigned delegations.
446    ///    
447    ///    If the Opt-Out flag is clear, the NSEC3 record covers zero unsigned
448    ///    delegations.
449    ///    
450    ///    The Opt-Out Flag indicates whether this NSEC3 RR may cover unsigned
451    ///    delegations.  It is the least significant bit in the Flags field.
452    ///    See Section 6 for details about the use of this flag."
453    flags: u8,
454
455    /// https://www.rfc-editor.org/rfc/rfc5155.html#section-3.1.3
456    /// 3.1.3.  Iterations
457    ///   "The Iterations field defines the number of additional times the
458    ///    hash function has been performed.  More iterations result in
459    ///    greater resiliency of the hash value against dictionary attacks,
460    ///    but at a higher computational cost for both the server and
461    ///    resolver.  See Section 5 for details of the use of this field, and
462    ///    Section 10.3 for limitations on the value."
463    /// 
464    /// https://www.rfc-editor.org/rfc/rfc9276.html#section-3.1
465    /// 3.1. Best Practice for Zone Publishers 
466    ///   "If NSEC3 must be used, then an iterations count of 0 MUST be used
467    ///    to alleviate computational burdens."
468    iterations: u16,
469
470    /// https://datatracker.ietf.org/doc/html/rfc5155#section-3.1.5
471    /// 3.1.5.  Salt
472    ///   "The Salt field is appended to the original owner name before
473    ///    hashing in order to defend against pre-calculated dictionary
474    ///    attacks."
475    /// 
476    /// https://www.rfc-editor.org/rfc/rfc9276.html#section-3.1
477    /// 3.1. Best Practice for Zone Publishers 
478    ///   "Operators SHOULD NOT use a salt by indicating a zero-length salt
479    ///   value instead (represented as a "-" in the presentation format)."
480    salt: Nsec3Salt<Octs>,
481}
482
483impl Nsec3param<()> {
484    /// The rtype of this record data type.
485    pub(crate) const RTYPE: Rtype = Rtype::NSEC3PARAM;
486}
487
488impl<Octs> Nsec3param<Octs> {
489    pub fn new(
490        hash_algorithm: Nsec3HashAlgorithm,
491        flags: u8,
492        iterations: u16,
493        salt: Nsec3Salt<Octs>,
494    ) -> Self {
495        Nsec3param {
496            hash_algorithm,
497            flags,
498            iterations,
499            salt,
500        }
501    }
502
503    pub fn hash_algorithm(&self) -> Nsec3HashAlgorithm {
504        self.hash_algorithm
505    }
506
507    pub fn flags(&self) -> u8 {
508        self.flags
509    }
510
511    pub fn set_opt_out_flag(&mut self) {
512        self.flags |= NSEC3_OPT_OUT_FLAG_MASK;
513    }
514
515    pub fn opt_out_flag(&self) -> bool {
516        self.flags & NSEC3_OPT_OUT_FLAG_MASK == NSEC3_OPT_OUT_FLAG_MASK
517    }    
518
519    pub fn iterations(&self) -> u16 {
520        self.iterations
521    }
522
523    pub fn salt(&self) -> &Nsec3Salt<Octs> {
524        &self.salt
525    }
526
527    pub fn into_salt(self) -> Nsec3Salt<Octs> {
528        self.salt
529    }
530
531    pub(super) fn convert_octets<Target>(
532        self,
533    ) -> Result<Nsec3param<Target>, Target::Error>
534    where
535        Target: OctetsFrom<Octs>,
536    {
537        Ok(Nsec3param::new(
538            self.hash_algorithm,
539            self.flags,
540            self.iterations,
541            self.salt.try_octets_into()?,
542        ))
543    }
544
545    pub(super) fn flatten<Target: OctetsFrom<Octs>>(
546        self,
547    ) -> Result<Nsec3param<Target>, Target::Error> {
548        self.convert_octets()
549    }
550
551    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
552        parser: &mut Parser<'a, Src>,
553    ) -> Result<Self, ParseError> {
554        Ok(Self::new(
555            Nsec3HashAlgorithm::parse(parser)?,
556            u8::parse(parser)?,
557            u16::parse(parser)?,
558            Nsec3Salt::parse(parser)?,
559        ))
560    }
561
562    pub fn scan<S: Scanner<Octets = Octs>>(
563        scanner: &mut S,
564    ) -> Result<Self, S::Error> {
565        Ok(Self::new(
566            Nsec3HashAlgorithm::scan(scanner)?,
567            u8::scan(scanner)?,
568            u16::scan(scanner)?,
569            Nsec3Salt::scan(scanner)?,
570        ))
571    }
572}
573
574//--- Default
575
576impl<Octs> Default for Nsec3param<Octs>
577where
578    Octs: From<&'static [u8]>,
579{
580    /// Best practice default values for NSEC3 hashing.
581    ///
582    /// Per [RFC 9276] section 3.1:
583    ///
584    /// - _SHA-1, no extra iterations, empty salt._
585    ///
586    /// Per [RFC 5155] section 4.1.2:
587    ///
588    /// - _The Opt-Out flag is not used and is set to zero._
589    /// - _All other flags are reserved for future use, and must be zero._
590    ///
591    /// [RFC 5155]: https://www.rfc-editor.org/rfc/rfc5155.html
592    /// [RFC 9276]: https://www.rfc-editor.org/rfc/rfc9276.html
593    fn default() -> Self {
594        Self {
595            hash_algorithm: Nsec3HashAlgorithm::SHA1,
596            flags: 0,
597            iterations: 0,
598            salt: Nsec3Salt::empty(),
599        }
600    }
601}
602
603//--- OctetsFrom
604
605impl<Octs, SrcOcts> OctetsFrom<Nsec3param<SrcOcts>> for Nsec3param<Octs>
606where
607    Octs: OctetsFrom<SrcOcts>,
608{
609    type Error = Octs::Error;
610
611    fn try_octets_from(
612        source: Nsec3param<SrcOcts>,
613    ) -> Result<Self, Self::Error> {
614        Ok(Nsec3param::new(
615            source.hash_algorithm,
616            source.flags,
617            source.iterations,
618            Nsec3Salt::try_octets_from(source.salt)?,
619        ))
620    }
621}
622
623//--- PartialEq and Eq
624
625impl<Octs, Other> PartialEq<Nsec3param<Other>> for Nsec3param<Octs>
626where
627    Octs: AsRef<[u8]>,
628    Other: AsRef<[u8]>,
629{
630    fn eq(&self, other: &Nsec3param<Other>) -> bool {
631        self.hash_algorithm == other.hash_algorithm
632            && self.flags == other.flags
633            && self.iterations == other.iterations
634            && self.salt == other.salt
635    }
636}
637
638impl<Octs: AsRef<[u8]>> Eq for Nsec3param<Octs> {}
639
640//--- PartialOrd, CanonicalOrd, and Ord
641
642impl<Octs, Other> PartialOrd<Nsec3param<Other>> for Nsec3param<Octs>
643where
644    Octs: AsRef<[u8]>,
645    Other: AsRef<[u8]>,
646{
647    fn partial_cmp(&self, other: &Nsec3param<Other>) -> Option<Ordering> {
648        match self.hash_algorithm.partial_cmp(&other.hash_algorithm) {
649            Some(Ordering::Equal) => {}
650            other => return other,
651        }
652        match self.flags.partial_cmp(&other.flags) {
653            Some(Ordering::Equal) => {}
654            other => return other,
655        }
656        match self.iterations.partial_cmp(&other.iterations) {
657            Some(Ordering::Equal) => {}
658            other => return other,
659        }
660        self.salt.partial_cmp(&other.salt)
661    }
662}
663
664impl<Octs, Other> CanonicalOrd<Nsec3param<Other>> for Nsec3param<Octs>
665where
666    Octs: AsRef<[u8]>,
667    Other: AsRef<[u8]>,
668{
669    fn canonical_cmp(&self, other: &Nsec3param<Other>) -> Ordering {
670        match self.hash_algorithm.cmp(&other.hash_algorithm) {
671            Ordering::Equal => {}
672            other => return other,
673        }
674        match self.flags.cmp(&other.flags) {
675            Ordering::Equal => {}
676            other => return other,
677        }
678        match self.iterations.cmp(&other.iterations) {
679            Ordering::Equal => {}
680            other => return other,
681        }
682        self.salt.canonical_cmp(&other.salt)
683    }
684}
685
686impl<Octs: AsRef<[u8]>> Ord for Nsec3param<Octs> {
687    fn cmp(&self, other: &Self) -> Ordering {
688        match self.hash_algorithm.cmp(&other.hash_algorithm) {
689            Ordering::Equal => {}
690            other => return other,
691        }
692        match self.flags.cmp(&other.flags) {
693            Ordering::Equal => {}
694            other => return other,
695        }
696        match self.iterations.cmp(&other.iterations) {
697            Ordering::Equal => {}
698            other => return other,
699        }
700        self.salt.cmp(&other.salt)
701    }
702}
703
704//--- Hash
705
706impl<Octs: AsRef<[u8]>> hash::Hash for Nsec3param<Octs> {
707    fn hash<H: hash::Hasher>(&self, state: &mut H) {
708        self.hash_algorithm.hash(state);
709        self.flags.hash(state);
710        self.iterations.hash(state);
711        self.salt.hash(state);
712    }
713}
714
715//--- RecordData, ParseRecordData, ComposeRecordData
716
717impl<Octs> RecordData for Nsec3param<Octs> {
718    fn rtype(&self) -> Rtype {
719        Nsec3param::RTYPE
720    }
721}
722
723impl<'a, Octs> ParseRecordData<'a, Octs> for Nsec3param<Octs::Range<'a>>
724where
725    Octs: Octets + ?Sized,
726{
727    fn parse_rdata(
728        rtype: Rtype,
729        parser: &mut Parser<'a, Octs>,
730    ) -> Result<Option<Self>, ParseError> {
731        if rtype == Nsec3param::RTYPE {
732            Self::parse(parser).map(Some)
733        } else {
734            Ok(None)
735        }
736    }
737}
738
739impl<Octs: AsRef<[u8]>> ComposeRecordData for Nsec3param<Octs> {
740    fn rdlen(&self, _compress: bool) -> Option<u16> {
741        Some(
742            u16::checked_add(
743                Nsec3HashAlgorithm::COMPOSE_LEN
744                    + u8::COMPOSE_LEN
745                    + u16::COMPOSE_LEN,
746                self.salt.compose_len(),
747            )
748            .expect("long NSEC3"),
749        )
750    }
751
752    fn compose_rdata<Target: Composer + ?Sized>(
753        &self,
754        target: &mut Target,
755    ) -> Result<(), Target::AppendError> {
756        self.hash_algorithm.compose(target)?;
757        self.flags.compose(target)?;
758        self.iterations.compose(target)?;
759        self.salt.compose(target)
760    }
761
762    fn compose_canonical_rdata<Target: Composer + ?Sized>(
763        &self,
764        target: &mut Target,
765    ) -> Result<(), Target::AppendError> {
766        self.compose_rdata(target)
767    }
768}
769
770//--- Display and Debug
771
772impl<Octs: AsRef<[u8]>> fmt::Display for Nsec3param<Octs> {
773    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
774        write!(
775            f,
776            "{} {} {} {}",
777            self.hash_algorithm, self.flags, self.iterations, self.salt
778        )
779    }
780}
781
782impl<Octs: AsRef<[u8]>> fmt::Debug for Nsec3param<Octs> {
783    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
784        f.debug_struct("Nsec3param")
785            .field("hash_algorithm", &self.hash_algorithm)
786            .field("flags", &self.flags)
787            .field("iterations", &self.iterations)
788            .field("salt", &self.salt)
789            .finish()
790    }
791}
792
793//--- ZonefileFmt
794
795impl<Octs: AsRef<[u8]>> ZonefileFmt for Nsec3param<Octs> {
796    fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
797        p.block(|p| {
798            p.write_show(self.hash_algorithm)?;
799            p.write_token(self.flags)?;
800            p.write_comment("flags")?;
801            p.write_token(self.iterations)?;
802            p.write_comment("iterations")?;
803            p.write_show(&self.salt)
804        })
805    }
806}
807
808//------------ Nsec3Salt -----------------------------------------------------
809
810/// The salt value of an NSEC3 record.
811///
812/// The salt can never be longer than 255 octets since its length is encoded
813/// as a single octet.
814///
815/// The salt uses Base 16 (i.e., hex digits) as its representation format with
816/// no whitespace allowed.
817#[derive(Clone)]
818#[repr(transparent)]
819#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
820pub struct Nsec3Salt<Octs: ?Sized>(Octs);
821
822impl Nsec3Salt<()> {
823    /// The salt has a maximum length 255 octets since its length is encoded
824    /// as a single octet.
825    pub const MAX_LEN: usize = 255;
826}
827
828impl<Octs: ?Sized> Nsec3Salt<Octs> {
829    /// Creates an empty salt value.
830    #[must_use]
831    pub fn empty() -> Self
832    where
833        Octs: From<&'static [u8]>,
834    {
835        Self(b"".as_ref().into())
836    }
837
838    /// Crates a new salt value from the given octets.
839    ///
840    /// Returns succesfully if `octets` can indeed be used as a
841    /// character string, i.e., it is not longer than 255 bytes.
842    pub fn from_octets(octets: Octs) -> Result<Self, Nsec3SaltError>
843    where
844        Octs: AsRef<[u8]> + Sized,
845    {
846        if octets.as_ref().len() > Nsec3Salt::MAX_LEN {
847            Err(Nsec3SaltError(()))
848        } else {
849            Ok(unsafe { Self::from_octets_unchecked(octets) })
850        }
851    }
852
853    /// Creates a salt value from octets without length check.
854    ///
855    /// As this can break the guarantees made by the type, it is unsafe.
856    unsafe fn from_octets_unchecked(octets: Octs) -> Self
857    where
858        Octs: Sized,
859    {
860        Self(octets)
861    }
862
863    /// Converts the salt value into the underlying octets.
864    pub fn into_octets(self) -> Octs
865    where
866        Octs: Sized,
867    {
868        self.0
869    }
870
871    /// Returns a reference to a slice of the salt.
872    pub fn as_slice(&self) -> &[u8]
873    where
874        Octs: AsRef<[u8]>,
875    {
876        self.0.as_ref()
877    }
878
879    fn salt_len(&self) -> u8
880    where
881        Octs: AsRef<[u8]>,
882    {
883        self.0.as_ref().len().try_into().expect("long salt")
884    }
885
886    fn compose_len(&self) -> u16
887    where
888        Octs: AsRef<[u8]>,
889    {
890        u16::from(self.salt_len()) + 1
891    }
892
893    fn compose<Target: Composer /*OctetsBuilder*/ + ?Sized>(
894        &self,
895        target: &mut Target,
896    ) -> Result<(), Target::AppendError>
897    where
898        Octs: AsRef<[u8]>,
899    {
900        self.salt_len().compose(target)?;
901        target.append_slice(self.0.as_ref())
902    }
903}
904
905#[cfg(feature = "bytes")]
906#[cfg_attr(docsrs, doc(cfg(feature = "bytes")))]
907impl Nsec3Salt<Bytes> {
908    /// Creates a new salt from a bytes value.
909    pub fn from_bytes(bytes: Bytes) -> Result<Self, Nsec3SaltError> {
910        Self::from_octets(bytes)
911    }
912}
913
914impl Nsec3Salt<[u8]> {
915    /// Creates a new salt value from an octet slice.
916    pub fn from_slice(slice: &[u8]) -> Result<&Self, Nsec3SaltError> {
917        if slice.len() > Nsec3Salt::MAX_LEN {
918            Err(Nsec3SaltError(()))
919        } else {
920            Ok(unsafe { Self::from_slice_unchecked(slice) })
921        }
922    }
923
924    /// Creates a new salt value from an octets slice without checking.
925    ///
926    /// # Safety
927    ///
928    /// The passed slice must be no longer than [`Nsec3Salt::MAX_LEN`].
929    unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
930        // SAFETY: Nsec3Salt has repr(transparent)
931        mem::transmute(slice)
932    }
933}
934
935impl<Octs> Nsec3Salt<Octs> {
936    pub fn scan<S: Scanner<Octets = Octs>>(
937        scanner: &mut S,
938    ) -> Result<Self, S::Error> {
939        #[derive(Default)]
940        struct Converter(Option<Option<base16::SymbolConverter>>);
941
942        impl<Sym, Error> ConvertSymbols<Sym, Error> for Converter
943        where
944            Sym: Into<EntrySymbol>,
945            Error: ScannerError,
946        {
947            fn process_symbol(
948                &mut self,
949                symbol: Sym,
950            ) -> Result<Option<&[u8]>, Error> {
951                let symbol = symbol.into();
952                // If we are none, this is the first symbol. A '-' means
953                // empty. Anything else means Base 16.
954                if self.0.is_none() {
955                    match symbol {
956                        EntrySymbol::Symbol(symbol)
957                            if symbol.into_char() == Ok('-') =>
958                        {
959                            self.0 = Some(None);
960                            return Ok(None);
961                        }
962                        _ => {
963                            self.0 =
964                                Some(Some(base16::SymbolConverter::new()));
965                        }
966                    }
967                }
968
969                match self.0.as_mut() {
970                    None => unreachable!(),
971                    Some(None) => Err(Error::custom("illegal NSEC3 salt")),
972                    Some(Some(ref mut base16)) => {
973                        base16.process_symbol(symbol)
974                    }
975                }
976            }
977
978            fn process_tail(&mut self) -> Result<Option<&[u8]>, Error> {
979                if let Some(Some(ref mut base16)) = self.0 {
980                    <base16::SymbolConverter
981                        as ConvertSymbols<Sym, Error>
982                    >::process_tail(base16)
983                } else {
984                    Ok(None)
985                }
986            }
987        }
988
989        scanner
990            .convert_token(Converter::default())
991            .map(|res| unsafe { Self::from_octets_unchecked(res) })
992    }
993
994    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
995        parser: &mut Parser<'a, Src>,
996    ) -> Result<Self, ParseError> {
997        let len = parser.parse_u8()? as usize;
998        parser
999            .parse_octets(len)
1000            .map(|octets| unsafe { Self::from_octets_unchecked(octets) })
1001            .map_err(Into::into)
1002    }
1003}
1004
1005//--- OctetsFrom and FromStr
1006
1007impl<Octs, SrcOcts> OctetsFrom<Nsec3Salt<SrcOcts>> for Nsec3Salt<Octs>
1008where
1009    Octs: OctetsFrom<SrcOcts>,
1010{
1011    type Error = Octs::Error;
1012
1013    fn try_octets_from(
1014        source: Nsec3Salt<SrcOcts>,
1015    ) -> Result<Self, Self::Error> {
1016        Octs::try_octets_from(source.0)
1017            .map(|octets| unsafe { Self::from_octets_unchecked(octets) })
1018    }
1019}
1020
1021impl<Octs> str::FromStr for Nsec3Salt<Octs>
1022where
1023    Octs: FromBuilder,
1024    <Octs as FromBuilder>::Builder: EmptyBuilder,
1025{
1026    type Err = Nsec3SaltFromStrError;
1027
1028    fn from_str(s: &str) -> Result<Self, Self::Err> {
1029        if s == "-" {
1030            Ok(unsafe {
1031                Self::from_octets_unchecked(Octs::Builder::empty().freeze())
1032            })
1033        } else {
1034            base16::decode(s)
1035                .map_err(Nsec3SaltFromStrError::DecodeError)
1036                .and_then(|octets| {
1037                    Self::from_octets(octets)
1038                        .map_err(Nsec3SaltFromStrError::Nsec3SaltError)
1039                })
1040        }
1041    }
1042}
1043
1044//--- AsRef
1045
1046impl<Octs: AsRef<U> + ?Sized, U: ?Sized> AsRef<U> for Nsec3Salt<Octs> {
1047    fn as_ref(&self) -> &U {
1048        self.0.as_ref()
1049    }
1050}
1051
1052//--- PartialEq and Eq
1053
1054impl<T, U> PartialEq<U> for Nsec3Salt<T>
1055where
1056    T: AsRef<[u8]> + ?Sized,
1057    U: AsRef<[u8]> + ?Sized,
1058{
1059    fn eq(&self, other: &U) -> bool {
1060        self.as_slice().eq(other.as_ref())
1061    }
1062}
1063
1064impl<T: AsRef<[u8]> + ?Sized> Eq for Nsec3Salt<T> {}
1065
1066//--- PartialOrd, Ord, and CanonicalOrd
1067
1068impl<T, U> PartialOrd<U> for Nsec3Salt<T>
1069where
1070    T: AsRef<[u8]> + ?Sized,
1071    U: AsRef<[u8]> + ?Sized,
1072{
1073    fn partial_cmp(&self, other: &U) -> Option<Ordering> {
1074        self.0.as_ref().partial_cmp(other.as_ref())
1075    }
1076}
1077
1078impl<T: AsRef<[u8]> + ?Sized> Ord for Nsec3Salt<T> {
1079    fn cmp(&self, other: &Self) -> Ordering {
1080        self.0.as_ref().cmp(other.as_ref())
1081    }
1082}
1083
1084impl<T, U> CanonicalOrd<Nsec3Salt<U>> for Nsec3Salt<T>
1085where
1086    T: AsRef<[u8]> + ?Sized,
1087    U: AsRef<[u8]> + ?Sized,
1088{
1089    fn canonical_cmp(&self, other: &Nsec3Salt<U>) -> Ordering {
1090        match self.0.as_ref().len().cmp(&other.0.as_ref().len()) {
1091            Ordering::Equal => {}
1092            other => return other,
1093        }
1094        self.as_slice().cmp(other.as_slice())
1095    }
1096}
1097
1098//--- Hash
1099
1100impl<T: AsRef<[u8]> + ?Sized> hash::Hash for Nsec3Salt<T> {
1101    fn hash<H: hash::Hasher>(&self, state: &mut H) {
1102        self.0.as_ref().hash(state)
1103    }
1104}
1105
1106//--- Display and Debug
1107
1108impl<Octs: AsRef<[u8]> + ?Sized> fmt::Display for Nsec3Salt<Octs> {
1109    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1110        let s = self.as_slice();
1111        if s.is_empty() {
1112            // https://www.rfc-editor.org/rfc/rfc5155.html#section-3.3
1113            //   "The Salt field is represented as "-" (without the quotes)
1114            //    when the Salt Length field has a value of 0."
1115            f.write_char('-')
1116        } else {
1117            base16::display(s, f)
1118        }
1119    }
1120}
1121
1122impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for Nsec3Salt<Octs> {
1123    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1124        f.debug_tuple("Nsec3Salt")
1125            .field(&format_args!("{}", self))
1126            .finish()
1127    }
1128}
1129
1130//--- ZonefileFmt
1131
1132impl<Octs: AsRef<[u8]> + ?Sized> ZonefileFmt for Nsec3Salt<Octs> {
1133    fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
1134        p.block(|p| {
1135            if self.as_slice().is_empty() {
1136                p.write_token("-")?;
1137            } else {
1138                p.write_token(base16::encode_display(self))?;
1139            }
1140            p.write_comment(format_args!(
1141                "salt (length: {})",
1142                self.salt_len()
1143            ))
1144        })
1145    }
1146}
1147
1148//--- Serialize and Deserialize
1149
1150#[cfg(feature = "serde")]
1151impl<T: AsRef<[u8]> + SerializeOctets> serde::Serialize for Nsec3Salt<T> {
1152    fn serialize<S: serde::Serializer>(
1153        &self,
1154        serializer: S,
1155    ) -> Result<S::Ok, S::Error> {
1156        if serializer.is_human_readable() {
1157            serializer.serialize_newtype_struct(
1158                "Nsec3Salt",
1159                &format_args!("{}", self),
1160            )
1161        } else {
1162            serializer.serialize_newtype_struct(
1163                "Nsec3Salt",
1164                &self.0.as_serialized_octets(),
1165            )
1166        }
1167    }
1168}
1169
1170#[cfg(feature = "serde")]
1171impl<'de, Octs> serde::Deserialize<'de> for Nsec3Salt<Octs>
1172where
1173    Octs: FromBuilder + DeserializeOctets<'de>,
1174    <Octs as FromBuilder>::Builder: OctetsBuilder + EmptyBuilder,
1175{
1176    fn deserialize<D: serde::Deserializer<'de>>(
1177        deserializer: D,
1178    ) -> Result<Self, D::Error> {
1179        use core::marker::PhantomData;
1180        use core::str::FromStr;
1181
1182        struct InnerVisitor<'de, T: DeserializeOctets<'de>>(T::Visitor);
1183
1184        impl<'de, Octs> serde::de::Visitor<'de> for InnerVisitor<'de, Octs>
1185        where
1186            Octs: FromBuilder + DeserializeOctets<'de>,
1187            <Octs as FromBuilder>::Builder: OctetsBuilder + EmptyBuilder,
1188        {
1189            type Value = Nsec3Salt<Octs>;
1190
1191            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
1192                f.write_str("an NSEC3 salt value")
1193            }
1194
1195            fn visit_str<E: serde::de::Error>(
1196                self,
1197                v: &str,
1198            ) -> Result<Self::Value, E> {
1199                Nsec3Salt::from_str(v).map_err(E::custom)
1200            }
1201
1202            fn visit_borrowed_bytes<E: serde::de::Error>(
1203                self,
1204                value: &'de [u8],
1205            ) -> Result<Self::Value, E> {
1206                self.0.visit_borrowed_bytes(value).and_then(|octets| {
1207                    Nsec3Salt::from_octets(octets).map_err(E::custom)
1208                })
1209            }
1210
1211            #[cfg(feature = "std")]
1212            fn visit_byte_buf<E: serde::de::Error>(
1213                self,
1214                value: std::vec::Vec<u8>,
1215            ) -> Result<Self::Value, E> {
1216                self.0.visit_byte_buf(value).and_then(|octets| {
1217                    Nsec3Salt::from_octets(octets).map_err(E::custom)
1218                })
1219            }
1220        }
1221
1222        struct NewtypeVisitor<T>(PhantomData<T>);
1223
1224        impl<'de, Octs> serde::de::Visitor<'de> for NewtypeVisitor<Octs>
1225        where
1226            Octs: FromBuilder + DeserializeOctets<'de>,
1227            <Octs as FromBuilder>::Builder: OctetsBuilder + EmptyBuilder,
1228        {
1229            type Value = Nsec3Salt<Octs>;
1230
1231            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
1232                f.write_str("an NSEC3 salt value")
1233            }
1234
1235            fn visit_newtype_struct<D: serde::Deserializer<'de>>(
1236                self,
1237                deserializer: D,
1238            ) -> Result<Self::Value, D::Error> {
1239                if deserializer.is_human_readable() {
1240                    deserializer
1241                        .deserialize_str(InnerVisitor(Octs::visitor()))
1242                } else {
1243                    Octs::deserialize_with_visitor(
1244                        deserializer,
1245                        InnerVisitor(Octs::visitor()),
1246                    )
1247                }
1248            }
1249        }
1250
1251        deserializer.deserialize_newtype_struct(
1252            "Nsec3Salt",
1253            NewtypeVisitor(PhantomData),
1254        )
1255    }
1256}
1257
1258//------------ Nsec3SaltFromStrError -----------------------------------------
1259
1260/// An error happened while parsing an NSEC3 salt from a string.
1261#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1262pub enum Nsec3SaltFromStrError {
1263    DecodeError(base16::DecodeError),
1264    Nsec3SaltError(Nsec3SaltError),
1265}
1266
1267impl fmt::Display for Nsec3SaltFromStrError {
1268    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1269        match self {
1270            Nsec3SaltFromStrError::DecodeError(err) => err.fmt(f),
1271            Nsec3SaltFromStrError::Nsec3SaltError(err) => err.fmt(f),
1272        }
1273    }
1274}
1275
1276#[cfg(feature = "std")]
1277impl std::error::Error for Nsec3SaltFromStrError {}
1278
1279//------------ OwnerHash -----------------------------------------------------
1280
1281/// The hash over the next owner name.
1282///
1283/// This hash is used instead of the actual owner name in an NSEC3 record.
1284///
1285/// The hash can never be longer than 255 octets since its length is encoded
1286/// as a single octet.
1287///
1288/// For its presentation format, the hash uses an unpadded Base 32 encoding
1289/// with no whitespace allowed.
1290#[derive(Clone)]
1291#[repr(transparent)]
1292pub struct OwnerHash<Octs: ?Sized>(Octs);
1293
1294impl OwnerHash<()> {
1295    /// The hash has a maximum length 255 octets since its length is encoded
1296    /// as a single octet.
1297    pub const MAX_LEN: usize = 255;
1298}
1299
1300impl<Octs> OwnerHash<Octs> {
1301    /// Creates a new owner hash from the given octets.
1302    ///
1303    /// Returns succesfully if `octets` can indeed be used as a
1304    /// character string, i.e., it is not longer than 255 bytes.
1305    pub fn from_octets(octets: Octs) -> Result<Self, OwnerHashError>
1306    where
1307        Octs: AsRef<[u8]>,
1308    {
1309        if octets.as_ref().len() > OwnerHash::MAX_LEN {
1310            Err(OwnerHashError(()))
1311        } else {
1312            Ok(unsafe { Self::from_octets_unchecked(octets) })
1313        }
1314    }
1315
1316    /// Creates an owner hash from octets without length check.
1317    ///
1318    /// As this can break the guarantees made by the type, it is unsafe.
1319    unsafe fn from_octets_unchecked(octets: Octs) -> Self {
1320        Self(octets)
1321    }
1322
1323    pub fn scan<S: Scanner<Octets = Octs>>(
1324        scanner: &mut S,
1325    ) -> Result<Self, S::Error> {
1326        scanner
1327            .convert_token(base32::SymbolConverter::new())
1328            .map(|octets| unsafe { Self::from_octets_unchecked(octets) })
1329    }
1330
1331    /// Converts the hash into the underlying octets.
1332    pub fn into_octets(self) -> Octs
1333    where
1334        Octs: Sized,
1335    {
1336        self.0
1337    }
1338}
1339
1340impl<Octs: ?Sized> OwnerHash<Octs> {
1341    /// Returns a reference to a slice of the hash.
1342    pub fn as_slice(&self) -> &[u8]
1343    where
1344        Octs: AsRef<[u8]>,
1345    {
1346        self.0.as_ref()
1347    }
1348
1349    fn hash_len(&self) -> u8
1350    where
1351        Octs: AsRef<[u8]>,
1352    {
1353        self.0.as_ref().len().try_into().expect("long hash")
1354    }
1355
1356    fn compose_len(&self) -> u16
1357    where
1358        Octs: AsRef<[u8]>,
1359    {
1360        u16::from(self.hash_len()) + 1
1361    }
1362
1363    fn compose<Target: Composer /*OctetsBuilder*/ + ?Sized>(
1364        &self,
1365        target: &mut Target,
1366    ) -> Result<(), Target::AppendError>
1367    where
1368        Octs: AsRef<[u8]>,
1369    {
1370        self.hash_len().compose(target)?;
1371        target.append_slice(self.0.as_ref())
1372    }
1373}
1374
1375#[cfg(feature = "bytes")]
1376#[cfg_attr(docsrs, doc(cfg(feature = "bytes")))]
1377impl OwnerHash<Bytes> {
1378    /// Creates a new owner hash from a bytes value.
1379    pub fn from_bytes(bytes: Bytes) -> Result<Self, OwnerHashError> {
1380        Self::from_octets(bytes)
1381    }
1382}
1383
1384impl OwnerHash<[u8]> {
1385    /// Creates a new owner hash from an octet slice.
1386    pub fn from_slice(slice: &[u8]) -> Result<&Self, OwnerHashError> {
1387        if slice.len() > OwnerHash::MAX_LEN {
1388            Err(OwnerHashError(()))
1389        } else {
1390            Ok(unsafe { Self::from_slice_unchecked(slice) })
1391        }
1392    }
1393
1394    /// Creates a new owner hash from an octet slice without checking.
1395    ///
1396    /// # Safety
1397    ///
1398    /// The passed slice must be no longer than [`OwnerHash::MAX_LEN`].
1399    unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
1400        // SAFETY: OwnerHash has repr(transparent)
1401        mem::transmute(slice)
1402    }
1403}
1404
1405impl<Octs> OwnerHash<Octs> {
1406    fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
1407        parser: &mut Parser<'a, Src>,
1408    ) -> Result<Self, ParseError> {
1409        let len = parser.parse_u8()? as usize;
1410        parser
1411            .parse_octets(len)
1412            .map(|octets| unsafe { Self::from_octets_unchecked(octets) })
1413            .map_err(Into::into)
1414    }
1415}
1416
1417//--- OctetsFrom and FromStr
1418
1419impl<Octs, SrcOcts> OctetsFrom<OwnerHash<SrcOcts>> for OwnerHash<Octs>
1420where
1421    Octs: OctetsFrom<SrcOcts>,
1422{
1423    type Error = Octs::Error;
1424
1425    fn try_octets_from(
1426        source: OwnerHash<SrcOcts>,
1427    ) -> Result<Self, Self::Error> {
1428        Octs::try_octets_from(source.0)
1429            .map(|octets| unsafe { Self::from_octets_unchecked(octets) })
1430    }
1431}
1432
1433impl<Octs> str::FromStr for OwnerHash<Octs>
1434where
1435    Octs: FromBuilder,
1436    <Octs as FromBuilder>::Builder: OctetsBuilder + EmptyBuilder,
1437{
1438    type Err = base32::DecodeError;
1439
1440    fn from_str(s: &str) -> Result<Self, Self::Err> {
1441        base32::decode_hex(s)
1442            .map(|octets| unsafe { Self::from_octets_unchecked(octets) })
1443    }
1444}
1445
1446//--- AsRef
1447
1448impl<Octs: AsRef<U> + ?Sized, U: ?Sized> AsRef<U> for OwnerHash<Octs> {
1449    fn as_ref(&self) -> &U {
1450        self.0.as_ref()
1451    }
1452}
1453
1454//--- PartialEq and Eq
1455
1456impl<T, U> PartialEq<U> for OwnerHash<T>
1457where
1458    T: AsRef<[u8]> + ?Sized,
1459    U: AsRef<[u8]> + ?Sized,
1460{
1461    fn eq(&self, other: &U) -> bool {
1462        self.as_slice().eq(other.as_ref())
1463    }
1464}
1465
1466impl<T: AsRef<[u8]> + ?Sized> Eq for OwnerHash<T> {}
1467
1468//--- PartialOrd, Ord, and CanonicalOrd
1469
1470impl<T, U> PartialOrd<U> for OwnerHash<T>
1471where
1472    T: AsRef<[u8]> + ?Sized,
1473    U: AsRef<[u8]> + ?Sized,
1474{
1475    fn partial_cmp(&self, other: &U) -> Option<Ordering> {
1476        self.0.as_ref().partial_cmp(other.as_ref())
1477    }
1478}
1479
1480impl<T: AsRef<[u8]> + ?Sized> Ord for OwnerHash<T> {
1481    fn cmp(&self, other: &Self) -> Ordering {
1482        self.0.as_ref().cmp(other.as_ref())
1483    }
1484}
1485
1486impl<T, U> CanonicalOrd<OwnerHash<U>> for OwnerHash<T>
1487where
1488    T: AsRef<[u8]> + ?Sized,
1489    U: AsRef<[u8]> + ?Sized,
1490{
1491    fn canonical_cmp(&self, other: &OwnerHash<U>) -> Ordering {
1492        match self.0.as_ref().len().cmp(&other.0.as_ref().len()) {
1493            Ordering::Equal => {}
1494            other => return other,
1495        }
1496        self.as_slice().cmp(other.as_slice())
1497    }
1498}
1499
1500//--- Hash
1501
1502impl<T: AsRef<[u8]> + ?Sized> hash::Hash for OwnerHash<T> {
1503    fn hash<H: hash::Hasher>(&self, state: &mut H) {
1504        self.0.as_ref().hash(state)
1505    }
1506}
1507
1508//--- Display
1509
1510impl<Octs: AsRef<[u8]> + ?Sized> fmt::Display for OwnerHash<Octs> {
1511    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1512        base32::display_hex(self.as_slice(), f)
1513    }
1514}
1515
1516impl<Octs: AsRef<[u8]> + ?Sized> fmt::Debug for OwnerHash<Octs> {
1517    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1518        f.debug_tuple("OwnerHash")
1519            .field(&format_args!("{}", self))
1520            .finish()
1521    }
1522}
1523
1524//--- Serialize and Deserialize
1525
1526#[cfg(feature = "serde")]
1527impl<T: AsRef<[u8]> + SerializeOctets> serde::Serialize for OwnerHash<T> {
1528    fn serialize<S: serde::Serializer>(
1529        &self,
1530        serializer: S,
1531    ) -> Result<S::Ok, S::Error> {
1532        if serializer.is_human_readable() {
1533            serializer.serialize_newtype_struct(
1534                "OwnerHash",
1535                &format_args!("{}", self),
1536            )
1537        } else {
1538            serializer.serialize_newtype_struct(
1539                "OwnerHash",
1540                &self.0.as_serialized_octets(),
1541            )
1542        }
1543    }
1544}
1545
1546#[cfg(feature = "serde")]
1547impl<'de, Octs> serde::Deserialize<'de> for OwnerHash<Octs>
1548where
1549    Octs: FromBuilder + DeserializeOctets<'de>,
1550    <Octs as FromBuilder>::Builder: OctetsBuilder + EmptyBuilder,
1551{
1552    fn deserialize<D: serde::Deserializer<'de>>(
1553        deserializer: D,
1554    ) -> Result<Self, D::Error> {
1555        use core::marker::PhantomData;
1556        use core::str::FromStr;
1557
1558        struct InnerVisitor<'de, T: DeserializeOctets<'de>>(T::Visitor);
1559
1560        impl<'de, Octs> serde::de::Visitor<'de> for InnerVisitor<'de, Octs>
1561        where
1562            Octs: FromBuilder + DeserializeOctets<'de>,
1563            <Octs as FromBuilder>::Builder: OctetsBuilder + EmptyBuilder,
1564        {
1565            type Value = OwnerHash<Octs>;
1566
1567            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
1568                f.write_str("an owner name hash value")
1569            }
1570
1571            fn visit_str<E: serde::de::Error>(
1572                self,
1573                v: &str,
1574            ) -> Result<Self::Value, E> {
1575                OwnerHash::from_str(v).map_err(E::custom)
1576            }
1577
1578            fn visit_borrowed_bytes<E: serde::de::Error>(
1579                self,
1580                value: &'de [u8],
1581            ) -> Result<Self::Value, E> {
1582                self.0.visit_borrowed_bytes(value).and_then(|octets| {
1583                    OwnerHash::from_octets(octets).map_err(E::custom)
1584                })
1585            }
1586
1587            #[cfg(feature = "std")]
1588            fn visit_byte_buf<E: serde::de::Error>(
1589                self,
1590                value: std::vec::Vec<u8>,
1591            ) -> Result<Self::Value, E> {
1592                self.0.visit_byte_buf(value).and_then(|octets| {
1593                    OwnerHash::from_octets(octets).map_err(E::custom)
1594                })
1595            }
1596        }
1597
1598        struct NewtypeVisitor<T>(PhantomData<T>);
1599
1600        impl<'de, Octs> serde::de::Visitor<'de> for NewtypeVisitor<Octs>
1601        where
1602            Octs: FromBuilder + DeserializeOctets<'de>,
1603            <Octs as FromBuilder>::Builder: OctetsBuilder + EmptyBuilder,
1604        {
1605            type Value = OwnerHash<Octs>;
1606
1607            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
1608                f.write_str("an owner name hash value")
1609            }
1610
1611            fn visit_newtype_struct<D: serde::Deserializer<'de>>(
1612                self,
1613                deserializer: D,
1614            ) -> Result<Self::Value, D::Error> {
1615                if deserializer.is_human_readable() {
1616                    deserializer
1617                        .deserialize_str(InnerVisitor(Octs::visitor()))
1618                } else {
1619                    Octs::deserialize_with_visitor(
1620                        deserializer,
1621                        InnerVisitor(Octs::visitor()),
1622                    )
1623                }
1624            }
1625        }
1626
1627        deserializer.deserialize_newtype_struct(
1628            "OwnerHash",
1629            NewtypeVisitor(PhantomData),
1630        )
1631    }
1632}
1633
1634//============ Error Types ===================================================
1635
1636//------------ Nsec3SaltError ------------------------------------------------
1637
1638/// A byte sequence does not represent a valid NSEC3 salt.
1639///
1640/// This can only mean that the sequence is longer than 255 bytes.
1641#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1642pub struct Nsec3SaltError(());
1643
1644impl fmt::Display for Nsec3SaltError {
1645    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1646        f.write_str("illegal NSEC3 salt")
1647    }
1648}
1649
1650#[cfg(feature = "std")]
1651impl std::error::Error for Nsec3SaltError {}
1652
1653//------------ OwnerHashError ------------------------------------------------
1654
1655/// A byte sequence does not represent a valid owner hash.
1656///
1657/// This can only mean that the sequence is longer than 255 bytes.
1658#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1659pub struct OwnerHashError(());
1660
1661impl fmt::Display for OwnerHashError {
1662    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1663        f.write_str("illegal owner name hash")
1664    }
1665}
1666
1667#[cfg(feature = "std")]
1668impl std::error::Error for OwnerHashError {}
1669
1670//============ Testing ======================================================
1671
1672#[cfg(test)]
1673#[cfg(all(feature = "std", feature = "bytes"))]
1674mod test {
1675    use super::super::dnssec::RtypeBitmapBuilder;
1676    use super::*;
1677    use crate::base::rdata::test::{
1678        test_compose_parse, test_rdlen, test_scan,
1679    };
1680    use crate::base::zonefile_fmt::DisplayKind;
1681    use std::vec::Vec;
1682
1683    #[test]
1684    #[allow(clippy::redundant_closure)] // lifetimes ...
1685    fn nsec3_compose_parse_scan() {
1686        let mut rtype = RtypeBitmapBuilder::new_vec();
1687        rtype.add(Rtype::A).unwrap();
1688        rtype.add(Rtype::SRV).unwrap();
1689        let rdata = Nsec3::new(
1690            Nsec3HashAlgorithm::SHA1,
1691            10,
1692            11,
1693            Nsec3Salt::from_octets(Vec::from("bar")).unwrap(),
1694            OwnerHash::from_octets(Vec::from("foo")).unwrap(),
1695            rtype.finalize(),
1696        );
1697        test_rdlen(&rdata);
1698        test_compose_parse(&rdata, |parser| Nsec3::parse(parser));
1699        test_scan(
1700            &["1", "10", "11", "626172", "CPNMU", "A", "SRV"],
1701            Nsec3::scan,
1702            &rdata,
1703        );
1704        assert_eq!(
1705            &format!("{}", rdata.display_zonefile(DisplayKind::Simple)),
1706            "1 10 11 626172 CPNMU A SRV"
1707        );
1708    }
1709
1710    #[test]
1711    #[allow(clippy::redundant_closure)] // lifetimes ...
1712    fn nsec3_compose_parse_scan_empty_salt() {
1713        let mut rtype = RtypeBitmapBuilder::new_vec();
1714        rtype.add(Rtype::A).unwrap();
1715        rtype.add(Rtype::SRV).unwrap();
1716        let rdata = Nsec3::new(
1717            Nsec3HashAlgorithm::SHA1,
1718            10,
1719            11,
1720            Nsec3Salt::empty(),
1721            OwnerHash::from_octets(Vec::from("foo")).unwrap(),
1722            rtype.finalize(),
1723        );
1724        test_rdlen(&rdata);
1725        test_compose_parse(&rdata, |parser| Nsec3::parse(parser));
1726        test_scan(
1727            &["1", "10", "11", "-", "CPNMU", "A", "SRV"],
1728            Nsec3::scan,
1729            &rdata,
1730        );
1731        assert_eq!(
1732            &format!("{}", rdata.display_zonefile(DisplayKind::Simple)),
1733            "1 10 11 - CPNMU A SRV"
1734        );
1735    }
1736
1737    #[test]
1738    #[allow(clippy::redundant_closure)] // lifetimes ...
1739    fn nsec3param_compose_parse_scan() {
1740        let rdata = Nsec3param::new(
1741            Nsec3HashAlgorithm::SHA1,
1742            10,
1743            11,
1744            Nsec3Salt::from_octets(Vec::from("bar")).unwrap(),
1745        );
1746        test_rdlen(&rdata);
1747        test_compose_parse(&rdata, |parser| Nsec3param::parse(parser));
1748        test_scan(&["1", "10", "11", "626172"], Nsec3param::scan, &rdata);
1749    }
1750}