use crate::DnsError;
use core::convert::TryFrom;
use core::fmt::{Display, Formatter};
use fixed_buffer::{escape_ascii, FixedBuf};
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DnsString(Vec<u8>);
impl DnsString {
pub fn new(value: impl AsRef<[u8]>) -> Result<Self, String> {
let bytes = value.as_ref();
if bytes.len() > 255 {
return Err(format!(
"longer than 255 bytes, not a valid DNS string: '{}'",
escape_ascii(bytes)
));
}
Ok(Self(bytes.to_vec()))
}
pub fn read_one<const N: usize>(buf: &mut FixedBuf<N>) -> Result<DnsString, DnsError> {
let len = buf.try_read_byte().ok_or(DnsError::Truncated)?;
let bytes = buf
.try_read_bytes(len as usize)
.ok_or(DnsError::Truncated)?;
Ok(Self(bytes.to_vec()))
}
pub fn read_multiple<const N: usize>(
buf: &mut FixedBuf<N>,
) -> Result<Vec<DnsString>, DnsError> {
let mut strings = Vec::new();
while !buf.is_empty() {
let string = Self::read_one(buf)?;
strings.push(string);
}
Ok(strings)
}
pub fn write<const N: usize>(&self, out: &mut FixedBuf<N>) -> Result<(), DnsError> {
let len: u8 = self
.0
.len()
.try_into()
.map_err(|_e| DnsError::StringTooLong)?;
out.write_bytes(&[len])
.map_err(|_| DnsError::ResponseBufferFull)?;
out.write_bytes(self.0.as_slice())
.map_err(|_| DnsError::ResponseBufferFull)?;
Ok(())
}
pub fn as_bytes(&self) -> Result<FixedBuf<256>, DnsError> {
let mut buf: FixedBuf<256> = FixedBuf::new();
self.write(&mut buf)?;
Ok(buf)
}
#[must_use]
pub fn inner(&self) -> &[u8] {
self.0.as_slice()
}
}
impl Display for DnsString {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
write!(f, "{}", escape_ascii(self.0.as_slice()))
}
}
impl TryFrom<&'static str> for DnsString {
type Error = String;
fn try_from(value: &'static str) -> Result<Self, Self::Error> {
DnsString::new(value)
}
}
#[cfg(test)]
#[test]
fn test_str() {
DnsString::new("0").unwrap();
DnsString::new("a").unwrap();
DnsString::new("a".repeat(255).as_str()).unwrap();
assert_eq!(
"longer than 255 bytes, not a valid DNS string: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'",
DnsString::new("a".repeat(256).as_str()).unwrap_err().as_str()
);
}
#[cfg(test)]
#[test]
fn test_bytes() {
DnsString::new([]).unwrap();
DnsString::new([97u8]).unwrap();
DnsString::new([97u8; 255]).unwrap();
assert_eq!(
"longer than 255 bytes, not a valid DNS string: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'",
DnsString::new([97u8; 256]).unwrap_err().as_str()
);
for c in 0..=255u8 {
let array = [c];
DnsString::new(array).unwrap_or_else(|_| panic!("{}", escape_ascii(&array)));
}
}
#[cfg(test)]
#[test]
fn test_inner() {
assert_eq!(b"abc", DnsString::new("abc").unwrap().inner());
}
#[cfg(test)]
#[test]
fn test_display() {
assert_eq!(
"\\x00abc",
format!("{}", DnsString::new(b"\x00abc").unwrap())
);
}