use crate::ast::*;
use crate::builders::WireContext;
use alloc::vec::Vec;
use core::net::IpAddr;
pub fn encode_dns_name(domain: &str) -> Vec<u8> {
let mut encoded = Vec::new();
let trimmed = domain.trim_matches('.');
if trimmed.is_empty() {
encoded.push(0);
return encoded;
}
for label in trimmed.split('.') {
let len = label.len().min(63) as u8;
encoded.push(len);
encoded.extend_from_slice(label.as_bytes());
}
encoded.push(0);
encoded
}
pub fn parse_dns_qtype(qtype: &str) -> u16 {
match qtype.to_ascii_uppercase().as_str() {
"A" => 1,
"NS" => 2,
"CNAME" => 5,
"SOA" => 6,
"PTR" => 12,
"MX" => 15,
"TXT" => 16,
"AAAA" => 28,
"SRV" => 33,
"AXFR" => 252,
"ANY" => 255,
_ => qtype.parse::<u16>().unwrap_or(1),
}
}
#[doc(alias = "DNS")]
#[doc(alias = "RFC1035")]
#[doc(alias = "RFC3596")]
#[doc(alias = "QNAME")]
#[doc(alias = "QTYPE")]
#[doc(alias = "RDATA")]
pub fn build_dns_payload(
_ctx: &WireContext,
spec: &DnsSpec,
payload_bytes: &[u8],
is_tcp: bool,
) -> Vec<u8> {
let txid = spec.txid.unwrap_or(0x1234);
let is_resp = spec.is_response && !spec.is_query;
let mut flags: u16 = if is_resp {
0x8180 } else {
0x0100 };
if let Some(rcode) = spec.rcode {
flags = (flags & 0xFFF0) | (rcode as u16 & 0x000F);
}
let qdcount = 1u16;
let has_answer = is_resp || spec.rdata.is_some();
let ancount = if has_answer { 1u16 } else { 0u16 };
let nscount = 0u16;
let arcount = 0u16;
let mut dns_msg = Vec::new();
dns_msg.extend_from_slice(&txid.to_be_bytes());
dns_msg.extend_from_slice(&flags.to_be_bytes());
dns_msg.extend_from_slice(&qdcount.to_be_bytes());
dns_msg.extend_from_slice(&ancount.to_be_bytes());
dns_msg.extend_from_slice(&nscount.to_be_bytes());
dns_msg.extend_from_slice(&arcount.to_be_bytes());
let qname = spec.qname.unwrap_or("example.com");
let encoded_qname = encode_dns_name(qname);
dns_msg.extend_from_slice(&encoded_qname);
let qtype = spec.qtype.map(parse_dns_qtype).unwrap_or(1);
dns_msg.extend_from_slice(&qtype.to_be_bytes());
dns_msg.extend_from_slice(&1u16.to_be_bytes());
if has_answer {
dns_msg.extend_from_slice(&0xC00Cu16.to_be_bytes());
dns_msg.extend_from_slice(&qtype.to_be_bytes());
dns_msg.extend_from_slice(&1u16.to_be_bytes()); let ttl = spec.ans_ttl.unwrap_or(300);
dns_msg.extend_from_slice(&ttl.to_be_bytes());
let rdata_bytes = if let Some(rdata_str) = spec.rdata {
if qtype == 1 {
if let Ok(IpAddr::V4(v4)) = rdata_str.parse::<IpAddr>() {
v4.octets().to_vec()
} else {
[93, 184, 216, 34].to_vec()
}
} else if qtype == 28 {
if let Ok(IpAddr::V6(v6)) = rdata_str.parse::<IpAddr>() {
v6.octets().to_vec()
} else {
alloc::vec![0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
}
} else if qtype == 5 || qtype == 2 || qtype == 12 {
encode_dns_name(rdata_str)
} else {
rdata_str.as_bytes().to_vec()
}
} else if !payload_bytes.is_empty() {
payload_bytes.to_vec()
} else {
[93, 184, 216, 34].to_vec() };
dns_msg.extend_from_slice(&(rdata_bytes.len() as u16).to_be_bytes());
dns_msg.extend_from_slice(&rdata_bytes);
} else if !payload_bytes.is_empty() {
dns_msg.extend_from_slice(payload_bytes);
}
if is_tcp {
let mut tcp_frame = Vec::with_capacity(dns_msg.len() + 2);
tcp_frame.extend_from_slice(&(dns_msg.len() as u16).to_be_bytes());
tcp_frame.extend_from_slice(&dns_msg);
tcp_frame
} else {
dns_msg
}
}