You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
5.3 KiB

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<Vec<u8>> {
*data = (*data).trim_start();
if data.is_empty() { failure::bail!("missing field"); }
let result = quoted::UnquoteIterator::new(data).collect::<Result<Vec<_>, _>>()?;
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<DnsTextFormatField<'a, 'b>, 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<common_types::Class>,
origin: Option<common_types::DnsName>,
record_type: Option<common_types::Type>,
last_ttl: Option<u32>,
}
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<common_types::Class> {
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<common_types::Type> {
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<u32> {
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<T>(&self, data: &str) -> crate::errors::Result<T>
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<Self>
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<O>
where
for<'b> F: FnOnce(&'b mut &'a str) -> crate::errors::Result<O>,
{
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))
}
}