use hickory_proto::{ op::{Message, MessageType, ResponseCode}, rr::{record_type::RecordType, Name, RData, Record}, }; use std::{net::IpAddr, str::FromStr}; pub fn build_dns_response(mut request: Message, domain: &str, ip: IpAddr, ttl: u32) -> Result { let record = match ip { IpAddr::V4(ip) => { let mut record = Record::with(Name::from_str(domain)?, RecordType::A, ttl); record.set_data(Some(RData::A(ip.into()))); record } IpAddr::V6(ip) => { let mut record = Record::with(Name::from_str(domain)?, RecordType::AAAA, ttl); record.set_data(Some(RData::AAAA(ip.into()))); record } }; // We must indicate that this message is a response. Otherwise, implementations may not // recognize it. request.set_message_type(MessageType::Response); request.add_answer(record); Ok(request) } pub fn remove_ipv6_entries(message: &mut Message) { message .answers_mut() .retain(|answer| !matches!(answer.data(), Some(RData::AAAA(_)))); } pub fn extract_ipaddr_from_dns_message(message: &Message) -> Result { if message.response_code() != ResponseCode::NoError { return Err(format!("{:?}", message.response_code())); } let mut cname = None; for answer in message.answers() { match answer.data().ok_or("DNS response not contains answer data")? { RData::A(addr) => { return Ok(IpAddr::V4((*addr).into())); } RData::AAAA(addr) => { return Ok(IpAddr::V6((*addr).into())); } RData::CNAME(name) => { cname = Some(name.to_utf8()); } _ => {} } } if let Some(cname) = cname { return Err(cname); } Err(format!("{:?}", message.answers())) } pub fn extract_domain_from_dns_message(message: &Message) -> Result { let query = message.queries().first().ok_or("DnsRequest no query body")?; let name = query.name().to_string(); Ok(name) } pub fn parse_data_to_dns_message(data: &[u8], used_by_tcp: bool) -> Result { if used_by_tcp { if data.len() < 2 { return Err("invalid dns data".into()); } let len = u16::from_be_bytes([data[0], data[1]]) as usize; let data = data.get(2..len + 2).ok_or("invalid dns data")?; return parse_data_to_dns_message(data, false); } let message = Message::from_vec(data).map_err(|e| e.to_string())?; Ok(message) }