use core::{cmp, fmt, hash, ops, str};
use core::str::FromStr;
#[cfg(feature = "std")] use std::vec::Vec;
#[cfg(feature = "bytes")] use bytes::Bytes;
#[cfg(feature="master")] use crate::master::scan::{
CharSource, Scan, Scanner, ScanError, SyntaxError
};
use super::super::cmp::CanonicalOrd;
use super::super::octets::{
Compose, EmptyBuilder, FormError, FromBuilder, OctetsBuilder, OctetsExt,
OctetsRef, Parse, Parser, ParseError, ShortBuf
};
use super::builder::{DnameBuilder, FromStrError};
use super::label::{Label, LabelTypeError, SplitLabelError};
use super::relative::{RelativeDname, DnameIter};
use super::traits::{ToLabelIter, ToDname};
#[cfg(feature="master")] use super::uncertain::UncertainDname;
#[derive(Clone)]
pub struct Dname<Octets: ?Sized>(Octets);
impl<Octets> Dname<Octets> {
pub const unsafe fn from_octets_unchecked(octets: Octets) -> Self {
Dname(octets)
}
pub fn from_octets(octets: Octets) -> Result<Self, DnameError>
where Octets: AsRef<[u8]> {
Dname::check_slice(octets.as_ref())?;
Ok(unsafe { Dname::from_octets_unchecked(octets) })
}
pub fn from_chars<C>(chars: C) -> Result<Self, FromStrError>
where
Octets: FromBuilder,
<Octets as FromBuilder>::Builder: EmptyBuilder,
C: IntoIterator<Item=char>
{
let mut builder = DnameBuilder::<Octets::Builder>::new();
builder.append_chars(chars)?;
builder.into_dname().map_err(Into::into)
}
pub fn root() -> Self
where Octets: From<&'static [u8]> {
unsafe { Self::from_octets_unchecked(b"\0".as_ref().into()) }
}
}
impl Dname<[u8]> {
unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
&*(slice as *const [u8] as *const Dname<[u8]>)
}
pub fn from_slice(slice: &[u8]) -> Result<&Self, DnameError> {
Self::check_slice(slice)?;
Ok(unsafe { Self::from_slice_unchecked(slice) })
}
pub fn root_slice() -> &'static Self {
unsafe { Self::from_slice_unchecked("\0".as_ref()) }
}
fn check_slice(mut slice: &[u8]) -> Result<(), DnameError> {
if slice.len() > 255 {
return Err(DnameError::LongName);
}
loop {
let (label, tail) = Label::split_from(slice)?;
if label.is_root() {
if tail.is_empty() {
break;
}
else {
return Err(DnameError::TrailingData)
}
}
if tail.is_empty() {
return Err(DnameError::RelativeName)
}
slice = tail;
}
Ok(())
}
}
impl Dname<&'static [u8]> {
pub fn root_ref() -> Self {
Self::root()
}
}
#[cfg(feature = "std")]
impl Dname<Vec<u8>> {
pub fn root_vec() -> Self {
Self::root()
}
pub fn vec_from_str(s: &str) -> Result<Self, FromStrError> {
FromStr::from_str(s)
}
}
#[cfg(feature="bytes")]
impl Dname<Bytes> {
pub fn root_bytes() -> Self {
Self::root()
}
pub fn bytes_from_str(s: &str) -> Result<Self, FromStrError> {
FromStr::from_str(s)
}
}
impl<Octets: ?Sized> Dname<Octets> {
pub fn as_octets(&self) -> &Octets {
&self.0
}
pub fn into_octets(self) -> Octets
where Octets: Sized {
self.0
}
pub fn into_relative(mut self) -> RelativeDname<Octets>
where Octets: Sized + OctetsExt {
let len = self.0.as_ref().len() - 1;
self.0.truncate(len);
unsafe { RelativeDname::from_octets_unchecked(self.0) }
}
pub fn for_ref(&self) -> Dname<&Octets> {
unsafe { Dname::from_octets_unchecked(&self.0) }
}
pub fn as_slice(&self) -> &[u8]
where Octets: AsRef<[u8]> {
self.0.as_ref()
}
pub fn for_slice(&self) -> Dname<&[u8]>
where Octets: AsRef<[u8]> {
unsafe { Dname::from_octets_unchecked(self.0.as_ref()) }
}
}
impl<Octets: AsRef<[u8]> + ?Sized> Dname<Octets> {
pub fn is_root(&self) -> bool {
self.0.as_ref().len() == 1
}
}
impl<Octets: AsRef<[u8]> + ?Sized> Dname<Octets> {
pub fn iter(&self) -> DnameIter {
DnameIter::new(self.0.as_ref())
}
pub fn iter_suffixes(&self) -> SuffixIter<&Octets> {
SuffixIter::new(self.for_ref())
}
pub fn label_count(&self) -> usize {
self.iter().count()
}
pub fn first(&self) -> &Label {
self.iter().next().unwrap()
}
pub fn last(&self) -> &'static Label {
Label::root()
}
pub fn starts_with<'a, N: ToLabelIter<'a> + ?Sized>(
&'a self, base: &'a N
) -> bool {
<Self as ToLabelIter>::starts_with(self, base)
}
pub fn ends_with<'a, N: ToLabelIter<'a> + ?Sized>(
&'a self, base: &'a N
) -> bool {
<Self as ToLabelIter>::ends_with(self, base)
}
pub fn is_label_start(&self, mut index: usize) -> bool {
if index == 0 {
return true
}
let mut tmp = self.as_slice();
while !tmp.is_empty() {
let (label, tail) = Label::split_from(tmp).unwrap();
let len = label.len() + 1;
if index < len || len == 1 {
return false
}
else if index == len {
return true
}
index -= len;
tmp = tail;
}
false
}
fn check_index(&self, index: usize) {
if !self.is_label_start(index) {
panic!("index not at start of a label");
}
}
pub fn slice(&self, begin: usize, end: usize) -> &RelativeDname<[u8]> {
self.check_index(begin);
self.check_index(end);
unsafe {
RelativeDname::from_slice_unchecked(&self.0.as_ref()[begin..end])
}
}
pub fn slice_from(&self, begin: usize) -> &Dname<[u8]> {
self.check_index(begin);
unsafe {
Dname::from_slice_unchecked(&self.0.as_ref()[begin..])
}
}
pub fn slice_to(&self, end: usize) -> &RelativeDname<[u8]> {
self.check_index(end);
unsafe {
RelativeDname::from_slice_unchecked(&self.0.as_ref()[..end])
}
}
pub fn range<'a>(
&'a self, begin: usize, end: usize
) -> RelativeDname<<&'a Octets as OctetsRef>::Range>
where &'a Octets: OctetsRef {
self.check_index(begin);
self.check_index(end);
unsafe {
RelativeDname::from_octets_unchecked(self.0.range(begin, end))
}
}
pub fn range_from<'a>(
&'a self, begin: usize
) -> Dname<<&'a Octets as OctetsRef>::Range>
where &'a Octets: OctetsRef {
self.check_index(begin);
unsafe {
Dname::from_octets_unchecked(self.0.range_from(begin))
}
}
pub fn range_to<'a>(
&'a self,
end: usize
) -> RelativeDname<<&'a Octets as OctetsRef>::Range>
where &'a Octets: OctetsRef {
self.check_index(end);
unsafe {
RelativeDname::from_octets_unchecked(self.0.range_to(end))
}
}
}
impl<Octets: AsRef<[u8]>> Dname<Octets> {
pub fn split_at(mut self, mid: usize) -> (RelativeDname<Octets>, Self)
where for<'a> &'a Octets: OctetsRef<Range = Octets> {
let left = self.split_to(mid);
(left, self)
}
pub fn split_to(&mut self, mid: usize) -> RelativeDname<Octets>
where for<'a> &'a Octets: OctetsRef<Range = Octets> {
self.check_index(mid);
let res = self.0.range_to(mid);
self.0 = self.0.range_from(mid);
unsafe {
RelativeDname::from_octets_unchecked(res)
}
}
pub fn truncate(mut self, len: usize) -> RelativeDname<Octets>
where Octets: OctetsExt {
self.check_index(len);
self.0.truncate(len);
unsafe { RelativeDname::from_octets_unchecked(self.0) }
}
pub fn split_first(&mut self) -> Option<RelativeDname<Octets>>
where for<'a> &'a Octets: OctetsRef<Range = Octets> {
if self.len() == 1 {
return None
}
let end = self.iter().next().unwrap().len() + 1;
Some(self.split_to(end))
}
pub fn parent(&mut self) -> bool
where for<'a> &'a Octets: OctetsRef<Range = Octets> {
self.split_first().is_some()
}
pub fn strip_suffix<N: ToDname + ?Sized>(
self,
base: &N
) -> Result<RelativeDname<Octets>, Self>
where Octets: OctetsExt {
if self.ends_with(base) {
let len = self.0.as_ref().len() - base.len();
Ok(self.truncate(len))
}
else {
Err(self)
}
}
}
impl<Octets: ?Sized> ops::Deref for Dname<Octets> {
type Target = Octets;
fn deref(&self) -> &Octets {
&self.0
}
}
impl<Octets: AsRef<T> + ?Sized, T: ?Sized> AsRef<T> for Dname<Octets> {
fn as_ref(&self) -> &T {
self.0.as_ref()
}
}
impl<Octets> FromStr for Dname<Octets>
where
Octets: FromBuilder,
<Octets as FromBuilder>::Builder: EmptyBuilder,
{
type Err = FromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_chars(s.chars())
}
}
impl<Octets, N> PartialEq<N> for Dname<Octets>
where
Octets: AsRef<[u8]> + ?Sized,
N: ToDname + ?Sized
{
fn eq(&self, other: &N) -> bool {
self.name_eq(other)
}
}
impl<Octets: AsRef<[u8]> + ?Sized> Eq for Dname<Octets> { }
impl<Octets, N> PartialOrd<N> for Dname<Octets>
where
Octets: AsRef<[u8]> + ?Sized,
N: ToDname + ?Sized
{
fn partial_cmp(&self, other: &N) -> Option<cmp::Ordering> {
Some(self.name_cmp(other))
}
}
impl<Octets: AsRef<[u8]> + ?Sized> Ord for Dname<Octets> {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.name_cmp(other)
}
}
impl<Octets, N> CanonicalOrd<N> for Dname<Octets>
where
Octets: AsRef<[u8]> + ?Sized,
N: ToDname + ?Sized
{
fn canonical_cmp(&self, other: &N) -> cmp::Ordering {
self.name_cmp(other)
}
}
impl<Octets: AsRef<[u8]> + ?Sized> hash::Hash for Dname<Octets> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
for item in self.iter() {
item.hash(state)
}
}
}
impl<'a, Octets> ToLabelIter<'a> for Dname<Octets>
where Octets: AsRef<[u8]> + ?Sized {
type LabelIter = DnameIter<'a>;
fn iter_labels(&'a self) -> Self::LabelIter {
self.iter()
}
fn len(&self) -> usize {
self.0.as_ref().len()
}
}
impl<Octets: AsRef<[u8]> + ?Sized> ToDname for Dname<Octets> {
fn as_flat_slice(&self) -> Option<&[u8]> {
Some(self.0.as_ref())
}
}
impl<'a, Octets> IntoIterator for &'a Dname<Octets>
where Octets: AsRef<[u8]> + ?Sized {
type Item = &'a Label;
type IntoIter = DnameIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<Ref: OctetsRef> Parse<Ref> for Dname<Ref::Range> {
fn parse(parser: &mut Parser<Ref>) -> Result<Self, ParseError> {
let len = name_len(parser)?;
Ok(unsafe {
Self::from_octets_unchecked(parser.parse_octets(len)?)
})
}
fn skip(parser: &mut Parser<Ref>) -> Result<(), ParseError> {
let len = name_len(parser)?;
parser.advance(len).map_err(Into::into)
}
}
fn name_len<Source: AsRef<[u8]>>(
parser: &mut Parser<Source>
) -> Result<usize, ParseError> {
let len = {
let mut tmp = parser.peek_all();
loop {
if tmp.is_empty() {
return Err(ParseError::ShortInput)
}
let (label, tail) = Label::split_from(tmp)?;
tmp = tail;
if label.is_root() {
break;
}
}
parser.remaining() - tmp.len()
};
if len > 255 {
Err(DnameError::LongName.into())
}
else {
Ok(len)
}
}
impl<Octets: AsRef<[u8]> + ?Sized> Compose for Dname<Octets> {
fn compose<T: OctetsBuilder>(
&self,
target: &mut T
) -> Result<(), ShortBuf> {
target.append_slice(self.0.as_ref())
}
fn compose_canonical<T: OctetsBuilder>(
&self,
target: &mut T
) -> Result<(), ShortBuf> {
target.append_all(|target| {
for label in self.iter_labels() {
label.compose_canonical(target)?;
}
Ok(())
})
}
}
#[cfg(feature="master")]
impl Scan for Dname<Bytes> {
fn scan<C: CharSource>(scanner: &mut Scanner<C>)
-> Result<Self, ScanError> {
let pos = scanner.pos();
let name = match UncertainDname::scan(scanner)? {
UncertainDname::Relative(name) => name,
UncertainDname::Absolute(name) => return Ok(name)
};
let origin = match *scanner.origin() {
Some(ref origin) => origin,
None => return Err((SyntaxError::NoOrigin, pos).into())
};
name.into_builder().append_origin(origin)
.map_err(|err| (SyntaxError::from(err), pos).into())
}
}
impl<Octets: AsRef<[u8]> + ?Sized> fmt::Display for Dname<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut iter = self.iter();
write!(f, "{}", iter.next().unwrap())?;
for label in iter {
if !label.is_root() {
write!(f, ".{}", label)?
}
}
Ok(())
}
}
impl<Octets: AsRef<[u8]> + ?Sized> fmt::Debug for Dname<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Dname({}.)", self)
}
}
#[derive(Clone)]
pub struct SuffixIter<Ref> {
name: Dname<Ref>,
start: Option<usize>,
}
impl<Ref> SuffixIter<Ref> {
fn new(name: Dname<Ref>) -> Self {
SuffixIter { name, start: Some(0), }
}
}
impl<Ref: OctetsRef> Iterator for SuffixIter<Ref> {
type Item = Dname<Ref::Range>;
fn next(&mut self) -> Option<Self::Item> {
let start = match self.start {
Some(start) => start,
None => return None
};
let res = self.name.range_from(start);
let label = res.first();
if label.is_root() {
self.start = None;
}
else {
self.start = Some(start + label.compose_len())
}
Some(res)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum DnameError {
BadLabel(LabelTypeError),
CompressedName,
LongName,
RelativeName,
TrailingData,
ShortInput,
}
impl From<LabelTypeError> for DnameError {
fn from(err: LabelTypeError) -> DnameError {
DnameError::BadLabel(err)
}
}
impl From<SplitLabelError> for DnameError {
fn from(err: SplitLabelError) -> DnameError {
match err {
SplitLabelError::Pointer(_) => DnameError::CompressedName,
SplitLabelError::BadType(t) => DnameError::BadLabel(t),
SplitLabelError::ShortInput => DnameError::ShortInput,
}
}
}
impl From<DnameError> for FormError {
fn from(err: DnameError) -> FormError {
FormError::new(
match err {
DnameError::BadLabel(_) => "unknown label type",
DnameError::CompressedName => "compressed domain name",
DnameError::LongName => "long domain name",
DnameError::RelativeName => "relative domain name",
DnameError::TrailingData => "trailing data in buffer",
DnameError::ShortInput => "unexpected end of buffer",
}
)
}
}
impl From<DnameError> for ParseError {
fn from(err: DnameError) -> ParseError {
match err {
DnameError::ShortInput => ParseError::ShortInput,
other => ParseError::Form(other.into())
}
}
}
impl fmt::Display for DnameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
DnameError::BadLabel(ref err)
=> err.fmt(f),
DnameError::CompressedName
=> f.write_str("compressed domain name"),
DnameError::LongName
=> f.write_str("long domain name"),
DnameError::RelativeName
=> f.write_str("relative name"),
DnameError::TrailingData
=> f.write_str("trailing data"),
DnameError::ShortInput
=> ParseError::ShortInput.fmt(f)
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for DnameError { }
#[cfg(test)]
pub(crate) mod test {
use super::*;
macro_rules! assert_panic {
( $cond:expr ) => {
{
let result = ::std::panic::catch_unwind(|| $cond);
assert!(result.is_err());
}
}
}
#[test]
fn impls() {
fn assert_to_dname<T: ToDname + ?Sized>(_: &T) { }
assert_to_dname(
Dname::from_slice(b"\0".as_ref()).unwrap()
);
assert_to_dname(
&Dname::from_octets(b"\0").unwrap()
);
assert_to_dname(
&Dname::from_octets(b"\0".as_ref()).unwrap()
);
assert_to_dname(
&Dname::from_octets(Vec::from(b"\0".as_ref())).unwrap()
);
}
#[cfg(feature = "bytes")]
#[test]
fn impls_bytes() {
fn assert_to_dname<T: ToDname + ?Sized>(_: &T) { }
assert_to_dname(
&Dname::from_octets(Bytes::from(b"\0".as_ref())).unwrap()
);
}
#[test]
fn root() {
assert_eq!(Dname::root_ref().as_slice(), b"\0");
assert_eq!(Dname::root_vec().as_slice(), b"\0");
assert_eq!(Dname::root_slice().as_slice(), b"\0");
}
#[cfg(feature = "bytes")]
#[test]
fn root_bytes() {
assert_eq!(Dname::root_bytes().as_slice(), b"\0");
}
#[test]
fn from_slice() {
assert_eq!(
Dname::from_slice(
b"\x03www\x07example\x03com\0"
).unwrap().as_slice(),
b"\x03www\x07example\x03com\0"
);
assert_eq!(
Dname::from_slice(b"\x03www\x07example\x03com"),
Err(DnameError::RelativeName)
);
assert_eq!(
Dname::from_slice(b"\x03www\x07exa"),
Err(DnameError::ShortInput)
);
let mut slice = [0u8; 65];
slice[0] = 63;
assert!(Dname::from_slice(&slice[..]).is_ok());
let mut slice = [0u8; 66];
slice[0] = 64;
assert!(Dname::from_slice(&slice[..]).is_err());
let mut buf = Vec::new();
for _ in 0..25 {
buf.extend_from_slice(b"\x09123456789");
}
assert_eq!(buf.len(), 250);
let mut tmp = buf.clone();
tmp.extend_from_slice(b"\x03123\0");
assert_eq!(Dname::from_slice(&tmp).map(|_| ()), Ok(()));
buf.extend_from_slice(b"\x041234\0");
assert!(Dname::from_slice(&buf).is_err());
assert!(Dname::from_slice(b"\x03com\0\x03www\0").is_err());
assert_eq!(Dname::from_slice(b"\xa2asdasds"),
Err(LabelTypeError::Undefined.into()));
assert_eq!(Dname::from_slice(b"\x62asdasds"),
Err(LabelTypeError::Extended(0x62).into()));
assert_eq!(Dname::from_slice(b"\xccasdasds"),
Err(DnameError::CompressedName.into()));
assert_eq!(Dname::from_slice(b""), Err(DnameError::ShortInput));
}
#[test]
fn into_relative() {
assert_eq!(
Dname::from_octets(
b"\x03www\0".as_ref()
).unwrap().into_relative().as_slice(),
b"\x03www"
);
}
#[test]
fn is_root() {
assert_eq!(Dname::from_slice(b"\0").unwrap().is_root(), true);
assert_eq!(Dname::from_slice(b"\x03www\0").unwrap().is_root(), false);
assert_eq!(Dname::root_ref().is_root(), true);
}
pub fn cmp_iter<I>(mut iter: I, labels: &[&[u8]])
where
I: Iterator,
I::Item: AsRef<[u8]>
{
let mut labels = labels.iter();
loop {
match (iter.next(), labels.next()) {
(Some(left), Some(right)) => assert_eq!(left.as_ref(), *right),
(None, None) => break,
(_, None) => panic!("extra items in iterator"),
(None, _) => panic!("missing items in iterator"),
}
}
}
#[test]
fn iter() {
cmp_iter(Dname::root_ref().iter(), &[b""]);
cmp_iter(
Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap().iter(),
&[b"www", b"example", b"com", b""]
);
}
pub fn cmp_iter_back<I>(mut iter: I, labels: &[&[u8]])
where
I: DoubleEndedIterator,
I::Item: AsRef<[u8]>
{
let mut labels = labels.iter();
loop {
match (iter.next_back(), labels.next()) {
(Some(left), Some(right)) => assert_eq!(left.as_ref(), *right),
(None, None) => break,
(_, None) => panic!("extra items in iterator"),
(None, _) => panic!("missing items in iterator"),
}
}
}
#[test]
fn iter_back() {
cmp_iter_back(Dname::root_ref().iter(), &[b""]);
cmp_iter_back(
Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap().iter(),
&[b"", b"com", b"example", b"www"]
);
}
#[test]
fn iter_suffixes() {
cmp_iter( Dname::root_ref().iter_suffixes(), &[b"\0"]);
cmp_iter(
Dname::from_octets(
b"\x03www\x07example\x03com\0".as_ref()
).unwrap().iter_suffixes(),
&[
b"\x03www\x07example\x03com\0", b"\x07example\x03com\0",
b"\x03com\0", b"\0"
]
);
}
#[test]
fn label_count() {
assert_eq!(Dname::root_ref().label_count(), 1);
assert_eq!(
Dname::from_slice(
b"\x03www\x07example\x03com\0"
).unwrap().label_count(),
4
);
}
#[test]
fn first() {
assert_eq!(Dname::root_ref().first().as_slice(), b"");
assert_eq!(
Dname::from_slice(
b"\x03www\x07example\x03com\0"
).unwrap().first().as_slice(),
b"www"
);
}
#[test]
fn last() {
assert_eq!(Dname::root_ref().last().as_slice(), b"");
assert_eq!(
Dname::from_slice(
b"\x03www\x07example\x03com\0"
).unwrap().last().as_slice(),
b""
);
}
#[test]
fn starts_with() {
let root = Dname::root_ref();
let wecr = Dname::from_octets(
b"\x03www\x07example\x03com\0".as_ref()
).unwrap();
assert!(root.starts_with(&root));
assert!(wecr.starts_with(&wecr));
assert!( root.starts_with(&RelativeDname::empty_ref()));
assert!( wecr.starts_with(&RelativeDname::empty_ref()));
let test = RelativeDname::from_slice(b"\x03www").unwrap();
assert!(!root.starts_with(&test));
assert!( wecr.starts_with(&test));
let test = RelativeDname::from_slice(b"\x03www\x07example").unwrap();
assert!(!root.starts_with(&test));
assert!( wecr.starts_with(&test));
let test = RelativeDname::from_slice(b"\x03www\x07example\x03com")
.unwrap();
assert!(!root.starts_with(&test));
assert!( wecr.starts_with(&test));
let test = RelativeDname::from_slice(b"\x07example\x03com").unwrap();
assert!(!root.starts_with(&test));
assert!(!wecr.starts_with(&test));
let test = RelativeDname::from_octets(
b"\x03www".as_ref()
).unwrap().chain(
RelativeDname::from_octets(b"\x07example".as_ref()).unwrap()
).unwrap();
assert!(!root.starts_with(&test));
assert!( wecr.starts_with(&test));
let test = test.chain(
RelativeDname::from_octets(b"\x03com".as_ref()).unwrap()
).unwrap();
assert!(!root.starts_with(&test));
assert!( wecr.starts_with(&test));
}
#[test]
fn ends_with() {
let root = Dname::root_ref();
let wecr = Dname::from_octets(
b"\x03www\x07example\x03com\0".as_ref()
).unwrap();
for name in wecr.iter_suffixes() {
if name.is_root() {
assert!(root.ends_with(&name));
}
else {
assert!(!root.ends_with(&name));
}
assert!(wecr.ends_with(&name));
}
}
#[test]
fn is_label_start() {
let wecr = Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap();
assert!( wecr.is_label_start(0));
assert!(!wecr.is_label_start(1));
assert!(!wecr.is_label_start(2));
assert!(!wecr.is_label_start(3));
assert!( wecr.is_label_start(4));
assert!(!wecr.is_label_start(5));
assert!(!wecr.is_label_start(6));
assert!(!wecr.is_label_start(7));
assert!(!wecr.is_label_start(8));
assert!(!wecr.is_label_start(9));
assert!(!wecr.is_label_start(10));
assert!(!wecr.is_label_start(11));
assert!( wecr.is_label_start(12));
assert!(!wecr.is_label_start(13));
assert!(!wecr.is_label_start(14));
assert!(!wecr.is_label_start(15));
assert!( wecr.is_label_start(16));
assert!(!wecr.is_label_start(17));
assert!(!wecr.is_label_start(18));
}
#[test]
fn slice() {
let wecr = Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap();
assert_eq!(wecr.slice(0, 4).as_slice(), b"\x03www");
assert_eq!(wecr.slice(0, 12).as_slice(), b"\x03www\x07example");
assert_eq!(wecr.slice(4, 12).as_slice(), b"\x07example");
assert_eq!(wecr.slice(4, 16).as_slice(), b"\x07example\x03com");
assert_panic!(wecr.slice(0,3));
assert_panic!(wecr.slice(1,4));
assert_panic!(wecr.slice(0,11));
assert_panic!(wecr.slice(1,12));
assert_panic!(wecr.slice(0,17));
assert_panic!(wecr.slice(4,17));
assert_panic!(wecr.slice(0,18));
}
#[test]
fn slice_from() {
let wecr = Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap();
assert_eq!(wecr.slice_from(0).as_slice(),
b"\x03www\x07example\x03com\0");
assert_eq!(wecr.slice_from(4).as_slice(), b"\x07example\x03com\0");
assert_eq!(wecr.slice_from(12).as_slice(), b"\x03com\0");
assert_eq!(wecr.slice_from(16).as_slice(), b"\0");
assert_panic!(wecr.slice_from(17));
assert_panic!(wecr.slice_from(18));
}
#[test]
fn slice_to() {
let wecr = Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap();
assert_eq!(wecr.slice_to(0).as_slice(), b"");
assert_eq!(wecr.slice_to(4).as_slice(), b"\x03www");
assert_eq!(wecr.slice_to(12).as_slice(), b"\x03www\x07example");
assert_eq!(wecr.slice_to(16).as_slice(), b"\x03www\x07example\x03com");
assert_panic!(wecr.slice_to(17));
assert_panic!(wecr.slice_to(18));
}
#[test]
fn range() {
let wecr = Dname::from_octets(
b"\x03www\x07example\x03com\0".as_ref()
).unwrap();
assert_eq!(wecr.range(0, 4).as_slice(), b"\x03www");
assert_eq!(wecr.range(0, 12).as_slice(), b"\x03www\x07example");
assert_eq!(wecr.range(4, 12).as_slice(), b"\x07example");
assert_eq!(wecr.range(4, 16).as_slice(), b"\x07example\x03com");
assert_panic!(wecr.range(0,3));
assert_panic!(wecr.range(1,4));
assert_panic!(wecr.range(0,11));
assert_panic!(wecr.range(1,12));
assert_panic!(wecr.range(0,17));
assert_panic!(wecr.range(4,17));
assert_panic!(wecr.range(0,18));
}
#[test]
fn range_from() {
let wecr = Dname::from_octets(
b"\x03www\x07example\x03com\0".as_ref()
).unwrap();
assert_eq!(
wecr.range_from(0).as_slice(),
b"\x03www\x07example\x03com\0"
);
assert_eq!(wecr.range_from(4).as_slice(), b"\x07example\x03com\0");
assert_eq!(wecr.range_from(12).as_slice(), b"\x03com\0");
assert_eq!(wecr.range_from(16).as_slice(), b"\0");
assert_panic!(wecr.range_from(17));
assert_panic!(wecr.range_from(18));
}
#[test]
fn range_to() {
let wecr = Dname::from_octets(
b"\x03www\x07example\x03com\0".as_ref()
).unwrap();
assert_eq!(wecr.range_to(0).as_slice(), b"");
assert_eq!(wecr.range_to(4).as_slice(), b"\x03www");
assert_eq!(wecr.range_to(12).as_slice(), b"\x03www\x07example");
assert_eq!(wecr.range_to(16).as_slice(), b"\x03www\x07example\x03com");
assert_panic!(wecr.range_to(17));
assert_panic!(wecr.range_to(18));
}
#[test]
fn split_at() {
let wecr = Dname::from_octets(
b"\x03www\x07example\x03com\0".as_ref()
).unwrap();
let (left, right) = wecr.clone().split_at(0);
assert_eq!(left.as_slice(), b"");
assert_eq!(right.as_slice(), b"\x03www\x07example\x03com\0");
let (left, right) = wecr.clone().split_at(4);
assert_eq!(left.as_slice(), b"\x03www");
assert_eq!(right.as_slice(), b"\x07example\x03com\0");
let (left, right) = wecr.clone().split_at(12);
assert_eq!(left.as_slice(), b"\x03www\x07example");
assert_eq!(right.as_slice(), b"\x03com\0");
let (left, right) = wecr.clone().split_at(16);
assert_eq!(left.as_slice(), b"\x03www\x07example\x03com");
assert_eq!(right.as_slice(), b"\0");
assert_panic!(wecr.clone().split_at(1));
assert_panic!(wecr.clone().split_at(14));
assert_panic!(wecr.clone().split_at(17));
assert_panic!(wecr.clone().split_at(18));
}
#[test]
fn split_to() {
let wecr = Dname::from_octets(
b"\x03www\x07example\x03com\0".as_ref()
).unwrap();
let mut tmp = wecr.clone();
assert_eq!(tmp.split_to(0).as_slice(), b"");
assert_eq!(tmp.as_slice(), b"\x03www\x07example\x03com\0");
let mut tmp = wecr.clone();
assert_eq!(tmp.split_to(4).as_slice(), b"\x03www");
assert_eq!(tmp.as_slice(), b"\x07example\x03com\0");
let mut tmp = wecr.clone();
assert_eq!(tmp.split_to(12).as_slice(), b"\x03www\x07example");
assert_eq!(tmp.as_slice(), b"\x03com\0");
let mut tmp = wecr.clone();
assert_eq!(tmp.split_to(16).as_slice(), b"\x03www\x07example\x03com");
assert_eq!(tmp.as_slice(), b"\0");
assert_panic!(wecr.clone().split_to(1));
assert_panic!(wecr.clone().split_to(14));
assert_panic!(wecr.clone().split_to(17));
assert_panic!(wecr.clone().split_to(18));
}
#[test]
fn truncate() {
let wecr = Dname::from_octets(
b"\x03www\x07example\x03com\0".as_ref()
).unwrap();
assert_eq!(wecr.clone().truncate(0).as_slice(),
b"");
assert_eq!(wecr.clone().truncate(4).as_slice(),
b"\x03www");
assert_eq!(wecr.clone().truncate(12).as_slice(),
b"\x03www\x07example");
assert_eq!(wecr.clone().truncate(16).as_slice(),
b"\x03www\x07example\x03com");
assert_panic!(wecr.clone().truncate(1));
assert_panic!(wecr.clone().truncate(14));
assert_panic!(wecr.clone().truncate(17));
assert_panic!(wecr.clone().truncate(18));
}
#[test]
fn split_first() {
let mut wecr = Dname::from_octets(
b"\x03www\x07example\x03com\0".as_ref()
).unwrap();
assert_eq!(wecr.split_first().unwrap().as_slice(), b"\x03www");
assert_eq!(wecr.as_slice(), b"\x07example\x03com\0");
assert_eq!(wecr.split_first().unwrap().as_slice(), b"\x07example");
assert_eq!(wecr.as_slice(), b"\x03com\0");
assert_eq!(wecr.split_first().unwrap().as_slice(), b"\x03com");
assert_eq!(wecr.as_slice(), b"\0");
assert!(wecr.split_first().is_none());
assert_eq!(wecr.as_slice(), b"\0");
assert!(wecr.split_first().is_none());
assert_eq!(wecr.as_slice(), b"\0");
}
#[test]
fn parent() {
let mut wecr = Dname::from_octets(
b"\x03www\x07example\x03com\0".as_ref()
).unwrap();
assert!(wecr.parent());
assert_eq!(wecr.as_slice(), b"\x07example\x03com\0");
assert!(wecr.parent());
assert_eq!(wecr.as_slice(), b"\x03com\0");
assert!(wecr.parent());
assert_eq!(wecr.as_slice(), b"\0");
assert!(!wecr.parent());
assert_eq!(wecr.as_slice(), b"\0");
assert!(!wecr.parent());
assert_eq!(wecr.as_slice(), b"\0");
}
#[test]
fn strip_suffix() {
let wecr = Dname::from_octets(
b"\x03www\x07example\x03com\0".as_ref()
).unwrap();
let ecr = Dname::from_octets(
b"\x07example\x03com\0".as_ref()
).unwrap();
let cr = Dname::from_octets(b"\x03com\0".as_ref()).unwrap();
let wenr = Dname::from_octets(
b"\x03www\x07example\x03net\0".as_ref()
).unwrap();
let enr = Dname::from_octets(
b"\x07example\x03net\0".as_ref()
).unwrap();
let nr = Dname::from_octets(b"\x03net\0".as_ref()).unwrap();
assert_eq!(wecr.clone().strip_suffix(&wecr).unwrap().as_slice(),
b"");
assert_eq!(wecr.clone().strip_suffix(&ecr).unwrap().as_slice(),
b"\x03www");
assert_eq!(wecr.clone().strip_suffix(&cr).unwrap().as_slice(),
b"\x03www\x07example");
assert_eq!(wecr.clone().strip_suffix(&Dname::root_slice())
.unwrap().as_slice(),
b"\x03www\x07example\x03com");
assert_eq!(wecr.clone().strip_suffix(&wenr).unwrap_err().as_slice(),
b"\x03www\x07example\x03com\0");
assert_eq!(wecr.clone().strip_suffix(&enr).unwrap_err().as_slice(),
b"\x03www\x07example\x03com\0");
assert_eq!(wecr.clone().strip_suffix(&nr).unwrap_err().as_slice(),
b"\x03www\x07example\x03com\0");
}
#[test]
fn parse() {
let mut p = Parser::from_static(b"\x03www\x07example\x03com\0af");
assert_eq!(
Dname::parse(&mut p).unwrap().as_slice(),
b"\x03www\x07example\x03com\0"
);
assert_eq!(p.peek_all(), b"af");
let mut p = Parser::from_static(b"\x03www\x07exam");
assert_eq!(Dname::parse(&mut p), Err(ParseError::ShortInput));
let mut p = Parser::from_static(b"\x03www\x07example");
assert_eq!(Dname::parse(&mut p), Err(ParseError::ShortInput));
let mut p = Parser::from_static(b"\x03com\x03www\x07example\xc0\0");
p.advance(4).unwrap();
assert_eq!(
Dname::parse(&mut p),
Err(DnameError::CompressedName.into())
);
let mut p = Parser::from_static(b"\x03www\x07example\xbffoo");
assert!(Dname::parse(&mut p).is_err());
let mut buf = Vec::new();
for _ in 0..50 {
buf.extend_from_slice(b"\x041234");
}
buf.extend_from_slice(b"\x03123\0");
assert_eq!(buf.len(), 255);
let mut p = Parser::from_ref(buf.as_slice());
assert!(Dname::parse(&mut p).is_ok());
assert_eq!(p.peek_all(), b"");
let mut buf = Vec::new();
for _ in 0..51 {
buf.extend_from_slice(b"\x041234");
}
buf.extend_from_slice(b"\0");
assert_eq!(buf.len(), 256);
let mut p = Parser::from_ref(buf.as_slice());
assert_eq!(
Dname::parse(&mut p),
Err(DnameError::LongName.into())
);
}
#[test]
fn compose_canonical() {
let mut buf = Vec::new();
Dname::from_slice(
b"\x03wWw\x07exaMPle\x03com\0"
).unwrap().compose_canonical(&mut buf).unwrap();
assert_eq!(buf.as_slice(), b"\x03www\x07example\x03com\0");
}
#[test]
fn from_str() {
use std::str::FromStr;
assert_eq!(
Dname::<Vec<u8>>::from_str("www.example.com").unwrap().as_slice(),
b"\x03www\x07example\x03com\0"
);
assert_eq!(
Dname::<Vec<u8>>::from_str("www.example.com.").unwrap().as_slice(),
b"\x03www\x07example\x03com\0"
);
}
#[test]
fn eq() {
assert_eq!(
Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap(),
Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap()
);
assert_eq!(
Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap(),
Dname::from_slice(b"\x03wWw\x07eXAMple\x03Com\0").unwrap()
);
assert_eq!(
Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap(),
&RelativeDname::from_octets(
b"\x03www".as_ref()
).unwrap().chain(
RelativeDname::from_octets(
b"\x07example\x03com".as_ref()
).unwrap()
).unwrap().chain(Dname::root_ref()).unwrap()
);
assert_eq!(
Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap(),
&RelativeDname::from_octets(
b"\x03wWw".as_ref()
).unwrap().chain(
RelativeDname::from_octets(
b"\x07eXAMple\x03coM".as_ref()
).unwrap()
).unwrap().chain(Dname::root_ref()).unwrap()
);
assert_ne!(
Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap(),
Dname::from_slice(b"\x03ww4\x07example\x03com\0").unwrap()
);
assert_ne!(
Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap(),
&RelativeDname::from_octets(
b"\x03www".as_ref()
).unwrap().chain(
RelativeDname::from_octets(
b"\x073xample\x03com".as_ref()
).unwrap()
).unwrap().chain(Dname::root_ref()).unwrap()
);
}
#[test]
fn cmp() {
use std::cmp::Ordering;
let names = [
Dname::from_slice(b"\x07example\0").unwrap(),
Dname::from_slice(b"\x01a\x07example\0").unwrap(),
Dname::from_slice(b"\x08yljkjljk\x01a\x07example\0").unwrap(),
Dname::from_slice(b"\x01Z\x01a\x07example\0").unwrap(),
Dname::from_slice(b"\x04zABC\x01a\x07example\0").unwrap(),
Dname::from_slice(b"\x01z\x07example\0").unwrap(),
Dname::from_slice(b"\x01\x01\x01z\x07example\0").unwrap(),
Dname::from_slice(b"\x01*\x01z\x07example\0").unwrap(),
Dname::from_slice(b"\x01\xc8\x01z\x07example\0").unwrap(),
];
for i in 0..names.len() {
for j in 0..names.len() {
let ord = if i < j { Ordering::Less }
else if i == j { Ordering::Equal }
else { Ordering::Greater };
assert_eq!(names[i].partial_cmp(&names[j]), Some(ord));
assert_eq!(names[i].cmp(&names[j]), ord);
}
}
let n1 = Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap();
let n2 = Dname::from_slice(b"\x03wWw\x07eXAMple\x03Com\0").unwrap();
assert_eq!(n1.partial_cmp(&n2), Some(Ordering::Equal));
assert_eq!(n1.cmp(&n2), Ordering::Equal);
}
#[test]
fn hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut s1 = DefaultHasher::new();
let mut s2 = DefaultHasher::new();
Dname::from_slice(b"\x03www\x07example\x03com\0").unwrap()
.hash(&mut s1);
Dname::from_slice(b"\x03wWw\x07eXAMple\x03Com\0").unwrap()
.hash(&mut s2);
assert_eq!(s1.finish(), s2.finish());
}
}