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}