1use 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#[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 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
174impl<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
194impl<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
213impl<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
281impl<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
294impl<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
355impl<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
385impl<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
408const 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 hash_algorithm: Nsec3HashAlgorithm,
436
437 flags: u8,
454
455 iterations: u16,
469
470 salt: Nsec3Salt<Octs>,
481}
482
483impl Nsec3param<()> {
484 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
574impl<Octs> Default for Nsec3param<Octs>
577where
578 Octs: From<&'static [u8]>,
579{
580 fn default() -> Self {
594 Self {
595 hash_algorithm: Nsec3HashAlgorithm::SHA1,
596 flags: 0,
597 iterations: 0,
598 salt: Nsec3Salt::empty(),
599 }
600 }
601}
602
603impl<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
623impl<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
640impl<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
704impl<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
715impl<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
770impl<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
793impl<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#[derive(Clone)]
818#[repr(transparent)]
819#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
820pub struct Nsec3Salt<Octs: ?Sized>(Octs);
821
822impl Nsec3Salt<()> {
823 pub const MAX_LEN: usize = 255;
826}
827
828impl<Octs: ?Sized> Nsec3Salt<Octs> {
829 #[must_use]
831 pub fn empty() -> Self
832 where
833 Octs: From<&'static [u8]>,
834 {
835 Self(b"".as_ref().into())
836 }
837
838 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 unsafe fn from_octets_unchecked(octets: Octs) -> Self
857 where
858 Octs: Sized,
859 {
860 Self(octets)
861 }
862
863 pub fn into_octets(self) -> Octs
865 where
866 Octs: Sized,
867 {
868 self.0
869 }
870
871 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 + ?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 pub fn from_bytes(bytes: Bytes) -> Result<Self, Nsec3SaltError> {
910 Self::from_octets(bytes)
911 }
912}
913
914impl Nsec3Salt<[u8]> {
915 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 unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
930 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 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
1005impl<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
1044impl<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
1052impl<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
1066impl<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
1098impl<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
1106impl<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 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
1130impl<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#[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#[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#[derive(Clone)]
1291#[repr(transparent)]
1292pub struct OwnerHash<Octs: ?Sized>(Octs);
1293
1294impl OwnerHash<()> {
1295 pub const MAX_LEN: usize = 255;
1298}
1299
1300impl<Octs> OwnerHash<Octs> {
1301 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 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 pub fn into_octets(self) -> Octs
1333 where
1334 Octs: Sized,
1335 {
1336 self.0
1337 }
1338}
1339
1340impl<Octs: ?Sized> OwnerHash<Octs> {
1341 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 + ?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 pub fn from_bytes(bytes: Bytes) -> Result<Self, OwnerHashError> {
1380 Self::from_octets(bytes)
1381 }
1382}
1383
1384impl OwnerHash<[u8]> {
1385 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 unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
1400 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
1417impl<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
1446impl<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
1454impl<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
1468impl<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
1500impl<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
1508impl<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#[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#[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#[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#[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)] 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)] 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)] 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}