217 lines
5.8 KiB
Rust
217 lines
5.8 KiB
Rust
//! Types and constants for DNS CLASSes
|
|
|
|
use bytes::Bytes;
|
|
use crate::errors::*;
|
|
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
|
use crate::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<KnownQClass> {
|
|
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<W: fmt::Write>(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<Self> {
|
|
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> {
|
|
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<Self> {
|
|
if name.len() > 5 && name.as_bytes()[0..5].eq_ignore_ascii_case(b"CLASS") {
|
|
name[5..].parse::<u16>().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> {
|
|
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> {
|
|
Self::from_generic_name(name).or_else(|| {
|
|
Self::from_known_name(name)
|
|
})
|
|
}
|
|
}
|
|
|
|
impl From<KnownClass> for Class {
|
|
fn from(value: KnownClass) -> Self {
|
|
Class(value as u16)
|
|
}
|
|
}
|
|
|
|
impl From<KnownQClass> 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<Bytes>) -> Result<Self> {
|
|
Ok(Class(DnsPacketData::deserialize(data)?))
|
|
}
|
|
|
|
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
|
self.0.serialize(context, packet)
|
|
}
|
|
}
|
|
|
|
impl DnsTextData for Class {
|
|
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> Result<Self> {
|
|
let field = next_field(data)?;
|
|
Class::from_name(field).ok_or_else(|| failure::format_err!("unknown CLASS {:?}", field))
|
|
}
|
|
|
|
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
|
write!(f, "{}", self)
|
|
}
|
|
}
|