use crate::common_types::{classes, Class, Type}; use crate::errors::*; use crate::records::UnknownRecord; use crate::ser::packet::{DnsPacketData, DnsPacketWriteContext}; use crate::ser::text::{DnsTextContext, DnsTextData, DnsTextFormatter}; use bytes::Bytes; use std::any::Any; use std::borrow::Cow; use std::fmt; use std::io::Cursor; pub use dnsbox_derive::RRData; pub trait RRDataPacket { fn deserialize_rr_data( ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor, ) -> Result where Self: Sized; fn rr_type(&self) -> Type; fn serialize_rr_data( &self, context: &mut DnsPacketWriteContext, packet: &mut Vec, ) -> Result<()>; } impl RRDataPacket for T { fn deserialize_rr_data( _ttl: u32, rr_class: Class, rr_type: Type, data: &mut Cursor, ) -> Result { failure::ensure!(rr_type == T::TYPE, "type mismatch"); if T::CLASS != classes::ANY { failure::ensure!( rr_class == T::CLASS, "class mismatch: got {}, need {}", rr_class, T::CLASS ); } T::deserialize(data) } fn rr_type(&self) -> Type { T::TYPE } fn serialize_rr_data( &self, context: &mut DnsPacketWriteContext, packet: &mut Vec, ) -> Result<()> { self.serialize(context, packet) } } pub trait RRDataText { fn dns_parse_rr_data(context: &DnsTextContext, data: &mut &str) -> Result where Self: Sized; // format might fail if there is no (known) text representation. fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result; fn rr_type_txt(&self) -> Cow<'static, str>; } impl RRDataText for T { fn dns_parse_rr_data(context: &DnsTextContext, data: &mut &str) -> Result where Self: Sized, { failure::ensure!(context.record_type() == Some(T::TYPE), "type mismatch"); let rr_class = context .zone_class() .expect("require zone CLASS to parse record"); if T::CLASS != classes::ANY { failure::ensure!( rr_class == T::CLASS, "class mismatch: got {}, need {}", rr_class, T::CLASS ); } failure::ensure!(context.last_ttl().is_some(), "require TTL to parse record"); T::dns_parse(context, data) } fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result { self.dns_format(f) } fn rr_type_txt(&self) -> Cow<'static, str> { Cow::Borrowed(T::NAME) } } pub trait RRData: RRDataPacket + RRDataText + fmt::Debug + 'static { fn clone_box(&self) -> Box; fn as_any(&self) -> &dyn Any; fn as_box_any(self: Box) -> Box; // (type, rrdata) fn text(&self) -> Result<(String, String)> { let mut buf = String::new(); match self.dns_format_rr_data(&mut DnsTextFormatter::new(&mut buf)) { Ok(()) => return Ok((self.rr_type_txt().into(), buf)), Err(_) => (), } let mut raw = Vec::new(); self.serialize_rr_data(&mut DnsPacketWriteContext::new(), &mut raw)?; let ur = UnknownRecord::new(self.rr_type(), raw.into()); // formatting UnknownRecord should not fail buf.clear(); ur.dns_format_rr_data(&mut DnsTextFormatter::new(&mut buf)) .expect("formatting UnknownRecord must not fail"); Ok((ur.rr_type_txt().into(), buf)) } } impl Clone for Box { fn clone(&self) -> Self { self.clone_box() } } pub trait StaticRRData: RRData { const TYPE: Type; const NAME: &'static str; // classes::ANY marks class independent types const CLASS: Class; }