mod powerdns_tests; use crate::common_types::classes; use crate::errors::*; use crate::records::structs; 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(data: &'static [u8]) -> Result where T: StaticRRData, { let mut data = Cursor::new(Bytes::from_static(data)); let result = T::deserialize_rr_data(3600, classes::IN, T::TYPE, &mut data)?; failure::ensure!(!data.has_remaining(), "rrdata not read completely"); Ok(result) } fn rrdata_parse(data: &str) -> Result where 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)) } fn check(txt: &str, data: &'static [u8]) -> Result<()> where 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")?; failure::ensure!(d1 == d2, "decoded data not equal: {:?} != {:?}", d1, d2); Ok(()) } fn check2(txt: &str, data: &'static [u8], canon: &str) -> Result<()> where 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")?; failure::ensure!(d1 == d2, "decoded data not equal: {:?} != {:?}", d1, d2); let d1_text = d1.text().unwrap(); let d2_text = d2.text().unwrap(); let canon_text = (T::NAME.to_owned(), canon.into()); 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(()) } #[test] fn test_a() { check::("127.0.0.1", b"\x7f\x00\x00\x01").unwrap(); } #[test] fn test_mx() { check::("10 mx.rec.test.", b"\x00\x0a\x02mx\x03rec\x04test\x00").unwrap(); } fn test_txt_for() where T: StaticRRData + fmt::Debug + PartialEq, { // at least one "segment" (which could be empty) check2::(r#" "" "#, b"", r#""""#).unwrap_err(); check2::(r#""#, b"\x00", r#""""#).unwrap_err(); // one empty segment check2::(r#" "" "#, b"\x00", r#""""#).unwrap(); // one segment check::(r#" "foo" "#, b"\x03foo").unwrap(); // two segments check::(r#" "foo" "bar!" "#, b"\x03foo\x04bar!").unwrap(); // segment with too many bytes in text form { let mut s = String::new(); s.push('"'); for _ in 0..256 { s.push('a'); } s.push('"'); rrdata_parse::(&s).unwrap_err(); } } #[test] fn test_txt() { test_txt_for::(); } #[test] fn test_ds() { check::(" 1 2 3 ", b"\x00\x01\x02\x03").unwrap(); check::(" 1 2 3 abcd", b"\x00\x01\x02\x03\xab\xcd").unwrap(); check::(" 1 2 3 a b c d", b"\x00\x01\x02\x03\xab\xcd").unwrap(); } #[test] fn test_nsec() { check::("foo.bar. ", b"\x03foo\x03bar\x00").unwrap(); check::("foo.bar. A NS ", b"\x03foo\x03bar\x00\x00\x01\x60").unwrap(); check::( "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::( "foo.bar. A NS TYPE256 TYPE65280 ", b"\x03foo\x03bar\x00\x00\x01\x60\x01\x01\x80\xff\x01\x80", ) .unwrap(); } #[test] fn test_dnskey() { check::("256 2 3", b"\x01\x00\x02\x03").unwrap(); check::("256 2 3 /w==", b"\x01\x00\x02\x03\xff").unwrap(); check::("256 2 3 /w ==", b"\x01\x00\x02\x03\xff").unwrap(); } #[test] fn test_nsec3() { check::("1 2 300 - vs", b"\x01\x02\x01\x2c\x00\x01\xff").unwrap(); check::( "1 2 300 - vs A NS", b"\x01\x02\x01\x2c\x00\x01\xff\x00\x01\x60", ) .unwrap(); check::( "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::("1 2 300 - v").unwrap_err(); rrdata_parse::("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(); } #[test] fn test_nsec3param() { check::("1 2 300 -", b"\x01\x02\x01\x2c\x00").unwrap(); check::("1 2 300 ab", b"\x01\x02\x01\x2c\x01\xab").unwrap(); // `salt` hex string must not contain spaces rrdata_parse::("1 2 300 a b").unwrap_err(); } #[test] fn test_spf() { test_txt_for::(); } #[test] fn test_apl() { check::("1:0.0.0.0/0", b"\x00\x01\x00\x00").unwrap(); check::("!1:0.0.0.0/0", b"\x00\x01\x00\x80").unwrap(); check::("2:::/0", b"\x00\x02\x00\x00").unwrap(); check::("!2:::/0", b"\x00\x02\x00\x80").unwrap(); check::( "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(); }