#![deny(missing_docs)]
#![deny(clippy::missing_docs_in_private_items)]
use core::fmt;
use core::hash::{Hash, Hasher};
use crate::new::base::build::{BuildInMessage, NameCompressor};
use crate::new::base::parse::{ParseMessageBytes, SplitMessageBytes};
use crate::new::base::wire::{
AsBytes, BuildBytes, ParseBytes, ParseBytesZC, ParseError, SizePrefixed,
SplitBytes, SplitBytesZC, TruncationError, U16,
};
use crate::new::base::{RClass, RType, Record};
use crate::new::rdata::{Opt, RecordData};
use crate::utils::dst::UnsizedCopy;
mod cookie;
pub use cookie::{ClientCookie, Cookie, CookieError};
mod ext_err;
pub use ext_err::{ExtError, ExtErrorCode};
#[derive(Clone, Debug)]
pub struct EdnsRecord<D: ?Sized> {
pub max_udp_payload: U16,
pub ext_rcode: u8,
pub version: u8,
pub flags: EdnsFlags,
pub data: SizePrefixed<U16, D>,
}
impl<D> EdnsRecord<D> {
pub fn transform<ND>(
self,
data_map: impl FnOnce(D) -> ND,
) -> EdnsRecord<ND> {
EdnsRecord {
max_udp_payload: self.max_udp_payload,
ext_rcode: self.ext_rcode,
version: self.version,
flags: self.flags,
data: SizePrefixed::new((data_map)(self.data.into_data())),
}
}
}
impl<D: ?Sized> EdnsRecord<D> {
pub fn transform_ref<'a, ND>(
&'a self,
data_map: impl FnOnce(&'a D) -> ND,
) -> EdnsRecord<ND> {
EdnsRecord {
max_udp_payload: self.max_udp_payload,
ext_rcode: self.ext_rcode,
version: self.version,
flags: self.flags,
data: SizePrefixed::new((data_map)(&*self.data)),
}
}
}
impl<'a, D: ParseBytes<'a>> SplitMessageBytes<'a> for EdnsRecord<D> {
fn split_message_bytes(
contents: &'a [u8],
start: usize,
) -> Result<(Self, usize), ParseError> {
Self::split_bytes(contents.get(start..).ok_or(ParseError)?)
.map(|(this, rest)| (this, contents.len() - start - rest.len()))
}
}
impl<'a, D: ParseBytes<'a>> ParseMessageBytes<'a> for EdnsRecord<D> {
fn parse_message_bytes(
contents: &'a [u8],
start: usize,
) -> Result<Self, ParseError> {
Self::parse_bytes(contents.get(start..).ok_or(ParseError)?)
}
}
impl<D: ?Sized + BuildBytes> BuildInMessage for EdnsRecord<D> {
fn build_in_message(
&self,
contents: &mut [u8],
start: usize,
_compressor: &mut NameCompressor,
) -> Result<usize, TruncationError> {
let bytes = contents.get_mut(start..).ok_or(TruncationError)?;
let rest_len = self.build_bytes(bytes)?.len();
Ok(contents.len() - rest_len)
}
}
impl<LD: ?Sized, RD: ?Sized> PartialEq<EdnsRecord<RD>> for EdnsRecord<LD>
where
LD: PartialEq<RD>,
{
fn eq(&self, other: &EdnsRecord<RD>) -> bool {
self.max_udp_payload == other.max_udp_payload
&& self.ext_rcode == other.ext_rcode
&& self.version == other.version
&& self.flags == other.flags
&& *self.data == *other.data
}
}
impl<D: ?Sized + Eq> Eq for EdnsRecord<D> {}
impl<'a, D: ParseBytes<'a>> SplitBytes<'a> for EdnsRecord<D> {
fn split_bytes(bytes: &'a [u8]) -> Result<(Self, &'a [u8]), ParseError> {
let rest = bytes.strip_prefix(&[0, 0, 41]).ok_or(ParseError)?;
let (&max_udp_payload, rest) = <&U16>::split_bytes(rest)?;
let (&ext_rcode, rest) = <&u8>::split_bytes(rest)?;
let (&version, rest) = <&u8>::split_bytes(rest)?;
let (&flags, rest) = <&EdnsFlags>::split_bytes(rest)?;
let (data, rest) = <SizePrefixed<U16, D>>::split_bytes(rest)?;
Ok((
Self {
max_udp_payload,
ext_rcode,
version,
flags,
data,
},
rest,
))
}
}
impl<'a, D: ParseBytes<'a>> ParseBytes<'a> for EdnsRecord<D> {
fn parse_bytes(bytes: &'a [u8]) -> Result<Self, ParseError> {
match Self::split_bytes(bytes) {
Ok((this, &[])) => Ok(this),
_ => Err(ParseError),
}
}
}
impl<D: ?Sized + BuildBytes> BuildBytes for EdnsRecord<D> {
fn build_bytes<'b>(
&self,
mut bytes: &'b mut [u8],
) -> Result<&'b mut [u8], TruncationError> {
bytes = [0, 0, 41].as_slice().build_bytes(bytes)?;
bytes = self.max_udp_payload.build_bytes(bytes)?;
bytes = self.ext_rcode.build_bytes(bytes)?;
bytes = self.version.build_bytes(bytes)?;
bytes = self.flags.build_bytes(bytes)?;
bytes = self.data.build_bytes(bytes)?;
Ok(bytes)
}
fn built_bytes_size(&self) -> usize {
9 + self.data.built_bytes_size()
}
}
impl<'a, N: ParseBytes<'static>, DN> From<EdnsRecord<&'a Opt>>
for Record<N, RecordData<'a, DN>>
{
fn from(value: EdnsRecord<&'a Opt>) -> Self {
let root =
N::parse_bytes(&[0u8]).expect("The root name is always valid");
let [flags_hi, flags_lo] = value.flags.inner.get().to_be_bytes();
let ttl = u32::from_be_bytes([
value.ext_rcode,
value.version,
flags_hi,
flags_lo,
]);
Self {
rname: root,
rtype: RType::OPT,
rclass: RClass {
code: value.max_udp_payload,
},
ttl: ttl.into(),
rdata: RecordData::Opt(*value.data),
}
}
}
impl<'a, N: BuildBytes, DN> TryFrom<Record<N, RecordData<'a, DN>>>
for EdnsRecord<&'a Opt>
{
type Error = ParseError;
fn try_from(
value: Record<N, RecordData<'a, DN>>,
) -> Result<Self, Self::Error> {
let mut root = [0u8];
if value.rname.build_bytes(&mut root) != Ok(&mut []) {
return Err(ParseError);
} else if root != [0] {
return Err(ParseError);
}
if value.rtype != RType::OPT {
return Err(ParseError);
}
let data = match value.rdata {
RecordData::Opt(data) => data,
RecordData::Unknown(RType::OPT, data) => {
Opt::parse_bytes_by_ref(&data.octets)?
}
_ => return Err(ParseError),
};
let [ext_rcode, version, flags_hi, flags_lo] =
value.ttl.value.get().to_be_bytes();
let flags = u16::from_be_bytes([flags_hi, flags_lo]);
Ok(Self {
max_udp_payload: value.rclass.code,
ext_rcode,
version,
flags: EdnsFlags {
inner: U16::new(flags),
},
data: SizePrefixed::new(data),
})
}
}
#[derive(
Copy,
Clone,
Default,
PartialEq,
Eq,
Hash,
AsBytes,
BuildBytes,
ParseBytes,
ParseBytesZC,
SplitBytes,
SplitBytesZC,
UnsizedCopy,
)]
#[repr(transparent)]
pub struct EdnsFlags {
inner: U16,
}
impl EdnsFlags {
fn get_flag(&self, pos: u32) -> bool {
self.inner.get() & (1 << pos) != 0
}
fn set_flag(mut self, pos: u32, value: bool) -> Self {
self.inner &= !(1 << pos);
self.inner |= (value as u16) << pos;
self
}
pub fn bits(&self) -> u16 {
self.inner.get()
}
pub fn is_dnssec_ok(&self) -> bool {
self.get_flag(15)
}
pub fn set_dnssec_ok(self, value: bool) -> Self {
self.set_flag(15, value)
}
}
impl fmt::Debug for EdnsFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EdnsFlags")
.field("dnssec_ok (do)", &self.is_dnssec_ok())
.field("bits", &self.bits())
.finish()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum EdnsOption<'b> {
ClientCookie(ClientCookie),
Cookie(&'b Cookie),
ExtError(&'b ExtError),
Unknown(OptionCode, &'b UnknownOptionData),
}
impl EdnsOption<'_> {
pub fn code(&self) -> OptionCode {
match self {
Self::ClientCookie(_) => OptionCode::COOKIE,
Self::Cookie(_) => OptionCode::COOKIE,
Self::ExtError(_) => OptionCode::EXT_ERROR,
Self::Unknown(code, _) => *code,
}
}
#[cfg(feature = "bumpalo")]
pub fn clone_to_bump<'r>(
&self,
bump: &'r bumpalo::Bump,
) -> EdnsOption<'r> {
use crate::utils::dst::copy_to_bump;
match *self {
EdnsOption::ClientCookie(client_cookie) => {
EdnsOption::ClientCookie(client_cookie)
}
EdnsOption::Cookie(cookie) => {
EdnsOption::Cookie(copy_to_bump(cookie, bump))
}
EdnsOption::ExtError(ext_error) => {
EdnsOption::ExtError(copy_to_bump(ext_error, bump))
}
EdnsOption::Unknown(option_code, unknown_option) => {
EdnsOption::Unknown(
option_code,
copy_to_bump(unknown_option, bump),
)
}
}
}
}
impl<'b> ParseBytes<'b> for EdnsOption<'b> {
fn parse_bytes(bytes: &'b [u8]) -> Result<Self, ParseError> {
UnparsedEdnsOption::parse_bytes_by_ref(bytes)?.try_into()
}
}
impl<'b> SplitBytes<'b> for EdnsOption<'b> {
fn split_bytes(bytes: &'b [u8]) -> Result<(Self, &'b [u8]), ParseError> {
let (this, rest) = UnparsedEdnsOption::split_bytes_by_ref(bytes)?;
Ok((this.try_into()?, rest))
}
}
impl<'b> TryFrom<&'b UnparsedEdnsOption> for EdnsOption<'b> {
type Error = ParseError;
fn try_from(value: &'b UnparsedEdnsOption) -> Result<Self, Self::Error> {
let UnparsedEdnsOption { code, data } = value;
match *code {
OptionCode::COOKIE => match data.len() {
8 => ClientCookie::parse_bytes(data).map(Self::ClientCookie),
16..=40 => <&Cookie>::parse_bytes(data).map(Self::Cookie),
_ => Err(ParseError),
},
OptionCode::EXT_ERROR => {
<&ExtError>::parse_bytes(data).map(Self::ExtError)
}
_ => <&UnknownOptionData>::parse_bytes(data)
.map(|data| Self::Unknown(*code, data)),
}
}
}
impl BuildBytes for EdnsOption<'_> {
fn build_bytes<'b>(
&self,
mut bytes: &'b mut [u8],
) -> Result<&'b mut [u8], TruncationError> {
bytes = self.code().build_bytes(bytes)?;
let data = match self {
Self::ClientCookie(this) => this.as_bytes(),
Self::Cookie(this) => this.as_bytes(),
Self::ExtError(this) => this.as_bytes(),
Self::Unknown(_, this) => this.as_bytes(),
};
bytes = SizePrefixed::<U16, _>::new(data).build_bytes(bytes)?;
Ok(bytes)
}
fn built_bytes_size(&self) -> usize {
4 + match self {
Self::ClientCookie(this) => this.built_bytes_size(),
Self::Cookie(this) => this.built_bytes_size(),
Self::ExtError(this) => this.built_bytes_size(),
Self::Unknown(_, this) => this.built_bytes_size(),
}
}
}
#[derive(
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
AsBytes,
BuildBytes,
ParseBytes,
ParseBytesZC,
SplitBytes,
SplitBytesZC,
UnsizedCopy,
)]
#[repr(transparent)]
pub struct OptionCode {
pub code: U16,
}
impl OptionCode {
const fn new(code: u16) -> Self {
Self {
code: U16::new(code),
}
}
pub const COOKIE: Self = Self::new(10);
pub const EXT_ERROR: Self = Self::new(15);
}
impl fmt::Debug for OptionCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self {
Self::COOKIE => "OptionCode::COOKIE",
Self::EXT_ERROR => "OptionCode::EXT_ERROR",
_ => {
return f
.debug_tuple("OptionCode")
.field(&self.code.get())
.finish();
}
})
}
}
#[derive(
Debug, PartialEq, Eq, AsBytes, BuildBytes, ParseBytesZC, UnsizedCopy,
)]
#[repr(transparent)]
pub struct UnknownOptionData {
pub octets: [u8],
}
#[cfg(feature = "alloc")]
impl Clone for alloc::boxed::Box<UnknownOptionData> {
fn clone(&self) -> Self {
(*self).unsized_copy_into()
}
}
impl Hash for UnknownOptionData {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(&self.octets)
}
}
#[derive(
Debug, AsBytes, BuildBytes, ParseBytesZC, SplitBytesZC, UnsizedCopy,
)]
#[repr(C)]
pub struct UnparsedEdnsOption {
pub code: OptionCode,
pub data: SizePrefixed<U16, [u8]>,
}
#[cfg(feature = "alloc")]
impl Clone for alloc::boxed::Box<UnparsedEdnsOption> {
fn clone(&self) -> Self {
(*self).unsized_copy_into()
}
}
impl PartialEq for UnparsedEdnsOption {
fn eq(&self, other: &Self) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl Eq for UnparsedEdnsOption {}
impl Hash for UnparsedEdnsOption {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write(self.as_bytes())
}
}