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

86 lines
2.3 KiB
Rust

use crate::errors::*;
use crate::ser::text::{quoted, DnsTextContext};
use super::{DnsLabelRef, DnsName};
/// Parse text representation of a domain name
pub fn parse_name(context: &DnsTextContext, value: &str) -> Result<DnsName> {
let raw = value.as_bytes();
let mut name = DnsName::new_root();
if raw == b"." {
return Ok(name);
} else if raw == b"@" {
match context.origin() {
Some(o) => return Ok(o.clone()),
None => failure::bail!("@ invalid without $ORIGIN"),
}
}
failure::ensure!(!raw.is_empty(), "invalid empty name");
let mut label = Vec::new();
let mut pos = 0;
while pos < raw.len() {
if raw[pos] == b'.' {
failure::ensure!(!label.is_empty(), "empty label in name: {:?}", value);
name.push_back(DnsLabelRef::new(&label)?)?;
label.clear();
} else if raw[pos] == b'\\' {
failure::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
failure::ensure!(
pos + 3 < raw.len(),
"unexpected end of name after backslash with digit: {:?}",
value
);
failure::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;
failure::ensure!(v < 256, "invalid escape in name, {} > 255: {:?}", v, name);
label.push(v as u8);
} else {
failure::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 {
failure::ensure!(
!quoted::is_ascii_whitespace(raw[pos]),
"whitespace must be encoded as \\{:03} in: {:?}",
raw[pos],
name
);
label.push(raw[pos]);
}
pos += 1;
}
if !label.is_empty() {
// no trailing dot, relative name
// push last label
name.push_back(DnsLabelRef::new(&label)?)?;
match context.origin() {
Some(o) => {
for l in o {
name.push_back(l)?;
}
},
None => failure::bail!("missing trailing dot without $ORIGIN"),
}
}
Ok(name)
}