domain/rdata/
rfc1035.rs

1//! Record data from [RFC 1035]: initial record types.
2//!
3//! This RFC defines the initial set of record types.
4//!
5//! [RFC 1035]: https://tools.ietf.org/html/rfc1035
6
7use crate::base::charstr::CharStr;
8use crate::base::cmp::CanonicalOrd;
9use crate::base::iana::Rtype;
10use crate::base::name::{FlattenInto, ParsedDname, ToDname};
11use crate::base::net::Ipv4Addr;
12use crate::base::rdata::{
13    ComposeRecordData, LongRecordData, ParseRecordData, RecordData,
14};
15use crate::base::scan::{Scan, Scanner, ScannerError, Symbol};
16use crate::base::serial::Serial;
17use crate::base::wire::{Compose, Composer, FormError, Parse, ParseError};
18use crate::base::Ttl;
19#[cfg(feature = "bytes")]
20use bytes::BytesMut;
21use core::cmp::Ordering;
22use core::convert::{Infallible, TryFrom};
23use core::str::FromStr;
24use core::{fmt, hash, str};
25use octseq::builder::{
26    infallible, EmptyBuilder, FreezeBuilder, FromBuilder, OctetsBuilder,
27    ShortBuf,
28};
29use octseq::octets::{Octets, OctetsFrom, OctetsInto};
30use octseq::parse::Parser;
31#[cfg(feature = "serde")]
32use octseq::serde::{DeserializeOctets, SerializeOctets};
33
34//------------ A ------------------------------------------------------------
35
36/// A record data.
37///
38/// A records convey the IPv4 address of a host. The wire format is the 32
39/// bit IPv4 address in network byte order. The representation file format
40/// is the usual dotted notation.
41///
42/// The A record type is defined in RFC 1035, section 3.4.1.
43#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
45pub struct A {
46    addr: Ipv4Addr,
47}
48
49impl A {
50    /// Creates a new A record data from an IPv4 address.
51    #[must_use]
52    pub fn new(addr: Ipv4Addr) -> A {
53        A { addr }
54    }
55
56    /// Creates a new A record from the IPv4 address components.
57    #[must_use]
58    pub fn from_octets(a: u8, b: u8, c: u8, d: u8) -> A {
59        A::new(Ipv4Addr::new(a, b, c, d))
60    }
61
62    #[must_use]
63    pub fn addr(&self) -> Ipv4Addr {
64        self.addr
65    }
66    pub fn set_addr(&mut self, addr: Ipv4Addr) {
67        self.addr = addr
68    }
69
70    pub(super) fn convert_octets<E>(self) -> Result<Self, E> {
71        Ok(self)
72    }
73
74    pub(super) fn flatten<E>(self) -> Result<Self, E> {
75        Ok(self)
76    }
77
78    pub fn parse<Octs: AsRef<[u8]> + ?Sized>(
79        parser: &mut Parser<Octs>,
80    ) -> Result<Self, ParseError> {
81        Ipv4Addr::parse(parser).map(Self::new)
82    }
83
84    pub fn scan<S: Scanner>(scanner: &mut S) -> Result<Self, S::Error> {
85        let token = scanner.scan_octets()?;
86        let token = str::from_utf8(token.as_ref())
87            .map_err(|_| S::Error::custom("expected IPv4 address"))?;
88        A::from_str(token)
89            .map_err(|_| S::Error::custom("expected IPv4 address"))
90    }
91}
92
93//--- OctetsFrom
94
95impl OctetsFrom<A> for A {
96    type Error = Infallible;
97
98    fn try_octets_from(source: A) -> Result<Self, Self::Error> {
99        Ok(source)
100    }
101}
102
103//--- From and FromStr
104
105impl From<Ipv4Addr> for A {
106    fn from(addr: Ipv4Addr) -> Self {
107        Self::new(addr)
108    }
109}
110
111impl From<A> for Ipv4Addr {
112    fn from(a: A) -> Self {
113        a.addr
114    }
115}
116
117impl FromStr for A {
118    type Err = <Ipv4Addr as FromStr>::Err;
119
120    fn from_str(s: &str) -> Result<Self, Self::Err> {
121        Ipv4Addr::from_str(s).map(A::new)
122    }
123}
124
125//--- CanonicalOrd
126
127impl CanonicalOrd for A {
128    fn canonical_cmp(&self, other: &Self) -> Ordering {
129        self.cmp(other)
130    }
131}
132
133//--- RecordData, ParseRecordData, ComposeRecordData
134
135impl RecordData for A {
136    fn rtype(&self) -> Rtype {
137        Rtype::A
138    }
139}
140
141impl<'a, Octs: AsRef<[u8]> + ?Sized> ParseRecordData<'a, Octs> for A {
142    fn parse_rdata(
143        rtype: Rtype,
144        parser: &mut Parser<'a, Octs>,
145    ) -> Result<Option<Self>, ParseError> {
146        if rtype == Rtype::A {
147            Self::parse(parser).map(Some)
148        } else {
149            Ok(None)
150        }
151    }
152}
153
154impl ComposeRecordData for A {
155    fn rdlen(&self, _compress: bool) -> Option<u16> {
156        Some(4)
157    }
158
159    fn compose_rdata<Target: Composer + ?Sized>(
160        &self,
161        target: &mut Target,
162    ) -> Result<(), Target::AppendError> {
163        target.append_slice(&self.addr.octets())
164    }
165
166    fn compose_canonical_rdata<Target: Composer + ?Sized>(
167        &self,
168        target: &mut Target,
169    ) -> Result<(), Target::AppendError> {
170        self.compose_rdata(target)
171    }
172}
173
174//--- Display
175
176impl fmt::Display for A {
177    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
178        self.addr.fmt(f)
179    }
180}
181
182//--- AsRef and AsMut
183
184impl AsRef<Ipv4Addr> for A {
185    fn as_ref(&self) -> &Ipv4Addr {
186        &self.addr
187    }
188}
189
190impl AsMut<Ipv4Addr> for A {
191    fn as_mut(&mut self) -> &mut Ipv4Addr {
192        &mut self.addr
193    }
194}
195
196//------------ Cname --------------------------------------------------------
197
198dname_type_well_known! {
199    /// CNAME record data.
200    ///
201    /// The CNAME record specifies the canonical or primary name for domain
202    /// name alias.
203    ///
204    /// The CNAME type is defined in RFC 1035, section 3.3.1.
205    (Cname, Cname, cname, into_cname)
206}
207
208//------------ Hinfo --------------------------------------------------------
209
210/// Hinfo record data.
211///
212/// Hinfo records are used to acquire general information about a host,
213/// specifically the CPU type and operating system type.
214///
215/// The Hinfo type is defined in RFC 1035, section 3.3.2.
216#[derive(Clone)]
217#[cfg_attr(
218    feature = "serde",
219    derive(serde::Serialize, serde::Deserialize),
220    serde(bound(
221        serialize = "Octs: AsRef<[u8]> + octseq::serde::SerializeOctets",
222        deserialize = "Octs: \
223                FromBuilder \
224                + octseq::serde::DeserializeOctets<'de>, \
225            <Octs as FromBuilder>::Builder: AsRef<[u8]> + EmptyBuilder ",
226    ))
227)]
228pub struct Hinfo<Octs> {
229    cpu: CharStr<Octs>,
230    os: CharStr<Octs>,
231}
232
233impl<Octs> Hinfo<Octs> {
234    /// Creates a new Hinfo record data from the components.
235    pub fn new(cpu: CharStr<Octs>, os: CharStr<Octs>) -> Self {
236        Hinfo { cpu, os }
237    }
238
239    /// The CPU type of the host.
240    pub fn cpu(&self) -> &CharStr<Octs> {
241        &self.cpu
242    }
243
244    /// The operating system type of the host.
245    pub fn os(&self) -> &CharStr<Octs> {
246        &self.os
247    }
248
249    pub(super) fn convert_octets<Target: OctetsFrom<Octs>>(
250        self,
251    ) -> Result<Hinfo<Target>, Target::Error> {
252        Ok(Hinfo::new(
253            self.cpu.try_octets_into()?,
254            self.os.try_octets_into()?,
255        ))
256    }
257
258    pub(super) fn flatten<Target: OctetsFrom<Octs>>(
259        self,
260    ) -> Result<Hinfo<Target>, Target::Error> {
261        self.convert_octets()
262    }
263
264    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
265        parser: &mut Parser<'a, Src>,
266    ) -> Result<Self, ParseError> {
267        Ok(Self::new(CharStr::parse(parser)?, CharStr::parse(parser)?))
268    }
269
270    pub fn scan<S: Scanner<Octets = Octs>>(
271        scanner: &mut S,
272    ) -> Result<Self, S::Error> {
273        Ok(Self::new(scanner.scan_charstr()?, scanner.scan_charstr()?))
274    }
275}
276
277//--- OctetsFrom
278
279impl<Octs, SrcOcts> OctetsFrom<Hinfo<SrcOcts>> for Hinfo<Octs>
280where
281    Octs: OctetsFrom<SrcOcts>,
282{
283    type Error = Octs::Error;
284
285    fn try_octets_from(source: Hinfo<SrcOcts>) -> Result<Self, Self::Error> {
286        Ok(Hinfo::new(
287            CharStr::try_octets_from(source.cpu)?,
288            CharStr::try_octets_from(source.os)?,
289        ))
290    }
291}
292
293//--- PartialEq and Eq
294
295impl<Octs, Other> PartialEq<Hinfo<Other>> for Hinfo<Octs>
296where
297    Octs: AsRef<[u8]>,
298    Other: AsRef<[u8]>,
299{
300    fn eq(&self, other: &Hinfo<Other>) -> bool {
301        self.cpu.eq(&other.cpu) && self.os.eq(&other.os)
302    }
303}
304
305impl<Octs: AsRef<[u8]>> Eq for Hinfo<Octs> {}
306
307//--- PartialOrd, CanonicalOrd, and Ord
308
309impl<Octs, Other> PartialOrd<Hinfo<Other>> for Hinfo<Octs>
310where
311    Octs: AsRef<[u8]>,
312    Other: AsRef<[u8]>,
313{
314    fn partial_cmp(&self, other: &Hinfo<Other>) -> Option<Ordering> {
315        match self.cpu.partial_cmp(&other.cpu) {
316            Some(Ordering::Equal) => {}
317            other => return other,
318        }
319        self.os.partial_cmp(&other.os)
320    }
321}
322
323impl<Octs, Other> CanonicalOrd<Hinfo<Other>> for Hinfo<Octs>
324where
325    Octs: AsRef<[u8]>,
326    Other: AsRef<[u8]>,
327{
328    fn canonical_cmp(&self, other: &Hinfo<Other>) -> Ordering {
329        match self.cpu.canonical_cmp(&other.cpu) {
330            Ordering::Equal => {}
331            other => return other,
332        }
333        self.os.canonical_cmp(&other.os)
334    }
335}
336
337impl<Octs: AsRef<[u8]>> Ord for Hinfo<Octs> {
338    fn cmp(&self, other: &Self) -> Ordering {
339        match self.cpu.cmp(&other.cpu) {
340            Ordering::Equal => {}
341            other => return other,
342        }
343        self.os.cmp(&other.os)
344    }
345}
346
347//--- Hash
348
349impl<Octs: AsRef<[u8]>> hash::Hash for Hinfo<Octs> {
350    fn hash<H: hash::Hasher>(&self, state: &mut H) {
351        self.cpu.hash(state);
352        self.os.hash(state);
353    }
354}
355
356//--- RecordData, ParseRecordData, ComposeRecordData
357
358impl<Octs> RecordData for Hinfo<Octs> {
359    fn rtype(&self) -> Rtype {
360        Rtype::Hinfo
361    }
362}
363
364impl<'a, Octs> ParseRecordData<'a, Octs> for Hinfo<Octs::Range<'a>>
365where
366    Octs: Octets + ?Sized,
367{
368    fn parse_rdata(
369        rtype: Rtype,
370        parser: &mut Parser<'a, Octs>,
371    ) -> Result<Option<Self>, ParseError> {
372        if rtype == Rtype::Hinfo {
373            Self::parse(parser).map(Some)
374        } else {
375            Ok(None)
376        }
377    }
378}
379
380impl<Octs: AsRef<[u8]>> ComposeRecordData for Hinfo<Octs> {
381    fn rdlen(&self, _compress: bool) -> Option<u16> {
382        Some(self.cpu.compose_len() + self.os.compose_len())
383    }
384
385    fn compose_rdata<Target: Composer + ?Sized>(
386        &self,
387        target: &mut Target,
388    ) -> Result<(), Target::AppendError> {
389        self.cpu.compose(target)?;
390        self.os.compose(target)
391    }
392
393    fn compose_canonical_rdata<Target: Composer + ?Sized>(
394        &self,
395        target: &mut Target,
396    ) -> Result<(), Target::AppendError> {
397        self.compose_rdata(target)
398    }
399}
400
401//--- Display
402
403impl<Octs: AsRef<[u8]>> fmt::Display for Hinfo<Octs> {
404    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
405        write!(f, "{} {}", self.cpu, self.os)
406    }
407}
408
409//--- Debug
410
411impl<Octs: AsRef<[u8]>> fmt::Debug for Hinfo<Octs> {
412    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
413        f.debug_struct("Hinfo")
414            .field("cpu", &self.cpu)
415            .field("os", &self.os)
416            .finish()
417    }
418}
419
420//------------ Mb -----------------------------------------------------------
421
422dname_type_well_known! {
423    /// MB record data.
424    ///
425    /// The experimental MB record specifies a host that serves a mailbox.
426    ///
427    /// The MB record type is defined in RFC 1035, section 3.3.3.
428    (Mb, Mb, madname, into_madname)
429}
430
431//------------ Md -----------------------------------------------------------
432
433dname_type_well_known! {
434    /// MD record data.
435    ///
436    /// The MD record specifices a host which has a mail agent for
437    /// the domain which should be able to deliver mail for the domain.
438    ///
439    /// The MD record is obsolete. It is recommended to either reject the record
440    /// or convert them into an Mx record at preference 0.
441    ///
442    /// The MD record type is defined in RFC 1035, section 3.3.4.
443    (Md, Md, madname, into_madname)
444}
445
446//------------ Mf -----------------------------------------------------------
447
448dname_type_well_known! {
449    /// MF record data.
450    ///
451    /// The MF record specifices a host which has a mail agent for
452    /// the domain which will be accept mail for forwarding to the domain.
453    ///
454    /// The MF record is obsolete. It is recommended to either reject the record
455    /// or convert them into an Mx record at preference 10.
456    ///
457    /// The MF record type is defined in RFC 1035, section 3.3.5.
458    (Mf, Mf, madname, into_madname)
459}
460
461//------------ Mg -----------------------------------------------------------
462
463dname_type_well_known! {
464    /// MG record data.
465    ///
466    /// The MG record specifices a mailbox which is a member of the mail group
467    /// specified by the domain name.
468    ///
469    /// The MG record is experimental.
470    ///
471    /// The MG record type is defined in RFC 1035, section 3.3.6.
472    (Mg, Mg, madname, into_madname)
473}
474
475//------------ Minfo --------------------------------------------------------
476
477/// Minfo record data.
478///
479/// The Minfo record specifies a mailbox which is responsible for the mailing
480/// list or mailbox and a mailbox that receives error messages related to the
481/// list or box.
482///
483/// The Minfo record is experimental.
484///
485/// The Minfo record type is defined in RFC 1035, section 3.3.7.
486#[derive(Clone, Debug, Hash)]
487#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
488pub struct Minfo<N> {
489    rmailbx: N,
490    emailbx: N,
491}
492
493impl<N> Minfo<N> {
494    /// Creates a new Minfo record data from the components.
495    pub fn new(rmailbx: N, emailbx: N) -> Self {
496        Minfo { rmailbx, emailbx }
497    }
498
499    /// The responsible mail box.
500    ///
501    /// The domain name specifies the mailbox which is responsible for the
502    /// mailing list or mailbox. If this domain name is the root, the owner
503    /// of the Minfo record is responsible for itself.
504    pub fn rmailbx(&self) -> &N {
505        &self.rmailbx
506    }
507
508    /// The error mail box.
509    ///
510    /// The domain name specifies a mailbox which is to receive error
511    /// messages related to the mailing list or mailbox specified by the
512    /// owner of the record. If this is the root domain name, errors should
513    /// be returned to the sender of the message.
514    pub fn emailbx(&self) -> &N {
515        &self.emailbx
516    }
517
518    pub(super) fn convert_octets<Target: OctetsFrom<N>>(
519        self,
520    ) -> Result<Minfo<Target>, Target::Error> {
521        Ok(Minfo::new(
522            self.rmailbx.try_octets_into()?,
523            self.emailbx.try_octets_into()?,
524        ))
525    }
526
527    pub(super) fn flatten<TargetName>(
528        self,
529    ) -> Result<Minfo<TargetName>, N::AppendError>
530    where N: FlattenInto<TargetName> {
531        Ok(Minfo::new(
532            self.rmailbx.try_flatten_into()?,
533            self.emailbx.try_flatten_into()?,
534        ))
535    }
536
537    pub fn scan<S: Scanner<Dname = N>>(
538        scanner: &mut S,
539    ) -> Result<Self, S::Error> {
540        Ok(Self::new(scanner.scan_dname()?, scanner.scan_dname()?))
541    }
542}
543
544impl<Octs> Minfo<ParsedDname<Octs>> {
545    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
546        parser: &mut Parser<'a, Src>,
547    ) -> Result<Self, ParseError> {
548        Ok(Self::new(
549            ParsedDname::parse(parser)?,
550            ParsedDname::parse(parser)?,
551        ))
552    }
553}
554
555//--- OctetsFrom and FlattenInto
556
557impl<Name, SrcName> OctetsFrom<Minfo<SrcName>> for Minfo<Name>
558where
559    Name: OctetsFrom<SrcName>,
560{
561    type Error = Name::Error;
562
563    fn try_octets_from(source: Minfo<SrcName>) -> Result<Self, Self::Error> {
564        Ok(Minfo::new(
565            Name::try_octets_from(source.rmailbx)?,
566            Name::try_octets_from(source.emailbx)?,
567        ))
568    }
569}
570
571impl<Name, TName> FlattenInto<Minfo<TName>> for Minfo<Name>
572where
573    Name: FlattenInto<TName>,
574{
575    type AppendError = Name::AppendError;
576
577    fn try_flatten_into(self) -> Result<Minfo<TName>, Name::AppendError> {
578        self.flatten()
579    }
580}
581
582//--- PartialEq and Eq
583
584impl<N, NN> PartialEq<Minfo<NN>> for Minfo<N>
585where
586    N: ToDname,
587    NN: ToDname,
588{
589    fn eq(&self, other: &Minfo<NN>) -> bool {
590        self.rmailbx.name_eq(&other.rmailbx)
591            && self.emailbx.name_eq(&other.emailbx)
592    }
593}
594
595impl<N: ToDname> Eq for Minfo<N> {}
596
597//--- PartialOrd, Ord, and CanonicalOrd
598
599impl<N, NN> PartialOrd<Minfo<NN>> for Minfo<N>
600where
601    N: ToDname,
602    NN: ToDname,
603{
604    fn partial_cmp(&self, other: &Minfo<NN>) -> Option<Ordering> {
605        match self.rmailbx.name_cmp(&other.rmailbx) {
606            Ordering::Equal => {}
607            other => return Some(other),
608        }
609        Some(self.emailbx.name_cmp(&other.emailbx))
610    }
611}
612
613impl<N: ToDname> Ord for Minfo<N> {
614    fn cmp(&self, other: &Self) -> Ordering {
615        match self.rmailbx.name_cmp(&other.rmailbx) {
616            Ordering::Equal => {}
617            other => return other,
618        }
619        self.emailbx.name_cmp(&other.emailbx)
620    }
621}
622
623impl<N: ToDname, NN: ToDname> CanonicalOrd<Minfo<NN>> for Minfo<N> {
624    fn canonical_cmp(&self, other: &Minfo<NN>) -> Ordering {
625        match self.rmailbx.lowercase_composed_cmp(&other.rmailbx) {
626            Ordering::Equal => {}
627            other => return other,
628        }
629        self.emailbx.lowercase_composed_cmp(&other.emailbx)
630    }
631}
632
633//--- RecordData, ParseRecordData, ComposeRecordData
634
635impl<N> RecordData for Minfo<N> {
636    fn rtype(&self) -> Rtype {
637        Rtype::Minfo
638    }
639}
640
641impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs>
642    for Minfo<ParsedDname<Octs::Range<'a>>>
643{
644    fn parse_rdata(
645        rtype: Rtype,
646        parser: &mut Parser<'a, Octs>,
647    ) -> Result<Option<Self>, ParseError> {
648        if rtype == Rtype::Minfo {
649            Self::parse(parser).map(Some)
650        } else {
651            Ok(None)
652        }
653    }
654}
655
656impl<Name: ToDname> ComposeRecordData for Minfo<Name> {
657    fn rdlen(&self, compress: bool) -> Option<u16> {
658        if compress {
659            None
660        } else {
661            Some(self.rmailbx.compose_len() + self.emailbx.compose_len())
662        }
663    }
664
665    fn compose_rdata<Target: Composer + ?Sized>(
666        &self,
667        target: &mut Target,
668    ) -> Result<(), Target::AppendError> {
669        if target.can_compress() {
670            target.append_compressed_dname(&self.rmailbx)?;
671            target.append_compressed_dname(&self.emailbx)
672        } else {
673            self.rmailbx.compose(target)?;
674            self.emailbx.compose(target)
675        }
676    }
677
678    fn compose_canonical_rdata<Target: Composer + ?Sized>(
679        &self,
680        target: &mut Target,
681    ) -> Result<(), Target::AppendError> {
682        self.rmailbx.compose_canonical(target)?;
683        self.emailbx.compose_canonical(target)
684    }
685}
686
687//--- Display
688
689impl<N: fmt::Display> fmt::Display for Minfo<N> {
690    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
691        write!(f, "{}. {}.", self.rmailbx, self.emailbx)
692    }
693}
694
695//------------ Mr -----------------------------------------------------------
696
697dname_type_well_known! {
698    /// MR record data.
699    ///
700    /// The MR record specifices a mailbox which is the proper rename of the
701    /// specified mailbox.
702    ///
703    /// The MR record is experimental.
704    ///
705    /// The MR record type is defined in RFC 1035, section 3.3.8.
706    (Mr, Mr, newname, into_newname)
707}
708
709//------------ Mx -----------------------------------------------------------
710
711/// Mx record data.
712///
713/// The Mx record specifies a host willing to serve as a mail exchange for
714/// the owner name.
715///
716/// The Mx record type is defined in RFC 1035, section 3.3.9.
717#[derive(Clone, Debug, Hash)]
718#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
719pub struct Mx<N> {
720    preference: u16,
721    exchange: N,
722}
723
724impl<N> Mx<N> {
725    /// Creates a new Mx record data from the components.
726    pub fn new(preference: u16, exchange: N) -> Self {
727        Mx {
728            preference,
729            exchange,
730        }
731    }
732
733    /// The preference for this record.
734    ///
735    /// Defines an order if there are several Mx records for the same owner.
736    /// Lower values are preferred.
737    pub fn preference(&self) -> u16 {
738        self.preference
739    }
740
741    /// The name of the host that is the exchange.
742    pub fn exchange(&self) -> &N {
743        &self.exchange
744    }
745
746    pub(super) fn convert_octets<Target: OctetsFrom<N>>(
747        self,
748    ) -> Result<Mx<Target>, Target::Error> {
749        Ok(Mx::new(self.preference, self.exchange.try_octets_into()?))
750    }
751
752    pub(super) fn flatten<TargetName>(
753        self,
754    ) -> Result<Mx<TargetName>, N::AppendError>
755    where N: FlattenInto<TargetName> {
756        Ok(Mx::new(self.preference, self.exchange.try_flatten_into()?))
757    }
758
759    pub fn scan<S: Scanner<Dname = N>>(
760        scanner: &mut S,
761    ) -> Result<Self, S::Error> {
762        Ok(Self::new(u16::scan(scanner)?, scanner.scan_dname()?))
763    }
764}
765
766impl<Octs> Mx<ParsedDname<Octs>> {
767    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized + 'a>(
768        parser: &mut Parser<'a, Src>,
769    ) -> Result<Self, ParseError> {
770        Ok(Self::new(u16::parse(parser)?, ParsedDname::parse(parser)?))
771    }
772}
773
774//--- OctetsFrom and FlattenInto
775
776impl<Name, SrcName> OctetsFrom<Mx<SrcName>> for Mx<Name>
777where
778    Name: OctetsFrom<SrcName>,
779{
780    type Error = Name::Error;
781
782    fn try_octets_from(source: Mx<SrcName>) -> Result<Self, Self::Error> {
783        Ok(Mx::new(
784            source.preference,
785            Name::try_octets_from(source.exchange)?,
786        ))
787    }
788}
789
790impl<Name, TName> FlattenInto<Mx<TName>> for Mx<Name>
791where
792    Name: FlattenInto<TName>,
793{
794    type AppendError = Name::AppendError;
795
796    fn try_flatten_into(self) -> Result<Mx<TName>, Name::AppendError> {
797        self.flatten()
798    }
799}
800
801//--- PartialEq and Eq
802
803impl<N, NN> PartialEq<Mx<NN>> for Mx<N>
804where
805    N: ToDname,
806    NN: ToDname,
807{
808    fn eq(&self, other: &Mx<NN>) -> bool {
809        self.preference == other.preference
810            && self.exchange.name_eq(&other.exchange)
811    }
812}
813
814impl<N: ToDname> Eq for Mx<N> {}
815
816//--- PartialOrd, Ord, and CanonicalOrd
817
818impl<N, NN> PartialOrd<Mx<NN>> for Mx<N>
819where
820    N: ToDname,
821    NN: ToDname,
822{
823    fn partial_cmp(&self, other: &Mx<NN>) -> Option<Ordering> {
824        match self.preference.partial_cmp(&other.preference) {
825            Some(Ordering::Equal) => {}
826            other => return other,
827        }
828        Some(self.exchange.name_cmp(&other.exchange))
829    }
830}
831
832impl<N: ToDname> Ord for Mx<N> {
833    fn cmp(&self, other: &Self) -> Ordering {
834        match self.preference.cmp(&other.preference) {
835            Ordering::Equal => {}
836            other => return other,
837        }
838        self.exchange.name_cmp(&other.exchange)
839    }
840}
841
842impl<N: ToDname, NN: ToDname> CanonicalOrd<Mx<NN>> for Mx<N> {
843    fn canonical_cmp(&self, other: &Mx<NN>) -> Ordering {
844        match self.preference.cmp(&other.preference) {
845            Ordering::Equal => {}
846            other => return other,
847        }
848        self.exchange.lowercase_composed_cmp(&other.exchange)
849    }
850}
851
852//--- RecordData, ParseRecordData, ComposeRecordData
853
854impl<N> RecordData for Mx<N> {
855    fn rtype(&self) -> Rtype {
856        Rtype::Mx
857    }
858}
859
860impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs>
861    for Mx<ParsedDname<Octs::Range<'a>>>
862{
863    fn parse_rdata(
864        rtype: Rtype,
865        parser: &mut Parser<'a, Octs>,
866    ) -> Result<Option<Self>, ParseError> {
867        if rtype == Rtype::Mx {
868            Self::parse(parser).map(Some)
869        } else {
870            Ok(None)
871        }
872    }
873}
874
875impl<Name: ToDname> ComposeRecordData for Mx<Name> {
876    fn rdlen(&self, compress: bool) -> Option<u16> {
877        if compress {
878            None
879        } else {
880            Some(u16::COMPOSE_LEN + self.exchange.compose_len())
881        }
882    }
883
884    fn compose_rdata<Target: Composer + ?Sized>(
885        &self,
886        target: &mut Target,
887    ) -> Result<(), Target::AppendError> {
888        if target.can_compress() {
889            self.preference.compose(target)?;
890            target.append_compressed_dname(&self.exchange)
891        } else {
892            self.preference.compose(target)?;
893            self.exchange.compose(target)
894        }
895    }
896
897    fn compose_canonical_rdata<Target: Composer + ?Sized>(
898        &self,
899        target: &mut Target,
900    ) -> Result<(), Target::AppendError> {
901        self.preference.compose(target)?;
902        self.exchange.compose_canonical(target)
903    }
904}
905
906//--- Display
907
908impl<N: fmt::Display> fmt::Display for Mx<N> {
909    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
910        write!(f, "{} {}.", self.preference, self.exchange)
911    }
912}
913
914//------------ Ns -----------------------------------------------------------
915
916dname_type_well_known! {
917    /// NS record data.
918    ///
919    /// NS records specify hosts that are authoritative for a class and domain.
920    ///
921    /// The NS record type is defined in RFC 1035, section 3.3.11.
922    (Ns, Ns, nsdname, into_nsdname)
923}
924
925//------------ Null ---------------------------------------------------------
926
927/// Null record data.
928///
929/// Null records can contain whatever data. They are experimental and not
930/// allowed in zone files.
931///
932/// The Null record type is defined in RFC 1035, section 3.3.10.
933#[derive(Clone)]
934#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
935pub struct Null<Octs: ?Sized> {
936    #[cfg_attr(
937        feature = "serde",
938        serde(
939            serialize_with = "octseq::serde::SerializeOctets::serialize_octets",
940            deserialize_with = "octseq::serde::DeserializeOctets::deserialize_octets",
941            bound(
942                serialize = "Octs: octseq::serde::SerializeOctets",
943                deserialize = "Octs: octseq::serde::DeserializeOctets<'de>",
944            )
945        )
946    )]
947    data: Octs,
948}
949
950impl<Octs> Null<Octs> {
951    /// Creates new NULL record data from the given octets.
952    ///
953    /// The function will fail if `data` is longer than 65,535 octets.
954    pub fn from_octets(data: Octs) -> Result<Self, LongRecordData>
955    where
956        Octs: AsRef<[u8]>,
957    {
958        Null::check_slice(data.as_ref())?;
959        Ok(unsafe { Self::from_octets_unchecked(data) })
960    }
961
962    /// Creates new NULL record data without checking.
963    ///
964    /// # Safety
965    ///
966    /// The caller has to ensure that `data` is at most 65,535 octets long.
967    pub unsafe fn from_octets_unchecked(data: Octs) -> Self {
968        Null { data }
969    }
970}
971
972impl Null<[u8]> {
973    /// Creates new NULL record data from an octets slice.
974    ///
975    /// The function will fail if `data` is longer than 65,535 octets.
976    pub fn from_slice(data: &[u8]) -> Result<&Self, LongRecordData> {
977        Self::check_slice(data)?;
978        Ok(unsafe { Self::from_slice_unchecked(data) })
979    }
980
981    /// Creates new NULL record from an octets slice data without checking.
982    ///
983    /// # Safety
984    ///
985    /// The caller has to ensure that `data` is at most 65,535 octets long.
986    #[must_use]
987    pub unsafe fn from_slice_unchecked(data: &[u8]) -> &Self {
988        &*(data as *const [u8] as *const Self)
989    }
990
991    /// Checks that a slice can be used for NULL record data.
992    fn check_slice(slice: &[u8]) -> Result<(), LongRecordData> {
993        LongRecordData::check_len(slice.len())
994    }
995}
996
997impl<Octs: ?Sized> Null<Octs> {
998    /// The raw content of the record.
999    pub fn data(&self) -> &Octs {
1000        &self.data
1001    }
1002}
1003
1004impl<Octs: AsRef<[u8]>> Null<Octs> {
1005    pub fn len(&self) -> usize {
1006        self.data.as_ref().len()
1007    }
1008
1009    pub fn is_empty(&self) -> bool {
1010        self.data.as_ref().is_empty()
1011    }
1012}
1013
1014impl<Octs> Null<Octs> {
1015    pub(super) fn convert_octets<Target: OctetsFrom<Octs>>(
1016        self,
1017    ) -> Result<Null<Target>, Target::Error> {
1018        Ok(unsafe {
1019            Null::from_octets_unchecked(self.data.try_octets_into()?)
1020        })
1021    }
1022
1023    pub(super) fn flatten<Target: OctetsFrom<Octs>>(
1024        self,
1025    ) -> Result<Null<Target>, Target::Error> {
1026        self.convert_octets()
1027    }
1028}
1029
1030impl<Octs> Null<Octs> {
1031    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
1032        parser: &mut Parser<'a, Src>,
1033    ) -> Result<Self, ParseError> {
1034        let len = parser.remaining();
1035        parser
1036            .parse_octets(len)
1037            .map(|res| unsafe { Self::from_octets_unchecked(res) })
1038            .map_err(Into::into)
1039    }
1040}
1041
1042//--- OctetsFrom
1043
1044impl<Octs, SrcOcts> OctetsFrom<Null<SrcOcts>> for Null<Octs>
1045where
1046    Octs: OctetsFrom<SrcOcts>,
1047{
1048    type Error = Octs::Error;
1049
1050    fn try_octets_from(source: Null<SrcOcts>) -> Result<Self, Self::Error> {
1051        Octs::try_octets_from(source.data)
1052            .map(|res| unsafe { Self::from_octets_unchecked(res) })
1053    }
1054}
1055
1056//--- PartialEq and Eq
1057
1058impl<Octs, Other> PartialEq<Null<Other>> for Null<Octs>
1059where
1060    Octs: AsRef<[u8]> + ?Sized,
1061    Other: AsRef<[u8]> + ?Sized,
1062{
1063    fn eq(&self, other: &Null<Other>) -> bool {
1064        self.data.as_ref().eq(other.data.as_ref())
1065    }
1066}
1067
1068impl<Octs: AsRef<[u8]> + ?Sized> Eq for Null<Octs> {}
1069
1070//--- PartialOrd, CanonicalOrd, and Ord
1071
1072impl<Octs, Other> PartialOrd<Null<Other>> for Null<Octs>
1073where
1074    Octs: AsRef<[u8]> + ?Sized,
1075    Other: AsRef<[u8]> + ?Sized,
1076{
1077    fn partial_cmp(&self, other: &Null<Other>) -> Option<Ordering> {
1078        self.data.as_ref().partial_cmp(other.data.as_ref())
1079    }
1080}
1081
1082impl<Octs, Other> CanonicalOrd<Null<Other>> for Null<Octs>
1083where
1084    Octs: AsRef<[u8]> + ?Sized,
1085    Other: AsRef<[u8]> + ?Sized,
1086{
1087    fn canonical_cmp(&self, other: &Null<Other>) -> Ordering {
1088        self.data.as_ref().cmp(other.data.as_ref())
1089    }
1090}
1091
1092impl<Octs: AsRef<[u8]> + ?Sized> Ord for Null<Octs> {
1093    fn cmp(&self, other: &Self) -> Ordering {
1094        self.data.as_ref().cmp(other.data.as_ref())
1095    }
1096}
1097
1098//--- Hash
1099
1100impl<Octs: AsRef<[u8]> + ?Sized> hash::Hash for Null<Octs> {
1101    fn hash<H: hash::Hasher>(&self, state: &mut H) {
1102        self.data.as_ref().hash(state)
1103    }
1104}
1105
1106//--- RecordData, ParseRecordData, ComposeRecordData
1107
1108impl<Octs: ?Sized> RecordData for Null<Octs> {
1109    fn rtype(&self) -> Rtype {
1110        Rtype::Null
1111    }
1112}
1113
1114impl<'a, Octs> ParseRecordData<'a, Octs> for Null<Octs::Range<'a>>
1115where
1116    Octs: Octets + ?Sized,
1117{
1118    fn parse_rdata(
1119        rtype: Rtype,
1120        parser: &mut Parser<'a, Octs>,
1121    ) -> Result<Option<Self>, ParseError> {
1122        if rtype == Rtype::Null {
1123            Self::parse(parser).map(Some)
1124        } else {
1125            Ok(None)
1126        }
1127    }
1128}
1129
1130impl<Octs: AsRef<[u8]> + ?Sized> ComposeRecordData for Null<Octs> {
1131    fn rdlen(&self, _compress: bool) -> Option<u16> {
1132        Some(
1133            u16::try_from(self.data.as_ref().len()).expect("long NULL rdata"),
1134        )
1135    }
1136
1137    fn compose_rdata<Target: Composer + ?Sized>(
1138        &self,
1139        target: &mut Target,
1140    ) -> Result<(), Target::AppendError> {
1141        target.append_slice(self.data.as_ref())
1142    }
1143
1144    fn compose_canonical_rdata<Target: Composer + ?Sized>(
1145        &self,
1146        target: &mut Target,
1147    ) -> Result<(), Target::AppendError> {
1148        self.compose_rdata(target)
1149    }
1150}
1151
1152//--- AsRef
1153
1154impl<Octs: AsRef<Other>, Other> AsRef<Other> for Null<Octs> {
1155    fn as_ref(&self) -> &Other {
1156        self.data.as_ref()
1157    }
1158}
1159
1160//--- Display and Debug
1161
1162impl<Octs: AsRef<[u8]>> fmt::Display for Null<Octs> {
1163    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1164        write!(f, "\\# {}", self.data.as_ref().len())?;
1165        for ch in self.data.as_ref().iter() {
1166            write!(f, " {:02x}", ch)?;
1167        }
1168        Ok(())
1169    }
1170}
1171
1172impl<Octs: AsRef<[u8]>> fmt::Debug for Null<Octs> {
1173    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1174        f.write_str("Null(")?;
1175        fmt::Display::fmt(self, f)?;
1176        f.write_str(")")
1177    }
1178}
1179
1180//------------ Ptr ----------------------------------------------------------
1181
1182dname_type_well_known! {
1183    /// PTR record data.
1184    ///
1185    /// PRT records are used in special domains to point to some other location
1186    /// in the domain space.
1187    ///
1188    /// The PTR record type is defined in RFC 1035, section 3.3.12.
1189    (Ptr, Ptr, ptrdname, into_ptrdname)
1190}
1191
1192//------------ Soa ----------------------------------------------------------
1193
1194/// Soa record data.
1195///
1196/// Soa records mark the top of a zone and contain information pertinent to
1197/// name server maintenance operations.
1198///
1199/// The Soa record type is defined in RFC 1035, section 3.3.13.
1200#[derive(Clone, Debug, Hash)]
1201#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1202pub struct Soa<N> {
1203    mname: N,
1204    rname: N,
1205    serial: Serial,
1206    refresh: Ttl,
1207    retry: Ttl,
1208    expire: Ttl,
1209    minimum: Ttl,
1210}
1211
1212impl<N> Soa<N> {
1213    /// Creates new Soa record data from content.
1214    pub fn new(
1215        mname: N,
1216        rname: N,
1217        serial: Serial,
1218        refresh: Ttl,
1219        retry: Ttl,
1220        expire: Ttl,
1221        minimum: Ttl,
1222    ) -> Self {
1223        Soa {
1224            mname,
1225            rname,
1226            serial,
1227            refresh,
1228            retry,
1229            expire,
1230            minimum,
1231        }
1232    }
1233
1234    /// The primary name server for the zone.
1235    pub fn mname(&self) -> &N {
1236        &self.mname
1237    }
1238
1239    /// The mailbox for the person responsible for this zone.
1240    pub fn rname(&self) -> &N {
1241        &self.rname
1242    }
1243
1244    /// The serial number of the original copy of the zone.
1245    pub fn serial(&self) -> Serial {
1246        self.serial
1247    }
1248
1249    /// The time interval before the zone should be refreshed.
1250    pub fn refresh(&self) -> Ttl {
1251        self.refresh
1252    }
1253
1254    /// The time before a failed refresh is retried.
1255    pub fn retry(&self) -> Ttl {
1256        self.retry
1257    }
1258
1259    /// The upper limit of time the zone is authoritative.
1260    pub fn expire(&self) -> Ttl {
1261        self.expire
1262    }
1263
1264    /// The minimum TTL to be exported with any RR from this zone.
1265    pub fn minimum(&self) -> Ttl {
1266        self.minimum
1267    }
1268
1269    pub(super) fn convert_octets<Target: OctetsFrom<N>>(
1270        self,
1271    ) -> Result<Soa<Target>, Target::Error> {
1272        Ok(Soa::new(
1273            self.mname.try_octets_into()?,
1274            self.rname.try_octets_into()?,
1275            self.serial,
1276            self.refresh,
1277            self.retry,
1278            self.expire,
1279            self.minimum,
1280        ))
1281    }
1282
1283    pub(super) fn flatten<TargetName>(
1284        self,
1285    ) -> Result<Soa<TargetName>, N::AppendError>
1286    where N: FlattenInto<TargetName> {
1287        Ok(Soa::new(
1288            self.mname.try_flatten_into()?,
1289            self.rname.try_flatten_into()?,
1290            self.serial,
1291            self.refresh,
1292            self.retry,
1293            self.expire,
1294            self.minimum,
1295        ))
1296    }
1297
1298    pub fn scan<S: Scanner<Dname = N>>(
1299        scanner: &mut S,
1300    ) -> Result<Self, S::Error> {
1301        Ok(Self::new(
1302            scanner.scan_dname()?,
1303            scanner.scan_dname()?,
1304            Serial::scan(scanner)?,
1305            Ttl::scan(scanner)?,
1306            Ttl::scan(scanner)?,
1307            Ttl::scan(scanner)?,
1308            Ttl::scan(scanner)?,
1309        ))
1310    }
1311}
1312
1313impl<Octs> Soa<ParsedDname<Octs>> {
1314    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized + 'a>(
1315        parser: &mut Parser<'a, Src>,
1316    ) -> Result<Self, ParseError> {
1317        Ok(Self::new(
1318            ParsedDname::parse(parser)?,
1319            ParsedDname::parse(parser)?,
1320            Serial::parse(parser)?,
1321            Ttl::parse(parser)?,
1322            Ttl::parse(parser)?,
1323            Ttl::parse(parser)?,
1324            Ttl::parse(parser)?,
1325        ))
1326    }
1327}
1328
1329//--- OctetsFrom and FlattenInto
1330
1331impl<Name, SrcName> OctetsFrom<Soa<SrcName>> for Soa<Name>
1332where
1333    Name: OctetsFrom<SrcName>,
1334{
1335    type Error = Name::Error;
1336
1337    fn try_octets_from(source: Soa<SrcName>) -> Result<Self, Self::Error> {
1338        Ok(Soa::new(
1339            Name::try_octets_from(source.mname)?,
1340            Name::try_octets_from(source.rname)?,
1341            source.serial,
1342            source.refresh,
1343            source.retry,
1344            source.expire,
1345            source.minimum,
1346        ))
1347    }
1348}
1349
1350impl<Name, TName> FlattenInto<Soa<TName>> for Soa<Name>
1351where
1352    Name: FlattenInto<TName>,
1353{
1354    type AppendError = Name::AppendError;
1355
1356    fn try_flatten_into(self) -> Result<Soa<TName>, Name::AppendError> {
1357        self.flatten()
1358    }
1359}
1360
1361//--- PartialEq and Eq
1362
1363impl<N, NN> PartialEq<Soa<NN>> for Soa<N>
1364where
1365    N: ToDname,
1366    NN: ToDname,
1367{
1368    fn eq(&self, other: &Soa<NN>) -> bool {
1369        self.mname.name_eq(&other.mname)
1370            && self.rname.name_eq(&other.rname)
1371            && self.serial == other.serial
1372            && self.refresh == other.refresh
1373            && self.retry == other.retry
1374            && self.expire == other.expire
1375            && self.minimum == other.minimum
1376    }
1377}
1378
1379impl<N: ToDname> Eq for Soa<N> {}
1380
1381//--- PartialOrd, Ord, and CanonicalOrd
1382
1383impl<N, NN> PartialOrd<Soa<NN>> for Soa<N>
1384where
1385    N: ToDname,
1386    NN: ToDname,
1387{
1388    fn partial_cmp(&self, other: &Soa<NN>) -> Option<Ordering> {
1389        match self.mname.name_cmp(&other.mname) {
1390            Ordering::Equal => {}
1391            other => return Some(other),
1392        }
1393        match self.rname.name_cmp(&other.rname) {
1394            Ordering::Equal => {}
1395            other => return Some(other),
1396        }
1397        match u32::from(self.serial).partial_cmp(&u32::from(other.serial)) {
1398            Some(Ordering::Equal) => {}
1399            other => return other,
1400        }
1401        match self.refresh.partial_cmp(&other.refresh) {
1402            Some(Ordering::Equal) => {}
1403            other => return other,
1404        }
1405        match self.retry.partial_cmp(&other.retry) {
1406            Some(Ordering::Equal) => {}
1407            other => return other,
1408        }
1409        match self.expire.partial_cmp(&other.expire) {
1410            Some(Ordering::Equal) => {}
1411            other => return other,
1412        }
1413        self.minimum.partial_cmp(&other.minimum)
1414    }
1415}
1416
1417impl<N: ToDname> Ord for Soa<N> {
1418    fn cmp(&self, other: &Self) -> Ordering {
1419        match self.mname.name_cmp(&other.mname) {
1420            Ordering::Equal => {}
1421            other => return other,
1422        }
1423        match self.rname.name_cmp(&other.rname) {
1424            Ordering::Equal => {}
1425            other => return other,
1426        }
1427        match u32::from(self.serial).cmp(&u32::from(other.serial)) {
1428            Ordering::Equal => {}
1429            other => return other,
1430        }
1431        match self.refresh.cmp(&other.refresh) {
1432            Ordering::Equal => {}
1433            other => return other,
1434        }
1435        match self.retry.cmp(&other.retry) {
1436            Ordering::Equal => {}
1437            other => return other,
1438        }
1439        match self.expire.cmp(&other.expire) {
1440            Ordering::Equal => {}
1441            other => return other,
1442        }
1443        self.minimum.cmp(&other.minimum)
1444    }
1445}
1446
1447impl<N: ToDname, NN: ToDname> CanonicalOrd<Soa<NN>> for Soa<N> {
1448    fn canonical_cmp(&self, other: &Soa<NN>) -> Ordering {
1449        match self.mname.lowercase_composed_cmp(&other.mname) {
1450            Ordering::Equal => {}
1451            other => return other,
1452        }
1453        match self.rname.lowercase_composed_cmp(&other.rname) {
1454            Ordering::Equal => {}
1455            other => return other,
1456        }
1457        match self.serial.canonical_cmp(&other.serial) {
1458            Ordering::Equal => {}
1459            other => return other,
1460        }
1461        match self.refresh.cmp(&other.refresh) {
1462            Ordering::Equal => {}
1463            other => return other,
1464        }
1465        match self.retry.cmp(&other.retry) {
1466            Ordering::Equal => {}
1467            other => return other,
1468        }
1469        match self.expire.cmp(&other.expire) {
1470            Ordering::Equal => {}
1471            other => return other,
1472        }
1473        self.minimum.cmp(&other.minimum)
1474    }
1475}
1476
1477//--- RecordData, ParseRecordData, ComposeRecordData
1478
1479impl<N> RecordData for Soa<N> {
1480    fn rtype(&self) -> Rtype {
1481        Rtype::Soa
1482    }
1483}
1484
1485impl<'a, Octs: Octets + ?Sized> ParseRecordData<'a, Octs>
1486    for Soa<ParsedDname<Octs::Range<'a>>>
1487{
1488    fn parse_rdata(
1489        rtype: Rtype,
1490        parser: &mut Parser<'a, Octs>,
1491    ) -> Result<Option<Self>, ParseError> {
1492        if rtype == Rtype::Soa {
1493            Self::parse(parser).map(Some)
1494        } else {
1495            Ok(None)
1496        }
1497    }
1498}
1499
1500impl<Name: ToDname> ComposeRecordData for Soa<Name> {
1501    fn rdlen(&self, compress: bool) -> Option<u16> {
1502        if compress {
1503            None
1504        } else {
1505            Some(
1506                self.mname.compose_len()
1507                    + self.rname.compose_len()
1508                    + Serial::COMPOSE_LEN
1509                    + 4 * u32::COMPOSE_LEN,
1510            )
1511        }
1512    }
1513
1514    fn compose_rdata<Target: Composer + ?Sized>(
1515        &self,
1516        target: &mut Target,
1517    ) -> Result<(), Target::AppendError> {
1518        if target.can_compress() {
1519            target.append_compressed_dname(&self.mname)?;
1520            target.append_compressed_dname(&self.rname)?;
1521        } else {
1522            self.mname.compose(target)?;
1523            self.rname.compose(target)?;
1524        }
1525        self.compose_fixed(target)
1526    }
1527
1528    fn compose_canonical_rdata<Target: Composer + ?Sized>(
1529        &self,
1530        target: &mut Target,
1531    ) -> Result<(), Target::AppendError> {
1532        self.mname.compose_canonical(target)?;
1533        self.rname.compose_canonical(target)?;
1534        self.compose_fixed(target)
1535    }
1536}
1537
1538impl<Name: ToDname> Soa<Name> {
1539    fn compose_fixed<Target: Composer + ?Sized>(
1540        &self,
1541        target: &mut Target,
1542    ) -> Result<(), Target::AppendError> {
1543        self.serial.compose(target)?;
1544        self.refresh.compose(target)?;
1545        self.retry.compose(target)?;
1546        self.expire.compose(target)?;
1547        self.minimum.compose(target)
1548    }
1549}
1550
1551//--- Display
1552
1553impl<N: fmt::Display> fmt::Display for Soa<N> {
1554    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1555        write!(
1556            f,
1557            "{}. {}. {} {} {} {} {}",
1558            self.mname,
1559            self.rname,
1560            self.serial,
1561            self.refresh.as_secs(),
1562            self.retry.as_secs(),
1563            self.expire.as_secs(),
1564            self.minimum.as_secs()
1565        )
1566    }
1567}
1568
1569//------------ Txt ----------------------------------------------------------
1570
1571/// Txt record data.
1572///
1573/// Txt records hold descriptive text.
1574///
1575/// The Txt record type is defined in RFC 1035, section 3.3.14.
1576#[derive(Clone)]
1577pub struct Txt<Octs: ?Sized>(Octs);
1578
1579impl<Octs: FromBuilder> Txt<Octs> {
1580    /// Creates a new Txt record from a single character string.
1581    pub fn build_from_slice(text: &[u8]) -> Result<Self, ShortBuf>
1582    where
1583        <Octs as FromBuilder>::Builder:
1584            EmptyBuilder + AsRef<[u8]> + AsMut<[u8]>,
1585    {
1586        let mut builder = TxtBuilder::<Octs::Builder>::new();
1587        builder.append_slice(text)?;
1588        Ok(builder.finish())
1589    }
1590}
1591
1592impl<Octs> Txt<Octs> {
1593    /// Creates new TXT record data from its encoded content.
1594    pub fn from_octets(octets: Octs) -> Result<Self, TxtError>
1595    where
1596        Octs: AsRef<[u8]>,
1597    {
1598        Txt::check_slice(octets.as_ref())?;
1599        Ok(unsafe { Txt::from_octets_unchecked(octets) })
1600    }
1601
1602    /// Creates new TXT record data without checking.
1603    ///
1604    /// # Safety
1605    ///
1606    /// The passed octets must contain correctly encoded TXT record data,
1607    /// that is a sequence of encoded character strings.
1608    unsafe fn from_octets_unchecked(octets: Octs) -> Self {
1609        Txt(octets)
1610    }
1611}
1612
1613impl Txt<[u8]> {
1614    /// Creates new TXT record data on an octets slice.
1615    pub fn from_slice(slice: &[u8]) -> Result<&Self, TxtError> {
1616        Txt::check_slice(slice)?;
1617        Ok(unsafe { Txt::from_slice_unchecked(slice) })
1618    }
1619
1620    /// Creates new TXT record data on an octets slice without checking.
1621    ///
1622    /// # Safety
1623    ///
1624    /// The passed octets must contain correctly encoded TXT record data,
1625    /// that is a sequence of encoded character strings.
1626    unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
1627        unsafe { &*(slice as *const [u8] as *const Self) }
1628    }
1629
1630    /// Checks that a slice contains correctly encoded TXT data.
1631    fn check_slice(mut slice: &[u8]) -> Result<(), TxtError> {
1632        LongRecordData::check_len(slice.len())?;
1633        while let Some(&len) = slice.first() {
1634            let len = usize::from(len);
1635            if slice.len() <= len {
1636                return Err(TxtError(TxtErrorInner::ShortInput));
1637            }
1638            slice = &slice[len + 1..];
1639        }
1640        Ok(())
1641    }
1642}
1643
1644impl<Octs> Txt<Octs> {
1645    pub fn parse<'a, Src: Octets<Range<'a> = Octs> + ?Sized>(
1646        parser: &mut Parser<'a, Src>,
1647    ) -> Result<Self, ParseError>
1648    where
1649        Octs: AsRef<[u8]>,
1650    {
1651        let len = parser.remaining();
1652        let text = parser.parse_octets(len)?;
1653        let mut tmp = Parser::from_ref(text.as_ref());
1654        while tmp.remaining() != 0 {
1655            CharStr::skip(&mut tmp)?
1656        }
1657        Ok(Txt(text))
1658    }
1659
1660    pub fn scan<S: Scanner<Octets = Octs>>(
1661        scanner: &mut S,
1662    ) -> Result<Self, S::Error> {
1663        scanner.scan_charstr_entry().map(Txt)
1664    }
1665}
1666
1667impl<Octs: AsRef<[u8]> + ?Sized> Txt<Octs> {
1668    /// Returns an iterator over the text items.
1669    ///
1670    /// The Txt format contains one or more length-delimited byte strings.
1671    /// This method returns an iterator over each of them.
1672    pub fn iter(&self) -> TxtIter {
1673        TxtIter(self.iter_char_strs())
1674    }
1675
1676    pub fn iter_char_strs(&self) -> TxtCharStrIter {
1677        TxtCharStrIter(Parser::from_ref(self.0.as_ref()))
1678    }
1679
1680    /// Returns the content if it consists of a single character string.
1681    pub fn as_flat_slice(&self) -> Option<&[u8]> {
1682        if usize::from(self.0.as_ref()[0]) == self.0.as_ref().len() - 1 {
1683            Some(&self.0.as_ref()[1..])
1684        } else {
1685            None
1686        }
1687    }
1688
1689    pub fn len(&self) -> usize {
1690        self.0.as_ref().len()
1691    }
1692
1693    pub fn is_empty(&self) -> bool {
1694        self.0.as_ref().is_empty()
1695    }
1696
1697    /// Returns the text content.
1698    ///
1699    /// If the data is only one single character string, returns a simple
1700    /// clone of the slice with the data. If there are several character
1701    /// strings, their content will be copied together into one single,
1702    /// newly allocated bytes value.
1703    ///
1704    /// Access to the individual character strings is possible via iteration.
1705    pub fn try_text<T: FromBuilder>(
1706        &self,
1707    ) -> Result<T, <<T as FromBuilder>::Builder as OctetsBuilder>::AppendError>
1708    where
1709        <T as FromBuilder>::Builder: EmptyBuilder,
1710    {
1711        // Capacity will be a few bytes too much. Probably better than
1712        // re-allocating.
1713        let mut res = T::Builder::with_capacity(self.len());
1714        for item in self.iter() {
1715            res.append_slice(item)?;
1716        }
1717        Ok(res.freeze())
1718    }
1719
1720    pub fn text<T: FromBuilder>(&self) -> T
1721    where
1722        <T as FromBuilder>::Builder: EmptyBuilder,
1723        <<T as FromBuilder>::Builder as OctetsBuilder>::AppendError:
1724            Into<Infallible>,
1725    {
1726        infallible(self.try_text())
1727    }
1728}
1729
1730impl<SrcOcts> Txt<SrcOcts> {
1731    pub(super) fn convert_octets<Target: OctetsFrom<SrcOcts>>(
1732        self,
1733    ) -> Result<Txt<Target>, Target::Error> {
1734        Ok(Txt(self.0.try_octets_into()?))
1735    }
1736
1737    pub(super) fn flatten<Octs: OctetsFrom<SrcOcts>>(
1738        self,
1739    ) -> Result<Txt<Octs>, Octs::Error> {
1740        self.convert_octets()
1741    }
1742}
1743
1744//--- OctetsFrom
1745
1746impl<Octs, SrcOcts> OctetsFrom<Txt<SrcOcts>> for Txt<Octs>
1747where
1748    Octs: OctetsFrom<SrcOcts>,
1749{
1750    type Error = Octs::Error;
1751
1752    fn try_octets_from(source: Txt<SrcOcts>) -> Result<Self, Self::Error> {
1753        Octs::try_octets_from(source.0).map(Self)
1754    }
1755}
1756
1757//--- IntoIterator
1758
1759impl<'a, Octs: AsRef<[u8]>> IntoIterator for &'a Txt<Octs> {
1760    type Item = &'a [u8];
1761    type IntoIter = TxtIter<'a>;
1762
1763    fn into_iter(self) -> Self::IntoIter {
1764        self.iter()
1765    }
1766}
1767
1768//--- PartialEq and Eq
1769
1770impl<Octs, Other> PartialEq<Txt<Other>> for Txt<Octs>
1771where
1772    Octs: AsRef<[u8]>,
1773    Other: AsRef<[u8]>,
1774{
1775    fn eq(&self, other: &Txt<Other>) -> bool {
1776        self.iter()
1777            .flat_map(|s| s.iter().copied())
1778            .eq(other.iter().flat_map(|s| s.iter().copied()))
1779    }
1780}
1781
1782impl<Octs: AsRef<[u8]>> Eq for Txt<Octs> {}
1783
1784//--- PartialOrd, CanonicalOrd, and Ord
1785
1786impl<Octs, Other> PartialOrd<Txt<Other>> for Txt<Octs>
1787where
1788    Octs: AsRef<[u8]>,
1789    Other: AsRef<[u8]>,
1790{
1791    fn partial_cmp(&self, other: &Txt<Other>) -> Option<Ordering> {
1792        self.iter()
1793            .flat_map(|s| s.iter().copied())
1794            .partial_cmp(other.iter().flat_map(|s| s.iter().copied()))
1795    }
1796}
1797
1798impl<Octs, Other> CanonicalOrd<Txt<Other>> for Txt<Octs>
1799where
1800    Octs: AsRef<[u8]>,
1801    Other: AsRef<[u8]>,
1802{
1803    fn canonical_cmp(&self, other: &Txt<Other>) -> Ordering {
1804        // Canonical comparison requires TXT RDATA to be canonically
1805        // sorted in the wire format.
1806        // The TXT has each label prefixed by length, which must be
1807        // taken into account.
1808        for (a, b) in self.iter().zip(other.iter()) {
1809            match (a.len(), a).cmp(&(b.len(), b)) {
1810                Ordering::Equal => continue,
1811                r => return r,
1812            }
1813        }
1814
1815        Ordering::Equal
1816    }
1817}
1818
1819impl<Octs: AsRef<[u8]>> Ord for Txt<Octs> {
1820    fn cmp(&self, other: &Self) -> Ordering {
1821        self.iter()
1822            .flat_map(|s| s.iter().copied())
1823            .cmp(other.iter().flat_map(|s| s.iter().copied()))
1824    }
1825}
1826
1827//--- Hash
1828
1829impl<Octs: AsRef<[u8]>> hash::Hash for Txt<Octs> {
1830    fn hash<H: hash::Hasher>(&self, state: &mut H) {
1831        self.iter()
1832            .flat_map(|s| s.iter().copied())
1833            .for_each(|c| c.hash(state))
1834    }
1835}
1836
1837//--- RecordData, ParseRecordData, ComposeRecordData
1838
1839impl<Octs> RecordData for Txt<Octs> {
1840    fn rtype(&self) -> Rtype {
1841        Rtype::Txt
1842    }
1843}
1844
1845impl<'a, Octs> ParseRecordData<'a, Octs> for Txt<Octs::Range<'a>>
1846where
1847    Octs: Octets + ?Sized,
1848{
1849    fn parse_rdata(
1850        rtype: Rtype,
1851        parser: &mut Parser<'a, Octs>,
1852    ) -> Result<Option<Self>, ParseError> {
1853        if rtype == Rtype::Txt {
1854            Self::parse(parser).map(Some)
1855        } else {
1856            Ok(None)
1857        }
1858    }
1859}
1860
1861impl<Octs: AsRef<[u8]>> ComposeRecordData for Txt<Octs> {
1862    fn rdlen(&self, _compress: bool) -> Option<u16> {
1863        Some(u16::try_from(self.0.as_ref().len()).expect("long TXT rdata"))
1864    }
1865
1866    fn compose_rdata<Target: Composer + ?Sized>(
1867        &self,
1868        target: &mut Target,
1869    ) -> Result<(), Target::AppendError> {
1870        target.append_slice(self.0.as_ref())
1871    }
1872
1873    fn compose_canonical_rdata<Target: Composer + ?Sized>(
1874        &self,
1875        target: &mut Target,
1876    ) -> Result<(), Target::AppendError> {
1877        self.compose_rdata(target)
1878    }
1879}
1880
1881//--- Display
1882
1883impl<Octs: AsRef<[u8]>> fmt::Display for Txt<Octs> {
1884    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1885        for slice in self.iter() {
1886            for ch in slice.iter() {
1887                fmt::Display::fmt(&Symbol::from_octet(*ch), f)?
1888            }
1889        }
1890        Ok(())
1891    }
1892}
1893
1894//--- Debug
1895
1896impl<Octs: AsRef<[u8]>> fmt::Debug for Txt<Octs> {
1897    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1898        f.write_str("Txt(")?;
1899        fmt::Display::fmt(self, f)?;
1900        f.write_str(")")
1901    }
1902}
1903
1904//--- Serialize and Deserialize
1905
1906#[cfg(feature = "serde")]
1907impl<Octs> serde::Serialize for Txt<Octs>
1908where
1909    Octs: AsRef<[u8]> + SerializeOctets,
1910{
1911    fn serialize<S: serde::Serializer>(
1912        &self,
1913        serializer: S,
1914    ) -> Result<S::Ok, S::Error> {
1915        use serde::ser::SerializeSeq;
1916
1917        struct TxtSeq<'a, Octs>(&'a Txt<Octs>);
1918
1919        impl<'a, Octs> serde::Serialize for TxtSeq<'a, Octs>
1920        where
1921            Octs: AsRef<[u8]> + SerializeOctets,
1922        {
1923            fn serialize<S: serde::Serializer>(
1924                &self,
1925                serializer: S,
1926            ) -> Result<S::Ok, S::Error> {
1927                let mut serializer = serializer.serialize_seq(None)?;
1928                for item in self.0.iter_char_strs() {
1929                    serializer
1930                        .serialize_element(&format_args!("{}", item))?;
1931                }
1932                serializer.end()
1933            }
1934        }
1935
1936        if serializer.is_human_readable() {
1937            serializer.serialize_newtype_struct("Txt", &TxtSeq(self))
1938        } else {
1939            serializer.serialize_newtype_struct(
1940                "Txt",
1941                &self.0.as_serialized_octets(),
1942            )
1943        }
1944    }
1945}
1946
1947#[cfg(feature = "serde")]
1948impl<'de, Octs> serde::Deserialize<'de> for Txt<Octs>
1949where
1950    Octs: FromBuilder + DeserializeOctets<'de>,
1951    <Octs as FromBuilder>::Builder: EmptyBuilder + AsRef<[u8]> + AsMut<[u8]>,
1952{
1953    fn deserialize<D: serde::Deserializer<'de>>(
1954        deserializer: D,
1955    ) -> Result<Self, D::Error> {
1956        use core::marker::PhantomData;
1957
1958        struct InnerVisitor<'de, T: DeserializeOctets<'de>>(T::Visitor);
1959
1960        impl<'de, Octs> serde::de::Visitor<'de> for InnerVisitor<'de, Octs>
1961        where
1962            Octs: FromBuilder + DeserializeOctets<'de>,
1963            <Octs as FromBuilder>::Builder:
1964                OctetsBuilder + EmptyBuilder + AsRef<[u8]> + AsMut<[u8]>,
1965        {
1966            type Value = Txt<Octs>;
1967
1968            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
1969                f.write_str("TXT record data")
1970            }
1971
1972            fn visit_str<E: serde::de::Error>(
1973                self,
1974                v: &str,
1975            ) -> Result<Self::Value, E> {
1976                // This is a non-canonical serialization. We accept strings
1977                // of any length and break them down into chunks.
1978                let mut builder =
1979                    TxtBuilder::<<Octs as FromBuilder>::Builder>::new();
1980                let mut chars = v.chars();
1981                while let Some(ch) =
1982                    Symbol::from_chars(&mut chars).map_err(E::custom)?
1983                {
1984                    builder
1985                        .append_u8(ch.into_octet().map_err(E::custom)?)
1986                        .map_err(E::custom)?;
1987                }
1988                Ok(builder.finish())
1989            }
1990
1991            fn visit_seq<A: serde::de::SeqAccess<'de>>(
1992                self,
1993                mut seq: A,
1994            ) -> Result<Self::Value, A::Error> {
1995                let mut builder =
1996                    TxtBuilder::<<Octs as FromBuilder>::Builder>::new();
1997                while let Some(s) = seq.next_element::<&'de str>()? {
1998                    builder.append_zone_char_str(s)?;
1999                }
2000                Ok(builder.finish())
2001            }
2002
2003            fn visit_borrowed_bytes<E: serde::de::Error>(
2004                self,
2005                value: &'de [u8],
2006            ) -> Result<Self::Value, E> {
2007                self.0.visit_borrowed_bytes(value).and_then(|octets| {
2008                    Txt::from_octets(octets).map_err(E::custom)
2009                })
2010            }
2011
2012            #[cfg(feature = "std")]
2013            fn visit_byte_buf<E: serde::de::Error>(
2014                self,
2015                value: std::vec::Vec<u8>,
2016            ) -> Result<Self::Value, E> {
2017                self.0.visit_byte_buf(value).and_then(|octets| {
2018                    Txt::from_octets(octets).map_err(E::custom)
2019                })
2020            }
2021        }
2022
2023        struct NewtypeVisitor<T>(PhantomData<T>);
2024
2025        impl<'de, Octs> serde::de::Visitor<'de> for NewtypeVisitor<Octs>
2026        where
2027            Octs: FromBuilder + DeserializeOctets<'de>,
2028            <Octs as FromBuilder>::Builder:
2029                OctetsBuilder + EmptyBuilder + AsRef<[u8]> + AsMut<[u8]>,
2030        {
2031            type Value = Txt<Octs>;
2032
2033            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
2034                f.write_str("TXT record data")
2035            }
2036
2037            fn visit_newtype_struct<D: serde::Deserializer<'de>>(
2038                self,
2039                deserializer: D,
2040            ) -> Result<Self::Value, D::Error> {
2041                deserializer.deserialize_any(InnerVisitor(Octs::visitor()))
2042                /*
2043                if deserializer.is_human_readable() {
2044                    deserializer
2045                        .deserialize_str(InnerVisitor(Octs::visitor()))
2046                } else {
2047                    Octs::deserialize_with_visitor(
2048                        deserializer,
2049                        InnerVisitor(Octs::visitor()),
2050                    )
2051                }
2052                */
2053            }
2054        }
2055
2056        deserializer
2057            .deserialize_newtype_struct("Txt", NewtypeVisitor(PhantomData))
2058    }
2059}
2060
2061//------------ TxtCharStrIter ------------------------------------------------
2062
2063/// An iterator over the character strings of a Txt record.
2064#[derive(Clone)]
2065pub struct TxtCharStrIter<'a>(Parser<'a, [u8]>);
2066
2067impl<'a> Iterator for TxtCharStrIter<'a> {
2068    type Item = CharStr<&'a [u8]>;
2069
2070    fn next(&mut self) -> Option<Self::Item> {
2071        if self.0.remaining() == 0 {
2072            None
2073        } else {
2074            Some(CharStr::parse(&mut self.0).unwrap())
2075        }
2076    }
2077}
2078
2079//------------ TxtIter -------------------------------------------------------
2080
2081/// An iterator over the character strings of a Txt record.
2082#[derive(Clone)]
2083pub struct TxtIter<'a>(TxtCharStrIter<'a>);
2084
2085impl<'a> Iterator for TxtIter<'a> {
2086    type Item = &'a [u8];
2087
2088    fn next(&mut self) -> Option<Self::Item> {
2089        self.0.next().map(CharStr::into_octets)
2090    }
2091}
2092
2093//------------ TxtBuilder ---------------------------------------------------
2094
2095#[derive(Clone, Debug)]
2096pub struct TxtBuilder<Builder> {
2097    builder: Builder,
2098
2099    /// The index of the start of the current char string.
2100    ///
2101    /// If this is `None`, there currently is no char string being worked on.
2102    start: Option<usize>,
2103}
2104
2105impl<Builder: OctetsBuilder + EmptyBuilder> TxtBuilder<Builder> {
2106    #[must_use]
2107    pub fn new() -> Self {
2108        TxtBuilder {
2109            builder: Builder::empty(),
2110            start: None,
2111        }
2112    }
2113}
2114
2115#[cfg(feature = "bytes")]
2116impl TxtBuilder<BytesMut> {
2117    pub fn new_bytes() -> Self {
2118        Self::new()
2119    }
2120}
2121
2122impl<Builder: OctetsBuilder + AsRef<[u8]> + AsMut<[u8]>> TxtBuilder<Builder> {
2123    fn builder_append_slice(&mut self, slice: &[u8]) -> Result<(), ShortBuf> {
2124        self.builder.append_slice(slice).map_err(Into::into)
2125    }
2126
2127    pub fn append_slice(&mut self, mut slice: &[u8]) -> Result<(), ShortBuf> {
2128        if let Some(start) = self.start {
2129            let left = 255 - (self.builder.as_ref().len() - (start + 1));
2130            if slice.len() < left {
2131                self.builder_append_slice(slice)?;
2132                return Ok(());
2133            }
2134            let (append, left) = slice.split_at(left);
2135            self.builder_append_slice(append)?;
2136            self.builder.as_mut()[start] = 255;
2137            slice = left;
2138        }
2139        for chunk in slice.chunks(255) {
2140            if self.builder.as_ref().len() + chunk.len() + 1 >= 0xFFFF {
2141                return Err(ShortBuf);
2142            }
2143            // Remember offset of this incomplete chunk
2144            self.start = if chunk.len() == 255 {
2145                None
2146            } else {
2147                Some(self.builder.as_ref().len())
2148            };
2149            self.builder_append_slice(&[chunk.len() as u8])?;
2150            self.builder_append_slice(chunk)?;
2151        }
2152        Ok(())
2153    }
2154
2155    pub fn append_u8(&mut self, ch: u8) -> Result<(), ShortBuf> {
2156        self.append_slice(&[ch])
2157    }
2158
2159    #[cfg(feature = "serde")]
2160    fn append_zone_char_str<E: serde::de::Error>(
2161        &mut self,
2162        s: &str,
2163    ) -> Result<(), E> {
2164        use crate::base::charstr::CharStrError;
2165
2166        self.close_char_str();
2167        self.start = Some(self.builder.as_ref().len());
2168        self.builder_append_slice(&[0]).map_err(E::custom)?;
2169        let mut len = 0;
2170        let mut chars = s.chars();
2171        while let Some(sym) =
2172            Symbol::from_chars(&mut chars).map_err(E::custom)?
2173        {
2174            if len == 255 {
2175                return Err(E::custom(CharStrError));
2176            }
2177            let sym = sym.into_octet().map_err(E::custom)?;
2178            self.builder_append_slice(&[sym]).map_err(E::custom)?;
2179            len += 1;
2180        }
2181        self.close_char_str();
2182        Ok(())
2183    }
2184
2185    fn close_char_str(&mut self) {
2186        if let Some(start) = self.start {
2187            let last_slice_len = self.builder.as_ref().len() - (start + 1);
2188            self.builder.as_mut()[start] = last_slice_len as u8;
2189        }
2190    }
2191
2192    pub fn finish(mut self) -> Txt<Builder::Octets>
2193    where
2194        Builder: FreezeBuilder,
2195    {
2196        self.close_char_str();
2197        Txt(self.builder.freeze())
2198    }
2199}
2200
2201impl<Builder: OctetsBuilder + EmptyBuilder> Default for TxtBuilder<Builder> {
2202    fn default() -> Self {
2203        Self::new()
2204    }
2205}
2206
2207//============ Error Types ===================================================
2208
2209//------------ TxtError ------------------------------------------------------
2210
2211/// An octets sequence does not form valid TXT record data.
2212#[derive(Clone, Copy, Debug)]
2213pub struct TxtError(TxtErrorInner);
2214
2215#[derive(Clone, Copy, Debug)]
2216enum TxtErrorInner {
2217    Long(LongRecordData),
2218    ShortInput,
2219}
2220
2221impl TxtError {
2222    #[must_use]
2223    pub fn as_str(self) -> &'static str {
2224        match self.0 {
2225            TxtErrorInner::Long(err) => err.as_str(),
2226            TxtErrorInner::ShortInput => "short input",
2227        }
2228    }
2229}
2230
2231impl From<LongRecordData> for TxtError {
2232    fn from(err: LongRecordData) -> TxtError {
2233        TxtError(TxtErrorInner::Long(err))
2234    }
2235}
2236
2237impl From<TxtError> for FormError {
2238    fn from(err: TxtError) -> FormError {
2239        FormError::new(err.as_str())
2240    }
2241}
2242
2243impl fmt::Display for TxtError {
2244    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2245        f.write_str(self.as_str())
2246    }
2247}
2248
2249//============ Testing =======================================================
2250
2251#[cfg(test)]
2252#[cfg(all(feature = "std", feature = "bytes"))]
2253mod test {
2254    use super::*;
2255    use crate::base::name::Dname;
2256    use crate::base::rdata::test::{
2257        test_compose_parse, test_rdlen, test_scan,
2258    };
2259    use octseq::octets::OctetsInto;
2260    use std::vec::Vec;
2261
2262    //--- A
2263
2264    #[test]
2265    fn a_compose_parse_scan() {
2266        let rdata = A::from_octets(1, 2, 3, 4);
2267        test_rdlen(&rdata);
2268        test_compose_parse(&rdata, A::parse);
2269        test_scan(&["1.2.3.4"], A::scan, &rdata);
2270    }
2271
2272    //--- Cname
2273    //
2274    // This covers all the other generated types, too.
2275
2276    #[test]
2277    #[allow(clippy::redundant_closure)] // lifetimes ...
2278    fn cname_compose_parse_scan() {
2279        let rdata =
2280            Cname::<Dname<Vec<u8>>>::from_str("www.example.com").unwrap();
2281        test_rdlen(&rdata);
2282        test_compose_parse(&rdata, |parser| Cname::parse(parser));
2283        test_scan(&["www.example.com"], Cname::scan, &rdata);
2284    }
2285
2286    //--- Hinfo
2287
2288    #[test]
2289    #[allow(clippy::redundant_closure)] // lifetimes ...
2290    fn hinfo_compose_parse_scan() {
2291        let rdata = Hinfo::new(
2292            CharStr::from_octets("cpu").unwrap(),
2293            CharStr::from_octets("os").unwrap(),
2294        );
2295        test_rdlen(&rdata);
2296        test_compose_parse(&rdata, |parser| Hinfo::parse(parser));
2297        test_scan(&["cpu", "os"], Hinfo::scan, &rdata);
2298    }
2299
2300    #[test]
2301    fn hinfo_octets_into() {
2302        let hinfo: Hinfo<Vec<u8>> =
2303            Hinfo::new("1234".parse().unwrap(), "abcd".parse().unwrap());
2304        let hinfo_bytes: Hinfo<bytes::Bytes> = hinfo.clone().octets_into();
2305        assert_eq!(hinfo.cpu(), hinfo_bytes.cpu());
2306        assert_eq!(hinfo.os(), hinfo_bytes.os());
2307    }
2308
2309    //--- Minfo
2310
2311    #[test]
2312    #[allow(clippy::redundant_closure)] // lifetimes ...
2313    fn minfo_compose_parse_scan() {
2314        let rdata = Minfo::<Dname<Vec<u8>>>::new(
2315            Dname::from_str("r.example.com").unwrap(),
2316            Dname::from_str("e.example.com").unwrap(),
2317        );
2318        test_rdlen(&rdata);
2319        test_compose_parse(&rdata, |parser| Minfo::parse(parser));
2320        test_scan(&["r.example.com", "e.example.com"], Minfo::scan, &rdata);
2321    }
2322
2323    #[test]
2324    fn minfo_octets_into() {
2325        let minfo: Minfo<Dname<Vec<u8>>> = Minfo::new(
2326            "a.example".parse().unwrap(),
2327            "b.example".parse().unwrap(),
2328        );
2329        let minfo_bytes: Minfo<Dname<bytes::Bytes>> =
2330            minfo.clone().octets_into();
2331        assert_eq!(minfo.rmailbx(), minfo_bytes.rmailbx());
2332        assert_eq!(minfo.emailbx(), minfo_bytes.emailbx());
2333    }
2334
2335    //--- Mx
2336
2337    #[test]
2338    #[allow(clippy::redundant_closure)] // lifetimes ...
2339    fn mx_compose_parse_scan() {
2340        let rdata = Mx::<Dname<Vec<u8>>>::new(
2341            12,
2342            Dname::from_str("mail.example.com").unwrap(),
2343        );
2344        test_rdlen(&rdata);
2345        test_compose_parse(&rdata, |parser| Mx::parse(parser));
2346        test_scan(&["12", "mail.example.com"], Mx::scan, &rdata);
2347    }
2348
2349    //--- Null
2350
2351    #[test]
2352    #[allow(clippy::redundant_closure)] // lifetimes ...
2353    fn null_compose_parse_scan() {
2354        let rdata = Null::from_octets("foo").unwrap();
2355        test_rdlen(&rdata);
2356        test_compose_parse(&rdata, |parser| Null::parse(parser));
2357    }
2358
2359    //--- Soa
2360
2361    #[test]
2362    #[allow(clippy::redundant_closure)] // lifetimes ...
2363    fn soa_compose_parse_scan() {
2364        let rdata = Soa::<Dname<Vec<u8>>>::new(
2365            Dname::from_str("m.example.com").unwrap(),
2366            Dname::from_str("r.example.com").unwrap(),
2367            Serial(11),
2368            Ttl::from_secs(12),
2369            Ttl::from_secs(13),
2370            Ttl::from_secs(14),
2371            Ttl::from_secs(15),
2372        );
2373        test_rdlen(&rdata);
2374        test_compose_parse(&rdata, |parser| Soa::parse(parser));
2375        test_scan(
2376            &[
2377                "m.example.com",
2378                "r.example.com",
2379                "11",
2380                "12",
2381                "13",
2382                "14",
2383                "15",
2384            ],
2385            Soa::scan,
2386            &rdata,
2387        );
2388    }
2389
2390    //--- Txt
2391
2392    #[test]
2393    #[allow(clippy::redundant_closure)] // lifetimes ...
2394    fn txt_compose_parse_scan() {
2395        let rdata = Txt::from_octets(b"\x03foo\x03bar".as_ref()).unwrap();
2396        test_rdlen(&rdata);
2397        test_compose_parse(&rdata, |parser| Txt::parse(parser));
2398        test_scan(&["foo", "bar"], Txt::scan, &rdata);
2399    }
2400
2401    #[test]
2402    fn txt_from_slice() {
2403        let short = b"01234";
2404        let txt: Txt<Vec<u8>> = Txt::build_from_slice(short).unwrap();
2405        assert_eq!(Some(&short[..]), txt.as_flat_slice());
2406        assert_eq!(short.to_vec(), txt.text::<Vec<u8>>());
2407
2408        // One full slice
2409        let full = short.repeat(51);
2410        let txt: Txt<Vec<u8>> = Txt::build_from_slice(&full).unwrap();
2411        assert_eq!(Some(&full[..]), txt.as_flat_slice());
2412        assert_eq!(full.to_vec(), txt.text::<Vec<u8>>());
2413
2414        // Two slices: 255, 5
2415        let long = short.repeat(52);
2416        let txt: Txt<Vec<u8>> = Txt::build_from_slice(&long).unwrap();
2417        assert_eq!(None, txt.as_flat_slice());
2418        assert_eq!(long.to_vec(), txt.text::<Vec<u8>>());
2419
2420        // Partial
2421        let mut builder: TxtBuilder<Vec<u8>> = TxtBuilder::new();
2422        for chunk in long.chunks(9) {
2423            builder.append_slice(chunk).unwrap();
2424        }
2425        let txt = builder.finish();
2426        assert_eq!(None, txt.as_flat_slice());
2427        assert_eq!(long.to_vec(), txt.text::<Vec<u8>>());
2428
2429        // Empty
2430        let mut builder: TxtBuilder<Vec<u8>> = TxtBuilder::new();
2431        assert!(builder.append_slice(&[]).is_ok());
2432        let empty = builder.finish();
2433        assert!(empty.is_empty());
2434        assert_eq!(0, empty.iter().count());
2435
2436        // Invalid
2437        let mut parser = Parser::from_static(b"\x01");
2438        assert!(Txt::parse(&mut parser).is_err());
2439
2440        // Too long
2441        let mut builder: TxtBuilder<Vec<u8>> = TxtBuilder::new();
2442        assert!(builder
2443            .append_slice(&b"\x00".repeat(std::u16::MAX as usize))
2444            .is_err());
2445
2446        // Incremental, reserve space for offsets
2447        let mut builder: TxtBuilder<Vec<u8>> = TxtBuilder::new();
2448        assert!(builder
2449            .append_slice(&b"\x00".repeat(std::u16::MAX as usize - 512))
2450            .is_ok());
2451        assert!(builder.append_slice(&b"\x00".repeat(512)).is_err());
2452    }
2453
2454    #[test]
2455    fn txt_canonical_compare() {
2456        let data = [
2457            "mailru-verification: 14505c6eb222c847",
2458            "yandex-verification: 6059b187e78de544",
2459            "v=spf1 include:_spf.protonmail.ch ~all",
2460            "swisssign-check=CF0JHMTlTDNoES3rrknIRggocffSwqmzMb9X8YbjzK",
2461            "google-site-verification=aq9zJnp3H3bNE0Y4D4rH5I5Dhj8VMaLYx0uQ7Rozfgg",
2462            "ahrefs-site-verification_4bdac6bbaa81e0d591d7c0f3ef238905c0521b69bf3d74e64d3775bcb2743afd",
2463            "brave-ledger-verification=66a7f27fb99949cc0c564ab98efcc58ea1bac3e97eb557c782ab2d44b49aefd7",
2464        ];
2465
2466        let records = data
2467            .iter()
2468            .map(|e| {
2469                let mut builder = TxtBuilder::<Vec<u8>>::new();
2470                builder.append_slice(e.as_bytes()).unwrap();
2471                builder.finish()
2472            })
2473            .collect::<Vec<_>>();
2474
2475        // The canonical sort must sort by TXT labels which are prefixed by length byte first.
2476        let mut sorted = records.clone();
2477        sorted.sort_by(|a, b| a.canonical_cmp(b));
2478
2479        for (a, b) in records.iter().zip(sorted.iter()) {
2480            assert_eq!(a, b);
2481        }
2482    }
2483
2484    #[cfg(all(feature = "serde", feature = "std"))]
2485    #[test]
2486    fn txt_ser_de() {
2487        use serde_test::{assert_tokens, Configure, Token};
2488
2489        let txt = Txt::from_octets(Vec::from(b"\x03foo".as_ref())).unwrap();
2490        assert_tokens(
2491            &txt.clone().compact(),
2492            &[
2493                Token::NewtypeStruct { name: "Txt" },
2494                Token::ByteBuf(b"\x03foo"),
2495            ],
2496        );
2497        assert_tokens(
2498            &txt.readable(),
2499            &[
2500                Token::NewtypeStruct { name: "Txt" },
2501                Token::Seq { len: None },
2502                Token::BorrowedStr("foo"),
2503                Token::SeqEnd,
2504            ],
2505        );
2506    }
2507
2508    #[cfg(all(feature = "serde", feature = "std"))]
2509    #[test]
2510    fn txt_de_str() {
2511        use serde_test::{assert_de_tokens, Configure, Token};
2512
2513        assert_de_tokens(
2514            &Txt::from_octets(Vec::from(b"\x03foo".as_ref()))
2515                .unwrap()
2516                .readable(),
2517            &[
2518                Token::NewtypeStruct { name: "Txt" },
2519                Token::BorrowedStr("foo"),
2520            ],
2521        );
2522    }
2523}