step
This commit is contained in:
parent
c4c84bd887
commit
302300c184
@ -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)))?;
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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)))?;
|
||||||
|
@ -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)
|
||||||
|
@ -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()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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()))
|
||||||
|
@ -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]
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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 })
|
||||||
|
Loading…
Reference in New Issue
Block a user