trust_dns_proto/rr/
record_type.rs

1// Copyright 2015-2021 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! record type definitions
9#![allow(clippy::use_self)]
10
11use std::cmp::Ordering;
12use std::convert::From;
13use std::fmt;
14use std::fmt::{Display, Formatter};
15use std::str::FromStr;
16
17#[cfg(feature = "serde-config")]
18use serde::{Deserialize, Serialize};
19
20use crate::error::*;
21use crate::serialize::binary::*;
22
23// TODO: adopt proper restrictions on usage: https://tools.ietf.org/html/rfc6895 section 3.1
24//  add the data TYPEs, QTYPEs, and Meta-TYPEs
25//
26
27/// The type of the resource record.
28///
29/// This specifies the type of data in the RData field of the Resource Record
30#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
31#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
32#[allow(dead_code)]
33#[non_exhaustive]
34pub enum RecordType {
35    /// [RFC 1035](https://tools.ietf.org/html/rfc1035) IPv4 Address record
36    A,
37    /// [RFC 3596](https://tools.ietf.org/html/rfc3596) IPv6 address record
38    AAAA,
39    /// [ANAME draft-ietf-dnsop-aname](https://tools.ietf.org/html/draft-ietf-dnsop-aname-04)
40    ANAME,
41    //  AFSDB,      //	18	RFC 1183	AFS database record
42    /// [RFC 1035](https://tools.ietf.org/html/rfc1035) All cached records, aka ANY
43    ANY,
44    //  APL,        //	42	RFC 3123	Address Prefix List
45    /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Authoritative Zone Transfer
46    AXFR,
47    /// [RFC 6844](https://tools.ietf.org/html/rfc6844) Certification Authority Authorization
48    CAA,
49    /// [RFC 7344](https://tools.ietf.org/html/rfc7344) Child DS
50    CDS,
51    /// [RFC 7344](https://tools.ietf.org/html/rfc7344) Child DNSKEY
52    CDNSKEY,
53    //  CERT,       // 37 RFC 4398 Certificate record
54    /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Canonical name record
55    CNAME,
56    //  DHCID,      // 49 RFC 4701 DHCP identifier
57    //  DLV,        //	32769	RFC 4431	DNSSEC Lookaside Validation record
58    //  DNAME,      // 39 RFC 2672 Delegation Name
59    /// [RFC 7477](https://tools.ietf.org/html/rfc4034) Child-to-parent synchronization record
60    CSYNC,
61    /// [RFC 4034](https://tools.ietf.org/html/rfc4034) DNS Key record: RSASHA256 and RSASHA512, RFC5702
62    DNSKEY,
63    /// [RFC 4034](https://tools.ietf.org/html/rfc4034) Delegation signer: RSASHA256 and RSASHA512, RFC5702
64    DS,
65    /// [RFC 1035](https://tools.ietf.org/html/rfc1035) host information
66    HINFO,
67    //  HIP,        // 55 RFC 5205 Host Identity Protocol
68    /// [RFC draft-ietf-dnsop-svcb-https-03](https://tools.ietf.org/html/draft-ietf-dnsop-svcb-httpssvc-03) DNS SVCB and HTTPS RRs
69    HTTPS,
70    //  IPSECKEY,   // 45 RFC 4025 IPsec Key
71    /// [RFC 1996](https://tools.ietf.org/html/rfc1996) Incremental Zone Transfer
72    IXFR,
73    //  KX,         // 36 RFC 2230 Key eXchanger record
74    /// [RFC 2535](https://tools.ietf.org/html/rfc2535) and [RFC 2930](https://tools.ietf.org/html/rfc2930) Key record
75    KEY,
76    //  LOC,        // 29 RFC 1876 Location record
77    /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Mail exchange record
78    MX,
79    /// [RFC 3403](https://tools.ietf.org/html/rfc3403) Naming Authority Pointer
80    NAPTR,
81    /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Name server record
82    NS,
83    /// [RFC 4034](https://tools.ietf.org/html/rfc4034) Next-Secure record
84    NSEC,
85    /// [RFC 5155](https://tools.ietf.org/html/rfc5155) NSEC record version 3
86    NSEC3,
87    /// [RFC 5155](https://tools.ietf.org/html/rfc5155) NSEC3 parameters
88    NSEC3PARAM,
89    /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Null server record, for testing
90    NULL,
91    /// [RFC 7929](https://tools.ietf.org/html/rfc7929) OpenPGP public key
92    OPENPGPKEY,
93    /// [RFC 6891](https://tools.ietf.org/html/rfc6891) Option
94    OPT,
95    /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Pointer record
96    PTR,
97    //  RP,         // 17 RFC 1183 Responsible person
98    /// [RFC 4034](https://tools.ietf.org/html/rfc4034) DNSSEC signature: RSASHA256 and RSASHA512, RFC5702
99    RRSIG,
100    /// [RFC 2535](https://tools.ietf.org/html/rfc2535) (and [RFC 2931](https://tools.ietf.org/html/rfc2931)) Signature, to support [RFC 2137](https://tools.ietf.org/html/rfc2137) Update.
101    SIG,
102    /// [RFC 1035](https://tools.ietf.org/html/rfc1035) and [RFC 2308](https://tools.ietf.org/html/rfc2308) Start of [a zone of] authority record
103    SOA,
104    /// [RFC 2782](https://tools.ietf.org/html/rfc2782) Service locator
105    SRV,
106    /// [RFC 4255](https://tools.ietf.org/html/rfc4255) SSH Public Key Fingerprint
107    SSHFP,
108    /// [RFC draft-ietf-dnsop-svcb-https-03](https://tools.ietf.org/html/draft-ietf-dnsop-svcb-httpssvc-03) DNS SVCB and HTTPS RRs
109    SVCB,
110    //  TA,         // 32768 N/A DNSSEC Trust Authorities
111    //  TKEY,       // 249 RFC 2930 Secret key record
112    /// [RFC 6698](https://tools.ietf.org/html/rfc6698) TLSA certificate association
113    TLSA,
114    /// [RFC 8945](https://tools.ietf.org/html/rfc8945) Transaction Signature
115    TSIG,
116    /// [RFC 1035](https://tools.ietf.org/html/rfc1035) Text record
117    TXT,
118    /// Unknown Record type, or unsupported
119    Unknown(u16),
120
121    /// This corresponds to a record type of 0, unspecified
122    ZERO,
123}
124
125impl RecordType {
126    /// Returns true if this is an ANY
127    #[inline]
128    pub fn is_any(self) -> bool {
129        self == Self::ANY
130    }
131
132    /// Returns true if this is a CNAME
133    #[inline]
134    pub fn is_cname(self) -> bool {
135        self == Self::CNAME
136    }
137
138    /// Returns true if this is an NS
139    #[inline]
140    pub fn is_ns(self) -> bool {
141        self == Self::NS
142    }
143
144    /// Returns true if this is an SOA
145    #[inline]
146    pub fn is_soa(self) -> bool {
147        self == Self::SOA
148    }
149
150    /// Returns true if this is an SRV
151    #[inline]
152    pub fn is_srv(self) -> bool {
153        self == Self::SRV
154    }
155
156    /// Returns true if this is an A or an AAAA record
157    #[inline]
158    pub fn is_ip_addr(self) -> bool {
159        matches!(self, Self::A | Self::AAAA)
160    }
161
162    /// Returns true if this is a DNSSEC RecordType
163    #[inline]
164    pub fn is_dnssec(self) -> bool {
165        matches!(
166            self,
167            Self::DNSKEY
168                | Self::CDNSKEY
169                | Self::CDS
170                | Self::DS
171                | Self::KEY
172                | Self::NSEC
173                | Self::NSEC3
174                | Self::NSEC3PARAM
175                | Self::RRSIG
176                | Self::SIG
177                | Self::TSIG
178        )
179    }
180
181    /// Returns true if this is a Zero (unspecified) RecordType
182    #[inline]
183    pub fn is_zero(self) -> bool {
184        self == Self::ZERO
185    }
186}
187
188impl FromStr for RecordType {
189    type Err = ProtoError;
190
191    /// Convert `&str` to `RecordType`
192    ///
193    /// ```
194    /// use std::str::FromStr;
195    /// use trust_dns_proto::rr::record_type::RecordType;
196    ///
197    /// let var: RecordType = RecordType::from_str("A").unwrap();
198    /// assert_eq!(RecordType::A, var);
199    /// ```
200    fn from_str(str: &str) -> ProtoResult<Self> {
201        // TODO missing stuff?
202        debug_assert!(str.chars().all(|x| char::is_digit(x, 36)));
203        match str {
204            "A" => Ok(Self::A),
205            "AAAA" => Ok(Self::AAAA),
206            "ANAME" => Ok(Self::ANAME),
207            "AXFR" => Ok(Self::AXFR),
208            "CAA" => Ok(Self::CAA),
209            "CDNSKEY" => Ok(Self::CDNSKEY),
210            "CDS" => Ok(Self::CDS),
211            "CNAME" => Ok(Self::CNAME),
212            "CSYNC" => Ok(Self::CSYNC),
213            "DNSKEY" => Ok(Self::DNSKEY),
214            "DS" => Ok(Self::DS),
215            "HINFO" => Ok(Self::HINFO),
216            "HTTPS" => Ok(Self::HTTPS),
217            "KEY" => Ok(Self::KEY),
218            "MX" => Ok(Self::MX),
219            "NAPTR" => Ok(Self::NAPTR),
220            "NSEC" => Ok(Self::NSEC),
221            "NSEC3" => Ok(Self::NSEC3),
222            "NSEC3PARAM" => Ok(Self::NSEC3PARAM),
223            "NS" => Ok(Self::NS),
224            "NULL" => Ok(Self::NULL),
225            "OPENPGPKEY" => Ok(Self::OPENPGPKEY),
226            "PTR" => Ok(Self::PTR),
227            "RRSIG" => Ok(Self::RRSIG),
228            "SIG" => Ok(Self::SIG),
229            "SOA" => Ok(Self::SOA),
230            "SRV" => Ok(Self::SRV),
231            "SSHFP" => Ok(Self::SSHFP),
232            "SVCB" => Ok(Self::SVCB),
233            "TLSA" => Ok(Self::TLSA),
234            "TXT" => Ok(Self::TXT),
235            "TSIG" => Ok(Self::TSIG),
236            "ANY" | "*" => Ok(Self::ANY),
237            _ => Err(ProtoErrorKind::UnknownRecordTypeStr(str.to_string()).into()),
238        }
239    }
240}
241
242impl From<u16> for RecordType {
243    /// Convert from `u16` to `RecordType`
244    ///
245    /// ```
246    /// use trust_dns_proto::rr::record_type::RecordType;
247    ///
248    /// let var = RecordType::from(1);
249    /// assert_eq!(RecordType::A, var);
250    /// ```
251    fn from(value: u16) -> Self {
252        match value {
253            1 => Self::A,
254            28 => Self::AAAA,
255            // TODO: wrong value here, see https://github.com/bluejekyll/trust-dns/issues/723
256            65305 => Self::ANAME,
257            255 => Self::ANY,
258            251 => Self::IXFR,
259            252 => Self::AXFR,
260            257 => Self::CAA,
261            59 => Self::CDS,
262            60 => Self::CDNSKEY,
263            5 => Self::CNAME,
264            62 => Self::CSYNC,
265            48 => Self::DNSKEY,
266            43 => Self::DS,
267            13 => Self::HINFO,
268            65 => Self::HTTPS,
269            25 => Self::KEY,
270            15 => Self::MX,
271            35 => Self::NAPTR,
272            2 => Self::NS,
273            47 => Self::NSEC,
274            50 => Self::NSEC3,
275            51 => Self::NSEC3PARAM,
276            10 => Self::NULL,
277            61 => Self::OPENPGPKEY,
278            41 => Self::OPT,
279            12 => Self::PTR,
280            46 => Self::RRSIG,
281            24 => Self::SIG,
282            6 => Self::SOA,
283            33 => Self::SRV,
284            44 => Self::SSHFP,
285            64 => Self::SVCB,
286            52 => Self::TLSA,
287            250 => Self::TSIG,
288            16 => Self::TXT,
289            0 => Self::ZERO,
290            // all unknown record types
291            _ => Self::Unknown(value),
292        }
293    }
294}
295
296impl BinEncodable for RecordType {
297    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
298        encoder.emit_u16((*self).into())
299    }
300}
301
302impl<'r> BinDecodable<'r> for RecordType {
303    fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<Self> {
304        Ok(decoder
305            .read_u16()
306            .map(
307                Restrict::unverified, /*RecordType is safe with any u16*/
308            )
309            .map(Self::from)?)
310    }
311}
312
313// TODO make these a macro...
314
315/// Convert from `RecordType` to `&str`
316///
317/// ```
318/// use std::convert::From;
319/// use trust_dns_proto::rr::record_type::RecordType;
320///
321/// let var: &'static str = From::from(RecordType::A);
322/// assert_eq!("A", var);
323///
324/// let var: &'static str = RecordType::A.into();
325/// assert_eq!("A", var);
326/// ```
327impl From<RecordType> for &'static str {
328    fn from(rt: RecordType) -> &'static str {
329        match rt {
330            RecordType::A => "A",
331            RecordType::AAAA => "AAAA",
332            RecordType::ANAME => "ANAME",
333            RecordType::ANY => "ANY",
334            RecordType::AXFR => "AXFR",
335            RecordType::CAA => "CAA",
336            RecordType::CDNSKEY => "CDNSKEY",
337            RecordType::CDS => "CDS",
338            RecordType::CNAME => "CNAME",
339            RecordType::CSYNC => "CSYNC",
340            RecordType::DNSKEY => "DNSKEY",
341            RecordType::DS => "DS",
342            RecordType::HINFO => "HINFO",
343            RecordType::HTTPS => "HTTPS",
344            RecordType::KEY => "KEY",
345            RecordType::IXFR => "IXFR",
346            RecordType::MX => "MX",
347            RecordType::NAPTR => "NAPTR",
348            RecordType::NS => "NS",
349            RecordType::NSEC => "NSEC",
350            RecordType::NSEC3 => "NSEC3",
351            RecordType::NSEC3PARAM => "NSEC3PARAM",
352            RecordType::NULL => "NULL",
353            RecordType::OPENPGPKEY => "OPENPGPKEY",
354            RecordType::OPT => "OPT",
355            RecordType::PTR => "PTR",
356            RecordType::RRSIG => "RRSIG",
357            RecordType::SIG => "SIG",
358            RecordType::SOA => "SOA",
359            RecordType::SRV => "SRV",
360            RecordType::SSHFP => "SSHFP",
361            RecordType::SVCB => "SVCB",
362            RecordType::TLSA => "TLSA",
363            RecordType::TSIG => "TSIG",
364            RecordType::TXT => "TXT",
365            RecordType::ZERO => "ZERO",
366            RecordType::Unknown(_) => "Unknown",
367        }
368    }
369}
370
371/// Convert from `RecordType` to `u16`
372///
373/// ```
374/// use std::convert::From;
375/// use trust_dns_proto::rr::record_type::RecordType;
376///
377/// let var: u16 = RecordType::A.into();
378/// assert_eq!(1, var);
379/// ```
380impl From<RecordType> for u16 {
381    fn from(rt: RecordType) -> Self {
382        match rt {
383            RecordType::A => 1,
384            RecordType::AAAA => 28,
385            // TODO: wrong value here, see https://github.com/bluejekyll/trust-dns/issues/723
386            RecordType::ANAME => 65305,
387            RecordType::ANY => 255,
388            RecordType::AXFR => 252,
389            RecordType::CAA => 257,
390            RecordType::CDNSKEY => 60,
391            RecordType::CDS => 59,
392            RecordType::CNAME => 5,
393            RecordType::CSYNC => 62,
394            RecordType::DNSKEY => 48,
395            RecordType::DS => 43,
396            RecordType::HINFO => 13,
397            RecordType::HTTPS => 65,
398            RecordType::KEY => 25,
399            RecordType::IXFR => 251,
400            RecordType::MX => 15,
401            RecordType::NAPTR => 35,
402            RecordType::NS => 2,
403            RecordType::NSEC => 47,
404            RecordType::NSEC3 => 50,
405            RecordType::NSEC3PARAM => 51,
406            RecordType::NULL => 10,
407            RecordType::OPENPGPKEY => 61,
408            RecordType::OPT => 41,
409            RecordType::PTR => 12,
410            RecordType::RRSIG => 46,
411            RecordType::SIG => 24,
412            RecordType::SOA => 6,
413            RecordType::SRV => 33,
414            RecordType::SSHFP => 44,
415            RecordType::SVCB => 64,
416            RecordType::TLSA => 52,
417            RecordType::TSIG => 250,
418            RecordType::TXT => 16,
419            RecordType::ZERO => 0,
420            RecordType::Unknown(code) => code,
421        }
422    }
423}
424
425/// [Canonical DNS Name Order](https://tools.ietf.org/html/rfc4034#section-6)
426impl PartialOrd<Self> for RecordType {
427    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
428        Some(self.cmp(other))
429    }
430}
431
432/// [Canonical DNS Name Order](https://tools.ietf.org/html/rfc4034#section-6)
433impl Ord for RecordType {
434    fn cmp(&self, other: &Self) -> Ordering {
435        u16::from(*self).cmp(&u16::from(*other))
436    }
437}
438
439impl Display for RecordType {
440    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
441        f.write_str(Into::<&str>::into(*self))
442    }
443}
444
445#[cfg(test)]
446mod tests {
447    #![allow(clippy::dbg_macro, clippy::print_stdout)]
448
449    use super::*;
450
451    #[test]
452    fn test_order() {
453        let ordered = vec![
454            RecordType::A,
455            RecordType::NS,
456            RecordType::CNAME,
457            RecordType::SOA,
458            RecordType::NULL,
459            RecordType::PTR,
460            RecordType::HINFO,
461            RecordType::MX,
462            RecordType::TXT,
463            RecordType::AAAA,
464            RecordType::SRV,
465            RecordType::CSYNC,
466            RecordType::AXFR,
467            RecordType::ANY,
468        ];
469
470        let mut unordered = vec![
471            RecordType::ANY,
472            RecordType::NULL,
473            RecordType::AXFR,
474            RecordType::A,
475            RecordType::NS,
476            RecordType::SOA,
477            RecordType::SRV,
478            RecordType::PTR,
479            RecordType::MX,
480            RecordType::CNAME,
481            RecordType::TXT,
482            RecordType::AAAA,
483            RecordType::HINFO,
484            RecordType::CSYNC,
485        ];
486
487        unordered.sort();
488
489        for rtype in unordered.clone() {
490            println!("u16 for {:?}: {}", rtype, u16::from(rtype));
491        }
492
493        assert_eq!(ordered, unordered);
494    }
495
496    /// Check that all record type names parse into unique `RecordType` instances,
497    /// and can be converted back into the same name.
498    #[test]
499    fn test_record_type_parse() {
500        let record_names = &[
501            "A",
502            "AAAA",
503            "ANAME",
504            "CAA",
505            "CNAME",
506            "CSYNC",
507            "HINFO",
508            "NULL",
509            "MX",
510            "NAPTR",
511            "NS",
512            "OPENPGPKEY",
513            "PTR",
514            "SOA",
515            "SRV",
516            "SSHFP",
517            "TLSA",
518            "TXT",
519            "ANY",
520            "AXFR",
521        ];
522
523        #[cfg(feature = "dnssec")]
524        let dnssec_record_names = &[
525            "CDNSKEY",
526            "CDS",
527            "DNSKEY",
528            "DS",
529            "KEY",
530            "NSEC",
531            "NSEC3",
532            "NSEC3PARAM",
533            "RRSIG",
534            "SIG",
535            "TSIG",
536        ];
537        #[cfg(not(feature = "dnssec"))]
538        let dnssec_record_names = &[];
539
540        let mut rtypes = std::collections::HashSet::new();
541        for name in record_names.iter().chain(dnssec_record_names) {
542            let rtype: RecordType = name.parse().unwrap();
543            assert_eq!(rtype.to_string().to_ascii_uppercase().as_str(), *name);
544            assert!(rtypes.insert(rtype));
545        }
546    }
547}