1use anyhow::{bail, Result};
4use bytes::{Buf, BufMut, Bytes, BytesMut};
5use domain::base::opt::AllOptData;
6use std::convert::TryFrom;
7use std::net::IpAddr;
8use thiserror::Error;
9
10pub fn try_from_u8_slice_for_ipaddr(value: &[u8]) -> Result<IpAddr> {
14 match value.len() {
15 4 => Ok(IpAddr::from(<[u8; 4]>::try_from(value)?)),
16 16 => Ok(IpAddr::from(<[u8; 16]>::try_from(value)?)),
17 _ => bail!(
18 "Cannot decode an IP address from a {} byte field",
19 value.len()
20 ),
21 }
22}
23
24#[derive(Debug, Error, PartialEq)]
25pub enum DnstapHandlerError {
26 #[error("Mismatch between logged dnstap response and re-queried DNS response, expecting {1} but received {2}")]
27 Mismatch(Bytes, String, String),
28
29 #[error("Timeout sending DNS query")]
30 Timeout,
31
32 #[error("dnstap payload is missing a required field")]
33 MissingField,
34}
35
36const DNSTAP_HANDLER_ERROR_PREFIX: &[u8] = b"dnstap-replay/DnstapHandlerError\x00";
37
38impl DnstapHandlerError {
39 pub fn serialize(&self) -> Bytes {
40 match self {
41 DnstapHandlerError::Mismatch(mismatch, _, _) => {
42 let mut b =
43 BytesMut::with_capacity(DNSTAP_HANDLER_ERROR_PREFIX.len() + 4 + mismatch.len());
44 b.extend_from_slice(DNSTAP_HANDLER_ERROR_PREFIX);
45 b.put_u32(1);
46 b.extend_from_slice(mismatch);
47 b
48 }
49 DnstapHandlerError::Timeout => {
50 let mut b = BytesMut::with_capacity(DNSTAP_HANDLER_ERROR_PREFIX.len() + 4);
51 b.extend_from_slice(DNSTAP_HANDLER_ERROR_PREFIX);
52 b.put_u32(2);
53 b
54 }
55 DnstapHandlerError::MissingField => {
56 let mut b = BytesMut::with_capacity(DNSTAP_HANDLER_ERROR_PREFIX.len() + 4);
57 b.extend_from_slice(DNSTAP_HANDLER_ERROR_PREFIX);
58 b.put_u32(3);
59 b
60 }
61 }
62 .freeze()
63 }
64}
65
66pub fn deserialize_dnstap_handler_error(input: &[u8]) -> Result<DnstapHandlerError> {
67 if input.len() < DNSTAP_HANDLER_ERROR_PREFIX.len() {
68 bail!("Input buffer is too small");
69 }
70
71 let mut buf = Bytes::copy_from_slice(input);
72
73 let prefix = buf.copy_to_bytes(DNSTAP_HANDLER_ERROR_PREFIX.len());
74 if prefix != DNSTAP_HANDLER_ERROR_PREFIX {
75 bail!("DnstapHandlerError prefix not present");
76 }
77
78 if buf.remaining() < 4 {
79 bail!("DnstapHandlerError type not present");
80 }
81
82 match buf.get_u32() {
83 1 => Ok(DnstapHandlerError::Mismatch(
84 buf.split_off(0),
85 String::from(""),
86 String::from(""),
87 )),
88 2 => Ok(DnstapHandlerError::Timeout),
89 3 => Ok(DnstapHandlerError::MissingField),
90 _ => {
91 bail!("Unknown DnstapHandlerError type");
92 }
93 }
94}
95
96#[test]
97fn test_deserialize_dnstap_handler_error() {
98 assert_eq!(
101 deserialize_dnstap_handler_error(b"dnstap-replay/DnstapHandlerError\x00\x00\x00\x00\x01")
102 .unwrap(),
103 DnstapHandlerError::Mismatch(Bytes::from(&b""[..]), String::from(""), String::from(""))
104 );
105
106 assert_eq!(
108 deserialize_dnstap_handler_error(
109 b"dnstap-replay/DnstapHandlerError\x00\x00\x00\x00\x01\x42"
110 )
111 .unwrap(),
112 DnstapHandlerError::Mismatch(
113 Bytes::from(&b"\x42"[..]),
114 String::from(""),
115 String::from("")
116 )
117 );
118
119 assert_eq!(
121 deserialize_dnstap_handler_error(b"dnstap-replay/DnstapHandlerError\x00\x00\x00\x00\x02")
122 .unwrap(),
123 DnstapHandlerError::Timeout
124 );
125
126 assert_eq!(
128 deserialize_dnstap_handler_error(b"dnstap-replay/DnstapHandlerError\x00\x00\x00\x00\x03")
129 .unwrap(),
130 DnstapHandlerError::MissingField
131 );
132}
133
134pub fn fmt_dns_message(s: &mut String, prefix: &str, raw_msg_bytes: &[u8]) {
135 use domain::base::iana::rtype::Rtype;
136 use domain::base::Message;
137 use domain::rdata::AllRecordData;
138
139 let msg = match Message::from_octets(raw_msg_bytes) {
140 Ok(msg) => msg,
141 Err(err) => {
142 s.push_str(prefix);
143 s.push_str(";; PARSE ERROR: ");
144 s.push_str(&err.to_string());
145 s.push('\n');
146 return;
147 }
148 };
149
150 let hdr = msg.header();
151
152 s.push_str(prefix);
154 s.push_str(";; ->>HEADER<<- opcode: ");
155 s.push_str(&hdr.opcode().to_string());
156
157 s.push_str(", rcode: ");
159 s.push_str(&hdr.rcode().to_string());
160
161 s.push_str(", id: ");
163 s.push_str(&hdr.id().to_string());
164 s.push('\n');
165
166 s.push_str(prefix);
168 s.push_str(";; flags: ");
169 if hdr.qr() {
170 s.push_str("qr ");
171 }
172 if hdr.aa() {
173 s.push_str("aa ");
174 }
175 if hdr.tc() {
176 s.push_str("tc ");
177 }
178 if hdr.rd() {
179 s.push_str("rd ");
180 }
181 if hdr.ra() {
182 s.push_str("ra ");
183 }
184 if hdr.ad() {
185 s.push_str("ad ");
186 }
187 if hdr.cd() {
188 s.push_str("cd ");
189 }
190
191 let hdr_counts = msg.header_counts();
193 s.push_str("; QUERY: ");
194 s.push_str(&hdr_counts.qdcount().to_string());
195 s.push_str(", ANSWER: ");
196 s.push_str(&hdr_counts.ancount().to_string());
197 s.push_str(", AUTHORITY: ");
198 s.push_str(&hdr_counts.nscount().to_string());
199 s.push_str(", ADDITIONAL: ");
200 s.push_str(&hdr_counts.adcount().to_string());
201 s.push_str("\n\n");
202
203 if let Ok(sections) = msg.sections() {
204 s.push_str(prefix);
205 s.push_str(";; QUESTION SECTION:\n");
206 for question in sections.0.flatten() {
207 s.push_str(prefix);
208 s.push(';');
209 s.push_str(&question.qname().to_string());
210 s.push_str(". ");
211 s.push_str(&question.qclass().to_string());
212 s.push(' ');
213 s.push_str(&question.qtype().to_string());
214 s.push('\n')
215 }
216 s.push('\n');
217
218 s.push_str(prefix);
219 s.push_str(";; ANSWER SECTION:\n");
220 for record in sections.1.limit_to::<AllRecordData<_, _>>().flatten() {
221 s.push_str(prefix);
222 s.push_str(&record.to_string());
223 s.push('\n')
224 }
225 s.push('\n');
226
227 s.push_str(prefix);
228 s.push_str(";; AUTHORITY SECTION:\n");
229 for record in sections.2.limit_to::<AllRecordData<_, _>>().flatten() {
230 s.push_str(prefix);
231 s.push_str(&record.to_string());
232 s.push('\n')
233 }
234 s.push('\n');
235
236 s.push_str(prefix);
237 s.push_str(";; ADDITIONAL SECTION:\n");
238 for record in sections.3.limit_to::<AllRecordData<_, _>>().flatten() {
239 if record.rtype() == Rtype::Opt {
240 continue;
241 }
242 s.push_str(prefix);
243 s.push_str(&record.to_string());
244 s.push('\n')
245 }
246
247 if let Some(optrec) = msg.opt() {
248 s.push('\n');
249 s.push_str(prefix);
250 s.push_str(";; OPT PSEUDOSECTION:\n");
251 s.push_str(prefix);
252 s.push_str("; EDNS: version ");
253 s.push_str(&optrec.version().to_string());
254 s.push_str("; flags: ");
255 if optrec.dnssec_ok() {
256 s.push_str("do ");
257 }
258 s.push_str("; udp: ");
259 s.push_str(&optrec.udp_payload_size().to_string());
260 s.push('\n');
261
262 for opt in optrec.iter::<AllOptData<_>>().flatten() {
263 s.push_str(prefix);
264 match opt {
265 AllOptData::Nsid(nsid) => {
266 s.push_str("; NSID: ");
267 s.push_str(&nsid.to_string());
268 if let Ok(nsid_data) = hex::decode(&nsid.to_string()) {
269 if let Ok(nsid_str) = std::str::from_utf8(&nsid_data) {
270 s.push_str(" (\"");
271 s.push_str(nsid_str);
272 s.push_str("\")");
273 }
274 }
275 }
276 AllOptData::Dau(data) => {
277 s.push_str("; DAU:");
278 for alg in &data {
279 s.push(' ');
280 s.push_str(&alg.to_string());
281 }
282 }
283 AllOptData::Dhu(data) => {
284 s.push_str("; DHU:");
285 for alg in &data {
286 s.push(' ');
287 s.push_str(&alg.to_string());
288 }
289 }
290 AllOptData::N3u(data) => {
291 s.push_str("; N3U:");
292 for alg in &data {
293 s.push(' ');
294 s.push_str(&alg.to_string());
295 }
296 }
297 AllOptData::Expire(expire) => {
298 s.push_str("; EXPIRE: ");
299 if let Some(expire_data) = expire.expire() {
300 s.push_str(&expire_data.to_string());
301 s.push_str(" seconds");
302 }
303 }
304 AllOptData::TcpKeepalive(data) => {
305 s.push_str("; TCP-KEEPALIVE: ");
306 let iseconds = data.timeout() / 10;
307 let fseconds = data.timeout() % 10;
308 s.push_str(&iseconds.to_string());
309 s.push('.');
310 s.push_str(&fseconds.to_string());
311 s.push_str(" seconds");
312 }
313 AllOptData::Padding(padding) => {
314 s.push_str("; PADDING: [");
315 s.push_str(&padding.len().to_string());
316 s.push_str(" bytes]");
317 }
318 AllOptData::ClientSubnet(subnet) => {
319 s.push_str("; CLIENT-SUBNET:\n");
320 s.push_str(prefix);
321 s.push_str("; NETWORK ADDRESS: ");
322 s.push_str(&subnet.addr().to_string());
323 s.push('\n');
324 s.push_str(prefix);
325 s.push_str("; SOURCE PREFIX-LENGTH: ");
326 s.push_str(&subnet.source_prefix_len().to_string());
327 s.push('\n');
328 s.push_str(prefix);
329 s.push_str("; SCOPE PREFIX-LENGTH: ");
330 s.push_str(&subnet.scope_prefix_len().to_string());
331 }
332 AllOptData::Cookie(data) => {
333 s.push_str("; COOKIE: ");
334 s.push_str(&hex::encode(data.cookie()));
335 }
336 AllOptData::Chain(data) => {
337 s.push_str("; CHAIN: ");
338 s.push_str(&data.start().to_string());
339 }
340 AllOptData::KeyTag(data) => {
341 s.push_str("; KEY-TAG:");
342 for keytag in &data {
343 s.push(' ');
344 s.push_str(&keytag.to_string());
345 }
346 }
347 AllOptData::ExtendedError(data) => {
348 s.push_str("; EXTENDED-DNS-ERROR:\n");
349 s.push_str(prefix);
350 s.push_str("; INFO-CODE: (");
351 s.push_str(&data.code().to_int().to_string());
352 s.push_str(") ");
353 s.push_str(&data.code().to_string());
354 s.push('\n');
355 if let Some(text) = data.text() {
356 if let Ok(text_str) = std::str::from_utf8(text) {
357 s.push_str(prefix);
358 s.push_str("; EXTRA-TEXT: \"");
359 s.push_str(text_str);
360 s.push('"');
361 }
362 }
363 }
364 AllOptData::Other(data) => {
365 s.push_str("; OPT=");
366 s.push_str(&data.code().to_int().to_string());
367 s.push(':');
368 s.push_str(&hex::encode(data.data()).to_uppercase());
369 }
370 _ => {
371 s.push_str("; Other unknown EDNS option, giving up.");
372 }
373 }
374 s.push('\n');
375 }
376 }
377 }
378}
379
380pub fn dns_message_is_truncated(raw_msg_bytes: &[u8]) -> bool {
381 if raw_msg_bytes.len() >= 12 {
383 let msg = domain::base::Header::for_message_slice(raw_msg_bytes);
384 return msg.tc();
385 }
386 false
387}
388
389#[test]
390fn test_dns_message_is_truncated() {
391 assert!(!dns_message_is_truncated(&hex::decode("1234").unwrap()));
393
394 assert!(dns_message_is_truncated(
396 &hex::decode("b84587000001000000000001").unwrap()
397 ));
398
399 assert!(!dns_message_is_truncated(
401 &hex::decode("b84585000001000000010001").unwrap()
402 ));
403}