This commit is contained in:
Stefan Bühler 2017-12-27 21:50:51 +01:00
parent af95fbdf67
commit d362520632
9 changed files with 539 additions and 61 deletions

View File

@ -2,7 +2,7 @@ use bytes::{Bytes, BufMut};
use data_encoding::{self, HEXLOWER_PERMISSIVE}; use data_encoding::{self, HEXLOWER_PERMISSIVE};
use errors::*; use errors::*;
use failure::{Fail, ResultExt}; use failure::{Fail, ResultExt};
use ser::packet::{DnsPacketData, DnsPacketWriteContext, remaining_bytes, short_blob, write_short_blob}; use ser::packet::{DnsPacketData, DnsPacketWriteContext, remaining_bytes, short_blob, write_short_blob, get_blob};
use ser::text::*; use ser::text::*;
use std::fmt; use std::fmt;
use std::io::Cursor; use std::io::Cursor;
@ -65,6 +65,57 @@ impl DnsTextData for HexShortBlob {
} }
} }
// 16-bit length, uses decimal length + base64 encoding (if length > 0)
// for text representation.
//
// In base64 encoding no whitespace allowed and padding required.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Base64LongBlob(Bytes);
impl DnsPacketData for Base64LongBlob {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
let len = u16::deserialize(data)? as usize;
check_enough_data!(data, len, "data for long blob");
Ok(Base64LongBlob(get_blob(data, len)?))
}
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
let len = self.0.len();
ensure!(len < 0x1_0000, "blob too long");
(len as u16).serialize(context, packet)?;
packet.reserve(len);
packet.put_slice(&self.0);
Ok(())
}
}
impl DnsTextData for Base64LongBlob {
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let length_field = next_field(data)?;
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())
.with_context(|e| e.context(format!("invalid base64: {:?}", blob_field)))?;
Ok(Base64LongBlob(result.into()))
} else {
Ok(Base64LongBlob(Bytes::new()))
}
}
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
if self.0.len() >= 0x1_0000 { return Err(fmt::Error); }
if self.0.is_empty() {
write!(f, "0")
} else {
write!(f, "{} {}", self.0.len(), BASE64_ALLOW_WS.encode(&self.0))
}
}
}
// No length byte (or restriction), just all data to end of record. uses // No length byte (or restriction), just all data to end of record. uses
// base64 encoding for text representation, whitespace allowed, padding // base64 encoding for text representation, whitespace allowed, padding
// required. // required.
@ -95,7 +146,11 @@ impl DnsTextData for Base64RemainingBlob {
} }
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
write!(f, "{}", BASE64_ALLOW_WS.encode(&self.0)) if !self.0.is_empty() {
write!(f, "{}", BASE64_ALLOW_WS.encode(&self.0))
} else {
Ok(())
}
} }
} }

View File

