From 01fccdf9422a368f080b7a0c4e003ed387a33b70 Mon Sep 17 00:00:00 2001 From: Ximon Eighteen <3304436+ximon18@users.noreply.github.com> Date: Wed, 2 Oct 2024 21:28:01 +0200 Subject: [PATCH] Add XFR middleware. (#384) Add XFR middleware and: - Fixes a bug where the incorrect owner name was passed to the zone walker callback. - New `ixfr-client.rs` example. - Updated `serve-zone.rs` example demonstrating TSIG authenticated XFR and NOTIFY middlewares. - Additional From impls for CallResult. - Renames ZoneDiff to InMemoryZoneDiff. - Renames ZoneDiffBuilder to InMemoryZoneDiffBuilder. - Introduces new ZoneDiff and related traits. - Adds AnswerContent::first(). --- Cargo.toml | 4 + examples/common/serve-utils.rs | 10 +- examples/ixfr-client.rs | 69 ++ examples/serve-zone.rs | 214 +++- src/net/server/middleware/mod.rs | 2 + src/net/server/middleware/xfr/axfr.rs | 94 ++ src/net/server/middleware/xfr/batcher.rs | 224 ++++ .../server/middleware/xfr/data_provider.rs | 243 ++++ src/net/server/middleware/xfr/ixfr.rs | 146 +++ src/net/server/middleware/xfr/mod.rs | 60 + src/net/server/middleware/xfr/responder.rs | 161 +++ src/net/server/middleware/xfr/service.rs | 754 ++++++++++++ src/net/server/middleware/xfr/tests.rs | 824 +++++++++++++ src/net/server/middleware/xfr/util.rs | 39 + src/net/server/mod.rs | 44 + src/net/server/service.rs | 18 + src/net/server/tests/integration.rs | 171 +-- src/net/xfr/mod.rs | 65 +- src/zonetree/answer.rs | 35 +- src/zonetree/in_memory/mod.rs | 11 +- src/zonetree/in_memory/read.rs | 71 +- src/zonetree/in_memory/write.rs | 26 +- src/zonetree/mod.rs | 6 +- src/zonetree/parsed.rs | 2 +- src/zonetree/traits.rs | 148 ++- src/zonetree/types.rs | 170 ++- src/zonetree/update.rs | 8 +- src/zonetree/walk.rs | 19 +- test-data/server/axfr_large_out.rpl | 1055 +++++++++++++++++ test-data/server/axfr_out.rpl | 140 +++ test-data/server/edns_downstream_cookies.rpl | 4 +- test-data/server/edns_keepalive.rpl | 9 +- test-data/zonefiles/nsd-example.txt | 16 +- 33 files changed, 4670 insertions(+), 192 deletions(-) create mode 100644 examples/ixfr-client.rs create mode 100644 src/net/server/middleware/xfr/axfr.rs create mode 100644 src/net/server/middleware/xfr/batcher.rs create mode 100644 src/net/server/middleware/xfr/data_provider.rs create mode 100644 src/net/server/middleware/xfr/ixfr.rs create mode 100644 src/net/server/middleware/xfr/mod.rs create mode 100644 src/net/server/middleware/xfr/responder.rs create mode 100644 src/net/server/middleware/xfr/service.rs create mode 100644 src/net/server/middleware/xfr/tests.rs create mode 100644 src/net/server/middleware/xfr/util.rs create mode 100644 test-data/server/axfr_large_out.rpl create mode 100644 test-data/server/axfr_out.rpl diff --git a/Cargo.toml b/Cargo.toml index 82108c64..c0bd217f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -127,6 +127,10 @@ required-features = ["zonefile", "unstable-zonetree"] name = "serve-zone" required-features = ["zonefile", "net", "unstable-server-transport", "unstable-zonetree"] +[[example]] +name = "ixfr-client" +required-features = ["zonefile", "net", "unstable-client-transport", "unstable-zonetree"] + # This example is commented out because it is difficult, if not impossible, # when including the sqlx dependency, to make the dependency tree compatible # with both `cargo +nightly update -Z minimal versions` and the crate minimum diff --git a/examples/common/serve-utils.rs b/examples/common/serve-utils.rs index 0978481c..e4d287c1 100644 --- a/examples/common/serve-utils.rs +++ b/examples/common/serve-utils.rs @@ -2,7 +2,9 @@ use bytes::Bytes; use domain::base::{Message, MessageBuilder, Name, ParsedName, Rtype}; use domain::rdata::ZoneRecordData; use domain::zonetree::Answer; +use octseq::Octets; +#[allow(dead_code)] pub fn generate_wire_query( qname: &Name, qtype: Rtype, @@ -13,6 +15,7 @@ pub fn generate_wire_query( query.into() } +#[allow(dead_code)] pub fn generate_wire_response( wire_query: &Message>, zone_answer: Answer, @@ -22,9 +25,10 @@ pub fn generate_wire_response( response.into() } -pub fn print_dig_style_response( - query: &Message>, - response: &Message>, +#[allow(dead_code)] +pub fn print_dig_style_response( + query: &Message, + response: &Message, short: bool, ) { if !short { diff --git a/examples/ixfr-client.rs b/examples/ixfr-client.rs new file mode 100644 index 00000000..1e0613b4 --- /dev/null +++ b/examples/ixfr-client.rs @@ -0,0 +1,69 @@ +/// Using the `domain::net::client` module for sending a query. +use core::str::FromStr; + +use std::vec::Vec; + +use tokio::net::TcpStream; + +use domain::base::Name; +use domain::base::Rtype; +use domain::base::{MessageBuilder, Serial, Ttl}; +use domain::net::client::request::SendRequestMulti; +use domain::net::client::request::{RequestMessage, RequestMessageMulti}; +use domain::net::client::stream; +use domain::rdata::Soa; + +#[path = "common/serve-utils.rs"] +mod common; + +#[tokio::main] +async fn main() { + let args: Vec = std::env::args().collect(); + + if args.len() != 4 { + eprintln!( + "Usage: {} ", + args[0] + ); + eprintln!("E.g.: {} 127.0.0.1:8053 example.com 2020080302", args[0]); + std::process::exit(1); + } + + let server_addr = &args[1]; + let qname = Name::>::from_str(&args[2]).unwrap(); + let soa_serial: u32 = args[3].parse().unwrap(); + + eprintln!("Requesting IXFR from {server_addr} for zone {qname} from serial {soa_serial}"); + + let tcp_conn = TcpStream::connect(server_addr).await.unwrap(); + let (tcp, transport) = stream::Connection::< + RequestMessage>, + RequestMessageMulti>, + >::new(tcp_conn); + tokio::spawn(async move { + transport.run().await; + println!("single TSIG TCP run terminated"); + }); + + let mname = Name::>::from_str("mname").unwrap(); + let rname = Name::>::from_str("rname").unwrap(); + let ttl = Ttl::from_secs(3600); + let soa = Soa::new(mname, rname, Serial(soa_serial), ttl, ttl, ttl, ttl); + + let mut msg = MessageBuilder::new_vec(); + msg.header_mut().set_rd(true); + msg.header_mut().set_ad(true); + let mut msg = msg.question(); + msg.push((&qname, Rtype::IXFR)).unwrap(); + let mut msg = msg.authority(); + msg.push((&qname, 3600, soa)).unwrap(); + let req = RequestMessageMulti::new(msg.clone()).unwrap(); + + let mut request = SendRequestMulti::send_request(&tcp, req); + + // Get the reply + let mock_req = msg.into_message(); + while let Some(reply) = request.get_response().await.unwrap() { + common::print_dig_style_response(&mock_req, &reply, false); + } +} diff --git a/examples/serve-zone.rs b/examples/serve-zone.rs index db9c986e..c82b30ce 100644 --- a/examples/serve-zone.rs +++ b/examples/serve-zone.rs @@ -15,14 +15,27 @@ //! //! dig @127.0.0.1 -p 8053 AXFR example.com +use core::future::{ready, Future}; +use core::pin::Pin; +use core::str::FromStr; + +use std::collections::HashMap; use std::future::pending; use std::io::BufReader; use std::process::exit; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::time::Duration; -use domain::base::iana::Rcode; -use domain::base::ToName; +use octseq::Octets; +use rand::distributions::Alphanumeric; +use rand::Rng; +use tokio::net::{TcpListener, UdpSocket}; +use tracing_subscriber::EnvFilter; + +use domain::base::iana::{Class, Rcode}; +use domain::base::name::OwnedLabel; +use domain::base::net::IpAddr; +use domain::base::{Name, Rtype, Serial, ToName, Ttl}; use domain::net::server::buf::VecBufSource; use domain::net::server::dgram::DgramServer; use domain::net::server::message::Request; @@ -30,14 +43,22 @@ use domain::net::server::message::Request; use domain::net::server::middleware::cookies::CookiesMiddlewareSvc; use domain::net::server::middleware::edns::EdnsMiddlewareSvc; use domain::net::server::middleware::mandatory::MandatoryMiddlewareSvc; +use domain::net::server::middleware::notify::{ + Notifiable, NotifyError, NotifyMiddlewareSvc, +}; +use domain::net::server::middleware::tsig::TsigMiddlewareSvc; +use domain::net::server::middleware::xfr::{ + XfrData, XfrDataProvider, XfrDataProviderError, XfrMiddlewareSvc, +}; use domain::net::server::service::{CallResult, ServiceResult}; use domain::net::server::stream::StreamServer; use domain::net::server::util::{mk_builder_for_target, service_fn}; +use domain::tsig::{Algorithm, Key, KeyName}; use domain::zonefile::inplace; -use domain::zonetree::Answer; +use domain::zonetree::{ + Answer, InMemoryZoneDiff, Rrset, SharedRrset, StoredName, +}; use domain::zonetree::{Zone, ZoneTree}; -use tokio::net::{TcpListener, UdpSocket}; -use tracing_subscriber::EnvFilter; #[tokio::main()] async fn main() { @@ -50,6 +71,18 @@ async fn main() { .try_init() .ok(); + // Create a TSIG key store with a demo key. + let mut key_store = HashMap::<(KeyName, Algorithm), Key>::new(); + let key_name = KeyName::from_str("demo-key").unwrap(); + let secret = domain::utils::base64::decode::>( + "zlCZbVJPIhobIs1gJNQfrsS3xCxxsR9pMUrGwG8OgG8=", + ) + .unwrap(); + let key = + Key::new(Algorithm::Sha256, &secret, key_name.clone(), None, None) + .unwrap(); + key_store.insert((key_name, Algorithm::Sha256), key); + // Populate a zone tree with test data let zone_bytes = include_bytes!("../test-data/zonefiles/nsd-example.txt"); let mut zone_bytes = BufReader::new(&zone_bytes[..]); @@ -76,16 +109,28 @@ async fn main() { zones.insert_zone(zone).unwrap(); let zones = Arc::new(zones); + // Create an XFR data provider that can serve diffs for our zone. + let zones_and_diffs = ZoneTreeWithDiffs::new(zones.clone()); + + // Create a server with middleware layers and an application service + // listening on localhost port 8053. let addr = "127.0.0.1:8053"; - let svc = service_fn(my_service, zones); + let svc = service_fn(my_service, zones.clone()); #[cfg(feature = "siphasher")] let svc = CookiesMiddlewareSvc::, _, _>::with_random_secret(svc); let svc = EdnsMiddlewareSvc::, _, _>::new(svc); + let svc = XfrMiddlewareSvc::, _, _, _>::new( + svc, + zones_and_diffs.clone(), + 1, + ); + let svc = NotifyMiddlewareSvc::new(svc, DemoNotifyTarget); let svc = MandatoryMiddlewareSvc::, _, _>::new(svc); + let svc = TsigMiddlewareSvc::new(svc, key_store); let svc = Arc::new(svc); - let sock = UdpSocket::bind(addr).await.unwrap(); + let sock = UdpSocket::bind(&addr).await.unwrap(); let sock = Arc::new(sock); let mut udp_metrics = vec![]; let num_cores = std::thread::available_parallelism().unwrap().get(); @@ -103,8 +148,17 @@ async fn main() { tokio::spawn(async move { tcp_srv.run().await }); - eprintln!("Ready"); + eprintln!("Listening on {addr}"); + eprintln!("Try:"); + eprintln!(" dig @127.0.0.1 -p 8053 example.com"); + eprintln!(" dig @127.0.0.1 -p 8053 example.com AXFR"); + eprintln!(" dig @127.0.0.1 -p 8053 -y hmac-sha256:demo-key:zlCZbVJPIhobIs1gJNQfrsS3xCxxsR9pMUrGwG8OgG8= example.com AXFR"); + eprintln!(" dig @127.0.0.1 -p 8053 +opcode=notify example.com SOA"); + eprintln!(" cargo run --example ixfr-client --all-features -- 127.0.0.1:8053 example.com 2020080302"); + eprintln!(); + eprintln!("Tip: set env var RUST_LOG=info (or debug or trace) for more log output."); + // Print some status information every 5 seconds tokio::spawn(async move { loop { tokio::time::sleep(Duration::from_millis(5000)).await; @@ -138,6 +192,50 @@ async fn main() { } }); + // Mutate our own zone every 10 seconds. + tokio::spawn(async move { + let zone_name = Name::>::from_str("example.com").unwrap(); + let mut label: Option = None; + + loop { + tokio::time::sleep(Duration::from_millis(10000)).await; + + let zone = zones.get_zone(&zone_name, Class::IN).unwrap(); + let mut writer = zone.write().await; + { + let node = writer.open(true).await.unwrap(); + + if let Some(old_label) = label { + let node = node.update_child(&old_label).await.unwrap(); + node.remove_rrset(Rtype::A).await.unwrap(); + } + + let random_string: String = rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(7) + .map(char::from) + .collect(); + let new_label = OwnedLabel::from_str(&random_string).unwrap(); + + let node = node.update_child(&new_label).await.unwrap(); + let mut rrset = Rrset::new(Rtype::A, Ttl::from_secs(60)); + let rec = domain::rdata::A::new("127.0.0.1".parse().unwrap()); + rrset.push_data(rec.into()); + node.update_rrset(SharedRrset::new(rrset)).await.unwrap(); + + label = Some(new_label); + } + let diff = writer.commit(true).await.unwrap(); + if let Some(diff) = diff { + zones_and_diffs.add_diff(diff); + } + eprintln!( + "Added {} A record to zone example.com", + label.unwrap() + ); + } + }); + pending::<()>().await; } @@ -163,3 +261,101 @@ fn my_service( let additional = answer.to_message(request.message(), builder); Ok(CallResult::new(additional)) } + +#[derive(Copy, Clone, Default, Debug)] +struct DemoNotifyTarget; + +impl Notifiable for DemoNotifyTarget { + fn notify_zone_changed( + &self, + class: Class, + apex_name: &StoredName, + source: IpAddr, + ) -> Pin< + Box> + Sync + Send + '_>, + > { + eprintln!("Notify received from {source} of change to zone {apex_name} in class {class}"); + + let res = match apex_name.to_string().to_lowercase().as_str() { + "example.com" => Ok(()), + "othererror.com" => Err(NotifyError::Other), + _ => Err(NotifyError::NotAuthForZone), + }; + + Box::pin(ready(res)) + } +} + +#[derive(Clone)] +struct ZoneTreeWithDiffs { + zones: Arc, + diffs: Arc>>, +} + +impl ZoneTreeWithDiffs { + fn new(zones: Arc) -> Self { + Self { + zones, + diffs: Default::default(), + } + } + + fn add_diff(&self, diff: InMemoryZoneDiff) { + self.diffs.lock().unwrap().push(diff); + } + + fn get_diffs(&self, diff_from: Option) -> Vec { + let diffs = self.diffs.lock().unwrap(); + if let Some(idx) = diffs + .iter() + .position(|diff| Some(diff.start_serial) == diff_from) + { + diffs[idx..].to_vec() + } else { + vec![] + } + } +} + +impl XfrDataProvider for ZoneTreeWithDiffs { + type Diff = InMemoryZoneDiff; + + fn request( + &self, + req: &Request, + diff_from: Option, + ) -> Pin< + Box< + dyn Future< + Output = Result< + XfrData, + XfrDataProviderError, + >, + > + Sync + + Send, + >, + > + where + Octs: Octets + Send + Sync, + { + let res = req + .message() + .sole_question() + .map_err(XfrDataProviderError::ParseError) + .and_then(|q| { + if let Some(zone) = + self.zones.find_zone(q.qname(), q.qclass()) + { + Ok(XfrData::new( + zone.clone(), + self.get_diffs(diff_from), + false, + )) + } else { + Err(XfrDataProviderError::UnknownZone) + } + }); + + Box::pin(ready(res)) + } +} diff --git a/src/net/server/middleware/mod.rs b/src/net/server/middleware/mod.rs index f6c3b0a9..43791fb7 100644 --- a/src/net/server/middleware/mod.rs +++ b/src/net/server/middleware/mod.rs @@ -47,3 +47,5 @@ pub mod notify; pub mod stream; #[cfg(feature = "tsig")] pub mod tsig; +#[cfg(feature = "unstable-xfr")] +pub mod xfr; diff --git a/src/net/server/middleware/xfr/axfr.rs b/src/net/server/middleware/xfr/axfr.rs new file mode 100644 index 00000000..755b8503 --- /dev/null +++ b/src/net/server/middleware/xfr/axfr.rs @@ -0,0 +1,94 @@ +use std::boxed::Box; +use std::sync::Arc; + +use bytes::Bytes; +use tokio::sync::mpsc::Sender; +use tokio::sync::Semaphore; +use tracing::error; + +use crate::base::iana::OptRcode; +use crate::base::{Name, Rtype}; +use crate::zonetree::{ReadableZone, SharedRrset, StoredName}; + +//------------ ZoneFunneler --------------------------------------------------- + +pub struct ZoneFunneler { + read: Box, + qname: StoredName, + zone_soa_rrset: SharedRrset, + batcher_tx: Sender<(Name, SharedRrset)>, + zone_walk_semaphore: Arc, +} + +impl ZoneFunneler { + pub fn new( + read: Box, + qname: StoredName, + zone_soa_rrset: SharedRrset, + batcher_tx: Sender<(Name, SharedRrset)>, + zone_walk_semaphore: Arc, + ) -> Self { + Self { + read, + qname, + zone_soa_rrset, + batcher_tx, + zone_walk_semaphore, + } + } + + pub async fn run(self) -> Result<(), OptRcode> { + // Limit the number of concurrently running XFR related zone walking + // operations. + if self.zone_walk_semaphore.acquire().await.is_err() { + error!("Internal error: Failed to acquire XFR zone walking semaphore"); + return Err(OptRcode::SERVFAIL); + } + + let cloned_batcher_tx = self.batcher_tx.clone(); + let op = Box::new(move |owner: StoredName, rrset: &SharedRrset| { + if rrset.rtype() != Rtype::SOA { + let _ = cloned_batcher_tx + .blocking_send((owner.clone(), rrset.clone())); + // If the blocking send fails it means that the + // batcher is no longer available. This can happen if + // it was no longer able to pass messages back to the + // underlying transport, which can happen if the + // client closed the connection. We don't log this + // because we can't stop the tree walk and so will + // keep hitting this error until the tree walk is + // complete, causing a lot of noise if we were to log + // this. + } + }); + + // Walk the zone tree, invoking our operation for each leaf. + match self.read.is_async() { + true => { + self.read.walk_async(op).await; + if let Err(err) = self + .batcher_tx + .send((self.qname, self.zone_soa_rrset)) + .await + { + error!("Internal error: Failed to send final AXFR SOA to batcher: {err}"); + return Err(OptRcode::SERVFAIL); + } + } + false => { + tokio::task::spawn_blocking(move || { + self.read.walk(op); + if let Err(err) = self + .batcher_tx + .blocking_send((self.qname, self.zone_soa_rrset)) + { + error!("Internal error: Failed to send final AXFR SOA to batcher: {err}"); + // Note: The lack of the final SOA will be detected by the batcher. + } + }); + } + } + + Ok(()) + } +} diff --git a/src/net/server/middleware/xfr/batcher.rs b/src/net/server/middleware/xfr/batcher.rs new file mode 100644 index 00000000..ae83d5d6 --- /dev/null +++ b/src/net/server/middleware/xfr/batcher.rs @@ -0,0 +1,224 @@ +use core::marker::PhantomData; + +use std::sync::Arc; + +use octseq::Octets; +use tokio::sync::mpsc::UnboundedSender; +use tracing::trace; + +use crate::base::iana::{Opcode, Rcode}; +use crate::base::message_builder::{ + AdditionalBuilder, AnswerBuilder, PushError, +}; +use crate::base::wire::Composer; +use crate::base::{Message, StreamTarget}; +use crate::net::server::batcher::{ + CallbackBatcher, Callbacks, ResourceRecordBatcher, +}; +use crate::net::server::service::{CallResult, ServiceResult}; +use crate::net::server::util::mk_builder_for_target; + +//------------ BatchReadyError ------------------------------------------------ + +#[derive(Clone, Copy, Debug)] +pub enum BatchReadyError { + PushError(PushError), + + SendError, + + MustFitInSingleMessage, +} + +//--- Display + +impl std::fmt::Display for BatchReadyError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + BatchReadyError::MustFitInSingleMessage => { + f.write_str("MustFitInSingleMessage") + } + BatchReadyError::PushError(err) => { + f.write_fmt(format_args!("PushError: {err}")) + } + BatchReadyError::SendError => f.write_str("SendError"), + } + } +} + +//--- From + +impl From for BatchReadyError { + fn from(err: PushError) -> Self { + Self::PushError(err) + } +} + +//------------ XfrRrBatcher --------------------------------------------------- + +pub struct XfrRrBatcher { + _phantom: PhantomData<(RequestOctets, Target)>, +} + +impl XfrRrBatcher +where + RequestOctets: Octets + Sync + Send + 'static, + Target: Composer + Default + Send + 'static, +{ + pub fn build( + req_msg: Arc>, + sender: UnboundedSender>, + soft_byte_limit: Option, + hard_rr_limit: Option, + must_fit_in_single_message: bool, + ) -> impl ResourceRecordBatcher + { + let cb_state = CallbackState::new( + req_msg.clone(), + sender, + soft_byte_limit, + hard_rr_limit, + must_fit_in_single_message, + ); + + CallbackBatcher::< + RequestOctets, + Target, + Self, + CallbackState, + >::new(req_msg, cb_state) + } +} + +impl XfrRrBatcher +where + RequestOctets: Octets, + Target: Composer + Default, +{ + fn set_axfr_header( + msg: &Message, + additional: &mut AdditionalBuilder>, + ) { + // https://datatracker.ietf.org/doc/html/rfc5936#section-2.2.1 + // 2.2.1: Header Values + // + // "These are the DNS message header values for AXFR responses. + // + // ID MUST be copied from request -- see Note a) + // + // QR MUST be 1 (Response) + // + // OPCODE MUST be 0 (Standard Query) + // + // Flags: + // AA normally 1 -- see Note b) + // TC MUST be 0 (Not truncated) + // RD RECOMMENDED: copy request's value; MAY be set to 0 + // RA SHOULD be 0 -- see Note c) + // Z "mbz" -- see Note d) + // AD "mbz" -- see Note d) + // CD "mbz" -- see Note d)" + let header = additional.header_mut(); + + // Note: MandatoryMiddlewareSvc will also "fix" ID and QR, so strictly + // speaking this isn't necessary, but as a caller might not use + // MandatoryMiddlewareSvc we do it anyway to try harder to conform to + // the RFC. + header.set_id(msg.header().id()); + header.set_qr(true); + + header.set_opcode(Opcode::QUERY); + header.set_aa(true); + header.set_tc(false); + header.set_rd(msg.header().rd()); + header.set_ra(false); + header.set_z(false); + header.set_ad(false); + header.set_cd(false); + } +} + +//--- Callbacks + +impl + Callbacks> + for XfrRrBatcher +where + RequestOctets: Octets, + Target: Composer + Default, +{ + type Error = BatchReadyError; + + fn batch_started( + cb_state: &CallbackState, + msg: &Message, + ) -> Result>, PushError> { + let mut builder = mk_builder_for_target(); + if let Some(limit) = cb_state.soft_byte_limit { + builder.set_push_limit(limit); + } + let answer = builder.start_answer(msg, Rcode::NOERROR)?; + Ok(answer) + } + + fn batch_ready( + cb_state: &CallbackState, + builder: AnswerBuilder>, + finished: bool, + ) -> Result<(), Self::Error> { + if !finished && cb_state.must_fit_in_single_message { + return Err(BatchReadyError::MustFitInSingleMessage); + } + + trace!("Sending RR batch"); + let mut additional = builder.additional(); + Self::set_axfr_header(&cb_state.req_msg, &mut additional); + let call_result = Ok(CallResult::new(additional)); + cb_state + .sender + .send(call_result) + .map_err(|_unsent_msg| BatchReadyError::SendError) + } + + fn record_pushed( + cb_state: &CallbackState, + answer: &AnswerBuilder>, + ) -> bool { + if let Some(hard_rr_limit) = cb_state.hard_rr_limit { + let ancount = answer.counts().ancount(); + let limit_reached = ancount == hard_rr_limit; + trace!( + "ancount={ancount}, hard_rr_limit={hard_rr_limit}, limit_reached={limit_reached}"); + limit_reached + } else { + false + } + } +} + +//------------ CallbackState -------------------------------------------------- + +struct CallbackState { + req_msg: Arc>, + sender: UnboundedSender>, + soft_byte_limit: Option, + hard_rr_limit: Option, + must_fit_in_single_message: bool, +} + +impl CallbackState { + fn new( + req_msg: Arc>, + sender: UnboundedSender>, + soft_byte_limit: Option, + hard_rr_limit: Option, + must_fit_in_single_message: bool, + ) -> Self { + Self { + req_msg, + sender, + soft_byte_limit, + hard_rr_limit, + must_fit_in_single_message, + } + } +} diff --git a/src/net/server/middleware/xfr/data_provider.rs b/src/net/server/middleware/xfr/data_provider.rs new file mode 100644 index 00000000..65aae261 --- /dev/null +++ b/src/net/server/middleware/xfr/data_provider.rs @@ -0,0 +1,243 @@ +use core::future::{ready, Future}; +use core::ops::Deref; +use core::pin::Pin; + +use octseq::Octets; + +use std::boxed::Box; +use std::vec::Vec; + +use crate::base::wire::ParseError; +use crate::base::Serial; +use crate::net::server::message::Request; +use crate::zonetree::types::EmptyZoneDiff; +use crate::zonetree::{Zone, ZoneDiff, ZoneTree}; + +//------------ XfrDataProviderError ------------------------------------------- + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum XfrDataProviderError { + ParseError(ParseError), + + UnknownZone, + + Refused, + + TemporarilyUnavailable, +} + +//--- From + +impl From for XfrDataProviderError { + fn from(err: ParseError) -> Self { + Self::ParseError(err) + } +} + +//------------ XfrData -------------------------------------------------------- + +/// The data supplied by an [`XfrDataProvider`]. +pub struct XfrData { + /// The zone to transfer. + zone: Zone, + + /// The requested diffs. + /// + /// Empty if the requested diff range could not be satisfied. + diffs: Vec, + + /// Should XFR be done in RFC 5936 backward compatible mode? + /// + /// See: https://www.rfc-editor.org/rfc/rfc5936#section-7 + compatibility_mode: bool, +} + +impl XfrData { + pub fn new( + zone: Zone, + diffs: Vec, + backward_compatible: bool, + ) -> Self { + Self { + zone, + diffs, + compatibility_mode: backward_compatible, + } + } + + pub fn zone(&self) -> &Zone { + &self.zone + } + + pub fn diffs(&self) -> &[Diff] { + &self.diffs + } + + pub fn into_diffs(self) -> Vec { + self.diffs + } + + pub fn compatibility_mode(&self) -> bool { + self.compatibility_mode + } +} + +//------------ XfrDataProvider ------------------------------------------------ + +/// A provider of data needed for responding to XFR requests. +pub trait XfrDataProvider { + type Diff: ZoneDiff + Send + Sync; + + /// Request data needed to respond to an XFR request. + /// + /// Returns Ok if the request is allowed and the requested data is + /// available. + /// + /// Returns Err otherwise. + /// + /// Pass `Some` zone SOA serial number in the `diff_from` parameter to + /// request `ZoneDiff`s from the specified serial to the current SOA + /// serial number of the zone, inclusive, if available. + #[allow(clippy::type_complexity)] + fn request( + &self, + req: &Request, + diff_from: Option, + ) -> Pin< + Box< + dyn Future< + Output = Result< + XfrData, + XfrDataProviderError, + >, + > + Sync + + Send + + '_, + >, + > + where + Octs: Octets + Send + Sync; +} + +//--- impl XfrDataProvider for Deref + +impl XfrDataProvider for U +where + T: XfrDataProvider + 'static, + U: Deref, +{ + type Diff = T::Diff; + + fn request( + &self, + req: &Request, + diff_from: Option, + ) -> Pin< + Box< + dyn Future< + Output = Result< + XfrData, + XfrDataProviderError, + >, + > + Sync + + Send + + '_, + >, + > + where + Octs: Octets + Send + Sync, + { + (**self).request(req, diff_from) + } +} + +//--- impl XfrDataProvider for Zone + +impl XfrDataProvider for Zone { + type Diff = EmptyZoneDiff; + + /// Request data needed to respond to an XFR request. + /// + /// Returns Ok(Self, vec![]) if the given apex name and class match this + /// zone, irrespective of the given request or diff range. + /// + /// Returns Err if the requested zone is not this zone. + fn request( + &self, + req: &Request, + _diff_from: Option, + ) -> Pin< + Box< + dyn Future< + Output = Result< + XfrData, + XfrDataProviderError, + >, + > + Sync + + Send, + >, + > + where + Octs: Octets + Send + Sync, + { + let res = req + .message() + .sole_question() + .map_err(XfrDataProviderError::ParseError) + .and_then(|q| { + if q.qname() == self.apex_name() && q.qclass() == self.class() + { + Ok(XfrData::new(self.clone(), vec![], false)) + } else { + Err(XfrDataProviderError::UnknownZone) + } + }); + + Box::pin(ready(res)) + } +} + +//--- impl XfrDataProvider for ZoneTree + +impl XfrDataProvider for ZoneTree { + type Diff = EmptyZoneDiff; + + /// Request data needed to respond to an XFR request. + /// + /// Returns Ok(zone, vec![]) if the given apex name and class match a zone + /// in this zone tree, irrespective of the given request or diff range. + /// + /// Returns Err if the requested zone is not this zone tree. + fn request( + &self, + req: &Request, + _diff_from: Option, + ) -> Pin< + Box< + dyn Future< + Output = Result< + XfrData, + XfrDataProviderError, + >, + > + Sync + + Send, + >, + > + where + Octs: Octets + Send + Sync, + { + let res = req + .message() + .sole_question() + .map_err(XfrDataProviderError::ParseError) + .and_then(|q| { + if let Some(zone) = self.find_zone(q.qname(), q.qclass()) { + Ok(XfrData::new(zone.clone(), vec![], false)) + } else { + Err(XfrDataProviderError::UnknownZone) + } + }); + + Box::pin(ready(res)) + } +} diff --git a/src/net/server/middleware/xfr/ixfr.rs b/src/net/server/middleware/xfr/ixfr.rs new file mode 100644 index 00000000..26361ba9 --- /dev/null +++ b/src/net/server/middleware/xfr/ixfr.rs @@ -0,0 +1,146 @@ +use std::vec::Vec; + +use bytes::Bytes; +use futures_util::{pin_mut, StreamExt}; +use tokio::sync::mpsc::Sender; +use tracing::error; + +use crate::base::iana::OptRcode; +use crate::base::{Name, Rtype}; +use crate::zonetree::{SharedRrset, StoredName, ZoneDiff, ZoneDiffItem}; + +//------------ DiffFunneler ---------------------------------------------------- + +pub struct DiffFunneler { + qname: StoredName, + zone_soa_rrset: SharedRrset, + diffs: Vec, + batcher_tx: Sender<(Name, SharedRrset)>, +} + +impl DiffFunneler +where + Diff: ZoneDiff, +{ + pub fn new( + qname: StoredName, + zone_soa_rrset: SharedRrset, + diffs: Vec, + batcher_tx: Sender<(Name, SharedRrset)>, + ) -> Self { + Self { + qname, + zone_soa_rrset, + diffs, + batcher_tx, + } + } + + pub async fn run(self) -> Result<(), OptRcode> { + // https://datatracker.ietf.org/doc/html/rfc1995#section-4 + // 4. Response Format + // ... + // "If incremental zone transfer is available, one or more + // difference sequences is returned. The list of difference + // sequences is preceded and followed by a copy of the server's + // current version of the SOA. + // + // Each difference sequence represents one update to the zone + // (one SOA serial change) consisting of deleted RRs and added + // RRs. The first RR of the deleted RRs is the older SOA RR + // and the first RR of the added RRs is the newer SOA RR. + // + // Modification of an RR is performed first by removing the + // original RR and then adding the modified one. + // + // The sequences of differential information are ordered oldest + // first newest last. Thus, the differential sequences are the + // history of changes made since the version known by the IXFR + // client up to the server's current version. + // + // RRs in the incremental transfer messages may be partial. That + // is, if a single RR of multiple RRs of the same RR type changes, + // only the changed RR is transferred." + + if let Err(err) = self + .batcher_tx + .send((self.qname.clone(), self.zone_soa_rrset.clone())) + .await + { + error!("Internal error: Failed to send initial IXFR SOA to batcher: {err}"); + return Err(OptRcode::SERVFAIL); + } + + let qname = self.qname.clone(); + + for diff in self.diffs { + // 4. Response Format + // "Each difference sequence represents one update to the + // zone (one SOA serial change) consisting of deleted RRs + // and added RRs. The first RR of the deleted RRs is the + // older SOA RR and the first RR of the added RRs is the + // newer SOA RR. + + let removed_soa = + diff.get_removed(qname.clone(), Rtype::SOA).await.unwrap(); // The diff MUST have a SOA record + Self::send_diff_section( + &qname, + &self.batcher_tx, + removed_soa, + diff.removed(), + ) + .await?; + + let added_soa = + diff.get_added(qname.clone(), Rtype::SOA).await.unwrap(); // The diff MUST have a SOA record + Self::send_diff_section( + &qname, + &self.batcher_tx, + added_soa, + diff.added(), + ) + .await?; + } + + if let Err(err) = self + .batcher_tx + .send((qname.clone(), self.zone_soa_rrset)) + .await + { + error!("Internal error: Failed to send final IXFR SOA to batcher: {err}"); + return Err(OptRcode::SERVFAIL); + } + + Ok(()) + } + + async fn send_diff_section( + qname: &StoredName, + batcher_tx: &Sender<(Name, SharedRrset)>, + soa: &SharedRrset, + diff_stream: ::Stream<'_>, + ) -> Result<(), OptRcode> { + if let Err(err) = batcher_tx.send((qname.clone(), soa.clone())).await + { + error!("Internal error: Failed to send SOA to batcher: {err}"); + return Err(OptRcode::SERVFAIL); + } + + pin_mut!(diff_stream); + + while let Some(item) = diff_stream.next().await { + let (owner, rtype) = item.key(); + if *rtype != Rtype::SOA { + let rrset = item.value(); + if let Err(err) = + batcher_tx.send((owner.clone(), rrset.clone())).await + { + error!("Internal error: Failed to send RRSET to batcher: {err}"); + return Err(OptRcode::SERVFAIL); + } + } + } + + Ok(()) + } +} diff --git a/src/net/server/middleware/xfr/mod.rs b/src/net/server/middleware/xfr/mod.rs new file mode 100644 index 00000000..9eb7f74d --- /dev/null +++ b/src/net/server/middleware/xfr/mod.rs @@ -0,0 +1,60 @@ +//! RFC 5936 AXFR and RFC 1995 IXFR request handling middleware. +//! +//! This module provides the [`XfrMiddlewareSvc`] service which responds to +//! [RFC 5936] AXFR and [RFC 1995] IXFR requests to perform entire or +//! incremental difference based zone transfers. +//! +//! Determining which requests to honour and with what data is delegated to a +//! caller supplied implementation of the [`XfrDataProvider`] trait. +//! [`XfrDataProvider`] implementations for [`Zone`] and [`ZoneTree`] are +//! provided allowing those types to be used as-is as XFR data providers with +//! this middleware service. +//! +//! # Requiring TSIG authenticated XFR requests +//! +//! To require XFR requests to be TSIG authenticated, implement +//! `XfrDataProvider>`, extract the key data using +//! [`Request::metadata()`] and verify that a TSIG key was used to sign the +//! request, and that the name and algorithm of the used key are acceptable to +//! you. +//! +//! You can then use your [`XfrDataProvider`] impl with [`XfrMiddlewareSvc`], +//! and add [`TsigMiddlewareSvc`] directly before [`XfrMiddlewareSvc`] in the +//! middleware layer stack so that the used `Key` is made available from the +//! TSIG middleware to the XFR middleware. +//! +//! # Limitations +//! +//! * RFC 1995 2 Brief Description of the Protocol states: _"To ensure +//! integrity, servers should use UDP checksums for all UDP responses."_. +//! This is not implemented. +//! * RFC 1995 5 Purging Strategy states: _"Information about older versions +//! should be purged if the total length of an IXFR response would be longer +//! than that of an AXFR response."_. This is not implemented. +//! * RFC 1995 6 Optional Condensation of Multiple Versions states: _"An IXFR +//! server may optionally condense multiple difference sequences into a +//! single difference sequence, thus, dropping information on intermediate +//! versions."_. This is not implemented. +//! +//! [RFC 5936]: https://www.rfc-editor.org/info/rfc5936 +//! [RFC 1995]: https://www.rfc-editor.org/info/rfc1995 +//! [`Request::metadata()`]: crate::net::server::message::Request::metadata +//! [`TsigMiddlewareSvc`]: +//! crate::net::server::middleware::tsig::TsigMiddlewareSvc +//! [`XfrDataProvider`]: super::data_provider::XfrDataProvider +//! [`Zone`]: crate::net::zonetree::Zone +//! [`ZoneTree`]: crate::net::zonetree::ZoneTree +mod axfr; +mod batcher; +mod ixfr; +mod responder; +mod util; + +pub mod data_provider; +pub mod service; + +pub use data_provider::{XfrData, XfrDataProvider, XfrDataProviderError}; +pub use service::XfrMiddlewareSvc; + +#[cfg(test)] +mod tests; diff --git a/src/net/server/middleware/xfr/responder.rs b/src/net/server/middleware/xfr/responder.rs new file mode 100644 index 00000000..3b0f64b9 --- /dev/null +++ b/src/net/server/middleware/xfr/responder.rs @@ -0,0 +1,161 @@ +use std::sync::Arc; + +use bytes::Bytes; +use octseq::Octets; +use tokio::sync::mpsc::{Receiver, UnboundedSender}; +use tokio::sync::Semaphore; +use tracing::{debug, error}; + +use crate::base::iana::OptRcode; +use crate::base::rdata::RecordData; +use crate::base::wire::Composer; +use crate::base::{Message, Name, Rtype}; +use crate::net::server::batcher::ResourceRecordBatcher; +use crate::net::server::middleware::xfr::util::add_to_stream; +use crate::net::server::service::ServiceResult; +use crate::net::server::util::mk_builder_for_target; +use crate::zonetree::{Answer, SharedRrset}; + +use super::batcher::{BatchReadyError, XfrRrBatcher}; + +//------------ BatchingRrResponder --------------------------------------------- + +pub struct BatchingRrResponder { + msg: Arc>, + zone_soa_answer: Answer, + batcher_rx: Receiver<(Name, SharedRrset)>, + response_tx: UnboundedSender>, + compatibility_mode: bool, + soft_byte_limit: usize, + must_fit_in_single_message: bool, + batcher_semaphore: Arc, +} + +impl BatchingRrResponder +where + RequestOctets: Octets + Send + Sync + 'static + Unpin, + Target: Composer + Default + Send + Sync + 'static, +{ + #[allow(clippy::too_many_arguments)] + pub fn new( + msg: Arc>, + zone_soa_answer: Answer, + batcher_rx: Receiver<(Name, SharedRrset)>, + response_tx: UnboundedSender>, + compatibility_mode: bool, + soft_byte_limit: usize, + must_fit_in_single_message: bool, + batcher_semaphore: Arc, + ) -> Self { + Self { + msg, + zone_soa_answer, + batcher_rx, + response_tx, + compatibility_mode, + soft_byte_limit, + must_fit_in_single_message, + batcher_semaphore, + } + } + + pub async fn run(mut self) -> Result<(), OptRcode> { + // Limit the number of concurrently running XFR batching + // operations. + if self.batcher_semaphore.acquire().await.is_err() { + error!("Internal error: Failed to acquire XFR batcher semaphore"); + return Err(OptRcode::SERVFAIL); + } + + // SAFETY: msg.sole_question() was already checked in + // get_relevant_question(). + let qclass = self.msg.sole_question().unwrap().qclass(); + + // Note: NSD apparently uses name compresson on AXFR responses + // because AXFR responses they typically contain lots of + // alphabetically ordered duplicate names which compress well. NSD + // limits AXFR responses to 16,383 bytes because DNS name + // compression uses a 14-bit offset (2^14-1=16383) from the start + // of the message to the first occurence of a name instead of + // repeating the name, and name compression is less effective + // over 16383 bytes. (Credit: Wouter Wijngaards) + // + // TODO: Once we start supporting name compression in responses decide + // if we want to behave the same way. + + let hard_rr_limit = match self.compatibility_mode { + true => Some(1), + false => None, + }; + + let mut batcher = XfrRrBatcher::build( + self.msg.clone(), + self.response_tx.clone(), + Some(self.soft_byte_limit), + hard_rr_limit, + self.must_fit_in_single_message, + ); + + let mut last_rr_rtype = None; + + while let Some((owner, rrset)) = self.batcher_rx.recv().await { + for rr in rrset.data() { + last_rr_rtype = Some(rr.rtype()); + + if let Err(err) = + batcher.push((owner.clone(), qclass, rrset.ttl(), rr)) + { + match err { + BatchReadyError::MustFitInSingleMessage => { + // https://datatracker.ietf.org/doc/html/rfc1995#section-2 + // 2. Brief Description of the Protocol + // .. + // "If the UDP reply does not fit, the + // query is responded to with a single SOA + // record of the server's current version + // to inform the client that a TCP query + // should be initiated." + debug_assert!(self.must_fit_in_single_message); + debug!("Responding to IXFR with single SOA because response does not fit in a single UDP reply"); + + let builder = mk_builder_for_target(); + + let resp = self + .zone_soa_answer + .to_message(&self.msg, builder); + + add_to_stream(resp, &self.response_tx); + + return Ok(()); + } + + BatchReadyError::PushError(err) => { + error!("Internal error: Failed to send RR to batcher: {err}"); + return Err(OptRcode::SERVFAIL); + } + + BatchReadyError::SendError => { + debug!("Batcher was unable to send completed batch. Was the receiver dropped?"); + return Err(OptRcode::SERVFAIL); + } + } + } + } + } + + if let Err(err) = batcher.finish() { + debug!("Batcher was unable to finish: {err}"); + return Err(OptRcode::SERVFAIL); + } + + if last_rr_rtype != Some(Rtype::SOA) { + error!( + "Internal error: Last RR was {}, expected SOA", + last_rr_rtype.unwrap() + ); + return Err(OptRcode::SERVFAIL); + } + + Ok(()) + } +} diff --git a/src/net/server/middleware/xfr/service.rs b/src/net/server/middleware/xfr/service.rs new file mode 100644 index 00000000..d0862f5e --- /dev/null +++ b/src/net/server/middleware/xfr/service.rs @@ -0,0 +1,754 @@ +use core::future::{ready, Future, Ready}; +use core::marker::PhantomData; +use core::ops::ControlFlow; + +use std::boxed::Box; +use std::fmt::Debug; +use std::pin::Pin; +use std::sync::Arc; +use std::vec::Vec; + +use futures_util::stream::{once, Once, Stream}; +use octseq::Octets; +use tokio::sync::mpsc::unbounded_channel; +use tokio::sync::Semaphore; +use tokio_stream::wrappers::UnboundedReceiverStream; +use tracing::{debug, error, info, trace, warn}; + +use crate::base::iana::{Opcode, OptRcode}; +use crate::base::wire::Composer; +use crate::base::{Message, ParsedName, Question, Rtype, Serial, ToName}; +use crate::net::server::message::{Request, TransportSpecificContext}; +use crate::net::server::middleware::stream::MiddlewareStream; +use crate::net::server::middleware::xfr::axfr::ZoneFunneler; +use crate::net::server::middleware::xfr::data_provider::XfrDataProvider; +use crate::net::server::middleware::xfr::data_provider::XfrDataProviderError; +use crate::net::server::middleware::xfr::ixfr::DiffFunneler; +use crate::net::server::middleware::xfr::responder::BatchingRrResponder; +use crate::net::server::service::{CallResult, Service, ServiceFeedback}; +use crate::net::server::util::{mk_builder_for_target, mk_error_response}; +use crate::rdata::{Soa, ZoneRecordData}; +use crate::zonetree::{ + Answer, AnswerContent, ReadableZone, SharedRrset, StoredName, +}; + +use super::util::{add_to_stream, read_soa}; + +//------------ Constants ----------------------------------------------------- + +/// https://datatracker.ietf.org/doc/html/rfc1035#section-2.3.4 +/// 2.3.4. Size limits +/// "UDP messages 512 octets or less" +const MAX_UDP_MSG_BYTE_LEN: u16 = 512; + +/// https://datatracker.ietf.org/doc/html/rfc1035#section-4.2.2 +/// 4.2.2. TCP usage +/// "The message is prefixed with a two byte length field which gives the +/// message length, excluding the two byte length field" +const MAX_TCP_MSG_BYTE_LEN: u16 = u16::MAX; + +//------------ XfrMiddlewareSvc ---------------------------------------------- + +/// RFC 5936 AXFR and RFC 1995 IXFR request handling middleware. +/// +/// See the [module documentation] for a high level introduction. +/// +/// [module documentation]: crate::net::server::middleware::xfr +#[derive(Clone, Debug)] +pub struct XfrMiddlewareSvc { + /// The upstream [`Service`] to pass requests to and receive responses + /// from. + next_svc: NextSvc, + + /// A caller supplied implementation of [`XfrDataProvider`] for + /// determining which requests to answer and with which data. + xfr_data_provider: XDP, + + /// A limit on the number of XFR related zone walking operations + /// that may run concurrently. + zone_walking_semaphore: Arc, + + /// A limit on the number of XFR related response batching operations that + /// may run concurrently. + batcher_semaphore: Arc, + + _phantom: PhantomData<(RequestOctets, RequestMeta)>, +} + +impl + XfrMiddlewareSvc +where + XDP: XfrDataProvider, +{ + /// Creates a new instance of this middleware. + /// + /// Takes an implementation of [`XfrDataProvider`] as a parameter to + /// determine which requests to honour and with which data. + /// + /// The `max_concurrency` parameter limits the number of simultaneous zone + /// transfer operations that may occur concurrently without blocking. + #[must_use] + pub fn new( + next_svc: NextSvc, + xfr_data_provider: XDP, + max_concurrency: usize, + ) -> Self { + let zone_walking_semaphore = + Arc::new(Semaphore::new(max_concurrency)); + let batcher_semaphore = Arc::new(Semaphore::new(max_concurrency)); + + Self { + next_svc, + xfr_data_provider, + zone_walking_semaphore, + batcher_semaphore, + _phantom: PhantomData, + } + } +} + +impl + XfrMiddlewareSvc +where + RequestOctets: Octets + Send + Sync + 'static + Unpin, + for<'a> ::Range<'a>: Send + Sync, + NextSvc: Service + Clone + Send + Sync + 'static, + NextSvc::Future: Send + Sync + Unpin, + NextSvc::Target: Composer + Default + Send + Sync, + NextSvc::Stream: Send + Sync, + XDP: XfrDataProvider, + XDP::Diff: Debug + 'static, +{ + /// Pre-process received DNS XFR queries. + /// + /// Other types of query will be propagated unmodified to the next + /// middleware or application service in the layered stack of services. + /// + /// Data to respond to the query will be requested from the given + /// [`XfrDataProvider`] which will act according to its policy concerning + /// the given [`Request`]. + pub async fn preprocess( + zone_walking_semaphore: Arc, + batcher_semaphore: Arc, + req: &Request, + xfr_data_provider: XDP, + ) -> Result< + ControlFlow< + XfrMiddlewareStream< + NextSvc::Future, + NextSvc::Stream, + ::Item, + >, + >, + OptRcode, + > { + let msg = req.message(); + + // Do we support this type of request? + let Some(q) = Self::get_relevant_question(msg) else { + return Ok(ControlFlow::Continue(())); + }; + + // https://datatracker.ietf.org/doc/html/rfc1995#section-3 + // 3. Query Format + // "The IXFR query packet format is the same as that of a normal DNS + // query, but with the query type being IXFR and the authority + // section containing the SOA record of client's version of the + // zone." + let ixfr_query_serial = if let Ok(Some(Ok(query_soa))) = msg + .authority() + .map(|section| section.limit_to::>>().next()) + { + Some(query_soa.data().serial()) + } else { + None + }; + + if q.qtype() == Rtype::IXFR && ixfr_query_serial.is_none() { + warn!( + "{} for {} from {} refused: IXFR request lacks authority section SOA", + q.qtype(), + q.qname(), + req.client_addr() + ); + return Err(OptRcode::FORMERR); + } + + // Is transfer allowed for the requested zone for this requestor? + let xfr_data = xfr_data_provider + .request(req, ixfr_query_serial) + .await + .map_err(|err| match err { + XfrDataProviderError::ParseError(err) => { + debug!( + "{} for {} from {} refused: parse error: {err}", + q.qtype(), + q.qname(), + req.client_addr() + ); + OptRcode::FORMERR + } + + XfrDataProviderError::UnknownZone => { + // https://datatracker.ietf.org/doc/html/rfc5936#section-2.2.1 + // 2.2.1 Header Values + // "If a server is not authoritative for the queried + // zone, the server SHOULD set the value to NotAuth(9)" + debug!( + "{} for {} from {} refused: unknown zone", + q.qtype(), + q.qname(), + req.client_addr() + ); + OptRcode::NOTAUTH + } + + XfrDataProviderError::TemporarilyUnavailable => { + // The zone is not yet loaded or has expired, both of + // which are presumably transient conditions and thus + // SERVFAIL is the appropriate response, not NOTAUTH, as + // we know we are supposed to be authoritative for the + // zone but we just don't have the data right now. + warn!( + "{} for {} from {} refused: zone not currently available", + q.qtype(), + q.qname(), + req.client_addr() + ); + OptRcode::SERVFAIL + } + + XfrDataProviderError::Refused => { + warn!( + "{} for {} from {} refused: access denied", + q.qtype(), + q.qname(), + req.client_addr() + ); + OptRcode::REFUSED + } + })?; + + // Read the zone SOA RR + let read = xfr_data.zone().read(); + let Ok(zone_soa_answer) = read_soa(&read, q.qname().to_name()).await + else { + debug!( + "{} for {} from {} refused: name is outside the zone", + q.qtype(), + q.qname(), + req.client_addr() + ); + return Err(OptRcode::SERVFAIL); + }; + + match q.qtype() { + Rtype::AXFR if req.transport_ctx().is_udp() => { + // https://datatracker.ietf.org/doc/html/rfc5936#section-4.2 + // 4.2. UDP + // "With the addition of EDNS0 and applications that require + // many small zones, such as in web hosting and some ENUM + // scenarios, AXFR sessions on UDP would now seem + // desirable. However, there are still some aspects of + // AXFR sessions that are not easily translated to UDP. + // + // Therefore, this document does not update RFC 1035 in + // this respect: AXFR sessions over UDP transport are not + // defined." + warn!( + "{} for {} from {} refused: AXFR not supported over UDP", + q.qtype(), + q.qname(), + req.client_addr() + ); + let response = mk_error_response(msg, OptRcode::NOTIMP); + let res = Ok(CallResult::new(response)); + Ok(ControlFlow::Break(MiddlewareStream::Map(once(ready( + res, + ))))) + } + + Rtype::AXFR | Rtype::IXFR if xfr_data.diffs().is_empty() => { + if q.qtype() == Rtype::IXFR && xfr_data.diffs().is_empty() { + // https://datatracker.ietf.org/doc/html/rfc1995#section-4 + // 4. Response Format + // "If incremental zone transfer is not available, the + // entire zone is returned. The first and the last RR of + // the response is the SOA record of the zone. I.e. the + // behavior is the same as an AXFR response except the + // query type is IXFR." + info!( + "IXFR for {} (serial {} from {}: diffs not available, falling back to AXFR", + q.qname(), + ixfr_query_serial.unwrap(), // SAFETY: Always Some() if IXFR + req.client_addr() + ); + } else { + info!( + "AXFR for {} from {}", + q.qname(), + req.client_addr() + ); + } + let stream = Self::respond_to_axfr_query( + zone_walking_semaphore, + batcher_semaphore, + req, + q.qname().to_name(), + &zone_soa_answer, + read, + xfr_data.compatibility_mode(), + ) + .await?; + + Ok(ControlFlow::Break(stream)) + } + + Rtype::IXFR => { + // SAFETY: Always Some() if IXFR + let ixfr_query_serial = ixfr_query_serial.unwrap(); + info!( + "IXFR for {} (serial {ixfr_query_serial}) from {}", + q.qname(), + req.client_addr() + ); + + // https://datatracker.ietf.org/doc/html/rfc1995#section-2 + // 2. Brief Description of the Protocol + // "Transport of a query may be by either UDP or TCP. If an + // IXFR query is via UDP, the IXFR server may attempt to + // reply using UDP if the entire response can be contained + // in a single DNS packet. If the UDP reply does not fit, + // the query is responded to with a single SOA record of + // the server's current version to inform the client that a + // TCP query should be initiated." + let stream = Self::respond_to_ixfr_query( + batcher_semaphore.clone(), + req, + ixfr_query_serial, + q.qname().to_name(), + &zone_soa_answer, + xfr_data.into_diffs(), + ) + .await?; + + Ok(ControlFlow::Break(stream)) + } + + _ => { + // Other QTYPEs should have been filtered out by get_relevant_question(). + unreachable!(); + } + } + } + + /// Generate and send an AXFR response for a given request and zone. + #[allow(clippy::too_many_arguments)] + async fn respond_to_axfr_query( + zone_walk_semaphore: Arc, + batcher_semaphore: Arc, + req: &Request, + qname: StoredName, + zone_soa_answer: &Answer, + read: Box, + compatibility_mode: bool, + ) -> Result< + XfrMiddlewareStream< + NextSvc::Future, + NextSvc::Stream, + ::Item, + >, + OptRcode, + > { + let AnswerContent::Data(zone_soa_rrset) = + zone_soa_answer.content().clone() + else { + error!( + "AXFR for {qname} from {} refused: zone lacks SOA RR", + req.client_addr() + ); + return Err(OptRcode::SERVFAIL); + }; + + if compatibility_mode { + trace!( + "Compatibility mode enabled for client with IP address {}", + req.client_addr().ip() + ); + } + + // Return a stream of response messages containing: + // - SOA + // - RRSETs, one or more per response message + // - SOA + // + // Neither RFC 5936 nor RFC 1035 defined AXFR for UDP, only for TCP. + // However, RFC 1995 says that for IXFR if no diffs are available the + // full zone should be served just as with AXFR, and that UDP is + // supported as long as the entire XFR response fits in a single + // datagram. Thus we don't check for UDP or TCP here, except to abort + // if the response is too large to fit in a single UDP datagram, + // instead we let the caller that has the context decide whether AXFR + // is supported or not. + // + // References: + // - https://datatracker.ietf.org/doc/html/rfc1995#section-2 + // - https://datatracker.ietf.org/doc/html/rfc1995#section-4 + // - https://datatracker.ietf.org/doc/html/rfc5936#section-4.2 + + let soft_byte_limit = Self::calc_msg_bytes_available(req); + + // Create a stream that will be immediately returned to the caller. + // Async tasks will then push DNS response messages into the stream as + // they become available. + let (response_tx, response_rx) = unbounded_channel(); + let stream = UnboundedReceiverStream::new(response_rx); + + // Create a bounded queue for passing RRsets found during zone walking + // to a task which will batch the RRs together before pushing them + // into the result stream. + let (batcher_tx, batcher_rx) = + tokio::sync::mpsc::channel::<(StoredName, SharedRrset)>(100); + + let must_fit_in_single_message = + matches!(req.transport_ctx(), TransportSpecificContext::Udp(_)); + + if !must_fit_in_single_message { + // Notify the underlying transport to expect a stream of related + // responses. The transport should modify its behaviour to account + // for the potentially slow and long running nature of a + // transaction. + add_to_stream(ServiceFeedback::BeginTransaction, &response_tx); + } + + // Enqueue the zone SOA RRset for the batcher to process. + if batcher_tx + .send((qname.clone(), zone_soa_rrset.clone())) + .await + .is_err() + { + return Err(OptRcode::SERVFAIL); + } + + let msg = req.message().clone(); + + // Stream the remaining non-SOA zone RRsets in the background to the + // batcher. + let zone_funneler = ZoneFunneler::new( + read, + qname, + zone_soa_rrset, + batcher_tx, + zone_walk_semaphore, + ); + + let batching_responder = BatchingRrResponder::new( + req.message().clone(), + zone_soa_answer.clone(), + batcher_rx, + response_tx.clone(), + compatibility_mode, + soft_byte_limit, + must_fit_in_single_message, + batcher_semaphore, + ); + + let cloned_msg = msg.clone(); + let cloned_response_tx = response_tx.clone(); + + // Start the funneler. It will walk the zone and send all of the RRs + // one at a time to the batching responder. + tokio::spawn(async move { + if let Err(rcode) = zone_funneler.run().await { + add_to_stream( + mk_error_response(&cloned_msg, rcode), + &cloned_response_tx, + ); + } + }); + + // Start the batching responder. It will receive RRs from the funneler + // and push them in batches into the response stream. + tokio::spawn(async move { + match batching_responder.run().await { + Ok(()) => { + trace!("Ending transaction"); + add_to_stream( + ServiceFeedback::EndTransaction, + &response_tx, + ); + } + + Err(rcode) => { + add_to_stream( + mk_error_response(&msg, rcode), + &response_tx, + ); + } + } + }); + + // If either the funneler or batcher responder terminate then so will + // the other as they each own half of a send <-> receive channel and + // abort if the other side of the channel is gone. + + Ok(MiddlewareStream::Result(stream)) + } + + // Generate and send an IXFR response for the given request and zone + // diffs. + #[allow(clippy::too_many_arguments)] + async fn respond_to_ixfr_query( + batcher_semaphore: Arc, + req: &Request, + query_serial: Serial, + qname: StoredName, + zone_soa_answer: &Answer, + diffs: Vec, + ) -> Result< + XfrMiddlewareStream< + NextSvc::Future, + NextSvc::Stream, + ::Item, + >, + OptRcode, + > + where + XDP::Diff: Send + 'static, + { + let msg = req.message(); + + let AnswerContent::Data(zone_soa_rrset) = + zone_soa_answer.content().clone() + else { + return Err(OptRcode::SERVFAIL); + }; + let Some(first_rr) = zone_soa_rrset.first() else { + return Err(OptRcode::SERVFAIL); + }; + let ZoneRecordData::Soa(soa) = first_rr.data() else { + return Err(OptRcode::SERVFAIL); + }; + + // Note: Unlike RFC 5936 for AXFR, neither RFC 1995 nor RFC 9103 say + // anything about whether an IXFR response can consist of more than + // one response message, but given the 2^16 byte maximum response size + // of a TCP DNS message and the 2^16 maximum number of ANSWER RRs + // allowed per DNS response, large zones may not fit in a single + // response message and will have to be split into multiple response + // messages. + + // https://datatracker.ietf.org/doc/html/rfc1995#section-2 + // 2. Brief Description of the Protocol + // "If an IXFR query with the same or newer version number than that + // of the server is received, it is replied to with a single SOA + // record of the server's current version, just as in AXFR." + // ^^^^^^^^^^^^^^^ + // Errata https://www.rfc-editor.org/errata/eid3196 points out that + // this is NOT "just as in AXFR" as AXFR does not do that. + if query_serial >= soa.serial() { + trace!("Responding to IXFR with single SOA because query serial >= zone serial"); + let builder = mk_builder_for_target(); + let response = zone_soa_answer.to_message(msg, builder); + let res = Ok(CallResult::new(response)); + return Ok(MiddlewareStream::Map(once(ready(res)))); + } + + // TODO: Add something like the Bind `max-ixfr-ratio` option that + // "sets the size threshold (expressed as a percentage of the size of + // the full zone) beyond which named chooses to use an AXFR response + // rather than IXFR when answering zone transfer requests"? + + let soft_byte_limit = Self::calc_msg_bytes_available(req); + + // Create a stream that will be immediately returned to the caller. + // Async tasks will then push DNS response messages into the stream as + // they become available. + let (response_tx, response_rx) = unbounded_channel(); + let stream = UnboundedReceiverStream::new(response_rx); + + // Create a bounded queue for passing RRsets found during diff walking + // to a task which will batch the RRs together before pushing them + // into the result stream. + let (batcher_tx, batcher_rx) = + tokio::sync::mpsc::channel::<(StoredName, SharedRrset)>(100); + + let must_fit_in_single_message = + matches!(req.transport_ctx(), TransportSpecificContext::Udp(_)); + + if !must_fit_in_single_message { + // Notify the underlying transport to expect a stream of related + // responses. The transport should modify its behaviour to account + // for the potentially slow and long running nature of a + // transaction. + add_to_stream(ServiceFeedback::BeginTransaction, &response_tx); + } + + // Stream the IXFR diffs in the background to the batcher. + let diff_funneler = + DiffFunneler::new(qname, zone_soa_rrset, diffs, batcher_tx); + + let batching_responder = BatchingRrResponder::new( + req.message().clone(), + zone_soa_answer.clone(), + batcher_rx, + response_tx.clone(), + false, + soft_byte_limit, + must_fit_in_single_message, + batcher_semaphore, + ); + + let cloned_msg = msg.clone(); + let cloned_response_tx = response_tx.clone(); + + // Start the funneler. It will walk the diffs and send all of the RRs + // one at a time to the batching responder. + tokio::spawn(async move { + if let Err(rcode) = diff_funneler.run().await { + add_to_stream( + mk_error_response(&cloned_msg, rcode), + &cloned_response_tx, + ); + } + }); + + let cloned_msg = msg.clone(); + + // Start the batching responder. It will receive RRs from the funneler + // and push them in batches into the response stream. + tokio::spawn(async move { + match batching_responder.run().await { + Ok(()) => { + trace!("Ending transaction"); + add_to_stream( + ServiceFeedback::EndTransaction, + &response_tx, + ); + } + + Err(rcode) => { + add_to_stream( + mk_error_response(&cloned_msg, rcode), + &response_tx, + ); + } + } + }); + + // If either the funneler or batcher responder terminate then so will + // the other as they each own half of a send <-> receive channel and + // abort if the other side of the channel is gone. + + Ok(MiddlewareStream::Result(stream)) + } + + /// Is this message for us? + /// + /// Returns `Some(Question)` if the given query uses OPCODE QUERYY and has + /// a first question with a QTYPE of `AXFR` or `IXFR`, `None` otherwise. + fn get_relevant_question( + msg: &Message, + ) -> Option>>> { + if Opcode::QUERY == msg.header().opcode() && !msg.header().qr() { + if let Ok(q) = msg.sole_question() { + if matches!(q.qtype(), Rtype::AXFR | Rtype::IXFR) { + return Some(q); + } + } + } + + None + } + + fn calc_msg_bytes_available(req: &Request) -> usize { + let bytes_available = match req.transport_ctx() { + TransportSpecificContext::Udp(ctx) => { + let max_msg_size = ctx + .max_response_size_hint() + .unwrap_or(MAX_UDP_MSG_BYTE_LEN); + max_msg_size - req.num_reserved_bytes() + } + TransportSpecificContext::NonUdp(_) => { + MAX_TCP_MSG_BYTE_LEN - req.num_reserved_bytes() + } + }; + + bytes_available as usize + } +} + +//--- impl Service + +impl + Service + for XfrMiddlewareSvc +where + RequestOctets: Octets + Send + Sync + Unpin + 'static, + for<'a> ::Range<'a>: Send + Sync, + NextSvc: Service + Clone + Send + Sync + 'static, + NextSvc::Future: Send + Sync + Unpin, + NextSvc::Target: Composer + Default + Send + Sync, + NextSvc::Stream: Send + Sync, + XDP: XfrDataProvider + Clone + Sync + Send + 'static, + XDP::Diff: Debug + Sync, + RequestMeta: Clone + Default + Sync + Send + 'static, +{ + type Target = NextSvc::Target; + type Stream = XfrMiddlewareStream< + NextSvc::Future, + NextSvc::Stream, + ::Item, + >; + type Future = Pin + Send + Sync>>; + + fn call( + &self, + request: Request, + ) -> Self::Future { + let request = request.clone(); + let next_svc = self.next_svc.clone(); + let xfr_data_provider = self.xfr_data_provider.clone(); + let zone_walking_semaphore = self.zone_walking_semaphore.clone(); + let batcher_semaphore = self.batcher_semaphore.clone(); + Box::pin(async move { + match Self::preprocess( + zone_walking_semaphore, + batcher_semaphore, + &request, + xfr_data_provider, + ) + .await + { + Ok(ControlFlow::Continue(())) => { + let request = request.with_new_metadata(()); + let stream = next_svc.call(request).await; + MiddlewareStream::IdentityStream(stream) + } + + Ok(ControlFlow::Break(stream)) => stream, + + Err(rcode) => { + let response = + mk_error_response(request.message(), rcode); + let res = Ok(CallResult::new(response)); + MiddlewareStream::Map(once(ready(res))) + } + } + }) + } +} + +//------------ XfrMapStream --------------------------------------------------- + +pub type XfrResultStream = UnboundedReceiverStream; + +//------------ XfrMiddlewareStream -------------------------------------------- + +pub type XfrMiddlewareStream = MiddlewareStream< + Future, + Stream, + Once>, + XfrResultStream, + StreamItem, +>; diff --git a/src/net/server/middleware/xfr/tests.rs b/src/net/server/middleware/xfr/tests.rs new file mode 100644 index 00000000..ec87646a --- /dev/null +++ b/src/net/server/middleware/xfr/tests.rs @@ -0,0 +1,824 @@ +use core::future::{ready, Future, Ready}; +use core::ops::ControlFlow; +use core::pin::Pin; +use core::str::FromStr; +use core::sync::atomic::{AtomicBool, Ordering}; + +use std::borrow::ToOwned; +use std::boxed::Box; +use std::fmt::Debug; +use std::sync::Arc; +use std::vec::Vec; + +use bytes::Bytes; +use futures_util::stream::Once; +use futures_util::{Stream, StreamExt}; +use octseq::Octets; +use tokio::sync::Semaphore; +use tokio::time::Instant; + +use crate::base::iana::{Class, OptRcode, Rcode}; +use crate::base::{ + Message, MessageBuilder, Name, ParsedName, Rtype, Serial, ToName, Ttl, +}; +use crate::net::server::message::{ + NonUdpTransportContext, Request, TransportSpecificContext, + UdpTransportContext, +}; +use crate::net::server::middleware::xfr::data_provider::{ + XfrData, XfrDataProvider, XfrDataProviderError, +}; +use crate::net::server::service::{ + CallResult, Service, ServiceError, ServiceFeedback, ServiceResult, +}; +use crate::rdata::{ + Aaaa, AllRecordData, Cname, Mx, Ns, Soa, Txt, ZoneRecordData, A, +}; +use crate::tsig::{Algorithm, Key, KeyName}; +use crate::zonefile::inplace::Zonefile; +use crate::zonetree::types::{EmptyZoneDiff, Rrset}; +use crate::zonetree::{ + AnswerContent, InMemoryZoneDiff, InMemoryZoneDiffBuilder, SharedRrset, + Zone, +}; + +use super::service::{XfrMiddlewareStream, XfrMiddlewareSvc}; +use super::util::read_soa; + +//------------ ExpectedRecords ------------------------------------------------ + +type ExpectedRecords = Vec<(Name, AllRecordData>)>; + +//------------ Tests ---------------------------------------------------------- + +#[tokio::test] +async fn axfr_with_example_zone() { + let zone = load_zone(include_bytes!( + "../../../../../test-data/zonefiles/nsd-example.txt" + )); + + let req = mk_axfr_request(zone.apex_name(), ()); + + let res = do_preprocess(zone.clone(), &req).await.unwrap(); + + let ControlFlow::Break(mut stream) = res else { + panic!("AXFR failed"); + }; + + let zone_soa = get_zone_soa(&zone).await; + + let mut expected_records: ExpectedRecords = vec![ + (n("example.com"), zone_soa.clone().into()), + (n("example.com"), Ns::new(n("example.com")).into()), + (n("example.com"), A::new(p("192.0.2.1")).into()), + (n("example.com"), Aaaa::new(p("2001:db8::3")).into()), + (n("www.example.com"), Cname::new(n("example.com")).into()), + (n("mail.example.com"), Mx::new(10, n("example.com")).into()), + (n("example.com"), zone_soa.into()), + ]; + + let msg = stream.next().await.unwrap().unwrap(); + assert!(matches!( + msg.feedback(), + Some(ServiceFeedback::BeginTransaction) + )); + + let stream = + assert_stream_eq(req.message(), &mut stream, &mut expected_records) + .await; + + let msg = stream.next().await.unwrap().unwrap(); + assert!(matches!( + msg.feedback(), + Some(ServiceFeedback::EndTransaction) + )); +} + +#[tokio::test] +async fn axfr_multi_response() { + let zone = load_zone(include_bytes!( + "../../../../../test-data/zonefiles/big.example.com.txt" + )); + + let req = mk_axfr_request(zone.apex_name(), ()); + + let res = do_preprocess(zone.clone(), &req).await.unwrap(); + + let ControlFlow::Break(mut stream) = res else { + panic!("AXFR failed"); + }; + + let zone_soa = get_zone_soa(&zone).await; + + let mut expected_records: ExpectedRecords = vec![ + (n("example.com"), zone_soa.clone().into()), + (n("example.com"), Ns::new(n("ns1.example.com")).into()), + (n("example.com"), Ns::new(n("ns2.example.com")).into()), + (n("example.com"), Mx::new(10, n("mail.example.com")).into()), + (n("example.com"), A::new(p("192.0.2.1")).into()), + (n("example.com"), Aaaa::new(p("2001:db8:10::1")).into()), + (n("ns1.example.com"), A::new(p("192.0.2.2")).into()), + (n("ns1.example.com"), Aaaa::new(p("2001:db8:10::2")).into()), + (n("ns2.example.com"), A::new(p("192.0.2.3")).into()), + (n("ns2.example.com"), Aaaa::new(p("2001:db8:10::3")).into()), + (n("mail.example.com"), A::new(p("192.0.2.4")).into()), + (n("mail.example.com"), Aaaa::new(p("2001:db8:10::4")).into()), + ]; + + for i in 1..=10000 { + expected_records.push(( + n(&format!("host-{i}.example.com")), + Txt::build_from_slice(b"text").unwrap().into(), + )); + } + + expected_records.push((n("example.com"), zone_soa.into())); + + let msg = stream.next().await.unwrap().unwrap(); + assert!(matches!( + msg.feedback(), + Some(ServiceFeedback::BeginTransaction) + )); + + let stream = + assert_stream_eq(req.message(), &mut stream, &mut expected_records) + .await; + + let msg = stream.next().await.unwrap().unwrap(); + assert!(matches!( + msg.feedback(), + Some(ServiceFeedback::EndTransaction) + )); +} + +#[tokio::test] +async fn axfr_delegation_records() { + // https://datatracker.ietf.org/doc/html/rfc5936#section-3.2 +} + +#[tokio::test] +async fn axfr_glue_records() { + // https://datatracker.ietf.org/doc/html/rfc5936#section-3.3 +} + +#[tokio::test] +async fn axfr_name_compression_not_yet_supported() { + // https://datatracker.ietf.org/doc/html/rfc5936#section-3.4 +} + +#[tokio::test] +async fn axfr_occluded_names() { + // https://datatracker.ietf.org/doc/html/rfc5936#section-3.5 +} + +#[tokio::test] +async fn axfr_not_allowed_over_udp() { + // https://datatracker.ietf.org/doc/html/rfc5936#section-4.2 + let zone = load_zone(include_bytes!( + "../../../../../test-data/zonefiles/nsd-example.txt" + )); + + let req = mk_udp_axfr_request(zone.apex_name(), ()); + + let res = do_preprocess(zone, &req).await.unwrap(); + + let ControlFlow::Break(mut stream) = res else { + panic!("AXFR failed"); + }; + + let msg = stream.next().await.unwrap().unwrap(); + let resp_builder = msg.into_inner().0.unwrap(); + let resp = resp_builder.as_message(); + + assert_eq!(resp.header().rcode(), Rcode::NOTIMP); +} + +#[tokio::test] +async fn ixfr_rfc1995_section7_full_zone_reply() { + // Based on https://datatracker.ietf.org/doc/html/rfc1995#section-7 + + // initial zone content: + // JAIN.AD.JP. IN SOA NS.JAIN.AD.JP. mohta.jain.ad.jp. ( + // 1 600 600 3600000 604800) + // IN NS NS.JAIN.AD.JP. + // NS.JAIN.AD.JP. IN A 133.69.136.1 + // NEZU.JAIN.AD.JP. IN A 133.69.136.5 + + // Final zone content: + let rfc_1995_zone = r#" +JAIN.AD.JP. IN SOA NS.JAIN.AD.JP. mohta.jain.ad.jp. ( + 3 600 600 3600000 604800) + IN NS NS.JAIN.AD.JP. +NS.JAIN.AD.JP. IN A 133.69.136.1 +JAIN-BB.JAIN.AD.JP. IN A 133.69.136.3 +JAIN-BB.JAIN.AD.JP. IN A 192.41.197.2 + "#; + let zone = load_zone(rfc_1995_zone.as_bytes()); + + // Create an object that knows how to provide zone and diff data for + // our zone and no diffs. + let zone_with_diffs = ZoneWithDiffs::new(zone.clone(), vec![]); + + // The following IXFR query + let req = mk_udp_ixfr_request(zone.apex_name(), Serial(1), ()); + + let res = do_preprocess(zone_with_diffs, &req).await.unwrap(); + + let ControlFlow::Break(mut stream) = res else { + panic!("IXFR failed"); + }; + + // could be replied to with the following full zone transfer message: + let zone_soa = get_zone_soa(&zone).await; + + let mut expected_records: ExpectedRecords = vec![ + (n("JAIN.AD.JP."), zone_soa.clone().into()), + (n("JAIN.AD.JP."), Ns::new(n("NS.JAIN.AD.JP.")).into()), + (n("NS.JAIN.AD.JP."), A::new(p("133.69.136.1")).into()), + (n("JAIN-BB.JAIN.AD.JP."), A::new(p("133.69.136.3")).into()), + (n("JAIN-BB.JAIN.AD.JP."), A::new(p("192.41.197.2")).into()), + (n("JAIN.AD.JP."), zone_soa.into()), + ]; + + assert_stream_eq(req.message(), &mut stream, &mut expected_records).await; +} + +#[tokio::test] +async fn ixfr_rfc1995_section7_incremental_reply() { + // Based on https://datatracker.ietf.org/doc/html/rfc1995#section-7 + let mut diffs = Vec::new(); + + // initial zone content: + // JAIN.AD.JP. IN SOA NS.JAIN.AD.JP. mohta.jain.ad.jp. ( + // 1 600 600 3600000 604800) + // IN NS NS.JAIN.AD.JP. + // NS.JAIN.AD.JP. IN A 133.69.136.1 + // NEZU.JAIN.AD.JP. IN A 133.69.136.5 + + // Final zone content: + let rfc_1995_zone = r#" +JAIN.AD.JP. IN SOA NS.JAIN.AD.JP. mohta.jain.ad.jp. ( + 3 600 600 3600000 604800) + IN NS NS.JAIN.AD.JP. +NS.JAIN.AD.JP. IN A 133.69.136.1 +JAIN-BB.JAIN.AD.JP. IN A 133.69.136.3 +JAIN-BB.JAIN.AD.JP. IN A 192.41.197.2 + "#; + let zone = load_zone(rfc_1995_zone.as_bytes()); + + // Diff 1: NEZU.JAIN.AD.JP. is removed and JAIN-BB.JAIN.AD.JP. is added. + let mut diff = InMemoryZoneDiffBuilder::new(); + + // -- Remove the old SOA. + let mut rrset = Rrset::new(Rtype::SOA, Ttl::from_secs(0)); + let soa = Soa::new( + n("NS.JAIN.AD.JP."), + n("mohta.jain.ad.jp."), + Serial(1), + Ttl::from_secs(600), + Ttl::from_secs(600), + Ttl::from_secs(3600000), + Ttl::from_secs(604800), + ); + rrset.push_data(soa.into()); + diff.remove(n("JAIN.AD.JP"), Rtype::SOA, SharedRrset::new(rrset)); + + // -- Remove the A record. + let mut rrset = Rrset::new(Rtype::A, Ttl::from_secs(0)); + rrset.push_data(A::new(p("133.69.136.5")).into()); + diff.remove(n("NEZU.JAIN.AD.JP"), Rtype::A, SharedRrset::new(rrset)); + + // -- Add the new SOA. + let mut rrset = Rrset::new(Rtype::SOA, Ttl::from_secs(0)); + let soa = Soa::new( + n("NS.JAIN.AD.JP."), + n("mohta.jain.ad.jp."), + Serial(2), + Ttl::from_secs(600), + Ttl::from_secs(600), + Ttl::from_secs(3600000), + Ttl::from_secs(604800), + ); + rrset.push_data(soa.into()); + diff.add(n("JAIN.AD.JP"), Rtype::SOA, SharedRrset::new(rrset)); + + // -- Add the new A records. + let mut rrset = Rrset::new(Rtype::A, Ttl::from_secs(0)); + rrset.push_data(A::new(p("133.69.136.4")).into()); + rrset.push_data(A::new(p("192.41.197.2")).into()); + diff.add(n("JAIN-BB.JAIN.AD.JP"), Rtype::A, SharedRrset::new(rrset)); + + diffs.push(diff.build().unwrap()); + + // Diff 2: One of the IP addresses of JAIN-BB.JAIN.AD.JP. is changed. + let mut diff = InMemoryZoneDiffBuilder::new(); + + // -- Remove the old SOA. + let mut rrset = Rrset::new(Rtype::SOA, Ttl::from_secs(0)); + let soa = Soa::new( + n("NS.JAIN.AD.JP."), + n("mohta.jain.ad.jp."), + Serial(2), + Ttl::from_secs(600), + Ttl::from_secs(600), + Ttl::from_secs(3600000), + Ttl::from_secs(604800), + ); + rrset.push_data(soa.into()); + diff.remove(n("JAIN.AD.JP"), Rtype::SOA, SharedRrset::new(rrset)); + + // Remove the outdated IP address. + let mut rrset = Rrset::new(Rtype::A, Ttl::from_secs(0)); + rrset.push_data(A::new(p("133.69.136.4")).into()); + diff.remove(n("JAIN-BB.JAIN.AD.JP"), Rtype::A, SharedRrset::new(rrset)); + + // -- Add the new SOA. + let mut rrset = Rrset::new(Rtype::SOA, Ttl::from_secs(0)); + let soa = Soa::new( + n("NS.JAIN.AD.JP."), + n("mohta.jain.ad.jp."), + Serial(3), + Ttl::from_secs(600), + Ttl::from_secs(600), + Ttl::from_secs(3600000), + Ttl::from_secs(604800), + ); + rrset.push_data(soa.into()); + diff.add(n("JAIN.AD.JP"), Rtype::SOA, SharedRrset::new(rrset)); + + // Add the updated IP address. + let mut rrset = Rrset::new(Rtype::A, Ttl::from_secs(0)); + rrset.push_data(A::new(p("133.69.136.3")).into()); + diff.add(n("JAIN-BB.JAIN.AD.JP"), Rtype::A, SharedRrset::new(rrset)); + + diffs.push(diff.build().unwrap()); + + // Create an object that knows how to provide zone and diff data for + // our zone and diffs. + let zone_with_diffs = ZoneWithDiffs::new(zone.clone(), diffs); + + // The following IXFR query + let req = mk_ixfr_request(zone.apex_name(), Serial(1), ()); + + let res = do_preprocess(zone_with_diffs, &req).await.unwrap(); + + let ControlFlow::Break(mut stream) = res else { + panic!("IXFR failed"); + }; + + let zone_soa = get_zone_soa(&zone).await; + + // could be replied to with the following incremental message: + let mut expected_records: ExpectedRecords = vec![ + (n("JAIN.AD.JP."), zone_soa.clone().into()), + ( + n("JAIN.AD.JP."), + Soa::new( + n("NS.JAIN.AD.JP."), + n("mohta.jain.ad.jp."), + Serial(1), + Ttl::from_secs(600), + Ttl::from_secs(600), + Ttl::from_secs(3600000), + Ttl::from_secs(604800), + ) + .into(), + ), + (n("NEZU.JAIN.AD.JP."), A::new(p("133.69.136.5")).into()), + ( + n("JAIN.AD.JP."), + Soa::new( + n("NS.JAIN.AD.JP."), + n("mohta.jain.ad.jp."), + Serial(2), + Ttl::from_secs(600), + Ttl::from_secs(600), + Ttl::from_secs(3600000), + Ttl::from_secs(604800), + ) + .into(), + ), + (n("JAIN-BB.JAIN.AD.JP."), A::new(p("133.69.136.4")).into()), + (n("JAIN-BB.JAIN.AD.JP."), A::new(p("192.41.197.2")).into()), + ( + n("JAIN.AD.JP."), + Soa::new( + n("NS.JAIN.AD.JP."), + n("mohta.jain.ad.jp."), + Serial(2), + Ttl::from_secs(600), + Ttl::from_secs(600), + Ttl::from_secs(3600000), + Ttl::from_secs(604800), + ) + .into(), + ), + (n("JAIN-BB.JAIN.AD.JP."), A::new(p("133.69.136.4")).into()), + ( + n("JAIN.AD.JP."), + Soa::new( + n("NS.JAIN.AD.JP."), + n("mohta.jain.ad.jp."), + Serial(3), + Ttl::from_secs(600), + Ttl::from_secs(600), + Ttl::from_secs(3600000), + Ttl::from_secs(604800), + ) + .into(), + ), + (n("JAIN-BB.JAIN.AD.JP."), A::new(p("133.69.136.3")).into()), + (n("JAIN.AD.JP."), zone_soa.into()), + ]; + + let msg = stream.next().await.unwrap().unwrap(); + assert!(matches!( + msg.feedback(), + Some(ServiceFeedback::BeginTransaction) + )); + + let stream = + assert_stream_eq(req.message(), &mut stream, &mut expected_records) + .await; + + let msg = stream.next().await.unwrap().unwrap(); + assert!(matches!( + msg.feedback(), + Some(ServiceFeedback::EndTransaction) + )); +} + +#[tokio::test] +async fn ixfr_rfc1995_section7_udp_packet_overflow() { + // Based on https://datatracker.ietf.org/doc/html/rfc1995#section-7 + let zone = load_zone(include_bytes!( + "../../../../../test-data/zonefiles/big.example.com.txt" + )); + + let req = mk_udp_ixfr_request(zone.apex_name(), Serial(0), ()); + + let res = do_preprocess(zone.clone(), &req).await.unwrap(); + + let ControlFlow::Break(mut stream) = res else { + panic!("IXFR failed"); + }; + + let zone_soa = get_zone_soa(&zone).await; + + let mut expected_records: ExpectedRecords = + vec![(n("example.com"), zone_soa.into())]; + + assert_stream_eq(req.message(), &mut stream, &mut expected_records).await; +} + +#[tokio::test] +async fn ixfr_multi_response_tcp() {} + +#[tokio::test] +async fn axfr_with_tsig_key() { + // Define an XfrDataProvider that expects to receive a Request that is + // generic over a type that we specify: Authentication. This is the + // type over which the Request produced by TsigMiddlewareSvc is generic. + // When the XfrMiddlewareSvc receives a Request it + // passes it to the XfrDataProvider which in turn can inspect it. + struct KeyReceivingXfrDataProvider { + key: Arc, + checked: Arc, + } + + impl XfrDataProvider>> for KeyReceivingXfrDataProvider { + type Diff = EmptyZoneDiff; + + #[allow(clippy::type_complexity)] + fn request( + &self, + req: &Request>>, + _diff_from: Option, + ) -> Pin< + Box< + dyn Future< + Output = Result< + XfrData, + XfrDataProviderError, + >, + > + Sync + + Send, + >, + > + where + Octs: Octets + Send + Sync, + { + let key = req.metadata().as_ref().unwrap(); + assert_eq!(key.name(), self.key.name()); + self.checked.store(true, Ordering::SeqCst); + Box::pin(ready(Err(XfrDataProviderError::Refused))) + } + } + + let key_name = KeyName::from_str("some_tsig_key_name").unwrap(); + let secret = crate::utils::base64::decode::>( + "zlCZbVJPIhobIs1gJNQfrsS3xCxxsR9pMUrGwG8OgG8=", + ) + .unwrap(); + let key = Arc::new( + Key::new(Algorithm::Sha256, &secret, key_name, None, None).unwrap(), + ); + + let metadata = Some(key.clone()); + let req = mk_axfr_request(n("example.com"), metadata); + let checked = Arc::new(AtomicBool::new(false)); + let xdp = KeyReceivingXfrDataProvider { + key, + checked: checked.clone(), + }; + + // Invoke XfrMiddlewareSvc with our custom XfrDataProvidedr. + let _ = do_preprocess(xdp, &req).await; + + // Veirfy that our XfrDataProvider was invoked and received the expected + // TSIG key name data. + assert!(checked.load(Ordering::SeqCst)); +} + +//------------ Helper functions ------------------------------------------- + +fn n(name: &str) -> Name { + Name::from_str(name).unwrap() +} + +fn p(txt: &str) -> T +where + ::Err: Debug, +{ + txt.parse().unwrap() +} + +fn load_zone(bytes: &[u8]) -> Zone { + let mut zone_bytes = std::io::BufReader::new(bytes); + let reader = Zonefile::load(&mut zone_bytes).unwrap(); + Zone::try_from(reader).unwrap() +} + +async fn get_zone_soa(zone: &Zone) -> Soa> { + let read = zone.read(); + let zone_soa_answer = + read_soa(&read, zone.apex_name().to_owned()).await.unwrap(); + let AnswerContent::Data(zone_soa_rrset) = + zone_soa_answer.content().clone() + else { + unreachable!() + }; + let first_rr = zone_soa_rrset.first().unwrap(); + let ZoneRecordData::Soa(soa) = first_rr.data() else { + unreachable!() + }; + soa.clone() +} + +fn mk_axfr_request( + qname: impl ToName, + metadata: T, +) -> Request, T> { + mk_axfr_request_for_transport( + qname, + metadata, + TransportSpecificContext::NonUdp(NonUdpTransportContext::new(None)), + ) +} + +fn mk_udp_axfr_request( + qname: impl ToName, + metadata: T, +) -> Request, T> { + mk_axfr_request_for_transport( + qname, + metadata, + TransportSpecificContext::Udp(UdpTransportContext::new(None)), + ) +} + +fn mk_axfr_request_for_transport( + qname: impl ToName, + metadata: T, + transport_specific: TransportSpecificContext, +) -> Request, T> { + let client_addr = "127.0.0.1:12345".parse().unwrap(); + let received_at = Instant::now(); + let msg = MessageBuilder::new_vec(); + let mut msg = msg.question(); + msg.push((qname, Rtype::AXFR)).unwrap(); + let msg = msg.into_message(); + + Request::new(client_addr, received_at, msg, transport_specific, metadata) +} + +fn mk_ixfr_request( + qname: impl ToName + Clone, + serial: Serial, + metadata: T, +) -> Request, T> { + mk_ixfr_request_for_transport( + qname, + serial, + metadata, + TransportSpecificContext::NonUdp(NonUdpTransportContext::new(None)), + ) +} + +fn mk_udp_ixfr_request( + qname: impl ToName + Clone, + serial: Serial, + metadata: T, +) -> Request, T> { + mk_ixfr_request_for_transport( + qname, + serial, + metadata, + TransportSpecificContext::Udp(UdpTransportContext::new(None)), + ) +} + +fn mk_ixfr_request_for_transport( + qname: impl ToName + Clone, + serial: Serial, + metadata: T, + transport_specific: TransportSpecificContext, +) -> Request, T> { + let client_addr = "127.0.0.1:12345".parse().unwrap(); + let received_at = Instant::now(); + let msg = MessageBuilder::new_vec(); + let mut msg = msg.question(); + msg.push((qname.clone(), Rtype::IXFR)).unwrap(); + + let mut msg = msg.authority(); + let ttl = Ttl::from_secs(0); + let soa = Soa::new(n("name"), n("rname"), serial, ttl, ttl, ttl, ttl); + msg.push((qname, Class::IN, Ttl::from_secs(0), soa)) + .unwrap(); + let msg = msg.into_message(); + + Request::new(client_addr, received_at, msg, transport_specific, metadata) +} + +async fn do_preprocess>( + zone: XDP, + req: &Request, RequestMeta>, +) -> Result< + ControlFlow< + XfrMiddlewareStream< + ::Future, + ::Stream, + <::Stream as Stream>::Item, + >, + >, + OptRcode, +> +where + XDP::Diff: Debug + 'static, +{ + XfrMiddlewareSvc::, TestNextSvc, RequestMeta, XDP>::preprocess( + Arc::new(Semaphore::new(1)), + Arc::new(Semaphore::new(1)), + req, + zone, + ) + .await +} + +async fn assert_stream_eq< + O: octseq::Octets, + S: Stream>, ServiceError>> + Unpin, +>( + req: &Message, + mut stream: S, + expected_records: &mut ExpectedRecords, +) -> S { + while !expected_records.is_empty() { + let msg = stream.next().await.unwrap().unwrap(); + + let resp_builder = msg.into_inner().0.unwrap(); + let resp = resp_builder.as_message(); + assert!(resp.is_answer(req)); + let mut records = resp.answer().unwrap().peekable(); + + for (idx, rec) in records.by_ref().enumerate() { + let rec = rec.unwrap(); + + let rec = rec + .into_record::>>() + .unwrap() + .unwrap(); + + eprintln!( + "XFR record {idx} {} {} {} {}", + rec.owner(), + rec.class(), + rec.rtype(), + rec.data(), + ); + + let pos = expected_records + .iter() + .position(|(name, data)| { + name == rec.owner() && data == rec.data() + }) + .unwrap_or_else(|| { + panic!( + "XFR record {idx} {} {} {} {} was not expected", + rec.owner(), + rec.class(), + rec.rtype(), + rec.data(), + ) + }); + + let _ = expected_records.remove(pos); + + eprintln!("Found {} {} {}", rec.owner(), rec.class(), rec.rtype()) + } + + assert!(records.next().is_none()); + } + + stream +} + +#[derive(Clone)] +struct TestNextSvc; + +impl Service, ()> for TestNextSvc { + type Target = Vec; + type Stream = Once>>; + type Future = Ready; + + fn call(&self, _request: Request, ()>) -> Self::Future { + todo!() + } +} + +struct ZoneWithDiffs { + zone: Zone, + diffs: Vec>, +} + +impl ZoneWithDiffs { + fn new(zone: Zone, diffs: Vec) -> Self { + Self { + zone, + diffs: diffs.into_iter().map(Arc::new).collect(), + } + } + + fn get_diffs( + &self, + diff_from: Option, + ) -> Vec> { + if self.diffs.first().map(|diff| diff.start_serial) == diff_from { + self.diffs.clone() + } else { + vec![] + } + } +} + +impl XfrDataProvider for ZoneWithDiffs { + type Diff = Arc; + fn request( + &self, + req: &Request, + diff_from: Option, + ) -> Pin< + Box< + dyn Future< + Output = Result< + XfrData, + XfrDataProviderError, + >, + > + Sync + + Send, + >, + > + where + Octs: Octets + Send + Sync, + { + let res = req + .message() + .sole_question() + .map_err(XfrDataProviderError::ParseError) + .and_then(|q| { + if q.qname() == self.zone.apex_name() + && q.qclass() == self.zone.class() + { + Ok(XfrData::new( + self.zone.clone(), + self.get_diffs(diff_from), + false, + )) + } else { + Err(XfrDataProviderError::UnknownZone) + } + }); + + Box::pin(ready(res)) + } +} diff --git a/src/net/server/middleware/xfr/util.rs b/src/net/server/middleware/xfr/util.rs new file mode 100644 index 00000000..f404bcbb --- /dev/null +++ b/src/net/server/middleware/xfr/util.rs @@ -0,0 +1,39 @@ +use std::boxed::Box; + +use bytes::Bytes; +use tokio::sync::mpsc::UnboundedSender; +use tracing::error; + +use crate::base::{Name, Rtype}; +use crate::net::server::service::{CallResult, ServiceResult}; +use crate::zonetree::error::OutOfZone; +use crate::zonetree::{Answer, ReadableZone}; + +//------------ read_soa() ----------------------------------------------------- + +#[allow(clippy::borrowed_box)] +pub async fn read_soa( + read: &Box, + qname: Name, +) -> Result { + match read.is_async() { + true => read.query_async(qname, Rtype::SOA).await, + false => read.query(qname, Rtype::SOA), + } +} + +//------------ add_to_stream() ------------------------------------------------ + +pub fn add_to_stream>>( + call_result: T, + response_tx: &UnboundedSender>, +) { + if response_tx.send(Ok(call_result.into())).is_err() { + // We failed to write the message into the response stream. This + // shouldn't happen. We can't now return an error to the client + // because that would require writing to the response stream as well. + // We don't want to panic and take down the entire application, so + // instead just log. + error!("Failed to send DNS message to the internal response stream"); + } +} diff --git a/src/net/server/mod.rs b/src/net/server/mod.rs index 7a250810..3842e3f6 100644 --- a/src/net/server/mod.rs +++ b/src/net/server/mod.rs @@ -105,6 +105,50 @@ //! [`Service`] trait yourself and passing an instance of that service to the //! server or middleware service as input. //! +//! ## Zone maintenance and zone transfers +//! +//! This crate provides everything you need to do zone maintenance, i.e. +//! serving entire zones to clients and keeping your own zones synchronized +//! with those of a primary server. +//! +//! If acting as a primary nameserver: +//! - Use [`XfrMiddlewareSvc`] to respond to AXFR and IXFR requests from +//! secondary nameservers. +//! - Implement [`XfrDataProvider`] to define your XFR access policy. +//! - Create [`ZoneDiff`]s when making changes to [`Zone`] content and make +//! those diffs available via your [`XfrDataProvider`] implementation. +//! - Use [`TsigMiddlewareSvc`] to authenticate transfer requests from +//! secondary nameservers. +//! - Use the UDP client support in `net::client` to send out NOTIFY messages +//! on zone change. +//! +//! If acting as a secondary nameserver: +//! - Use [`NotifyMiddlewareSvc`] to detect changes at the primary to zones +//! that you are mirroring. +//! - Use the TCP client support in [`net::client`] to make outbound XFR +//! requests on SOA timer expiration or NOTIFY to fetch changes to zone +//! content. +//! - Use [`net::client::tsig`] to authenticate your transfer requests to +//! primary nameservers. +//! - Use [`XfrResponseInterpreter`] and [`ZoneUpdater`] to parse transfer +//! responses and apply the changes to your zones. +//! +//! Additionally you may wish to use [`ZoneTree`] to simplify serving multiple +//! zones. +//! +//! [`net::client`]: crate::net::client +//! [`net::client::tsig`]: crate::net::client::tsig +//! [`NotifyMiddlewareSvc`]: middleware::notify::NotifyMiddlewareSvc +//! [`TsigMiddlewareSvc`]: middleware::tsig::TsigMiddlewareSvc +//! [`XfrMiddlewareSvc`]: middleware::xfr::XfrMiddlewareSvc +//! [`XfrDataProvider`]: middleware::xfr::XfrDataProvider +//! [`XfrResponseInterpreter`]: +//! crate::net::xfr::protocol::XfrResponseInterpreter +//! [`Zone`]: crate::zonetree::Zone +//! [`ZoneDiff`]: crate::zonetree::ZoneDiff +//! [`ZoneTree`]: crate::zonetree::ZoneTree +//! [`ZoneUpdater`]: crate::zonetree::update::ZoneUpdater +//! //! # Advanced //! //! ## Memory allocation diff --git a/src/net/server/service.rs b/src/net/server/service.rs index 4c799324..13e177bd 100644 --- a/src/net/server/service.rs +++ b/src/net/server/service.rs @@ -374,3 +374,21 @@ impl CallResult { (response, feedback) } } + +//--- From + +impl From>> + for CallResult +{ + fn from(response: AdditionalBuilder>) -> Self { + Self::new(response) + } +} + +//--- From + +impl From for CallResult { + fn from(feedback: ServiceFeedback) -> Self { + Self::feedback_only(feedback) + } +} diff --git a/src/net/server/tests/integration.rs b/src/net/server/tests/integration.rs index 33d447df..6d63adcc 100644 --- a/src/net/server/tests/integration.rs +++ b/src/net/server/tests/integration.rs @@ -1,3 +1,6 @@ +use core::future::{ready, Future}; +use core::ops::Deref; +use core::pin::Pin; use core::str::FromStr; use std::boxed::Box; @@ -6,6 +9,7 @@ use std::fs::File; use std::net::SocketAddr; use std::path::PathBuf; use std::result::Result; +use std::string::{String, ToString}; use std::sync::Arc; use std::time::Duration; use std::vec::Vec; @@ -16,9 +20,10 @@ use tracing::instrument; use tracing::{trace, warn}; use crate::base::iana::{Class, Rcode}; -use crate::base::name::{Name, ToName}; +use crate::base::name::ToName; use crate::base::net::IpAddr; use crate::base::wire::Composer; +use crate::base::Name; use crate::base::Rtype; use crate::net::client::request::{RequestMessage, RequestMessageMulti}; use crate::net::client::{dgram, stream, tsig}; @@ -32,6 +37,8 @@ use crate::net::server::middleware::mandatory::MandatoryMiddlewareSvc; use crate::net::server::middleware::notify::{ Notifiable, NotifyError, NotifyMiddlewareSvc, }; +use crate::net::server::middleware::tsig::TsigMiddlewareSvc; +use crate::net::server::middleware::xfr::XfrMiddlewareSvc; use crate::net::server::service::{CallResult, Service, ServiceResult}; use crate::net::server::stream::StreamServer; use crate::net::server::util::{mk_builder_for_target, service_fn}; @@ -44,11 +51,9 @@ use crate::stelline::parse_stelline::{self, parse_file, Config, Matches}; use crate::stelline::simple_dgram_client; use crate::tsig::{Algorithm, Key, KeyName, KeyStore}; use crate::utils::base16; -use crate::zonefile::inplace::{Entry, ScannedRecord, Zonefile}; -use crate::zonetree::StoredName; -use core::future::{ready, Future}; -use core::pin::Pin; -use std::string::ToString; +use crate::zonefile::inplace::Zonefile; +use crate::zonetree::{Answer, Zone}; +use crate::zonetree::{StoredName, ZoneBuilder, ZoneTree}; //----------- Tests ---------------------------------------------------------- @@ -57,8 +62,6 @@ use std::string::ToString; /// /// Note: Adding or removing .rpl files on disk won't be detected until the /// test is re-compiled. -// #[cfg(feature = "mock-time")] # Needed for the cookies test but that is -// currently disabled by renaming it to .rpl.not. #[instrument(skip_all, fields(rpl = rpl_file.file_name().unwrap().to_str()))] #[rstest] #[tokio::test(start_paused = true)] @@ -70,8 +73,6 @@ async fn server_tests(#[files("test-data/server/*.rpl")] rpl_file: PathBuf) { // Initialize tracing based logging. Override with env var RUST_LOG, e.g. // RUST_LOG=trace. DEBUG level will show the .rpl file name, Stelline step // numbers and types as they are being executed. - - use crate::net::server::middleware::tsig::TsigMiddlewareSvc; tracing_subscriber::fmt() .with_env_filter(tracing_subscriber::EnvFilter::from_default_env()) .with_thread_ids(true) @@ -100,7 +101,34 @@ async fn server_tests(#[files("test-data/server/*.rpl")] rpl_file: PathBuf) { let dgram_server_conn = ClientServerChannel::new_dgram(); let stream_server_conn = ClientServerChannel::new_stream(); - let zonefile = server_config.zonefile.clone(); + // Build the test defined zone, if any. + let mut zones = ZoneTree::new(); + let zone = match &server_config.zone { + ServerZone { + zone_file: Some(zone_file), + .. + } => { + // This is a primary zone with content already defined. + Some(Zone::try_from(zone_file.clone()).unwrap()) + } + ServerZone { + zone_name: Some(zone_name), + zone_file: None, + } => { + // This is a secondary zone with content to be received via + // XFR. + let builder = ZoneBuilder::new( + Name::from_str(zone_name).unwrap(), + Class::IN, + ); + Some(builder.build()) + } + _ => None, + }; + if let Some(zone) = zone { + zones.insert_zone(zone).unwrap(); + } + let zones = Arc::new(zones); let with_cookies = server_config.cookies.enabled && server_config.cookies.secret.is_some(); @@ -120,7 +148,7 @@ async fn server_tests(#[files("test-data/server/*.rpl")] rpl_file: PathBuf) { // it and without it having to know or do anything about it. // 1. Application logic service - let svc = service_fn(test_service, zonefile); + let svc = service_fn(test_service, zones.clone()); // 2. DNS COOKIES middleware service let svc = CookiesMiddlewareSvc::new(svc, secret) @@ -131,13 +159,18 @@ async fn server_tests(#[files("test-data/server/*.rpl")] rpl_file: PathBuf) { let svc = EdnsMiddlewareSvc::new(svc).enable(server_config.edns_tcp_keepalive); - // 4. Mandatory DNS behaviour (e.g. RFC 1034/35 rules). - let svc = MandatoryMiddlewareSvc::new(svc); + // 4. RFC 5936 AXFR and RFC 1995 IXFR middleware service. + let svc = XfrMiddlewareSvc::, _, Option>, _>::new( + svc, zones, 1, + ); // 5. RFC 1996 NOTIFY support. let svc = NotifyMiddlewareSvc::new(svc, TestNotifyTarget); - // 6. TSIG message authentication. + // 6. Mandatory DNS behaviour (e.g. RFC 1034/35 rules). + let svc = MandatoryMiddlewareSvc::new(svc); + + // 7. TSIG message authentication. let svc = TsigMiddlewareSvc::new(svc, key_store.clone()); // NOTE: TSIG middleware *MUST* be the first middleware in the chain per @@ -145,7 +178,8 @@ async fn server_tests(#[files("test-data/server/*.rpl")] rpl_file: PathBuf) { // in order to verify the signature, and has to sign outgoing messages in // their final state without any modification occuring thereafter. - // Create dgram and stream servers for answering requests + // 8. The dgram and stream servers that receive DNS queries and dispatch + // them to the service layers above. let (dgram_srv, stream_srv) = mk_servers( svc, &server_config, @@ -153,7 +187,11 @@ async fn server_tests(#[files("test-data/server/*.rpl")] rpl_file: PathBuf) { stream_server_conn.clone(), ); - // Create a client factory for sending requests + // Create a client factory for creating DNS clients per Stelline STEP with + // the appropriate configuration (as defined by the .rpl content) to + // submit requests to our DNS servers. No actual network communication + // takes place, these clients and servers use a direct in-memory channel + // to exchange messages instead of actual network sockets. let client_factory = mk_client_factory(dgram_server_conn, stream_server_conn, key_store); @@ -369,70 +407,40 @@ fn mk_server_configs( #[allow(clippy::type_complexity)] fn test_service( request: Request, RequestMeta>, - zonefile: Zonefile, + zones: Arc, ) -> ServiceResult> { - fn as_record_and_dname( - r: ScannedRecord, - ) -> Option<(ScannedRecord, Name>)> { - let dname = r.owner().to_name(); - Some((r, dname)) - } + let question = request.message().sole_question().unwrap(); - fn as_records( - e: Result, - ) -> Option { - match e { - Ok(Entry::Record(r)) => Some(r), - Ok(_) => None, - Err(err) => panic!( - "Error while extracting records from the zonefile: {err}" - ), + let answer = match zones.find_zone(question.qname(), question.qclass()) { + Some(zone) => { + let readable_zone = zone.read(); + let qname = question.qname().to_bytes(); + let qtype = question.qtype(); + readable_zone.query(qname, qtype).unwrap() } - } + None => Answer::new(Rcode::NXDOMAIN), + }; - trace!("Service received request"); - trace!("Service is constructing a single response"); - // If given a single question: - let answer = request - .message() - .sole_question() - .ok() - .and_then(|q| { - // Walk the zone to find the queried name - zonefile - .clone() - .filter_map(as_records) - .filter_map(as_record_and_dname) - .find(|(_record, dname)| dname == q.qname()) - }) - .map_or_else( - || { - // The Qname was not found in the zone: - mk_builder_for_target() - .start_answer(request.message(), Rcode::NXDOMAIN) - .unwrap() - }, - |(record, _)| { - // Respond with the found record: - let mut answer = mk_builder_for_target() - .start_answer(request.message(), Rcode::NOERROR) - .unwrap(); - answer.push(record).unwrap(); - answer - }, - ); - - Ok(CallResult::new(answer.additional())) + let builder = mk_builder_for_target(); + let additional = answer.to_message(request.message(), builder); + Ok(CallResult::new(additional)) } //----------- Stelline config block parsing ----------------------------------- +#[derive(Default)] +struct ServerZone { + /// Used for an empty secondary zone. Ignored if zone_file is Some. + zone_name: Option, + zone_file: Option, +} + #[derive(Default)] struct ServerConfig<'a> { cookies: CookieConfig<'a>, edns_tcp_keepalive: bool, idle_timeout: Option, - zonefile: Zonefile, + zone: ServerZone, } #[derive(Default)] @@ -444,8 +452,9 @@ struct CookieConfig<'a> { fn parse_server_config(config: &Config) -> ServerConfig { let mut parsed_config = ServerConfig::default(); - let mut zone_file_bytes = VecDeque::::new(); let mut in_server_block = false; + let mut zone_name = None; + let mut zone_file_bytes = VecDeque::::new(); for line in config.lines() { if line.starts_with("server:") { @@ -498,9 +507,6 @@ fn parse_server_config(config: &Config) -> ServerConfig { } } ("local-data", v) => { - if !zone_file_bytes.is_empty() { - zone_file_bytes.push_back(b'\n'); - } zone_file_bytes .extend(v.trim_matches('"').as_bytes().iter()); zone_file_bytes.push_back(b'\n'); @@ -515,6 +521,10 @@ fn parse_server_config(config: &Config) -> ServerConfig { ); } } + ("zone", v) => { + // zone: + zone_name = Some(v.to_string()); + } _ => { eprintln!("Ignoring unknown server setting '{setting}' with value: {value}"); } @@ -523,10 +533,13 @@ fn parse_server_config(config: &Config) -> ServerConfig { } } - if !zone_file_bytes.is_empty() { - parsed_config.zonefile = - Zonefile::load(&mut zone_file_bytes).unwrap(); - } + let zone_file = (!zone_file_bytes.is_empty()) + .then(|| Zonefile::load(&mut zone_file_bytes).unwrap()); + + parsed_config.zone = ServerZone { + zone_name, + zone_file, + }; parsed_config } @@ -570,10 +583,6 @@ impl KeyStore for Arc { name: &N, algorithm: Algorithm, ) -> Option { - if let Ok(name) = name.try_to_name() { - self.get(&(name, algorithm)).cloned() - } else { - None - } + Arc::deref(self).get_key(name, algorithm) } } diff --git a/src/net/xfr/mod.rs b/src/net/xfr/mod.rs index 22b877ab..ea7a8ae5 100644 --- a/src/net/xfr/mod.rs +++ b/src/net/xfr/mod.rs @@ -4,5 +4,68 @@ )] // #![warn(missing_docs)] // #![warn(clippy::missing_docs_in_private_items)] -//! XFR protocol related functionality. +//! XFR related functionality. +//! +//! # What is XFR? +//! +//! XFR refers to the protocols used to transfer entire zones between +//! nameservers. +//! +//! There are two XFR protocols and a couple of protocols often used in +//! combination with XFR: +//! +//! - AXFR defined by [RFC 5936] "DNS Zone Transfer Protocol (AXFR)" +//! - IXFR defined by [RFC 1995] "Incremental Zone Transfer in DNS" +//! - NOTIFY defined by [RFC 1996] "A Mechanism for Prompt Notification of +//! Zone Changes" +//! - TSIG defined by [RFC 8945] "Secret Key Transaction Authentication for +//! DNS (TSIG)" +//! +//! AXFR is used to transfer a complete zone via one or more DNS responses. +//! +//! IXFR is used to incrementally apply the changes that occur to a zone on +//! one nameserver to the same zone on another server, assuming that the +//! latter server has a reasonably up-to-date copy of the zone. +//! +//! NOTIFY allows the server that holds the primary copy of a zone to notify +//! interested servers that the zone has changed and should be re-fetched. +//! +//! TSIG can be used to sign XFR requests and responses to authenticate the +//! servers involved to each other. +//! +//! # XFR support available in this crate +//! +//! Sending requests & handling responses: +//! - [`net::client::stream`] supports sending of XFR requests and receiving +//! one or more responses via [`RequestMessageMulti`]. +//! - [`net::client::tsig`] can be wrapped around another transport to add +//! TSIG request signing and response validation. +//! - [`net::xfr::protocol::XfrResponseInterpreter`] can be used to parse +//! those XFR responses into [`ZoneUpdate`]s. +//! - [`zonetree::update::ZoneUpdater`] can then be used to apply those +//! updates to a [`Zone`]. +//! +//! Responding to requests: +//! - [`net::server::middleware::xfr::XfrMiddlewareSvc`] can respond to +//! XFR requests with zone transfer responses. +//! - [`net::server::middleware::tsig::TsigMiddlewareSvc`] can validate +//! request signatures and sign transer responses. +//! - [`net::server::middleware::notify::NotifyMiddlewareSvc`] can invoke +//! a user supplied callback when a NOTIFY request is received. +//! +//! [RFC 5936]: https://www.rfc-editor.org/info/rfc5936 +//! [RFC 1995]: https://www.rfc-editor.org/info/rfc1995 +//! [RFC 1996]: https://www.rfc-editor.org/info/rfc1996 +//! [RFC 8945]: https://www.rfc-editor.org/info/rfc8945 +//! [`net::client::stream`]: crate::net::client::stream +//! [`RequestMessageMulti`]: crate::net::client::request::RequestMessageMulti +//! [`net::client::tsig`]: crate::net::client::tsig +//! [`net::xfr::protocol::XfrResponseInterpreter`]: crate::net::xfr::protocol::XfrResponseInterpreter +//! [`ZoneUpdate`]: crate::zonetree::types::ZoneUpdate +//! [`zonetree::update::ZoneUpdater`]: crate::zonetree::update::ZoneUpdater +//! [`Zone`]: crate::zonetree::Zone +//! [`net::server::middleware::xfr::XfrMiddlewareSvc`]: crate::net::server::middleware::xfr::XfrMiddlewareSvc +//! [`net::server::middleware::tsig::TsigMiddlewareSvc`]: crate::net::server::middleware::tsig::TsigMiddlewareSvc +//! [`net::server::middleware::notify::NotifyMiddlewareSvc`]: crate::net::server::middleware::notify::NotifyMiddlewareSvc + pub mod protocol; diff --git a/src/zonetree/answer.rs b/src/zonetree/answer.rs index 46e0dc65..2c31927a 100644 --- a/src/zonetree/answer.rs +++ b/src/zonetree/answer.rs @@ -5,10 +5,10 @@ use octseq::Octets; use crate::base::iana::Rcode; use crate::base::message_builder::AdditionalBuilder; use crate::base::wire::Composer; -use crate::base::Message; use crate::base::MessageBuilder; +use crate::base::{Message, Ttl}; -use super::types::StoredName; +use super::types::{StoredName, StoredRecordData}; use super::{SharedRr, SharedRrset}; //------------ Answer -------------------------------------------------------- @@ -202,6 +202,37 @@ pub enum AnswerContent { NoData, } +impl AnswerContent { + /// Gets the first record TTL and data, if any. + /// + /// This can be used to get both the data as a specific variant, and the + /// associated TTL, in a single step: + /// + /// ```should_panic + /// # use domain::base::iana::Rcode; + /// # use domain::rdata::ZoneRecordData; + /// # use domain::zonetree::Answer; + /// # let some_answer = Answer::new(Rcode::NOERROR); + /// let Some((soa_ttl, ZoneRecordData::Soa(soa))) = + /// some_answer.content().first() + /// else { + /// panic!("some_answer is not a variant of AnswerContent that has data"); + /// }; + /// ``` + pub fn first(&self) -> Option<(Ttl, StoredRecordData)> { + match self { + AnswerContent::Data(shared_rrset) => shared_rrset + .data() + .first() + .map(|data| (shared_rrset.ttl(), data.clone())), + AnswerContent::Cname(shared_rr) => { + Some((shared_rr.ttl(), shared_rr.data().clone())) + } + AnswerContent::NoData => None, + } + } +} + //------------ AnswerAuthority ----------------------------------------------- /// The authority section of a query answer. diff --git a/src/zonetree/in_memory/mod.rs b/src/zonetree/in_memory/mod.rs index 59c36b45..6d7d7537 100644 --- a/src/zonetree/in_memory/mod.rs +++ b/src/zonetree/in_memory/mod.rs @@ -1,6 +1,13 @@ -//! An in-memory backing store for [`Zone`]s. +//! A versioned in-memory backing store for [`Zone`]s. //! -//! [`Zone`]: super::Zone +//! # Limitations +//! +//! * There is currently no support for removing old versions of zone data +//! stored in the tree. The only optionis to [`walk()`] the [`Zone`] cloning +//! the current version into a new [`Zone`] then dropping the old [`Zone`]. +//! +//! [`Zone`]: crate::zonetree::Zone +//! [`walk()`]: crate::zoneree::ReadableZone::walk() mod builder; mod nodes; mod read; diff --git a/src/zonetree/in_memory/read.rs b/src/zonetree/in_memory/read.rs index a38ef2f5..d1828f39 100644 --- a/src/zonetree/in_memory/read.rs +++ b/src/zonetree/in_memory/read.rs @@ -9,6 +9,7 @@ use crate::base::iana::{Rcode, Rtype}; use crate::base::name::Label; use crate::base::Name; use crate::zonetree::answer::{Answer, AnswerAuthority}; +use crate::zonetree::error::OutOfZone; use crate::zonetree::types::ZoneCut; use crate::zonetree::walk::WalkState; use crate::zonetree::{ReadableZone, Rrset, SharedRr, SharedRrset, WalkOp}; @@ -16,7 +17,6 @@ use crate::zonetree::{ReadableZone, Rrset, SharedRr, SharedRrset, WalkOp}; use super::nodes::{NodeChildren, NodeRrsets, Special, ZoneApex, ZoneNode}; use super::versioned::Version; use super::versioned::VersionMarker; -use crate::zonetree::error::OutOfZone; //------------ ReadZone ------------------------------------------------------ @@ -86,19 +86,23 @@ impl ReadZone { ) -> NodeAnswer { node.with_special(self.version, |special| match special { Some(Special::Cut(ref cut)) => { - let answer = NodeAnswer::authority(AnswerAuthority::new( - cut.name.clone(), - None, - Some(cut.ns.clone()), - cut.ds.as_ref().cloned(), - )); - - walk.op(&cut.ns); - if let Some(ds) = &cut.ds { - walk.op(ds); + if walk.enabled() { + walk.op(&cut.ns); + if let Some(ds) = &cut.ds { + walk.op(ds); + } + NodeAnswer::no_data() + } else { + // There is nothing more in this zone, only a cut here. + // Respond with NODATA and an authority section referring the + // client to the nameserver that should know more. + NodeAnswer::authority(AnswerAuthority::new( + cut.name.clone(), + None, + Some(cut.ns.clone()), + cut.ds.as_ref().cloned(), + )) } - - answer } Some(Special::NxDomain) => NodeAnswer::nx_domain(), Some(Special::Cname(cname)) => { @@ -133,25 +137,8 @@ impl ReadZone { walk: WalkState, ) -> NodeAnswer { node.with_special(self.version, |special| match special { - Some(Special::Cut(cut)) => { - let answer = self.query_at_cut(cut, qtype); - if walk.enabled() { - walk.op(&cut.ns); - if let Some(ds) = &cut.ds { - walk.op(ds); - } - } - answer - } - Some(Special::Cname(cname)) => { - let answer = NodeAnswer::cname(cname.clone()); - if walk.enabled() { - let mut rrset = Rrset::new(Rtype::CNAME, cname.ttl()); - rrset.push_data(cname.data().clone()); - walk.op(&rrset); - } - answer - } + Some(Special::Cut(cut)) => self.query_at_cut(cut, qtype), + Some(Special::Cname(cname)) => NodeAnswer::cname(cname.clone()), Some(Special::NxDomain) => NodeAnswer::nx_domain(), None => self.query_rrsets(node.rrsets(), qtype, walk), }) @@ -197,9 +184,9 @@ impl ReadZone { // response." // // We choose for option 1 because option 2 would create lots of - // extra work in the offline signing case (because lots of HFINO + // extra work in the offline signing case (because lots of HINFO // records would need to be synthesized prior to signing) and - // option 3 as stated may still result in a large response. + // option 3, as stated, may still result in a large response. let guard = rrsets.iter(); guard .iter() @@ -299,15 +286,19 @@ impl ReadableZone for ReadZone { } fn walk(&self, op: WalkOp) { - // https://datatracker.ietf.org/doc/html/rfc8482 notes that the ANY - // query type is problematic and should be answered as minimally as - // possible. Rather than use ANY internally here to achieve a walk, as - // specific behaviour may actually be wanted for ANY we instead use - // the presence of a callback `op` to indicate that walking mode is + // The presence of a callback `op` indicates that walking mode is // requested. We still have to pass an Rtype but it won't be used for // matching when in walk mode, so we set it to Any as it most closely // matches our intent and will be ignored anyway. - let walk = WalkState::new(op); + // + // The walk is single threaded. With an empty callback function on a + // "13th Gen Intel(R) Core(TM) i9-13900K" over 43,347,447 resource + // records the walk took ~6 seconds, compared to 47 seconds for the + // callback function to emit the same records as DNS messages and for + // dig to receive the entire zone via AXFR: + // + // dig -4 @127.0.0.1 -p 8053 +noanswer +tries=1 +noidnout AXFR de. + let walk = WalkState::new(op, self.apex.name().clone()); self.query_rrsets(self.apex.rrsets(), Rtype::ANY, walk.clone()); self.query_below_apex(Label::root(), iter::empty(), Rtype::ANY, walk); } diff --git a/src/zonetree/in_memory/write.rs b/src/zonetree/in_memory/write.rs index 22dc9ca1..d99631ca 100644 --- a/src/zonetree/in_memory/write.rs +++ b/src/zonetree/in_memory/write.rs @@ -22,7 +22,9 @@ use crate::base::iana::Rtype; use crate::base::name::Label; use crate::base::{NameBuilder, Serial}; use crate::rdata::ZoneRecordData; -use crate::zonetree::types::{ZoneCut, ZoneDiff, ZoneDiffBuilder}; +use crate::zonetree::types::{ + InMemoryZoneDiff, InMemoryZoneDiffBuilder, ZoneCut, +}; use crate::zonetree::StoredName; use crate::zonetree::{Rrset, SharedRr}; use crate::zonetree::{SharedRrset, WritableZone, WritableZoneNode}; @@ -77,7 +79,7 @@ pub struct WriteZone { /// [`WriteNode::update_child()`] is called it creates a new [`WriteNode`] /// which also needs to be able to add and remove things from the same /// diff collection. - diff: Arc>>>>, + diff: Arc>>>>, /// The zone is dirty if changes have been made but not yet committed. /// @@ -142,7 +144,7 @@ impl WriteZone { fn add_soa_remove_diff_entry( &mut self, old_soa_rr: Option, - diff: &mut ZoneDiffBuilder, + diff: &mut InMemoryZoneDiffBuilder, ) -> Option { if let Some(old_soa_rr) = old_soa_rr { let ZoneRecordData::Soa(old_soa) = old_soa_rr.data() else { @@ -172,7 +174,7 @@ impl WriteZone { fn add_soa_add_diff_entry( &mut self, new_soa_rr: Option, - diff: &mut ZoneDiffBuilder, + diff: &mut InMemoryZoneDiffBuilder, ) -> Option { if let Some(new_soa_rr) = new_soa_rr { let ZoneRecordData::Soa(new_soa) = new_soa_rr.data() else { @@ -287,7 +289,7 @@ impl WritableZone for WriteZone { bump_soa_serial: bool, ) -> Pin< Box< - dyn Future, io::Error>> + dyn Future, io::Error>> + Send + Sync, >, @@ -303,10 +305,14 @@ impl WritableZone for WriteZone { // that case be a SOA record in the new version of the zone anyway. let old_soa_rr = self.apex.get_soa(self.last_published_version()); - let new_soa_rr = self.apex.get_soa(self.new_version); + let mut new_soa_rr = self.apex.get_soa(self.new_version); - if bump_soa_serial && old_soa_rr.is_some() && new_soa_rr.is_none() { + if bump_soa_serial + && old_soa_rr.is_some() + && (new_soa_rr.is_none() || new_soa_rr == old_soa_rr) + { self.bump_soa_serial(&old_soa_rr); + new_soa_rr = self.apex.get_soa(self.new_version); } // Extract (and finish) the created diff, if any. @@ -385,7 +391,7 @@ pub struct WriteNode { node: Either, Arc>, /// The diff we are building, if enabled. - diff: Option<(StoredName, Arc>)>, + diff: Option<(StoredName, Arc>)>, } impl WriteNode { @@ -398,7 +404,7 @@ impl WriteNode { let diff = if create_diff { Some(( zone.apex.name().clone(), - Arc::new(Mutex::new(ZoneDiffBuilder::new())), + Arc::new(Mutex::new(InMemoryZoneDiffBuilder::new())), )) } else { None @@ -677,7 +683,7 @@ impl WriteNode { Ok(()) } - fn diff(&self) -> Option>> { + fn diff(&self) -> Option>> { self.diff .as_ref() .map(|(_, diff_builder)| diff_builder.clone()) diff --git a/src/zonetree/mod.rs b/src/zonetree/mod.rs index 035e2874..5556f234 100644 --- a/src/zonetree/mod.rs +++ b/src/zonetree/mod.rs @@ -115,11 +115,13 @@ mod zone; pub use self::answer::{Answer, AnswerAuthority, AnswerContent}; pub use self::in_memory::ZoneBuilder; pub use self::traits::{ - ReadableZone, WritableZone, WritableZoneNode, ZoneStore, + ReadableZone, WritableZone, WritableZoneNode, ZoneDiff, ZoneDiffItem, + ZoneStore, }; pub use self::tree::{ZoneSetIter, ZoneTree}; pub use self::types::{ - Rrset, SharedRr, SharedRrset, StoredName, StoredRecord, ZoneDiff, + InMemoryZoneDiff, InMemoryZoneDiffBuilder, Rrset, SharedRr, SharedRrset, + StoredName, StoredRecord, }; pub use self::walk::WalkOp; pub use self::zone::Zone; diff --git a/src/zonetree/parsed.rs b/src/zonetree/parsed.rs index 765725b6..dc4cab13 100644 --- a/src/zonetree/parsed.rs +++ b/src/zonetree/parsed.rs @@ -3,7 +3,6 @@ use std::collections::{BTreeMap, HashMap}; use std::vec::Vec; -use super::error::{ContextError, RecordError, ZoneErrors}; use crate::base::iana::{Class, Rtype}; use crate::base::name::{FlattenInto, ToName}; use crate::base::Name; @@ -12,6 +11,7 @@ use crate::zonefile::inplace::{self, Entry}; use crate::zonetree::ZoneBuilder; use crate::zonetree::{Rrset, SharedRr}; +use super::error::{ContextError, RecordError, ZoneErrors}; use super::types::{StoredName, StoredRecord}; //------------ Zonefile ------------------------------------------------------ diff --git a/src/zonetree/traits.rs b/src/zonetree/traits.rs index 37904047..a7e26b87 100644 --- a/src/zonetree/traits.rs +++ b/src/zonetree/traits.rs @@ -9,6 +9,7 @@ //! [`ZoneTree`]: super::ZoneTree use core::any::Any; use core::future::ready; +use core::ops::Deref; use core::pin::Pin; use std::boxed::Box; @@ -18,14 +19,15 @@ use std::io; use std::sync::Arc; use bytes::Bytes; +use futures_util::Stream; use crate::base::iana::Class; use crate::base::name::Label; -use crate::base::{Name, Rtype}; +use crate::base::{Name, Rtype, Serial, ToName}; use super::answer::Answer; use super::error::OutOfZone; -use super::types::{ZoneCut, ZoneDiff}; +use super::types::{InMemoryZoneDiff, ZoneCut}; use super::{SharedRr, SharedRrset, StoredName, WalkOp}; //------------ ZoneStore ----------------------------------------------------- @@ -128,6 +130,16 @@ pub trait ReadableZone: Send + Sync { /// [`Zone`]: super::Zone pub trait WritableZone: Send + Sync { /// Start a write operation for the zone. + /// + /// If `create_diff` is true the zone backing store is requested to create + /// an [`InMemoryZoneDiff`] which will accumulate entries as changes are + /// made to the zone and will be returned finally when [`commit()`] is + /// invoked. + /// + /// Creating a diff is optional. If the backing store doesn't support + /// diff creation [`commit()`] will return `None`. + /// + /// [`commit()`]: Self::commit #[allow(clippy::type_complexity)] fn open( &self, @@ -149,13 +161,18 @@ pub trait WritableZone: Send + Sync { /// _after_ invoking this function will be able to see the changes made /// since [`open()`] was called. /// + /// If `create_diff` was set to `true` when [`open()`] was invoked then + /// this function _may_ return `Some` if a diff was created. `None` may be + /// returned if the zone backing store does not support creation of diffs + /// or was unable to create a diff for some reason. + /// /// [`open()`]: Self::open fn commit( &mut self, bump_soa_serial: bool, ) -> Pin< Box< - dyn Future, io::Error>> + dyn Future, io::Error>> + Send + Sync, >, @@ -229,8 +246,131 @@ pub trait WritableZoneNode: Send + Sync { cname: SharedRr, ) -> Pin> + Send + Sync>>; - /// Recursively make all content at and below this point appear to be removed. + /// Recursively make all content at and below this point appear to be + /// removed. fn remove_all( &self, ) -> Pin> + Send + Sync>>; } + +//------------ ZoneDiffItem --------------------------------------------------- + +/// One difference item in a set of changes made to a zone. +/// +/// Conceptually a diff is like a set of keys and values, representing a change +/// to a resource record set with key (owner name, resource type) and the value +/// being the changed resource records at that owner and with that type. +pub trait ZoneDiffItem { + /// The owner name and resource record type. + fn key(&self) -> &(StoredName, Rtype); + + /// The changed records. + /// + /// Each record has the same key (owner name and resource record type). + fn value(&self) -> &SharedRrset; +} + +//------------ ZoneDiff ------------------------------------------------------- + +/// A set of differences between two versions (SOA serial numbers) of a zone. +/// +/// Often referred to simply as a "diff". +/// +/// The default implementation of this trait supplied by the domain crate is +/// the [`InMemoryZoneDiff`]. As the name implies it stores its data in +/// memory. +/// +/// In order however to support less local backing stores for diff data, such +/// as on-disk storage or in a database possibly reached via a network, +/// asynchronous access to the diff is supported via use of [`Future`]s and +/// [`Stream`]s. +pub trait ZoneDiff { + /// A single item in the diff. + type Item<'a>: ZoneDiffItem + Send + where + Self: 'a; + + /// The type of [`Stream`] used to access the diff records. + type Stream<'a>: Stream> + Send + where + Self: 'a; + + /// The serial number of the zone which was modified. + fn start_serial( + &self, + ) -> Pin + Send + '_>>; + + /// The serial number of the zone that resulted from the modifications. + fn end_serial(&self) + -> Pin + Send + '_>>; + + /// An stream of RRsets that were added to the zone. + // TODO: Does this need to be Box>>? + fn added(&self) -> Self::Stream<'_>; + + /// An stream of RRsets that were removed from the zone. + // TODO: Does this need to be Box>>? + fn removed(&self) -> Self::Stream<'_>; + + /// Get an RRset that was added to the zone, if present in the diff. + fn get_added( + &self, + name: impl ToName, + rtype: Rtype, + ) -> Pin> + Send + '_>>; + + /// Get an RRset that was removed from the zone, if present in the diff. + fn get_removed( + &self, + name: impl ToName, + rtype: Rtype, + ) -> Pin> + Send + '_>>; +} + +//--- impl ZoneDiff for Arc + +impl ZoneDiff for Arc { + type Item<'a> = T::Item<'a> + where + Self: 'a; + + type Stream<'a> = T::Stream<'a> + where + Self: 'a; + + fn start_serial( + &self, + ) -> Pin + Send + '_>> { + Arc::deref(self).start_serial() + } + + fn end_serial( + &self, + ) -> Pin + Send + '_>> { + Arc::deref(self).end_serial() + } + + fn added(&self) -> Self::Stream<'_> { + Arc::deref(self).added() + } + + fn removed(&self) -> Self::Stream<'_> { + Arc::deref(self).removed() + } + + fn get_added( + &self, + name: impl ToName, + rtype: Rtype, + ) -> Pin> + Send + '_>> { + Arc::deref(self).get_added(name, rtype) + } + + fn get_removed( + &self, + name: impl ToName, + rtype: Rtype, + ) -> Pin> + Send + '_>> { + Arc::deref(self).get_removed(name, rtype) + } +} diff --git a/src/zonetree/types.rs b/src/zonetree/types.rs index 88e413e8..777ab5eb 100644 --- a/src/zonetree/types.rs +++ b/src/zonetree/types.rs @@ -1,19 +1,26 @@ //! Zone tree related types. -use std::collections::HashMap; +use core::future::{ready, Future}; +use core::pin::Pin; +use core::task::{Context, Poll}; + +use std::boxed::Box; +use std::collections::{hash_map, HashMap}; use std::ops; use std::sync::Arc; use std::vec::Vec; use bytes::Bytes; +use futures_util::stream; use serde::{Deserialize, Serialize}; use tracing::trace; +use super::traits::{ZoneDiff, ZoneDiffItem}; use crate::base::name::Name; use crate::base::rdata::RecordData; use crate::base::record::Record; -use crate::base::Serial; use crate::base::{iana::Rtype, Ttl}; +use crate::base::{Serial, ToName}; use crate::rdata::ZoneRecordData; //------------ Type Aliases -------------------------------------------------- @@ -252,13 +259,13 @@ pub struct ZoneCut { pub glue: Vec, } -//------------ ZoneDiffBuilder ----------------------------------------------- +//------------ InMemoryZoneDiffBuilder ---------------------------------------- -/// A [`ZoneDiff`] builder. +/// An [`InMemoryZoneDiff`] builder. /// /// Removes are assumed to occur before adds. #[derive(Debug, Default)] -pub struct ZoneDiffBuilder { +pub struct InMemoryZoneDiffBuilder { /// The records added to the Zone. added: HashMap<(StoredName, Rtype), SharedRrset>, @@ -266,7 +273,7 @@ pub struct ZoneDiffBuilder { removed: HashMap<(StoredName, Rtype), SharedRrset>, } -impl ZoneDiffBuilder { +impl InMemoryZoneDiffBuilder { /// Creates a new instance of the builder. pub fn new() -> Self { Default::default() @@ -301,22 +308,22 @@ impl ZoneDiffBuilder { /// Note: No check is currently done that the start and end serials match /// the SOA records in the removed and added records contained within the /// diff. - pub fn build(self) -> Result { - ZoneDiff::new(self.added, self.removed) + pub fn build(self) -> Result { + InMemoryZoneDiff::new(self.added, self.removed) } } -//------------ ZoneDiff ------------------------------------------------------ +//------------ InMemoryZoneDiff ----------------------------------------------- /// The differences between one serial and another for a DNS zone. /// /// Removes are assumed to occur before adds. #[derive(Clone, Debug)] -pub struct ZoneDiff { +pub struct InMemoryZoneDiff { /// The serial number of the zone which was modified. pub start_serial: Serial, - /// The serial number of the Zzone that resulted from the modifications. + /// The serial number of the zone that resulted from the modifications. pub end_serial: Serial, /// The RRsets added to the zone. @@ -326,7 +333,7 @@ pub struct ZoneDiff { pub removed: Arc>, } -impl ZoneDiff { +impl InMemoryZoneDiff { /// Creates a new immutable zone diff. /// /// Returns `Err(ZoneDiffError::MissingStartSoa)` If the removed records @@ -388,6 +395,145 @@ impl ZoneDiff { } } +//--- impl ZoneDiff + +impl<'a> ZoneDiffItem for (&'a (StoredName, Rtype), &'a SharedRrset) { + fn key(&self) -> &(StoredName, Rtype) { + self.0 + } + + fn value(&self) -> &SharedRrset { + self.1 + } +} + +impl ZoneDiff for InMemoryZoneDiff { + type Item<'a> = (&'a (StoredName, Rtype), &'a SharedRrset) + where + Self: 'a; + + type Stream<'a> = futures_util::stream::Iter> + where + Self: 'a; + + fn start_serial( + &self, + ) -> Pin + Send + '_>> { + Box::pin(ready(self.start_serial)) + } + + fn end_serial( + &self, + ) -> Pin + Send + '_>> { + Box::pin(ready(self.end_serial)) + } + + fn added(&self) -> Self::Stream<'_> { + stream::iter(self.added.iter()) + } + + fn removed(&self) -> Self::Stream<'_> { + stream::iter(self.removed.iter()) + } + + fn get_added( + &self, + name: impl ToName, + rtype: Rtype, + ) -> Pin> + Send + '_>> { + Box::pin(ready(self.added.get(&(name.to_name(), rtype)))) + } + + fn get_removed( + &self, + name: impl ToName, + rtype: Rtype, + ) -> Pin> + Send + '_>> { + Box::pin(ready(self.removed.get(&(name.to_name(), rtype)))) + } +} + +/// The item type used by [`EmptyZoneDiff`]. +pub struct EmptyZoneDiffItem; + +impl ZoneDiffItem for EmptyZoneDiffItem { + fn key(&self) -> &(StoredName, Rtype) { + unreachable!() + } + + fn value(&self) -> &SharedRrset { + unreachable!() + } +} + +/// The stream type used by [`EmptyZoneDiff`]. +#[derive(Debug)] +pub struct EmptyZoneDiffStream; + +impl futures_util::stream::Stream for EmptyZoneDiffStream { + type Item = EmptyZoneDiffItem; + + fn poll_next( + self: Pin<&mut Self>, + _cx: &mut Context<'_>, + ) -> Poll> { + Poll::Ready(None) + } +} + +/// A [`ZoneDiff`] implementation that is always empty. +/// +/// Useful when a [`ZoneDiff`] type is needed in a type declaration but for use +/// by a type that does not support zone difference data. +#[derive(Debug)] +pub struct EmptyZoneDiff; + +impl ZoneDiff for EmptyZoneDiff { + type Item<'a> = EmptyZoneDiffItem + where + Self: 'a; + + type Stream<'a> = EmptyZoneDiffStream + where + Self: 'a; + + fn start_serial( + &self, + ) -> Pin + Send + '_>> { + Box::pin(ready(Serial(0))) + } + + fn end_serial( + &self, + ) -> Pin + Send + '_>> { + Box::pin(ready(Serial(0))) + } + + fn added(&self) -> Self::Stream<'_> { + EmptyZoneDiffStream + } + + fn removed(&self) -> Self::Stream<'_> { + EmptyZoneDiffStream + } + + fn get_added( + &self, + _name: impl ToName, + _rtype: Rtype, + ) -> Pin> + Send + '_>> { + Box::pin(ready(None)) + } + + fn get_removed( + &self, + _name: impl ToName, + _rtype: Rtype, + ) -> Pin> + Send + '_>> { + Box::pin(ready(None)) + } +} + //------------ ZoneDiffError -------------------------------------------------- /// Creating a [`ZoneDiff`] failed for some reason. diff --git a/src/zonetree/update.rs b/src/zonetree/update.rs index 7ec28f93..62d90e7f 100644 --- a/src/zonetree/update.rs +++ b/src/zonetree/update.rs @@ -21,7 +21,7 @@ use crate::zonetree::{Rrset, SharedRrset}; use super::error::OutOfZone; use super::types::ZoneUpdate; use super::util::rel_name_rev_iter; -use super::{WritableZone, WritableZoneNode, Zone, ZoneDiff}; +use super::{InMemoryZoneDiff, WritableZone, WritableZoneNode, Zone}; /// Apply a sequence of [`ZoneUpdate`]s to update the content of a [`Zone`]. /// @@ -267,7 +267,7 @@ impl ZoneUpdater { pub async fn apply( &mut self, update: ZoneUpdate, - ) -> Result, Error> { + ) -> Result, Error> { trace!("Update: {update}"); if self.state == ZoneUpdaterState::Finished { @@ -337,6 +337,8 @@ impl ZoneUpdater { /// Has zone updating finished? /// /// If true, further calls to [`apply()`] will fail. + /// + /// [`apply()`]: Self::apply pub fn is_finished(&self) -> bool { self.state == ZoneUpdaterState::Finished } @@ -521,7 +523,7 @@ impl ReopenableZoneWriter { /// Commits any pending changes to the [`Zone`] being written to. /// /// Returns the created diff, if any. - async fn commit(&mut self) -> Result, Error> { + async fn commit(&mut self) -> Result, Error> { // Commit the deletes and adds that just occurred if let Some(writable) = self.writable.take() { // Ensure that there are no dangling references to the created diff --git a/src/zonetree/walk.rs b/src/zonetree/walk.rs index 3c350abe..6ba2ec4d 100644 --- a/src/zonetree/walk.rs +++ b/src/zonetree/walk.rs @@ -2,28 +2,29 @@ use std::boxed::Box; use std::sync::{Arc, Mutex}; use std::vec::Vec; -use bytes::Bytes; +use super::{SharedRrset, StoredName}; -use super::Rrset; use crate::base::name::OwnedLabel; -use crate::base::{Name, NameBuilder}; +use crate::base::NameBuilder; /// A callback function invoked for each leaf node visited while walking a /// [`Zone`]. /// /// [`Zone`]: super::Zone -pub type WalkOp = Box, &Rrset) + Send + Sync>; +pub type WalkOp = Box; struct WalkStateInner { op: WalkOp, label_stack: Mutex>, + apex_name: StoredName, } impl WalkStateInner { - fn new(op: WalkOp) -> Self { + fn new(op: WalkOp, apex_name: StoredName) -> Self { Self { op, label_stack: Default::default(), + apex_name, } } } @@ -36,9 +37,9 @@ pub(super) struct WalkState { impl WalkState { pub(super) const DISABLED: WalkState = WalkState { inner: None }; - pub(super) fn new(op: WalkOp) -> Self { + pub(super) fn new(op: WalkOp, apex_name: StoredName) -> Self { Self { - inner: Some(Arc::new(WalkStateInner::new(op))), + inner: Some(Arc::new(WalkStateInner::new(op, apex_name))), } } @@ -46,14 +47,14 @@ impl WalkState { self.inner.is_some() } - pub(super) fn op(&self, rrset: &Rrset) { + pub(super) fn op(&self, rrset: &SharedRrset) { if let Some(inner) = &self.inner { let labels = inner.label_stack.lock().unwrap(); let mut dname = NameBuilder::new_bytes(); for label in labels.iter().rev() { dname.append_label(label.as_slice()).unwrap(); } - let owner = dname.into_name().unwrap(); + let owner = dname.append_origin(&inner.apex_name).unwrap(); (inner.op)(owner, rrset); } } diff --git a/test-data/server/axfr_large_out.rpl b/test-data/server/axfr_large_out.rpl new file mode 100644 index 00000000..f28ed504 --- /dev/null +++ b/test-data/server/axfr_large_out.rpl @@ -0,0 +1,1055 @@ +;------------ Server configuration -------------------------------------------- + +server: + ; Permit the server to respond to clients that send XFR requests from + ; 127.0.0.1 (the default). No TSIG signing is expected. + provide-xfr: 127.0.0.1 NOKEY + + ; Permit the server to respond to clients that send XFR requests from + ; 127.0.0.2. Responses will be sent in "backward compatible" mode as + ; defined by RFC 5936. + ; See: https://datatracker.ietf.org/doc/html/rfc5936#section-7.1. + provide-xfr: 127.0.0.2 NOKEY COMPATIBLE + + ; Define an in-memory zone to be served by the server. The zone is large + ; enough that it cannot be fit into a single TCP AXFR response and so must + ; be split by the server across multiple responses. + local-data: "$ORIGIN example.com." + local-data: "example.com. 3600 IN SOA ns.example.com. hostmaster.example.com. 1 3600 900 86400 3600" + local-data: "example.com. 3600 IN NS ns.example.net." + local-data: "txt0000 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0001 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0002 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0003 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0004 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0005 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0006 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0007 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0008 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0009 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0010 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0011 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0012 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0013 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0014 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0015 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0016 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0017 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0018 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0019 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0020 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0021 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0022 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0023 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0024 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0025 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0026 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0027 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0028 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0029 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0030 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0031 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0032 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0033 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0034 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0035 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0036 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0037 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0038 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0039 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0040 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0041 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0042 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0043 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0044 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0045 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0046 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0047 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0048 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0049 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0050 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0051 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0052 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0053 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0054 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0055 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0056 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0057 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0058 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0059 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0060 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0061 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0062 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0063 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0064 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0065 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0066 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0067 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0068 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0069 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0070 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0071 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0072 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0073 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0074 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0075 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0076 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0077 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0078 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0079 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0080 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0081 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0082 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0083 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0084 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0085 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0086 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0087 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0088 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0089 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0090 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0091 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0092 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0093 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0094 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0095 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0096 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0097 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0098 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0099 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0100 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0101 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0102 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0103 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0104 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0105 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0106 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0107 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0108 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0109 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0110 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0111 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0112 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0113 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0114 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0115 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0116 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0117 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0118 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0119 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0120 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0121 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0122 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0123 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0124 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0125 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0126 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0127 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0128 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0129 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0130 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0131 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0132 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0133 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0134 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0135 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0136 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0137 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0138 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0139 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0140 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0141 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0142 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0143 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0144 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0145 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0146 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0147 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0148 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0149 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0150 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0151 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0152 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0153 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0154 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0155 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0156 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0157 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0158 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0159 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0160 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0161 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0162 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0163 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0164 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0165 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0166 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0167 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0168 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0169 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0170 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0171 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0172 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0173 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0174 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0175 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0176 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0177 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0178 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0179 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0180 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0181 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0182 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0183 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0184 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0185 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0186 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0187 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0188 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0189 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0190 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0191 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0192 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0193 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0194 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0195 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0196 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0197 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0198 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0199 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0200 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0201 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0202 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0203 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0204 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0205 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0206 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0207 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0208 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0209 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0210 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0211 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0212 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0213 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0214 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0215 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0216 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0217 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0218 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0219 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0220 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0221 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0222 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0223 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0224 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0225 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0226 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0227 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0228 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0229 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0230 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0231 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0232 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0233 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0234 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0235 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0236 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0237 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0238 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0239 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0240 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0241 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0242 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0243 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0244 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0245 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0246 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0247 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0248 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0249 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0250 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0251 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0252 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0253 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0254 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0255 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0256 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0257 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0258 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0259 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0260 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0261 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0262 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0263 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0264 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0265 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0266 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0267 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0268 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0269 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0270 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0271 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0272 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0273 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0274 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0275 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0276 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0277 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0278 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0279 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0280 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0281 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0282 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0283 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0284 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0285 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0286 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0287 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0288 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0289 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0290 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0291 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0292 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0293 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0294 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0295 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0296 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0297 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0298 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0299 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0300 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0301 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0302 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0303 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0304 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0305 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0306 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0307 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0308 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0309 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0310 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0311 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0312 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0313 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0314 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0315 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0316 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0317 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0318 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0319 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0320 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0321 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0322 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0323 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0324 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0325 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0326 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0327 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0328 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0329 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0330 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0331 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0332 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0333 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0334 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0335 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0336 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0337 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0338 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0339 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0340 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0341 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0342 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0343 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0344 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0345 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0346 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0347 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0348 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0349 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0350 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0351 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0352 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0353 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0354 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0355 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0356 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0357 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0358 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0359 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0360 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0361 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0362 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0363 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0364 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0365 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0366 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0367 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0368 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0369 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0370 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0371 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0372 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0373 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0374 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0375 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0376 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0377 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0378 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0379 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0380 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0381 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0382 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0383 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0384 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0385 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0386 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0387 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0388 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0389 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0390 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0391 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0392 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0393 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0394 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0395 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0396 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0397 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0398 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0399 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0400 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0401 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0402 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0403 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0404 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0405 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0406 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0407 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0408 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0409 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0410 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0411 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0412 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0413 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0414 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0415 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0416 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0417 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0418 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0419 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0420 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0421 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0422 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0423 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0424 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0425 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0426 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0427 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0428 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0429 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0430 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0431 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0432 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0433 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0434 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0435 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0436 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0437 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0438 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0439 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0440 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0441 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0442 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0443 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0444 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0445 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0446 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0447 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0448 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0449 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0450 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0451 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0452 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0453 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0454 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0455 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0456 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0457 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0458 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0459 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0460 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0461 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0462 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0463 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0464 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0465 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0466 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0467 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0468 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0469 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0470 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0471 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0472 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0473 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0474 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0475 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0476 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0477 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0478 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0479 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0480 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0481 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0482 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0483 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0484 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0485 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0486 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0487 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0488 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0489 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0490 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0491 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0492 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0493 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0494 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0495 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0496 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0497 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0498 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." + local-data: "txt0499 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible." +CONFIG_END + +;------------ Test definition ------------------------------------------------ + +SCENARIO_BEGIN Test large AXFR out. + +;--- Mock replies + +; None + +;--- Test steps + +; Retrieve the zone via AXFR over TCP from the server. +STEP 10 QUERY +ENTRY_BEGIN +MATCH TCP +SECTION QUESTION + example.com. IN AXFR +ENTRY_END + +; NOTE: See test-data/server/README.md regarding order of RRs across multiple +; response packets. +STEP 11 CHECK_ANSWER +ENTRY_BEGIN +MATCH all EXTRA_PACKETS +REPLY QR AA NOERROR +SECTION QUESTION + example.com. IN AXFR +SECTION ANSWER + $ORIGIN example.com. + example.com. 3600 IN SOA ns.example.com. hostmaster.example.com. 1 3600 900 86400 3600 + example.com. IN NS ns.example.net. + txt0000 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0001 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0002 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0003 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0004 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0005 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0006 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0007 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0008 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0009 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0010 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0011 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0012 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0013 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0014 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0015 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0016 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0017 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0018 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0019 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0020 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0021 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0022 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0023 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0024 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0025 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0026 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0027 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0028 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0029 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0030 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0031 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0032 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0033 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0034 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0035 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0036 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0037 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0038 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0039 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0040 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0041 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0042 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0043 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0044 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0045 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0046 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0047 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0048 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0049 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0050 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0051 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0052 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0053 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0054 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0055 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0056 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0057 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0058 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0059 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0060 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0061 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0062 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0063 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0064 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0065 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0066 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0067 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0068 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0069 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0070 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0071 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0072 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0073 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0074 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0075 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0076 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0077 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0078 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0079 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0080 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0081 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0082 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0083 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0084 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0085 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0086 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0087 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0088 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0089 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0090 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0091 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0092 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0093 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0094 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0095 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0096 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0097 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0098 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0099 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0100 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0101 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0102 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0103 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0104 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0105 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0106 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0107 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0108 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0109 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0110 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0111 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0112 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0113 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0114 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0115 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0116 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0117 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0118 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0119 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0120 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0121 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0122 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0123 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0124 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0125 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0126 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0127 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0128 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0129 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0130 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0131 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0132 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0133 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0134 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0135 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0136 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0137 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0138 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0139 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0140 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0141 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0142 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0143 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0144 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0145 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0146 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0147 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0148 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0149 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0150 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0151 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0152 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0153 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0154 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0155 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0156 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0157 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0158 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0159 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0160 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0161 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0162 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0163 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0164 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0165 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0166 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0167 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0168 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0169 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0170 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0171 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0172 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0173 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0174 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0175 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0176 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0177 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0178 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0179 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0180 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0181 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0182 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0183 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0184 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0185 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0186 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0187 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0188 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0189 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0190 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0191 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0192 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0193 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0194 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0195 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0196 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0197 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0198 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0199 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0200 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0201 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0202 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0203 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0204 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0205 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0206 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0207 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0208 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0209 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0210 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0211 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0212 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0213 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0214 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0215 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0216 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0217 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0218 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0219 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0220 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0221 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0222 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0223 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0224 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0225 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0226 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0227 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0228 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0229 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0230 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0231 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0232 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0233 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0234 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0235 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0236 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0237 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0238 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0239 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0240 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0241 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0242 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0243 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0244 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0245 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0246 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0247 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0248 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0249 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0250 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0251 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0252 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0253 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0254 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0255 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0256 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0257 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0258 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0259 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0260 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0261 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0262 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0263 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0264 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0265 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0266 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0267 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0268 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0269 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0270 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0271 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0272 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0273 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0274 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0275 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0276 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0277 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0278 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0279 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0280 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0281 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0282 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0283 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0284 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0285 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0286 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0287 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0288 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0289 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0290 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0291 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0292 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0293 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0294 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0295 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0296 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0297 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0298 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0299 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0300 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0301 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0302 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0303 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0304 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0305 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0306 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0307 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0308 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0309 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0310 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0311 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0312 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0313 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0314 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0315 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0316 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0317 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0318 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0319 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0320 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0321 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0322 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0323 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0324 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0325 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0326 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0327 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0328 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0329 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0330 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0331 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0332 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0333 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0334 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0335 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0336 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0337 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0338 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0339 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0340 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0341 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0342 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0343 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0344 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0345 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0346 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0347 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0348 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0349 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0350 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0351 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0352 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0353 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0354 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0355 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0356 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0357 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0358 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0359 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0360 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0361 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0362 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0363 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0364 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0365 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0366 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0367 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0368 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0369 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0370 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0371 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0372 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0373 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0374 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0375 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0376 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0377 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0378 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0379 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0380 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0381 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0382 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0383 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0384 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0385 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0386 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0387 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0388 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0389 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0390 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0391 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0392 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0393 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0394 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0395 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0396 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0397 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0398 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0399 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0400 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0401 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0402 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0403 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0404 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0405 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0406 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0407 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0408 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0409 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0410 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0411 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0412 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0413 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0414 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0415 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0416 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0417 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0418 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0419 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0420 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0421 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0422 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0423 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0424 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0425 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0426 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0427 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0428 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0429 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0430 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0431 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0432 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0433 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0434 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0435 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0436 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0437 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0438 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0439 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0440 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0441 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0442 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0443 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0444 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0445 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0446 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0447 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0448 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0449 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0450 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0451 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0452 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0453 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0454 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0455 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0456 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0457 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0458 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0459 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0460 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0461 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0462 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0463 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0464 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0465 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0466 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0467 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0468 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0469 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0470 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0471 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0472 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0473 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0474 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0475 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0476 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0477 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0478 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0479 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0480 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0481 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0482 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0483 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0484 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0485 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0486 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0487 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0488 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0489 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0490 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0491 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0492 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0493 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0494 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0495 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0496 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0497 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0498 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + txt0499 3600 IN CNAME some.very.long.domain.name.that.takes.up.a.lot.of.bytes.so.as.to.fill.the.dns.response.with.as.few.rrs.as.possible. + example.com. 3600 IN SOA ns.example.com. hostmaster.example.com. 1 3600 900 86400 3600 +ENTRY_END + +SCENARIO_END \ No newline at end of file diff --git a/test-data/server/axfr_out.rpl b/test-data/server/axfr_out.rpl new file mode 100644 index 00000000..01efe368 --- /dev/null +++ b/test-data/server/axfr_out.rpl @@ -0,0 +1,140 @@ +; Based on https://github.com/NLnetLabs/unbound/blob/172b84f7ce6507e96fe51bd94448222a5a47274b/testdata/auth_xfr.rpl + +;------------ Server configuration -------------------------------------------- + +server: + provide-xfr: 127.0.0.1 NOKEY + provide-xfr: 127.0.0.2 NOKEY COMPATIBLE + + ; Define an in-memory zone to be served by the server. + local-data: "com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1720688795 1800 900 604800 86400" + local-data: "com. 166972 IN NS a.gtld-servers.net." + local-data: "com. 166972 IN NS b.gtld-servers.net." + local-data: "com. 166972 IN NS c.gtld-servers.net." + local-data: "com. 166972 IN NS d.gtld-servers.net." + local-data: "com. 166972 IN NS e.gtld-servers.net." + local-data: "com. 166972 IN NS f.gtld-servers.net." + local-data: "com. 166972 IN NS g.gtld-servers.net." + local-data: "com. 166972 IN NS h.gtld-servers.net." + local-data: "com. 166972 IN NS i.gtld-servers.net." + local-data: "com. 166972 IN NS j.gtld-servers.net." + local-data: "com. 166972 IN NS k.gtld-servers.net." + local-data: "com. 166972 IN NS l.gtld-servers.net." + local-data: "com. 166972 IN NS m.gtld-servers.net." + local-data: "com. 86400 IN DNSKEY 257 3 13 tx8EZRAd2+K/DJRV0S+hbBzaRPS/G6JVNBitHzqpsGlz8huE61Ms9ANe 6NSDLKJtiTBqfTJWDAywEp1FCsEINQ==" + local-data: "com. 86400 IN DNSKEY 256 3 13 Nps5nxuQHRbY3e9hcbH36kxiELJH5wil+6dC4K1keQI9ci1nqyCP4k1X oXBBn2aeSK4KxwPEs0Opqc0dicuujg==" + local-data: "com. 86400 IN DNSKEY 256 3 13 cCRwZIITlPXwDm0yKpGVYSmWLL4OpEHxA7+Rt3jS0W1N4EMOaF8doSzr JuM7aDbgAR7jtQ9SNCvYZCH2xSyfaQ==" + local-data: "alt.com. 3600 IN CNAME example.com." + local-data: "a.alt.com. 3600 IN A 1.2.3.4" + local-data: "example.com. 172800 IN NS a.iana-servers.net." + local-data: "example.com. 172800 IN NS b.iana-servers.net." + local-data: "example.com. 86400 IN DS 370 13 2 BE74359954660069D5C63D200C39F5603827D7DD02B56F120EE9F3A8 6764247C" + local-data: "www.terminal.com. 3600 IN A 1.2.3.4" + local-data: "alt.terminal.com. 3600 IN CNAME www.example.com." +CONFIG_END + +;------------ Test definition ------------------------------------------------ + +SCENARIO_BEGIN Test AXFR out. + +; Note: It is not currently possible to construct a UDP AXFR query so we cannot +; test that the server refuses the request. This instead results in FORMERR +; during request construction rather than REFUSED from the server. +;STEP 10 QUERY +;ENTRY_BEGIN +;MATCH UDP +;SECTION QUESTION +; com. IN AXFR +;ENTRY_END +; +;STEP 11 CHECK_ANSWER +;ENTRY_BEGIN +;MATCH all +;REPLY QR AA REFUSED +;ENTRY_END + +; Retrieve the zone via AXFR from the server +STEP 20 QUERY +ENTRY_BEGIN +MATCH TCP +SECTION QUESTION + com. IN AXFR +ENTRY_END + +STEP 21 CHECK_ANSWER +ENTRY_BEGIN +MATCH all +REPLY QR AA NOERROR +SECTION QUESTION + com. IN AXFR +SECTION ANSWER + com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1720688795 1800 900 604800 86400 + com. 166972 IN NS a.gtld-servers.net. + com. 166972 IN NS b.gtld-servers.net. + com. 166972 IN NS c.gtld-servers.net. + com. 166972 IN NS d.gtld-servers.net. + com. 166972 IN NS e.gtld-servers.net. + com. 166972 IN NS f.gtld-servers.net. + com. 166972 IN NS g.gtld-servers.net. + com. 166972 IN NS h.gtld-servers.net. + com. 166972 IN NS i.gtld-servers.net. + com. 166972 IN NS j.gtld-servers.net. + com. 166972 IN NS k.gtld-servers.net. + com. 166972 IN NS l.gtld-servers.net. + com. 166972 IN NS m.gtld-servers.net. + com. 86400 IN DNSKEY 257 3 13 tx8EZRAd2+K/DJRV0S+hbBzaRPS/G6JVNBitHzqpsGlz8huE61Ms9ANe 6NSDLKJtiTBqfTJWDAywEp1FCsEINQ== + com. 86400 IN DNSKEY 256 3 13 Nps5nxuQHRbY3e9hcbH36kxiELJH5wil+6dC4K1keQI9ci1nqyCP4k1X oXBBn2aeSK4KxwPEs0Opqc0dicuujg== + com. 86400 IN DNSKEY 256 3 13 cCRwZIITlPXwDm0yKpGVYSmWLL4OpEHxA7+Rt3jS0W1N4EMOaF8doSzr JuM7aDbgAR7jtQ9SNCvYZCH2xSyfaQ== + alt.com. 3600 IN CNAME example.com. + a.alt.com. 3600 IN A 1.2.3.4 + example.com. 172800 IN NS a.iana-servers.net. + example.com. 172800 IN NS b.iana-servers.net. + example.com. 86400 IN DS 370 13 2 BE74359954660069D5C63D200C39F5603827D7DD02B56F120EE9F3A8 6764247C + www.terminal.com. 3600 IN A 1.2.3.4 + alt.terminal.com. 3600 IN CNAME www.example.com. + com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1720688795 1800 900 604800 86400 +ENTRY_END + +; Retrieve the zone via backward compatible AXFR from the server +STEP 30 QUERY ADDRESS 127.0.0.2 +ENTRY_BEGIN +MATCH TCP +SECTION QUESTION + com. IN AXFR +ENTRY_END + +STEP 31 CHECK_ANSWER +ENTRY_BEGIN +MATCH all EXTRA_PACKETS +REPLY QR AA NOERROR +SECTION QUESTION + com. IN AXFR +SECTION ANSWER + com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1720688795 1800 900 604800 86400 + com. 166972 IN NS a.gtld-servers.net. + com. 166972 IN NS b.gtld-servers.net. + com. 166972 IN NS c.gtld-servers.net. + com. 166972 IN NS d.gtld-servers.net. + com. 166972 IN NS e.gtld-servers.net. + com. 166972 IN NS f.gtld-servers.net. + com. 166972 IN NS g.gtld-servers.net. + com. 166972 IN NS h.gtld-servers.net. + com. 166972 IN NS i.gtld-servers.net. + com. 166972 IN NS j.gtld-servers.net. + com. 166972 IN NS k.gtld-servers.net. + com. 166972 IN NS l.gtld-servers.net. + com. 166972 IN NS m.gtld-servers.net. + com. 86400 IN DNSKEY 256 3 13 Nps5nxuQHRbY3e9hcbH36kxiELJH5wil+6dC4K1keQI9ci1nqyCP4k1X oXBBn2aeSK4KxwPEs0Opqc0dicuujg== + com. 86400 IN DNSKEY 256 3 13 cCRwZIITlPXwDm0yKpGVYSmWLL4OpEHxA7+Rt3jS0W1N4EMOaF8doSzr JuM7aDbgAR7jtQ9SNCvYZCH2xSyfaQ== + com. 86400 IN DNSKEY 257 3 13 tx8EZRAd2+K/DJRV0S+hbBzaRPS/G6JVNBitHzqpsGlz8huE61Ms9ANe 6NSDLKJtiTBqfTJWDAywEp1FCsEINQ== + alt.terminal.com. 3600 IN CNAME www.example.com. + a.alt.com. 3600 IN A 1.2.3.4 + example.com. 172800 IN NS a.iana-servers.net. + example.com. 172800 IN NS b.iana-servers.net. + example.com. 86400 IN DS 370 13 2 BE74359954660069D5C63D200C39F5603827D7DD02B56F120EE9F3A8 6764247C + www.terminal.com. 3600 IN A 1.2.3.4 + alt.com. 3600 IN CNAME example.com. + com. 900 IN SOA a.gtld-servers.net. nstld.verisign-grs.com. 1720688795 1800 900 604800 86400 +ENTRY_END + +SCENARIO_END \ No newline at end of file diff --git a/test-data/server/edns_downstream_cookies.rpl b/test-data/server/edns_downstream_cookies.rpl index f20ed409..a1a7bb1a 100644 --- a/test-data/server/edns_downstream_cookies.rpl +++ b/test-data/server/edns_downstream_cookies.rpl @@ -6,8 +6,10 @@ server: cookie-secret: "000102030405060708090a0b0c0d0e0f" access-control: 127.0.0.1 allow_cookie access-control: 1.2.3.4 allow - local-data: "test. TXT test" + ; Define an in-memory zone to be served by the server. + local-data: "test. 3600 IN SOA ns.test. hostmaster.test. 1 3600 900 86400 3600" + local-data: "test. TXT test" CONFIG_END SCENARIO_BEGIN Test downstream DNS Cookies diff --git a/test-data/server/edns_keepalive.rpl b/test-data/server/edns_keepalive.rpl index e9f24e0c..51153227 100644 --- a/test-data/server/edns_keepalive.rpl +++ b/test-data/server/edns_keepalive.rpl @@ -1,8 +1,11 @@ ; Based on: https://github.com/NLnetLabs/unbound/blob/49e425810275917e7fd09a24bae3b97d83b55c13/testdata/edns_keepalive.rpl server: - edns-tcp-keepalive: yes - edns-tcp-keepalive-timeout: 30000 - local-data: "test. TXT test" + edns-tcp-keepalive: yes + edns-tcp-keepalive-timeout: 30000 + + ; Define an in-memory zone to be served by the server. + local-data: "test. 3600 IN SOA ns.test. hostmaster.test. 1 3600 900 86400 3600" + local-data: "test. TXT test" CONFIG_END SCENARIO_BEGIN TCP Keepalive diff --git a/test-data/zonefiles/nsd-example.txt b/test-data/zonefiles/nsd-example.txt index 06ba2b8a..bedf91ac 100644 --- a/test-data/zonefiles/nsd-example.txt +++ b/test-data/zonefiles/nsd-example.txt @@ -1,7 +1,8 @@ -$ORIGIN example.com. ; 'default' domain as FQDN for this zone +; From: https://nsd.docs.nlnetlabs.nl/en/latest/zonefile.html#creating-a-zone +$ORIGIN example.com. $TTL 86400 ; default time-to-live for this zone -example.com. IN SOA ns.example.com. noc.dns.icann.org. ( +example.com. IN SOA ns.example.com. noc.dns.example.org. ( 2020080302 ;Serial 7200 ;Refresh 3600 ;Retry @@ -9,17 +10,14 @@ example.com. IN SOA ns.example.com. noc.dns.icann.org. ( 3600 ;Negative response caching TTL ) -; The nameserver that are authoritative for this zone. +; The nameservers that are authoritative for this zone. NS example.com. -; these A records below are equivalent +; A and AAAA records are for IPv4 and IPv6 addresses respectively example.com. A 192.0.2.1 -@ A 192.0.2.1 - A 192.0.2.1 + AAAA 2001:db8::3 -@ AAAA 2001:db8::3 - -; A CNAME redirect from www.exmaple.com to example.com +; A CNAME redirects from www.example.com to example.com www CNAME example.com. mail MX 10 example.com.