blob: a020355073acbe387813b433dc6c5452627716a0 [file] [log] [blame]
// Copyright 2015-2021 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! record type definitions
#![allow(clippy::use_self)]
use std::cmp::Ordering;
use std::convert::From;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
#[cfg(feature = "serde-config")]
use serde::{Deserialize, Serialize};
use crate::error::*;
use crate::serialize::binary::*;
// TODO: adopt proper restrictions on usage: https://tools.ietf.org/html/rfc6895 section 3.1
// add the data TYPEs, QTYPEs, and Meta-TYPEs
//
/// The type of the resource record.
///
/// This specifies the type of data in the RData field of the Resource Record
#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
#[allow(dead_code)]
#[non_exhaustive]
pub enum RecordType {
/// [RFC 1035](https://tools.ietf.org/html/rfc1035) IPv4 Address record
A,
/// [RFC 3596](https://tools.ietf.org/html/rfc3596) IPv6 address record
AAAA,
/// [ANAME draft-ietf-dnsop-aname](https://tools.ietf.org/html/draft-ietf-dnsop-aname-04)
ANAME,
// AFSDB, // 18 RFC 1183 AFS database record
/// [RFC 1035](https://tools.ietf.org/html/rfc1035) All cached records, aka ANY
ANY,
// APL, // 42 RFC 3123 Address Prefix List
/// [RFC 1035](https://tools.ietf.org/html/rfc1035) Authoritative Zone Transfer
AXFR,
/// [RFC 6844](https://tools.ietf.org/html/rfc6844) Certification Authority Authorization
CAA,
/// [RFC 7344](https://tools.ietf.org/html/rfc7344) Child DS
CDS,
/// [RFC 7344](https://tools.ietf.org/html/rfc7344) Child DNSKEY
CDNSKEY,
// CERT, // 37 RFC 4398 Certificate record
/// [RFC 1035](https://tools.ietf.org/html/rfc1035) Canonical name record
CNAME,
// DHCID, // 49 RFC 4701 DHCP identifier
// DLV, // 32769 RFC 4431 DNSSEC Lookaside Validation record
// DNAME, // 39 RFC 2672 Delegation Name
/// [RFC 7477](https://tools.ietf.org/html/rfc4034) Child-to-parent synchronization record
CSYNC,
/// [RFC 4034](https://tools.ietf.org/html/rfc4034) DNS Key record: RSASHA256 and RSASHA512, RFC5702
DNSKEY,
/// [RFC 4034](https://tools.ietf.org/html/rfc4034) Delegation signer: RSASHA256 and RSASHA512, RFC5702
DS,
/// [RFC 1035](https://tools.ietf.org/html/rfc1035) host information
HINFO,
// HIP, // 55 RFC 5205 Host Identity Protocol
/// [RFC draft-ietf-dnsop-svcb-https-03](https://tools.ietf.org/html/draft-ietf-dnsop-svcb-httpssvc-03) DNS SVCB and HTTPS RRs
HTTPS,
// IPSECKEY, // 45 RFC 4025 IPsec Key
/// [RFC 1996](https://tools.ietf.org/html/rfc1996) Incremental Zone Transfer
IXFR,
// KX, // 36 RFC 2230 Key eXchanger record
/// [RFC 2535](https://tools.ietf.org/html/rfc2535) and [RFC 2930](https://tools.ietf.org/html/rfc2930) Key record
KEY,
// LOC, // 29 RFC 1876 Location record
/// [RFC 1035](https://tools.ietf.org/html/rfc1035) Mail exchange record
MX,
/// [RFC 3403](https://tools.ietf.org/html/rfc3403) Naming Authority Pointer
NAPTR,
/// [RFC 1035](https://tools.ietf.org/html/rfc1035) Name server record
NS,
/// [RFC 4034](https://tools.ietf.org/html/rfc4034) Next-Secure record
NSEC,
/// [RFC 5155](https://tools.ietf.org/html/rfc5155) NSEC record version 3
NSEC3,
/// [RFC 5155](https://tools.ietf.org/html/rfc5155) NSEC3 parameters
NSEC3PARAM,
/// [RFC 1035](https://tools.ietf.org/html/rfc1035) Null server record, for testing
NULL,
/// [RFC 7929](https://tools.ietf.org/html/rfc7929) OpenPGP public key
OPENPGPKEY,
/// [RFC 6891](https://tools.ietf.org/html/rfc6891) Option
OPT,
/// [RFC 1035](https://tools.ietf.org/html/rfc1035) Pointer record
PTR,
// RP, // 17 RFC 1183 Responsible person
/// [RFC 4034](https://tools.ietf.org/html/rfc4034) DNSSEC signature: RSASHA256 and RSASHA512, RFC5702
RRSIG,
/// [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.
SIG,
/// [RFC 1035](https://tools.ietf.org/html/rfc1035) and [RFC 2308](https://tools.ietf.org/html/rfc2308) Start of [a zone of] authority record
SOA,
/// [RFC 2782](https://tools.ietf.org/html/rfc2782) Service locator
SRV,
/// [RFC 4255](https://tools.ietf.org/html/rfc4255) SSH Public Key Fingerprint
SSHFP,
/// [RFC draft-ietf-dnsop-svcb-https-03](https://tools.ietf.org/html/draft-ietf-dnsop-svcb-httpssvc-03) DNS SVCB and HTTPS RRs
SVCB,
// TA, // 32768 N/A DNSSEC Trust Authorities
// TKEY, // 249 RFC 2930 Secret key record
/// [RFC 6698](https://tools.ietf.org/html/rfc6698) TLSA certificate association
TLSA,
/// [RFC 8945](https://tools.ietf.org/html/rfc8945) Transaction Signature
TSIG,
/// [RFC 1035](https://tools.ietf.org/html/rfc1035) Text record
TXT,
/// Unknown Record type, or unsupported
Unknown(u16),
/// This corresponds to a record type of 0, unspecified
ZERO,
}
impl RecordType {
/// Returns true if this is an ANY
#[inline]
pub fn is_any(self) -> bool {
self == Self::ANY
}
/// Returns true if this is a CNAME
#[inline]
pub fn is_cname(self) -> bool {
self == Self::CNAME
}
/// Returns true if this is an NS
#[inline]
pub fn is_ns(self) -> bool {
self == Self::NS
}
/// Returns true if this is an SOA
#[inline]
pub fn is_soa(self) -> bool {
self == Self::SOA
}
/// Returns true if this is an SRV
#[inline]
pub fn is_srv(self) -> bool {
self == Self::SRV
}
/// Returns true if this is an A or an AAAA record
#[inline]
pub fn is_ip_addr(self) -> bool {
matches!(self, Self::A | Self::AAAA)
}
/// Returns true if this is a DNSSEC RecordType
#[inline]
pub fn is_dnssec(self) -> bool {
matches!(
self,
Self::DNSKEY
| Self::CDNSKEY
| Self::CDS
| Self::DS
| Self::KEY
| Self::NSEC
| Self::NSEC3
| Self::NSEC3PARAM
| Self::RRSIG
| Self::SIG
| Self::TSIG
)
}
/// Returns true if this is a Zero (unspecified) RecordType
#[inline]
pub fn is_zero(self) -> bool {
self == Self::ZERO
}
}
impl FromStr for RecordType {
type Err = ProtoError;
/// Convert `&str` to `RecordType`
///
/// ```
/// use std::str::FromStr;
/// use trust_dns_proto::rr::record_type::RecordType;
///
/// let var: RecordType = RecordType::from_str("A").unwrap();
/// assert_eq!(RecordType::A, var);
/// ```
fn from_str(str: &str) -> ProtoResult<Self> {
// TODO missing stuff?
debug_assert!(str.chars().all(|x| char::is_digit(x, 36)));
match str {
"A" => Ok(Self::A),
"AAAA" => Ok(Self::AAAA),
"ANAME" => Ok(Self::ANAME),
"AXFR" => Ok(Self::AXFR),
"CAA" => Ok(Self::CAA),
"CDNSKEY" => Ok(Self::CDNSKEY),
"CDS" => Ok(Self::CDS),
"CNAME" => Ok(Self::CNAME),
"CSYNC" => Ok(Self::CSYNC),
"DNSKEY" => Ok(Self::DNSKEY),
"DS" => Ok(Self::DS),
"HINFO" => Ok(Self::HINFO),
"HTTPS" => Ok(Self::HTTPS),
"KEY" => Ok(Self::KEY),
"MX" => Ok(Self::MX),
"NAPTR" => Ok(Self::NAPTR),
"NSEC" => Ok(Self::NSEC),
"NSEC3" => Ok(Self::NSEC3),
"NSEC3PARAM" => Ok(Self::NSEC3PARAM),
"NS" => Ok(Self::NS),
"NULL" => Ok(Self::NULL),
"OPENPGPKEY" => Ok(Self::OPENPGPKEY),
"PTR" => Ok(Self::PTR),
"RRSIG" => Ok(Self::RRSIG),
"SIG" => Ok(Self::SIG),
"SOA" => Ok(Self::SOA),
"SRV" => Ok(Self::SRV),
"SSHFP" => Ok(Self::SSHFP),
"SVCB" => Ok(Self::SVCB),
"TLSA" => Ok(Self::TLSA),
"TXT" => Ok(Self::TXT),
"TSIG" => Ok(Self::TSIG),
"ANY" | "*" => Ok(Self::ANY),
_ => Err(ProtoErrorKind::UnknownRecordTypeStr(str.to_string()).into()),
}
}
}
impl From<u16> for RecordType {
/// Convert from `u16` to `RecordType`
///
/// ```
/// use trust_dns_proto::rr::record_type::RecordType;
///
/// let var = RecordType::from(1);
/// assert_eq!(RecordType::A, var);
/// ```
fn from(value: u16) -> Self {
match value {
1 => Self::A,
28 => Self::AAAA,
// TODO: wrong value here, see https://github.com/bluejekyll/trust-dns/issues/723
65305 => Self::ANAME,
255 => Self::ANY,
251 => Self::IXFR,
252 => Self::AXFR,
257 => Self::CAA,
59 => Self::CDS,
60 => Self::CDNSKEY,
5 => Self::CNAME,
62 => Self::CSYNC,
48 => Self::DNSKEY,
43 => Self::DS,
13 => Self::HINFO,
65 => Self::HTTPS,
25 => Self::KEY,
15 => Self::MX,
35 => Self::NAPTR,
2 => Self::NS,
47 => Self::NSEC,
50 => Self::NSEC3,
51 => Self::NSEC3PARAM,
10 => Self::NULL,
61 => Self::OPENPGPKEY,
41 => Self::OPT,
12 => Self::PTR,
46 => Self::RRSIG,
24 => Self::SIG,
6 => Self::SOA,
33 => Self::SRV,
44 => Self::SSHFP,
64 => Self::SVCB,
52 => Self::TLSA,
250 => Self::TSIG,
16 => Self::TXT,
0 => Self::ZERO,
// all unknown record types
_ => Self::Unknown(value),
}
}
}
impl BinEncodable for RecordType {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
encoder.emit_u16((*self).into())
}
}
impl<'r> BinDecodable<'r> for RecordType {
fn read(decoder: &mut BinDecoder<'_>) -> ProtoResult<Self> {
Ok(decoder
.read_u16()
.map(
Restrict::unverified, /*RecordType is safe with any u16*/
)
.map(Self::from)?)
}
}
// TODO make these a macro...
/// Convert from `RecordType` to `&str`
///
/// ```
/// use std::convert::From;
/// use trust_dns_proto::rr::record_type::RecordType;
///
/// let var: &'static str = From::from(RecordType::A);
/// assert_eq!("A", var);
///
/// let var: &'static str = RecordType::A.into();
/// assert_eq!("A", var);
/// ```
impl From<RecordType> for &'static str {
fn from(rt: RecordType) -> &'static str {
match rt {
RecordType::A => "A",
RecordType::AAAA => "AAAA",
RecordType::ANAME => "ANAME",
RecordType::ANY => "ANY",
RecordType::AXFR => "AXFR",
RecordType::CAA => "CAA",
RecordType::CDNSKEY => "CDNSKEY",
RecordType::CDS => "CDS",
RecordType::CNAME => "CNAME",
RecordType::CSYNC => "CSYNC",
RecordType::DNSKEY => "DNSKEY",
RecordType::DS => "DS",
RecordType::HINFO => "HINFO",
RecordType::HTTPS => "HTTPS",
RecordType::KEY => "KEY",
RecordType::IXFR => "IXFR",
RecordType::MX => "MX",
RecordType::NAPTR => "NAPTR",
RecordType::NS => "NS",
RecordType::NSEC => "NSEC",
RecordType::NSEC3 => "NSEC3",
RecordType::NSEC3PARAM => "NSEC3PARAM",
RecordType::NULL => "NULL",
RecordType::OPENPGPKEY => "OPENPGPKEY",
RecordType::OPT => "OPT",
RecordType::PTR => "PTR",
RecordType::RRSIG => "RRSIG",
RecordType::SIG => "SIG",
RecordType::SOA => "SOA",
RecordType::SRV => "SRV",
RecordType::SSHFP => "SSHFP",
RecordType::SVCB => "SVCB",
RecordType::TLSA => "TLSA",
RecordType::TSIG => "TSIG",
RecordType::TXT => "TXT",
RecordType::ZERO => "ZERO",
RecordType::Unknown(_) => "Unknown",
}
}
}
/// Convert from `RecordType` to `u16`
///
/// ```
/// use std::convert::From;
/// use trust_dns_proto::rr::record_type::RecordType;
///
/// let var: u16 = RecordType::A.into();
/// assert_eq!(1, var);
/// ```
impl From<RecordType> for u16 {
fn from(rt: RecordType) -> Self {
match rt {
RecordType::A => 1,
RecordType::AAAA => 28,
// TODO: wrong value here, see https://github.com/bluejekyll/trust-dns/issues/723
RecordType::ANAME => 65305,
RecordType::ANY => 255,
RecordType::AXFR => 252,
RecordType::CAA => 257,
RecordType::CDNSKEY => 60,
RecordType::CDS => 59,
RecordType::CNAME => 5,
RecordType::CSYNC => 62,
RecordType::DNSKEY => 48,
RecordType::DS => 43,
RecordType::HINFO => 13,
RecordType::HTTPS => 65,
RecordType::KEY => 25,
RecordType::IXFR => 251,
RecordType::MX => 15,
RecordType::NAPTR => 35,
RecordType::NS => 2,
RecordType::NSEC => 47,
RecordType::NSEC3 => 50,
RecordType::NSEC3PARAM => 51,
RecordType::NULL => 10,
RecordType::OPENPGPKEY => 61,
RecordType::OPT => 41,
RecordType::PTR => 12,
RecordType::RRSIG => 46,
RecordType::SIG => 24,
RecordType::SOA => 6,
RecordType::SRV => 33,
RecordType::SSHFP => 44,
RecordType::SVCB => 64,
RecordType::TLSA => 52,
RecordType::TSIG => 250,
RecordType::TXT => 16,
RecordType::ZERO => 0,
RecordType::Unknown(code) => code,
}
}
}
/// [Canonical DNS Name Order](https://tools.ietf.org/html/rfc4034#section-6)
impl PartialOrd<Self> for RecordType {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
/// [Canonical DNS Name Order](https://tools.ietf.org/html/rfc4034#section-6)
impl Ord for RecordType {
fn cmp(&self, other: &Self) -> Ordering {
u16::from(*self).cmp(&u16::from(*other))
}
}
impl Display for RecordType {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str(Into::<&str>::into(*self))
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]
use super::*;
#[test]
fn test_order() {
let ordered = vec![
RecordType::A,
RecordType::NS,
RecordType::CNAME,
RecordType::SOA,
RecordType::NULL,
RecordType::PTR,
RecordType::HINFO,
RecordType::MX,
RecordType::TXT,
RecordType::AAAA,
RecordType::SRV,
RecordType::CSYNC,
RecordType::AXFR,
RecordType::ANY,
];
let mut unordered = vec![
RecordType::ANY,
RecordType::NULL,
RecordType::AXFR,
RecordType::A,
RecordType::NS,
RecordType::SOA,
RecordType::SRV,
RecordType::PTR,
RecordType::MX,
RecordType::CNAME,
RecordType::TXT,
RecordType::AAAA,
RecordType::HINFO,
RecordType::CSYNC,
];
unordered.sort();
for rtype in unordered.clone() {
println!("u16 for {:?}: {}", rtype, u16::from(rtype));
}
assert_eq!(ordered, unordered);
}
/// Check that all record type names parse into unique `RecordType` instances,
/// and can be converted back into the same name.
#[test]
fn test_record_type_parse() {
let record_names = &[
"A",
"AAAA",
"ANAME",
"CAA",
"CNAME",
"CSYNC",
"HINFO",
"NULL",
"MX",
"NAPTR",
"NS",
"OPENPGPKEY",
"PTR",
"SOA",
"SRV",
"SSHFP",
"TLSA",
"TXT",
"ANY",
"AXFR",
];
#[cfg(feature = "dnssec")]
let dnssec_record_names = &[
"CDNSKEY",
"CDS",
"DNSKEY",
"DS",
"KEY",
"NSEC",
"NSEC3",
"NSEC3PARAM",
"RRSIG",
"SIG",
"TSIG",
];
#[cfg(not(feature = "dnssec"))]
let dnssec_record_names = &[];
let mut rtypes = std::collections::HashSet::new();
for name in record_names.iter().chain(dnssec_record_names) {
let rtype: RecordType = name.parse().unwrap();
assert_eq!(rtype.to_string().to_ascii_uppercase().as_str(), *name);
assert!(rtypes.insert(rtype));
}
}
}