zone signing complete
This commit is contained in:
parent
7b271c468d
commit
aab326f9fd
@ -4,7 +4,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## unreleased
|
||||
### Added
|
||||
- Documentation on all modules
|
||||
- Documentation on all modules, and many standard RFC types
|
||||
- Authority zone signing now complete, still need to load/save private keys
|
||||
|
||||
### Fixed
|
||||
- Added loop on TCP accept requests
|
||||
|
@ -81,6 +81,10 @@ impl Authority {
|
||||
allow_update: allow_update, secure_keys: Vec::new() }
|
||||
}
|
||||
|
||||
pub fn add_secure_key(&mut self, signer: Signer) {
|
||||
self.secure_keys.push(signer);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn set_allow_update(&mut self, allow_update: bool) {
|
||||
self.allow_update = allow_update;
|
||||
@ -94,13 +98,28 @@ impl Authority {
|
||||
self.zone_type
|
||||
}
|
||||
|
||||
pub fn get_soa(&self) -> Option<&Record> {
|
||||
pub fn get_soa(&self, is_secure: bool) -> Option<&Record> {
|
||||
// SOA should be origin|SOA
|
||||
self.lookup(&self.origin, RecordType::SOA).and_then(|v|v.first().map(|v| *v))
|
||||
self.lookup(&self.origin, RecordType::SOA, is_secure).and_then(|v|v.first().map(|v| *v))
|
||||
}
|
||||
|
||||
fn get_serial(&self) -> u32 {
|
||||
let soa = if let Some(ref soa_record) = self.get_soa(false) {
|
||||
soa_record.clone()
|
||||
} else {
|
||||
warn!("no soa record found for zone: {}", self.origin);
|
||||
return 0;
|
||||
};
|
||||
|
||||
if let &RData::SOA(ref soa_rdata) = soa.get_rdata() {
|
||||
soa_rdata.get_serial()
|
||||
} else {
|
||||
panic!("This was not an SOA record");
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_soa_serial(&mut self) -> u32 {
|
||||
let mut soa = if let Some(ref mut soa_record) = self.get_soa() {
|
||||
let mut soa = if let Some(ref mut soa_record) = self.get_soa(false) {
|
||||
soa_record.clone()
|
||||
} else {
|
||||
warn!("no soa record found for zone: {}", self.origin);
|
||||
@ -118,8 +137,8 @@ impl Authority {
|
||||
return serial;
|
||||
}
|
||||
|
||||
pub fn get_ns(&self) -> Option<Vec<&Record>> {
|
||||
self.lookup(&self.origin, RecordType::NS)
|
||||
pub fn get_ns(&self, is_secure: bool) -> Option<Vec<&Record>> {
|
||||
self.lookup(&self.origin, RecordType::NS, is_secure)
|
||||
}
|
||||
|
||||
/// ```text
|
||||
@ -221,7 +240,7 @@ impl Authority {
|
||||
match require.get_rr_type() {
|
||||
// ANY ANY empty Name is in use
|
||||
RecordType::ANY => {
|
||||
if let None = self.lookup(require.get_name(), RecordType::ANY) {
|
||||
if let None = self.lookup(require.get_name(), RecordType::ANY, false) {
|
||||
return Err(ResponseCode::NXDomain);
|
||||
} else {
|
||||
continue;
|
||||
@ -229,7 +248,7 @@ impl Authority {
|
||||
},
|
||||
// ANY rrset empty RRset exists (value independent)
|
||||
rrset @ _ => {
|
||||
if let None = self.lookup(require.get_name(), rrset) {
|
||||
if let None = self.lookup(require.get_name(), rrset, false) {
|
||||
return Err(ResponseCode::NXRRSet);
|
||||
} else {
|
||||
continue;
|
||||
@ -245,7 +264,7 @@ impl Authority {
|
||||
match require.get_rr_type() {
|
||||
// NONE ANY empty Name is not in use
|
||||
RecordType::ANY => {
|
||||
if let Some(..) = self.lookup(require.get_name(), RecordType::ANY) {
|
||||
if let Some(..) = self.lookup(require.get_name(), RecordType::ANY, false) {
|
||||
return Err(ResponseCode::YXDomain);
|
||||
} else {
|
||||
continue;
|
||||
@ -253,7 +272,7 @@ impl Authority {
|
||||
},
|
||||
// NONE rrset empty RRset does not exist
|
||||
rrset @ _ => {
|
||||
if let Some(..) = self.lookup(require.get_name(), rrset) {
|
||||
if let Some(..) = self.lookup(require.get_name(), rrset, false) {
|
||||
return Err(ResponseCode::YXRRSet);
|
||||
} else {
|
||||
continue;
|
||||
@ -266,7 +285,7 @@ impl Authority {
|
||||
,
|
||||
class @ _ if class == self.class =>
|
||||
// zone rrset rr RRset exists (value dependent)
|
||||
if let Some(rrset) = self.lookup(require.get_name(), require.get_rr_type()) {
|
||||
if let Some(rrset) = self.lookup(require.get_name(), require.get_rr_type(), false) {
|
||||
if rrset.iter().filter(|rr| *rr == &require).next().is_none() {
|
||||
return Err(ResponseCode::NXRRSet);
|
||||
} else {
|
||||
@ -428,8 +447,10 @@ impl Authority {
|
||||
/// NONE rrset rr Delete an RR from an RRset
|
||||
/// zone rrset rr Add to an RRset
|
||||
/// ```
|
||||
fn update_records(&mut self, records: &[Record], serial: u32) -> UpdateResult<bool> {
|
||||
fn update_records(&mut self, records: &[Record]) -> UpdateResult<bool> {
|
||||
let mut updated = false;
|
||||
let serial: u32 = self.get_serial();
|
||||
|
||||
|
||||
// 3.4.2.7 - Pseudocode For Update Section Processing
|
||||
//
|
||||
@ -527,6 +548,7 @@ impl Authority {
|
||||
if let &RData::NULL( .. ) = rr.get_rdata() {
|
||||
let deleted = self.records.remove(&rr_key);
|
||||
info!("deleted rrset: {:?}", deleted);
|
||||
updated = updated || deleted.is_some();
|
||||
} else {
|
||||
info!("expected empty rdata: {:?}", rr);
|
||||
return Err(ResponseCode::FormErr)
|
||||
@ -552,6 +574,17 @@ impl Authority {
|
||||
}
|
||||
}
|
||||
|
||||
// update the serial...
|
||||
if updated {
|
||||
|
||||
// need to resign any records at the current serial number and bump the number.
|
||||
// first bump the serial number on the SOA, so that it is resigned with the new serial.
|
||||
self.increment_soa_serial();
|
||||
|
||||
// TODO: should we auto sign here? or maybe up a level...
|
||||
self.sign_zone();
|
||||
}
|
||||
|
||||
Ok(updated)
|
||||
}
|
||||
|
||||
@ -630,24 +663,15 @@ impl Authority {
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// Ok() on success or Err() with the `ResponseCode` associated with the error.
|
||||
pub fn update(&mut self, update: &UpdateMessage) -> UpdateResult<()> {
|
||||
/// true if any of additions, updates or deletes were made to the zone, false otherwise. Err is
|
||||
/// returned in the case of bad data, etc.
|
||||
pub fn update(&mut self, update: &UpdateMessage) -> UpdateResult<bool> {
|
||||
// the spec says to authorize after prereqs, seems better to auth first.
|
||||
try!(self.authorize(update));
|
||||
try!(self.verify_prerequisites(update.get_pre_requisites()));
|
||||
try!(self.pre_scan(update.get_updates()));
|
||||
|
||||
let serial: u32 = self.get_soa().map_or(0, |r| if let &RData::SOA (ref soa) = r.get_rdata() { soa.get_serial() } else { 0 });
|
||||
|
||||
// TODO at this point, we've accepted the updates, we should write
|
||||
// to a write-ahead-log (journal) and then commit to memory.
|
||||
if try!(self.update_records(update.get_updates(), serial)) {
|
||||
// need to resign any records at the current serial number and bump the number.
|
||||
// first bump the serial number on the SOA, so that it is resigned with the new serial.
|
||||
self.increment_soa_serial();
|
||||
self.sign_zone();
|
||||
}
|
||||
Ok(())
|
||||
self.update_records(update.get_updates())
|
||||
}
|
||||
|
||||
/// Looks up all Resource Records matching the giving `Name` and `RecordType`.
|
||||
@ -659,11 +683,12 @@ impl Authority {
|
||||
/// `name`. `RecordType::AXFR` will return all record types except `RecordType::SOA`
|
||||
/// due to the requirements that on zone transfers the `RecordType::SOA` must both
|
||||
/// preceed and follow all other records.
|
||||
/// * `is_secure` - If the DO bit is set on the EDNS OPT record, then return RRSIGs as well.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// None if there are no matching records, otherwise a `Vec` containing the found records.
|
||||
pub fn lookup(&self, name: &Name, rtype: RecordType) -> Option<Vec<&Record>> {
|
||||
pub fn lookup(&self, name: &Name, rtype: RecordType, is_secure: bool) -> Option<Vec<&Record>> {
|
||||
// on an SOA request always return the SOA, regardless of the name
|
||||
let name: &Name = if rtype == RecordType::SOA { &self.origin } else { name };
|
||||
let rr_key = RrKey::new(name, rtype);
|
||||
@ -674,12 +699,12 @@ impl Authority {
|
||||
self.records.values().filter(|rr_set| rtype == RecordType::ANY || rr_set.get_record_type() != RecordType::SOA)
|
||||
.filter(|rr_set| rtype == RecordType::AXFR || rr_set.get_name() == name)
|
||||
.fold(Vec::<&Record>::new(), |mut vec, rr_set| {
|
||||
vec.append(&mut rr_set.get_records().into_iter().collect());
|
||||
vec.append(&mut rr_set.get_records(is_secure));
|
||||
vec
|
||||
})
|
||||
},
|
||||
_ => {
|
||||
self.records.get(&rr_key).map_or(vec![], |rr_set| rr_set.get_records().into_iter().collect())
|
||||
self.records.get(&rr_key).map_or(vec![], |rr_set| rr_set.get_records(is_secure).into_iter().collect())
|
||||
}
|
||||
};
|
||||
|
||||
@ -695,13 +720,16 @@ impl Authority {
|
||||
/// record set serial is greater than or equal to the `serial`, then it will be
|
||||
/// resigned.
|
||||
pub fn sign_zone(&mut self) {
|
||||
debug!("signing zone: {}", self.origin);
|
||||
let now = UTC::now().timestamp() as u32;
|
||||
let zone_ttl = self.get_soa()
|
||||
let zone_ttl = self.get_soa(false)
|
||||
.map_or(0, |soa_rec| if let &RData::SOA(ref soa) = soa_rec.get_rdata() { soa.get_minimum() }
|
||||
else { panic!("SOA has no RDATA: {}", self.origin) } );
|
||||
|
||||
for rr_set in self.records.iter_mut().filter_map(|(_, rr_set)| { rr_set.get_rrsigs().is_empty();
|
||||
Some(rr_set) } ) {
|
||||
debug!("signing rr_set: {}", rr_set.get_name());
|
||||
rr_set.clear_rrsigs();
|
||||
let rrsig_temp = Record::with(rr_set.get_name().clone(), RecordType::RRSIG, zone_ttl);
|
||||
|
||||
for signer in self.secure_keys.iter() {
|
||||
@ -715,7 +743,9 @@ impl Authority {
|
||||
now,
|
||||
signer.calculate_key_tag(),
|
||||
signer.get_signer_name(),
|
||||
rr_set.get_records());
|
||||
// TODO: this is a nasty clone... the issue is that the vec
|
||||
// from get_records is of Vec<&R>, but we really want &[R]
|
||||
&rr_set.get_records(false).into_iter().cloned().collect::<Vec<Record>>());
|
||||
let signature = signer.sign(&hash);
|
||||
let mut rrsig = rrsig_temp.clone();
|
||||
rrsig.rdata(RData::SIG(SIG::new(
|
||||
@ -738,6 +768,9 @@ impl Authority {
|
||||
// sig: Vec<u8>
|
||||
signature,
|
||||
)));
|
||||
|
||||
rr_set.insert_rrsig(rrsig);
|
||||
debug!("signed rr_set: {}", rr_set.get_name());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -813,25 +846,25 @@ pub mod authority_tests {
|
||||
fn test_authority() {
|
||||
let authority: Authority = create_example();
|
||||
|
||||
assert!(authority.get_soa().is_some());
|
||||
assert_eq!(authority.get_soa().unwrap().get_dns_class(), DNSClass::IN);
|
||||
assert!(authority.get_soa(false).is_some());
|
||||
assert_eq!(authority.get_soa(false).unwrap().get_dns_class(), DNSClass::IN);
|
||||
|
||||
assert!(authority.lookup(authority.get_origin(), RecordType::NS).is_some());
|
||||
assert!(authority.lookup(authority.get_origin(), RecordType::NS, false).is_some());
|
||||
|
||||
let mut lookup: Vec<_> = authority.get_ns().unwrap();
|
||||
let mut lookup: Vec<_> = authority.get_ns(false).unwrap();
|
||||
lookup.sort();
|
||||
|
||||
assert_eq!(**lookup.first().unwrap(), Record::new().name(authority.get_origin().clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()) ).clone());
|
||||
assert_eq!(**lookup.last().unwrap(), Record::new().name(authority.get_origin().clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()) ).clone());
|
||||
|
||||
assert!(authority.lookup(authority.get_origin(), RecordType::TXT).is_some());
|
||||
assert!(authority.lookup(authority.get_origin(), RecordType::TXT, false).is_some());
|
||||
|
||||
let mut lookup: Vec<_> = authority.lookup(authority.get_origin(), RecordType::TXT).unwrap();
|
||||
let mut lookup: Vec<_> = authority.lookup(authority.get_origin(), RecordType::TXT, false).unwrap();
|
||||
lookup.sort();
|
||||
|
||||
assert_eq!(**lookup.first().unwrap(), Record::new().name(authority.get_origin().clone()).ttl(60).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT(TXT::new(vec!["$Id: example.com 4415 2015-08-24 20:12:23Z davids $".to_string()]))).clone());
|
||||
|
||||
assert_eq!(**authority.lookup(authority.get_origin(), RecordType::A).unwrap().first().unwrap(), Record::new().name(authority.get_origin().clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,34))).clone());
|
||||
assert_eq!(**authority.lookup(authority.get_origin(), RecordType::A, false).unwrap().first().unwrap(), Record::new().name(authority.get_origin().clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,34))).clone());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -923,6 +956,8 @@ pub mod authority_tests {
|
||||
let new_name = Name::new().label("new").label("example").label("com");
|
||||
let www_name = Name::new().label("www").label("example").label("com");
|
||||
let mut authority: Authority = create_example();
|
||||
let serial = authority.get_serial();
|
||||
|
||||
authority.set_allow_update(true);
|
||||
|
||||
let mut original_vec: Vec<Record> = vec![
|
||||
@ -935,26 +970,28 @@ pub mod authority_tests {
|
||||
|
||||
{
|
||||
// assert that the correct set of records is there.
|
||||
let mut www_rrset: Vec<&Record> = authority.lookup(&www_name, RecordType::ANY).unwrap();
|
||||
let mut www_rrset: Vec<&Record> = authority.lookup(&www_name, RecordType::ANY, false).unwrap();
|
||||
www_rrset.sort();
|
||||
|
||||
assert_eq!(www_rrset, original_vec.iter().collect::<Vec<&Record>>());
|
||||
|
||||
// assert new record doesn't exist
|
||||
assert!(authority.lookup(&new_name, RecordType::ANY).is_none());
|
||||
assert!(authority.lookup(&new_name, RecordType::ANY, false).is_none());
|
||||
}
|
||||
|
||||
//
|
||||
// zone rrset rr Add to an RRset
|
||||
let add_record = &[Record::new().name(new_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone()];
|
||||
assert!(authority.update_records(add_record, 0).is_ok());
|
||||
assert_eq!(authority.lookup(&new_name, RecordType::ANY).unwrap_or(vec![]), add_record.iter().collect::<Vec<&Record>>());
|
||||
assert!(authority.update_records(add_record).expect("update failed"));
|
||||
assert_eq!(authority.lookup(&new_name, RecordType::ANY, false).unwrap_or(vec![]), add_record.iter().collect::<Vec<&Record>>());
|
||||
assert_eq!(serial + 1, authority.get_serial());
|
||||
|
||||
let add_www_record = &[Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(10,0,0,1))).clone()];
|
||||
assert!(authority.update_records(add_www_record, 0).is_ok());
|
||||
assert!(authority.update_records(add_www_record).expect("update failed"));
|
||||
assert_eq!(serial + 2, authority.get_serial());
|
||||
|
||||
{
|
||||
let mut www_rrset = authority.lookup(&www_name, RecordType::ANY).unwrap_or(vec![]);
|
||||
let mut www_rrset = authority.lookup(&www_name, RecordType::ANY, false).unwrap_or(vec![]);
|
||||
www_rrset.sort();
|
||||
|
||||
let mut plus_10 = original_vec.clone();
|
||||
@ -966,17 +1003,19 @@ pub mod authority_tests {
|
||||
//
|
||||
// NONE rrset rr Delete an RR from an RRset
|
||||
let del_record = &[Record::new().name(new_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::NONE).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone()];
|
||||
assert!(authority.update_records(del_record, 0).is_ok());
|
||||
assert!(authority.update_records(del_record).expect("update failed"));
|
||||
assert_eq!(serial + 3, authority.get_serial());
|
||||
{
|
||||
println!("after delete of specific record: {:?}", authority.lookup(&new_name, RecordType::ANY));
|
||||
assert!(authority.lookup(&new_name, RecordType::ANY).is_none());
|
||||
println!("after delete of specific record: {:?}", authority.lookup(&new_name, RecordType::ANY, false));
|
||||
assert!(authority.lookup(&new_name, RecordType::ANY, false).is_none(), false);
|
||||
}
|
||||
|
||||
// remove one from www
|
||||
let del_record = &[Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::NONE).rdata(RData::A(Ipv4Addr::new(10,0,0,1))).clone()];
|
||||
assert!(authority.update_records(del_record, 0).is_ok());
|
||||
assert!(authority.update_records(del_record).expect("update failed"));
|
||||
assert_eq!(serial + 4, authority.get_serial());
|
||||
{
|
||||
let mut www_rrset = authority.lookup(&www_name, RecordType::ANY).unwrap_or(vec![]);
|
||||
let mut www_rrset = authority.lookup(&www_name, RecordType::ANY, false).unwrap_or(vec![]);
|
||||
www_rrset.sort();
|
||||
|
||||
assert_eq!(www_rrset, original_vec.iter().collect::<Vec<&Record>>());
|
||||
@ -985,7 +1024,8 @@ pub mod authority_tests {
|
||||
//
|
||||
// ANY rrset empty Delete an RRset
|
||||
let del_record = &[Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::ANY).rdata(RData::NULL(NULL::new())).clone()];
|
||||
assert!(authority.update_records(del_record, 0).is_ok());
|
||||
assert!(authority.update_records(del_record).expect("update failed"));
|
||||
assert_eq!(serial + 5, authority.get_serial());
|
||||
let mut removed_a_vec: Vec<_> = vec![
|
||||
Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT(TXT::new(vec!["v=spf1 -all".to_string()]))).clone(),
|
||||
Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA(Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946))).clone(),
|
||||
@ -993,7 +1033,7 @@ pub mod authority_tests {
|
||||
removed_a_vec.sort();
|
||||
|
||||
{
|
||||
let mut www_rrset = authority.lookup(&www_name, RecordType::ANY).unwrap_or(vec![]);
|
||||
let mut www_rrset = authority.lookup(&www_name, RecordType::ANY, false).unwrap_or(vec![]);
|
||||
www_rrset.sort();
|
||||
|
||||
assert_eq!(www_rrset, removed_a_vec.iter().collect::<Vec<&Record>>());
|
||||
@ -1003,7 +1043,38 @@ pub mod authority_tests {
|
||||
// ANY ANY empty Delete all RRsets from a name
|
||||
println!("deleting all records");
|
||||
let del_record = &[Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::ANY).dns_class(DNSClass::ANY).rdata(RData::NULL(NULL::new())).clone()];
|
||||
assert!(authority.update_records(del_record, 0).is_ok());
|
||||
assert_eq!(authority.lookup(&www_name, RecordType::ANY), None);
|
||||
assert!(authority.update_records(del_record).expect("update failed"));
|
||||
assert_eq!(authority.lookup(&www_name, RecordType::ANY, false), None);
|
||||
assert_eq!(serial + 6, authority.get_serial());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_zone_signing() {
|
||||
use openssl::crypto::pkey::PKey;
|
||||
use ::rr::dnssec::{Algorithm, Signer};
|
||||
use ::rr::{RData};
|
||||
|
||||
let mut authority: Authority = create_example();
|
||||
let mut pkey = PKey::new();
|
||||
pkey.gen(512);
|
||||
let signer = Signer::new(Algorithm::RSASHA256, pkey, Name::root(), u32::max_value(), 0);
|
||||
|
||||
authority.add_secure_key(signer);
|
||||
authority.sign_zone();
|
||||
|
||||
let results = authority.lookup(&authority.get_origin(), RecordType::AXFR, true).expect("AXFR broken");
|
||||
|
||||
for record in results.iter() {
|
||||
if record.get_rr_type() == RecordType::RRSIG { continue }
|
||||
|
||||
// validate all records have associated RRSIGs after signing
|
||||
assert!(results.iter().any(|r| r.get_rr_type() == RecordType::RRSIG &&
|
||||
r.get_name() == record.get_name() &&
|
||||
if let &RData::SIG(ref rrsig) = r.get_rdata() {
|
||||
rrsig.get_type_covered() == record.get_rr_type()
|
||||
} else {
|
||||
false
|
||||
} ), "record type not covered: {:?}", record);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ impl Catalog {
|
||||
response.add_all_answers(&records);
|
||||
|
||||
// get the NS records
|
||||
let ns = authority.get_ns();
|
||||
let ns = authority.get_ns(false);
|
||||
if ns.is_none() { warn!("there are no NS records for: {:?}", authority.get_origin()); }
|
||||
else {
|
||||
response.add_all_name_servers(&ns.unwrap());
|
||||
@ -233,7 +233,7 @@ impl Catalog {
|
||||
// in the not found case it's standard to return the SOA in the authority section
|
||||
response.response_code(ResponseCode::NXDomain);
|
||||
|
||||
let soa = authority.get_soa();
|
||||
let soa = authority.get_soa(false);
|
||||
if soa.is_none() { warn!("there is no SOA record for: {:?}", authority.get_origin()); }
|
||||
else {
|
||||
response.add_name_server(soa.unwrap().clone());
|
||||
@ -266,10 +266,10 @@ impl Catalog {
|
||||
|
||||
// it would be better to stream this back, rather than packaging everything up in an array
|
||||
// though for UDP it would still need to be bundled
|
||||
let mut query_result: Option<Vec<_>> = authority.lookup(query.get_name(), record_type);
|
||||
let mut query_result: Option<Vec<_>> = authority.lookup(query.get_name(), record_type, false);
|
||||
|
||||
if RecordType::AXFR == record_type {
|
||||
if let Some(soa) = authority.get_soa() {
|
||||
if let Some(soa) = authority.get_soa(false) {
|
||||
let mut xfr: Vec<&Record> = query_result.unwrap_or(Vec::with_capacity(2));
|
||||
// TODO: probably make Records Rc or Arc, to remove the clone
|
||||
xfr.insert(0, soa);
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
use ::rr::{Name, Record, RecordType, RData};
|
||||
|
||||
|
||||
/// Set of resource records associated to a name and type
|
||||
#[derive(Debug)]
|
||||
pub struct RRSet {
|
||||
@ -69,8 +70,12 @@ impl RRSet {
|
||||
/// # Return value
|
||||
///
|
||||
/// Slice of all records in the set
|
||||
pub fn get_records(&self) -> &[Record] {
|
||||
&self.records
|
||||
pub fn get_records(&self, and_rrsigs: bool) -> Vec<&Record> {
|
||||
if and_rrsigs {
|
||||
self.records.iter().chain(self.rrsigs.iter()).collect()
|
||||
} else {
|
||||
self.records.iter().collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// # Return value
|
||||
@ -95,6 +100,10 @@ impl RRSet {
|
||||
self.rrsigs.push(rrsig)
|
||||
}
|
||||
|
||||
pub fn clear_rrsigs(&mut self) {
|
||||
self.rrsigs.clear()
|
||||
}
|
||||
|
||||
fn updated(&mut self, serial: u32) {
|
||||
self.serial = serial;
|
||||
self.rrsigs.clear(); // on updates, the rrsigs are invalid
|
||||
@ -275,20 +284,20 @@ mod test {
|
||||
let insert = Record::new().name(name.clone()).ttl(86400).rr_type(record_type).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,24))).clone();
|
||||
|
||||
assert!(rr_set.insert(insert.clone(), 0));
|
||||
assert_eq!(rr_set.get_records().len(), 1);
|
||||
assert!(rr_set.get_records().contains(&insert));
|
||||
assert_eq!(rr_set.get_records(false).len(), 1);
|
||||
assert!(rr_set.get_records(false).contains(&&insert));
|
||||
|
||||
// dups ignored
|
||||
assert!(!rr_set.insert(insert.clone(), 0));
|
||||
assert_eq!(rr_set.get_records().len(), 1);
|
||||
assert!(rr_set.get_records().contains(&insert));
|
||||
assert_eq!(rr_set.get_records(false).len(), 1);
|
||||
assert!(rr_set.get_records(false).contains(&&insert));
|
||||
|
||||
// add one
|
||||
let insert1 = Record::new().name(name.clone()).ttl(86400).rr_type(record_type).dns_class(DNSClass::IN).rdata(RData::A(Ipv4Addr::new(93,184,216,25))).clone();
|
||||
assert!(rr_set.insert(insert1.clone(), 0));
|
||||
assert_eq!(rr_set.get_records().len(), 2);
|
||||
assert!(rr_set.get_records().contains(&insert));
|
||||
assert!(rr_set.get_records().contains(&insert1));
|
||||
assert_eq!(rr_set.get_records(false).len(), 2);
|
||||
assert!(rr_set.get_records(false).contains(&&insert));
|
||||
assert!(rr_set.get_records(false).contains(&&insert1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -302,19 +311,19 @@ mod test {
|
||||
let new_serial = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.net.", None).unwrap(), Name::parse("noc.dns.icann.net.", None).unwrap(), 2015082404, 7200, 3600, 1209600, 3600 ))).clone();
|
||||
|
||||
assert!(rr_set.insert(insert.clone(), 0));
|
||||
assert!(rr_set.get_records().contains(&insert));
|
||||
assert!(rr_set.get_records(false).contains(&&insert));
|
||||
// same serial number
|
||||
assert!(!rr_set.insert(same_serial.clone(), 0));
|
||||
assert!(rr_set.get_records().contains(&insert));
|
||||
assert!(!rr_set.get_records().contains(&same_serial));
|
||||
assert!(rr_set.get_records(false).contains(&&insert));
|
||||
assert!(!rr_set.get_records(false).contains(&&same_serial));
|
||||
|
||||
assert!(rr_set.insert(new_serial.clone(), 0));
|
||||
assert!(!rr_set.insert(same_serial.clone(), 0));
|
||||
assert!(!rr_set.insert(insert.clone(), 0));
|
||||
|
||||
assert!(rr_set.get_records().contains(&new_serial));
|
||||
assert!(!rr_set.get_records().contains(&insert));
|
||||
assert!(!rr_set.get_records().contains(&same_serial));
|
||||
assert!(rr_set.get_records(false).contains(&&new_serial));
|
||||
assert!(!rr_set.get_records(false).contains(&&insert));
|
||||
assert!(!rr_set.get_records(false).contains(&&same_serial));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -330,12 +339,12 @@ mod test {
|
||||
let new_record = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::CNAME).dns_class(DNSClass::IN).rdata(RData::CNAME(new_cname.clone()) ).clone();
|
||||
|
||||
assert!(rr_set.insert(insert.clone(), 0));
|
||||
assert!(rr_set.get_records().contains(&insert));
|
||||
assert!(rr_set.get_records(false).contains(&&insert));
|
||||
|
||||
// update the record
|
||||
assert!(rr_set.insert(new_record.clone(), 0));
|
||||
assert!(!rr_set.get_records().contains(&insert));
|
||||
assert!(rr_set.get_records().contains(&new_record));
|
||||
assert!(!rr_set.get_records(false).contains(&&insert));
|
||||
assert!(rr_set.get_records(false).contains(&&new_record));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -366,7 +375,7 @@ mod test {
|
||||
|
||||
assert!(rr_set.insert(insert.clone(), 0));
|
||||
assert!(!rr_set.remove(&insert, 0));
|
||||
assert!(rr_set.get_records().contains(&insert));
|
||||
assert!(rr_set.get_records(false).contains(&&insert));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -46,7 +46,7 @@ VENERA A 10.1.0.52
|
||||
// not validating everything, just one of each...
|
||||
|
||||
// SOA
|
||||
let soa_record = authority.get_soa().unwrap();
|
||||
let soa_record = authority.get_soa(false).unwrap();
|
||||
assert_eq!(RecordType::SOA, soa_record.get_rr_type());
|
||||
assert_eq!(&Name::new().label("isi").label("edu"), soa_record.get_name()); // i.e. the origin or domain
|
||||
assert_eq!(3600000, soa_record.get_ttl());
|
||||
@ -65,7 +65,7 @@ VENERA A 10.1.0.52
|
||||
}
|
||||
|
||||
// NS
|
||||
let mut ns_records: Vec<&Record> = authority.lookup(&Name::with_labels(vec!["isi".into(),"edu".into()]), RecordType::NS).unwrap();
|
||||
let mut ns_records: Vec<&Record> = authority.lookup(&Name::with_labels(vec!["isi".into(),"edu".into()]), RecordType::NS, false).unwrap();
|
||||
let mut compare = vec![ // this is cool, zip up the expected results... works as long as the order is good.
|
||||
Name::new().label("a").label("isi").label("edu"),
|
||||
Name::new().label("venera").label("isi").label("edu"),
|
||||
@ -89,7 +89,7 @@ VENERA A 10.1.0.52
|
||||
}
|
||||
|
||||
// MX
|
||||
let mut mx_records: Vec<&Record> = authority.lookup(&Name::new().label("isi").label("edu"), RecordType::MX).unwrap();
|
||||
let mut mx_records: Vec<&Record> = authority.lookup(&Name::new().label("isi").label("edu"), RecordType::MX, false).unwrap();
|
||||
let mut compare = vec![
|
||||
(10, Name::new().label("venera").label("isi").label("edu")),
|
||||
(20, Name::new().label("vaxa").label("isi").label("edu")),
|
||||
@ -114,7 +114,7 @@ VENERA A 10.1.0.52
|
||||
}
|
||||
|
||||
// A
|
||||
let a_record: &Record = authority.lookup(&Name::new().label("a").label("isi").label("edu"), RecordType::A).unwrap().first().cloned().unwrap();
|
||||
let a_record: &Record = authority.lookup(&Name::new().label("a").label("isi").label("edu"), RecordType::A, false).unwrap().first().cloned().unwrap();
|
||||
assert_eq!(&Name::new().label("a").label("isi").label("edu"), a_record.get_name());
|
||||
assert_eq!(60, a_record.get_ttl()); // TODO: should this be minimum or expire?
|
||||
assert_eq!(DNSClass::IN, a_record.get_dns_class());
|
||||
@ -126,7 +126,7 @@ VENERA A 10.1.0.52
|
||||
}
|
||||
|
||||
// AAAA
|
||||
let aaaa_record: &Record = authority.lookup(&Name::new().label("aaaa").label("isi").label("edu"), RecordType::AAAA).unwrap().first().cloned().unwrap();
|
||||
let aaaa_record: &Record = authority.lookup(&Name::new().label("aaaa").label("isi").label("edu"), RecordType::AAAA, false).unwrap().first().cloned().unwrap();
|
||||
assert_eq!(&Name::new().label("aaaa").label("isi").label("edu"), aaaa_record.get_name());
|
||||
if let RData::AAAA(ref address) = *aaaa_record.get_rdata() {
|
||||
assert_eq!(&Ipv6Addr::from_str("4321:0:1:2:3:4:567:89ab").unwrap(), address);
|
||||
@ -135,7 +135,7 @@ VENERA A 10.1.0.52
|
||||
}
|
||||
|
||||
// SHORT
|
||||
let short_record: &Record = authority.lookup(&Name::new().label("short").label("isi").label("edu"), RecordType::A).unwrap().first().cloned().unwrap();
|
||||
let short_record: &Record = authority.lookup(&Name::new().label("short").label("isi").label("edu"), RecordType::A, false).unwrap().first().cloned().unwrap();
|
||||
assert_eq!(&Name::new().label("short").label("isi").label("edu"), short_record.get_name());
|
||||
assert_eq!(70, short_record.get_ttl());
|
||||
if let RData::A(ref address) = *short_record.get_rdata() {
|
||||
@ -145,7 +145,7 @@ VENERA A 10.1.0.52
|
||||
}
|
||||
|
||||
// TXT
|
||||
let mut txt_records: Vec<&Record> = authority.lookup(&Name::new().label("a").label("isi").label("edu"), RecordType::TXT).unwrap();
|
||||
let mut txt_records: Vec<&Record> = authority.lookup(&Name::new().label("a").label("isi").label("edu"), RecordType::TXT, false).unwrap();
|
||||
let compare = vec![
|
||||
vec!["I".to_string(), "am".to_string(), "a".to_string(), "txt".to_string(), "record".to_string()],
|
||||
vec!["I".to_string(), "am".to_string(), "another".to_string(), "txt".to_string(), "record".to_string()],
|
||||
@ -170,7 +170,7 @@ VENERA A 10.1.0.52
|
||||
}
|
||||
|
||||
// PTR
|
||||
let ptr_record: &Record = authority.lookup(&Name::new().label("103").label("0").label("3").label("26").label("in-addr").label("arpa"), RecordType::PTR).unwrap().first().cloned().unwrap();
|
||||
let ptr_record: &Record = authority.lookup(&Name::new().label("103").label("0").label("3").label("26").label("in-addr").label("arpa"), RecordType::PTR, false).unwrap().first().cloned().unwrap();
|
||||
if let RData::PTR( ref ptrdname ) = *ptr_record.get_rdata() {
|
||||
assert_eq!(&Name::new().label("a").label("isi").label("edu"), ptrdname);
|
||||
} else {
|
||||
@ -178,7 +178,7 @@ VENERA A 10.1.0.52
|
||||
}
|
||||
|
||||
// SRV
|
||||
let srv_record: &Record = authority.lookup(&Name::new().label("_ldap").label("_tcp").label("service").label("isi").label("edu"), RecordType::SRV).unwrap().first().cloned().unwrap();
|
||||
let srv_record: &Record = authority.lookup(&Name::new().label("_ldap").label("_tcp").label("service").label("isi").label("edu"), RecordType::SRV, false).unwrap().first().cloned().unwrap();
|
||||
if let RData::SRV(ref rdata) = *srv_record.get_rdata() {
|
||||
assert_eq!(rdata.get_priority(), 1);
|
||||
assert_eq!(rdata.get_weight(), 2);
|
||||
|
Loading…
Reference in New Issue
Block a user