This commit is contained in:
Stefan Bühler 2017-12-26 22:23:51 +01:00
parent c4c84bd887
commit 302300c184
15 changed files with 148 additions and 70 deletions

View File

@ -40,7 +40,7 @@ impl DnsPacketData for HexShortBlob {
} }
impl DnsTextData for HexShortBlob { impl DnsTextData for HexShortBlob {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let s = next_field(data)?; let s = next_field(data)?;
if s == "-" { if s == "-" {
Ok(HexShortBlob(Bytes::new())) Ok(HexShortBlob(Bytes::new()))
@ -76,7 +76,7 @@ impl DnsPacketData for Base64RemainingBlob {
} }
impl DnsTextData for Base64RemainingBlob { impl DnsTextData for Base64RemainingBlob {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
skip_whitespace(data); skip_whitespace(data);
let result = BASE64_ALLOW_WS.decode(data.as_bytes()) let result = BASE64_ALLOW_WS.decode(data.as_bytes())
.with_context(|e| e.context(format!("invalid base64: {:?}", data)))?; .with_context(|e| e.context(format!("invalid base64: {:?}", data)))?;
@ -101,7 +101,7 @@ impl DnsPacketData for HexRemainingBlob {
} }
impl DnsTextData for HexRemainingBlob { impl DnsTextData for HexRemainingBlob {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
skip_whitespace(data); skip_whitespace(data);
let result = HEXLOWER_PERMISSIVE_ALLOW_WS.decode(data.as_bytes()) let result = HEXLOWER_PERMISSIVE_ALLOW_WS.decode(data.as_bytes())
.with_context(|e| e.context(format!("invalid hex: {:?}", data)))?; .with_context(|e| e.context(format!("invalid hex: {:?}", data)))?;

View File

@ -3,7 +3,7 @@
use bytes::Bytes; use bytes::Bytes;
use errors::*; use errors::*;
use ser::DnsPacketData; use ser::DnsPacketData;
use ser::text::{DnsTextData, DnsTextFormatter, next_field}; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field};
use std::fmt; use std::fmt;
use std::io::Cursor; use std::io::Cursor;
use std::borrow::Cow; use std::borrow::Cow;
@ -153,7 +153,7 @@ impl Class {
/// parses generic names of the form "CLASS..." /// parses generic names of the form "CLASS..."
pub fn from_generic_name(name: &str) -> Option<Self> { pub fn from_generic_name(name: &str) -> Option<Self> {
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
if name.as_bytes()[0..5].eq_ignore_ascii_case(b"CLASS") { if name.len() > 5 && name.as_bytes()[0..5].eq_ignore_ascii_case(b"CLASS") {
name[5..].parse::<u16>().ok().map(Class) name[5..].parse::<u16>().ok().map(Class)
} else { } else {
None None
@ -204,7 +204,7 @@ impl DnsPacketData for Class {
} }
impl DnsTextData for Class { impl DnsTextData for Class {
fn dns_parse(data: &mut &str) -> Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> Result<Self> {
let field = next_field(data)?; let field = next_field(data)?;
Class::from_name(field).ok_or_else(|| format_err!("unknown CLASS {:?}", field)) Class::from_name(field).ok_or_else(|| format_err!("unknown CLASS {:?}", field))
} }

View File

@ -1,19 +1,19 @@
use super::*; use super::*;
use ser::text::{DnsTextData, DnsTextFormatter, next_field, quoted}; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field, quoted};
impl DnsName { impl DnsName {
/// Parse text representation of a domain name /// Parse text representation of a domain name
pub fn parse<'a, O>(value: &str, origin: O) -> ::errors::Result<Self> pub fn parse(context: &DnsTextContext, value: &str) -> Result<Self>
where
O: IntoIterator<Item = DnsLabelRef<'a>>
{ {
let raw = value.as_bytes(); let raw = value.as_bytes();
let mut name = DnsName::new_root(); let mut name = DnsName::new_root();
if raw == b"." { if raw == b"." {
return Ok(name); return Ok(name);
} else if raw == b"@" { } else if raw == b"@" {
for l in origin.into_iter() { name.push_back(l)?; } match context.origin() {
return Ok(name); Some(o) => return Ok(o.clone()),
None => bail!("@ invalid without $ORIGIN"),
}
} }
ensure!(!raw.is_empty(), "invalid empty name"); ensure!(!raw.is_empty(), "invalid empty name");
let mut label = Vec::new(); let mut label = Vec::new();
@ -48,8 +48,16 @@ impl DnsName {
if !label.is_empty() { if !label.is_empty() {
// no trailing dot, relative name // no trailing dot, relative name
// push last label
name.push_back(DnsLabelRef::new(&label)?)?; name.push_back(DnsLabelRef::new(&label)?)?;
for l in origin.into_iter() { name.push_back(l)?; }
match context.origin() {
Some(o) => {
for l in o { name.push_back(l)?; }
},
None => bail!("missing trailing dot without $ORIGIN"),
}
} }
Ok(name) Ok(name)
@ -57,9 +65,9 @@ impl DnsName {
} }
impl DnsTextData for DnsName { impl DnsTextData for DnsName {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(context: &DnsTextContext, data: &mut &str) -> Result<Self> {
let field = next_field(data)?; let field = next_field(data)?;
DnsName::parse(field, &DnsName::new_root()) DnsName::parse(context, field)
} }
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
@ -69,18 +77,16 @@ impl DnsTextData for DnsName {
impl DnsCompressedName { impl DnsCompressedName {
/// Parse text representation of a domain name /// Parse text representation of a domain name
pub fn parse<'a, O>(value: &str, origin: O) -> ::errors::Result<Self> pub fn parse(context: &DnsTextContext, value: &str) -> Result<Self>
where
O: IntoIterator<Item = DnsLabelRef<'a>>
{ {
Ok(DnsCompressedName(DnsName::parse(value, origin)?)) Ok(DnsCompressedName(DnsName::parse(context, value)?))
} }
} }
impl DnsTextData for DnsCompressedName { impl DnsTextData for DnsCompressedName {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(context: &DnsTextContext, data: &mut &str) -> Result<Self> {
let field = next_field(data)?; let field = next_field(data)?;
DnsCompressedName::parse(field, &DnsName::new_root()) DnsCompressedName::parse(context, field)
} }
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {

View File

@ -4,7 +4,7 @@ use data_encoding;
use errors::*; use errors::*;
use failure::{Fail, ResultExt}; use failure::{Fail, ResultExt};
use ser::packet::{DnsPacketData, remaining_bytes, short_blob}; use ser::packet::{DnsPacketData, remaining_bytes, short_blob};
use ser::text::{DnsTextData, DnsTextFormatter, skip_whitespace, next_field}; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, skip_whitespace, next_field};
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::fmt; use std::fmt;
use std::io::Cursor; use std::io::Cursor;
@ -103,11 +103,11 @@ impl DnsPacketData for NsecTypeBitmap {
} }
impl DnsTextData for NsecTypeBitmap { impl DnsTextData for NsecTypeBitmap {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let mut set = BTreeSet::new(); let mut set = BTreeSet::new();
skip_whitespace(data); skip_whitespace(data);
while !data.is_empty() { while !data.is_empty() {
let t = Type::dns_parse(data)?; let t = Type::dns_parse(context, data)?;
set.insert(t); set.insert(t);
} }
Ok(NsecTypeBitmap::from_set(set)) Ok(NsecTypeBitmap::from_set(set))
@ -137,7 +137,7 @@ impl DnsPacketData for NextHashedOwnerName {
} }
impl DnsTextData for NextHashedOwnerName { impl DnsTextData for NextHashedOwnerName {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let field = next_field(data)?; let field = next_field(data)?;
let raw = BASE32HEX_NOPAD_ALLOW_WS.decode(field.as_bytes()) let raw = BASE32HEX_NOPAD_ALLOW_WS.decode(field.as_bytes())
.with_context(|e| e.context(format!("invalid base32hex (no padding): {:?}", field)))?; .with_context(|e| e.context(format!("invalid base32hex (no padding): {:?}", field)))?;

View File

@ -2,7 +2,7 @@ use bytes::{Bytes, Buf};
use common_types::Type; use common_types::Type;
use errors::*; use errors::*;
use ser::packet::{DnsPacketData, remaining_bytes}; use ser::packet::{DnsPacketData, remaining_bytes};
use ser::text::{DnsTextData, DnsTextFormatter, skip_whitespace}; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, skip_whitespace};
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::fmt; use std::fmt;
use std::io::Cursor; use std::io::Cursor;
@ -75,11 +75,11 @@ impl DnsPacketData for NxtTypeBitmap {
} }
impl DnsTextData for NxtTypeBitmap { impl DnsTextData for NxtTypeBitmap {
fn dns_parse(data: &mut &str) -> Result<Self> { fn dns_parse(context: &DnsTextContext, data: &mut &str) -> Result<Self> {
let mut set = BTreeSet::new(); let mut set = BTreeSet::new();
skip_whitespace(data); skip_whitespace(data);
while !data.is_empty() { while !data.is_empty() {
let t = Type::dns_parse(data)?; let t = Type::dns_parse(context, data)?;
set.insert(t); set.insert(t);
} }
NxtTypeBitmap::from_set(set) NxtTypeBitmap::from_set(set)

View File

@ -16,7 +16,7 @@ impl DnsPacketData for ShortText {
} }
impl DnsTextData for ShortText { impl DnsTextData for ShortText {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let raw = next_quoted_field(data)?; let raw = next_quoted_field(data)?;
ensure!(raw.len() < 256, "short text must be at most 255 bytes long"); ensure!(raw.len() < 256, "short text must be at most 255 bytes long");
Ok(ShortText(raw.into())) Ok(ShortText(raw.into()))
@ -46,7 +46,7 @@ impl DnsPacketData for LongText {
} }
impl DnsTextData for LongText { impl DnsTextData for LongText {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let mut result = Vec::new(); let mut result = Vec::new();
// `next_quoted_field` should skip trailing whitespace, we only // `next_quoted_field` should skip trailing whitespace, we only
// need to skip the beginning whitespace for the first // need to skip the beginning whitespace for the first
@ -80,7 +80,7 @@ impl DnsPacketData for UnquotedShortText {
} }
impl DnsTextData for UnquotedShortText { impl DnsTextData for UnquotedShortText {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let raw = next_quoted_field(data)?; let raw = next_quoted_field(data)?;
ensure!(raw.len() < 256, "short text must be at most 255 bytes long"); ensure!(raw.len() < 256, "short text must be at most 255 bytes long");
Ok(UnquotedShortText(raw.into())) Ok(UnquotedShortText(raw.into()))
@ -105,7 +105,7 @@ impl DnsPacketData for RemainingText {
} }
impl DnsTextData for RemainingText { impl DnsTextData for RemainingText {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
Ok(RemainingText(next_quoted_field(data)?.into())) Ok(RemainingText(next_quoted_field(data)?.into()))
} }

View File

@ -1,7 +1,7 @@
use bytes::Bytes; use bytes::Bytes;
use errors::*; use errors::*;
use ser::packet::DnsPacketData; use ser::packet::DnsPacketData;
use ser::text::{DnsTextData, DnsTextFormatter, next_field}; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field};
use std::fmt; use std::fmt;
use std::io::Cursor; use std::io::Cursor;
@ -18,7 +18,7 @@ impl DnsPacketData for Time {
} }
impl DnsTextData for Time { impl DnsTextData for Time {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let field = next_field(data)?; let field = next_field(data)?;
let epoch = field.parse::<u32>(); let epoch = field.parse::<u32>();
if field.len() == 14 && epoch.is_err() { if field.len() == 14 && epoch.is_err() {

View File

@ -4,7 +4,7 @@ use bytes::Bytes;
use errors::*; use errors::*;
use records::registry::{lookup_type_to_name, lookup_type_name}; use records::registry::{lookup_type_to_name, lookup_type_name};
use ser::DnsPacketData; use ser::DnsPacketData;
use ser::text::{DnsTextData, DnsTextFormatter, next_field}; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field};
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::io::Cursor; use std::io::Cursor;
@ -475,7 +475,7 @@ impl Type {
/// parses generic names of the form "TYPE..." /// parses generic names of the form "TYPE..."
pub fn from_generic_name(name: &str) -> Option<Self> { pub fn from_generic_name(name: &str) -> Option<Self> {
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
if name.as_bytes()[0..4].eq_ignore_ascii_case(b"TYPE") { if name.len() > 4 && name.as_bytes()[0..4].eq_ignore_ascii_case(b"TYPE") {
name[4..].parse::<u16>().ok().map(Type) name[4..].parse::<u16>().ok().map(Type)
} else { } else {
None None
@ -576,7 +576,7 @@ impl DnsPacketData for Type {
} }
impl DnsTextData for Type { impl DnsTextData for Type {
fn dns_parse(data: &mut &str) -> Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> Result<Self> {
let field = next_field(data)?; let field = next_field(data)?;
Type::from_name(field).ok_or_else(|| format_err!("unknown TYPE {:?}", field)) Type::from_name(field).ok_or_else(|| format_err!("unknown TYPE {:?}", field))
} }

View File

@ -24,7 +24,7 @@ impl DnsPacketData for UriText {
} }
impl DnsTextData for UriText { impl DnsTextData for UriText {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let raw = next_quoted_field(data)?; let raw = next_quoted_field(data)?;
ensure!(!raw.is_empty(), "URI must not be empty"); ensure!(!raw.is_empty(), "URI must not be empty");
Ok(UriText(raw.into())) Ok(UriText(raw.into()))

View File

@ -20,11 +20,13 @@ fn rrdata_parse<T>(data: &str) -> ::errors::Result<T>
where where
T: StaticRRData T: StaticRRData
{ {
let mut data = data; let mut ctx = text::DnsTextContext::new();
let result = T::dns_parse_rr_data(3600, classes::IN, T::TYPE, &mut data)?; ctx.set_zone_class(classes::IN);
let data = data.trim(); ctx.set_record_type(T::TYPE);
ensure!(data.is_empty(), "didn't parse complete rrdata text, remaining: {:?}", data); ctx.set_last_ttl(3600);
Ok(result) text::parse_with(data, |data| {
T::dns_parse_rr_data(&ctx, data)
})
} }
fn check<T>(txt: &str, data: &'static [u8]) -> ::errors::Result<()> fn check<T>(txt: &str, data: &'static [u8]) -> ::errors::Result<()>
@ -109,8 +111,8 @@ fn test_nsec3() {
check::<structs::NSEC3>("1 2 300 ab vs A NS", b"\x01\x02\x01\x2c\x01\xab\x01\xff\x00\x01\x60").unwrap(); check::<structs::NSEC3>("1 2 300 ab vs A NS", b"\x01\x02\x01\x2c\x01\xab\x01\xff\x00\x01\x60").unwrap();
// invalid base32 texts // invalid base32 texts
text::parse::<structs::NSEC3>("1 2 300 - v").unwrap_err(); rrdata_parse::<structs::NSEC3>("1 2 300 - v").unwrap_err();
text::parse::<structs::NSEC3>("1 2 300 - vv").unwrap_err(); rrdata_parse::<structs::NSEC3>("1 2 300 - vv").unwrap_err();
// invalid (empty) next-hashed values // invalid (empty) next-hashed values
packet::deserialize::<structs::NSEC3>(Bytes::from_static(b"\x01\x02\x01\x2c\x00\x00")).unwrap_err(); packet::deserialize::<structs::NSEC3>(Bytes::from_static(b"\x01\x02\x01\x2c\x00\x00")).unwrap_err();
@ -121,7 +123,7 @@ fn test_nsec3param() {
check::<structs::NSEC3PARAM>("1 2 300 -", b"\x01\x02\x01\x2c\x00").unwrap(); check::<structs::NSEC3PARAM>("1 2 300 -", b"\x01\x02\x01\x2c\x00").unwrap();
check::<structs::NSEC3PARAM>("1 2 300 ab", b"\x01\x02\x01\x2c\x01\xab").unwrap(); check::<structs::NSEC3PARAM>("1 2 300 ab", b"\x01\x02\x01\x2c\x01\xab").unwrap();
// `salt` hex string must not contain spaces // `salt` hex string must not contain spaces
text::parse::<structs::NSEC3PARAM>("1 2 300 a b").unwrap_err(); rrdata_parse::<structs::NSEC3PARAM>("1 2 300 a b").unwrap_err();
} }
#[test] #[test]

View File

@ -2,7 +2,7 @@ use bytes::{Bytes, Buf};
use common_types::*; use common_types::*;
use failure::ResultExt; use failure::ResultExt;
use ser::DnsPacketData; use ser::DnsPacketData;
use ser::text::{DnsTextData, DnsTextFormatter}; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext};
use std::fmt; use std::fmt;
use std::io::Read; use std::io::Read;
use std::net::Ipv6Addr; use std::net::Ipv6Addr;
@ -36,7 +36,7 @@ impl DnsPacketData for LOC {
} }
impl DnsTextData for LOC { impl DnsTextData for LOC {
fn dns_parse(_data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, _data: &mut &str) -> ::errors::Result<Self> {
unimplemented!() unimplemented!()
} }
@ -101,15 +101,15 @@ impl DnsPacketData for A6 {
} }
impl DnsTextData for A6 { impl DnsTextData for A6 {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let prefix: u8 = DnsTextData::dns_parse(data) let prefix: u8 = DnsTextData::dns_parse(context, data)
.context("failed parsing field A6::prefix")?; .context("failed parsing field A6::prefix")?;
ensure!(prefix <= 128, "invalid A6::prefix {}", prefix); ensure!(prefix <= 128, "invalid A6::prefix {}", prefix);
let suffix_offset = (prefix / 8) as usize; let suffix_offset = (prefix / 8) as usize;
debug_assert!(suffix_offset <= 16); debug_assert!(suffix_offset <= 16);
let suffix: Ipv6Addr = DnsTextData::dns_parse(data) let suffix: Ipv6Addr = DnsTextData::dns_parse(context, data)
.context("failed parsing field A6::suffix")?; .context("failed parsing field A6::suffix")?;
// clear prefix bits // clear prefix bits
@ -124,7 +124,7 @@ impl DnsTextData for A6 {
let suffix = Ipv6Addr::from(suffix); let suffix = Ipv6Addr::from(suffix);
let prefix_name = if !data.is_empty() { let prefix_name = if !data.is_empty() {
Some(DnsTextData::dns_parse(data) Some(DnsTextData::dns_parse(context, data)
.context("failed parsing field A6::prefix_name")?) .context("failed parsing field A6::prefix_name")?)
} else { } else {
None None

View File

@ -2,7 +2,7 @@ use bytes::Bytes;
use common_types::{Class, Type, classes}; use common_types::{Class, Type, classes};
use errors::*; use errors::*;
use ser::DnsPacketData; use ser::DnsPacketData;
use ser::text::{DnsTextData, DnsTextFormatter}; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext};
use std::borrow::Cow; use std::borrow::Cow;
use std::fmt; use std::fmt;
use std::io::Cursor; use std::io::Cursor;
@ -31,7 +31,7 @@ impl<T: DnsPacketData + StaticRRData> RRDataPacket for T {
} }
pub trait RRDataText { pub trait RRDataText {
fn dns_parse_rr_data(ttl: u32, rr_class: Class, rr_type: Type, data: &mut &str) -> Result<Self> fn dns_parse_rr_data(context: &DnsTextContext, data: &mut &str) -> Result<Self>
where where
Self: Sized, Self: Sized,
; ;
@ -59,15 +59,17 @@ pub trait RRDataText {
} }
impl<T: DnsTextData + StaticRRData> RRDataText for T { impl<T: DnsTextData + StaticRRData> RRDataText for T {
fn dns_parse_rr_data(_ttl: u32, rr_class: Class, rr_type: Type, data: &mut &str) -> Result<Self> fn dns_parse_rr_data(context: &DnsTextContext, data: &mut &str) -> Result<Self>
where where
Self: Sized, Self: Sized,
{ {
ensure!(rr_type == T::TYPE, "type mismatch"); ensure!(context.record_type() == Some(T::TYPE), "type mismatch");
let rr_class = context.zone_class().expect("require zone CLASS to parse record");
if T::CLASS != classes::ANY { if T::CLASS != classes::ANY {
ensure!(rr_class == T::CLASS, "class mismatch: got {}, need {}", rr_class, T::CLASS); ensure!(rr_class == T::CLASS, "class mismatch: got {}, need {}", rr_class, T::CLASS);
} }
T::dns_parse(data) ensure!(context.last_ttl().is_some(), "require TTL to parse record");
T::dns_parse(context, data)
} }
fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result { fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result {

View File

@ -1,3 +1,4 @@
use common_types;
use std::fmt; use std::fmt;
mod std_impls; mod std_impls;
@ -94,8 +95,74 @@ impl<'a, 'b> DnsTextFormatter<'a, 'b> {
} }
} }
#[derive(Clone, Debug, Default)]
pub struct DnsTextContext {
zone_class: Option<common_types::Class>,
origin: Option<common_types::DnsName>,
record_type: Option<common_types::Type>,
last_ttl: Option<u32>,
}
impl DnsTextContext {
pub fn new() -> Self {
Self::default()
}
pub fn zone_class(&self) -> Option<common_types::Class> {
self.zone_class
}
pub fn set_zone_class(&mut self, zone_class: common_types::Class) -> &mut Self {
self.zone_class = Some(zone_class);
self
}
pub fn unset_zone_class(&mut self) -> &mut Self {
self
}
pub fn origin(&self) -> Option<&common_types::DnsName> {
self.origin.as_ref()
}
pub fn set_origin(&mut self, origin: common_types::DnsName) -> &mut Self {
self.origin = Some(origin);
self
}
pub fn unset_origin(&mut self) -> &mut Self {
self
}
pub fn record_type(&self) -> Option<common_types::Type> {
self.record_type
}
pub fn set_record_type(&mut self, record_type: common_types::Type) -> &mut Self {
self.record_type = Some(record_type);
self
}
pub fn unset_record_type(&mut self) -> &mut Self {
self
}
pub fn last_ttl(&self) -> Option<u32> {
self.last_ttl
}
pub fn set_last_ttl(&mut self, last_ttl: u32) -> &mut Self {
self.last_ttl = Some(last_ttl);
self
}
pub fn unset_last_ttl(&mut self) -> &mut Self {
self
}
}
pub trait DnsTextData { pub trait DnsTextData {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> fn dns_parse(context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self>
where where
Self: Sized, Self: Sized,
; ;
@ -103,12 +170,12 @@ pub trait DnsTextData {
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result; fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result;
} }
pub fn parse<T>(data: &str) -> ::errors::Result<T> pub fn parse_with<'a, F, O>(data: &'a str, parser: F) -> ::errors::Result<O>
where where
T: DnsTextData for<'b> F: FnOnce(&'b mut &'a str) -> ::errors::Result<O>,
{ {
let mut data = data; let mut data = data;
let result = T::dns_parse(&mut data)?; let result = parser(&mut data)?;
let data = data.trim(); let data = data.trim();
ensure!(data.is_empty(), "didn't parse complete text, remaining: {:?}", data); ensure!(data.is_empty(), "didn't parse complete text, remaining: {:?}", data);
Ok(result) Ok(result)

View File

@ -1,10 +1,10 @@
use std::fmt; use std::fmt;
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use ser::text::{DnsTextData, DnsTextFormatter, next_field}; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field};
/* only decimal representations are used for numbers */ /* only decimal representations are used for numbers */
impl DnsTextData for u8 { impl DnsTextData for u8 {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
Ok(next_field(data)?.parse()?) Ok(next_field(data)?.parse()?)
} }
@ -14,7 +14,7 @@ impl DnsTextData for u8 {
} }
impl DnsTextData for u16 { impl DnsTextData for u16 {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
Ok(next_field(data)?.parse()?) Ok(next_field(data)?.parse()?)
} }
@ -24,7 +24,7 @@ impl DnsTextData for u16 {
} }
impl DnsTextData for u32 { impl DnsTextData for u32 {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
Ok(next_field(data)?.parse()?) Ok(next_field(data)?.parse()?)
} }
@ -35,7 +35,7 @@ impl DnsTextData for u32 {
/* only decimal representations are needed for octets */ /* only decimal representations are needed for octets */
impl DnsTextData for Ipv4Addr { impl DnsTextData for Ipv4Addr {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
Ok(next_field(data)?.parse()?) Ok(next_field(data)?.parse()?)
} }
@ -46,7 +46,7 @@ impl DnsTextData for Ipv4Addr {
/* representation as in RFC 3513: https://tools.ietf.org/html/rfc3513#section-2.2 */ /* representation as in RFC 3513: https://tools.ietf.org/html/rfc3513#section-2.2 */
impl DnsTextData for Ipv6Addr { impl DnsTextData for Ipv6Addr {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
Ok(next_field(data)?.parse()?) Ok(next_field(data)?.parse()?)
} }
@ -60,8 +60,9 @@ mod tests {
use super::*; use super::*;
fn deserialize<T: DnsTextData>(data: &str) -> ::errors::Result<T> { fn deserialize<T: DnsTextData>(data: &str) -> ::errors::Result<T> {
let context = DnsTextContext::new();
let mut data = data; let mut data = data;
let res = T::dns_parse(&mut data)?; let res = T::dns_parse(&context, &mut data)?;
let data = data.trim(); let data = data.trim();
ensure!(data.is_empty(), "didn't read data completely"); ensure!(data.is_empty(), "didn't read data completely");
Ok(res) Ok(res)

View File

@ -28,7 +28,7 @@ pub fn build(ast: &syn::DeriveInput) -> quote::Tokens {
let field_name = field.ident.as_ref().unwrap(); let field_name = field.ident.as_ref().unwrap();
parse_fields = quote!{#parse_fields parse_fields = quote!{#parse_fields
#field_name: DnsTextData::dns_parse(_data).with_context(|_| format!("failed parsing field {}::{}", stringify!(#name), stringify!(#field_name)))?, #field_name: DnsTextData::dns_parse(_context, _data).with_context(|_| format!("failed parsing field {}::{}", stringify!(#name), stringify!(#field_name)))?,
}; };
format_fields = quote!{#format_fields format_fields = quote!{#format_fields
@ -39,7 +39,7 @@ pub fn build(ast: &syn::DeriveInput) -> quote::Tokens {
quote!{ quote!{
#[allow(unused_imports)] #[allow(unused_imports)]
impl ::dnsbox_base::ser::DnsTextData for #name { impl ::dnsbox_base::ser::DnsTextData for #name {
fn dns_parse(_data: &mut &str) -> ::dnsbox_base::errors::Result<Self> { fn dns_parse(_context: &::dnsbox_base::ser::text::DnsTextContext, _data: &mut &str) -> ::dnsbox_base::errors::Result<Self> {
use dnsbox_base::failure::ResultExt; use dnsbox_base::failure::ResultExt;
use dnsbox_base::ser::DnsTextData; use dnsbox_base::ser::DnsTextData;
Ok(#name{ #parse_fields }) Ok(#name{ #parse_fields })