use crate::common_types; use std::fmt; mod std_impls; pub mod quoted; pub use dnsbox_derive::DnsTextData; pub fn skip_whitespace(data: &mut &str) { *data = (*data).trim_start(); } pub fn next_field<'a>(data: &mut &'a str) -> crate::errors::Result<&'a str> { *data = (*data).trim_start(); if data.is_empty() { failure::bail!("missing field"); } match data.find(char::is_whitespace) { None => { let result = *data; *data = ""; Ok(result) }, Some(next) => { let result = &(*data)[..next]; *data = &(*data)[next..].trim_start(); Ok(result) }, } } pub fn next_quoted_field(data: &mut &str) -> crate::errors::Result> { *data = (*data).trim_start(); if data.is_empty() { failure::bail!("missing field"); } let result = quoted::UnquoteIterator::new(data).collect::, _>>()?; Ok(result) } pub fn quote(data: &[u8]) -> String { let mut result = String::with_capacity(data.len() + 2); result.push('"'); for qc in quoted::EncodeIterator::new_quoted(data) { result += &qc; } result.push('"'); result } // also escapes whitespace, but doesn't use quotes to surround it pub fn escape(data: &[u8]) -> String { let mut result = String::with_capacity(data.len()); for qc in quoted::EncodeIterator::new_encode_whitespace(data) { result += &qc; } result } /// Each call to write!() makes sure a space is emitted to separate it /// from previous fields. pub struct DnsTextFormatter<'a> { w: &'a mut dyn fmt::Write, need_space: bool, } impl<'a> DnsTextFormatter<'a> { pub fn new(w: &'a mut dyn fmt::Write) -> Self { DnsTextFormatter { w: w, need_space: false, } } /// make sure a field separator was emitted pub fn next_field(&mut self) -> fmt::Result { if self.need_space { write!(self.w, " ")?; self.need_space = false; } Ok(()) } /// a field was emitted through `inner`; next field needs a separator pub fn end_field(&mut self) { self.need_space = true; } /// direct access to underlying output; you'll need to call /// `next_field` and `end_field` manually. pub fn inner(&mut self) -> &mut dyn fmt::Write { self.w } pub fn format_field<'b>(&'b mut self) -> Result, fmt::Error> { self.next_field()?; Ok(DnsTextFormatField{ inner: self }) } pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result { self.next_field()?; self.w.write_fmt(args)?; self.end_field(); Ok(()) } } pub struct DnsTextFormatField<'a: 'b, 'b> { inner: &'b mut DnsTextFormatter<'a>, } impl<'a, 'b> ::std::ops::Deref for DnsTextFormatField<'a, 'b> { type Target = dyn fmt::Write + 'a; fn deref(&self) -> &Self::Target { self.inner.w } } impl<'a, 'b> ::std::ops::DerefMut for DnsTextFormatField<'a, 'b> { fn deref_mut(&mut self) -> &mut Self::Target { self.inner.w } } impl<'a, 'b> Drop for DnsTextFormatField<'a, 'b> { fn drop(&mut self) { self.inner.end_field(); } } #[derive(Clone, Debug, Default)] pub struct DnsTextContext { zone_class: Option, origin: Option, record_type: Option, last_ttl: Option, } impl DnsTextContext { pub fn new() -> Self { Self::default() } pub fn new_root_origin() -> Self { let mut this = Self::default(); this.set_origin(common_types::DnsName::new_root()); this } pub fn zone_class(&self) -> Option { self.zone_class } pub fn set_zone_class(&mut self, zone_class: common_types::Class) -> &mut Self { self.zone_class = Some(zone_class); self } pub fn unset_zone_class(&mut self) -> &mut Self { self } pub fn origin(&self) -> Option<&common_types::DnsName> { self.origin.as_ref() } pub fn set_origin(&mut self, origin: common_types::DnsName) -> &mut Self { self.origin = Some(origin); self } pub fn unset_origin(&mut self) -> &mut Self { self } pub fn record_type(&self) -> Option { self.record_type } pub fn set_record_type(&mut self, record_type: common_types::Type) -> &mut Self { self.record_type = Some(record_type); self } pub fn unset_record_type(&mut self) -> &mut Self { self } pub fn last_ttl(&self) -> Option { self.last_ttl } pub fn set_last_ttl(&mut self, last_ttl: u32) -> &mut Self { self.last_ttl = Some(last_ttl); self } pub fn unset_last_ttl(&mut self) -> &mut Self { self } pub fn parse(&self, data: &str) -> crate::errors::Result where T: DnsTextData, { parse_with(data, |data| T::dns_parse(self, data)) } } pub trait DnsTextData { fn dns_parse(context: &DnsTextContext, data: &mut &str) -> crate::errors::Result where Self: Sized, ; // format might fail if there is no (known) text representation. fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result; } pub fn parse_with<'a, F, O>(data: &'a str, parser: F) -> crate::errors::Result where for<'b> F: FnOnce(&'b mut &'a str) -> crate::errors::Result, { let mut data = data; let result = parser(&mut data)?; let data = data.trim(); failure::ensure!(data.is_empty(), "didn't parse complete text, remaining: {:?}", data); Ok(result) } #[derive(Debug)] pub struct DnsDisplay<'a, T: DnsTextData + ?Sized + 'a>(pub &'a T); impl<'a, T: DnsTextData + ?Sized + 'a> fmt::Display for DnsDisplay<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.0.dns_format(&mut DnsTextFormatter::new(f)) } }