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 errors::*;
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 std::fmt;
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
// base64 encoding for text representation, whitespace allowed, padding
// required.
@ -95,7 +146,11 @@ impl DnsTextData for Base64RemainingBlob {
}
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 classes;
pub mod name;
pub mod text;
pub mod types;
mod eui;
mod nsec;
mod nxt;
mod time;
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::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::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.
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Time(u32);
pub struct Time(pub u32);
impl DnsPacketData for Time {
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 {
fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self> {
let field = next_field(data)?;
let epoch = field.parse::<u32>();
if field.len() == 14 && epoch.is_err() {
unimplemented!()
let tm = field.parse::<Tm>()?;
Ok(Time(tm.epoch() as u32))
} else {
Ok(Time(epoch?))
}
@ -36,3 +104,39 @@ impl DnsTextData for Time {
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,
b"\xfflong record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\x0a2222222222",
);
/* autosplitting not supported
// autosplitting not supported
/*
check(types::TXT,
"\"long record test 11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111112222222222\"",
Some("\"long record test 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111\" \"2222222222\""),
@ -514,7 +515,8 @@ fn test_IPSECKEY() {
fn test_RRSIG() {
check(types::RRSIG,
"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",
);
}

View File

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

View File

@ -317,9 +317,7 @@ pub struct SSHFP {
fingerprint: HexRemainingBlob,
}
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)]
// pub struct IPSECKEY;
pub use super::weird_structs::IPSECKEY;
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(ANY)]
@ -351,9 +349,11 @@ pub struct DNSKEY {
public_key: Base64RemainingBlob,
}
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)]
// pub struct DHCID;
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(IN)]
pub struct DHCID {
content: Base64RemainingBlob,
}
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(ANY)]
@ -375,13 +375,25 @@ pub struct NSEC3PARAM {
salt: HexShortBlob,
}
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)]
// pub struct TLSA;
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(ANY)]
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)]
// #[RRClass(?)]
// pub struct SMIMEA;
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(ANY)]
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)]
// #[RRClass(?)]
@ -407,9 +419,11 @@ pub struct NSEC3PARAM {
// #[RRClass(?)]
// pub struct CDNSKEY;
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)]
// pub struct OPENPGPKEY;
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(ANY)]
pub struct OPENPGPKEY {
public_key: Base64RemainingBlob,
}
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)]
@ -453,21 +467,41 @@ pub struct SPF {
// #[RRClass(?)]
// pub struct LP;
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)]
// pub struct EUI48;
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(ANY)]
pub struct EUI48 {
addr: EUI48Addr,
}
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)]
// pub struct EUI64;
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(ANY)]
pub struct EUI64 {
addr: EUI64Addr
}
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// #[RRClass(?)]
// pub struct TKEY;
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(ANY)]
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)]
// #[RRClass(?)]
// pub struct TSIG;
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(ANY)]
pub struct TSIG {
algorithm: DnsName,
signed: Time48,
fudge: u16,
mac: Base64LongBlob,
original_id: u16,
error: u16,
other: Base64LongBlob,
}
// QTYPEs: IXFR, AXFR, MAILB, MAILA, ANY
@ -490,8 +524,14 @@ pub struct CAA {
// pub struct AVC;
// pub struct DOA;
// #[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
// pub struct DLV;
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]
#[RRClass(ANY)]
pub struct DLV {
key_tag: u16,
algorithm: u8,
digest_type: u8,
digest: HexRemainingBlob,
}
// powerdns
#[derive(Clone, PartialEq, Eq, Debug, DnsPacketData, DnsTextData, RRData)]

View File

@ -2,11 +2,11 @@ use bytes::{Bytes, Buf, BufMut};
use errors::*;
use common_types::*;
use failure::ResultExt;
use ser::packet::{DnsPacketData, DnsPacketWriteContext};
use ser::packet::{DnsPacketData, DnsPacketWriteContext, remaining_bytes};
use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext};
use std::fmt;
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
// registered; there must be a records::types::$name `Type` constant
@ -30,7 +30,7 @@ impl DnsPacketData for LOC {
} else {
Ok(LOC::UnknownVersion{
version: version,
data: ::ser::packet::remaining_bytes(data),
data: remaining_bytes(data),
})
}
}
@ -182,3 +182,131 @@ impl DnsTextData for A6 {
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
}
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> {
check_enough_data!(data, 1, "short blob length");
let blob_len = data.get_u8() as usize;
check_enough_data!(data, blob_len, "short blob content");
let pos = data.position() as usize;
let blob = data.get_ref().slice(pos, pos + blob_len);
data.advance(blob_len);
Ok(blob)
get_blob(data, blob_len)
}
pub fn write_short_blob(data: &[u8], packet: &mut Vec<u8>) -> Result<()> {