@ -0,0 +1,143 @@
use bytes::Bytes;
use errors::*;
use ser::packet::{DnsPacketData, DnsPacketWriteContext};
use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field};
use std::fmt;
use std::io::{Cursor, Read};
fn fmt_eui_hyphens(data: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:02x}", data[0])?;
for v in &data[1..] {
write!(f, "-{:02x}", v)?;
}
Ok(())
}
fn parse_eui_hyphens(dest: &mut [u8], source: &str) -> Result<()> {
let mut pos = 0;
for octet in source.split('-') {
ensure!(pos < dest.len(), "too many octets for EUI{}", dest.len() * 8);
ensure!(octet.len() == 2, "invalid octet {:?}", octet);
match u8::from_str_radix(octet, 16) {
Ok(o) => {
dest[pos] = o;
pos += 1;
},
Err(_) => {
bail!("invalid octet {:?}", octet);
},
}
}
ensure!(pos == dest.len(), "not enough octets for EUI{}", dest.len() * 8);
Ok(())
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct EUI48Addr(pub [u8; 6]);
impl fmt::Display for EUI48Addr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_eui_hyphens(&self.0, f)
}
}
impl fmt::Debug for EUI48Addr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_eui_hyphens(&self.0, f)
}
}
impl DnsPacketData for EUI48Addr {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
let mut buf = [0u8; 6];
check_enough_data!(data, 6, "not enough bytes for EUI48Addr");
data.read_exact(&mut buf)?;
Ok(EUI48Addr(buf))
}
fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
packet.extend_from_slice(&self.0);
Ok(())
}
}
impl DnsTextData for EUI48Addr {
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> Result<Self> {
let field = next_field(data)?;
let mut buf = [0u8; 6];
parse_eui_hyphens(&mut buf, field)?;
Ok(EUI48Addr(buf))
}
// format might fail if there is no (known) text representation.
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
write!(f, "{}", self)
}
}
impl From<[u8; 6]> for EUI48Addr {
fn from(v: [u8; 6]) -> Self {
EUI48Addr(v)
}
}
impl Into<[u8; 6]> for EUI48Addr {
fn into(self) -> [u8; 6] {
self.0
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct EUI64Addr(pub [u8; 8]);
impl fmt::Display for EUI64Addr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_eui_hyphens(&self.0, f)
}
}
impl fmt::Debug for EUI64Addr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_eui_hyphens(&self.0, f)
}
}
impl DnsPacketData for EUI64Addr {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
let mut buf = [0u8; 8];
check_enough_data!(data, 8, "not enough bytes for EUI64Addr");
data.read_exact(&mut buf)?;
Ok(EUI64Addr(buf))
}
fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
packet.extend_from_slice(&self.0);
Ok(())
}
}
impl DnsTextData for EUI64Addr {
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> Result<Self> {
let field = next_field(data)?;
let mut buf = [0u8; 8];
parse_eui_hyphens(&mut buf, field)?;
Ok(EUI64Addr(buf))
}
// format might fail if there is no (known) text representation.
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
write!(f, "{}", self)
}
}
impl From<[u8; 8]> for EUI64Addr {
fn from(v: [u8; 8]) -> Self {
EUI64Addr(v)
}
}
impl Into<[u8; 8]> for EUI64Addr {
fn into(self) -> [u8; 8] {
self.0
}
}

View File

@ -1,19 +1,21 @@
pub mod name;
pub mod text;
pub mod binary; pub mod binary;
pub mod classes; pub mod classes;
pub mod name;
pub mod text;
pub mod types; pub mod types;
mod eui;
mod nsec; mod nsec;
mod nxt; mod nxt;
mod time; mod time;
mod uri; mod uri;
pub use self::binary::{HexShortBlob, Base64RemainingBlob, HexRemainingBlob}; pub use self::binary::{HexShortBlob, Base64LongBlob, Base64RemainingBlob, HexRemainingBlob};
pub use self::classes::Class;
pub use self::eui::{EUI48Addr, EUI64Addr};
pub use self::name::{DnsName, DnsCanonicalName, DnsCompressedName}; pub use self::name::{DnsName, DnsCanonicalName, DnsCompressedName};
pub use self::nsec::{NsecTypeBitmap, NextHashedOwnerName}; pub use self::nsec::{NsecTypeBitmap, NextHashedOwnerName};
pub use self::types::Type;
pub use self::classes::Class;
pub use self::text::{ShortText, LongText, UnquotedShortText, RemainingText};
pub use self::uri::UriText;
pub use self::time::Time;
pub use self::nxt::NxtTypeBitmap; pub use self::nxt::NxtTypeBitmap;
pub use self::text::{ShortText, LongText, UnquotedShortText, RemainingText};
pub use self::time::{Time, Time48};
pub use self::types::Type;
pub use self::uri::UriText;

View File

