From e0f597ba9c684e9e2587858f8425d9be9744ed30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Tue, 26 Dec 2017 17:30:08 +0100 Subject: [PATCH] step --- lib/dnsbox-base/src/common_types/classes.rs | 13 + lib/dnsbox-base/src/common_types/mod.rs | 3 +- .../src/common_types/rr_type/mod.rs | 99 ------- lib/dnsbox-base/src/common_types/types.rs | 249 ++++++++++++++++-- lib/dnsbox-base/src/records/registry.rs | 84 ++++-- lib/dnsbox-base/src/records/structs.rs | 165 ++++++++---- lib/dnsbox-base/src/records/tests.rs | 3 +- lib/dnsbox-base/src/records/unknown.rs | 16 +- lib/dnsbox-base/src/records/weird_structs.rs | 4 +- lib/dnsbox-base/src/ser/rrdata.rs | 44 +++- lib/dnsbox-base/src/ser/text/mod.rs | 7 +- lib/dnsbox-derive/src/lib.rs | 2 +- lib/dnsbox-derive/src/rrdata.rs | 30 ++- 13 files changed, 504 insertions(+), 215 deletions(-) delete mode 100644 lib/dnsbox-base/src/common_types/rr_type/mod.rs diff --git a/lib/dnsbox-base/src/common_types/classes.rs b/lib/dnsbox-base/src/common_types/classes.rs index 3cce6c0..e77eb46 100644 --- a/lib/dnsbox-base/src/common_types/classes.rs +++ b/lib/dnsbox-base/src/common_types/classes.rs @@ -21,6 +21,7 @@ pub const ANY : Class = Class(KnownQClass::ANY as u16); pub enum KnownClass { // try to list "original" rfc IN = 0x0001, // RFC 1035 + // CS = 0x0002, // "CSNET" CH = 0x0003, // "Chaos" HS = 0x0004, // "Hesiod" } @@ -110,6 +111,18 @@ impl Class { } } +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 { let n = match *self { diff --git a/lib/dnsbox-base/src/common_types/mod.rs b/lib/dnsbox-base/src/common_types/mod.rs index 577fde8..baa89ae 100644 --- a/lib/dnsbox-base/src/common_types/mod.rs +++ b/lib/dnsbox-base/src/common_types/mod.rs @@ -5,14 +5,13 @@ pub mod classes; pub mod types; mod nsec; mod nxt; -mod rr_type; mod time; mod uri; pub use self::binary::{HexShortBlob, Base64RemainingBlob, HexRemainingBlob}; pub use self::name::{DnsName, DnsCanonicalName, DnsCompressedName}; pub use self::nsec::{NsecTypeBitmap, NextHashedOwnerName}; -pub use self::rr_type::Type; +pub use self::types::Type; pub use self::classes::Class; pub use self::text::{ShortText, LongText, UnquotedShortText, RemainingText}; pub use self::uri::UriText; diff --git a/lib/dnsbox-base/src/common_types/rr_type/mod.rs b/lib/dnsbox-base/src/common_types/rr_type/mod.rs deleted file mode 100644 index dfc9a60..0000000 --- a/lib/dnsbox-base/src/common_types/rr_type/mod.rs +++ /dev/null @@ -1,99 +0,0 @@ -use bytes::Bytes; -use errors::*; -use ser::DnsPacketData; -use ser::text::{DnsTextData, DnsTextFormatter, next_field}; -use std::fmt; -use std::io::Cursor; -use records::registry::{name_to_type, type_name}; -use common_types::types; -use std::borrow::Cow; - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub struct Type(pub u16); - -impl Type { - pub fn name(self) -> Cow<'static, str> { - if let Some(name) = type_name(self) { - Cow::Borrowed(name) - } else { - Cow::Owned(format!("TYPE{}", self.0)) - } - } - - /// defined in RFC 1035 - pub fn well_known(self) -> bool { - // 0x0001 (A) ... 0x0010 (TXT) are defined in RFC 1035 - self.0 >= 0x0001 && self.0 <= 0x0010 - } - - /// require converting to canonical form for DNSSEC (i.e. names - /// must be converted to (ASCII) lower case, no compression) - /// - /// See https://tools.ietf.org/html/rfc4034#section-6.2 (updates RFC 3597). - pub fn canonical(self) -> bool { - match self { - types::NS => true, - types::MD => true, - types::MF => true, - types::CNAME => true, - types::SOA => true, - types::MB => true, - types::MG => true, - types::MR => true, - types::PTR => true, - // types::HINFO => true, // doesn't have a name in data - types::MINFO => true, - types::MX => true, - // types::HINFO => true, // see above, also duplicate in the RFCs - types::RP => true, - types::AFSDB => true, - types::RT => true, - types::NSAP_PTR => true, // not listed in the RFCs, but probably should be. - types::SIG => true, - types::PX => true, - types::NXT => true, - types::SRV => true, // moved up to match numeric order - types::NAPTR => true, - types::KX => true, - // types::SRV => true, // moved up to match numeric order - types::A6 => true, // moved up to match numeric order - types::DNAME => true, - // types::A6 => true, // moved up to match numeric order - types::RRSIG => true, - types::NSEC => true, - _ => false, - } - } -} - -impl fmt::Display for Type { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if let Some(name) = type_name(*self) { - write!(f, "{}", name) - } else { - write!(f, "TYPE{}", self.0) - } - } -} - -impl DnsPacketData for Type { - fn deserialize(data: &mut Cursor) -> Result { - Ok(Type(DnsPacketData::deserialize(data)?)) - } -} - -impl DnsTextData for Type { - fn dns_parse(data: &mut &str) -> Result { - let field = next_field(data)?; - if field.starts_with("TYPE") || field.starts_with("type") { - if let Ok(t) = field[4..].parse::() { - return Ok(Type(t)); - } - } - name_to_type(field).ok_or_else(|| format_err!("unknown type {:?}", field)) - } - - fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { - write!(f, "{}", self) - } -} diff --git a/lib/dnsbox-base/src/common_types/types.rs b/lib/dnsbox-base/src/common_types/types.rs index d807924..f395568 100644 --- a/lib/dnsbox-base/src/common_types/types.rs +++ b/lib/dnsbox-base/src/common_types/types.rs @@ -1,4 +1,14 @@ -use common_types::Type; +use bytes::Bytes; +use errors::*; +use records::registry::{lookup_type_to_name, lookup_type_name}; +use ser::DnsPacketData; +use ser::text::{DnsTextData, DnsTextFormatter, next_field}; +use std::borrow::Cow; +use std::fmt; +use std::io::Cursor; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub struct Type(pub u16); pub const A : Type = Type(KnownType::A as u16); pub const NS : Type = Type(KnownType::NS as u16); @@ -40,7 +50,7 @@ pub const CERT : Type = Type(KnownType::CERT as u16); pub const A6 : Type = Type(KnownType::A6 as u16); pub const DNAME : Type = Type(KnownType::DNAME as u16); pub const SINK : Type = Type(KnownType::SINK as u16); -pub const OPT : Type = Type(KnownType::OPT as u16); +pub const OPT : Type = Type(KnownMetaType::OPT as u16); pub const APL : Type = Type(KnownType::APL as u16); pub const DS : Type = Type(KnownType::DS as u16); pub const SSHFP : Type = Type(KnownType::SSHFP as u16); @@ -72,19 +82,19 @@ pub const L64 : Type = Type(KnownType::L64 as u16); pub const LP : Type = Type(KnownType::LP as u16); pub const EUI48 : Type = Type(KnownType::EUI48 as u16); pub const EUI64 : Type = Type(KnownType::EUI64 as u16); -pub const TKEY : Type = Type(KnownType::TKEY as u16); -pub const TSIG : Type = Type(KnownType::TSIG as u16); -pub const IXFR : Type = Type(KnownType::IXFR as u16); -pub const AXFR : Type = Type(KnownType::AXFR as u16); -pub const MAILB : Type = Type(KnownType::MAILB as u16); -pub const MAILA : Type = Type(KnownType::MAILA as u16); -pub const ANY : Type = Type(KnownType::ANY as u16); +pub const TKEY : Type = Type(KnownMetaType::TKEY as u16); +pub const TSIG : Type = Type(KnownMetaType::TSIG as u16); +pub const IXFR : Type = Type(KnownQType::IXFR as u16); +pub const AXFR : Type = Type(KnownQType::AXFR as u16); +pub const MAILB : Type = Type(KnownQType::MAILB as u16); +pub const MAILA : Type = Type(KnownQType::MAILA as u16); +pub const ANY : Type = Type(KnownQType::ANY as u16); pub const URI : Type = Type(KnownType::URI as u16); pub const CAA : Type = Type(KnownType::CAA as u16); pub const AVC : Type = Type(KnownType::AVC as u16); pub const DOA : Type = Type(KnownType::DOA as u16); +pub const TA : Type = Type(KnownType::TA as u16); pub const DLV : Type = Type(KnownType::DLV as u16); -pub const ADDR : Type = Type(KnownType::ADDR as u16); pub const ALIAS : Type = Type(KnownType::ALIAS as u16); #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] @@ -132,7 +142,7 @@ pub enum KnownType { A6 = 0x0026, // RFC 2874 DNAME = 0x0027, // RFC 6672 SINK = 0x0028, // Donald E Eastlake: http://tools.ietf.org/html/draft-eastlake-kitchen-sink - OPT = 0x0029, // RFC 6891 + // OPT (0x0029) is a meta type APL = 0x002a, // RFC 3123 DS = 0x002b, // RFC 3658 SSHFP = 0x002c, // RFC 4255 @@ -164,22 +174,219 @@ pub enum KnownType { LP = 0x006b, // RFC 6742 EUI48 = 0x006c, // RFC 7043 EUI64 = 0x006d, // RFC 7043 + // 0x0080..0x00ff: meta and qtypes + URI = 0x0100, // RFC 7553 + CAA = 0x0101, // RFC 6844 + AVC = 0x0102, // Wolfgang Riedel + DOA = 0x0103, // http://www.iana.org/go/draft-durand-doa-over-dns + TA = 0x8000, // + DLV = 0x8001, // RFC 4431 + ALIAS = 0xff79, // powerdns +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[repr(u16)] +#[allow(non_camel_case_types)] +pub enum KnownMetaType { + OPT = 0x0029, // RFC 6891 TKEY = 0x00f9, // RFC 2930 TSIG = 0x00fa, // RFC 2845 +} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +#[repr(u16)] +#[allow(non_camel_case_types)] +pub enum KnownQType { IXFR = 0x00fb, // RFC 1995 AXFR = 0x00fc, // RFC 1035 MAILB = 0x00fd, // RFC 1035 MAILA = 0x00fe, // RFC 1035 ANY = 0x00ff, // RFC 1035, "*" - URI = 0x0100, // RFC 7553 - CAA = 0x0101, // RFC 6844 - AVC = 0x0102, // Wolfgang Riedel - DOA = 0x0103, // http://www.iana.org/go/draft-durand-doa-over-dns - DLV = 0x8001, // RFC 4431 - ADDR = 0xff78, // powerdns? - ALIAS = 0xff79, // powerdns? } +impl Type { + pub fn name(self) -> Cow<'static, str> { + if let Some(name) = lookup_type_to_name(self) { + Cow::Borrowed(name) + } else { + Cow::Owned(self.generic_name()) + } + } + + // 0x0080-0x00FF is reserved for QTYPEs and Meta TYPEs. + // OPT is a special meta type + pub fn is_q_or_meta_type(self) -> bool { + self == OPT || (self.0 >= 128 && self.0 < 256) + } + + // checks for known qtypes + pub fn is_known_qtype(self) -> bool { + match self { + IXFR => true, + AXFR => true, + MAILB => true, + MAILA => true, + ANY => true, + _ => false, + } + } + + // checks for known meta types + pub fn is_known_meta_type(self) -> bool { + match self { + OPT => true, + TKEY => true, + TSIG => true, + _ => false, + } + } + + /// uses generic name for QTYPEs/Meta TYPEs (`is_q_or_meta_type`) + pub fn type_name(self) -> Cow<'static, str> { + if self.is_q_or_meta_type() { + Cow::Owned(self.generic_name()) + } else { + self.name() + } + } + + pub fn generic_name(self) -> String { + format!("TYPE{}", self.0) + } + + pub fn from_name(name: &str) -> Option { + use std::ascii::AsciiExt; + if let Some(t) = lookup_type_name(name) { return Some(t); } + if name.as_bytes()[0..4].eq_ignore_ascii_case(b"TYPE") { + if let Ok(t) = name[4..].parse::() { + return Some(Type(t)); + } + } + None + } + + /// similar to `from_name`, but doesn't accept QTYPE/Meta TYPE names (it + /// always accepts "TYPE..." names though, even if they are known to + /// be QTYPE/Meta TYPE) + pub fn type_from_name(name: &str) -> Option { + use std::ascii::AsciiExt; + if let Some(t) = lookup_type_name(name) { + if t.is_q_or_meta_type() { return None; } + return Some(t); + } + if name.as_bytes()[0..4].eq_ignore_ascii_case(b"TYPE") { + if let Ok(t) = name[4..].parse::() { + return Some(Type(t)); + } + } + None + } + + pub fn write_type_name(self, f: &mut fmt::Formatter) -> fmt::Result { + if self.is_q_or_meta_type() { + self.write_generic_name(f) + } else { + write!(f, "{}", self) + } + } + + pub fn write_generic_name(self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "TYPE{}", self.0) + } + + /// defined in RFC 1035 + pub fn well_known(self) -> bool { + // 0x0001 (A) ... 0x0010 (TXT) are defined in RFC 1035 + self.0 >= 0x0001 && self.0 <= 0x0010 + } + + /// require converting to canonical form for DNSSEC (i.e. names + /// must be converted to (ASCII) lower case, no compression) + /// + /// See https://tools.ietf.org/html/rfc4034#section-6.2 (updates RFC 3597). + /// Also updated by https://tools.ietf.org/html/rfc6840#section-5.1 + pub fn canonical(self) -> bool { + match self { + NS => true, + MD => true, + MF => true, + CNAME => true, + SOA => true, + MB => true, + MG => true, + MR => true, + PTR => true, + // HINFO => true, // removed by RFC 6840: doesn't have a name in data + MINFO => true, + MX => true, + // HINFO => true, // removed by RFC 6840: see above, also duplicate + RP => true, + AFSDB => true, + RT => true, + NSAP_PTR => true, // not listed in the RFCs, but probably should be. + SIG => true, + PX => true, + NXT => true, + SRV => true, // moved up to match numeric order + NAPTR => true, + KX => true, + // SRV => true, // moved up to match numeric order + A6 => true, // moved up to match numeric order + DNAME => true, + // A6 => true, // moved up to match numeric order + RRSIG => true, + // NSEC => true, // removed by RFC 6840 + _ => false, + } + } +} + +impl From for Type { + fn from(value: KnownType) -> Self { + Type(value as u16) + } +} + +impl From for Type { + fn from(value: KnownMetaType) -> Self { + Type(value as u16) + } +} + +impl From for Type { + fn from(value: KnownQType) -> Self { + Type(value as u16) + } +} + +impl fmt::Display for Type { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(name) = lookup_type_to_name(*self) { + write!(f, "{}", name) + } else { + self.write_generic_name(f) + } + } +} + +impl DnsPacketData for Type { + fn deserialize(data: &mut Cursor) -> Result { + Ok(Type(DnsPacketData::deserialize(data)?)) + } +} + +impl DnsTextData for Type { + fn dns_parse(data: &mut &str) -> Result { + let field = next_field(data)?; + Type::from_name(field).ok_or_else(|| format_err!("unknown type {:?}", field)) + } + + fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { + write!(f, "{}", self) + } +} + + #[cfg(test)] macro_rules! check_type { ($t:ident, $dec:expr) => { @@ -188,7 +395,7 @@ macro_rules! check_type { // compare decimal value assert_eq!($t, Type($dec), "wrong decimal value for {}", stringify!($t)); // make sure it's registered - assert_eq!(registry::name_to_type(stringify!($t)), Some($t), "{} not registered", stringify!($t)); + assert_eq!(registry::lookup_type_name(stringify!($t)), Some($t), "{} not registered", stringify!($t)); } }; ($t:ident, $dec:expr, $name:expr) => { @@ -197,7 +404,7 @@ macro_rules! check_type { // compare decimal value assert_eq!($t, Type($dec), "wrong decimal value for {}", stringify!($t)); // make sure it's registered - assert_eq!(registry::name_to_type($name), Some($t), "{} not registered as {:?}", stringify!($t), $name); + assert_eq!(registry::lookup_type_name($name), Some($t), "{} not registered as {:?}", stringify!($t), $name); } }; } @@ -288,7 +495,7 @@ fn check_types() { check_type!(CAA , 257); check_type!(AVC , 258); check_type!(DOA , 259); + check_type!(TA , 32768); check_type!(DLV , 32769); - check_type!(ADDR , 65400); check_type!(ALIAS , 65401); } diff --git a/lib/dnsbox-base/src/records/registry.rs b/lib/dnsbox-base/src/records/registry.rs index b48198d..cf5644a 100644 --- a/lib/dnsbox-base/src/records/registry.rs +++ b/lib/dnsbox-base/src/records/registry.rs @@ -1,12 +1,18 @@ -use std::collections::HashMap; +use bytes::Bytes; +use std::any::TypeId; use std::ascii::AsciiExt; +use std::collections::HashMap; +use std::io::Cursor; use std::marker::PhantomData; +use common_types::{Class, Type, types}; +use errors::*; use records::structs; -use common_types::types; -use common_types::Type; use ser::{RRData, StaticRRData}; +// this should be enough for registered names +const TYPE_NAME_MAX_LEN: usize = 16; + lazy_static!{ static ref REGISTRY: Registry = Registry::init(); } @@ -15,31 +21,59 @@ fn registry() -> &'static Registry { &*REGISTRY } +pub(crate) fn lookup_type_name(name: &str) -> Option { + if name.len() >= TYPE_NAME_MAX_LEN { return None; } + let mut name_buf_storage = [0u8; TYPE_NAME_MAX_LEN]; + let name_buf = &mut name_buf_storage[..name.len()]; + name_buf.copy_from_slice(name.as_bytes()); + + name_buf.make_ascii_uppercase(); + + let registry = registry(); + let &t = registry.names_to_type.get(name_buf)?; + Some(t) +} + pub fn known_name_to_type(name: &str) -> Option { let registry = registry(); - let t = name_to_type(name)?; + let t = lookup_type_name(name)?; registry.type_parser.get(&t)?; Some(t) } -pub fn name_to_type(name: &str) -> Option { - let registry = registry(); - let &t = registry.names_to_type.get(name)?; - Some(t) -} - -pub(crate) fn type_name(rrtype: Type) -> Option<&'static str> { +pub(crate) fn lookup_type_to_name(rrtype: Type) -> Option<&'static str> { let registry = registry(); registry.type_names.get(&rrtype).map(|s| s as _) } +#[doc(hidden)] +pub fn check_registration() { + registry().check_registration::(); +} + +#[derive(Clone, Copy, Debug)] struct TagRRDataType(PhantomData); +trait RRDataTypeParse: 'static { + fn type_id(&self) -> TypeId { + TypeId::of::() + } + + fn deserialize_rr_data(&self, ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor) -> Result>; +} + +impl RRDataTypeParse for TagRRDataType { + fn deserialize_rr_data(&self, ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor) -> Result> { + T::deserialize_rr_data(ttl, rr_class, rr_type, data).map(|d| Box::new(d) as _) + } +} + struct Registry { - names_to_type: HashMap, + // store (ascii) upper-case names. + names_to_type: HashMap, Type>, type_names: HashMap, - type_parser: HashMap, + type_parser: HashMap>, // make sure registrations are in order prev_type: Option, } @@ -79,7 +113,7 @@ impl Registry { r.register_known::(); r.register_known::(); r.register_known::(); - r.register_unknown("GPOS" , types::GPOS); + r.register_known::(); r.register_known::(); r.register_known::(); r.register_known::(); @@ -136,9 +170,12 @@ impl Registry { r.register_known::(); r.register_unknown("AVC" , types::AVC); r.register_unknown("DOA" , types::DOA); + r.register_unknown("TA" , types::TA); r.register_unknown("DLV" , types::DLV); - r.register_unknown("ADDR" , types::ADDR); - r.register_unknown("ALIAS" , types::ALIAS); + r.register_known::(); + + // "ALL" could be an alias for the ANY type? + // assert!(r.names_to_type.insert("ALL".into(), types::ANY).is_none()); r } @@ -148,7 +185,9 @@ impl Registry { self.prev_type = Some(rrtype); let mut name: String = name.into(); name.make_ascii_uppercase(); - assert!(self.names_to_type.insert(name.clone(), rrtype).is_none()); + assert!(!name.starts_with("TYPE"), "must not register generic name: {}", name); + assert!(name.len() <= TYPE_NAME_MAX_LEN, "name too long: {} - maybe you need to increase TYPE_NAME_MAX_LEN", name); + assert!(self.names_to_type.insert(name.clone().into_bytes(), rrtype).is_none()); self.type_names.insert(rrtype, name); } @@ -156,10 +195,17 @@ impl Registry { self.register_name(name, rrtype); } - fn register_known(&mut self) { + fn register_known(&mut self) { let rrtype = T::TYPE; let name = T::NAME; self.register_name(name, rrtype); - self.type_parser.insert(rrtype, ()); + self.type_parser.insert(rrtype, Box::new(TagRRDataType::(PhantomData))); + } + + fn check_registration(&self) { + assert_eq!(self.names_to_type.get(T::NAME.as_bytes()), Some(&T::TYPE)); + let p: &RRDataTypeParse = &**self.type_parser.get(&T::TYPE).expect("no parser registered"); + let tid = TypeId::of::>(); + assert_eq!(p.type_id(), tid); } } diff --git a/lib/dnsbox-base/src/records/structs.rs b/lib/dnsbox-base/src/records/structs.rs index 9d6a196..7569dfe 100644 --- a/lib/dnsbox-base/src/records/structs.rs +++ b/lib/dnsbox-base/src/records/structs.rs @@ -7,38 +7,38 @@ use std::net::{Ipv4Addr, Ipv6Addr}; // registered; there must be a records::types::$name `Type` constant // with the same name as the struct. -// class IN #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(IN)] pub struct A { addr: Ipv4Addr, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct NS { nsdname: DnsCompressedName, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct MD { madname: DnsCompressedName, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct MF { madname: DnsCompressedName, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct CNAME { cname: DnsCompressedName, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct SOA { mname: DnsCompressedName, rname: DnsCompressedName, @@ -49,84 +49,86 @@ pub struct SOA { minimum: u32, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct MB { madname: DnsCompressedName, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct MG { mgmname: DnsCompressedName, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct MR { newname: DnsCompressedName, } // not allowed in zone files anyway, i.e. no text representation. -// content not restricted either, just some bytes. no need to parse it. +// content not restricted either, just some bytes. no need to parse it, +// generic representation should be just fine. // -// class independent pub struct NULL; +// #[RRClass(ANY)] +// pub struct NULL; // text representation like: `WKS 127.0.0.1 TCP smtp http 110`. would // have to parse protocol and service names. // -// class IN // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(IN)] // pub struct WKS { // address: Ipv4Addr, // protocol: u8, // bitmap: ..., // remaining bytes // } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct PTR { ptrdname: DnsCompressedName, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct HINFO { cpu: ShortText, os: ShortText, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct MINFO { rmailbx: DnsCompressedName, emailbx: DnsCompressedName, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct MX { preference: u16, mxname: DnsCompressedName, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct TXT { text: LongText, } // end of RFC 1035: no DnsCompressedName below! -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct RP { mbox: DnsCanonicalName, txt: DnsCanonicalName, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct AFSDB { subtype: u16, hostname: DnsCanonicalName, @@ -142,35 +144,37 @@ pub struct AFSDB { // least 4 bytes in the field: probably due to "beginning with the 4 // digit DNIC"). // -// class independent +// #[RRClass(ANY)] // pub struct X25 { // psdn_address: ShortText, // } -// class independent +// #[RRClass(ANY)] // pub struct ISDN { // isdn_address: ShortText, // subaddress: Option, // } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct RT { preference: u16, intermediate: DnsCanonicalName, } +// #[RRClass(ANY)] // pub struct NSAP; #[allow(non_camel_case_types)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[RRTypeName = "NSAP-PTR"] +#[RRClass(ANY)] pub struct NSAP_PTR { owner: DnsCanonicalName, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct SIG { rr_type: Type, algorithm: u8, @@ -186,8 +190,8 @@ pub struct SIG { signature: Base64RemainingBlob, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct KEY { flags: u16, protocol: u8, @@ -195,18 +199,24 @@ pub struct KEY { public_key: Base64RemainingBlob, } -// class IN #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(IN)] pub struct PX { preference: u16, map822: DnsCanonicalName, mapx400: DnsCanonicalName, } -// pub struct GPOS; - -// class IN #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] // not restricted in rfc 1712 +pub struct GPOS { + longitude: ShortText, + latitude: ShortText, + altitude: ShortText, +} + +#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(IN)] pub struct AAAA { addr: Ipv6Addr, } @@ -214,16 +224,22 @@ pub struct AAAA { pub use super::weird_structs::LOC; #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct NXT { next: DnsCanonicalName, types: NxtTypeBitmap, } +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct EID; + +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct NIMLOC; -// class IN #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(IN)] pub struct SRV { preference: u16, weight: u16, @@ -231,10 +247,12 @@ pub struct SRV { target: DnsCanonicalName, } +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct ATMA; -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct NAPTR { order: u16, preference: u16, @@ -244,15 +262,15 @@ pub struct NAPTR { replacement: DnsCanonicalName, } -// class IN #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(IN)] pub struct KX { preference: u16, exchanger: DnsCanonicalName, } -// class ?? #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] pub struct CERT { cert_type: u16, key_tag: u16, @@ -262,21 +280,26 @@ pub struct CERT { pub use super::weird_structs::A6; -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct DNAME { target: DnsCanonicalName, } +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct SINK; // OPT should be decoded at "transport level", abuses ttl and class // fields too. // pub struct OPT; +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct APL; #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct DS { key_tag: u16, algorithm: u8, @@ -285,6 +308,7 @@ pub struct DS { } #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] pub struct SSHFP { algorithm: u8, fingerprint_type: u8, @@ -294,10 +318,11 @@ pub struct SSHFP { } // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct IPSECKEY; -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct RRSIG { rr_type: Type, algorithm: u8, @@ -310,15 +335,15 @@ pub struct RRSIG { signature: Base64RemainingBlob, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct NSEC { - next: DnsCanonicalName, + next: DnsName, // RFC 6840 says not canonic (updates RFC 4034) types: NsecTypeBitmap, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct DNSKEY { flags: u16, protocol: u8, @@ -327,10 +352,11 @@ pub struct DNSKEY { } // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct DHCID; -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct NSEC3 { hash_algorithm: u8, flags: u8, @@ -340,8 +366,8 @@ pub struct NSEC3 { types: NsecTypeBitmap, } -// class independent #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct NSEC3PARAM { hash_algorithm: u8, flags: u8, @@ -350,63 +376,103 @@ pub struct NSEC3PARAM { } // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct TLSA; // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct SMIMEA; +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct HIP; + +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct NINFO; // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct RKEY; +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct TALINK; // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct CDS; // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct CDNSKEY; // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct OPENPGPKEY; +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct CSYNC; #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] pub struct SPF { text: LongText, } +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct UINFO; + +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct UID; + +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct GID; + +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct UNSPEC; + +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct NID; + +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct L32; + +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct L64; + +// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct LP; // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct EUI48; // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct EUI64; // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct TKEY; // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] // pub struct TSIG; -// pub struct IXFR; // qtype only? -// pub struct AXFR; // qtype only? -// pub struct MAILB; // qtype only? -// pub struct MAILA; // qtype only? -// pub struct ANY; // qtype only? +// QTYPEs: IXFR, AXFR, MAILB, MAILA, ANY #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(ANY)] pub struct URI { priority: u16, weight: u16, @@ -414,6 +480,7 @@ pub struct URI { } #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +// #[RRClass(?)] pub struct CAA { flags: u8, tag: UnquotedShortText, @@ -426,7 +493,9 @@ pub struct CAA { // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] // pub struct DLV; -// pub struct ADDR; - -// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] -// pub struct ALIAS; +// powerdns +#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] +#[RRClass(IN)] // used to lookup A and AAAA - only useful in IN +pub struct ALIAS { + content: DnsName, +} diff --git a/lib/dnsbox-base/src/records/tests.rs b/lib/dnsbox-base/src/records/tests.rs index d303cc0..e4ff9ac 100644 --- a/lib/dnsbox-base/src/records/tests.rs +++ b/lib/dnsbox-base/src/records/tests.rs @@ -1,4 +1,5 @@ use bytes::{Bytes, Buf}; +use common_types::classes; use failure::ResultExt; use records::structs; use ser::{packet, text, StaticRRData}; @@ -10,7 +11,7 @@ where T: StaticRRData { let mut data = Cursor::new(Bytes::from_static(data)); - let result = T::deserialize_rr_data(3600, 0x0001, T::TYPE, &mut data)?; + let result = T::deserialize_rr_data(3600, classes::IN, T::TYPE, &mut data)?; ensure!(!data.has_remaining(), "rrdata not read completely"); Ok(result) } diff --git a/lib/dnsbox-base/src/records/unknown.rs b/lib/dnsbox-base/src/records/unknown.rs index 4b32011..d05d29d 100644 --- a/lib/dnsbox-base/src/records/unknown.rs +++ b/lib/dnsbox-base/src/records/unknown.rs @@ -1,11 +1,13 @@ use bytes::Bytes; use common_types::*; -use failure::{ResultExt, Fail}; -use ser::text::{DnsTextFormatter, next_field}; -use ser::packet::remaining_bytes; use common_types::binary::HEXLOWER_PERMISSIVE_ALLOW_WS; -use std::fmt; use errors::*; +use failure::{ResultExt, Fail}; +use ser::packet::remaining_bytes; +use ser::RRDataPacket; +use ser::text::{DnsTextFormatter, next_field}; +use std::fmt; +use std::io::Cursor; #[derive(Clone, PartialEq, Eq, Debug)] pub struct UnknownRecord { @@ -45,3 +47,9 @@ impl UnknownRecord { write!(f, "TYPE{} \\# {} {}", self.rr_type.0, self.raw.len(), HEXLOWER_PERMISSIVE_ALLOW_WS.encode(&self.raw)) } } + +impl RRDataPacket for UnknownRecord { + fn deserialize_rr_data(_ttl: u32, _rr_class: Class, rr_type: Type, data: &mut Cursor) -> Result { + UnknownRecord::deserialize(rr_type, data) + } +} diff --git a/lib/dnsbox-base/src/records/weird_structs.rs b/lib/dnsbox-base/src/records/weird_structs.rs index 7bac790..4ab8a69 100644 --- a/lib/dnsbox-base/src/records/weird_structs.rs +++ b/lib/dnsbox-base/src/records/weird_structs.rs @@ -11,8 +11,8 @@ use std::net::Ipv6Addr; // registered; there must be a records::types::$name `Type` constant // with the same name as the struct. -// class independent #[derive(Clone, PartialEq, Eq, Debug, RRData)] +#[RRClass(ANY)] pub enum LOC { Version0(LOC0), UnknownVersion{ @@ -56,8 +56,8 @@ pub struct LOC0 { altitude: u32, } -// class IN #[derive(Clone, PartialEq, Eq, Debug, RRData)] +#[RRClass(IN)] pub struct A6 { prefix: u8, // [0...128] // might include non-zero padding diff --git a/lib/dnsbox-base/src/ser/rrdata.rs b/lib/dnsbox-base/src/ser/rrdata.rs index b9ea8fe..b40d257 100644 --- a/lib/dnsbox-base/src/ser/rrdata.rs +++ b/lib/dnsbox-base/src/ser/rrdata.rs @@ -1,24 +1,54 @@ use bytes::Bytes; -use common_types::Type; +use common_types::{Class, Type}; +use common_types::classes; use errors::*; -use std::io::Cursor; use ser::DnsPacketData; +use ser::text::{DnsTextData, DnsTextFormatter}; +use std::fmt; +use std::io::Cursor; -pub trait RRDataPacket: Sized { - fn deserialize_rr_data(ttl: u32, rr_class: u16, rr_type: Type, data: &mut Cursor) -> Result; +pub trait RRDataPacket { + fn deserialize_rr_data(ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor) -> Result + where + Self: Sized, + ; } -impl RRDataPacket for T { - fn deserialize_rr_data(_ttl: u32, _rr_class: u16, _rr_type: Type, data: &mut Cursor) -> Result { +impl RRDataPacket for T { + fn deserialize_rr_data(_ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor) -> Result { + ensure!(rr_type == T::TYPE, "type mismatch"); + if T::CLASS != classes::ANY { + ensure!(rr_class == T::CLASS, "class mismatch"); + } T::deserialize(data) } } -pub trait RRData: RRDataPacket + super::DnsTextData { +pub trait RRDataText: Sized { + fn dns_parse_rr_data(ttl: u32, rr_class: Class, rr_type: Type, data: &mut &str) -> Result + where + Self: Sized, + ; + + // format might fail if there is no (known) text representation. + fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result; +} + +/* +impl RRDataText for T { + fn deserialize_rr_data(_ttl: u32, _rr_class: Class, _rr_type: Type, data: &mut Cursor) -> Result { + T::deserialize(data) + } +} +*/ + +pub trait RRData: RRDataPacket + DnsTextData { fn rr_type(&self) -> Type; } pub trait StaticRRData: RRData { const TYPE: Type; const NAME: &'static str; + // classes::ANY marks class independent types + const CLASS: Class; } diff --git a/lib/dnsbox-base/src/ser/text/mod.rs b/lib/dnsbox-base/src/ser/text/mod.rs index d16bfb6..963f0d6 100644 --- a/lib/dnsbox-base/src/ser/text/mod.rs +++ b/lib/dnsbox-base/src/ser/text/mod.rs @@ -94,8 +94,11 @@ impl<'a> DnsTextFormatter<'a> { } } -pub trait DnsTextData: Sized { - fn dns_parse(data: &mut &str) -> ::errors::Result; +pub trait DnsTextData { + fn dns_parse(data: &mut &str) -> ::errors::Result + where + Self: Sized, + ; // format might fail if there is no (known) text representation. fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result; } diff --git a/lib/dnsbox-derive/src/lib.rs b/lib/dnsbox-derive/src/lib.rs index c0722fb..f0f82c3 100644 --- a/lib/dnsbox-derive/src/lib.rs +++ b/lib/dnsbox-derive/src/lib.rs @@ -51,7 +51,7 @@ pub fn derive_dns_text_data(input: TokenStream) -> TokenStream { gen.parse().unwrap() } -#[proc_macro_derive(RRData, attributes(RRTypeName))] +#[proc_macro_derive(RRData, attributes(RRTypeName, RRClass))] pub fn derive_rr_data(input: TokenStream) -> TokenStream { let s = input.to_string(); diff --git a/lib/dnsbox-derive/src/rrdata.rs b/lib/dnsbox-derive/src/rrdata.rs index f83973b..f378ba2 100644 --- a/lib/dnsbox-derive/src/rrdata.rs +++ b/lib/dnsbox-derive/src/rrdata.rs @@ -6,6 +6,7 @@ use super::{attr_get_single_list_arg}; #[derive(Clone,Debug)] enum StructAttribute { RRTypeName(quote::Tokens), + RRClass(quote::Tokens), } struct StructAttributeParser<'a>(pub &'a [syn::Attribute]); @@ -21,6 +22,9 @@ impl<'a> Iterator for StructAttributeParser<'a> { "RRTypeName" => { return Some(StructAttribute::RRTypeName(attr_get_single_list_arg(a))); }, + "RRClass" => { + return Some(StructAttribute::RRClass(attr_get_single_list_arg(a))); + }, _ => (), } } @@ -42,19 +46,29 @@ pub fn build(ast: &syn::DeriveInput) -> quote::Tokens { } let name = &ast.ident; - let mut name_str = { - let name_str: &str = name.as_ref(); - quote!{#name_str} - }; + + let mut name_str = None; + let mut rr_class = None; for attr in StructAttributeParser(&ast.attrs) { match attr { StructAttribute::RRTypeName(name) => { - name_str = name; + assert_eq!(name_str, None, "only one RRTypeName attribute allowed"); + name_str = Some(name); + }, + StructAttribute::RRClass(c) => { + assert_eq!(rr_class, None, "only one RRTypeName attribute allowed"); + rr_class = Some(c); }, } } + let name_str = name_str.unwrap_or_else(|| { + let name_str: &str = name.as_ref(); + quote!{#name_str} + }); + let rr_class = rr_class.unwrap_or_else(|| quote!{ANY}); + let test_mod_name = syn::Ident::from(format!("test_rr_{}", name)); quote!{ @@ -67,6 +81,7 @@ pub fn build(ast: &syn::DeriveInput) -> quote::Tokens { impl ::dnsbox_base::ser::StaticRRData for #name { const TYPE: ::dnsbox_base::common_types::Type = ::dnsbox_base::common_types::types::#name; const NAME: &'static str = #name_str; + const CLASS: ::dnsbox_base::common_types::Class = ::dnsbox_base::common_types::classes::#rr_class; } // #[cfg(test)] @@ -78,10 +93,7 @@ pub fn build(ast: &syn::DeriveInput) -> quote::Tokens { #[test] fn test_registry() { - assert_eq!( - registry::known_name_to_type(#name::NAME), - Some(types::#name) - ); + registry::check_registration::<#name>(); } } }