use crate::enums::{PacketType, Rcode};
use crate::resourcerecord::{DNSCharString, InternalResourceRecord};
use crate::{Header, Question};
use crate::{ResourceRecord, UDP_BUFFER_SIZE};
use log::error;
use packed_struct::prelude::*;
#[derive(Debug, Clone)]
pub struct Reply {
pub header: Header,
pub question: Option<Question>,
pub answers: Vec<InternalResourceRecord>,
pub authorities: Vec<ResourceRecord>,
pub additional: Vec<ResourceRecord>,
}
impl Reply {
pub async fn as_bytes(&self) -> Result<Vec<u8>, String> {
let mut retval: Vec<u8> = vec![];
let mut final_reply = self.clone();
final_reply.header.ancount = final_reply.answers.len() as u16;
let reply_header = match final_reply.header.pack() {
Ok(value) => value,
Err(err) => return Err(format!("Failed to pack reply header bytes: {:?}", err)),
};
retval.extend(reply_header);
if let Some(question) = &final_reply.question {
retval.extend(question.to_bytes());
for answer in &final_reply.answers {
let ttl: &u32 = match answer {
InternalResourceRecord::A { ttl, .. } => ttl,
InternalResourceRecord::AAAA { ttl, .. } => ttl,
InternalResourceRecord::AXFR { ttl, .. } => ttl,
InternalResourceRecord::CAA { ttl, .. } => ttl,
InternalResourceRecord::CNAME { ttl, .. } => ttl,
InternalResourceRecord::HINFO { ttl, .. } => ttl,
InternalResourceRecord::InvalidType => &1u32,
InternalResourceRecord::LOC { ttl, .. } => ttl,
InternalResourceRecord::MX { ttl, .. } => ttl,
InternalResourceRecord::NAPTR { ttl, .. } => ttl,
InternalResourceRecord::NS { ttl, .. } => ttl,
InternalResourceRecord::PTR { ttl, .. } => ttl,
InternalResourceRecord::SOA { minimum, .. } => minimum,
InternalResourceRecord::TXT { ttl, .. } => ttl,
InternalResourceRecord::URI { ttl, .. } => ttl,
};
let answer_record = ResourceRecord {
name: question.qname.clone(),
record_type: answer.to_owned().into(),
class: question.qclass,
ttl: *ttl,
rdata: answer.as_bytes(&question.qname),
};
let reply_bytes: Vec<u8> = answer_record.into();
retval.extend(reply_bytes);
}
}
for authority in &final_reply.authorities {
error!(
"Should be handling authority rr's in reply: {:?}",
authority
);
}
for additional in &final_reply.additional {
error!(
"Should be handling additional rr's in reply: {:?}",
additional
);
}
Ok(retval)
}
pub async fn as_bytes_udp(&self) -> Result<Vec<u8>, String> {
let mut result = self.as_bytes().await?;
if result.len() > UDP_BUFFER_SIZE {
result.truncate(UDP_BUFFER_SIZE);
};
Ok(result)
}
pub async fn check_set_truncated(&self) -> Reply {
if let Ok(ret_bytes) = self.as_bytes().await {
if ret_bytes.len() > UDP_BUFFER_SIZE {
let mut header = self.header.clone();
header.truncated = true;
return Self {
header,
..self.clone()
};
}
}
self.clone()
}
}
pub fn reply_builder(id: u16, rcode: Rcode) -> Result<Reply, String> {
let header = Header {
id,
qr: PacketType::Answer,
rcode,
..Default::default()
};
Ok(Reply {
header,
question: None,
answers: vec![],
authorities: vec![],
additional: vec![],
})
}
pub fn reply_nxdomain(id: u16) -> Result<Reply, String> {
reply_builder(id, Rcode::NameError)
}
pub fn reply_any(id: u16, question: Question) -> Result<Reply, String> {
Ok(Reply {
header: Header {
id,
qr: PacketType::Answer,
rcode: Rcode::NoError,
authoritative: true,
qdcount: 1,
ancount: 1,
..Header::default()
},
question: Some(question.clone()),
answers: vec![InternalResourceRecord::HINFO {
cpu: Some(DNSCharString::from("RFC8482")),
os: Some(DNSCharString::from("")),
ttl: 3789,
rclass: question.qclass,
}],
authorities: vec![],
additional: vec![],
})
}