use super::super::cmp::CanonicalOrd;
use super::super::octets::{
Compose, EmptyBuilder, FormError, FromBuilder, OctetsBuilder, OctetsExt,
OctetsFrom, OctetsRef, Parse, ParseError, Parser, ShortBuf,
};
#[cfg(feature = "serde")]
use super::super::octets::{DeserializeOctets, SerializeOctets};
use super::builder::{DnameBuilder, FromStrError};
use super::label::{Label, LabelTypeError, SplitLabelError};
use super::relative::{DnameIter, RelativeDname};
use super::traits::{ToDname, ToLabelIter};
#[cfg(feature = "master")]
use super::uncertain::UncertainDname;
#[cfg(feature = "master")]
use crate::master::scan::{
CharSource, Scan, ScanError, Scanner, SyntaxError,
};
#[cfg(feature = "bytes")]
use bytes::Bytes;
use core::str::FromStr;
use core::{cmp, fmt, hash, ops, str};
#[cfg(feature = "std")]
use std::vec::Vec;
#[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 + AsMut<[u8]>,
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, SrcOctets> OctetsFrom<Dname<SrcOctets>> for Dname<Octets>
where
Octets: OctetsFrom<SrcOctets>,
{
fn octets_from(source: Dname<SrcOctets>) -> Result<Self, ShortBuf> {
Octets::octets_from(source.0)
.map(|octets| unsafe { Self::from_octets_unchecked(octets) })
}
}
impl<Octets> FromStr for Dname<Octets>
where
Octets: FromBuilder,
<Octets as FromBuilder>::Builder: EmptyBuilder + AsMut<[u8]>,
{
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 + AsMut<[u8]>>(
&self,
target: &mut T,
) -> Result<(), ShortBuf> {
target.append_slice(self.0.as_ref())
}
fn compose_canonical<T: OctetsBuilder + AsMut<[u8]>>(
&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)
}
}
#[cfg(feature = "serde")]
impl<Octets> serde::Serialize for Dname<Octets>
where
Octets: AsRef<[u8]> + SerializeOctets,
{
fn serialize<S: serde::Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
if serializer.is_human_readable() {
serializer
.serialize_newtype_struct("Dname", &format_args!("{}", self))
} else {
serializer.serialize_newtype_struct(
"Dname",
&self.0.as_serialized_octets(),
)
}
}
}
#[cfg(feature = "serde")]
impl<'de, Octets> serde::Deserialize<'de> for Dname<Octets>
where
Octets: FromBuilder + DeserializeOctets<'de>,
<Octets as FromBuilder>::Builder: EmptyBuilder + AsMut<[u8]>,
{
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
use core::marker::PhantomData;
struct InnerVisitor<'de, T: DeserializeOctets<'de>>(T::Visitor);
impl<'de, Octets> serde::de::Visitor<'de> for InnerVisitor<'de, Octets>
where
Octets: FromBuilder + DeserializeOctets<'de>,
<Octets as FromBuilder>::Builder:
OctetsBuilder<Octets = Octets> + EmptyBuilder + AsMut<[u8]>,
{
type Value = Dname<Octets>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("an absolute domain name")
}
fn visit_str<E: serde::de::Error>(
self,
v: &str,
) -> Result<Self::Value, E> {
Dname::from_str(v).map_err(E::custom)
}
fn visit_borrowed_bytes<E: serde::de::Error>(
self,
value: &'de [u8],
) -> Result<Self::Value, E> {
self.0.visit_borrowed_bytes(value).and_then(|octets| {
Dname::from_octets(octets).map_err(E::custom)
})
}
#[cfg(feature = "std")]
fn visit_byte_buf<E: serde::de::Error>(
self,
value: std::vec::Vec<u8>,
) -> Result<Self::Value, E> {
self.0.visit_byte_buf(value).and_then(|octets| {
Dname::from_octets(octets).map_err(E::custom)
})
}
}
struct NewtypeVisitor<T>(PhantomData<T>);
impl<'de, Octets> serde::de::Visitor<'de> for NewtypeVisitor<Octets>
where
Octets: FromBuilder + DeserializeOctets<'de>,
<Octets as FromBuilder>::Builder:
OctetsBuilder<Octets = Octets> + EmptyBuilder + AsMut<[u8]>,
{
type Value = Dname<Octets>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("an absolute domain name")
}
fn visit_newtype_struct<D: serde::Deserializer<'de>>(
self,
deserializer: D,
) -> Result<Self::Value, D::Error> {
if deserializer.is_human_readable() {
deserializer
.deserialize_str(InnerVisitor(Octets::visitor()))
} else {
Octets::deserialize_with_visitor(
deserializer,
InnerVisitor(Octets::visitor()),
)
}
}
}
deserializer
.deserialize_newtype_struct("Dname", NewtypeVisitor(PhantomData))
}
}
#[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::*;
#[cfg(feature = "std")]
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());
#[cfg(feature = "std")]
{
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");
#[cfg(feature = "std")]
{
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]
#[cfg(feature = "std")]
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 = std::vec::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)
);
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!(Dname::from_slice(b"\0").unwrap().is_root());
assert!(!Dname::from_slice(b"\x03www\0").unwrap().is_root());
assert!(Dname::root_ref().is_root());
}
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]
#[cfg(feature = "std")]
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]
#[cfg(feature = "std")]
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]
#[cfg(feature = "std")]
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]
#[cfg(feature = "std")]
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]
#[cfg(feature = "std")]
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]
#[cfg(feature = "std")]
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]
#[cfg(feature = "std")]
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]
#[cfg(feature = "std")]
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]
#[cfg(feature = "std")]
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]
#[cfg(feature = "std")]
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]
#[cfg(feature = "std")]
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]
#[cfg(feature = "std")]
fn from_str() {
use core::str::FromStr;
use std::vec::Vec;
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 core::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 = i.cmp(&j);
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]
#[cfg(feature = "std")]
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());
}
#[cfg(all(feature = "serde", feature = "std"))]
#[test]
fn ser_de() {
use serde_test::{assert_tokens, Configure, Token};
let name = Dname::<Vec<u8>>::from_str("www.example.com.").unwrap();
assert_tokens(
&name.clone().compact(),
&[
Token::NewtypeStruct { name: "Dname" },
Token::ByteBuf(b"\x03www\x07example\x03com\0"),
],
);
assert_tokens(
&name.readable(),
&[
Token::NewtypeStruct { name: "Dname" },
Token::Str("www.example.com"),
],
);
}
}