@ -9,7 +9,7 @@ use std::io::Cursor;
/// ///
/// Is expected to wrap around. /// Is expected to wrap around.
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
pub struct Time(u32); pub struct Time(pub u32);
impl DnsPacketData for Time { impl DnsPacketData for Time {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> { fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
@ -21,12 +21,80 @@ impl DnsPacketData for Time {
} }
} }
struct Tm {
year: u16, // 1970...9999
month: u8, // 01...12
day: u8, // 01..31
hour: u8, // 00..23
minute: u8, // 00..59
second: u8, // 00..59
}
impl Tm {
// 0..365
fn dayofyear(&self) -> u16 {
let is_leap_year = (0 == self.year % 4) && ((0 != self.year % 100) || (0 == self.year % 400));
static MONTH_START: [u16; 12] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334];
let leap_add = if self.month > 2 && is_leap_year { 1 } else { 0 };
MONTH_START[self.month as usize - 1] + leap_add + (self.day as u16 - 1)
}
fn epoch(&self) -> u64 {
let yday = self.dayofyear();
self.second as u64
+ 60 * self.minute as u64
+ 3600 * self.hour as u64
+ 86400 * yday as u64
+ 31536000 * (self.year as u64 - 1970)
+ 86400 * ((self.year as u64 - 1969) / 4)
- 86400 * ((self.year as u64 - 1901) / 100)
+ 86400 * ((self.year as u64 - 1900 + 299) / 400)
}
}
impl ::std::str::FromStr for Tm {
type Err = ::failure::Error;
fn from_str(s: &str) -> ::errors::Result<Self> {
ensure!(s.len() == 14, "Tm string must be exactly 14 digits long");
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::<u16>()?;
ensure!(year >= 1970, "year must be >= 1970");
ensure!(year <= 9999, "year must be <= 9999");
fn p(s: &str, min: u8, max: u8, name: &'static str) -> ::errors::Result<u8> {
let v = s.parse::<u8>()?;
ensure!(v >= min && v <= max, "{} {} out of range {}-{}", name, v, min, max);
Ok(v)
}
let month = p(&s[4..6], 1, 12, "month")?;
let day = p(&s[6..8], 1, 31, "day")?;
let hour = p(&s[8..10], 0, 23, "hour")?;
let minute = p(&s[10..12], 0, 59, "minute")?;
let second = p(&s[12..14], 0, 59, "second")?;
if 2 == month {
let is_leap_year = (0 == year % 4) && ((0 != year % 100) || (0 == year % 400));
ensure!(day < 30, "day {} out of range in february", day);
ensure!(is_leap_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];
ensure!(day <= max_days, "day {} out of range for month {}", day, month);
}
Ok(Tm{ year, month, day, hour, minute, second })
}
}
impl DnsTextData for Time { impl DnsTextData for Time {
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let field = next_field(data)?; let field = next_field(data)?;
let epoch = field.parse::<u32>(); let epoch = field.parse::<u32>();
if field.len() == 14 && epoch.is_err() { if field.len() == 14 && epoch.is_err() {
unimplemented!() let tm = field.parse::<Tm>()?;
Ok(Time(tm.epoch() as u32))
} else { } else {
Ok(Time(epoch?)) Ok(Time(epoch?))
} }
@ -36,3 +104,39 @@ impl DnsTextData for Time {
write!(f, "{}", self.0) write!(f, "{}", self.0)
} }
} }
pub const TIME48_MAX: u64 = 0xffff_ffff_ffff;
/// 48-bit timestamp in seconds since epoch (ignoring leap seconds)
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Time48(pub u64);
impl DnsPacketData for Time48 {
fn deserialize(data: &mut Cursor<Bytes>) -> Result<Self> {
let high16 = u16::deserialize(data)? as u64;
let low32 = u32::deserialize(data)? as u64;
Ok(Time48(high16 | low32))
}
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
ensure!(self.0 <= TIME48_MAX, "time48 overflow");
let high16 = (self.0 >> 32) as u16;
let low32 = self.0 as u32;
high16.serialize(context, packet)?;
low32.serialize(context, packet)?;
Ok(())
}
}
impl DnsTextData for Time48 {
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let field = next_field(data)?;
let epoch = field.parse::<u64>()?;
ensure!(epoch <= TIME48_MAX, "time48 overflow");
Ok(Time48(epoch))
}
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}

