use anyhow::{ensure, Result};
use hickory_proto as proto;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
pub struct DnsResponse {
#[serde(rename = "Status")]
pub status: u32,
#[serde(rename = "TC")]
pub tc: bool,
#[serde(rename = "RD")]
pub rd: bool,
#[serde(rename = "RA")]
pub ra: bool,
#[serde(rename = "AD")]
pub ad: bool,
#[serde(rename = "CD")]
pub cd: bool,
#[serde(rename = "Question")]
pub question: Vec<DohQuestionJson>,
#[serde(rename = "Answer")]
#[serde(skip_serializing_if = "Vec::is_empty")]
pub answer: Vec<DohRecordJson>,
#[serde(rename = "Comment")]
pub comment: Option<String>,
pub edns_client_subnet: Option<String>,
}
impl DnsResponse {
pub fn from_message(message: proto::op::Message) -> Result<Self> {
ensure!(
message.message_type() == proto::op::MessageType::Response,
"Expected message type to be response"
);
ensure!(
message.query_count() == message.queries().len() as u16,
"Query count mismatch"
);
ensure!(
message.answer_count() == message.answers().len() as u16,
"Answer count mismatch"
);
let status: u32 =
<u16 as From<proto::op::ResponseCode>>::from(message.response_code()) as u32;
let question: Vec<_> = message
.queries()
.iter()
.map(DohQuestionJson::from_query)
.collect();
let answer: Vec<_> = message
.answers()
.iter()
.map(DohRecordJson::from_record)
.collect::<Result<_>>()?;
Ok(DnsResponse {
status,
tc: message.truncated(),
rd: message.recursion_desired(),
ra: message.recursion_available(),
ad: message.authentic_data(),
cd: message.checking_disabled(),
question,
answer,
comment: None,
edns_client_subnet: None,
})
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DohQuestionJson {
pub name: String,
#[serde(rename = "type")]
pub question_type: u16,
}
impl DohQuestionJson {
pub fn from_query(query: &proto::op::Query) -> Self {
Self {
name: query.name().to_string(),
question_type: query.query_type().into(),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DohRecordJson {
pub name: String,
#[serde(rename = "type")]
pub record_type: u16,
#[serde(rename = "TTL")]
pub ttl: u32,
pub data: String,
}
impl DohRecordJson {
pub fn from_record(record: &proto::rr::Record) -> Result<Self> {
Ok(Self {
name: record.name().to_string(),
record_type: record.record_type().into(),
ttl: record.ttl(),
data: record.data().to_string(),
})
}
}