rust-dnsbox/lib/dnsbox-base/src/common_types/classes.rs

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)
}
}