diff --git a/Cargo.toml b/Cargo.toml index 46b3a6b0..0d659423 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ bytes = { version = "1.0", optional = true, default-features = false } chrono = { version = "0.4.6", optional = true, default-features = false } futures-util = { version = "0.3", optional = true } heapless = { version = "0.8", optional = true } +moka = { version = "0.12.3", optional = true, features = ["future"] } #openssl = { version = "0.10", optional = true } ring = { version = "0.17", optional = true } serde = { version = "1.0.130", optional = true, features = ["derive"] } @@ -57,15 +58,17 @@ validate = ["std", "ring"] zonefile = ["bytes", "std"] # Unstable features -unstable-client-transport = [] +unstable-client-transport = [ "moka" ] [dev-dependencies] +rstest = "0.18.2" rustls = { version = "0.21.9" } serde_test = "1.0.130" serde_json = "1.0.113" serde_yaml = "0.9" tokio = { version = "1", features = ["rt-multi-thread", "io-util", "net"] } tokio-test = "0.4" +tracing = "0.1.40" webpki-roots = { version = "0.25" } [package.metadata.docs.rs] @@ -94,4 +97,4 @@ required-features = ["std", "rand"] [[example]] name = "client-transports" -required-features = ["net"] +required-features = ["net", "unstable-client-transport"] diff --git a/examples/client-transports.rs b/examples/client-transports.rs index acda31f9..056c3927 100644 --- a/examples/client-transports.rs +++ b/examples/client-transports.rs @@ -2,6 +2,7 @@ use domain::base::Dname; use domain::base::MessageBuilder; use domain::base::Rtype::Aaaa; +use domain::net::client::cache; use domain::net::client::dgram; use domain::net::client::dgram_stream; use domain::net::client::multi_stream; @@ -81,6 +82,28 @@ async fn main() { // when it is no longer needed. drop(request); + // Create a cached transport. + let mut cache_config = cache::Config::new(); + cache_config.set_max_cache_entries(100); // Just an example. + let cache = + cache::Connection::with_config(udptcp_conn.clone(), cache_config); + + // Send a request message. + let mut request = cache.send_request(req.clone()); + + // Get the reply + println!("Wating for cache reply"); + let reply = request.get_response().await; + println!("Cache reply: {:?}", reply); + + // Send the request message again. + let mut request = cache.send_request(req.clone()); + + // Get the reply + println!("Wating for cached reply"); + let reply = request.get_response().await; + println!("Cached reply: {:?}", reply); + // Create a new TCP connections object. Pass the destination address and // port as parameter. let tcp_connect = TcpConnect::new(server_addr); diff --git a/src/base/message.rs b/src/base/message.rs index 5ac2a4cc..add7d148 100644 --- a/src/base/message.rs +++ b/src/base/message.rs @@ -11,7 +11,7 @@ //! [`Message`]: struct.Message.html use super::header::{Header, HeaderCounts, HeaderSection}; -use super::iana::{Class, Rcode, Rtype}; +use super::iana::{Class, OptRcode, Rcode, Rtype}; use super::message_builder::{AdditionalBuilder, AnswerBuilder, PushError}; use super::name::ParsedDname; use super::opt::{Opt, OptRecord}; @@ -634,6 +634,14 @@ impl Message { Ok(target) } + + /// Get the extended rcode of a message or the normal rcode converted + /// to an extended rcode if no opt record is present. + pub fn opt_rcode(&self) -> OptRcode { + self.opt() + .map(|opt| opt.rcode(self.header())) + .unwrap_or_else(|| self.header().rcode().into()) + } } //--- AsRef diff --git a/src/base/opt/mod.rs b/src/base/opt/mod.rs index fa80333d..648e2055 100644 --- a/src/base/opt/mod.rs +++ b/src/base/opt/mod.rs @@ -564,6 +564,14 @@ impl OptRecord { self.flags & 0x8000 != 0 } + pub fn set_dnssec_ok(&mut self, value: bool) { + if value { + self.flags |= 0x8000; + } else { + self.flags &= !0x8000; + } + } + /// Returns a reference to the raw options. pub fn opt(&self) -> &Opt { &self.data diff --git a/src/net/client/cache.rs b/src/net/client/cache.rs new file mode 100644 index 00000000..70bb41c4 --- /dev/null +++ b/src/net/client/cache.rs @@ -0,0 +1,1292 @@ +//! A client cache. +//! +//! This module implements a simple message cache provided as a pass through +//! transport. The cache works with any of the other transports. +//! The basic operation is that from a request the query name, class, and type +//! are extracted and the result is cached such that when a new request +//! arrives with the same name, class, and type then the cached response can +//! be returned with the TTL values of the DNS resource records reduced by +//! the amount of time the message has been cached. +//! +//! The response to a query is in general affected by four flags: the +//! AD, CD, DO, and RD flags. +//! These flags are defined in the following RFCs: +//! [RFC 1035](https://www.rfc-editor.org/info/rfc1035), +//! [RFC 2535](https://www.rfc-editor.org/info/rfc2535), +//! [RFC 3225](https://www.rfc-editor.org/info/rfc3225), +//! [RFC 4035](https://www.rfc-editor.org/info/rfc4035), +//! [RFC 6840](https://www.rfc-editor.org/info/rfc6840). +//! The cache takes these flags into account to +//! see if a cached response can be returned. In some cases, a cached response +//! with one set of flags can be made suitable for a query with different +//! flags. +//! +//! The [Config] object provides various configuration options, such as +//! the maximum number of cache entries, how long different types of +//! responses should be cached and whether truncated responses should be cached +//! or not. + +#![warn(missing_docs)] +#![warn(clippy::missing_docs_in_private_items)] + +use crate::base::iana::{Class, Opcode, OptRcode, Rtype}; +use crate::base::name::ToDname; +use crate::base::{ + Dname, Header, Message, MessageBuilder, ParsedDname, StaticCompressor, + Ttl, +}; +use crate::dep::octseq::{octets::OctetsInto, Octets}; +use crate::net::client::clock::{Clock, Elapsed, SystemClock}; +use crate::net::client::request::{ + ComposeRequest, Error, GetResponse, SendRequest, +}; +use crate::rdata::AllRecordData; +use crate::utils::config::DefMinMax; +use bytes::Bytes; +use moka::future::Cache; +use std::boxed::Box; +use std::cmp::min; +use std::fmt::{Debug, Formatter}; +use std::future::Future; +use std::pin::Pin; +use std::sync::Arc; +use std::time::Duration; +use std::vec::Vec; + +/// Configuration limit for the maximum number of entries in the cache. +const MAX_CACHE_ENTRIES: DefMinMax = + DefMinMax::new(1_000, 1, 1_000_000_000); + +/// Limit on the maximum time a cache entry is considered valid. +/// +/// According to [RFC 8767](https://www.rfc-editor.org/info/rfc8767) the +/// limit should be on the order of days to weeks with a recommended cap of +/// 604800 seconds (7 days). +const MAX_VALIDITY: DefMinMax = DefMinMax::new( + Duration::from_secs(604800), + Duration::from_secs(60), + Duration::from_secs(6048000), +); + +/// Amount of time to cache transport failures. +/// +/// According to [RFC 9520](https://www.rfc-editor.org/info/rfc9520) +/// at least 1 second and at most 5 minutes. +const TRANSPORT_FAILURE_DURATION: DefMinMax = DefMinMax::new( + Duration::from_secs(30), + Duration::from_secs(1), + Duration::from_secs(5 * 60), +); + +/// Limit on the amount of time to cache DNS result codes that are not +/// NOERROR or NXDOMAIN. +/// +/// According to [RFC 9520](https://www.rfc-editor.org/info/rfc9520) +/// at least 1 second and at most 5 minutes. +const MISC_ERROR_DURATION: DefMinMax = DefMinMax::new( + Duration::from_secs(30), + Duration::from_secs(1), + Duration::from_secs(5 * 60), +); + +/// Limit on the amount of time to cache a NXDOMAIN error. +/// +/// According to [RFC 2308](https://www.rfc-editor.org/info/rfc2308) +/// the limit should be one to three hours with a maximum of one day. +const MAX_NXDOMAIN_VALIDITY: DefMinMax = DefMinMax::new( + Duration::from_secs(3600), + Duration::from_secs(60), + Duration::from_secs(24 * 3600), +); + +/// Limit on the amount of time to cache a NODATA response. +/// +/// According to [RFC 2308](https://www.rfc-editor.org/info/rfc2308) +/// the limit should be one to three hours with a maximum of one day. +const MAX_NODATA_VALIDITY: DefMinMax = DefMinMax::new( + Duration::from_secs(3600), + Duration::from_secs(60), + Duration::from_secs(24 * 3600), +); + +/// Limit on the amount of time a delegation is considered valid. +const MAX_DELEGATION_VALIDITY: DefMinMax = DefMinMax::new( + Duration::from_secs(1_000_000), + Duration::from_secs(60), + Duration::from_secs(1_000_000_000), +); + +// The following four flags are relevant to caching: AD, CD, DO, and RD. +// The RD flag is defined in RFC 1035 +// (https://www.rfc-editor.org/info/rfc1035) Section 4.1.1. +// The AD and CD flags are defined in RFC 2535 +// (https://www.rfc-editor.org/info/rfc2535) Section 6.1. However the +// meaning of those flags has been redefined in RFC 4035 +// (https://www.rfc-editor.org/info/rfc4035). With another update for the +// AD flag in RFC 6840 (https://www.rfc-editor.org/info/rfc6840) +// Sections 5.7 and 5.8. +// The DO flag is defined in RFC 3225 +// (https://www.rfc-editor.org/info/rfc3225) Section 3. +// +// The AD flag needs to be part of the key when DO is clear. When replying, +// if both AD and DO are not set in the original request then AD needs to be +// cleared if it was set in the response (extra look up if no entry with +// AD clear exists). +// +// The CD flag partitions the cache, responses to request with CD set must not +// be visible to requests with CD clear and vice versa. +// +// A request with DO set can only be satisfied with a response to a request +// with DO set. However, if DO in the request is clear then a response to a +// request with DO set can be used if all unrequested DNSSEC records are +// stripped. +// +// A request with RD clear can be satisfied by a response to a request with +// RD set. For simplicitly requests with RD set will only get a cached +// response to another request with RD set. In theory some responses to +// requests with RD clear could be used to satisfy requests with RD set. +// However, this is not implemented. + +// Caching the result of a query for a wildcard record seems to disallowed +// by Section 4.3.3 of RFC 1034 (https://www.rfc-editor.org/info/rfc1034) +// which says: +// A * label appearing in a query name has no special effect, but can be +// used to test for wildcards in an authoritative zone; such a query is the +// only way to get a response containing RRs with an owner name with * in +// it. The result of such a query should not be cached. +// +// However Erratum #5316 (https://www.rfc-editor.org/errata/eid5316) fixes +// this by replacing the word 'cached' with 'used to synthesize RRs' + +// Negative caching is described in RFC 2308 +// (https://www.rfc-editor.org/info/rfc2308). +// NXDOMAIN and NODATA require special treatment. NXDOMAIN can be found +// directly in the rcode field. NODATA is the condition where the answer +// section does not contain any record that matches qtype and the message +// is not a referral. NODATA is distinguished from a referral by the presence +// of a SOA record in the authority section (a SOA record present implies +// NODATA). A referral has one or more NS records in the authority section. +// An NXDOMAIN response can only be cached if a SOA record is present in the +// authority section. If the SOA record is absent then the NXDOMAIN response +// should not be cached. +// The TTL of the SOA record should reflect how long the response can be +// cached. Section 3 of the RFC requires authoritative servers to limit the +// TTL of the SOA record in negative responses to the minimum of the MINIUM +// field in the SOA record and the original TTL of the SOA record. For this +// reason, no special treatment is needed. Except that a different value +// should limit the maximum time a negative response can be cached. +// +// Caching unreachable upstream should be limited to 5 minutes. +// Caching SERVFAIL should be limited to 5 minutes. + +// Truncated responses require special treatment. RFC 1035, Section 7.4 +// (https://www.rfc-editor.org/info/rfc1035) warns against potentially +// caching partial sets of resource records. However, because this is a +// message cache, the users of the cache still has to decide what to do +// with a truncated response and there is no risk of using cached +// resource records in a different context. +// The issue is made more complex by the introduction of the UDP payload +// size field in RFC 6891, Section 6.1.2 +// (https://www.rfc-editor.org/info/rfc6891). +// This means that a later request with a larger value UDP payload size might +// get an answer that is not truncated. However the complexity of keeping +// track of the UDP payload size in the cache does not seem worth it for the +// following reasons: +// 1) truncated responses are returned by the dgram transport but we expect +// that the dgram_stream transport will be commonly used. So we expect +// very little actual caching of truncated responses. +// 2) To avoid fragmentation, servers are likely to have their own limits on +// the size of replies they send. So a higher UDP payload size may not have +// an effect. +// 3) It is likely that applications have one UDP payload size and do not +// issue the same query with different UDP payload sizes. +// For these reasons, the default is that truncated responses are not cached. +// A configuration option is provided (set_cache_truncated) that enables +// caching of truncated responses without taking into account the UDP payload +// size. + +// RFC 8020 (https://www.rfc-editor.org/info/rfc8020) suggests a separate +// cache for NXDOMAIN, but that may be too hard to implement. + +// RFC 9520 (https://www.rfc-editor.org/info/rfc9520) requires resolution +// failures to be cached for at least one second. Resolution failure must +// not be cached for longer than 5 minutes. + +// RFC 8767 (https://www.rfc-editor.org/info/rfc8767) describes serving stale +// data. + +//------------ Config --------------------------------------------------------- + +/// Configuration of a cache. +#[derive(Clone, Debug)] +pub struct Config { + /// Maximum number of cache entries. + max_cache_entries: u64, + + /// Maximum validity of a normal result. + max_validity: Duration, + + /// Cache duration of transport failures. + transport_failure_duration: Duration, + + /// Cache durations of misc. errors. (not NXDOMAIN or NOERROR) + misc_error_duration: Duration, + + /// Maximum validity of NXDOMAIN results. + max_nxdomain_validity: Duration, + + /// Maximum validity of NODATA results. + max_nodata_validity: Duration, + + /// Maximum validity of delegations. + max_delegation_validity: Duration, + + /// Whether to cache a truncated response or not. + cache_truncated: bool, +} + +impl Config { + /// Creates a new config with default values. + /// + /// The default values are documented at the relevant set_* methods. + pub fn new() -> Self { + Default::default() + } + + /// Set the maximum number of cache entries. + /// + /// The value has to be at least one, at most 1,000,000,000 and the + /// default is 1000. + /// + /// The values are just best guesses at the moment. The upper limit is + /// set to be somewhat safe without being too limiting. The default is + /// meant to be reasonable for a small system. + pub fn set_max_cache_entries(&mut self, value: u64) { + self.max_cache_entries = MAX_CACHE_ENTRIES.limit(value) + } + + /// Set the maximum validity of cache entries. + /// + /// The value has to be at least 60 seconds, at most 6,048,000 seconds + /// (10 weeks) and the default is 604800 seconds (one week). + pub fn set_max_validity(&mut self, value: Duration) { + self.max_validity = MAX_VALIDITY.limit(value) + } + + /// Set the time to cache transport failures. + /// + /// The value has to be at least one second, at most 300 seconds + /// (five minutes) and the default is 30 seconds. + pub fn set_transport_failure_duration(&mut self, value: Duration) { + self.transport_failure_duration = + TRANSPORT_FAILURE_DURATION.limit(value) + } + + /// Set the maximum time to cache results other than NOERROR or NXDOMAIN. + /// + /// The value has to be at least one second, at most 300 seconds + /// (five minutes) and the default is 30 seconds. + pub fn set_misc_error_duration(&mut self, value: Duration) { + self.misc_error_duration = MISC_ERROR_DURATION.limit(value) + } + + /// Set the maximum time to cache NXDOMAIN results. + /// + /// The value has to be at least 60 seconds (one minute), at most 86,400 + /// seconds (one day) and the default is 3,600 seconds (one hour). + pub fn set_max_nxdomain_validity(&mut self, value: Duration) { + self.max_nxdomain_validity = MAX_NXDOMAIN_VALIDITY.limit(value) + } + + /// Set the maximum time to cache NODATA results. + /// + /// The value has to be at least 60 seconds (one minute), at most 86,400 + /// seconds (one day) and the default is 3,600 seconds (one hour). + pub fn set_max_nodata_validity(&mut self, value: Duration) { + self.max_nodata_validity = MAX_NODATA_VALIDITY.limit(value) + } + + /// Set the maximum time to cache delegations. + /// + /// The value has to be at least 60 seconds (one minute), at most + /// 1,000,000,000 seconds and the default is 1,000,000 seconds. + pub fn set_max_delegation_validity(&mut self, value: Duration) { + self.max_delegation_validity = MAX_DELEGATION_VALIDITY.limit(value) + } + + /// Enable or disable caching of response messages with the TC + /// (truncated) flag set. + /// + /// The default value is false (disabled). + pub fn set_cache_truncated(&mut self, value: bool) { + self.cache_truncated = value; + } +} + +impl Default for Config { + fn default() -> Self { + Self { + max_cache_entries: MAX_CACHE_ENTRIES.default(), + max_validity: MAX_VALIDITY.default(), + transport_failure_duration: TRANSPORT_FAILURE_DURATION.default(), + misc_error_duration: MISC_ERROR_DURATION.default(), + max_nxdomain_validity: MAX_NXDOMAIN_VALIDITY.default(), + max_nodata_validity: MAX_NODATA_VALIDITY.default(), + max_delegation_validity: MAX_DELEGATION_VALIDITY.default(), + cache_truncated: false, + } + } +} + +//------------ Connection ----------------------------------------------------- + +#[derive(Clone)] +/// A connection that caches responses from an upstream connection. +pub struct Connection { + /// Upstream transport to use for requests. + upstream: Upstream, + + /// The cache for this connection. + cache: Cache>>, + + /// The configuration of this connection. + config: Config, + + /// The clock to use for expiring cache entries. + clock: C, +} + +impl Connection { + /// Create a new connection with default configuration parameters. + /// + /// Note that Upstream needs to implement [SendRequest] + /// (and Clone/Send/Sync) to be useful. + pub fn new(upstream: Upstream) -> Self { + Self::with_config(upstream, Default::default()) + } + + /// Create a new connection with specified configuration parameters. + /// + /// Note that Upstream needs to implement [SendRequest] + /// (and Clone/Send/Sync) to be useful. + pub fn with_config(upstream: Upstream, config: Config) -> Self { + Self { + upstream, + cache: Cache::new(config.max_cache_entries), + config, + clock: SystemClock::new(), + } + } +} + +impl Connection +where + C: Clock + Send + Sync + 'static, +{ + /// Create a new connection with default configuration parameters. + pub fn new_with_time(upstream: Upstream, clock: C) -> Self { + Self::with_time_config(upstream, clock, Default::default()) + } + + /// Create a new connection with specified configuration parameters. + pub fn with_time_config( + upstream: Upstream, + clock: C, + config: Config, + ) -> Self { + Self { + upstream, + cache: Cache::new(config.max_cache_entries), + config, + clock, + } + } +} + +//------------ SendRequest ---------------------------------------------------- + +impl SendRequest for Connection +where + CR: Clone + ComposeRequest + 'static, + Upstream: Clone + SendRequest + Send + Sync + 'static, + C: Clock + Debug + Send + Sync + 'static, +{ + fn send_request( + &self, + request_msg: CR, + ) -> Box { + Box::new(Request::::new( + request_msg, + self.upstream.clone(), + self.cache.clone(), + self.config.clone(), + self.clock.clone(), + )) + } +} + +//------------ Request -------------------------------------------------------- + +/// The state of a request that is executed. +pub struct Request +where + CR: Send + Sync, + Upstream: Send + Sync, + C: Clock + Send + Sync, +{ + /// State of the request. + state: RequestState, + + /// The request message. + request_msg: CR, + + /// The upstream transport of the connection. + upstream: Upstream, + + /// The cache of the connection. + cache: Cache>>, + + /// The configuration of the connection. + config: Config, + + /// The clock to use for expiring cache entries. + clock: C, +} + +impl Request +where + CR: Clone + ComposeRequest + Send + Sync, + Upstream: SendRequest + Send + Sync, + C: Clock + Debug + Send + Sync + 'static, +{ + /// Create a new Request object. + fn new( + request_msg: CR, + upstream: Upstream, + cache: Cache>>, + config: Config, + clock: C, + ) -> Request { + Self { + state: RequestState::Init, + request_msg, + upstream, + cache, + config, + clock, + } + } + + /// This is the implementation of the get_response method. + /// + /// This function is cancel safe. + async fn get_response_impl(&mut self) -> Result, Error> { + loop { + match &mut self.state { + RequestState::Init => { + let msg = self.request_msg.to_message()?; + let header = msg.header(); + let opcode = header.opcode(); + + // Extract Qname, Qclass, Qtype + let mut question_section = msg.question(); + let question = match question_section.next() { + None => { + // No question. Just forward the request. + let request = self + .upstream + .send_request(self.request_msg.clone()); + self.state = + RequestState::GetResponseNoCache(request); + continue; + } + Some(question) => question?, + }; + if question_section.next().is_some() { + // More than one question. Just forward the request. + let request = self + .upstream + .send_request(self.request_msg.clone()); + self.state = + RequestState::GetResponseNoCache(request); + continue; + } + let qname = question.qname(); + let qclass = question.qclass(); + let qtype = question.qtype(); + + if !(opcode == Opcode::Query && qclass == Class::In) { + // Anything other than a query on the Internet class + // should not be cached. + let request = self + .upstream + .send_request(self.request_msg.clone()); + self.state = + RequestState::GetResponseNoCache(request); + continue; + } + + let mut ad = header.ad(); + let cd = header.cd(); + let rd = header.rd(); + + let dnssec_ok = + msg.opt().map_or(false, |opt| opt.dnssec_ok()); + if dnssec_ok && !ad { + ad = true; + } + + let key = + Key::new(qname, qclass, qtype, ad, cd, dnssec_ok, rd); + let opt_ce = self.cache_lookup(&key).await?; + if let Some(value) = opt_ce { + let opt_response = value.get_response(qname); + if let Some(response) = opt_response { + return response; + } + } + + let request = + self.upstream.send_request(self.request_msg.clone()); + self.state = RequestState::GetResponse(key, request); + continue; + } + RequestState::GetResponse(key, request) => { + let response = request.get_response().await; + + // The clone of key needs to happen before cache_insert + // otherwise there will be a conflict between self and key. + let key = key.clone(); + let value = Arc::new(Value::new( + response.clone(), + &self.config, + &self.clock, + )?); + self.cache_insert(key, value).await; + + return response; + } + RequestState::GetResponseNoCache(request) => { + return request.get_response().await; + } + } + } + } + + /// Try to find a cache entry for the key. + async fn cache_lookup( + &self, + key: &Key, + ) -> Result>>, Error> { + // There are 4 flags that may affect the response to a query. + // In some cases the response to one value of a flag could be + // used for the other value. + // This function takes all 4 flags into account. First we take care + // of the CD flag. This flag has to be used as is, so there is not + // much to do. Next we pass the request to a function that looks + // at RD, DO, and AD. + self.cache_lookup_rd_do_ad(key).await + } + + /// Try to find an cache entry for the key taking into account the + /// RD, DO, and AD flags. The CD flag is kept unchanged. + async fn cache_lookup_rd_do_ad( + &self, + key: &Key, + ) -> Result>>, Error> { + // For RD=1 we can only use responses to queries with RD set. + // For RD=0, first try with RD=0 and then try with RD=1. If + // RD=1 has an answer, store it as an answer for RD=0. + let opt_value = self.cache_lookup_do_ad(key).await?; + if opt_value.is_some() || key.rd { + return Ok(opt_value); + } + + // Look if there is something with RD=1. We can use the + // response unmodified. + let mut alt_key = key.clone(); + alt_key.rd = true; + let opt_value = self.cache_lookup_do_ad(&alt_key).await?; + if let Some(value) = opt_value { + let value = update_header( + value, + &self.config, + |_hdr| true, + |hdr| hdr.set_rd(false), + )?; + self.cache_insert(key.clone(), value.clone()).await; + return Ok(Some(value)); + } + Ok(opt_value) + } + + /// Try to find an cache entry for the key taking into account the + /// DO and AD flags. The CD and RD flags are kept unchanged. + async fn cache_lookup_do_ad( + &self, + key: &Key, + ) -> Result>>, Error> { + // For DO=1 we can only use responses to queries with DO set. + // For DO=0, first try with DO=0 and then try with DO=1. If + // DO=1 has an answer, remove DNSSEC related resource records. + // If AD is clear then clear the AD bit. + + // If DO is set then AD is irrelevant. Force AD to be set for + // consistency (if DO is set then with respect to the AD flag + // the behavior is as if AD is set). + + let opt_value = self.cache_lookup_ad(key).await?; + if opt_value.is_some() || key.addo.dnssec_ok() { + return Ok(opt_value); + } + + if is_dnssec(key.qtype) { + // An explicit request for one of the DNSSEC types but + // DO is not set. Force the request to be sent explicitly. + return Ok(None); + } + + let mut alt_key = key.clone(); + alt_key.addo = AdDo::Do; + let opt_value = self.cache.get(&alt_key).await; + if let Some(value) = opt_value { + let value = update_message( + value, + &self.config, + |_hdr| true, + |msg| remove_dnssec(msg, key.addo.ad()), + )?; + self.cache_insert(key.clone(), value.clone()).await; + return Ok(Some(value)); + } + Ok(opt_value) + } + + /// Try to find an cache entry for the key taking into account the + /// AD flag. The CD, DO, and RD flags are kept unchanged. + async fn cache_lookup_ad( + &self, + key: &Key, + ) -> Result>>, Error> { + // For AD=1 we can only use responses to queries with AD set. + // For AD=0, first try with AD=0 and then try with AD=1. If + // AD=1 has an answer, clear the AD bit. + let opt_value = self.cache.get(key).await; + if opt_value.is_some() || key.addo.ad() { + return Ok(opt_value); + } + let mut alt_key = key.clone(); + alt_key.addo = AdDo::Ad; + let opt_value = self.cache.get(&alt_key).await; + if let Some(value) = opt_value { + let value = update_header( + value, + &self.config, + |hdr| hdr.ad(), + |hdr| hdr.set_ad(false), + )?; + self.cache_insert(key.clone(), value.clone()).await; + return Ok(Some(value)); + } + Ok(opt_value) + } + + /// Insert new entry in the cache. + /// + /// Do not insert if the validity is zero. + /// Make sure to clear the AA flag. + async fn cache_insert(&self, key: Key, value: Arc>) { + if value.valid_for.is_zero() { + return; + } + let value = match prepare_for_insert(value.clone(), &self.config) { + Ok(value) => value, + Err(e) => { + // Create a new value based on this error + Arc::new( + Value::::new_from_value_and_response( + value, + Err(e), + &self.config, + ) + .expect("value from error does not fail"), + ) + } + }; + self.cache.insert(key, value).await + } +} + +impl Debug for Request +where + CR: Send + Sync, + Upstream: Send + Sync, + C: Clock + Send + Sync, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), core::fmt::Error> { + f.debug_struct("Request") + .field("fut", &format_args!("_")) + .finish() + } +} + +impl GetResponse for Request +where + CR: Clone + ComposeRequest + Debug + Sync, + Upstream: SendRequest + Send + Sync + 'static, + C: Clock + Debug + Send + Sync + 'static, +{ + fn get_response( + &mut self, + ) -> Pin< + Box< + dyn Future, Error>> + + Send + + Sync + + '_, + >, + > { + Box::pin(self.get_response_impl()) + } +} + +//------------ RequestState --------------------------------------------------- +/// States of the state machine in get_response_impl +enum RequestState { + /// Initial state, perform a cache lookup. + Init, + + /// Wait for a response and insert the response in the cache. + GetResponse(Key, Box), + + /// Wait for a response but do not insert the response in the cache. + GetResponseNoCache(Box), +} + +//------------ Key ------------------------------------------------------------ + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +/// The key for cache entries. +/// +/// Note that the AD and DO flags are combined into a single enum. +struct Key { + /// DNS name in the request. + qname: Dname, + + /// The request class. Always IN at the moment. + qclass: Class, + + /// The requested type. + qtype: Rtype, + + /// Value of the AD and Do flags. + addo: AdDo, + + /// Value of the CD flag. + cd: bool, + + /// Value of the RD flag. + rd: bool, +} + +impl Key { + /// Create a new key object. + fn new( + qname: TDN, + qclass: Class, + qtype: Rtype, + ad: bool, + cd: bool, + dnssec_ok: bool, + rd: bool, + ) -> Key + where + TDN: ToDname, + { + let mut qname: Dname> = + qname.to_dname().expect("to_dname should not fail"); + + // Make sure qname is canonical. + qname.make_canonical(); + let qname: Dname = qname.octets_into(); + + Self { + qname, + qclass, + qtype, + addo: AdDo::new(ad, dnssec_ok), + cd, + rd, + } + } +} + +/// The DO and AD flag have a special relationship. If the DO flag is set, +/// then the AD flag is irrelevant, but to code looking for the AD flag +/// we pretend that it is set. So we have three possibilities: DO is set +/// and AD is irrelevant, DO is not set, but AD is set. Or neither DO nor +/// AD is set. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +enum AdDo { + /// DO is set, AD is ignored. + Do, + + /// DO is clear, AD is set. + Ad, + + /// Both AD and DO are clear. + None, +} + +impl AdDo { + /// Create a new AdDo object based on the AD and DO flags. + fn new(ad: bool, dnssec_ok: bool) -> Self { + if dnssec_ok { + AdDo::Do + } else if ad { + AdDo::Ad + } else { + AdDo::None + } + } + + /// Return whether AD is set or should be considered set. + fn ad(&self) -> bool { + match self { + // Do acts as if Ad is set + AdDo::Ad | AdDo::Do => true, + AdDo::None => false, + } + } + + /// Return whether DO is set. + fn dnssec_ok(&self) -> bool { + match self { + AdDo::Do => true, + AdDo::Ad | AdDo::None => false, + } + } +} + +//------------ Value ---------------------------------------------------------- + +#[derive(Debug)] +/// The value to be cached. +struct Value +where + C: Clock + Send + Sync, +{ + /// Creation time of the cache entry. + created_at: C::Instant, + + /// The amount time the cache entry is valid. + valid_for: Duration, + + /// The cached response. + response: Result, Error>, +} + +impl Value +where + C: Clock + Send + Sync, +{ + /// Create a new value object. + fn new( + response: Result, Error>, + config: &Config, + clock: &C, + ) -> Result, Error> { + Ok(Self { + created_at: clock.now(), + valid_for: validity(&response, config)?, + response, + }) + } + + /// Create a value object that is derived from another value object. + fn new_from_value_and_response( + val: Arc>, + response: Result, Error>, + config: &Config, + ) -> Result, Error> { + Ok(Self { + created_at: val.created_at.clone(), + valid_for: validity(&response, config)?, + response, + }) + } + + /// Get a response. Either return None if the value has expired or + /// return a response message with decremented TTL values. + fn get_response( + &self, + orig_qname: TDN, + ) -> Option, Error>> + where + TDN: ToDname + Clone, + C: Clock + Send + Sync, + { + let elapsed = self.created_at.elapsed(); + if elapsed > self.valid_for { + return None; + } + let secs = elapsed.as_secs() as u32; + let response = decrement_ttl(orig_qname, &self.response, secs); + Some(response) + } +} + +//------------ Utility functions ---------------------------------------------- + +/// Compute how long a response can be cached. +fn validity( + response: &Result, Error>, + config: &Config, +) -> Result { + let Ok(msg) = response else { + return Ok(config.transport_failure_duration); + }; + + if msg.header().tc() && !config.cache_truncated { + // Return zero duration to signal that the truncated message should + // not be cached. + return Ok(Duration::ZERO); + } + + let mut min_val = config.max_validity; + + match msg.opt_rcode() { + OptRcode::NoError => { + match classify_no_error(msg)? { + NoErrorType::Answer => (), + NoErrorType::NoData => { + min_val = min(min_val, config.max_nodata_validity) + } + NoErrorType::Delegation => { + min_val = min(min_val, config.max_delegation_validity) + } + NoErrorType::NoErrorWeird => + // Weird NODATA response. Don't cache this. + { + min_val = Duration::ZERO + } + } + } + OptRcode::NXDomain => { + min_val = min(min_val, config.max_nxdomain_validity); + } + + _ => { + min_val = min(min_val, config.misc_error_duration); + } + } + + let msg = msg.question(); + let mut msg = msg.answer()?; + for rr in &mut msg { + let rr = rr?; + min_val = + min(min_val, Duration::from_secs(rr.ttl().as_secs() as u64)); + } + + let mut msg = msg.next_section()?.expect("section should be present"); + for rr in &mut msg { + let rr = rr?; + min_val = + min(min_val, Duration::from_secs(rr.ttl().as_secs() as u64)); + } + + let msg = msg.next_section()?.expect("section should be present"); + for rr in msg { + let rr = rr?; + if rr.rtype() != Rtype::Opt { + min_val = + min(min_val, Duration::from_secs(rr.ttl().as_secs() as u64)); + } + } + + Ok(min_val) +} + +/// Return a new message with decremented TTL values. +fn decrement_ttl( + orig_qname: TDN, + response: &Result, Error>, + amount: u32, +) -> Result, Error> +where + TDN: ToDname + Clone, +{ + let msg = match response { + Err(err) => return Err(err.clone()), + Ok(msg) => msg, + }; + + let amount = Ttl::from_secs(amount); + + let mut target = + MessageBuilder::from_target(StaticCompressor::new(Vec::new())) + .expect("Vec is expected to have enough space"); + + let source = msg; + + *target.header_mut() = source.header(); + + let source = source.question(); + let mut target = target.question(); + for rr in source { + let rr = rr?; + target + .push((orig_qname.clone(), rr.qtype(), rr.qclass())) + .expect("push failed"); + } + let mut source = source.answer()?; + let mut target = target.answer(); + for rr in &mut source { + let mut rr = rr? + .into_record::>>()? + .expect("record expected"); + rr.set_ttl(rr.ttl() - amount); + target.push(rr).expect("push failed"); + } + + let mut source = + source.next_section()?.expect("section should be present"); + let mut target = target.authority(); + for rr in &mut source { + let mut rr = rr? + .into_record::>>()? + .expect("record expected"); + rr.set_ttl(rr.ttl() - amount); + target.push(rr).expect("push failed"); + } + + let source = source.next_section()?.expect("section should be present"); + let mut target = target.additional(); + for rr in source { + let rr = rr?; + let mut rr = rr + .into_record::>>()? + .expect("record expected"); + if rr.rtype() != Rtype::Opt { + rr.set_ttl(rr.ttl() - amount); + } + target.push(rr).expect("push failed"); + } + + let result = target.as_builder().clone(); + let msg = + Message::::from_octets(result.finish().into_target().into()) + .expect( + "Message should be able to parse output from MessageBuilder", + ); + Ok(msg) +} + +/// Return a new message without the DNSSEC type RRSIG, NSEC, and NSEC3. +fn remove_dnssec( + msg: &Message, + ad: bool, +) -> Result, Error> { + let mut target = + MessageBuilder::from_target(StaticCompressor::new(Vec::new())) + .expect("Vec is expected to have enough space"); + + let source = msg; + + *target.header_mut() = source.header(); + + if !ad { + // Clear ad + target.header_mut().set_ad(false); + } + + let source = source.question(); + let mut target = target.question(); + for rr in source { + target.push(rr?).expect("push failed"); + } + let mut source = source.answer()?; + let mut target = target.answer(); + for rr in &mut source { + let rr = rr? + .into_record::>>()? + .expect("record expected"); + if is_dnssec(rr.rtype()) { + continue; + } + target.push(rr).expect("push error"); + } + + let mut source = + source.next_section()?.expect("section should be present"); + let mut target = target.authority(); + for rr in &mut source { + let rr = rr? + .into_record::>>()? + .expect("record expected"); + if is_dnssec(rr.rtype()) { + continue; + } + target.push(rr).expect("push error"); + } + + let source = source.next_section()?.expect("section should be present"); + let mut target = target.additional(); + for rr in source { + let rr = rr?; + let rr = rr + .into_record::>>()? + .expect("record expected"); + if is_dnssec(rr.rtype()) { + continue; + } + target.push(rr).expect("push error"); + } + + let result = target.as_builder().clone(); + Ok( + Message::::from_octets(result.finish().into_target().into()) + .expect( + "Message should be able to parse output from MessageBuilder", + ), + ) +} + +/// Check if a type is a DNSSEC type that needs to be removed. +fn is_dnssec(rtype: Rtype) -> bool { + rtype == Rtype::Rrsig || rtype == Rtype::Nsec || rtype == Rtype::Nsec3 +} + +/// This type represents that various subtypes of a NOERROR result. +enum NoErrorType { + /// The result is an answer to the question. + Answer, + + /// The name exists, but there is not data for the request class and tpye + /// combination. + NoData, + + /// The upstream DNS server sent a delegation to another DNS zone. + Delegation, + + /// None of the above. This is not a valid response. + NoErrorWeird, +} + +/// Classify a responses with a NOERROR result. +fn classify_no_error(msg: &Message) -> Result +where + Octs: Octets, +{ + // Check if we have something that resembles an answer. + let mut question_section = msg.question(); + let question = question_section.next().expect("section expected")?; + let qtype = question.qtype(); + let qclass = question.qclass(); + + // Note we only look qtype and qclass. The goal is not to perform + // a consistency check. Just whether there is supposed to be an + // answer or not. + let mut msg = msg.answer()?; + for rr in &mut msg { + let rr = rr?; + if rr.rtype() == qtype && rr.class() == qclass { + // We found an answer. + return Ok(NoErrorType::Answer); + } + } + + // No answer. Check the authority section for SOA and NS records. + // If the SOA is present then the response is a NODATA response. + // If SOA records are absent but NS records are present then the + // response is a delegation. + let mut found_ns = false; + let mut msg = msg.next_section()?.expect("section should be present"); + for rr in &mut msg { + let rr = rr?; + if rr.class() == qclass && rr.rtype() == Rtype::Soa { + return Ok(NoErrorType::NoData); + } + if rr.class() == qclass && rr.rtype() == Rtype::Ns { + found_ns = true; + } + } + + if found_ns { + return Ok(NoErrorType::Delegation); + } + + // Neither SOA nor NS were found. This is a broken response. + Ok(NoErrorType::NoErrorWeird) +} + +/// Prepare a value for inserting in the cache by clearing the AA flag if +/// set. +fn prepare_for_insert( + value: Arc>, + config: &Config, +) -> Result>, Error> +where + C: Clock + Send + Sync, +{ + update_header(value, config, |hdr| hdr.aa(), |hdr| hdr.set_aa(false)) +} + +/// Update the Header of a Message in a Value by creating a new Value with a +/// new Message if the Header needs to be changed. +/// +/// Return the original Value if no change is needed. +/// hdrtst checks if the header needs updating, fhdr modifies the header. +fn update_header( + value: Arc>, + config: &Config, + hdrtst: fn(hdr: &Header) -> bool, + fhdr: fn(&mut Header) -> (), +) -> Result>, Error> +where + C: Clock + Send + Sync, +{ + update_message(value, config, hdrtst, |msg| { + let mut msg = Message::>::from_octets(msg.as_slice().into())?; + let hdr = msg.header_mut(); + fhdr(hdr); + Ok(Message::::from_octets(msg.into_octets().into())?) + }) +} + +/// Update a Message in a Value by creating a new Value with a +/// new Message if the Message needs to be changed. +/// +/// Return the original Value if no change is needed. +/// hdrtst checks if the Message needs updating, fmsg returns a new Message. +fn update_message( + value: Arc>, + config: &Config, + hdrtst: fn(hdr: &Header) -> bool, + fmsg: FmsgFn, +) -> Result>, Error> +where + C: Clock + Send + Sync, + FmsgFn: Fn(&Message) -> Result, Error>, +{ + Ok(match &value.response { + Err(_) => { + // No message, no need to change anything. + value + } + Ok(msg) => { + if hdrtst(&msg.header()) { + let msg = fmsg(msg)?; + Arc::new(Value::::new_from_value_and_response( + value.clone(), + Ok(msg), + config, + )?) + } else { + // No need to change anything. Just insert this value. + value + } + } + }) +} diff --git a/src/net/client/clock.rs b/src/net/client/clock.rs new file mode 100644 index 00000000..530cb5d4 --- /dev/null +++ b/src/net/client/clock.rs @@ -0,0 +1,127 @@ +//! A time interface that can be replaced by a fake time implementation +//! during testing. + +#![warn(missing_docs)] +#![warn(clippy::missing_docs_in_private_items)] + +use std::fmt::Debug; +use std::sync::{Arc, Mutex}; +use std::time; +use std::time::Duration; + +//------------ Clock ----------------------------------------------------------- + +/// A trait for storing the current time in an object that implements the +/// [Elapsed] trait. +pub trait Clock: Clone { + /// The type that implements the [Elapsed] trait. + type Instant: Clone + Debug + Elapsed + Send + Sync; + + /// Create a new instance of the clock. + fn new() -> Self; + + /// Record the current time in an [Self::Instant] object. + fn now(&self) -> Self::Instant; +} + +//------------ Elapsed -------------------------------------------------------- + +/// Trait for reporting the time that has elapsed since the creation of an +/// instance object. +pub trait Elapsed { + /// Return the elapsed time. + fn elapsed(&self) -> Duration; +} + +//------------ SystemClock ----------------------------------------------------- + +/// Implementation of the [Clock] trait using the Instant type from +/// std::time. +#[derive(Clone, Debug)] +pub struct SystemClock {} + +impl Clock for SystemClock { + type Instant = time::Instant; + + fn new() -> Self { + Self {} + } + + fn now(&self) -> Self::Instant { + Self::Instant::now() + } +} + +impl Elapsed for time::Instant { + fn elapsed(&self) -> Duration { + self.elapsed() + } +} + +//------------ FakeClock ----------------------------------------------------- + +/// Implementation of the [Clock] trait to fake the passing of time, for example +/// for testing. +#[derive(Clone, Debug)] +pub struct FakeClock { + /// The current fake time. + now: Arc>, +} + +impl FakeClock { + /// Adjust the current time by adding a [Duration] + pub fn adjust_time(&self, adjust: Duration) { + println!("adjust_time: adjust {:?}", adjust); + let mut now = self.now.lock().expect("lock should not fail"); + *now = (*now).checked_add(adjust).expect("time wrapped"); + } + + /// Return the current (fake) time. + fn curr_time(&self) -> Duration { + let now = self.now.lock().expect("lock should not fail"); + *now + } +} + +impl Clock for FakeClock { + type Instant = FakeInstant; + + fn new() -> Self { + Self { + now: Arc::new(Mutex::new(Duration::from_secs(0))), + } + } + + fn now(&self) -> Self::Instant { + let now = self.now.lock().expect("lock should not fail"); + Self::Instant::now(*now, self.clone()) + } +} + +//------------ FakeInstant ---------------------------------------------------- + +/// An instant that provides fake time. +#[derive(Clone, Debug)] +pub struct FakeInstant { + /// When the FakeInstant was created. + start: Duration, + + /// The clock that was used to create it. + clock: FakeClock, +} + +impl FakeInstant { + /// Create a new FakeInstant. + fn now(now: Duration, clock: FakeClock) -> Self { + Self { start: now, clock } + } +} + +impl Elapsed for FakeInstant { + fn elapsed(&self) -> Duration { + self.clock + .curr_time() + .checked_sub(self.start) + .expect("clock went backwards") + } +} diff --git a/src/net/client/dgram.rs b/src/net/client/dgram.rs index a6197e63..65e958ae 100644 --- a/src/net/client/dgram.rs +++ b/src/net/client/dgram.rs @@ -17,8 +17,9 @@ use crate::net::client::protocol::{ use crate::net::client::request::{ ComposeRequest, Error, GetResponse, SendRequest, }; +use crate::utils::config::DefMinMax; use bytes::Bytes; -use core::{cmp, fmt}; +use core::fmt; use octseq::OctetsInto; use std::boxed::Box; use std::future::Future; @@ -254,7 +255,7 @@ where } // Create the message and send it out. - let request_msg = request.to_message(); + let request_msg = request.to_message()?; let dgram = request_msg.as_slice(); let sent = sock.send(dgram).await.map_err(QueryError::send)?; if sent != dgram.len() { @@ -324,7 +325,10 @@ where AsyncDgramRecv + AsyncDgramSend + Send + Sync + Unpin + 'static, Req: ComposeRequest + Clone + Send + Sync + 'static, { - fn send_request(&self, request_msg: Req) -> Box { + fn send_request( + &self, + request_msg: Req, + ) -> Box { Box::new(Request { fut: Box::pin(self.clone().handle_request_impl(request_msg)), }) @@ -336,7 +340,9 @@ where /// The state of a DNS request. pub struct Request { /// Future that does the actual work of GetResponse. - fut: Pin, Error>> + Send>>, + fut: Pin< + Box, Error>> + Send + Sync>, + >, } impl Request { @@ -356,47 +362,17 @@ impl GetResponse for Request { fn get_response( &mut self, ) -> Pin< - Box, Error>> + Send + '_>, + Box< + dyn Future, Error>> + + Send + + Sync + + '_, + >, > { Box::pin(self.get_response_impl()) } } -//------------ DefMinMax ----------------------------------------------------- - -/// The default, minimum, and maximum values for a config variable. -#[derive(Clone, Copy)] -struct DefMinMax { - /// The default value, - def: T, - - /// The minimum value, - min: T, - - /// The maximum value, - max: T, -} - -impl DefMinMax { - /// Creates a new value. - const fn new(def: T, min: T, max: T) -> Self { - Self { def, min, max } - } - - /// Returns the default value. - fn default(self) -> T { - self.def - } - - /// Trims the given value to fit into the minimum/maximum range. - fn limit(self, value: T) -> T - where - T: Ord, - { - cmp::max(self.min, cmp::min(self.max, value)) - } -} - //============ Errors ======================================================== //------------ QueryError ---------------------------------------------------- diff --git a/src/net/client/dgram_stream.rs b/src/net/client/dgram_stream.rs index 42b007ad..a1c3378f 100644 --- a/src/net/client/dgram_stream.rs +++ b/src/net/client/dgram_stream.rs @@ -86,7 +86,7 @@ impl Config { /// DNS transport connection that first issues a query over a UDP transport and /// falls back to TCP if the reply is truncated. -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Connection { /// The UDP transport connection. udp_conn: Arc>, @@ -133,7 +133,10 @@ where DgramS::Connection: AsyncDgramRecv + AsyncDgramSend + Send + Sync + Unpin, Req: ComposeRequest + Clone + 'static, { - fn send_request(&self, request_msg: Req) -> Box { + fn send_request( + &self, + request_msg: Req, + ) -> Box { Box::new(Request::new( request_msg, self.udp_conn.clone(), @@ -167,13 +170,13 @@ enum QueryState { StartUdpRequest, /// Get the response from the UDP transport. - GetUdpResponse(Box), + GetUdpResponse(Box), /// Start a request over the TCP transport. StartTcpRequest, /// Get the response from the TCP transport. - GetTcpResponse(Box), + GetTcpResponse(Box), } impl Request @@ -244,7 +247,12 @@ where fn get_response( &mut self, ) -> Pin< - Box, Error>> + Send + '_>, + Box< + dyn Future, Error>> + + Send + + Sync + + '_, + >, > { Box::pin(self.get_response_impl()) } diff --git a/src/net/client/mod.rs b/src/net/client/mod.rs index 85e16209..348798e1 100644 --- a/src/net/client/mod.rs +++ b/src/net/client/mod.rs @@ -1,8 +1,29 @@ +#![cfg_attr( + not(feature = "unstable-client-transport"), + doc = " The `unstable-client-transport` feature is necessary to enable this module." +)] //! Sending requests and receiving responses. //! //! This module provides DNS transport protocols that allow sending a DNS //! request and receiving the corresponding reply. //! +//! Currently the following transport protocols are supported: +//! * [dgram] DNS over a datagram protocol, typically UDP. +//! * [stream] DNS over an octet stream protocol, typically TCP or TLS. +//! Only a single connection is supported. +//! The transport works as long as the connection continues to exist. +//! * [multi_stream] This is a layer on top of [stream] where new connections +//! are established as old connections are closed (or fail). +//! * [dgram_stream] This is a combination of [dgram] and [multi_stream]. +//! This is typically needed because a request over UDP can receive +//! a truncated response, which should be retried over TCP. +//! * [redundant] This transport multiplexes requests over a collection of +//! transport connections. The [redundant] transport favors the connection +//! with the lowest response time. Any of the other transports can be added +//! as upstream transports. +//! * [cache] This is a simple message cache provided as a pass through +//! transport. The cache works with any of the other transports. +//! //! Sending a request and receiving the reply consists of four steps: //! 1) Creating a request message, //! 2) Creating a DNS transport, @@ -19,7 +40,7 @@ //! [ComposeRequest][request::ComposeRequest] trait. //! This trait allows transports to add ENDS(0) options, set flags, etc. //! The [RequestMessage][request::RequestMessage] type implements this trait. -//! The [new][request::RequestMessage::new] method of RequestMessage create +//! The [new][request::RequestMessage::new] method of RequestMessage creates //! a new RequestMessage object based an existing messsage (that implements //! ```Into>```). //! @@ -46,9 +67,11 @@ //! # use domain::net::client::multi_stream; //! # use domain::net::client::protocol::TcpConnect; //! # use domain::net::client::request::SendRequest; +//! # use std::net::{IpAddr, SocketAddr}; +//! # use std::str::FromStr; //! # use std::time::Duration; //! # async fn _test() { -//! # let server_addr = String::from("127.0.0.1:53"); +//! # let server_addr = SocketAddr::new(IpAddr::from_str("::1").unwrap(), 53); //! let mut multi_stream_config = multi_stream::Config::default(); //! multi_stream_config.stream_mut().set_response_timeout( //! Duration::from_millis(100), @@ -64,22 +87,9 @@ //! # let mut request = tcp_conn.send_request(req); //! # } //! ``` -//! The currently implemented DNS transports have the following layering. At -//! the lowest layer are [dgram] and [stream]. The dgram transport is used for -//! DNS over UDP, the stream transport is used for DNS over a single TCP or -//! TLS connection. The transport works as long as the connection continuous -//! to exist. -//! The [multi_stream] transport is layered on top of stream, and creates new -//! TCP or TLS connections when old ones terminates. -//! Next, [dgram_stream] combines the dgram transport with the multi_stream -//! transport. This is typically needed because a request over UDP can receive -//! a truncated response, which should be retried over TCP. -//! Finally, the [redundant] transport can select the best transport out of -//! a collection of underlying transports. - //! # Sending the request //! -//! A DNS transport implements the [SendRequest][request::SendRequest] trait. +//! A connection implements the [SendRequest][request::SendRequest] trait. //! This trait provides a single method, //! [send_request][request::SendRequest::send_request] and returns an object //! that provides the response. @@ -87,10 +97,12 @@ //! For example: //! ```no_run //! # use domain::net::client::request::SendRequest; +//! # use std::net::{IpAddr, SocketAddr}; +//! # use std::str::FromStr; //! # async fn _test() { //! # let (tls_conn, _) = domain::net::client::stream::Connection::new( //! # domain::net::client::protocol::TcpConnect::new( -//! # String::from("127.0.0.1:53") +//! # SocketAddr::new(IpAddr::from_str("::1").unwrap(), 53) //! # ) //! # ); //! # let req = domain::net::client::request::RequestMessage::new( @@ -101,7 +113,7 @@ //! ``` //! where ```tls_conn``` is a transport connection for DNS over TLS. -//! # Receiving the request +//! # Receiving the response //! //! The [send_request][request::SendRequest::send_request] method returns an //! object that implements the [GetResponse][request::GetResponse] trait. @@ -113,10 +125,12 @@ //! For example: //! ```no_run //! # use crate::domain::net::client::request::SendRequest; +//! # use std::net::{IpAddr, SocketAddr}; +//! # use std::str::FromStr; //! # async fn _test() { //! # let (tls_conn, _) = domain::net::client::stream::Connection::new( //! # domain::net::client::protocol::TcpConnect::new( -//! # String::from("127.0.0.1:53") +//! # SocketAddr::new(IpAddr::from_str("::1").unwrap(), 53) //! # ) //! # ); //! # let req = domain::net::client::request::RequestMessage::new( @@ -127,6 +141,25 @@ //! # } //! ``` +//! # Limitations +//! +//! The current implementaton has the following limitations: +//! * The [dgram] transport does not support DNS Cookies +//! ([`RFC 7873`](https://www.rfc-editor.org/info/rfc7873) +//! Domain Name System (DNS) Cookies). +//! * The [multi_stream] transport does not support timeouts or other limits on +//! the number of attempts to open a connection. The caller has to +//! implement a timeout mechanism. +//! * The [cache] transport does not support: +//! * prefetching. In this context, prefetching means updating a cache entry +//! before it expires. +//! * [RFC 8767](https://www.rfc-editor.org/info/rfc8767) +//! (Serving Stale Data to Improve DNS Resiliency) +//! * [RFC 7871](https://www.rfc-editor.org/info/rfc7871) +//! (Client Subnet in DNS Queries) +//! * [RFC 8198](https://www.rfc-editor.org/info/rfc8198) +//! (Aggressive Use of DNSSEC-Validated Cache) + //! # Example with various transport connections //! ```no_run #![doc = include_str!("../../../examples/client-transports.rs")] @@ -136,6 +169,8 @@ #![cfg_attr(docsrs, doc(cfg(feature = "unstable-client-transport")))] #![warn(missing_docs)] +pub mod cache; +pub mod clock; pub mod dgram; pub mod dgram_stream; pub mod multi_stream; diff --git a/src/net/client/multi_stream.rs b/src/net/client/multi_stream.rs index a5690a46..f7e5d022 100644 --- a/src/net/client/multi_stream.rs +++ b/src/net/client/multi_stream.rs @@ -159,7 +159,10 @@ impl SendRequest for Connection where Req: ComposeRequest + Clone + 'static, { - fn send_request(&self, request: Req) -> Box { + fn send_request( + &self, + request: Req, + ) -> Box { Box::new(Request::new(self.clone(), request)) } } @@ -328,7 +331,12 @@ impl GetResponse for Request { fn get_response( &mut self, ) -> Pin< - Box, Error>> + Send + '_>, + Box< + dyn Future, Error>> + + Send + + Sync + + '_, + >, > { Box::pin(Self::get_response(self)) } diff --git a/src/net/client/protocol.rs b/src/net/client/protocol.rs index d5c4d0b9..4c4b07b3 100644 --- a/src/net/client/protocol.rs +++ b/src/net/client/protocol.rs @@ -9,7 +9,7 @@ use std::net::SocketAddr; use std::sync::Arc; use std::task::{Context, Poll}; use tokio::io::ReadBuf; -use tokio::net::{TcpStream, ToSocketAddrs, UdpSocket}; +use tokio::net::{TcpStream, UdpSocket}; use tokio_rustls::client::TlsStream; use tokio_rustls::rustls::{ClientConfig, ServerName}; use tokio_rustls::TlsConnector; @@ -27,7 +27,9 @@ pub trait AsyncConnect { type Connection; /// The future establishing the connection. - type Fut: Future> + Send; + type Fut: Future> + + Send + + Sync; /// Returns a future that establishing a connection. fn connect(&self) -> Self::Fut; @@ -37,34 +39,32 @@ pub trait AsyncConnect { /// Create new TCP connections. #[derive(Clone, Copy, Debug)] -pub struct TcpConnect { +pub struct TcpConnect { /// Remote address to connect to. - addr: Addr, + addr: SocketAddr, } -impl TcpConnect { +impl TcpConnect { /// Create new TCP connections. /// /// addr is the destination address to connect to. - pub fn new(addr: Addr) -> Self { + pub fn new(addr: SocketAddr) -> Self { Self { addr } } } -impl AsyncConnect for TcpConnect -where - Addr: ToSocketAddrs + Clone + Send + 'static, -{ +impl AsyncConnect for TcpConnect { type Connection = TcpStream; type Fut = Pin< Box< dyn Future> - + Send, + + Send + + Sync, >, >; fn connect(&self) -> Self::Fut { - Box::pin(TcpStream::connect(self.addr.clone())) + Box::pin(TcpStream::connect(self.addr)) } } @@ -72,7 +72,7 @@ where /// Create new TLS connections #[derive(Clone, Debug)] -pub struct TlsConnect { +pub struct TlsConnect { /// Configuration for setting up a TLS connection. client_config: Arc, @@ -80,15 +80,15 @@ pub struct TlsConnect { server_name: ServerName, /// Remote address to connect to. - addr: Addr, + addr: SocketAddr, } -impl TlsConnect { +impl TlsConnect { /// Function to create a new TLS connection stream pub fn new( client_config: impl Into>, server_name: ServerName, - addr: Addr, + addr: SocketAddr, ) -> Self { Self { client_config: client_config.into(), @@ -98,23 +98,21 @@ impl TlsConnect { } } -impl AsyncConnect for TlsConnect -where - Addr: ToSocketAddrs + Clone + Send + 'static, -{ +impl AsyncConnect for TlsConnect { type Connection = TlsStream; type Fut = Pin< Box< dyn Future> - + Send, + + Send + + Sync, >, >; fn connect(&self) -> Self::Fut { let tls_connection = TlsConnector::from(self.client_config.clone()); let server_name = self.server_name.clone(); - let addr = self.addr.clone(); - Box::pin(async { + let addr = self.addr; + Box::pin(async move { let box_connection = Box::new(tls_connection); let tcp = TcpStream::connect(addr).await?; box_connection.connect(server_name, tcp).await @@ -169,7 +167,8 @@ impl AsyncConnect for UdpConnect { type Fut = Pin< Box< dyn Future> - + Send, + + Send + + Sync, >, >; diff --git a/src/net/client/redundant.rs b/src/net/client/redundant.rs index d0e9a4d9..30b27ca3 100644 --- a/src/net/client/redundant.rs +++ b/src/net/client/redundant.rs @@ -81,7 +81,10 @@ pub struct Config { /// This type represents a transport connection. #[derive(Debug)] -pub struct Connection { +pub struct Connection +where + Req: Send + Sync, +{ /// User configuation. config: Config, @@ -131,7 +134,10 @@ impl Connection { } } -impl Clone for Connection { +impl Clone for Connection +where + Req: Send + Sync, +{ fn clone(&self) -> Self { Self { config: self.config, @@ -143,7 +149,10 @@ impl Clone for Connection { impl SendRequest for Connection { - fn send_request(&self, request_msg: Req) -> Box { + fn send_request( + &self, + request_msg: Req, + ) -> Box { Box::new(Request { fut: Box::pin(self.clone().request_impl(request_msg)), }) @@ -155,7 +164,9 @@ impl SendRequest /// An active request. pub struct Request { /// The underlying future. - fut: Pin, Error>> + Send>>, + fut: Pin< + Box, Error>> + Send + Sync>, + >, } impl Request { @@ -169,7 +180,12 @@ impl GetResponse for Request { fn get_response( &mut self, ) -> Pin< - Box, Error>> + Send + '_>, + Box< + dyn Future, Error>> + + Send + + Sync + + '_, + >, > { Box::pin(self.get_response_impl()) } @@ -187,7 +203,10 @@ impl Debug for Request { /// This type represents an active query request. #[derive(Debug)] -pub struct Query { +pub struct Query +where + Req: Send + Sync, +{ /// User configuration. config: Config, @@ -204,8 +223,9 @@ pub struct Query { sender: mpsc::Sender>, /// List of futures for outstanding requests. - fut_list: - FuturesUnordered + Send>>>, + fut_list: FuturesUnordered< + Pin + Send + Sync>>, + >, /// Transport error that should be reported if nothing better shows /// up. @@ -239,7 +259,10 @@ enum QueryState { } /// The commands that can be sent to the run function. -enum ChanReq { +enum ChanReq +where + Req: Send + Sync, +{ /// Add a connection Add(AddReq), @@ -256,7 +279,10 @@ enum ChanReq { Failure(TimeReport), } -impl Debug for ChanReq { +impl Debug for ChanReq +where + Req: Send + Sync, +{ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { f.debug_struct("ChanReq").finish() } @@ -284,7 +310,10 @@ struct RTReq /**/ { type RTReply = Result, Error>; /// Request to start a request -struct RequestReq { +struct RequestReq +where + Req: Send + Sync, +{ /// Identifier of connection id: u64, @@ -295,7 +324,10 @@ struct RequestReq { tx: oneshot::Sender, } -impl Debug for RequestReq { +impl Debug for RequestReq +where + Req: Send + Sync, +{ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { f.debug_struct("RequestReq") .field("id", &self.id) @@ -305,7 +337,7 @@ impl Debug for RequestReq { } /// Reply to a request request. -type RequestReply = Result, Error>; +type RequestReply = Result, Error>; /// Report the amount of time until success or failure. #[derive(Debug)] @@ -579,7 +611,10 @@ impl Query { /// Type that actually implements the connection. #[derive(Debug)] -pub struct Transport { +pub struct Transport +where + Req: Send + Sync, +{ /// Receive side of the channel used by the runner. receiver: mpsc::Receiver>, } @@ -701,7 +736,10 @@ async fn start_request( id: u64, sender: mpsc::Sender>, request_msg: Req, -) -> (usize, Result, Error>) { +) -> (usize, Result, Error>) +where + Req: Send + Sync, +{ let (tx, rx) = oneshot::channel(); sender .send(ChanReq::Query(RequestReq { @@ -732,7 +770,7 @@ fn skip(msg: &Message, config: &Config) -> bool { return false; } - let opt_rcode = get_opt_rcode(msg); + let opt_rcode = msg.opt_rcode(); // OptRcode needs PartialEq if let OptRcode::Refused = opt_rcode { if config.defer_refused { @@ -747,16 +785,3 @@ fn skip(msg: &Message, config: &Config) -> bool { false } - -/// Get the extended rcode of a message. -fn get_opt_rcode(msg: &Message) -> OptRcode { - let opt = msg.opt(); - match opt { - Some(opt) => opt.rcode(msg.header()), - None => { - // Convert Rcode to OptRcode, this should be part of - // OptRcode - OptRcode::from_int(msg.header().rcode().to_int() as u16) - } - } -} diff --git a/src/net/client/request.rs b/src/net/client/request.rs index b89bf0ee..5318452c 100644 --- a/src/net/client/request.rs +++ b/src/net/client/request.rs @@ -4,12 +4,12 @@ #![warn(clippy::missing_docs_in_private_items)] use crate::base::iana::Rcode; -use crate::base::message::CopyRecordsError; +use crate::base::message::{CopyRecordsError, ShortMessage}; use crate::base::message_builder::{ AdditionalBuilder, MessageBuilder, PushError, StaticCompressor, }; use crate::base::opt::{ComposeOptData, LongOptData, OptRecord}; -use crate::base::wire::Composer; +use crate::base::wire::{Composer, ParseError}; use crate::base::{Header, Message, ParsedDname, Rtype}; use crate::rdata::AllRecordData; use bytes::Bytes; @@ -33,11 +33,11 @@ pub trait ComposeRequest: Debug + Send + Sync { ) -> Result<(), CopyRecordsError>; /// Create a message that captures the recorded changes. - fn to_message(&self) -> Message>; + fn to_message(&self) -> Result>, Error>; /// Create a message that captures the recorded changes and convert to /// a Vec. - fn to_vec(&self) -> Vec; + fn to_vec(&self) -> Result, Error>; /// Return a reference to a mutable Header to record changes to the header. fn header_mut(&mut self) -> &mut Header; @@ -45,6 +45,9 @@ pub trait ComposeRequest: Debug + Send + Sync { /// Set the UDP payload size. fn set_udp_payload_size(&mut self, value: u16); + /// Set the DNSSEC OK flag. + fn set_dnssec_ok(&mut self, value: bool); + /// Add an EDNS option. fn add_opt( &mut self, @@ -63,7 +66,10 @@ pub trait ComposeRequest: Debug + Send + Sync { /// However, the use of 'dyn Request' in redundant currently prevents that. pub trait SendRequest { /// Request function that takes a ComposeRequest type. - fn send_request(&self, request_msg: CR) -> Box; + fn send_request( + &self, + request_msg: CR, + ) -> Box; } //------------ GetResponse --------------------------------------------------- @@ -79,7 +85,12 @@ pub trait GetResponse: Debug { fn get_response( &mut self, ) -> Pin< - Box, Error>> + Send + '_>, + Box< + dyn Future, Error>> + + Send + + Sync + + '_, + >, >; } @@ -202,13 +213,13 @@ impl + Clone + Debug + Octets + Send + Sync + 'static> Ok(()) } - fn to_vec(&self) -> Vec { - let msg = self.to_message(); - msg.as_octets().clone() + fn to_vec(&self) -> Result, Error> { + let msg = self.to_message()?; + Ok(msg.as_octets().clone()) } - fn to_message(&self) -> Message> { - self.to_message_impl().unwrap() + fn to_message(&self) -> Result>, Error> { + self.to_message_impl() } fn header_mut(&mut self) -> &mut Header { @@ -219,6 +230,10 @@ impl + Clone + Debug + Octets + Send + Sync + 'static> self.opt_mut().set_udp_payload_size(value); } + fn set_dnssec_ok(&mut self, value: bool) { + self.opt_mut().set_dnssec_ok(value); + } + fn add_opt( &mut self, opt: &impl ComposeOptData, @@ -321,6 +336,18 @@ impl From for Error { } } +impl From for Error { + fn from(_: ParseError) -> Self { + Self::MessageParseError + } +} + +impl From for Error { + fn from(_: ShortMessage) -> Self { + Self::ShortMessage + } +} + impl From for Error { fn from(err: super::dgram::QueryError) -> Self { Self::Dgram(err.into()) diff --git a/src/net/client/stream.rs b/src/net/client/stream.rs index 29b69d55..5ac532d4 100644 --- a/src/net/client/stream.rs +++ b/src/net/client/stream.rs @@ -173,7 +173,10 @@ impl Clone for Connection { impl SendRequest for Connection { - fn send_request(&self, request_msg: Req) -> Box { + fn send_request( + &self, + request_msg: Req, + ) -> Box { Box::new(self.get_request(request_msg)) } } @@ -183,7 +186,9 @@ impl SendRequest /// An active request. pub struct Request { /// The underlying future. - fut: Pin, Error>> + Send>>, + fut: Pin< + Box, Error>> + Send + Sync>, + >, } impl Request { @@ -197,7 +202,12 @@ impl GetResponse for Request { fn get_response( &mut self, ) -> Pin< - Box, Error>> + Send + '_>, + Box< + dyn Future, Error>> + + Send + + Sync + + '_, + >, > { Box::pin(self.get_response_impl()) } @@ -850,7 +860,7 @@ mod test { let mut queries = Queries::new(); for i in 0..12 { - let (idx, item) = queries.insert(i).unwrap(); + let (idx, item) = queries.insert(i).expect("test failed"); idxs[i] = Some(idx); assert_eq!(i, *item); } @@ -858,7 +868,9 @@ mod test { assert_eq!(queries.vec.iter().flatten().count(), 12); for i in [1, 2, 3, 4, 7, 9] { - let item = queries.try_remove(idxs[i].unwrap()).unwrap(); + let item = queries + .try_remove(idxs[i].expect("test failed")) + .expect("test failed"); assert_eq!(i, item); idxs[i] = None; } @@ -866,7 +878,7 @@ mod test { assert_eq!(queries.vec.iter().flatten().count(), 6); for i in 12..20 { - let (idx, item) = queries.insert(i).unwrap(); + let (idx, item) = queries.insert(i).expect("test failed"); idxs[i] = Some(idx); assert_eq!(i, *item); } @@ -875,7 +887,7 @@ mod test { for i in 0..20 { if let Some(idx) = idxs[i] { - let item = queries.try_remove(idx).unwrap(); + let item = queries.try_remove(idx).expect("test failed"); assert_eq!(i, item); } } diff --git a/src/net/mod.rs b/src/net/mod.rs index 5b7e9435..62b9359d 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -1,12 +1,12 @@ //! Sending and receiving DNS messages. //! -//! This module provides types, traits, and function for sending and receiving +//! This module provides types, traits, and functions for sending and receiving //! DNS messages. //! //! Currently, the module only provides the unstable #![cfg_attr(feature = "unstable-client-transport", doc = " [`client`]")] #![cfg_attr(not(feature = "unstable-client-transport"), doc = " `client`")] -//! sub-module intended for sending requests and receiving responses to them. +//! sub-module for sending requests and receiving responses to them. #![cfg_attr( not(feature = "unstable-client-transport"), doc = " The `unstable-client-transport` feature is necessary to enable this module." diff --git a/src/utils/config.rs b/src/utils/config.rs new file mode 100644 index 00000000..36a124c8 --- /dev/null +++ b/src/utils/config.rs @@ -0,0 +1,36 @@ +use core::cmp; + +//------------ DefMinMax ----------------------------------------------------- + +/// The default, minimum, and maximum values for a config variable. +#[derive(Clone, Copy)] +pub struct DefMinMax { + /// The default value, + def: T, + + /// The minimum value, + min: T, + + /// The maximum value, + max: T, +} + +impl DefMinMax { + /// Creates a new value. + pub const fn new(def: T, min: T, max: T) -> Self { + Self { def, min, max } + } + + /// Returns the default value. + pub fn default(self) -> T { + self.def + } + + /// Trims the given value to fit into the minimum/maximum range, inclusive. + pub fn limit(self, value: T) -> T + where + T: Ord, + { + cmp::max(self.min, cmp::min(self.max, value)) + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 2fef1319..58472471 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,7 +1,8 @@ //! Various utility modules. -//! -//! This should probably be separate crates … pub mod base16; pub mod base32; pub mod base64; + +#[cfg(feature = "net")] +pub(crate) mod config; diff --git a/test-data/basic.rpl b/test-data/basic.rpl index 72f453fe..704944c9 100644 --- a/test-data/basic.rpl +++ b/test-data/basic.rpl @@ -131,7 +131,7 @@ RANGE_BEGIN 0 100 ENTRY_BEGIN MATCH opcode qtype qname ADJUST copy_id copy_query -REPLY QR RD NOERROR +REPLY QR RD RA NOERROR SECTION QUESTION example.com. IN A SECTION ANSWER diff --git a/test-data/client-cache/cache_aa.rpl b/test-data/client-cache/cache_aa.rpl new file mode 100644 index 00000000..c25b0437 --- /dev/null +++ b/test-data/client-cache/cache_aa.rpl @@ -0,0 +1,198 @@ +; Test if caching clears the AA flag. We issue the same query twice. +; First we expect the AA bit to be set. For the second query, +; make sure that we get an answer from the cache. Make sure the AA flag is +; clear. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD AA NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD AA NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD AA NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_ad.rpl b/test-data/client-cache/cache_ad.rpl new file mode 100644 index 00000000..57cd0d24 --- /dev/null +++ b/test-data/client-cache/cache_ad.rpl @@ -0,0 +1,198 @@ +; Test if AD caching works properly. First we issue a query with the AD flag +; set and we receive and answer also with the AD flag set. Then we +; issue a test with the AD flag clear. Make sure that we get an answer from +; the cache. Make sure the AD flags is clear. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname AD +ADJUST copy_id copy_query +REPLY QR RD AD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD AD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD AD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD AD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_ad_rev.rpl b/test-data/client-cache/cache_ad_rev.rpl new file mode 100644 index 00000000..280570bb --- /dev/null +++ b/test-data/client-cache/cache_ad_rev.rpl @@ -0,0 +1,208 @@ +; Test if AD caching works properly. First we issue a query with the AD flag +; clear and we receive and answer also with the AD flag clear. Then we +; issue a test with the AD flag set. Make sure that we don't get an answer from +; the cache. Make sure the AD flags is set. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname AD +ADJUST copy_id copy_query +REPLY QR RD AD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD AD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD AD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD AD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_broken_nodata.rpl b/test-data/client-cache/cache_broken_nodata.rpl new file mode 100644 index 00000000..f7c1e355 --- /dev/null +++ b/test-data/client-cache/cache_broken_nodata.rpl @@ -0,0 +1,196 @@ +; Make sure a broken NODATA response is not cached. We issue the same query +; twice. For the second query, make sure that we do not get an answer from +; the cache. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN SSHFP +SECTION AUTHORITY +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN SSHFP +SECTION AUTHORITY +example.com. 1800 IN SOA ns.icann.org. noc.dns.icann.org. 2024013008 7200 3600 1209600 3600 +ENTRY_END + + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN SSHFP +SECTION AUTHORITY +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN SSHFP +SECTION AUTHORITY +example.com. 1800 IN SOA ns.icann.org. noc.dns.icann.org. 2024013008 7200 3600 1209600 3600 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_case.rpl b/test-data/client-cache/cache_case.rpl new file mode 100644 index 00000000..99407c9a --- /dev/null +++ b/test-data/client-cache/cache_case.rpl @@ -0,0 +1,198 @@ +; Test if case-insensitive caching works properly. First we issue a query +; with the first letter capitalized and the rest lower case. +; Then we issue a test with another letter capitalized. Make sure that we +; get an answer from the cache. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +Example.com. IN AAAA +SECTION ANSWER +Example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +eXample.com. IN AAAA +SECTION ANSWER +eXample.com. IN AAAA 2001:DB8::1 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +Example.com. IN AAAA +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +Example.com. IN AAAA +SECTION ANSWER +Example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +eXample.com. IN AAAA +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +eXample.com. IN AAAA +SECTION ANSWER +Example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_cd.rpl b/test-data/client-cache/cache_cd.rpl new file mode 100644 index 00000000..89f16f24 --- /dev/null +++ b/test-data/client-cache/cache_cd.rpl @@ -0,0 +1,217 @@ +; Test if CD caching works properly. First we issue a query with the CD flag +; set. Then we issue a test with the CD flag clear. Make sure that we +; don't get an answer from the cache. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname CD +ADJUST copy_id copy_query +REPLY QR RD CD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::2 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname CD +ADJUST copy_id copy_query +REPLY QR RD CD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD CD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD CD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_cd_rev.rpl b/test-data/client-cache/cache_cd_rev.rpl new file mode 100644 index 00000000..f03dc2a9 --- /dev/null +++ b/test-data/client-cache/cache_cd_rev.rpl @@ -0,0 +1,207 @@ +; Test if CD caching works properly. First we issue a query with the CD flag +; clear. Then we issue a test with the CD flag set. Make sure that we +; don't get an answer from the cache. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname CD +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname CD +ADJUST copy_id copy_query +REPLY QR RD CD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD CD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD CD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_chaos.rpl b/test-data/client-cache/cache_chaos.rpl new file mode 100644 index 00000000..dc960e2c --- /dev/null +++ b/test-data/client-cache/cache_chaos.rpl @@ -0,0 +1,198 @@ +; Make sure a class other than IN is not cached. We issue the same query +; twice. For the second query, make sure that we do not get an answer from +; the cache. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +version.server. CH TXT +SECTION ANSWER +version.server. CH TXT "example server" +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +version.server. CH TXT +SECTION ANSWER +version.server. CH TXT "domain server" +ENTRY_END + + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +version.server. CH TXT +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +version.server. CH TXT +SECTION ANSWER +version.server. CH TXT "example server" +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +version.server. CH TXT +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +version.server. CH TXT +SECTION ANSWER +version.server. CH TXT "domain server" +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_delegation.rpl b/test-data/client-cache/cache_delegation.rpl new file mode 100644 index 00000000..db612d21 --- /dev/null +++ b/test-data/client-cache/cache_delegation.rpl @@ -0,0 +1,199 @@ +; Test if delegations are cached. We issue the same query twice. +; For the second query, make sure that we get an answer from the cache. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN A +SECTION AUTHORITY +example.com. 172800 IN NS a.iana-servers.net. +example.com. 172800 IN NS b.iana-servers.net. +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD AA NOERROR +SECTION QUESTION +example.com. IN A +SECTION ANSWER +example.com. IN A 1.2.3.4 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN A +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN A +SECTION AUTHORITY +example.com. 172800 IN NS a.iana-servers.net. +example.com. 172800 IN NS b.iana-servers.net. +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN A +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN A +SECTION AUTHORITY +example.com. 172800 IN NS a.iana-servers.net. +example.com. 172800 IN NS b.iana-servers.net. +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_do_nsec.rpl b/test-data/client-cache/cache_do_nsec.rpl new file mode 100644 index 00000000..fa4e779a --- /dev/null +++ b/test-data/client-cache/cache_do_nsec.rpl @@ -0,0 +1,225 @@ +; Test if DO caching works properly. First we issue a query with the DO flag +; set and we receive an answer with NSEC and RRSIG records and with the AD +; flag set. Then we issue a test with the DO flag clear and AD clear. Make sure +; that we get an answer from the cache and that the DNSSEC records are +; stripped and the AD is clear. Then issue a test with AD set. Make sure we +; get the same answer as with AD clear but now AD should be set. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test DO flag set followed by DO flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname DO +ADJUST copy_id copy_query +REPLY QR RD AD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN TXT +SECTION ANSWER +doesnotexist.example.com. IN TXT "star" +doesnotexist.example.com. 60 IN RRSIG TXT 8 2 60 20240229091551 20240201091551 63939 example.com. l01lAi1yB6JvZtWoil1ZGhESrT/2sqr2I3YDey/Ub3npAJ/6TctahmIInnQHispcCZomv0N6/aIKpJSCvRrs5XZSjdD2mspnzyFSmYev6Lbf9spes3HkZtjwU7ANXcQN9g8eck4XIe6+KRFi40FCSpH/Ldf9igaCPGGz6IxEc04LdfvUN6BcJGHZ98bdbT/J3IjnJAwDUYWXZUKqfZSdoapUEtVga12Lk3cvv8/WKWNlNFYj3Qz42QOuBkQTlVV2sqGKtHkzhn+wkQyoUEbkNTaqtsVIPW3xNbvLpLi2laPOQic9XBEbs7tW9H9lXofn1168R0HtkCe7TRMZKoBFLQ== +SECTION AUTHORITY +does-exist.example.com. 3600 IN NSEC exists.example.com. A RRSIG NSEC +does-exist.example.com. 3600 IN RRSIG NSEC 8 3 3600 20240229091553 20240201091553 63939 example.com. WezFhdCGSG4azmZkeBXxILnfwWuvhkjijsfpkcKqSfhOvQXPEjY0T0Gm4FoHOGieReGPQi4+Jgqp5AjC08yQwphPR9Cq3IsIVCAhPEzh1E9pVRmAFrlf+k/EnxCZ7aN7rq9rrFsx1jK5JtB1hUuvBLpVsVwIqx5yM7LohxWwhnTj+JqqiUbMVp0BcGzz5UubaSIlyJjiGc5ra79X6PGp2Ql19+krqEzaqrVuuD044+BBQWRvG3PzIEQwC1iEumKcfyWb+4F6s806f3NqvliBZl4nxVZUdl2vwhq2+gguN/+o6l3EjySvlKUFu6z5pto+qC9qrML2EM5mPETm253pVg== +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD AD NOERROR +SECTION QUESTION +example.com. IN A +SECTION ANSWER +example.com. IN A 1.2.3.4 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +doesnotexist.example.com. IN TXT +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD AD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN TXT +SECTION ANSWER +doesnotexist.example.com. IN TXT "star" +doesnotexist.example.com. 60 IN RRSIG TXT 8 2 60 20240229091551 20240201091551 63939 example.com. l01lAi1yB6JvZtWoil1ZGhESrT/2sqr2I3YDey/Ub3npAJ/6TctahmIInnQHispcCZomv0N6/aIKpJSCvRrs5XZSjdD2mspnzyFSmYev6Lbf9spes3HkZtjwU7ANXcQN9g8eck4XIe6+KRFi40FCSpH/Ldf9igaCPGGz6IxEc04LdfvUN6BcJGHZ98bdbT/J3IjnJAwDUYWXZUKqfZSdoapUEtVga12Lk3cvv8/WKWNlNFYj3Qz42QOuBkQTlVV2sqGKtHkzhn+wkQyoUEbkNTaqtsVIPW3xNbvLpLi2laPOQic9XBEbs7tW9H9lXofn1168R0HtkCe7TRMZKoBFLQ== +SECTION AUTHORITY +does-exist.example.com. 3600 IN NSEC exists.example.com. A RRSIG NSEC +does-exist.example.com. 3600 IN RRSIG NSEC 8 3 3600 20240229091553 20240201091553 63939 example.com. WezFhdCGSG4azmZkeBXxILnfwWuvhkjijsfpkcKqSfhOvQXPEjY0T0Gm4FoHOGieReGPQi4+Jgqp5AjC08yQwphPR9Cq3IsIVCAhPEzh1E9pVRmAFrlf+k/EnxCZ7aN7rq9rrFsx1jK5JtB1hUuvBLpVsVwIqx5yM7LohxWwhnTj+JqqiUbMVp0BcGzz5UubaSIlyJjiGc5ra79X6PGp2Ql19+krqEzaqrVuuD044+BBQWRvG3PzIEQwC1iEumKcfyWb+4F6s806f3NqvliBZl4nxVZUdl2vwhq2+gguN/+o6l3EjySvlKUFu6z5pto+qC9qrML2EM5mPETm253pVg== +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +doesnotexist.example.com. IN TXT +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN TXT +SECTION ANSWER +doesnotexist.example.com. IN TXT "star" +ENTRY_END + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD AD +SECTION QUESTION +doesnotexist.example.com. IN TXT +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD AD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN TXT +SECTION ANSWER +doesnotexist.example.com. IN TXT "star" +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_do_nsec3.rpl b/test-data/client-cache/cache_do_nsec3.rpl new file mode 100644 index 00000000..47b7213e --- /dev/null +++ b/test-data/client-cache/cache_do_nsec3.rpl @@ -0,0 +1,231 @@ +; Test if DO caching works properly. First we issue a query with the DO flag +; set and we receive an answer with NSEC3 and RRSIG records and with the AD +; flag set. Then we issue a test with the DO flag clear and AD set. Make sure +; that we get an answer from the cache and that the DNSSEC records are +; stripped and the AD is set. Then issue a test with AD clear. Make sure we +; get the same answer as with AD set but now AD should be clear. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test DO flag set followed by DO flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname DO +ADJUST copy_id copy_query +REPLY QR RD AD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN TXT +SECTION AUTHORITY +example.com. 900 IN SOA a.example.net. nstld.example.com. 1706885485 1800 900 604800 86400 +example.com. 900 IN RRSIG SOA 13 1 900 20240209145125 20240202134125 4534 example.com. uAEEE4oYH9x/QE/5xi41m5TkELdDLEQ+kqoIag/NcISzf//phx+i5ezFPUY3Y/XnaeZLIKFdGVx6D1oFZmLxpA== +CK0POJMG874LJREF7EFN8430QVIT8BSM.example.com. 21600 IN NSEC3 1 1 0 - ck0q2d6ni4i7eqh8na30ns61o48ul8g5 NS SOA RRSIG DNSKEY NSEC3PARAM +CK0POJMG874LJREF7EFN8430QVIT8BSM.example.com. 21600 IN RRSIG NSEC3 13 2 86400 20240206052637 20240130041637 4534 example.com. +NFtkRVj+SxKGDAJypPm9byEhYAkLFqco9kgi1cI+bO4kJ55Zd/9QFay3xzFIPduA7pjBrWthR9uhHE0Qnf5OA== +7K5NUBQUB56BBNKQJ6B485STCN1RQ6HT.example.com. 21600 IN NSEC3 1 1 0 - 7k5oetj08ci9mdtvqueq1gq0dgp84qe8 NS DS RRSIG +7K5NUBQUB56BBNKQJ6B485STCN1RQ6HT.example.com. 21600 IN RRSIG NSEC3 13 2 86400 20240206080851 20240130065851 4534 example.com. vYruIKBWnObM4V/+aqPmoxdAi5+UvAQsWBH6i3SbgT7GChssl7FcX8UFlQfeUPilc3lriST4FTXGswGa5111XA== +3RL2Q58205687C8I9KC9MV46DGHCNS45.example.com. 21600 IN NSEC3 1 1 0 - 3rl2shvumc300iuc2tdl4vml2hnf0o7i NS DS RRSIG +3RL2Q58205687C8I9KC9MV46DGHCNS45.example.com. 21600 IN RRSIG NSEC3 13 2 86400 20240209055857 20240202044857 4534 example.com. TgLjE/venWu8OxOn7iLvdt87u4aojGY4Nh7Susc7xXWAKir5s1yjoW/R/7E8E/9vEouJGKViZ82NQ3PMec3jbw== +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD AD NOERROR +SECTION QUESTION +example.com. IN A +SECTION ANSWER +example.com. IN A 1.2.3.4 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +doesnotexist.example.com. IN TXT +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD AD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN TXT +SECTION AUTHORITY +example.com. 900 IN SOA a.example.net. nstld.example.com. 1706885485 1800 900 604800 86400 +example.com. 900 IN RRSIG SOA 13 1 900 20240209145125 20240202134125 4534 example.com. uAEEE4oYH9x/QE/5xi41m5TkELdDLEQ+kqoIag/NcISzf//phx+i5ezFPUY3Y/XnaeZLIKFdGVx6D1oFZmLxpA== +CK0POJMG874LJREF7EFN8430QVIT8BSM.example.com. 21600 IN NSEC3 1 1 0 - ck0q2d6ni4i7eqh8na30ns61o48ul8g5 NS SOA RRSIG DNSKEY NSEC3PARAM +CK0POJMG874LJREF7EFN8430QVIT8BSM.example.com. 21600 IN RRSIG NSEC3 13 2 86400 20240206052637 20240130041637 4534 example.com. +NFtkRVj+SxKGDAJypPm9byEhYAkLFqco9kgi1cI+bO4kJ55Zd/9QFay3xzFIPduA7pjBrWthR9uhHE0Qnf5OA== +7K5NUBQUB56BBNKQJ6B485STCN1RQ6HT.example.com. 21600 IN NSEC3 1 1 0 - 7k5oetj08ci9mdtvqueq1gq0dgp84qe8 NS DS RRSIG +7K5NUBQUB56BBNKQJ6B485STCN1RQ6HT.example.com. 21600 IN RRSIG NSEC3 13 2 86400 20240206080851 20240130065851 4534 example.com. vYruIKBWnObM4V/+aqPmoxdAi5+UvAQsWBH6i3SbgT7GChssl7FcX8UFlQfeUPilc3lriST4FTXGswGa5111XA== +3RL2Q58205687C8I9KC9MV46DGHCNS45.example.com. 21600 IN NSEC3 1 1 0 - 3rl2shvumc300iuc2tdl4vml2hnf0o7i NS DS RRSIG +3RL2Q58205687C8I9KC9MV46DGHCNS45.example.com. 21600 IN RRSIG NSEC3 13 2 86400 20240209055857 20240202044857 4534 example.com. TgLjE/venWu8OxOn7iLvdt87u4aojGY4Nh7Susc7xXWAKir5s1yjoW/R/7E8E/9vEouJGKViZ82NQ3PMec3jbw== +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +doesnotexist.example.com. IN TXT +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN TXT +SECTION AUTHORITY +example.com. 900 IN SOA a.example.net. nstld.example.com. 1706885485 1800 900 604800 86400 +ENTRY_END + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD AD +SECTION QUESTION +doesnotexist.example.com. IN TXT +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD AD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN TXT +SECTION AUTHORITY +example.com. 900 IN SOA a.example.net. nstld.example.com. 1706885485 1800 900 604800 86400 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_do_q_nsec.rpl b/test-data/client-cache/cache_do_q_nsec.rpl new file mode 100644 index 00000000..faf85a34 --- /dev/null +++ b/test-data/client-cache/cache_do_q_nsec.rpl @@ -0,0 +1,250 @@ +; Test if DO caching works properly. First we issue a query with QTYPE equals +; RRSIG and the DO flag set and we receive an answer with NSEC and RRSIG +; records and with the AD flag set. Then we issue a test with the DO flag +; clear and AD clear. Make sure we don't get an answer from the cache because +; queries for RRSIG, NSEC, and NSEC3 are special. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test DO flag set followed by DO flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname DO +ADJUST copy_id copy_query +REPLY QR RD AD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN NSEC +SECTION ANSWER +doesnotexist.example.com. 3600 IN NSEC a.example.com. TXT RRSIG NSEC +doesnotexist.example.com. 3600 IN RRSIG NSEC 8 2 3600 20240302200620 20240203200620 63939 example.com. vtM5AjaxYJLqDSXGuPkhtjH1S+YLhMkgg1YVrUpxt2QuY2APIjwUThKN3bPsht4vt/ydVchIMfkHeZRTV4a4yMfuDwr9j5gi/RhrnOLEfQyrBMT0chmKpnh/3gAjHSj9rWNwh1Lyk4p6xSwcKTkYWPHR2q6wPG9Kt6hWQpbLiU66lZE3q5DUvQmKhhorT+sdwVLbpgcilrRQ7Wj5ocXVMfVWmNPbpAg0trruqB7WbkWjkReb+v0bUCDRcGZnd+GJD+YA9rvn7cJHiJQHrURE1Gb6OdxHZslr3NTo+wizbGXN01SLOZHaLTqk+ke8qPW6RRrVwZQyk/13kPMN8Lnr7Q== +SECTION AUTHORITY +does-exist.example.com. 3600 IN NSEC exists.example.com. A RRSIG NSEC +does-exist.example.com. 3600 IN RRSIG NSEC 8 3 3600 20240229091553 20240201091553 63939 example.com. WezFhdCGSG4azmZkeBXxILnfwWuvhkjijsfpkcKqSfhOvQXPEjY0T0Gm4FoHOGieReGPQi4+Jgqp5AjC08yQwphPR9Cq3IsIVCAhPEzh1E9pVRmAFrlf+k/EnxCZ7aN7rq9rrFsx1jK5JtB1hUuvBLpVsVwIqx5yM7LohxWwhnTj+JqqiUbMVp0BcGzz5UubaSIlyJjiGc5ra79X6PGp2Ql19+krqEzaqrVuuD044+BBQWRvG3PzIEQwC1iEumKcfyWb+4F6s806f3NqvliBZl4nxVZUdl2vwhq2+gguN/+o6l3EjySvlKUFu6z5pto+qC9qrML2EM5mPETm253pVg== +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN NSEC +SECTION ANSWER +doesnotexist.example.com. 3600 IN NSEC aa.example.com. TXT RRSIG NSEC +ENTRY_END + +RANGE_END +; a.gtld-servers.net. +RANGE_BEGIN 20 29 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname AD +ADJUST copy_id copy_query +REPLY QR RD AD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN NSEC +SECTION ANSWER +doesnotexist.example.com. 3600 IN NSEC aaa.example.com. TXT RRSIG NSEC +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN NSEC +SECTION ANSWER +doesnotexist.example.com. 3600 IN NSEC aa.example.com. TXT RRSIG NSEC +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +doesnotexist.example.com. IN NSEC +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD AD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN NSEC +SECTION ANSWER +doesnotexist.example.com. 3600 IN NSEC a.example.com. TXT RRSIG NSEC +doesnotexist.example.com. 3600 IN RRSIG NSEC 8 2 3600 20240302200620 20240203200620 63939 example.com. vtM5AjaxYJLqDSXGuPkhtjH1S+YLhMkgg1YVrUpxt2QuY2APIjwUThKN3bPsht4vt/ydVchIMfkHeZRTV4a4yMfuDwr9j5gi/RhrnOLEfQyrBMT0chmKpnh/3gAjHSj9rWNwh1Lyk4p6xSwcKTkYWPHR2q6wPG9Kt6hWQpbLiU66lZE3q5DUvQmKhhorT+sdwVLbpgcilrRQ7Wj5ocXVMfVWmNPbpAg0trruqB7WbkWjkReb+v0bUCDRcGZnd+GJD+YA9rvn7cJHiJQHrURE1Gb6OdxHZslr3NTo+wizbGXN01SLOZHaLTqk+ke8qPW6RRrVwZQyk/13kPMN8Lnr7Q== + +SECTION AUTHORITY +does-exist.example.com. 3600 IN NSEC exists.example.com. A RRSIG NSEC +does-exist.example.com. 3600 IN RRSIG NSEC 8 3 3600 20240229091553 20240201091553 63939 example.com. WezFhdCGSG4azmZkeBXxILnfwWuvhkjijsfpkcKqSfhOvQXPEjY0T0Gm4FoHOGieReGPQi4+Jgqp5AjC08yQwphPR9Cq3IsIVCAhPEzh1E9pVRmAFrlf+k/EnxCZ7aN7rq9rrFsx1jK5JtB1hUuvBLpVsVwIqx5yM7LohxWwhnTj+JqqiUbMVp0BcGzz5UubaSIlyJjiGc5ra79X6PGp2Ql19+krqEzaqrVuuD044+BBQWRvG3PzIEQwC1iEumKcfyWb+4F6s806f3NqvliBZl4nxVZUdl2vwhq2+gguN/+o6l3EjySvlKUFu6z5pto+qC9qrML2EM5mPETm253pVg== +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +doesnotexist.example.com. IN NSEC +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN NSEC +SECTION ANSWER +doesnotexist.example.com. 3600 IN NSEC aa.example.com. TXT RRSIG NSEC +ENTRY_END + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD AD +SECTION QUESTION +doesnotexist.example.com. IN NSEC +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD AD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN NSEC +SECTION ANSWER +doesnotexist.example.com. 3600 IN NSEC aaa.example.com. TXT RRSIG NSEC +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_do_q_rrsig.rpl b/test-data/client-cache/cache_do_q_rrsig.rpl new file mode 100644 index 00000000..050fb6b7 --- /dev/null +++ b/test-data/client-cache/cache_do_q_rrsig.rpl @@ -0,0 +1,254 @@ +; Test if DO caching works properly. First we issue a query with QTYPE equals +; RRSIG and the DO flag set and we receive an answer with NSEC and RRSIG +; records and with the AD flag set. Then we issue a test with the DO flag +; clear and AD clear. Make sure we don't get an answer from the cache because +; queries for RRSIG, NSEC, and NSEC3 are special. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test DO flag set followed by DO flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname DO +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN RRSIG +SECTION ANSWER +doesnotexist.example.com. 60 IN RRSIG TXT 8 2 60 20240229091551 20240201091551 63939 example.com. l01lAi1yB6JvZtWoil1ZGhESrT/2sqr2I3YDey/Ub3npAJ/6TctahmIInnQHispcCZomv0N6/aIKpJSCvRrs5XZSjdD2mspnzyFSmYev6Lbf9spes3HkZtjwU7ANXcQN9g8eck4XIe6+KRFi40FCSpH/Ldf9igaCPGGz6IxEc04LdfvUN6BcJGHZ98bdbT/J3IjnJAwDUYWXZUKqfZSdoapUEtVga12Lk3cvv8/WKWNlNFYj3Qz42QOuBkQTlVV2sqGKtHkzhn+wkQyoUEbkNTaqtsVIPW3xNbvLpLi2laPOQic9XBEbs7tW9H9lXofn1168R0HtkCe7TRMZKoBFLQ== +doesnotexist.example.com. 3591 IN RRSIG NSEC 8 2 3600 20240302200620 20240203200620 63939 example.com. vtM5AjaxYJLqDSXGuPkhtjH1S+YLhMkgg1YVrUpxt2QuY2APIjwUThKN3bPsht4vt/ydVchIMfkHeZRTV4a4yMfuDwr9j5gi/RhrnOLEfQyrBMT0chmKpnh/3gAjHSj9rWNwh1Lyk4p6xSwcKTkYWPHR2q6wPG9Kt6hWQpbLiU66lZE3q5DUvQmKhhorT+sdwVLbpgcilrRQ7Wj5ocXVMfVWmNPbpAg0trruqB7WbkWjkReb+v0bUCDRcGZnd+GJD+YA9rvn7cJHiJQHrURE1Gb6OdxHZslr3NTo+wizbGXN01SLOZHaLTqk+ke8qPW6RRrVwZQyk/13kPMN8Lnr7Q== +SECTION AUTHORITY +does-exist.example.com. 3600 IN NSEC exists.example.com. A RRSIG NSEC +does-exist.example.com. 3600 IN RRSIG NSEC 8 3 3600 20240229091553 20240201091553 63939 example.com. WezFhdCGSG4azmZkeBXxILnfwWuvhkjijsfpkcKqSfhOvQXPEjY0T0Gm4FoHOGieReGPQi4+Jgqp5AjC08yQwphPR9Cq3IsIVCAhPEzh1E9pVRmAFrlf+k/EnxCZ7aN7rq9rrFsx1jK5JtB1hUuvBLpVsVwIqx5yM7LohxWwhnTj+JqqiUbMVp0BcGzz5UubaSIlyJjiGc5ra79X6PGp2Ql19+krqEzaqrVuuD044+BBQWRvG3PzIEQwC1iEumKcfyWb+4F6s806f3NqvliBZl4nxVZUdl2vwhq2+gguN/+o6l3EjySvlKUFu6z5pto+qC9qrML2EM5mPETm253pVg== +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN RRSIG +SECTION ANSWER +doesnotexist.example.com. 60 IN RRSIG TXT 6 6 66 20240229091551 20240201091551 63939 example.com. l01lAi1yB6JvZtWoil1ZGhESrT/2sqr2I3YDey/Ub3npAJ/6TctahmIInnQHispcCZomv0N6/aIKpJSCvRrs5XZSjdD2mspnzyFSmYev6Lbf9spes3HkZtjwU7ANXcQN9g8eck4XIe6+KRFi40FCSpH/Ldf9igaCPGGz6IxEc04LdfvUN6BcJGHZ98bdbT/J3IjnJAwDUYWXZUKqfZSdoapUEtVga12Lk3cvv8/WKWNlNFYj3Qz42QOuBkQTlVV2sqGKtHkzhn+wkQyoUEbkNTaqtsVIPW3xNbvLpLi2laPOQic9XBEbs7tW9H9lXofn1168R0HtkCe7TRMZKoBFLQ== +doesnotexist.example.com. 3591 IN RRSIG NSEC 6 6 66 20240302200620 20240203200620 63939 example.com. vtM5AjaxYJLqDSXGuPkhtjH1S+YLhMkgg1YVrUpxt2QuY2APIjwUThKN3bPsht4vt/ydVchIMfkHeZRTV4a4yMfuDwr9j5gi/RhrnOLEfQyrBMT0chmKpnh/3gAjHSj9rWNwh1Lyk4p6xSwcKTkYWPHR2q6wPG9Kt6hWQpbLiU66lZE3q5DUvQmKhhorT+sdwVLbpgcilrRQ7Wj5ocXVMfVWmNPbpAg0trruqB7WbkWjkReb+v0bUCDRcGZnd+GJD+YA9rvn7cJHiJQHrURE1Gb6OdxHZslr3NTo+wizbGXN01SLOZHaLTqk+ke8qPW6RRrVwZQyk/13kPMN8Lnr7Q== +ENTRY_END + +RANGE_END +; a.gtld-servers.net. +RANGE_BEGIN 20 29 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname AD +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN RRSIG +SECTION ANSWER +doesnotexist.example.com. 60 IN RRSIG TXT 7 7 77 20240229091551 20240201091551 63939 example.com. l01lAi1yB6JvZtWoil1ZGhESrT/2sqr2I3YDey/Ub3npAJ/6TctahmIInnQHispcCZomv0N6/aIKpJSCvRrs5XZSjdD2mspnzyFSmYev6Lbf9spes3HkZtjwU7ANXcQN9g8eck4XIe6+KRFi40FCSpH/Ldf9igaCPGGz6IxEc04LdfvUN6BcJGHZ98bdbT/J3IjnJAwDUYWXZUKqfZSdoapUEtVga12Lk3cvv8/WKWNlNFYj3Qz42QOuBkQTlVV2sqGKtHkzhn+wkQyoUEbkNTaqtsVIPW3xNbvLpLi2laPOQic9XBEbs7tW9H9lXofn1168R0HtkCe7TRMZKoBFLQ== +doesnotexist.example.com. 3591 IN RRSIG NSEC 7 7 77 20240302200620 20240203200620 63939 example.com. vtM5AjaxYJLqDSXGuPkhtjH1S+YLhMkgg1YVrUpxt2QuY2APIjwUThKN3bPsht4vt/ydVchIMfkHeZRTV4a4yMfuDwr9j5gi/RhrnOLEfQyrBMT0chmKpnh/3gAjHSj9rWNwh1Lyk4p6xSwcKTkYWPHR2q6wPG9Kt6hWQpbLiU66lZE3q5DUvQmKhhorT+sdwVLbpgcilrRQ7Wj5ocXVMfVWmNPbpAg0trruqB7WbkWjkReb+v0bUCDRcGZnd+GJD+YA9rvn7cJHiJQHrURE1Gb6OdxHZslr3NTo+wizbGXN01SLOZHaLTqk+ke8qPW6RRrVwZQyk/13kPMN8Lnr7Q== +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN RRSIG +SECTION ANSWER +doesnotexist.example.com. 60 IN RRSIG TXT 6 6 66 20240229091551 20240201091551 63939 example.com. l01lAi1yB6JvZtWoil1ZGhESrT/2sqr2I3YDey/Ub3npAJ/6TctahmIInnQHispcCZomv0N6/aIKpJSCvRrs5XZSjdD2mspnzyFSmYev6Lbf9spes3HkZtjwU7ANXcQN9g8eck4XIe6+KRFi40FCSpH/Ldf9igaCPGGz6IxEc04LdfvUN6BcJGHZ98bdbT/J3IjnJAwDUYWXZUKqfZSdoapUEtVga12Lk3cvv8/WKWNlNFYj3Qz42QOuBkQTlVV2sqGKtHkzhn+wkQyoUEbkNTaqtsVIPW3xNbvLpLi2laPOQic9XBEbs7tW9H9lXofn1168R0HtkCe7TRMZKoBFLQ== +doesnotexist.example.com. 3591 IN RRSIG NSEC 6 6 66 20240302200620 20240203200620 63939 example.com. vtM5AjaxYJLqDSXGuPkhtjH1S+YLhMkgg1YVrUpxt2QuY2APIjwUThKN3bPsht4vt/ydVchIMfkHeZRTV4a4yMfuDwr9j5gi/RhrnOLEfQyrBMT0chmKpnh/3gAjHSj9rWNwh1Lyk4p6xSwcKTkYWPHR2q6wPG9Kt6hWQpbLiU66lZE3q5DUvQmKhhorT+sdwVLbpgcilrRQ7Wj5ocXVMfVWmNPbpAg0trruqB7WbkWjkReb+v0bUCDRcGZnd+GJD+YA9rvn7cJHiJQHrURE1Gb6OdxHZslr3NTo+wizbGXN01SLOZHaLTqk+ke8qPW6RRrVwZQyk/13kPMN8Lnr7Q== +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD DO +SECTION QUESTION +doesnotexist.example.com. IN RRSIG +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN RRSIG +SECTION ANSWER +doesnotexist.example.com. 60 IN RRSIG TXT 8 2 60 20240229091551 20240201091551 63939 example.com. l01lAi1yB6JvZtWoil1ZGhESrT/2sqr2I3YDey/Ub3npAJ/6TctahmIInnQHispcCZomv0N6/aIKpJSCvRrs5XZSjdD2mspnzyFSmYev6Lbf9spes3HkZtjwU7ANXcQN9g8eck4XIe6+KRFi40FCSpH/Ldf9igaCPGGz6IxEc04LdfvUN6BcJGHZ98bdbT/J3IjnJAwDUYWXZUKqfZSdoapUEtVga12Lk3cvv8/WKWNlNFYj3Qz42QOuBkQTlVV2sqGKtHkzhn+wkQyoUEbkNTaqtsVIPW3xNbvLpLi2laPOQic9XBEbs7tW9H9lXofn1168R0HtkCe7TRMZKoBFLQ== +doesnotexist.example.com. 3591 IN RRSIG NSEC 8 2 3600 20240302200620 20240203200620 63939 example.com. vtM5AjaxYJLqDSXGuPkhtjH1S+YLhMkgg1YVrUpxt2QuY2APIjwUThKN3bPsht4vt/ydVchIMfkHeZRTV4a4yMfuDwr9j5gi/RhrnOLEfQyrBMT0chmKpnh/3gAjHSj9rWNwh1Lyk4p6xSwcKTkYWPHR2q6wPG9Kt6hWQpbLiU66lZE3q5DUvQmKhhorT+sdwVLbpgcilrRQ7Wj5ocXVMfVWmNPbpAg0trruqB7WbkWjkReb+v0bUCDRcGZnd+GJD+YA9rvn7cJHiJQHrURE1Gb6OdxHZslr3NTo+wizbGXN01SLOZHaLTqk+ke8qPW6RRrVwZQyk/13kPMN8Lnr7Q== +SECTION AUTHORITY +does-exist.example.com. 3600 IN NSEC exists.example.com. A RRSIG NSEC +does-exist.example.com. 3600 IN RRSIG NSEC 8 3 3600 20240229091553 20240201091553 63939 example.com. WezFhdCGSG4azmZkeBXxILnfwWuvhkjijsfpkcKqSfhOvQXPEjY0T0Gm4FoHOGieReGPQi4+Jgqp5AjC08yQwphPR9Cq3IsIVCAhPEzh1E9pVRmAFrlf+k/EnxCZ7aN7rq9rrFsx1jK5JtB1hUuvBLpVsVwIqx5yM7LohxWwhnTj+JqqiUbMVp0BcGzz5UubaSIlyJjiGc5ra79X6PGp2Ql19+krqEzaqrVuuD044+BBQWRvG3PzIEQwC1iEumKcfyWb+4F6s806f3NqvliBZl4nxVZUdl2vwhq2+gguN/+o6l3EjySvlKUFu6z5pto+qC9qrML2EM5mPETm253pVg== +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +doesnotexist.example.com. IN RRSIG +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN RRSIG +SECTION ANSWER +doesnotexist.example.com. 60 IN RRSIG TXT 6 6 66 20240229091551 20240201091551 63939 example.com. l01lAi1yB6JvZtWoil1ZGhESrT/2sqr2I3YDey/Ub3npAJ/6TctahmIInnQHispcCZomv0N6/aIKpJSCvRrs5XZSjdD2mspnzyFSmYev6Lbf9spes3HkZtjwU7ANXcQN9g8eck4XIe6+KRFi40FCSpH/Ldf9igaCPGGz6IxEc04LdfvUN6BcJGHZ98bdbT/J3IjnJAwDUYWXZUKqfZSdoapUEtVga12Lk3cvv8/WKWNlNFYj3Qz42QOuBkQTlVV2sqGKtHkzhn+wkQyoUEbkNTaqtsVIPW3xNbvLpLi2laPOQic9XBEbs7tW9H9lXofn1168R0HtkCe7TRMZKoBFLQ== +doesnotexist.example.com. 3591 IN RRSIG NSEC 6 6 66 20240302200620 20240203200620 63939 example.com. vtM5AjaxYJLqDSXGuPkhtjH1S+YLhMkgg1YVrUpxt2QuY2APIjwUThKN3bPsht4vt/ydVchIMfkHeZRTV4a4yMfuDwr9j5gi/RhrnOLEfQyrBMT0chmKpnh/3gAjHSj9rWNwh1Lyk4p6xSwcKTkYWPHR2q6wPG9Kt6hWQpbLiU66lZE3q5DUvQmKhhorT+sdwVLbpgcilrRQ7Wj5ocXVMfVWmNPbpAg0trruqB7WbkWjkReb+v0bUCDRcGZnd+GJD+YA9rvn7cJHiJQHrURE1Gb6OdxHZslr3NTo+wizbGXN01SLOZHaLTqk+ke8qPW6RRrVwZQyk/13kPMN8Lnr7Q== +ENTRY_END + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD AD +SECTION QUESTION +doesnotexist.example.com. IN RRSIG +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +doesnotexist.example.com. IN RRSIG +SECTION ANSWER +doesnotexist.example.com. 60 IN RRSIG TXT 7 7 77 20240229091551 20240201091551 63939 example.com. l01lAi1yB6JvZtWoil1ZGhESrT/2sqr2I3YDey/Ub3npAJ/6TctahmIInnQHispcCZomv0N6/aIKpJSCvRrs5XZSjdD2mspnzyFSmYev6Lbf9spes3HkZtjwU7ANXcQN9g8eck4XIe6+KRFi40FCSpH/Ldf9igaCPGGz6IxEc04LdfvUN6BcJGHZ98bdbT/J3IjnJAwDUYWXZUKqfZSdoapUEtVga12Lk3cvv8/WKWNlNFYj3Qz42QOuBkQTlVV2sqGKtHkzhn+wkQyoUEbkNTaqtsVIPW3xNbvLpLi2laPOQic9XBEbs7tW9H9lXofn1168R0HtkCe7TRMZKoBFLQ== +doesnotexist.example.com. 3591 IN RRSIG NSEC 7 7 77 20240302200620 20240203200620 63939 example.com. vtM5AjaxYJLqDSXGuPkhtjH1S+YLhMkgg1YVrUpxt2QuY2APIjwUThKN3bPsht4vt/ydVchIMfkHeZRTV4a4yMfuDwr9j5gi/RhrnOLEfQyrBMT0chmKpnh/3gAjHSj9rWNwh1Lyk4p6xSwcKTkYWPHR2q6wPG9Kt6hWQpbLiU66lZE3q5DUvQmKhhorT+sdwVLbpgcilrRQ7Wj5ocXVMfVWmNPbpAg0trruqB7WbkWjkReb+v0bUCDRcGZnd+GJD+YA9rvn7cJHiJQHrURE1Gb6OdxHZslr3NTo+wizbGXN01SLOZHaLTqk+ke8qPW6RRrVwZQyk/13kPMN8Lnr7Q== +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_nodata.rpl b/test-data/client-cache/cache_nodata.rpl new file mode 100644 index 00000000..75250b41 --- /dev/null +++ b/test-data/client-cache/cache_nodata.rpl @@ -0,0 +1,196 @@ +; Test if NODATA is cached. We issue the same query twice. +; For the second query, make sure that we get an answer from the cache. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN SSHFP +SECTION AUTHORITY +example.com. 1800 IN SOA ns.icann.org. noc.dns.icann.org. 2024013008 7200 3600 1209600 3600 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD AA NOERROR +SECTION QUESTION +example.com. IN A +SECTION ANSWER +example.com. IN A 1.2.3.4 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN SSHFP +SECTION AUTHORITY +example.com. 1800 IN SOA ns.icann.org. noc.dns.icann.org. 2024013008 7200 3600 1209600 3600 +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN SSHFP +SECTION AUTHORITY +example.com. 1800 IN SOA ns.icann.org. noc.dns.icann.org. 2024013008 7200 3600 1209600 3600 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_notify.rpl b/test-data/client-cache/cache_notify.rpl new file mode 100644 index 00000000..891dfc48 --- /dev/null +++ b/test-data/client-cache/cache_notify.rpl @@ -0,0 +1,189 @@ +; Make sure an opcode other than QUERY is not cached. We issue the same request +; twice. For the second request, make sure that we do not get an answer from +; the cache. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR NOTIFY NOERROR +SECTION QUESTION +example.com. IN SOA +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR NOTIFY NOTIMP +SECTION QUESTION +example.com. IN SOA +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY NOTIFY +SECTION QUESTION +example.com. IN SOA +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR NOTIFY NOERROR +SECTION QUESTION +example.com. IN SOA +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY NOTIFY +SECTION QUESTION +example.com. IN SOA +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR NOTIFY NOTIMP +SECTION QUESTION +example.com. IN SOA +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_nxdomain.rpl b/test-data/client-cache/cache_nxdomain.rpl new file mode 100644 index 00000000..a5d23cc1 --- /dev/null +++ b/test-data/client-cache/cache_nxdomain.rpl @@ -0,0 +1,196 @@ +; Test if NXDOMAIN is cached. We issue the same query twice. +; For the second query, make sure that we get an answer from the cache. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NXDOMAIN +SECTION QUESTION +doesnotexist.example.com. IN A +SECTION AUTHORITY +example.com. 1800 IN SOA ns.icann.org. noc.dns.icann.org. 2024013008 7200 3600 1209600 3600 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD AA NOERROR +SECTION QUESTION +example.com. IN A +SECTION ANSWER +example.com. IN A 1.2.3.4 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +doesnotexist.example.com. IN A +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NXDOMAIN +SECTION QUESTION +doesnotexist.example.com. IN A +SECTION AUTHORITY +example.com. 1800 IN SOA ns.icann.org. noc.dns.icann.org. 2024013008 7200 3600 1209600 3600 +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +doesnotexist.example.com. IN A +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NXDOMAIN +SECTION QUESTION +doesnotexist.example.com. IN A +SECTION AUTHORITY +example.com. 1800 IN SOA ns.icann.org. noc.dns.icann.org. 2024013008 7200 3600 1209600 3600 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_rd.rpl b/test-data/client-cache/cache_rd.rpl new file mode 100644 index 00000000..b507dee0 --- /dev/null +++ b/test-data/client-cache/cache_rd.rpl @@ -0,0 +1,197 @@ +; Test if RD caching works properly. First we issue a query with the RD flag +; set. Then we issue a test with the RD flag clear. Make sure that we get an +; answer from the cache. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD AD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_rd_rev.rpl b/test-data/client-cache/cache_rd_rev.rpl new file mode 100644 index 00000000..dc618103 --- /dev/null +++ b/test-data/client-cache/cache_rd_rev.rpl @@ -0,0 +1,207 @@ +; Test if RD caching works properly. First we issue a query with the RD flag +; clear. Then we issue a test with the RD flag set. Make sure that we +; don't get an answer from the cache. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname RD +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::2 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname RD +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_refused.rpl b/test-data/client-cache/cache_refused.rpl new file mode 100644 index 00000000..6e735cca --- /dev/null +++ b/test-data/client-cache/cache_refused.rpl @@ -0,0 +1,208 @@ +; Make sure errors other than NXDOMAIN are cached. Test REFUSED. We issue +; the same query twice. For the second query, make sure that we get +; an answer from the cache. Wait 60 seconds, issue a new request and +; make sure it does not come from the cache. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD REFUSED +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 29 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD FORMERR +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD REFUSED +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD REFUSED +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +STEP 12 TIME_PASSES ELAPSE 60 + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD FORMERR +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_transport_error.rpl b/test-data/client-cache/cache_transport_error.rpl new file mode 100644 index 00000000..4cfb4827 --- /dev/null +++ b/test-data/client-cache/cache_transport_error.rpl @@ -0,0 +1,178 @@ +; Make sure transport errors are cached. The first two queries are hand +; crafted. Let some time elapse and issue a normal query to see if the +; cache entry has expired. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD REFUSED +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 19 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD FORMERR +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + + +RANGE_END + +STEP 1 TIME_PASSES ELAPSE 10 + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD FORMERR +SECTION QUESTION +example.com. IN SSHFP +ENTRY_END + + +SCENARIO_END diff --git a/test-data/client-cache/cache_ttl.rpl b/test-data/client-cache/cache_ttl.rpl new file mode 100644 index 00000000..90706c70 --- /dev/null +++ b/test-data/client-cache/cache_ttl.rpl @@ -0,0 +1,219 @@ +; Test if caching properly decrements the TTL and expire entries. First we +; issue a query. Then let some time elapse and query again. Verify that the +; TTL has decremented. Then let more time elapse and verify that a new +; request is issued. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. 3600 IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 29 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +RANGE_END + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +STEP 3 TIME_PASSES ELAPSE 1800 + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ttl +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. 1800 IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +STEP 12 TIME_PASSES ELAPSE 3600 + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +example.com. IN AAAA +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +example.com. IN AAAA +SECTION ANSWER +example.com. IN AAAA 2001:DB8::1 +ENTRY_END + +SCENARIO_END diff --git a/test-data/client-cache/cache_ttl_sections.rpl b/test-data/client-cache/cache_ttl_sections.rpl new file mode 100644 index 00000000..831afc61 --- /dev/null +++ b/test-data/client-cache/cache_ttl_sections.rpl @@ -0,0 +1,448 @@ +; Test if caching properly decrements the TTL in all sections and expires +; entries if the minimum TTL is in the different section. +; Probe 3 different results, with the minimum in respectively the answer, +; authority, and additional sections. For each result, check half way through +; the expire time to check if TTL is decremented properly and check after the +; entry has expired to see if a new request goes out. + +do-ip6: no + +; config options +; target-fetch-policy: "3 2 1 0 0" +; name: "." + stub-addr: 193.0.14.129 # K.ROOT-SERVERS.NET. +CONFIG_END + +SCENARIO_BEGIN Test AD flag set followed by AD flag clear. + +; K.ROOT-SERVERS.NET. +RANGE_BEGIN 0 100 + ADDRESS 193.0.14.129 +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +. IN NS +SECTION ANSWER +. IN NS K.ROOT-SERVERS.NET. +SECTION ADDITIONAL +K.ROOT-SERVERS.NET. IN A 193.0.14.129 +ENTRY_END + +; net. +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +net. IN NS +SECTION AUTHORITY +. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; root-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN NS +SECTION ANSWER +root-servers.net. IN NS k.root-servers.net. +SECTION ADDITIONAL +k.root-servers.net. IN A 193.0.14.129 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +root-servers.net. IN A +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN A +SECTION ANSWER +k.root-servers.net. IN A 193.0.14.129 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +k.root-servers.net. IN AAAA +SECTION AUTHORITY +root-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +; gtld-servers.net. +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN NS +SECTION ANSWER +gtld-servers.net. IN NS a.gtld-servers.net. +SECTION ADDITIONAL +a.gtld-servers.net. IN A 192.5.6.30 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qname +ADJUST copy_id copy_query +REPLY QR NOERROR +SECTION QUESTION +gtld-servers.net. IN A +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN A +SECTION ANSWER +a.gtld-servers.net. IN A 192.5.6.30 +SECTION ADDITIONAL +ENTRY_END + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id +REPLY QR NOERROR +SECTION QUESTION +a.gtld-servers.net. IN AAAA +SECTION AUTHORITY +gtld-servers.net. IN SOA . . 0 0 0 0 0 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 0 9 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +a.example.com. IN AAAA +SECTION ANSWER +a.example.com. 3600 IN AAAA 2001:DB8::1 +SECTION AUTHORITY +example.com. 172800 IN NS a.iana-servers.net. +example.com. 172800 IN NS b.iana-servers.net. +SECTION ADDITIONAL +a.iana-servers.net. 172800 IN A 199.43.135.53 +b.iana-servers.net. 172800 IN A 199.43.133.53 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 10 29 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +a.example.com. IN AAAA +SECTION ANSWER +a.example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +RANGE_END + +RANGE_BEGIN 30 39 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +b.example.com. IN AAAA +SECTION ANSWER +b.example.com. 172800 IN AAAA 2001:DB8::2 +SECTION AUTHORITY +example.com. 3600 IN NS a.iana-servers.net. +example.com. 3600 IN NS b.iana-servers.net. +SECTION ADDITIONAL +a.iana-servers.net. 172800 IN A 199.43.135.53 +b.iana-servers.net. 172800 IN A 199.43.133.53 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 50 59 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +b.example.com. IN AAAA +SECTION ANSWER +b.example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +RANGE_END + +RANGE_BEGIN 60 69 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +c.example.com. IN AAAA +SECTION ANSWER +c.example.com. 172800 IN AAAA 2001:DB8::3 +SECTION AUTHORITY +example.com. 172800 IN NS a.iana-servers.net. +example.com. 172800 IN NS b.iana-servers.net. +SECTION ADDITIONAL +a.iana-servers.net. 3600 IN A 199.43.135.53 +b.iana-servers.net. 3600 IN A 199.43.133.53 +ENTRY_END + +RANGE_END + +; a.gtld-servers.net. +RANGE_BEGIN 80 89 + ADDRESS 192.5.6.30 + +ENTRY_BEGIN +MATCH opcode qtype qname +ADJUST copy_id copy_query +REPLY QR RD NOERROR +SECTION QUESTION +c.example.com. IN AAAA +SECTION ANSWER +c.example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +RANGE_END + + +STEP 1 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.example.com. IN AAAA +ENTRY_END + +STEP 2 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ttl +REPLY QR RD NOERROR +SECTION QUESTION +a.example.com. IN AAAA +SECTION ANSWER +a.example.com. 3600 IN AAAA 2001:DB8::1 +SECTION AUTHORITY +example.com. 172800 IN NS a.iana-servers.net. +example.com. 172800 IN NS b.iana-servers.net. +SECTION ADDITIONAL +a.iana-servers.net. 172800 IN A 199.43.135.53 +b.iana-servers.net. 172800 IN A 199.43.133.53 +ENTRY_END + +STEP 3 TIME_PASSES ELAPSE 1800 + +STEP 10 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.example.com. IN AAAA +ENTRY_END + +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ttl +REPLY QR RD NOERROR +SECTION QUESTION +a.example.com. IN AAAA +SECTION ANSWER +a.example.com. 1800 IN AAAA 2001:DB8::1 +SECTION AUTHORITY +example.com. 171000 IN NS a.iana-servers.net. +example.com. 171000 IN NS b.iana-servers.net. +SECTION ADDITIONAL +a.iana-servers.net. 171000 IN A 199.43.135.53 +b.iana-servers.net. 171000 IN A 199.43.133.53 +ENTRY_END + +STEP 12 TIME_PASSES ELAPSE 3600 + +STEP 20 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +a.example.com. IN AAAA +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +a.example.com. IN AAAA +SECTION ANSWER +a.example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +STEP 31 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +b.example.com. IN AAAA +ENTRY_END + +STEP 32 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ttl +REPLY QR RD NOERROR +SECTION QUESTION +b.example.com. IN AAAA +SECTION ANSWER +b.example.com. 172800 IN AAAA 2001:DB8::2 +SECTION AUTHORITY +example.com. 3600 IN NS a.iana-servers.net. +example.com. 3600 IN NS b.iana-servers.net. +SECTION ADDITIONAL +a.iana-servers.net. 172800 IN A 199.43.135.53 +b.iana-servers.net. 172800 IN A 199.43.133.53 +ENTRY_END + +STEP 33 TIME_PASSES ELAPSE 1800 + +STEP 40 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +b.example.com. IN AAAA +ENTRY_END + +STEP 41 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ttl +REPLY QR RD NOERROR +SECTION QUESTION +b.example.com. IN AAAA +SECTION ANSWER +b.example.com. 171000 IN AAAA 2001:DB8::2 +SECTION AUTHORITY +example.com. 1800 IN NS a.iana-servers.net. +example.com. 1800 IN NS b.iana-servers.net. +SECTION ADDITIONAL +a.iana-servers.net. 171000 IN A 199.43.135.53 +b.iana-servers.net. 171000 IN A 199.43.133.53 +ENTRY_END + +STEP 42 TIME_PASSES ELAPSE 3600 + +STEP 50 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +b.example.com. IN AAAA +ENTRY_END + +STEP 51 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +b.example.com. IN AAAA +SECTION ANSWER +b.example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +STEP 61 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +c.example.com. IN AAAA +ENTRY_END + +STEP 62 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ttl +REPLY QR RD NOERROR +SECTION QUESTION +c.example.com. IN AAAA +SECTION ANSWER +c.example.com. 172800 IN AAAA 2001:DB8::3 +SECTION AUTHORITY +example.com. 172800 IN NS a.iana-servers.net. +example.com. 172800 IN NS b.iana-servers.net. +SECTION ADDITIONAL +a.iana-servers.net. 3600 IN A 199.43.135.53 +b.iana-servers.net. 3600 IN A 199.43.133.53 +ENTRY_END + +STEP 63 TIME_PASSES ELAPSE 1800 + +STEP 70 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +c.example.com. IN AAAA +ENTRY_END + +STEP 71 CHECK_ANSWER +ENTRY_BEGIN +MATCH all ttl +REPLY QR RD NOERROR +SECTION QUESTION +c.example.com. IN AAAA +SECTION ANSWER +c.example.com. 171000 IN AAAA 2001:DB8::3 +SECTION AUTHORITY +example.com. 171000 IN NS a.iana-servers.net. +example.com. 171000 IN NS b.iana-servers.net. +SECTION ADDITIONAL +a.iana-servers.net. 1800 IN A 199.43.135.53 +b.iana-servers.net. 1800 IN A 199.43.133.53 +ENTRY_END + +STEP 72 TIME_PASSES ELAPSE 3600 + +STEP 80 QUERY +ENTRY_BEGIN +REPLY RD +SECTION QUESTION +c.example.com. IN AAAA +ENTRY_END + +STEP 81 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR RD NOERROR +SECTION QUESTION +c.example.com. IN AAAA +SECTION ANSWER +c.example.com. IN AAAA 2606:2800:220:1:248:1893:25c8:1946 +ENTRY_END + +SCENARIO_END diff --git a/tests/net-client-cache.rs b/tests/net-client-cache.rs new file mode 100644 index 00000000..93850a2b --- /dev/null +++ b/tests/net-client-cache.rs @@ -0,0 +1,128 @@ +#![cfg(feature = "net")] +mod net; + +use crate::net::deckard::client::do_client; +use crate::net::deckard::client::CurrStepValue; +use crate::net::deckard::connect::Connect; +use crate::net::deckard::parse_deckard::parse_file; +use domain::base::{Dname, MessageBuilder, Rtype::Aaaa}; +use domain::net::client::cache; +use domain::net::client::clock::{Clock, FakeClock}; +use domain::net::client::multi_stream; +use domain::net::client::redundant; +use domain::net::client::request::{ + Error::NoTransportAvailable, RequestMessage, SendRequest, +}; +use rstest::rstest; +use std::fs::File; +use std::path::PathBuf; +use std::sync::Arc; +use tracing::instrument; + +const TEST_FILE_AD: &str = "test-data/client-cache/cache_ad.rpl"; +const TEST_FILE_TRANSPORT_ERROR: &str = + "test-data/client-cache/cache_transport_error.rpl"; + +async fn async_test_cache(filename: &str) { + let file = File::open(filename).unwrap(); + let deckard = parse_file(file); + + let step_value = Arc::new(CurrStepValue::new()); + let multi_conn = Connect::new(deckard.clone(), step_value.clone()); + let (ms, ms_tran) = multi_stream::Connection::new(multi_conn); + tokio::spawn(async move { + ms_tran.run().await; + println!("multi conn run terminated"); + }); + let clock = FakeClock::new(); + let cached = cache::Connection::new_with_time(ms, clock.clone()); + + do_client(&deckard, cached, &step_value, &clock).await; +} + +async fn async_test_no_cache(filename: &str) { + let file = File::open(filename).unwrap(); + let deckard = parse_file(file); + + let step_value = Arc::new(CurrStepValue::new()); + let multi_conn = Connect::new(deckard.clone(), step_value.clone()); + let (ms, ms_tran) = multi_stream::Connection::new(multi_conn); + tokio::spawn(async move { + ms_tran.run().await; + println!("multi conn run terminated"); + }); + + let clock = FakeClock::new(); + do_client(&deckard, ms, &step_value, &clock).await; +} + +#[tokio::test] +#[should_panic] +async fn test_ad_no_cache() { + async_test_no_cache(TEST_FILE_AD).await; +} + +#[tokio::test] +async fn test_transport_error() { + // Transport errors should be cached. Create an empty redundant transport + // and manually issue a query to trigger a transport error. Then add a + // transport and issue a new query. + let file = File::open(TEST_FILE_TRANSPORT_ERROR).unwrap(); + let deckard = parse_file(file); + + let step_value = Arc::new(CurrStepValue::new()); + let (redun, redun_tran) = redundant::Connection::new(); + tokio::spawn(async move { + redun_tran.run().await; + println!("redundant conn run terminated"); + }); + let clock = FakeClock::new(); + let cached = + cache::Connection::new_with_time(redun.clone(), clock.clone()); + + let mut msg = MessageBuilder::new_vec(); + msg.header_mut().set_rd(true); + let mut msg = msg.question(); + msg.push((Dname::vec_from_str("example.com").unwrap(), Aaaa)) + .unwrap(); + let req = RequestMessage::new(msg); + + let mut request = cached.send_request(req.clone()); + let reply = request.get_response().await; + + println!("got {reply:?}"); + + if let Err(NoTransportAvailable) = reply { + // This is what we expect. + } else { + panic!("Bad result {reply:?}"); + } + + let multi_conn = Connect::new(deckard.clone(), step_value.clone()); + let (ms, ms_tran) = multi_stream::Connection::new(multi_conn); + tokio::spawn(async move { + ms_tran.run().await; + println!("multi conn run terminated"); + }); + redun.add(Box::new(ms)).await.unwrap(); + + let mut request = cached.send_request(req); + let reply = request.get_response().await; + + if let Err(NoTransportAvailable) = reply { + // This is what we expect. + } else { + panic!("Bad result {reply:?}"); + } + + do_client(&deckard, redun, &step_value, &clock).await; +} + +#[instrument(skip_all, fields(rpl = rpl_file.file_name().unwrap().to_str()))] +#[rstest] +#[tokio::test] +async fn test_all( + #[files("test-data/client-cache/*.rpl")] rpl_file: PathBuf, +) { + async_test_cache(rpl_file.to_str().unwrap()).await; +} diff --git a/tests/net-client.rs b/tests/net-client.rs index 10a7ab46..d930422c 100644 --- a/tests/net-client.rs +++ b/tests/net-client.rs @@ -7,6 +7,7 @@ use crate::net::deckard::connect::Connect; use crate::net::deckard::connection::Connection; use crate::net::deckard::dgram::Dgram; use crate::net::deckard::parse_deckard::parse_file; +use domain::net::client::clock::{Clock, FakeClock}; use domain::net::client::dgram; use domain::net::client::dgram_stream; use domain::net::client::multi_stream; @@ -31,7 +32,8 @@ fn dgram() { let conn = Dgram::new(deckard.clone(), step_value.clone()); let octstr = dgram::Connection::new(conn); - do_client(&deckard, octstr, &step_value).await; + let clock = FakeClock::new(); + do_client(&deckard, octstr, &step_value, &clock).await; }); } @@ -48,7 +50,8 @@ fn single() { transport.run().await; }); - do_client(&deckard, octstr, &step_value).await; + let clock = FakeClock::new(); + do_client(&deckard, octstr, &step_value, &clock).await; }); } @@ -66,7 +69,8 @@ fn multi() { println!("multi conn run terminated"); }); - do_client(&deckard, ms.clone(), &step_value).await; + let clock = FakeClock::new(); + do_client(&deckard, ms.clone(), &step_value, &clock).await; }); } @@ -85,7 +89,8 @@ fn dgram_stream() { println!("dgram_stream conn run terminated"); }); - do_client(&deckard, ds, &step_value).await; + let clock = FakeClock::new(); + do_client(&deckard, ds, &step_value, &clock).await; }); } @@ -112,7 +117,8 @@ fn redundant() { }); redun.add(Box::new(ms.clone())).await.unwrap(); - do_client(&deckard, redun, &step_value).await; + let clock = FakeClock::new(); + do_client(&deckard, redun, &step_value, &clock).await; }); } @@ -143,6 +149,7 @@ fn tcp() { println!("single TCP run terminated"); }); - do_client(&deckard, tcp, &CurrStepValue::new()).await; + let clock = FakeClock::new(); + do_client(&deckard, tcp, &CurrStepValue::new(), &clock).await; }); } diff --git a/tests/net/deckard/client.rs b/tests/net/deckard/client.rs index 5929c9d7..6006ed47 100644 --- a/tests/net/deckard/client.rs +++ b/tests/net/deckard/client.rs @@ -3,14 +3,20 @@ use crate::net::deckard::parse_deckard::{Deckard, Entry, Reply, StepType}; use crate::net::deckard::parse_query; use bytes::Bytes; +use domain::base::iana::Opcode; use domain::base::{Message, MessageBuilder}; -use domain::net::client::request::{RequestMessage, SendRequest}; +use domain::net::client::clock::FakeClock; +use domain::net::client::request::{ + ComposeRequest, RequestMessage, SendRequest, +}; use std::sync::Mutex; +use std::time::Duration; pub async fn do_client>>>( deckard: &Deckard, request: R, step_value: &CurrStepValue, + clock: &FakeClock, ) { let mut resp: Option> = None; @@ -26,11 +32,19 @@ pub async fn do_client>>>( StepType::CheckAnswer => { let answer = resp.take().unwrap(); if !match_msg(step.entry.as_ref().unwrap(), &answer, true) { + println!( + "Reply message does not match at step {}", + step_value.get() + ); panic!("reply failed"); } } - StepType::TimePasses - | StepType::Traffic + StepType::TimePasses => { + clock.adjust_time(Duration::from_secs( + step.time_passes.unwrap(), + )); + } + StepType::Traffic | StepType::CheckTempfile | StepType::Assign => todo!(), } @@ -64,11 +78,17 @@ fn entry2reqmsg(entry: &Entry) -> RequestMessage> { Some(reply) => reply.clone(), None => Default::default(), }; - if reply.rd { - msg.header_mut().set_rd(true); - } + let header = msg.header_mut(); + header.set_rd(reply.rd); + header.set_ad(reply.ad); + header.set_cd(reply.cd); let msg = msg.into_message(); - RequestMessage::new(msg) + let mut msg = RequestMessage::new(msg); + msg.set_dnssec_ok(reply.fl_do); + if reply.notify { + msg.header_mut().set_opcode(Opcode::Notify); + } + msg } #[derive(Debug)] diff --git a/tests/net/deckard/connect.rs b/tests/net/deckard/connect.rs index 28771029..0805417e 100644 --- a/tests/net/deckard/connect.rs +++ b/tests/net/deckard/connect.rs @@ -23,7 +23,11 @@ impl Connect { impl AsyncConnect for Connect { type Connection = Connection; type Fut = Pin< - Box> + Send>, + Box< + dyn Future> + + Send + + Sync, + >, >; fn connect(&self) -> Self::Fut { diff --git a/tests/net/deckard/dgram.rs b/tests/net/deckard/dgram.rs index 6fd0eb33..91602b3e 100644 --- a/tests/net/deckard/dgram.rs +++ b/tests/net/deckard/dgram.rs @@ -21,6 +21,7 @@ pub struct Dgram { } impl Dgram { + #[allow(dead_code)] pub fn new(deckard: Deckard, step_value: Arc) -> Self { Self { deckard, @@ -34,7 +35,8 @@ impl AsyncConnect for Dgram { type Fut = Pin< Box< dyn Future> - + Send, + + Send + + Sync, >, >; fn connect(&self) -> Self::Fut { diff --git a/tests/net/deckard/matches.rs b/tests/net/deckard/matches.rs index 1da79149..7db4bc28 100644 --- a/tests/net/deckard/matches.rs +++ b/tests/net/deckard/matches.rs @@ -57,6 +57,7 @@ where sections.additional.clone(), msg.additional().unwrap(), arcount, + matches.ttl, verbose, ) { if verbose { @@ -70,11 +71,12 @@ where sections.answer.clone(), msg.answer().unwrap(), msg.header_counts().ancount(), + matches.ttl, verbose, ) { if verbose { - todo!(); + println!("match_msg: answer section does not match"); } return false; } @@ -83,16 +85,47 @@ where sections.authority.clone(), msg.authority().unwrap(), msg.header_counts().nscount(), + matches.ttl, verbose, ) { if verbose { - todo!(); + println!("match_msg: authority section does not match"); + } + return false; + } + if matches.ad && !msg.header().ad() { + if verbose { + println!("match_msg: AD not in message",); + } + return false; + } + if matches.cd && !msg.header().cd() { + if verbose { + println!("match_msg: CD not in message",); } return false; } if matches.fl_do { - todo!(); + if let Some(opt) = msg.opt() { + if !opt.dnssec_ok() { + if verbose { + println!("match_msg: DO not in message",); + } + return false; + } + } else { + if verbose { + println!("match_msg: DO not in message (not opt record)",); + } + return false; + } + } + if matches.rd && !msg.header().rd() { + if verbose { + println!("match_msg: RD not in message",); + } + return false; } if matches.flags { let header = msg.header(); @@ -118,34 +151,60 @@ where } return false; } + if reply.ra != header.ra() { + if verbose { + println!( + "match_msg: RA does not match, got {}, expected {}", + header.ra(), + reply.ra + ); + } + return false; + } if reply.rd != header.rd() { if verbose { println!( "match_msg: RD does not match, got {}, expected {}", - header.aa(), - reply.aa + header.rd(), + reply.rd ); } return false; } if reply.ad != header.ad() { if verbose { - todo!(); + println!( + "match_msg: AD does not match, got {}, expected {}", + header.ad(), + reply.ad + ); } return false; } if reply.cd != header.cd() { if verbose { - todo!(); + println!( + "match_msg: CD does not match, got {}, expected {}", + header.cd(), + reply.cd + ); } return false; } } if matches.opcode { - // Not clear what that means. JUst check if it is Query - if msg.header().opcode() != Opcode::Query { + let expected_opcode = if reply.notify { + Opcode::Notify + } else { + Opcode::Query + }; + if msg.header().opcode() != expected_opcode { if verbose { - todo!(); + println!( + "Opcode does not match, got {} expected {}", + msg.header().opcode(), + expected_opcode + ); } return false; } @@ -171,7 +230,51 @@ where // Okay } else { if verbose { - todo!(); + println!( + "Wrong Rcode, expected NOERROR, got {msg_rcode}" + ); + } + return false; + } + } else if reply.formerr { + if let OptRcode::FormErr = msg_rcode { + // Okay + } else { + if verbose { + println!( + "Wrong Rcode, expected FORMERR, got {msg_rcode}" + ); + } + return false; + } + } else if reply.notimp { + if let OptRcode::NotImp = msg_rcode { + // Okay + } else { + if verbose { + println!("Wrong Rcode, expected NOTIMP, got {msg_rcode}"); + } + return false; + } + } else if reply.nxdomain { + if let OptRcode::NXDomain = msg_rcode { + // Okay + } else { + if verbose { + println!( + "Wrong Rcode, expected NXDOMAIN, got {msg_rcode}" + ); + } + return false; + } + } else if reply.refused { + if let OptRcode::Refused = msg_rcode { + // Okay + } else { + if verbose { + println!( + "Wrong Rcode, expected REFUSED, got {msg_rcode}" + ); } return false; } @@ -187,7 +290,7 @@ where todo!() } if matches.ttl { - todo!() + // Nothing to do. TTLs are checked in the relevant sections. } if matches.udp { todo!() @@ -205,11 +308,16 @@ fn match_section< mut match_section: Vec, msg_section: RecordSection<'a, Octs>, msg_count: u16, + match_ttl: bool, verbose: bool, ) -> bool { if match_section.len() != msg_count.into() { if verbose { - todo!(); + println!( + "Expected {} entries, got {}", + match_section.len(), + msg_count + ); } return false; } @@ -225,12 +333,27 @@ fn match_section< } else { panic!("include not expected"); }; + println!( + "matching {:?} with {:?}", + msg_rr.owner(), + mat_rr.owner() + ); if msg_rr.owner() != mat_rr.owner() { continue; } + println!( + "matching {:?} with {:?}", + msg_rr.class(), + mat_rr.class() + ); if msg_rr.class() != mat_rr.class() { continue; } + println!( + "matching {:?} with {:?}", + msg_rr.rtype(), + mat_rr.rtype() + ); if msg_rr.rtype() != mat_rr.rtype() { continue; } @@ -239,11 +362,25 @@ fn match_section< .into_record::>>() .unwrap() .unwrap(); + println!( + "matching {:?} with {:?}", + msg_rdata.data(), + mat_rr.data() + ); if msg_rdata.data() != mat_rr.data() { continue; } - // Found one. Delete this entry + // Found one. Check TTL + if match_ttl && msg_rr.ttl() != mat_rr.ttl() { + if verbose { + println!("match_section: TTL does not match for {} {} {}: got {:?} expected {:?}", + msg_rr.owner(), msg_rr.class(), msg_rr.rtype(), + msg_rr.ttl(), mat_rr.ttl()); + } + return false; + } + // Delete this entry match_section.swap_remove(index); continue 'outer; } diff --git a/tests/net/deckard/parse_deckard.rs b/tests/net/deckard/parse_deckard.rs index b7fe2fb5..c2699f62 100644 --- a/tests/net/deckard/parse_deckard.rs +++ b/tests/net/deckard/parse_deckard.rs @@ -28,6 +28,7 @@ const STEP: &str = "STEP"; const STEP_TYPE_QUERY: &str = "QUERY"; const STEP_TYPE_CHECK_ANSWER: &str = "CHECK_ANSWER"; const STEP_TYPE_TIME_PASSES: &str = "TIME_PASSES"; +const STEP_TYPE_TIME_PASSES_ELAPSE: &str = "ELAPSE"; const STEP_TYPE_TRAFFIC: &str = "TRAFFIC"; const STEP_TYPE_CHECK_TEMPFILE: &str = "CHECK_TEMPFILE"; const STEP_TYPE_ASSIGN: &str = "ASSIGN"; @@ -190,6 +191,7 @@ fn parse_range>>( pub struct Step { pub step_value: u64, pub step_type: StepType, + pub time_passes: Option, pub entry: Option, } @@ -217,6 +219,7 @@ fn parse_step>>( let mut step = Step { step_value, step_type, + time_passes: None, entry: None, }; @@ -224,7 +227,16 @@ fn parse_step>>( StepType::Query => (), // Continue with entry StepType::CheckAnswer => (), // Continue with entry StepType::TimePasses => { - println!("parse_step: should handle TIME_PASSES"); + // The next token needs to be ELAPSE. Later we can add EVAL as + // well. + let elapsed_str = tokens.next().unwrap(); + if elapsed_str != STEP_TYPE_TIME_PASSES_ELAPSE { + panic!("Expect ELAPSE after TIME_PASSES"); + } + + // Then we get the number of seconds that has passed. + let seconds = tokens.next().unwrap().parse::().unwrap(); + step.time_passes = Some(seconds); return step; } StepType::Traffic => { @@ -406,7 +418,10 @@ pub struct Matches { pub all: bool, pub answer: bool, pub authority: bool, + pub ad: bool, + pub cd: bool, pub fl_do: bool, + pub rd: bool, pub flags: bool, pub opcode: bool, pub qname: bool, @@ -430,10 +445,18 @@ fn parse_match(mut tokens: LineTokens<'_>) -> Matches { if token == "all" { matches.all = true; + } else if token == "AD" { + matches.ad = true; + } else if token == "CD" { + matches.cd = true; } else if token == "DO" { matches.fl_do = true; + } else if token == "RD" { + matches.rd = true; } else if token == "opcode" { matches.opcode = true; + } else if token == "flags" { + matches.flags = true; } else if token == "qname" { matches.qname = true; } else if token == "question" { @@ -487,16 +510,18 @@ pub struct Reply { pub ad: bool, pub cd: bool, pub fl_do: bool, - pub formerr: bool, - pub noerror: bool, - pub nxdomain: bool, pub qr: bool, pub ra: bool, pub rd: bool, + pub tc: bool, + pub formerr: bool, + pub noerror: bool, + pub notimp: bool, + pub nxdomain: bool, pub refused: bool, pub servfail: bool, - pub tc: bool, pub yxdomain: bool, + pub notify: bool, } fn parse_reply(mut tokens: LineTokens<'_>) -> Reply { @@ -516,26 +541,30 @@ fn parse_reply(mut tokens: LineTokens<'_>) -> Reply { reply.cd = true; } else if token == "DO" { reply.fl_do = true; - } else if token == "FORMERR" { - reply.formerr = true; - } else if token == "NOERROR" { - reply.noerror = true; - } else if token == "NXDOMAIN" { - reply.nxdomain = true; } else if token == "QR" { reply.qr = true; } else if token == "RA" { reply.ra = true; } else if token == "RD" { reply.rd = true; + } else if token == "TC" { + reply.tc = true; + } else if token == "FORMERR" { + reply.formerr = true; + } else if token == "NOERROR" { + reply.noerror = true; + } else if token == "NOTIMP" { + reply.notimp = true; + } else if token == "NXDOMAIN" { + reply.nxdomain = true; } else if token == "REFUSED" { reply.refused = true; } else if token == "SERVFAIL" { reply.servfail = true; - } else if token == "TC" { - reply.tc = true; } else if token == "YXDOMAIN" { reply.yxdomain = true; + } else if token == "NOTIFY" { + reply.notify = true; } else { println!("should handle reply {token:?}"); todo!(); diff --git a/tests/net/deckard/server.rs b/tests/net/deckard/server.rs index f8eecf76..17bee64e 100644 --- a/tests/net/deckard/server.rs +++ b/tests/net/deckard/server.rs @@ -4,6 +4,7 @@ use crate::net::deckard::parse_deckard; use crate::net::deckard::parse_deckard::{Adjust, Deckard, Reply}; use crate::net::deckard::parse_query; use domain::base::iana::rcode::Rcode; +use domain::base::iana::Opcode; use domain::base::{Message, MessageBuilder}; use domain::dep::octseq::Octets; use domain::zonefile::inplace::Entry as ZonefileEntry; @@ -30,6 +31,7 @@ where return Some(reply); } } + println!("do_server: no reply at step value {step}"); todo!(); } @@ -75,45 +77,42 @@ fn do_adjust( msg.push(rec).unwrap(); } let mut msg = msg.additional(); - for _a in §ions.additional { - todo!(); + for a in §ions.additional { + let rec = if let ZonefileEntry::Record(record) = a { + record + } else { + panic!("include not expected") + }; + msg.push(rec).unwrap(); } let reply: Reply = match &entry.reply { Some(reply) => reply.clone(), None => Default::default(), }; - if reply.aa { - msg.header_mut().set_aa(true); - } - if reply.ad { - todo!() - } - if reply.cd { - todo!() - } + let header = msg.header_mut(); + header.set_aa(reply.aa); + header.set_ad(reply.ad); + header.set_cd(reply.cd); if reply.fl_do { todo!() } if reply.formerr { - todo!() + header.set_rcode(Rcode::FormErr); } if reply.noerror { - msg.header_mut().set_rcode(Rcode::NoError); + header.set_rcode(Rcode::NoError); + } + if reply.notimp { + header.set_rcode(Rcode::NotImp); } if reply.nxdomain { - todo!() - } - if reply.qr { - msg.header_mut().set_qr(true); - } - if reply.ra { - todo!() - } - if reply.rd { - msg.header_mut().set_rd(true); + header.set_rcode(Rcode::NXDomain); } + header.set_qr(reply.qr); + header.set_ra(reply.ra); + header.set_rd(reply.rd); if reply.refused { - todo!() + header.set_rcode(Rcode::Refused); } if reply.servfail { todo!() @@ -124,8 +123,11 @@ fn do_adjust( if reply.yxdomain { todo!() } + if reply.notify { + header.set_opcode(Opcode::Notify); + } if adjust.copy_id { - msg.header_mut().set_id(reqmsg.header().id()); + header.set_id(reqmsg.header().id()); } else { todo!(); }