1use crate::types::{DonutError, DonutResult, ErrorKind};
20use serde::Serialize;
21use std::str;
22use tracing::{event, Level};
23use trust_dns_client::op::DnsResponse;
24use trust_dns_client::proto::serialize::binary::BinEncodable;
25use trust_dns_client::rr::{RData, Record};
26
27#[derive(Clone, Default, Debug, PartialEq, Eq, Hash)]
28pub struct ResponseMetadata {
29 min_ttl: Option<u32>,
30}
31
32impl ResponseMetadata {
33 pub fn min_ttl(&self) -> Option<u32> {
34 self.min_ttl
35 }
36}
37
38impl From<&DnsResponse> for ResponseMetadata {
39 fn from(r: &DnsResponse) -> Self {
40 let min_ttl = r.answers().iter().map(|a| a.ttl()).min();
41 ResponseMetadata { min_ttl }
42 }
43}
44
45#[derive(Debug, Default, Clone)]
46pub struct ResponseEncoderJson;
47
48impl ResponseEncoderJson {
49 pub fn new() -> Self {
50 ResponseEncoderJson
51 }
52
53 pub async fn encode(&self, res: DnsResponse) -> DonutResult<(ResponseMetadata, Vec<u8>)> {
54 let questions: Vec<JsonQuestion> = res
55 .queries()
56 .iter()
57 .map(|query| JsonQuestion::new(query.name().to_utf8(), u16::from(query.query_type())))
58 .collect();
59
60 let answers: Vec<JsonAnswer> = res
61 .answers()
62 .iter()
63 .map(|record| {
64 let data = record_to_data(record);
65 JsonAnswer::new(
66 record.name().to_utf8(),
67 u16::from(record.record_type()),
68 record.ttl(),
69 data,
70 )
71 })
72 .collect();
73
74 let meta = ResponseMetadata::from(&res);
75 let bytes = serde_json::to_vec(&JsonResponse::new(
76 u16::from(res.response_code()),
77 res.truncated(),
78 res.recursion_desired(),
79 res.recursion_available(),
80 false,
81 res.checking_disabled(),
82 questions,
83 answers,
84 ))
85 .map_err(|e| DonutError::from((ErrorKind::Internal, "unable to serialize to response", Box::new(e))))?;
86
87 event!(
88 Level::TRACE,
89 message = "encoded DNS response to JSON format",
90 num_bytes = bytes.len(),
91 );
92
93 Ok((meta, bytes))
94 }
95}
96
97pub fn record_to_data(record: &Record) -> String {
98 match record.rdata() {
99 RData::A(v) => v.to_string(),
100 RData::AAAA(v) => v.to_string(),
101 RData::ANAME(v) => v.to_string(),
102 RData::CNAME(v) => v.to_utf8(),
104 RData::MX(v) => format!("{} {}", v.preference(), v.exchange()),
105 RData::NAPTR(v) => format!(
106 "{} {} \"{}\" \"{}\" \"{}\" {}",
107 v.order(),
108 v.preference(),
109 str::from_utf8(v.flags()).unwrap_or(""),
110 str::from_utf8(v.services()).unwrap_or(""),
111 str::from_utf8(v.regexp()).unwrap_or(""),
112 v.replacement(),
113 ),
114 RData::NS(v) => v.to_utf8(),
115 RData::PTR(v) => v.to_utf8(),
119 RData::SOA(v) => format!(
120 "{} {} {} {} {} {} {}",
121 v.mname(),
122 v.rname(),
123 v.serial(),
124 v.refresh(),
125 v.retry(),
126 v.expire(),
127 v.minimum(),
128 ),
129 RData::SRV(v) => format!("{} {} {} {}", v.priority(), v.weight(), v.port(), v.target()),
130 RData::TXT(v) => format!(
133 "\"{}\"",
134 v.txt_data()
135 .iter()
136 .flat_map(|t| str::from_utf8(t))
137 .collect::<Vec<&str>>()
138 .concat()
139 ),
140 _ => panic!("Unexpected result: {:?}", record),
141 }
142}
143
144#[derive(Debug, Default, Clone, PartialEq, Serialize)]
145struct JsonQuestion {
146 #[serde(rename = "name")]
147 name: String,
148
149 #[serde(rename = "type")]
150 kind: u16,
151}
152
153impl JsonQuestion {
154 fn new<S>(name: S, kind: u16) -> Self
155 where
156 S: Into<String>,
157 {
158 JsonQuestion {
159 name: name.into(),
160 kind,
161 }
162 }
163}
164
165#[derive(Debug, Default, Clone, PartialEq, Serialize)]
166struct JsonAnswer {
167 #[serde(rename = "name")]
168 name: String,
169
170 #[serde(rename = "type")]
171 kind: u16,
172
173 #[serde(rename = "TTL")]
174 ttl: u32,
175
176 #[serde(rename = "data")]
177 data: String,
178}
179
180impl JsonAnswer {
181 fn new<S1, S2>(name: S1, kind: u16, ttl: u32, data: S2) -> Self
182 where
183 S1: Into<String>,
184 S2: Into<String>,
185 {
186 JsonAnswer {
187 name: name.into(),
188 kind,
189 ttl,
190 data: data.into(),
191 }
192 }
193}
194
195#[derive(Debug, Default, Clone, PartialEq, Serialize)]
196pub struct JsonResponse {
197 #[serde(rename = "Status")]
198 status: u16,
199
200 #[serde(rename = "TC")]
201 truncated: bool,
202
203 #[serde(rename = "RD")]
204 recursion_desired: bool,
205
206 #[serde(rename = "RA")]
207 recursion_available: bool,
208
209 #[serde(rename = "AD")]
210 all_validated: bool,
211
212 #[serde(rename = "CD")]
213 checking_disabled: bool,
214
215 #[serde(rename = "Question")]
216 questions: Vec<JsonQuestion>,
217
218 #[serde(rename = "Answer")]
219 answers: Vec<JsonAnswer>,
220}
221
222impl JsonResponse {
223 #[allow(clippy::too_many_arguments)]
224 fn new(
225 status: u16,
226 truncated: bool,
227 recursion_desired: bool,
228 recursion_available: bool,
229 all_validated: bool,
230 checking_disabled: bool,
231 questions: Vec<JsonQuestion>,
232 answers: Vec<JsonAnswer>,
233 ) -> Self {
234 JsonResponse {
235 status,
236 truncated,
237 recursion_desired,
238 recursion_available,
239 all_validated,
240 checking_disabled,
241 questions,
242 answers,
243 }
244 }
245}
246
247#[derive(Debug, Default, Clone)]
248pub struct ResponseEncoderWire;
249
250impl ResponseEncoderWire {
251 pub fn new() -> Self {
252 ResponseEncoderWire
253 }
254
255 pub async fn encode(&self, res: DnsResponse) -> DonutResult<(ResponseMetadata, Vec<u8>)> {
256 let meta = ResponseMetadata::from(&res);
257 let bytes = res.to_bytes()?;
258
259 event!(
260 Level::TRACE,
261 message = "encoded DNS response to wire format",
262 num_bytes = bytes.len(),
263 );
264
265 Ok((meta, bytes))
266 }
267}