use std::cmp::Ordering;
use std::convert::From;
use std::fmt;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
use crate::error::*;
use crate::serialize::binary::*;
#[cfg(feature = "dnssec")]
use crate::rr::dnssec::rdata::DNSSECRecordType;
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
#[allow(dead_code)]
pub enum RecordType {
A,
AAAA,
ANAME,
ANY,
AXFR,
CAA,
CNAME,
IXFR,
MX,
NAPTR,
NS,
NULL,
OPENPGPKEY,
OPT,
PTR,
SOA,
SRV,
SSHFP,
TLSA,
TXT,
#[cfg(feature = "dnssec")]
DNSSEC(DNSSECRecordType),
Unknown(u16),
ZERO,
}
impl RecordType {
#[inline]
pub fn is_any(self) -> bool {
self == RecordType::ANY
}
#[inline]
pub fn is_cname(self) -> bool {
self == RecordType::CNAME
}
#[inline]
pub fn is_srv(self) -> bool {
self == RecordType::SRV
}
#[inline]
pub fn is_ip_addr(self) -> bool {
match self {
RecordType::A | RecordType::AAAA => true,
_ => false,
}
}
}
impl FromStr for RecordType {
type Err = ProtoError;
fn from_str(str: &str) -> ProtoResult<Self> {
match str {
"A" => Ok(RecordType::A),
"AAAA" => Ok(RecordType::AAAA),
"ANAME" => Ok(RecordType::ANAME),
"CAA" => Ok(RecordType::CAA),
"CNAME" => Ok(RecordType::CNAME),
"NULL" => Ok(RecordType::NULL),
"MX" => Ok(RecordType::MX),
"NAPTR" => Ok(RecordType::NAPTR),
"NS" => Ok(RecordType::NS),
"OPENPGPKEY" => Ok(RecordType::OPENPGPKEY),
"PTR" => Ok(RecordType::PTR),
"SOA" => Ok(RecordType::SOA),
"SRV" => Ok(RecordType::SRV),
"SSHFP" => Ok(RecordType::SSHFP),
"TLSA" => Ok(RecordType::TLSA),
"TXT" => Ok(RecordType::TXT),
"ANY" | "*" => Ok(RecordType::ANY),
"AXFR" => Ok(RecordType::AXFR),
#[cfg(feature = "dnssec")]
"DNSKEY" | "DS" | "KEY" | "NSEC" | "NSEC3" | "NSEC3PARAM" | "RRSIG" | "SIG" => {
Ok(RecordType::DNSSEC(str.parse()?))
}
_ => Err(ProtoErrorKind::UnknownRecordTypeStr(str.to_string()).into()),
}
}
}
impl From<u16> for RecordType {
fn from(value: u16) -> Self {
match value {
1 => RecordType::A,
28 => RecordType::AAAA,
65305 => RecordType::ANAME,
255 => RecordType::ANY,
252 => RecordType::AXFR,
257 => RecordType::CAA,
5 => RecordType::CNAME,
0 => RecordType::ZERO,
15 => RecordType::MX,
35 => RecordType::NAPTR,
2 => RecordType::NS,
10 => RecordType::NULL,
61 => RecordType::OPENPGPKEY,
41 => RecordType::OPT,
12 => RecordType::PTR,
6 => RecordType::SOA,
33 => RecordType::SRV,
44 => RecordType::SSHFP,
52 => RecordType::TLSA,
16 => RecordType::TXT,
#[cfg(feature = "dnssec")]
48 |
43 |
25 |
47|
50|
51|
46|
24 => RecordType::DNSSEC(DNSSECRecordType::from(value)),
_ => RecordType::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> {
decoder
.read_u16()
.map(
Restrict::unverified,
)
.map(Self::from)
}
}
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::CNAME => "CNAME",
RecordType::ZERO => "",
RecordType::IXFR => "IXFR",
RecordType::MX => "MX",
RecordType::NAPTR => "NAPTR",
RecordType::NS => "NS",
RecordType::NULL => "NULL",
RecordType::OPENPGPKEY => "OPENPGPKEY",
RecordType::OPT => "OPT",
RecordType::PTR => "PTR",
RecordType::SOA => "SOA",
RecordType::SRV => "SRV",
RecordType::SSHFP => "SSHFP",
RecordType::TLSA => "TLSA",
RecordType::TXT => "TXT",
#[cfg(feature = "dnssec")]
RecordType::DNSSEC(rt) => rt.into(),
RecordType::Unknown(_) => "Unknown",
}
}
}
impl From<RecordType> for u16 {
fn from(rt: RecordType) -> Self {
match rt {
RecordType::A => 1,
RecordType::AAAA => 28,
RecordType::ANAME => 65305,
RecordType::ANY => 255,
RecordType::AXFR => 252,
RecordType::CAA => 257,
RecordType::CNAME => 5,
RecordType::ZERO => 0,
RecordType::IXFR => 251,
RecordType::MX => 15,
RecordType::NAPTR => 35,
RecordType::NS => 2,
RecordType::NULL => 10,
RecordType::OPENPGPKEY => 61,
RecordType::OPT => 41,
RecordType::PTR => 12,
RecordType::SOA => 6,
RecordType::SRV => 33,
RecordType::SSHFP => 44,
RecordType::TLSA => 52,
RecordType::TXT => 16,
#[cfg(feature = "dnssec")]
RecordType::DNSSEC(rt) => rt.into(),
RecordType::Unknown(code) => code,
}
}
}
impl PartialOrd<RecordType> for RecordType {
fn partial_cmp(&self, other: &RecordType) -> Option<Ordering> {
Some(self.cmp(other))
}
}
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::MX,
RecordType::TXT,
RecordType::AAAA,
RecordType::SRV,
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,
];
unordered.sort();
for rtype in unordered.clone() {
println!("u16 for {:?}: {}", rtype, u16::from(rtype));
}
assert_eq!(ordered, unordered);
}
#[test]
fn test_record_type_parse() {
let record_names = &[
"A",
"AAAA",
"ANAME",
"CAA",
"CNAME",
"NULL",
"MX",
"NAPTR",
"NS",
"OPENPGPKEY",
"PTR",
"SOA",
"SRV",
"SSHFP",
"TLSA",
"TXT",
"ANY",
"AXFR",
];
#[cfg(feature = "dnssec")]
let dnssec_record_names = &[
"DNSKEY",
"DS",
"KEY",
"NSEC",
"NSEC3",
"NSEC3PARAM",
"RRSIG",
"SIG",
];
#[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().as_str(), *name);
assert!(rtypes.insert(rtype));
}
}
}