step
This commit is contained in:
parent
302300c184
commit
e8df03648a
@ -318,9 +318,9 @@ mod tests {
|
||||
|
||||
/*
|
||||
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);
|
||||
}
|
||||
Ok(result)
|
||||
@ -328,7 +328,7 @@ mod tests {
|
||||
*/
|
||||
|
||||
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());
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -141,6 +141,10 @@ impl DnsName {
|
||||
if label_offsets.is_empty() {
|
||||
// root name
|
||||
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); }
|
||||
data[0] = 0;
|
||||
return (data, 0)
|
||||
|
@ -17,7 +17,7 @@ impl DnsName {
|
||||
{
|
||||
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);
|
||||
ensure!(new_pos < pos, "Compressed label offset to big");
|
||||
ensure!(new_pos < pos, "Compressed label offset too big: {} >= {}", new_pos, pos);
|
||||
pos = new_pos;
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ impl DnsPacketData for NsecTypeBitmap {
|
||||
check_enough_data!(data, window_len as usize, "nsec bitmap window length");
|
||||
for i in 0..window_len {
|
||||
let mut v = data.get_u8();
|
||||
for j in 0..7 {
|
||||
for j in 0..8 {
|
||||
if 0 != v & 0x80 {
|
||||
set.insert(Type(window_base + i*8 + j));
|
||||
}
|
||||
|
@ -7,4 +7,7 @@ pub use self::structs::*;
|
||||
pub use self::unknown::*;
|
||||
|
||||
#[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 records::structs;
|
||||
use ser::{RRData, StaticRRData};
|
||||
use ser::text::DnsTextContext;
|
||||
|
||||
// this should be enough for registered names
|
||||
const TYPE_NAME_MAX_LEN: usize = 16;
|
||||
@ -42,6 +43,26 @@ pub fn known_name_to_type(name: &str) -> Option<Type> {
|
||||
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> {
|
||||
let registry = registry();
|
||||
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 parse_rr_data(&self, context: &DnsTextContext, data: &mut &str) -> 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 _)
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -2,7 +2,7 @@ use bytes::{Bytes, Buf};
|
||||
use common_types::classes;
|
||||
use failure::ResultExt;
|
||||
use records::structs;
|
||||
use ser::{packet, text, StaticRRData};
|
||||
use ser::{packet, text, StaticRRData, DnsPacketData};
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
|
||||
@ -55,6 +55,11 @@ fn test_a() {
|
||||
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>()
|
||||
where
|
||||
T: StaticRRData + fmt::Debug + PartialEq
|
||||
@ -94,6 +99,7 @@ fn test_ds() {
|
||||
fn test_nsec() {
|
||||
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 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();
|
||||
}
|
||||
|
||||
@ -115,7 +121,7 @@ fn test_nsec3() {
|
||||
rrdata_parse::<structs::NSEC3>("1 2 300 - vv").unwrap_err();
|
||||
|
||||
// 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]
|
||||
|
@ -4,8 +4,9 @@ use common_types::binary::HEXLOWER_PERMISSIVE_ALLOW_WS;
|
||||
use errors::*;
|
||||
use failure::{ResultExt, Fail};
|
||||
use ser::packet::remaining_bytes;
|
||||
use ser::RRDataPacket;
|
||||
use ser::text::{DnsTextFormatter, next_field};
|
||||
use ser::{RRData, RRDataPacket, RRDataText};
|
||||
use ser::text::{DnsTextFormatter, DnsTextContext, next_field};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
|
||||
@ -36,16 +37,13 @@ impl UnknownRecord {
|
||||
let result = HEXLOWER_PERMISSIVE_ALLOW_WS.decode(data.as_bytes())
|
||||
.with_context(|e| e.context(format!("invalid hex: {:?}", data)))?;
|
||||
ensure!(len == result.len(), "length {} doesn't match length of encoded data {}", len, result.len());
|
||||
*data = ""; // read all data
|
||||
|
||||
Ok(UnknownRecord {
|
||||
rr_type,
|
||||
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 {
|
||||
@ -57,3 +55,29 @@ impl RRDataPacket for UnknownRecord {
|
||||
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::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>;
|
||||
}
|
||||
|
||||
pub fn deserialize<T>(data: Bytes) -> Result<T>
|
||||
pub fn deserialize_with<F, O>(data: Bytes, parser: F) -> Result<O>
|
||||
where
|
||||
T: DnsPacketData
|
||||
F: FnOnce(&mut Cursor<Bytes>) -> Result<O>,
|
||||
{
|
||||
let mut c = Cursor::new(data);
|
||||
let result = T::deserialize(&mut c)?;
|
||||
let result = parser(&mut c)?;
|
||||
if c.remaining() != 0 {
|
||||
bail!("data remaining: {}", c.remaining())
|
||||
bail!("data remaining: {} bytes", c.remaining())
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
@ -39,15 +39,12 @@ pub trait RRDataText {
|
||||
// format might fail if there is no (known) text representation.
|
||||
fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result;
|
||||
|
||||
fn rr_type_txt(&self) -> Cow<'static, str> {
|
||||
unimplemented!()
|
||||
}
|
||||
fn rr_type_txt(&self) -> Cow<'static, str>;
|
||||
|
||||
// (type, rrdata)
|
||||
fn text(&self) -> Result<(String, String)> {
|
||||
use std::fmt::Write;
|
||||
let mut buf = String::new();
|
||||
match write!(&mut buf, "{}", DnsDisplayRR(self)) {
|
||||
match self.dns_format_rr_data(&mut DnsTextFormatter::new(&mut buf)) {
|
||||
Ok(()) => {
|
||||
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 {
|
||||
self.dns_format(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DnsDisplayRR<'a, T: RRDataText + ?Sized + 'a>(pub &'a T);
|
||||
|
||||
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))
|
||||
fn rr_type_txt(&self) -> Cow<'static, str> {
|
||||
Cow::Borrowed(T::NAME)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,15 +54,15 @@ pub fn escape(data: &[u8]) -> String {
|
||||
|
||||
/// Each call to write!() makes sure a space is emitted to separate it
|
||||
/// from previous fields.
|
||||
pub struct DnsTextFormatter<'a, 'b: 'a> {
|
||||
f: &'a mut fmt::Formatter<'b>,
|
||||
pub struct DnsTextFormatter<'a> {
|
||||
w: &'a mut fmt::Write,
|
||||
need_space: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'b> DnsTextFormatter<'a, 'b> {
|
||||
pub fn new(f: &'a mut fmt::Formatter<'b>) -> Self {
|
||||
impl<'a> DnsTextFormatter<'a> {
|
||||
pub fn new(w: &'a mut fmt::Write) -> Self {
|
||||
DnsTextFormatter {
|
||||
f: f,
|
||||
w: w,
|
||||
need_space: false,
|
||||
}
|
||||
}
|
||||
@ -70,7 +70,7 @@ impl<'a, 'b> DnsTextFormatter<'a, 'b> {
|
||||
/// make sure a field separator was emitted
|
||||
pub fn next_field(&mut self) -> fmt::Result {
|
||||
if self.need_space {
|
||||
write!(self.f, " ")?;
|
||||
write!(self.w, " ")?;
|
||||
self.need_space = false;
|
||||
}
|
||||
Ok(())
|
||||
@ -83,13 +83,13 @@ impl<'a, 'b> DnsTextFormatter<'a, 'b> {
|
||||
|
||||
/// direct access to underlying output; you'll need to call
|
||||
/// `next_field` and `end_field` manually.
|
||||
pub fn inner(&mut self) -> &mut fmt::Formatter<'b> {
|
||||
self.f
|
||||
pub fn inner(&mut self) -> &mut fmt::Write {
|
||||
self.w
|
||||
}
|
||||
|
||||
pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
|
||||
self.next_field()?;
|
||||
self.f.write_fmt(args)?;
|
||||
self.w.write_fmt(args)?;
|
||||
self.end_field();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ pub fn build(ast: &syn::DeriveInput) -> quote::Tokens {
|
||||
let mut parse_fields = quote!{};
|
||||
for field in fields {
|
||||
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
|
||||
#field_name: DnsPacketData::deserialize(_data).context(#ctx_msg)?,
|
||||
|
Loading…
Reference in New Issue
Block a user