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

238 lines
5.1 KiB
Rust

use bytes::Bytes;
use crate::errors::*;
use std::fmt;
use std::cmp::Ordering;
#[inline]
fn check_label(label: &[u8]) -> Result<()> {
if label.len() == 0 {
failure::bail!("label must not be empty")
}
if label.len() > 63 {
failure::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<Self> {
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<DnsLabelRef<'a>>>(&self, rhs: L) -> Ordering {
compare_ascii_lc(self.as_raw(), rhs.into().as_raw())
}
}
impl<'a> From<DnsLabelRef<'a>> for DnsLabel {
fn from(label_ref: DnsLabelRef<'a>) -> Self {
DnsLabel{
label: Bytes::from(label_ref.label),
}
}
}
impl PartialEq<DnsLabel> for DnsLabel {
#[inline]
fn eq(&self, rhs: &DnsLabel) -> bool {
self.as_ref().eq(&rhs.as_ref())
}
}
impl<'a> PartialEq<DnsLabelRef<'a>> for DnsLabel {
#[inline]
fn eq(&self, rhs: &DnsLabelRef<'a>) -> bool {
self.as_ref().eq(rhs)
}
}
impl Eq for DnsLabel{}
impl PartialOrd<DnsLabel> for DnsLabel {
#[inline]
fn partial_cmp(&self, rhs: &DnsLabel) -> Option<Ordering> {
Some(self.compare(rhs))
}
}
impl<'a> PartialOrd<DnsLabelRef<'a>> for DnsLabel {
#[inline]
fn partial_cmp(&self, rhs: &DnsLabelRef<'a>) -> Option<Ordering> {
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<Self> {
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<DnsLabelRef<'b>>>(&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::cmp::min;
let l = min(lhs.len(), rhs.len());
for i in 0..l {
let a: u8 = lhs[i].to_ascii_lowercase();
let b: u8 = rhs[i].to_ascii_lowercase();
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<DnsLabelRef<'a>> for DnsLabelRef<'b> {
fn eq(&self, rhs: &DnsLabelRef<'a>) -> bool {
self.as_raw().eq_ignore_ascii_case(rhs.as_raw())
}
}
impl<'a> PartialEq<DnsLabel> 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<DnsLabelRef<'a>> for DnsLabelRef<'b> {
#[inline]
fn partial_cmp(&self, rhs: &DnsLabelRef<'a>) -> Option<Ordering> {
Some(self.compare(*rhs))
}
}
impl<'a> PartialOrd<DnsLabel> for DnsLabelRef<'a> {
#[inline]
fn partial_cmp(&self, rhs: &DnsLabel) -> Option<Ordering> {
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(crate::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(crate::unsafe_ops::from_utf8_unchecked(&self.label[done..]))?;
}
Ok(())
}
}