rust-dnsbox/lib/dnsbox-base/src/common_types/name/name_text_parser.rs

90 lines
2.8 KiB
Rust

use super::*;
use ser::text::{DnsTextData, DnsTextFormatter, next_field, quoted};
impl DnsName {
/// Parse text representation of a domain name
pub fn parse<'a, O>(value: &str, origin: O) -> ::errors::Result<Self>
where
O: IntoIterator<Item = DnsLabelRef<'a>>
{
let raw = value.as_bytes();
let mut name = DnsName::new_root();
if raw == b"." {
return Ok(name);
} else if raw == b"@" {
for l in origin.into_iter() { name.push_back(l)?; }
return Ok(name);
}
ensure!(!raw.is_empty(), "invalid empty name");
let mut label = Vec::new();
let mut pos = 0;
while pos < raw.len() {
if raw[pos] == b'.' {
ensure!(!label.is_empty(), "empty label in name: {:?}", value);
name.push_back(DnsLabelRef::new(&label)?)?;
label.clear();
} else if raw[pos] == b'\\' {
ensure!(pos + 1 < raw.len(), "unexpected end of name after backslash: {:?}", value);
if raw[pos+1] >= b'0' && raw[pos+1] <= b'9' {
// \ddd escape
ensure!(pos + 3 < raw.len(), "unexpected end of name after backslash with digit: {:?}", value);
ensure!(raw[pos+2] >= b'0' && raw[pos+2] <= b'9' && raw[pos+3] >= b'0' && raw[pos+3] <= b'9', "expected three digits after backslash in name: {:?}", name);
let d1 = (raw[pos+1] - b'0') as u32;
let d2 = (raw[pos+2] - b'0') as u32;
let d3 = (raw[pos+3] - b'0') as u32;
let v = d1 * 100 + d2 * 10 + d3;
ensure!(v < 256, "invalid escape in name, {} > 255: {:?}", v, name);
label.push(v as u8);
} else {
ensure!(!quoted::is_ascii_whitespace(raw[pos+1]), "whitespace cannot be escaped with backslash prefix; encode it as \\{:03} in: {:?}", raw[pos+1], name);
label.push(raw[pos+1]);
}
} else {
ensure!(!quoted::is_ascii_whitespace(raw[pos+1]), "whitespace must be encoded as \\{:03} in: {:?}", raw[pos+1], name);
label.push(raw[pos]);
}
pos += 1;
}
if !label.is_empty() {
// no trailing dot, relative name
name.push_back(DnsLabelRef::new(&label)?)?;
for l in origin.into_iter() { name.push_back(l)?; }
}
Ok(name)
}
}
impl DnsTextData for DnsName {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> {
let field = next_field(data)?;
DnsName::parse(field, &DnsName::new_root())
}
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
write!(f, "{}", self)
}
}
impl DnsCompressedName {
/// Parse text representation of a domain name
pub fn parse<'a, O>(value: &str, origin: O) -> ::errors::Result<Self>
where
O: IntoIterator<Item = DnsLabelRef<'a>>
{
Ok(DnsCompressedName(DnsName::parse(value, origin)?))
}
}
impl DnsTextData for DnsCompressedName {
fn dns_parse(data: &mut &str) -> ::errors::Result<Self> {
let field = next_field(data)?;
DnsCompressedName::parse(field, &DnsName::new_root())
}
fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result {
self.0.dns_format(f)
}
}