use core::fmt;
use domain_macros::*;
use super::{
build::{BuildInMessage, NameCompressor, TruncationError},
parse::{ParseMessageBytes, SplitMessageBytes},
wire::{AsBytes, ParseError, U16},
};
#[derive(
Clone, Debug, BuildBytes, ParseBytes, SplitBytes, PartialEq, Eq, Hash,
)]
pub struct Question<N> {
pub qname: N,
pub qtype: QType,
pub qclass: QClass,
}
impl<N> Question<N> {
pub fn new(qname: N, qtype: QType, qclass: QClass) -> Self {
Self {
qname,
qtype,
qclass,
}
}
}
impl<N> Question<N> {
pub fn transform<NN>(
self,
name_map: impl FnOnce(N) -> NN,
) -> Question<NN> {
Question {
qname: (name_map)(self.qname),
qtype: self.qtype,
qclass: self.qclass,
}
}
pub fn transform_ref<'a, NN>(
&'a self,
name_map: impl FnOnce(&'a N) -> NN,
) -> Question<NN> {
Question {
qname: (name_map)(&self.qname),
qtype: self.qtype,
qclass: self.qclass,
}
}
}
impl<'a, N> SplitMessageBytes<'a> for Question<N>
where
N: SplitMessageBytes<'a>,
{
fn split_message_bytes(
contents: &'a [u8],
start: usize,
) -> Result<(Self, usize), ParseError> {
let (qname, rest) = N::split_message_bytes(contents, start)?;
let (&qtype, rest) = <&QType>::split_message_bytes(contents, rest)?;
let (&qclass, rest) = <&QClass>::split_message_bytes(contents, rest)?;
Ok((Self::new(qname, qtype, qclass), rest))
}
}
impl<'a, N> ParseMessageBytes<'a> for Question<N>
where
N: SplitMessageBytes<'a>,
{
fn parse_message_bytes(
contents: &'a [u8],
start: usize,
) -> Result<Self, ParseError> {
match Self::split_message_bytes(contents, start) {
Ok((this, rest)) if rest == contents.len() => Ok(this),
_ => Err(ParseError),
}
}
}
impl<N> BuildInMessage for Question<N>
where
N: BuildInMessage,
{
fn build_in_message(
&self,
contents: &mut [u8],
mut start: usize,
compressor: &mut NameCompressor,
) -> Result<usize, TruncationError> {
start = self.qname.build_in_message(contents, start, compressor)?;
let end = start + 4;
let bytes = contents.get_mut(start..end).ok_or(TruncationError)?;
bytes[0..2].copy_from_slice(self.qtype.as_bytes());
bytes[2..4].copy_from_slice(self.qclass.as_bytes());
Ok(end)
}
}
#[derive(
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
AsBytes,
BuildBytes,
ParseBytes,
ParseBytesZC,
SplitBytes,
SplitBytesZC,
UnsizedCopy,
)]
#[repr(transparent)]
pub struct QType {
pub code: U16,
}
impl QType {
const fn new(value: u16) -> Self {
Self {
code: U16::new(value),
}
}
pub const A: Self = Self::new(1);
pub const NS: Self = Self::new(2);
pub const CNAME: Self = Self::new(5);
pub const SOA: Self = Self::new(6);
pub const PTR: Self = Self::new(12);
pub const HINFO: Self = Self::new(13);
pub const MX: Self = Self::new(15);
pub const TXT: Self = Self::new(16);
pub const RP: Self = Self::new(17);
pub const AAAA: Self = Self::new(28);
pub const DNAME: Self = Self::new(39);
pub const OPT: Self = Self::new(41);
pub const DS: Self = Self::new(43);
pub const RRSIG: Self = Self::new(46);
pub const NSEC: Self = Self::new(47);
pub const DNSKEY: Self = Self::new(48);
pub const NSEC3: Self = Self::new(50);
pub const NSEC3PARAM: Self = Self::new(51);
pub const CDS: Self = Self::new(59);
pub const CDNSKEY: Self = Self::new(60);
pub const ZONEMD: Self = Self::new(63);
pub const TSIG: Self = Self::new(250);
pub const IXFR: Self = Self::new(251);
pub const AXFR: Self = Self::new(252);
pub const ANY: Self = Self::new(255);
}
impl From<u16> for QType {
fn from(value: u16) -> Self {
Self {
code: U16::new(value),
}
}
}
impl From<QType> for u16 {
fn from(value: QType) -> Self {
value.code.get()
}
}
impl fmt::Debug for QType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self {
Self::A => "QType::A",
Self::NS => "QType::NS",
Self::CNAME => "QType::CNAME",
Self::SOA => "QType::SOA",
Self::PTR => "QType::PTR",
Self::HINFO => "QType::HINFO",
Self::MX => "QType::MX",
Self::TXT => "QType::TXT",
Self::RP => "QType::RP",
Self::AAAA => "QType::AAAA",
Self::DNAME => "QType::DNAME",
Self::OPT => "QType::OPT",
Self::DS => "QType::DS",
Self::RRSIG => "QType::RRSIG",
Self::NSEC => "QType::NSEC",
Self::DNSKEY => "QType::DNSKEY",
Self::NSEC3 => "QType::NSEC3",
Self::NSEC3PARAM => "QType::NSEC3PARAM",
Self::CDS => "QType::CDS",
Self::CDNSKEY => "QType::CDNSKEY",
Self::ZONEMD => "QType::ZONEMD",
Self::TSIG => "QType::TSIG",
Self::IXFR => "QType::IXFR",
Self::AXFR => "QType::AXFR",
Self::ANY => "QType::ANY",
_ => return write!(f, "QType({})", self.code),
})
}
}
#[derive(
Copy,
Clone,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
AsBytes,
BuildBytes,
ParseBytes,
ParseBytesZC,
SplitBytes,
SplitBytesZC,
UnsizedCopy,
)]
#[repr(transparent)]
pub struct QClass {
pub code: U16,
}
impl QClass {
const fn new(value: u16) -> Self {
Self {
code: U16::new(value),
}
}
pub const IN: Self = Self::new(1);
pub const CH: Self = Self::new(3);
}
impl fmt::Debug for QClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match *self {
Self::IN => "QClass::IN",
Self::CH => "QClass::CH",
_ => return write!(f, "QClass({})", self.code),
})
}
}
#[cfg(test)]
mod test {
use super::{QClass, QType, Question};
use crate::new::base::{
name::Name,
wire::{BuildBytes, ParseBytes, ParseError, SplitBytes},
};
#[test]
fn parse_build() {
let bytes = b"\x03com\x00\x00\x01\x00\x01\x2A";
let (question, rest) = <Question<&Name>>::split_bytes(bytes).unwrap();
assert_eq!(question.qname.as_bytes(), b"\x03com\x00");
assert_eq!(question.qtype, QType::A);
assert_eq!(question.qclass, QClass::IN);
assert_eq!(rest, b"\x2A");
assert_eq!(<Question<&Name>>::parse_bytes(bytes), Err(ParseError));
assert!(<Question<&Name>>::parse_bytes(&bytes[..9]).is_ok());
let mut buffer = [0u8; 9];
assert_eq!(
question.build_bytes(&mut buffer),
Ok(&mut [] as &mut [u8])
);
assert_eq!(buffer, &bytes[..9]);
}
}