//! Types and constants for DNS CLASSes use bytes::Bytes; use errors::*; use ser::packet::{DnsPacketData, DnsPacketWriteContext}; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field}; use std::fmt; use std::io::Cursor; use std::borrow::Cow; /// CLASS Internet pub const IN : Class = Class(KnownClass::IN as u16); /// CLASS "Chaos" pub const CH : Class = Class(KnownClass::CH as u16); /// CLASS "Hesiod" pub const HS : Class = Class(KnownClass::HS as u16); /// QCLASS NONE pub const NONE : Class = Class(KnownQClass::NONE as u16); /// QCLASS "*" (ANY) pub const ANY : Class = Class(KnownQClass::ANY as u16); #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[repr(u16)] #[allow(non_camel_case_types)] pub enum KnownClass { // try to list "original" rfc /// CLASS Internet IN = 0x0001, // RFC 1035 // CS = 0x0002, // "CSNET" (not just obsolete; unassigned in the IANA registry) /// CLASS "Chaos" CH = 0x0003, // "Chaos" /// CLASS "Hesiod" HS = 0x0004, // "Hesiod" } #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] #[repr(u16)] #[allow(non_camel_case_types)] pub enum KnownQClass { // try to list "original" rfc /// QCLASS NONE NONE = 0x00fe, // RFC 2136 /// QCLASS "*" (ANY) ANY = 0x00ff, // RFC 1035 } /// DNS CLASS /// /// Originally QCLASS was a superset of CLASS; RFC 6895 now defines: /// /// > There are currently two subcategories of DNS CLASSes: normal, /// > data-containing classes; and QCLASSes that are only meaningful in /// > queries or updates. /// /// ## `ANY` /// /// QTYPE 255 either (rules from RFC 6895): /// /// - doesn't have a mnemonic, violating the existence rule /// - has "*" as mnemonic, violating the formatting rule /// - has "ANY" as mnemonic, violating the uniquess rule (class ANY) /// /// Solution: use "ANY" for type 255, don't accept "ANY" as class in /// places a type could be given too: `*_without_any`. /// /// The QCLASS `ANY` is mostly useless anyway and shouldn't be used in /// normal queries. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct Class(pub u16); impl Class { /// map to `KnownQClass` pub fn known_qclass(self) -> Option { Some(match self { NONE => KnownQClass::NONE, ANY => KnownQClass::ANY, _ => return None, }) } /// whether class is known to be a QCLASS pub fn is_known_qclass(self) -> bool { self.known_qclass().is_some() } /// name for CLASS if known /// /// Known CLASSes are represented using their known mnemonic, others /// return None. pub fn known_name(self) -> Option<&'static str> { Some(match self { IN => "IN", CH => "CH", HS => "HS", NONE => "NONE", ANY => "ANY", _ => return None, }) } /// name for CLASS /// /// Known CLASSes are represented using their known mnemonic, others /// using the "CLASS..." syntax (RFC 3597). pub fn name(self) -> Cow<'static, str> { match self.known_name() { Some(n) => Cow::Borrowed(n), None => Cow::Owned(self.generic_name()), } } /// similar to `name`, but returns "CLASS255" for the `ANY` class pub fn name_without_any(self) -> Cow<'static, str> { match self { ANY => Cow::Borrowed("CLASS255"), _ => self.name(), } } /// generic name "CLASS..." pub fn generic_name(self) -> String { let mut result = String::new(); self.write_generic_name(&mut result).unwrap(); result } /// directly write generic name "CLASS..." to some target pub fn write_generic_name(self, w: &mut W) -> fmt::Result { write!(w, "CLASS{}", self.0) } /// only parse known names (mnemonics), but not `ANY` /// /// Avoids conflict with parsing RRTYPE mnemonics. pub fn from_known_name_without_any(name: &str) -> Option { if name.eq_ignore_ascii_case("IN") { return Some(IN); } if name.eq_ignore_ascii_case("CH") { return Some(CH); } if name.eq_ignore_ascii_case("HS") { return Some(HS); } if name.eq_ignore_ascii_case("NONE") { return Some(NONE); } None } /// parses known names (mnemonics) pub fn from_known_name(name: &str) -> Option { Self::from_known_name_without_any(name).or_else(|| { if name.eq_ignore_ascii_case("ANY") { return Some(ANY); } None }) } /// parses generic names of the form "CLASS..." pub fn from_generic_name(name: &str) -> Option { if name.len() > 5 && name.as_bytes()[0..5].eq_ignore_ascii_case(b"CLASS") { name[5..].parse::().ok().map(Class) } else { None } } /// parses any name (mnemonics and "CLASS..."), but not `ANY` /// ("CLASS255" works though) pub fn from_name_without_any(name: &str) -> Option { Self::from_generic_name(name).or_else(|| { Self::from_known_name_without_any(name) }) } /// parses any name (mnemonics and "CLASS...") pub fn from_name(name: &str) -> Option { Self::from_generic_name(name).or_else(|| { Self::from_known_name(name) }) } } impl From for Class { fn from(value: KnownClass) -> Self { Class(value as u16) } } impl From for Class { fn from(value: KnownQClass) -> Self { Class(value as u16) } } impl fmt::Display for Class { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.known_name() { Some(n) => write!(f, "{}", n), None => self.write_generic_name(f), } } } impl DnsPacketData for Class { fn deserialize(data: &mut Cursor) -> Result { Ok(Class(DnsPacketData::deserialize(data)?)) } fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec) -> Result<()> { self.0.serialize(context, packet) } } impl DnsTextData for Class { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> Result { let field = next_field(data)?; Class::from_name(field).ok_or_else(|| format_err!("unknown CLASS {:?}", field)) } fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { write!(f, "{}", self) } }