use super::name::{CompressionMap, DnsName};
use super::rdata::RData;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RecordType {
A, NS, CNAME, SOA, PTR, HINFO, MX, TXT, AAAA, SRV, OPT, DS, RRSIG, NSEC, DNSKEY, NSEC3, SVCB, HTTPS, CAA, ANY, Unknown(u16),
}
impl From<u16> for RecordType {
fn from(val: u16) -> Self {
match val {
1 => Self::A,
2 => Self::NS,
5 => Self::CNAME,
6 => Self::SOA,
12 => Self::PTR,
13 => Self::HINFO,
15 => Self::MX,
16 => Self::TXT,
28 => Self::AAAA,
33 => Self::SRV,
41 => Self::OPT,
43 => Self::DS,
46 => Self::RRSIG,
47 => Self::NSEC,
48 => Self::DNSKEY,
50 => Self::NSEC3,
64 => Self::SVCB,
65 => Self::HTTPS,
255 => Self::ANY,
257 => Self::CAA,
v => Self::Unknown(v),
}
}
}
impl From<RecordType> for u16 {
fn from(val: RecordType) -> u16 {
match val {
RecordType::A => 1,
RecordType::NS => 2,
RecordType::CNAME => 5,
RecordType::SOA => 6,
RecordType::PTR => 12,
RecordType::HINFO => 13,
RecordType::MX => 15,
RecordType::TXT => 16,
RecordType::AAAA => 28,
RecordType::SRV => 33,
RecordType::OPT => 41,
RecordType::DS => 43,
RecordType::RRSIG => 46,
RecordType::NSEC => 47,
RecordType::DNSKEY => 48,
RecordType::NSEC3 => 50,
RecordType::SVCB => 64,
RecordType::HTTPS => 65,
RecordType::ANY => 255,
RecordType::CAA => 257,
RecordType::Unknown(v) => v,
}
}
}
impl std::fmt::Display for RecordType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::A => write!(f, "A"),
Self::NS => write!(f, "NS"),
Self::CNAME => write!(f, "CNAME"),
Self::SOA => write!(f, "SOA"),
Self::PTR => write!(f, "PTR"),
Self::HINFO => write!(f, "HINFO"),
Self::MX => write!(f, "MX"),
Self::TXT => write!(f, "TXT"),
Self::AAAA => write!(f, "AAAA"),
Self::SRV => write!(f, "SRV"),
Self::OPT => write!(f, "OPT"),
Self::DS => write!(f, "DS"),
Self::RRSIG => write!(f, "RRSIG"),
Self::NSEC => write!(f, "NSEC"),
Self::DNSKEY => write!(f, "DNSKEY"),
Self::NSEC3 => write!(f, "NSEC3"),
Self::SVCB => write!(f, "SVCB"),
Self::HTTPS => write!(f, "HTTPS"),
Self::ANY => write!(f, "ANY"),
Self::CAA => write!(f, "CAA"),
Self::Unknown(v) => write!(f, "TYPE{}", v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum RecordClass {
IN, CH, HS, ANY, Unknown(u16),
}
impl From<u16> for RecordClass {
fn from(val: u16) -> Self {
match val {
1 => Self::IN,
3 => Self::CH,
4 => Self::HS,
255 => Self::ANY,
v => Self::Unknown(v),
}
}
}
impl From<RecordClass> for u16 {
fn from(val: RecordClass) -> u16 {
match val {
RecordClass::IN => 1,
RecordClass::CH => 3,
RecordClass::HS => 4,
RecordClass::ANY => 255,
RecordClass::Unknown(v) => v,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResourceRecord {
pub name: DnsName,
pub rtype: RecordType,
pub rclass: RecordClass,
pub ttl: u32,
pub rdata: RData,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Question {
pub name: DnsName,
pub qtype: RecordType,
pub qclass: RecordClass,
}
impl Question {
pub fn decode(buf: &[u8], offset: usize) -> Result<(Self, usize), QuestionError> {
let (name, name_len) = DnsName::decode(buf, offset)
.map_err(QuestionError::Name)?;
let pos = offset + name_len;
if pos + 4 > buf.len() {
return Err(QuestionError::TooShort);
}
let qtype = RecordType::from(u16::from_be_bytes([buf[pos], buf[pos + 1]]));
let qclass = RecordClass::from(u16::from_be_bytes([buf[pos + 2], buf[pos + 3]]));
Ok((Self { name, qtype, qclass }, name_len + 4))
}
pub fn encode_compressed(&self, buf: &mut Vec<u8>, map: &mut CompressionMap) {
self.name.encode_compressed(buf, map);
buf.extend_from_slice(&u16::from(self.qtype).to_be_bytes());
buf.extend_from_slice(&u16::from(self.qclass).to_be_bytes());
}
}
#[derive(Debug, thiserror::Error)]
pub enum QuestionError {
#[error("name decode error: {0}")]
Name(#[from] super::name::NameError),
#[error("question section too short")]
TooShort,
}
#[derive(Debug, thiserror::Error)]
pub enum RecordError {
#[error("name decode error: {0}")]
Name(#[from] super::name::NameError),
#[error("record too short")]
TooShort,
#[error("rdata decode error: {0}")]
RData(#[from] super::rdata::RDataError),
}
impl ResourceRecord {
pub fn decode(buf: &[u8], offset: usize) -> Result<(Self, usize), RecordError> {
let (name, name_len) = DnsName::decode(buf, offset)?;
let pos = offset + name_len;
if pos + 10 > buf.len() {
return Err(RecordError::TooShort);
}
let rtype = RecordType::from(u16::from_be_bytes([buf[pos], buf[pos + 1]]));
let rclass = RecordClass::from(u16::from_be_bytes([buf[pos + 2], buf[pos + 3]]));
let ttl = u32::from_be_bytes([buf[pos + 4], buf[pos + 5], buf[pos + 6], buf[pos + 7]]);
let rdlength = u16::from_be_bytes([buf[pos + 8], buf[pos + 9]]) as usize;
let rdata_start = pos + 10;
let rdata_end = rdata_start + rdlength;
if rdata_end > buf.len() {
return Err(RecordError::TooShort);
}
let rdata = RData::decode(rtype, buf, rdata_start, rdlength)?;
Ok((
Self {
name,
rtype,
rclass,
ttl,
rdata,
},
name_len + 10 + rdlength,
))
}
pub fn encode_compressed(&self, buf: &mut Vec<u8>, map: &mut CompressionMap) {
self.name.encode_compressed(buf, map);
buf.extend_from_slice(&u16::from(self.rtype).to_be_bytes());
buf.extend_from_slice(&u16::from(self.rclass).to_be_bytes());
buf.extend_from_slice(&self.ttl.to_be_bytes());
let len_pos = buf.len();
buf.extend_from_slice(&[0, 0]);
let rdata_start = buf.len();
self.rdata.encode_compressed(buf, map);
let rdlen = (buf.len() - rdata_start).min(65535) as u16;
buf[len_pos..len_pos + 2].copy_from_slice(&rdlen.to_be_bytes());
}
}