From adb00b77b69c2ca0dd86045095c9468f93641fde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Sun, 11 Feb 2018 11:13:04 +0100 Subject: [PATCH] step (SIG) --- lib/dnsbox-base/src/common_types/mod.rs | 4 +- .../common_types/name/name_packet_parser.rs | 2 +- lib/dnsbox-base/src/common_types/sig.rs | 52 +++++++++++++++++++ lib/dnsbox-base/src/common_types/time/mod.rs | 36 ++++++++++++- lib/dnsbox-base/src/records/powerdns_tests.rs | 40 ++++++++++++++ lib/dnsbox-base/src/records/structs.rs | 6 +-- 6 files changed, 133 insertions(+), 7 deletions(-) create mode 100644 lib/dnsbox-base/src/common_types/sig.rs diff --git a/lib/dnsbox-base/src/common_types/mod.rs b/lib/dnsbox-base/src/common_types/mod.rs index f17db6f..8aef4f4 100644 --- a/lib/dnsbox-base/src/common_types/mod.rs +++ b/lib/dnsbox-base/src/common_types/mod.rs @@ -6,6 +6,7 @@ pub mod types; mod eui; mod nsec; mod nxt; +mod sig; mod time; mod uri; @@ -15,7 +16,8 @@ pub use self::eui::{EUI48Addr, EUI64Addr}; pub use self::name::{DnsName, DnsCanonicalName, DnsCompressedName}; pub use self::nsec::{NsecTypeBitmap, NextHashedOwnerName}; pub use self::nxt::NxtTypeBitmap; +pub use self::sig::OptionalTTL; pub use self::text::{ShortText, LongText, UnquotedShortText, RemainingText}; -pub use self::time::{Time, Time48}; +pub use self::time::{Time, TimeStrict, Time48}; pub use self::types::Type; pub use self::uri::UriText; diff --git a/lib/dnsbox-base/src/common_types/name/name_packet_parser.rs b/lib/dnsbox-base/src/common_types/name/name_packet_parser.rs index 63d7f08..600f77b 100644 --- a/lib/dnsbox-base/src/common_types/name/name_packet_parser.rs +++ b/lib/dnsbox-base/src/common_types/name/name_packet_parser.rs @@ -78,7 +78,7 @@ pub fn deserialize_name(data: &mut Cursor, accept_compressed: bool) -> Re total_len += 1 + label_len; // max len 255, but there also needs to be an empty label at the end if total_len > 254 { bail!{"DNS name too long"} } - check_enough_data!(data, (label_len), "DnsName label"); + check_enough_data!(data, label_len, "DnsName label"); data.advance(label_len); } } diff --git a/lib/dnsbox-base/src/common_types/sig.rs b/lib/dnsbox-base/src/common_types/sig.rs new file mode 100644 index 0000000..c7a2c86 --- /dev/null +++ b/lib/dnsbox-base/src/common_types/sig.rs @@ -0,0 +1,52 @@ +use bytes::Bytes; +use errors::*; +use failure::Fail; +use ser::packet::{DnsPacketData, DnsPacketWriteContext}; +use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field}; +use std::fmt; +use std::io::Cursor; + +/// Optional TTL +/// +/// Always present in wire format, but optional in zone; defaults to TTL +/// of the RR. +/// +/// Must be followed by a field that doesn't accept any unsigned 32-bit +/// integer as text representation (like `TimeStrict` for `SIG`). +/// +/// If field is missing in zone representation the context must provide +/// a TTL. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct OptionalTTL(pub u32); + +impl DnsPacketData for OptionalTTL { + fn deserialize(data: &mut Cursor) -> Result { + Ok(OptionalTTL(DnsPacketData::deserialize(data)?)) + } + + fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec) -> Result<()> { + self.0.serialize(context, packet) + } +} + +impl DnsTextData for OptionalTTL { + fn dns_parse(context: &DnsTextContext, data: &mut &str) -> ::errors::Result { + let mut data_found = *data; + let field = next_field(&mut data_found)?; + match field.parse::() { + Ok(ttl) => { + *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"))? + )) + }, + } + } + + fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { + self.0.dns_format(f) + } +} diff --git a/lib/dnsbox-base/src/common_types/time/mod.rs b/lib/dnsbox-base/src/common_types/time/mod.rs index 527483f..4177363 100644 --- a/lib/dnsbox-base/src/common_types/time/mod.rs +++ b/lib/dnsbox-base/src/common_types/time/mod.rs @@ -7,9 +7,12 @@ use std::io::Cursor; mod epoch; -/// timestamp in seconds since epoch (ignoring leap seconds) +/// Timestamp in seconds since epoch (ignoring leap seconds) /// /// Is expected to wrap around. +/// +/// Accepts both YYYYMMDDHHmmSS or an unsigned 32-bit integer in text +/// form; formatted as YYYYMMDDHHmmSS for now. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Time(pub u32); @@ -37,7 +40,36 @@ 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()?) - // write!(f, "{}", self.0) + } +} + +/// Timestamp in seconds since epoch (ignoring leap seconds) +/// +/// Is expected to wrap around. +/// +/// Accepts only YYYYMMDDHHmmSS. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct TimeStrict(pub u32); + +impl DnsPacketData for TimeStrict { + fn deserialize(data: &mut Cursor) -> Result { + Ok(TimeStrict(DnsPacketData::deserialize(data)?)) + } + + fn serialize(&self, context: &mut DnsPacketWriteContext, packet: &mut Vec) -> Result<()> { + self.0.serialize(context, packet) + } +} + +impl DnsTextData for TimeStrict { + fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result { + let field = next_field(data)?; + let tm = epoch::Tm::parse_YYYYMMDDHHmmSS(field)?; + Ok(TimeStrict(tm.epoch() as u32)) + } + + fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { + epoch::Tm::from_epoch(self.0 as i64).unwrap().format_YYYYMMDDHHmmSS(&mut*f.format_field()?) } } diff --git a/lib/dnsbox-base/src/records/powerdns_tests.rs b/lib/dnsbox-base/src/records/powerdns_tests.rs index 5956100..f4ad18f 100644 --- a/lib/dnsbox-base/src/records/powerdns_tests.rs +++ b/lib/dnsbox-base/src/records/powerdns_tests.rs @@ -80,6 +80,11 @@ fn serialized_answer(rrdata: Box) -> Result { 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 { + check(q, canonic, None, raw); + } + let canonic = canonic.unwrap_or(text_input); let mut context = text::DnsTextContext::new(); @@ -144,6 +149,9 @@ fn test_NS() { ); } +// missing test_MD +// missing test_MF + #[test] fn test_CNAME() { // local alias @@ -200,6 +208,9 @@ fn test_SOA() { ); } +// missing test_MB +// missing test_MG + #[test] fn test_MR() { // BROKEN TESTS (2) (deprecated) @@ -257,6 +268,8 @@ fn test_HINFO() { ); } +// missing test_MINFO + #[test] fn test_MX() { // local name @@ -348,6 +361,20 @@ fn test_AFSDB() { ); } +// missing test_RT +// missing test_NSAP_PTR + +#[test] +fn test_SIG() { + // not supported by powerdns + // last ttl is 3600 in this test environment + check(types::SIG, + "NXT 1 2 19970102030405 19961211100908 2143 foo.nil. AIYADP8d3zYNyQwW2EM4wXVFdslEJcUx/fxkfBeH1El4ixPFhpfHFElxbvKoWmvjDTCm fiYy2X+8XpFjwICHc398kzWsTMKlxovpz2FnCTM=", + Some("NXT 1 2 3600 19970102030405 19961211100908 2143 foo.nil. AIYADP8d3zYNyQwW2EM4wXVFdslEJcUx/fxkfBeH1El4ixPFhpfHFElxbvKoWmvjDTCmfiYy2X+8XpFjwICHc398kzWsTMKlxovpz2FnCTM="), + b"\x00\x1e\x01\x02\x00\x00\x0e\x10\x32\xcb\x25\xa5\x32\xae\x88\x44\x08\x5f\x03foo\x03nil\x00\x00\x86\x00\x0c\xff\x1d\xdf\x36\x0d\xc9\x0c\x16\xd8\x43\x38\xc1\x75\x45\x76\xc9\x44\x25\xc5\x31\xfd\xfc\x64\x7c\x17\x87\xd4\x49\x78\x8b\x13\xc5\x86\x97\xc7\x14\x49\x71\x6e\xf2\xa8\x5a\x6b\xe3\x0d\x30\xa6\x7e\x26\x32\xd9\x7f\xbc\x5e\x91\x63\xc0\x80\x87\x73\x7f\x7c\x93\x35\xac\x4c\xc2\xa5\xc6\x8b\xe9\xcf\x61\x67\x09\x33" + ); +} + #[test] fn test_KEY() { // deprecated (and i don't know what i am doing wrong) @@ -358,6 +385,9 @@ fn test_KEY() { ); } +// missing test_PX +// missing test_GPOS + #[test] fn test_AAAA() { check(types::AAAA, @@ -401,6 +431,8 @@ fn test_LOC() { ); } +// missing test_NXT + #[test] fn test_SRV() { // local name @@ -456,6 +488,9 @@ fn test_CERT() { ); } +// missing test_A6 +// missing test_DNAME + #[test] fn test_DS() { check(types::DS, @@ -700,6 +735,11 @@ fn test_SMIMEA() { ); } +// missing test_NINFO +// missing test_RKEY +// missing test_CDS +// missing test_CDNSKEY + #[test] fn test_OPENPGPKEY() { check(types::OPENPGPKEY, diff --git a/lib/dnsbox-base/src/records/structs.rs b/lib/dnsbox-base/src/records/structs.rs index edc78c8..602a473 100644 --- a/lib/dnsbox-base/src/records/structs.rs +++ b/lib/dnsbox-base/src/records/structs.rs @@ -182,9 +182,9 @@ pub struct SIG { // RFC says this can be omitted in text form if it is the same as // the TTL on the SIG record. not supported to be omitted here // (TODO?). - original_ttl: u32, - signature_expiration: u32, - signature_inception: u32, + original_ttl: OptionalTTL, + signature_expiration: TimeStrict, + signature_inception: TimeStrict, key_tag: u16, signers_name: DnsCanonicalName, signature: Base64RemainingBlob,