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