trust_dns_proto/xfer/
dns_response.rs

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