use super::super::scan::BadSymbol;
use super::super::wire::{FormError, ParseError};
use super::builder::{
parse_escape, LabelFromStrError, LabelFromStrErrorEnum,
};
use core::str::FromStr;
use core::{borrow, cmp, fmt, hash, iter, mem, ops, slice};
use octseq::builder::OctetsBuilder;
#[repr(transparent)]
pub struct Label([u8]);
impl Label {
pub const MAX_LEN: usize = 63;
pub(super) const unsafe fn from_slice_unchecked(slice: &[u8]) -> &Self {
mem::transmute(slice)
}
pub(super) unsafe fn from_slice_mut_unchecked(
slice: &mut [u8],
) -> &mut Self {
mem::transmute(slice)
}
#[must_use]
pub fn root() -> &'static Self {
unsafe { Self::from_slice_unchecked(b"") }
}
#[must_use]
pub fn wildcard() -> &'static Self {
unsafe { Self::from_slice_unchecked(b"*") }
}
pub fn from_slice(slice: &[u8]) -> Result<&Self, LongLabelError> {
if slice.len() > Label::MAX_LEN {
Err(LongLabelError(()))
} else {
Ok(unsafe { Self::from_slice_unchecked(slice) })
}
}
pub fn from_slice_mut(
slice: &mut [u8],
) -> Result<&mut Self, LongLabelError> {
if slice.len() > Label::MAX_LEN {
Err(LongLabelError(()))
} else {
Ok(unsafe { Self::from_slice_mut_unchecked(slice) })
}
}
pub const fn split_from(
slice: &[u8],
) -> Result<(&Self, &[u8]), SplitLabelError> {
let head = match slice.first() {
Some(ch) => *ch,
None => return Err(SplitLabelError::ShortInput),
};
let end = match head {
0..=0x3F => (head as usize) + 1,
0x40..=0x7F => {
return Err(SplitLabelError::BadType(
LabelTypeError::Extended(head),
))
}
0xC0..=0xFF => {
if slice.len() < 2 {
return Err(SplitLabelError::ShortInput);
}
let res = slice[1] as u16;
let res = res | (((head as u16) & 0x3F) << 8);
return Err(SplitLabelError::Pointer(res));
}
_ => {
return Err(SplitLabelError::BadType(
LabelTypeError::Undefined,
))
}
};
if slice.len() < end {
return Err(SplitLabelError::ShortInput);
}
let (left, right) = slice.split_at(end);
let (_, label_data) = left.split_at(1);
Ok((unsafe { Self::from_slice_unchecked(label_data) }, right))
}
pub fn split_from_mut(
slice: &mut [u8],
) -> Result<(&mut Self, &mut [u8]), SplitLabelError> {
let head = match slice.first_mut() {
Some(ch) => *ch,
None => return Err(SplitLabelError::ShortInput),
};
let end = match head {
0..=0x3F => (head as usize) + 1,
0x40..=0x7F => {
return Err(SplitLabelError::BadType(
LabelTypeError::Extended(head),
))
}
0xC0..=0xFF => {
let res = match slice.get(1) {
Some(ch) => u16::from(*ch),
None => return Err(SplitLabelError::ShortInput),
};
let res = res | ((u16::from(head) & 0x3F) << 8);
return Err(SplitLabelError::Pointer(res));
}
_ => {
return Err(SplitLabelError::BadType(
LabelTypeError::Undefined,
))
}
};
if slice.len() < end {
return Err(SplitLabelError::ShortInput);
}
let (left, right) = slice.split_at_mut(end);
Ok((
unsafe { Self::from_slice_mut_unchecked(&mut left[1..]) },
right,
))
}
pub fn iter(&self) -> iter::Copied<slice::Iter<'_, u8>> {
self.as_slice().iter().copied()
}
#[must_use]
pub fn iter_slice(slice: &[u8], start: usize) -> SliceLabelsIter<'_> {
SliceLabelsIter { slice, start }
}
#[must_use]
pub const fn as_slice(&self) -> &[u8] {
&self.0
}
pub fn as_slice_mut(&mut self) -> &mut [u8] {
&mut self.0
}
pub fn make_canonical(&mut self) {
self.0.make_ascii_lowercase()
}
pub(super) fn make_slice_canonical(mut slice: &mut [u8]) {
while !slice.is_empty() {
let (head, tail) =
Label::split_from_mut(slice).expect("invalid name");
head.make_canonical();
slice = tail;
}
}
#[must_use]
pub fn to_canonical(&self) -> OwnedLabel {
let mut res = OwnedLabel::from_label(self);
res.make_canonical();
res
}
#[must_use]
pub fn composed_cmp(&self, other: &Self) -> cmp::Ordering {
match self.0.len().cmp(&other.0.len()) {
cmp::Ordering::Equal => {}
other => return other,
}
self.0.cmp(other.as_ref())
}
#[must_use]
pub fn lowercase_composed_cmp(&self, other: &Self) -> cmp::Ordering {
match self.0.len().cmp(&other.0.len()) {
cmp::Ordering::Equal => {}
other => return other,
}
self.cmp(other)
}
pub fn compose<Builder: OctetsBuilder + ?Sized>(
&self,
target: &mut Builder,
) -> Result<(), Builder::AppendError> {
target.append_slice(&[self.len() as u8])?;
target.append_slice(self.as_slice())
}
pub fn compose_canonical<Builder: OctetsBuilder + ?Sized>(
&self,
target: &mut Builder,
) -> Result<(), Builder::AppendError> {
target.append_slice(&[self.len() as u8])?;
for ch in self.into_iter() {
target.append_slice(&[ch.to_ascii_lowercase()])?;
}
Ok(())
}
}
impl Label {
#[must_use]
pub fn len(&self) -> usize {
self.as_slice().len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.as_slice().is_empty()
}
#[must_use]
pub const fn is_root(&self) -> bool {
self.is_empty()
}
#[must_use]
pub fn is_wildcard(&self) -> bool {
self.0.len() == 1 && self.0[0] == b'*'
}
#[must_use]
pub fn compose_len(&self) -> u16 {
u16::try_from(self.len()).expect("long label") + 1
}
}
impl AsRef<[u8]> for Label {
fn as_ref(&self) -> &[u8] {
self.as_slice()
}
}
impl AsMut<[u8]> for Label {
fn as_mut(&mut self) -> &mut [u8] {
self.as_slice_mut()
}
}
#[cfg(feature = "std")]
impl std::borrow::ToOwned for Label {
type Owned = OwnedLabel;
fn to_owned(&self) -> Self::Owned {
self.into()
}
}
impl<T: AsRef<[u8]> + ?Sized> PartialEq<T> for Label {
fn eq(&self, other: &T) -> bool {
self.as_slice().eq_ignore_ascii_case(other.as_ref())
}
}
impl Eq for Label {}
impl PartialOrd for Label {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Label {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.as_slice()
.iter()
.map(u8::to_ascii_lowercase)
.cmp(other.as_slice().iter().map(u8::to_ascii_lowercase))
}
}
impl hash::Hash for Label {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
(self.len() as u8).hash(state);
for c in self.iter() {
c.to_ascii_lowercase().hash(state)
}
}
}
impl<'a> IntoIterator for &'a Label {
type Item = u8;
type IntoIter = iter::Copied<slice::Iter<'a, u8>>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl fmt::Display for Label {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for ch in self.iter() {
if ch == b' ' || ch == b'.' || ch == b'\\' {
write!(f, "\\{}", ch as char)?;
} else if !(0x20..0x7F).contains(&ch) {
write!(f, "\\{:03}", ch)?;
} else {
write!(f, "{}", (ch as char))?;
}
}
Ok(())
}
}
impl fmt::Debug for Label {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Label(")?;
fmt::Display::fmt(self, f)?;
f.write_str(")")
}
}
#[derive(Clone, Copy)]
pub struct OwnedLabel([u8; 64]);
impl OwnedLabel {
#[must_use]
pub fn from_label(label: &Label) -> Self {
let mut res = [0; 64];
res[0] = label.len() as u8;
res[1..=label.len()].copy_from_slice(label.as_slice());
OwnedLabel(res)
}
pub fn from_chars(
mut chars: impl Iterator<Item = char>,
) -> Result<Self, LabelFromStrError> {
let mut res = [0u8; 64];
while let Some(ch) = chars.next() {
if res[0] as usize >= Label::MAX_LEN {
return Err(LabelFromStrErrorEnum::LongLabel.into());
}
let ch = match ch {
' '..='-' | '/'..='[' | ']'..='~' => ch as u8,
'\\' => parse_escape(&mut chars, res[0] > 0)?,
_ => return Err(BadSymbol::non_ascii().into()),
};
res[(res[0] as usize) + 1] = ch;
res[0] += 1;
}
Ok(OwnedLabel(res))
}
pub fn make_canonical(&mut self) {
self.0[1..].make_ascii_lowercase()
}
#[must_use]
pub fn as_label(&self) -> &Label {
unsafe {
Label::from_slice_unchecked(&self.0[1..=(self.0[0] as usize)])
}
}
pub fn as_label_mut(&mut self) -> &mut Label {
let len = self.0[0] as usize;
unsafe { Label::from_slice_mut_unchecked(&mut self.0[1..=len]) }
}
#[must_use]
pub fn as_wire_slice(&self) -> &[u8] {
let len = self.0[0] as usize;
&self.0[..=len]
}
}
impl<'a> From<&'a Label> for OwnedLabel {
fn from(label: &'a Label) -> Self {
Self::from_label(label)
}
}
impl FromStr for OwnedLabel {
type Err = LabelFromStrError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::from_chars(s.chars())
}
}
impl ops::Deref for OwnedLabel {
type Target = Label;
fn deref(&self) -> &Label {
self.as_label()
}
}
impl ops::DerefMut for OwnedLabel {
fn deref_mut(&mut self) -> &mut Label {
self.as_label_mut()
}
}
impl AsRef<Label> for OwnedLabel {
fn as_ref(&self) -> &Label {
self.as_label()
}
}
impl AsRef<[u8]> for OwnedLabel {
fn as_ref(&self) -> &[u8] {
self.as_label().as_slice()
}
}
impl AsMut<Label> for OwnedLabel {
fn as_mut(&mut self) -> &mut Label {
self.as_label_mut()
}
}
impl AsMut<[u8]> for OwnedLabel {
fn as_mut(&mut self) -> &mut [u8] {
self.as_label_mut().as_slice_mut()
}
}
impl borrow::Borrow<Label> for OwnedLabel {
fn borrow(&self) -> &Label {
self.as_label()
}
}
impl borrow::BorrowMut<Label> for OwnedLabel {
fn borrow_mut(&mut self) -> &mut Label {
self.as_label_mut()
}
}
impl<T: AsRef<Label>> PartialEq<T> for OwnedLabel {
fn eq(&self, other: &T) -> bool {
self.as_label().eq(other.as_ref())
}
}
impl Eq for OwnedLabel {}
impl<T: AsRef<Label>> PartialOrd<T> for OwnedLabel {
fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
self.as_label().partial_cmp(other.as_ref())
}
}
impl Ord for OwnedLabel {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.as_label().cmp(other.as_ref())
}
}
impl hash::Hash for OwnedLabel {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
self.as_label().hash(state)
}
}
impl fmt::Display for OwnedLabel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_label().fmt(f)
}
}
impl fmt::Debug for OwnedLabel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("OwnedLabel").field(&self.as_label()).finish()
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for OwnedLabel {
fn serialize<S: serde::Serializer>(
&self,
serializer: S,
) -> Result<S::Ok, S::Error> {
use octseq::serde::SerializeOctets;
if serializer.is_human_readable() {
serializer.serialize_newtype_struct(
"OwnedLabel",
&format_args!("{}", self),
)
} else {
serializer.serialize_newtype_struct(
"OwnedLabel",
&self.as_label().as_slice().as_serialized_octets(),
)
}
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for OwnedLabel {
fn deserialize<D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
use serde::de::Error;
struct InnerVisitor;
impl<'de> serde::de::Visitor<'de> for InnerVisitor {
type Value = OwnedLabel;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("an domain name label")
}
fn visit_str<E: Error>(self, v: &str) -> Result<Self::Value, E> {
OwnedLabel::from_str(v).map_err(E::custom)
}
fn visit_borrowed_bytes<E: serde::de::Error>(
self,
value: &'de [u8],
) -> Result<Self::Value, E> {
Label::from_slice(value)
.map(OwnedLabel::from_label)
.map_err(E::custom)
}
}
struct NewtypeVisitor;
impl<'de> serde::de::Visitor<'de> for NewtypeVisitor {
type Value = OwnedLabel;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("an domain name label")
}
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)
} else {
deserializer.deserialize_bytes(InnerVisitor)
}
}
}
deserializer.deserialize_newtype_struct("OwnedLabel", NewtypeVisitor)
}
}
pub struct SliceLabelsIter<'a> {
slice: &'a [u8],
start: usize,
}
impl<'a> Iterator for SliceLabelsIter<'a> {
type Item = &'a Label;
fn next(&mut self) -> Option<Self::Item> {
if self.start >= self.slice.len() {
return None;
}
loop {
match Label::split_from(&self.slice[self.start..]) {
Ok((label, _)) => {
if label.is_root() {
self.start = usize::MAX;
} else {
self.start += label.len() + 1;
}
return Some(label);
}
Err(SplitLabelError::Pointer(pos)) => {
let pos = pos as usize;
if pos > self.start {
self.start = usize::MAX;
return None;
}
self.start = pos;
continue;
}
Err(_) => {
self.start = usize::MAX;
return None;
}
}
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum LabelTypeError {
Undefined,
Extended(u8),
}
impl fmt::Display for LabelTypeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
LabelTypeError::Undefined => f.write_str("undefined label type"),
LabelTypeError::Extended(value) => {
write!(f, "unknown extended label 0x{:02x}", value)
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for LabelTypeError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct LongLabelError(());
impl fmt::Display for LongLabelError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("long label")
}
}
#[cfg(feature = "std")]
impl std::error::Error for LongLabelError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SplitLabelError {
Pointer(u16),
BadType(LabelTypeError),
ShortInput,
}
impl From<LabelTypeError> for SplitLabelError {
fn from(err: LabelTypeError) -> SplitLabelError {
SplitLabelError::BadType(err)
}
}
impl From<SplitLabelError> for ParseError {
fn from(err: SplitLabelError) -> ParseError {
match err {
SplitLabelError::Pointer(_) => {
ParseError::Form(FormError::new("compressed domain name"))
}
SplitLabelError::BadType(_) => {
ParseError::Form(FormError::new("invalid label type"))
}
SplitLabelError::ShortInput => ParseError::ShortInput,
}
}
}
impl fmt::Display for SplitLabelError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
SplitLabelError::Pointer(_) => {
f.write_str("compressed domain name")
}
SplitLabelError::BadType(ltype) => ltype.fmt(f),
SplitLabelError::ShortInput => ParseError::ShortInput.fmt(f),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for SplitLabelError {}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn from_slice() {
let x = [0u8; 10];
assert_eq!(Label::from_slice(&x[..]).unwrap().as_slice(), &x[..]);
let x = [0u8; 63];
assert_eq!(Label::from_slice(&x[..]).unwrap().as_slice(), &x[..]);
let x = [0u8; 64];
assert!(Label::from_slice(&x[..]).is_err());
}
#[test]
fn split_from() {
assert_eq!(
Label::split_from(b"\x03www\x07example\x03com\0").unwrap(),
(
Label::from_slice(b"www").unwrap(),
&b"\x07example\x03com\0"[..]
)
);
assert_eq!(
Label::split_from(b"\x03www").unwrap(),
(Label::from_slice(b"www").unwrap(), &b""[..])
);
assert_eq!(
Label::split_from(b"\0some").unwrap(),
(Label::from_slice(b"").unwrap(), &b"some"[..])
);
assert_eq!(
Label::split_from(b"\x03ww"),
Err(SplitLabelError::ShortInput)
);
assert_eq!(Label::split_from(b""), Err(SplitLabelError::ShortInput));
assert_eq!(
Label::split_from(b"\xc0\x05foo"),
Err(SplitLabelError::Pointer(5))
);
assert_eq!(
Label::split_from(b"\xb3foo"),
Err(LabelTypeError::Undefined.into())
);
assert_eq!(
Label::split_from(b"\x66foo"),
Err(LabelTypeError::Extended(0x66).into())
);
}
#[test]
#[cfg(feature = "std")]
fn compose() {
use octseq::builder::infallible;
use std::vec::Vec;
let mut buf = Vec::new();
infallible(Label::root().compose(&mut buf));
assert_eq!(buf, &b"\0"[..]);
let mut buf = Vec::new();
let label = Label::from_slice(b"123").unwrap();
infallible(label.compose(&mut buf));
assert_eq!(buf, &b"\x03123"[..]);
}
#[test]
fn eq() {
assert_eq!(
Label::from_slice(b"example").unwrap(),
Label::from_slice(b"eXAMple").unwrap()
);
assert_ne!(
Label::from_slice(b"example").unwrap(),
Label::from_slice(b"e4ample").unwrap()
);
}
#[test]
fn cmp() {
use core::cmp::Ordering;
let labels = [
Label::root(),
Label::from_slice(b"\x01").unwrap(),
Label::from_slice(b"*").unwrap(),
Label::from_slice(b"\xc8").unwrap(),
];
for i in 0..labels.len() {
for j in 0..labels.len() {
let ord = i.cmp(&j);
assert_eq!(labels[i].partial_cmp(labels[j]), Some(ord));
assert_eq!(labels[i].cmp(labels[j]), ord);
}
}
let l1 = Label::from_slice(b"example").unwrap();
let l2 = Label::from_slice(b"eXAMple").unwrap();
assert_eq!(l1.partial_cmp(l2), Some(Ordering::Equal));
assert_eq!(l1.cmp(l2), 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();
Label::from_slice(b"example").unwrap().hash(&mut s1);
Label::from_slice(b"eXAMple").unwrap().hash(&mut s2);
assert_eq!(s1.finish(), s2.finish());
}
#[cfg(feature = "serde")]
#[test]
fn owned_label_ser_de() {
use serde_test::{assert_tokens, Configure, Token};
let label =
OwnedLabel::from_label(Label::from_slice(b"fo.").unwrap());
assert_tokens(
&label.compact(),
&[
Token::NewtypeStruct { name: "OwnedLabel" },
Token::BorrowedBytes(b"fo."),
],
);
assert_tokens(
&label.readable(),
&[
Token::NewtypeStruct { name: "OwnedLabel" },
Token::Str("fo\\."),
],
);
}
#[test]
fn iter_slice() {
assert_eq!(None, Label::iter_slice(&[], 0).next());
assert_eq!(None, Label::iter_slice(&[], 1).next());
let buf = [
0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, 0x63, 0x6f,
0x6d, 0x00,
];
let mut it = Label::iter_slice(&buf, 0);
assert_eq!(Label::from_slice(b"example").ok(), it.next());
assert_eq!(Label::from_slice(b"com").ok(), it.next());
assert_eq!(Some(Label::root()), it.next());
assert_eq!(None, it.next());
let mut it = Label::iter_slice(&buf, b"example".len() + 1);
assert_eq!(
Label::from_slice(b"com").ok(),
it.next(),
"should jump to 2nd label"
);
let mut it = Label::iter_slice(&buf, buf.len() - 1);
assert_eq!(
Some(Label::root()),
it.next(),
"should jump to last/root label"
);
}
}