use std::cmp::Ordering;
use std::fmt;
#[cfg(feature = "serde-config")]
use serde::{Deserialize, Serialize};
use crate::error::*;
use crate::rr::dns_class::DNSClass;
use crate::rr::rdata::NULL;
#[allow(deprecated)]
use crate::rr::IntoRecordSet;
use crate::rr::Name;
use crate::rr::RData;
use crate::rr::RecordSet;
use crate::rr::RecordType;
use crate::serialize::binary::*;
#[cfg(feature = "mdns")]
const MDNS_ENABLE_CACHE_FLUSH: u16 = 1 << 15;
const NULL_RDATA: &RData = &RData::NULL(NULL::new());
#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
#[derive(Eq, Debug, Clone)]
pub struct Record {
name_labels: Name,
rr_type: RecordType,
dns_class: DNSClass,
ttl: u32,
rdata: Option<RData>,
#[cfg(feature = "mdns")]
mdns_cache_flush: bool,
}
impl Default for Record {
fn default() -> Self {
Self {
name_labels: Name::new(),
rr_type: RecordType::NULL,
dns_class: DNSClass::IN,
ttl: 0,
rdata: None,
#[cfg(feature = "mdns")]
mdns_cache_flush: false,
}
}
}
impl Record {
pub fn new() -> Self {
Self::default()
}
pub fn with(name: Name, rr_type: RecordType, ttl: u32) -> Self {
Self {
name_labels: name,
rr_type,
dns_class: DNSClass::IN,
ttl,
rdata: None,
#[cfg(feature = "mdns")]
mdns_cache_flush: false,
}
}
pub fn from_rdata(name: Name, ttl: u32, rdata: RData) -> Self {
Self {
name_labels: name,
rr_type: rdata.to_record_type(),
dns_class: DNSClass::IN,
ttl,
rdata: Some(rdata),
#[cfg(feature = "mdns")]
mdns_cache_flush: false,
}
}
pub fn set_name(&mut self, name: Name) -> &mut Self {
self.name_labels = name;
self
}
pub fn set_rr_type(&mut self, rr_type: RecordType) -> &mut Self {
self.rr_type = rr_type;
self
}
pub fn set_record_type(&mut self, rr_type: RecordType) -> &mut Self {
self.rr_type = rr_type;
self
}
pub fn set_dns_class(&mut self, dns_class: DNSClass) -> &mut Self {
self.dns_class = dns_class;
self
}
pub fn set_ttl(&mut self, ttl: u32) -> &mut Self {
self.ttl = ttl;
self
}
#[deprecated(note = "use `Record::set_data` instead")]
pub fn set_rdata(&mut self, rdata: RData) -> &mut Self {
self.rdata = Some(rdata);
self
}
#[allow(deprecated)]
pub fn set_data(&mut self, rdata: Option<RData>) -> &mut Self {
debug_assert!(
!(matches!(&rdata, Some(RData::ZERO))
&& matches!(&rdata, Some(RData::NULL(null)) if null.anything().is_empty())),
"pass None rather than ZERO or NULL"
);
self.rdata = rdata;
self
}
#[cfg(feature = "mdns")]
#[cfg_attr(docsrs, doc(cfg(feature = "mdns")))]
pub fn set_mdns_cache_flush(&mut self, flag: bool) -> &mut Self {
self.mdns_cache_flush = flag;
self
}
pub fn name(&self) -> &Name {
&self.name_labels
}
pub fn rr_type(&self) -> RecordType {
self.rr_type
}
pub fn record_type(&self) -> RecordType {
self.rr_type
}
pub fn dns_class(&self) -> DNSClass {
self.dns_class
}
pub fn ttl(&self) -> u32 {
self.ttl
}
#[deprecated(note = "use `Record::data` instead")]
pub fn rdata(&self) -> &RData {
if let Some(ref rdata) = &self.rdata {
rdata
} else {
NULL_RDATA
}
}
pub fn data(&self) -> Option<&RData> {
self.rdata.as_ref()
}
#[cfg(feature = "mdns")]
#[cfg_attr(docsrs, doc(cfg(feature = "mdns")))]
pub fn mdns_cache_flush(&self) -> bool {
self.mdns_cache_flush
}
pub fn data_mut(&mut self) -> Option<&mut RData> {
self.rdata.as_mut()
}
pub fn into_data(self) -> Option<RData> {
self.rdata
}
pub fn into_parts(self) -> RecordParts {
self.into()
}
}
pub struct RecordParts {
pub name_labels: Name,
pub rr_type: RecordType,
pub dns_class: DNSClass,
pub ttl: u32,
pub rdata: Option<RData>,
#[cfg(feature = "mdns")]
#[cfg_attr(docsrs, doc(cfg(feature = "mdns")))]
pub mdns_cache_flush: bool,
}
impl From<Record> for RecordParts {
fn from(record: Record) -> Self {
cfg_if::cfg_if! {
if #[cfg(feature = "mdns")] {
let Record {
name_labels,
rr_type,
dns_class,
ttl,
rdata,
mdns_cache_flush,
} = record;
} else {
let Record {
name_labels,
rr_type,
dns_class,
ttl,
rdata,
} = record;
}
}
Self {
name_labels,
rr_type,
dns_class,
ttl,
rdata,
#[cfg(feature = "mdns")]
mdns_cache_flush,
}
}
}
#[allow(deprecated)]
impl IntoRecordSet for Record {
fn into_record_set(self) -> RecordSet {
RecordSet::from(self)
}
}
impl BinEncodable for Record {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
self.name_labels.emit(encoder)?;
self.rr_type.emit(encoder)?;
#[cfg(not(feature = "mdns"))]
self.dns_class.emit(encoder)?;
#[cfg(feature = "mdns")]
{
if self.mdns_cache_flush {
encoder.emit_u16(u16::from(self.dns_class()) | MDNS_ENABLE_CACHE_FLUSH)?;
} else {
self.dns_class.emit(encoder)?;
}
}
encoder.emit_u32(self.ttl)?;
let place = encoder.place::<u16>()?;
if let Some(rdata) = &self.rdata {
rdata.emit(encoder)?;
}
let len = encoder.len_since_place(&place);
assert!(len <= u16::max_value() as usize);
place.replace(encoder, len as u16)?;
Ok(())
}
}
impl<'r> BinDecodable<'r> for Record {
fn read(decoder: &mut BinDecoder<'r>) -> ProtoResult<Self> {
let name_labels: Name = Name::read(decoder)?;
let record_type: RecordType = RecordType::read(decoder)?;
#[cfg(feature = "mdns")]
let mut mdns_cache_flush = false;
let class: DNSClass = if record_type == RecordType::OPT {
if !name_labels.is_root() {
return Err(ProtoErrorKind::EdnsNameNotRoot(name_labels).into());
}
DNSClass::for_opt(
decoder.read_u16()?.unverified(),
)
} else {
#[cfg(not(feature = "mdns"))]
{
DNSClass::read(decoder)?
}
#[cfg(feature = "mdns")]
{
let dns_class_value =
decoder.read_u16()?.unverified();
if dns_class_value & MDNS_ENABLE_CACHE_FLUSH > 0 {
mdns_cache_flush = true;
DNSClass::from_u16(dns_class_value & !MDNS_ENABLE_CACHE_FLUSH)?
} else {
DNSClass::from_u16(dns_class_value)?
}
}
};
let ttl: u32 = decoder.read_u32()?.unverified();
let rd_length = decoder
.read_u16()?
.verify_unwrap(|u| (*u as usize) <= decoder.len())
.map_err(|u| {
ProtoError::from(format!(
"rdata length too large for remaining bytes, need: {} remain: {}",
u,
decoder.len()
))
})?;
let rdata = if rd_length == 0 {
None
} else {
Some(RData::read(decoder, record_type, Restrict::new(rd_length))?)
};
Ok(Self {
name_labels,
rr_type: record_type,
dns_class: class,
ttl,
rdata,
#[cfg(feature = "mdns")]
mdns_cache_flush,
})
}
}
impl fmt::Display for Record {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"{name} {ttl} {class} {ty}",
name = self.name_labels,
ttl = self.ttl,
class = self.dns_class,
ty = self.rr_type,
)?;
if let Some(rdata) = &self.rdata {
write!(f, " {rdata}", rdata = rdata)?;
}
Ok(())
}
}
impl PartialEq for Record {
fn eq(&self, other: &Self) -> bool {
self.name_labels == other.name_labels
&& self.rr_type == other.rr_type
&& self.dns_class == other.dns_class
&& self.rdata == other.rdata
}
}
macro_rules! compare_or_equal {
($x:ident, $y:ident, $z:ident) => {
match $x.$z.cmp(&$y.$z) {
o @ Ordering::Less | o @ Ordering::Greater => return o,
Ordering::Equal => (),
}
};
}
impl Ord for Record {
fn cmp(&self, other: &Self) -> Ordering {
compare_or_equal!(self, other, name_labels);
compare_or_equal!(self, other, rr_type);
compare_or_equal!(self, other, dns_class);
compare_or_equal!(self, other, ttl);
compare_or_equal!(self, other, rdata);
Ordering::Equal
}
}
impl PartialOrd<Self> for Record {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]
use std::cmp::Ordering;
use std::net::Ipv4Addr;
use std::str::FromStr;
use super::*;
use crate::rr::dns_class::DNSClass;
use crate::rr::record_data::RData;
use crate::rr::record_type::RecordType;
use crate::rr::Name;
#[allow(clippy::useless_attribute)]
#[allow(unused)]
use crate::serialize::binary::*;
#[test]
fn test_emit_and_read() {
let mut record = Record::new();
record
.set_name(Name::from_str("www.example.com").unwrap())
.set_rr_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_ttl(5)
.set_data(Some(RData::A(Ipv4Addr::new(192, 168, 0, 1))));
let mut vec_bytes: Vec<u8> = Vec::with_capacity(512);
{
let mut encoder = BinEncoder::new(&mut vec_bytes);
record.emit(&mut encoder).unwrap();
}
let mut decoder = BinDecoder::new(&vec_bytes);
let got = Record::read(&mut decoder).unwrap();
assert_eq!(got, record);
}
#[test]
fn test_order() {
let mut record = Record::new();
record
.set_name(Name::from_str("www.example.com").unwrap())
.set_rr_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_ttl(5)
.set_data(Some(RData::A(Ipv4Addr::new(192, 168, 0, 1))));
let mut greater_name = record.clone();
greater_name.set_name(Name::from_str("zzz.example.com").unwrap());
let mut greater_type = record.clone();
greater_type.set_rr_type(RecordType::AAAA);
let mut greater_class = record.clone();
greater_class.set_dns_class(DNSClass::NONE);
let mut greater_rdata = record.clone();
greater_rdata.set_data(Some(RData::A(Ipv4Addr::new(192, 168, 0, 255))));
let compares = vec![
(&record, &greater_name),
(&record, &greater_type),
(&record, &greater_class),
(&record, &greater_rdata),
];
assert_eq!(record.clone(), record.clone());
for (r, g) in compares {
println!("r, g: {:?}, {:?}", r, g);
assert_eq!(r.cmp(g), Ordering::Less);
}
}
#[cfg(feature = "mdns")]
#[test]
fn test_mdns_cache_flush_bit_handling() {
const RR_CLASS_OFFSET: usize = 1 +
std::mem::size_of::<u16>() ;
let mut record = Record::new();
record.set_mdns_cache_flush(true);
let mut vec_bytes: Vec<u8> = Vec::with_capacity(512);
{
let mut encoder = BinEncoder::new(&mut vec_bytes);
record.emit(&mut encoder).unwrap();
let rr_class_slice = encoder.slice_of(RR_CLASS_OFFSET, RR_CLASS_OFFSET + 2);
assert_eq!(rr_class_slice, &[0x80, 0x01]);
}
let mut decoder = BinDecoder::new(&vec_bytes);
let got = Record::read(&mut decoder).unwrap();
assert_eq!(got.dns_class(), DNSClass::IN);
assert!(got.mdns_cache_flush());
}
}