step
This commit is contained in:
parent
5a15519480
commit
af95fbdf67
@ -79,15 +79,6 @@ pub struct DnsName {
|
|||||||
total_len: u8,
|
total_len: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// names that should be written in canonical form for DNSSEC according
|
|
||||||
/// to https://tools.ietf.org/html/rfc4034#section-6.2
|
|
||||||
///
|
|
||||||
/// TODO: make it a newtype.
|
|
||||||
///
|
|
||||||
/// DnsCompressedName always needs to be written in canonical form for
|
|
||||||
/// DNSSEC.
|
|
||||||
pub type DnsCanonicalName = DnsName;
|
|
||||||
|
|
||||||
impl DnsName {
|
impl DnsName {
|
||||||
/// Create new name representing the DNS root (".")
|
/// Create new name representing the DNS root (".")
|
||||||
pub fn new_root() -> Self {
|
pub fn new_root() -> Self {
|
||||||
@ -274,6 +265,15 @@ impl DnsPacketData for DnsCompressedName {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// names that should be written in canonical form for DNSSEC according
|
||||||
|
/// to https://tools.ietf.org/html/rfc4034#section-6.2
|
||||||
|
///
|
||||||
|
/// TODO: make it a newtype.
|
||||||
|
///
|
||||||
|
/// DnsCompressedName always needs to be written in canonical form for
|
||||||
|
/// DNSSEC.
|
||||||
|
pub type DnsCanonicalName = DnsName;
|
||||||
|
|
||||||
/// Iterator type for [`DnsName::labels`]
|
/// Iterator type for [`DnsName::labels`]
|
||||||
///
|
///
|
||||||
/// [`DnsName::labels`]: struct.DnsName.html#method.labels
|
/// [`DnsName::labels`]: struct.DnsName.html#method.labels
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field, quoted};
|
use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field, quoted, parse_with};
|
||||||
|
|
||||||
impl DnsName {
|
impl DnsName {
|
||||||
/// Parse text representation of a domain name
|
/// Parse text representation of a domain name
|
||||||
@ -75,6 +75,14 @@ impl DnsTextData for DnsName {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ::std::str::FromStr for DnsName {
|
||||||
|
type Err = ::failure::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
|
parse_with(s, |data| DnsName::dns_parse(&DnsTextContext::new(), data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl DnsCompressedName {
|
impl DnsCompressedName {
|
||||||
/// Parse text representation of a domain name
|
/// Parse text representation of a domain name
|
||||||
pub fn parse(context: &DnsTextContext, value: &str) -> Result<Self>
|
pub fn parse(context: &DnsTextContext, value: &str) -> Result<Self>
|
||||||
@ -93,3 +101,11 @@ impl DnsTextData for DnsCompressedName {
|
|||||||
self.0.dns_format(f)
|
self.0.dns_format(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ::std::str::FromStr for DnsCompressedName {
|
||||||
|
type Err = ::failure::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
|
parse_with(s, |data| DnsCompressedName::dns_parse(&DnsTextContext::new(), data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,7 +23,7 @@ impl DnsPacketData for UriText {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||||
ensure!(self.0.is_empty(), "URI must not be empty");
|
ensure!(!self.0.is_empty(), "URI must not be empty");
|
||||||
packet.reserve(self.0.len());
|
packet.reserve(self.0.len());
|
||||||
packet.put_slice(&self.0);
|
packet.put_slice(&self.0);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -15,6 +15,7 @@ mod unsafe_ops;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
pub mod common_types;
|
pub mod common_types;
|
||||||
|
pub mod packet;
|
||||||
pub mod records;
|
pub mod records;
|
||||||
pub mod ser;
|
pub mod ser;
|
||||||
|
|
||||||
|
233
lib/dnsbox-base/src/packet/mod.rs
Normal file
233
lib/dnsbox-base/src/packet/mod.rs
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
use byteorder::ByteOrder;
|
||||||
|
use bytes::{Bytes, Buf, BufMut, BigEndian};
|
||||||
|
use common_types::{Type, Class, DnsCompressedName};
|
||||||
|
use errors::*;
|
||||||
|
use ser::RRData;
|
||||||
|
use ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||||
|
use std::io::Cursor;
|
||||||
|
use records::registry::deserialize_rr_data;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
|
pub enum QueryResponse {
|
||||||
|
Query,
|
||||||
|
Response,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for QueryResponse {
|
||||||
|
fn default() -> Self {
|
||||||
|
QueryResponse::Query
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
|
||||||
|
pub struct DnsHeaderFlags {
|
||||||
|
pub qr: QueryResponse,
|
||||||
|
pub opcode: u8, // 0...15
|
||||||
|
pub authoritative_answer: bool,
|
||||||
|
pub truncation: bool,
|
||||||
|
pub recursion_desired: bool,
|
||||||
|
pub recursion_available: bool,
|
||||||
|
pub reserved_bit9: bool,
|
||||||
|
pub authentic_data: bool,
|
||||||
|
pub checking_disabled: bool,
|
||||||
|
pub rcode: u8, // 0...15
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DnsPacketData for DnsHeaderFlags {
|
||||||
|
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
||||||
|
let raw = u16::deserialize(data)?;
|
||||||
|
let qr = if 0 == raw & 0x8000 { QueryResponse::Query } else { QueryResponse::Response };
|
||||||
|
let opcode = 0xf & (raw >> 11) as u8;
|
||||||
|
let authoritative_answer = 0 != raw & 0x0400;
|
||||||
|
let truncation = 0 != raw & 0x0200;
|
||||||
|
let recursion_desired = 0 != raw & 0x0100;
|
||||||
|
let recursion_available = 0 != raw & 0x0080;
|
||||||
|
let reserved_bit9 = 0 != raw & 0x0040;
|
||||||
|
let authentic_data = 0 != raw & 0x0020;
|
||||||
|
let checking_disabled = 0 != raw & 0x0010;
|
||||||
|
let rcode = 0xf & raw as u8;
|
||||||
|
Ok(DnsHeaderFlags{
|
||||||
|
qr,
|
||||||
|
opcode,
|
||||||
|
authoritative_answer,
|
||||||
|
truncation,
|
||||||
|
recursion_desired,
|
||||||
|
recursion_available,
|
||||||
|
reserved_bit9,
|
||||||
|
authentic_data,
|
||||||
|
checking_disabled,
|
||||||
|
rcode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||||
|
let flags: u16 = 0
|
||||||
|
| match self.qr {
|
||||||
|
QueryResponse::Query => 0,
|
||||||
|
QueryResponse::Response => 1,
|
||||||
|
}
|
||||||
|
| (((0xf & self.opcode) as u16) << 11)
|
||||||
|
| if self.authoritative_answer { 0x0400 } else { 0 }
|
||||||
|
| if self.truncation { 0x0200 } else { 0 }
|
||||||
|
| if self.recursion_desired { 0x0100 } else { 0 }
|
||||||
|
| if self.recursion_available { 0x0080 } else { 0 }
|
||||||
|
| if self.reserved_bit9 { 0x0040 } else { 0 }
|
||||||
|
| if self.authentic_data { 0x0020 } else { 0 }
|
||||||
|
| if self.checking_disabled { 0x0010 } else { 0 }
|
||||||
|
| (0xf & self.rcode) as u16
|
||||||
|
;
|
||||||
|
flags.serialize(context, packet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData)]
|
||||||
|
pub struct DnsHeader {
|
||||||
|
pub id: u16,
|
||||||
|
pub flags: DnsHeaderFlags,
|
||||||
|
pub qdcount: u16,
|
||||||
|
pub ancount: u16,
|
||||||
|
pub nscount: u16,
|
||||||
|
pub arcount: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData)]
|
||||||
|
pub struct Question {
|
||||||
|
pub qname: DnsCompressedName,
|
||||||
|
pub qtype: Type,
|
||||||
|
pub qclass: Class,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Resource {
|
||||||
|
pub name: DnsCompressedName,
|
||||||
|
pub class: Class,
|
||||||
|
pub ttl: u32,
|
||||||
|
pub data: Box<RRData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DnsPacketData for Resource {
|
||||||
|
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
||||||
|
let name = DnsCompressedName::deserialize(data)?;
|
||||||
|
let rr_type = Type::deserialize(data)?;
|
||||||
|
let class = Class::deserialize(data)?;
|
||||||
|
let ttl = u32::deserialize(data)?;
|
||||||
|
|
||||||
|
let rdlength = u16::deserialize(data)? as usize;
|
||||||
|
check_enough_data!(data, rdlength, "RDATA");
|
||||||
|
let pos = data.position() as usize;
|
||||||
|
let rrdata_from0 = data.get_ref().slice(0, pos + rdlength);
|
||||||
|
data.advance(rdlength);
|
||||||
|
let mut rrdata = Cursor::new(rrdata_from0);
|
||||||
|
rrdata.advance(pos);
|
||||||
|
let rd = deserialize_rr_data(ttl, class, rr_type, &mut rrdata)?;
|
||||||
|
|
||||||
|
ensure!(!rrdata.has_remaining(), "data remaining: {} bytes", rrdata.remaining());
|
||||||
|
|
||||||
|
Ok(Resource{
|
||||||
|
name,
|
||||||
|
class,
|
||||||
|
ttl,
|
||||||
|
data: rd,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||||
|
self.name.serialize(context, packet)?;
|
||||||
|
let rrtype = self.data.rr_type();
|
||||||
|
rrtype.serialize(context, packet)?;
|
||||||
|
self.class.serialize(context, packet)?;
|
||||||
|
self.ttl.serialize(context, packet)?;
|
||||||
|
|
||||||
|
let rdlen_pos = packet.len();
|
||||||
|
packet.reserve(2);
|
||||||
|
packet.put_u16::<BigEndian>(0); // stub
|
||||||
|
|
||||||
|
let rd_start = packet.len();
|
||||||
|
self.data.serialize_rr_data(context, packet)?;
|
||||||
|
let rd_end = packet.len();
|
||||||
|
let rdlen = rd_end - rd_start;
|
||||||
|
|
||||||
|
ensure!(rdlen < 0x1_0000, "RDATA too big");
|
||||||
|
|
||||||
|
// now patch length
|
||||||
|
BigEndian::write_u16(&mut packet[rdlen_pos..][..2], rdlen as u16);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct DnsPacket {
|
||||||
|
pub id: u16,
|
||||||
|
pub flags: DnsHeaderFlags,
|
||||||
|
pub question: Vec<Question>,
|
||||||
|
pub answer: Vec<Resource>,
|
||||||
|
pub authority: Vec<Resource>,
|
||||||
|
pub additional: Vec<Resource>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DnsPacket {
|
||||||
|
pub fn to_bytes(&self) -> Result<Vec<u8>> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let mut ctx = DnsPacketWriteContext::new();
|
||||||
|
ctx.enable_compression();
|
||||||
|
self.serialize(&mut ctx, &mut buf)?;
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DnsPacket {
|
||||||
|
fn default() -> Self {
|
||||||
|
DnsPacket{
|
||||||
|
id: 0,
|
||||||
|
flags: DnsHeaderFlags::default(),
|
||||||
|
question: Vec::new(),
|
||||||
|
answer: Vec::new(),
|
||||||
|
authority: Vec::new(),
|
||||||
|
additional: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DnsPacketData for DnsPacket {
|
||||||
|
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
||||||
|
let header = DnsHeader::deserialize(data)?;
|
||||||
|
Ok(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<_>>>()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||||
|
ensure!(self.question.len() < 0x1_0000, "too many question entries");
|
||||||
|
ensure!(self.answer.len() < 0x1_0000, "too many answer entries");
|
||||||
|
ensure!(self.authority.len() < 0x1_0000, "too many authority entries");
|
||||||
|
ensure!(self.additional.len() < 0x1_0000, "too many additional entries");
|
||||||
|
let header = DnsHeader{
|
||||||
|
id: self.id,
|
||||||
|
flags: self.flags,
|
||||||
|
qdcount: self.question.len() as u16,
|
||||||
|
ancount: self.answer.len() as u16,
|
||||||
|
nscount: self.authority.len() as u16,
|
||||||
|
arcount: self.additional.len() as u16,
|
||||||
|
};
|
||||||
|
header.serialize(context, packet)?;
|
||||||
|
for r in &self.question {
|
||||||
|
r.serialize(context, packet)?;
|
||||||
|
}
|
||||||
|
for r in &self.answer {
|
||||||
|
r.serialize(context, packet)?;
|
||||||
|
}
|
||||||
|
for r in &self.authority {
|
||||||
|
r.serialize(context, packet)?;
|
||||||
|
}
|
||||||
|
for r in &self.additional {
|
||||||
|
r.serialize(context, packet)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,48 +1,82 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
use bytes::{Bytes, BufMut, BigEndian};
|
use bytes::Bytes;
|
||||||
use common_types::{DnsName, Type, types, classes};
|
use common_types::{DnsName, DnsCompressedName, Class, Type, types, classes};
|
||||||
use records::registry;
|
use errors::*;
|
||||||
use ser::{RRDataText, text, packet};
|
use packet::*;
|
||||||
|
use records::{UnknownRecord, registry};
|
||||||
|
use ser::{RRData, RRDataText, text};
|
||||||
|
use ser::packet::{DnsPacketData, deserialize_with};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
fn fake_packet(rrtype: Type, raw: &[u8]) -> Bytes {
|
fn fake_packet(rrtype: Type, raw: &[u8]) -> Bytes {
|
||||||
let mut buf = Vec::from(&[
|
let p = DnsPacket{
|
||||||
// id: 0
|
question: vec![
|
||||||
0, 0,
|
Question {
|
||||||
// flags: 0, opcode: 0 (QUERY), no error
|
qname: ".".parse().unwrap(),
|
||||||
0, 0,
|
qtype: rrtype,
|
||||||
// qdcount: 1,
|
qclass: classes::IN,
|
||||||
0, 1,
|
|
||||||
// ancount: 1,
|
|
||||||
0, 1,
|
|
||||||
// nscount: 0,
|
|
||||||
0, 0,
|
|
||||||
// arcount: 0,
|
|
||||||
0, 0,
|
|
||||||
// question 0:
|
|
||||||
// - qname: "."
|
|
||||||
0,
|
|
||||||
// - qtype: A (1),
|
|
||||||
0, 1,
|
|
||||||
// - qclass: IN (1),
|
|
||||||
0, 1,
|
|
||||||
// answer 0:
|
|
||||||
// - name: "rec.test"
|
|
||||||
3, b'r', b'e', b'c', 4, b't', b'e', b's', b't', 0,
|
|
||||||
][..]);
|
|
||||||
// - type:
|
|
||||||
buf.put_u16::<BigEndian>(rrtype.0);
|
|
||||||
// - class: IN(1)
|
|
||||||
buf.put_u16::<BigEndian>(1);
|
|
||||||
// - ttl: 0
|
|
||||||
buf.put_u32::<BigEndian>(0);
|
|
||||||
let len = raw.len();
|
|
||||||
assert!(len <= 0xffff);
|
|
||||||
buf.put_u16::<BigEndian>(len as u16);
|
|
||||||
buf.extend_from_slice(raw);
|
|
||||||
|
|
||||||
Bytes::from(buf)
|
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
answer: vec![
|
||||||
|
Resource {
|
||||||
|
name: "rec.test.".parse().unwrap(),
|
||||||
|
class: classes::IN,
|
||||||
|
ttl: 0,
|
||||||
|
data: Box::new(UnknownRecord::new(rrtype, Bytes::from(raw))),
|
||||||
|
}
|
||||||
|
],
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
p.to_bytes().unwrap().into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_first_answer_rdata(packet: Bytes) -> Result<Bytes> {
|
||||||
|
let mut data = Cursor::new(packet);
|
||||||
|
let data = &mut data;
|
||||||
|
let header = DnsHeader::deserialize(data)?;
|
||||||
|
for _ in 0..header.qdcount {
|
||||||
|
Question::deserialize(data)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure!(header.ancount > 0, "need at least one answer");
|
||||||
|
|
||||||
|
let _name = DnsCompressedName::deserialize(data)?;
|
||||||
|
let _rr_type = Type::deserialize(data)?;
|
||||||
|
let _class = Class::deserialize(data)?;
|
||||||
|
let _ttl = u32::deserialize(data)?;
|
||||||
|
|
||||||
|
let rdlength = u16::deserialize(data)? as usize;
|
||||||
|
check_enough_data!(data, rdlength, "RDATA");
|
||||||
|
let pos = data.position() as usize;
|
||||||
|
|
||||||
|
Ok(data.get_ref().slice(pos, pos + rdlength))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialized_answer(rrdata: Box<RRData>) -> Result<Bytes> {
|
||||||
|
let p = DnsPacket{
|
||||||
|
question: vec![
|
||||||
|
Question {
|
||||||
|
qname: ".".parse().unwrap(),
|
||||||
|
qtype: rrdata.rr_type(),
|
||||||
|
qclass: classes::IN,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
answer: vec![
|
||||||
|
Resource {
|
||||||
|
name: "rec.test.".parse().unwrap(),
|
||||||
|
class: classes::IN,
|
||||||
|
ttl: 0,
|
||||||
|
data: rrdata,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
get_first_answer_rdata(p.to_bytes()?.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn check(q: Type, text_input: &'static str, canonic: Option<&'static str>, raw: &'static [u8]) {
|
fn check(q: Type, text_input: &'static str, canonic: Option<&'static str>, raw: &'static [u8]) {
|
||||||
let canonic = canonic.unwrap_or(text_input);
|
let canonic = canonic.unwrap_or(text_input);
|
||||||
@ -57,13 +91,8 @@ fn check(q: Type, text_input: &'static str, canonic: Option<&'static str>, raw:
|
|||||||
registry::parse_rr_data(&context, data)
|
registry::parse_rr_data(&context, data)
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
||||||
let d_wire = packet::deserialize_with(fake_packet(q, raw), |data| {
|
let d_wire_packet = deserialize_with(fake_packet(q, raw), DnsPacket::deserialize).unwrap();
|
||||||
// skip header, question, and header of answer
|
let d_wire = &d_wire_packet.answer[0].data;
|
||||||
use bytes::Buf;
|
|
||||||
data.advance(37);
|
|
||||||
|
|
||||||
registry::deserialize_rr_data(3600, classes::IN, q, data)
|
|
||||||
}).unwrap();
|
|
||||||
|
|
||||||
let d_zone_text = d_zone.text().unwrap();
|
let d_zone_text = d_zone.text().unwrap();
|
||||||
let d_wire_text = d_wire.text().unwrap();
|
let d_wire_text = d_wire.text().unwrap();
|
||||||
@ -71,7 +100,8 @@ fn check(q: Type, text_input: &'static str, canonic: Option<&'static str>, raw:
|
|||||||
assert_eq!(d_zone_text, d_wire_text, "data parsed from zone doesn't match data from wire");
|
assert_eq!(d_zone_text, d_wire_text, "data parsed from zone doesn't match data from wire");
|
||||||
assert_eq!(&d_zone_text.1, canonic);
|
assert_eq!(&d_zone_text.1, canonic);
|
||||||
|
|
||||||
// TODO: serialize d_zone_text and compare with raw.
|
let zone_as_wire = serialized_answer(d_zone).unwrap();
|
||||||
|
assert_eq!(zone_as_wire, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -2,7 +2,8 @@ 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, DnsPacketData};
|
use ser::{StaticRRData, packet, text};
|
||||||
|
use ser::packet::DnsPacketData;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
@ -86,4 +86,7 @@ impl RRDataText for UnknownRecord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RRData for UnknownRecord {
|
impl RRData for UnknownRecord {
|
||||||
|
fn clone_box(&self) -> Box<RRData> {
|
||||||
|
Box::new(self.clone()) as _
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,7 @@ where
|
|||||||
{
|
{
|
||||||
let mut c = Cursor::new(data);
|
let mut c = Cursor::new(data);
|
||||||
let result = parser(&mut c)?;
|
let result = parser(&mut c)?;
|
||||||
if c.remaining() != 0 {
|
ensure!(!c.has_remaining(), "data remaining: {} bytes", c.remaining());
|
||||||
bail!("data remaining: {} bytes", c.remaining())
|
|
||||||
}
|
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +93,14 @@ impl<T: DnsTextData + StaticRRData> RRDataText for T {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait RRData: RRDataPacket + RRDataText {
|
pub trait RRData: RRDataPacket + RRDataText + fmt::Debug {
|
||||||
|
fn clone_box(&self) -> Box<RRData>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Box<RRData> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
self.clone_box()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait StaticRRData: RRData {
|
pub trait StaticRRData: RRData {
|
||||||
|
@ -73,6 +73,9 @@ pub fn build(ast: &syn::DeriveInput) -> quote::Tokens {
|
|||||||
|
|
||||||
quote!{
|
quote!{
|
||||||
impl ::dnsbox_base::ser::RRData for #name {
|
impl ::dnsbox_base::ser::RRData for #name {
|
||||||
|
fn clone_box(&self) -> Box<::dnsbox_base::ser::RRData> {
|
||||||
|
Box::new(self.clone() as #name) as _
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::dnsbox_base::ser::StaticRRData for #name {
|
impl ::dnsbox_base::ser::StaticRRData for #name {
|
||||||
|
Loading…
Reference in New Issue
Block a user