use bytes::Bytes; use common_types::{Class, Type, classes}; use errors::*; use ser::packet::{DnsPacketData, DnsPacketWriteContext}; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext}; use std::borrow::Cow; use std::fmt; use std::io::Cursor; use records::UnknownRecord; 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 { ensure!(rr_type == T::TYPE, "type mismatch"); if T::CLASS != classes::ANY { 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>; // (type, rrdata) fn text(&self) -> Result<(String, String)> where Self: RRDataPacket, { 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(); self.dns_format_rr_data(&mut DnsTextFormatter::new(&mut buf)).unwrap(); Ok((ur.rr_type_txt().into(), buf)) } } impl RRDataText for T { fn dns_parse_rr_data(context: &DnsTextContext, data: &mut &str) -> Result where Self: Sized, { 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 { ensure!(rr_class == T::CLASS, "class mismatch: got {}, need {}", rr_class, T::CLASS); } 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 { fn clone_box(&self) -> Box; } 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; }