use core::cmp::Ordering;
use core::fmt;
use core::hash::{Hash, Hasher};
use core::iter::FusedIterator;
use crate::new::base::build::{
BuildInMessage, NameCompressor, TruncationError,
};
use crate::new::base::wire::{
AsBytes, BuildBytes, ParseBytesZC, ParseError, SplitBytesZC,
};
use crate::new::base::{
CanonicalRecordData, ParseRecordData, ParseRecordDataBytes, RType,
};
use crate::new::edns::{EdnsOption, UnparsedEdnsOption};
use crate::utils::dst::UnsizedCopy;
#[derive(AsBytes, BuildBytes, UnsizedCopy)]
#[repr(transparent)]
pub struct Opt {
contents: [u8],
}
impl Opt {
pub const EMPTY: &'static Self =
unsafe { core::mem::transmute(&[] as &[u8]) };
}
impl Opt {
pub const unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
unsafe { core::mem::transmute::<&[u8], &Opt>(bytes) }
}
}
impl Opt {
pub fn options(&self) -> EdnsOptionsIter<'_> {
EdnsOptionsIter::new(&self.contents)
}
}
#[cfg(feature = "alloc")]
impl Clone for alloc::boxed::Box<Opt> {
fn clone(&self) -> Self {
(*self).unsized_copy_into()
}
}
impl PartialEq for Opt {
fn eq(&self, other: &Self) -> bool {
self.options().eq(other.options())
}
}
impl PartialEq<[EdnsOption<'_>]> for Opt {
fn eq(&self, other: &[EdnsOption<'_>]) -> bool {
self.options().eq(other.iter().map(|opt| Ok(opt.clone())))
}
}
impl<const N: usize> PartialEq<[EdnsOption<'_>; N]> for Opt {
fn eq(&self, other: &[EdnsOption<'_>; N]) -> bool {
*self == *other.as_slice()
}
}
impl PartialEq<[EdnsOption<'_>]> for &Opt {
fn eq(&self, other: &[EdnsOption<'_>]) -> bool {
**self == *other
}
}
impl<const N: usize> PartialEq<[EdnsOption<'_>; N]> for &Opt {
fn eq(&self, other: &[EdnsOption<'_>; N]) -> bool {
**self == *other.as_slice()
}
}
impl Eq for Opt {}
impl Hash for Opt {
fn hash<H: Hasher>(&self, state: &mut H) {
for option in self.options() {
option.hash(state);
}
}
}
impl CanonicalRecordData for Opt {
fn cmp_canonical(&self, other: &Self) -> Ordering {
self.contents.cmp(&other.contents)
}
}
impl BuildInMessage for Opt {
fn build_in_message(
&self,
contents: &mut [u8],
start: usize,
_compressor: &mut NameCompressor,
) -> Result<usize, TruncationError> {
let end = start + self.contents.len();
contents
.get_mut(start..end)
.ok_or(TruncationError)?
.copy_from_slice(&self.contents);
Ok(end)
}
}
unsafe impl ParseBytesZC for Opt {
fn parse_bytes_by_ref(bytes: &[u8]) -> Result<&Self, ParseError> {
if bytes.len() > 65535 {
return Err(ParseError);
}
let mut offset = 0usize;
while offset < bytes.len() {
offset += 2;
let size = bytes.get(offset..offset + 2).ok_or(ParseError)?;
let size: usize = u16::from_be_bytes([size[0], size[1]]).into();
offset += 2;
let _ = bytes.get(offset..offset + size).ok_or(ParseError)?;
offset += size;
}
Ok(unsafe { core::mem::transmute::<&[u8], &Opt>(bytes) })
}
}
impl fmt::Debug for Opt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Opt").field(&self.options()).finish()
}
}
impl<'a> ParseRecordData<'a> for &'a Opt {}
impl<'a> ParseRecordDataBytes<'a> for &'a Opt {
fn parse_record_data_bytes(
bytes: &'a [u8],
rtype: RType,
) -> Result<Self, ParseError> {
match rtype {
RType::OPT => Opt::parse_bytes_by_ref(bytes),
_ => Err(ParseError),
}
}
}
#[derive(Clone)]
pub struct EdnsOptionsIter<'a> {
options: &'a [u8],
}
impl<'a> EdnsOptionsIter<'a> {
pub const fn new(options: &'a [u8]) -> Self {
Self { options }
}
}
impl<'a> EdnsOptionsIter<'a> {
pub const fn remaining(&self) -> &'a [u8] {
self.options
}
}
impl fmt::Debug for EdnsOptionsIter<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut entries = f.debug_set();
for option in self.clone() {
match option {
Ok(option) => entries.entry(&option),
Err(_err) => entries.entry(&format_args!("<error>")),
};
}
entries.finish()
}
}
impl<'a> Iterator for EdnsOptionsIter<'a> {
type Item = Result<EdnsOption<'a>, &'a UnparsedEdnsOption>;
fn next(&mut self) -> Option<Self::Item> {
if !self.options.is_empty() {
let (option, rest) = UnparsedEdnsOption::split_bytes_by_ref(
self.options,
)
.expect("An 'Opt' always contains valid 'UnparsedEdnsOption's");
self.options = rest;
Some(EdnsOption::try_from(option).map_err(|_| option))
} else {
None
}
}
}
impl FusedIterator for EdnsOptionsIter<'_> {}