use bytes::Bytes; use errors::*; use std::fmt; use std::cmp::Ordering; #[inline] fn check_label(label: &[u8]) -> Result<()> { if label.len() == 0 { bail!("label must not be empty") } if label.len() > 63 { bail!("label must not be longer than 63 bytes") } Ok(()) } /// A DNS label (any binary string with `0 < length < 64`) #[derive(Clone)] pub struct DnsLabel { pub(super) label: Bytes, // 0 < len < 64 } impl DnsLabel { /// Create new label from existing storage /// /// Fails when the length doesn't match the requirement `0 < length < 64`. pub fn new(label: Bytes) -> Result { check_label(&label)?; Ok(DnsLabel{label}) } /// Convert to a representation without storage pub fn as_ref<'a>(&'a self) -> DnsLabelRef<'a> { DnsLabelRef{label: self.label.as_ref()} } /// Access as raw bytes pub fn as_bytes(&self) -> &Bytes { &self.label } /// Access as raw bytes pub fn as_raw(&self) -> &[u8] { &self.label } /// Length of label (0 < len < 64) pub fn len(&self) -> u8 { self.label.len() as u8 } /// compares two labels (ASCII case insensitive) #[inline] pub fn compare<'a, L: Into>>(&self, rhs: L) -> Ordering { compare_ascii_lc(self.as_raw(), rhs.into().as_raw()) } } impl<'a> From> for DnsLabel { fn from(label_ref: DnsLabelRef<'a>) -> Self { DnsLabel{ label: Bytes::from(label_ref.label), } } } impl PartialEq for DnsLabel { #[inline] fn eq(&self, rhs: &DnsLabel) -> bool { self.as_ref().eq(&rhs.as_ref()) } } impl<'a> PartialEq> for DnsLabel { #[inline] fn eq(&self, rhs: &DnsLabelRef<'a>) -> bool { self.as_ref().eq(rhs) } } impl Eq for DnsLabel{} impl PartialOrd for DnsLabel { #[inline] fn partial_cmp(&self, rhs: &DnsLabel) -> Option { Some(self.compare(rhs)) } } impl<'a> PartialOrd> for DnsLabel { #[inline] fn partial_cmp(&self, rhs: &DnsLabelRef<'a>) -> Option { Some(self.compare(*rhs)) } } impl Ord for DnsLabel { #[inline] fn cmp(&self, rhs: &Self) -> Ordering { self.compare(rhs) } } impl fmt::Debug for DnsLabel { fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.as_ref().fmt(w) } } impl fmt::Display for DnsLabel { fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.as_ref().fmt(w) } } /// A DNS label (any binary string with `0 < length < 64`) /// /// Storage is provided through lifetime. #[derive(Clone,Copy)] pub struct DnsLabelRef<'a> { pub(super) label: &'a [u8], // 0 < len < 64 } impl<'a> DnsLabelRef<'a> { /// Create new label from existing storage /// /// Fails when the length doesn't match the requirement `0 < length < 64`. pub fn new(label: &'a [u8]) -> Result { check_label(label)?; Ok(DnsLabelRef{label}) } /// Access as raw bytes pub fn as_raw(&self) -> &'a [u8] { self.label } /// Length of label (0 < len < 64) pub fn len(&self) -> u8 { self.label.len() as u8 } /// compares two labels (ASCII case insensitive) #[inline] pub fn compare<'b, L: Into>>(&self, rhs: L) -> Ordering { compare_ascii_lc(self.as_raw(), rhs.into().as_raw()) } } fn compare_ascii_lc(lhs: &[u8], rhs: &[u8]) -> Ordering { use std::ascii::AsciiExt; use std::cmp::min; let l = min(lhs.len(), rhs.len()); for i in 0..l { let a : u8 = AsciiExt::to_ascii_lowercase(&lhs[i]); let b : u8 = AsciiExt::to_ascii_lowercase(&rhs[i]); match Ord::cmp(&a, &b) { Ordering::Equal => continue, c => return c, } } Ord::cmp(&lhs.len(), &rhs.len()) } impl<'a> From<&'a DnsLabel> for DnsLabelRef<'a> { fn from(label: &'a DnsLabel) -> Self { label.as_ref() } } impl<'a, 'b> PartialEq> for DnsLabelRef<'b> { fn eq(&self, rhs: &DnsLabelRef<'a>) -> bool { use std::ascii::AsciiExt; AsciiExt::eq_ignore_ascii_case(self.as_raw(), rhs.as_raw()) } } impl<'a> PartialEq for DnsLabelRef<'a> { #[inline] fn eq(&self, rhs: &DnsLabel) -> bool { self.eq(&rhs.as_ref()) } } impl<'a> Eq for DnsLabelRef<'a>{} impl<'a, 'b> PartialOrd> for DnsLabelRef<'b> { #[inline] fn partial_cmp(&self, rhs: &DnsLabelRef<'a>) -> Option { Some(self.compare(*rhs)) } } impl<'a> PartialOrd for DnsLabelRef<'a> { #[inline] fn partial_cmp(&self, rhs: &DnsLabel) -> Option { Some(self.compare(rhs)) } } impl<'a> Ord for DnsLabelRef<'a> { #[inline] fn cmp(&self, rhs: &Self) -> Ordering { self.compare(*rhs) } } impl<'a> fmt::Debug for DnsLabelRef<'a> { fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { // just escape the display version format!("{}", self).fmt(w) } } impl<'a> fmt::Display for DnsLabelRef<'a> { fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { let mut done = 0; for pos in 0..self.label.len() { let c = self.label[pos]; if c <= 0x21 || c >= 0x7e || b'.' == c || b'\\' == c { // flush if done < pos { w.write_str(::unsafe_ops::from_utf8_unchecked(&self.label[done..pos]))?; } match c { b'.' => w.write_str(r#"\."#)?, b'\\' => w.write_str(r#"\\"#)?, _ => write!(w, r"\{:03}", c)?, } done = pos + 1; } } // final flush if done < self.label.len() { w.write_str(::unsafe_ops::from_utf8_unchecked(&self.label[done..]))?; } Ok(()) } }