This commit is contained in:
Stefan Bühler 2017-12-26 17:30:08 +01:00
parent 2e380af9bb
commit e0f597ba9c
13 changed files with 504 additions and 215 deletions

View File

@ -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<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 {
let n = match *self {

View File

@ -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;

View File

@ -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<Bytes>) -> Result<Self> {
Ok(Type(DnsPacketData::deserialize(data)?))
}
}
impl DnsTextData for Type {
fn dns_parse(data: &mut &str) -> Result<Self> {
let field = next_field(data)?;
if field.starts_with("TYPE") || field.starts_with("type") {
if let Ok(t) = field[4..].parse::<u16>() {
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)
}
}

View File

@ -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<Self> {
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::<u16>() {
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<Self> {
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::<u16>() {
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<KnownType> for Type {
fn from(value: KnownType) -> Self {
Type(value as u16)
}
}
impl From<KnownMetaType> for Type {
fn from(value: KnownMetaType) -> Self {
Type(value as u16)
}
}
impl From<KnownQType> 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<Bytes>) -> Result<Self> {
Ok(Type(DnsPacketData::deserialize(data)?))
}
}
impl DnsTextData for Type {
fn dns_parse(data: &mut &str) -> Result<Self> {
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);
}

View File

@ -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<Type> {
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<Type> {
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<Type> {
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<T: StaticRRData + Sync + 'static>() {
registry().check_registration::<T>();
}
#[derive(Clone, Copy, Debug)]
struct TagRRDataType<T: RRData>(PhantomData<T>);
trait RRDataTypeParse: 'static {
fn type_id(&self) -> TypeId {
TypeId::of::<Self>()
}
fn deserialize_rr_data(&self, ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor<Bytes>) -> Result<Box<RRData>>;
}
impl<T: RRData + 'static> RRDataTypeParse for TagRRDataType<T> {
fn deserialize_rr_data(&self, ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor<Bytes>) -> Result<Box<RRData>> {
T::deserialize_rr_data(ttl, rr_class, rr_type, data).map(|d| Box::new(d) as _)
}
}
struct Registry {
names_to_type: HashMap<String, Type>,
// store (ascii) upper-case names.
names_to_type: HashMap<Vec<u8>, Type>,
type_names: HashMap<Type, String>,
type_parser: HashMap<Type, ()>,
type_parser: HashMap<Type, Box<RRDataTypeParse + Sync>>,
// make sure registrations are in order
prev_type: Option<Type>,
}
@ -79,7 +113,7 @@ impl Registry {
r.register_known::<structs::SIG>();
r.register_known::<structs::KEY>();
r.register_known::<structs::PX>();
r.register_unknown("GPOS" , types::GPOS);
r.register_known::<structs::GPOS>();
r.register_known::<structs::AAAA>();
r.register_known::<structs::LOC>();
r.register_known::<structs::NXT>();
@ -136,9 +170,12 @@ impl Registry {
r.register_known::<structs::CAA>();
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::<structs::ALIAS>();
// "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<T: StaticRRData>(&mut self) {
fn register_known<T: StaticRRData + Sync + 'static>(&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::<T>(PhantomData)));
}
fn check_registration<T: StaticRRData + Sync + 'static>(&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::<TagRRDataType<T>>();
assert_eq!(p.type_id(), tid);
}
}

View File

@ -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<ShortText>,
// }
// 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,
}

View File

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

View File

@ -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<Bytes>) -> Result<Self> {
UnknownRecord::deserialize(rr_type, data)
}
}

View File

@ -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

View File

@ -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<Bytes>) -> Result<Self>;
pub trait RRDataPacket {
fn deserialize_rr_data(ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor<Bytes>) -> Result<Self>
where
Self: Sized,
;
}
impl<T: DnsPacketData> RRDataPacket for T {
fn deserialize_rr_data(_ttl: u32, _rr_class: u16, _rr_type: Type, data: &mut Cursor<Bytes>) -> Result<Self> {
impl<T: DnsPacketData + StaticRRData> RRDataPacket for T {
fn deserialize_rr_data(_ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor<Bytes>) -> Result<Self> {
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<Self>
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<T: DnsTextData> RRDataText for T {
fn deserialize_rr_data(_ttl: u32, _rr_class: Class, _rr_type: Type, data: &mut Cursor<Bytes>) -> Result<Self> {
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;
}

View File

@ -94,8 +94,11 @@ impl<'a> DnsTextFormatter<'a> {
}
}
pub trait DnsTextData: Sized {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self>;
pub trait DnsTextData {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self>
where
Self: Sized,
;
// format might fail if there is no (known) text representation.
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result;
}

View File

@ -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();

View File

@ -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>();
}
}
}