step
This commit is contained in:
parent
302300c184
commit
e8df03648a
@ -318,9 +318,9 @@ mod tests {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
fn deserialize(bytes: &'static [u8]) -> Result<DnsName> {
|
fn deserialize(bytes: &'static [u8]) -> Result<DnsName> {
|
||||||
let result = packet::deserialize::<DnsName>(Bytes::from_static(bytes))?;
|
let result = packet::deserialize_with(Bytes::from_static(bytes), DnsName::deserialize)?;
|
||||||
{
|
{
|
||||||
let check_result = packet::deserialize::<DnsName>(result.clone().encode()).unwrap();
|
let check_result = packet::deserialize_with(result.clone().encode(), DnsName::deserialize).unwrap();
|
||||||
assert_eq!(check_result, result);
|
assert_eq!(check_result, result);
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
@ -328,7 +328,7 @@ mod tests {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
fn de_uncompressed(bytes: &'static [u8]) -> Result<DnsName> {
|
fn de_uncompressed(bytes: &'static [u8]) -> Result<DnsName> {
|
||||||
let result = packet::deserialize::<DnsName>(Bytes::from_static(bytes))?;
|
let result = packet::deserialize_with(Bytes::from_static(bytes), DnsName::deserialize)?;
|
||||||
assert_eq!(bytes, result.clone().encode());
|
assert_eq!(bytes, result.clone().encode());
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
@ -141,6 +141,10 @@ impl DnsName {
|
|||||||
if label_offsets.is_empty() {
|
if label_offsets.is_empty() {
|
||||||
// root name
|
// root name
|
||||||
let mut data = data.unwrap_or_else(|_| BytesMut::with_capacity(new_len));
|
let mut data = data.unwrap_or_else(|_| BytesMut::with_capacity(new_len));
|
||||||
|
if data.len() < new_len {
|
||||||
|
let add = new_len - data.len();
|
||||||
|
data.reserve(add);
|
||||||
|
}
|
||||||
unsafe { data.set_len(new_len); }
|
unsafe { data.set_len(new_len); }
|
||||||
data[0] = 0;
|
data[0] = 0;
|
||||||
return (data, 0)
|
return (data, 0)
|
||||||
|
@ -17,7 +17,7 @@ impl DnsName {
|
|||||||
{
|
{
|
||||||
ensure!(pos + 1 < data.len(), "not enough data for compressed label");
|
ensure!(pos + 1 < data.len(), "not enough data for compressed label");
|
||||||
let new_pos = ((label_len as usize & 0x3f) << 8) | (data[pos + 1] as usize);
|
let new_pos = ((label_len as usize & 0x3f) << 8) | (data[pos + 1] as usize);
|
||||||
ensure!(new_pos < pos, "Compressed label offset to big");
|
ensure!(new_pos < pos, "Compressed label offset too big: {} >= {}", new_pos, pos);
|
||||||
pos = new_pos;
|
pos = new_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ impl DnsPacketData for NsecTypeBitmap {
|
|||||||
check_enough_data!(data, window_len as usize, "nsec bitmap window length");
|
check_enough_data!(data, window_len as usize, "nsec bitmap window length");
|
||||||
for i in 0..window_len {
|
for i in 0..window_len {
|
||||||
let mut v = data.get_u8();
|
let mut v = data.get_u8();
|
||||||
for j in 0..7 {
|
for j in 0..8 {
|
||||||
if 0 != v & 0x80 {
|
if 0 != v & 0x80 {
|
||||||
set.insert(Type(window_base + i*8 + j));
|
set.insert(Type(window_base + i*8 + j));
|
||||||
}
|
}
|
||||||
|
@ -7,4 +7,7 @@ pub use self::structs::*;
|
|||||||
pub use self::unknown::*;
|
pub use self::unknown::*;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod powerdns_tests;
|
||||||
|
755
lib/dnsbox-base/src/records/powerdns_tests.rs
Normal file
755
lib/dnsbox-base/src/records/powerdns_tests.rs
Normal file
File diff suppressed because one or more lines are too long
@ -9,6 +9,7 @@ use common_types::{Class, Type, types};
|
|||||||
use errors::*;
|
use errors::*;
|
||||||
use records::structs;
|
use records::structs;
|
||||||
use ser::{RRData, StaticRRData};
|
use ser::{RRData, StaticRRData};
|
||||||
|
use ser::text::DnsTextContext;
|
||||||
|
|
||||||
// this should be enough for registered names
|
// this should be enough for registered names
|
||||||
const TYPE_NAME_MAX_LEN: usize = 16;
|
const TYPE_NAME_MAX_LEN: usize = 16;
|
||||||
@ -42,6 +43,26 @@ pub fn known_name_to_type(name: &str) -> Option<Type> {
|
|||||||
Some(t)
|
Some(t)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deserialize_rr_data(ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor<Bytes>) -> Result<Box<RRData>> {
|
||||||
|
let registry = registry();
|
||||||
|
match registry.type_parser.get(&rr_type) {
|
||||||
|
Some(p) => p.deserialize_rr_data(ttl, rr_class, rr_type, data),
|
||||||
|
None => Ok(Box::new(super::UnknownRecord::deserialize(rr_type, data)?) as _),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_rr_data(context: &DnsTextContext, data: &mut &str) -> Result<Box<RRData>> {
|
||||||
|
let registry = registry();
|
||||||
|
let t = match context.record_type() {
|
||||||
|
Some(t) => t,
|
||||||
|
None => bail!("require record type to parse record data"),
|
||||||
|
};
|
||||||
|
match registry.type_parser.get(&t) {
|
||||||
|
Some(p) => p.parse_rr_data(context, data),
|
||||||
|
None => bail!("unknown type: {}", t),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn lookup_type_to_name(rrtype: Type) -> Option<&'static str> {
|
pub(crate) fn lookup_type_to_name(rrtype: Type) -> Option<&'static str> {
|
||||||
let registry = registry();
|
let registry = registry();
|
||||||
registry.type_names.get(&rrtype).map(|s| s as _)
|
registry.type_names.get(&rrtype).map(|s| s as _)
|
||||||
@ -61,12 +82,18 @@ trait RRDataTypeParse: 'static {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_rr_data(&self, ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor<Bytes>) -> Result<Box<RRData>>;
|
fn deserialize_rr_data(&self, ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor<Bytes>) -> Result<Box<RRData>>;
|
||||||
|
|
||||||
|
fn parse_rr_data(&self, context: &DnsTextContext, data: &mut &str) -> Result<Box<RRData>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: RRData + 'static> RRDataTypeParse for TagRRDataType<T> {
|
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>> {
|
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 _)
|
T::deserialize_rr_data(ttl, rr_class, rr_type, data).map(|d| Box::new(d) as _)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_rr_data(&self, context: &DnsTextContext, data: &mut &str) -> Result<Box<RRData>> {
|
||||||
|
T::dns_parse_rr_data(context, data).map(|d| Box::new(d) as _)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Registry {
|
struct Registry {
|
||||||
|
@ -2,7 +2,7 @@ use bytes::{Bytes, Buf};
|
|||||||
use common_types::classes;
|
use common_types::classes;
|
||||||
use failure::ResultExt;
|
use failure::ResultExt;
|
||||||
use records::structs;
|
use records::structs;
|
||||||
use ser::{packet, text, StaticRRData};
|
use ser::{packet, text, StaticRRData, DnsPacketData};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
@ -55,6 +55,11 @@ fn test_a() {
|
|||||||
check::<structs::A>("127.0.0.1", b"\x7f\x00\x00\x01").unwrap();
|
check::<structs::A>("127.0.0.1", b"\x7f\x00\x00\x01").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mx() {
|
||||||
|
check::<structs::MX>("10 mx.rec.test.", b"\x00\x0a\x02mx\x03rec\x04test\x00").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn test_txt_for<T>()
|
fn test_txt_for<T>()
|
||||||
where
|
where
|
||||||
T: StaticRRData + fmt::Debug + PartialEq
|
T: StaticRRData + fmt::Debug + PartialEq
|
||||||
@ -94,6 +99,7 @@ fn test_ds() {
|
|||||||
fn test_nsec() {
|
fn test_nsec() {
|
||||||
check::<structs::NSEC>("foo.bar. ", b"\x03foo\x03bar\x00").unwrap();
|
check::<structs::NSEC>("foo.bar. ", b"\x03foo\x03bar\x00").unwrap();
|
||||||
check::<structs::NSEC>("foo.bar. A NS ", b"\x03foo\x03bar\x00\x00\x01\x60").unwrap();
|
check::<structs::NSEC>("foo.bar. A NS ", b"\x03foo\x03bar\x00\x00\x01\x60").unwrap();
|
||||||
|
check::<structs::NSEC>("foo.bar. A NS SOA MX AAAA RRSIG NSEC DNSKEY ", b"\x03foo\x03bar\x00\x00\x07\x62\x01\x00\x08\x00\x03\x80").unwrap();
|
||||||
check::<structs::NSEC>("foo.bar. A NS TYPE256 TYPE65280 ", b"\x03foo\x03bar\x00\x00\x01\x60\x01\x01\x80\xff\x01\x80").unwrap();
|
check::<structs::NSEC>("foo.bar. A NS TYPE256 TYPE65280 ", b"\x03foo\x03bar\x00\x00\x01\x60\x01\x01\x80\xff\x01\x80").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,7 +121,7 @@ fn test_nsec3() {
|
|||||||
rrdata_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_with(Bytes::from_static(b"\x01\x02\x01\x2c\x00\x00"), structs::NSEC3::deserialize).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -4,8 +4,9 @@ use common_types::binary::HEXLOWER_PERMISSIVE_ALLOW_WS;
|
|||||||
use errors::*;
|
use errors::*;
|
||||||
use failure::{ResultExt, Fail};
|
use failure::{ResultExt, Fail};
|
||||||
use ser::packet::remaining_bytes;
|
use ser::packet::remaining_bytes;
|
||||||
use ser::RRDataPacket;
|
use ser::{RRData, RRDataPacket, RRDataText};
|
||||||
use ser::text::{DnsTextFormatter, next_field};
|
use ser::text::{DnsTextFormatter, DnsTextContext, next_field};
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
@ -36,16 +37,13 @@ impl UnknownRecord {
|
|||||||
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)))?;
|
||||||
ensure!(len == result.len(), "length {} doesn't match length of encoded data {}", len, result.len());
|
ensure!(len == result.len(), "length {} doesn't match length of encoded data {}", len, result.len());
|
||||||
|
*data = ""; // read all data
|
||||||
|
|
||||||
Ok(UnknownRecord {
|
Ok(UnknownRecord {
|
||||||
rr_type,
|
rr_type,
|
||||||
raw: result.into(),
|
raw: result.into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
|
||||||
write!(f, "TYPE{} \\# {} {}", self.rr_type.0, self.raw.len(), HEXLOWER_PERMISSIVE_ALLOW_WS.encode(&self.raw))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RRDataPacket for UnknownRecord {
|
impl RRDataPacket for UnknownRecord {
|
||||||
@ -57,3 +55,29 @@ impl RRDataPacket for UnknownRecord {
|
|||||||
self.rr_type
|
self.rr_type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl RRDataText for UnknownRecord {
|
||||||
|
fn dns_parse_rr_data(context: &DnsTextContext, data: &mut &str) -> Result<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let t = match context.record_type() {
|
||||||
|
Some(t) => t,
|
||||||
|
None => bail!("must parse DNS record with record type context"),
|
||||||
|
};
|
||||||
|
|
||||||
|
UnknownRecord::dns_parse(t, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// format might fail if there is no (known) text representation.
|
||||||
|
fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
||||||
|
write!(f, "\\# {} {}", self.raw.len(), HEXLOWER_PERMISSIVE_ALLOW_WS.encode(&self.raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rr_type_txt(&self) -> Cow<'static, str> {
|
||||||
|
Cow::Owned(self.rr_type.generic_name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RRData for UnknownRecord {
|
||||||
|
}
|
||||||
|
@ -4,4 +4,4 @@ mod rrdata;
|
|||||||
|
|
||||||
pub use self::packet::DnsPacketData;
|
pub use self::packet::DnsPacketData;
|
||||||
pub use self::text::DnsTextData;
|
pub use self::text::DnsTextData;
|
||||||
pub use self::rrdata::{RRDataPacket, RRData, StaticRRData};
|
pub use self::rrdata::{RRDataPacket, RRDataText, RRData, StaticRRData};
|
||||||
|
@ -8,14 +8,14 @@ pub trait DnsPacketData: Sized {
|
|||||||
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self>;
|
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize<T>(data: Bytes) -> Result<T>
|
pub fn deserialize_with<F, O>(data: Bytes, parser: F) -> Result<O>
|
||||||
where
|
where
|
||||||
T: DnsPacketData
|
F: FnOnce(&mut Cursor<Bytes>) -> Result<O>,
|
||||||
{
|
{
|
||||||
let mut c = Cursor::new(data);
|
let mut c = Cursor::new(data);
|
||||||
let result = T::deserialize(&mut c)?;
|
let result = parser(&mut c)?;
|
||||||
if c.remaining() != 0 {
|
if c.remaining() != 0 {
|
||||||
bail!("data remaining: {}", c.remaining())
|
bail!("data remaining: {} bytes", c.remaining())
|
||||||
}
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
@ -39,15 +39,12 @@ pub trait RRDataText {
|
|||||||
// format might fail if there is no (known) text representation.
|
// format might fail if there is no (known) text representation.
|
||||||
fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result;
|
fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result;
|
||||||
|
|
||||||
fn rr_type_txt(&self) -> Cow<'static, str> {
|
fn rr_type_txt(&self) -> Cow<'static, str>;
|
||||||
unimplemented!()
|
|
||||||
}
|
|
||||||
|
|
||||||
// (type, rrdata)
|
// (type, rrdata)
|
||||||
fn text(&self) -> Result<(String, String)> {
|
fn text(&self) -> Result<(String, String)> {
|
||||||
use std::fmt::Write;
|
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
match write!(&mut buf, "{}", DnsDisplayRR(self)) {
|
match self.dns_format_rr_data(&mut DnsTextFormatter::new(&mut buf)) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
return Ok((self.rr_type_txt().into(), buf))
|
return Ok((self.rr_type_txt().into(), buf))
|
||||||
},
|
},
|
||||||
@ -75,14 +72,9 @@ impl<T: DnsTextData + StaticRRData> RRDataText for T {
|
|||||||
fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
||||||
self.dns_format(f)
|
self.dns_format(f)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
fn rr_type_txt(&self) -> Cow<'static, str> {
|
||||||
pub struct DnsDisplayRR<'a, T: RRDataText + ?Sized + 'a>(pub &'a T);
|
Cow::Borrowed(T::NAME)
|
||||||
|
|
||||||
impl<'a, T: RRDataText + ?Sized + 'a> fmt::Display for DnsDisplayRR<'a, T> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
self.0.dns_format_rr_data(&mut DnsTextFormatter::new(f))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,15 +54,15 @@ pub fn escape(data: &[u8]) -> String {
|
|||||||
|
|
||||||
/// Each call to write!() makes sure a space is emitted to separate it
|
/// Each call to write!() makes sure a space is emitted to separate it
|
||||||
/// from previous fields.
|
/// from previous fields.
|
||||||
pub struct DnsTextFormatter<'a, 'b: 'a> {
|
pub struct DnsTextFormatter<'a> {
|
||||||
f: &'a mut fmt::Formatter<'b>,
|
w: &'a mut fmt::Write,
|
||||||
need_space: bool,
|
need_space: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b> DnsTextFormatter<'a, 'b> {
|
impl<'a> DnsTextFormatter<'a> {
|
||||||
pub fn new(f: &'a mut fmt::Formatter<'b>) -> Self {
|
pub fn new(w: &'a mut fmt::Write) -> Self {
|
||||||
DnsTextFormatter {
|
DnsTextFormatter {
|
||||||
f: f,
|
w: w,
|
||||||
need_space: false,
|
need_space: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -70,7 +70,7 @@ impl<'a, 'b> DnsTextFormatter<'a, 'b> {
|
|||||||
/// make sure a field separator was emitted
|
/// make sure a field separator was emitted
|
||||||
pub fn next_field(&mut self) -> fmt::Result {
|
pub fn next_field(&mut self) -> fmt::Result {
|
||||||
if self.need_space {
|
if self.need_space {
|
||||||
write!(self.f, " ")?;
|
write!(self.w, " ")?;
|
||||||
self.need_space = false;
|
self.need_space = false;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -83,13 +83,13 @@ impl<'a, 'b> DnsTextFormatter<'a, 'b> {
|
|||||||
|
|
||||||
/// direct access to underlying output; you'll need to call
|
/// direct access to underlying output; you'll need to call
|
||||||
/// `next_field` and `end_field` manually.
|
/// `next_field` and `end_field` manually.
|
||||||
pub fn inner(&mut self) -> &mut fmt::Formatter<'b> {
|
pub fn inner(&mut self) -> &mut fmt::Write {
|
||||||
self.f
|
self.w
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
|
pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
|
||||||
self.next_field()?;
|
self.next_field()?;
|
||||||
self.f.write_fmt(args)?;
|
self.w.write_fmt(args)?;
|
||||||
self.end_field();
|
self.end_field();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ pub fn build(ast: &syn::DeriveInput) -> quote::Tokens {
|
|||||||
let mut parse_fields = quote!{};
|
let mut parse_fields = quote!{};
|
||||||
for field in fields {
|
for field in fields {
|
||||||
let field_name = field.ident.as_ref().unwrap();
|
let field_name = field.ident.as_ref().unwrap();
|
||||||
let ctx_msg = format!("failed parsing field {}::{}", stringify!(#name), stringify!(#field_name));
|
let ctx_msg = format!("failed parsing field {}::{}", name, field_name);
|
||||||
|
|
||||||
parse_fields = quote!{#parse_fields
|
parse_fields = quote!{#parse_fields
|
||||||
#field_name: DnsPacketData::deserialize(_data).context(#ctx_msg)?,
|
#field_name: DnsPacketData::deserialize(_data).context(#ctx_msg)?,
|
||||||
|
Loading…
Reference in New Issue
Block a user