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}