cargo fmt
This commit is contained in:
parent
460a8d1755
commit
9d5314d127
12
dnsbox/src/bin/resolver/cache/mod.rs
vendored
12
dnsbox/src/bin/resolver/cache/mod.rs
vendored
@ -1,8 +1,8 @@
|
||||
use dnsbox_base::common_types::{Type, DnsName};
|
||||
use failure::Error;
|
||||
use dnsbox_base::common_types::{DnsName, Type};
|
||||
use dnsbox_base::ser::RRData;
|
||||
use futures::{Future, Poll, Async};
|
||||
use failure::Error;
|
||||
use futures::unsync::oneshot;
|
||||
use futures::{Async, Future, Poll};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::mem::replace;
|
||||
@ -82,13 +82,13 @@ impl InnerEntry {
|
||||
replace(self, InnerEntry::Refreshing(e));
|
||||
(false, res)
|
||||
}
|
||||
}
|
||||
},
|
||||
InnerEntry::Pending(mut queue) => {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
queue.push(tx);
|
||||
replace(self, InnerEntry::Pending(queue));
|
||||
(false, CacheResult(InnerResult::Waiting(rx)))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -168,7 +168,7 @@ impl Future for CacheResult {
|
||||
// keep waiting
|
||||
replace(&mut self.0, InnerResult::Waiting(rc));
|
||||
Ok(Async::NotReady)
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
InnerResult::Finished => Ok(Async::NotReady),
|
||||
|
20
dnsbox/src/bin/resolver/cache/root_hints.rs
vendored
20
dnsbox/src/bin/resolver/cache/root_hints.rs
vendored
@ -1,6 +1,6 @@
|
||||
use dnsbox_base::common_types::{types};
|
||||
use dnsbox_base::common_types::{DnsName, DnsCompressedName};
|
||||
use dnsbox_base::records::{NS, A, AAAA};
|
||||
use dnsbox_base::common_types::types;
|
||||
use dnsbox_base::common_types::{DnsCompressedName, DnsName};
|
||||
use dnsbox_base::records::{A, AAAA, NS};
|
||||
use dnsbox_base::ser::RRData;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
@ -26,13 +26,21 @@ pub fn load_hints(cache: &super::Cache) {
|
||||
|
||||
for &(name, ipv4, ipv6) in &DATA {
|
||||
let name = name.parse::<DnsName>().expect("invalid root hint name");
|
||||
let ipv4 = ipv4.parse::<Ipv4Addr>().expect("invalid root hint ipv4 addr");
|
||||
let ipv6 = ipv6.parse::<Ipv6Addr>().expect("invalid root hint ipv6 addr");
|
||||
let ipv4 = ipv4
|
||||
.parse::<Ipv4Addr>()
|
||||
.expect("invalid root hint ipv4 addr");
|
||||
let ipv6 = ipv6
|
||||
.parse::<Ipv6Addr>()
|
||||
.expect("invalid root hint ipv6 addr");
|
||||
root_ns_set.push(Box::new(NS {
|
||||
nsdname: DnsCompressedName(name.clone()),
|
||||
}));
|
||||
cache.insert_hint(name.clone(), types::A, vec![Box::new(A { addr: ipv4 })]);
|
||||
cache.insert_hint(name.clone(), types::AAAA, vec![Box::new(AAAA { addr: ipv6 })]);
|
||||
cache.insert_hint(
|
||||
name.clone(),
|
||||
types::AAAA,
|
||||
vec![Box::new(AAAA { addr: ipv6 })],
|
||||
);
|
||||
}
|
||||
cache.insert_hint(DnsName::new_root(), types::NS, root_ns_set);
|
||||
|
||||
|
@ -1,9 +1,11 @@
|
||||
use bytes::{Bytes, BufMut};
|
||||
use data_encoding::{self, HEXLOWER_PERMISSIVE};
|
||||
use crate::errors::*;
|
||||
use failure::{Fail, ResultExt};
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext, remaining_bytes, short_blob, write_short_blob, get_blob};
|
||||
use crate::ser::packet::{
|
||||
get_blob, remaining_bytes, short_blob, write_short_blob, DnsPacketData, DnsPacketWriteContext,
|
||||
};
|
||||
use crate::ser::text::*;
|
||||
use bytes::{BufMut, Bytes};
|
||||
use data_encoding::{self, HEXLOWER_PERMISSIVE};
|
||||
use failure::{Fail, ResultExt};
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
|
||||
@ -35,7 +37,10 @@ pub struct HexShortBlob(Bytes);
|
||||
|
||||
impl HexShortBlob {
|
||||
pub fn new(data: Vec<u8>) -> Result<Self> {
|
||||
failure::ensure!(data.len() < 256, "short hex blob must be at most 255 bytes long");
|
||||
failure::ensure!(
|
||||
data.len() < 256,
|
||||
"short hex blob must be at most 255 bytes long"
|
||||
);
|
||||
Ok(Self(data.into()))
|
||||
}
|
||||
}
|
||||
@ -56,9 +61,13 @@ impl DnsTextData for HexShortBlob {
|
||||
if s == "-" {
|
||||
Ok(HexShortBlob(Bytes::new()))
|
||||
} else {
|
||||
let raw = HEXLOWER_PERMISSIVE.decode(s.as_bytes())
|
||||
let raw = HEXLOWER_PERMISSIVE
|
||||
.decode(s.as_bytes())
|
||||
.with_context(|e| e.context(format!("invalid hex: {:?}", s)))?;
|
||||
failure::ensure!(raw.len() < 256, "short hex field must be at most 255 bytes long");
|
||||
failure::ensure!(
|
||||
raw.len() < 256,
|
||||
"short hex field must be at most 255 bytes long"
|
||||
);
|
||||
Ok(HexShortBlob(raw.into()))
|
||||
}
|
||||
}
|
||||
@ -89,7 +98,10 @@ pub struct Base64LongBlob(Bytes);
|
||||
|
||||
impl Base64LongBlob {
|
||||
pub fn new(data: Vec<u8>) -> Result<Self> {
|
||||
failure::ensure!(data.len() < 0x1_0000, "long base64 blob must be at most 65535 bytes long");
|
||||
failure::ensure!(
|
||||
data.len() < 0x1_0000,
|
||||
"long base64 blob must be at most 65535 bytes long"
|
||||
);
|
||||
Ok(Self(data.into()))
|
||||
}
|
||||
}
|
||||
@ -115,12 +127,14 @@ impl DnsPacketData for Base64LongBlob {
|
||||
impl DnsTextData for Base64LongBlob {
|
||||
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> crate::errors::Result<Self> {
|
||||
let length_field = next_field(data)?;
|
||||
let length = length_field.parse::<u16>()
|
||||
let length = length_field
|
||||
.parse::<u16>()
|
||||
.with_context(|_| format!("invalid length for blob: {:?}", length_field))?;
|
||||
|
||||
if length > 0 {
|
||||
let blob_field = next_field(data)?;
|
||||
let result = BASE64_ALLOW_WS.decode(blob_field.as_bytes())
|
||||
let result = BASE64_ALLOW_WS
|
||||
.decode(blob_field.as_bytes())
|
||||
.with_context(|e| e.context(format!("invalid base64: {:?}", blob_field)))?;
|
||||
Ok(Base64LongBlob(result.into()))
|
||||
} else {
|
||||
@ -129,7 +143,9 @@ impl DnsTextData for Base64LongBlob {
|
||||
}
|
||||
|
||||
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
||||
if self.0.len() >= 0x1_0000 { return Err(fmt::Error); }
|
||||
if self.0.len() >= 0x1_0000 {
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
if self.0.is_empty() {
|
||||
write!(f, "0")
|
||||
} else {
|
||||
@ -175,7 +191,8 @@ impl DnsPacketData for Base64RemainingBlob {
|
||||
impl DnsTextData for Base64RemainingBlob {
|
||||
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> crate::errors::Result<Self> {
|
||||
skip_whitespace(data);
|
||||
let result = BASE64_ALLOW_WS.decode(data.as_bytes())
|
||||
let result = BASE64_ALLOW_WS
|
||||
.decode(data.as_bytes())
|
||||
.with_context(|e| e.context(format!("invalid base64: {:?}", data)))?;
|
||||
*data = "";
|
||||
Ok(Base64RemainingBlob(result.into()))
|
||||
@ -226,7 +243,8 @@ impl DnsPacketData for HexRemainingBlob {
|
||||
impl DnsTextData for HexRemainingBlob {
|
||||
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> crate::errors::Result<Self> {
|
||||
skip_whitespace(data);
|
||||
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)))?;
|
||||
*data = "";
|
||||
Ok(HexRemainingBlob(result.into()))
|
||||
@ -279,7 +297,8 @@ impl DnsPacketData for HexRemainingBlobNotEmpty {
|
||||
impl DnsTextData for HexRemainingBlobNotEmpty {
|
||||
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> crate::errors::Result<Self> {
|
||||
skip_whitespace(data);
|
||||
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)))?;
|
||||
*data = "";
|
||||
failure::ensure!(!result.is_empty(), "must not be empty");
|
||||
@ -287,7 +306,9 @@ impl DnsTextData for HexRemainingBlobNotEmpty {
|
||||
}
|
||||
|
||||
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
||||
if self.0.is_empty() { return Err(fmt::Error); }
|
||||
if self.0.is_empty() {
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
write!(f, "{}", HEXLOWER_PERMISSIVE_ALLOW_WS.encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@ pub enum DnsSecAlgorithm {
|
||||
DSA = 3,
|
||||
|
||||
// Reserved: 4 [RFC6725]
|
||||
|
||||
/// RSA/SHA-1
|
||||
// [RFC3110][RFC4034]
|
||||
RSASHA1 = 5,
|
||||
@ -33,13 +32,11 @@ pub enum DnsSecAlgorithm {
|
||||
RSASHA256 = 8,
|
||||
|
||||
// Reserved: 9 [RFC6725]
|
||||
|
||||
/// RSA/SHA-512
|
||||
// [RFC5702][proposed standard]
|
||||
RSASHA512 = 10,
|
||||
|
||||
// Reserved: 11 [RFC6725]
|
||||
|
||||
/// GOST R 34.10-2001
|
||||
// [RFC5933][standards track]
|
||||
ECC_GOST = 12,
|
||||
@ -64,7 +61,6 @@ pub enum DnsSecAlgorithm {
|
||||
/// private algorithm OID
|
||||
// [RFC4034]
|
||||
PRIVATEOID = 254,
|
||||
|
||||
// Reserved: 255 [RFC4034][proposed standard]
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use bytes::Bytes;
|
||||
use crate::errors::*;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||
use crate::ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field};
|
||||
use crate::ser::text::{next_field, DnsTextContext, DnsTextData, DnsTextFormatter};
|
||||
use bytes::Bytes;
|
||||
use std::fmt;
|
||||
use std::io::{Cursor, Read};
|
||||
|
||||
@ -16,7 +16,11 @@ fn fmt_eui_hyphens(data: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fn parse_eui_hyphens(dest: &mut [u8], source: &str) -> Result<()> {
|
||||
let mut pos = 0;
|
||||
for octet in source.split('-') {
|
||||
failure::ensure!(pos < dest.len(), "too many octets for EUI{}", dest.len() * 8);
|
||||
failure::ensure!(
|
||||
pos < dest.len(),
|
||||
"too many octets for EUI{}",
|
||||
dest.len() * 8
|
||||
);
|
||||
failure::ensure!(octet.len() == 2, "invalid octet {:?}", octet);
|
||||
match u8::from_str_radix(octet, 16) {
|
||||
Ok(o) => {
|
||||
@ -28,7 +32,11 @@ fn parse_eui_hyphens(dest: &mut [u8], source: &str) -> Result<()> {
|
||||
},
|
||||
}
|
||||
}
|
||||
failure::ensure!(pos == dest.len(), "not enough octets for EUI{}", dest.len() * 8);
|
||||
failure::ensure!(
|
||||
pos == dest.len(),
|
||||
"not enough octets for EUI{}",
|
||||
dest.len() * 8
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1,71 +1,35 @@
|
||||
pub mod binary;
|
||||
pub mod classes;
|
||||
pub mod name;
|
||||
pub mod text;
|
||||
pub mod types;
|
||||
mod caa;
|
||||
pub mod classes;
|
||||
mod dnssec;
|
||||
mod eui;
|
||||
pub mod name;
|
||||
mod nsec;
|
||||
mod nxt;
|
||||
mod sig;
|
||||
mod sshfp;
|
||||
pub mod text;
|
||||
mod time;
|
||||
pub mod types;
|
||||
mod uri;
|
||||
|
||||
pub use self::binary::{
|
||||
Base64LongBlob,
|
||||
Base64RemainingBlob,
|
||||
HexRemainingBlob,
|
||||
HexRemainingBlobNotEmpty,
|
||||
HexShortBlob,
|
||||
Base64LongBlob, Base64RemainingBlob, HexRemainingBlob, HexRemainingBlobNotEmpty, HexShortBlob,
|
||||
};
|
||||
pub use self::classes::Class;
|
||||
pub use self::caa::CaaFlags;
|
||||
pub use self::classes::Class;
|
||||
pub use self::dnssec::{
|
||||
DnskeyFlags,
|
||||
DnskeyProtocol,
|
||||
DnskeyProtocolKnown,
|
||||
DnsSecAlgorithm,
|
||||
DnsSecAlgorithmKnown,
|
||||
DnsSecDigestAlgorithm,
|
||||
DnsSecDigestAlgorithmKnown,
|
||||
Nsec3Algorithm,
|
||||
Nsec3AlgorithmKnown,
|
||||
Nsec3Flags,
|
||||
Nsec3ParamFlags,
|
||||
};
|
||||
pub use self::eui::{
|
||||
EUI48Addr,
|
||||
EUI64Addr,
|
||||
};
|
||||
pub use self::name::{
|
||||
DnsCanonicalName,
|
||||
DnsCompressedName,
|
||||
DnsName,
|
||||
};
|
||||
pub use self::nsec::{
|
||||
NextHashedOwnerName,
|
||||
NsecTypeBitmap,
|
||||
DnsSecAlgorithm, DnsSecAlgorithmKnown, DnsSecDigestAlgorithm, DnsSecDigestAlgorithmKnown,
|
||||
DnskeyFlags, DnskeyProtocol, DnskeyProtocolKnown, Nsec3Algorithm, Nsec3AlgorithmKnown,
|
||||
Nsec3Flags, Nsec3ParamFlags,
|
||||
};
|
||||
pub use self::eui::{EUI48Addr, EUI64Addr};
|
||||
pub use self::name::{DnsCanonicalName, DnsCompressedName, DnsName};
|
||||
pub use self::nsec::{NextHashedOwnerName, NsecTypeBitmap};
|
||||
pub use self::nxt::NxtTypeBitmap;
|
||||
pub use self::sig::OptionalTTL;
|
||||
pub use self::sshfp::{
|
||||
SshFpAlgorithm,
|
||||
SshFpAlgorithmKnown,
|
||||
SshFpType,
|
||||
SshFpTypeKnown,
|
||||
};
|
||||
pub use self::text::{
|
||||
LongText,
|
||||
RemainingText,
|
||||
ShortText,
|
||||
UnquotedShortText,
|
||||
};
|
||||
pub use self::time::{
|
||||
Time,
|
||||
Time48,
|
||||
TimeStrict,
|
||||
};
|
||||
pub use self::sshfp::{SshFpAlgorithm, SshFpAlgorithmKnown, SshFpType, SshFpTypeKnown};
|
||||
pub use self::text::{LongText, RemainingText, ShortText, UnquotedShortText};
|
||||
pub use self::time::{Time, Time48, TimeStrict};
|
||||
pub use self::types::Type;
|
||||
pub use self::uri::UriText;
|
||||
|
@ -1,13 +1,13 @@
|
||||
use bytes::Bytes;
|
||||
use crate::errors::*;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||
use crate::ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field, parse_with};
|
||||
use crate::ser::text::{next_field, parse_with, DnsTextContext, DnsTextData, DnsTextFormatter};
|
||||
use bytes::Bytes;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::{DnsName, DnsNameIterator, DnsLabelRef};
|
||||
use super::{DnsLabelRef, DnsName, DnsNameIterator};
|
||||
|
||||
/// names that should be written in canonical form for DNSSEC according
|
||||
/// to https://tools.ietf.org/html/rfc4034#section-6.2
|
||||
@ -24,8 +24,7 @@ impl DnsCanonicalName {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
Ok(DnsCanonicalName(DnsName::parse(context, value)?))
|
||||
}
|
||||
}
|
||||
@ -65,8 +64,7 @@ impl<'a> IntoIterator for &'a DnsCanonicalName {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<DnsName> for DnsCanonicalName
|
||||
{
|
||||
impl PartialEq<DnsName> for DnsCanonicalName {
|
||||
fn eq(&self, rhs: &DnsName) -> bool {
|
||||
let this: &DnsName = self;
|
||||
this == rhs
|
||||
@ -75,7 +73,7 @@ impl PartialEq<DnsName> for DnsCanonicalName
|
||||
|
||||
impl<T> PartialEq<T> for DnsCanonicalName
|
||||
where
|
||||
T: AsRef<DnsName>
|
||||
T: AsRef<DnsName>,
|
||||
{
|
||||
fn eq(&self, rhs: &T) -> bool {
|
||||
let this: &DnsName = self.as_ref();
|
||||
@ -101,7 +99,9 @@ impl FromStr for DnsCanonicalName {
|
||||
type Err = ::failure::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
parse_with(s, |data| DnsCanonicalName::dns_parse(&DnsTextContext::new(), data))
|
||||
parse_with(s, |data| {
|
||||
DnsCanonicalName::dns_parse(&DnsTextContext::new(), data)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,7 +118,9 @@ impl DnsTextData for DnsCanonicalName {
|
||||
|
||||
impl DnsPacketData for DnsCanonicalName {
|
||||
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
||||
Ok(DnsCanonicalName(super::name_packet_parser::deserialize_name(data, false)?))
|
||||
Ok(DnsCanonicalName(
|
||||
super::name_packet_parser::deserialize_name(data, false)?,
|
||||
))
|
||||
}
|
||||
|
||||
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||
|
@ -1,13 +1,13 @@
|
||||
use bytes::Bytes;
|
||||
use crate::errors::*;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||
use crate::ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field, parse_with};
|
||||
use crate::ser::text::{next_field, parse_with, DnsTextContext, DnsTextData, DnsTextFormatter};
|
||||
use bytes::Bytes;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::{DnsName, DnsNameIterator, DnsLabelRef};
|
||||
use super::{DnsLabelRef, DnsName, DnsNameIterator};
|
||||
|
||||
/// Similar to `DnsName`, but allows using compressed labels in the
|
||||
/// serialized form
|
||||
@ -21,8 +21,7 @@ impl DnsCompressedName {
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
Ok(DnsCompressedName(DnsName::parse(context, value)?))
|
||||
}
|
||||
}
|
||||
@ -62,8 +61,7 @@ impl<'a> IntoIterator for &'a DnsCompressedName {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<DnsName> for DnsCompressedName
|
||||
{
|
||||
impl PartialEq<DnsName> for DnsCompressedName {
|
||||
fn eq(&self, rhs: &DnsName) -> bool {
|
||||
let this: &DnsName = self;
|
||||
this == rhs
|
||||
@ -72,7 +70,7 @@ impl PartialEq<DnsName> for DnsCompressedName
|
||||
|
||||
impl<T> PartialEq<T> for DnsCompressedName
|
||||
where
|
||||
T: AsRef<DnsName>
|
||||
T: AsRef<DnsName>,
|
||||
{
|
||||
fn eq(&self, rhs: &T) -> bool {
|
||||
let this: &DnsName = self.as_ref();
|
||||
@ -98,7 +96,9 @@ impl FromStr for DnsCompressedName {
|
||||
type Err = ::failure::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
parse_with(s, |data| DnsCompressedName::dns_parse(&DnsTextContext::new(), data))
|
||||
parse_with(s, |data| {
|
||||
DnsCompressedName::dns_parse(&DnsTextContext::new(), data)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -115,7 +115,9 @@ impl DnsTextData for DnsCompressedName {
|
||||
|
||||
impl DnsPacketData for DnsCompressedName {
|
||||
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
|
||||
Ok(DnsCompressedName(super::name_packet_parser::deserialize_name(data, true)?))
|
||||
Ok(DnsCompressedName(
|
||||
super::name_packet_parser::deserialize_name(data, true)?,
|
||||
))
|
||||
}
|
||||
|
||||
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
use std::fmt;
|
||||
use super::DnsLabelRef;
|
||||
use std::fmt;
|
||||
|
||||
/// Customize formatting of DNS labels
|
||||
///
|
||||
@ -38,7 +38,7 @@ impl Default for DisplayLabelsOptions {
|
||||
#[derive(Clone)]
|
||||
pub struct DisplayLabels<'a, I>
|
||||
where
|
||||
I: IntoIterator<Item=DnsLabelRef<'a>>+Clone
|
||||
I: IntoIterator<Item = DnsLabelRef<'a>> + Clone,
|
||||
{
|
||||
/// Label collection to iterate over
|
||||
pub labels: I,
|
||||
@ -48,7 +48,7 @@ where
|
||||
|
||||
impl<'a, I> fmt::Debug for DisplayLabels<'a, I>
|
||||
where
|
||||
I: IntoIterator<Item=DnsLabelRef<'a>>+Clone
|
||||
I: IntoIterator<Item = DnsLabelRef<'a>> + Clone,
|
||||
{
|
||||
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
|
||||
// just escape the display version1
|
||||
@ -58,7 +58,7 @@ where
|
||||
|
||||
impl<'a, I> fmt::Display for DisplayLabels<'a, I>
|
||||
where
|
||||
I: IntoIterator<Item=DnsLabelRef<'a>>+Clone
|
||||
I: IntoIterator<Item = DnsLabelRef<'a>> + Clone,
|
||||
{
|
||||
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut i = self.labels.clone().into_iter();
|
||||
|
@ -1,7 +1,7 @@
|
||||
use bytes::Bytes;
|
||||
use crate::errors::*;
|
||||
use std::fmt;
|
||||
use bytes::Bytes;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
|
||||
#[inline]
|
||||
fn check_label(label: &[u8]) -> Result<()> {
|
||||
@ -31,7 +31,9 @@ impl DnsLabel {
|
||||
|
||||
/// Convert to a representation without storage
|
||||
pub fn as_ref<'a>(&'a self) -> DnsLabelRef<'a> {
|
||||
DnsLabelRef{label: self.label.as_ref()}
|
||||
DnsLabelRef {
|
||||
label: self.label.as_ref(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Access as raw bytes
|
||||
@ -218,7 +220,9 @@ impl<'a> fmt::Display for DnsLabelRef<'a> {
|
||||
if c <= 0x21 || c >= 0x7e || b'.' == c || b'\\' == c {
|
||||
// flush
|
||||
if done < pos {
|
||||
w.write_str(crate::unsafe_ops::from_utf8_unchecked(&self.label[done..pos]))?;
|
||||
w.write_str(crate::unsafe_ops::from_utf8_unchecked(
|
||||
&self.label[done..pos],
|
||||
))?;
|
||||
}
|
||||
match c {
|
||||
b'.' => w.write_str(r#"\."#)?,
|
||||
|
@ -31,7 +31,7 @@ impl LabelOffsets {
|
||||
LabelOffsets::Compressed(start, ref offs) => match offs[ndx as usize] {
|
||||
LabelOffset::LabelStart(o) => start + (o as usize),
|
||||
LabelOffset::PacketStart(o) => o as usize,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
#![deny(missing_docs)]
|
||||
//! Various structs to represents DNS names and labels
|
||||
|
||||
use bytes::Bytes;
|
||||
use crate::errors::*;
|
||||
use bytes::Bytes;
|
||||
use smallvec::SmallVec;
|
||||
use std::io::Cursor;
|
||||
|
||||
@ -10,9 +10,9 @@ pub use self::canonical_name::*;
|
||||
pub use self::compressed_name::*;
|
||||
pub use self::display::*;
|
||||
pub use self::label::*;
|
||||
pub use self::name_iterator::*;
|
||||
pub use self::name::*;
|
||||
use self::label_offsets::*;
|
||||
pub use self::name::*;
|
||||
pub use self::name_iterator::*;
|
||||
|
||||
mod canonical_name;
|
||||
mod compressed_name;
|
||||
|
@ -1,13 +1,13 @@
|
||||
use bytes::Bytes;
|
||||
use crate::errors::*;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||
use crate::ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field, parse_with};
|
||||
use crate::ser::text::{next_field, parse_with, DnsTextContext, DnsTextData, DnsTextFormatter};
|
||||
use bytes::Bytes;
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::{LabelOffsets, DnsNameIterator, DnsLabelRef, DnsLabel, DisplayLabels};
|
||||
use super::{DisplayLabels, DnsLabel, DnsLabelRef, DnsNameIterator, LabelOffsets};
|
||||
|
||||
/// A DNS name
|
||||
///
|
||||
@ -83,7 +83,9 @@ impl DnsName {
|
||||
let label_len = self.data[pos];
|
||||
debug_assert!(label_len < 64);
|
||||
let end = pos + 1 + label_len as usize;
|
||||
DnsLabelRef{label: &self.data[pos + 1..end]}
|
||||
DnsLabelRef {
|
||||
label: &self.data[pos + 1..end],
|
||||
}
|
||||
}
|
||||
|
||||
/// Return label at index `ndx`
|
||||
@ -96,7 +98,9 @@ impl DnsName {
|
||||
let label_len = self.data[pos];
|
||||
debug_assert!(label_len < 64);
|
||||
let end = pos + 1 + label_len as usize;
|
||||
DnsLabel{label: self.data.slice(pos + 1, end) }
|
||||
DnsLabel {
|
||||
label: self.data.slice(pos + 1, end),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,19 +113,20 @@ impl<'a> IntoIterator for &'a DnsName {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<DnsName> for DnsName
|
||||
{
|
||||
impl PartialEq<DnsName> for DnsName {
|
||||
fn eq(&self, rhs: &DnsName) -> bool {
|
||||
let a_labels = self.labels();
|
||||
let b_labels = rhs.labels();
|
||||
if a_labels.len() != b_labels.len() { return false; }
|
||||
if a_labels.len() != b_labels.len() {
|
||||
return false;
|
||||
}
|
||||
a_labels.zip(b_labels).all(|(a, b)| a == b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<T> for DnsName
|
||||
where
|
||||
T: AsRef<DnsName>
|
||||
T: AsRef<DnsName>,
|
||||
{
|
||||
fn eq(&self, rhs: &T) -> bool {
|
||||
self == rhs.as_ref()
|
||||
@ -135,7 +140,8 @@ impl fmt::Debug for DnsName {
|
||||
DisplayLabels {
|
||||
labels: self,
|
||||
options: Default::default(),
|
||||
}.fmt(w)
|
||||
}
|
||||
.fmt(w)
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,7 +150,8 @@ impl fmt::Display for DnsName {
|
||||
DisplayLabels {
|
||||
labels: self,
|
||||
options: Default::default(),
|
||||
}.fmt(w)
|
||||
}
|
||||
.fmt(w)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{DnsName, DnsLabelRef};
|
||||
use super::{DnsLabelRef, DnsName};
|
||||
|
||||
/// Iterator type for [`DnsName::labels`]
|
||||
///
|
||||
@ -14,7 +14,9 @@ impl<'a> Iterator for DnsNameIterator<'a> {
|
||||
type Item = DnsLabelRef<'a>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.front_label >= self.back_label { return None }
|
||||
if self.front_label >= self.back_label {
|
||||
return None;
|
||||
}
|
||||
let label = self.name.label_ref(self.front_label);
|
||||
self.front_label += 1;
|
||||
Some(label)
|
||||
@ -38,7 +40,9 @@ impl<'a> ExactSizeIterator for DnsNameIterator<'a> {
|
||||
|
||||
impl<'a> DoubleEndedIterator for DnsNameIterator<'a> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if self.front_label >= self.back_label { return None }
|
||||
if self.front_label >= self.back_label {
|
||||
return None;
|
||||
}
|
||||
self.back_label -= 1;
|
||||
let label = self.name.label_ref(self.back_label);
|
||||
Some(label)
|
||||
|
@ -1,5 +1,5 @@
|
||||
use bytes::{BytesMut,BufMut};
|
||||
use super::*;
|
||||
use bytes::{BufMut, BytesMut};
|
||||
|
||||
impl DnsName {
|
||||
/// Remove the front label
|
||||
@ -8,12 +8,16 @@ impl DnsName {
|
||||
pub fn try_pop_front(&mut self) -> bool {
|
||||
match self.label_offsets {
|
||||
LabelOffsets::Uncompressed(ref mut offs) => {
|
||||
if offs.is_empty() { return false; }
|
||||
if offs.is_empty() {
|
||||
return false;
|
||||
}
|
||||
self.total_len -= self.data[offs[0] as usize] + 1;
|
||||
offs.remove(0);
|
||||
},
|
||||
LabelOffsets::Compressed(ref mut start_pos, ref mut offs) => {
|
||||
if offs.is_empty() { return false; }
|
||||
if offs.is_empty() {
|
||||
return false;
|
||||
}
|
||||
match offs[0] {
|
||||
LabelOffset::LabelStart(o) => {
|
||||
let label_space = self.data[*start_pos + o as usize] + 1;
|
||||
@ -36,7 +40,9 @@ impl DnsName {
|
||||
///
|
||||
/// Panics if the name was the root (".")
|
||||
pub fn pop_front(&mut self) {
|
||||
if !self.try_pop_front() { panic!("Cannot pop label from root name") }
|
||||
if !self.try_pop_front() {
|
||||
panic!("Cannot pop label from root name")
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a new label at the front
|
||||
@ -44,12 +50,15 @@ impl DnsName {
|
||||
/// Returns an error if the resulting name would be too long
|
||||
pub fn push_front<'a, L: Into<DnsLabelRef<'a>>>(&mut self, label: L) -> Result<()> {
|
||||
let label = label.into();
|
||||
if label.len() > 254 - self.total_len { failure::bail!("Cannot append label, resulting name too long") }
|
||||
if label.len() > 254 - self.total_len {
|
||||
failure::bail!("Cannot append label, resulting name too long")
|
||||
}
|
||||
let (mut data, start) = self.reserve(label.len() as usize + 1, 0);
|
||||
|
||||
let new_label_pos = start - (label.len() + 1) as usize;
|
||||
data[new_label_pos] = label.len();
|
||||
data[new_label_pos+1..new_label_pos+1+label.len() as usize].copy_from_slice(label.as_raw());
|
||||
data[new_label_pos + 1..new_label_pos + 1 + label.len() as usize]
|
||||
.copy_from_slice(label.as_raw());
|
||||
self.data = data.freeze();
|
||||
self.total_len += label.len() + 1;
|
||||
match self.label_offsets {
|
||||
@ -68,12 +77,16 @@ impl DnsName {
|
||||
pub fn try_pop_back(&mut self) -> bool {
|
||||
match self.label_offsets {
|
||||
LabelOffsets::Uncompressed(ref mut offs) => {
|
||||
if offs.is_empty() { return false; }
|
||||
if offs.is_empty() {
|
||||
return false;
|
||||
}
|
||||
self.total_len -= self.data[offs[offs.len() - 1] as usize] + 1;
|
||||
offs.pop();
|
||||
},
|
||||
LabelOffsets::Compressed(ref mut start_pos, ref mut offs) => {
|
||||
if offs.is_empty() { return false; }
|
||||
if offs.is_empty() {
|
||||
return false;
|
||||
}
|
||||
match offs[offs.len() - 1] {
|
||||
LabelOffset::LabelStart(o) => {
|
||||
self.total_len -= self.data[*start_pos + o as usize] + 1;
|
||||
@ -94,7 +107,9 @@ impl DnsName {
|
||||
///
|
||||
/// Panics if the name was the root (".")
|
||||
pub fn pop_back(&mut self) {
|
||||
if !self.try_pop_back() { panic!("Cannot pop label from root name") }
|
||||
if !self.try_pop_back() {
|
||||
panic!("Cannot pop label from root name")
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert a new label at the back
|
||||
@ -102,12 +117,15 @@ impl DnsName {
|
||||
/// Returns an error if the resulting name would be too long
|
||||
pub fn push_back<'a, L: Into<DnsLabelRef<'a>>>(&mut self, label: L) -> Result<()> {
|
||||
let label = label.into();
|
||||
if label.len() > 254 - self.total_len { failure::bail!("Cannot append label, resulting name too long") }
|
||||
if label.len() > 254 - self.total_len {
|
||||
failure::bail!("Cannot append label, resulting name too long")
|
||||
}
|
||||
let (mut data, start) = self.reserve(0, label.len() as usize + 1);
|
||||
|
||||
let new_label_pos = start + self.total_len as usize - 1;
|
||||
data[new_label_pos] = label.len();
|
||||
data[new_label_pos+1..new_label_pos+1+label.len() as usize].copy_from_slice(label.as_raw());
|
||||
data[new_label_pos + 1..new_label_pos + 1 + label.len() as usize]
|
||||
.copy_from_slice(label.as_raw());
|
||||
data[new_label_pos + 1 + label.len() as usize] = 0;
|
||||
self.data = data.freeze();
|
||||
self.total_len += label.len() + 1;
|
||||
@ -145,15 +163,18 @@ impl DnsName {
|
||||
let add = new_len - data.len();
|
||||
data.reserve(add);
|
||||
}
|
||||
unsafe { data.set_len(new_len); }
|
||||
unsafe {
|
||||
data.set_len(new_len);
|
||||
}
|
||||
data[0] = 0;
|
||||
return (data, 0)
|
||||
return (data, 0);
|
||||
}
|
||||
|
||||
let old_start = label_offsets[0] as usize;
|
||||
// if current "prefix" space (old_start) is bigger than
|
||||
// requested but fits, just increase the prefix
|
||||
let (prefix, new_len) = if old_start > prefix && self.total_len as usize + old_start + suffix < 256 {
|
||||
let (prefix, new_len) =
|
||||
if old_start > prefix && self.total_len as usize + old_start + suffix < 256 {
|
||||
(old_start, self.total_len as usize + old_start + suffix)
|
||||
} else {
|
||||
(prefix, new_len)
|
||||
@ -173,13 +194,14 @@ impl DnsName {
|
||||
Err(data) => Err(data),
|
||||
};
|
||||
|
||||
|
||||
match data {
|
||||
Ok(mut data) => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if old_start < prefix {
|
||||
// need more space in front, move back
|
||||
@ -213,9 +235,12 @@ impl DnsName {
|
||||
},
|
||||
Err(data) => {
|
||||
let mut new_data = BytesMut::with_capacity(new_len);
|
||||
unsafe { new_data.set_len(new_len); }
|
||||
unsafe {
|
||||
new_data.set_len(new_len);
|
||||
}
|
||||
// copy old data
|
||||
new_data[prefix..prefix + self.total_len as usize].copy_from_slice(&data[old_start..old_start+self.total_len as usize]);
|
||||
new_data[prefix..prefix + self.total_len as usize]
|
||||
.copy_from_slice(&data[old_start..old_start + self.total_len as usize]);
|
||||
// adjust labels
|
||||
for o in label_offsets.iter_mut() {
|
||||
*o = (*o - old_start as u8) + prefix as u8;
|
||||
|
@ -1,13 +1,20 @@
|
||||
use bytes::Buf;
|
||||
use super::*;
|
||||
use bytes::Buf;
|
||||
|
||||
/// `data`: bytes of packet from beginning until at least the end of the name
|
||||
/// `start_pos`: position of first byte of the name
|
||||
/// `uncmpr_offsets`: offsets of uncompressed labels so far
|
||||
/// `label_len`: first compressed label length (`0xc0 | offset-high, offset-low`)
|
||||
/// `total_len`: length of (uncompressed) label encoding so far
|
||||
fn deserialize_name_compressed_cont(data: Bytes, start_pos: usize, uncmpr_offsets: SmallVec<[u8;16]>, mut total_len: usize, mut label_len: u8) -> Result<DnsName> {
|
||||
let mut label_offsets = uncmpr_offsets.into_iter()
|
||||
fn deserialize_name_compressed_cont(
|
||||
data: Bytes,
|
||||
start_pos: usize,
|
||||
uncmpr_offsets: SmallVec<[u8; 16]>,
|
||||
mut total_len: usize,
|
||||
mut label_len: u8,
|
||||
) -> Result<DnsName> {
|
||||
let mut label_offsets = uncmpr_offsets
|
||||
.into_iter()
|
||||
.map(LabelOffset::LabelStart)
|
||||
.collect::<SmallVec<_>>();
|
||||
|
||||
@ -16,7 +23,12 @@ fn deserialize_name_compressed_cont(data: Bytes, start_pos: usize, uncmpr_offset
|
||||
{
|
||||
failure::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);
|
||||
failure::ensure!(new_pos < pos, "Compressed label offset too big: {} >= {}", new_pos, pos);
|
||||
failure::ensure!(
|
||||
new_pos < pos,
|
||||
"Compressed label offset too big: {} >= {}",
|
||||
new_pos,
|
||||
pos
|
||||
);
|
||||
pos = new_pos;
|
||||
}
|
||||
|
||||
@ -29,15 +41,19 @@ fn deserialize_name_compressed_cont(data: Bytes, start_pos: usize, uncmpr_offset
|
||||
data: data,
|
||||
label_offsets: LabelOffsets::Compressed(start_pos, label_offsets),
|
||||
total_len: total_len as u8 + 1,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if label_len & 0xc0 == 0xc0 { continue 'next_compressed; }
|
||||
if label_len & 0xc0 == 0xc0 {
|
||||
continue 'next_compressed;
|
||||
}
|
||||
failure::ensure!(label_len < 64, "Invalid label length {}", label_len);
|
||||
|
||||
total_len += 1 + label_len as usize;
|
||||
// max len 255, but there also needs to be an empty label at the end
|
||||
if total_len > 254 { failure::bail!("DNS name too long") }
|
||||
if total_len > 254 {
|
||||
failure::bail!("DNS name too long")
|
||||
}
|
||||
|
||||
label_offsets.push(LabelOffset::PacketStart(pos as u16));
|
||||
pos += 1 + label_len as usize;
|
||||
@ -59,11 +75,13 @@ pub fn deserialize_name(data: &mut Cursor<Bytes>, accept_compressed: bool) -> Re
|
||||
data: data.get_ref().slice(start_pos, end_pos),
|
||||
label_offsets: LabelOffsets::Uncompressed(label_offsets),
|
||||
total_len: total_len as u8 + 1,
|
||||
})
|
||||
});
|
||||
}
|
||||
if label_len & 0xc0 == 0xc0 {
|
||||
// compressed label
|
||||
if !accept_compressed { failure::bail!("Invalid label compression {}", label_len) }
|
||||
if !accept_compressed {
|
||||
failure::bail!("Invalid label compression {}", label_len)
|
||||
}
|
||||
check_enough_data!(data, 1, "DnsName compressed label target");
|
||||
// eat second part of compressed label
|
||||
data.get_u8();
|
||||
@ -71,13 +89,23 @@ pub fn deserialize_name(data: &mut Cursor<Bytes>, accept_compressed: bool) -> Re
|
||||
let end_pos = data.position() as usize;
|
||||
let data = data.get_ref().slice(0, end_pos);
|
||||
|
||||
return deserialize_name_compressed_cont(data, start_pos, label_offsets, total_len, label_len as u8);
|
||||
return deserialize_name_compressed_cont(
|
||||
data,
|
||||
start_pos,
|
||||
label_offsets,
|
||||
total_len,
|
||||
label_len as u8,
|
||||
);
|
||||
}
|
||||
label_offsets.push(total_len as u8);
|
||||
if label_len > 63 { failure::bail!("Invalid label length {}", label_len) }
|
||||
if label_len > 63 {
|
||||
failure::bail!("Invalid label length {}", label_len)
|
||||
}
|
||||
total_len += 1 + label_len;
|
||||
// max len 255, but there also needs to be an empty label at the end
|
||||
if total_len > 254 { failure::bail!{"DNS name too long"} }
|
||||
if total_len > 254 {
|
||||
failure::bail! {"DNS name too long"}
|
||||
}
|
||||
check_enough_data!(data, label_len, "DnsName label");
|
||||
data.advance(label_len);
|
||||
}
|
||||
|
@ -1,11 +1,10 @@
|
||||
use crate::errors::*;
|
||||
use crate::ser::text::{DnsTextContext, quoted};
|
||||
use crate::ser::text::{quoted, DnsTextContext};
|
||||
|
||||
use super::{DnsName, DnsLabelRef};
|
||||
use super::{DnsLabelRef, DnsName};
|
||||
|
||||
/// Parse text representation of a domain name
|
||||
pub fn parse_name(context: &DnsTextContext, value: &str) -> Result<DnsName>
|
||||
{
|
||||
pub fn parse_name(context: &DnsTextContext, value: &str) -> Result<DnsName> {
|
||||
let raw = value.as_bytes();
|
||||
let mut name = DnsName::new_root();
|
||||
if raw == b"." {
|
||||
@ -25,11 +24,25 @@ pub fn parse_name(context: &DnsTextContext, value: &str) -> Result<DnsName>
|
||||
name.push_back(DnsLabelRef::new(&label)?)?;
|
||||
label.clear();
|
||||
} else if raw[pos] == b'\\' {
|
||||
failure::ensure!(pos + 1 < raw.len(), "unexpected end of name after backslash: {:?}", value);
|
||||
failure::ensure!(
|
||||
pos + 1 < raw.len(),
|
||||
"unexpected end of name after backslash: {:?}",
|
||||
value
|
||||
);
|
||||
if raw[pos + 1] >= b'0' && raw[pos + 1] <= b'9' {
|
||||
// \ddd escape
|
||||
failure::ensure!(pos + 3 < raw.len(), "unexpected end of name after backslash with digit: {:?}", value);
|
||||
failure::ensure!(raw[pos+2] >= b'0' && raw[pos+2] <= b'9' && raw[pos+3] >= b'0' && raw[pos+3] <= b'9', "expected three digits after backslash in name: {:?}", name);
|
||||
failure::ensure!(
|
||||
pos + 3 < raw.len(),
|
||||
"unexpected end of name after backslash with digit: {:?}",
|
||||
value
|
||||
);
|
||||
failure::ensure!(
|
||||
raw[pos + 2] >= b'0'
|
||||
&& raw[pos + 2] <= b'9' && raw[pos + 3] >= b'0'
|
||||
&& raw[pos + 3] <= b'9',
|
||||
"expected three digits after backslash in name: {:?}",
|
||||
name
|
||||
);
|
||||
let d1 = (raw[pos + 1] - b'0') as u32;
|
||||
let d2 = (raw[pos + 2] - b'0') as u32;
|
||||
let d3 = (raw[pos + 3] - b'0') as u32;
|
||||
@ -41,7 +54,12 @@ pub fn parse_name(context: &DnsTextContext, value: &str) -> Result<DnsName>
|
||||
label.push(raw[pos + 1]);
|
||||
}
|
||||
} else {
|
||||
failure::ensure!(!quoted::is_ascii_whitespace(raw[pos]), "whitespace must be encoded as \\{:03} in: {:?}", raw[pos], name);
|
||||
failure::ensure!(
|
||||
!quoted::is_ascii_whitespace(raw[pos]),
|
||||
"whitespace must be encoded as \\{:03} in: {:?}",
|
||||
raw[pos],
|
||||
name
|
||||
);
|
||||
label.push(raw[pos]);
|
||||
}
|
||||
pos += 1;
|
||||
@ -55,7 +73,9 @@ pub fn parse_name(context: &DnsTextContext, value: &str) -> Result<DnsName>
|
||||
|
||||
match context.origin() {
|
||||
Some(o) => {
|
||||
for l in o { name.push_back(l)?; }
|
||||
for l in o {
|
||||
name.push_back(l)?;
|
||||
}
|
||||
},
|
||||
None => failure::bail!("missing trailing dot without $ORIGIN"),
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
use bytes::Bytes;
|
||||
use crate::errors::*;
|
||||
use crate::ser::packet;
|
||||
use crate::ser::packet::DnsPacketData;
|
||||
use bytes::Bytes;
|
||||
use std::io::Cursor;
|
||||
use crate::errors::*;
|
||||
|
||||
use super::{DnsName, DnsCompressedName, DnsLabelRef, DisplayLabels, DisplayLabelsOptions};
|
||||
use super::{DisplayLabels, DisplayLabelsOptions, DnsCompressedName, DnsLabelRef, DnsName};
|
||||
|
||||
/*
|
||||
fn deserialize(bytes: &'static [u8]) -> Result<DnsName> {
|
||||
@ -25,40 +25,20 @@ fn de_uncompressed(bytes: &'static [u8]) -> Result<DnsName> {
|
||||
|
||||
fn check_uncompressed_display(bytes: &'static [u8], txt: &str, label_count: u8) {
|
||||
let name = de_uncompressed(bytes).unwrap();
|
||||
assert_eq!(
|
||||
name.labels().count(),
|
||||
label_count as usize
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", name),
|
||||
txt
|
||||
);
|
||||
assert_eq!(name.labels().count(), label_count as usize);
|
||||
assert_eq!(format!("{}", name), txt);
|
||||
}
|
||||
|
||||
fn check_uncompressed_debug(bytes: &'static [u8], txt: &str) {
|
||||
let name = de_uncompressed(bytes).unwrap();
|
||||
assert_eq!(
|
||||
format!("{:?}", name),
|
||||
txt
|
||||
);
|
||||
assert_eq!(format!("{:?}", name), txt);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_and_display_name() {
|
||||
check_uncompressed_display(
|
||||
b"\x07example\x03com\x00",
|
||||
"example.com.",
|
||||
2,
|
||||
);
|
||||
check_uncompressed_display(
|
||||
b"\x07e!am.l\\\x03com\x00",
|
||||
"e\\033am\\.l\\\\.com.",
|
||||
2,
|
||||
);
|
||||
check_uncompressed_debug(
|
||||
b"\x07e!am.l\\\x03com\x00",
|
||||
r#""e\\033am\\.l\\\\.com.""#,
|
||||
);
|
||||
check_uncompressed_display(b"\x07example\x03com\x00", "example.com.", 2);
|
||||
check_uncompressed_display(b"\x07e!am.l\\\x03com\x00", "e\\033am\\.l\\\\.com.", 2);
|
||||
check_uncompressed_debug(b"\x07e!am.l\\\x03com\x00", r#""e\\033am\\.l\\\\.com.""#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -84,41 +64,24 @@ fn modifications() {
|
||||
let mut name = de_uncompressed(b"\x07example\x03com\x00").unwrap();
|
||||
|
||||
name.push_front(DnsLabelRef::new(b"www").unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
format!("{}", name),
|
||||
"www.example.com."
|
||||
);
|
||||
assert_eq!(format!("{}", name), "www.example.com.");
|
||||
|
||||
name.push_back(DnsLabelRef::new(b"org").unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
format!("{}", name),
|
||||
"www.example.com.org."
|
||||
);
|
||||
assert_eq!(format!("{}", name), "www.example.com.org.");
|
||||
|
||||
name.pop_front();
|
||||
assert_eq!(
|
||||
format!("{}", name),
|
||||
"example.com.org."
|
||||
);
|
||||
assert_eq!(format!("{}", name), "example.com.org.");
|
||||
|
||||
name.push_front(DnsLabelRef::new(b"mx").unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
format!("{}", name),
|
||||
"mx.example.com.org."
|
||||
);
|
||||
assert_eq!(format!("{}", name), "mx.example.com.org.");
|
||||
// the "mx" label should fit into the place "www" used before,
|
||||
// make sure the buffer was reused and the name not moved within
|
||||
assert_eq!(1, name.label_offsets.label_pos(0));
|
||||
|
||||
name.push_back(DnsLabelRef::new(b"com").unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
format!("{}", name),
|
||||
"mx.example.com.org.com."
|
||||
);
|
||||
assert_eq!(format!("{}", name), "mx.example.com.org.com.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn de_compressed(bytes: &'static [u8], offset: usize) -> Result<DnsCompressedName> {
|
||||
use bytes::Buf;
|
||||
|
||||
@ -133,22 +96,13 @@ fn de_compressed(bytes: &'static [u8], offset: usize) -> Result<DnsCompressedNam
|
||||
|
||||
fn check_compressed_display(bytes: &'static [u8], offset: usize, txt: &str, label_count: u8) {
|
||||
let name = de_compressed(bytes, offset).unwrap();
|
||||
assert_eq!(
|
||||
name.labels().count(),
|
||||
label_count as usize
|
||||
);
|
||||
assert_eq!(
|
||||
format!("{}", name),
|
||||
txt
|
||||
);
|
||||
assert_eq!(name.labels().count(), label_count as usize);
|
||||
assert_eq!(format!("{}", name), txt);
|
||||
}
|
||||
|
||||
fn check_compressed_debug(bytes: &'static [u8], offset: usize, txt: &str) {
|
||||
let name = de_compressed(bytes, offset).unwrap();
|
||||
assert_eq!(
|
||||
format!("{:?}", name),
|
||||
txt
|
||||
);
|
||||
assert_eq!(format!("{:?}", name), txt);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -159,22 +113,21 @@ fn parse_invalid_compressed_name() {
|
||||
|
||||
#[test]
|
||||
fn parse_and_display_compressed_name() {
|
||||
check_compressed_display(b"\x03com\x00\x07example\xc0\x00", 5, "example.com.", 2);
|
||||
check_compressed_display(
|
||||
b"\x03com\x00\x07example\xc0\x00", 5,
|
||||
"example.com.",
|
||||
2,
|
||||
);
|
||||
check_compressed_display(
|
||||
b"\x03com\x00\x07e!am.l\\\xc0\x00", 5,
|
||||
b"\x03com\x00\x07e!am.l\\\xc0\x00",
|
||||
5,
|
||||
"e\\033am\\.l\\\\.com.",
|
||||
2,
|
||||
);
|
||||
check_compressed_debug(
|
||||
b"\x03com\x00\x07e!am.l\\\xc0\x00", 5,
|
||||
b"\x03com\x00\x07e!am.l\\\xc0\x00",
|
||||
5,
|
||||
r#""e\\033am\\.l\\\\.com.""#,
|
||||
);
|
||||
check_compressed_display(
|
||||
b"\x03com\x00\x07example\xc0\x00\x03www\xc0\x05", 15,
|
||||
b"\x03com\x00\x07example\xc0\x00\x03www\xc0\x05",
|
||||
15,
|
||||
"www.example.com.",
|
||||
3,
|
||||
);
|
||||
@ -185,35 +138,20 @@ fn modifications_compressed() {
|
||||
let mut name = de_compressed(b"\x03com\x00\x07example\xc0\x00\xc0\x05", 15).unwrap();
|
||||
|
||||
name.push_front(DnsLabelRef::new(b"www").unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
format!("{}", name),
|
||||
"www.example.com."
|
||||
);
|
||||
assert_eq!(format!("{}", name), "www.example.com.");
|
||||
|
||||
name.push_back(DnsLabelRef::new(b"org").unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
format!("{}", name),
|
||||
"www.example.com.org."
|
||||
);
|
||||
assert_eq!(format!("{}", name), "www.example.com.org.");
|
||||
|
||||
name.pop_front();
|
||||
assert_eq!(
|
||||
format!("{}", name),
|
||||
"example.com.org."
|
||||
);
|
||||
assert_eq!(format!("{}", name), "example.com.org.");
|
||||
|
||||
name.push_front(DnsLabelRef::new(b"mx").unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
format!("{}", name),
|
||||
"mx.example.com.org."
|
||||
);
|
||||
assert_eq!(format!("{}", name), "mx.example.com.org.");
|
||||
// the "mx" label should fit into the place "www" used before,
|
||||
// make sure the buffer was reused and the name not moved within
|
||||
assert_eq!(1, name.label_offsets.label_pos(0));
|
||||
|
||||
name.push_back(DnsLabelRef::new(b"com").unwrap()).unwrap();
|
||||
assert_eq!(
|
||||
format!("{}", name),
|
||||
"mx.example.com.org.com."
|
||||
);
|
||||
assert_eq!(format!("{}", name), "mx.example.com.org.com.");
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
use bytes::{Bytes, Buf, BufMut};
|
||||
use crate::common_types::Type;
|
||||
use data_encoding;
|
||||
use crate::errors::*;
|
||||
use crate::ser::packet::{
|
||||
remaining_bytes, short_blob, write_short_blob, DnsPacketData, DnsPacketWriteContext,
|
||||
};
|
||||
use crate::ser::text::{
|
||||
next_field, skip_whitespace, DnsTextContext, DnsTextData, DnsTextFormatter,
|
||||
};
|
||||
use bytes::{Buf, BufMut, Bytes};
|
||||
use data_encoding;
|
||||
use failure::{Fail, ResultExt};
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext, remaining_bytes, short_blob, write_short_blob};
|
||||
use crate::ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, skip_whitespace, next_field};
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
@ -79,7 +83,12 @@ impl DnsPacketData for NsecTypeBitmap {
|
||||
let mut prev_window = None;
|
||||
while data.has_remaining() {
|
||||
let window_base = (data.get_u8() as u16) << 8;
|
||||
failure::ensure!(Some(window_base) > prev_window, "wrong nsec bitmap window order, {:?} <= {:?}", Some(window_base), prev_window);
|
||||
failure::ensure!(
|
||||
Some(window_base) > prev_window,
|
||||
"wrong nsec bitmap window order, {:?} <= {:?}",
|
||||
Some(window_base),
|
||||
prev_window
|
||||
);
|
||||
prev_window = Some(window_base);
|
||||
check_enough_data!(data, 1, "nsec bitmap window length");
|
||||
let window_len = data.get_u8() as u16;
|
||||
@ -95,10 +104,7 @@ impl DnsPacketData for NsecTypeBitmap {
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(NsecTypeBitmap{
|
||||
raw: raw,
|
||||
set: set,
|
||||
})
|
||||
Ok(NsecTypeBitmap { raw: raw, set: set })
|
||||
}
|
||||
|
||||
fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||
@ -150,15 +156,21 @@ impl DnsPacketData for NextHashedOwnerName {
|
||||
impl DnsTextData for NextHashedOwnerName {
|
||||
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> crate::errors::Result<Self> {
|
||||
let field = next_field(data)?;
|
||||
let raw = BASE32HEX_NOPAD_ALLOW_WS.decode(field.as_bytes())
|
||||
let raw = BASE32HEX_NOPAD_ALLOW_WS
|
||||
.decode(field.as_bytes())
|
||||
.with_context(|e| e.context(format!("invalid base32hex (no padding): {:?}", field)))?;
|
||||
failure::ensure!(raw.len() > 0, "NextHashedOwnerName must not be empty");
|
||||
failure::ensure!(raw.len() < 256, "NextHashedOwnerName field must be at most 255 bytes long");
|
||||
failure::ensure!(
|
||||
raw.len() < 256,
|
||||
"NextHashedOwnerName field must be at most 255 bytes long"
|
||||
);
|
||||
Ok(NextHashedOwnerName(raw.into()))
|
||||
}
|
||||
|
||||
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
||||
if self.0.is_empty() { return Err(fmt::Error); }
|
||||
if self.0.is_empty() {
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
write!(f, "{}", BASE32HEX_NOPAD_ALLOW_WS.encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use bytes::{Bytes, Buf, BufMut};
|
||||
use crate::common_types::Type;
|
||||
use crate::errors::*;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext, remaining_bytes};
|
||||
use crate::ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, skip_whitespace};
|
||||
use crate::ser::packet::{remaining_bytes, DnsPacketData, DnsPacketWriteContext};
|
||||
use crate::ser::text::{skip_whitespace, DnsTextContext, DnsTextData, DnsTextFormatter};
|
||||
use bytes::{Buf, BufMut, Bytes};
|
||||
use std::collections::BTreeSet;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
@ -67,10 +67,7 @@ impl DnsPacketData for NxtTypeBitmap {
|
||||
current += 1;
|
||||
}
|
||||
}
|
||||
Ok(NxtTypeBitmap{
|
||||
raw: raw,
|
||||
set: set,
|
||||
})
|
||||
Ok(NxtTypeBitmap { raw: raw, set: set })
|
||||
}
|
||||
|
||||
fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||
|
@ -1,8 +1,8 @@
|
||||
use bytes::Bytes;
|
||||
use crate::errors::*;
|
||||
use failure::Fail;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||
use crate::ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field};
|
||||
use crate::ser::text::{next_field, DnsTextContext, DnsTextData, DnsTextFormatter};
|
||||
use bytes::Bytes;
|
||||
use failure::Fail;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
|
||||
@ -38,11 +38,9 @@ impl DnsTextData for OptionalTTL {
|
||||
*data = data_found;
|
||||
Ok(OptionalTTL(ttl))
|
||||
},
|
||||
Err(e) => {
|
||||
Ok(OptionalTTL(context.last_ttl()
|
||||
.ok_or_else(|| e.context("TTL not available in context, failed parsing optional TTL"))?
|
||||
))
|
||||
},
|
||||
Err(e) => Ok(OptionalTTL(context.last_ttl().ok_or_else(|| {
|
||||
e.context("TTL not available in context, failed parsing optional TTL")
|
||||
})?)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
use bytes::{Bytes, Buf, BufMut};
|
||||
use crate::errors::*;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext, short_blob, write_short_blob, remaining_bytes};
|
||||
use crate::ser::packet::{
|
||||
remaining_bytes, short_blob, write_short_blob, DnsPacketData, DnsPacketWriteContext,
|
||||
};
|
||||
use crate::ser::text::*;
|
||||
use bytes::{Buf, BufMut, Bytes};
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
|
||||
@ -43,7 +45,9 @@ impl DnsPacketData for LongText {
|
||||
let mut texts = Vec::new();
|
||||
loop {
|
||||
texts.push(short_blob(data)?);
|
||||
if !data.has_remaining() { break; }
|
||||
if !data.has_remaining() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(LongText(texts))
|
||||
}
|
||||
@ -66,7 +70,10 @@ impl DnsTextData for LongText {
|
||||
skip_whitespace(data);
|
||||
while !data.is_empty() {
|
||||
let part = next_quoted_field(data)?;
|
||||
failure::ensure!(part.len() < 256, "long text component must be at most 255 bytes long");
|
||||
failure::ensure!(
|
||||
part.len() < 256,
|
||||
"long text component must be at most 255 bytes long"
|
||||
);
|
||||
result.push(part.into());
|
||||
}
|
||||
Ok(LongText(result))
|
||||
|
@ -36,7 +36,11 @@ fn test_is_leap_year() {
|
||||
|
||||
fn month_day_of_year_since_march(month: u8) -> u16 {
|
||||
debug_assert!(month >= 1 && month <= 12);
|
||||
let month_from_march = if month > 2 { month as u16 - 3} else { month as u16 + 9 };
|
||||
let month_from_march = if month > 2 {
|
||||
month as u16 - 3
|
||||
} else {
|
||||
month as u16 + 9
|
||||
};
|
||||
(153 * month_from_march + 2) / 5
|
||||
}
|
||||
|
||||
@ -49,7 +53,11 @@ fn month_and_day_from_day_of_year_since_march(day_of_year: i32) -> (u8, u8) {
|
||||
|
||||
let month_from_march = month_from_march as u8;
|
||||
|
||||
let month = if month_from_march < 10 { month_from_march + 3 } else { month_from_march - 9 };
|
||||
let month = if month_from_march < 10 {
|
||||
month_from_march + 3
|
||||
} else {
|
||||
month_from_march - 9
|
||||
};
|
||||
debug_assert!(month >= 1 && month <= 12);
|
||||
|
||||
(month, day as u8)
|
||||
@ -57,12 +65,18 @@ fn month_and_day_from_day_of_year_since_march(day_of_year: i32) -> (u8, u8) {
|
||||
|
||||
#[test]
|
||||
fn test_month_day_of_year_since_march() {
|
||||
static MONTH_START: [u16; 12] = [306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, ];
|
||||
static MONTH_START: [u16; 12] = [306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275];
|
||||
static DAYS_IN_MONTHS: [u8; 12] = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
||||
for (m, &s) in MONTH_START.iter().enumerate() {
|
||||
assert_eq!(month_day_of_year_since_march(m as u8 + 1), s);
|
||||
assert_eq!(month_and_day_from_day_of_year_since_march(s as i32), (m as u8 + 1, 1));
|
||||
assert_eq!(month_and_day_from_day_of_year_since_march(s as i32 + DAYS_IN_MONTHS[m] as i32 - 1), (m as u8 + 1, DAYS_IN_MONTHS[m]));
|
||||
assert_eq!(
|
||||
month_and_day_from_day_of_year_since_march(s as i32),
|
||||
(m as u8 + 1, 1)
|
||||
);
|
||||
assert_eq!(
|
||||
month_and_day_from_day_of_year_since_march(s as i32 + DAYS_IN_MONTHS[m] as i32 - 1),
|
||||
(m as u8 + 1, DAYS_IN_MONTHS[m])
|
||||
);
|
||||
}
|
||||
assert_eq!(month_and_day_from_day_of_year_since_march(365), (2, 29));
|
||||
}
|
||||
@ -197,15 +211,21 @@ fn test_days_since_march1_y0() {
|
||||
assert_eq!(days_since_march1_y0(0, 3, 1), 0);
|
||||
assert_eq!(days_since_march1_y0(1, 3, 1), 365);
|
||||
assert_eq!(days_since_march1_y0(1970, 1, 1), EPOCH_DAYS_SINCE_MARCH1_Y0);
|
||||
assert_eq!(days_since_march1_y0(1970, 1, 1),
|
||||
/* regular days in years: */ 365 * 1969
|
||||
assert_eq!(
|
||||
days_since_march1_y0(1970, 1, 1),
|
||||
/* regular days in years: */
|
||||
365 * 1969
|
||||
/* days from leap years: */ + (1969 / 4 - 15)
|
||||
/* days from march 1st year 0 to january 1st year 1: */ + 306);
|
||||
/* days from march 1st year 0 to january 1st year 1: */ + 306
|
||||
);
|
||||
|
||||
assert_eq!(split_days_since_march1_y0(-366), (-1, 3, 1));
|
||||
assert_eq!(split_days_since_march1_y0(0), (0, 3, 1));
|
||||
assert_eq!(split_days_since_march1_y0(365), (1, 3, 1));
|
||||
assert_eq!(split_days_since_march1_y0(EPOCH_DAYS_SINCE_MARCH1_Y0), (1970, 1, 1));
|
||||
assert_eq!(
|
||||
split_days_since_march1_y0(EPOCH_DAYS_SINCE_MARCH1_Y0),
|
||||
(1970, 1, 1)
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
@ -223,7 +243,10 @@ impl Tm {
|
||||
pub fn from_epoch(epoch: i64) -> Result<Self> {
|
||||
let (day, time_of_day) = pos_div_rem64(epoch, 86400);
|
||||
let days_since_march1_y0 = day + EPOCH_DAYS_SINCE_MARCH1_Y0 as i64;
|
||||
failure::ensure!((days_since_march1_y0 as i32) as i64 == days_since_march1_y0, "days in epoch out of range");
|
||||
failure::ensure!(
|
||||
(days_since_march1_y0 as i32) as i64 == days_since_march1_y0,
|
||||
"days in epoch out of range"
|
||||
);
|
||||
let days_since_march1_y0 = days_since_march1_y0 as i32;
|
||||
|
||||
let (year, month, day) = split_days_since_march1_y0(days_since_march1_y0);
|
||||
@ -249,9 +272,7 @@ impl Tm {
|
||||
}
|
||||
|
||||
fn day_seconds(&self) -> u32 {
|
||||
self.second as u32
|
||||
+ 60 * self.minute as u32
|
||||
+ 3600 * self.hour as u32
|
||||
self.second as u32 + 60 * self.minute as u32 + 3600 * self.hour as u32
|
||||
}
|
||||
|
||||
pub fn epoch(&self) -> i64 {
|
||||
@ -261,14 +282,24 @@ impl Tm {
|
||||
#[allow(non_snake_case)]
|
||||
pub fn parse_YYYYMMDDHHmmSS(s: &str) -> Result<Self> {
|
||||
failure::ensure!(s.len() == 14, "Tm string must be exactly 14 digits long");
|
||||
failure::ensure!(s.as_bytes().iter().all(|&b| b >= b'0' && b <= b'9'), "Tm string must be exactly 14 digits long");
|
||||
failure::ensure!(
|
||||
s.as_bytes().iter().all(|&b| b >= b'0' && b <= b'9'),
|
||||
"Tm string must be exactly 14 digits long"
|
||||
);
|
||||
let year = s[0..4].parse::<i16>()?;
|
||||
failure::ensure!(year >= 1, "year must be >= 1");
|
||||
failure::ensure!(year <= 9999, "year must be <= 9999");
|
||||
|
||||
fn p(s: &str, min: u8, max: u8, name: &'static str) -> crate::errors::Result<u8> {
|
||||
let v = s.parse::<u8>()?;
|
||||
failure::ensure!(v >= min && v <= max, "{} {} out of range {}-{}", name, v, min, max);
|
||||
failure::ensure!(
|
||||
v >= min && v <= max,
|
||||
"{} {} out of range {}-{}",
|
||||
name,
|
||||
v,
|
||||
min,
|
||||
max
|
||||
);
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
@ -280,25 +311,44 @@ impl Tm {
|
||||
|
||||
if 2 == month {
|
||||
failure::ensure!(day < 30, "day {} out of range in february", day);
|
||||
failure::ensure!(is_leap_year(year) || day < 29, "day {} out of range in february (not a leap year)", day);
|
||||
failure::ensure!(
|
||||
is_leap_year(year) || day < 29,
|
||||
"day {} out of range in february (not a leap year)",
|
||||
day
|
||||
);
|
||||
} else {
|
||||
static DAYS_IN_MONTHS: [u8; 12] = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
||||
let max_days = DAYS_IN_MONTHS[month as usize - 1];
|
||||
failure::ensure!(day <= max_days, "day {} out of range for month {}", day, month);
|
||||
failure::ensure!(
|
||||
day <= max_days,
|
||||
"day {} out of range for month {}",
|
||||
day,
|
||||
month
|
||||
);
|
||||
}
|
||||
|
||||
Ok(Tm{ year, month, day, hour, minute, second })
|
||||
Ok(Tm {
|
||||
year,
|
||||
month,
|
||||
day,
|
||||
hour,
|
||||
minute,
|
||||
second,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn format_YYYYMMDDHHmmSS<W>(&self, f: &mut W) -> fmt::Result
|
||||
where
|
||||
W: fmt::Write + ?Sized
|
||||
W: fmt::Write + ?Sized,
|
||||
{
|
||||
if self.year < 0 || self.year > 9999 { return Err(fmt::Error); }
|
||||
write!(f, "{:04}{:02}{:02}{:02}{:02}{:02}",
|
||||
self.year, self.month, self.day,
|
||||
self.hour, self.minute, self.second
|
||||
if self.year < 0 || self.year > 9999 {
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
write!(
|
||||
f,
|
||||
"{:04}{:02}{:02}{:02}{:02}{:02}",
|
||||
self.year, self.month, self.day, self.hour, self.minute, self.second
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use bytes::Bytes;
|
||||
use crate::errors::*;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||
use crate::ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field};
|
||||
use crate::ser::text::{next_field, DnsTextContext, DnsTextData, DnsTextFormatter};
|
||||
use bytes::Bytes;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
|
||||
@ -39,7 +39,9 @@ impl DnsTextData for Time {
|
||||
}
|
||||
|
||||
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
||||
epoch::Tm::from_epoch(self.0 as i64).unwrap().format_YYYYMMDDHHmmSS(&mut*f.format_field()?)
|
||||
epoch::Tm::from_epoch(self.0 as i64)
|
||||
.unwrap()
|
||||
.format_YYYYMMDDHHmmSS(&mut *f.format_field()?)
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,7 +71,9 @@ impl DnsTextData for TimeStrict {
|
||||
}
|
||||
|
||||
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
||||
epoch::Tm::from_epoch(self.0 as i64).unwrap().format_YYYYMMDDHHmmSS(&mut*f.format_field()?)
|
||||
epoch::Tm::from_epoch(self.0 as i64)
|
||||
.unwrap()
|
||||
.format_YYYYMMDDHHmmSS(&mut *f.format_field()?)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use bytes::{Bytes, BufMut};
|
||||
use crate::errors::*;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext, remaining_bytes};
|
||||
use crate::ser::packet::{remaining_bytes, DnsPacketData, DnsPacketWriteContext};
|
||||
use crate::ser::text::*;
|
||||
use bytes::{BufMut, Bytes};
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
|
||||
|
@ -14,10 +14,16 @@ fn parse_rsa(data: &[u8]) -> crate::errors::Result<PublicKey> {
|
||||
let exp_len: usize;
|
||||
let offset: usize;
|
||||
if data[0] == 0 {
|
||||
failure::ensure!(data.len() >= 3, "RSA public key: unexpected end of data when decoding exponent length");
|
||||
failure::ensure!(
|
||||
data.len() >= 3,
|
||||
"RSA public key: unexpected end of data when decoding exponent length"
|
||||
);
|
||||
exp_len = (data[1] as usize) << 8 + (data[2] as usize);
|
||||
offset = 3;
|
||||
failure::ensure!(exp_len >= 256, "RSA public key: exponent length in long form but too small");
|
||||
failure::ensure!(
|
||||
exp_len >= 256,
|
||||
"RSA public key: exponent length in long form but too small"
|
||||
);
|
||||
} else {
|
||||
exp_len = data[0] as usize;
|
||||
offset = 1;
|
||||
@ -25,23 +31,37 @@ fn parse_rsa(data: &[u8]) -> crate::errors::Result<PublicKey> {
|
||||
|
||||
assert!(exp_len > 0); // should be unreachable: 0 means two bytes, which are checked for >= 256
|
||||
|
||||
failure::ensure!(exp_len <= RSA_BYTES_LIMIT, "RSA public key: exponent too long (limit: {} bits)", RSA_BITS_LIMIT);
|
||||
failure::ensure!(
|
||||
exp_len <= RSA_BYTES_LIMIT,
|
||||
"RSA public key: exponent too long (limit: {} bits)",
|
||||
RSA_BITS_LIMIT
|
||||
);
|
||||
|
||||
failure::ensure!(data.len() >= offset + exp_len, "RSA public key: unexpected end of data when reading exponent");
|
||||
failure::ensure!(data[offset] != 0, "RSA public key: leading zero in exponent");
|
||||
failure::ensure!(
|
||||
data.len() >= offset + exp_len,
|
||||
"RSA public key: unexpected end of data when reading exponent"
|
||||
);
|
||||
failure::ensure!(
|
||||
data[offset] != 0,
|
||||
"RSA public key: leading zero in exponent"
|
||||
);
|
||||
let exponent = BigUint::from_bytes_be(&data[offset..][..exp_len]);
|
||||
|
||||
let modulus_data = &data[offset..][exp_len..];
|
||||
failure::ensure!(modulus_data.len() <= RSA_BYTES_LIMIT, "RSA public key: modulus too long (limit: {} bits)", RSA_BITS_LIMIT);
|
||||
failure::ensure!(
|
||||
modulus_data.len() <= RSA_BYTES_LIMIT,
|
||||
"RSA public key: modulus too long (limit: {} bits)",
|
||||
RSA_BITS_LIMIT
|
||||
);
|
||||
failure::ensure!(!modulus_data.is_empty(), "RSA public key: modulus empty");
|
||||
failure::ensure!(modulus_data[offset] != 0, "RSA public key: leading zero in modulus");
|
||||
failure::ensure!(
|
||||
modulus_data[offset] != 0,
|
||||
"RSA public key: leading zero in modulus"
|
||||
);
|
||||
|
||||
let modulus = BigUint::from_bytes_be(modulus_data);
|
||||
|
||||
Ok(PublicKey::RSA {
|
||||
exponent,
|
||||
modulus,
|
||||
})
|
||||
Ok(PublicKey::RSA { exponent, modulus })
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
@ -58,34 +78,53 @@ impl PublicKey {
|
||||
pub fn parse(algorithm: DnsSecAlgorithm, data: &[u8]) -> crate::errors::Result<Self> {
|
||||
use DnsSecAlgorithmKnown::*;
|
||||
|
||||
let algorithm = algorithm.into_known().ok_or_else(|| failure::format_err!("Unknown algorithm"))?;
|
||||
let algorithm = algorithm
|
||||
.into_known()
|
||||
.ok_or_else(|| failure::format_err!("Unknown algorithm"))?;
|
||||
match algorithm {
|
||||
DELETE|INDIRECT|PRIVATEDNS|PRIVATEOID => failure::bail!("Algorithm {:?} not used with actual key", algorithm),
|
||||
DELETE | INDIRECT | PRIVATEDNS | PRIVATEOID => {
|
||||
failure::bail!("Algorithm {:?} not used with actual key", algorithm)
|
||||
},
|
||||
RSAMD5 | RSASHA1 | RSASHA1_NSEC3_SHA1 | RSASHA256 | RSASHA512 => parse_rsa(data),
|
||||
DH | DSA | DSA_NSEC3_SHA1 => failure::bail!("Algorithm {:?} not supported", algorithm),
|
||||
ECDSAP256SHA256 => {
|
||||
failure::ensure!(data.len() == 64, "Expected 64 bytes public key for ECDSAP256");
|
||||
failure::ensure!(
|
||||
data.len() == 64,
|
||||
"Expected 64 bytes public key for ECDSAP256"
|
||||
);
|
||||
let mut x = [0u8; 32];
|
||||
x.copy_from_slice(&data[..32]);
|
||||
let mut y = [0u8; 32];
|
||||
y.copy_from_slice(&data[32..]);
|
||||
Ok(PublicKey::ECDSAP256 { xy: Box::new((x, y)) })
|
||||
Ok(PublicKey::ECDSAP256 {
|
||||
xy: Box::new((x, y)),
|
||||
})
|
||||
},
|
||||
ECDSAP384SHA384 => {
|
||||
failure::ensure!(data.len() == 96, "Expected 96 bytes public key for ECDSAP384");
|
||||
failure::ensure!(
|
||||
data.len() == 96,
|
||||
"Expected 96 bytes public key for ECDSAP384"
|
||||
);
|
||||
let mut x = [0u8; 48];
|
||||
x.copy_from_slice(&data[..48]);
|
||||
let mut y = [0u8; 48];
|
||||
y.copy_from_slice(&data[48..]);
|
||||
Ok(PublicKey::ECDSAP384 { xy: Box::new((x, y)) })
|
||||
Ok(PublicKey::ECDSAP384 {
|
||||
xy: Box::new((x, y)),
|
||||
})
|
||||
},
|
||||
ECC_GOST => {
|
||||
failure::ensure!(data.len() == 64, "Expected 64 bytes public key for ECC_GOST");
|
||||
failure::ensure!(
|
||||
data.len() == 64,
|
||||
"Expected 64 bytes public key for ECC_GOST"
|
||||
);
|
||||
let mut x = [0u8; 32];
|
||||
x.copy_from_slice(&data[..32]);
|
||||
let mut y = [0u8; 32];
|
||||
y.copy_from_slice(&data[32..]);
|
||||
Ok(PublicKey::ECC_GOST { xy: Box::new((x, y)) })
|
||||
Ok(PublicKey::ECC_GOST {
|
||||
xy: Box::new((x, y)),
|
||||
})
|
||||
},
|
||||
ED25519 => {
|
||||
failure::ensure!(data.len() == 32, "Expected 32 bytes public key for ED25519");
|
||||
|
@ -26,17 +26,19 @@ impl NotEnoughData {
|
||||
|
||||
impl fmt::Display for NotEnoughData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "not enough data at position {} in {:?}", self.position, self.data)
|
||||
write!(
|
||||
f,
|
||||
"not enough data at position {} in {:?}",
|
||||
self.position, self.data
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl failure::Fail for NotEnoughData {}
|
||||
|
||||
macro_rules! check_enough_data {
|
||||
($data:ident, $n:expr, $context:expr) => {
|
||||
{
|
||||
($data:ident, $n:expr, $context:expr) => {{
|
||||
use $crate::_failure::ResultExt;
|
||||
$crate::errors::NotEnoughData::check($data, $n).context($context)?;
|
||||
}
|
||||
};
|
||||
}};
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
#[doc(hidden)]
|
||||
pub use failure as _failure; // re-export for macros
|
||||
pub use bytes as _bytes;
|
||||
#[doc(hidden)]
|
||||
pub use bytes as _bytes; // re-export for macros
|
||||
pub use failure as _failure; // re-export for macros // re-export for macros
|
||||
|
||||
extern crate self as dnsbox_base;
|
||||
|
||||
#[macro_use]
|
||||
pub mod errors;
|
||||
|
||||
pub mod common_types;
|
||||
#[cfg(feature = "crypto")]
|
||||
pub mod crypto;
|
||||
pub mod common_types;
|
||||
pub mod ser;
|
||||
pub mod packet;
|
||||
pub mod records;
|
||||
pub mod ser;
|
||||
mod unsafe_ops;
|
||||
|
@ -1,11 +1,11 @@
|
||||
use byteorder::ByteOrder;
|
||||
use bytes::{Bytes, Buf, BufMut, BigEndian};
|
||||
use crate::common_types::{Type, Class, DnsCompressedName, types};
|
||||
use crate::common_types::{types, Class, DnsCompressedName, Type};
|
||||
use crate::errors::*;
|
||||
use crate::ser::RRData;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||
use std::io::Cursor;
|
||||
use crate::records::registry::deserialize_rr_data;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||
use crate::ser::RRData;
|
||||
use byteorder::ByteOrder;
|
||||
use bytes::{BigEndian, Buf, BufMut, Bytes};
|
||||
use std::io::Cursor;
|
||||
|
||||
pub mod opt;
|
||||
|
||||
@ -38,7 +38,11 @@ pub struct DnsHeaderFlags {
|
||||
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 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;
|
||||
@ -67,8 +71,7 @@ impl DnsPacketData for DnsHeaderFlags {
|
||||
| match self.qr {
|
||||
QueryResponse::Query => 0,
|
||||
QueryResponse::Response => 1,
|
||||
}
|
||||
| (((0xf & self.opcode) as u16) << 11)
|
||||
} | (((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 }
|
||||
@ -76,8 +79,7 @@ impl DnsPacketData for DnsHeaderFlags {
|
||||
| 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
|
||||
;
|
||||
| (0xf & self.rcode) as u16;
|
||||
flags.serialize(context, packet)
|
||||
}
|
||||
}
|
||||
@ -123,7 +125,11 @@ impl DnsPacketData for Resource {
|
||||
rrdata.advance(pos);
|
||||
let rd = deserialize_rr_data(ttl, class, rr_type, &mut rrdata)?;
|
||||
|
||||
failure::ensure!(!rrdata.has_remaining(), "data remaining: {} bytes", rrdata.remaining());
|
||||
failure::ensure!(
|
||||
!rrdata.has_remaining(),
|
||||
"data remaining: {} bytes",
|
||||
rrdata.remaining()
|
||||
);
|
||||
|
||||
Ok(Resource {
|
||||
name,
|
||||
@ -222,10 +228,18 @@ impl DnsPacketData for 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<_>>>()?,
|
||||
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,
|
||||
};
|
||||
|
||||
@ -249,8 +263,14 @@ impl DnsPacketData for DnsPacket {
|
||||
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||
failure::ensure!(self.question.len() < 0x1_0000, "too many question entries");
|
||||
failure::ensure!(self.answer.len() < 0x1_0000, "too many answer entries");
|
||||
failure::ensure!(self.authority.len() < 0x1_0000, "too many authority entries");
|
||||
failure::ensure!(self.additional.len() < 0x1_0000, "too many additional entries");
|
||||
failure::ensure!(
|
||||
self.authority.len() < 0x1_0000,
|
||||
"too many authority entries"
|
||||
);
|
||||
failure::ensure!(
|
||||
self.additional.len() < 0x1_0000,
|
||||
"too many additional entries"
|
||||
);
|
||||
let header = DnsHeader {
|
||||
id: self.id,
|
||||
flags: self.flags,
|
||||
|
@ -1,10 +1,10 @@
|
||||
use byteorder::ByteOrder;
|
||||
use bytes::{Bytes, Buf, BufMut, BigEndian};
|
||||
use crate::common_types::{DnsCompressedName, Class, types};
|
||||
use crate::common_types::{types, Class, DnsCompressedName};
|
||||
use crate::errors::*;
|
||||
use crate::packet::Resource;
|
||||
use crate::records::UnknownRecord;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext, get_blob, remaining_bytes};
|
||||
use crate::ser::packet::{get_blob, remaining_bytes, DnsPacketData, DnsPacketWriteContext};
|
||||
use byteorder::ByteOrder;
|
||||
use bytes::{BigEndian, Buf, BufMut, Bytes};
|
||||
use std::io::{Cursor, Read};
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
|
||||
@ -65,7 +65,7 @@ pub enum DnsOption {
|
||||
Unknown {
|
||||
code: u16,
|
||||
data: Bytes,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
impl DnsOption {
|
||||
@ -74,7 +74,12 @@ impl DnsOption {
|
||||
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;
|
||||
failure::ensure!(scope_prefix_length <= source_prefix_length, "scope prefix {} > source prefix {}", scope_prefix_length, source_prefix_length);
|
||||
failure::ensure!(
|
||||
scope_prefix_length <= source_prefix_length,
|
||||
"scope prefix {} > source prefix {}",
|
||||
scope_prefix_length,
|
||||
source_prefix_length
|
||||
);
|
||||
let addr = match addr_family {
|
||||
1 => {
|
||||
failure::ensure!(source_prefix_length <= 32, "invalid prefix for IPv4");
|
||||
@ -110,24 +115,38 @@ impl DnsOption {
|
||||
fn parse_opt(code: u16, opt_data: Bytes) -> Result<Self> {
|
||||
let mut data = Cursor::new(opt_data);
|
||||
|
||||
let result = (|| Ok(match code {
|
||||
let result = (|| {
|
||||
Ok(match code {
|
||||
0x0003 => DnsOption::NSID(remaining_bytes(&mut data)),
|
||||
0x0008 => DnsOption::parse_client_subnet(&mut data)?,
|
||||
_ => failure::bail!("unknown option {}", code),
|
||||
}))()?;
|
||||
})
|
||||
})()?;
|
||||
|
||||
failure::ensure!(!data.has_remaining(), "option data remaining: {} bytes", data.remaining());
|
||||
failure::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<()> {
|
||||
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} => {
|
||||
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_be(match *addr {
|
||||
@ -230,7 +249,9 @@ impl Opt {
|
||||
let version = (r.ttl >> 16) as u8;
|
||||
let flags = OptFlags(r.ttl as u16);
|
||||
|
||||
if version > 0 { return Ok(Err(OptError::UnknownVersion)); }
|
||||
if version > 0 {
|
||||
return Ok(Err(OptError::UnknownVersion));
|
||||
}
|
||||
|
||||
let ur = match r.data.as_any().downcast_ref::<UnknownRecord>() {
|
||||
Some(ur) => ur,
|
||||
|
@ -1,7 +1,7 @@
|
||||
mod weird_structs;
|
||||
pub mod registry;
|
||||
mod structs;
|
||||
mod unknown;
|
||||
pub mod registry;
|
||||
mod weird_structs;
|
||||
|
||||
pub use self::structs::*;
|
||||
pub use self::unknown::*;
|
||||
|
@ -1,32 +1,28 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use bytes::Bytes;
|
||||
use crate::common_types::{DnsName, DnsCompressedName, Class, Type, types, classes};
|
||||
use crate::common_types::{classes, types, Class, DnsCompressedName, DnsName, Type};
|
||||
use crate::errors::*;
|
||||
use crate::packet::opt::{DnsOption, Opt};
|
||||
use crate::packet::*;
|
||||
use crate::packet::opt::{Opt, DnsOption};
|
||||
use crate::records::{UnknownRecord, registry, A};
|
||||
use crate::ser::{RRData, text};
|
||||
use crate::ser::packet::{DnsPacketData, deserialize_with};
|
||||
use crate::records::{registry, UnknownRecord, A};
|
||||
use crate::ser::packet::{deserialize_with, DnsPacketData};
|
||||
use crate::ser::{text, RRData};
|
||||
use bytes::Bytes;
|
||||
use std::io::Cursor;
|
||||
|
||||
fn fake_packet(rrtype: Type, raw: &[u8]) -> Bytes {
|
||||
let mut p = DnsPacket {
|
||||
question: vec![
|
||||
Question {
|
||||
question: vec![Question {
|
||||
qname: ".".parse().unwrap(),
|
||||
qtype: rrtype,
|
||||
qclass: classes::IN,
|
||||
}
|
||||
],
|
||||
answer: vec![
|
||||
Resource {
|
||||
}],
|
||||
answer: vec![Resource {
|
||||
name: "rec.test.".parse().unwrap(),
|
||||
class: classes::IN,
|
||||
ttl: 0,
|
||||
data: Box::new(UnknownRecord::new(rrtype, Bytes::from(raw))),
|
||||
}
|
||||
],
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -57,28 +53,23 @@ fn get_first_answer_rdata(packet: Bytes) -> Result<Bytes> {
|
||||
|
||||
fn serialized_answer(rrdata: Box<dyn RRData>) -> Result<Bytes> {
|
||||
let mut p = DnsPacket {
|
||||
question: vec![
|
||||
Question {
|
||||
question: vec![Question {
|
||||
qname: ".".parse().unwrap(),
|
||||
qtype: rrdata.rr_type(),
|
||||
qclass: classes::IN,
|
||||
}
|
||||
],
|
||||
answer: vec![
|
||||
Resource {
|
||||
}],
|
||||
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]) {
|
||||
// Make sure the canonic representation is sound itself
|
||||
if let Some(canonic) = canonic {
|
||||
@ -93,9 +84,8 @@ fn check(q: Type, text_input: &'static str, canonic: Option<&'static str>, raw:
|
||||
context.set_record_type(q);
|
||||
context.set_last_ttl(3600);
|
||||
|
||||
let d_zone: Box<dyn RRData> = text::parse_with(text_input, |data| {
|
||||
registry::parse_rr_data(&context, data)
|
||||
}).unwrap();
|
||||
let d_zone: Box<dyn RRData> =
|
||||
text::parse_with(text_input, |data| registry::parse_rr_data(&context, data)).unwrap();
|
||||
|
||||
let d_zone_text = d_zone.text().unwrap();
|
||||
// make sure we actually know the type and the text representation
|
||||
@ -117,7 +107,10 @@ fn check(q: Type, text_input: &'static str, canonic: Option<&'static str>, raw:
|
||||
|
||||
// pdns tests compare d_wire_text and canonic, but d_zone_text
|
||||
// already matches canonic
|
||||
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"
|
||||
);
|
||||
|
||||
let zone_as_wire = serialized_answer(d_zone).unwrap();
|
||||
assert_eq!(zone_as_wire, raw);
|
||||
@ -125,24 +118,16 @@ fn check(q: Type, text_input: &'static str, canonic: Option<&'static str>, raw:
|
||||
|
||||
#[test]
|
||||
fn test_A() {
|
||||
check(types::A,
|
||||
"127.0.0.1",
|
||||
None,
|
||||
b"\x7F\x00\x00\x01",
|
||||
);
|
||||
check(types::A, "127.0.0.1", None, b"\x7F\x00\x00\x01");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_NS() {
|
||||
// local nameserver
|
||||
check(types::NS,
|
||||
"ns.rec.test.",
|
||||
None,
|
||||
b"\x02ns\xc0\x11",
|
||||
);
|
||||
check(types::NS, "ns.rec.test.", None, b"\x02ns\xc0\x11");
|
||||
// non-local nameserver
|
||||
check(types::NS,
|
||||
check(
|
||||
types::NS,
|
||||
"ns.example.com.",
|
||||
None,
|
||||
b"\x02ns\x07example\x03com\x00",
|
||||
@ -155,13 +140,10 @@ fn test_NS() {
|
||||
#[test]
|
||||
fn test_CNAME() {
|
||||
// local alias
|
||||
check(types::CNAME,
|
||||
"name.rec.test.",
|
||||
None,
|
||||
b"\x04name\xc0\x11",
|
||||
);
|
||||
check(types::CNAME, "name.rec.test.", None, b"\x04name\xc0\x11");
|
||||
// non-local alias
|
||||
check(types::CNAME,
|
||||
check(
|
||||
types::CNAME,
|
||||
"name.example.com.",
|
||||
None,
|
||||
b"\x04name\x07example\x03com\x00",
|
||||
@ -215,13 +197,15 @@ fn test_SOA() {
|
||||
fn test_MR() {
|
||||
// BROKEN TESTS (2) (deprecated)
|
||||
// local name
|
||||
check(types::MR,
|
||||
check(
|
||||
types::MR,
|
||||
"newmailbox.rec.test.",
|
||||
None,
|
||||
b"\x0anewmailbox\xc0\x11",
|
||||
);
|
||||
// non-local name
|
||||
check(types::MR,
|
||||
check(
|
||||
types::MR,
|
||||
"newmailbox.example.com.",
|
||||
None,
|
||||
b"\x0anewmailbox\x07example\x03com\x00",
|
||||
@ -231,13 +215,10 @@ fn test_MR() {
|
||||
#[test]
|
||||
fn test_PTR() {
|
||||
// local name
|
||||
check(types::PTR,
|
||||
"ptr.rec.test.",
|
||||
None,
|
||||
b"\x03ptr\xc0\x11",
|
||||
);
|
||||
check(types::PTR, "ptr.rec.test.", None, b"\x03ptr\xc0\x11");
|
||||
// non-local name
|
||||
check(types::PTR,
|
||||
check(
|
||||
types::PTR,
|
||||
"ptr.example.com.",
|
||||
None,
|
||||
b"\x03ptr\x07example\x03com\x00",
|
||||
@ -246,22 +227,26 @@ fn test_PTR() {
|
||||
|
||||
#[test]
|
||||
fn test_HINFO() {
|
||||
check(types::HINFO,
|
||||
check(
|
||||
types::HINFO,
|
||||
"\"i686\" \"Linux\"",
|
||||
None,
|
||||
b"\x04i686\x05Linux",
|
||||
);
|
||||
check(types::HINFO,
|
||||
check(
|
||||
types::HINFO,
|
||||
"i686 \"Linux\"",
|
||||
Some("\"i686\" \"Linux\""),
|
||||
b"\x04i686\x05Linux",
|
||||
);
|
||||
check(types::HINFO,
|
||||
check(
|
||||
types::HINFO,
|
||||
"\"i686\" Linux",
|
||||
Some("\"i686\" \"Linux\""),
|
||||
b"\x04i686\x05Linux",
|
||||
);
|
||||
check(types::HINFO,
|
||||
check(
|
||||
types::HINFO,
|
||||
"i686 Linux",
|
||||
Some("\"i686\" \"Linux\""),
|
||||
b"\x04i686\x05Linux",
|
||||
@ -273,32 +258,26 @@ fn test_HINFO() {
|
||||
#[test]
|
||||
fn test_MX() {
|
||||
// local name
|
||||
check(types::MX,
|
||||
check(
|
||||
types::MX,
|
||||
"10 mx.rec.test.",
|
||||
None,
|
||||
b"\x00\x0a\x02mx\xc0\x11",
|
||||
);
|
||||
// non-local name
|
||||
check(types::MX,
|
||||
check(
|
||||
types::MX,
|
||||
"20 mx.example.com.",
|
||||
None,
|
||||
b"\x00\x14\x02mx\x07example\x03com\x00",
|
||||
);
|
||||
// root label
|
||||
check(types::MX,
|
||||
"20 .",
|
||||
None,
|
||||
b"\x00\x14\x00",
|
||||
);
|
||||
check(types::MX, "20 .", None, b"\x00\x14\x00");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_TXT() {
|
||||
check(types::TXT,
|
||||
"\"short text\"",
|
||||
None,
|
||||
b"\x0ashort text",
|
||||
);
|
||||
check(types::TXT, "\"short text\"", None, b"\x0ashort text");
|
||||
check(types::TXT,
|
||||
"\"long record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\" \"2222222222\"",
|
||||
None,
|
||||
@ -312,17 +291,20 @@ fn test_TXT() {
|
||||
b"\xfflong record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\x0a2222222222",
|
||||
);
|
||||
*/
|
||||
check(types::TXT,
|
||||
check(
|
||||
types::TXT,
|
||||
"\"\\195\\133LAND ISLANDS\"",
|
||||
None,
|
||||
b"\x0e\xc3\x85LAND ISLANDS",
|
||||
);
|
||||
check(types::TXT,
|
||||
check(
|
||||
types::TXT,
|
||||
"\"\u{00c5}LAND ISLANDS\"",
|
||||
Some("\"\\195\\133LAND ISLANDS\""),
|
||||
b"\x0e\xc3\x85LAND ISLANDS",
|
||||
);
|
||||
check(types::TXT,
|
||||
check(
|
||||
types::TXT,
|
||||
"\"nonbreakingtxt\"",
|
||||
None,
|
||||
b"\x0enonbreakingtxt",
|
||||
@ -332,13 +314,15 @@ fn test_TXT() {
|
||||
#[test]
|
||||
fn test_RP() {
|
||||
// local name
|
||||
check(types::RP,
|
||||
check(
|
||||
types::RP,
|
||||
"admin.rec.test. admin-info.rec.test.",
|
||||
None,
|
||||
b"\x05admin\x03rec\x04test\x00\x0aadmin-info\x03rec\x04test\x00",
|
||||
);
|
||||
// non-local name
|
||||
check(types::RP,
|
||||
check(
|
||||
types::RP,
|
||||
"admin.example.com. admin-info.example.com.",
|
||||
None,
|
||||
b"\x05admin\x07example\x03com\x00\x0aadmin-info\x07example\x03com\x00",
|
||||
@ -348,13 +332,15 @@ fn test_RP() {
|
||||
#[test]
|
||||
fn test_AFSDB() {
|
||||
// local name
|
||||
check(types::AFSDB,
|
||||
check(
|
||||
types::AFSDB,
|
||||
"1 afs-server.rec.test.",
|
||||
None,
|
||||
b"\x00\x01\x0aafs-server\x03rec\x04test\x00",
|
||||
);
|
||||
// non-local name
|
||||
check(types::AFSDB,
|
||||
check(
|
||||
types::AFSDB,
|
||||
"1 afs-server.example.com.",
|
||||
None,
|
||||
b"\x00\x01\x0aafs-server\x07example\x03com\x00",
|
||||
@ -390,17 +376,20 @@ fn test_KEY() {
|
||||
|
||||
#[test]
|
||||
fn test_AAAA() {
|
||||
check(types::AAAA,
|
||||
check(
|
||||
types::AAAA,
|
||||
"fe80::250:56ff:fe9b:114",
|
||||
None,
|
||||
b"\xFE\x80\x00\x00\x00\x00\x00\x00\x02\x50\x56\xFF\xFE\x9B\x01\x14",
|
||||
);
|
||||
check(types::AAAA,
|
||||
check(
|
||||
types::AAAA,
|
||||
"2a02:1b8:10:2::151",
|
||||
None,
|
||||
b"\x2a\x02\x01\xb8\x00\x10\x00\x02\x00\x00\x00\x00\x00\x00\x01\x51",
|
||||
);
|
||||
check(types::AAAA,
|
||||
check(
|
||||
types::AAAA,
|
||||
"::1",
|
||||
None,
|
||||
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01",
|
||||
@ -409,22 +398,26 @@ fn test_AAAA() {
|
||||
|
||||
#[test]
|
||||
fn test_LOC() {
|
||||
check(types::LOC,
|
||||
check(
|
||||
types::LOC,
|
||||
"32 7 19 S 116 2 25 E",
|
||||
Some("32 7 19.000 S 116 2 25.000 E 0.00m 1.00m 10000.00m 10.00m"),
|
||||
b"\x00\x12\x16\x13\x79\x1b\x7d\x28\x98\xe6\x48\x68\x00\x98\x96\x80",
|
||||
);
|
||||
check(types::LOC,
|
||||
check(
|
||||
types::LOC,
|
||||
"32 7 19 S 116 2 25 E 10m",
|
||||
Some("32 7 19.000 S 116 2 25.000 E 10.00m 1.00m 10000.00m 10.00m"),
|
||||
b"\x00\x12\x16\x13\x79\x1b\x7d\x28\x98\xe6\x48\x68\x00\x98\x9a\x68",
|
||||
);
|
||||
check(types::LOC,
|
||||
check(
|
||||
types::LOC,
|
||||
"42 21 54 N 71 06 18 W -24m 30m",
|
||||
Some("42 21 54.000 N 71 6 18.000 W -24.00m 30.00m 10000.00m 10.00m"),
|
||||
b"\x00\x33\x16\x13\x89\x17\x2d\xd0\x70\xbe\x15\xf0\x00\x98\x8d\x20",
|
||||
);
|
||||
check(types::LOC,
|
||||
check(
|
||||
types::LOC,
|
||||
"42 21 43.952 N 71 5 6.344 W -24m 1m 200m",
|
||||
Some("42 21 43.952 N 71 5 6.344 W -24.00m 1.00m 200.00m 10.00m"),
|
||||
b"\x00\x12\x24\x13\x89\x17\x06\x90\x70\xbf\x2d\xd8\x00\x98\x8d\x20",
|
||||
@ -436,19 +429,22 @@ fn test_LOC() {
|
||||
#[test]
|
||||
fn test_SRV() {
|
||||
// local name
|
||||
check(types::SRV,
|
||||
check(
|
||||
types::SRV,
|
||||
"10 10 5060 sip.rec.test.",
|
||||
None,
|
||||
b"\x00\x0a\x00\x0a\x13\xc4\x03sip\x03rec\x04test\x00",
|
||||
);
|
||||
// non-local name
|
||||
check(types::SRV,
|
||||
check(
|
||||
types::SRV,
|
||||
"10 10 5060 sip.example.com.",
|
||||
None,
|
||||
b"\x00\x0a\x00\x0a\x13\xc4\x03sip\x07example\x03com\x00",
|
||||
);
|
||||
// root name
|
||||
check(types::SRV,
|
||||
check(
|
||||
types::SRV,
|
||||
"10 10 5060 .",
|
||||
None,
|
||||
b"\x00\x0a\x00\x0a\x13\xc4\x00",
|
||||
@ -457,12 +453,14 @@ fn test_SRV() {
|
||||
|
||||
#[test]
|
||||
fn test_NAPTR() {
|
||||
check(types::NAPTR,
|
||||
check(
|
||||
types::NAPTR,
|
||||
"100 10 \"\" \"\" \"/urn:cid:.+@([^\\\\.]+\\\\.)(.*)$/\\\\2/i\" .",
|
||||
None,
|
||||
b"\x00\x64\x00\x0a\x00\x00\x20/urn:cid:.+@([^\\.]+\\.)(.*)$/\\2/i\x00",
|
||||
);
|
||||
check(types::NAPTR,
|
||||
check(
|
||||
types::NAPTR,
|
||||
"100 50 \"s\" \"http+I2L+I2C+I2R\" \"\" _http._tcp.rec.test.",
|
||||
None,
|
||||
b"\x00\x64\x00\x32\x01s\x10http+I2L+I2C+I2R\x00\x05_http\x04_tcp\x03rec\x04test\x00",
|
||||
@ -471,7 +469,8 @@ fn test_NAPTR() {
|
||||
|
||||
#[test]
|
||||
fn test_KX() {
|
||||
check(types::KX,
|
||||
check(
|
||||
types::KX,
|
||||
"10 mail.rec.test.",
|
||||
None,
|
||||
b"\x00\x0a\x04mail\x03rec\x04test\x00",
|
||||
@ -507,13 +506,15 @@ fn test_DS() {
|
||||
|
||||
#[test]
|
||||
fn test_SSHFP() {
|
||||
check(types::SSHFP,
|
||||
check(
|
||||
types::SSHFP,
|
||||
"1 1 aa65e3415a50d9b3519c2b17aceb815fc2538d88",
|
||||
None,
|
||||
b"\x01\x01\xaa\x65\xe3\x41\x5a\x50\xd9\xb3\x51\x9c\x2b\x17\xac\xeb\x81\x5f\xc2\x53\x8d\x88",
|
||||
);
|
||||
// as per RFC4025
|
||||
check(types::SSHFP,
|
||||
check(
|
||||
types::SSHFP,
|
||||
"1 1 aa65e3415a50d9b3519c2b17aceb815fc253 8d88",
|
||||
Some("1 1 aa65e3415a50d9b3519c2b17aceb815fc2538d88"),
|
||||
b"\x01\x01\xaa\x65\xe3\x41\x5a\x50\xd9\xb3\x51\x9c\x2b\x17\xac\xeb\x81\x5f\xc2\x53\x8d\x88",
|
||||
@ -523,22 +524,20 @@ fn test_SSHFP() {
|
||||
#[test]
|
||||
fn test_IPSECKEY() {
|
||||
// as per RFC4025
|
||||
check(types::IPSECKEY,
|
||||
"255 0 0",
|
||||
None,
|
||||
b"\xff\x00\x00",
|
||||
);
|
||||
check(types::IPSECKEY, "255 0 0", None, b"\xff\x00\x00");
|
||||
check(types::IPSECKEY,
|
||||
"255 0 1 V19hwufL6LJARVIxzHDyGdvZ7dbQE0Kyl18yPIWj/sbCcsBbz7zO6Q2qgdzmWI3OvGNne2nxflhorhefKIMsUg==",
|
||||
None,
|
||||
b"\xff\x00\x01\x57\x5f\x61\xc2\xe7\xcb\xe8\xb2\x40\x45\x52\x31\xcc\x70\xf2\x19\xdb\xd9\xed\xd6\xd0\x13\x42\xb2\x97\x5f\x32\x3c\x85\xa3\xfe\xc6\xc2\x72\xc0\x5b\xcf\xbc\xce\xe9\x0d\xaa\x81\xdc\xe6\x58\x8d\xce\xbc\x63\x67\x7b\x69\xf1\x7e\x58\x68\xae\x17\x9f\x28\x83\x2c\x52",
|
||||
);
|
||||
check(types::IPSECKEY,
|
||||
check(
|
||||
types::IPSECKEY,
|
||||
"255 1 0 127.0.0.1",
|
||||
None,
|
||||
b"\xff\x01\x00\x7f\x00\x00\x01",
|
||||
);
|
||||
check(types::IPSECKEY,
|
||||
check(
|
||||
types::IPSECKEY,
|
||||
"255 2 0 fe80::250:56ff:fe9b:114",
|
||||
None,
|
||||
b"\xff\x02\x00\xFE\x80\x00\x00\x00\x00\x00\x00\x02\x50\x56\xFF\xFE\x9B\x01\x14",
|
||||
@ -573,7 +572,8 @@ fn test_RRSIG() {
|
||||
|
||||
#[test]
|
||||
fn test_NSEC() {
|
||||
check(types::NSEC,
|
||||
check(
|
||||
types::NSEC,
|
||||
"a.rec.test. A NS SOA MX AAAA RRSIG NSEC DNSKEY",
|
||||
None,
|
||||
b"\x01a\x03rec\x04test\x00\x00\x07\x62\x01\x00\x08\x00\x03\x80",
|
||||
@ -619,7 +619,8 @@ fn test_NSEC3() {
|
||||
|
||||
#[test]
|
||||
fn test_NSEC3PARAM() {
|
||||
check(types::NSEC3PARAM,
|
||||
check(
|
||||
types::NSEC3PARAM,
|
||||
"1 0 1 f00b",
|
||||
None,
|
||||
b"\x01\x00\x00\x01\x02\xf0\x0b",
|
||||
@ -751,7 +752,8 @@ fn test_OPENPGPKEY() {
|
||||
|
||||
#[test]
|
||||
fn test_SPF() {
|
||||
check(types::SPF,
|
||||
check(
|
||||
types::SPF,
|
||||
"\"v=spf1 a:mail.rec.test ~all\"",
|
||||
None,
|
||||
b"\x1bv=spf1 a:mail.rec.test ~all",
|
||||
@ -760,7 +762,8 @@ fn test_SPF() {
|
||||
|
||||
#[test]
|
||||
fn test_EUI48() {
|
||||
check(types::EUI48,
|
||||
check(
|
||||
types::EUI48,
|
||||
"00-11-22-33-44-55",
|
||||
None,
|
||||
b"\x00\x11\x22\x33\x44\x55",
|
||||
@ -769,7 +772,8 @@ fn test_EUI48() {
|
||||
|
||||
#[test]
|
||||
fn test_EUI64() {
|
||||
check(types::EUI64,
|
||||
check(
|
||||
types::EUI64,
|
||||
"00-11-22-33-44-55-66-77",
|
||||
None,
|
||||
b"\x00\x11\x22\x33\x44\x55\x66\x77",
|
||||
@ -778,7 +782,8 @@ fn test_EUI64() {
|
||||
|
||||
#[test]
|
||||
fn test_TKEY() {
|
||||
check(types::TKEY,
|
||||
check(
|
||||
types::TKEY,
|
||||
"gss-tsig. 12345 12345 3 21 4 dGVzdA== 4 dGVzdA==",
|
||||
None,
|
||||
b"\x08gss-tsig\x00\x00\x00\x30\x39\x00\x00\x30\x39\x00\x03\x00\x15\x00\x04test\x00\x04test",
|
||||
@ -815,7 +820,8 @@ fn test_URI() {
|
||||
|
||||
#[test]
|
||||
fn test_CAA() {
|
||||
check(types::CAA,
|
||||
check(
|
||||
types::CAA,
|
||||
"0 issue \"example.net\"",
|
||||
None,
|
||||
b"\x00\x05\x69\x73\x73\x75\x65\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6e\x65\x74",
|
||||
@ -835,10 +841,14 @@ fn test_DLV() {
|
||||
fn test_TYPE65226() {
|
||||
let d1 = text::parse_with("\\# 3 414243", |data| {
|
||||
super::UnknownRecord::dns_parse(types::Type(65226), data)
|
||||
}).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
let d2 = super::UnknownRecord::new(types::Type(65226), Bytes::from_static(b"\x41\x42\x43"));
|
||||
assert_eq!(d1, d2);
|
||||
assert_eq!(d1.text().unwrap(), ("TYPE65226".into(), "\\# 3 414243".into()));
|
||||
assert_eq!(
|
||||
d1.text().unwrap(),
|
||||
("TYPE65226".into(), "\\# 3 414243".into())
|
||||
);
|
||||
}
|
||||
|
||||
fn check_invalid_zone(q: Type, text_input: &str) {
|
||||
@ -848,9 +858,7 @@ fn check_invalid_zone(q: Type, text_input: &str) {
|
||||
context.set_record_type(q);
|
||||
context.set_last_ttl(3600);
|
||||
|
||||
text::parse_with(text_input, |data| {
|
||||
registry::parse_rr_data(&context, data)
|
||||
}).unwrap_err();
|
||||
text::parse_with(text_input, |data| registry::parse_rr_data(&context, data)).unwrap_err();
|
||||
}
|
||||
|
||||
fn check_invalid_wire(q: Type, raw: &'static [u8]) {
|
||||
@ -873,16 +881,25 @@ fn test_invalid_data_checks() {
|
||||
check_invalid_zone(types::AAAA, "23:00"); // time when this test was written
|
||||
check_invalid_zone(types::AAAA, "23:00::15::43"); // double compression
|
||||
check_invalid_zone(types::AAAA, "2a23:00::15::"); // ditto
|
||||
check_invalid_wire(types::AAAA, b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff"); // truncated wire value
|
||||
check_invalid_wire(
|
||||
types::AAAA,
|
||||
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff",
|
||||
); // truncated wire value
|
||||
// empty label, must be broken
|
||||
check_invalid_zone(types::CNAME, "name..example.com.");
|
||||
// overly large label (64), must be broken
|
||||
check_invalid_zone(types::CNAME, "1234567890123456789012345678901234567890123456789012345678901234.example.com.");
|
||||
check_invalid_zone(
|
||||
types::CNAME,
|
||||
"1234567890123456789012345678901234567890123456789012345678901234.example.com.",
|
||||
);
|
||||
// local overly large name (256), must be broken
|
||||
check_invalid_zone(types::CNAME, "123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.123456789012345678901234567890123456789012345678901234567890123.12345678901234567890123456789012345678901234567890123.rec.test.");
|
||||
// non-local overly large name (256), must be broken
|
||||
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]
|
||||
@ -895,9 +912,10 @@ fn test_opt_record_in() {
|
||||
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")),
|
||||
]);
|
||||
assert_eq!(
|
||||
opt.options,
|
||||
vec![DnsOption::NSID(Bytes::from_static(b"powerdns")),]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -908,26 +926,22 @@ fn test_opt_record_out() {
|
||||
recursion_desired: true,
|
||||
..Default::default()
|
||||
},
|
||||
question: vec![
|
||||
Question {
|
||||
question: vec![Question {
|
||||
qname: "www.powerdns.com.".parse().unwrap(),
|
||||
qtype: types::A,
|
||||
qclass: classes::IN,
|
||||
}
|
||||
],
|
||||
answer: vec![
|
||||
Resource {
|
||||
}],
|
||||
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() }),
|
||||
}
|
||||
],
|
||||
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")),
|
||||
],
|
||||
options: vec![DnsOption::NSID(Bytes::from_static(b"powerdns"))],
|
||||
..Default::default()
|
||||
})),
|
||||
..Default::default()
|
||||
|
@ -4,11 +4,11 @@ use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::common_types::{Class, Type, types};
|
||||
use crate::common_types::{types, Class, Type};
|
||||
use crate::errors::*;
|
||||
use crate::records::structs;
|
||||
use crate::ser::{RRData, StaticRRData};
|
||||
use crate::ser::text::DnsTextContext;
|
||||
use crate::ser::{RRData, StaticRRData};
|
||||
|
||||
// this should be enough for registered names
|
||||
const TYPE_NAME_MAX_LEN: usize = 16;
|
||||
@ -22,7 +22,9 @@ fn registry() -> &'static Registry {
|
||||
}
|
||||
|
||||
pub(crate) fn lookup_type_name(name: &str) -> Option<Type> {
|
||||
if name.len() >= TYPE_NAME_MAX_LEN { return None; }
|
||||
if name.len() >= TYPE_NAME_MAX_LEN {
|
||||
return None;
|
||||
}
|
||||
let mut name_buf_storage = [0u8; TYPE_NAME_MAX_LEN];
|
||||
let name_buf = &mut name_buf_storage[..name.len()];
|
||||
name_buf.copy_from_slice(name.as_bytes());
|
||||
@ -42,7 +44,12 @@ 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<dyn RRData>> {
|
||||
pub fn deserialize_rr_data(
|
||||
ttl: u32,
|
||||
rr_class: Class,
|
||||
rr_type: Type,
|
||||
data: &mut Cursor<Bytes>,
|
||||
) -> Result<Box<dyn RRData>> {
|
||||
let registry = registry();
|
||||
match registry.type_parser.get(&rr_type) {
|
||||
Some(p) => p.deserialize_rr_data(ttl, rr_class, rr_type, data),
|
||||
@ -80,13 +87,25 @@ trait RRDataTypeParse: 'static {
|
||||
TypeId::of::<Self>()
|
||||
}
|
||||
|
||||
fn deserialize_rr_data(&self, ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor<Bytes>) -> Result<Box<dyn RRData>>;
|
||||
fn deserialize_rr_data(
|
||||
&self,
|
||||
ttl: u32,
|
||||
rr_class: Class,
|
||||
rr_type: Type,
|
||||
data: &mut Cursor<Bytes>,
|
||||
) -> Result<Box<dyn RRData>>;
|
||||
|
||||
fn parse_rr_data(&self, context: &DnsTextContext, data: &mut &str) -> Result<Box<dyn 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<dyn RRData>> {
|
||||
fn deserialize_rr_data(
|
||||
&self,
|
||||
ttl: u32,
|
||||
rr_class: Class,
|
||||
rr_type: Type,
|
||||
data: &mut Cursor<Bytes>,
|
||||
) -> Result<Box<dyn RRData>> {
|
||||
T::deserialize_rr_data(ttl, rr_class, rr_type, data).map(|d| Box::new(d) as _)
|
||||
}
|
||||
|
||||
@ -213,9 +232,20 @@ impl Registry {
|
||||
self.prev_type = Some(rrtype);
|
||||
let mut name: String = name.into();
|
||||
name.make_ascii_uppercase();
|
||||
assert!(!name.starts_with("TYPE"), "must not register generic name: {}", name);
|
||||
assert!(name.len() <= TYPE_NAME_MAX_LEN, "name too long: {} - maybe you need to increase TYPE_NAME_MAX_LEN", name);
|
||||
assert!(self.names_to_type.insert(name.clone().into_bytes(), rrtype).is_none());
|
||||
assert!(
|
||||
!name.starts_with("TYPE"),
|
||||
"must not register generic name: {}",
|
||||
name
|
||||
);
|
||||
assert!(
|
||||
name.len() <= TYPE_NAME_MAX_LEN,
|
||||
"name too long: {} - maybe you need to increase TYPE_NAME_MAX_LEN",
|
||||
name
|
||||
);
|
||||
assert!(self
|
||||
.names_to_type
|
||||
.insert(name.clone().into_bytes(), rrtype)
|
||||
.is_none());
|
||||
self.type_names.insert(rrtype, name);
|
||||
}
|
||||
|
||||
@ -227,12 +257,16 @@ impl Registry {
|
||||
let rrtype = T::TYPE;
|
||||
let name = T::NAME;
|
||||
self.register_name(name, rrtype);
|
||||
self.type_parser.insert(rrtype, Box::new(TagRRDataType::<T>(PhantomData)));
|
||||
self.type_parser
|
||||
.insert(rrtype, Box::new(TagRRDataType::<T>(PhantomData)));
|
||||
}
|
||||
|
||||
fn check_registration<T: StaticRRData + Sync + 'static>(&self) {
|
||||
assert_eq!(self.names_to_type.get(T::NAME.as_bytes()), Some(&T::TYPE));
|
||||
let p: &dyn RRDataTypeParse = &**self.type_parser.get(&T::TYPE).expect("no parser registered");
|
||||
let p: &dyn RRDataTypeParse = &**self
|
||||
.type_parser
|
||||
.get(&T::TYPE)
|
||||
.expect("no parser registered");
|
||||
let tid = TypeId::of::<TagRRDataType<T>>();
|
||||
assert_eq!(p.type_id(), tid);
|
||||
}
|
||||
|
@ -354,28 +354,42 @@ pub struct DNSKEY {
|
||||
impl DNSKEY {
|
||||
fn alg1_tag(&self) -> u16 {
|
||||
let key: &[u8] = &self.public_key;
|
||||
if key.is_empty() { return 0; } // not enough data
|
||||
if key.is_empty() {
|
||||
return 0;
|
||||
} // not enough data
|
||||
let pkey;
|
||||
if 0 == key[0] {
|
||||
// two-byte length encoding of exponent
|
||||
if key.len() < 3 { return 0; } // not enough data
|
||||
if key.len() < 3 {
|
||||
return 0;
|
||||
} // not enough data
|
||||
let explen = ((key[1] as u16) << 8) + (key[2] as u16);
|
||||
if explen < 256 { return 0; } // should have used shorter length encoding
|
||||
if key.len() < 3 + (explen as usize) { return 0; } // not enough data
|
||||
if explen < 256 {
|
||||
return 0;
|
||||
} // should have used shorter length encoding
|
||||
if key.len() < 3 + (explen as usize) {
|
||||
return 0;
|
||||
} // not enough data
|
||||
pkey = &key[3 + (explen as usize)..];
|
||||
} else {
|
||||
// one-byte length encoding of exponent
|
||||
let explen = key[0];
|
||||
if key.len() < 1 + (explen as usize) { return 0; } // not enough data
|
||||
if key.len() < 1 + (explen as usize) {
|
||||
return 0;
|
||||
} // not enough data
|
||||
pkey = &key[1 + (explen as usize)..];
|
||||
}
|
||||
if pkey.len() < 3 { return 0; } // not enough data
|
||||
if pkey.len() < 3 {
|
||||
return 0;
|
||||
} // not enough data
|
||||
((pkey[pkey.len() - 3] as u16) << 8) + (pkey[pkey.len() - 3] as u16)
|
||||
}
|
||||
|
||||
/// calculate key tag
|
||||
pub fn tag(&self) -> u16 {
|
||||
if self.algorithm == DnsSecAlgorithm::RSAMD5 { return self.alg1_tag(); }
|
||||
if self.algorithm == DnsSecAlgorithm::RSAMD5 {
|
||||
return self.alg1_tag();
|
||||
}
|
||||
|
||||
let mut sum = 0u32;
|
||||
|
||||
@ -395,7 +409,9 @@ impl DNSKEY {
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
pub fn build_ds(&self, zone: &DnsName, algs: &[DnsSecDigestAlgorithmKnown]) -> Result<Vec<DS>> {
|
||||
if algs.is_empty() { return Ok(Vec::new()); }
|
||||
if algs.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
use crate::ser::packet::DnsPacketWriteContext;
|
||||
let mut ctx = DnsPacketWriteContext::new();
|
||||
@ -406,12 +422,15 @@ impl DNSKEY {
|
||||
|
||||
let key_tag = self.tag();
|
||||
|
||||
Ok(algs.iter().map(|alg| DS {
|
||||
Ok(algs
|
||||
.iter()
|
||||
.map(|alg| DS {
|
||||
key_tag,
|
||||
algorithm: self.algorithm,
|
||||
digest_type: (*alg).into(),
|
||||
digest: HexRemainingBlob::new(crate::crypto::ds_hash(*alg, &bin)),
|
||||
}).collect())
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
@ -476,7 +495,6 @@ pub struct NINFO {
|
||||
pub text: LongText,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
#[RRClass(ANY)]
|
||||
pub struct RKEY {
|
||||
@ -515,7 +533,8 @@ impl CDNSKEY {
|
||||
protocol: self.protocol,
|
||||
algorithm: self.algorithm,
|
||||
public_key: self.public_key.clone(),
|
||||
}.tag()
|
||||
}
|
||||
.tag()
|
||||
}
|
||||
}
|
||||
|
||||
@ -585,7 +604,7 @@ pub struct EUI48 {
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
#[RRClass(ANY)]
|
||||
pub struct EUI64 {
|
||||
pub addr: EUI64Addr
|
||||
pub addr: EUI64Addr,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
|
||||
|
@ -1,15 +1,15 @@
|
||||
use bytes::{Bytes, Buf};
|
||||
use crate::common_types::classes;
|
||||
use failure::ResultExt;
|
||||
use crate::records::structs;
|
||||
use crate::ser::{StaticRRData, packet, text};
|
||||
use crate::ser::packet::DnsPacketData;
|
||||
use crate::ser::{packet, text, StaticRRData};
|
||||
use bytes::{Buf, Bytes};
|
||||
use failure::ResultExt;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
|
||||
fn rrdata_de<T>(data: &'static [u8]) -> crate::errors::Result<T>
|
||||
where
|
||||
T: StaticRRData
|
||||
T: StaticRRData,
|
||||
{
|
||||
let mut data = Cursor::new(Bytes::from_static(data));
|
||||
let result = T::deserialize_rr_data(3600, classes::IN, T::TYPE, &mut data)?;
|
||||
@ -19,20 +19,18 @@ where
|
||||
|
||||
fn rrdata_parse<T>(data: &str) -> crate::errors::Result<T>
|
||||
where
|
||||
T: StaticRRData
|
||||
T: StaticRRData,
|
||||
{
|
||||
let mut ctx = text::DnsTextContext::new();
|
||||
ctx.set_zone_class(classes::IN);
|
||||
ctx.set_record_type(T::TYPE);
|
||||
ctx.set_last_ttl(3600);
|
||||
text::parse_with(data, |data| {
|
||||
T::dns_parse_rr_data(&ctx, data)
|
||||
})
|
||||
text::parse_with(data, |data| T::dns_parse_rr_data(&ctx, data))
|
||||
}
|
||||
|
||||
fn check<T>(txt: &str, data: &'static [u8]) -> crate::errors::Result<()>
|
||||
where
|
||||
T: StaticRRData + fmt::Debug + PartialEq
|
||||
T: StaticRRData + fmt::Debug + PartialEq,
|
||||
{
|
||||
let d1: T = rrdata_de(data).context("couldn't parse binary record")?;
|
||||
let d2: T = rrdata_parse(txt).context("couldn't parse text record")?;
|
||||
@ -42,7 +40,7 @@ where
|
||||
|
||||
fn check2<T>(txt: &str, data: &'static [u8], canon: &str) -> crate::errors::Result<()>
|
||||
where
|
||||
T: StaticRRData + fmt::Debug + PartialEq
|
||||
T: StaticRRData + fmt::Debug + PartialEq,
|
||||
{
|
||||
let d1: T = rrdata_de(data).context("couldn't parse binary record")?;
|
||||
let d2: T = rrdata_parse(txt).context("couldn't parse text record")?;
|
||||
@ -52,8 +50,18 @@ where
|
||||
let d2_text = d2.text().unwrap();
|
||||
let canon_text = (T::NAME.to_owned(), canon.into());
|
||||
|
||||
failure::ensure!(d1_text == canon_text, "re-formatted binary record not equal to canonical representation: {:?} != {:?}", d1_text, canon_text);
|
||||
failure::ensure!(d2_text == canon_text, "re-formatted text record not equal to canonical representation: {:?} != {:?}", d2_text, canon_text);
|
||||
failure::ensure!(
|
||||
d1_text == canon_text,
|
||||
"re-formatted binary record not equal to canonical representation: {:?} != {:?}",
|
||||
d1_text,
|
||||
canon_text
|
||||
);
|
||||
failure::ensure!(
|
||||
d2_text == canon_text,
|
||||
"re-formatted text record not equal to canonical representation: {:?} != {:?}",
|
||||
d2_text,
|
||||
canon_text
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -70,7 +78,7 @@ fn test_mx() {
|
||||
|
||||
fn test_txt_for<T>()
|
||||
where
|
||||
T: StaticRRData + fmt::Debug + PartialEq
|
||||
T: StaticRRData + fmt::Debug + PartialEq,
|
||||
{
|
||||
// at least one "segment" (which could be empty)
|
||||
check2::<T>(r#" "" "#, b"", r#""""#).unwrap_err();
|
||||
@ -85,7 +93,9 @@ where
|
||||
{
|
||||
let mut s = String::new();
|
||||
s.push('"');
|
||||
for _ in 0..256 { s.push('a'); }
|
||||
for _ in 0..256 {
|
||||
s.push('a');
|
||||
}
|
||||
s.push('"');
|
||||
rrdata_parse::<T>(&s).unwrap_err();
|
||||
}
|
||||
@ -107,8 +117,16 @@ 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();
|
||||
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();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -121,15 +139,27 @@ fn test_dnskey() {
|
||||
#[test]
|
||||
fn test_nsec3() {
|
||||
check::<structs::NSEC3>("1 2 300 - vs", b"\x01\x02\x01\x2c\x00\x01\xff").unwrap();
|
||||
check::<structs::NSEC3>("1 2 300 - vs A NS", b"\x01\x02\x01\x2c\x00\x01\xff\x00\x01\x60").unwrap();
|
||||
check::<structs::NSEC3>("1 2 300 ab vs A NS", b"\x01\x02\x01\x2c\x01\xab\x01\xff\x00\x01\x60").unwrap();
|
||||
check::<structs::NSEC3>(
|
||||
"1 2 300 - vs A NS",
|
||||
b"\x01\x02\x01\x2c\x00\x01\xff\x00\x01\x60",
|
||||
)
|
||||
.unwrap();
|
||||
check::<structs::NSEC3>(
|
||||
"1 2 300 ab vs A NS",
|
||||
b"\x01\x02\x01\x2c\x01\xab\x01\xff\x00\x01\x60",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// invalid base32 texts
|
||||
rrdata_parse::<structs::NSEC3>("1 2 300 - v").unwrap_err();
|
||||
rrdata_parse::<structs::NSEC3>("1 2 300 - vv").unwrap_err();
|
||||
|
||||
// invalid (empty) next-hashed values
|
||||
packet::deserialize_with(Bytes::from_static(b"\x01\x02\x01\x2c\x00\x00"), structs::NSEC3::deserialize).unwrap_err();
|
||||
packet::deserialize_with(
|
||||
Bytes::from_static(b"\x01\x02\x01\x2c\x00\x00"),
|
||||
structs::NSEC3::deserialize,
|
||||
)
|
||||
.unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -151,5 +181,9 @@ fn test_apl() {
|
||||
check::<structs::APL>("!1:0.0.0.0/0", b"\x00\x01\x00\x80").unwrap();
|
||||
check::<structs::APL>("2:::/0", b"\x00\x02\x00\x00").unwrap();
|
||||
check::<structs::APL>("!2:::/0", b"\x00\x02\x00\x80").unwrap();
|
||||
check::<structs::APL>("1:192.0.2.0/24 !2:2001:db8::/32", b"\x00\x01\x18\x03\xc0\x00\x02\x00\x02\x20\x84\x20\x01\x0d\xb8").unwrap();
|
||||
check::<structs::APL>(
|
||||
"1:192.0.2.0/24 !2:2001:db8::/32",
|
||||
b"\x00\x01\x18\x03\xc0\x00\x02\x00\x02\x20\x84\x20\x01\x0d\xb8",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
use bytes::{Bytes, BufMut};
|
||||
use crate::common_types::*;
|
||||
use crate::common_types::binary::HEXLOWER_PERMISSIVE_ALLOW_WS;
|
||||
use crate::common_types::*;
|
||||
use crate::errors::*;
|
||||
use failure::{ResultExt, Fail};
|
||||
use crate::ser::packet::{DnsPacketWriteContext, remaining_bytes};
|
||||
use crate::ser::packet::{remaining_bytes, DnsPacketWriteContext};
|
||||
use crate::ser::text::{next_field, DnsTextContext, DnsTextFormatter};
|
||||
use crate::ser::{RRData, RRDataPacket, RRDataText};
|
||||
use crate::ser::text::{DnsTextFormatter, DnsTextContext, next_field};
|
||||
use bytes::{BufMut, Bytes};
|
||||
use failure::{Fail, ResultExt};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
@ -38,9 +38,15 @@ impl UnknownRecord {
|
||||
let field = next_field(data).context("generic record data length")?;
|
||||
let len: usize = field.parse()?;
|
||||
|
||||
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)))?;
|
||||
failure::ensure!(len == result.len(), "length {} doesn't match length of encoded data {}", len, result.len());
|
||||
failure::ensure!(
|
||||
len == result.len(),
|
||||
"length {} doesn't match length of encoded data {}",
|
||||
len,
|
||||
result.len()
|
||||
);
|
||||
*data = ""; // read all data
|
||||
|
||||
Ok(UnknownRecord {
|
||||
@ -51,7 +57,12 @@ impl UnknownRecord {
|
||||
}
|
||||
|
||||
impl RRDataPacket for UnknownRecord {
|
||||
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> {
|
||||
UnknownRecord::deserialize(rr_type, data)
|
||||
}
|
||||
|
||||
@ -59,7 +70,11 @@ impl RRDataPacket for UnknownRecord {
|
||||
self.rr_type
|
||||
}
|
||||
|
||||
fn serialize_rr_data(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||
fn serialize_rr_data(
|
||||
&self,
|
||||
_context: &mut DnsPacketWriteContext,
|
||||
packet: &mut Vec<u8>,
|
||||
) -> Result<()> {
|
||||
packet.reserve(self.raw.len());
|
||||
packet.put_slice(&self.raw);
|
||||
Ok(())
|
||||
@ -81,7 +96,12 @@ impl RRDataText for UnknownRecord {
|
||||
|
||||
/// this must never fail unless the underlying buffer fails.
|
||||
fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
||||
write!(f, "\\# {} {}", self.raw.len(), HEXLOWER_PERMISSIVE_ALLOW_WS.encode(&self.raw))
|
||||
write!(
|
||||
f,
|
||||
"\\# {} {}",
|
||||
self.raw.len(),
|
||||
HEXLOWER_PERMISSIVE_ALLOW_WS.encode(&self.raw)
|
||||
)
|
||||
}
|
||||
|
||||
fn rr_type_txt(&self) -> Cow<'static, str> {
|
||||
|
@ -1,10 +1,10 @@
|
||||
use bytes::{Bytes, Buf, BufMut};
|
||||
use crate::errors::*;
|
||||
use crate::common_types::*;
|
||||
use failure::ResultExt;
|
||||
use crate::errors::*;
|
||||
use crate::ser::packet::{get_blob, remaining_bytes, DnsPacketData, DnsPacketWriteContext};
|
||||
use crate::ser::text::{next_field, DnsTextContext, DnsTextData, DnsTextFormatter};
|
||||
use crate::ser::RRData;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext, remaining_bytes, get_blob};
|
||||
use crate::ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field};
|
||||
use bytes::{Buf, BufMut, Bytes};
|
||||
use failure::ResultExt;
|
||||
use std::fmt;
|
||||
use std::io::Read;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
@ -17,10 +17,7 @@ use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
#[RRClass(ANY)]
|
||||
pub enum LOC {
|
||||
Version0(LOC0),
|
||||
UnknownVersion{
|
||||
version: u8,
|
||||
data: Bytes,
|
||||
},
|
||||
UnknownVersion { version: u8, data: Bytes },
|
||||
}
|
||||
|
||||
impl DnsPacketData for LOC {
|
||||
@ -56,21 +53,35 @@ impl DnsPacketData for LOC {
|
||||
impl DnsTextData for LOC {
|
||||
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> Result<Self> {
|
||||
let degrees_latitude = next_field(data)?.parse::<u8>()?;
|
||||
failure::ensure!(degrees_latitude <= 90, "degrees latitude out of range: {}", degrees_latitude);
|
||||
failure::ensure!(
|
||||
degrees_latitude <= 90,
|
||||
"degrees latitude out of range: {}",
|
||||
degrees_latitude
|
||||
);
|
||||
let mut minutes_latitude = 0;
|
||||
let mut seconds_latitude = 0.0;
|
||||
let mut field = next_field(data)?;
|
||||
if field != "N" && field != "n" && field != "S" && field != "s" {
|
||||
minutes_latitude = field.parse::<u8>()?;
|
||||
failure::ensure!(minutes_latitude < 60, "minutes latitude out of range: {}", minutes_latitude);
|
||||
failure::ensure!(
|
||||
minutes_latitude < 60,
|
||||
"minutes latitude out of range: {}",
|
||||
minutes_latitude
|
||||
);
|
||||
field = next_field(data)?;
|
||||
if field != "N" && field != "n" && field != "S" && field != "s" {
|
||||
seconds_latitude = field.parse::<f32>()?;
|
||||
failure::ensure!(seconds_latitude >= 0.0 && seconds_latitude < 60.0, "seconds latitude out of range: {}", seconds_latitude);
|
||||
failure::ensure!(
|
||||
seconds_latitude >= 0.0 && seconds_latitude < 60.0,
|
||||
"seconds latitude out of range: {}",
|
||||
seconds_latitude
|
||||
);
|
||||
field = next_field(data)?;
|
||||
}
|
||||
}
|
||||
let latitude_off = (3600_000 * degrees_latitude as u32) + (60_000 * minutes_latitude as u32) + (1_000.0 * seconds_latitude).round() as u32;
|
||||
let latitude_off = (3600_000 * degrees_latitude as u32)
|
||||
+ (60_000 * minutes_latitude as u32)
|
||||
+ (1_000.0 * seconds_latitude).round() as u32;
|
||||
failure::ensure!(latitude_off <= 3600_000 * 180, "latitude out of range");
|
||||
let latitude = match field {
|
||||
"N" | "n" => 0x8000_0000 + latitude_off,
|
||||
@ -79,21 +90,35 @@ impl DnsTextData for LOC {
|
||||
};
|
||||
|
||||
let degrees_longitude = next_field(data)?.parse::<u8>()?;
|
||||
failure::ensure!(degrees_longitude <= 180, "degrees longitude out of range: {}", degrees_longitude);
|
||||
failure::ensure!(
|
||||
degrees_longitude <= 180,
|
||||
"degrees longitude out of range: {}",
|
||||
degrees_longitude
|
||||
);
|
||||
let mut minutes_longitude = 0;
|
||||
let mut seconds_longitude = 0.0;
|
||||
let mut field = next_field(data)?;
|
||||
if field != "E" && field != "e" && field != "W" && field != "w" {
|
||||
minutes_longitude = field.parse::<u8>()?;
|
||||
failure::ensure!(minutes_longitude < 60, "minutes longitude out of range: {}", minutes_longitude);
|
||||
failure::ensure!(
|
||||
minutes_longitude < 60,
|
||||
"minutes longitude out of range: {}",
|
||||
minutes_longitude
|
||||
);
|
||||
field = next_field(data)?;
|
||||
if field != "E" && field != "e" && field != "W" && field != "w" {
|
||||
seconds_longitude = field.parse::<f32>()?;
|
||||
failure::ensure!(seconds_longitude >= 0.0 && seconds_longitude < 60.0, "seconds longitude out of range: {}", seconds_longitude);
|
||||
failure::ensure!(
|
||||
seconds_longitude >= 0.0 && seconds_longitude < 60.0,
|
||||
"seconds longitude out of range: {}",
|
||||
seconds_longitude
|
||||
);
|
||||
field = next_field(data)?;
|
||||
}
|
||||
}
|
||||
let longitude_off = (3600_000 * degrees_longitude as u32) + (60_000 * minutes_longitude as u32) + (1_000.0 * seconds_longitude).round() as u32;
|
||||
let longitude_off = (3600_000 * degrees_longitude as u32)
|
||||
+ (60_000 * minutes_longitude as u32)
|
||||
+ (1_000.0 * seconds_longitude).round() as u32;
|
||||
failure::ensure!(longitude_off <= 3600_000 * 180, "longitude out of range");
|
||||
let longitude = match field {
|
||||
"E" | "e" => 0x8000_0000 + longitude_off,
|
||||
@ -102,7 +127,11 @@ impl DnsTextData for LOC {
|
||||
};
|
||||
|
||||
fn trim_unit_m(s: &str) -> &str {
|
||||
if s.ends_with('m') { &s[..s.len()-1] } else { s }
|
||||
if s.ends_with('m') {
|
||||
&s[..s.len() - 1]
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_precision(s: &str) -> Result<u8> {
|
||||
@ -112,13 +141,23 @@ impl DnsTextData for LOC {
|
||||
let mut dec_point = None;
|
||||
for &b in s.as_bytes() {
|
||||
if b == b'.' {
|
||||
failure::ensure!(dec_point.is_none(), "invalid precision (double decimal point): {:?}", s);
|
||||
failure::ensure!(
|
||||
dec_point.is_none(),
|
||||
"invalid precision (double decimal point): {:?}",
|
||||
s
|
||||
);
|
||||
dec_point = Some(0);
|
||||
continue;
|
||||
}
|
||||
failure::ensure!(b >= b'0' && b <= b'9', "invalid precision (invalid character): {:?}", s);
|
||||
failure::ensure!(
|
||||
b >= b'0' && b <= b'9',
|
||||
"invalid precision (invalid character): {:?}",
|
||||
s
|
||||
);
|
||||
if let Some(ref mut dp) = dec_point {
|
||||
if *dp == 2 { continue; } // ignore following digits
|
||||
if *dp == 2 {
|
||||
continue;
|
||||
} // ignore following digits
|
||||
*dp += 1;
|
||||
}
|
||||
let d = b - b'0';
|
||||
@ -138,7 +177,10 @@ impl DnsTextData for LOC {
|
||||
Ok(field) => {
|
||||
let f_altitude = trim_unit_m(field).parse::<f64>()?;
|
||||
let altitude = (f_altitude * 100.0 + 10000000.0).round() as i64;
|
||||
failure::ensure!(altitude > 0 && (altitude as u32) as i64 == altitude, "altitude out of range");
|
||||
failure::ensure!(
|
||||
altitude > 0 && (altitude as u32) as i64 == altitude,
|
||||
"altitude out of range"
|
||||
);
|
||||
altitude as u32
|
||||
},
|
||||
// standard requires the field, but the example parser doesn't..
|
||||
@ -178,10 +220,14 @@ impl DnsTextData for LOC {
|
||||
const MAX_LAT_OFFSET: u32 = 3600_000 * 180;
|
||||
const MAX_LON_OFFSET: u32 = 3600_000 * 180;
|
||||
const LATLON_MID: u32 = 0x8000_0000;
|
||||
if this.latitude < LATLON_MID - MAX_LAT_OFFSET || this.latitude > LATLON_MID + MAX_LAT_OFFSET {
|
||||
if this.latitude < LATLON_MID - MAX_LAT_OFFSET
|
||||
|| this.latitude > LATLON_MID + MAX_LAT_OFFSET
|
||||
{
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
if this.longitude < LATLON_MID - MAX_LON_OFFSET || this.longitude > LATLON_MID + MAX_LON_OFFSET {
|
||||
if this.longitude < LATLON_MID - MAX_LON_OFFSET
|
||||
|| this.longitude > LATLON_MID + MAX_LON_OFFSET
|
||||
{
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
|
||||
@ -190,7 +236,10 @@ impl DnsTextData for LOC {
|
||||
// if the leading digit is 0, the exponent must be 0 too.
|
||||
(v > 0x00 && v < 0x10) || (v >> 4) > 9 || (v & 0xf) > 9
|
||||
}
|
||||
if is_invalid_prec(this.size) || is_invalid_prec(this.horizontal_precision) || is_invalid_prec(this.vertical_precision) {
|
||||
if is_invalid_prec(this.size)
|
||||
|| is_invalid_prec(this.horizontal_precision)
|
||||
|| is_invalid_prec(this.vertical_precision)
|
||||
{
|
||||
return Err(fmt::Error);
|
||||
}
|
||||
|
||||
@ -227,7 +276,8 @@ impl DnsTextData for LOC {
|
||||
write!(f, "{:0<width$}.00m", m, width = e as usize - 1)
|
||||
} else if e == 1 {
|
||||
write!(f, ".{}0m", m)
|
||||
} else { // e == 0
|
||||
} else {
|
||||
// e == 0
|
||||
write!(f, ".0{}m", m)
|
||||
}
|
||||
}
|
||||
@ -262,8 +312,8 @@ pub struct A6 {
|
||||
|
||||
impl DnsPacketData for A6 {
|
||||
fn deserialize(data: &mut ::std::io::Cursor<Bytes>) -> Result<Self> {
|
||||
let prefix: u8 = DnsPacketData::deserialize(data)
|
||||
.context("failed parsing field A6::prefix")?;
|
||||
let prefix: u8 =
|
||||
DnsPacketData::deserialize(data).context("failed parsing field A6::prefix")?;
|
||||
failure::ensure!(prefix <= 128, "invalid A6::prefix {}", prefix);
|
||||
let suffix_offset = (prefix / 8) as usize;
|
||||
debug_assert!(suffix_offset <= 16);
|
||||
@ -310,15 +360,15 @@ impl DnsPacketData for A6 {
|
||||
|
||||
impl DnsTextData for A6 {
|
||||
fn dns_parse(context: &DnsTextContext, data: &mut &str) -> Result<Self> {
|
||||
let prefix: u8 = DnsTextData::dns_parse(context, data)
|
||||
.context("failed parsing field A6::prefix")?;
|
||||
let prefix: u8 =
|
||||
DnsTextData::dns_parse(context, data).context("failed parsing field A6::prefix")?;
|
||||
failure::ensure!(prefix <= 128, "invalid A6::prefix {}", prefix);
|
||||
|
||||
let suffix_offset = (prefix / 8) as usize;
|
||||
debug_assert!(suffix_offset <= 16);
|
||||
|
||||
let suffix: Ipv6Addr = DnsTextData::dns_parse(context, data)
|
||||
.context("failed parsing field A6::suffix")?;
|
||||
let suffix: Ipv6Addr =
|
||||
DnsTextData::dns_parse(context, data).context("failed parsing field A6::suffix")?;
|
||||
|
||||
// clear prefix bits
|
||||
let mut suffix = suffix.octets();
|
||||
@ -332,8 +382,10 @@ impl DnsTextData for A6 {
|
||||
let suffix = Ipv6Addr::from(suffix);
|
||||
|
||||
let prefix_name = if !data.is_empty() {
|
||||
Some(DnsTextData::dns_parse(context, data)
|
||||
.context("failed parsing field A6::prefix_name")?)
|
||||
Some(
|
||||
DnsTextData::dns_parse(context, data)
|
||||
.context("failed parsing field A6::prefix_name")?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -376,22 +428,34 @@ impl DnsPacketData for APL {
|
||||
fn deserialize(data: &mut ::std::io::Cursor<Bytes>) -> Result<Self> {
|
||||
let mut items = Vec::new();
|
||||
while data.has_remaining() {
|
||||
let family: u16 = DnsPacketData::deserialize(data)
|
||||
.context("failed parsing APL::ADDRESSFAMILY")?;
|
||||
failure::ensure!(family == 1 || family == 2, "unknown APL::ADDRESSFAMILY {}", family);
|
||||
let prefix: u8 = DnsPacketData::deserialize(data)
|
||||
.context("failed parsing field APL::PREFIX")?;
|
||||
let afd_length: u8 = DnsPacketData::deserialize(data)
|
||||
.context("failed parsing field APL::AFDLENGTH")?;
|
||||
let family: u16 =
|
||||
DnsPacketData::deserialize(data).context("failed parsing APL::ADDRESSFAMILY")?;
|
||||
failure::ensure!(
|
||||
family == 1 || family == 2,
|
||||
"unknown APL::ADDRESSFAMILY {}",
|
||||
family
|
||||
);
|
||||
let prefix: u8 =
|
||||
DnsPacketData::deserialize(data).context("failed parsing field APL::PREFIX")?;
|
||||
let afd_length: u8 =
|
||||
DnsPacketData::deserialize(data).context("failed parsing field APL::AFDLENGTH")?;
|
||||
let negation = 0 != (afd_length & 0x80);
|
||||
let afd_length = afd_length & 0x7f;
|
||||
let data = get_blob(data, afd_length as usize)?;
|
||||
|
||||
failure::ensure!(!data.ends_with(b"\0"), "APL::AFDPART ends with trailing zero");
|
||||
failure::ensure!(
|
||||
!data.ends_with(b"\0"),
|
||||
"APL::AFDPART ends with trailing zero"
|
||||
);
|
||||
|
||||
let address = if family == 1 {
|
||||
failure::ensure!(prefix <= 32, "invalid APL::prefix {} for IPv4", prefix);
|
||||
failure::ensure!((afd_length as u32) * 8 < (prefix as u32) + 7, "APL::AFDPART too long {} for prefix {}", afd_length, prefix);
|
||||
failure::ensure!(
|
||||
(afd_length as u32) * 8 < (prefix as u32) + 7,
|
||||
"APL::AFDPART too long {} for prefix {}",
|
||||
afd_length,
|
||||
prefix
|
||||
);
|
||||
assert!(afd_length <= 4);
|
||||
let mut buf = [0u8; 4];
|
||||
buf[..data.len()].copy_from_slice(&data);
|
||||
@ -399,7 +463,12 @@ impl DnsPacketData for APL {
|
||||
} else {
|
||||
assert!(family == 2);
|
||||
failure::ensure!(prefix <= 128, "invalid APL::prefix {} for IPv6", prefix);
|
||||
failure::ensure!((afd_length as u32) * 8 < (prefix as u32) + 7, "AFD::AFDPART too long {} for prefix {}", afd_length, prefix);
|
||||
failure::ensure!(
|
||||
(afd_length as u32) * 8 < (prefix as u32) + 7,
|
||||
"AFD::AFDPART too long {} for prefix {}",
|
||||
afd_length,
|
||||
prefix
|
||||
);
|
||||
assert!(afd_length <= 16);
|
||||
let mut buf = [0u8; 16];
|
||||
buf[..data.len()].copy_from_slice(&data);
|
||||
@ -408,10 +477,7 @@ impl DnsPacketData for APL {
|
||||
use cidr::Cidr;
|
||||
let prefix = cidr::IpCidr::new(address, prefix)?;
|
||||
|
||||
items.push(AplItem {
|
||||
prefix,
|
||||
negation,
|
||||
})
|
||||
items.push(AplItem { prefix, negation })
|
||||
}
|
||||
Ok(APL { items })
|
||||
}
|
||||
@ -427,13 +493,17 @@ impl DnsPacketData for APL {
|
||||
match &item.prefix {
|
||||
cidr::IpCidr::V4(p) => {
|
||||
let addr = p.first_address().octets();
|
||||
while l > 0 && addr[l as usize -1] == 0 { l -= 1; }
|
||||
while l > 0 && addr[l as usize - 1] == 0 {
|
||||
l -= 1;
|
||||
}
|
||||
packet.put_u8(l | negation_flag);
|
||||
packet.extend_from_slice(&addr[..l as usize]);
|
||||
},
|
||||
cidr::IpCidr::V6(p) => {
|
||||
let addr = p.first_address().octets();
|
||||
while l > 0 && addr[l as usize -1] == 0 { l -= 1; }
|
||||
while l > 0 && addr[l as usize - 1] == 0 {
|
||||
l -= 1;
|
||||
}
|
||||
packet.put_u8(l | negation_flag);
|
||||
packet.extend_from_slice(&addr[..l as usize]);
|
||||
},
|
||||
@ -462,10 +532,7 @@ impl DnsTextData for APL {
|
||||
2 => prefix.parse::<cidr::Ipv6Cidr>()?.into(),
|
||||
_ => failure::bail!("Unknown address family {} in item: {:?}", afi, item),
|
||||
};
|
||||
items.push(AplItem {
|
||||
prefix,
|
||||
negation,
|
||||
});
|
||||
items.push(AplItem { prefix, negation });
|
||||
}
|
||||
*data = "";
|
||||
Ok(APL { items })
|
||||
@ -482,7 +549,6 @@ impl DnsTextData for APL {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum IpsecKeyGateway {
|
||||
None,
|
||||
@ -506,7 +572,7 @@ pub enum IPSECKEY {
|
||||
algorithm: u8,
|
||||
// length of gateway is unknown, can't split gateway and public key
|
||||
remaining: Bytes,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
impl DnsPacketData for IPSECKEY {
|
||||
@ -519,12 +585,14 @@ impl DnsPacketData for IPSECKEY {
|
||||
1 => IpsecKeyGateway::Ipv4(Ipv4Addr::deserialize(data)?),
|
||||
2 => IpsecKeyGateway::Ipv6(Ipv6Addr::deserialize(data)?),
|
||||
3 => IpsecKeyGateway::Name(DnsName::deserialize(data)?),
|
||||
_ => return Ok(IPSECKEY::UnknownGateway{
|
||||
_ => {
|
||||
return Ok(IPSECKEY::UnknownGateway {
|
||||
precedence,
|
||||
gateway_type,
|
||||
algorithm,
|
||||
remaining: remaining_bytes(data),
|
||||
}),
|
||||
})
|
||||
},
|
||||
};
|
||||
Ok(IPSECKEY::Known {
|
||||
precedence,
|
||||
@ -536,7 +604,12 @@ impl DnsPacketData for IPSECKEY {
|
||||
|
||||
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||
match *self {
|
||||
IPSECKEY::Known{precedence, algorithm, ref gateway, ref public_key} => {
|
||||
IPSECKEY::Known {
|
||||
precedence,
|
||||
algorithm,
|
||||
ref gateway,
|
||||
ref public_key,
|
||||
} => {
|
||||
packet.reserve(3);
|
||||
packet.put_u8(precedence);
|
||||
let gateway_type: u8 = match *gateway {
|
||||
@ -555,13 +628,18 @@ impl DnsPacketData for IPSECKEY {
|
||||
};
|
||||
public_key.serialize(context, packet)?;
|
||||
},
|
||||
IPSECKEY::UnknownGateway{precedence, gateway_type, algorithm, ref remaining} => {
|
||||
IPSECKEY::UnknownGateway {
|
||||
precedence,
|
||||
gateway_type,
|
||||
algorithm,
|
||||
ref remaining,
|
||||
} => {
|
||||
packet.reserve(3 + remaining.len());
|
||||
packet.put_u8(precedence);
|
||||
packet.put_u8(gateway_type);
|
||||
packet.put_u8(algorithm);
|
||||
packet.put_slice(remaining);
|
||||
}
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@ -589,7 +667,12 @@ impl DnsTextData for IPSECKEY {
|
||||
|
||||
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
|
||||
match *self {
|
||||
IPSECKEY::Known{precedence, algorithm, ref gateway, ref public_key} => {
|
||||
IPSECKEY::Known {
|
||||
precedence,
|
||||
algorithm,
|
||||
ref gateway,
|
||||
ref public_key,
|
||||
} => {
|
||||
let gateway_type: u8 = match *gateway {
|
||||
IpsecKeyGateway::None => 0,
|
||||
IpsecKeyGateway::Ipv4(_) => 1,
|
||||
|
@ -1,7 +1,7 @@
|
||||
mod rrdata;
|
||||
pub mod packet;
|
||||
mod rrdata;
|
||||
pub mod text;
|
||||
|
||||
pub use self::packet::DnsPacketWriteContext;
|
||||
pub use self::rrdata::{RRDataPacket, RRDataText, RRData, StaticRRData};
|
||||
pub use self::rrdata::{RRData, RRDataPacket, RRDataText, StaticRRData};
|
||||
pub use self::text::DnsTextContext;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use bytes::{Bytes, Buf, BufMut};
|
||||
use crate::errors::*;
|
||||
use bytes::{Buf, BufMut, Bytes};
|
||||
use std::io::Cursor;
|
||||
|
||||
mod std_impls;
|
||||
@ -20,7 +20,11 @@ where
|
||||
{
|
||||
let mut c = Cursor::new(data);
|
||||
let result = parser(&mut c)?;
|
||||
failure::ensure!(!c.has_remaining(), "data remaining: {} bytes", c.remaining());
|
||||
failure::ensure!(
|
||||
!c.has_remaining(),
|
||||
"data remaining: {} bytes",
|
||||
c.remaining()
|
||||
);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@ -47,7 +51,10 @@ pub fn short_blob(data: &mut Cursor<Bytes>) -> Result<Bytes> {
|
||||
}
|
||||
|
||||
pub fn write_short_blob(data: &[u8], packet: &mut Vec<u8>) -> Result<()> {
|
||||
failure::ensure!(data.len() < 256, "short blob must be at most 255 bytes long");
|
||||
failure::ensure!(
|
||||
data.len() < 256,
|
||||
"short blob must be at most 255 bytes long"
|
||||
);
|
||||
packet.reserve(data.len() + 1);
|
||||
packet.put_u8(data.len() as u8);
|
||||
packet.put_slice(data);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use bytes::{Bytes, Buf, BufMut};
|
||||
use crate::errors::*;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||
use bytes::{Buf, BufMut, Bytes};
|
||||
use std::io::{Cursor, Read};
|
||||
use std::mem::size_of;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
@ -101,7 +101,7 @@ mod tests {
|
||||
use crate::errors::*;
|
||||
|
||||
fn deserialize<T: super::DnsPacketData>(data: &'static [u8]) -> Result<T> {
|
||||
use bytes::{Bytes,Buf};
|
||||
use bytes::{Buf, Bytes};
|
||||
use std::io::Cursor;
|
||||
let mut c = Cursor::new(Bytes::from_static(data));
|
||||
let result = T::deserialize(&mut c)?;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use bytes::{BufMut};
|
||||
use crate::common_types::name::{DnsCompressedName, DnsLabelRef, DnsName};
|
||||
use crate::errors::*;
|
||||
use crate::common_types::name::{DnsName, DnsCompressedName, DnsLabelRef};
|
||||
use bytes::BufMut;
|
||||
|
||||
// only points to uncompressed labels; if a label of a name is stored,
|
||||
// all following labels must be stored too, even if their pos >= 0x4000.
|
||||
@ -28,7 +28,13 @@ impl LabelEntry {
|
||||
}
|
||||
}
|
||||
|
||||
fn matches(&self, packet: &Vec<u8>, labels: &Vec<LabelEntry>, name: &DnsName, min: u8) -> Option<u8> {
|
||||
fn matches(
|
||||
&self,
|
||||
packet: &Vec<u8>,
|
||||
labels: &Vec<LabelEntry>,
|
||||
name: &DnsName,
|
||||
min: u8,
|
||||
) -> Option<u8> {
|
||||
'outer: for i in 0..min {
|
||||
if name.label_ref(i) != self.label_ref(packet) {
|
||||
continue;
|
||||
@ -86,7 +92,12 @@ fn write_canonical_name(packet: &mut Vec<u8>, name: &DnsName) {
|
||||
packet.put_u8(0);
|
||||
}
|
||||
|
||||
fn write_label_remember(packet: &mut Vec<u8>, labels: &mut Vec<LabelEntry>, label: DnsLabelRef, next_entry: usize) {
|
||||
fn write_label_remember(
|
||||
packet: &mut Vec<u8>,
|
||||
labels: &mut Vec<LabelEntry>,
|
||||
label: DnsLabelRef,
|
||||
next_entry: usize,
|
||||
) {
|
||||
labels.push(LabelEntry {
|
||||
pos: packet.len(),
|
||||
next_entry: next_entry,
|
||||
@ -110,7 +121,6 @@ impl Default for LabelWriteMethod {
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct DnsPacketWriteContext {
|
||||
labels: LabelWriteMethod,
|
||||
|
||||
}
|
||||
|
||||
impl DnsPacketWriteContext {
|
||||
@ -137,7 +147,11 @@ impl DnsPacketWriteContext {
|
||||
self.labels = LabelWriteMethod::Canonical;
|
||||
}
|
||||
|
||||
pub(crate) fn write_uncompressed_name(&mut self, packet: &mut Vec<u8>, name: &DnsName) -> Result<()> {
|
||||
pub(crate) fn write_uncompressed_name(
|
||||
&mut self,
|
||||
packet: &mut Vec<u8>,
|
||||
name: &DnsName,
|
||||
) -> Result<()> {
|
||||
// for now we don't remember labels of these names.
|
||||
//
|
||||
// if we did: would we want to check whether a suffix is already
|
||||
@ -147,7 +161,11 @@ impl DnsPacketWriteContext {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn write_canonical_name(&mut self, packet: &mut Vec<u8>, name: &DnsName) -> Result<()> {
|
||||
pub(crate) fn write_canonical_name(
|
||||
&mut self,
|
||||
packet: &mut Vec<u8>,
|
||||
name: &DnsName,
|
||||
) -> Result<()> {
|
||||
match self.labels {
|
||||
LabelWriteMethod::Uncompressed | LabelWriteMethod::Compressed(_) => {
|
||||
// uncompressed
|
||||
@ -157,10 +175,14 @@ impl DnsPacketWriteContext {
|
||||
write_canonical_name(packet, name);
|
||||
},
|
||||
}
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub(crate) fn write_compressed_name(&mut self, packet: &mut Vec<u8>, name: &DnsCompressedName) -> Result<()> {
|
||||
pub(crate) fn write_compressed_name(
|
||||
&mut self,
|
||||
packet: &mut Vec<u8>,
|
||||
name: &DnsCompressedName,
|
||||
) -> Result<()> {
|
||||
// for DNSSEC we need to write it canonical
|
||||
if name.is_root() {
|
||||
write_name(packet, name);
|
||||
@ -175,7 +197,7 @@ impl DnsPacketWriteContext {
|
||||
LabelWriteMethod::Compressed(ref mut labels) => labels,
|
||||
LabelWriteMethod::Canonical => {
|
||||
write_canonical_name(packet, name);
|
||||
return Ok(())
|
||||
return Ok(());
|
||||
},
|
||||
};
|
||||
|
||||
@ -183,7 +205,9 @@ impl DnsPacketWriteContext {
|
||||
let mut best_match_len = name.label_count();
|
||||
|
||||
for (e_ndx, e) in (labels as &Vec<LabelEntry>).into_iter().enumerate() {
|
||||
if e.pos >= 0x4000 { break; } // this and following labels can't be used for compression
|
||||
if e.pos >= 0x4000 {
|
||||
break;
|
||||
} // this and following labels can't be used for compression
|
||||
if let Some(l) = e.matches(packet, labels, name, best_match_len) {
|
||||
debug_assert!(l < best_match_len);
|
||||
best_match_len = l;
|
||||
@ -207,7 +231,12 @@ impl DnsPacketWriteContext {
|
||||
write_label_remember(packet, labels, name.label_ref(i), n);
|
||||
}
|
||||
// the next label following is at e_ndx
|
||||
write_label_remember(packet, labels, name.label_ref(best_match_len-1), e_ndx);
|
||||
write_label_remember(
|
||||
packet,
|
||||
labels,
|
||||
name.label_ref(best_match_len - 1),
|
||||
e_ndx,
|
||||
);
|
||||
} else {
|
||||
// no need to remember, can't be used for compression
|
||||
for i in 0..best_match_len {
|
||||
@ -240,7 +269,7 @@ impl DnsPacketWriteContext {
|
||||
// no need to remember, can't be used for compression
|
||||
write_name(packet, name);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -1,9 +1,9 @@
|
||||
use bytes::Bytes;
|
||||
use crate::common_types::{Class, Type, classes};
|
||||
use crate::common_types::{classes, Class, Type};
|
||||
use crate::errors::*;
|
||||
use crate::records::UnknownRecord;
|
||||
use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext};
|
||||
use crate::ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext};
|
||||
use crate::ser::text::{DnsTextContext, DnsTextData, DnsTextFormatter};
|
||||
use bytes::Bytes;
|
||||
use std::any::Any;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
@ -12,21 +12,39 @@ use std::io::Cursor;
|
||||
pub use dnsbox_derive::RRData;
|
||||
|
||||
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>
|
||||
where
|
||||
Self: Sized,
|
||||
;
|
||||
Self: Sized;
|
||||
|
||||
fn rr_type(&self) -> Type;
|
||||
|
||||
fn serialize_rr_data(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()>;
|
||||
fn serialize_rr_data(
|
||||
&self,
|
||||
context: &mut DnsPacketWriteContext,
|
||||
packet: &mut Vec<u8>,
|
||||
) -> Result<()>;
|
||||
}
|
||||
|
||||
impl<T: DnsPacketData + StaticRRData> RRDataPacket for T {
|
||||
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> {
|
||||
failure::ensure!(rr_type == T::TYPE, "type mismatch");
|
||||
if T::CLASS != classes::ANY {
|
||||
failure::ensure!(rr_class == T::CLASS, "class mismatch: got {}, need {}", rr_class, T::CLASS);
|
||||
failure::ensure!(
|
||||
rr_class == T::CLASS,
|
||||
"class mismatch: got {}, need {}",
|
||||
rr_class,
|
||||
T::CLASS
|
||||
);
|
||||
}
|
||||
T::deserialize(data)
|
||||
}
|
||||
@ -35,7 +53,11 @@ impl<T: DnsPacketData + StaticRRData> RRDataPacket for T {
|
||||
T::TYPE
|
||||
}
|
||||
|
||||
fn serialize_rr_data(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
|
||||
fn serialize_rr_data(
|
||||
&self,
|
||||
context: &mut DnsPacketWriteContext,
|
||||
packet: &mut Vec<u8>,
|
||||
) -> Result<()> {
|
||||
self.serialize(context, packet)
|
||||
}
|
||||
}
|
||||
@ -43,8 +65,7 @@ impl<T: DnsPacketData + StaticRRData> RRDataPacket for T {
|
||||
pub trait RRDataText {
|
||||
fn dns_parse_rr_data(context: &DnsTextContext, data: &mut &str) -> Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
;
|
||||
Self: Sized;
|
||||
|
||||
// format might fail if there is no (known) text representation.
|
||||
fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result;
|
||||
@ -58,9 +79,16 @@ impl<T: DnsTextData + StaticRRData> RRDataText for T {
|
||||
Self: Sized,
|
||||
{
|
||||
failure::ensure!(context.record_type() == Some(T::TYPE), "type mismatch");
|
||||
let rr_class = context.zone_class().expect("require zone CLASS to parse record");
|
||||
let rr_class = context
|
||||
.zone_class()
|
||||
.expect("require zone CLASS to parse record");
|
||||
if T::CLASS != classes::ANY {
|
||||
failure::ensure!(rr_class == T::CLASS, "class mismatch: got {}, need {}", rr_class, T::CLASS);
|
||||
failure::ensure!(
|
||||
rr_class == T::CLASS,
|
||||
"class mismatch: got {}, need {}",
|
||||
rr_class,
|
||||
T::CLASS
|
||||
);
|
||||
}
|
||||
failure::ensure!(context.last_ttl().is_some(), "require TTL to parse record");
|
||||
T::dns_parse(context, data)
|
||||
@ -84,9 +112,7 @@ pub trait RRData: RRDataPacket + RRDataText + fmt::Debug + 'static {
|
||||
fn text(&self) -> Result<(String, String)> {
|
||||
let mut buf = String::new();
|
||||
match self.dns_format_rr_data(&mut DnsTextFormatter::new(&mut buf)) {
|
||||
Ok(()) => {
|
||||
return Ok((self.rr_type_txt().into(), buf))
|
||||
},
|
||||
Ok(()) => return Ok((self.rr_type_txt().into(), buf)),
|
||||
Err(_) => (),
|
||||
}
|
||||
let mut raw = Vec::new();
|
||||
@ -94,7 +120,8 @@ pub trait RRData: RRDataPacket + RRDataText + fmt::Debug + 'static {
|
||||
let ur = UnknownRecord::new(self.rr_type(), raw.into());
|
||||
// formatting UnknownRecord should not fail
|
||||
buf.clear();
|
||||
ur.dns_format_rr_data(&mut DnsTextFormatter::new(&mut buf)).expect("formatting UnknownRecord must not fail");
|
||||
ur.dns_format_rr_data(&mut DnsTextFormatter::new(&mut buf))
|
||||
.expect("formatting UnknownRecord must not fail");
|
||||
Ok((ur.rr_type_txt().into(), buf))
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::common_types;
|
||||
use std::fmt;
|
||||
|
||||
mod std_impls;
|
||||
pub mod quoted;
|
||||
mod std_impls;
|
||||
|
||||
pub use dnsbox_derive::DnsTextData;
|
||||
|
||||
@ -12,7 +12,9 @@ pub fn skip_whitespace(data: &mut &str) {
|
||||
|
||||
pub fn next_field<'a>(data: &mut &'a str) -> crate::errors::Result<&'a str> {
|
||||
*data = (*data).trim_start();
|
||||
if data.is_empty() { failure::bail!("missing field"); }
|
||||
if data.is_empty() {
|
||||
failure::bail!("missing field");
|
||||
}
|
||||
match data.find(char::is_whitespace) {
|
||||
None => {
|
||||
let result = *data;
|
||||
@ -29,7 +31,9 @@ pub fn next_field<'a>(data: &mut &'a str) -> crate::errors::Result<&'a str> {
|
||||
|
||||
pub fn next_quoted_field(data: &mut &str) -> crate::errors::Result<Vec<u8>> {
|
||||
*data = (*data).trim_start();
|
||||
if data.is_empty() { failure::bail!("missing field"); }
|
||||
if data.is_empty() {
|
||||
failure::bail!("missing field");
|
||||
}
|
||||
|
||||
let result = quoted::UnquoteIterator::new(data).collect::<Result<Vec<_>, _>>()?;
|
||||
Ok(result)
|
||||
@ -208,8 +212,7 @@ impl DnsTextContext {
|
||||
pub trait DnsTextData {
|
||||
fn dns_parse(context: &DnsTextContext, data: &mut &str) -> crate::errors::Result<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
;
|
||||
Self: Sized;
|
||||
// format might fail if there is no (known) text representation.
|
||||
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result;
|
||||
}
|
||||
@ -221,7 +224,11 @@ where
|
||||
let mut data = data;
|
||||
let result = parser(&mut data)?;
|
||||
let data = data.trim();
|
||||
failure::ensure!(data.is_empty(), "didn't parse complete text, remaining: {:?}", data);
|
||||
failure::ensure!(
|
||||
data.is_empty(),
|
||||
"didn't parse complete text, remaining: {:?}",
|
||||
data
|
||||
);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ impl ::std::ops::Deref for EncodedByte {
|
||||
|
||||
pub struct EncodeIterator<'a> {
|
||||
encode_whitespace: bool,
|
||||
data: &'a [u8]
|
||||
data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> EncodeIterator<'a> {
|
||||
@ -38,7 +38,9 @@ impl<'a> Iterator for EncodeIterator<'a> {
|
||||
type Item = EncodedByte;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.data.is_empty() { return None; }
|
||||
if self.data.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let b = self.data[0];
|
||||
self.data = &self.data[1..];
|
||||
if b < 32 || b > 127 || (self.encode_whitespace && is_ascii_whitespace(b)) {
|
||||
@ -74,7 +76,11 @@ pub struct UnquoteError {
|
||||
|
||||
impl fmt::Display for UnquoteError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "unquote error at position {} in {:?}: {}", self.position, self.data, self.msg)
|
||||
write!(
|
||||
f,
|
||||
"unquote error at position {} in {:?}: {}",
|
||||
self.position, self.data, self.msg
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -123,7 +129,9 @@ impl<'a, 'b: 'a> Iterator for UnquoteIterator<'a, 'b> {
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let raw = self.data.as_bytes();
|
||||
|
||||
if raw.is_empty() { return self.err("empty input"); }
|
||||
if raw.is_empty() {
|
||||
return self.err("empty input");
|
||||
}
|
||||
|
||||
if 0 == self.pos {
|
||||
// check for starting quote:
|
||||
@ -159,7 +167,9 @@ impl<'a, 'b: 'a> Iterator for UnquoteIterator<'a, 'b> {
|
||||
*self.data = self.data[self.pos..].trim_start();
|
||||
return None;
|
||||
} else if raw[self.pos] == b'\\' {
|
||||
if self.pos + 1 >= raw.len() { return self.err("unexpected end of string after backslash"); }
|
||||
if self.pos + 1 >= raw.len() {
|
||||
return self.err("unexpected end of string after backslash");
|
||||
}
|
||||
if raw[self.pos + 1] < b'0' || raw[self.pos + 1] > b'9' {
|
||||
let result = raw[self.pos + 1];
|
||||
if !self.quoted && is_ascii_whitespace(result) {
|
||||
@ -169,16 +179,24 @@ impl<'a, 'b: 'a> Iterator for UnquoteIterator<'a, 'b> {
|
||||
return Some(Ok(result));
|
||||
}
|
||||
// otherwise require 3 decimal digits
|
||||
if self.pos + 3 >= raw.len() { return self.err("unexpected end of string after backslash with decimal"); }
|
||||
if self.pos + 3 >= raw.len() {
|
||||
return self.err("unexpected end of string after backslash with decimal");
|
||||
}
|
||||
// raw[self.pos+1] already checked for digit above
|
||||
if raw[self.pos+2] < b'0' || raw[self.pos+2] > b'9' || raw[self.pos+3] < b'0' || raw[self.pos+3] > b'9' {
|
||||
if raw[self.pos + 2] < b'0'
|
||||
|| raw[self.pos + 2] > b'9'
|
||||
|| raw[self.pos + 3] < b'0'
|
||||
|| raw[self.pos + 3] > b'9'
|
||||
{
|
||||
return self.err("expecting 3 digits after backslash with decimal");
|
||||
}
|
||||
let d1 = raw[self.pos + 1] - b'0';
|
||||
let d2 = raw[self.pos + 2] - b'0';
|
||||
let d3 = raw[self.pos + 3] - b'0';
|
||||
let val = (d1 as u32 * 100) + (d2 as u32 * 10) + (d3 as u32);
|
||||
if val > 255 { return self.err("invalid decimal escape"); }
|
||||
if val > 255 {
|
||||
return self.err("invalid decimal escape");
|
||||
}
|
||||
self.pos += 4;
|
||||
Some(Ok(val as u8))
|
||||
} else {
|
||||
@ -194,17 +212,11 @@ mod tests {
|
||||
use crate::ser::text::{next_quoted_field, quote};
|
||||
|
||||
fn check_quote(data: &[u8], quoted: &str) {
|
||||
assert_eq!(
|
||||
quote(data),
|
||||
quoted
|
||||
);
|
||||
assert_eq!(quote(data), quoted);
|
||||
}
|
||||
|
||||
fn check_unquote(mut input: &str, data: &[u8]) {
|
||||
assert_eq!(
|
||||
next_quoted_field(&mut input).unwrap(),
|
||||
data
|
||||
);
|
||||
assert_eq!(next_quoted_field(&mut input).unwrap(), data);
|
||||
assert!(input.is_empty());
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::ser::text::{next_field, DnsTextContext, DnsTextData, DnsTextFormatter};
|
||||
use std::fmt;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use crate::ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field};
|
||||
|
||||
impl DnsTextData for () {
|
||||
fn dns_parse(_context: &DnsTextContext, _data: &mut &str) -> crate::errors::Result<Self> {
|
||||
@ -92,30 +92,22 @@ mod tests {
|
||||
fn test_ipv6() {
|
||||
assert_eq!(
|
||||
deserialize::<Ipv6Addr>("FEDC:BA98:7654:3210:FEDC:BA98:7654:3210").unwrap(),
|
||||
Ipv6Addr::new(
|
||||
0xfedc, 0xba98, 0x7654, 0x3210, 0xfedc, 0xba98, 0x7654, 0x3210
|
||||
)
|
||||
Ipv6Addr::new(0xfedc, 0xba98, 0x7654, 0x3210, 0xfedc, 0xba98, 0x7654, 0x3210)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
deserialize::<Ipv6Addr>("1080::8:800:200C:417A").unwrap(),
|
||||
Ipv6Addr::new(
|
||||
0x1080, 0, 0, 0, 0x8, 0x800, 0x200c, 0x417a
|
||||
)
|
||||
Ipv6Addr::new(0x1080, 0, 0, 0, 0x8, 0x800, 0x200c, 0x417a)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
deserialize::<Ipv6Addr>("::13.1.68.3").unwrap(),
|
||||
Ipv6Addr::new(
|
||||
0, 0, 0, 0, 0, 0, 0x0d01, 0x4403
|
||||
)
|
||||
Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0x0d01, 0x4403)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
deserialize::<Ipv6Addr>("::FFFF:129.144.52.38").unwrap(),
|
||||
Ipv6Addr::new(
|
||||
0, 0, 0, 0, 0, 0xffff, 0x8190, 0x3426
|
||||
)
|
||||
Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0x8190, 0x3426)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
#[cfg(not(feature = "no-unsafe"))]
|
||||
pub fn from_utf8_unchecked(v: &[u8]) -> &str {
|
||||
unsafe {
|
||||
::std::str::from_utf8_unchecked(v)
|
||||
}
|
||||
unsafe { ::std::str::from_utf8_unchecked(v) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "no-unsafe")]
|
||||
|
@ -1,6 +1,10 @@
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
fn derive_impl(s: &synstructure::Structure, parse_fields: TokenStream, serialize_fields: TokenStream) -> TokenStream {
|
||||
fn derive_impl(
|
||||
s: &synstructure::Structure,
|
||||
parse_fields: TokenStream,
|
||||
serialize_fields: TokenStream,
|
||||
) -> TokenStream {
|
||||
s.gen_impl(quote!{
|
||||
#[allow(unused_imports)]
|
||||
use dnsbox_base::_failure::ResultExt;
|
||||
@ -73,9 +77,18 @@ pub fn derive(s: synstructure::Structure) -> TokenStream {
|
||||
let ast = s.ast();
|
||||
|
||||
match &ast.data {
|
||||
syn::Data::Struct(syn::DataStruct{ fields: syn::Fields::Unit, .. }) => derive_unit(&s),
|
||||
syn::Data::Struct(syn::DataStruct{ fields: syn::Fields::Named(fields), .. }) => derive_named(&s, fields),
|
||||
syn::Data::Struct(syn::DataStruct{ fields: syn::Fields::Unnamed(fields), .. }) => derive_unnamed(&s, fields),
|
||||
syn::Data::Struct(syn::DataStruct {
|
||||
fields: syn::Fields::Unit,
|
||||
..
|
||||
}) => derive_unit(&s),
|
||||
syn::Data::Struct(syn::DataStruct {
|
||||
fields: syn::Fields::Named(fields),
|
||||
..
|
||||
}) => derive_named(&s, fields),
|
||||
syn::Data::Struct(syn::DataStruct {
|
||||
fields: syn::Fields::Unnamed(fields),
|
||||
..
|
||||
}) => derive_unnamed(&s, fields),
|
||||
_ => panic!("Deriving DnsPacketData not supported for non struct types"),
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
fn derive_impl(s: &synstructure::Structure, parse_fields: TokenStream, format_fields: TokenStream) -> TokenStream {
|
||||
fn derive_impl(
|
||||
s: &synstructure::Structure,
|
||||
parse_fields: TokenStream,
|
||||
format_fields: TokenStream,
|
||||
) -> TokenStream {
|
||||
s.gen_impl(quote!{
|
||||
#[allow(unused_imports)]
|
||||
use dnsbox_base::_failure::ResultExt as _;
|
||||
@ -71,9 +75,18 @@ pub fn derive(s: synstructure::Structure) -> TokenStream {
|
||||
let ast = s.ast();
|
||||
|
||||
match &ast.data {
|
||||
syn::Data::Struct(syn::DataStruct{ fields: syn::Fields::Unit, .. }) => derive_unit(&s),
|
||||
syn::Data::Struct(syn::DataStruct{ fields: syn::Fields::Named(fields), .. }) => derive_named(&s, fields),
|
||||
syn::Data::Struct(syn::DataStruct{ fields: syn::Fields::Unnamed(fields), .. }) => derive_unnamed(&s, fields),
|
||||
syn::Data::Struct(syn::DataStruct {
|
||||
fields: syn::Fields::Unit,
|
||||
..
|
||||
}) => derive_unit(&s),
|
||||
syn::Data::Struct(syn::DataStruct {
|
||||
fields: syn::Fields::Named(fields),
|
||||
..
|
||||
}) => derive_named(&s, fields),
|
||||
syn::Data::Struct(syn::DataStruct {
|
||||
fields: syn::Fields::Unnamed(fields),
|
||||
..
|
||||
}) => derive_unnamed(&s, fields),
|
||||
_ => panic!("Deriving DnsTextData not supported for non struct types"),
|
||||
}
|
||||
}
|
||||
|
@ -6,11 +6,11 @@ extern crate synstructure;
|
||||
extern crate quote;
|
||||
extern crate proc_macro2;
|
||||
|
||||
mod rrdata;
|
||||
mod dns_packet_data;
|
||||
mod dns_text_data;
|
||||
mod native_enum;
|
||||
mod native_flags;
|
||||
mod rrdata;
|
||||
|
||||
decl_derive!([DnsPacketData] => dns_packet_data::derive);
|
||||
decl_derive!([DnsTextData] => dns_text_data::derive);
|
||||
@ -20,12 +20,13 @@ decl_attribute!([native_flags] => native_flags::attribute_native_flags);
|
||||
|
||||
fn attr_get_single_list_arg(attr_meta: &syn::Meta) -> proc_macro2::TokenStream {
|
||||
match attr_meta {
|
||||
syn::Meta::Word(_) => {
|
||||
panic!("{:?} attribute requires an argument", attr_meta.name())
|
||||
},
|
||||
syn::Meta::Word(_) => panic!("{:?} attribute requires an argument", attr_meta.name()),
|
||||
syn::Meta::List(l) => {
|
||||
if l.nested.len() != 1 {
|
||||
panic!("{:?} attribute requires exactly one argument", attr_meta.name());
|
||||
panic!(
|
||||
"{:?} attribute requires exactly one argument",
|
||||
attr_meta.name()
|
||||
);
|
||||
}
|
||||
let arg = *l.nested.first().unwrap().value();
|
||||
quote! {#arg}
|
||||
|
@ -5,7 +5,11 @@ pub fn attribute_native_enum(
|
||||
structure: synstructure::Structure,
|
||||
) -> TokenStream {
|
||||
let ast = structure.ast();
|
||||
let in_attrs = ast.attrs.iter().map(|a| quote!{#a}).collect::<TokenStream>();
|
||||
let in_attrs = ast
|
||||
.attrs
|
||||
.iter()
|
||||
.map(|a| quote! {#a})
|
||||
.collect::<TokenStream>();
|
||||
let in_vis = &ast.vis;
|
||||
let name = &ast.ident;
|
||||
|
||||
@ -19,7 +23,10 @@ pub fn attribute_native_enum(
|
||||
|
||||
{
|
||||
let variants = &enumdata.variants;
|
||||
let doc_str = format!("Known enum variants of [`{}`]\n\n[`{}`]: struct.{}.html\n", name, name, name);
|
||||
let doc_str = format!(
|
||||
"Known enum variants of [`{}`]\n\n[`{}`]: struct.{}.html\n",
|
||||
name, name, name
|
||||
);
|
||||
known_enum = quote! {
|
||||
#[doc = #doc_str]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
@ -34,10 +41,20 @@ pub fn attribute_native_enum(
|
||||
let mut consts = TokenStream::new();
|
||||
let mut convert = TokenStream::new();
|
||||
for variant in &enumdata.variants {
|
||||
let variant_attrs = variant.attrs.iter().map(|a| quote!{#a}).collect::<TokenStream>();
|
||||
let variant_attrs = variant
|
||||
.attrs
|
||||
.iter()
|
||||
.map(|a| quote! {#a})
|
||||
.collect::<TokenStream>();
|
||||
let disc_name = &variant.ident;
|
||||
let disc = variant.discriminant.as_ref().map(|(_, d)| quote!{#d}).unwrap_or_else(|| quote!{
|
||||
let disc = variant
|
||||
.discriminant
|
||||
.as_ref()
|
||||
.map(|(_, d)| quote! {#d})
|
||||
.unwrap_or_else(|| {
|
||||
quote! {
|
||||
#known_name::#disc_name as #native_type
|
||||
}
|
||||
});
|
||||
consts.extend(quote! {
|
||||
#variant_attrs
|
||||
|
@ -5,11 +5,18 @@ pub fn attribute_native_flags(
|
||||
structure: synstructure::Structure,
|
||||
) -> TokenStream {
|
||||
let ast = structure.ast();
|
||||
let in_attrs = ast.attrs.iter().map(|a| quote!{#a}).collect::<TokenStream>();
|
||||
let in_attrs = ast
|
||||
.attrs
|
||||
.iter()
|
||||
.map(|a| quote! {#a})
|
||||
.collect::<TokenStream>();
|
||||
let in_vis = &ast.vis;
|
||||
let name = &ast.ident;
|
||||
|
||||
let hidden_impl_mod = syn::Ident::new(&format!("_{}_hidden_native_flags", name), proc_macro2::Span::call_site());
|
||||
let hidden_impl_mod = syn::Ident::new(
|
||||
&format!("_{}_hidden_native_flags", name),
|
||||
proc_macro2::Span::call_site(),
|
||||
);
|
||||
|
||||
let enumdata = match &ast.data {
|
||||
syn::Data::Enum(de) => de,
|
||||
@ -20,9 +27,17 @@ pub fn attribute_native_flags(
|
||||
let mut known_mask = TokenStream::new();
|
||||
let mut dbg = TokenStream::new();
|
||||
for variant in &enumdata.variants {
|
||||
let variant_attrs = variant.attrs.iter().map(|a| quote!{#a}).collect::<TokenStream>();
|
||||
let variant_attrs = variant
|
||||
.attrs
|
||||
.iter()
|
||||
.map(|a| quote! {#a})
|
||||
.collect::<TokenStream>();
|
||||
let disc_name = &variant.ident;
|
||||
let disc = variant.discriminant.as_ref().map(|(_, d)| d).expect("all variants need explicit bitmask discriminants");
|
||||
let disc = variant
|
||||
.discriminant
|
||||
.as_ref()
|
||||
.map(|(_, d)| d)
|
||||
.expect("all variants need explicit bitmask discriminants");
|
||||
consts.extend(quote! {
|
||||
#variant_attrs
|
||||
pub const #disc_name: Flag = Flag { mask: #disc };
|
||||
|
@ -53,7 +53,8 @@ pub fn rrdata_derive(s: synstructure::Structure) -> proc_macro2::TokenStream {
|
||||
});
|
||||
let rr_class = rr_class.unwrap_or_else(|| quote! {ANY});
|
||||
|
||||
let test_mod_name = syn::Ident::new(&format!("test_rr_{}", name), proc_macro2::Span::call_site());
|
||||
let test_mod_name =
|
||||
syn::Ident::new(&format!("test_rr_{}", name), proc_macro2::Span::call_site());
|
||||
|
||||
let impl_rrdata = s.unbound_impl(
|
||||
quote!(::dnsbox_base::ser::RRData),
|
||||
|
Loading…
Reference in New Issue
Block a user