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 where O: IntoIterator> { 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 { 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 where O: IntoIterator> { Ok(DnsCompressedName(DnsName::parse(value, origin)?)) } } impl DnsTextData for DnsCompressedName { fn dns_parse(data: &mut &str) -> ::errors::Result { 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) } }