step
This commit is contained in:
parent
503bc29fcf
commit
36ee9e704a
@ -116,11 +116,11 @@ impl DnsTextData for Base64LongBlob {
|
||||
}
|
||||
}
|
||||
|
||||
// No length byte (or restriction), just all data to end of record. uses
|
||||
// base64 encoding for text representation, whitespace allowed, padding
|
||||
// required.
|
||||
//
|
||||
// No following field allowed, i.e. last field in the record.
|
||||
/// No length byte (or restriction), just all data to end of record. uses
|
||||
/// base64 encoding for text representation, whitespace allowed, padding
|
||||
/// required.
|
||||
///
|
||||
/// No following field allowed, i.e. last field in the record.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Base64RemainingBlob(Bytes);
|
||||
|
||||
@ -154,11 +154,13 @@ impl DnsTextData for Base64RemainingBlob {
|
||||
}
|
||||
}
|
||||
|
||||
/// No length byte (or restriction), just all data to end of record. uses
|
||||
/// hex encoding for text representation, whitespace allowed.
|
||||
///
|
||||
/// No following field allowed, i.e. last field in the record.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct HexRemainingBlob(Bytes);
|
||||
|
||||
// No length byte, just all data to end of record. uses hex encoding for
|
||||
// text representation (spaces are ignored - last field in record).
|
||||
impl DnsPacketData for HexRemainingBlob {
|
||||
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
||||
Ok(HexRemainingBlob(remaining_bytes(data)))
|
||||
|
@ -200,6 +200,13 @@ impl DnsPacketData for DnsName {
|
||||
#[derive(Clone)]
|
||||
pub struct DnsCompressedName(pub DnsName);
|
||||
|
||||
impl DnsCompressedName {
|
||||
/// Create new name representing the DNS root (".")
|
||||
pub fn new_root() -> Self {
|
||||
DnsCompressedName(DnsName::new_root())
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for DnsCompressedName {
|
||||
type Target = DnsName;
|
||||
|
||||
|
@ -1,12 +1,14 @@
|
||||
use byteorder::ByteOrder;
|
||||
use bytes::{Bytes, Buf, BufMut, BigEndian};
|
||||
use common_types::{Type, Class, DnsCompressedName};
|
||||
use common_types::{Type, Class, DnsCompressedName, types};
|
||||
use errors::*;
|
||||
use ser::RRData;
|
||||
use ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||
use std::io::Cursor;
|
||||
use records::registry::deserialize_rr_data;
|
||||
|
||||
pub mod opt;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub enum QueryResponse {
|
||||
Query,
|
||||
@ -164,16 +166,40 @@ pub struct DnsPacket {
|
||||
pub answer: Vec<Resource>,
|
||||
pub authority: Vec<Resource>,
|
||||
pub additional: Vec<Resource>,
|
||||
pub opt: Option<opt::OptResult<opt::Opt>>,
|
||||
}
|
||||
|
||||
impl DnsPacket {
|
||||
pub fn to_bytes(&self) -> Result<Vec<u8>> {
|
||||
/// also serializes OPT before conversion if present
|
||||
pub fn to_bytes(&mut self) -> Result<Vec<u8>> {
|
||||
if self.opt.is_some() {
|
||||
// delete other OPTs, so only call it if there is a "new" OPT
|
||||
self.serialize_opt()?;
|
||||
}
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let mut ctx = DnsPacketWriteContext::new();
|
||||
ctx.enable_compression();
|
||||
self.serialize(&mut ctx, &mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// overwrites existing OPT records, and serializes a new one (if
|
||||
/// self.opt is not None)
|
||||
pub fn serialize_opt(&mut self) -> Result<()> {
|
||||
// delete all additional OPT records
|
||||
self.additional.retain(|r| r.data.rr_type() != types::OPT);
|
||||
|
||||
match self.opt.take() {
|
||||
Some(Err(e)) => bail!("can't serialize broken OPT: {:?}", e),
|
||||
Some(Ok(opt)) => {
|
||||
self.additional.push(opt.serialize()?);
|
||||
},
|
||||
None => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DnsPacket {
|
||||
@ -185,6 +211,7 @@ impl Default for DnsPacket {
|
||||
answer: Vec::new(),
|
||||
authority: Vec::new(),
|
||||
additional: Vec::new(),
|
||||
opt: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -192,14 +219,31 @@ impl Default for DnsPacket {
|
||||
impl DnsPacketData for DnsPacket {
|
||||
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
||||
let header = DnsHeader::deserialize(data)?;
|
||||
Ok(DnsPacket {
|
||||
let mut p = DnsPacket {
|
||||
id: header.id,
|
||||
flags: header.flags,
|
||||
question: (0..header.qdcount).map(|_| Question::deserialize(data)).collect::<Result<Vec<_>>>()?,
|
||||
answer: (0..header.ancount).map(|_| Resource::deserialize(data)).collect::<Result<Vec<_>>>()?,
|
||||
authority: (0..header.nscount).map(|_| Resource::deserialize(data)).collect::<Result<Vec<_>>>()?,
|
||||
additional: (0..header.arcount).map(|_| Resource::deserialize(data)).collect::<Result<Vec<_>>>()?,
|
||||
})
|
||||
opt: None,
|
||||
};
|
||||
|
||||
let mut opt_resource_ndx = None;
|
||||
|
||||
for (i, r) in p.additional.iter().enumerate() {
|
||||
if r.data.rr_type() == types::OPT {
|
||||
ensure!(opt_resource_ndx.is_none(), "multiple OPT resource records");
|
||||
opt_resource_ndx = Some(i);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ndx) = opt_resource_ndx {
|
||||
let opt_rr = p.additional.remove(ndx);
|
||||
p.opt = Some(opt::Opt::deserialize(&opt_rr)?);
|
||||
}
|
||||
|
||||
Ok(p)
|
||||
}
|
||||
|
||||
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||
|
285
lib/dnsbox-base/src/packet/opt.rs
Normal file
285
lib/dnsbox-base/src/packet/opt.rs
Normal file
@ -0,0 +1,285 @@
|
||||
use byteorder::ByteOrder;
|
||||
use bytes::{Bytes, Buf, BufMut, BigEndian};
|
||||
use common_types::{DnsCompressedName, Class, types};
|
||||
use errors::*;
|
||||
use packet::Resource;
|
||||
use records::UnknownRecord;
|
||||
use ser::packet::{DnsPacketData, DnsPacketWriteContext, get_blob, remaining_bytes};
|
||||
use std::io::{Cursor, Read};
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub enum OptError {
|
||||
UnknownVersion,
|
||||
InvalidData,
|
||||
}
|
||||
|
||||
/// parsing OPT can fail without having to throw away the packet
|
||||
/// completely; just fail in some way
|
||||
///
|
||||
/// TODO: define some useful error...
|
||||
pub type OptResult<T> = ::std::result::Result<T, OptError>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct OptFlag(pub u16);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct OptFlags(pub u16);
|
||||
|
||||
impl ::std::ops::BitOr<OptFlag> for OptFlags {
|
||||
type Output = bool;
|
||||
|
||||
fn bitor(self, rhs: OptFlag) -> Self::Output {
|
||||
0 != self.0 & rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::ops::BitOr<OptFlags> for OptFlag {
|
||||
type Output = bool;
|
||||
|
||||
fn bitor(self, rhs: OptFlags) -> Self::Output {
|
||||
0 != self.0 & rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum DnsOption {
|
||||
/// DNS Name Server Identifier (0x0003)
|
||||
NSID(Bytes),
|
||||
// DAU (0x0005)
|
||||
// DHU (0x0006)
|
||||
// N3U (0x0007)
|
||||
// edns-client-subnet (0x0008)
|
||||
ClientSubnet {
|
||||
source_prefix_length: u8,
|
||||
scope_prefix_length: u8,
|
||||
addr: IpAddr,
|
||||
},
|
||||
// EDNS EXPIRE (0x0009)
|
||||
// COOKIE (0x000a)
|
||||
// edns-tcp-keepalive (0x000b)
|
||||
// Padding (0x000c)
|
||||
// CHAIN (0x000d)
|
||||
// edns-key-tag (0x000e)
|
||||
// DeviceID (26946 = 0x6942)
|
||||
Unknown {
|
||||
code: u16,
|
||||
data: Bytes,
|
||||
}
|
||||
}
|
||||
|
||||
impl DnsOption {
|
||||
fn parse_client_subnet(data: &mut Cursor<Bytes>) -> Result<Self> {
|
||||
let addr_family = u16::deserialize(data)?;
|
||||
let source_prefix_length = u8::deserialize(data)?;
|
||||
let scope_prefix_length = u8::deserialize(data)?;
|
||||
let addr_prefix_len = ((source_prefix_length + 7) / 8) as usize;
|
||||
ensure!(scope_prefix_length <= source_prefix_length, "scope prefix {} > source prefix {}", scope_prefix_length, source_prefix_length);
|
||||
let addr = match addr_family {
|
||||
1 => {
|
||||
ensure!(source_prefix_length <= 32, "invalid prefix for IPv4");
|
||||
let mut o = [0u8; 4];
|
||||
data.read_exact(&mut o[0..addr_prefix_len])?;
|
||||
if 0 != source_prefix_length % 8 {
|
||||
let mask = 0xff >> (source_prefix_length % 8);
|
||||
ensure!(0 == o[addr_prefix_len - 1] & mask, "non-zero padding");
|
||||
}
|
||||
IpAddr::V4(Ipv4Addr::from(o))
|
||||
},
|
||||
2 => {
|
||||
ensure!(source_prefix_length <= 128, "invalid prefix for IPv6");
|
||||
let mut o = [0u8; 16];
|
||||
data.read_exact(&mut o[0..addr_prefix_len])?;
|
||||
if 0 != source_prefix_length % 8 {
|
||||
let mask = 0xff >> (source_prefix_length % 8);
|
||||
ensure!(0 == o[addr_prefix_len - 1] & mask, "non-zero padding");
|
||||
}
|
||||
IpAddr::V6(Ipv6Addr::from(o))
|
||||
},
|
||||
_ => {
|
||||
bail!("unknown address family {}", addr_family);
|
||||
},
|
||||
};
|
||||
Ok(DnsOption::ClientSubnet{
|
||||
source_prefix_length,
|
||||
scope_prefix_length,
|
||||
addr,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_opt(code: u16, opt_data: Bytes) -> Result<Self> {
|
||||
let mut data = Cursor::new(opt_data);
|
||||
|
||||
let result = (|| Ok(match code {
|
||||
0x0003 => DnsOption::NSID(remaining_bytes(&mut data)),
|
||||
0x0008 => DnsOption::parse_client_subnet(&mut data)?,
|
||||
_ => bail!("unknown option {}", code),
|
||||
}))()?;
|
||||
|
||||
ensure!(!data.has_remaining(), "option data remaining: {} bytes", data.remaining());
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn write_opt_data(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||
match *self {
|
||||
DnsOption::NSID(ref id) => {
|
||||
packet.reserve(id.len());
|
||||
packet.put_slice(id);
|
||||
},
|
||||
DnsOption::ClientSubnet{source_prefix_length, scope_prefix_length, ref addr} => {
|
||||
let addr_prefix_len = ((source_prefix_length + 7) / 8) as usize;
|
||||
packet.reserve(4 + addr_prefix_len);
|
||||
packet.put_u16::<BigEndian>(match *addr {
|
||||
IpAddr::V4(_) => 1,
|
||||
IpAddr::V6(_) => 2,
|
||||
});
|
||||
packet.put_u8(source_prefix_length);
|
||||
packet.put_u8(scope_prefix_length);
|
||||
|
||||
match *addr {
|
||||
IpAddr::V4(ref addr) => {
|
||||
let o = addr.octets();
|
||||
packet.put_slice(&o[..addr_prefix_len]);
|
||||
},
|
||||
IpAddr::V6(ref addr) => {
|
||||
let o = addr.octets();
|
||||
packet.put_slice(&o[..addr_prefix_len]);
|
||||
},
|
||||
}
|
||||
},
|
||||
DnsOption::Unknown{ref data, ..} => {
|
||||
packet.reserve(data.len());
|
||||
packet.put_slice(data);
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DnsPacketData for DnsOption {
|
||||
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
||||
let code = u16::deserialize(data)?;
|
||||
let opt_len = u16::deserialize(data)? as usize;
|
||||
|
||||
let opt_data = get_blob(data, opt_len)?;
|
||||
|
||||
DnsOption::parse_opt(code, opt_data.clone()).or_else(|_| {
|
||||
Ok(DnsOption::Unknown{
|
||||
code: code,
|
||||
data: opt_data,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||
let code: u16 = match *self {
|
||||
DnsOption::NSID(_) => 0x0003,
|
||||
DnsOption::ClientSubnet{..} => 0x0008,
|
||||
DnsOption::Unknown{code, ..} => code,
|
||||
};
|
||||
code.serialize(context, packet)?;
|
||||
|
||||
let opt_len_pos = packet.len();
|
||||
packet.reserve(2);
|
||||
packet.put_u16::<BigEndian>(0); // stub
|
||||
|
||||
let opt_start = packet.len();
|
||||
self.write_opt_data(context, packet)?;
|
||||
let opt_end = packet.len();
|
||||
let opt_len = opt_end - opt_start;
|
||||
|
||||
ensure!(opt_len < 0x1_0000, "OPTION DATA too big");
|
||||
|
||||
// now patch length
|
||||
BigEndian::write_u16(&mut packet[opt_len_pos..][..2], opt_len as u16);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub const DNSSEC_OK: OptFlag = OptFlag(0x8000);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Opt {
|
||||
// "CLASS"
|
||||
pub udp_payload_size: u16,
|
||||
// "TTL"
|
||||
pub extended_rcode_high: u8,
|
||||
pub version: u8,
|
||||
pub flags: OptFlags,
|
||||
|
||||
// RDATA
|
||||
pub options: Vec<DnsOption>,
|
||||
}
|
||||
|
||||
impl Opt {
|
||||
fn deserialize_options(ur: &UnknownRecord) -> Result<Vec<DnsOption>> {
|
||||
let mut data = Cursor::new(ur.raw().clone());
|
||||
let mut options = Vec::new();
|
||||
while data.has_remaining() {
|
||||
options.push(DnsOption::deserialize(&mut data)?);
|
||||
}
|
||||
|
||||
Ok(options)
|
||||
}
|
||||
|
||||
pub fn deserialize(r: &Resource) -> Result<OptResult<Self>> {
|
||||
let udp_payload_size = r.class.0;
|
||||
let extended_rcode_high = (r.ttl >> 24) as u8;
|
||||
let version = (r.ttl >> 16) as u8;
|
||||
let flags = OptFlags(r.ttl as u16);
|
||||
|
||||
if version > 0 { return Ok(Err(OptError::UnknownVersion)); }
|
||||
|
||||
let ur = match r.data.as_any().downcast_ref::<UnknownRecord>() {
|
||||
Some(ur) => ur,
|
||||
None => bail!("need to parse OPT from UnknownRecord"),
|
||||
};
|
||||
|
||||
let options = match Opt::deserialize_options(ur) {
|
||||
Ok(options) => options,
|
||||
Err(_) => return Ok(Err(OptError::InvalidData)),
|
||||
};
|
||||
|
||||
Ok(Ok(Opt {
|
||||
udp_payload_size,
|
||||
extended_rcode_high,
|
||||
version,
|
||||
flags,
|
||||
options,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn serialize(&self) -> Result<Resource> {
|
||||
let mut data = Vec::new();
|
||||
let mut ctx = DnsPacketWriteContext::new();
|
||||
|
||||
for o in &self.options {
|
||||
o.serialize(&mut ctx, &mut data)?;
|
||||
}
|
||||
let ur = UnknownRecord::new(types::OPT, data.into());
|
||||
|
||||
let ttl = ((self.extended_rcode_high as u32) << 24)
|
||||
| ((self.version as u32) << 16)
|
||||
| self.flags.0 as u32;
|
||||
Ok(Resource{
|
||||
name: DnsCompressedName::new_root(),
|
||||
class: Class(self.udp_payload_size),
|
||||
ttl: ttl,
|
||||
data: Box::new(ur) as _,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Opt {
|
||||
fn default() -> Self {
|
||||
Opt {
|
||||
udp_payload_size: 500,
|
||||
extended_rcode_high: 0,
|
||||
version: 0,
|
||||
flags: OptFlags(0),
|
||||
options: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
@ -4,13 +4,14 @@ use bytes::Bytes;
|
||||
use common_types::{DnsName, DnsCompressedName, Class, Type, types, classes};
|
||||
use errors::*;
|
||||
use packet::*;
|
||||
use records::{UnknownRecord, registry};
|
||||
use packet::opt::{Opt, DnsOption};
|
||||
use records::{UnknownRecord, registry, A};
|
||||
use ser::{RRData, RRDataText, text};
|
||||
use ser::packet::{DnsPacketData, deserialize_with};
|
||||
use std::io::Cursor;
|
||||
|
||||
fn fake_packet(rrtype: Type, raw: &[u8]) -> Bytes {
|
||||
let p = DnsPacket{
|
||||
let mut p = DnsPacket{
|
||||
question: vec![
|
||||
Question {
|
||||
qname: ".".parse().unwrap(),
|
||||
@ -55,7 +56,7 @@ fn get_first_answer_rdata(packet: Bytes) -> Result<Bytes> {
|
||||
}
|
||||
|
||||
fn serialized_answer(rrdata: Box<RRData>) -> Result<Bytes> {
|
||||
let p = DnsPacket{
|
||||
let mut p = DnsPacket{
|
||||
question: vec![
|
||||
Question {
|
||||
qname: ".".parse().unwrap(),
|
||||
@ -841,3 +842,57 @@ fn test_invalid_data_checks() {
|
||||
check_invalid_zone(types::CNAME, "123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012.");
|
||||
check_invalid_zone(types::SOA, "ns.rec.test hostmaster.test.rec 20130512010 3600 3600 604800 120"); // too long serial
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_record_in() {
|
||||
let p = deserialize_with(
|
||||
Bytes::from_static(b"\xf0\x01\x01\x00\x00\x01\x00\x01\x00\x00\x00\x01\x03www\x08powerdns\x03com\x00\x00\x01\x00\x01\x03www\x08powerdns\x03com\x00\x00\x01\x00\x01\x00\x00\x00\x10\x00\x04\x7f\x00\x00\x01\x00\x00\x29\x05\x00\x00\x00\x00\x00\x00\x0c\x00\x03\x00\x08powerdns"),
|
||||
DnsPacket::deserialize
|
||||
).unwrap();
|
||||
|
||||
let opt = p.opt.unwrap().unwrap();
|
||||
|
||||
assert_eq!(opt.udp_payload_size, 1280);
|
||||
assert_eq!(opt.options, vec![
|
||||
DnsOption::NSID(Bytes::from_static(b"powerdns")),
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opt_record_out() {
|
||||
let mut p = DnsPacket{
|
||||
id: 0xf001,
|
||||
flags: DnsHeaderFlags {
|
||||
recursion_desired: true,
|
||||
.. Default::default()
|
||||
},
|
||||
question: vec![
|
||||
Question {
|
||||
qname: "www.powerdns.com.".parse().unwrap(),
|
||||
qtype: types::A,
|
||||
qclass: classes::IN,
|
||||
}
|
||||
],
|
||||
answer: vec![
|
||||
Resource {
|
||||
name: "www.powerdns.com.".parse().unwrap(),
|
||||
class: classes::IN,
|
||||
ttl: 16,
|
||||
data: Box::new(A { addr: "127.0.0.1".parse().unwrap() }),
|
||||
}
|
||||
],
|
||||
opt: Some(Ok(Opt {
|
||||
udp_payload_size: 1280,
|
||||
options: vec![
|
||||
DnsOption::NSID(Bytes::from_static(b"powerdns")),
|
||||
],
|
||||
.. Default::default()
|
||||
})),
|
||||
.. Default::default()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
&p.to_bytes().unwrap() as &[u8],
|
||||
b"\xf0\x01\x01\x00\x00\x01\x00\x01\x00\x00\x00\x01\x03www\x08powerdns\x03com\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x00\x10\x00\x04\x7f\x00\x00\x01\x00\x00\x29\x05\x00\x00\x00\x00\x00\x00\x0c\x00\x03\x00\x08powerdns" as &[u8]
|
||||
);
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
#[RRClass(IN)]
|
||||
pub struct A {
|
||||
addr: Ipv4Addr,
|
||||
pub addr: Ipv4Addr,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
|
@ -48,6 +48,13 @@ where
|
||||
let d2: T = rrdata_parse(txt).context("couldn't parse text record")?;
|
||||
ensure!(d1 == d2, "decoded data not equal: {:?} != {:?}", d1, d2);
|
||||
|
||||
let d1_text = d1.text().unwrap();
|
||||
let d2_text = d2.text().unwrap();
|
||||
let canon_text = (T::NAME.to_owned(), canon.into());
|
||||
|
||||
ensure!(d1_text == canon_text, "re-formatted binary record not equal to canonical representation: {:?} != {:?}", d1_text, canon_text);
|
||||
ensure!(d2_text == canon_text, "re-formatted text record not equal to canonical representation: {:?} != {:?}", d2_text, canon_text);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -66,10 +73,10 @@ where
|
||||
T: StaticRRData + fmt::Debug + PartialEq
|
||||
{
|
||||
// at least one "segment" (which could be empty)
|
||||
check2::<T>(r#" "" "#, b"", r#" "" "#).unwrap_err();
|
||||
check2::<T>(r#""#, b"\x00", r#" "" "#).unwrap_err();
|
||||
check2::<T>(r#" "" "#, b"", r#""""#).unwrap_err();
|
||||
check2::<T>(r#""#, b"\x00", r#""""#).unwrap_err();
|
||||
// one empty segment
|
||||
check2::<T>(r#" "" "#, b"\x00", r#" "" "#).unwrap();
|
||||
check2::<T>(r#" "" "#, b"\x00", r#""""#).unwrap();
|
||||
// one segment
|
||||
check::<T>(r#" "foo" "#, b"\x03foo").unwrap();
|
||||
// two segments
|
||||
|
@ -24,6 +24,10 @@ impl UnknownRecord {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw(&self) -> &Bytes {
|
||||
&self.raw
|
||||
}
|
||||
|
||||
pub fn deserialize(rr_type: Type, data: &mut ::std::io::Cursor<Bytes>) -> Result<Self> {
|
||||
Ok(UnknownRecord::new(rr_type, remaining_bytes(data)))
|
||||
}
|
||||
@ -89,4 +93,8 @@ impl RRData for UnknownRecord {
|
||||
fn clone_box(&self) -> Box<RRData> {
|
||||
Box::new(self.clone()) as _
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &::std::any::Any {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,13 @@
|
||||
use bytes::Bytes;
|
||||
use common_types::{Class, Type, classes};
|
||||
use errors::*;
|
||||
use records::UnknownRecord;
|
||||
use ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||
use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext};
|
||||
use std::any::Any;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
use records::UnknownRecord;
|
||||
|
||||
pub trait RRDataPacket {
|
||||
fn deserialize_rr_data(ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor<Bytes>) -> Result<Self>
|
||||
@ -93,8 +94,10 @@ impl<T: DnsTextData + StaticRRData> RRDataText for T {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RRData: RRDataPacket + RRDataText + fmt::Debug {
|
||||
pub trait RRData: RRDataPacket + RRDataText + fmt::Debug + 'static {
|
||||
fn clone_box(&self) -> Box<RRData>;
|
||||
|
||||
fn as_any(&self) -> &Any;
|
||||
}
|
||||
|
||||
impl Clone for Box<RRData> {
|
||||
|
@ -76,6 +76,10 @@ pub fn build(ast: &syn::DeriveInput) -> quote::Tokens {
|
||||
fn clone_box(&self) -> Box<::dnsbox_base::ser::RRData> {
|
||||
Box::new(self.clone() as #name) as _
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &::std::any::Any {
|
||||
self as _
|
||||
}
|
||||
}
|
||||
|
||||
impl ::dnsbox_base::ser::StaticRRData for #name {
|
||||
|
Loading…
Reference in New Issue
Block a user