use std::str::Utf8Error;
use std::{fmt::Write as _, ops::Deref};
use data_encoding::{BASE32HEX_NOPAD, BASE64, HEXUPPER};
use hickory_proto::{
error::ProtoError,
op::{message::Message as TrustDnsMessage, Query},
rr::{
dnssec::{
rdata::{DNSSECRData, DNSKEY, DS},
Algorithm, SupportedAlgorithms,
},
rdata::{
caa::Value,
opt::{EdnsCode, EdnsOption},
A, AAAA, NULL, OPT, SVCB,
},
record_data::RData,
resource::Record,
Name, RecordType,
},
serialize::binary::{BinDecodable, BinDecoder},
};
use snafu::Snafu;
use crate::ede::{EDE, EDE_OPTION_CODE};
use super::dns_message::{
self, DnsQueryMessage, DnsRecord, DnsUpdateMessage, EdnsOptionEntry, OptPseudoSection,
QueryHeader, QueryQuestion, UpdateHeader, ZoneInfo,
};
#[derive(Debug, Snafu)]
pub enum DnsMessageParserError {
#[snafu(display("Encountered error: {}", cause))]
SimpleError { cause: String },
#[snafu(display("Encountered error from TrustDns: {}", source))]
TrustDnsError { source: ProtoError },
#[snafu(display("UTF8Error: {}", source))]
Utf8ParsingError { source: Utf8Error },
}
pub type DnsParserResult<T> = Result<T, DnsMessageParserError>;
#[derive(Debug, Default, Clone)]
pub struct DnsParserOptions {
pub lowercase_hostnames: bool,
}
trait DnsParserOptionsTarget {
fn to_string_with_options(&self, options: &DnsParserOptions) -> String;
}
impl DnsParserOptionsTarget for Name {
fn to_string_with_options(&self, options: &DnsParserOptions) -> String {
if options.lowercase_hostnames {
self.to_lowercase().to_string()
} else {
self.to_string()
}
}
}
#[derive(Debug)]
pub struct DnsMessageParser {
raw_message: Vec<u8>,
raw_message_for_rdata_parsing: Option<Vec<u8>>,
options: DnsParserOptions,
}
impl DnsMessageParser {
pub fn new(raw_message: Vec<u8>) -> Self {
DnsMessageParser {
raw_message,
raw_message_for_rdata_parsing: None,
options: DnsParserOptions::default(),
}
}
pub fn with_options(raw_message: Vec<u8>, options: DnsParserOptions) -> Self {
DnsMessageParser {
raw_message,
raw_message_for_rdata_parsing: None,
options,
}
}
pub fn raw_message(&self) -> &[u8] {
&self.raw_message
}
pub fn parse_as_query_message(&mut self) -> DnsParserResult<DnsQueryMessage> {
let msg = TrustDnsMessage::from_vec(&self.raw_message)
.map_err(|source| DnsMessageParserError::TrustDnsError { source })?;
let header = parse_dns_query_message_header(&msg);
let edns_section = parse_edns(&msg).transpose()?;
let rcode_high = edns_section.as_ref().map_or(0, |edns| edns.extended_rcode);
let response_code = (u16::from(rcode_high) << 4) | ((u16::from(header.rcode)) & 0x000F);
Ok(DnsQueryMessage {
response_code,
response: parse_response_code(response_code),
header,
question_section: self.parse_dns_query_message_question_section(&msg),
answer_section: self.parse_dns_message_section(msg.answers())?,
authority_section: self.parse_dns_message_section(msg.name_servers())?,
additional_section: self.parse_dns_message_section(msg.additionals())?,
opt_pseudo_section: edns_section,
})
}
pub fn parse_as_update_message(&mut self) -> DnsParserResult<DnsUpdateMessage> {
let msg = TrustDnsMessage::from_vec(&self.raw_message)
.map_err(|source| DnsMessageParserError::TrustDnsError { source })?;
let header = parse_dns_update_message_header(&msg);
let response_code = (u16::from(header.rcode)) & 0x000F;
Ok(DnsUpdateMessage {
response_code,
response: parse_response_code(response_code),
header,
zone_to_update: self.parse_dns_update_message_zone_section(&msg)?,
prerequisite_section: self.parse_dns_message_section(msg.answers())?,
update_section: self.parse_dns_message_section(msg.name_servers())?,
additional_section: self.parse_dns_message_section(msg.additionals())?,
})
}
fn parse_dns_query_message_question_section(
&self,
dns_message: &TrustDnsMessage,
) -> Vec<QueryQuestion> {
dns_message
.queries()
.iter()
.map(|query| self.parse_dns_query_question(query))
.collect()
}
fn parse_dns_query_question(&self, question: &Query) -> QueryQuestion {
QueryQuestion {
name: question.name().to_string_with_options(&self.options),
class: question.query_class().to_string(),
record_type: format_record_type(question.query_type()),
record_type_id: u16::from(question.query_type()),
}
}
fn parse_dns_update_message_zone_section(
&self,
dns_message: &TrustDnsMessage,
) -> DnsParserResult<ZoneInfo> {
let zones = dns_message
.queries()
.iter()
.map(|query| self.parse_dns_query_question(query).into())
.collect::<Vec<ZoneInfo>>();
zones
.first()
.cloned()
.ok_or_else(|| DnsMessageParserError::SimpleError {
cause: format!(
"Unexpected number of records in update section: {}",
zones.len()
),
})
}
fn parse_dns_message_section(&mut self, records: &[Record]) -> DnsParserResult<Vec<DnsRecord>> {
records
.iter()
.map(|record| self.parse_dns_record(record))
.collect::<Result<Vec<_>, _>>()
}
pub(crate) fn parse_dns_record(&mut self, record: &Record) -> DnsParserResult<DnsRecord> {
let record_data = match record.data() {
Some(RData::Unknown { code, rdata }) => {
self.format_unknown_rdata((*code).into(), rdata)
}
Some(rdata) => self.format_rdata(rdata),
None => Ok((Some(String::from("")), None)), }?;
Ok(DnsRecord {
name: record.name().to_string_with_options(&self.options),
class: record.dns_class().to_string(),
record_type: format_record_type(record.record_type()),
record_type_id: u16::from(record.record_type()),
ttl: record.ttl(),
rdata: record_data.0,
rdata_bytes: record_data.1,
})
}
fn get_rdata_decoder_with_raw_message(&mut self, raw_rdata: &[u8]) -> BinDecoder<'_> {
let (index, raw_message_for_rdata_parsing_data) =
match self.raw_message_for_rdata_parsing.take() {
Some(mut buf) => {
let index = buf.len();
buf.extend_from_slice(raw_rdata);
(index, buf)
}
None => {
let mut buf = Vec::<u8>::with_capacity(self.raw_message.len() * 2);
buf.extend(&self.raw_message);
buf.extend_from_slice(raw_rdata);
(self.raw_message.len(), buf)
}
};
self.raw_message_for_rdata_parsing = Some(raw_message_for_rdata_parsing_data);
BinDecoder::new(self.raw_message_for_rdata_parsing.as_ref().unwrap()).clone(index as u16)
}
fn parse_wks_rdata(
&mut self,
raw_rdata: &[u8],
) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
let mut decoder = BinDecoder::new(raw_rdata);
let address = parse_ipv4_address(&mut decoder)?;
let protocol = parse_u8(&mut decoder)?;
let port = {
let mut port_string = String::new();
let mut current_bit: u32 = 0;
while !decoder.is_empty() {
let mut current_byte = parse_u8(&mut decoder)?;
if current_byte == 0 {
current_bit += 8;
continue;
}
for _i in 0..8 {
if current_byte & 0b1000_0000 == 0b1000_0000 {
write!(port_string, "{} ", current_bit)
.expect("can always write to String");
}
current_byte <<= 1;
current_bit += 1;
}
}
port_string
};
Ok((
Some(format!("{} {} {}", address, protocol, port.trim_end())),
None,
))
}
fn parse_a6_rdata(
&mut self,
raw_rdata: &[u8],
) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
let mut decoder = BinDecoder::new(raw_rdata);
let prefix = parse_u8(&mut decoder)?;
let ipv6_address = {
let address_length = (128 - prefix) / 8;
let mut address_vec = parse_vec(&mut decoder, address_length)?;
if address_vec.len() < 16 {
let pad_len = 16 - address_length;
let mut padded_address_vec = vec![0; pad_len as usize];
padded_address_vec.extend(&address_vec);
address_vec = padded_address_vec;
}
let mut dec = BinDecoder::new(&address_vec);
parse_ipv6_address(&mut dec)?
};
let domain_name = Self::parse_domain_name(&mut decoder, &self.options)?;
Ok((
Some(format!("{} {} {}", prefix, ipv6_address, domain_name)),
None,
))
}
fn parse_loc_rdata(
&mut self,
raw_rdata: &[u8],
) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
let mut decoder = BinDecoder::new(raw_rdata);
let _max_latitude: u32 = 0x8000_0000 + 90 * 3_600_000;
let _min_latitude: u32 = 0x8000_0000 - 90 * 3_600_000;
let _max_longitude: u32 = 0x8000_0000 + 180 * 3_600_000;
let _min_longitude: u32 = 0x8000_0000 - 180 * 3_600_000;
let _version = parse_u8(&mut decoder)?;
if _version != 0 {
return Err(DnsMessageParserError::SimpleError {
cause: String::from("LOC record version should be 0."),
});
}
let size = parse_loc_rdata_size(parse_u8(&mut decoder)?)?;
let horizontal_precision = parse_loc_rdata_size(parse_u8(&mut decoder)?)?;
let vertical_precision = parse_loc_rdata_size(parse_u8(&mut decoder)?)?;
let latitude = {
let received_lat = parse_u32(&mut decoder)?;
if received_lat < _min_latitude || received_lat > _max_latitude {
return Err(DnsMessageParserError::SimpleError {
cause: String::from("LOC record latitude out of bounds"),
});
}
let dir = if received_lat > 0x8000_0000 { "N" } else { "S" };
parse_loc_rdata_coordinates(received_lat, dir)
};
let longitude = {
let received_lon = parse_u32(&mut decoder)?;
if received_lon < _min_longitude || received_lon > _max_longitude {
return Err(DnsMessageParserError::SimpleError {
cause: String::from("LOC record longitude out of bounds"),
});
}
let dir = if received_lon > 0x8000_0000 { "E" } else { "W" };
parse_loc_rdata_coordinates(received_lon, dir)
};
let altitude = (parse_u32(&mut decoder)? as f64 - 10_000_000.0) / 100.0;
Ok((
Some(format!(
"{} {} {:.2}m {}m {}m {}m",
latitude, longitude, altitude, size, horizontal_precision, vertical_precision
)),
None,
))
}
fn parse_apl_rdata(
&mut self,
raw_rdata: &[u8],
) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
let mut decoder = BinDecoder::new(raw_rdata);
let mut apl_rdata = "".to_string();
while !decoder.is_empty() {
let address_family = parse_u16(&mut decoder)?;
let prefix = parse_u8(&mut decoder)?;
let mut afd_length = parse_u8(&mut decoder)?;
let negation = if afd_length > 127 {
afd_length -= 128;
"!"
} else {
""
};
let mut address_vec = parse_vec(&mut decoder, afd_length)?;
let address = if address_family == 1 {
if afd_length < 4 {
address_vec.resize(4, 0);
}
let mut dec = BinDecoder::new(&address_vec);
parse_ipv4_address(&mut dec)?
} else {
if afd_length < 16 {
address_vec.resize(16, 0);
}
let mut dec = BinDecoder::new(&address_vec);
parse_ipv6_address(&mut dec)?
};
write!(
apl_rdata,
"{}{}:{}/{}",
negation, address_family, address, prefix
)
.expect("can always write to String");
apl_rdata.push(' ');
}
Ok((Some(apl_rdata.trim_end().to_string()), None))
}
pub fn format_unknown_rdata(
&mut self,
code: u16,
rdata: &NULL,
) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
match code {
dns_message::RTYPE_MB => {
let options = self.options.clone();
let mut decoder = self.get_rdata_decoder_with_raw_message(rdata.anything());
let madname = Self::parse_domain_name(&mut decoder, &options)?;
Ok((Some(madname), None))
}
dns_message::RTYPE_MG => {
let options = self.options.clone();
let mut decoder = self.get_rdata_decoder_with_raw_message(rdata.anything());
let mgname = Self::parse_domain_name(&mut decoder, &options)?;
Ok((Some(mgname), None))
}
dns_message::RTYPE_MR => {
let options = self.options.clone();
let mut decoder = self.get_rdata_decoder_with_raw_message(rdata.anything());
let newname = Self::parse_domain_name(&mut decoder, &options)?;
Ok((Some(newname), None))
}
dns_message::RTYPE_WKS => self.parse_wks_rdata(rdata.anything()),
dns_message::RTYPE_MINFO => {
let options = self.options.clone();
let mut decoder = self.get_rdata_decoder_with_raw_message(rdata.anything());
let rmailbx = Self::parse_domain_name(&mut decoder, &options)?;
let emailbx = Self::parse_domain_name(&mut decoder, &options)?;
Ok((Some(format!("{} {}", rmailbx, emailbx)), None))
}
dns_message::RTYPE_RP => {
let options = self.options.clone();
let mut decoder = self.get_rdata_decoder_with_raw_message(rdata.anything());
let mbox = Self::parse_domain_name(&mut decoder, &options)?;
let txt = Self::parse_domain_name(&mut decoder, &options)?;
Ok((Some(format!("{} {}", mbox, txt)), None))
}
dns_message::RTYPE_AFSDB => {
let options = self.options.clone();
let mut decoder = self.get_rdata_decoder_with_raw_message(rdata.anything());
let subtype = parse_u16(&mut decoder)?;
let hostname = Self::parse_domain_name(&mut decoder, &options)?;
Ok((Some(format!("{} {}", subtype, hostname)), None))
}
dns_message::RTYPE_X25 => {
let mut decoder = BinDecoder::new(rdata.anything());
let psdn_address = parse_character_string(&mut decoder)?;
Ok((
Some(format!(
"\"{}\"",
escape_string_for_text_representation(psdn_address)
)),
None,
))
}
dns_message::RTYPE_ISDN => {
let mut decoder = BinDecoder::new(rdata.anything());
let address = parse_character_string(&mut decoder)?;
if decoder.is_empty() {
Ok((
Some(format!(
"\"{}\"",
escape_string_for_text_representation(address)
)),
None,
))
} else {
let sub_address = parse_character_string(&mut decoder)?;
Ok((
Some(format!(
"\"{}\" \"{}\"",
escape_string_for_text_representation(address),
escape_string_for_text_representation(sub_address)
)),
None,
))
}
}
dns_message::RTYPE_RT => {
let options = self.options.clone();
let mut decoder = self.get_rdata_decoder_with_raw_message(rdata.anything());
let preference = parse_u16(&mut decoder)?;
let intermediate_host = Self::parse_domain_name(&mut decoder, &options)?;
Ok((Some(format!("{} {}", preference, intermediate_host)), None))
}
dns_message::RTYPE_NSAP => {
let raw_rdata = rdata.anything();
let mut decoder = BinDecoder::new(raw_rdata);
let rdata_len = raw_rdata.len() as u16;
let nsap_rdata = HEXUPPER.encode(&parse_vec_with_u16_len(&mut decoder, rdata_len)?);
Ok((Some(format!("0x{}", nsap_rdata)), None))
}
dns_message::RTYPE_PX => {
let options = self.options.clone();
let mut decoder = self.get_rdata_decoder_with_raw_message(rdata.anything());
let preference = parse_u16(&mut decoder)?;
let map822 = Self::parse_domain_name(&mut decoder, &options)?;
let mapx400 = Self::parse_domain_name(&mut decoder, &options)?;
Ok((Some(format!("{} {} {}", preference, map822, mapx400)), None))
}
dns_message::RTYPE_LOC => self.parse_loc_rdata(rdata.anything()),
dns_message::RTYPE_KX => {
let options = self.options.clone();
let mut decoder = self.get_rdata_decoder_with_raw_message(rdata.anything());
let preference = parse_u16(&mut decoder)?;
let exchanger = Self::parse_domain_name(&mut decoder, &options)?;
Ok((Some(format!("{} {}", preference, exchanger)), None))
}
dns_message::RTYPE_CERT => {
let raw_rdata = rdata.anything();
let mut decoder = BinDecoder::new(raw_rdata);
let cert_type = parse_u16(&mut decoder)?;
let key_tag = parse_u16(&mut decoder)?;
let algorithm = Algorithm::from_u8(parse_u8(&mut decoder)?).as_str();
let crl_len = raw_rdata.len() as u16 - 5;
let crl = BASE64.encode(&parse_vec_with_u16_len(&mut decoder, crl_len)?);
Ok((
Some(format!("{} {} {} {}", cert_type, key_tag, algorithm, crl)),
None,
))
}
dns_message::RTYPE_A6 => self.parse_a6_rdata(rdata.anything()),
dns_message::RTYPE_SINK => {
let raw_rdata = rdata.anything();
let mut decoder = BinDecoder::new(raw_rdata);
let meaning = parse_u8(&mut decoder)?;
let coding = parse_u8(&mut decoder)?;
let subcoding = parse_u8(&mut decoder)?;
let data_len = raw_rdata.len() as u16 - 3;
let data = BASE64.encode(&parse_vec_with_u16_len(&mut decoder, data_len)?);
Ok((
Some(format!("{} {} {} {}", meaning, coding, subcoding, data)),
None,
))
}
dns_message::RTYPE_APL => self.parse_apl_rdata(rdata.anything()),
dns_message::RTYPE_DHCID => {
let raw_rdata = rdata.anything();
let mut decoder = BinDecoder::new(raw_rdata);
let raw_data_len = raw_rdata.len() as u16;
let digest = BASE64.encode(&parse_vec_with_u16_len(&mut decoder, raw_data_len)?);
Ok((Some(digest), None))
}
dns_message::RTYPE_SPF => {
let mut decoder = BinDecoder::new(rdata.anything());
let mut text = String::new();
while !decoder.is_empty() {
text.push('\"');
text.push_str(&parse_character_string(&mut decoder)?);
text.push_str("\" ");
}
Ok((Some(text.trim_end().to_string()), None))
}
_ => Ok((None, Some(rdata.anything().to_vec()))),
}
}
fn format_rdata(&self, rdata: &RData) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
match rdata {
RData::A(ip) => Ok((Some(ip.to_string()), None)),
RData::AAAA(ip) => Ok((Some(ip.to_string()), None)),
RData::ANAME(name) => Ok((Some(name.to_string_with_options(&self.options)), None)),
RData::CNAME(name) => Ok((Some(name.to_string_with_options(&self.options)), None)),
RData::CSYNC(csync) => {
let csync_rdata = format!("{}", csync);
Ok((Some(csync_rdata), None))
}
RData::MX(mx) => {
let srv_rdata = format!(
"{} {}",
mx.preference(),
mx.exchange().to_string_with_options(&self.options),
);
Ok((Some(srv_rdata), None))
}
RData::NULL(null) => Ok((Some(BASE64.encode(null.anything())), None)),
RData::NS(ns) => Ok((Some(ns.to_string_with_options(&self.options)), None)),
RData::OPENPGPKEY(key) => {
if let Ok(key_string) = String::from_utf8(Vec::from(key.public_key())) {
Ok((Some(format!("({})", &key_string)), None))
} else {
Err(DnsMessageParserError::SimpleError {
cause: String::from("Invalid OPENPGPKEY rdata"),
})
}
}
RData::PTR(ptr) => Ok((Some(ptr.to_string_with_options(&self.options)), None)),
RData::SOA(soa) => Ok((
Some(format!(
"{} {} {} {} {} {} {}",
soa.mname().to_string_with_options(&self.options),
soa.rname().to_string_with_options(&self.options),
soa.serial(),
soa.refresh(),
soa.retry(),
soa.expire(),
soa.minimum()
)),
None,
)),
RData::SRV(srv) => {
let srv_rdata = format!(
"{} {} {} {}",
srv.priority(),
srv.weight(),
srv.port(),
srv.target().to_string_with_options(&self.options)
);
Ok((Some(srv_rdata), None))
}
RData::TXT(txt) => {
let txt_rdata = txt
.txt_data()
.iter()
.map(|value| {
format!(
"\"{}\"",
escape_string_for_text_representation(
String::from_utf8_lossy(value).to_string()
)
)
})
.collect::<Vec<String>>()
.join(" ");
Ok((Some(txt_rdata), None))
}
RData::CAA(caa) => {
let caa_rdata = format!(
"{} {} \"{}\"",
caa.issuer_critical() as u8,
caa.tag().as_str(),
match caa.value() {
Value::Url(url) => {
url.as_str().to_string()
}
Value::Issuer(option_name, vec_keyvalue) => {
let mut final_issuer = String::new();
if let Some(name) = option_name {
final_issuer.push_str(&name.to_string_with_options(&self.options));
for keyvalue in vec_keyvalue.iter() {
final_issuer.push_str("; ");
final_issuer.push_str(keyvalue.key());
final_issuer.push('=');
final_issuer.push_str(keyvalue.value());
}
}
final_issuer.trim_end().to_string()
}
Value::Unknown(unknown) => std::str::from_utf8(unknown)
.map_err(|source| DnsMessageParserError::Utf8ParsingError { source })?
.to_string(),
}
);
Ok((Some(caa_rdata), None))
}
RData::TLSA(tlsa) => {
let tlsa_rdata = format!(
"{} {} {} {}",
u8::from(tlsa.cert_usage()),
u8::from(tlsa.selector()),
u8::from(tlsa.matching()),
HEXUPPER.encode(tlsa.cert_data())
);
Ok((Some(tlsa_rdata), None))
}
RData::SSHFP(sshfp) => {
let sshfp_rdata = format!(
"{} {} {}",
Into::<u8>::into(sshfp.algorithm()),
Into::<u8>::into(sshfp.fingerprint_type()),
HEXUPPER.encode(sshfp.fingerprint())
);
Ok((Some(sshfp_rdata), None))
}
RData::NAPTR(naptr) => {
let naptr_rdata = format!(
r#"{} {} "{}" "{}" "{}" {}"#,
naptr.order(),
naptr.preference(),
escape_string_for_text_representation(
std::str::from_utf8(naptr.flags())
.map_err(|source| DnsMessageParserError::Utf8ParsingError { source })?
.to_string()
),
escape_string_for_text_representation(
std::str::from_utf8(naptr.services())
.map_err(|source| DnsMessageParserError::Utf8ParsingError { source })?
.to_string()
),
escape_string_for_text_representation(
std::str::from_utf8(naptr.regexp())
.map_err(|source| DnsMessageParserError::Utf8ParsingError { source })?
.to_string()
),
naptr.replacement().to_string_with_options(&self.options)
);
Ok((Some(naptr_rdata), None))
}
RData::HINFO(hinfo) => {
let hinfo_data = format!(
r#""{}" "{}""#,
std::str::from_utf8(hinfo.cpu())
.map_err(|source| DnsMessageParserError::Utf8ParsingError { source })?,
std::str::from_utf8(hinfo.os())
.map_err(|source| DnsMessageParserError::Utf8ParsingError { source })?,
);
Ok((Some(hinfo_data), None))
}
RData::HTTPS(https) => {
let https_data = format_svcb_record(&https.0, &self.options);
Ok((Some(https_data), None))
}
RData::SVCB(svcb) => {
let svcb_data = format_svcb_record(svcb, &self.options);
Ok((Some(svcb_data), None))
}
RData::OPT(opt) => {
let parsed = parse_edns_options(opt)?;
let ede_data = parsed.0.iter().map(|entry| EdnsOptionEntry {
opt_code: 15u16,
opt_name: "EDE".to_string(),
opt_data: format!(
"EDE={}({}){}",
entry.info_code(),
entry.purpose().unwrap_or(""),
entry.extra_text().unwrap_or("".to_string())
),
});
let opt_data = parsed
.1
.into_iter()
.chain(ede_data)
.map(|entry| format!("{}={}", entry.opt_name, entry.opt_data))
.collect::<Vec<String>>()
.join(",");
Ok((Some(opt_data), None))
}
RData::DNSSEC(dnssec) => match dnssec {
DNSSECRData::CDS(cds) => Ok((Some(format_ds_record(cds.deref())), None)),
DNSSECRData::DS(ds) => Ok((Some(format_ds_record(ds)), None)),
DNSSECRData::CDNSKEY(cdnskey) => {
Ok((Some(format_dnskey_record(cdnskey.deref())), None))
}
DNSSECRData::DNSKEY(dnskey) => Ok((Some(format_dnskey_record(dnskey)), None)),
DNSSECRData::NSEC(nsec) => {
let nsec_rdata = format!(
"{} {}",
nsec.next_domain_name()
.to_string_with_options(&self.options),
nsec.type_bit_maps()
.iter()
.flat_map(|e| format_record_type(*e))
.collect::<Vec<String>>()
.join(" ")
);
Ok((Some(nsec_rdata), None))
}
DNSSECRData::NSEC3(nsec3) => {
let nsec3_rdata = format!(
"{} {} {} {} {} {}",
u8::from(nsec3.hash_algorithm()),
nsec3.opt_out() as u8,
nsec3.iterations(),
HEXUPPER.encode(nsec3.salt()),
BASE32HEX_NOPAD.encode(nsec3.next_hashed_owner_name()),
nsec3
.type_bit_maps()
.iter()
.flat_map(|e| format_record_type(*e))
.collect::<Vec<String>>()
.join(" ")
);
Ok((Some(nsec3_rdata), None))
}
DNSSECRData::NSEC3PARAM(nsec3param) => {
let nsec3param_rdata = format!(
"{} {} {} {}",
u8::from(nsec3param.hash_algorithm()),
nsec3param.opt_out() as u8,
nsec3param.iterations(),
HEXUPPER.encode(nsec3param.salt()),
);
Ok((Some(nsec3param_rdata), None))
}
DNSSECRData::SIG(sig) => {
let sig_rdata = format!(
"{} {} {} {} {} {} {} {} {}",
match format_record_type(sig.type_covered()) {
Some(record_type) => record_type,
None => String::from("Unknown record type"),
},
u8::from(sig.algorithm()),
sig.num_labels(),
sig.original_ttl(),
sig.sig_expiration(), sig.sig_inception(), sig.key_tag(),
sig.signer_name().to_string_with_options(&self.options),
BASE64.encode(sig.sig())
);
Ok((Some(sig_rdata), None))
}
DNSSECRData::RRSIG(sig) => {
let sig_rdata = format!(
"{} {} {} {} {} {} {} {} {}",
match format_record_type(sig.type_covered()) {
Some(record_type) => record_type,
None => String::from("Unknown record type"),
},
u8::from(sig.algorithm()),
sig.num_labels(),
sig.original_ttl(),
sig.sig_expiration(), sig.sig_inception(), sig.key_tag(),
sig.signer_name().to_string_with_options(&self.options),
BASE64.encode(sig.sig())
);
Ok((Some(sig_rdata), None))
}
DNSSECRData::KEY(key) => {
let key_rdata = format!(
"{} {} {} {}",
key.flags(),
u8::from(key.protocol()),
u8::from(key.algorithm()),
BASE64.encode(key.public_key())
);
Ok((Some(key_rdata), None))
}
DNSSECRData::Unknown { code: _, rdata } => {
Ok((None, Some(rdata.anything().to_vec())))
}
_ => Err(DnsMessageParserError::SimpleError {
cause: format!("Unsupported rdata {:?}", rdata),
}),
},
_ => Err(DnsMessageParserError::SimpleError {
cause: format!("Unsupported rdata {:?}", rdata),
}),
}
}
fn parse_domain_name(
decoder: &mut BinDecoder<'_>,
options: &DnsParserOptions,
) -> DnsParserResult<String> {
parse_domain_name(decoder).map(|n| n.to_string_with_options(options))
}
}
fn format_record_type(record_type: RecordType) -> Option<String> {
match record_type {
RecordType::Unknown(code) => parse_unknown_record_type(code),
_ => Some(record_type.to_string()),
}
}
fn format_svcb_record(svcb: &SVCB, options: &DnsParserOptions) -> String {
format!(
"{} {} {}",
svcb.svc_priority(),
svcb.target_name().to_string_with_options(options),
svcb.svc_params()
.iter()
.map(|(key, value)| format!(r#"{}="{}""#, key, value.to_string().trim_end_matches(',')))
.collect::<Vec<_>>()
.join(" ")
)
}
fn format_dnskey_record(dnskey: &DNSKEY) -> String {
format!(
"{} 3 {} {}",
{
if dnskey.revoke() {
0b0000_0000_0000_0000
} else if dnskey.zone_key() && dnskey.secure_entry_point() {
0b0000_0001_0000_0001
} else {
0b0000_0001_0000_0000
}
},
u8::from(dnskey.algorithm()),
BASE64.encode(dnskey.public_key())
)
}
fn format_ds_record(ds: &DS) -> String {
format!(
"{} {} {} {}",
ds.key_tag(),
u8::from(ds.algorithm()),
u8::from(ds.digest_type()),
HEXUPPER.encode(ds.digest())
)
}
fn parse_response_code(rcode: u16) -> Option<&'static str> {
match rcode {
0 => Some("NoError"), 1 => Some("FormErr"), 2 => Some("ServFail"), 3 => Some("NXDomain"), 4 => Some("NotImp"), 5 => Some("Refused"), 6 => Some("YXDomain"), 7 => Some("YXRRSet"), 8 => Some("NXRRSet"), 9 => Some("NotAuth"), 10 => Some("NotZone"), 16 => Some("BADSIG"), 17 => Some("BADKEY"), 18 => Some("BADTIME"), 19 => Some("BADMODE"), 20 => Some("BADNAME"), 21 => Some("BADALG"), 22 => Some("BADTRUNC"), 23 => Some("BADCOOKIE"), _ => None,
}
}
fn parse_dns_query_message_header(dns_message: &TrustDnsMessage) -> QueryHeader {
QueryHeader {
id: dns_message.header().id(),
opcode: dns_message.header().op_code().into(),
rcode: dns_message.header().response_code(),
qr: dns_message.header().message_type() as u8,
aa: dns_message.header().authoritative(),
tc: dns_message.header().truncated(),
rd: dns_message.header().recursion_desired(),
ra: dns_message.header().recursion_available(),
ad: dns_message.header().authentic_data(),
cd: dns_message.header().checking_disabled(),
question_count: dns_message.header().query_count(),
answer_count: dns_message.header().answer_count(),
authority_count: dns_message.header().name_server_count(),
additional_count: dns_message.header().additional_count(),
}
}
fn parse_dns_update_message_header(dns_message: &TrustDnsMessage) -> UpdateHeader {
UpdateHeader {
id: dns_message.header().id(),
opcode: dns_message.header().op_code().into(),
rcode: dns_message.header().response_code(),
qr: dns_message.header().message_type() as u8,
zone_count: dns_message.header().query_count(),
prerequisite_count: dns_message.header().answer_count(),
update_count: dns_message.header().name_server_count(),
additional_count: dns_message.header().additional_count(),
}
}
fn parse_edns(dns_message: &TrustDnsMessage) -> Option<DnsParserResult<OptPseudoSection>> {
dns_message.extensions().as_ref().map(|edns| {
parse_edns_options(edns.options()).map(|(ede, rest)| OptPseudoSection {
extended_rcode: edns.rcode_high(),
version: edns.version(),
dnssec_ok: edns.dnssec_ok(),
udp_max_payload_size: edns.max_payload(),
ede,
options: rest,
})
})
}
fn parse_edns_options(edns: &OPT) -> DnsParserResult<(Vec<EDE>, Vec<EdnsOptionEntry>)> {
let ede_opts: Vec<EDE> = edns
.as_ref()
.iter()
.filter_map(|(_, option)| {
if let EdnsOption::Unknown(EDE_OPTION_CODE, option) = option {
Some(
EDE::from_bytes(option)
.map_err(|source| DnsMessageParserError::TrustDnsError { source }),
)
} else {
None
}
})
.collect::<Result<Vec<EDE>, DnsMessageParserError>>()?;
let rest: Vec<EdnsOptionEntry> = edns
.as_ref()
.iter()
.filter(|(&code, _)| u16::from(code) != EDE_OPTION_CODE)
.map(|(code, option)| match option {
EdnsOption::DAU(algorithms)
| EdnsOption::DHU(algorithms)
| EdnsOption::N3U(algorithms) => {
Ok(parse_edns_opt_dnssec_algorithms(*code, *algorithms))
}
EdnsOption::Unknown(_, opt_data) => Ok(parse_edns_opt(*code, opt_data)),
option => Vec::<u8>::try_from(option)
.map(|bytes| parse_edns_opt(*code, &bytes))
.map_err(|source| DnsMessageParserError::TrustDnsError { source }),
})
.collect::<Result<Vec<EdnsOptionEntry>, DnsMessageParserError>>()?;
Ok((ede_opts, rest))
}
fn parse_edns_opt_dnssec_algorithms(
opt_code: EdnsCode,
algorithms: SupportedAlgorithms,
) -> EdnsOptionEntry {
let algorithm_names: Vec<String> = algorithms.iter().map(|alg| alg.to_string()).collect();
EdnsOptionEntry {
opt_code: Into::<u16>::into(opt_code),
opt_name: format!("{:?}", opt_code),
opt_data: algorithm_names.join(" "),
}
}
fn parse_edns_opt(opt_code: EdnsCode, opt_data: &[u8]) -> EdnsOptionEntry {
EdnsOptionEntry {
opt_code: Into::<u16>::into(opt_code),
opt_name: format!("{:?}", opt_code),
opt_data: BASE64.encode(opt_data),
}
}
fn parse_loc_rdata_size(data: u8) -> DnsParserResult<f64> {
let base = (data & 0xF0) >> 4;
if base > 9 {
return Err(DnsMessageParserError::SimpleError {
cause: format!("The base shouldn't be greater than 9. Base: {}", base),
});
}
let exponent = data & 0x0F;
if exponent > 9 {
return Err(DnsMessageParserError::SimpleError {
cause: format!(
"The exponent shouldn't be greater than 9. Exponent: {}",
exponent
),
});
}
let ten: u64 = 10;
let ans = (base as f64) * ten.pow(exponent as u32) as f64;
Ok(ans / 100.0) }
fn parse_loc_rdata_coordinates(coordinates: u32, dir: &str) -> String {
let degree = (coordinates as i64 - 0x8000_0000) as f64 / 3_600_000.00;
let minute = degree.fract() * 60.0;
let second = minute.fract() * 60.0;
format!(
"{} {} {:.3} {}",
degree.trunc().abs(),
minute.trunc().abs(),
second.abs(),
dir
)
}
fn parse_character_string(decoder: &mut BinDecoder<'_>) -> DnsParserResult<String> {
let raw_len = decoder
.read_u8()
.map_err(|source| DnsMessageParserError::TrustDnsError {
source: ProtoError::from(source),
})?;
let len = raw_len.unverified() as usize;
let raw_text =
decoder
.read_slice(len)
.map_err(|source| DnsMessageParserError::TrustDnsError {
source: ProtoError::from(source),
})?;
match raw_text.verify_unwrap(|r| r.len() == len) {
Ok(verified_text) => Ok(String::from_utf8_lossy(verified_text).to_string()),
Err(raw_data) => Err(DnsMessageParserError::SimpleError {
cause: format!(
"Unexpected data length: expected {}, got {}. Raw data {}",
len,
raw_data.len(),
format_bytes_as_hex_string(raw_data)
),
}),
}
}
fn parse_u8(decoder: &mut BinDecoder<'_>) -> DnsParserResult<u8> {
Ok(decoder
.read_u8()
.map_err(|source| DnsMessageParserError::TrustDnsError {
source: ProtoError::from(source),
})?
.unverified())
}
fn parse_u16(decoder: &mut BinDecoder<'_>) -> DnsParserResult<u16> {
Ok(decoder
.read_u16()
.map_err(|source| DnsMessageParserError::TrustDnsError {
source: ProtoError::from(source),
})?
.unverified())
}
fn parse_u32(decoder: &mut BinDecoder<'_>) -> DnsParserResult<u32> {
Ok(decoder
.read_u32()
.map_err(|source| DnsMessageParserError::TrustDnsError {
source: ProtoError::from(source),
})?
.unverified())
}
fn parse_vec(decoder: &mut BinDecoder<'_>, buffer_len: u8) -> DnsParserResult<Vec<u8>> {
let len = buffer_len as usize;
Ok(decoder
.read_vec(len)
.map_err(|source| DnsMessageParserError::TrustDnsError {
source: ProtoError::from(source),
})?
.unverified())
}
fn parse_vec_with_u16_len(
decoder: &mut BinDecoder<'_>,
buffer_len: u16,
) -> DnsParserResult<Vec<u8>> {
let len = buffer_len as usize;
Ok(decoder
.read_vec(len)
.map_err(|source| DnsMessageParserError::TrustDnsError {
source: ProtoError::from(source),
})?
.unverified())
}
fn parse_ipv6_address(decoder: &mut BinDecoder<'_>) -> DnsParserResult<String> {
Ok(<AAAA as BinDecodable>::read(decoder)
.map_err(|source| DnsMessageParserError::TrustDnsError { source })?
.to_string())
}
fn parse_ipv4_address(decoder: &mut BinDecoder<'_>) -> DnsParserResult<String> {
Ok(<A as BinDecodable>::read(decoder)
.map_err(|source| DnsMessageParserError::TrustDnsError { source })?
.to_string())
}
fn parse_domain_name(decoder: &mut BinDecoder<'_>) -> DnsParserResult<Name> {
Name::read(decoder).map_err(|source| DnsMessageParserError::TrustDnsError { source })
}
fn escape_string_for_text_representation(original_string: String) -> String {
original_string.replace('\\', "\\\\").replace('\"', "\\\"")
}
fn parse_unknown_record_type(rtype: u16) -> Option<String> {
match rtype {
1 => Some(String::from("A")),
2 => Some(String::from("NS")),
3 => Some(String::from("MD")),
4 => Some(String::from("MF")),
5 => Some(String::from("CNAME")),
6 => Some(String::from("SOA")),
7 => Some(String::from("MB")),
8 => Some(String::from("MG")),
9 => Some(String::from("MR")),
10 => Some(String::from("NULL")),
11 => Some(String::from("WKS")),
12 => Some(String::from("PTR")),
13 => Some(String::from("HINFO")),
14 => Some(String::from("MINFO")),
15 => Some(String::from("MX")),
16 => Some(String::from("TXT")),
17 => Some(String::from("RP")),
18 => Some(String::from("AFSDB")),
19 => Some(String::from("X25")),
20 => Some(String::from("ISDN")),
21 => Some(String::from("RT")),
22 => Some(String::from("NSAP")),
23 => Some(String::from("NSAP-PTR")),
24 => Some(String::from("SIG")),
25 => Some(String::from("KEY")),
26 => Some(String::from("PX")),
27 => Some(String::from("GPOS")),
28 => Some(String::from("AAAA")),
29 => Some(String::from("LOC")),
30 => Some(String::from("NXT")),
31 => Some(String::from("EID")),
32 => Some(String::from("NIMLOC")),
33 => Some(String::from("SRV")),
34 => Some(String::from("ATMA")),
35 => Some(String::from("NAPTR")),
36 => Some(String::from("KX")),
37 => Some(String::from("CERT")),
38 => Some(String::from("A6")),
39 => Some(String::from("DNAME")),
40 => Some(String::from("SINK")),
41 => Some(String::from("OPT")),
42 => Some(String::from("APL")),
43 => Some(String::from("DS")),
44 => Some(String::from("SSHFP")),
45 => Some(String::from("IPSECKEY")),
46 => Some(String::from("RRSIG")),
47 => Some(String::from("NSEC")),
48 => Some(String::from("DNSKEY")),
49 => Some(String::from("DHCID")),
50 => Some(String::from("NSEC3")),
51 => Some(String::from("NSEC3PARAM")),
52 => Some(String::from("TLSA")),
53 => Some(String::from("SMIMEA")),
55 => Some(String::from("HIP")),
56 => Some(String::from("NINFO")),
57 => Some(String::from("RKEY")),
58 => Some(String::from("TALINK")),
59 => Some(String::from("CDS")),
60 => Some(String::from("CDNSKEY")),
61 => Some(String::from("OPENPGPKEY")),
62 => Some(String::from("CSYNC")),
63 => Some(String::from("ZONEMD")),
99 => Some(String::from("SPF")),
100 => Some(String::from("UINFO")),
101 => Some(String::from("UID")),
102 => Some(String::from("GID")),
103 => Some(String::from("UNSPEC")),
104 => Some(String::from("NID")),
105 => Some(String::from("L32")),
106 => Some(String::from("L64")),
107 => Some(String::from("LP")),
108 => Some(String::from("EUI48")),
109 => Some(String::from("EUI64")),
249 => Some(String::from("TKEY")),
250 => Some(String::from("TSIG")),
251 => Some(String::from("IXFR")),
252 => Some(String::from("AXFR")),
253 => Some(String::from("MAILB")),
254 => Some(String::from("MAILA")),
255 => Some(String::from("ANY")),
256 => Some(String::from("URI")),
257 => Some(String::from("CAA")),
258 => Some(String::from("AVC")),
259 => Some(String::from("DOA")),
260 => Some(String::from("AMTRELAY")),
32768 => Some(String::from("TA")),
32769 => Some(String::from("DLV")),
_ => None,
}
}
fn format_bytes_as_hex_string(bytes: &[u8]) -> String {
bytes
.iter()
.map(|e| format!("{:02X}", e))
.collect::<Vec<String>>()
.join(".")
}
#[cfg(test)]
mod tests {
use std::{
collections::HashMap,
net::{Ipv4Addr, Ipv6Addr},
str::FromStr,
};
#[allow(deprecated)]
use hickory_proto::rr::{
dnssec::{
rdata::{
dnskey::DNSKEY,
ds::DS,
key::{KeyTrust, KeyUsage, Protocol, UpdateScope},
nsec::NSEC,
nsec3::NSEC3,
nsec3param::NSEC3PARAM,
sig::SIG,
DNSSECRData, KEY, RRSIG,
},
Algorithm as DNSSEC_Algorithm, DigestType, Nsec3HashAlgorithm,
},
domain::Name,
rdata::{
caa::KeyValue,
sshfp::{Algorithm, FingerprintType},
svcb,
tlsa::{CertUsage, Matching, Selector},
CAA, CSYNC, HINFO, HTTPS, NAPTR, OPT, SSHFP, TLSA, TXT,
},
};
use hickory_proto::serialize::binary::Restrict;
use super::*;
impl DnsMessageParser {
pub fn raw_message_for_rdata_parsing(&self) -> Option<&Vec<u8>> {
self.raw_message_for_rdata_parsing.as_ref()
}
}
fn format_rdata(rdata: &RData) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
DnsMessageParser::new(Vec::new()).format_rdata(rdata)
}
fn format_rdata_with_options(
rdata: &RData,
options: DnsParserOptions,
) -> DnsParserResult<(Option<String>, Option<Vec<u8>>)> {
DnsMessageParser::with_options(Vec::new(), options).format_rdata(rdata)
}
#[test]
fn test_parse_as_query_message() {
let raw_dns_message = "szgAAAABAAAAAAAAAmg1B2V4YW1wbGUDY29tAAAGAAE=";
let raw_query_message = BASE64
.decode(raw_dns_message.as_bytes())
.expect("Invalid base64 encoded data.");
let parse_result = DnsMessageParser::new(raw_query_message).parse_as_query_message();
assert!(parse_result.is_ok());
let message = parse_result.expect("Message is not parsed.");
assert_eq!(message.header.qr, 0);
assert_eq!(message.question_section.len(), 1);
assert_eq!(
message.question_section.first().unwrap().name,
"h5.example.com."
);
assert_eq!(
&message
.question_section
.first()
.unwrap()
.record_type
.clone()
.unwrap(),
"SOA"
);
}
#[test]
fn test_parse_as_query_message_with_ede() {
let raw_dns_message =
"szgAAAABAAAAAAABAmg1B2V4YW1wbGUDY29tAAAGAAEAACkE0AEBQAAABgAPAAIAFQ==";
let raw_query_message = BASE64
.decode(raw_dns_message.as_bytes())
.expect("Invalid base64 encoded data.");
let parse_result = DnsMessageParser::new(raw_query_message).parse_as_query_message();
assert!(parse_result.is_ok());
let message = parse_result.expect("Message is not parsed.");
let opt_pseudo_section = message.opt_pseudo_section.expect("OPT section was missing");
assert_eq!(opt_pseudo_section.ede.len(), 1);
assert_eq!(opt_pseudo_section.ede[0].info_code(), 21u16);
assert_eq!(opt_pseudo_section.ede[0].purpose(), Some("Not Supported"));
assert_eq!(opt_pseudo_section.ede[0].extra_text(), None);
}
#[test]
fn test_parse_as_query_message_with_ede_with_extra_text() {
let raw_dns_message =
"szgAAAABAAAAAAABAmg1B2V4YW1wbGUDY29tAAAGAAEAACkE0AEBQAAAOQAPADUACW5vIFNFUCBtYXRjaGluZyB0aGUgRFMgZm91bmQgZm9yIGRuc3NlYy1mYWlsZWQub3JnLg==";
let raw_query_message = BASE64
.decode(raw_dns_message.as_bytes())
.expect("Invalid base64 encoded data.");
let parse_result = DnsMessageParser::new(raw_query_message).parse_as_query_message();
assert!(parse_result.is_ok());
let message = parse_result.expect("Message is not parsed.");
let opt_pseudo_section = message.opt_pseudo_section.expect("OPT section was missing");
assert_eq!(opt_pseudo_section.ede.len(), 1);
assert_eq!(opt_pseudo_section.ede[0].info_code(), 9u16);
assert_eq!(opt_pseudo_section.ede[0].purpose(), Some("DNSKEY Missing"));
assert_eq!(
opt_pseudo_section.ede[0].extra_text(),
Some("no SEP matching the DS found for dnssec-failed.org.".to_string())
);
}
#[test]
fn test_parse_as_query_message_with_invalid_data() {
let err = DnsMessageParser::new(vec![1, 2, 3])
.parse_as_query_message()
.expect_err("Expected TrustDnsError.");
match err {
DnsMessageParserError::TrustDnsError { source: e } => {
assert_eq!(e.to_string(), "unexpected end of input reached")
}
DnsMessageParserError::SimpleError { cause: e } => {
panic!("Expected TrustDnsError, got {}.", &e)
}
_ => panic!("{}.", err),
}
}
#[test]
fn test_parse_as_query_message_with_unsupported_rdata() {
let raw_query_message_base64 = "eEaFgAABAAEAAAAABGRvYTEHZXhhbXBsZQNjb20AAQMAAcAMAQMAAQAADhAAIAAAAAAAAAAAAgIiImh0dHBzOi8vd3d3LmlzYy5vcmcv";
let raw_query_message = BASE64
.decode(raw_query_message_base64.as_bytes())
.expect("Invalid base64 encoded data.");
let dns_query_message = DnsMessageParser::new(raw_query_message)
.parse_as_query_message()
.expect("Invalid DNS query message.");
assert_eq!(dns_query_message.answer_section[0].rdata, None);
assert_ne!(dns_query_message.answer_section[0].rdata_bytes, None);
}
#[test]
fn test_parse_response_with_https_rdata() {
let raw_response_message_base64 = "Oe2BgAABAAEAAAABBGNkbnAHc2FuamFnaANjb20AAEEAAcAMAEEAAQAAASwAPQABAAABAAYCaDMCaDIABAAIrEDEHKxAxRwABgAgJgZHAADmAAAAAAAArEDEHCYGRwAA5gAAAAAAAKxAxRwAACkE0AAAAAAAHAAKABjWOVAgEGik/gEAAABlwiAuXkvEOviB1sk=";
let raw_response_message = BASE64
.decode(raw_response_message_base64.as_bytes())
.expect("Invalid base64 encoded data.");
let dns_response_message = DnsMessageParser::new(raw_response_message)
.parse_as_query_message()
.expect("Invalid DNS query message.");
assert_eq!(
dns_response_message.answer_section[0].rdata,
Some(r#"1 . alpn="h3,h2" ipv4hint="172.64.196.28,172.64.197.28" ipv6hint="2606:4700:e6::ac40:c41c,2606:4700:e6::ac40:c51c""#.to_string())
);
assert_eq!(dns_response_message.answer_section[0].record_type_id, 65u16);
assert_eq!(dns_response_message.answer_section[0].rdata_bytes, None);
}
#[test]
fn test_parse_response_with_hinfo_rdata() {
let raw_response_message_base64 =
"wS2BgAABAAEAAAAAB3RyYWNrZXIEZGxlcgNvcmcAAP8AAcAMAA0AAQAAC64ACQdSRkM4NDgyAA==";
let raw_response_message = BASE64
.decode(raw_response_message_base64.as_bytes())
.expect("Invalid base64 encoded data.");
let dns_response_message = DnsMessageParser::new(raw_response_message)
.parse_as_query_message()
.expect("Invalid DNS query message.");
assert_eq!(
dns_response_message.answer_section[0].rdata,
Some(r#""RFC8482" """#.to_string())
);
assert_eq!(dns_response_message.answer_section[0].record_type_id, 13u16);
assert_eq!(dns_response_message.answer_section[0].rdata_bytes, None);
}
#[test]
fn test_format_bytes_as_hex_string() {
assert_eq!(
"01.02.03.AB.CD.EF",
&format_bytes_as_hex_string(&[1, 2, 3, 0xab, 0xcd, 0xef])
);
}
#[test]
fn test_parse_unknown_record_type() {
assert_eq!("A", parse_unknown_record_type(1).unwrap());
assert_eq!("ANY", parse_unknown_record_type(255).unwrap());
assert!(parse_unknown_record_type(22222).is_none());
}
#[test]
fn test_parse_as_update_message_failure() {
let raw_dns_message = "ChVqYW1lcy1WaXJ0dWFsLU1hY2hpbmUSC0JJTkQgOS4xNi4zcq0ICAQQARgBIgSs\
FDetKgTAN1MeMMn/Ajg1QL6m6fcFTQLEKhVS+QMSSIIAAAEAAAAFAAUJZmFjZWJvb2sxA2NvbQAAAQABwAwAAgABAAKjA\
AAPA25zMQhyZW50b25kY8AWwAwAAgABAAKjAAAGA25zMsAvIENLMFBPSk1HODc0TEpSRUY3RUZOODQzMFFWSVQ4QlNNwB\
YAMgABAAFRgAAjAQEAAAAUZQGgwlcg7hVvbE45Y2s62gMS2SoAByIAAAAAApDATAAuAAEAAVGAALcAMggCAAFRgF8ACGF\
e9r15m6QDY29tAFOih16McCzogcR6RZIu3kqZa27Bo1jtfzwzDENJIZItSCRuLqRO6oA90sCLItOEQv0skpQKtJQXmTZU\
nqe3XK+1t/Op8G9cmeMXgCvynTJmm0WouSv+SuwBOjgqCaNuWpwbiIcaXY/NlId1lPpl8LJyTIRtFqGifW0FnYFe/Lzs3\
pfZLoKMAG4/8Upqqv4F+Ij1oue1C6KWe0hn+beIKkIgN0pLMjVFTUhITThBMDFNTzBBRVFRTjdHMlVQSjI4NjfAFgAyAA\
EAAVGAACIBAQAAABQ86ELf24DH1kAfgQ4dyyuf0+6y5wAGIAAAAAASwCsAAQABAAKjAAAEbD0TCsArAAEAAQACowAABKx\
iwCLARgABAAEAAqMAAAQuprYzwEYAAQABAAKjAAAEXXMcaAAAKRAAAACAAAAAWgUDY29tAGC+pun3BW2/LUQYcvkDEkiC\
AAABAAAABQAFCWZhY2Vib29rMQNjb20AAAEAAcAMAAIAAQACowAADwNuczEIcmVudG9uZGPAFsAMAAIAAQACowAABgNuc\
zLALyBDSzBQT0pNRzg3NExKUkVGN0VGTjg0MzBRVklUOEJTTcAWADIAAQABUYAAIwEBAAAAFGUBoMJXIO4Vb2xOOWNrOt\
oDEtkqAAciAAAAAAKQwEwALgABAAFRgAC3ADIIAgABUYBfAAhhXva9eZukA2NvbQBToodejHAs6IHEekWSLt5KmWtuwaN\
Y7X88MwxDSSGSLUgkbi6kTuqAPdLAiyLThEL9LJKUCrSUF5k2VJ6nt1yvtbfzqfBvXJnjF4Ar8p0yZptFqLkr/krsATo4\
KgmjblqcG4iHGl2PzZSHdZT6ZfCyckyEbRahon1tBZ2BXvy87N6X2S6CjABuP/FKaqr+BfiI9aLntQuilntIZ/m3iCpCI\
DdKSzI1RU1ISE04QTAxTU8wQUVRUU43RzJVUEoyODY3wBYAMgABAAFRgAAiAQEAAAAUPOhC39uAx9ZAH4EOHcsrn9Pusu\
cABiAAAAAAEsArAAEAAQACowAABGw9EwrAKwABAAEAAqMAAASsYsAiwEYAAQABAAKjAAAELqa2M8BGAAEAAQACowAABF1\
zHGgAACkQAAAAgAAAAHgB";
let raw_update_message = BASE64
.decode(raw_dns_message.as_bytes())
.expect("Invalid base64 encoded data.");
assert!(DnsMessageParser::new(raw_update_message)
.parse_as_update_message()
.is_err());
}
#[test]
fn test_parse_as_update_message() {
let raw_dns_message = "xjUoAAABAAAAAQAAB2V4YW1wbGUDY29tAAAGAAECaDXADAD/AP8AAAAAAAA=";
let raw_update_message = BASE64
.decode(raw_dns_message.as_bytes())
.expect("Invalid base64 encoded data.");
let parse_result = DnsMessageParser::new(raw_update_message).parse_as_update_message();
assert!(parse_result.is_ok());
let message = parse_result.expect("Message is not parsed.");
assert_eq!(message.header.qr, 0);
assert_eq!(message.update_section.len(), 1);
assert_eq!(message.update_section.first().unwrap().class, "ANY");
assert_eq!(&message.zone_to_update.zone_type.clone().unwrap(), "SOA");
assert_eq!(message.zone_to_update.name, "example.com.");
}
#[test]
fn test_parse_loc_rdata_size() {
let data: u8 = 51;
let expected: f64 = 30.0;
assert!((expected - parse_loc_rdata_size(data).unwrap()).abs() < f64::EPSILON);
let data: u8 = 22;
let expected: f64 = 10000.0;
assert!((expected - parse_loc_rdata_size(data).unwrap()).abs() < f64::EPSILON);
let data: u8 = 19;
let expected: f64 = 10.0;
assert!((expected - parse_loc_rdata_size(data).unwrap()).abs() < f64::EPSILON);
}
#[test]
fn test_parse_loc_rdata_coordinates() {
let coordinates: u32 = 2299997648;
let dir = "N";
let expected = String::from("42 21 54.000 N");
assert_eq!(expected, parse_loc_rdata_coordinates(coordinates, dir));
let coordinates: u32 = 1891505648;
let dir = "W";
let expected = String::from("71 6 18.000 W");
assert_eq!(expected, parse_loc_rdata_coordinates(coordinates, dir));
}
#[test]
fn test_format_rdata_for_a_type() {
let rdata = RData::A(Ipv4Addr::from_str("1.2.3.4").unwrap().into());
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!("1.2.3.4", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_aaaa_type() {
let rdata = RData::AAAA(Ipv6Addr::from_str("2001::1234").unwrap().into());
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!("2001::1234", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_cname_type() {
let rdata = RData::CNAME(hickory_proto::rr::rdata::CNAME(
Name::from_str("www.example.com.").unwrap(),
));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!("www.example.com.", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_cname_type_downcase() {
let rdata = RData::CNAME(hickory_proto::rr::rdata::CNAME(
Name::from_str("WWW.Example.Com.").unwrap(),
));
let rdata_text = format_rdata_with_options(
&rdata,
DnsParserOptions {
lowercase_hostnames: true,
},
);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!("www.example.com.", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_txt_type() {
let rdata = RData::TXT(TXT::new(vec![
"abc\"def".to_string(),
"gh\\i".to_string(),
"".to_string(),
"j".to_string(),
]));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!(r#""abc\"def" "gh\\i" "" "j""#, parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_caa_type() {
let rdata1 = RData::CAA(CAA::new_issue(
true,
Some(Name::parse("example.com", None).unwrap()),
vec![],
));
let rdata2 = RData::CAA(CAA::new_issue(
true,
Some(Name::parse("example.com", None).unwrap()),
vec![KeyValue::new("key", "value")],
));
let rdata_text1 = format_rdata(&rdata1);
let rdata_text2 = format_rdata(&rdata2);
assert!(rdata_text1.is_ok());
assert!(rdata_text2.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text1 {
assert!(raw_rdata.is_none());
assert_eq!("1 issue \"example.com\"", parsed.unwrap());
}
if let Ok((parsed, raw_rdata)) = rdata_text2 {
assert!(raw_rdata.is_none());
assert_eq!("1 issue \"example.com; key=value\"", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_tlsa_type() {
let rdata = RData::TLSA(TLSA::new(
CertUsage::Service,
Selector::Spki,
Matching::Sha256,
vec![1, 2, 3, 4, 5, 6, 7, 8],
));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!("1 1 1 0102030405060708", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_sshfp_type() {
let rdata = RData::SSHFP(SSHFP::new(
Algorithm::ECDSA,
FingerprintType::SHA1,
vec![115, 115, 104, 102, 112],
));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!("3 1 7373686670", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_naptr_type() {
let rdata1 = RData::NAPTR(NAPTR::new(
8,
16,
b"aa11AA-".to_vec().into_boxed_slice(),
b"services".to_vec().into_boxed_slice(),
b"regexpr".to_vec().into_boxed_slice(),
Name::from_str("naptr.example.com").unwrap(),
));
let rdata_text1 = format_rdata(&rdata1);
let rdata2 = RData::NAPTR(NAPTR::new(
8,
16,
b"aa1\"\\1AA-".to_vec().into_boxed_slice(),
b"\\services2\"".to_vec().into_boxed_slice(),
b"re%ge\"xp.r\\".to_vec().into_boxed_slice(),
Name::from_str("naptr.example.com").unwrap(),
));
let rdata_text2 = format_rdata(&rdata2);
assert!(rdata_text1.is_ok());
assert!(rdata_text2.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text1 {
assert!(raw_rdata.is_none());
assert_eq!(
"8 16 \"aa11AA-\" \"services\" \"regexpr\" naptr.example.com",
parsed.unwrap()
);
}
if let Ok((parsed, raw_rdata)) = rdata_text2 {
assert!(raw_rdata.is_none());
assert_eq!(
"8 16 \"aa1\\\"\\\\1AA-\" \"\\\\services2\\\"\" \"re%ge\\\"xp.r\\\\\" naptr.example.com",
parsed.unwrap()
);
}
}
#[test]
fn test_format_rdata_for_dnskey_type() {
let rdata1 = RData::DNSSEC(DNSSECRData::DNSKEY(DNSKEY::new(
true,
true,
false,
DNSSEC_Algorithm::RSASHA256,
vec![0, 1, 2, 3, 4, 5, 6, 7],
)));
let rdata_text1 = format_rdata(&rdata1);
let rdata2 = RData::DNSSEC(DNSSECRData::DNSKEY(DNSKEY::new(
true,
false,
false,
DNSSEC_Algorithm::RSASHA256,
vec![0, 1, 2, 3, 4, 5, 6, 7],
)));
let rdata_text2 = format_rdata(&rdata2);
let rdata3 = RData::DNSSEC(DNSSECRData::DNSKEY(DNSKEY::new(
true,
true,
true,
DNSSEC_Algorithm::RSASHA256,
vec![0, 1, 2, 3, 4, 5, 6, 7],
)));
let rdata_text3 = format_rdata(&rdata3);
assert!(rdata_text1.is_ok());
assert!(rdata_text2.is_ok());
assert!(rdata_text3.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text1 {
assert!(raw_rdata.is_none());
assert_eq!("257 3 8 AAECAwQFBgc=", parsed.unwrap());
}
if let Ok((parsed, raw_rdata)) = rdata_text2 {
assert!(raw_rdata.is_none());
assert_eq!("256 3 8 AAECAwQFBgc=", parsed.unwrap());
}
if let Ok((parsed, raw_rdata)) = rdata_text3 {
assert!(raw_rdata.is_none());
assert_eq!("0 3 8 AAECAwQFBgc=", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_nsec_type() {
let rdata = RData::DNSSEC(DNSSECRData::NSEC(NSEC::new(
Name::from_str("www.example.com").unwrap(),
vec![RecordType::A, RecordType::AAAA],
)));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!("www.example.com A AAAA", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_nsec3_type() {
let rdata = RData::DNSSEC(DNSSECRData::NSEC3(NSEC3::new(
Nsec3HashAlgorithm::SHA1,
true,
2,
vec![1, 2, 3, 4, 5],
vec![6, 7, 8, 9, 0],
vec![RecordType::A, RecordType::AAAA],
)));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!("1 1 2 0102030405 0O3GG280 A AAAA", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_nsec3param_type() {
let rdata = RData::DNSSEC(DNSSECRData::NSEC3PARAM(NSEC3PARAM::new(
Nsec3HashAlgorithm::SHA1,
true,
2,
vec![1, 2, 3, 4, 5],
)));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!("1 1 2 0102030405", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_sig_type() {
let rdata = RData::DNSSEC(DNSSECRData::SIG(SIG::new(
RecordType::NULL,
DNSSEC_Algorithm::RSASHA256,
0,
0,
2,
1,
5,
Name::from_str("www.example.com").unwrap(),
vec![
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 29, 31,
],
)));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!(
"NULL 8 0 0 2 1 5 www.example.com AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHR8=",
parsed.unwrap()
);
}
}
#[test]
fn test_format_rdata_for_key_type() {
let rdata = RData::DNSSEC(DNSSECRData::KEY(KEY::new(
KeyTrust::NotPrivate,
KeyUsage::Host,
#[allow(deprecated)]
UpdateScope {
zone: false,
strong: false,
unique: true,
general: true,
},
Protocol::DNSSEC,
DNSSEC_Algorithm::RSASHA256,
vec![
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 29, 31,
],
)));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!(
"16387 3 8 AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHR8=",
parsed.unwrap()
);
}
}
#[test]
fn test_format_rdata_for_rsig_type() {
let rdata = RData::DNSSEC(DNSSECRData::RRSIG(RRSIG::new(
RecordType::NULL,
DNSSEC_Algorithm::RSASHA256,
0,
0,
2,
1,
5,
Name::from_str("www.example.com").unwrap(),
vec![
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 29, 31,
],
)));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!(
"NULL 8 0 0 2 1 5 www.example.com AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHR8=",
parsed.unwrap()
);
}
}
#[test]
fn test_format_rdata_for_ds_type() {
let rdata = RData::DNSSEC(DNSSECRData::DS(DS::new(
0xF00F,
DNSSEC_Algorithm::RSASHA256,
DigestType::SHA256,
vec![5, 6, 7, 8],
)));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!("61455 8 2 05060708", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_svcb_type() {
let rdata = RData::SVCB(svcb::SVCB::new(
1,
Name::root(),
vec![
(
svcb::SvcParamKey::Alpn,
svcb::SvcParamValue::Alpn(svcb::Alpn(vec!["h3".to_string(), "h2".to_string()])),
),
(
svcb::SvcParamKey::Ipv4Hint,
svcb::SvcParamValue::Ipv4Hint(svcb::IpHint(vec![
A(Ipv4Addr::new(104, 18, 36, 155)),
A(Ipv4Addr::new(172, 64, 151, 101)),
])),
),
],
));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!(
r#"1 . alpn="h3,h2" ipv4hint="104.18.36.155,172.64.151.101""#,
parsed.unwrap()
);
}
}
#[test]
fn test_format_rdata_for_https_type() {
let rdata = RData::HTTPS(HTTPS(svcb::SVCB::new(
1,
Name::root(),
vec![
(
svcb::SvcParamKey::Alpn,
svcb::SvcParamValue::Alpn(svcb::Alpn(vec!["h3".to_string(), "h2".to_string()])),
),
(
svcb::SvcParamKey::Ipv4Hint,
svcb::SvcParamValue::Ipv4Hint(svcb::IpHint(vec![
A(Ipv4Addr::new(104, 18, 36, 155)),
A(Ipv4Addr::new(172, 64, 151, 101)),
])),
),
],
)));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!(
r#"1 . alpn="h3,h2" ipv4hint="104.18.36.155,172.64.151.101""#,
parsed.unwrap()
);
}
}
#[test]
fn test_format_rdata_for_hinfo_type() {
let rdata = RData::HINFO(HINFO::new("intel".to_string(), "linux".to_string()));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!(r#""intel" "linux""#, parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_csync_type() {
let types = vec![RecordType::A, RecordType::NS, RecordType::AAAA];
let rdata = RData::CSYNC(CSYNC::new(123, true, true, types));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!("123 3 A NS AAAA", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_opt_type() {
let mut options = HashMap::new();
options.insert(
EdnsCode::LLQ,
EdnsOption::Unknown(u16::from(EdnsCode::LLQ), vec![0x01; 18]),
);
let rdata = RData::OPT(OPT::new(options));
let rdata_text = format_rdata(&rdata);
assert!(rdata_text.is_ok());
if let Ok((parsed, raw_rdata)) = rdata_text {
assert!(raw_rdata.is_none());
assert_eq!("LLQ=AQEBAQEBAQEBAQEBAQEBAQEB", parsed.unwrap());
}
}
#[test]
fn test_format_rdata_for_minfo_type() {
test_format_rdata_with_compressed_domain_names(
"5ZWBgAABAAEAAAABBm1pbmZvbwhleGFtcGxlMQNjb20AAA4AAcAMAA4AAQAADGsADQRmcmVkwBMDam9lwBMAACkQAAAAAAAAHAAKABgZ5zwJEK3VJQEAAABfSBqpS2bKf9CNBXg=",
"BGZyZWTAEwNqb2XAEw==",
14,
"fred.example1.com. joe.example1.com."
);
}
#[test]
fn test_format_rdata_for_mb_type() {
test_format_rdata_with_compressed_domain_names(
"t8eBgAABAAEAAAABAm1iCGV4YW1wbGUxA2NvbQAABwABwAwABwABAAAA5AAJBmFhYmJjY8APAA\
ApEAAAAAAAABwACgAYedbJkVVpMhsBAAAAX0U+y6UJQtCd0MuPBmFhYmJjY8AP",
"BmFhYmJjY8AP",
7,
"aabbcc.example1.com.",
);
}
#[test]
fn test_format_rdata_for_mg_type() {
test_format_rdata_with_compressed_domain_names(
"o8ABIAABAAAAAAABAm1nCGV4YW1wbGUxA2NvbQAACAABAAApEAAAAAAAAAwACgAICQ3LVdp9euQ=",
"wAw=",
8,
"mg.example1.com.",
);
}
#[test]
fn test_format_rdata_for_mr_type() {
test_format_rdata_with_compressed_domain_names(
"VWQBIAABAAAAAAABAm1yCGV4YW1wbGUxA2NvbQAACQABAAApEAAAAAAAAAwACgAIaPayFPJ4rmY=",
"wAw=",
9,
"mr.example1.com.",
);
}
#[test]
fn test_format_rdata_for_wks_type() {
test_format_rdata("gAgBDgYAAAFA", 11, "128.8.1.14 6 23 25");
test_format_rdata("gAgBDgYAAAE=", 11, "128.8.1.14 6 23");
}
#[test]
fn test_format_rdata_for_rp_type() {
test_format_rdata_with_compressed_domain_names(
"Xc0BIAABAAAAAAABAnJwCGV4YW1wbGUxA2NvbQAAEQABAAApEAAAAAAAAAwACgAIMoUjsVrqjwo=",
"BWxvdWllB3RyYW50b3IDdW1kA2VkdQAETEFNMQZwZW9wbGUDdW1kA2VkdQA=",
17,
"louie.trantor.umd.edu. LAM1.people.umd.edu.",
);
}
#[test]
fn test_format_rdata_for_afsdb_type() {
test_format_rdata_with_compressed_domain_names(
"uaMBIAABAAAAAAABBWFmc2RiCGV4YW1wbGUxA2NvbQAAEgABAAApEAAAAAAAAAwACgAINy\
n/qwKTyVc=",
"AAEHYmlnYmlyZAd0b2FzdGVyA2NvbQA=",
18,
"1 bigbird.toaster.com.",
);
}
#[test]
fn test_format_rdata_for_x25_type() {
test_format_rdata("DDMxMTA2MTcwMDk1Ng==", 19, "\"311061700956\"");
}
#[test]
fn test_format_rdata_for_isdn_type() {
test_format_rdata("DzE1MDg2MjAyODAwMzIxNw==", 20, "\"150862028003217\"");
}
#[test]
fn test_format_rdata_for_rt_type() {
test_format_rdata_with_compressed_domain_names(
"K1cBEAABAAAAAAABAnJ0CGV4YW1wbGUxA2NvbQAAFQABAAApAgAAAIAAABwACgAY4Rzxu\
TfOxRwNw0bSX0VXy7WIF30GJ7DD",
"AAoCYWEHZXhhbXBsZQNjb20A",
21,
"10 aa.example.com.",
);
}
#[test]
fn test_format_rdata_for_nsap_type() {
test_format_rdata(
"RwAFgABaAAAAAAHhM////wABYQA=",
22,
"0x47000580005A0000000001E133FFFFFF00016100",
);
}
#[test]
fn test_format_rdata_for_px_type() {
test_format_rdata_with_compressed_domain_names(
"QF+BgAABAAEAAAABAnB4CGV4YW1wbGUxA2NvbQAAGgABwAwAGgABAAAOEAAlAAoEbmV0\
MgJpdAAJUFJNRC1uZXQyCUFETUQtcDQwMARDLWl0AAAAKRAAAAAAAAAcAAoAGDnSHBrTcxU1AQAAAF9FWK\
fIBBM9awy20w==",
"AAoEbmV0MgJpdAAJUFJNRC1uZXQyCUFETUQtcDQwMARDLWl0AA==",
26,
"10 net2.it. PRMD-net2.ADMD-p400.C-it.",
);
}
#[test]
fn test_format_rdata_for_loc_type() {
test_format_rdata(
"ADMWE4kXLdBwvhXwAJiNIA==",
29,
"42 21 54.000 N 71 6 18.000 W -24.00m 30m 10000m 10m",
);
}
#[test]
fn test_format_rdata_for_kx_type() {
test_format_rdata_with_compressed_domain_names(
"E4yBgAABAAEAAAABAmt4CGV4YW1wbGUxA2NvbQAAJAABwAwAJAABAAAOEAASAAoCYWEHZ\
XhhbXBsZQNjb20AAAApEAAAAAAAABwACgAYohY6RsSf9dsBAAAAX0VY5DfEoTM1iq9G",
"AAoCYWEHZXhhbXBsZQNjb20A",
36,
"10 aa.example.com.",
);
}
#[test]
fn test_format_rdata_for_cert_type() {
test_format_rdata(
"//7//wUzEVxvL2T/K950x9CArOEfl6vQy7+8gvPjkiSyRx4UaCJYKf8bEeFq\
LpUC4cCg1TPhihTW1V9IJKpBifr//XVTo2V3zSMR4LxpOs74oqYJpg==",
37,
"65534 65535 RSASHA1 MxFcby9k/yvedMfQgKzhH5er0Mu/vILz4\
5IkskceFGgiWCn/GxHhai6VAuHAoNUz4YoU1tVfSCSqQYn6//11U6Nld80jEeC8aTrO+KKmCaY=",
);
}
#[test]
fn test_format_rdata_for_a6_type() {
test_format_rdata(
"QBI0VniavN7wCFNVQk5FVC0xA0lQNghleGFtcGxlMQNjb20A",
38,
"64 ::1234:5678:9abc:def0 SUBNET-1.IP6.example1.com.",
);
}
#[test]
fn test_format_rdata_for_sink_type() {
test_format_rdata("AQIDdddd", 40, "1 2 3 dddd");
}
#[test]
fn test_format_rdata_for_apl_type() {
test_format_rdata(
"AAEVA8CoIAABHIPAqCY=",
42,
"1:192.168.32.0/21 !1:192.168.38.0/28",
);
test_format_rdata("AAEEAeAAAggB/w==", 42, "1:224.0.0.0/4 2:ff00::/8");
test_format_rdata(
"AAEVA8CoIAABHATAqCYsAAEdA8AAJgABHYPAACYAAR2EwAAmCA==",
42,
"1:192.168.32.0/21 1:192.168.38.44/28 \
1:192.0.38.0/29 !1:192.0.38.0/29 !1:192.0.38.8/29",
);
test_format_rdata(
"AAEVA8CoIAABHATAqCYsAAEdA8AAJg==",
42,
"1:192.168.32.0/21 1:192.168.38.44/28 1:192.0.38.0/29",
);
}
#[test]
fn test_format_rdata_for_dhcid_type() {
test_format_rdata(
"AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA=",
49,
"AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA=",
);
}
#[test]
fn test_format_rdata_for_spf_type() {
test_format_rdata(
"BnY9c3BmMQMrbXgVYTpjb2xvLmV4YW1wbGUuY29tLzI4BC1hbGw=",
99,
"\"v=spf1\" \"+mx\" \"a:colo.example.com/28\" \"-all\"",
);
}
fn test_format_rdata(raw_data: &str, code: u16, expected_output: &str) {
let raw_rdata = BASE64
.decode(raw_data.as_bytes())
.expect("Invalid base64 encoded rdata.");
let mut decoder = BinDecoder::new(&raw_rdata);
let record = Record::from_rdata(
Name::new(),
1,
RData::read(
&mut decoder,
RecordType::from(code),
Restrict::new(raw_rdata.len() as u16),
)
.unwrap(),
);
let rdata_text = DnsMessageParser::new(Vec::<u8>::new())
.parse_dns_record(&record)
.map(|r| r.rdata);
assert!(rdata_text.is_ok());
assert_eq!(expected_output, rdata_text.unwrap().unwrap());
}
fn test_format_rdata_with_compressed_domain_names(
raw_message: &str,
raw_data_encoded: &str,
code: u16,
expected_output: &str,
) {
let raw_message = BASE64
.decode(raw_message.as_bytes())
.expect("Invalid base64 encoded raw message.");
let raw_message_len = raw_message.len();
let mut message_parser = DnsMessageParser::new(raw_message);
let raw_rdata = BASE64
.decode(raw_data_encoded.as_bytes())
.expect("Invalid base64 encoded raw rdata.");
for i in 1..=2 {
let record_rdata = NULL::with(raw_rdata.clone());
let rdata_text = message_parser.format_unknown_rdata(code, &record_rdata);
assert!(rdata_text.is_ok());
assert_eq!(expected_output, rdata_text.unwrap().0.unwrap());
assert_eq!(
raw_message_len + i * raw_rdata.len(),
message_parser
.raw_message_for_rdata_parsing()
.unwrap()
.len()
);
}
}
}