use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
pub fn pad_query(query: &[u8], min_len: usize) -> Vec<u8> {
let mut padded = query.to_vec();
padded.push(0x80);
let current = padded.len().max(min_len);
let base_target = if current.is_multiple_of(64) {
current
} else {
(current / 64 + 1) * 64
};
let mut rb = [0u8; 4];
getrandom::fill(&mut rb).expect("getrandom failed");
let extra_blocks = (u32::from_ne_bytes(rb) % 3) as usize;
let target = base_target + (extra_blocks * 64);
padded.resize(target, 0x00);
padded
}
pub fn unpad_response(decrypted: &[u8]) -> Result<Vec<u8>, String> {
let mut end = decrypted.len();
while end > 0 && decrypted[end - 1] == 0x00 {
end -= 1;
}
if end == 0 || decrypted[end - 1] != 0x80 {
return Err("Invalid ISO/IEC 7816-4 padding".to_string());
}
Ok(decrypted[..end - 1].to_vec())
}
pub fn build_txt_record_query(domain: &str, txid: [u8; 2]) -> Vec<u8> {
build_query(domain, 0x0010, txid) }
pub fn build_a_record_query(domain: &str, txid: [u8; 2]) -> Vec<u8> {
build_query(domain, 0x0001, txid) }
pub fn build_aaaa_record_query(domain: &str, txid: [u8; 2]) -> Vec<u8> {
build_query(domain, 0x001c, txid) }
fn build_query(domain: &str, qtype: u16, txid: [u8; 2]) -> Vec<u8> {
let mut query: Vec<u8> = vec![
txid[0], txid[1], 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
for label in domain.split('.') {
if label.is_empty() {
continue;
}
let len = label.len().min(63);
query.push(len as u8);
query.extend_from_slice(&label.as_bytes()[..len]);
}
query.push(0x00); query.extend_from_slice(&qtype.to_be_bytes());
query.extend_from_slice(&[0x00, 0x01]); query
}
pub fn parse_txt_response(data: &[u8]) -> Vec<Vec<u8>> {
let mut results = Vec::new();
if data.len() < 12 {
return results;
}
let ancount = u16::from_be_bytes([data[6], data[7]]) as usize;
let mut offset = 12;
offset = skip_name(data, offset);
offset = offset.saturating_add(4);
for _ in 0..ancount {
offset = skip_name(data, offset);
if offset + 10 > data.len() {
break;
}
let rtype = u16::from_be_bytes([data[offset], data[offset + 1]]);
let rdlength = u16::from_be_bytes([data[offset + 8], data[offset + 9]]) as usize;
offset += 10;
if offset + rdlength > data.len() {
break;
}
if rtype == 16 {
let mut txt_data = Vec::new();
let rdata = &data[offset..offset + rdlength];
let mut i = 0;
while i < rdata.len() {
let chunk_len = rdata[i] as usize;
i += 1;
if i + chunk_len > rdata.len() {
break;
}
txt_data.extend_from_slice(&rdata[i..i + chunk_len]);
i += chunk_len;
}
if !txt_data.is_empty() {
results.push(txt_data);
}
}
offset += rdlength;
}
results
}
pub fn parse_dns_response(data: &[u8]) -> Vec<IpAddr> {
let mut ips = Vec::new();
if data.len() < 12 {
return ips;
}
let ancount = u16::from_be_bytes([data[6], data[7]]) as usize;
let mut offset = 12;
offset = skip_name(data, offset);
offset = offset.saturating_add(4);
for _ in 0..ancount {
offset = skip_name(data, offset);
if offset + 10 > data.len() {
break;
}
let atype = u16::from_be_bytes([data[offset], data[offset + 1]]);
let rdlength = u16::from_be_bytes([data[offset + 8], data[offset + 9]]) as usize;
offset += 10;
if atype == 1 && rdlength == 4 && offset + 4 <= data.len() {
ips.push(IpAddr::V4(Ipv4Addr::new(
data[offset],
data[offset + 1],
data[offset + 2],
data[offset + 3],
)));
} else if atype == 28 && rdlength == 16 && offset + 16 <= data.len() {
let mut addr = [0u8; 16];
addr.copy_from_slice(&data[offset..offset + 16]);
ips.push(IpAddr::V6(Ipv6Addr::from(addr)));
}
if offset
.checked_add(rdlength)
.is_none_or(|end| end > data.len())
{
break;
}
offset += rdlength;
}
ips
}
fn skip_name(data: &[u8], mut offset: usize) -> usize {
loop {
if offset >= data.len() {
return offset;
}
if (data[offset] & 0xC0) == 0xC0 {
return offset + 2;
}
let label_len = data[offset] as usize;
if label_len == 0 {
return offset + 1;
}
offset += 1 + label_len;
}
}