Files
hickory-dns/tests/integration-tests/tests/sqlite_authority_tests.rs
2024-04-20 13:08:29 -07:00

1194 lines
35 KiB
Rust

#![cfg(feature = "sqlite")]
use std::str::FromStr;
use rusqlite::*;
use hickory_proto::op::*;
use hickory_proto::rr::dnssec::*;
use hickory_proto::rr::rdata::*;
use hickory_proto::rr::*;
use hickory_server::authority::LookupOptions;
use hickory_server::authority::{Authority, ZoneType};
use hickory_server::server::Protocol;
use hickory_server::server::RequestInfo;
use hickory_server::store::in_memory::InMemoryAuthority;
use hickory_server::store::sqlite::{Journal, SqliteAuthority};
const TEST_HEADER: &Header = &Header::new();
fn create_example() -> SqliteAuthority {
let authority = hickory_integration::example_authority::create_example();
SqliteAuthority::new(authority, true, false)
}
#[cfg(feature = "dnssec")]
fn create_secure_example() -> SqliteAuthority {
let authority = hickory_integration::example_authority::create_secure_example();
SqliteAuthority::new(authority, true, true)
}
#[tokio::test]
async fn test_search() {
let example = create_example();
let origin = example.origin().clone();
let mut query: Query = Query::new();
query.set_name(origin.into());
let query = LowerQuery::from(query);
let request_info = RequestInfo::new(
"127.0.0.1:53".parse().unwrap(),
Protocol::Udp,
TEST_HEADER,
&query,
);
let result = example
.search(request_info, LookupOptions::default())
.await
.unwrap();
if !result.is_empty() {
let record = result.iter().next().unwrap();
assert_eq!(record.record_type(), RecordType::A);
assert_eq!(record.dns_class(), DNSClass::IN);
assert_eq!(record.data().unwrap(), &RData::A(A::new(93, 184, 215, 14)));
} else {
panic!("expected a result"); // valid panic, in test
}
}
/// this is a little more interesting b/c it requires a recursive lookup for the origin
#[tokio::test]
async fn test_search_www() {
let example = create_example();
let www_name = Name::parse("www.example.com.", None).unwrap();
let mut query: Query = Query::new();
query.set_name(www_name);
let query = LowerQuery::from(query);
let request_info = RequestInfo::new(
"127.0.0.1:53".parse().unwrap(),
Protocol::Udp,
TEST_HEADER,
&query,
);
let result = example
.search(request_info, LookupOptions::default())
.await
.unwrap();
if !result.is_empty() {
let record = result.iter().next().unwrap();
assert_eq!(record.record_type(), RecordType::A);
assert_eq!(record.dns_class(), DNSClass::IN);
assert_eq!(record.data().unwrap(), &RData::A(A::new(93, 184, 215, 14)));
} else {
panic!("expected a result"); // valid panic, in test
}
}
#[tokio::test]
async fn test_authority() {
let authority = create_example();
assert_eq!(
authority
.soa()
.await
.unwrap()
.iter()
.next()
.unwrap()
.dns_class(),
DNSClass::IN
);
assert!(!authority
.lookup(authority.origin(), RecordType::NS, LookupOptions::default())
.await
.unwrap()
.was_empty());
let mut lookup: Vec<_> = authority
.ns(LookupOptions::default())
.await
.unwrap()
.iter()
.cloned()
.collect();
lookup.sort();
assert_eq!(
*lookup.first().unwrap(),
Record::new()
.set_name(authority.origin().clone().into())
.set_ttl(86400)
.set_record_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NS(NS(Name::parse(
"a.iana-servers.net.",
None
)
.unwrap()))))
.clone()
);
assert_eq!(
*lookup.last().unwrap(),
Record::new()
.set_name(authority.origin().clone().into())
.set_ttl(86400)
.set_record_type(RecordType::NS)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NS(NS(Name::parse(
"b.iana-servers.net.",
None
)
.unwrap()))))
.clone()
);
assert!(!authority
.lookup(
authority.origin(),
RecordType::TXT,
LookupOptions::default()
)
.await
.unwrap()
.was_empty());
let mut lookup: Vec<_> = authority
.lookup(
authority.origin(),
RecordType::TXT,
LookupOptions::default(),
)
.await
.unwrap()
.iter()
.cloned()
.collect();
lookup.sort();
assert_eq!(
*lookup.first().unwrap(),
Record::new()
.set_name(authority.origin().clone().into())
.set_ttl(60)
.set_record_type(RecordType::TXT)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::TXT(TXT::new(vec![
"$Id: example.com 4415 2015-08-24 \
20:12:23Z davids $"
.to_string(),
]))))
.clone()
);
assert_eq!(
*authority
.lookup(authority.origin(), RecordType::A, LookupOptions::default())
.await
.unwrap()
.iter()
.next()
.unwrap(),
Record::new()
.set_name(authority.origin().clone().into())
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::A(A::new(93, 184, 215, 14))))
.clone()
);
}
#[cfg(feature = "dnssec")]
#[tokio::test]
async fn test_authorize() {
use hickory_client::serialize::binary::{BinDecodable, BinEncodable};
use hickory_server::authority::MessageRequest;
let authority = create_example();
let mut message = Message::new();
message
.set_id(10)
.set_message_type(MessageType::Query)
.set_op_code(OpCode::Update);
message.add_query(Query::default());
let bytes = message.to_bytes().unwrap();
let message = MessageRequest::from_bytes(&bytes).unwrap();
assert_eq!(
authority.authorize(&message).await,
Err(ResponseCode::Refused)
);
// TODO: this will nee to be more complex as additional policies are added
// authority.set_allow_update(true);
// assert!(authority.authorize(&message).is_ok());
}
#[tokio::test]
async fn test_prerequisites() {
let not_zone = Name::from_str("not.a.domain.com").unwrap();
let not_in_zone = Name::from_str("not.example.com").unwrap();
let mut authority = create_example();
authority.set_allow_update(true);
// first check the initial negatives, ttl = 0, and the zone is the same
assert_eq!(
authority
.verify_prerequisites(&[Record::new()
.set_name(not_in_zone.clone())
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
assert_eq!(
authority
.verify_prerequisites(&[Record::new()
.set_name(not_zone)
.set_ttl(0)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::NotZone)
);
// * ANY ANY empty Name is in use
assert!(authority
.verify_prerequisites(&[Record::new()
.set_name(authority.origin().clone().into())
.set_ttl(0)
.set_dns_class(DNSClass::ANY)
.set_record_type(RecordType::ANY)
.set_data(Some(RData::NULL(NULL::new())))
.clone()])
.await
.is_ok());
assert_eq!(
authority
.verify_prerequisites(&[Record::new()
.set_name(not_in_zone.clone())
.set_ttl(0)
.set_dns_class(DNSClass::ANY)
.set_record_type(RecordType::ANY)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::NXDomain)
);
// * ANY rrset empty RRset exists (value independent)
assert!(authority
.verify_prerequisites(&[Record::new()
.set_name(authority.origin().clone().into())
.set_ttl(0)
.set_dns_class(DNSClass::ANY)
.set_record_type(RecordType::A)
.set_data(Some(RData::NULL(NULL::new())))
.clone()])
.await
.is_ok());
assert_eq!(
authority
.verify_prerequisites(&[Record::new()
.set_name(not_in_zone.clone())
.set_ttl(0)
.set_dns_class(DNSClass::ANY)
.set_record_type(RecordType::A)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::NXRRSet)
);
// * NONE ANY empty Name is not in use
assert!(authority
.verify_prerequisites(&[Record::new()
.set_name(not_in_zone.clone())
.set_ttl(0)
.set_dns_class(DNSClass::NONE)
.set_record_type(RecordType::ANY)
.set_data(Some(RData::NULL(NULL::new())))
.clone()])
.await
.is_ok());
assert_eq!(
authority
.verify_prerequisites(&[Record::new()
.set_name(authority.origin().clone().into())
.set_ttl(0)
.set_dns_class(DNSClass::NONE)
.set_record_type(RecordType::ANY)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::YXDomain)
);
// * NONE rrset empty RRset does not exist
assert!(authority
.verify_prerequisites(&[Record::new()
.set_name(not_in_zone.clone())
.set_ttl(0)
.set_dns_class(DNSClass::NONE)
.set_record_type(RecordType::A)
.set_data(Some(RData::NULL(NULL::new())))
.clone()])
.await
.is_ok());
assert_eq!(
authority
.verify_prerequisites(&[Record::new()
.set_name(authority.origin().clone().into())
.set_ttl(0)
.set_dns_class(DNSClass::NONE)
.set_record_type(RecordType::A)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::YXRRSet)
);
// * zone rrset rr RRset exists (value dependent)
assert!(authority
.verify_prerequisites(&[Record::new()
.set_name(authority.origin().clone().into())
.set_ttl(0)
.set_dns_class(DNSClass::IN)
.set_record_type(RecordType::A)
.set_data(Some(RData::A(A::new(93, 184, 215, 14))))
.clone()])
.await
.is_ok());
// wrong class
assert_eq!(
authority
.verify_prerequisites(&[Record::new()
.set_name(authority.origin().clone().into())
.set_ttl(0)
.set_dns_class(DNSClass::CH)
.set_record_type(RecordType::A)
.set_data(Some(RData::A(A::new(93, 184, 215, 14))))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
// wrong Name
assert_eq!(
authority
.verify_prerequisites(&[Record::new()
.set_name(not_in_zone)
.set_ttl(0)
.set_dns_class(DNSClass::IN)
.set_record_type(RecordType::A)
.set_data(Some(RData::A(A::new(93, 184, 216, 24))))
.clone()],)
.await,
Err(ResponseCode::NXRRSet)
);
// wrong IP
assert_eq!(
authority
.verify_prerequisites(&[Record::new()
.set_name(authority.origin().clone().into())
.set_ttl(0)
.set_dns_class(DNSClass::IN)
.set_record_type(RecordType::A)
.set_data(Some(RData::A(A::new(93, 184, 216, 24))))
.clone()],)
.await,
Err(ResponseCode::NXRRSet)
);
}
#[tokio::test]
async fn test_pre_scan() {
let up_name = Name::from_str("www.example.com").unwrap();
let not_zone = Name::from_str("not.zone.com").unwrap();
let authority = create_example();
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(not_zone)
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::A(A::new(93, 184, 216, 24))))
.clone()],)
.await,
Err(ResponseCode::NotZone)
);
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::ANY)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::AXFR)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::IXFR)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
assert!(authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::A(A::new(93, 184, 216, 24))))
.clone()])
.await
.is_ok());
assert!(authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::NULL(NULL::new())))
.clone()])
.await
.is_ok());
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::ANY)
.set_data(Some(RData::A(A::new(93, 184, 216, 24))))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(0)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::ANY)
.set_data(Some(RData::A(A::new(93, 184, 216, 24))))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(0)
.set_record_type(RecordType::AXFR)
.set_dns_class(DNSClass::ANY)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(0)
.set_record_type(RecordType::IXFR)
.set_dns_class(DNSClass::ANY)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
assert!(authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(0)
.set_record_type(RecordType::ANY)
.set_dns_class(DNSClass::ANY)
.set_data(Some(RData::NULL(NULL::new())))
.clone()])
.await
.is_ok());
assert!(authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(0)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::ANY)
.set_data(Some(RData::NULL(NULL::new())))
.clone()])
.await
.is_ok());
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::NONE)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(0)
.set_record_type(RecordType::ANY)
.set_dns_class(DNSClass::NONE)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(0)
.set_record_type(RecordType::AXFR)
.set_dns_class(DNSClass::NONE)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(0)
.set_record_type(RecordType::IXFR)
.set_dns_class(DNSClass::NONE)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
assert!(authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(0)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::NONE)
.set_data(Some(RData::NULL(NULL::new())))
.clone()])
.await
.is_ok());
assert!(authority
.pre_scan(&[Record::new()
.set_name(up_name.clone())
.set_ttl(0)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::NONE)
.set_data(Some(RData::A(A::new(93, 184, 216, 24))))
.clone()])
.await
.is_ok());
assert_eq!(
authority
.pre_scan(&[Record::new()
.set_name(up_name)
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::CH)
.set_data(Some(RData::NULL(NULL::new())))
.clone()],)
.await,
Err(ResponseCode::FormErr)
);
}
#[tokio::test]
async fn test_update() {
let new_name = Name::from_str("new.example.com").unwrap();
let www_name = Name::from_str("www.example.com").unwrap();
let mut authority = create_example();
let serial = authority.serial().await;
authority.set_allow_update(true);
let mut original_vec: Vec<Record> = vec![
Record::new()
.set_name(www_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::TXT)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::TXT(TXT::new(vec!["v=spf1 -all".to_string()]))))
.clone(),
Record::new()
.set_name(www_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::A(A::new(93, 184, 215, 14))))
.clone(),
Record::new()
.set_name(www_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::AAAA)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::AAAA(AAAA::new(
0x2606, 0x2800, 0x21f, 0xcb07, 0x6820, 0x80da, 0xaf6b, 0x8b2c,
))))
.clone(),
];
original_vec.sort();
{
// assert that the correct set of records is there.
let mut www_rrset: Vec<Record> = authority
.lookup(
&www_name.clone().into(),
RecordType::ANY,
LookupOptions::default(),
)
.await
.unwrap()
.iter()
.cloned()
.collect();
www_rrset.sort();
assert_eq!(www_rrset, original_vec);
// assert new record doesn't exist
assert!(authority
.lookup(
&new_name.clone().into(),
RecordType::ANY,
LookupOptions::default()
)
.await
.unwrap()
.was_empty());
}
//
// zone rrset rr Add to an RRset
let add_record = &[Record::new()
.set_name(new_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::A(A::new(93, 184, 216, 24))))
.clone()];
assert!(authority
.update_records(add_record, true,)
.await
.expect("update failed",));
assert_eq!(
authority
.lookup(
&new_name.clone().into(),
RecordType::ANY,
LookupOptions::default()
)
.await
.unwrap()
.iter()
.collect::<Vec<_>>(),
add_record.iter().collect::<Vec<&Record>>()
);
assert_eq!(serial + 1, authority.serial().await);
let add_www_record = &[Record::new()
.set_name(www_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::A(A::new(10, 0, 0, 1))))
.clone()];
assert!(authority
.update_records(add_www_record, true,)
.await
.expect("update failed",));
assert_eq!(serial + 2, authority.serial().await);
{
let mut www_rrset: Vec<_> = authority
.lookup(
&www_name.clone().into(),
RecordType::ANY,
LookupOptions::default(),
)
.await
.unwrap()
.iter()
.cloned()
.collect();
www_rrset.sort();
let mut plus_10 = original_vec.clone();
plus_10.push(add_www_record[0].clone());
plus_10.sort();
assert_eq!(www_rrset, plus_10);
}
//
// NONE rrset rr Delete an RR from an RRset
let del_record = &[Record::new()
.set_name(new_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::NONE)
.set_data(Some(RData::A(A::new(93, 184, 216, 24))))
.clone()];
assert!(authority
.update_records(del_record, true,)
.await
.expect("update failed",));
assert_eq!(serial + 3, authority.serial().await);
{
let lookup = authority
.lookup(&new_name.into(), RecordType::ANY, LookupOptions::default())
.await
.unwrap();
println!("after delete of specific record: {lookup:?}");
assert!(lookup.was_empty());
}
// remove one from www
let del_record = &[Record::new()
.set_name(www_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::NONE)
.set_data(Some(RData::A(A::new(10, 0, 0, 1))))
.clone()];
assert!(authority
.update_records(del_record, true,)
.await
.expect("update failed",));
assert_eq!(serial + 4, authority.serial().await);
{
let mut www_rrset: Vec<_> = authority
.lookup(
&www_name.clone().into(),
RecordType::ANY,
LookupOptions::default(),
)
.await
.unwrap()
.iter()
.cloned()
.collect();
www_rrset.sort();
assert_eq!(www_rrset, original_vec);
}
//
// ANY rrset empty Delete an RRset
let del_record = &[Record::new()
.set_name(www_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::A)
.set_dns_class(DNSClass::ANY)
.set_data(Some(RData::NULL(NULL::new())))
.clone()];
assert!(authority
.update_records(del_record, true,)
.await
.expect("update failed",));
assert_eq!(serial + 5, authority.serial().await);
let mut removed_a_vec: Vec<_> = vec![
Record::new()
.set_name(www_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::TXT)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::TXT(TXT::new(vec!["v=spf1 -all".to_string()]))))
.clone(),
Record::new()
.set_name(www_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::AAAA)
.set_dns_class(DNSClass::IN)
.set_data(Some(RData::AAAA(AAAA::new(
0x2606, 0x2800, 0x21f, 0xcb07, 0x6820, 0x80da, 0xaf6b, 0x8b2c,
))))
.clone(),
];
removed_a_vec.sort();
{
let mut www_rrset: Vec<Record> = authority
.lookup(
&www_name.clone().into(),
RecordType::ANY,
LookupOptions::default(),
)
.await
.unwrap()
.iter()
.cloned()
.collect();
www_rrset.sort();
assert_eq!(www_rrset, removed_a_vec);
}
//
// ANY ANY empty Delete all RRsets from a name
println!("deleting all records");
let del_record = &[Record::new()
.set_name(www_name.clone())
.set_ttl(86400)
.set_record_type(RecordType::ANY)
.set_dns_class(DNSClass::ANY)
.set_data(Some(RData::NULL(NULL::new())))
.clone()];
assert!(authority
.update_records(del_record, true,)
.await
.expect("update failed",));
assert!(authority
.lookup(&www_name.into(), RecordType::ANY, LookupOptions::default())
.await
.unwrap()
.was_empty());
assert_eq!(serial + 6, authority.serial().await);
}
#[cfg(feature = "dnssec")]
#[tokio::test]
#[allow(clippy::uninlined_format_args)]
async fn test_zone_signing() {
use hickory_proto::rr::dnssec::rdata::RRSIG;
let authority = create_secure_example();
let results = authority
.lookup(
authority.origin(),
RecordType::AXFR,
LookupOptions::for_dnssec(true, SupportedAlgorithms::all()),
)
.await
.unwrap();
assert!(
results
.iter()
.any(|r| r.record_type() == RecordType::DNSKEY),
"must contain a DNSKEY"
);
let results = authority
.lookup(
authority.origin(),
RecordType::AXFR,
LookupOptions::for_dnssec(true, SupportedAlgorithms::all()),
)
.await
.unwrap();
for record in &results {
if record.record_type() == RecordType::RRSIG {
continue;
}
if record.record_type() == RecordType::DNSKEY {
continue;
}
let inner_results = authority
.lookup(
authority.origin(),
RecordType::AXFR,
LookupOptions::for_dnssec(true, SupportedAlgorithms::all()),
)
.await
.unwrap();
// validate all records have associated RRSIGs after signing
assert!(
inner_results
.iter()
.filter_map(|r| {
match r.record_type() {
RecordType::RRSIG if r.name() == record.name() => {
r.data().and_then(RRSIG::try_borrow)
}
_ => None,
}
})
.any(|rrsig| rrsig.type_covered() == record.record_type()),
"record type not covered: {:?}",
record
);
}
}
#[cfg(feature = "dnssec")]
#[tokio::test]
async fn test_get_nsec() {
let name = Name::from_str("zzz.example.com").unwrap();
let authority = create_secure_example();
let lower_name = LowerName::from(name.clone());
let results = authority
.get_nsec_records(
&lower_name,
LookupOptions::for_dnssec(true, SupportedAlgorithms::all()),
)
.await
.unwrap();
for record in &results {
assert!(*record.name() < name);
}
}
#[tokio::test]
async fn test_journal() {
// test that this message can be inserted
let conn = Connection::open_in_memory().expect("could not create in memory DB");
let mut journal = Journal::new(conn).unwrap();
journal.schema_up().unwrap();
let mut authority = create_example();
authority.set_journal(journal).await;
authority.persist_to_journal().await.unwrap();
let new_name = Name::from_str("new.example.com").unwrap();
let delete_name = Name::from_str("www.example.com").unwrap();
let new_record = Record::new()
.set_name(new_name.clone())
.set_record_type(RecordType::A)
.set_data(Some(RData::A(A::new(10, 11, 12, 13))))
.clone();
let delete_record = Record::new()
.set_name(delete_name.clone())
.set_record_type(RecordType::A)
.set_data(Some(RData::A(A::new(93, 184, 215, 14))))
.set_dns_class(DNSClass::NONE)
.clone();
authority
.update_records(&[new_record.clone(), delete_record], true)
.await
.unwrap();
// assert that the correct set of records is there.
let new_rrset: Vec<Record> = authority
.lookup(
&new_name.clone().into(),
RecordType::A,
LookupOptions::default(),
)
.await
.unwrap()
.iter()
.cloned()
.collect();
assert!(new_rrset.iter().all(|r| *r == new_record));
let lower_delete_name = LowerName::from(delete_name);
let delete_rrset = authority
.lookup(&lower_delete_name, RecordType::A, LookupOptions::default())
.await
.unwrap();
assert!(delete_rrset.was_empty());
// that record should have been recorded... let's reload the journal and see if we get it.
let in_memory =
InMemoryAuthority::empty(authority.origin().clone().into(), ZoneType::Primary, false);
let mut recovered_authority = SqliteAuthority::new(in_memory, false, false);
recovered_authority
.recover_with_journal(
authority
.journal()
.await
.as_ref()
.expect("journal not Some"),
)
.await
.expect("recovery");
// assert that the correct set of records is there.
let new_rrset: Vec<Record> = recovered_authority
.lookup(&new_name.into(), RecordType::A, LookupOptions::default())
.await
.unwrap()
.iter()
.cloned()
.collect();
assert!(new_rrset.iter().all(|r| *r == new_record));
let delete_rrset = authority
.lookup(&lower_delete_name, RecordType::A, LookupOptions::default())
.await
.unwrap();
assert!(delete_rrset.was_empty());
}
#[tokio::test]
#[allow(clippy::blocks_in_conditions)]
async fn test_recovery() {
// test that this message can be inserted
let conn = Connection::open_in_memory().expect("could not create in memory DB");
let mut journal = Journal::new(conn).unwrap();
journal.schema_up().unwrap();
let mut authority = create_example();
authority.set_journal(journal).await;
authority.persist_to_journal().await.unwrap();
let journal = authority.journal().await;
let journal = journal
.as_ref()
.expect("test should have associated journal");
let in_memory =
InMemoryAuthority::empty(authority.origin().clone().into(), ZoneType::Primary, false);
let mut recovered_authority = SqliteAuthority::new(in_memory, false, false);
recovered_authority
.recover_with_journal(journal)
.await
.expect("recovery");
assert_eq!(
recovered_authority.records().await.len(),
authority.records().await.len()
);
assert!(recovered_authority
.soa()
.await
.unwrap()
.iter()
.zip(authority.soa().await.unwrap().iter())
.all(|(r1, r2)| r1 == r2));
let recovered_records = recovered_authority.records().await;
let records = authority.records().await;
assert!(recovered_records.iter().all(|(rr_key, rr_set)| {
let other_rr_set = records
.get(rr_key)
.unwrap_or_else(|| panic!("key doesn't exist: {:?}", rr_key));
rr_set
.records_without_rrsigs()
.zip(other_rr_set.records_without_rrsigs())
.all(|(record, other_record)| {
record.ttl() == other_record.ttl() && record.data() == other_record.data()
})
},));
assert!(records.iter().all(|(rr_key, rr_set)| {
let other_rr_set = recovered_records
.get(rr_key)
.unwrap_or_else(|| panic!("key doesn't exist: {:?}", rr_key));
rr_set
.records_without_rrsigs()
.zip(other_rr_set.records_without_rrsigs())
.all(|(record, other_record)| {
record.ttl() == other_record.ttl() && record.data() == other_record.data()
})
}));
}
#[tokio::test]
async fn test_axfr() {
let mut authority = create_example();
authority.set_allow_axfr(true);
// query: &'q LowerQuery,
// is_secure: bool,
// supported_algorithms: SupportedAlgorithms,
let query = LowerQuery::from(Query::query(
Name::from_str("example.com.").unwrap(),
RecordType::AXFR,
));
let request_info = RequestInfo::new(
"127.0.0.1:53".parse().unwrap(),
Protocol::Udp,
TEST_HEADER,
&query,
);
let result = authority
.search(request_info, LookupOptions::default())
.await
.unwrap();
// just update this if the count goes up in the authority
assert_eq!(result.iter().count(), 12);
}
#[tokio::test]
async fn test_refused_axfr() {
let mut authority = create_example();
authority.set_allow_axfr(false);
let query = LowerQuery::from(Query::query(
Name::from_str("example.com.").unwrap(),
RecordType::AXFR,
));
let request_info = RequestInfo::new(
"127.0.0.1:53".parse().unwrap(),
Protocol::Udp,
TEST_HEADER,
&query,
);
let result = authority
.search(request_info, LookupOptions::default())
.await;
// just update this if the count goes up in the authority
assert!(result.unwrap_err().is_refused());
}