use dns_message_parser::question::{QClass, QType, Question};
use dns_message_parser::rr::edns::{Cookie, EDNSOption, Padding, ECS};
use dns_message_parser::rr::{
APItem, Address, Class, ISDNAddress, PSDNAddress, SSHFPAlgorithm, SSHFPType, A, AAAA, APL,
CNAME, DNAME, EID, EUI48, EUI64, GPOS, HINFO, ISDN, KX, L32, L64, LP, MB, MD, MF, MG, MINFO,
MR, MX, NID, NIMLOC, NS, OPT, PTR, PX, RP, RR, RT, SA, SOA, SRV, SSHFP, TXT, URI, X25,
};
use dns_message_parser::{Dns, DomainName, Flags, Opcode, RCode};
use std::convert::TryFrom;
use std::fmt::Display;
fn check_output<T>(t: &T, output: &str)
where
T: Display,
{
let format = format!("{}", t);
assert_eq!(&format, output);
}
#[test]
fn rr_a() {
let domain_name = DomainName::try_from("example.org").unwrap();
let ipv4_addr = "10.0.0.1".parse().unwrap();
let rr = RR::A(A {
domain_name,
ttl: 100,
ipv4_addr,
});
check_output(&rr, "example.org. 100 IN A 10.0.0.1");
}
#[test]
fn rr_ns() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CS;
let ns_d_name = DomainName::try_from("ns1.example.org").unwrap();
let rr = RR::NS(NS {
domain_name,
ttl: 100,
class,
ns_d_name,
});
check_output(&rr, "example.org. 100 CS NS ns1.example.org.");
}
#[test]
fn rr_cname() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::IN;
let c_name = DomainName::try_from("example.org").unwrap();
let rr = RR::CNAME(CNAME {
domain_name,
ttl: 100,
class,
c_name,
});
check_output(&rr, "example.org. 100 IN CNAME example.org.");
}
#[test]
fn rr_soa() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CH;
let m_name = DomainName::try_from("ns1.example.org.").unwrap();
let r_name = DomainName::try_from("admin.example.org.").unwrap();
let serial = 1;
let refresh = 10800;
let retry = 3600;
let expire = 604800;
let min_ttl = 3600;
let rr = RR::SOA(SOA {
domain_name,
ttl: 100,
class,
m_name,
r_name,
serial,
refresh,
retry,
expire,
min_ttl,
});
check_output(
&rr,
"example.org. 100 CH SOA ns1.example.org. admin.example.org. (1 10800 3600 604800 3600)",
);
}
#[test]
fn rr_mb() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::HS;
let mad_name = DomainName::try_from("mail.example.org").unwrap();
let rr = RR::MB(MB {
domain_name,
ttl: 100,
class,
mad_name,
});
check_output(&rr, "example.org. 100 HS MB mail.example.org.");
}
#[test]
fn rr_md() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::HS;
let mad_name = DomainName::try_from("mail.example.org").unwrap();
let rr = RR::MD(MD {
domain_name,
ttl: 100,
class,
mad_name,
});
check_output(&rr, "example.org. 100 HS MD mail.example.org.");
}
#[test]
fn rr_mf() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::HS;
let mad_name = DomainName::try_from("mail.example.org").unwrap();
let rr = RR::MF(MF {
domain_name,
ttl: 100,
class,
mad_name,
});
check_output(&rr, "example.org. 100 HS MF mail.example.org.");
}
#[test]
fn rr_mg() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::IN;
let mgm_name = DomainName::try_from("mail.example.org").unwrap();
let rr = RR::MG(MG {
domain_name,
ttl: 100,
class,
mgm_name,
});
check_output(&rr, "example.org. 100 IN MG mail.example.org.");
}
#[test]
fn rr_mr() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CS;
let new_name = DomainName::try_from("mail.example.org").unwrap();
let rr = RR::MR(MR {
domain_name,
ttl: 100,
class,
new_name,
});
check_output(&rr, "example.org. 100 CS MR mail.example.org.");
}
#[test]
fn rr_ptr() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CH;
let ptr_d_name = DomainName::try_from("example.org").unwrap();
let rr = RR::PTR(PTR {
domain_name,
ttl: 100,
class,
ptr_d_name,
});
check_output(&rr, "example.org. 100 CH PTR example.org.");
}
#[test]
fn rr_hinfo() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::HS;
let cpu = String::from("TEST");
let os = String::from("Linux");
let rr = RR::HINFO(HINFO {
domain_name,
ttl: 100,
class,
cpu,
os,
});
check_output(&rr, "example.org. 100 HS HINFO TEST Linux");
}
#[test]
fn rr_minfo() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::IN;
let r_mail_bx = DomainName::try_from("admin.example.org").unwrap();
let e_mail_bx = DomainName::try_from("error.example.org").unwrap();
let rr = RR::MINFO(MINFO {
domain_name,
ttl: 100,
class,
r_mail_bx,
e_mail_bx,
});
check_output(
&rr,
"example.org. 100 IN MINFO admin.example.org. error.example.org.",
);
}
#[test]
fn rr_mx() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CS;
let preference = 10;
let exchange = DomainName::try_from("mail.example.org").unwrap();
let rr = RR::MX(MX {
domain_name,
ttl: 100,
class,
preference,
exchange,
});
check_output(&rr, "example.org. 100 CS MX 10 mail.example.org.");
}
#[test]
fn rr_txt() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CH;
let string = String::from("Text");
let rr = RR::TXT(TXT {
domain_name,
ttl: 100,
class,
string,
});
check_output(&rr, "example.org. 100 CH TXT Text");
}
#[test]
fn rr_rp() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::HS;
let mbox_dname = DomainName::try_from("admin.example.org").unwrap();
let txt_dname = DomainName::try_from("error.example.org").unwrap();
let rr = RR::RP(RP {
domain_name,
ttl: 100,
class,
mbox_dname,
txt_dname,
});
check_output(
&rr,
"example.org. 100 HS RP admin.example.org. error.example.org.",
);
}
#[test]
fn rr_x25() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::IN;
let psdn_address = PSDNAddress::try_from(String::from("311061700956")).unwrap();
let rr = RR::X25(X25 {
domain_name,
ttl: 100,
class,
psdn_address,
});
check_output(&rr, "example.org. 100 IN X25 311061700956");
}
#[test]
fn rr_isdn_1() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CS;
let isdn_address = ISDNAddress::try_from(String::from("150862028003217")).unwrap();
let sa = Some(SA::try_from(String::from("004")).unwrap());
let rr = RR::ISDN(ISDN {
domain_name,
ttl: 100,
class,
isdn_address,
sa,
});
check_output(&rr, "example.org. 100 CS ISDN 150862028003217 004");
}
#[test]
fn rr_isdn_2() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CH;
let isdn_address = ISDNAddress::try_from(String::from("150862028003217")).unwrap();
let sa = None;
let rr = RR::ISDN(ISDN {
domain_name,
ttl: 100,
class,
isdn_address,
sa,
});
check_output(&rr, "example.org. 100 CH ISDN 150862028003217");
}
#[test]
fn rr_rt() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::HS;
let preference = 2;
let intermediate_host = DomainName::try_from("relay.example.org").unwrap();
let rr = RR::RT(RT {
domain_name,
ttl: 100,
class,
preference,
intermediate_host,
});
check_output(&rr, "example.org. 100 HS RT 2 relay.example.org.");
}
#[test]
fn rr_px() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::IN;
let preference = 10;
let map822 = DomainName::try_from("example.org").unwrap();
let mapx400 = DomainName::try_from("px400.example.org").unwrap();
let rr = RR::PX(PX {
domain_name,
ttl: 100,
class,
preference,
map822,
mapx400,
});
check_output(
&rr,
"example.org. 100 IN PX 10 example.org. px400.example.org.",
);
}
#[test]
fn rr_gpos() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CS;
let longitude = String::from("-32.0082");
let latitude = String::from("120.0050");
let altitude = String::from("10.0");
let rr = RR::GPOS(GPOS {
domain_name,
ttl: 100,
class,
longitude,
latitude,
altitude,
});
check_output(&rr, "example.org. 100 CS GPOS -32.0082 120.0050 10.0");
}
#[test]
fn rr_aaaa() {
let domain_name = DomainName::try_from("example.org").unwrap();
let ipv6_addr = "::1".parse().unwrap();
let rr = RR::AAAA(AAAA {
domain_name,
ttl: 100,
ipv6_addr,
});
check_output(&rr, "example.org. 100 IN AAAA ::1");
}
#[test]
fn rr_eid() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CH;
let data = Vec::from(&b"\xe3\x2c\x6f\x78\x16\x3a\x93\x48"[..]);
let rr = RR::EID(EID {
domain_name,
ttl: 100,
class,
data,
});
check_output(&rr, "example.org. 100 CH EID e32c6f78163a9348");
}
#[test]
fn rr_nimloc() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::HS;
let data = Vec::from(&b"\x32\x25\x1a\x03\x00\x67"[..]);
let rr = RR::NIMLOC(NIMLOC {
domain_name,
ttl: 100,
class,
data,
});
check_output(&rr, "example.org. 100 HS NIMLOC 32251a030067");
}
#[test]
fn rr_srv() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::IN;
let priority = 0;
let weight = 1;
let port = 80;
let target = DomainName::try_from("srv.example.org").unwrap();
let rr = RR::SRV(SRV {
domain_name,
ttl: 100,
class,
priority,
weight,
port,
target,
});
check_output(&rr, "example.org. 100 IN SRV 0 1 80 srv.example.org.");
}
#[test]
fn rr_kx() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CS;
let preference = 10;
let exchanger = DomainName::try_from("kx.example.org").unwrap();
let rr = RR::KX(KX {
domain_name,
ttl: 100,
class,
preference,
exchanger,
});
check_output(&rr, "example.org. 100 CS KX 10 kx.example.org.");
}
#[test]
fn rr_dname() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CH;
let target = DomainName::try_from("dname.example.org").unwrap();
let rr = RR::DNAME(DNAME {
domain_name,
ttl: 100,
class,
target,
});
check_output(&rr, "example.org. 100 CH DNAME dname.example.org.");
}
#[test]
fn rr_sshfp() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::HS;
let algorithm = SSHFPAlgorithm::DSS;
let type_ = SSHFPType::Sha1;
let fp = b"\x12\x34\x56\x78\x9a\xbc\xde\xf6\x78\x90\x12\x34\x56\x78\x9a\xbc\xde\xf6\x78\x90"
.to_vec();
let rr = RR::SSHFP(SSHFP {
domain_name,
ttl: 100,
class,
algorithm,
type_,
fp,
});
check_output(
&rr,
"example.org. 100 HS SSHFP DSS Sha1 123456789abcdef67890123456789abcdef67890",
);
}
#[test]
fn rr_nid() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CH;
let preference = 10;
let node_id = 0xffeeddccbbaa9988;
let rr = RR::NID(NID {
domain_name,
ttl: 100,
class,
preference,
node_id,
});
check_output(&rr, "example.org. 100 CH NID 10 ffee:ddcc:bbaa:9988");
}
#[test]
fn rr_l32() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::HS;
let preference = 10;
let locator_32 = 0x0a000001;
let rr = RR::L32(L32 {
domain_name,
ttl: 100,
class,
preference,
locator_32,
});
check_output(&rr, "example.org. 100 HS L32 10 10.0.0.1");
}
#[test]
fn rr_l64() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::IN;
let preference = 100;
let locator_64 = 0x2021222324252627;
let rr = RR::L64(L64 {
domain_name,
ttl: 200,
class,
preference,
locator_64,
});
check_output(&rr, "example.org. 200 IN L64 100 2021:2223:2425:2627");
}
#[test]
fn rr_lp() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CS;
let preference = 200;
let fqdn = DomainName::try_from("l64-subnet.example.org.").unwrap();
let rr = RR::LP(LP {
domain_name,
ttl: 100,
class,
preference,
fqdn,
});
check_output(&rr, "example.org. 100 CS LP 200 l64-subnet.example.org.");
}
#[test]
fn rr_eui48() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CS;
let eui_48 = b"\x00\x11\x22\x33\x44\x55".to_owned();
let rr = RR::EUI48(EUI48 {
domain_name,
ttl: 100,
class,
eui_48,
});
check_output(&rr, "example.org. 100 CS EUI48 00-11-22-33-44-55");
}
#[test]
fn rr_eui64() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::CH;
let eui_64 = b"\x00\x11\x22\x33\x44\x55\x66\x77".to_owned();
let rr = RR::EUI64(EUI64 {
domain_name,
ttl: 100,
class,
eui_64,
});
check_output(&rr, "example.org. 100 CH EUI64 00-11-22-33-44-55-66-77");
}
#[test]
fn rr_uri() {
let domain_name = DomainName::try_from("example.org").unwrap();
let class = Class::IN;
let uri = "https://example.org/".to_string();
let rr = RR::URI(URI {
domain_name,
ttl: 100,
class,
priority: 1,
weight: 10,
uri,
});
check_output(&rr, "example.org. 100 IN URI 1 10 https://example.org/");
}
#[test]
fn rr_opt_ecs_1() {
let address = Address::Ipv4("10.0.0.0".parse().unwrap());
let ecs = ECS::new(24, 24, address).unwrap();
let rr = RR::OPT(OPT {
requestor_payload_size: 1024,
dnssec: false,
version: 0,
extend_rcode: 0,
edns_options: vec![EDNSOption::ECS(ecs)],
});
check_output(&rr, ". OPT 1024 0 0 false ECS 24 24 10.0.0.0");
}
#[test]
fn rr_opt_ecs_2() {
let address = Address::Ipv6("10::".parse().unwrap());
let ecs = ECS::new(24, 24, address).unwrap();
let rr = RR::OPT(OPT {
requestor_payload_size: 1024,
dnssec: false,
version: 0,
extend_rcode: 0,
edns_options: vec![EDNSOption::ECS(ecs)],
});
check_output(&rr, ". OPT 1024 0 0 false ECS 24 24 10::");
}
#[test]
fn rr_opt_cookie_1() {
let client_cookie = b"\xd5\xa7\xe3\x00\x4d\x79\x05\x1e".to_owned();
let server_cookie = None;
let cookie = Cookie::new(client_cookie, server_cookie).unwrap();
let rr = RR::OPT(OPT {
requestor_payload_size: 1024,
dnssec: false,
version: 0,
extend_rcode: 0,
edns_options: vec![EDNSOption::Cookie(cookie)],
});
check_output(&rr, ". OPT 1024 0 0 false Cookie d5a7e3004d79051e");
}
#[test]
fn rr_opt_cookie_2() {
let client_cookie = b"\xd5\xa7\xe3\x00\x4d\x79\x05\x1e".to_owned();
let server_cookie =
Some(b"\x01\x00\x00\x00\x5f\xe5\xd6\xb1\x62\xda\x1b\xe3\xbc\x92\x5b\xd6".to_vec());
let cookie = Cookie::new(client_cookie, server_cookie).unwrap();
let rr = RR::OPT(OPT {
requestor_payload_size: 1024,
dnssec: false,
version: 0,
extend_rcode: 0,
edns_options: vec![EDNSOption::Cookie(cookie)],
});
check_output(
&rr,
". OPT 1024 0 0 false Cookie d5a7e3004d79051e 010000005fe5d6b162da1be3bc925bd6",
);
}
#[test]
fn rr_opt_padding() {
let padding = Padding(6);
let rr = RR::OPT(OPT {
requestor_payload_size: 1024,
dnssec: false,
version: 0,
extend_rcode: 0,
edns_options: vec![EDNSOption::Padding(padding)],
});
check_output(&rr, ". OPT 1024 0 0 false Padding 6");
}
#[test]
fn rr_apl_1() {
let domain_name = DomainName::try_from("example.org").unwrap();
let apitems = Vec::new();
let rr = RR::APL(APL {
domain_name,
ttl: 100,
apitems,
});
check_output(&rr, "example.org. 100 IN APL");
}
#[test]
fn rr_apl_2() {
let domain_name = DomainName::try_from("example.org").unwrap();
let mut apitems = Vec::new();
let address = Address::Ipv4("10.0.0.0".parse().unwrap());
let apitem = APItem::new(8, false, address).unwrap();
apitems.push(apitem);
let address = Address::Ipv4("20.0.0.0".parse().unwrap());
let apitem = APItem::new(16, false, address).unwrap();
apitems.push(apitem);
let rr = RR::APL(APL {
domain_name,
ttl: 100,
apitems,
});
check_output(&rr, "example.org. 100 IN APL 1:10.0.0.0/8 1:20.0.0.0/16");
}
#[test]
fn rr_apl_3() {
let domain_name = DomainName::try_from("example.org").unwrap();
let address = Address::Ipv6("1122:3344::".parse().unwrap());
let apitem = APItem::new(64, true, address).unwrap();
let apitems = vec![apitem];
let rr = RR::APL(APL {
domain_name,
ttl: 100,
apitems,
});
check_output(&rr, "example.org. 100 IN APL !2:1122:3344::/64");
}
#[test]
fn q_type_type() {
let q_type = QType::A;
check_output(&q_type, "A");
}
#[test]
fn q_type_q_type() {
let q_type = QType::ALL;
check_output(&q_type, "ALL");
}
#[test]
fn q_class_class() {
let q_type = QClass::IN;
check_output(&q_type, "IN");
}
#[test]
fn q_class_q_class() {
let q_type = QClass::ANY;
check_output(&q_type, "ANY");
}
#[test]
fn question() {
let domain_name = DomainName::try_from("example.org.").unwrap();
let q_class = QClass::IN;
let q_type = QType::A;
let question = Question {
domain_name,
q_class,
q_type,
};
check_output(&question, "example.org. IN A")
}
#[test]
fn flags() {
let opcode = Opcode::Query;
let rcode = RCode::NoError;
let flags = Flags {
qr: true,
opcode,
aa: true,
tc: true,
rd: true,
ra: true,
ad: true,
cd: true,
rcode,
};
check_output(&flags, "qr Query aa tc rd ra ad cd NoError");
}
#[test]
fn dns() {
let id = 10105;
let opcode = Opcode::Query;
let rcode = RCode::NoError;
let flags = Flags {
qr: false,
opcode,
aa: false,
tc: false,
rd: false,
ra: false,
ad: false,
cd: false,
rcode,
};
let questions = {
let domain_name = DomainName::try_from("cname.example.org.").unwrap();
let q_class = QClass::IN;
let q_type = QType::CNAME;
let question = Question {
domain_name,
q_class,
q_type,
};
vec![question]
};
let answers = {
let domain_name = DomainName::try_from("cname.example.org.").unwrap();
let class = Class::IN;
let ttl = 3600;
let c_name = DomainName::try_from("example.org.").unwrap();
let c_name = CNAME {
domain_name,
ttl,
class,
c_name,
};
vec![RR::CNAME(c_name)]
};
let authorities = {
let domain_name = DomainName::try_from("ns1.example.org.").unwrap();
let class = Class::IN;
let ttl = 3600;
let ns_d_name = DomainName::try_from("example.org.").unwrap();
let ns = NS {
domain_name,
class,
ttl,
ns_d_name,
};
vec![RR::NS(ns)]
};
let additionals = {
let domain_name = DomainName::try_from("example.org.").unwrap();
let ttl = 3600;
let ipv4_addr = "10.0.0.10".parse().unwrap();
let a = A {
domain_name,
ttl,
ipv4_addr,
};
vec![RR::A(a)]
};
let dns = Dns {
id,
flags,
questions,
answers,
authorities,
additionals,
};
check_output(
&dns,
"10105 Query NoError questions [cname.example.org. IN CNAME, ] \
answers [cname.example.org. 3600 IN CNAME example.org., ] \
authorities [ns1.example.org. 3600 IN NS example.org., ] \
additionals [example.org. 3600 IN A 10.0.0.10, ]",
);
}