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
|
/// No length byte (or restriction), just all data to end of record. uses
|
||||||
// base64 encoding for text representation, whitespace allowed, padding
|
/// base64 encoding for text representation, whitespace allowed, padding
|
||||||
// required.
|
/// required.
|
||||||
//
|
///
|
||||||
// No following field allowed, i.e. last field in the record.
|
/// No following field allowed, i.e. last field in the record.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct Base64RemainingBlob(Bytes);
|
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)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct HexRemainingBlob(Bytes);
|
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 {
|
impl DnsPacketData for HexRemainingBlob {
|
||||||
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
||||||
Ok(HexRemainingBlob(remaining_bytes(data)))
|
Ok(HexRemainingBlob(remaining_bytes(data)))
|
||||||
|
@ -200,6 +200,13 @@ impl DnsPacketData for DnsName {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DnsCompressedName(pub DnsName);
|
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 {
|
impl Deref for DnsCompressedName {
|
||||||
type Target = DnsName;
|
type Target = DnsName;
|
||||||
|
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
use byteorder::ByteOrder;
|
use byteorder::ByteOrder;
|
||||||
use bytes::{Bytes, Buf, BufMut, BigEndian};
|
use bytes::{Bytes, Buf, BufMut, BigEndian};
|
||||||
use common_types::{Type, Class, DnsCompressedName};
|
use common_types::{Type, Class, DnsCompressedName, types};
|
||||||
use errors::*;
|
use errors::*;
|
||||||
use ser::RRData;
|
use ser::RRData;
|
||||||
use ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
use ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use records::registry::deserialize_rr_data;
|
use records::registry::deserialize_rr_data;
|
||||||
|
|
||||||
|
pub mod opt;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
pub enum QueryResponse {
|
pub enum QueryResponse {
|
||||||
Query,
|
Query,
|
||||||
@ -164,16 +166,40 @@ pub struct DnsPacket {
|
|||||||
pub answer: Vec<Resource>,
|
pub answer: Vec<Resource>,
|
||||||
pub authority: Vec<Resource>,
|
pub authority: Vec<Resource>,
|
||||||
pub additional: Vec<Resource>,
|
pub additional: Vec<Resource>,
|
||||||
|
pub opt: Option<opt::OptResult<opt::Opt>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DnsPacket {
|
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 buf = Vec::new();
|
||||||
let mut ctx = DnsPacketWriteContext::new();
|
let mut ctx = DnsPacketWriteContext::new();
|
||||||
ctx.enable_compression();
|
ctx.enable_compression();
|
||||||
self.serialize(&mut ctx, &mut buf)?;
|
self.serialize(&mut ctx, &mut buf)?;
|
||||||
Ok(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 {
|
impl Default for DnsPacket {
|
||||||
@ -185,6 +211,7 @@ impl Default for DnsPacket {
|
|||||||
answer: Vec::new(),
|
answer: Vec::new(),
|
||||||
authority: Vec::new(),
|
authority: Vec::new(),
|
||||||
additional: Vec::new(),
|
additional: Vec::new(),
|
||||||
|
opt: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -192,14 +219,31 @@ impl Default for DnsPacket {
|
|||||||
impl DnsPacketData for DnsPacket {
|
impl DnsPacketData for DnsPacket {
|
||||||
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
||||||
let header = DnsHeader::deserialize(data)?;
|
let header = DnsHeader::deserialize(data)?;
|
||||||
Ok(DnsPacket {
|
let mut p = DnsPacket {
|
||||||
id: header.id,
|
id: header.id,
|
||||||
flags: header.flags,
|
flags: header.flags,
|
||||||
question: (0..header.qdcount).map(|_| Question::deserialize(data)).collect::<Result<Vec<_>>>()?,
|
question: (0..header.qdcount).map(|_| Question::deserialize(data)).collect::<Result<Vec<_>>>()?,
|
||||||
answer: (0..header.ancount).map(|_| Resource::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<_>>>()?,
|
authority: (0..header.nscount).map(|_| Resource::deserialize(data)).collect::<Result<Vec<_>>>()?,
|
||||||
additional: (0..header.arcount).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<()> {
|
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 common_types::{DnsName, DnsCompressedName, Class, Type, types, classes};
|
||||||
use errors::*;
|
use errors::*;
|
||||||
use packet::*;
|
use packet::*;
|
||||||
use records::{UnknownRecord, registry};
|
use packet::opt::{Opt, DnsOption};
|
||||||
|
use records::{UnknownRecord, registry, A};
|
||||||
use ser::{RRData, RRDataText, text};
|
use ser::{RRData, RRDataText, text};
|
||||||
use ser::packet::{DnsPacketData, deserialize_with};
|
use ser::packet::{DnsPacketData, deserialize_with};
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
|
|
||||||
fn fake_packet(rrtype: Type, raw: &[u8]) -> Bytes {
|
fn fake_packet(rrtype: Type, raw: &[u8]) -> Bytes {
|
||||||
let p = DnsPacket{
|
let mut p = DnsPacket{
|
||||||
question: vec![
|
question: vec![
|
||||||
Question {
|
Question {
|
||||||
qname: ".".parse().unwrap(),
|
qname: ".".parse().unwrap(),
|
||||||
@ -55,7 +56,7 @@ fn get_first_answer_rdata(packet: Bytes) -> Result<Bytes> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn serialized_answer(rrdata: Box<RRData>) -> Result<Bytes> {
|
fn serialized_answer(rrdata: Box<RRData>) -> Result<Bytes> {
|
||||||
let p = DnsPacket{
|
let mut p = DnsPacket{
|
||||||
question: vec![
|
question: vec![
|
||||||
Question {
|
Question {
|
||||||
qname: ".".parse().unwrap(),
|
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::CNAME, "123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123456789012.");
|
||||||
check_invalid_zone(types::SOA, "ns.rec.test hostmaster.test.rec 20130512010 3600 3600 604800 120"); // too long serial
|
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)]
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||||
#[RRClass(IN)]
|
#[RRClass(IN)]
|
||||||
pub struct A {
|
pub struct A {
|
||||||
addr: Ipv4Addr,
|
pub addr: Ipv4Addr,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
#[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")?;
|
let d2: T = rrdata_parse(txt).context("couldn't parse text record")?;
|
||||||
ensure!(d1 == d2, "decoded data not equal: {:?} != {:?}", d1, d2);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,10 +73,10 @@ where
|
|||||||
T: StaticRRData + fmt::Debug + PartialEq
|
T: StaticRRData + fmt::Debug + PartialEq
|
||||||
{
|
{
|
||||||
// at least one "segment" (which could be empty)
|
// at least one "segment" (which could be empty)
|
||||||
check2::<T>(r#" "" "#, b"", r#" "" "#).unwrap_err();
|
check2::<T>(r#" "" "#, b"", r#""""#).unwrap_err();
|
||||||
check2::<T>(r#""#, b"\x00", r#" "" "#).unwrap_err();
|
check2::<T>(r#""#, b"\x00", r#""""#).unwrap_err();
|
||||||
// one empty segment
|
// one empty segment
|
||||||
check2::<T>(r#" "" "#, b"\x00", r#" "" "#).unwrap();
|
check2::<T>(r#" "" "#, b"\x00", r#""""#).unwrap();
|
||||||
// one segment
|
// one segment
|
||||||
check::<T>(r#" "foo" "#, b"\x03foo").unwrap();
|
check::<T>(r#" "foo" "#, b"\x03foo").unwrap();
|
||||||
// two segments
|
// 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> {
|
pub fn deserialize(rr_type: Type, data: &mut ::std::io::Cursor<Bytes>) -> Result<Self> {
|
||||||
Ok(UnknownRecord::new(rr_type, remaining_bytes(data)))
|
Ok(UnknownRecord::new(rr_type, remaining_bytes(data)))
|
||||||
}
|
}
|
||||||
@ -89,4 +93,8 @@ impl RRData for UnknownRecord {
|
|||||||
fn clone_box(&self) -> Box<RRData> {
|
fn clone_box(&self) -> Box<RRData> {
|
||||||
Box::new(self.clone()) as _
|
Box::new(self.clone()) as _
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &::std::any::Any {
|
||||||
|
self as _
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
use common_types::{Class, Type, classes};
|
use common_types::{Class, Type, classes};
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
use records::UnknownRecord;
|
||||||
use ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
use ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||||
use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext};
|
use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext};
|
||||||
|
use std::any::Any;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use records::UnknownRecord;
|
|
||||||
|
|
||||||
pub trait RRDataPacket {
|
pub trait RRDataPacket {
|
||||||
fn deserialize_rr_data(ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor<Bytes>) -> Result<Self>
|
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 clone_box(&self) -> Box<RRData>;
|
||||||
|
|
||||||
|
fn as_any(&self) -> &Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Box<RRData> {
|
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> {
|
fn clone_box(&self) -> Box<::dnsbox_base::ser::RRData> {
|
||||||
Box::new(self.clone() as #name) as _
|
Box::new(self.clone() as #name) as _
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &::std::any::Any {
|
||||||
|
self as _
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::dnsbox_base::ser::StaticRRData for #name {
|
impl ::dnsbox_base::ser::StaticRRData for #name {
|
||||||
|
Loading…
Reference in New Issue
Block a user