View File

@ -277,7 +277,8 @@ fn test_TXT() {
None, None,
b"\xfflong record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\x0a2222222222", b"\xfflong record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\x0a2222222222",
); );
/* autosplitting not supported // autosplitting not supported
/*
check(types::TXT, check(types::TXT,
"\"long record test 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112222222222\"", "\"long record test 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112222222222\"",
Some("\"long record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\" \"2222222222\""), Some("\"long record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\" \"2222222222\""),
@ -514,7 +515,8 @@ fn test_IPSECKEY() {
fn test_RRSIG() { fn test_RRSIG() {
check(types::RRSIG, check(types::RRSIG,
"SOA 8 3 300 20130523000000 20130509000000 54216 rec.test. ecWKD/OsdAiXpbM/sgPT82KVD/WiQnnqcxoJgiH3ixHa+LOAcYU7FG7V4BRRJxLriY1e0rB2gAs3kCel9D4bzfK6wAqG4Di/eHUgHptRlaR2ycELJ4t1pjzrnuGiIzA1wM2izRmeE+Xoy1367Qu0pOz5DLzTfQITWFsB2iUzN4Y=", "SOA 8 3 300 20130523000000 20130509000000 54216 rec.test. ecWKD/OsdAiXpbM/sgPT82KVD/WiQnnqcxoJgiH3ixHa+LOAcYU7FG7V4BRRJxLriY1e0rB2gAs3kCel9D4bzfK6wAqG4Di/eHUgHptRlaR2ycELJ4t1pjzrnuGiIzA1wM2izRmeE+Xoy1367Qu0pOz5DLzTfQITWFsB2iUzN4Y=",
None, // None, // we use the epoch as canoninc format
Some("SOA 8 3 300 1369267200 1368057600 54216 rec.test. ecWKD/OsdAiXpbM/sgPT82KVD/WiQnnqcxoJgiH3ixHa+LOAcYU7FG7V4BRRJxLriY1e0rB2gAs3kCel9D4bzfK6wAqG4Di/eHUgHptRlaR2ycELJ4t1pjzrnuGiIzA1wM2izRmeE+Xoy1367Qu0pOz5DLzTfQITWFsB2iUzN4Y="),
b"\x00\x06\x08\x03\x00\x00\x01\x2c\x51\x9d\x5c\x00\x51\x8a\xe7\x00\xd3\xc8\x03\x72\x65\x63\x04\x74\x65\x73\x74\x00\x79\xc5\x8a\x0f\xf3\xac\x74\x08\x97\xa5\xb3\x3f\xb2\x03\xd3\xf3\x62\x95\x0f\xf5\xa2\x42\x79\xea\x73\x1a\x09\x82\x21\xf7\x8b\x11\xda\xf8\xb3\x80\x71\x85\x3b\x14\x6e\xd5\xe0\x14\x51\x27\x12\xeb\x89\x8d\x5e\xd2\xb0\x76\x80\x0b\x37\x90\x27\xa5\xf4\x3e\x1b\xcd\xf2\xba\xc0\x0a\x86\xe0\x38\xbf\x78\x75\x20\x1e\x9b\x51\x95\xa4\x76\xc9\xc1\x0b\x27\x8b\x75\xa6\x3c\xeb\x9e\xe1\xa2\x23\x30\x35\xc0\xcd\xa2\xcd\x19\x9e\x13\xe5\xe8\xcb\x5d\xfa\xed\x0b\xb4\xa4\xec\xf9\x0c\xbc\xd3\x7d\x02\x13\x58\x5b\x01\xda\x25\x33\x37\x86", b"\x00\x06\x08\x03\x00\x00\x01\x2c\x51\x9d\x5c\x00\x51\x8a\xe7\x00\xd3\xc8\x03\x72\x65\x63\x04\x74\x65\x73\x74\x00\x79\xc5\x8a\x0f\xf3\xac\x74\x08\x97\xa5\xb3\x3f\xb2\x03\xd3\xf3\x62\x95\x0f\xf5\xa2\x42\x79\xea\x73\x1a\x09\x82\x21\xf7\x8b\x11\xda\xf8\xb3\x80\x71\x85\x3b\x14\x6e\xd5\xe0\x14\x51\x27\x12\xeb\x89\x8d\x5e\xd2\xb0\x76\x80\x0b\x37\x90\x27\xa5\xf4\x3e\x1b\xcd\xf2\xba\xc0\x0a\x86\xe0\x38\xbf\x78\x75\x20\x1e\x9b\x51\x95\xa4\x76\xc9\xc1\x0b\x27\x8b\x75\xa6\x3c\xeb\x9e\xe1\xa2\x23\x30\x35\xc0\xcd\xa2\xcd\x19\x9e\x13\xe5\xe8\xcb\x5d\xfa\xed\x0b\xb4\xa4\xec\xf9\x0c\xbc\xd3\x7d\x02\x13\x58\x5b\x01\xda\x25\x33\x37\x86",
); );
} }

View File

@ -158,22 +158,22 @@ impl Registry {
r.register_unknown("APL" , types::APL); r.register_unknown("APL" , types::APL);
r.register_known::<structs::DS>(); r.register_known::<structs::DS>();
r.register_known::<structs::SSHFP>(); r.register_known::<structs::SSHFP>();
r.register_unknown("IPSECKEY" , types::IPSECKEY); r.register_known::<structs::IPSECKEY>();
r.register_known::<structs::RRSIG>(); r.register_known::<structs::RRSIG>();
r.register_known::<structs::NSEC>(); r.register_known::<structs::NSEC>();
r.register_known::<structs::DNSKEY>(); r.register_known::<structs::DNSKEY>();
r.register_unknown("DHCID" , types::DHCID); r.register_known::<structs::DHCID>();
r.register_known::<structs::NSEC3>(); r.register_known::<structs::NSEC3>();
r.register_known::<structs::NSEC3PARAM>(); r.register_known::<structs::NSEC3PARAM>();
r.register_unknown("TLSA" , types::TLSA); r.register_known::<structs::TLSA>();
r.register_unknown("SMIMEA" , types::SMIMEA); r.register_known::<structs::SMIMEA>();
r.register_unknown("HIP" , types::HIP); r.register_unknown("HIP" , types::HIP);
r.register_unknown("NINFO" , types::NINFO); r.register_unknown("NINFO" , types::NINFO);
r.register_unknown("RKEY" , types::RKEY); r.register_unknown("RKEY" , types::RKEY);
r.register_unknown("TALINK" , types::TALINK); r.register_unknown("TALINK" , types::TALINK);
r.register_unknown("CDS" , types::CDS); r.register_unknown("CDS" , types::CDS);
r.register_unknown("CDNSKEY" , types::CDNSKEY); r.register_unknown("CDNSKEY" , types::CDNSKEY);
r.register_unknown("OPENPGPKEY", types::OPENPGPKEY); r.register_known::<structs::OPENPGPKEY>();
r.register_unknown("CSYNC" , types::CSYNC); r.register_unknown("CSYNC" , types::CSYNC);
r.register_known::<structs::SPF>(); r.register_known::<structs::SPF>();
r.register_unknown("UINFO" , types::UINFO); r.register_unknown("UINFO" , types::UINFO);
@ -184,10 +184,10 @@ impl Registry {
r.register_unknown("L32" , types::L32); r.register_unknown("L32" , types::L32);
r.register_unknown("L64" , types::L64); r.register_unknown("L64" , types::L64);
r.register_unknown("LP" , types::LP); r.register_unknown("LP" , types::LP);
r.register_unknown("EUI48" , types::EUI48); r.register_known::<structs::EUI48>();
r.register_unknown("EUI64" , types::EUI64); r.register_known::<structs::EUI64>();
r.register_unknown("TKEY" , types::TKEY); r.register_known::<structs::TKEY>();
r.register_unknown("TSIG" , types::TSIG); r.register_known::<structs::TSIG>();
r.register_unknown("IXFR" , types::IXFR); r.register_unknown("IXFR" , types::IXFR);
r.register_unknown("AXFR" , types::AXFR); r.register_unknown("AXFR" , types::AXFR);
r.register_unknown("MAILB" , types::MAILB); r.register_unknown("MAILB" , types::MAILB);
@ -198,7 +198,7 @@ impl Registry {
r.register_unknown("AVC" , types::AVC); r.register_unknown("AVC" , types::AVC);
r.register_unknown("DOA" , types::DOA); r.register_unknown("DOA" , types::DOA);
r.register_unknown("TA" , types::TA); r.register_unknown("TA" , types::TA);
r.register_unknown("DLV" , types::DLV); r.register_known::<structs::DLV>();
r.register_known::<structs::ALIAS>(); r.register_known::<structs::ALIAS>();
// "ALL" could be an alias for the ANY type? // "ALL" could be an alias for the ANY type?

View File

@ -317,9 +317,7 @@ pub struct SSHFP {
fingerprint: HexRemainingBlob, fingerprint: HexRemainingBlob,
} }
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] pub use super::weird_structs::IPSECKEY;
// #[RRClass(?)]
// pub struct IPSECKEY;
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(ANY)] #[RRClass(ANY)]
@ -351,9 +349,11 @@ pub struct DNSKEY {
public_key: Base64RemainingBlob, public_key: Base64RemainingBlob,
} }
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)] #[RRClass(IN)]
// pub struct DHCID; pub struct DHCID {
content: Base64RemainingBlob,
}
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(ANY)] #[RRClass(ANY)]
@ -375,13 +375,25 @@ pub struct NSEC3PARAM {
salt: HexShortBlob, salt: HexShortBlob,
} }
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)] #[RRClass(ANY)]
// pub struct TLSA; pub struct TLSA {
// TODO: support acronyms from https://tools.ietf.org/html/rfc7218
cert_usage: u8,
selector: u8,
matching_type: u8,
data: HexRemainingBlob,
}
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)] #[RRClass(ANY)]
// pub struct SMIMEA; pub struct SMIMEA {
// TODO: support acronyms from https://tools.ietf.org/html/rfc7218
cert_usage: u8,
selector: u8,
matching_type: u8,
data: HexRemainingBlob,
}
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)] // #[RRClass(?)]
@ -407,9 +419,11 @@ pub struct NSEC3PARAM {
// #[RRClass(?)] // #[RRClass(?)]
// pub struct CDNSKEY; // pub struct CDNSKEY;
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)] #[RRClass(ANY)]
// pub struct OPENPGPKEY; pub struct OPENPGPKEY {
public_key: Base64RemainingBlob,
}
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] // #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)] // #[RRClass(?)]
@ -453,21 +467,41 @@ pub struct SPF {
// #[RRClass(?)] // #[RRClass(?)]
// pub struct LP; // pub struct LP;
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)] #[RRClass(ANY)]
// pub struct EUI48; pub struct EUI48 {
addr: EUI48Addr,
}
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)] #[RRClass(ANY)]
// pub struct EUI64; pub struct EUI64 {
addr: EUI64Addr
}
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)] #[RRClass(ANY)]
// pub struct TKEY; pub struct TKEY {
algorithm: DnsName,
inception: Time,
expiration: Time,
mode: u16,
error: u16,
key: Base64LongBlob,
other: Base64LongBlob,
}
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)] #[RRClass(ANY)]
// pub struct TSIG; pub struct TSIG {
algorithm: DnsName,
signed: Time48,
fudge: u16,
mac: Base64LongBlob,
original_id: u16,
error: u16,
other: Base64LongBlob,
}
// QTYPEs: IXFR, AXFR, MAILB, MAILA, ANY // QTYPEs: IXFR, AXFR, MAILB, MAILA, ANY
@ -490,8 +524,14 @@ pub struct CAA {
// pub struct AVC; // pub struct AVC;
// pub struct DOA; // pub struct DOA;
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// pub struct DLV; #[RRClass(ANY)]
pub struct DLV {
key_tag: u16,
algorithm: u8,
digest_type: u8,
digest: HexRemainingBlob,
}
// powerdns // powerdns
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)] #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]

View File

@ -2,11 +2,11 @@ use bytes::{Bytes, Buf, BufMut};
use errors::*; use errors::*;
use common_types::*; use common_types::*;
use failure::ResultExt; use failure::ResultExt;
use ser::packet::{DnsPacketData, DnsPacketWriteContext}; use ser::packet::{DnsPacketData, DnsPacketWriteContext, remaining_bytes};
use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext}; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext};
use std::fmt; use std::fmt;
use std::io::Read; use std::io::Read;
use std::net::Ipv6Addr; use std::net::{Ipv4Addr, Ipv6Addr};
// deriving RRData will add a unit test to make sure the type is // deriving RRData will add a unit test to make sure the type is
// registered; there must be a records::types::$name `Type` constant // registered; there must be a records::types::$name `Type` constant
@ -30,7 +30,7 @@ impl DnsPacketData for LOC {
} else { } else {
Ok(LOC::UnknownVersion{ Ok(LOC::UnknownVersion{
version: version, version: version,
data: ::ser::packet::remaining_bytes(data), data: remaining_bytes(data),
}) })
} }
} }
@ -182,3 +182,131 @@ impl DnsTextData for A6 {
Ok(()) Ok(())
} }
} }
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum IpsecKeyGateway {
None,
Ipv4(Ipv4Addr),
Ipv6(Ipv6Addr),
Name(DnsName),
}
#[derive(Clone, PartialEq, Eq, Debug, RRData)]
#[RRClass(ANY)]
pub enum IPSECKEY {
Known{
precedence: u8,
algorithm: u8,
gateway: IpsecKeyGateway,
public_key: Base64RemainingBlob,
},
UnknownGateway{
precedence: u8,
gateway_type: u8,
algorithm: u8,
// length of gateway is unknown, can't split gateway and public key
remaining: Bytes,
}
}
impl DnsPacketData for IPSECKEY {
fn deserialize(data: &mut ::std::io::Cursor<Bytes>) -> Result<Self> {
let precedence = u8::deserialize(data)?;
let gateway_type = u8::deserialize(data)?;
let algorithm = u8::deserialize(data)?;
let gateway = match gateway_type {
0 => IpsecKeyGateway::None,
1 => IpsecKeyGateway::Ipv4(Ipv4Addr::deserialize(data)?),
2 => IpsecKeyGateway::Ipv6(Ipv6Addr::deserialize(data)?),
3 => IpsecKeyGateway::Name(DnsName::deserialize(data)?),
_ => return Ok(IPSECKEY::UnknownGateway{
precedence,
gateway_type,
algorithm,
remaining: remaining_bytes(data),
}),
};
Ok(IPSECKEY::Known{
precedence,
algorithm,
gateway,
public_key: Base64RemainingBlob::deserialize(data)?,
})
}
fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec<u8>) -> Result<()> {
match *self {
IPSECKEY::Known{precedence, algorithm, ref gateway, ref public_key} => {
packet.reserve(3);
packet.put_u8(precedence);
let gateway_type: u8 = match *gateway {
IpsecKeyGateway::None => 0,
IpsecKeyGateway::Ipv4(_) => 1,
IpsecKeyGateway::Ipv6(_) => 2,
IpsecKeyGateway::Name(_) => 3,
};
packet.put_u8(gateway_type);
packet.put_u8(algorithm);
match *gateway {
IpsecKeyGateway::None => (),
IpsecKeyGateway::Ipv4(ref a) => a.serialize(context, packet)?,
IpsecKeyGateway::Ipv6(ref a) => a.serialize(context, packet)?,
IpsecKeyGateway::Name(ref n) => n.serialize(context, packet)?,
};
public_key.serialize(context, packet)?;
},
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(())
}
}
impl DnsTextData for IPSECKEY {
fn dns_parse(context: &DnsTextContext, data: &mut &str) -> Result<Self> {
let precedence = u8::dns_parse(context, data)?;
let gateway_type = u8::dns_parse(context, data)?;
let algorithm = u8::dns_parse(context, data)?;
let gateway = match gateway_type {
0 => IpsecKeyGateway::None,
1 => IpsecKeyGateway::Ipv4(Ipv4Addr::dns_parse(context, data)?),
2 => IpsecKeyGateway::Ipv6(Ipv6Addr::dns_parse(context, data)?),
3 => IpsecKeyGateway::Name(DnsName::dns_parse(context, data)?),
_ => bail!("unknown gateway type {} for IPSECKEY", gateway_type),
};
Ok(IPSECKEY::Known{
precedence,
algorithm,
gateway,
public_key: Base64RemainingBlob::dns_parse(context, data)?,
})
}
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
match *self {
IPSECKEY::Known{precedence, algorithm, ref gateway, ref public_key} => {
let gateway_type: u8 = match *gateway {
IpsecKeyGateway::None => 0,
IpsecKeyGateway::Ipv4(_) => 1,
IpsecKeyGateway::Ipv6(_) => 2,
IpsecKeyGateway::Name(_) => 3,
};
write!(f, "{} {} {}", precedence, gateway_type, algorithm)?;
match *gateway {
IpsecKeyGateway::None => (),
IpsecKeyGateway::Ipv4(ref a) => a.dns_format(f)?,
IpsecKeyGateway::Ipv6(ref a) => a.dns_format(f)?,
IpsecKeyGateway::Name(ref n) => n.dns_format(f)?,
};
public_key.dns_format(f)?;
Ok(())
},
IPSECKEY::UnknownGateway{..} => Err(fmt::Error),
}
}
}

View File

@ -30,14 +30,18 @@ pub fn remaining_bytes(data: &mut Cursor<Bytes>) -> Bytes {
result result
} }
pub fn get_blob(data: &mut Cursor<Bytes>, len: usize) -> Result<Bytes> {
check_enough_data!(data, len, "blob content");
let pos = data.position() as usize;
let blob = data.get_ref().slice(pos, pos + len);
data.advance(len);
Ok(blob)
}
pub fn short_blob(data: &mut Cursor<Bytes>) -> Result<Bytes> { pub fn short_blob(data: &mut Cursor<Bytes>) -> Result<Bytes> {
check_enough_data!(data, 1, "short blob length"); check_enough_data!(data, 1, "short blob length");
let blob_len = data.get_u8() as usize; let blob_len = data.get_u8() as usize;
check_enough_data!(data, blob_len, "short blob content"); get_blob(data, blob_len)
let pos = data.position() as usize;
let blob = data.get_ref().slice(pos, pos + blob_len);
data.advance(blob_len);
Ok(blob)
} }
pub fn write_short_blob(data: &[u8], packet: &mut Vec<u8>) -> Result<()> { pub fn write_short_blob(data: &[u8], packet: &mut Vec<u8>) -> Result<()> {