use bytes::{Bytes, BufMut}; use common_types::*; use common_types::binary::HEXLOWER_PERMISSIVE_ALLOW_WS; use errors::*; use failure::{ResultExt, Fail}; use ser::packet::{DnsPacketWriteContext, remaining_bytes}; use ser::{RRData, RRDataPacket, RRDataText}; use ser::text::{DnsTextFormatter, DnsTextContext, next_field}; use std::borrow::Cow; use std::fmt; use std::io::Cursor; #[derive(Clone, PartialEq, Eq, Debug)] pub struct UnknownRecord { rr_type: Type, raw: Bytes, } impl UnknownRecord { pub fn new(rr_type: Type, raw: Bytes) -> Self { UnknownRecord { rr_type: rr_type, raw: raw, } } pub fn deserialize(rr_type: Type, data: &mut ::std::io::Cursor) -> Result { Ok(UnknownRecord::new(rr_type, remaining_bytes(data))) } pub fn dns_parse(rr_type: Type, data: &mut &str) -> Result { let field = next_field(data)?; ensure!(field == r"\#", "expect \\# token to mark generic encoding"); let field = next_field(data).context("generic record data length")?; let len: usize = field.parse()?; let result = HEXLOWER_PERMISSIVE_ALLOW_WS.decode(data.as_bytes()) .with_context(|e| e.context(format!("invalid hex: {:?}", data)))?; ensure!(len == result.len(), "length {} doesn't match length of encoded data {}", len, result.len()); *data = ""; // read all data Ok(UnknownRecord { rr_type, raw: result.into(), }) } } impl RRDataPacket for UnknownRecord { fn deserialize_rr_data(_ttl: u32, _rr_class: Class, rr_type: Type, data: &mut Cursor) -> Result { UnknownRecord::deserialize(rr_type, data) } fn rr_type(&self) -> Type { self.rr_type } fn serialize_rr_data(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec) -> Result<()> { packet.reserve(self.raw.len()); packet.put_slice(&self.raw); Ok(()) } } impl RRDataText for UnknownRecord { fn dns_parse_rr_data(context: &DnsTextContext, data: &mut &str) -> Result where Self: Sized, { let t = match context.record_type() { Some(t) => t, None => bail!("must parse DNS record with record type context"), }; UnknownRecord::dns_parse(t, data) } /// this must never fail unless the underlying buffer fails. fn dns_format_rr_data(&self, f: &mut DnsTextFormatter) -> fmt::Result { write!(f, "\\# {} {}", self.raw.len(), HEXLOWER_PERMISSIVE_ALLOW_WS.encode(&self.raw)) } fn rr_type_txt(&self) -> Cow<'static, str> { Cow::Owned(self.rr_type.generic_name()) } } impl RRData for UnknownRecord { fn clone_box(&self) -> Box { Box::new(self.clone()) as _ } }