use super::super::scan::Scanner;
use super::super::wire::ParseError;
use super::builder::{DnameBuilder, FromStrError, PushError};
use super::chain::{Chain, LongChainError};
use super::dname::Dname;
use super::label::{Label, LabelTypeError, SplitLabelError};
use super::relative::{DnameIter, RelativeDname};
use super::traits::ToLabelIter;
#[cfg(feature = "bytes")]
use bytes::Bytes;
use core::{fmt, hash, str};
use octseq::builder::{
EmptyBuilder, FreezeBuilder, FromBuilder, IntoBuilder,
};
#[cfg(feature = "serde")]
use octseq::serde::{DeserializeOctets, SerializeOctets};
#[cfg(feature = "std")]
use std::vec::Vec;
#[derive(Clone)]
pub enum UncertainDname<Octets> {
Absolute(Dname<Octets>),
Relative(RelativeDname<Octets>),
}
impl<Octets> UncertainDname<Octets> {
pub fn absolute(name: Dname<Octets>) -> Self {
UncertainDname::Absolute(name)
}
pub fn relative(name: RelativeDname<Octets>) -> Self {
UncertainDname::Relative(name)
}
#[must_use]
pub fn root() -> Self
where
Octets: From<&'static [u8]>,
{
UncertainDname::Absolute(Dname::root())
}
#[must_use]
pub fn empty() -> Self
where
Octets: From<&'static [u8]>,
{
UncertainDname::Relative(RelativeDname::empty())
}
pub fn from_octets(octets: Octets) -> Result<Self, UncertainDnameError>
where
Octets: AsRef<[u8]>,
{
if Self::is_slice_absolute(octets.as_ref())? {
Ok(UncertainDname::Absolute(unsafe {
Dname::from_octets_unchecked(octets)
}))
} else {
Ok(UncertainDname::Relative(unsafe {
RelativeDname::from_octets_unchecked(octets)
}))
}
}
fn is_slice_absolute(
mut slice: &[u8],
) -> Result<bool, UncertainDnameError> {
if slice.len() > Dname::MAX_LEN {
return Err(UncertainDnameError::LongName);
}
loop {
let (label, tail) = Label::split_from(slice)?;
if label.is_root() {
if tail.is_empty() {
return Ok(true);
} else {
return Err(UncertainDnameError::TrailingData);
}
}
if tail.is_empty() {
return Ok(false);
}
slice = tail;
}
}
pub fn from_chars<C>(chars: C) -> Result<Self, FromStrError>
where
Octets: FromBuilder,
<Octets as FromBuilder>::Builder: FreezeBuilder<Octets = Octets>
+ EmptyBuilder
+ AsRef<[u8]>
+ AsMut<[u8]>,
C: IntoIterator<Item = char>,
{
let mut builder =
DnameBuilder::<<Octets as FromBuilder>::Builder>::new();
builder.append_chars(chars)?;
if builder.in_label() || builder.is_empty() {
Ok(builder.finish().into())
} else {
Ok(builder.into_dname()?.into())
}
}
pub fn scan<S: Scanner<Dname = Dname<Octets>>>(
scanner: &mut S,
) -> Result<Self, S::Error> {
scanner.scan_dname().map(UncertainDname::Absolute)
}
}
impl UncertainDname<&'static [u8]> {
#[must_use]
pub fn empty_ref() -> Self {
Self::empty()
}
#[must_use]
pub fn root_ref() -> Self {
Self::root()
}
}
#[cfg(feature = "std")]
impl UncertainDname<Vec<u8>> {
#[must_use]
pub fn empty_vec() -> Self {
Self::empty()
}
#[must_use]
pub fn root_vec() -> Self {
Self::root()
}
}
#[cfg(feature = "bytes")]
impl UncertainDname<Bytes> {
pub fn empty_bytes() -> Self {
Self::empty()
}
pub fn root_bytes() -> Self {
Self::root()
}
}
impl<Octets> UncertainDname<Octets> {
pub fn is_absolute(&self) -> bool {
match *self {
UncertainDname::Absolute(_) => true,
UncertainDname::Relative(_) => false,
}
}
pub fn is_relative(&self) -> bool {
!self.is_absolute()
}
pub fn as_absolute(&self) -> Option<&Dname<Octets>> {
match *self {
UncertainDname::Absolute(ref name) => Some(name),
_ => None,
}
}
pub fn as_relative(&self) -> Option<&RelativeDname<Octets>> {
match *self {
UncertainDname::Relative(ref name) => Some(name),
_ => None,
}
}
pub fn into_absolute(self) -> Result<Dname<Octets>, PushError>
where
Octets: AsRef<[u8]> + IntoBuilder,
<Octets as IntoBuilder>::Builder:
FreezeBuilder<Octets = Octets> + AsRef<[u8]> + AsMut<[u8]>,
{
match self {
UncertainDname::Absolute(name) => Ok(name),
UncertainDname::Relative(name) => name.into_absolute(),
}
}
pub fn try_into_absolute(self) -> Result<Dname<Octets>, Self> {
if let UncertainDname::Absolute(name) = self {
Ok(name)
} else {
Err(self)
}
}
pub fn try_into_relative(self) -> Result<RelativeDname<Octets>, Self> {
if let UncertainDname::Relative(name) = self {
Ok(name)
} else {
Err(self)
}
}
pub fn as_octets(&self) -> &Octets {
match *self {
UncertainDname::Absolute(ref name) => name.as_octets(),
UncertainDname::Relative(ref name) => name.as_octets(),
}
}
pub fn as_slice(&self) -> &[u8]
where
Octets: AsRef<[u8]>,
{
match *self {
UncertainDname::Absolute(ref name) => name.as_slice(),
UncertainDname::Relative(ref name) => name.as_slice(),
}
}
pub fn chain<S: ToLabelIter>(
self,
suffix: S,
) -> Result<Chain<Self, S>, LongChainError>
where
Octets: AsRef<[u8]>,
{
Chain::new_uncertain(self, suffix)
}
}
impl<Octets> From<Dname<Octets>> for UncertainDname<Octets> {
fn from(src: Dname<Octets>) -> Self {
UncertainDname::Absolute(src)
}
}
impl<Octets> From<RelativeDname<Octets>> for UncertainDname<Octets> {
fn from(src: RelativeDname<Octets>) -> Self {
UncertainDname::Relative(src)
}
}
impl<Octets> str::FromStr for UncertainDname<Octets>
where
Octets: FromBuilder,
<Octets as FromBuilder>::Builder: EmptyBuilder
+ FreezeBuilder<Octets = Octets>
+ AsRef<[u8]>
+ AsMut<[u8]>,
{
type Err = FromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_chars(s.chars())
}
}
impl<Octs> AsRef<Octs> for UncertainDname<Octs> {
fn as_ref(&self) -> &Octs {
match *self {
UncertainDname::Absolute(ref name) => name.as_ref(),
UncertainDname::Relative(ref name) => name.as_ref(),
}
}
}
impl<Octs: AsRef<[u8]>> AsRef<[u8]> for UncertainDname<Octs> {
fn as_ref(&self) -> &[u8] {
match *self {
UncertainDname::Absolute(ref name) => name.as_ref(),
UncertainDname::Relative(ref name) => name.as_ref(),
}
}
}
impl<Octets, Other> PartialEq<UncertainDname<Other>>
for UncertainDname<Octets>
where
Octets: AsRef<[u8]>,
Other: AsRef<[u8]>,
{
fn eq(&self, other: &UncertainDname<Other>) -> bool {
use UncertainDname::*;
match (self, other) {
(Absolute(l), Absolute(r)) => l.eq(r),
(Relative(l), Relative(r)) => l.eq(r),
_ => false,
}
}
}
impl<Octets: AsRef<[u8]>> Eq for UncertainDname<Octets> {}
impl<Octets: AsRef<[u8]>> hash::Hash for UncertainDname<Octets> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
for item in self.iter_labels() {
item.hash(state)
}
}
}
impl<Octs: AsRef<[u8]>> ToLabelIter for UncertainDname<Octs> {
type LabelIter<'a> = DnameIter<'a> where Octs: 'a;
fn iter_labels(&self) -> Self::LabelIter<'_> {
match *self {
UncertainDname::Absolute(ref name) => name.iter_labels(),
UncertainDname::Relative(ref name) => name.iter_labels(),
}
}
fn compose_len(&self) -> u16 {
match *self {
UncertainDname::Absolute(ref name) => name.compose_len(),
UncertainDname::Relative(ref name) => name.compose_len(),
}
}
}
impl<'a, Octets: AsRef<[u8]>> IntoIterator for &'a UncertainDname<Octets> {
type Item = &'a Label;
type IntoIter = DnameIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter_labels()
}
}
impl<Octets: AsRef<[u8]>> fmt::Display for UncertainDname<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
UncertainDname::Absolute(ref name) => {
write!(f, "{}.", name)
}
UncertainDname::Relative(ref name) => name.fmt(f),
}
}
}
impl<Octets: AsRef<[u8]>> fmt::Debug for UncertainDname<Octets> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
UncertainDname::Absolute(ref name) => {
write!(f, "UncertainDname::Absolute({})", name)
}
UncertainDname::Relative(ref name) => {
write!(f, "UncertainDname::Relative({})", name)
}
}
}
}
#[cfg(feature = "serde")]
impl<Octets> serde::Serialize for UncertainDname<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(
"UncertainDname",
&format_args!("{}", self),
)
} else {
serializer.serialize_newtype_struct(
"UncertainDname",
&self.as_octets().as_serialized_octets(),
)
}
}
}
#[cfg(feature = "serde")]
impl<'de, Octets> serde::Deserialize<'de> for UncertainDname<Octets>
where
Octets: FromBuilder + DeserializeOctets<'de>,
<Octets as FromBuilder>::Builder: EmptyBuilder
+ FreezeBuilder<Octets = Octets>
+ AsRef<[u8]>
+ 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: EmptyBuilder
+ FreezeBuilder<Octets = Octets>
+ AsRef<[u8]>
+ AsMut<[u8]>,
{
type Value = UncertainDname<Octets>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a domain name")
}
fn visit_str<E: serde::de::Error>(
self,
v: &str,
) -> Result<Self::Value, E> {
use core::str::FromStr;
UncertainDname::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| {
UncertainDname::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| {
UncertainDname::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: EmptyBuilder
+ FreezeBuilder<Octets = Octets>
+ AsRef<[u8]>
+ AsMut<[u8]>,
{
type Value = UncertainDname<Octets>;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("a 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(
"UncertainDname",
NewtypeVisitor(PhantomData),
)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum UncertainDnameError {
BadLabel(LabelTypeError),
CompressedName,
LongName,
TrailingData,
ShortInput,
}
impl From<LabelTypeError> for UncertainDnameError {
fn from(err: LabelTypeError) -> UncertainDnameError {
UncertainDnameError::BadLabel(err)
}
}
impl From<SplitLabelError> for UncertainDnameError {
fn from(err: SplitLabelError) -> UncertainDnameError {
match err {
SplitLabelError::Pointer(_) => {
UncertainDnameError::CompressedName
}
SplitLabelError::BadType(t) => UncertainDnameError::BadLabel(t),
SplitLabelError::ShortInput => UncertainDnameError::ShortInput,
}
}
}
impl fmt::Display for UncertainDnameError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
UncertainDnameError::BadLabel(ref err) => err.fmt(f),
UncertainDnameError::CompressedName => {
f.write_str("compressed domain name")
}
UncertainDnameError::LongName => f.write_str("long domain name"),
UncertainDnameError::TrailingData => f.write_str("trailing data"),
UncertainDnameError::ShortInput => ParseError::ShortInput.fmt(f),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for UncertainDnameError {}
#[cfg(test)]
#[cfg(feature = "std")]
mod test {
use super::*;
use std::str::FromStr;
use std::string::String;
#[test]
fn from_str() {
type U = UncertainDname<Vec<u8>>;
fn name(s: &str) -> U {
U::from_str(s).unwrap()
}
assert_eq!(
name("www.example.com").as_relative().unwrap().as_slice(),
b"\x03www\x07example\x03com"
);
assert_eq!(
name("www.example.com.").as_absolute().unwrap().as_slice(),
b"\x03www\x07example\x03com\0"
);
assert_eq!(
name(r"www\.example.com").as_slice(),
b"\x0bwww.example\x03com"
);
assert_eq!(
name(r"w\119w.example.com").as_slice(),
b"\x03www\x07example\x03com"
);
assert_eq!(
name(r"w\000w.example.com").as_slice(),
b"\x03w\0w\x07example\x03com"
);
assert_eq!(U::from_str(r"w\01"), Err(FromStrError::UnexpectedEnd));
assert_eq!(U::from_str(r"w\"), Err(FromStrError::UnexpectedEnd));
assert_eq!(
U::from_str(r"www..example.com"),
Err(FromStrError::EmptyLabel)
);
assert_eq!(
U::from_str(r"www.example.com.."),
Err(FromStrError::EmptyLabel)
);
assert_eq!(
U::from_str(r".www.example.com"),
Err(FromStrError::EmptyLabel)
);
assert_eq!(
U::from_str(r"www.\[322].example.com"),
Err(FromStrError::BinaryLabel)
);
assert_eq!(
U::from_str(r"www.\2example.com"),
Err(FromStrError::IllegalEscape)
);
assert_eq!(
U::from_str(r"www.\29example.com"),
Err(FromStrError::IllegalEscape)
);
assert_eq!(
U::from_str(r"www.\299example.com"),
Err(FromStrError::IllegalEscape)
);
assert_eq!(
U::from_str(r"www.\892example.com"),
Err(FromStrError::IllegalEscape)
);
assert_eq!(
U::from_str("www.e\0ample.com"),
Err(FromStrError::IllegalCharacter('\0'))
);
assert_eq!(
U::from_str("www.eüample.com"),
Err(FromStrError::IllegalCharacter('ü'))
);
let mut s = String::from("www.");
for _ in 0..Label::MAX_LEN {
s.push('x');
}
s.push_str(".com");
assert!(U::from_str(&s).is_ok());
let mut s = String::from("www.");
for _ in 0..64 {
s.push('x');
}
s.push_str(".com");
assert_eq!(U::from_str(&s), Err(FromStrError::LongLabel));
let mut s = String::new();
for _ in 0..50 {
s.push_str("four.");
}
let mut s1 = s.clone();
s1.push_str("com.");
assert_eq!(name(&s1).as_slice().len(), 255);
let mut s1 = s.clone();
s1.push_str("com");
assert_eq!(name(&s1).as_slice().len(), 254);
let mut s1 = s.clone();
s1.push_str("coma.");
assert_eq!(U::from_str(&s1), Err(FromStrError::LongName));
let mut s1 = s.clone();
s1.push_str("coma");
assert_eq!(U::from_str(&s1), Err(FromStrError::LongName));
}
#[cfg(feature = "serde")]
#[test]
fn ser_de() {
use serde_test::{assert_tokens, Configure, Token};
let abs_name =
UncertainDname::<Vec<u8>>::from_str("www.example.com.").unwrap();
assert!(abs_name.is_absolute());
assert_tokens(
&abs_name.clone().compact(),
&[
Token::NewtypeStruct {
name: "UncertainDname",
},
Token::ByteBuf(b"\x03www\x07example\x03com\0"),
],
);
assert_tokens(
&abs_name.readable(),
&[
Token::NewtypeStruct {
name: "UncertainDname",
},
Token::Str("www.example.com."),
],
);
let rel_name =
UncertainDname::<Vec<u8>>::from_str("www.example.com").unwrap();
assert!(rel_name.is_relative());
assert_tokens(
&rel_name.clone().compact(),
&[
Token::NewtypeStruct {
name: "UncertainDname",
},
Token::ByteBuf(b"\x03www\x07example\x03com"),
],
);
assert_tokens(
&rel_name.readable(),
&[
Token::NewtypeStruct {
name: "UncertainDname",
},
Token::Str("www.example.com"),
],
);
}
}