hickory_proto/xfer/dns_response.rs
1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! `DnsResponse` wraps a `Message` and any associated connection details
9
10#[cfg(feature = "std")]
11use alloc::boxed::Box;
12use alloc::vec::Vec;
13use core::{
14 convert::TryFrom,
15 ops::{Deref, DerefMut},
16};
17#[cfg(feature = "std")]
18use core::{
19 future::Future,
20 pin::Pin,
21 task::{Context, Poll},
22};
23#[cfg(feature = "std")]
24use std::io;
25
26#[cfg(feature = "std")]
27use futures_channel::mpsc;
28#[cfg(feature = "std")]
29use futures_util::{ready, stream::Stream};
30
31#[cfg(feature = "serde")]
32use serde::{Deserialize, Serialize};
33
34#[cfg(feature = "std")]
35use crate::{ProtoErrorKind, error::ProtoResult};
36use crate::{
37 error::ProtoError,
38 op::{Message, ResponseCode},
39 rr::{RecordType, rdata::SOA, resource::RecordRef},
40};
41
42/// A stream returning DNS responses
43#[cfg(feature = "std")]
44pub struct DnsResponseStream {
45 inner: DnsResponseStreamInner,
46 done: bool,
47}
48
49#[cfg(feature = "std")]
50impl DnsResponseStream {
51 fn new(inner: DnsResponseStreamInner) -> Self {
52 Self { inner, done: false }
53 }
54}
55
56#[cfg(feature = "std")]
57impl Stream for DnsResponseStream {
58 type Item = Result<DnsResponse, ProtoError>;
59
60 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
61 use DnsResponseStreamInner::*;
62
63 // if the standard futures are done, don't poll again
64 if self.done {
65 return Poll::Ready(None);
66 }
67
68 // split mutable refs to Self
69 let Self { inner, done } = self.get_mut();
70
71 let result = match inner {
72 Timeout(fut) => {
73 let x = match ready!(fut.as_mut().poll(cx)) {
74 Ok(x) => x,
75 Err(e) => Err(e.into()),
76 };
77 *done = true;
78 x
79 }
80 Receiver(fut) => match ready!(Pin::new(fut).poll_next(cx)) {
81 Some(x) => x,
82 None => return Poll::Ready(None),
83 },
84 Error(err) => {
85 *done = true;
86 Err(err.take().expect("cannot poll after complete"))
87 }
88 Boxed(fut) => {
89 let x = ready!(fut.as_mut().poll(cx));
90 *done = true;
91 x
92 }
93 };
94
95 match result {
96 Err(e) if matches!(e.kind(), ProtoErrorKind::Timeout) => Poll::Ready(None),
97 r => Poll::Ready(Some(r)),
98 }
99 }
100}
101
102#[cfg(feature = "std")]
103impl From<TimeoutFuture> for DnsResponseStream {
104 fn from(f: TimeoutFuture) -> Self {
105 Self::new(DnsResponseStreamInner::Timeout(f))
106 }
107}
108
109#[cfg(feature = "std")]
110impl From<mpsc::Receiver<ProtoResult<DnsResponse>>> for DnsResponseStream {
111 fn from(receiver: mpsc::Receiver<ProtoResult<DnsResponse>>) -> Self {
112 Self::new(DnsResponseStreamInner::Receiver(receiver))
113 }
114}
115
116#[cfg(feature = "std")]
117impl From<ProtoError> for DnsResponseStream {
118 fn from(e: ProtoError) -> Self {
119 Self::new(DnsResponseStreamInner::Error(Some(e)))
120 }
121}
122
123#[cfg(feature = "std")]
124impl<F> From<Pin<Box<F>>> for DnsResponseStream
125where
126 F: Future<Output = Result<DnsResponse, ProtoError>> + Send + 'static,
127{
128 fn from(f: Pin<Box<F>>) -> Self {
129 Self::new(DnsResponseStreamInner::Boxed(
130 f as Pin<Box<dyn Future<Output = Result<DnsResponse, ProtoError>> + Send>>,
131 ))
132 }
133}
134
135#[cfg(feature = "std")]
136enum DnsResponseStreamInner {
137 Timeout(TimeoutFuture),
138 Receiver(mpsc::Receiver<ProtoResult<DnsResponse>>),
139 Error(Option<ProtoError>),
140 Boxed(Pin<Box<dyn Future<Output = Result<DnsResponse, ProtoError>> + Send>>),
141}
142
143#[cfg(feature = "std")]
144type TimeoutFuture = Pin<
145 Box<dyn Future<Output = Result<Result<DnsResponse, ProtoError>, io::Error>> + Send + 'static>,
146>;
147
148// TODO: this needs to have the IP addr of the remote system...
149// TODO: see https://github.com/hickory-dns/hickory-dns/issues/383 for removing vec of messages and instead returning a Stream
150/// A DNS response object
151///
152/// For Most DNS requests, only one response is expected, the exception is a multicast request.
153#[derive(Clone, Debug)]
154#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
155pub struct DnsResponse {
156 message: Message,
157 buffer: Vec<u8>,
158}
159
160// TODO: when `impl Trait` lands in stable, remove this, and expose FlatMap over answers, et al.
161impl DnsResponse {
162 /// Constructs a new DnsResponse with a buffer synthesized from the message
163 pub fn from_message(message: Message) -> Result<Self, ProtoError> {
164 Ok(Self {
165 buffer: message.to_vec()?,
166 message,
167 })
168 }
169
170 /// Constructs a new DnsResponse by parsing a message from a buffer.
171 ///
172 /// Returns an error if the response message cannot be decoded.
173 pub fn from_buffer(buffer: Vec<u8>) -> Result<Self, ProtoError> {
174 let message = Message::from_vec(&buffer)?;
175 Ok(Self { message, buffer })
176 }
177
178 /// Retrieves the SOA from the response. This will only exist if it was an authoritative response.
179 pub fn soa(&self) -> Option<RecordRef<'_, SOA>> {
180 self.name_servers()
181 .iter()
182 .find_map(|record| RecordRef::try_from(record).ok())
183 }
184
185 /// Looks in the authority section for an SOA record from the response, and returns the negative_ttl, None if not available.
186 ///
187 /// ```text
188 /// [RFC 2308](https://tools.ietf.org/html/rfc2308#section-5) DNS NCACHE March 1998
189 ///
190 /// 5 - Caching Negative Answers
191 ///
192 /// Like normal answers negative answers have a time to live (TTL). As
193 /// there is no record in the answer section to which this TTL can be
194 /// applied, the TTL must be carried by another method. This is done by
195 /// including the SOA record from the zone in the authority section of
196 /// the reply. When the authoritative server creates this record its TTL
197 /// is taken from the minimum of the SOA.MINIMUM field and SOA's TTL.
198 /// This TTL decrements in a similar manner to a normal cached answer and
199 /// upon reaching zero (0) indicates the cached negative answer MUST NOT
200 /// be used again.
201 ///
202 /// A negative answer that resulted from a name error (NXDOMAIN) should
203 /// be cached such that it can be retrieved and returned in response to
204 /// another query for the same <QNAME, QCLASS> that resulted in the
205 /// cached negative response.
206 ///
207 /// A negative answer that resulted from a no data error (NODATA) should
208 /// be cached such that it can be retrieved and returned in response to
209 /// another query for the same <QNAME, QTYPE, QCLASS> that resulted in
210 /// the cached negative response.
211 ///
212 /// The NXT record, if it exists in the authority section of a negative
213 /// answer received, MUST be stored such that it can be be located and
214 /// returned with SOA record in the authority section, as should any SIG
215 /// records in the authority section. For NXDOMAIN answers there is no
216 /// "necessary" obvious relationship between the NXT records and the
217 /// QNAME. The NXT record MUST have the same owner name as the query
218 /// name for NODATA responses.
219 ///
220 /// Negative responses without SOA records SHOULD NOT be cached as there
221 /// is no way to prevent the negative responses looping forever between a
222 /// pair of servers even with a short TTL.
223 ///
224 /// Despite the DNS forming a tree of servers, with various mis-
225 /// configurations it is possible to form a loop in the query graph, e.g.
226 /// two servers listing each other as forwarders, various lame server
227 /// configurations. Without a TTL count down a cache negative response
228 /// when received by the next server would have its TTL reset. This
229 /// negative indication could then live forever circulating between the
230 /// servers involved.
231 ///
232 /// As with caching positive responses it is sensible for a resolver to
233 /// limit for how long it will cache a negative response as the protocol
234 /// supports caching for up to 68 years. Such a limit should not be
235 /// greater than that applied to positive answers and preferably be
236 /// tunable. Values of one to three hours have been found to work well
237 /// and would make sensible a default. Values exceeding one day have
238 /// been found to be problematic.
239 /// ```
240 pub fn negative_ttl(&self) -> Option<u32> {
241 // TODO: should this ensure that the SOA zone matches the Queried Zone?
242 self.name_servers()
243 .iter()
244 .filter_map(|record| record.data().as_soa().map(|soa| (record.ttl(), soa)))
245 .next()
246 .map(|(ttl, soa)| (ttl).min(soa.minimum()))
247 }
248
249 /// Does the response contain any records matching the query name and type?
250 pub fn contains_answer(&self) -> bool {
251 for q in self.queries() {
252 let found = match q.query_type() {
253 RecordType::ANY => self.all_sections().any(|r| r.name() == q.name()),
254 RecordType::SOA => {
255 // for SOA name must be part of the SOA zone
256 self.all_sections()
257 .filter(|r| r.record_type().is_soa())
258 .any(|r| r.name().zone_of(q.name()))
259 }
260 q_type => {
261 if !self.answers().is_empty() {
262 true
263 } else {
264 self.all_sections()
265 .filter(|r| r.record_type() == q_type)
266 .any(|r| r.name() == q.name())
267 }
268 }
269 };
270
271 if found {
272 return true;
273 }
274 }
275
276 false
277 }
278
279 /// Retrieve the type of the negative response.
280 /// The Various types should be handled when caching or otherwise differently.
281 ///
282 /// See [NegativeType]
283 pub fn negative_type(&self) -> Option<NegativeType> {
284 let response_code = self.response_code();
285 let ttl_from_soa = self.negative_ttl();
286 let has_soa = ttl_from_soa.is_some();
287 let has_ns_records = self.name_servers().iter().any(|r| r.record_type().is_ns());
288 let has_cname = self.answers().iter().any(|r| r.record_type().is_cname());
289 let has_non_cname = self.answers().iter().any(|r| !r.record_type().is_cname());
290 let has_additionals = self.additional_count() > 0;
291
292 match (
293 response_code,
294 has_soa,
295 has_ns_records,
296 has_cname,
297 has_non_cname,
298 has_additionals,
299 ) {
300 (ResponseCode::NXDomain, true, true, _, false, _) => Some(NegativeType::NameErrorType1),
301 (ResponseCode::NXDomain, true, false, _, false, _) => {
302 Some(NegativeType::NameErrorType2)
303 }
304 (ResponseCode::NXDomain, false, false, true, false, _) => {
305 Some(NegativeType::NameErrorType3)
306 }
307 (ResponseCode::NXDomain, false, true, _, false, _) => {
308 Some(NegativeType::NameErrorType4)
309 }
310 (ResponseCode::NoError, true, true, false, false, _) => Some(NegativeType::NoDataType1),
311 (ResponseCode::NoError, true, false, false, false, _) => {
312 Some(NegativeType::NoDataType2)
313 }
314 (ResponseCode::NoError, false, false, false, false, false) => {
315 Some(NegativeType::NoDataType3)
316 }
317 (ResponseCode::NoError, false, true, _, false, _) => Some(NegativeType::Referral),
318 _ => None,
319 }
320 }
321
322 /// Borrow the inner buffer from the response
323 pub fn as_buffer(&self) -> &[u8] {
324 &self.buffer
325 }
326
327 /// Take the inner buffer from the response
328 pub fn into_buffer(self) -> Vec<u8> {
329 self.buffer
330 }
331
332 /// Take the inner Message from the response
333 pub fn into_message(self) -> Message {
334 self.message
335 }
336
337 /// Take the inner Message and buffer from the response
338 pub fn into_parts(self) -> (Message, Vec<u8>) {
339 (self.message, self.buffer)
340 }
341}
342
343impl Deref for DnsResponse {
344 type Target = Message;
345
346 fn deref(&self) -> &Self::Target {
347 &self.message
348 }
349}
350
351impl DerefMut for DnsResponse {
352 fn deref_mut(&mut self) -> &mut Self::Target {
353 &mut self.message
354 }
355}
356
357impl From<DnsResponse> for Message {
358 fn from(response: DnsResponse) -> Self {
359 response.message
360 }
361}
362
363/// ```text
364/// [RFC 2308](https://tools.ietf.org/html/rfc2308#section-2) DNS NCACHE March 1998
365///
366///
367/// 2 - Negative Responses
368///
369/// The most common negative responses indicate that a particular RRset
370/// does not exist in the DNS. The first sections of this document deal
371/// with this case. Other negative responses can indicate failures of a
372/// nameserver, those are dealt with in section 7 (Other Negative
373/// Responses).
374///
375/// A negative response is indicated by one of the following conditions:
376///
377/// 2.1 - Name Error
378///
379/// Name errors (NXDOMAIN) are indicated by the presence of "Name Error"
380/// in the RCODE field. In this case the domain referred to by the QNAME
381/// does not exist. Note: the answer section may have SIG and CNAME RRs
382/// and the authority section may have SOA, NXT [RFC2065] and SIG RRsets.
383///
384/// It is possible to distinguish between a referral and a NXDOMAIN
385/// response by the presence of NXDOMAIN in the RCODE regardless of the
386/// presence of NS or SOA records in the authority section.
387///
388/// NXDOMAIN responses can be categorised into four types by the contents
389/// of the authority section. These are shown below along with a
390/// referral for comparison. Fields not mentioned are not important in
391/// terms of the examples.
392///
393/// See [NegativeType] below:
394/// [NegativeType::NameErrorType1]
395/// [NegativeType::NameErrorType2]
396/// [NegativeType::NameErrorType3]
397/// [NegativeType::NameErrorType4]
398/// [NegativeType::Referral]
399///
400/// Note, in the four examples of NXDOMAIN responses, it is known that
401/// the name "AN.EXAMPLE." exists, and has as its value a CNAME record.
402/// The NXDOMAIN refers to "TRIPPLE.XX", which is then known not to
403/// exist. On the other hand, in the referral example, it is shown that
404/// "AN.EXAMPLE" exists, and has a CNAME RR as its value, but nothing is
405/// known one way or the other about the existence of "TRIPPLE.XX", other
406/// than that "NS1.XX" or "NS2.XX" can be consulted as the next step in
407/// obtaining information about it.
408///
409/// Where no CNAME records appear, the NXDOMAIN response refers to the
410/// name in the label of the RR in the question section.
411///
412/// 2.1.1 Special Handling of Name Error
413///
414/// This section deals with errors encountered when implementing negative
415/// caching of NXDOMAIN responses.
416///
417/// There are a large number of resolvers currently in existence that
418/// fail to correctly detect and process all forms of NXDOMAIN response.
419/// Some resolvers treat a TYPE 1 NXDOMAIN response as a referral. To
420/// alleviate this problem it is recommended that servers that are
421/// authoritative for the NXDOMAIN response only send TYPE 2 NXDOMAIN
422/// responses, that is the authority section contains a SOA record and no
423/// NS records. If a non- authoritative server sends a type 1 NXDOMAIN
424/// response to one of these old resolvers, the result will be an
425/// unnecessary query to an authoritative server. This is undesirable,
426/// but not fatal except when the server is being used a FORWARDER. If
427/// however the resolver is using the server as a FORWARDER to such a
428/// resolver it will be necessary to disable the sending of TYPE 1
429/// NXDOMAIN response to it, use TYPE 2 NXDOMAIN instead.
430///
431/// Some resolvers incorrectly continue processing if the authoritative
432/// answer flag is not set, looping until the query retry threshold is
433/// exceeded and then returning SERVFAIL. This is a problem when your
434/// nameserver is listed as a FORWARDER for such resolvers. If the
435/// nameserver is used as a FORWARDER by such resolver, the authority
436/// flag will have to be forced on for NXDOMAIN responses to these
437/// resolvers. In practice this causes no problems even if turned on
438/// always, and has been the default behaviour in BIND from 4.9.3
439/// onwards.
440///
441/// 2.2 - No Data
442///
443/// NODATA is indicated by an answer with the RCODE set to NOERROR and no
444/// relevant answers in the answer section. The authority section will
445/// contain an SOA record, or there will be no NS records there.
446/// NODATA responses have to be algorithmically determined from the
447/// response's contents as there is no RCODE value to indicate NODATA.
448/// In some cases to determine with certainty that NODATA is the correct
449/// response it can be necessary to send another query.
450///
451/// The authority section may contain NXT and SIG RRsets in addition to
452/// NS and SOA records. CNAME and SIG records may exist in the answer
453/// section.
454///
455/// It is possible to distinguish between a NODATA and a referral
456/// response by the presence of a SOA record in the authority section or
457/// the absence of NS records in the authority section.
458///
459/// NODATA responses can be categorised into three types by the contents
460/// of the authority section. These are shown below along with a
461/// referral for comparison. Fields not mentioned are not important in
462/// terms of the examples.
463///
464/// See [NegativeType] below:
465/// [NegativeType::NoDataType1]
466/// [NegativeType::NoDataType2]
467/// [NegativeType::NoDataType3]
468///
469/// These examples, unlike the NXDOMAIN examples above, have no CNAME
470/// records, however they could, in just the same way that the NXDOMAIN
471/// examples did, in which case it would be the value of the last CNAME
472/// (the QNAME) for which NODATA would be concluded.
473///
474/// 2.2.1 - Special Handling of No Data
475///
476/// There are a large number of resolvers currently in existence that
477/// fail to correctly detect and process all forms of NODATA response.
478/// Some resolvers treat a TYPE 1 NODATA response as a referral. To
479/// alleviate this problem it is recommended that servers that are
480/// authoritative for the NODATA response only send TYPE 2 NODATA
481/// responses, that is the authority section contains a SOA record and no
482/// NS records. Sending a TYPE 1 NODATA response from a non-
483/// authoritative server to one of these resolvers will only result in an
484/// unnecessary query. If a server is listed as a FORWARDER for another
485/// resolver it may also be necessary to disable the sending of TYPE 1
486/// NODATA response for non-authoritative NODATA responses.
487/// Some name servers fail to set the RCODE to NXDOMAIN in the presence
488/// of CNAMEs in the answer section. If a definitive NXDOMAIN / NODATA
489/// answer is required in this case the resolver must query again using
490/// the QNAME as the query label.
491///
492/// 3 - Negative Answers from Authoritative Servers
493///
494/// Name servers authoritative for a zone MUST include the SOA record of
495/// the zone in the authority section of the response when reporting an
496/// NXDOMAIN or indicating that no data of the requested type exists.
497/// This is required so that the response may be cached. The TTL of this
498/// record is set from the minimum of the MINIMUM field of the SOA record
499/// and the TTL of the SOA itself, and indicates how long a resolver may
500/// cache the negative answer. The TTL SIG record associated with the
501/// SOA record should also be trimmed in line with the SOA's TTL.
502///
503/// If the containing zone is signed [RFC2065] the SOA and appropriate
504/// NXT and SIG records MUST be added.
505///
506/// ```
507#[derive(Clone, Copy, Eq, PartialEq, Debug)]
508pub enum NegativeType {
509 /// ```text
510 /// NXDOMAIN RESPONSE: TYPE 1.
511 ///
512 /// Header:
513 /// RDCODE=NXDOMAIN
514 /// Query:
515 /// AN.EXAMPLE. A
516 /// Answer:
517 /// AN.EXAMPLE. CNAME TRIPPLE.XX.
518 /// Authority:
519 /// XX. SOA NS1.XX. HOSTMASTER.NS1.XX. ....
520 /// XX. NS NS1.XX.
521 /// XX. NS NS2.XX.
522 /// Additional:
523 /// NS1.XX. A 127.0.0.2
524 /// NS2.XX. A 127.0.0.3
525 /// ```
526 NameErrorType1,
527
528 /// ```text
529 /// NXDOMAIN RESPONSE: TYPE 2.
530 ///
531 /// Header:
532 /// RDCODE=NXDOMAIN
533 /// Query:
534 /// AN.EXAMPLE. A
535 /// Answer:
536 /// AN.EXAMPLE. CNAME TRIPPLE.XX.
537 /// Authority:
538 /// XX. SOA NS1.XX. HOSTMASTER.NS1.XX. ....
539 /// Additional:
540 /// <empty>
541 /// ```
542 NameErrorType2,
543
544 /// ```text
545 /// NXDOMAIN RESPONSE: TYPE 3.
546 ///
547 /// Header:
548 /// RDCODE=NXDOMAIN
549 /// Query:
550 /// AN.EXAMPLE. A
551 /// Answer:
552 /// AN.EXAMPLE. CNAME TRIPPLE.XX.
553 /// Authority:
554 /// <empty>
555 /// Additional:
556 /// <empty>
557 /// ```
558 NameErrorType3,
559
560 /// ```text
561 /// NXDOMAIN RESPONSE: TYPE 4
562 ///
563 /// Header:
564 /// RDCODE=NXDOMAIN
565 /// Query:
566 /// AN.EXAMPLE. A
567 /// Answer:
568 /// AN.EXAMPLE. CNAME TRIPPLE.XX.
569 /// Authority:
570 /// XX. NS NS1.XX.
571 /// XX. NS NS2.XX.
572 /// Additional:
573 /// NS1.XX. A 127.0.0.2
574 /// NS2.XX. A 127.0.0.3
575 /// ```
576 NameErrorType4,
577
578 /// ```text
579 /// NODATA RESPONSE: TYPE 1.
580 ///
581 /// Header:
582 /// RDCODE=NOERROR
583 /// Query:
584 /// ANOTHER.EXAMPLE. A
585 /// Answer:
586 /// <empty>
587 /// Authority:
588 /// EXAMPLE. SOA NS1.XX. HOSTMASTER.NS1.XX. ....
589 /// EXAMPLE. NS NS1.XX.
590 /// EXAMPLE. NS NS2.XX.
591 /// Additional:
592 /// NS1.XX. A 127.0.0.2
593 /// NS2.XX. A 127.0.0.3
594 /// ```
595 NoDataType1,
596
597 /// ```text
598 /// NO DATA RESPONSE: TYPE 2.
599 ///
600 /// Header:
601 /// RDCODE=NOERROR
602 /// Query:
603 /// ANOTHER.EXAMPLE. A
604 /// Answer:
605 /// <empty>
606 /// Authority:
607 /// EXAMPLE. SOA NS1.XX. HOSTMASTER.NS1.XX. ....
608 /// Additional:
609 /// <empty>
610 /// ```
611 NoDataType2,
612
613 /// ```text
614 /// NO DATA RESPONSE: TYPE 3.
615 /// Header:
616 /// RDCODE=NOERROR
617 /// Query:
618 /// ANOTHER.EXAMPLE. A
619 /// Answer:
620 /// <empty>
621 /// Authority:
622 /// <empty>
623 /// Additional:
624 /// <empty>
625 /// ```
626 NoDataType3,
627
628 /// ```text
629 /// REFERRAL RESPONSE.
630 ///
631 /// Header:
632 /// RDCODE=NOERROR
633 /// Query:
634 /// AN.EXAMPLE. A
635 /// Answer:
636 /// AN.EXAMPLE. CNAME TRIPPLE.XX.
637 /// Authority:
638 /// XX. NS NS1.XX.
639 /// XX. NS NS2.XX.
640 /// Additional:
641 /// NS1.XX. A 127.0.0.2
642 /// NS2.XX. A 127.0.0.3
643 ///
644 /// REFERRAL RESPONSE.
645 ///
646 /// Header:
647 /// RDCODE=NOERROR
648 /// Query:
649 /// ANOTHER.EXAMPLE. A
650 /// Answer:
651 /// <empty>
652 /// Authority:
653 /// EXAMPLE. NS NS1.XX.
654 /// EXAMPLE. NS NS2.XX.
655 /// Additional:
656 /// NS1.XX. A 127.0.0.2
657 /// NS2.XX. A 127.0.0.3
658 /// ```
659 Referral,
660}
661
662impl NegativeType {
663 /// The response contains an SOA record
664 pub fn is_authoritative(&self) -> bool {
665 matches!(
666 self,
667 Self::NameErrorType1 | Self::NameErrorType2 | Self::NoDataType1 | Self::NoDataType2
668 )
669 }
670}
671
672#[cfg(test)]
673mod tests {
674 use crate::op::{Message, Query, ResponseCode};
675 use crate::rr::RData;
676 use crate::rr::rdata::{A, CNAME, NS, SOA};
677 use crate::rr::{Name, Record, RecordType};
678
679 use super::*;
680
681 fn xx() -> Name {
682 Name::from_ascii("XX.").unwrap()
683 }
684
685 fn ns1() -> Name {
686 Name::from_ascii("NS1.XX.").unwrap()
687 }
688
689 fn ns2() -> Name {
690 Name::from_ascii("NS1.XX.").unwrap()
691 }
692
693 fn hostmaster() -> Name {
694 Name::from_ascii("HOSTMASTER.NS1.XX.").unwrap()
695 }
696
697 fn tripple_xx() -> Name {
698 Name::from_ascii("TRIPPLE.XX.").unwrap()
699 }
700
701 fn example() -> Name {
702 Name::from_ascii("EXAMPLE.").unwrap()
703 }
704
705 fn an_example() -> Name {
706 Name::from_ascii("AN.EXAMPLE.").unwrap()
707 }
708
709 fn another_example() -> Name {
710 Name::from_ascii("ANOTHER.EXAMPLE.").unwrap()
711 }
712
713 fn an_cname_record() -> Record {
714 Record::from_rdata(an_example(), 88640, RData::CNAME(CNAME(tripple_xx())))
715 }
716
717 fn ns1_record() -> Record {
718 Record::from_rdata(xx(), 88640, RData::NS(NS(ns1())))
719 }
720
721 fn ns2_record() -> Record {
722 Record::from_rdata(xx(), 88640, RData::NS(NS(ns2())))
723 }
724
725 fn ns1_a() -> Record {
726 Record::from_rdata(xx(), 88640, RData::A(A::new(127, 0, 0, 2)))
727 }
728
729 fn ns2_a() -> Record {
730 Record::from_rdata(xx(), 88640, RData::A(A::new(127, 0, 0, 3)))
731 }
732
733 fn soa() -> Record {
734 Record::from_rdata(
735 example(),
736 88640,
737 RData::SOA(SOA::new(ns1(), hostmaster(), 1, 2, 3, 4, 5)),
738 )
739 }
740
741 fn an_query() -> Query {
742 Query::query(an_example(), RecordType::A)
743 }
744
745 fn another_query() -> Query {
746 Query::query(another_example(), RecordType::A)
747 }
748
749 #[test]
750 fn test_contains_answer() {
751 let mut message = Message::default();
752 message.set_response_code(ResponseCode::NXDomain);
753 message.add_query(Query::query(Name::root(), RecordType::A));
754 message.add_answer(Record::from_rdata(
755 Name::root(),
756 88640,
757 RData::A(A::new(127, 0, 0, 2)),
758 ));
759
760 let response = DnsResponse::from_message(message).unwrap();
761
762 assert!(response.contains_answer())
763 }
764
765 #[test]
766 fn test_nx_type1() {
767 let mut message = Message::default();
768 message.set_response_code(ResponseCode::NXDomain);
769 message.add_query(an_query());
770 message.add_answer(an_cname_record());
771 message.add_name_server(soa());
772 message.add_name_server(ns1_record());
773 message.add_name_server(ns2_record());
774 message.add_additional(ns1_a());
775 message.add_additional(ns2_a());
776
777 let response = DnsResponse::from_message(message).unwrap();
778 let ty = response.negative_type();
779
780 assert!(response.contains_answer());
781 assert_eq!(ty.unwrap(), NegativeType::NameErrorType1);
782 }
783
784 #[test]
785 fn test_nx_type2() {
786 let mut message = Message::default();
787 message.set_response_code(ResponseCode::NXDomain);
788 message.add_query(an_query());
789 message.add_answer(an_cname_record());
790 message.add_name_server(soa());
791
792 let response = DnsResponse::from_message(message).unwrap();
793 let ty = response.negative_type();
794
795 assert!(response.contains_answer());
796 assert_eq!(ty.unwrap(), NegativeType::NameErrorType2);
797 }
798
799 #[test]
800 fn test_nx_type3() {
801 let mut message = Message::default();
802 message.set_response_code(ResponseCode::NXDomain);
803 message.add_query(an_query());
804 message.add_answer(an_cname_record());
805
806 let response = DnsResponse::from_message(message).unwrap();
807 let ty = response.negative_type();
808
809 assert!(response.contains_answer());
810 assert_eq!(ty.unwrap(), NegativeType::NameErrorType3);
811 }
812
813 #[test]
814 fn test_nx_type4() {
815 let mut message = Message::default();
816 message.set_response_code(ResponseCode::NXDomain);
817 message.add_query(an_query());
818 message.add_answer(an_cname_record());
819 message.add_name_server(ns1_record());
820 message.add_name_server(ns2_record());
821 message.add_additional(ns1_a());
822 message.add_additional(ns2_a());
823
824 let response = DnsResponse::from_message(message).unwrap();
825 let ty = response.negative_type();
826
827 assert!(response.contains_answer());
828 assert_eq!(ty.unwrap(), NegativeType::NameErrorType4);
829 }
830
831 #[test]
832 fn test_no_data_type1() {
833 let mut message = Message::default();
834 message.set_response_code(ResponseCode::NoError);
835 message.add_query(another_query());
836 message.add_name_server(soa());
837 message.add_name_server(ns1_record());
838 message.add_name_server(ns2_record());
839 message.add_additional(ns1_a());
840 message.add_additional(ns2_a());
841 let response = DnsResponse::from_message(message).unwrap();
842 let ty = response.negative_type();
843
844 assert!(!response.contains_answer());
845 assert_eq!(ty.unwrap(), NegativeType::NoDataType1);
846 }
847
848 #[test]
849 fn test_no_data_type2() {
850 let mut message = Message::default();
851 message.set_response_code(ResponseCode::NoError);
852 message.add_query(another_query());
853 message.add_name_server(soa());
854
855 let response = DnsResponse::from_message(message).unwrap();
856 let ty = response.negative_type();
857
858 assert!(!response.contains_answer());
859 assert_eq!(ty.unwrap(), NegativeType::NoDataType2);
860 }
861
862 #[test]
863 fn test_no_data_type3() {
864 let mut message = Message::default();
865 message.set_response_code(ResponseCode::NoError);
866 message.add_query(another_query());
867
868 let response = DnsResponse::from_message(message).unwrap();
869 let ty = response.negative_type();
870
871 assert!(!response.contains_answer());
872 assert_eq!(ty.unwrap(), NegativeType::NoDataType3);
873 }
874
875 #[test]
876 fn referral() {
877 let mut message = Message::default();
878 message.set_response_code(ResponseCode::NoError);
879 message.add_query(an_query());
880 message.add_answer(an_cname_record());
881 message.add_name_server(ns1_record());
882 message.add_name_server(ns2_record());
883 message.add_additional(ns1_a());
884 message.add_additional(ns2_a());
885
886 let response = DnsResponse::from_message(message).unwrap();
887 let ty = response.negative_type();
888
889 assert!(response.contains_answer());
890 assert_eq!(ty.unwrap(), NegativeType::Referral);
891
892 let mut message = Message::default();
893 message.set_response_code(ResponseCode::NoError);
894 message.add_query(another_query());
895 message.add_name_server(ns1_record());
896 message.add_name_server(ns2_record());
897 message.add_additional(ns1_a());
898 message.add_additional(ns2_a());
899
900 let response = DnsResponse::from_message(message).unwrap();
901 let ty = response.negative_type();
902
903 assert!(!response.contains_answer());
904 assert_eq!(ty.unwrap(), NegativeType::Referral);
905 }
906
907 #[test]
908 fn contains_soa() {
909 let mut message = Message::default();
910 message.set_response_code(ResponseCode::NoError);
911 message.add_query(Query::query(an_example(), RecordType::SOA));
912 message.add_name_server(soa());
913
914 let response = DnsResponse::from_message(message).unwrap();
915
916 assert!(response.contains_answer());
917 }
918
919 #[test]
920 fn contains_any() {
921 let mut message = Message::default();
922 message.set_response_code(ResponseCode::NoError);
923 message.add_query(Query::query(xx(), RecordType::ANY));
924 message.add_name_server(ns1_record());
925 message.add_additional(ns1_a());
926
927 let response = DnsResponse::from_message(message).unwrap();
928
929 assert!(response.contains_answer());
930 }
931}