use super::*; use ser::text::{DnsTextData, DnsTextFormatter, DnsTextContext, next_field, quoted, parse_with}; impl DnsName { /// Parse text representation of a domain name pub fn parse(context: &DnsTextContext, value: &str) -> Result { 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 => bail!("@ invalid without $ORIGIN"), } } 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 // push last label name.push_back(DnsLabelRef::new(&label)?)?; match context.origin() { Some(o) => { for l in o { name.push_back(l)?; } }, None => bail!("missing trailing dot without $ORIGIN"), } } Ok(name) } } impl DnsTextData for DnsName { fn dns_parse(context: &DnsTextContext, data: &mut &str) -> Result { let field = next_field(data)?; DnsName::parse(context, field) } fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { write!(f, "{}", self) } } impl ::std::str::FromStr for DnsName { type Err = ::failure::Error; fn from_str(s: &str) -> Result { parse_with(s, |data| DnsName::dns_parse(&DnsTextContext::new(), data)) } } impl DnsCompressedName { /// Parse text representation of a domain name pub fn parse(context: &DnsTextContext, value: &str) -> Result { Ok(DnsCompressedName(DnsName::parse(context, value)?)) } } impl DnsTextData for DnsCompressedName { fn dns_parse(context: &DnsTextContext, data: &mut &str) -> Result { let field = next_field(data)?; DnsCompressedName::parse(context, field) } fn dns_format(&self, f: &mut DnsTextFormatter) -> fmt::Result { self.0.dns_format(f) } } impl ::std::str::FromStr for DnsCompressedName { type Err = ::failure::Error; fn from_str(s: &str) -> Result { parse_with(s, |data| DnsCompressedName::dns_parse(&DnsTextContext::new(), data)) } }