use bytes::{Bytes, BufMut}; use data_encoding::{self, HEXLOWER_PERMISSIVE}; use errors::*; use failure::{Fail, ResultExt}; use ser::packet::{DnsPacketData, DnsPacketWriteContext, remaining_bytes, short_blob, write_short_blob}; use ser::text::*; use std::fmt; use std::io::Cursor; static WHITESPACE: &str = "\t\n\x0c\r "; // \f == \x0c formfeed lazy_static!{ pub(crate) static ref HEXLOWER_PERMISSIVE_ALLOW_WS: data_encoding::Encoding = { let mut spec = data_encoding::Specification::new(); spec.symbols.push_str("0123456789abcdef"); spec.translate.from.push_str("ABCDEF"); spec.translate.to.push_str("abcdef"); spec.ignore.push_str(WHITESPACE); spec.encoding().unwrap() }; static ref BASE64_ALLOW_WS: data_encoding::Encoding = { let mut spec = data_encoding::Specification::new(); spec.symbols.push_str("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); spec.padding = Some('='); spec.ignore.push_str(WHITESPACE); spec.encoding().unwrap() }; } // Similar to `ShortText`, but uses (unquoted, no spaces allowed) hex // for text representation; "-" when empty #[derive(Clone, PartialEq, Eq, Debug)] pub struct HexShortBlob(Bytes); impl DnsPacketData for HexShortBlob { fn deserialize(data: &mut Cursor) -> Result { Ok(HexShortBlob(short_blob(data)?)) } fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec) -> Result<()> { write_short_blob(&self.0, packet) } } impl DnsTextData for HexShortBlob { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result { let s = next_field(data)?; if s == "-" { Ok(HexShortBlob(Bytes::new())) } else { let raw = HEXLOWER_PERMISSIVE.decode(s.as_bytes()) .with_context(|e| e.context(format!("invalid hex: {:?}", s)))?; ensure!(raw.len() < 256, "short hex field must be at most 255 bytes long"); Ok(HexShortBlob(raw.into())) } } fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { if self.0.is_empty() { write!(f, "-") } else { write!(f, "{}", HEXLOWER_PERMISSIVE.encode(&self.0)) } } } // No length byte (or restriction), just all data to end of record. uses // base64 encoding for text representation, whitespace allowed, padding // required. // // No following field allowed, i.e. last field in the record. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Base64RemainingBlob(Bytes); impl DnsPacketData for Base64RemainingBlob { fn deserialize(data: &mut Cursor) -> Result { Ok(Base64RemainingBlob(remaining_bytes(data))) } fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec) -> Result<()> { packet.reserve(self.0.len()); packet.put_slice(&self.0); Ok(()) } } impl DnsTextData for Base64RemainingBlob { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result { skip_whitespace(data); let result = BASE64_ALLOW_WS.decode(data.as_bytes()) .with_context(|e| e.context(format!("invalid base64: {:?}", data)))?; *data = ""; Ok(Base64RemainingBlob(result.into())) } fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { write!(f, "{}", BASE64_ALLOW_WS.encode(&self.0)) } } #[derive(Clone, PartialEq, Eq, Debug)] pub struct HexRemainingBlob(Bytes); // No length byte, just all data to end of record. uses hex encoding for // text representation (spaces are ignored - last field in record). impl DnsPacketData for HexRemainingBlob { fn deserialize(data: &mut Cursor) -> Result { Ok(HexRemainingBlob(remaining_bytes(data))) } fn serialize(&self, _context: &mut DnsPacketWriteContext, packet: &mut Vec) -> Result<()> { packet.reserve(self.0.len()); packet.put_slice(&self.0); Ok(()) } } impl DnsTextData for HexRemainingBlob { fn dns_parse(_context: &DnsTextContext, data: &mut &str) -> ::errors::Result { skip_whitespace(data); let result = HEXLOWER_PERMISSIVE_ALLOW_WS.decode(data.as_bytes()) .with_context(|e| e.context(format!("invalid hex: {:?}", data)))?; *data = ""; Ok(HexRemainingBlob(result.into())) } fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { write!(f, "{}", HEXLOWER_PERMISSIVE_ALLOW_WS.encode(&self.0)) } }