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}