2017-12-26 21:23:51 +00:00
|
|
|
use common_types;
|
2017-12-21 12:32:14 +00:00
|
|
|
use std::fmt;
|
|
|
|
|
|
|
|
mod std_impls;
|
|
|
|
pub mod quoted;
|
|
|
|
|
|
|
|
pub fn skip_whitespace(data: &mut &str) {
|
|
|
|
*data = (*data).trim_left();
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn next_field<'a>(data: &mut &'a str) -> ::errors::Result<&'a str> {
|
|
|
|
*data = (*data).trim_left();
|
|
|
|
if data.is_empty() { 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_left();
|
|
|
|
Ok(result)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn next_quoted_field(data: &mut &str) -> ::errors::Result<Vec<u8>> {
|
|
|
|
*data = (*data).trim_left();
|
|
|
|
if data.is_empty() { 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.
|
2017-12-26 21:23:47 +00:00
|
|
|
pub struct DnsTextFormatter<'a, 'b: 'a> {
|
|
|
|
f: &'a mut fmt::Formatter<'b>,
|
2017-12-21 12:32:14 +00:00
|
|
|
need_space: bool,
|
|
|
|
}
|
|
|
|
|
2017-12-26 21:23:47 +00:00
|
|
|
impl<'a, 'b> DnsTextFormatter<'a, 'b> {
|
|
|
|
pub fn new(f: &'a mut fmt::Formatter<'b>) -> Self {
|
2017-12-21 12:32:14 +00:00
|
|
|
DnsTextFormatter {
|
|
|
|
f: f,
|
|
|
|
need_space: false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// make sure a field separator was emitted
|
|
|
|
pub fn next_field(&mut self) -> fmt::Result {
|
|
|
|
if self.need_space {
|
|
|
|
write!(self.f, " ")?;
|
|
|
|
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.
|
2017-12-26 21:23:47 +00:00
|
|
|
pub fn inner(&mut self) -> &mut fmt::Formatter<'b> {
|
2017-12-21 12:32:14 +00:00
|
|
|
self.f
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn write_fmt(&mut self, args: fmt::Arguments) -> fmt::Result {
|
|
|
|
self.next_field()?;
|
|
|
|
self.f.write_fmt(args)?;
|
|
|
|
self.end_field();
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-26 21:23:51 +00:00
|
|
|
#[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 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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-26 16:30:08 +00:00
|
|
|
pub trait DnsTextData {
|
2017-12-26 21:23:51 +00:00
|
|
|
fn dns_parse(context: &DnsTextContext, data: &mut &str) -> ::errors::Result<Self>
|
2017-12-26 16:30:08 +00:00
|
|
|
where
|
|
|
|
Self: Sized,
|
|
|
|
;
|
2017-12-21 12:32:14 +00:00
|
|
|
// format might fail if there is no (known) text representation.
|
|
|
|
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result;
|
|
|
|
}
|
|
|
|
|
2017-12-26 21:23:51 +00:00
|
|
|
pub fn parse_with<'a, F, O>(data: &'a str, parser: F) -> ::errors::Result<O>
|
2017-12-21 12:32:14 +00:00
|
|
|
where
|
2017-12-26 21:23:51 +00:00
|
|
|
for<'b> F: FnOnce(&'b mut &'a str) -> ::errors::Result<O>,
|
2017-12-21 12:32:14 +00:00
|
|
|
{
|
|
|
|
let mut data = data;
|
2017-12-26 21:23:51 +00:00
|
|
|
let result = parser(&mut data)?;
|
2017-12-21 12:32:14 +00:00
|
|
|
let data = data.trim();
|
|
|
|
ensure!(data.is_empty(), "didn't parse complete text, remaining: {:?}", data);
|
|
|
|
Ok(result)
|
2017-12-16 20:58:18 +00:00
|
|
|
}
|
2017-12-26 21:23:47 +00:00
|
|
|
|
|
|
|
#[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))
|
|
|
|
}
|
|
|
|
}
|