From e8c3e6fff2a226eae0ceccd0355cc4906fa8bda5 Mon Sep 17 00:00:00 2001 From: Martin Hoffmann Date: Tue, 22 Mar 2016 16:18:15 +0100 Subject: [PATCH] Rewrite everything. Again. --- src/bits/compose.rs | 287 ++++++++ src/bits/error.rs | 122 ++++ src/bits/flavor.rs | 54 ++ src/{ => bits}/header.rs | 98 ++- src/{ => bits}/iana/class.rs | 63 +- src/{ => bits}/iana/mod.rs | 0 src/{ => bits}/iana/opcode.rs | 0 src/{ => bits}/iana/rcode.rs | 0 src/{ => bits}/iana/rrtype.rs | 60 +- src/bits/message.rs | 470 +++++++++++++ src/bits/mod.rs | 15 + src/{ => bits}/name.rs | 1251 +++++++++++++++++++-------------- src/bits/nest.rs | 247 +++++++ src/bits/parse.rs | 222 ++++++ src/bits/question.rs | 92 +++ src/bits/rdata/generic.rs | 72 ++ src/{ => bits}/rdata/mod.rs | 3 + src/bits/rdata/rfc1035.rs | 93 +++ src/bits/rdata/traits.rs | 27 + src/bits/record.rs | 120 ++++ src/bits/u8.rs | 94 +++ src/bytes.rs | 189 ----- src/dig.rs | 80 +-- src/lib.rs | 12 +- src/message.rs | 912 ------------------------ src/question.rs | 131 ---- src/rdata/generic.rs | 92 --- src/rdata/rfc1035.rs | 94 --- src/rdata/traits.rs | 89 --- src/record.rs | 94 --- src/resolver/conf.rs | 15 +- 31 files changed, 2776 insertions(+), 2322 deletions(-) create mode 100644 src/bits/compose.rs create mode 100644 src/bits/error.rs create mode 100644 src/bits/flavor.rs rename src/{ => bits}/header.rs (82%) rename src/{ => bits}/iana/class.rs (69%) rename src/{ => bits}/iana/mod.rs (100%) rename src/{ => bits}/iana/opcode.rs (100%) rename src/{ => bits}/iana/rcode.rs (100%) rename src/{ => bits}/iana/rrtype.rs (94%) create mode 100644 src/bits/message.rs create mode 100644 src/bits/mod.rs rename src/{ => bits}/name.rs (50%) create mode 100644 src/bits/nest.rs create mode 100644 src/bits/parse.rs create mode 100644 src/bits/question.rs create mode 100644 src/bits/rdata/generic.rs rename src/{ => bits}/rdata/mod.rs (50%) create mode 100644 src/bits/rdata/rfc1035.rs create mode 100644 src/bits/rdata/traits.rs create mode 100644 src/bits/record.rs create mode 100644 src/bits/u8.rs delete mode 100644 src/bytes.rs delete mode 100644 src/message.rs delete mode 100644 src/question.rs delete mode 100644 src/rdata/generic.rs delete mode 100644 src/rdata/rfc1035.rs delete mode 100644 src/rdata/traits.rs delete mode 100644 src/record.rs diff --git a/src/bits/compose.rs b/src/bits/compose.rs new file mode 100644 index 00000000..00241124 --- /dev/null +++ b/src/bits/compose.rs @@ -0,0 +1,287 @@ +//! Building of wire-format DNS data. + +use std::collections::HashMap; +use std::fmt; +use std::mem; +use std::ptr; +use super::error::{ComposeError, ComposeResult}; +use super::name::{DName, DNameSlice, Label, OwnedDName}; + + +//------------ ComposeBytes --------------------------------------------------- + +/// A trait for composing a DNS wire format message. +/// +/// Messages are created by pushing data to the end of the message. +/// However, in order to avoid having to preassemble length-value parts of +/// the message such as record data, there is an option to update previously +/// written data. +pub trait ComposeBytes: Sized + fmt::Debug { + type Pos: Copy + fmt::Debug; + + //--- Appending of basic types + + /// Pushes a bytes slice to the end of the builder. + fn push_bytes(&mut self, data: &[u8]) -> ComposeResult<()>; + + /// Pushes placeholder bytes to the end of the target. + fn push_empty(&mut self, len: usize) -> ComposeResult<()>; + + /// Pushes a single octet to the end of the builder. + fn push_u8(&mut self, data: u8) -> ComposeResult<()> { + let bytes: [u8; 1] = unsafe { mem::transmute(data) }; + self.push_bytes(&bytes) + } + + /// Pushes an unsigned 16-bit word to the end of the builder. + /// + /// The word is converted to network byte order before writing if + /// necessary. + fn push_u16(&mut self, data: u16) -> ComposeResult<()> { + let data = data.to_be(); + let bytes: [u8; 2] = unsafe { mem::transmute(data) }; + self.push_bytes(&bytes) + } + + /// Pushes a unsigned 32-bit word to the end of the builder. + /// + /// The word is converted to network byte order before writing if + /// necessary. + fn push_u32(&mut self, data: u32) -> ComposeResult<()> { + let data = data.to_be(); + let bytes: [u8; 4] = unsafe { mem::transmute(data) }; + self.push_bytes(&bytes) + } + + //--- Appending of domain names. + + /// Pushes a domain name to the end of the builder. + fn push_dname(&mut self, name: &D) -> ComposeResult<()>; + + /// Pushes a domain name to the end of the builder using compression. + fn push_dname_compressed(&mut self, name: &D) + -> ComposeResult<()>; + + + //--- Checkpoint and rollback. + + /// Mark the current position as a point for truncation. + fn truncation_point(&mut self); + + /// Returns whether the target has been truncated. + fn truncated(&self) -> bool; + + + //--- Updating of earlier data. + + /// Returns the current write position of the builder. + fn pos(&self) -> Self::Pos; + + /// Returns the length of data added since the given position. + fn delta(&self, pos: Self::Pos) -> usize; + + /// Updates the builder starting at the given position with a bytes slice. + /// + /// This method panics if the bytes slice is longer than the data + /// assembled since the given write position. + fn update_bytes(&mut self, pos: Self::Pos, data: &[u8]) + -> ComposeResult<()>; + + /// Updates the octet at the given write position. + /// + /// This method panics if there is no data at the given write position. + fn update_u8(&mut self, pos: Self::Pos, data: u8) + -> ComposeResult<()> { + let bytes: [u8; 1] = unsafe { mem::transmute(data) }; + self.update_bytes(pos, &bytes) + } + + /// Updates an unsigned 16-bit word starting at the given write position. + /// + /// This method panics if there aren’t two octets following the write + /// position. + fn update_u16(&mut self, pos: Self::Pos, data: u16) + -> ComposeResult<()> { + let data = data.to_be(); + let bytes: [u8; 2] = unsafe { mem::transmute(data) }; + self.update_bytes(pos, &bytes) + } + + /// Updates an unsigned 32-bit word starting at the given write position. + /// + /// This method panics if there aren’t four octets following the write + /// position. + fn update_u32(&mut self, pos: Self::Pos, data: u32) + -> ComposeResult<()> { + let data = data.to_be(); + let bytes: [u8; 4] = unsafe { mem::transmute(data) }; + self.update_bytes(pos, &bytes) + } +} + + +//------------ ComposeVec ----------------------------------------------------- + +/// A compose target based on a simple vector. +#[derive(Clone, Debug)] +pub struct ComposeVec { + vec: Vec, + start: usize, + maxlen: Option, + checkpoint: Option, + truncated: bool, + compress: Option>, +} + + +impl ComposeVec { + pub fn new(maxlen: Option, compress: bool) -> ComposeVec { + ComposeVec::with_vec(Vec::new(), maxlen, compress) + } + + pub fn with_vec(vec: Vec, maxlen: Option, compress: bool) + -> ComposeVec { + let start = vec.len(); + ComposeVec { + vec: Vec::new(), + start: start, + maxlen: maxlen, + checkpoint: None, + truncated: false, + compress: if compress { Some(HashMap::new()) } + else { None } + } + } + + pub fn finish(self) -> Vec { + self.vec + } +} + + +impl ComposeVec { + fn keep_pushing(&mut self, len: usize) -> ComposeResult<()> { + if self.truncated { return Err(ComposeError::SizeExceeded) } + else if let Some(maxlen) = self.maxlen { + if maxlen < self.vec.len() + len { + self.checkpoint.map(|len| self.vec.truncate(len)); + self.truncated = true; + return Err(ComposeError::SizeExceeded) + } + } + Ok(()) + } + + fn push_dname_simple(&mut self, name: &D) -> ComposeResult<()> { + for label in try!(name.to_cow()).iter() { + try!(label.compose(self)) + } + Ok(()) + } + + fn compress_pos(&self) -> usize { + self.vec.len() - self.start + } + + fn add_compress_target(&mut self, name: OwnedDName, pos: usize) { + if let Some(ref mut compress) = self.compress { + if pos <= ::std::u16::MAX as usize { + let _ = compress.insert(name, pos as u16); + } + } + } + + fn get_compress_target(&self, name: &DNameSlice) -> Option { + if let Some(ref compress) = self.compress { + compress.get(name).map(|v| *v) + } + else { None } + } +} + + +impl ComposeBytes for ComposeVec { + type Pos = usize; + + fn push_bytes(&mut self, data: &[u8]) -> ComposeResult<()> { + try!(self.keep_pushing(data.len())); + self.vec.extend(data); + Ok(()) + } + + fn push_empty(&mut self, len: usize) -> ComposeResult<()> { + try!(self.keep_pushing(len)); + let len = self.vec.len() + len; + self.vec.resize(len, 0); + Ok(()) + } + + fn push_dname(&mut self, name: &D) -> ComposeResult<()> { + if self.compress.is_some() { + let name = try!(name.to_owned()); + let pos = self.compress_pos(); + try!(self.push_dname_simple(&name)); + self.add_compress_target(name, pos); + Ok(()) + } + else { + self.push_dname_simple(name) + } + } + + fn push_dname_compressed(&mut self, name: &D) + -> ComposeResult<()> { + if self.compress.is_some() { + let name = try!(name.to_cow()); + let mut iter = name.iter(); + loop { + let name = iter.as_name(); + if let Some(pos) = self.get_compress_target(name) { + try!(Label::compose_compressed(self, pos)); + break; + } + let label = match iter.next() { + Some(x) => x, None => break + }; + let pos = self.compress_pos(); + try!(label.compose(self)); + self.add_compress_target(name.to_owned(), pos); + } + Ok(()) + } + else { + self.push_dname_simple(name) + } + } + + fn truncation_point(&mut self) { + self.checkpoint = Some(self.vec.len()) + } + + fn truncated(&self) -> bool { + self.truncated + } + + fn pos(&self) -> Self::Pos { + self.vec.len() + } + + fn delta(&self, pos: Self::Pos) -> usize { + self.vec.len().checked_sub(pos).unwrap() + } + + fn update_bytes(&mut self, pos: Self::Pos, data: &[u8]) + -> ComposeResult<()> { + if pos + data.len() > self.vec.len() { + panic!("composer update overrun") + } + unsafe { + ptr::copy_nonoverlapping(data.as_ptr(), + self.vec[pos..pos + data.len()].as_mut_ptr(), + data.len()) + } + Ok(()) + } +} + + diff --git a/src/bits/error.rs b/src/bits/error.rs new file mode 100644 index 00000000..22390bd9 --- /dev/null +++ b/src/bits/error.rs @@ -0,0 +1,122 @@ +//! Errors and results +//! +//! There are three different pairs of errors and results related to the +//! three different operations that can fail: +//! +//! `ParseError` and `ParseResult` are used when parsing DNS data from its +//! wire-format. +//! +//! `ComposeError` and `ComposeResult` are used when composing DNS wire-format +//! data. +//! +//! `FromStrError` and `FromStrResult` are used when converting data from +//! strings. + +use std::error::Error; +use std::fmt; + + +//------------ ComposeError and ComposeResult ------------------------------- + +#[derive(Clone, Debug)] +pub enum ComposeError { + SizeExceeded, + Overflow, + ParseError(ParseError), +} + +impl Error for ComposeError { + fn description(&self) -> &str { + use self::ComposeError::*; + + match *self { + SizeExceeded => "message size has been exceeded", + Overflow => "a counter has overflowed", + ParseError(ref error) => error.description(), + } + } +} + +impl From for ComposeError { + fn from(error: ParseError) -> ComposeError { + ComposeError::ParseError(error) + } +} + +impl fmt::Display for ComposeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.description().fmt(f) + } +} + + +pub type ComposeResult = Result; + + +//------------ ParseError and ParseResult ----------------------------------- + +#[derive(Clone, Debug)] +pub enum ParseError { + UnexpectedEnd, + UnknownLabel, + UnknownType, + CompressedLabel, +} + +impl Error for ParseError { + fn description(&self) -> &str { + use self::ParseError::*; + + match *self { + UnexpectedEnd => "unexpected end of data", + UnknownLabel => "unknown label type in domain name", + UnknownType => "unknown type", + CompressedLabel => "a compressed label was encountered", + } + } +} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.description().fmt(f) + } +} + +pub type ParseResult = Result; + + +//------------ FromStrError and FromStrResult ------------------------------- + +#[derive(Clone, Debug)] +pub enum FromStrError { + UnexpectedEnd, + LongLabel, + IllegalEscape, + IllegalCharacter, + UnknownType, + UnknownClass, +} + +impl Error for FromStrError { + fn description(&self) -> &str { + use self::FromStrError::*; + + match *self { + UnexpectedEnd => "unexpected end of input", + LongLabel => "domain name label with more than 63 octets", + IllegalEscape => "illegal escape sequence", + IllegalCharacter => "illegal character", + UnknownType => "unknown type", + UnknownClass => "unknown class", + } + } +} + +impl fmt::Display for FromStrError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.description().fmt(f) + } +} + +pub type FromStrResult = Result; + diff --git a/src/bits/flavor.rs b/src/bits/flavor.rs new file mode 100644 index 00000000..e6b16c07 --- /dev/null +++ b/src/bits/flavor.rs @@ -0,0 +1,54 @@ +//! The flavors of DNS data. +//! +//! Because of the various types of domain names, all DNS data comes +//! in one of three flavors: it can be `Owned`, that is, entirely +//! self-contained; `Ref` when its data references an underlying bytes +//! slice; `Lazy` when its data references an underlying DNS message and +//! domain names can be compressed. +//! +//! This module defines the `Flavor` trait for referencing these three +//! flavors as well as the actual flavors as types. + +use std::marker::PhantomData; +use super::name; +use super::nest; + + +/// The trait for the three flavors of DNS data. +/// +/// This trait doesn’t actually define any methods but rather only collects +/// the associated types for each flavor. +pub trait Flavor<'a>: Sized { + type DName: name::DName; + type Nest: nest::Nest<'a, Self>; +} + + +/// The flavor for owned DNS data. +pub struct Owned; + +impl<'a> Flavor<'a> for Owned { + type DName = name::OwnedDName; + type Nest = nest::OwnedNest; +} + +/// The flavor for DNS data referencing an underlying bytes slice. +pub struct Ref<'a> { + marker: PhantomData<&'a u8> +} + +impl<'a> Flavor<'a> for Ref<'a> { + type DName = name::DNameRef<'a>; + type Nest = nest::NestRef<'a>; +} + + +/// The flavor for DNS data referencing an underlying DNS message. +pub struct Lazy<'a> { + marker: PhantomData<&'a u8> +} + +impl<'a> Flavor<'a> for Lazy<'a> { + type DName = name::LazyDName<'a>; + type Nest = nest::LazyNest<'a>; +} diff --git a/src/header.rs b/src/bits/header.rs similarity index 82% rename from src/header.rs rename to src/bits/header.rs index ce245372..7be7e94c 100644 --- a/src/header.rs +++ b/src/bits/header.rs @@ -13,7 +13,7 @@ //! use std::mem; -use super::bytes::{BytesSlice, Error, Result}; +use super::error::{ComposeError, ComposeResult}; use super::iana::{Opcode, Rcode}; @@ -21,7 +21,7 @@ use super::iana::{Opcode, Rcode}; /// The first part of the header of a DNS message. /// -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct Header { inner: [u8; 4] } @@ -39,16 +39,6 @@ impl Header { Header { inner: [0; 4] } } - /// Creates a header reference from the given slice. - /// - /// Returns the header reference and the remainder of the slice. Or - /// an error if the slice is to short. - /// - pub fn split_from(slice: &[u8]) -> Result<(&Header, &[u8])> { - let (left, right) = try!(slice.split_bytes(mem::size_of::())); - Ok((unsafe { Self::from_message(left) }, right)) - } - /// Creates a header reference from a bytes slice of a message. /// /// This function is unsafe as it assumes the bytes slice to have the @@ -183,7 +173,7 @@ impl Header { /// The section count part of the header of a DNS message. /// -#[derive(Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub struct HeaderCounts { inner: [u8; 8] } @@ -196,14 +186,6 @@ impl HeaderCounts { HeaderCounts { inner: [0; 8] } } - /// Creates a reference from the given slice. - /// - /// Returns the reference and the remainder of the slice. - pub fn split_from(slice: &[u8]) -> Result<(&HeaderCounts, &[u8])> { - let (left, right) = try!(slice.split_bytes(mem::size_of::())); - Ok((unsafe { mem::transmute(left.as_ptr()) }, right)) - } - /// Creates a reference from the bytes slice of a message. /// /// This function is unsafe as it assumes the bytes slice to have the @@ -243,7 +225,7 @@ impl HeaderCounts { } /// Increase the QDCOUNT field. - pub fn inc_qdcount(&mut self, inc: u16) -> Result<()> { + pub fn inc_qdcount(&mut self, inc: u16) -> ComposeResult<()> { self.inc_u16(0, inc) } @@ -258,7 +240,7 @@ impl HeaderCounts { } /// Increases the ANCOUNT field. - pub fn inc_ancount(&mut self, inc: u16) -> Result<()> { + pub fn inc_ancount(&mut self, inc: u16) -> ComposeResult<()> { self.inc_u16(2, inc) } @@ -273,7 +255,7 @@ impl HeaderCounts { } /// Increases the NSCOUNT field. - pub fn inc_nscount(&mut self, inc: u16) -> Result<()> { + pub fn inc_nscount(&mut self, inc: u16) -> ComposeResult<()> { self.inc_u16(4, inc) } @@ -288,7 +270,7 @@ impl HeaderCounts { } /// Increases the ARCOUNT field. - pub fn inc_arcount(&mut self, inc: u16) -> Result<()> { + pub fn inc_arcount(&mut self, inc: u16) -> ComposeResult<()> { self.inc_u16(6, inc) } @@ -331,10 +313,10 @@ impl HeaderCounts { self.inner[offset + 1] = value as u8; } - fn inc_u16(&mut self, offset: usize, inc: u16) -> Result<()> { + fn inc_u16(&mut self, offset: usize, inc: u16) -> ComposeResult<()> { let value = match self.get_u16(offset).checked_add(inc) { Some(value) => value, - None => return Err(Error::Overflow), + None => return Err(ComposeError::Overflow), }; self.set_u16(offset, value); Ok(()) @@ -346,9 +328,65 @@ impl HeaderCounts { /// The complete header of a DNS message. /// -/// Currently, this type is only used to conveniently get the size of the -/// entire header. -pub type FullHeader = [u8; 12]; +#[derive(Clone, Debug, PartialEq)] +pub struct FullHeader { + inner: [u8; 12] +} + +/// # Creation and Conversion +/// +impl FullHeader { + /// Creates a new empty header. + pub fn new() -> FullHeader { + FullHeader { inner: [0; 12] } + } + + /// Creates a reference from the bytes slice of a message. + /// + /// This function is unsafe as it assumes the bytes slice to have the + /// correct length. + pub unsafe fn from_message(s: &[u8]) -> &FullHeader { + mem::transmute(s.as_ptr()) + } + + /// Creates a mutable reference from the bytes slice of a message. + /// + /// This function is unsafe as it assumes the bytes slice to have the + /// correct length. + pub unsafe fn from_message_mut(s: &mut [u8]) -> &mut FullHeader { + mem::transmute(s.as_ptr()) + } + + /// Returns the underlying bytes slice. + pub fn as_bytes(&self) -> &[u8] { + &self.inner + } +} + + +/// # Access to Header and Dounts +/// +impl FullHeader { + /// Returns a reference to the header. + pub fn header(&self) -> &Header { + unsafe { Header::from_message(&self.inner) } + } + + /// Returns a mutable reference to the header. + pub fn header_mut(&mut self) -> &mut Header { + unsafe { Header::from_message_mut(&mut self. inner) } + } + + /// Returns a reference to the header counts. + pub fn counts(&self) -> &HeaderCounts { + unsafe { HeaderCounts::from_message(&self.inner) } + } + + /// Returns a mutable reference to the header counts. + pub fn counts_mut(&mut self) -> &mut HeaderCounts { + unsafe { HeaderCounts::from_message_mut(&mut self.inner) } + } +} //============ Testing ====================================================== diff --git a/src/iana/class.rs b/src/bits/iana/class.rs similarity index 69% rename from src/iana/class.rs rename to src/bits/iana/class.rs index db3f4698..ed2dd8d3 100644 --- a/src/iana/class.rs +++ b/src/bits/iana/class.rs @@ -1,12 +1,9 @@ //! DNS CLASSes. use std::convert; -use std::error; use std::fmt; -use std::num; -use std::result; use std::str; -use super::super::bytes::BytesBuf; +use super::super::error::{FromStrError, FromStrResult}; /// DNS CLASSes. @@ -62,10 +59,6 @@ impl Class { Int(value) => value } } - - pub fn push_buf(self, buf: &mut B) { - buf.push_u16(self.to_int()); - } } impl convert::From for Class { @@ -74,10 +67,16 @@ impl convert::From for Class { } } -impl str::FromStr for Class { - type Err = ParseError; +impl convert::From for u16 { + fn from(value: Class) -> u16 { + value.to_int() + } +} - fn from_str(s: &str) -> ParseResult { +impl str::FromStr for Class { + type Err = FromStrError; + + fn from_str(s: &str) -> FromStrResult { use std::ascii::AsciiExt; use self::Class::*; @@ -90,14 +89,18 @@ impl str::FromStr for Class { if let Some((n, _)) = s.char_indices().nth(5) { let (l, r) = s.split_at(n); if l.eq_ignore_ascii_case("CLASS") { - Ok(Int(try!(u16::from_str_radix(r, 10)))) + let value = match u16::from_str_radix(r, 10) { + Ok(x) => x, + Err(..) => return Err(FromStrError::UnknownClass) + }; + Ok(Int(value)) } else { - Err(ParseError::UnknownClass) + Err(FromStrError::UnknownClass) } } else { - Err(ParseError::UnknownClass) + Err(FromStrError::UnknownClass) } } } @@ -140,35 +143,3 @@ impl PartialEq for u16 { impl Eq for Class { } - -//------------ ParseError and ParseResult ----------------------------------- - -#[derive(Debug, Clone, PartialEq)] -pub enum ParseError { - UnknownClass, -} - -impl error::Error for ParseError { - fn description(&self) -> &str { - match *self { - ParseError::UnknownClass => "unknown class", - } - } -} - -impl convert::From for ParseError { - fn from(_: num::ParseIntError) -> Self { - ParseError::UnknownClass - } -} - -impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use std::error::Error; - - self.description().fmt(f) - } -} - -pub type ParseResult = result::Result; - diff --git a/src/iana/mod.rs b/src/bits/iana/mod.rs similarity index 100% rename from src/iana/mod.rs rename to src/bits/iana/mod.rs diff --git a/src/iana/opcode.rs b/src/bits/iana/opcode.rs similarity index 100% rename from src/iana/opcode.rs rename to src/bits/iana/opcode.rs diff --git a/src/iana/rcode.rs b/src/bits/iana/rcode.rs similarity index 100% rename from src/iana/rcode.rs rename to src/bits/iana/rcode.rs diff --git a/src/iana/rrtype.rs b/src/bits/iana/rrtype.rs similarity index 94% rename from src/iana/rrtype.rs rename to src/bits/iana/rrtype.rs index 9eb75bde..1f1d9b7c 100644 --- a/src/iana/rrtype.rs +++ b/src/bits/iana/rrtype.rs @@ -2,12 +2,9 @@ //! use std::convert; -use std::error; use std::fmt; -use std::num; -use std::result; use std::str; -use super::super::bytes::BytesBuf; +use super::super::error::{FromStrError, FromStrResult}; /// Resource Record Types. @@ -586,10 +583,6 @@ impl RRType { Int(value) => value } } - - pub fn push_buf(self, buf: &mut B) { - buf.push_u16(self.to_int()) - } } impl convert::From for RRType { @@ -598,11 +591,16 @@ impl convert::From for RRType { } } +impl convert::From for u16 { + fn from(value: RRType) -> u16 { + value.to_int() + } +} impl str::FromStr for RRType { - type Err = ParseError; + type Err = FromStrError; - fn from_str(s: &str) -> ParseResult { + fn from_str(s: &str) -> FromStrResult { use std::ascii::AsciiExt; use self::RRType::*; @@ -694,14 +692,18 @@ impl str::FromStr for RRType { if let Some((n, _)) = s.char_indices().nth(4) { let (l, r) = s.split_at(n); if l.eq_ignore_ascii_case("TYPE") { - Ok(Int(try!(u16::from_str_radix(r, 10)))) + let value = match u16::from_str_radix(r, 10) { + Ok(x) => x, + Err(..) => return Err(FromStrError::UnknownType) + }; + Ok(Int(value)) } else { - Err(ParseError::UnknownType) + Err(FromStrError::UnknownType) } } else { - Err(ParseError::UnknownType) + Err(FromStrError::UnknownType) } } } @@ -823,35 +825,3 @@ impl PartialEq for u16 { impl Eq for RRType { } - -//------------ ParseError and ParseResult ----------------------------------- - -#[derive(Debug, Clone, PartialEq)] -pub enum ParseError { - UnknownType, -} - -impl error::Error for ParseError { - fn description(&self) -> &str { - match *self { - ParseError::UnknownType => "unknown type", - } - } -} - -impl convert::From for ParseError { - fn from(_: num::ParseIntError) -> Self { - ParseError::UnknownType - } -} - -impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use std::error::Error; - - self.description().fmt(f) - } -} - -pub type ParseResult = result::Result; - diff --git a/src/bits/message.rs b/src/bits/message.rs new file mode 100644 index 00000000..099913f5 --- /dev/null +++ b/src/bits/message.rs @@ -0,0 +1,470 @@ +//! DNS messages. + +use std::marker::PhantomData; +use std::mem; +use super::compose::ComposeBytes; +use super::error::{ComposeError, ComposeResult, ParseResult}; +use super::flavor::{self, Flavor}; +use super::header::{Header, HeaderCounts, FullHeader}; +use super::nest::{self, Nest}; +use super::parse::ParseBytes; +use super::question::{ComposeQuestion, Question}; +use super::rdata::{GenericRecordData, RecordData}; +use super::record::{ComposeRecord, Record}; + + +//============ Disecting Existing Messages ================================== + +//============ Message ====================================================== + +/// A DNS message. +#[derive(Clone, Debug)] +pub struct Message<'a, F: Flavor<'a>> { + nest: F::Nest +} + +pub type OwnedMessage<'a> = Message<'a, flavor::Owned>; +pub type MessageRef<'a> = Message<'a, flavor::Ref<'a>>; +pub type LazyMessage<'a> = Message<'a, flavor::Lazy<'a>>; + +/// # Creation and Conversion +/// +impl<'a, F: Flavor<'a>> Message<'a, F> { + /// Creates a message from a nest. + pub fn from_nest(nest: F::Nest) -> Self { + Message { nest: nest } + } +} + +impl<'a> Message<'a, flavor::Ref<'a>> { + pub fn from_bytes(bytes: &'a [u8]) -> Self { + Message::from_nest(nest::NestRef::from_bytes(bytes)) + } +} + +impl<'a> Message<'a, flavor::Lazy<'a>> { + pub fn from_bytes(bytes: &'a [u8]) -> Self { + Message::from_nest(nest::LazyNest::new(bytes, bytes)) + } +} + + +// # Header Access +// +impl<'a, F: Flavor<'a>> Message<'a, F> { + /// Returns a reference to the message header. + pub fn header<'b: 'a>(&'b self) -> &'a Header { + unsafe { Header::from_message(self.nest.as_slice()) } + } + + /// Returns a reference to the header counts of the message. + pub fn counts<'b: 'a>(&'b self) -> &'a HeaderCounts { + unsafe { HeaderCounts::from_message(self.nest.as_slice()) } + } + + /// Returns an iterator over the question section + pub fn question<'b: 'a>(&'b self) -> QuestionSection<'a, F> { + let mut parser = self.nest.parser(); + parser.skip(mem::size_of::()).unwrap(); // XXX Hmm. + QuestionSection::new(parser, (*self.counts()).clone()) + } +} + + +//------------ QuestionSection ---------------------------------------------- + +#[derive(Clone, Debug)] +pub struct QuestionSection<'a, F: Flavor<'a>> { + parser: >::Parser, + counts: HeaderCounts, + count: u16 +} + +impl<'a, F: Flavor<'a>> QuestionSection<'a, F> { + fn new(parser: >::Parser, counts: HeaderCounts) + -> Self { + let count = counts.qdcount(); + QuestionSection { parser: parser, counts: counts, count: count } + } + + pub fn iter(&mut self) -> &mut Self { + self + } + + /// Continues to the answer section. + pub fn answer(mut self) -> ParseResult> { + for question in self.iter() { + if let Err(e) = question { + return Err(e) + } + } + Ok(AnswerSection::new(self.parser, self.counts)) + } +} + +impl<'a, F: Flavor<'a>> Iterator for QuestionSection<'a, F> { + type Item = ParseResult>; + + fn next(&mut self) -> Option { + if self.count == 0 { return None } + Some(Question::parse(&mut self.parser) + .map(|res| { self.count -= 1; res })) + } +} + + +//------------ AnswerSection ------------------------------------------------ + +/// The answer section of a message. +#[derive(Clone, Debug)] +pub struct AnswerSection<'a, F: Flavor<'a>> { + parser: >::Parser, + counts: HeaderCounts, +} + +impl<'a, F: Flavor<'a>> AnswerSection<'a, F> { + fn new(parser: >::Parser, counts: HeaderCounts) + -> Self { + AnswerSection { parser: parser, counts: counts } + } + + pub fn iter>(&self) -> RecordIter<'a, F, D> { + RecordIter::new(self.parser.clone(), self.counts.ancount()) + } + + pub fn authority(self) -> ParseResult> { + let mut iter = self.iter::>(); + try!(iter.exhaust()); + Ok(AuthoritySection::new(iter.parser, self.counts)) + } +} + + +//------------ AuthoritySection --------------------------------------------- + +/// The authority section of a message. +#[derive(Clone, Debug)] +pub struct AuthoritySection<'a, F: Flavor<'a>> { + parser: >::Parser, + counts: HeaderCounts, +} + +impl<'a, F: Flavor<'a>> AuthoritySection<'a, F> { + fn new(parser: >::Parser, counts: HeaderCounts) + -> Self { + AuthoritySection { parser: parser, counts: counts } + } + + pub fn iter>(&self) -> RecordIter<'a, F, D> { + RecordIter::new(self.parser.clone(), self.counts.nscount()) + } + + pub fn additional(self) -> ParseResult> { + let mut iter = self.iter::>(); + try!(iter.exhaust()); + Ok(AdditionalSection::new(iter.parser, self.counts)) + } +} + + +//------------ AdditionalSection -------------------------------------------- + +/// The additional section of a message. +#[derive(Clone, Debug)] +pub struct AdditionalSection<'a, F: Flavor<'a>> { + parser: >::Parser, + counts: HeaderCounts, +} + +impl<'a, F: Flavor<'a>> AdditionalSection<'a, F> { + fn new(parser: >::Parser, counts: HeaderCounts) + -> Self { + AdditionalSection { parser: parser, counts: counts } + } + + pub fn iter>(&self) -> RecordIter<'a, F, D> { + RecordIter::new(self.parser.clone(), self.counts.ancount()) + } +} + +//------------ RecordIter --------------------------------------------------- + +/// An iterator over the records in one of a record section. +#[derive(Clone, Debug)] +pub struct RecordIter<'a, F: Flavor<'a>, D: RecordData<'a, F>> { + parser: >::Parser, + count: u16, + marker: PhantomData +} + +impl<'a, F: Flavor<'a>, D: RecordData<'a, F>> RecordIter<'a, F, D> { + fn new(parser: >::Parser, count: u16) -> Self { + RecordIter { parser: parser, count: count, marker: PhantomData } + } + + pub fn iter(&mut self) -> &mut Self { self } + + fn exhaust(&mut self) -> ParseResult<()> { + for record in self.iter() { + if let Err(e) = record { + return Err(e) + } + } + Ok(()) + } + + fn step(&mut self) -> ParseResult>> { + Record::parse(&mut self.parser).map(|res| { self.count -= 1; res }) + } +} + +impl<'a, F, D> Iterator for RecordIter<'a, F, D> + where F: Flavor<'a>, D: RecordData<'a, F> { + type Item = ParseResult>; + + fn next(&mut self) -> Option { + if self.count == 0 { return None } + loop { + match self.step() { + Ok(Some(record)) => return Some(Ok(record)), + Ok(None) => (), + Err(e) => return Some(Err(e)), + } + } + } +} + + +//============ Building New Message ========================================= + +//------------ MessageBuilder ----------------------------------------------- + +/// A builder for constructing a DNS message. +#[derive(Clone, Debug)] +pub struct MessageBuilder { + target: MessageTarget +} + +impl MessageBuilder { + /// Creates a new message builder. + pub fn new(target: C) -> ComposeResult { + MessageTarget::new(target) + .map(|target| MessageBuilder { target: target }) + } + + pub fn header(&self) -> &Header { + self.target.header() + } + + pub fn header_mut(&mut self) -> &mut Header { + self.target.header_mut() + } + + pub fn finish(self) -> ComposeResult { + self.target.finish() + } + + pub fn question(self) -> QuestionBuilder { + QuestionBuilder::new(self.target) + } +} + + +//------------ QuestionBuilder ---------------------------------------------- + +#[derive(Clone, Debug)] +pub struct QuestionBuilder { + target: MessageTarget +} + +impl QuestionBuilder { + fn new(target: MessageTarget) -> Self { + QuestionBuilder { target: target } + } + + pub fn header(&self) -> &Header { + self.target.header() + } + + pub fn header_mut(&mut self) -> &mut Header { + self.target.header_mut() + } + + pub fn push(&mut self, question: &Q) + -> ComposeResult<()> { + self.target.push(|target| question.compose(target), + |counts| counts.inc_qdcount(1)) + } + + pub fn answer(self) -> AnswerBuilder { + AnswerBuilder::new(self.target) + } + + pub fn finish(self) -> ComposeResult { + self.target.finish() + } +} + + +//------------ AnswerBuilder ------------------------------------------------ + +#[derive(Clone, Debug)] +pub struct AnswerBuilder { + target: MessageTarget +} + +impl AnswerBuilder { + fn new(target: MessageTarget) -> Self { + AnswerBuilder { target: target } + } + + pub fn header(&self) -> &Header { + self.target.header() + } + + pub fn header_mut(&mut self) -> &mut Header { + self.target.header_mut() + } + + pub fn push(&mut self, record: &R) -> ComposeResult<()> { + self.target.push(|target| record.compose(target), + |counts| counts.inc_ancount(1)) + } + + pub fn authority(self) -> AuthorityBuilder { + AuthorityBuilder::new(self.target) + } + + pub fn finish(self) -> ComposeResult { + self.target.finish() + } +} + + +//------------ AuthorityBuilder --------------------------------------------- + +#[derive(Clone, Debug)] +pub struct AuthorityBuilder { + target: MessageTarget +} + +impl AuthorityBuilder { + fn new(target: MessageTarget) -> Self { + AuthorityBuilder { target: target } + } + + pub fn header(&self) -> &Header { + self.target.header() + } + + pub fn header_mut(&mut self) -> &mut Header { + self.target.header_mut() + } + + pub fn push(&mut self, record: &R) -> ComposeResult<()> { + self.target.push(|target| record.compose(target), + |counts| counts.inc_nscount(1)) + } + + pub fn additional(self) -> AdditionalBuilder { + AdditionalBuilder::new(self.target) + } + + pub fn finish(self) -> ComposeResult { + self.target.finish() + } +} + + +//------------ AuthorityBuilder --------------------------------------------- + +#[derive(Clone, Debug)] +pub struct AdditionalBuilder { + target: MessageTarget +} + +impl AdditionalBuilder { + fn new(target: MessageTarget) -> Self { + AdditionalBuilder { target: target } + } + + pub fn header(&self) -> &Header { + self.target.header() + } + + pub fn header_mut(&mut self) -> &mut Header { + self.target.header_mut() + } + + pub fn push(&mut self, record: &R) -> ComposeResult<()> { + self.target.push(|target| record.compose(target), + |counts| counts.inc_nscount(1)) + } + + pub fn finish(self) -> ComposeResult { + self.target.finish() + } +} + + +//------------ MessageTarget ------------------------------------------------ + +/// Underlying data for constructing a DNS message. +/// +/// This private type does all the heavy lifting for constructing messages. +#[derive(Clone, Debug)] +struct MessageTarget { + /// The underlying bytes target. + target: C, + + /// The message’s header. + header: FullHeader, + + /// Position in build where the message starts. + start: C::Pos, +} + + +impl MessageTarget { + fn new(mut target: C) -> ComposeResult { + let start = target.pos(); + try!(target.push_empty(mem::size_of::())); + Ok(MessageTarget { + target: target, + header: FullHeader::new(), + start: start + }) + } + + fn header(&self) -> &Header { + self.header.header() + } + + fn header_mut(&mut self) -> &mut Header { + self.header.header_mut() + } + + fn push(&mut self, composeop: O, incop: I) -> ComposeResult<()> + where O: FnOnce(&mut C) -> ComposeResult<()>, + I: FnOnce(&mut HeaderCounts) -> ComposeResult<()> { + if !self.target.truncated() { + self.target.truncation_point(); + match composeop(&mut self.target) { + Ok(()) => { + try!(incop(self.header.counts_mut())); + Ok(()) + } + Err(ComposeError::SizeExceeded) => Ok(()), + Err(error) => Err(error) + } + } + else { Ok(()) } + } + + fn finish(mut self) -> ComposeResult { + self.header.header_mut().set_tc(self.target.truncated()); + try!(self.target.update_bytes(self.start, self.header.as_bytes())); + Ok(self.target) + } +} + diff --git a/src/bits/mod.rs b/src/bits/mod.rs new file mode 100644 index 00000000..0da2cceb --- /dev/null +++ b/src/bits/mod.rs @@ -0,0 +1,15 @@ +//! DNS data. + +pub mod compose; +pub mod error; +pub mod flavor; +pub mod header; +pub mod iana; +pub mod message; +pub mod name; +pub mod nest; +pub mod question; +pub mod parse; +pub mod rdata; +pub mod record; +pub mod u8; diff --git a/src/name.rs b/src/bits/name.rs similarity index 50% rename from src/name.rs rename to src/bits/name.rs index 91c6f714..78dc90ad 100644 --- a/src/name.rs +++ b/src/bits/name.rs @@ -1,65 +1,59 @@ -//! Domain name handling. -//! +//! Domain names. use std::ascii::AsciiExt; -use std::borrow::{Borrow, Cow, ToOwned}; +use std::borrow::{Borrow, Cow}; use std::cmp; -use std::convert::{self, Into}; -use std::error; use std::fmt; use std::hash; use std::mem; use std::ops::Deref; -use std::result; use std::str; - -use super::bytes::{self, BytesBuf, BytesSlice}; +use super::compose::ComposeBytes; +use super::error::{ComposeResult, FromStrError, FromStrResult, ParseError, + ParseResult}; +use super::parse::{ParseBytes, ParseLazy}; +use super::u8::{BytesExt, BytesVecExt}; -//------------ DomainName --------------------------------------------------- +//------------ DName -------------------------------------------------------- /// A trait common to all domain name types. /// -/// This trait only exists so that composed types containing domain names -/// can be generic over the various domain name types we have. The common -/// functionality, however, is very limited. -/// -pub trait DomainName: fmt::Display { - /// Converts the domain name into an owned domain name. - fn to_owned(&self) -> Result; +/// This trait makes it possible to define types that are generic over all +/// three types of domain names. +pub trait DName: fmt::Display + Sized { + /// Return a cow to a domain name slice. + fn to_cow(&self) -> ParseResult>; - /// Converts the domain name into a self-contained cow. - fn to_cow(&self) -> Result>; - - /// Pushes the domain name to the end of theprovided buffer. - fn push_buf(&self, buf: &mut B) -> Result<()>; - - /// Pushes the domain name to buffer using name compression. - fn push_buf_compressed(&self, buf: &mut B) -> Result<()>; + /// Return an owned domain name. + fn to_owned(&self) -> ParseResult { + Ok(try!(self.to_cow()).into_owned()) + } } -//------------ DomainNameSlice ---------------------------------------------- +//------------ DNameSlice --------------------------------------------------- -/// A byte slice representing a self-contained domain name. +/// Unsized type for a complete domain name. /// -/// This is an *unsized* type. +/// This type implements functionality common to `DNameRef` and `OwnedDName` +/// both of which deref to it. /// +/// A domain name slice is a bytes slice encoded following the domain name +/// encoding rules with only normal and binary labels. #[derive(Debug)] -pub struct DomainNameSlice { +pub struct DNameSlice { slice: [u8] } - /// # Creation and Conversion /// -impl DomainNameSlice { - /// Create a domain name slice from a bytes slice. +impl DNameSlice { + /// Creates a domain name slice form a bytes slice. /// - /// This is only safe if the slice follows the encoding rules and does - /// not contain a compressed label. - /// - unsafe fn from_bytes(slice: &[u8]) -> &DomainNameSlice { + /// This is only safe if the input slice follows the encoding rules for + /// a domain name and does not contain compressed labels. + unsafe fn from_bytes(slice: &[u8]) -> &DNameSlice { mem::transmute(slice) } @@ -69,9 +63,8 @@ impl DomainNameSlice { } /// Converts `self` into an owned domain name. - /// - pub fn to_owned(&self) -> DomainNameBuf { - DomainNameBuf::from(self) + pub fn to_owned(&self) -> OwnedDName { + OwnedDName::from(self) } /// Converts `self` to a string. @@ -102,7 +95,7 @@ impl DomainNameSlice { /// # Properties /// -impl DomainNameSlice { +impl DNameSlice { /// Checks whether the domain name is absolute. /// /// A domain name is absolute if it ends with an empty normal label @@ -120,14 +113,12 @@ impl DomainNameSlice { } -/// # Iteration over labels. +/// # Iteration over Labels /// -impl DomainNameSlice { - +impl DNameSlice { /// Produces an iterator over the labels in the name. - /// - pub fn iter(&self) -> NameIter { - NameIter { slice: &self.slice } + pub fn iter<'a> (&'a self) -> SliceIter<'a> { + SliceIter::new(self) } /// Returns the number of labels in `self`. @@ -156,14 +147,13 @@ impl DomainNameSlice { } -/// # Manipulations +/// # Working with Parts /// -impl DomainNameSlice { +impl DNameSlice { /// Returns the first label and the rest of the name. /// /// Returns `None` if the name is empty. - /// - pub fn split_first(&self) -> Option<(Label, &DomainNameSlice)> { + pub fn split_first(&self) -> Option<(Label, &DNameSlice)> { let mut iter = self.iter(); iter.next().map(|l| (l, iter.as_name())) } @@ -172,13 +162,13 @@ impl DomainNameSlice { /// /// Returns `None` for an empty domain name. Returns an empty domain /// name for a single label domain name. - pub fn parent(&self) -> Option<&DomainNameSlice> { + pub fn parent(&self) -> Option<&DNameSlice> { self.split_first().map(|(_, tail)| tail) } /// Determines whether `base` is a prefix of `self`. /// - /// The method only cosiders whole labels and compares them + /// The method only considers whole labels and compares them /// case-insensitively. /// /// The current implementation does not compare a sequence of binary @@ -204,7 +194,7 @@ impl DomainNameSlice { /// Determines whether `base` is a suffix of `self`. /// - /// The method only cosiders whole labels and compares them + /// The method only considers whole labels and compares them /// case-insensitively. /// /// The current implementation does not compare a sequence of binary @@ -240,101 +230,56 @@ impl DomainNameSlice { } /// Creates an owned domain name with `base` adjoined to `self`. - pub fn join>(&self, base: N) -> DomainNameBuf { + pub fn join>(&self, base: N) -> OwnedDName { self._join(base.as_ref()) } - fn _join(&self, base: &Self) -> DomainNameBuf { + fn _join(&self, base: &Self) -> OwnedDName { let mut res = self.to_owned(); res.append(base); res } } -impl DomainName for DomainNameSlice { - fn to_owned(&self) -> Result { - Ok(self.to_owned()) - } - fn to_cow(&self) -> Result> { - Ok(Cow::Borrowed(self)) - } +//--- AsRef - fn push_buf(&self, buf: &mut B) -> Result<()> { - buf.add_name_pos(self); - buf.push_bytes(&self.slice); - Ok(()) - } - - fn push_buf_compressed(&self, buf: &mut B) -> Result<()> { - let mut name = self; - loop { - match buf.get_name_pos(name) { - Some(pos) => { - LabelHead::Compressed(((pos & 0xFF00) >> 8) as u8) - .push(buf); - buf.push_u8((pos & 0xFF) as u8); - break; - } - None => { - let (left, right) = match name.split_first() { - Some(x) => x, - None => break - }; - buf.add_name_pos(name); - left.push_buf(buf); - name = right; - } - } - } - Ok(()) - } +impl AsRef for DNameSlice { + fn as_ref(&self) -> &DNameSlice { self } } -impl AsRef for DomainNameSlice { - fn as_ref(&self) -> &DomainNameSlice { self } -} +//--- ToOwned - -impl ToOwned for DomainNameSlice { - type Owned = DomainNameBuf; +impl ToOwned for DNameSlice { + type Owned = OwnedDName; fn to_owned(&self) -> Self::Owned { self.to_owned() } } -impl + ?Sized> PartialEq for DomainNameSlice { + +//--- PartialEq and Eq + +impl + ?Sized> PartialEq for DNameSlice { fn eq(&self, other: &T) -> bool { self.iter().eq(other.as_ref().iter()) } } -impl<'a> PartialEq> for DomainNameSlice { +impl<'a> PartialEq> for DNameSlice { /// Test whether `self` and `other` are equal. /// /// An unparsable `other` always compares false. - fn eq(&self, other: &CompactDomainName) -> bool { - let mut self_iter = self.iter(); - let mut other_iter = other.iter(); - loop { - match (self_iter.next(), other_iter.next()) { - (Some(left), Some(Ok(right))) => { - if left != right { return false } - } - (None, None) => { return true } - _ => { return false } - } - } + fn eq(&self, other: &LazyDName<'a>) -> bool { + self.iter().eq(other.iter()) } } -//impl + ?Sized> PartialEq for DomainNameSlice { -// fn eq(&self, other: &T) -> bool { -impl PartialEq for DomainNameSlice { +impl PartialEq for DNameSlice { fn eq(&self, other: &str) -> bool { if !other.is_ascii() { return false } let mut other = other.as_bytes(); - let mut name = unsafe { DomainNameSlice::from_bytes(&self.slice) }; + let mut name = unsafe { DNameSlice::from_bytes(&self.slice) }; loop { let (label, tail) = match name.split_first() { Some(x) => x, @@ -352,44 +297,33 @@ impl PartialEq for DomainNameSlice { } } -impl cmp::Eq for DomainNameSlice { } +impl cmp::Eq for DNameSlice { } -impl + ?Sized> PartialOrd for DomainNameSlice { +//--- PartialOrd and Ord + +impl + ?Sized> PartialOrd for DNameSlice { fn partial_cmp(&self, other: &T) -> Option { self.iter().partial_cmp(other.as_ref().iter()) } } -impl<'a> PartialOrd> for DomainNameSlice { - fn partial_cmp(&self, other: &CompactDomainName) -> Option { - let mut self_iter = self.iter(); - let mut other_iter = other.iter(); - loop { - match (self_iter.next(), other_iter.next()) { - (None, None) => return Some(cmp::Ordering::Equal), - (None, _ ) => return Some(cmp::Ordering::Less), - (_ , None) => return Some(cmp::Ordering::Greater), - (Some(left), Some(Ok(right))) => { - let ordering = left.partial_cmp(&right); - if ordering != Some(cmp::Ordering::Equal) { - return ordering; - } - } - (_, Some(Err(_))) => return None, - } - } +impl<'a> PartialOrd> for DNameSlice { + fn partial_cmp(&self, other: &LazyDName) -> Option { + self.iter().partial_cmp(other.iter()) } } -impl Ord for DomainNameSlice { +impl Ord for DNameSlice { fn cmp(&self, other: &Self) -> cmp::Ordering { self.iter().cmp(other.iter()) } } -impl hash::Hash for DomainNameSlice { +//--- Hash + +impl hash::Hash for DNameSlice { fn hash(&self, state: &mut H) { use std::hash::Hash; @@ -399,47 +333,187 @@ impl hash::Hash for DomainNameSlice { } } -impl fmt::Display for DomainNameSlice { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + +//--- Display + +impl fmt::Display for DNameSlice { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_string().fmt(f) } } -//------------ DomainNameBuf ------------------------------------------------ +//------------ DNameRef ----------------------------------------------------- -/// An owned, mutable, self-contained domain name. +/// A reference to a complete domain name. /// +/// This is nothing more than a wrapper around a `&DNameSlice` to provide a +/// drop-in type with the same semantics as the other two domain name types. +/// It derefs to a `DNameSlice`, thus providing all the methods that type +/// provides. #[derive(Clone, Debug)] -pub struct DomainNameBuf { +pub struct DNameRef<'a> { + inner: &'a DNameSlice +} + +impl<'a> DNameRef<'a> { + unsafe fn from_bytes(bytes: &'a [u8]) -> Self { + DNameRef::from_slice(DNameSlice::from_bytes(bytes)) + } + + pub fn from_slice(slice: &'a DNameSlice) -> Self { + DNameRef { inner: slice } + } + + pub fn as_slice(&self) -> &DNameSlice { + self + } + + pub fn to_owned(&self) -> OwnedDName { + self.inner.to_owned() + } + + pub fn parse>(parser: &mut P) -> ParseResult { + let mut sub = parser.sub(); + loop { + let label = try!(Label::parse_complete(&mut sub)); + if label.is_root() { + let bytes = try!(parser.parse_bytes(sub.seen())); + return Ok(unsafe { DNameRef::from_bytes(bytes) }) + } + } + } +} + + +//--- DName + +impl<'a> DName for DNameRef<'a> { + fn to_cow(&self) -> ParseResult> { + Ok(Cow::Borrowed(self.inner)) + } +} + + +//--- From + +impl<'a> From<&'a DNameSlice> for DNameRef<'a> { + fn from(slice: &'a DNameSlice) -> DNameRef { + Self::from_slice(slice) + } +} + + +//--- Deref, Borrow, AsRef + +impl<'a> Deref for DNameRef<'a> { + type Target = DNameSlice; + + fn deref(&self) -> &Self::Target { + self.inner + } +} + +impl<'a> Borrow for DNameRef<'a> { + fn borrow(&self) -> &DNameSlice { + self.deref() + } +} + +impl<'a> AsRef for DNameRef<'a> { + fn as_ref(&self) -> &DNameSlice { + self.deref() + } +} + + +//--- PartialEq, Eq + +impl<'a, T: AsRef> PartialEq for DNameRef<'a> { + fn eq(&self, other: &T) -> bool { + self.deref().eq(other.as_ref()) + } +} + +impl<'a> Eq for DNameRef<'a> { } + + +//--- PartialOrd, Ord + +impl<'a, T: AsRef> PartialOrd for DNameRef<'a> { + fn partial_cmp(&self, other: &T) -> Option { + self.deref().partial_cmp(other.as_ref()) + } +} + +impl<'a> Ord for DNameRef<'a> { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.deref().cmp(other.deref()) + } +} + + +//--- Hash + +impl<'a> hash::Hash for DNameRef<'a> { + fn hash(&self, state: &mut H) { + self.deref().hash(state) + } +} + + +//--- Display + +impl<'a> fmt::Display for DNameRef<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.deref().fmt(f) + } +} + + +//------------ OwnedDName --------------------------------------------------- + +/// An owned complete domain name. +/// +/// This type derefs to `DNameSlice` and therefore provides all its +/// methods. +#[derive(Clone, Debug)] +pub struct OwnedDName { inner: Vec } -impl DomainNameBuf { - /// Creates a `DomainNameBuf` from a `u8` slice. +/// # Creation and Conversion +/// +impl OwnedDName { + /// Creates an owned domain name from a bytes slice. /// - /// This is only safe if the slice follows the encoding rules. - /// - unsafe fn from_bytes(s: &[u8]) -> DomainNameBuf { - DomainNameBuf { inner: Vec::from(s) } + /// This is only safe if the slice followes the domain name encoding + /// rules and does not contain a compressed label. + unsafe fn from_bytes(slice: &[u8]) -> Self { + OwnedDName { inner: Vec::from(slice) } } /// Creates a new empty domain name. - /// - pub fn new() -> DomainNameBuf { - DomainNameBuf { inner: Vec::new() } + pub fn new() -> OwnedDName { + OwnedDName { inner: Vec::new() } } /// Creates a new domain name with only the root label. - pub fn root() -> DomainNameBuf { - let mut res = DomainNameBuf::new(); - res.push(Label::Normal(b"")); + pub fn root() -> OwnedDName { + let mut res = OwnedDName::new(); + res.push(&Label::root()); res } /// Creates a new domain name from a string. - pub fn from_str(s: &str) -> ParseResult { - let mut res = DomainNameBuf::new(); + /// + /// The string must followed zone file conventions. It must only contain + /// of printable ASCII characters and no whitespace. Invidual labels are + /// separated by a dot. A backslash escapes the next character unless + /// that is a `0`, `1`, or `2`, in which case the next three characters + /// are the byte value in decimal representation. + pub fn from_str(s: &str) -> FromStrResult { + let mut res = OwnedDName::new(); let mut label = Vec::new(); let mut chars = s.chars(); loop { @@ -448,38 +522,17 @@ impl DomainNameBuf { match c { '.' => { if label.len() > 63 { - return Err(ParseError::OverlongLabel) + return Err(FromStrError::LongLabel) } res.inner.push(label.len() as u8); res.inner.extend(&label); label.clear(); } - '\\' => { - let ch = try!(chars.next() - .ok_or(ParseError::PrematureEnd)); - if ch.is_digit(10) { - let v = ch.to_digit(10).unwrap() * 100 - + try!(chars.next() - .ok_or(ParseError::PrematureEnd) - .and_then(|c| c.to_digit(10) - .ok_or( - ParseError::IllegalEscape))) - * 10 - + try!(chars.next() - .ok_or(ParseError::PrematureEnd) - .and_then(|c| c.to_digit(10) - .ok_or( - ParseError::IllegalEscape))); - label.push(v as u8); - } - else { - label.push(ch as u8); - } - } + '\\' => label.push(try!(parse_escape(&mut chars))), ' ' ... '-' | '/' ... '[' | ']' ... '~' => { label.push(c as u8); } - _ => return Err(ParseError::IllegalCharacter) + _ => return Err(FromStrError::IllegalCharacter) } } None => break @@ -490,181 +543,209 @@ impl DomainNameBuf { Ok(res) } - /// Coerces to a `DomainNameSlice` slice. - /// - pub fn as_name(&self) -> &DomainNameSlice { + pub fn parse_complete<'a, P: ParseBytes<'a>>(parser: &mut P) + -> ParseResult { + Ok(try!(DNameRef::parse(parser)).to_owned()) + } + + pub fn parse_compressed<'a, P: ParseLazy<'a>>(parser: &mut P) + -> ParseResult { + Ok(try!(try!(LazyDName::parse(parser)).to_owned())) + } + + pub fn as_slice(&self) -> &DNameSlice { self } +} - /// Extends self with a normal label. - /// - pub fn push_normal>(&mut self, label: L) -> Result<()> { - Ok(self.push(try!(Label::new_normal(label.as_ref())))) + +/// # Manipulation +/// +impl OwnedDName { + /// Extends the name with a label. + pub fn push(&mut self, label: &Label) { + label.push_vec(&mut self.inner); } - /// Extends self with a binary label. + /// Extends the name with a domain name. /// - pub fn push_binary(&mut self, count: u8, slice: &[u8]) -> Result<()>{ - Ok(self.push(try!(Label::new_binary(count, slice)))) - } - - /// Extends self with a label. - pub fn push(&mut self, label: Label) { - label.push_buf(&mut self.inner); - } - - /// Extends `self` with a domain name. - /// - /// If `self` is already absolute, nothing will happen. - /// - /// XXX Is this a good rule? Checking for absolute names is costly. - /// - pub fn append>(&mut self, name: N) { + /// This will always add `name`, even if `self` is already absolute. + /// While this may formally result in an illegal name, writing the name + /// to a message (or otherwise using it) will always assume the name to + /// end with the root label. Thus, this operation is safe. What’s more, + /// just appending the name is a lot quicker than first checking whether + /// the name is absolute already which involves walking the name. + pub fn append>(&mut self, name: N) { self._append(name.as_ref()) } - fn _append(&mut self, name: &DomainNameSlice) { - if !self.is_absolute() { - self.inner.extend(&name.slice) - } - } -} - -impl DomainName for DomainNameBuf { - fn to_owned(&self) -> Result { - Ok(self.clone()) - } - - fn to_cow(&self) -> Result> { - Ok(Cow::Borrowed(&self)) - } - - fn push_buf(&self, buf: &mut B) -> Result<()> { - self.deref().push_buf(buf) - } - - fn push_buf_compressed(&self, buf: &mut B) -> Result<()> { - self.deref().push_buf_compressed(buf) + fn _append(&mut self, name: &DNameSlice) { + self.inner.extend(&name.slice) } } -impl<'a> From<&'a DomainNameSlice> for DomainNameBuf { - fn from(n: &'a DomainNameSlice) -> DomainNameBuf { - unsafe { DomainNameBuf::from_bytes(&n.slice) } +//--- DName + +impl DName for OwnedDName { + fn to_cow(&self) -> ParseResult> { + Ok(Cow::Borrowed(self)) } } -impl str::FromStr for DomainNameBuf { - type Err = ParseError; - fn from_str(s: &str) -> ParseResult { - DomainNameBuf::from_str(s) +//--- From and FromStr + +impl<'a> From<&'a DNameSlice> for OwnedDName { + fn from(name: &'a DNameSlice) -> OwnedDName { + unsafe { OwnedDName::from_bytes(&name.slice) } } - } -impl Deref for DomainNameBuf { - type Target = DomainNameSlice; +impl str::FromStr for OwnedDName { + type Err = FromStrError; + + fn from_str(s: &str) -> FromStrResult { + OwnedDName::from_str(s) + } +} + + +//--- Deref, Borrow, and AsRef + +impl Deref for OwnedDName { + type Target = DNameSlice; fn deref(&self) -> &Self::Target { - unsafe { DomainNameSlice::from_bytes(&self.inner) } + unsafe { DNameSlice::from_bytes(&self.inner) } } } -impl Borrow for DomainNameBuf { - fn borrow(&self) -> &DomainNameSlice { +impl Borrow for OwnedDName { + fn borrow(&self) -> &DNameSlice { self.deref() } } - -impl AsRef for DomainNameBuf { - fn as_ref(&self) -> &DomainNameSlice { self } -} - - -impl cmp::PartialEq for DomainNameBuf { - fn eq(&self, other: &Self) -> bool { - self.deref().eq(other.deref()) +impl AsRef for OwnedDName { + fn as_ref(&self) -> &DNameSlice { + self } } -impl cmp::Eq for DomainNameBuf { } -impl cmp::PartialOrd for DomainNameBuf { - fn partial_cmp(&self, other: &Self) -> Option { - self.deref().partial_cmp(other.deref()) +//--- PartialEq and Eq + +impl> PartialEq for OwnedDName { + fn eq(&self, other: &T) -> bool { + self.deref().eq(other.as_ref()) } } -impl cmp::Ord for DomainNameBuf { - fn cmp(&self, other: &DomainNameBuf) -> cmp::Ordering { +impl Eq for OwnedDName { } + + +//--- PartialOrd and Ord + +impl> PartialOrd for OwnedDName { + fn partial_cmp(&self, other: &T) -> Option { + self.deref().partial_cmp(other.as_ref()) + } +} + +impl Ord for OwnedDName { + fn cmp(&self, other: &Self) -> cmp::Ordering { self.deref().cmp(other.deref()) } } -impl hash::Hash for DomainNameBuf { + +//--- Hash + +impl hash::Hash for OwnedDName { fn hash(&self, state: &mut H) { self.deref().hash(state) } } -impl fmt::Display for DomainNameBuf { +//--- Display + +impl fmt::Display for OwnedDName { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.deref().fmt(f) } } -//------------ CompactDomainName -------------------------------------------- -/// A reference to a possibly compressed domain name. +//------------ LazyDName ---------------------------------------------------- + +/// A possibly compressed domain name. /// -/// This domain name is not self-contained but rather may reference -/// another domain name. Because if this, it always needs a second bytes -/// slice representing the message and called the *context*. -/// +/// In order to avoid allocations, compression is only resolved when needed. +/// The consequence of this is that a lazy domain name needs to have a +/// reference to the original message handy. To avoid too many type +/// parameters, this reference, called context, is always to a bytes slice. +/// Another consequence is that format errors may only surface when +/// using the name. Thus, there is `ParseResult` return types all over +/// the place. #[derive(Clone, Debug)] -pub struct CompactDomainName<'a> { +pub struct LazyDName<'a> { slice: &'a [u8], - context: &'a [u8], + context: &'a [u8] } /// # Creation and Conversion /// -impl<'a> CompactDomainName<'a> { - /// Creates a new domain name from its components. - pub fn new(slice: &'a[u8], context: &'a[u8]) -> CompactDomainName<'a> { - CompactDomainName { slice: slice, context: context } +impl<'a> LazyDName<'a> { + /// Creates a lazy domain name from its components. + pub fn new(slice: &'a[u8], context: &'a[u8]) -> Self { + LazyDName { slice: slice, context: context } } - /// Splits a wire domain name from the beginning of a bytes slice. - pub fn split_from(slice: &'a[u8], context: &'a[u8]) - -> Result<(CompactDomainName<'a>, &'a[u8])> { + /// Splits a lazy domain name from the beginning of a bytes slice. + pub fn split_from(slice: &'a [u8], context: &'a [u8]) + -> ParseResult<(Self, &'a[u8])> { + Self::steal_from(slice, context).map(|(res, len)| (res, &slice[len..])) + } + + /// Steal a lazy domain name from the beginning of a byte slice. + /// + /// This returns the name and the octet length of name. + pub fn steal_from(slice: &'a[u8], context: &'a [u8]) + -> ParseResult<(Self, usize)> { let mut pos = 0; loop { let (end, head) = try!(Label::peek(slice, pos)); if head.is_final() { - let (bytes, slice) = try!(slice.split_bytes(end)); - return Ok((CompactDomainName::new(bytes, context), slice)); + return Ok((LazyDName::new(&slice[..end], context), end)) } pos = end; } } - /// Converts `self` into a self-contained, owned domain name. - pub fn to_owned(&self) -> Result { + pub fn parse>(parser: &mut P) -> ParseResult { + let mut sub = parser.sub(); + loop { + if try!(Label::skip(&mut sub)) { + let bytes = try!(parser.parse_bytes(sub.seen())); + return Ok(LazyDName::new(bytes, parser.context())) + } + } + } + + /// Converts the lazy domain name into an owned complete domain name. + pub fn to_owned(&self) -> ParseResult { Ok(try!(self.decompress()).into_owned()) } - /// Decompresses `self`. + /// Decompress the lazy domain name, /// /// If `self` does not contain any compressed labels, it will be /// coerced into a regular domain name slice. If it does, it will be /// converted into an owned domain name. - pub fn decompress(&self) -> Result> { + pub fn decompress(&self) -> ParseResult> { // Walk over the name and return it if it ends without compression. let mut pos = 0; loop { @@ -672,7 +753,7 @@ impl<'a> CompactDomainName<'a> { match head { LabelHead::Normal(0) => { let name = unsafe { - DomainNameSlice::from_bytes(&self.slice[..end]) + DNameSlice::from_bytes(&self.slice[..end]) }; return Ok(Cow::Borrowed(name)); } @@ -685,17 +766,18 @@ impl<'a> CompactDomainName<'a> { // We have compression. Copy all until the compressed label, then // iterate over the rest and append each label. let (bytes, slice) = try!(self.slice.split_bytes(pos)); - let mut res = unsafe { DomainNameBuf::from_bytes(bytes) }; - for label in CompactIter::new(slice, self.context) { + let mut res = unsafe { OwnedDName::from_bytes(bytes) }; + for label in LazyIter::new(slice, self.context) { let label = try!(label); - res.push(label) + res.push(&label) } Ok(Cow::Owned(res)) } - pub fn to_string(&self) -> Result { + /// Converts the lazy domain name into a string. + pub fn to_string(&self) -> ParseResult { // Assuming a properly parsed out slice, the resulting string is - // at least its sice. + // at least its size. let mut res = String::with_capacity(self.slice.len()); for label in self.iter() { let label = try!(label); @@ -707,84 +789,133 @@ impl<'a> CompactDomainName<'a> { } -// # Iteration over Labels -// -impl<'a> CompactDomainName<'a> { +/// # Iteration over Labels +/// +impl<'a> LazyDName<'a> { /// Returns an iterator over the labels. - pub fn iter(&self) -> CompactIter<'a> { - CompactIter::new(self.slice, self.context) + pub fn iter(&self) -> LazyIter<'a> { + LazyIter::from_name(self) } } -impl<'a> DomainName for CompactDomainName<'a> { - fn to_owned(&self) -> Result { - self.to_owned() - } +//--- DName - fn to_cow(&self) -> Result> { +impl<'a> DName for LazyDName<'a> { + fn to_cow(&self) -> ParseResult> { self.decompress() } +} - fn push_buf(&self, buf: &mut O) -> Result<()> { - for label in self.iter() { - try!(label).push_buf(buf) + +//--- PartialEq + +impl<'a, 'b> PartialEq> for LazyDName<'a> { + /// Check for equality. + /// + /// Lazy domain names that result in a parse error always compare + /// unequal. Which is also why `LazyDName` does not implement `Eq` + /// or `Ord`. (Hey, it ain’t called lazy for no reason!) + fn eq(&self, other: &LazyDName<'b>) -> bool { + // The orphan rule prohibits us using Iterator::eq() here. + let mut self_iter = self.iter(); + let mut other_iter = other.iter(); + + loop { + match (self_iter.next(), other_iter.next()) { + (Some(Ok(x)), Some(Ok(y))) => if x != y { return false }, + (None, None) => return true, + _ => return false, + } } - Ok(()) - } - - fn push_buf_compressed(&self, buf: &mut O) -> Result<()> { - try!(self.decompress()).push_buf_compressed(buf) } } - -impl<'a> PartialEq for CompactDomainName<'a> { - fn eq(&self, other: &CompactDomainName) -> bool { - self.iter().eq(other.iter()) - } -} - -impl<'a, T: AsRef + ?Sized> PartialEq for CompactDomainName<'a> -{ +impl<'a, T: AsRef> PartialEq for LazyDName<'a> { fn eq(&self, other: &T) -> bool { - other.as_ref().eq(self) + self.iter().eq(other.as_ref().iter()) } } -impl<'a, T: AsRef + ?Sized> PartialOrd for CompactDomainName<'a> -{ - fn partial_cmp(&self, other: &T) -> Option { - other.as_ref().partial_cmp(self).map(|o| o.reverse()) - } -} -impl<'a> fmt::Display for CompactDomainName<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self.to_string() { - Ok(s) => s.fmt(f), - Err(..) => "".fmt(f) +//--- PartialOrd + +impl<'a, 'b> PartialOrd> for LazyDName<'a> { + /// Compare. + /// + /// This will return `None` if either the names is broken. + fn partial_cmp(&self, other: &LazyDName<'b>) -> Option { + // Orphan rule strikes again. + use std::cmp::Ordering::*; + + let mut self_iter = self.iter(); + let mut other_iter = other.iter(); + + loop { + match (self_iter.next(), other_iter.next()) { + (Some(Ok(x)), Some(Ok(y))) => match x.partial_cmp(&y) { + Some(Equal) => (), + non_eq => return non_eq + }, + (Some(Err(..)), _) => return None, + (_, Some(Err(..))) => return None, + (None, None) => return Some(Equal), + (None, _) => return Some(Less), + (_, None) => return Some(Greater) + } } } } - -//------------ NameIter ----------------------------------------------------- - -/// An iterator over the labels in a domain name. -#[derive(Clone, Debug)] -pub struct NameIter<'a> { - slice: &'a [u8] -} - -impl<'a> NameIter<'a> { - /// Returns the domain name for the remaining portion. - pub fn as_name(&self) -> &'a DomainNameSlice { - unsafe { DomainNameSlice::from_bytes(self.slice) } +impl<'a, T: AsRef> PartialOrd for LazyDName<'a> { + fn partial_cmp(&self, other: &T) -> Option { + self.iter().partial_cmp(other.as_ref().iter()) } } -impl<'a> Iterator for NameIter<'a> { + +//--- Display + +impl<'a> fmt::Display for LazyDName<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut empty = true; + for label in self.iter() { + if !empty { try!('.'.fmt(f)) } else { empty = false } + match label { + Ok(label) => try!(label.fmt(f)), + Err(..) => { try!("".fmt(f)); break } + } + } + '.'.fmt(f) + } +} + + +//------------ SliceIter ---------------------------------------------------- + +/// An iterator over the labels of a domain name slice. +/// +/// This iterator stops either at the end of the domain name or when it +/// encounters the first root label, thus quietly turning every name into +/// a valid name. +#[derive(Clone, Debug)] +pub struct SliceIter<'a> { + slice: &'a[u8] +} + +impl<'a> SliceIter<'a> { + /// Creates a new iterator from a domain name slice. + fn new(name: &'a DNameSlice) -> Self { + SliceIter { slice: &name.slice } + } + + /// Returns th domain name of the remaining portion. + pub fn as_name(&self) -> &'a DNameSlice { + unsafe { DNameSlice::from_bytes(self.slice) } + } +} + +impl<'a> Iterator for SliceIter<'a> { type Item = Label<'a>; fn next(&mut self) -> Option { @@ -792,33 +923,38 @@ impl<'a> Iterator for NameIter<'a> { Err(..) => return None, Ok(res) => res }; - self.slice = slice; + self.slice = if label.is_root() { b"" } + else { slice }; Some(label) } } -//------------ CompactIter -------------------------------------------------- +//------------ LazyIter ----------------------------------------------------- -/// An iterator over the labels in a frail domain name. +/// An iterator over the labels of a lazy domain name. #[derive(Clone, Debug)] -pub struct CompactIter<'a> { +pub struct LazyIter<'a> { slice: &'a[u8], - context: &'a[u8], + context: &'a[u8] } -impl<'a> CompactIter<'a> { - fn new(slice: &'a[u8], context: &'a[u8]) -> CompactIter<'a> { - CompactIter { slice: slice, context: context } +impl<'a> LazyIter<'a> { + fn new(slice: &'a[u8], context: &'a[u8]) -> Self { + LazyIter { slice: slice, context: context } } - pub fn as_name(&self) -> Result> { - CompactDomainName::new(self.slice, self.context).decompress() + fn from_name(name: &LazyDName<'a>) -> Self { + LazyIter::new(name.slice, name.context) + } + + pub fn as_name(&self) -> ParseResult> { + LazyDName::new(self.slice, self.context).decompress() } } -impl<'a> Iterator for CompactIter<'a> { - type Item = Result>; +impl<'a> Iterator for LazyIter<'a> { + type Item = ParseResult>; fn next(&mut self) -> Option { if self.slice.is_empty() { return None } @@ -843,7 +979,15 @@ impl<'a> Iterator for CompactIter<'a> { /// actual content by the domain name types on the fly. /// #[derive(Clone, Debug)] -pub enum Label<'a> { +pub struct Label<'a>(LabelContent<'a>); + + +/// The actual content of a label. +/// +/// This type is private so that it is impossible to gate label creation +/// making public label handling error-free. +#[derive(Clone, Debug)] +enum LabelContent<'a> { /// A normal label containing up to 63 octets. Normal(&'a [u8]), @@ -853,45 +997,73 @@ pub enum Label<'a> { /// indicating 256 bits. The second element is the byte slice /// representing the bit field padded to full octets. /// - /// This vairant is historic and annoying and shouldn't really be - /// encountered anymore. + /// This variant is historic and annoying and shouldn't really be + /// encountered. Binary(u8, &'a[u8]), } impl<'a> Label<'a> { - /// Create a new normal label. - fn new_normal(s: &[u8]) -> Result