blob: f9f8105f2c3e2ab844118b8dca16c2e71e825619 [file] [log] [blame]
// Copyright 2015-2018 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! `DnsResponse` wraps a `Message` and any associated connection details
use std::future::Future;
use std::io;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::task::{Context, Poll};
use futures_channel::mpsc;
use futures_util::ready;
use futures_util::stream::Stream;
use crate::error::{ProtoError, ProtoErrorKind, ProtoResult};
use crate::op::{Message, ResponseCode};
use crate::rr::rdata::SOA;
use crate::rr::{RData, RecordType};
/// A stream returning DNS responses
pub struct DnsResponseStream {
inner: DnsResponseStreamInner,
done: bool,
}
impl DnsResponseStream {
fn new(inner: DnsResponseStreamInner) -> Self {
Self { inner, done: false }
}
}
impl Stream for DnsResponseStream {
type Item = Result<DnsResponse, ProtoError>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
use DnsResponseStreamInner::*;
// if the standard futures are done, don't poll again
if self.done {
return Poll::Ready(None);
}
// split mutable refs to Self
let Self {
ref mut inner,
ref mut done,
} = *self.as_mut();
let result = match inner {
Timeout(fut) => {
let x = match ready!(fut.as_mut().poll(cx)) {
Ok(x) => x,
Err(e) => Err(e.into()),
};
*done = true;
x
}
Receiver(ref mut fut) => match ready!(Pin::new(fut).poll_next(cx)) {
Some(x) => x,
None => return Poll::Ready(None),
},
Error(err) => {
*done = true;
Err(err.take().expect("cannot poll after complete"))
}
Boxed(fut) => {
let x = ready!(fut.as_mut().poll(cx));
*done = true;
x
}
};
match result {
Err(e) if matches!(e.kind(), ProtoErrorKind::Timeout) => Poll::Ready(None),
r => Poll::Ready(Some(r)),
}
}
}
impl From<TimeoutFuture> for DnsResponseStream {
fn from(f: TimeoutFuture) -> Self {
Self::new(DnsResponseStreamInner::Timeout(f))
}
}
impl From<mpsc::Receiver<ProtoResult<DnsResponse>>> for DnsResponseStream {
fn from(receiver: mpsc::Receiver<ProtoResult<DnsResponse>>) -> Self {
Self::new(DnsResponseStreamInner::Receiver(receiver))
}
}
impl From<ProtoError> for DnsResponseStream {
fn from(e: ProtoError) -> Self {
Self::new(DnsResponseStreamInner::Error(Some(e)))
}
}
impl<F> From<Pin<Box<F>>> for DnsResponseStream
where
F: Future<Output = Result<DnsResponse, ProtoError>> + Send + 'static,
{
fn from(f: Pin<Box<F>>) -> Self {
Self::new(DnsResponseStreamInner::Boxed(
f as Pin<Box<dyn Future<Output = Result<DnsResponse, ProtoError>> + Send>>,
))
}
}
enum DnsResponseStreamInner {
Timeout(TimeoutFuture),
Receiver(mpsc::Receiver<ProtoResult<DnsResponse>>),
Error(Option<ProtoError>),
Boxed(Pin<Box<dyn Future<Output = Result<DnsResponse, ProtoError>> + Send>>),
}
type TimeoutFuture = Pin<
Box<dyn Future<Output = Result<Result<DnsResponse, ProtoError>, io::Error>> + Send + 'static>,
>;
// TODO: this needs to have the IP addr of the remote system...
// TODO: see https://github.com/bluejekyll/trust-dns/issues/383 for removing vec of messages and instead returning a Stream
/// A DNS response object
///
/// For Most DNS requests, only one response is expected, the exception is a multicast request.
#[derive(Clone, Debug)]
pub struct DnsResponse(Message);
// TODO: when `impl Trait` lands in stable, remove this, and expose FlatMap over answers, et al.
impl DnsResponse {
/// Retrieves the SOA from the response. This will only exist if it was an authoritative response.
pub fn soa(&self) -> Option<SOA> {
self.name_servers()
.iter()
.filter_map(|record| record.data().and_then(RData::as_soa))
.next()
.cloned()
}
/// Looks in the authority section for an SOA record from the response, and returns the negative_ttl, None if not available.
///
/// ```text
/// [RFC 2308](https://tools.ietf.org/html/rfc2308#section-5) DNS NCACHE March 1998
///
/// 5 - Caching Negative Answers
///
/// Like normal answers negative answers have a time to live (TTL). As
/// there is no record in the answer section to which this TTL can be
/// applied, the TTL must be carried by another method. This is done by
/// including the SOA record from the zone in the authority section of
/// the reply. When the authoritative server creates this record its TTL
/// is taken from the minimum of the SOA.MINIMUM field and SOA's TTL.
/// This TTL decrements in a similar manner to a normal cached answer and
/// upon reaching zero (0) indicates the cached negative answer MUST NOT
/// be used again.
///
/// A negative answer that resulted from a name error (NXDOMAIN) should
/// be cached such that it can be retrieved and returned in response to
/// another query for the same <QNAME, QCLASS> that resulted in the
/// cached negative response.
///
/// A negative answer that resulted from a no data error (NODATA) should
/// be cached such that it can be retrieved and returned in response to
/// another query for the same <QNAME, QTYPE, QCLASS> that resulted in
/// the cached negative response.
///
/// The NXT record, if it exists in the authority section of a negative
/// answer received, MUST be stored such that it can be be located and
/// returned with SOA record in the authority section, as should any SIG
/// records in the authority section. For NXDOMAIN answers there is no
/// "necessary" obvious relationship between the NXT records and the
/// QNAME. The NXT record MUST have the same owner name as the query
/// name for NODATA responses.
///
/// Negative responses without SOA records SHOULD NOT be cached as there
/// is no way to prevent the negative responses looping forever between a
/// pair of servers even with a short TTL.
///
/// Despite the DNS forming a tree of servers, with various mis-
/// configurations it is possible to form a loop in the query graph, e.g.
/// two servers listing each other as forwarders, various lame server
/// configurations. Without a TTL count down a cache negative response
/// when received by the next server would have its TTL reset. This
/// negative indication could then live forever circulating between the
/// servers involved.
///
/// As with caching positive responses it is sensible for a resolver to
/// limit for how long it will cache a negative response as the protocol
/// supports caching for up to 68 years. Such a limit should not be
/// greater than that applied to positive answers and preferably be
/// tunable. Values of one to three hours have been found to work well
/// and would make sensible a default. Values exceeding one day have
/// been found to be problematic.
/// ```
pub fn negative_ttl(&self) -> Option<u32> {
// TODO: should this ensure that the SOA zone matches the Queried Zone?
self.name_servers()
.iter()
.filter_map(|record| {
record
.data()
.and_then(RData::as_soa)
.map(|soa| (record.ttl(), soa))
})
.next()
.map(|(ttl, soa)| (ttl as u32).min(soa.minimum()).max(0))
}
/// Does the response contain any records matching the query name and type?
pub fn contains_answer(&self) -> bool {
for q in self.queries() {
let found = match q.query_type() {
RecordType::ANY => self.all_sections().any(|r| r.name() == q.name()),
RecordType::SOA => {
// for SOA name must be part of the SOA zone
self.all_sections()
.filter(|r| r.record_type().is_soa())
.any(|r| r.name().zone_of(q.name()))
}
q_type => {
if !self.answers().is_empty() {
true
} else {
self.all_sections()
.filter(|r| r.record_type() == q_type)
.any(|r| r.name() == q.name())
}
}
};
if found {
return true;
}
}
false
}
/// Retrieve the type of the negative response.
/// The Various types should be handled when caching or otherwise differently.
///
/// See [NegativeType]
pub fn negative_type(&self) -> Option<NegativeType> {
let response_code = self.response_code();
let ttl_from_soa = self.negative_ttl();
let has_soa = ttl_from_soa.map_or(false, |_| true);
let has_ns_records = self.name_servers().iter().any(|r| r.record_type().is_ns());
let has_cname = self.answers().iter().any(|r| r.record_type().is_cname());
let has_non_cname = self.answers().iter().any(|r| !r.record_type().is_cname());
let has_additionals = self.additional_count() > 0;
match (
response_code,
has_soa,
has_ns_records,
has_cname,
has_non_cname,
has_additionals,
) {
(ResponseCode::NXDomain, true, true, _, false, _) => Some(NegativeType::NameErrorType1),
(ResponseCode::NXDomain, true, false, _, false, _) => {
Some(NegativeType::NameErrorType2)
}
(ResponseCode::NXDomain, false, false, true, false, _) => {
Some(NegativeType::NameErrorType3)
}
(ResponseCode::NXDomain, false, true, _, false, _) => {
Some(NegativeType::NameErrorType4)
}
(ResponseCode::NoError, true, true, false, false, _) => Some(NegativeType::NoDataType1),
(ResponseCode::NoError, true, false, false, false, _) => {
Some(NegativeType::NoDataType2)
}
(ResponseCode::NoError, false, false, false, false, false) => {
Some(NegativeType::NoDataType3)
}
(ResponseCode::NoError, false, true, _, false, _) => Some(NegativeType::Referral),
_ => None,
}
}
}
impl Deref for DnsResponse {
type Target = Message;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for DnsResponse {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<DnsResponse> for Message {
fn from(response: DnsResponse) -> Self {
response.0
}
}
impl From<Message> for DnsResponse {
fn from(message: Message) -> Self {
Self(message)
}
}
/// ```text
/// [RFC 2308](https://tools.ietf.org/html/rfc2308#section-2) DNS NCACHE March 1998
///
///
/// 2 - Negative Responses
///
/// The most common negative responses indicate that a particular RRset
/// does not exist in the DNS. The first sections of this document deal
/// with this case. Other negative responses can indicate failures of a
/// nameserver, those are dealt with in section 7 (Other Negative
/// Responses).
///
/// A negative response is indicated by one of the following conditions:
///
/// 2.1 - Name Error
///
/// Name errors (NXDOMAIN) are indicated by the presence of "Name Error"
/// in the RCODE field. In this case the domain referred to by the QNAME
/// does not exist. Note: the answer section may have SIG and CNAME RRs
/// and the authority section may have SOA, NXT [RFC2065] and SIG RRsets.
///
/// It is possible to distinguish between a referral and a NXDOMAIN
/// response by the presense of NXDOMAIN in the RCODE regardless of the
/// presence of NS or SOA records in the authority section.
///
/// NXDOMAIN responses can be categorised into four types by the contents
/// of the authority section. These are shown below along with a
/// referral for comparison. Fields not mentioned are not important in
/// terms of the examples.
///
/// See [NegativeType] below:
/// [NegativeType::NameErrorType1]
/// [NegativeType::NameErrorType2]
/// [NegativeType::NameErrorType3]
/// [NegativeType::NameErrorType4]
/// [NegativeType::Referral]
///
/// Note, in the four examples of NXDOMAIN responses, it is known that
/// the name "AN.EXAMPLE." exists, and has as its value a CNAME record.
/// The NXDOMAIN refers to "TRIPPLE.XX", which is then known not to
/// exist. On the other hand, in the referral example, it is shown that
/// "AN.EXAMPLE" exists, and has a CNAME RR as its value, but nothing is
/// known one way or the other about the existence of "TRIPPLE.XX", other
/// than that "NS1.XX" or "NS2.XX" can be consulted as the next step in
/// obtaining information about it.
///
/// Where no CNAME records appear, the NXDOMAIN response refers to the
/// name in the label of the RR in the question section.
///
/// 2.1.1 Special Handling of Name Error
///
/// This section deals with errors encountered when implementing negative
/// caching of NXDOMAIN responses.
///
/// There are a large number of resolvers currently in existence that
/// fail to correctly detect and process all forms of NXDOMAIN response.
/// Some resolvers treat a TYPE 1 NXDOMAIN response as a referral. To
/// alleviate this problem it is recommended that servers that are
/// authoritative for the NXDOMAIN response only send TYPE 2 NXDOMAIN
/// responses, that is the authority section contains a SOA record and no
/// NS records. If a non- authoritative server sends a type 1 NXDOMAIN
/// response to one of these old resolvers, the result will be an
/// unnecessary query to an authoritative server. This is undesirable,
/// but not fatal except when the server is being used a FORWARDER. If
/// however the resolver is using the server as a FORWARDER to such a
/// resolver it will be necessary to disable the sending of TYPE 1
/// NXDOMAIN response to it, use TYPE 2 NXDOMAIN instead.
///
/// Some resolvers incorrectly continue processing if the authoritative
/// answer flag is not set, looping until the query retry threshold is
/// exceeded and then returning SERVFAIL. This is a problem when your
/// nameserver is listed as a FORWARDER for such resolvers. If the
/// nameserver is used as a FORWARDER by such resolver, the authority
/// flag will have to be forced on for NXDOMAIN responses to these
/// resolvers. In practice this causes no problems even if turned on
/// always, and has been the default behaviour in BIND from 4.9.3
/// onwards.
///
/// 2.2 - No Data
///
/// NODATA is indicated by an answer with the RCODE set to NOERROR and no
/// relevant answers in the answer section. The authority section will
/// contain an SOA record, or there will be no NS records there.
/// NODATA responses have to be algorithmically determined from the
/// response's contents as there is no RCODE value to indicate NODATA.
/// In some cases to determine with certainty that NODATA is the correct
/// response it can be necessary to send another query.
///
/// The authority section may contain NXT and SIG RRsets in addition to
/// NS and SOA records. CNAME and SIG records may exist in the answer
/// section.
///
/// It is possible to distinguish between a NODATA and a referral
/// response by the presence of a SOA record in the authority section or
/// the absence of NS records in the authority section.
///
/// NODATA responses can be categorised into three types by the contents
/// of the authority section. These are shown below along with a
/// referral for comparison. Fields not mentioned are not important in
/// terms of the examples.
///
/// See [NegativeType] below:
/// [NegativeType::NoDataType1]
/// [NegativeType::NoDataType2]
/// [NegativeType::NoDataType3]
///
/// These examples, unlike the NXDOMAIN examples above, have no CNAME
/// records, however they could, in just the same way that the NXDOMAIN
/// examples did, in which case it would be the value of the last CNAME
/// (the QNAME) for which NODATA would be concluded.
///
/// 2.2.1 - Special Handling of No Data
///
/// There are a large number of resolvers currently in existence that
/// fail to correctly detect and process all forms of NODATA response.
/// Some resolvers treat a TYPE 1 NODATA response as a referral. To
/// alleviate this problem it is recommended that servers that are
/// authoritative for the NODATA response only send TYPE 2 NODATA
/// responses, that is the authority section contains a SOA record and no
/// NS records. Sending a TYPE 1 NODATA response from a non-
/// authoritative server to one of these resolvers will only result in an
/// unnecessary query. If a server is listed as a FORWARDER for another
/// resolver it may also be necessary to disable the sending of TYPE 1
/// NODATA response for non-authoritative NODATA responses.
/// Some name servers fail to set the RCODE to NXDOMAIN in the presence
/// of CNAMEs in the answer section. If a definitive NXDOMAIN / NODATA
/// answer is required in this case the resolver must query again using
/// the QNAME as the query label.
///
/// 3 - Negative Answers from Authoritative Servers
///
/// Name servers authoritative for a zone MUST include the SOA record of
/// the zone in the authority section of the response when reporting an
/// NXDOMAIN or indicating that no data of the requested type exists.
/// This is required so that the response may be cached. The TTL of this
/// record is set from the minimum of the MINIMUM field of the SOA record
/// and the TTL of the SOA itself, and indicates how long a resolver may
/// cache the negative answer. The TTL SIG record associated with the
/// SOA record should also be trimmed in line with the SOA's TTL.
///
/// If the containing zone is signed [RFC2065] the SOA and appropriate
/// NXT and SIG records MUST be added.
///
/// ```
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum NegativeType {
/// ```text
/// NXDOMAIN RESPONSE: TYPE 1.
///
/// Header:
/// RDCODE=NXDOMAIN
/// Query:
/// AN.EXAMPLE. A
/// Answer:
/// AN.EXAMPLE. CNAME TRIPPLE.XX.
/// Authority:
/// XX. SOA NS1.XX. HOSTMASTER.NS1.XX. ....
/// XX. NS NS1.XX.
/// XX. NS NS2.XX.
/// Additional:
/// NS1.XX. A 127.0.0.2
/// NS2.XX. A 127.0.0.3
/// ```
NameErrorType1,
/// ```text
/// NXDOMAIN RESPONSE: TYPE 2.
///
/// Header:
/// RDCODE=NXDOMAIN
/// Query:
/// AN.EXAMPLE. A
/// Answer:
/// AN.EXAMPLE. CNAME TRIPPLE.XX.
/// Authority:
/// XX. SOA NS1.XX. HOSTMASTER.NS1.XX. ....
/// Additional:
/// <empty>
/// ```
NameErrorType2,
/// ```text
/// NXDOMAIN RESPONSE: TYPE 3.
///
/// Header:
/// RDCODE=NXDOMAIN
/// Query:
/// AN.EXAMPLE. A
/// Answer:
/// AN.EXAMPLE. CNAME TRIPPLE.XX.
/// Authority:
/// <empty>
/// Additional:
/// <empty>
/// ```
NameErrorType3,
/// ```text
/// NXDOMAIN RESPONSE: TYPE 4
///
/// Header:
/// RDCODE=NXDOMAIN
/// Query:
/// AN.EXAMPLE. A
/// Answer:
/// AN.EXAMPLE. CNAME TRIPPLE.XX.
/// Authority:
/// XX. NS NS1.XX.
/// XX. NS NS2.XX.
/// Additional:
/// NS1.XX. A 127.0.0.2
/// NS2.XX. A 127.0.0.3
/// ```
NameErrorType4,
/// ```text
/// NODATA RESPONSE: TYPE 1.
///
/// Header:
/// RDCODE=NOERROR
/// Query:
/// ANOTHER.EXAMPLE. A
/// Answer:
/// <empty>
/// Authority:
/// EXAMPLE. SOA NS1.XX. HOSTMASTER.NS1.XX. ....
/// EXAMPLE. NS NS1.XX.
/// EXAMPLE. NS NS2.XX.
/// Additional:
/// NS1.XX. A 127.0.0.2
/// NS2.XX. A 127.0.0.3
/// ```
NoDataType1,
/// ```text
/// NO DATA RESPONSE: TYPE 2.
///
/// Header:
/// RDCODE=NOERROR
/// Query:
/// ANOTHER.EXAMPLE. A
/// Answer:
/// <empty>
/// Authority:
/// EXAMPLE. SOA NS1.XX. HOSTMASTER.NS1.XX. ....
/// Additional:
/// <empty>
/// ```
NoDataType2,
/// ```text
/// NO DATA RESPONSE: TYPE 3.
/// Header:
/// RDCODE=NOERROR
/// Query:
/// ANOTHER.EXAMPLE. A
/// Answer:
/// <empty>
/// Authority:
/// <empty>
/// Additional:
/// <empty>
/// ```
NoDataType3,
/// ```text
/// REFERRAL RESPONSE.
///
/// Header:
/// RDCODE=NOERROR
/// Query:
/// AN.EXAMPLE. A
/// Answer:
/// AN.EXAMPLE. CNAME TRIPPLE.XX.
/// Authority:
/// XX. NS NS1.XX.
/// XX. NS NS2.XX.
/// Additional:
/// NS1.XX. A 127.0.0.2
/// NS2.XX. A 127.0.0.3
///
/// REFERRAL RESPONSE.
///
/// Header:
/// RDCODE=NOERROR
/// Query:
/// ANOTHER.EXAMPLE. A
/// Answer:
/// <empty>
/// Authority:
/// EXAMPLE. NS NS1.XX.
/// EXAMPLE. NS NS2.XX.
/// Additional:
/// NS1.XX. A 127.0.0.2
/// NS2.XX. A 127.0.0.3
/// ```
Referral,
}
impl NegativeType {
/// The response contains an SOA record
pub fn is_authoritative(&self) -> bool {
matches!(
self,
NegativeType::NameErrorType1
| NegativeType::NameErrorType2
| NegativeType::NoDataType1
| NegativeType::NoDataType2
)
}
}
#[cfg(test)]
mod tests {
use crate::op::{Message, Query, ResponseCode};
use crate::rr::rdata::SOA;
use crate::rr::RData;
use crate::rr::{Name, Record, RecordType};
use super::*;
fn xx() -> Name {
Name::from_ascii("XX.").unwrap()
}
fn ns1() -> Name {
Name::from_ascii("NS1.XX.").unwrap()
}
fn ns2() -> Name {
Name::from_ascii("NS1.XX.").unwrap()
}
fn hostmaster() -> Name {
Name::from_ascii("HOSTMASTER.NS1.XX.").unwrap()
}
fn tripple_xx() -> Name {
Name::from_ascii("TRIPPLE.XX.").unwrap()
}
fn example() -> Name {
Name::from_ascii("EXAMPLE.").unwrap()
}
fn an_example() -> Name {
Name::from_ascii("AN.EXAMPLE.").unwrap()
}
fn another_example() -> Name {
Name::from_ascii("ANOTHER.EXAMPLE.").unwrap()
}
fn an_cname_record() -> Record {
Record::from_rdata(an_example(), 88640, RData::CNAME(tripple_xx()))
}
fn ns1_record() -> Record {
Record::from_rdata(xx(), 88640, RData::NS(ns1()))
}
fn ns2_record() -> Record {
Record::from_rdata(xx(), 88640, RData::NS(ns2()))
}
fn ns1_a() -> Record {
Record::from_rdata(xx(), 88640, RData::A([127, 0, 0, 2].into()))
}
fn ns2_a() -> Record {
Record::from_rdata(xx(), 88640, RData::A([127, 0, 0, 3].into()))
}
fn soa() -> Record {
Record::from_rdata(
example(),
88640,
RData::SOA(SOA::new(ns1(), hostmaster(), 1, 2, 3, 4, 5)),
)
}
fn an_query() -> Query {
Query::query(an_example(), RecordType::A)
}
fn another_query() -> Query {
Query::query(another_example(), RecordType::A)
}
#[test]
fn test_contains_answer() {
let mut message = Message::default();
message.set_response_code(ResponseCode::NXDomain);
message.add_query(Query::query(Name::root(), RecordType::A));
message.add_answer(Record::from_rdata(
Name::root(),
88640,
RData::A([127, 0, 0, 2].into()),
));
let response = DnsResponse::from(message);
assert!(response.contains_answer())
}
#[test]
fn test_nx_type1() {
let mut message = Message::default();
message.set_response_code(ResponseCode::NXDomain);
message.add_query(an_query());
message.add_answer(an_cname_record());
message.add_name_server(soa());
message.add_name_server(ns1_record());
message.add_name_server(ns2_record());
message.add_additional(ns1_a());
message.add_additional(ns2_a());
let response = DnsResponse::from(message);
let ty = response.negative_type();
assert!(response.contains_answer());
assert_eq!(ty.unwrap(), NegativeType::NameErrorType1);
}
#[test]
fn test_nx_type2() {
let mut message = Message::default();
message.set_response_code(ResponseCode::NXDomain);
message.add_query(an_query());
message.add_answer(an_cname_record());
message.add_name_server(soa());
let response = DnsResponse::from(message);
let ty = response.negative_type();
assert!(response.contains_answer());
assert_eq!(ty.unwrap(), NegativeType::NameErrorType2);
}
#[test]
fn test_nx_type3() {
let mut message = Message::default();
message.set_response_code(ResponseCode::NXDomain);
message.add_query(an_query());
message.add_answer(an_cname_record());
let response = DnsResponse::from(message);
let ty = response.negative_type();
assert!(response.contains_answer());
assert_eq!(ty.unwrap(), NegativeType::NameErrorType3);
}
#[test]
fn test_nx_type4() {
let mut message = Message::default();
message.set_response_code(ResponseCode::NXDomain);
message.add_query(an_query());
message.add_answer(an_cname_record());
message.add_name_server(ns1_record());
message.add_name_server(ns2_record());
message.add_additional(ns1_a());
message.add_additional(ns2_a());
let response = DnsResponse::from(message);
let ty = response.negative_type();
assert!(response.contains_answer());
assert_eq!(ty.unwrap(), NegativeType::NameErrorType4);
}
#[test]
fn test_no_data_type1() {
let mut message = Message::default();
message.set_response_code(ResponseCode::NoError);
message.add_query(another_query());
message.add_name_server(soa());
message.add_name_server(ns1_record());
message.add_name_server(ns2_record());
message.add_additional(ns1_a());
message.add_additional(ns2_a());
let response = DnsResponse::from(message);
let ty = response.negative_type();
assert!(!response.contains_answer());
assert_eq!(ty.unwrap(), NegativeType::NoDataType1);
}
#[test]
fn test_no_data_type2() {
let mut message = Message::default();
message.set_response_code(ResponseCode::NoError);
message.add_query(another_query());
message.add_name_server(soa());
let response = DnsResponse::from(message);
let ty = response.negative_type();
assert!(!response.contains_answer());
assert_eq!(ty.unwrap(), NegativeType::NoDataType2);
}
#[test]
fn test_no_data_type3() {
let mut message = Message::default();
message.set_response_code(ResponseCode::NoError);
message.add_query(another_query());
let response = DnsResponse::from(message);
let ty = response.negative_type();
assert!(!response.contains_answer());
assert_eq!(ty.unwrap(), NegativeType::NoDataType3);
}
#[test]
fn referral() {
let mut message = Message::default();
message.set_response_code(ResponseCode::NoError);
message.add_query(an_query());
message.add_answer(an_cname_record());
message.add_name_server(ns1_record());
message.add_name_server(ns2_record());
message.add_additional(ns1_a());
message.add_additional(ns2_a());
let response = DnsResponse::from(message);
let ty = response.negative_type();
assert!(response.contains_answer());
assert_eq!(ty.unwrap(), NegativeType::Referral);
let mut message = Message::default();
message.set_response_code(ResponseCode::NoError);
message.add_query(another_query());
message.add_name_server(ns1_record());
message.add_name_server(ns2_record());
message.add_additional(ns1_a());
message.add_additional(ns2_a());
let response = DnsResponse::from(message);
let ty = response.negative_type();
assert!(!response.contains_answer());
assert_eq!(ty.unwrap(), NegativeType::Referral);
}
#[test]
fn contains_soa() {
let mut message = Message::default();
message.set_response_code(ResponseCode::NoError);
message.add_query(Query::query(an_example(), RecordType::SOA));
message.add_name_server(soa());
let response = DnsResponse::from(message);
assert!(response.contains_answer());
}
#[test]
fn contains_any() {
let mut message = Message::default();
message.set_response_code(ResponseCode::NoError);
message.add_query(Query::query(xx(), RecordType::ANY));
message.add_name_server(ns1_record());
message.add_additional(ns1_a());
let response = DnsResponse::from(message);
assert!(response.contains_answer());
}
}