use std::ffi::CString;
use std::fmt;
use std::ptr;
use crate::error::{Error, Result};
use crate::string::AresBuf;
use crate::utils::{dns_string_as_str, status_to_result};
use super::enums::{
DnsCls, DnsFlags, DnsOpcode, DnsParseFlags, DnsRcode, DnsRecordType, DnsSection,
};
use super::rr::DnsRr;
pub struct DnsRecord {
dnsrec: *mut c_ares_sys::ares_dns_record_t,
}
impl DnsRecord {
pub(crate) unsafe fn from_raw(dnsrec: *mut c_ares_sys::ares_dns_record_t) -> DnsRecord {
DnsRecord { dnsrec }
}
pub(crate) fn as_raw(&self) -> *const c_ares_sys::ares_dns_record_t {
self.dnsrec
}
pub fn parse(data: &[u8], flags: DnsParseFlags) -> Result<DnsRecord> {
let mut dnsrec: *mut c_ares_sys::ares_dns_record_t = ptr::null_mut();
let status = unsafe {
c_ares_sys::ares_dns_parse(data.as_ptr(), data.len(), flags.bits(), &raw mut dnsrec)
};
status_to_result(status)?;
Ok(DnsRecord { dnsrec })
}
pub fn id(&self) -> u16 {
unsafe { c_ares_sys::ares_dns_record_get_id(self.dnsrec) }
}
pub fn flags(&self) -> DnsFlags {
let raw = unsafe { c_ares_sys::ares_dns_record_get_flags(self.dnsrec) };
DnsFlags::from_bits_truncate(raw)
}
pub fn opcode(&self) -> DnsOpcode {
let raw = unsafe { c_ares_sys::ares_dns_record_get_opcode(self.dnsrec) };
DnsOpcode::from(raw)
}
pub fn rcode(&self) -> DnsRcode {
let raw = unsafe { c_ares_sys::ares_dns_record_get_rcode(self.dnsrec) };
DnsRcode::from(raw)
}
pub fn query_count(&self) -> usize {
unsafe { c_ares_sys::ares_dns_record_query_cnt(self.dnsrec) }
}
pub fn query_get(&self, idx: usize) -> Result<(&str, DnsRecordType, DnsCls)> {
let mut name: *const core::ffi::c_char = ptr::null();
let mut qtype = c_ares_sys::ares_dns_rec_type_t::ARES_REC_TYPE_A;
let mut qclass = c_ares_sys::ares_dns_class_t::ARES_CLASS_IN;
let status = unsafe {
c_ares_sys::ares_dns_record_query_get(
self.dnsrec,
idx,
&raw mut name,
&raw mut qtype,
&raw mut qclass,
)
};
status_to_result(status)?;
let name_str = unsafe { dns_string_as_str(name) };
Ok((name_str, DnsRecordType::from(qtype), DnsCls::from(qclass)))
}
pub fn queries(&self) -> impl Iterator<Item = (&str, DnsRecordType, DnsCls)> {
(0..self.query_count()).map_while(move |i| self.query_get(i).ok())
}
pub fn rr_count(&self, section: DnsSection) -> usize {
unsafe { c_ares_sys::ares_dns_record_rr_cnt(self.dnsrec, section.into()) }
}
pub fn rr(&self, section: DnsSection, idx: usize) -> Option<&DnsRr> {
let rr =
unsafe { c_ares_sys::ares_dns_record_rr_get_const(self.dnsrec, section.into(), idx) };
(!rr.is_null()).then(|| unsafe { &*rr.cast::<DnsRr>() })
}
pub fn rrs(&self, section: DnsSection) -> impl Iterator<Item = &DnsRr> {
(0..self.rr_count(section)).map_while(move |i| self.rr(section, i))
}
pub fn new(id: u16, flags: DnsFlags, opcode: DnsOpcode, rcode: DnsRcode) -> Result<DnsRecord> {
let mut dnsrec: *mut c_ares_sys::ares_dns_record_t = ptr::null_mut();
let status = unsafe {
c_ares_sys::ares_dns_record_create(
&raw mut dnsrec,
id,
flags.bits(),
opcode.into(),
rcode.into(),
)
};
status_to_result(status)?;
Ok(DnsRecord { dnsrec })
}
pub fn set_id(&mut self, id: u16) -> &mut Self {
unsafe { c_ares_sys::ares_dns_record_set_id(self.dnsrec, id) };
self
}
pub fn query_add(
&mut self,
name: &str,
qtype: DnsRecordType,
qclass: DnsCls,
) -> Result<&mut Self> {
let c_name = CString::new(name).map_err(|_| Error::EBADSTR)?;
let status = unsafe {
c_ares_sys::ares_dns_record_query_add(
self.dnsrec,
c_name.as_ptr(),
qtype.into(),
qclass.into(),
)
};
status_to_result(status)?;
Ok(self)
}
pub fn query_set_name(&mut self, idx: usize, name: &str) -> Result<&mut Self> {
let c_name = CString::new(name).map_err(|_| Error::EBADSTR)?;
let status = unsafe {
c_ares_sys::ares_dns_record_query_set_name(self.dnsrec, idx, c_name.as_ptr())
};
status_to_result(status)?;
Ok(self)
}
pub fn query_set_type(&mut self, idx: usize, qtype: DnsRecordType) -> Result<&mut Self> {
let status =
unsafe { c_ares_sys::ares_dns_record_query_set_type(self.dnsrec, idx, qtype.into()) };
status_to_result(status)?;
Ok(self)
}
pub fn rr_add(
&mut self,
section: DnsSection,
name: &str,
rr_type: DnsRecordType,
rclass: DnsCls,
ttl: u32,
) -> Result<&mut DnsRr> {
let c_name = CString::new(name).map_err(|_| Error::EBADSTR)?;
let mut rr_out: *mut c_ares_sys::ares_dns_rr_t = ptr::null_mut();
let status = unsafe {
c_ares_sys::ares_dns_record_rr_add(
&raw mut rr_out,
self.dnsrec,
section.into(),
c_name.as_ptr(),
rr_type.into(),
rclass.into(),
ttl,
)
};
status_to_result(status)?;
Ok(unsafe { &mut *rr_out.cast::<DnsRr>() })
}
pub fn rr_mut(&mut self, section: DnsSection, idx: usize) -> Option<&mut DnsRr> {
let rr = unsafe { c_ares_sys::ares_dns_record_rr_get(self.dnsrec, section.into(), idx) };
(!rr.is_null()).then(|| unsafe { &mut *rr.cast::<DnsRr>() })
}
pub fn rrs_mut(&mut self, section: DnsSection) -> impl Iterator<Item = &mut DnsRr> {
let count = self.rr_count(section);
let dnsrec = self.dnsrec;
(0..count).map_while(move |i| {
let rr = unsafe { c_ares_sys::ares_dns_record_rr_get(dnsrec, section.into(), i) };
(!rr.is_null()).then(|| unsafe { &mut *rr.cast::<DnsRr>() })
})
}
pub fn rr_del(&mut self, section: DnsSection, idx: usize) -> Result<&mut Self> {
let status =
unsafe { c_ares_sys::ares_dns_record_rr_del(self.dnsrec, section.into(), idx) };
status_to_result(status)?;
Ok(self)
}
pub fn write(&self) -> Result<AresBuf> {
let mut buf: *mut core::ffi::c_uchar = ptr::null_mut();
let mut buf_len: usize = 0;
let status =
unsafe { c_ares_sys::ares_dns_write(self.dnsrec, &raw mut buf, &raw mut buf_len) };
status_to_result(status)?;
Ok(AresBuf::new(buf, buf_len))
}
pub fn try_clone(&self) -> Result<DnsRecord> {
let dup = unsafe { c_ares_sys::ares_dns_record_duplicate(self.dnsrec) };
if dup.is_null() {
Err(Error::ENOMEM)
} else {
Ok(DnsRecord { dnsrec: dup })
}
}
}
impl Drop for DnsRecord {
fn drop(&mut self) {
unsafe { c_ares_sys::ares_dns_record_destroy(self.dnsrec) }
}
}
unsafe impl Send for DnsRecord {}
unsafe impl Sync for DnsRecord {}
impl fmt::Debug for DnsRecord {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DnsRecord")
.field("id", &self.id())
.field("flags", &self.flags())
.field("opcode", &self.opcode())
.field("rcode", &self.rcode())
.field("query_count", &self.query_count())
.field("answer_count", &self.rr_count(DnsSection::Answer))
.field("authority_count", &self.rr_count(DnsSection::Authority))
.field("additional_count", &self.rr_count(DnsSection::Additional))
.finish()
}
}
#[cfg(test)]
mod tests {
use std::net::Ipv4Addr;
use crate::dns::*;
use crate::error::Error;
#[test]
fn parse_invalid_data() {
let result = DnsRecord::parse(&[], DnsParseFlags::empty());
assert!(result.is_err());
}
#[test]
fn parse_short_data() {
let result = DnsRecord::parse(&[0; 4], DnsParseFlags::empty());
assert!(result.is_err());
}
#[test]
fn parse_minimal_query() {
#[rustfmt::skip]
let data: &[u8] = &[
0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, ];
let rec = DnsRecord::parse(data, DnsParseFlags::empty()).expect("parse should succeed");
assert_eq!(rec.id(), 1);
assert!(rec.flags().contains(DnsFlags::RD));
assert_eq!(rec.opcode(), DnsOpcode::Query);
assert_eq!(rec.rcode(), DnsRcode::NoError);
assert_eq!(rec.query_count(), 1);
let (name, qtype, qclass) = rec.query_get(0).expect("query_get should succeed");
assert_eq!(name, "");
assert_eq!(qtype, DnsRecordType::A);
assert_eq!(qclass, DnsCls::IN);
assert_eq!(rec.rr_count(DnsSection::Answer), 0);
assert!(rec.rr(DnsSection::Answer, 0).is_none());
}
#[test]
fn query_get_preserves_unknown_query_type() {
#[rustfmt::skip]
let data: &[u8] = &[
0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x01, ];
let rec = DnsRecord::parse(data, DnsParseFlags::empty()).expect("parse should succeed");
assert_eq!(rec.query_count(), 1);
let (_, qtype, qclass) = rec.query_get(0).expect("query_get");
assert_eq!(qtype, DnsRecordType::UNKNOWN(0xff00));
assert_eq!(qclass, DnsCls::IN);
}
#[test]
fn parse_a_response() {
#[rustfmt::skip]
let data: &[u8] = &[
0x00, 0x02, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, b'e', b'x', b'a', b'm', b'p', b'l', b'e',
0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x2c, 0x00, 0x04, 0x01, 0x02, 0x03, 0x04, ];
let rec = DnsRecord::parse(data, DnsParseFlags::empty()).expect("parse should succeed");
assert_eq!(rec.id(), 2);
assert!(rec.flags().contains(DnsFlags::QR));
assert!(rec.flags().contains(DnsFlags::RD));
assert!(rec.flags().contains(DnsFlags::RA));
assert_eq!(rec.rr_count(DnsSection::Answer), 1);
let rr = rec.rr(DnsSection::Answer, 0).expect("rr should exist");
assert_eq!(rr.rr_type(), DnsRecordType::A);
assert_eq!(rr.dns_class(), DnsCls::IN);
assert_eq!(rr.ttl(), 300);
assert_eq!(rr.name(), "example");
let addr = rr.get_addr(DnsRrKey::A_ADDR).expect("should have addr");
assert_eq!(addr, Ipv4Addr::new(1, 2, 3, 4));
}
#[test]
fn rr_preserves_unknown_class_on_raw_rr() {
#[rustfmt::skip]
let data: &[u8] = &[
0x00, 0x03, 0x81, 0x80, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, b'e', b'x', b'a', b'm', b'p', b'l', b'e',
0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
let rec = DnsRecord::parse(data, DnsParseFlags::empty()).expect("parse should succeed");
let rr = rec.rr(DnsSection::Answer, 0).expect("rr should exist");
assert_eq!(rr.rr_type(), DnsRecordType::RAW_RR);
assert_eq!(rr.dns_class(), DnsCls::UNKNOWN(0xff00));
assert!(matches!(rr.as_typed(), TypedRr::RawRr(_)));
}
#[test]
fn dns_record_is_send() {
fn assert_send<T: Send>() {}
assert_send::<DnsRecord>();
}
#[test]
fn dns_record_is_sync() {
fn assert_sync<T: Sync>() {}
assert_sync::<DnsRecord>();
}
#[test]
fn create_empty_record() {
let rec = DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError)
.expect("create should succeed");
assert_eq!(rec.id(), 0);
assert!(rec.flags().contains(DnsFlags::RD));
assert_eq!(rec.opcode(), DnsOpcode::Query);
assert_eq!(rec.rcode(), DnsRcode::NoError);
assert_eq!(rec.query_count(), 0);
}
#[test]
fn create_and_add_query() {
let mut rec = DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError)
.expect("create should succeed");
rec.query_add("example.com", DnsRecordType::A, DnsCls::IN)
.expect("query_add should succeed");
assert_eq!(rec.query_count(), 1);
let (name, qtype, qclass) = rec.query_get(0).expect("query_get should succeed");
assert_eq!(name, "example.com");
assert_eq!(qtype, DnsRecordType::A);
assert_eq!(qclass, DnsCls::IN);
}
#[test]
fn set_id() {
let mut rec = DnsRecord::new(0, DnsFlags::empty(), DnsOpcode::Query, DnsRcode::NoError)
.expect("create should succeed");
assert_eq!(rec.id(), 0);
rec.set_id(42);
assert_eq!(rec.id(), 42);
}
#[test]
fn query_set_name_and_type() {
let mut rec = DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError)
.expect("create should succeed");
rec.query_add("example.com", DnsRecordType::A, DnsCls::IN)
.expect("query_add");
rec.query_set_name(0, "other.com").expect("set_name");
rec.query_set_type(0, DnsRecordType::AAAA)
.expect("set_type");
let (name, qtype, _) = rec.query_get(0).expect("query_get");
assert_eq!(name, "other.com");
assert_eq!(qtype, DnsRecordType::AAAA);
}
#[test]
fn rr_mut_and_rr_del() {
let mut rec =
DnsRecord::new(0, DnsFlags::QR, DnsOpcode::Query, DnsRcode::NoError).expect("create");
rec.query_add("example.com", DnsRecordType::MX, DnsCls::IN)
.expect("query_add");
let rr = rec
.rr_add(
DnsSection::Answer,
"example.com",
DnsRecordType::MX,
DnsCls::IN,
600,
)
.expect("rr_add");
rr.set_u16(DnsRrKey::MX_PREFERENCE, 10).expect("set_u16");
rr.set_str(DnsRrKey::MX_EXCHANGE, "mail.example.com")
.expect("set_str");
let rr = rec.rr_mut(DnsSection::Answer, 0).expect("rr_mut");
assert_eq!(rr.get_u16(DnsRrKey::MX_PREFERENCE), 10);
assert_eq!(rr.get_str(DnsRrKey::MX_EXCHANGE), Some("mail.example.com"));
rec.rr_del(DnsSection::Answer, 0).expect("rr_del");
assert_eq!(rec.rr_count(DnsSection::Answer), 0);
}
#[test]
fn rrs_mut_update_ttl() {
let mut rec =
DnsRecord::new(0, DnsFlags::QR, DnsOpcode::Query, DnsRcode::NoError).expect("create");
rec.query_add("example.com", DnsRecordType::A, DnsCls::IN)
.expect("query_add");
for i in 0..3u8 {
let rr = rec
.rr_add(
DnsSection::Answer,
"example.com",
DnsRecordType::A,
DnsCls::IN,
300,
)
.expect("rr_add");
rr.set_addr(DnsRrKey::A_ADDR, Ipv4Addr::new(10, 0, 0, i))
.expect("set_addr");
}
for rr in rec.rrs_mut(DnsSection::Answer) {
let addr = rr.get_addr(DnsRrKey::A_ADDR).expect("get_addr");
let octets = addr.octets();
rr.set_addr(
DnsRrKey::A_ADDR,
Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3] + 100),
)
.expect("set_addr");
}
let addrs: Vec<_> = rec
.rrs(DnsSection::Answer)
.map(|rr| rr.get_addr(DnsRrKey::A_ADDR).unwrap())
.collect();
assert_eq!(
addrs,
vec![
Ipv4Addr::new(10, 0, 0, 100),
Ipv4Addr::new(10, 0, 0, 101),
Ipv4Addr::new(10, 0, 0, 102),
]
);
}
#[test]
fn write_and_reparse() {
let mut rec =
DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).expect("create");
rec.query_add("example.com", DnsRecordType::A, DnsCls::IN)
.expect("query_add");
let wire = rec.write().expect("write");
assert!(!wire.is_empty());
let rec2 = DnsRecord::parse(&wire, DnsParseFlags::empty()).expect("parse");
assert_eq!(rec2.query_count(), 1);
let (name, qtype, qclass) = rec2.query_get(0).expect("query_get");
assert_eq!(name, "example.com");
assert_eq!(qtype, DnsRecordType::A);
assert_eq!(qclass, DnsCls::IN);
}
#[test]
fn duplicate_record() {
let mut rec =
DnsRecord::new(42, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).expect("create");
rec.query_add("test.org", DnsRecordType::AAAA, DnsCls::IN)
.expect("query_add");
let dup = rec.try_clone().expect("try_clone");
assert_eq!(dup.id(), 42);
assert_eq!(dup.query_count(), 1);
let (name, qtype, _) = dup.query_get(0).expect("query_get");
assert_eq!(name, "test.org");
assert_eq!(qtype, DnsRecordType::AAAA);
}
#[test]
fn rr_out_of_range() {
let rec =
DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).expect("create");
assert!(rec.rr(DnsSection::Answer, 0).is_none());
assert!(rec.rr(DnsSection::Answer, 100).is_none());
assert!(rec.rr(DnsSection::Authority, 0).is_none());
assert!(rec.rr(DnsSection::Additional, 0).is_none());
}
#[test]
fn rr_mut_out_of_range() {
let mut rec =
DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).expect("create");
assert!(rec.rr_mut(DnsSection::Answer, 0).is_none());
assert!(rec.rr_mut(DnsSection::Answer, 100).is_none());
}
#[test]
fn rr_del_out_of_range() {
let mut rec =
DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).expect("create");
let result = rec.rr_del(DnsSection::Answer, 0);
assert!(result.is_err());
}
#[test]
fn multiple_queries() {
let mut rec =
DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).expect("create");
rec.query_add("a.example.com", DnsRecordType::A, DnsCls::IN)
.expect("add 0");
rec.query_add("b.example.com", DnsRecordType::AAAA, DnsCls::IN)
.expect("add 1");
rec.query_add("c.example.com", DnsRecordType::MX, DnsCls::IN)
.expect("add 2");
assert_eq!(rec.query_count(), 3);
let (n, t, _) = rec.query_get(0).expect("get 0");
assert_eq!(n, "a.example.com");
assert_eq!(t, DnsRecordType::A);
let (n, t, _) = rec.query_get(1).expect("get 1");
assert_eq!(n, "b.example.com");
assert_eq!(t, DnsRecordType::AAAA);
let (n, t, _) = rec.query_get(2).expect("get 2");
assert_eq!(n, "c.example.com");
assert_eq!(t, DnsRecordType::MX);
}
#[test]
fn multiple_rrs_same_section() {
let mut rec =
DnsRecord::new(0, DnsFlags::QR, DnsOpcode::Query, DnsRcode::NoError).expect("create");
rec.query_add("example.com", DnsRecordType::A, DnsCls::IN)
.expect("query_add");
for i in 0..5 {
let rr = rec
.rr_add(
DnsSection::Answer,
"example.com",
DnsRecordType::A,
DnsCls::IN,
300,
)
.expect("rr_add");
rr.set_addr(DnsRrKey::A_ADDR, Ipv4Addr::new(10, 0, 0, i))
.expect("set_addr");
}
assert_eq!(rec.rr_count(DnsSection::Answer), 5);
for i in 0..5 {
let rr = rec.rr(DnsSection::Answer, i).expect("rr");
assert_eq!(
rr.get_addr(DnsRrKey::A_ADDR),
Some(Ipv4Addr::new(10, 0, 0, i as u8))
);
}
}
#[test]
fn authority_and_additional_sections() {
let mut rec =
DnsRecord::new(0, DnsFlags::QR, DnsOpcode::Query, DnsRcode::NoError).expect("create");
rec.query_add("example.com", DnsRecordType::A, DnsCls::IN)
.expect("query_add");
let rr = rec
.rr_add(
DnsSection::Authority,
"example.com",
DnsRecordType::NS,
DnsCls::IN,
3600,
)
.expect("rr_add authority");
rr.set_str(DnsRrKey::NS_NSDNAME, "ns1.example.com")
.expect("set ns name");
let rr = rec
.rr_add(
DnsSection::Additional,
"ns1.example.com",
DnsRecordType::A,
DnsCls::IN,
3600,
)
.expect("rr_add additional");
rr.set_addr(DnsRrKey::A_ADDR, Ipv4Addr::new(192, 0, 2, 1))
.expect("set_addr");
assert_eq!(rec.rr_count(DnsSection::Answer), 0);
assert_eq!(rec.rr_count(DnsSection::Authority), 1);
assert_eq!(rec.rr_count(DnsSection::Additional), 1);
let ns = rec.rr(DnsSection::Authority, 0).expect("rr authority");
assert_eq!(ns.rr_type(), DnsRecordType::NS);
assert_eq!(ns.get_str(DnsRrKey::NS_NSDNAME), Some("ns1.example.com"));
let a = rec.rr(DnsSection::Additional, 0).expect("rr additional");
assert_eq!(a.rr_type(), DnsRecordType::A);
assert_eq!(
a.get_addr(DnsRrKey::A_ADDR),
Some(Ipv4Addr::new(192, 0, 2, 1))
);
}
#[test]
fn write_reparse_with_answer() {
let mut rec = DnsRecord::new(
100,
DnsFlags::QR | DnsFlags::RD | DnsFlags::RA,
DnsOpcode::Query,
DnsRcode::NoError,
)
.expect("create");
rec.query_add("example.com", DnsRecordType::A, DnsCls::IN)
.expect("query_add");
let rr = rec
.rr_add(
DnsSection::Answer,
"example.com",
DnsRecordType::A,
DnsCls::IN,
300,
)
.expect("rr_add");
rr.set_addr(DnsRrKey::A_ADDR, Ipv4Addr::new(93, 184, 216, 34))
.expect("set_addr");
let wire = rec.write().expect("write");
let rec2 = DnsRecord::parse(&wire, DnsParseFlags::empty()).expect("reparse");
assert_eq!(rec2.id(), 100);
assert!(rec2.flags().contains(DnsFlags::QR));
assert!(rec2.flags().contains(DnsFlags::RD));
assert!(rec2.flags().contains(DnsFlags::RA));
assert_eq!(rec2.rr_count(DnsSection::Answer), 1);
let rr = rec2.rr(DnsSection::Answer, 0).expect("rr");
assert_eq!(
rr.get_addr(DnsRrKey::A_ADDR),
Some(Ipv4Addr::new(93, 184, 216, 34))
);
}
#[test]
fn soa_write_reparse() {
let mut rec = DnsRecord::new(
5,
DnsFlags::QR | DnsFlags::AA | DnsFlags::RD,
DnsOpcode::Query,
DnsRcode::NoError,
)
.expect("create");
rec.query_add("example.com", DnsRecordType::SOA, DnsCls::IN)
.expect("query_add");
let rr = rec
.rr_add(
DnsSection::Answer,
"example.com",
DnsRecordType::SOA,
DnsCls::IN,
86400,
)
.expect("rr_add");
rr.set_str(DnsRrKey::SOA_MNAME, "ns1.example.com")
.expect("mname");
rr.set_str(DnsRrKey::SOA_RNAME, "hostmaster.example.com")
.expect("rname");
rr.set_u32(DnsRrKey::SOA_SERIAL, 1).expect("serial");
rr.set_u32(DnsRrKey::SOA_REFRESH, 3600).expect("refresh");
rr.set_u32(DnsRrKey::SOA_RETRY, 900).expect("retry");
rr.set_u32(DnsRrKey::SOA_EXPIRE, 604800).expect("expire");
rr.set_u32(DnsRrKey::SOA_MINIMUM, 60).expect("minimum");
let wire = rec.write().expect("write");
let rec2 = DnsRecord::parse(&wire, DnsParseFlags::empty()).expect("reparse");
assert_eq!(rec2.id(), 5);
assert!(rec2.flags().contains(DnsFlags::AA));
let rr = rec2.rr(DnsSection::Answer, 0).expect("rr");
assert_eq!(rr.get_str(DnsRrKey::SOA_MNAME), Some("ns1.example.com"));
assert_eq!(
rr.get_str(DnsRrKey::SOA_RNAME),
Some("hostmaster.example.com")
);
assert_eq!(rr.get_u32(DnsRrKey::SOA_SERIAL), 1);
assert_eq!(rr.get_u32(DnsRrKey::SOA_EXPIRE), 604800);
}
#[test]
fn txt_abin_write_reparse() {
let mut rec =
DnsRecord::new(0, DnsFlags::QR, DnsOpcode::Query, DnsRcode::NoError).expect("create");
rec.query_add("example.com", DnsRecordType::TXT, DnsCls::IN)
.expect("query_add");
let rr = rec
.rr_add(
DnsSection::Answer,
"example.com",
DnsRecordType::TXT,
DnsCls::IN,
300,
)
.expect("rr_add");
rr.add_abin(DnsRrKey::TXT_DATA, b"v=spf1 include:example.com ~all")
.expect("add_abin");
let wire = rec.write().expect("write");
let rec2 = DnsRecord::parse(&wire, DnsParseFlags::empty()).expect("reparse");
let rr = rec2.rr(DnsSection::Answer, 0).expect("rr");
assert_eq!(rr.get_abin_count(DnsRrKey::TXT_DATA), 1);
assert_eq!(
rr.get_abin(DnsRrKey::TXT_DATA, 0).unwrap(),
b"v=spf1 include:example.com ~all"
);
}
#[test]
fn query_add_rejects_null_byte() {
let mut rec =
DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).expect("create");
let result = rec.query_add("ex\0ample.com", DnsRecordType::A, DnsCls::IN);
assert!(matches!(result, Err(Error::EBADSTR)));
}
#[test]
fn query_set_name_rejects_null_byte() {
let mut rec =
DnsRecord::new(0, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).expect("create");
rec.query_add("example.com", DnsRecordType::A, DnsCls::IN)
.expect("query_add");
let result = rec.query_set_name(0, "ex\0ample.com");
assert!(matches!(result, Err(Error::EBADSTR)));
}
#[test]
fn rr_add_rejects_null_byte() {
let mut rec =
DnsRecord::new(0, DnsFlags::QR, DnsOpcode::Query, DnsRcode::NoError).expect("create");
rec.query_add("example.com", DnsRecordType::A, DnsCls::IN)
.expect("query_add");
let result = rec.rr_add(
DnsSection::Answer,
"ex\0ample.com",
DnsRecordType::A,
DnsCls::IN,
300,
);
assert!(matches!(result, Err(Error::EBADSTR)));
}
#[test]
fn debug_dns_record() {
let rec =
DnsRecord::new(42, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).expect("create");
let debug = format!("{rec:?}");
assert!(debug.contains("DnsRecord"));
assert!(debug.contains("42"));
}
#[test]
fn clone_dns_record() {
let mut rec =
DnsRecord::new(7, DnsFlags::RD, DnsOpcode::Query, DnsRcode::NoError).expect("create");
rec.query_add("example.com", DnsRecordType::A, DnsCls::IN)
.expect("query_add");
let cloned = rec.try_clone().expect("try_clone");
assert_eq!(cloned.id(), 7);
assert_eq!(cloned.query_count(), 1);
let (name, qtype, _) = cloned.query_get(0).expect("query_get");
assert_eq!(name, "example.com");
assert_eq!(qtype, DnsRecordType::A);
rec.set_id(99);
assert_eq!(cloned.id(), 7);
}
}