cleanup in preparation for zone signing

This commit is contained in:
Benjamin Fry 2016-05-08 21:47:43 -07:00
parent ba0cb7d597
commit a331f249ed
22 changed files with 652 additions and 357 deletions

View File

@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Removed many excessive clones (should make requests even faster)
- Cleaned up authority upsert and lookup interfaces
- All authorities default to IN DNSCLASS now (none others currently supported)
- Cleaned up the Signer interface to support zone signing
## 0.5.3 2016-04-07
### Fixed

View File

@ -15,11 +15,13 @@
*/
use std::collections::HashMap;
use openssl::crypto::pkey::{PKey, Role};
use chrono::offset::utc::UTC;
use ::authority::{UpdateResult, ZoneType, RRSet};
use ::op::{UpdateMessage, ResponseCode};
use ::rr::*;
use ::rr::{DNSClass, Name, RData, Record, RecordType};
use ::rr::rdata::SIG;
use ::rr::dnssec::Signer;
#[derive(Eq, PartialEq, Debug, Hash, Clone)]
pub struct RrKey { name: Name, record_type: RecordType }
@ -42,7 +44,7 @@ pub struct Authority {
// server instance, but that requires requesting updates from the parent zone, which may or
// may not support dynamic updates to register the new key... Trust-DNS will provide support
// for this, in some form, perhaps alternate root zones...
secure_keys: Vec<PKey>,
secure_keys: Vec<Signer>,
}
/// Authority is responsible for storing the resource records for a particular zone.
@ -86,6 +88,25 @@ impl Authority {
self.lookup(&self.origin, RecordType::SOA).and_then(|v|v.first().map(|v| *v))
}
fn increment_soa_serial(&mut self) -> u32 {
let mut soa = if let Some(ref mut soa_record) = self.get_soa() {
soa_record.clone()
} else {
warn!("no soa record found for zone: {}", self.origin);
return 0;
};
let serial = if let &mut RData::SOA(ref mut soa_rdata) = soa.get_rdata_mut() {
soa_rdata.increment_serial();
soa_rdata.get_serial()
} else {
panic!("This was not an SOA record");
};
self.upsert(soa, serial);
return serial;
}
pub fn get_ns(&self) -> Option<Vec<&Record>> {
self.lookup(&self.origin, RecordType::NS)
}
@ -185,7 +206,7 @@ impl Authority {
match require.get_dns_class() {
DNSClass::ANY =>
if let &RData::NULL{ .. } = require.get_rdata() {
if let &RData::NULL( .. ) = require.get_rdata() {
match require.get_rr_type() {
// ANY ANY empty Name is in use
RecordType::ANY => {
@ -209,7 +230,7 @@ impl Authority {
}
,
DNSClass::NONE =>
if let &RData::NULL{ .. } = require.get_rdata() {
if let &RData::NULL( .. ) = require.get_rdata() {
match require.get_rr_type() {
// NONE ANY empty Name is not in use
RecordType::ANY => {
@ -360,7 +381,7 @@ impl Authority {
match class {
DNSClass::ANY => {
if rr.get_ttl() != 0 { return Err(ResponseCode::FormErr) }
if let &RData::NULL {..} = rr.get_rdata() { () }
if let &RData::NULL(..) = rr.get_rdata() { () }
else { return Err(ResponseCode::FormErr) }
match rr.get_rr_type() {
RecordType::AXFR | RecordType::IXFR => return Err(ResponseCode::FormErr),
@ -395,8 +416,10 @@ impl Authority {
/// ANY rrset empty Delete an RRset
/// NONE rrset rr Delete an RR from an RRset
/// zone rrset rr Add to an RRset
///
fn update_records(&mut self, records: &[Record]) -> UpdateResult<()> {
/// ```
fn update_records(&mut self, records: &[Record], serial: u32) -> UpdateResult<bool> {
let mut updated = false;
// 3.4.2.7 - Pseudocode For Update Section Processing
//
// [rr] for rr in updates
@ -454,7 +477,7 @@ impl Authority {
// zone rrset rr Add to an RRset
info!("upserting record: {:?}", rr);
self.upsert(rr.clone());
updated = self.upsert(rr.clone(), serial) || updated;
},
DNSClass::ANY => {
// This is a delete of entire RRSETs, either many or one. In either case, the spec is clear:
@ -480,6 +503,7 @@ impl Authority {
.collect::<Vec<RrKey>>();
for delete in to_delete {
self.records.remove(&delete);
updated = true;
}
},
_ => {
@ -489,7 +513,7 @@ impl Authority {
// SOA or NS RRs will be deleted.
// ANY rrset empty Delete an RRset
if let &RData::NULL { .. } = rr.get_rdata() {
if let &RData::NULL( .. ) = rr.get_rdata() {
let deleted = self.records.remove(&rr_key);
info!("deleted rrset: {:?}", deleted);
} else {
@ -504,9 +528,10 @@ impl Authority {
println!("deleting specific record: {:?}", rr);
// NONE rrset rr Delete an RR from an RRset
if let Some(rrset) = self.records.get_mut(&rr_key) {
let deleted = rrset.remove(rr);
let deleted = rrset.remove(rr, serial);
info!("deleted ({}) specific record: {:?}", deleted, rr);
println!("deleted ({}) specific record: {:?}", deleted, rr);
updated = updated || deleted;
}
},
class @ _ => {
@ -516,7 +541,28 @@ impl Authority {
}
}
Ok(())
Ok(updated)
}
/// Inserts or updates a `Record` depending on it's existence in the authority.
///
/// Guarantees that SOA, CNAME only has one record, will implicitly update if they already exist.
///
/// # Arguments
///
/// * `record` - The `Record` to be inserted or updated.
/// * `serial` - Current serial number to be recorded against updates.
///
/// # Return value
///
/// Ok() on success or Err() with the `ResponseCode` associated with the error.
pub fn upsert(&mut self, record: Record, serial: u32) -> bool {
assert_eq!(self.class, record.get_dns_class());
let rr_key = RrKey::new(record.get_name(), record.get_rr_type());
let records: &mut RRSet = self.records.entry(rr_key).or_insert(RRSet::new(record.get_name(), record.get_rr_type(), serial));
records.insert(record, serial)
}
/// Takes the UpdateMessage, extracts the Records, and applies the changes to the record set.
@ -580,33 +626,19 @@ impl Authority {
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.
try!(self.update_records(update.get_updates()));
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(())
}
/// Inserts or updates a `Record` depending on it's existence in the authority.
///
/// Guarantees that SOA, CNAME only has one record, will implicitly update if they already exist.
///
/// # Arguments
///
/// * `record` - The `Record` to be inserted or updated.
///
/// # Return value
///
/// Ok() on success or Err() with the `ResponseCode` associated with the error.
pub fn upsert(&mut self, record: Record) {
assert_eq!(self.class, record.get_dns_class());
let serial: u32 = self.get_soa().map_or(0, |r| if let &RData::SOA { serial, .. } = r.get_rdata() { serial } else { 0 });
let rr_key = RrKey::new(record.get_name(), record.get_rr_type());
let records: &mut RRSet = self.records.entry(rr_key).or_insert(RRSet::new(record.get_name(), record.get_rr_type(), serial));
records.insert(record, serial);
}
/// Looks up all Resource Records matching the giving `Name` and `RecordType`.
///
/// # Arguments
@ -643,6 +675,61 @@ impl Authority {
// TODO: change function signature to be non-optional
if result.is_empty() { None } else { Some(result) }
}
/// Signs any records in the zone that have serial numbers greater than or equal to `serial`
///
/// # Arguments
///
/// * `serial` - The serial to which the serial number of an `RRSet` should be compared. If the
/// record set serial is greater than or equal to the `serial`, then it will be
/// resigned.
pub fn sign_zone(&mut self) {
let now = UTC::now().timestamp() as u32;
let zone_ttl = self.get_soa()
.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) } ) {
let rrsig_temp = Record::with(rr_set.get_name().clone(), RecordType::RRSIG, zone_ttl);
for signer in self.secure_keys.iter() {
let hash = signer.hash_rrset(rr_set.get_name(),
self.class,
rr_set.get_name().num_labels(),
rr_set.get_record_type(),
signer.get_algorithm(),
rr_set.get_ttl(),
signer.get_expiration(),
now,
signer.calculate_key_tag(),
signer.get_signer_name(),
rr_set.get_records());
let signature = signer.sign(&hash);
let mut rrsig = rrsig_temp.clone();
rrsig.rdata(RData::SIG(SIG::new(
// type_covered: RecordType,
rr_set.get_record_type(),
// algorithm: Algorithm,
signer.get_algorithm(),
// num_labels: u8,
rr_set.get_name().num_labels(),
// original_ttl: u32,
rr_set.get_ttl(),
// sig_expiration: u32,
signer.get_expiration(),
// sig_inception: u32,
now,
// key_tag: u16,
signer.calculate_key_tag(),
// signer_name: Name,
signer.get_signer_name().clone(),
// sig: Vec<u8>
signature,
)));
}
}
}
}
#[cfg(test)]
@ -652,6 +739,8 @@ pub mod authority_tests {
use ::authority::ZoneType;
use ::rr::*;
use ::rr::rdata::NULL;
use ::rr::rdata::SOA;
use ::op::*;
use super::*;
@ -659,21 +748,21 @@ pub mod authority_tests {
let origin: Name = Name::parse("example.com.", None,).unwrap();
let mut records: Authority = Authority::new(origin.clone(), HashMap::new(), ZoneType::Master, false);
// example.com. 3600 IN SOA sns.dns.icann.org. noc.dns.icann.org. 2015082403 7200 3600 1209600 3600
records.upsert(Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA{ mname: Name::parse("sns.dns.icann.org.", None).unwrap(), rname: Name::parse("noc.dns.icann.org.", None).unwrap(), serial: 2015082403, refresh: 7200, retry: 3600, expire: 1209600, minimum: 3600 }).clone());
records.upsert(Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone(), 0);
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("a.iana-servers.net.", None).unwrap() }).clone());
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("b.iana-servers.net.", None).unwrap() }).clone());
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("a.iana-servers.net.", None).unwrap() }).clone(), 0);
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("b.iana-servers.net.", None).unwrap() }).clone(), 0);
// example.com. 60 IN TXT "v=spf1 -all"
//records.upsert(origin.clone(), Record::new().name(origin.clone()).ttl(60).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT{ txt_data: vec!["v=spf1 -all".to_string()] }).clone());
// example.com. 60 IN TXT "$Id: example.com 4415 2015-08-24 20:12:23Z davids $"
records.upsert(Record::new().name(origin.clone()).ttl(60).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT{ txt_data: vec!["$Id: example.com 4415 2015-08-24 20:12:23Z davids $".to_string()] }).clone());
records.upsert(Record::new().name(origin.clone()).ttl(60).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT{ txt_data: vec!["$Id: example.com 4415 2015-08-24 20:12:23Z davids $".to_string()] }).clone(), 0);
// example.com. 86400 IN A 93.184.216.34
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(93,184,216,34) }).clone());
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(93,184,216,34) }).clone(), 0);
// example.com. 86400 IN AAAA 2606:2800:220:1:248:1893:25c8:1946
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA{ address: Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946) }).clone());
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA{ address: Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946) }).clone(), 0);
// TODO support these later...
@ -693,13 +782,13 @@ pub mod authority_tests {
let www_name: Name = Name::parse("www.example.com.", None).unwrap();
// www.example.com. 86400 IN TXT "v=spf1 -all"
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT{ txt_data: vec!["v=spf1 -all".to_string()] }).clone());
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::TXT).dns_class(DNSClass::IN).rdata(RData::TXT{ txt_data: vec!["v=spf1 -all".to_string()] }).clone(), 0);
// www.example.com. 86400 IN A 93.184.216.34
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(93,184,216,34) }).clone());
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(93,184,216,34) }).clone(), 0);
// www.example.com. 86400 IN AAAA 2606:2800:220:1:248:1893:25c8:1946
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA{ address: Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946) }).clone());
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA{ address: Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946) }).clone(), 0);
// www.example.com. 3600 IN RRSIG NSEC 8 3 3600 20150925215757 20150905040848 54108 example.com. ZKIVt1IN3O1FWZPSfrQAH7nHt7RUFDjcbh7NxnEqd/uTGCnZ6SrAEgrY E9GMmBwvRjoucphGtjkYOpPJPe5MlnTHoYCjxL4qmG3LsD2KD0bfPufa ibtlQZRrPglxZ92hBKK3ZiPnPRe7I9yni2UQSQA7XDi7CQySYyo490It AxdXjAo=
// www.example.com. 3600 IN NSEC example.com. A TXT AAAA RRSIG NSEC
@ -758,24 +847,24 @@ pub mod authority_tests {
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().name(not_in_zone.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::NULL{ anything: vec![] }).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.verify_prerequisites(&[Record::new().name(not_zone.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::NULL{ anything: vec![] }).clone()]), Err(ResponseCode::NotZone));
assert_eq!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.verify_prerequisites(&[Record::new().name(not_zone.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::NotZone));
// * ANY ANY empty Name is in use
assert!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::ANY).rr_type(RecordType::ANY).rdata(RData::NULL{ anything: vec![] }).clone()]).is_ok());
assert_eq!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::ANY).rr_type(RecordType::ANY).rdata(RData::NULL{ anything: vec![] }).clone()]), Err(ResponseCode::NXDomain));
assert!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::ANY).rr_type(RecordType::ANY).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
assert_eq!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::ANY).rr_type(RecordType::ANY).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::NXDomain));
// * ANY rrset empty RRset exists (value independent)
assert!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::ANY).rr_type(RecordType::A).rdata(RData::NULL{ anything: vec![] } ).clone()]).is_ok());
assert_eq!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::ANY).rr_type(RecordType::A).rdata(RData::NULL{ anything: vec![] }).clone()]), Err(ResponseCode::NXRRSet));
assert!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::ANY).rr_type(RecordType::A).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
assert_eq!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::ANY).rr_type(RecordType::A).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::NXRRSet));
// * NONE ANY empty Name is not in use
assert!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::NONE).rr_type(RecordType::ANY).rdata(RData::NULL{ anything: vec![] }).clone()]).is_ok());
assert_eq!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::NONE).rr_type(RecordType::ANY).rdata(RData::NULL{ anything: vec![] }).clone()]), Err(ResponseCode::YXDomain));
assert!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::NONE).rr_type(RecordType::ANY).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
assert_eq!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::NONE).rr_type(RecordType::ANY).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::YXDomain));
// * NONE rrset empty RRset does not exist
assert!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::NONE).rr_type(RecordType::A).rdata(RData::NULL{ anything: vec![] }).clone()]).is_ok());
assert_eq!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::NONE).rr_type(RecordType::A).rdata(RData::NULL{ anything: vec![] }).clone()]), Err(ResponseCode::YXRRSet));
assert!(authority.verify_prerequisites(&[Record::new().name(not_in_zone.clone()).ttl(0).dns_class(DNSClass::NONE).rr_type(RecordType::A).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
assert_eq!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::NONE).rr_type(RecordType::A).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::YXRRSet));
// * zone rrset rr RRset exists (value dependent)
assert!(authority.verify_prerequisites(&[Record::new().name(authority.get_origin().clone()).ttl(0).dns_class(DNSClass::IN).rr_type(RecordType::A).rdata(RData::A{ address: Ipv4Addr::new(93,184,216,34) }).clone()]).is_ok());
@ -796,27 +885,27 @@ pub mod authority_tests {
assert_eq!(authority.pre_scan(&[Record::new().name(not_zone.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(93,184,216,24) }).clone()]), Err(ResponseCode::NotZone));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::ANY).dns_class(DNSClass::IN).rdata(RData::NULL{ anything: vec![]} ).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::AXFR).dns_class(DNSClass::IN).rdata(RData::NULL{ anything: vec![]} ).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::IXFR).dns_class(DNSClass::IN).rdata(RData::NULL{ anything: vec![]} ).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::ANY).dns_class(DNSClass::IN).rdata(RData::NULL(NULL::new()) ).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::AXFR).dns_class(DNSClass::IN).rdata(RData::NULL(NULL::new()) ).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::IXFR).dns_class(DNSClass::IN).rdata(RData::NULL(NULL::new()) ).clone()]), Err(ResponseCode::FormErr));
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(93,184,216,24) }).clone()]).is_ok());
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::NULL{ anything: vec![] }).clone()]).is_ok());
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::ANY).rdata(RData::A{ address: Ipv4Addr::new(93,184,216,24) }).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::ANY).rdata(RData::A{ address: Ipv4Addr::new(93,184,216,24) }).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::AXFR).dns_class(DNSClass::ANY).rdata(RData::NULL{ anything: vec![]}).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::IXFR).dns_class(DNSClass::ANY).rdata(RData::NULL{ anything: vec![]}).clone()]), Err(ResponseCode::FormErr));
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::ANY).dns_class(DNSClass::ANY).rdata(RData::NULL{ anything: vec![]}).clone()]).is_ok());
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::ANY).rdata(RData::NULL{ anything: vec![]}).clone()]).is_ok());
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::AXFR).dns_class(DNSClass::ANY).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::IXFR).dns_class(DNSClass::ANY).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::ANY).dns_class(DNSClass::ANY).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::ANY).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::NONE).rdata(RData::NULL{ anything: vec![]}).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::ANY).dns_class(DNSClass::NONE).rdata(RData::NULL{ anything: vec![]}).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::AXFR).dns_class(DNSClass::NONE).rdata(RData::NULL{ anything: vec![]}).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::IXFR).dns_class(DNSClass::NONE).rdata(RData::NULL{ anything: vec![]}).clone()]), Err(ResponseCode::FormErr));
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::NONE).rdata(RData::NULL{ anything: vec![]}).clone()]).is_ok());
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::NONE).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::ANY).dns_class(DNSClass::NONE).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::AXFR).dns_class(DNSClass::NONE).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::IXFR).dns_class(DNSClass::NONE).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::NONE).rdata(RData::NULL(NULL::new())).clone()]).is_ok());
assert!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(0).rr_type(RecordType::A).dns_class(DNSClass::NONE).rdata(RData::A{ address: Ipv4Addr::new(93,184,216,24) }).clone()]).is_ok());
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::CH).rdata(RData::NULL{ anything: vec![]}).clone()]), Err(ResponseCode::FormErr));
assert_eq!(authority.pre_scan(&[Record::new().name(up_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::CH).rdata(RData::NULL(NULL::new())).clone()]), Err(ResponseCode::FormErr));
}
#[test]
@ -848,11 +937,11 @@ pub mod authority_tests {
//
// 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{ address: Ipv4Addr::new(93,184,216,24) }).clone()];
assert!(authority.update_records(add_record).is_ok());
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>>());
let add_www_record = &[Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(10,0,0,1) }).clone()];
assert!(authority.update_records(add_www_record).is_ok());
assert!(authority.update_records(add_www_record, 0).is_ok());
{
let mut www_rrset = authority.lookup(&www_name, RecordType::ANY).unwrap_or(vec![]);
@ -867,7 +956,7 @@ 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{ address: Ipv4Addr::new(93,184,216,24) }).clone()];
assert!(authority.update_records(del_record).is_ok());
assert!(authority.update_records(del_record, 0).is_ok());
{
println!("after delete of specific record: {:?}", authority.lookup(&new_name, RecordType::ANY));
assert!(authority.lookup(&new_name, RecordType::ANY).is_none());
@ -875,7 +964,7 @@ pub mod authority_tests {
// 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{ address: Ipv4Addr::new(10,0,0,1) }).clone()];
assert!(authority.update_records(del_record).is_ok());
assert!(authority.update_records(del_record, 0).is_ok());
{
let mut www_rrset = authority.lookup(&www_name, RecordType::ANY).unwrap_or(vec![]);
www_rrset.sort();
@ -885,8 +974,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{ anything: vec![] }).clone()];
assert!(authority.update_records(del_record).is_ok());
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());
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_data: 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{ address: Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946) }).clone(),
@ -903,8 +992,8 @@ 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{ anything: vec![] }).clone()];
assert!(authority.update_records(del_record).is_ok());
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);
}
}

View File

@ -304,6 +304,7 @@ mod catalog_tests {
use ::authority::authority_tests::create_example;
use super::*;
use ::rr::*;
use ::rr::rdata::SOA;
use ::op::*;
use std::net::*;
@ -345,17 +346,17 @@ mod catalog_tests {
pub fn create_test() -> Authority {
let origin: Name = Name::parse("test.com.", None).unwrap();
let mut records: Authority = Authority::new(origin.clone(), HashMap::new(), ZoneType::Master, false);
records.upsert(Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA{ mname: Name::parse("sns.dns.icann.org.", None).unwrap(), rname: Name::parse("noc.dns.icann.org.", None).unwrap(), serial: 2015082403, refresh: 7200, retry: 3600, expire: 1209600, minimum: 3600 }).clone());
records.upsert(Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone(), 0);
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("a.iana-servers.net.", None).unwrap() }).clone());
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("b.iana-servers.net.", None).unwrap() }).clone());
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("a.iana-servers.net.", None).unwrap() }).clone(), 0);
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("b.iana-servers.net.", None).unwrap() }).clone(), 0);
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(94,184,216,34) }).clone());
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA{ address: Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946) }).clone());
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(94,184,216,34) }).clone(), 0);
records.upsert(Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA{ address: Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946) }).clone(), 0);
let www_name: Name = Name::parse("www.test.com.", None).unwrap();
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(94,184,216,34) }).clone());
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA{ address: Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946) }).clone());
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(94,184,216,34) }).clone(), 0);
records.upsert(Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA{ address: Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946) }).clone(), 0);
records
}
@ -440,14 +441,14 @@ mod catalog_tests {
assert_eq!(ns.len(), 1);
assert_eq!(ns.first().unwrap().get_rr_type(), RecordType::SOA);
assert_eq!(ns.first().unwrap().get_rdata(), &RData::SOA{ mname: Name::parse("sns.dns.icann.org.", None).unwrap(), rname: Name::parse("noc.dns.icann.org.", None).unwrap(), serial: 2015082403, refresh: 7200, retry: 3600, expire: 1209600, minimum: 3600 });
assert_eq!(ns.first().unwrap().get_rdata(), &RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 )));
}
#[test]
fn test_axfr() {
let test = create_test();
let origin = test.get_origin().clone();
let soa = Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA{ mname: Name::parse("sns.dns.icann.org.", None).unwrap(), rname: Name::parse("noc.dns.icann.org.", None).unwrap(), serial: 2015082403, refresh: 7200, retry: 3600, expire: 1209600, minimum: 3600 }).clone();
let soa = Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone();
let mut catalog: Catalog = Catalog::new();
catalog.upsert(origin.clone(), test);
@ -469,14 +470,14 @@ mod catalog_tests {
let www_name: Name = Name::parse("www.test.com.", None).unwrap();
let mut expected_set = vec![
Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA{ mname: Name::parse("sns.dns.icann.org.", None).unwrap(), rname: Name::parse("noc.dns.icann.org.", None).unwrap(), serial: 2015082403, refresh: 7200, retry: 3600, expire: 1209600, minimum: 3600 }).clone(),
Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone(),
Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("a.iana-servers.net.", None).unwrap() }).clone(),
Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("b.iana-servers.net.", None).unwrap() }).clone(),
Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(94,184,216,34) }).clone(),
Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA{ address: Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946) }).clone(),
Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::A).dns_class(DNSClass::IN).rdata(RData::A{ address: Ipv4Addr::new(94,184,216,34) }).clone(),
Record::new().name(www_name.clone()).ttl(86400).rr_type(RecordType::AAAA).dns_class(DNSClass::IN).rdata(RData::AAAA{ address: Ipv6Addr::new(0x2606,0x2800,0x220,0x1,0x248,0x1893,0x25c8,0x1946) }).clone(),
Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA{ mname: Name::parse("sns.dns.icann.org.", None).unwrap(), rname: Name::parse("noc.dns.icann.org.", None).unwrap(), serial: 2015082403, refresh: 7200, retry: 3600, expire: 1209600, minimum: 3600 }).clone(),
Record::new().name(origin.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA(SOA::new(Name::parse("sns.dns.icann.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone(),
];
expected_set.sort();

View File

@ -19,8 +19,9 @@ use ::rr::{Name, Record, RecordType, RData};
pub struct RRSet {
name: Name,
record_type: RecordType,
ttl: u32,
records: Vec<Record>,
rrsig: Option<Record>,
rrsigs: Vec<Record>,
serial: u32, // serial number at which this record was modified
}
@ -39,7 +40,7 @@ impl RRSet {
///
/// The newly created Resource Record Set
pub fn new(name: &Name, record_type: RecordType, serial: u32) -> RRSet {
RRSet{name: name.clone(), record_type: record_type, records: Vec::new(), rrsig: None, serial: serial}
RRSet{name: name.clone(), record_type: record_type, ttl: 0, records: Vec::new(), rrsigs: Vec::new(), serial: serial}
}
/// # Return value
@ -56,6 +57,14 @@ impl RRSet {
self.record_type
}
/// # Return value
///
/// TTL, time-to-live, of the Resource Record Set, this is the maximum length of time that an
/// RRSet should be cached.
pub fn get_ttl(&self) -> u32 {
self.ttl
}
/// # Return value
///
/// Slice of all records in the set
@ -70,8 +79,31 @@ impl RRSet {
self.records.is_empty()
}
/// # Return value
///
/// The serial number at which the record was updated.
pub fn get_serial(&self) -> u32 {
self.serial
}
pub fn get_rrsigs(&self) -> &[Record] {
&self.rrsigs
}
pub fn insert_rrsig(&mut self, rrsig: Record) {
self.rrsigs.push(rrsig)
}
fn updated(&mut self, serial: u32) {
self.serial = serial;
self.rrsigs.clear(); // on updates, the rrsigs are invalid
}
/// Inserts a new Resource Record into the Set.
///
/// If the record is inserted, the ttl for the most recent record will be used for the ttl of
/// the entire resource record set.
///
/// This abides by the following restrictions in RFC 2136, April 1997:
///
/// ```text
@ -116,10 +148,10 @@ impl RRSet {
if let Some(soa_record) = self.records.iter().next() {
match soa_record.get_rdata() {
&RData::SOA{ serial: existing_serial, .. } => {
if let &RData::SOA{ serial: new_serial, ..} = record.get_rdata() {
if new_serial <= existing_serial {
info!("update ignored serial out of data: {} <= {}", new_serial, existing_serial);
&RData::SOA(ref existing_soa) => {
if let &RData::SOA(ref new_soa) = record.get_rdata() {
if new_soa.get_serial() <= existing_soa.get_serial() {
info!("update ignored serial out of data: {:?} <= {:?}", new_soa, existing_soa);
return false;
}
} else {
@ -161,10 +193,14 @@ impl RRSet {
// TODO: this shouldn't really need a clone since there should only be one...
self.records.push(record.clone());
self.records.swap_remove(i);
self.ttl = record.get_ttl();
self.updated(serial);
replaced = true;
}
if !replaced {
self.ttl = record.get_ttl();
self.updated(serial);
self.records.push(record);
true
} else {
@ -172,7 +208,20 @@ impl RRSet {
}
}
pub fn remove(&mut self, record: &Record) -> bool {
/// Removes the Resource Record if it exists.
///
/// # Arguments
///
/// * `record` - `Record` asserts that the `name` and `record_type` match the `RRSet`. Removes
/// any `record` if the record data, `RData`, match.
/// * `serial` - current serial number of the `SOA` record, this is to be used for `IXFR` and
/// signing for DNSSec after updates. The serial will only be updated if the
/// record was added.
///
/// # Return value
///
/// True if a record was removed.
pub fn remove(&mut self, record: &Record, serial: u32) -> bool {
assert_eq!(record.get_name(), &self.name);
assert!(record.get_rr_type() == self.record_type || record.get_rr_type() == RecordType::ANY);
@ -202,6 +251,7 @@ impl RRSet {
for i in to_remove {
self.records.remove(i);
removed = true;
self.updated(serial);
}
removed
@ -212,6 +262,7 @@ impl RRSet {
mod test {
use std::net::Ipv4Addr;
use ::rr::*;
use ::rr::rdata::SOA;
use super::RRSet;
#[test]
@ -245,9 +296,9 @@ mod test {
let record_type = RecordType::SOA;
let mut rr_set = RRSet::new(&name, record_type, 0);
let insert = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA{ mname: Name::parse("sns.dns.icann.org.", None).unwrap(), rname: Name::parse("noc.dns.icann.org.", None).unwrap(), serial: 2015082403, refresh: 7200, retry: 3600, expire: 1209600, minimum: 3600 }).clone();
let same_serial = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA{ mname: Name::parse("sns.dns.icann.net.", None).unwrap(), rname: Name::parse("noc.dns.icann.net.", None).unwrap(), serial: 2015082403, refresh: 7200, retry: 3600, expire: 1209600, minimum: 3600 }).clone();
let new_serial = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA{ mname: Name::parse("sns.dns.icann.net.", None).unwrap(), rname: Name::parse("noc.dns.icann.net.", None).unwrap(), serial: 2015082404, refresh: 7200, retry: 3600, expire: 1209600, minimum: 3600 }).clone();
let insert = 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.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone();
let same_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(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone();
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));
@ -298,10 +349,10 @@ mod test {
assert!(rr_set.insert(insert.clone(), 0));
assert!(rr_set.insert(insert1.clone(), 0));
assert!(rr_set.remove(&insert));
assert!(!rr_set.remove(&insert));
assert!(rr_set.remove(&insert1));
assert!(!rr_set.remove(&insert1));
assert!(rr_set.remove(&insert, 0));
assert!(!rr_set.remove(&insert, 0));
assert!(rr_set.remove(&insert1, 0));
assert!(!rr_set.remove(&insert1, 0));
}
#[test]
@ -310,10 +361,10 @@ mod test {
let record_type = RecordType::SOA;
let mut rr_set = RRSet::new(&name, record_type, 0);
let insert = Record::new().name(name.clone()).ttl(3600).rr_type(RecordType::SOA).dns_class(DNSClass::IN).rdata(RData::SOA{ mname: Name::parse("sns.dns.icann.org.", None).unwrap(), rname: Name::parse("noc.dns.icann.org.", None).unwrap(), serial: 2015082403, refresh: 7200, retry: 3600, expire: 1209600, minimum: 3600 }).clone();
let insert = 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.org.", None).unwrap(), Name::parse("noc.dns.icann.org.", None).unwrap(), 2015082403, 7200, 3600, 1209600, 3600 ))).clone();
assert!(rr_set.insert(insert.clone(), 0));
assert!(!rr_set.remove(&insert));
assert!(!rr_set.remove(&insert, 0));
assert!(rr_set.get_records().contains(&insert));
}
@ -330,13 +381,13 @@ mod test {
assert!(rr_set.insert(ns2.clone(), 0));
// ok to remove one, but not two...
assert!(rr_set.remove(&ns1));
assert!(!rr_set.remove(&ns2));
assert!(rr_set.remove(&ns1, 0));
assert!(!rr_set.remove(&ns2, 0));
// check that we can swap which ones are removed
assert!(rr_set.insert(ns1.clone(), 0));
assert!(rr_set.remove(&ns2));
assert!(!rr_set.remove(&ns1));
assert!(rr_set.remove(&ns2, 0));
assert!(!rr_set.remove(&ns1, 0));
}
}

View File

@ -146,9 +146,10 @@ impl<C: ClientConnection> Client<C> {
// standard rrsig verification
for rrsig in rrsigs.iter().filter(|rr| rr.get_name() == name) {
if let &RData::SIG{ref sig, ref signer_name, algorithm: sig_alg, ..} = rrsig.get_rdata() {
// TODO: need to verify inception and experation...
if let &RData::SIG(ref sig) = rrsig.get_rdata() {
// get DNSKEY from signer_name
let key_response = try!(self.inner_query(&signer_name, query_class, RecordType::DNSKEY, true));
let key_response = try!(self.inner_query(sig.get_signer_name(), query_class, RecordType::DNSKEY, true));
let key_rrset: Vec<&Record> = key_response.get_answers().iter().filter(|rr| rr.get_rr_type() == RecordType::DNSKEY).collect();
let key_rrsigs: Vec<&Record> = key_response.get_answers().iter().filter(|rr| rr.get_rr_type() == RecordType::RRSIG).collect();
@ -156,23 +157,23 @@ impl<C: ClientConnection> Client<C> {
if let &RData::DNSKEY{zone_key, algorithm, revoke, ref public_key, ..} = dnskey.get_rdata() {
if revoke { debug!("revoked: {}", dnskey.get_name()); continue } // TODO: does this need to be validated? RFC 5011
if !zone_key { continue }
if algorithm != sig_alg { continue }
if algorithm != sig.get_algorithm() { continue }
let pkey = try!(algorithm.public_key_from_vec(public_key));
if !pkey.can(Role::Verify) { debug!("pkey can't verify, {:?}", dnskey.get_name()); continue }
let signer: Signer = Signer::new(algorithm, pkey, signer_name.clone());
let rrset_hash: Vec<u8> = signer.hash_rrset(rrsig, &rrset);
let signer: Signer = Signer::new_verifier(algorithm, pkey, sig.get_signer_name().clone());
let rrset_hash: Vec<u8> = signer.hash_rrset_with_rrsig(rrsig, &rrset);
if signer.verify(&rrset_hash, sig) {
if signer_name == name && query_type == RecordType::DNSKEY {
if signer.verify(&rrset_hash, sig.get_sig()) {
if sig.get_signer_name() == name && query_type == RecordType::DNSKEY {
// this is self signed... let's skip to DS validation
let mut proof: Vec<Record> = try!(self.verify_dnskey(dnskey));
// TODO: this is verified, cache it
proof.push((*dnskey).clone());
return Ok(proof);
} else {
let mut proof = try!(self.recursive_query_verify(&signer_name, key_rrset.clone(), key_rrsigs, RecordType::DNSKEY, query_class));
let mut proof = try!(self.recursive_query_verify(sig.get_signer_name(), key_rrset.clone(), key_rrsigs, RecordType::DNSKEY, query_class));
// TODO: this is verified, cache it
proof.push((*dnskey).clone());
return Ok(proof);

View File

@ -70,7 +70,7 @@ struct Args {
pub fn main() {
// read any command line options
let args: Args = Docopt::new(USAGE)
.and_then(|d| d.help(true).version(Some(trust_dns::version().into())).decode())
.and_then(|d| d.help(true).version(Some(version().into())).decode())
.unwrap_or_else(|e| e.exit());
// TODO, this should be set after loading config, but it's necessary for initial log lines, no?

View File

@ -64,7 +64,7 @@ impl<'a> From<&'a Record> for Edns {
// change to this match
match value.get_rdata() {
&RData::NULL{ .. } => {
&RData::NULL( .. ) => {
// NULL, there was no data in the OPT
},
&RData::OPT{ ref option_rdata } => {

View File

@ -19,6 +19,7 @@ use super::{MessageType, Header, Query, Edns, OpCode, ResponseCode};
use ::rr::resource::Record;
use ::rr::domain::Name;
use ::rr::{RData, RecordType, DNSClass};
use ::rr::rdata::SIG;
use ::serialize::binary::*;
use ::error::*;
use ::rr::dnssec::Signer;
@ -282,24 +283,24 @@ impl UpdateMessage for Message {
let expiration_time: u32 = inception_time + (5 * 60); // +5 minutes in seconds
sig0.rdata(
RData::SIG {
RData::SIG(SIG::new(
// type covered in SIG(0) is 0 which is what makes this SIG0 vs a standard SIG
type_covered: RecordType::NULL,
algorithm: signer.get_algorithm(),
num_labels: num_labels,
RecordType::NULL,
signer.get_algorithm(),
num_labels,
// see above, original_ttl is meaningless, The TTL fields SHOULD be zero
original_ttl: 0,
0,
// recommended time is +5 minutes from now, to prevent timing attacks, 2 is probably good
sig_expiration: expiration_time,
expiration_time,
// current time, this should be UTC
// unsigned numbers of seconds since the start of 1 January 1970, GMT
sig_inception: inception_time,
key_tag: key_tag,
inception_time,
key_tag,
// can probably get rid of this clone if the owndership is correct
signer_name: signer.get_signer_name().clone(),
sig: signature,
}
);
signer.get_signer_name().clone(),
signature,
)
));
}
}

View File

@ -17,7 +17,7 @@ use openssl::crypto::pkey::{PKey, Role};
use ::op::Message;
use ::rr::dnssec::{Algorithm, DigestType};
use ::rr::{DNSClass, Name, Record, RData};
use ::rr::{DNSClass, Name, Record, RecordType, RData};
use ::serialize::binary::{BinEncoder, BinSerializable};
use ::rr::rdata::sig;
@ -25,15 +25,23 @@ pub struct Signer {
algorithm: Algorithm,
pkey: PKey,
signer_name: Name,
expiration: u32,
inception: u32,
}
impl Signer {
pub fn new(algorithm: Algorithm, pkey: PKey, signer_name: Name) -> Self {
Signer{ algorithm: algorithm, pkey: pkey, signer_name: signer_name }
pub fn new_verifier(algorithm: Algorithm, pkey: PKey, signer_name: Name) -> Self {
Signer{ algorithm: algorithm, pkey: pkey, signer_name: signer_name, expiration: 0, inception: 0 }
}
pub fn new(algorithm: Algorithm, pkey: PKey, signer_name: Name, expiration: u32, inception: u32) -> Self {
Signer{ algorithm: algorithm, pkey: pkey, signer_name: signer_name, expiration: expiration, inception: inception }
}
pub fn get_algorithm(&self) -> Algorithm { self.algorithm }
pub fn get_signer_name(&self) -> &Name { &self.signer_name }
pub fn get_expiration(&self) -> u32 { self.expiration }
pub fn get_inception(&self) -> u32 { self.inception }
// RFC 2535 DNS Security Extensions March 1999
//
@ -359,83 +367,92 @@ impl Signer {
// NSEC RRs needed to authenticate the response (see Section 3.1.3).
//
/// name is the the name of the records in the rrset
pub fn hash_rrset(&self, rrsig: &Record, records: &[Record]) -> Vec<u8> {
let name: &Name = rrsig.get_name();
let dns_class: DNSClass = rrsig.get_dns_class();
let rrsig_rdata: &RData = rrsig.get_rdata();
pub fn hash_rrset(&self, name: &Name, dns_class: DNSClass, num_labels: u8,
type_covered: RecordType, algorithm: Algorithm, original_ttl: u32,
sig_expiration: u32, sig_inception: u32, key_tag: u16, signer_name: &Name,
records: &[Record]) -> Vec<u8> {
// TODO: change this to a BTreeSet so that it's preordered, no sort necessary
let mut rrset: Vec<&Record> = Vec::new();
if let &RData::SIG { num_labels, type_covered, original_ttl, .. } = rrsig_rdata {
// TODO: change this to a BTreeSet so that it's preordered, no sort necessary
let mut rrset: Vec<&Record> = Vec::new();
// collect only the records for this rrset
for record in records {
if dns_class == record.get_dns_class() &&
type_covered == record.get_rr_type() &&
name == record.get_name() {
rrset.push(record);
}
// collect only the records for this rrset
for record in records {
if dns_class == record.get_dns_class() &&
type_covered == record.get_rr_type() &&
name == record.get_name() {
rrset.push(record);
}
}
// put records in canonical order
rrset.sort();
// put records in canonical order
rrset.sort();
let name: Name = if let Some(name) = Self::determine_name(name, num_labels) {
name
} else {
// TODO: this should be an error
return vec![]
};
let mut buf: Vec<u8> = Vec::new();
{
let mut encoder: BinEncoder = BinEncoder::new(&mut buf);
encoder.set_canonical_names(true);
// signed_data = RRSIG_RDATA | RR(1) | RR(2)... where
//
// "|" denotes concatenation
//
// RRSIG_RDATA is the wire format of the RRSIG RDATA fields
// with the Signature field excluded and the Signer's Name
// in canonical form.
assert!(sig::emit_pre_sig(&mut encoder, rrsig_rdata).is_ok());
// construct the rrset signing data
for record in rrset {
// RR(i) = name | type | class | OrigTTL | RDATA length | RDATA
//
// name is calculated according to the function in the RFC 4035
assert!(name.emit_as_canonical(&mut encoder, true).is_ok());
//
// type is the RRset type and all RRs in the class
assert!(type_covered.emit(&mut encoder).is_ok());
//
// class is the RRset's class
assert!(dns_class.emit(&mut encoder).is_ok());
//
// OrigTTL is the value from the RRSIG Original TTL field
assert!(encoder.emit_u32(original_ttl).is_ok());
//
// RDATA length
// TODO: add support to the encoder to set a marker to go back and write the length
let mut rdata_buf = Vec::new();
{
let mut rdata_encoder = BinEncoder::new(&mut rdata_buf);
rdata_encoder.set_canonical_names(true);
assert!(record.get_rdata().emit(&mut rdata_encoder).is_ok());
}
assert!(encoder.emit_u16(rdata_buf.len() as u16).is_ok());
//
// All names in the RDATA field are in canonical form (set above)
assert!(encoder.emit_vec(&rdata_buf).is_ok());
}
}
DigestType::from(self.algorithm).hash(&buf)
let name: Name = if let Some(name) = Self::determine_name(name, num_labels) {
name
} else {
// TODO: this should be an error
warn!("could not determine name from {}", name);
return vec![]
};
let mut buf: Vec<u8> = Vec::new();
{
let mut encoder: BinEncoder = BinEncoder::new(&mut buf);
encoder.set_canonical_names(true);
// signed_data = RRSIG_RDATA | RR(1) | RR(2)... where
//
// "|" denotes concatenation
//
// RRSIG_RDATA is the wire format of the RRSIG RDATA fields
// with the Signature field excluded and the Signer's Name
// in canonical form.
assert!(sig::emit_pre_sig(&mut encoder, type_covered, algorithm,
name.num_labels(), original_ttl, sig_expiration,
sig_inception, key_tag, signer_name).is_ok());
// construct the rrset signing data
for record in rrset {
// RR(i) = name | type | class | OrigTTL | RDATA length | RDATA
//
// name is calculated according to the function in the RFC 4035
assert!(name.emit_as_canonical(&mut encoder, true).is_ok());
//
// type is the RRset type and all RRs in the class
assert!(type_covered.emit(&mut encoder).is_ok());
//
// class is the RRset's class
assert!(dns_class.emit(&mut encoder).is_ok());
//
// OrigTTL is the value from the RRSIG Original TTL field
assert!(encoder.emit_u32(original_ttl).is_ok());
//
// RDATA length
// TODO: add support to the encoder to set a marker to go back and write the length
let mut rdata_buf = Vec::new();
{
let mut rdata_encoder = BinEncoder::new(&mut rdata_buf);
rdata_encoder.set_canonical_names(true);
assert!(record.get_rdata().emit(&mut rdata_encoder).is_ok());
}
assert!(encoder.emit_u16(rdata_buf.len() as u16).is_ok());
//
// All names in the RDATA field are in canonical form (set above)
assert!(encoder.emit_vec(&rdata_buf).is_ok());
}
}
DigestType::from(self.algorithm).hash(&buf)
}
pub fn hash_rrset_with_rrsig(&self, rrsig: &Record, records: &[Record]) -> Vec<u8> {
if let &RData::SIG(ref sig) = rrsig.get_rdata() {
self.hash_rrset(rrsig.get_name(), rrsig.get_dns_class(),
sig.get_num_labels(), sig.get_type_covered(), sig.get_algorithm(),
sig.get_original_ttl(), sig.get_sig_expiration(), sig.get_sig_inception(),
sig.get_key_tag(), sig.get_signer_name(), records)
} else {
error!("rdata is not of type SIG: {:?}", rrsig.get_rdata());
vec![]
}
}
@ -476,15 +493,34 @@ impl Signer {
None
}
/// sign a series of bytes
/// this will panic if the key is not a private key and can be used for signing.
#[allow(dead_code)]
fn sign(&self, hash: &[u8]) -> Vec<u8> {
/// Signs a hash.
///
/// This will panic if the `key` is not a private key and can be used for signing.
///
/// # Arguments
///
/// * `hash` - the hashed resource record set, see `hash_rrset`.
///
/// # Return value
///
/// The signature, ready to be stored in an `RData::RRSIG`.
pub fn sign(&self, hash: &[u8]) -> Vec<u8> {
assert!(self.pkey.can(Role::Sign)); // this is bad code, not expected in regular runtime
self.pkey.sign_with_hash(&hash, DigestType::from(self.algorithm).to_hash())
}
/// verifies the hash matches the signature with the current key.
/// Verifies the hash matches the signature with the current `key`.
///
/// # Arguments
///
/// * `hash` - the hash to be validated, see `hash_rrset`
/// * `signature` - the signature to use to verify the hash, extracted from an `RData::RRSIG`
/// for example.
///
/// # Return value
///
/// True if and only if the signature is valid for the hash. This will always return
/// false if the `key`.
pub fn verify(&self, hash: &[u8], signature: &[u8]) -> bool {
if !self.pkey.can(Role::Verify) {
// this doesn't panic b/c the public key might just have been bad, and so this would fail
@ -499,18 +535,19 @@ impl Signer {
#[test]
fn test_hash_rrset() {
use ::rr::RecordType;
use ::rr::rdata::SIG;
let mut pkey = PKey::new();
pkey.gen(512);
let signer = Signer::new(Algorithm::RSASHA256, pkey, Name::root());
let signer = Signer::new(Algorithm::RSASHA256, pkey, Name::root(), u32::max_value(), 0);
let origin: Name = Name::parse("example.com.", None).unwrap();
let rrsig = Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::SIG{type_covered: RecordType::NS, algorithm: Algorithm::RSASHA256, num_labels: origin.num_labels(), original_ttl: 86400,
sig_expiration: 5, sig_inception: 0, key_tag: signer.calculate_key_tag(), signer_name: origin.clone(), sig: vec![]}).clone();
let rrsig = Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::SIG(SIG::new(RecordType::NS, Algorithm::RSASHA256, origin.num_labels(), 86400,
5, 0, signer.calculate_key_tag(), origin.clone(), vec![]))).clone();
let rrset = vec![Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("a.iana-servers.net.", None).unwrap() }).clone(),
Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("b.iana-servers.net.", None).unwrap() }).clone()];
let hash = signer.hash_rrset(&rrsig, &rrset);
let hash = signer.hash_rrset_with_rrsig(&rrsig, &rrset);
assert!(!hash.is_empty());
let rrset = vec![Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::CNAME).dns_class(DNSClass::IN).rdata(RData::CNAME{ cname: Name::parse("a.iana-servers.net.", None).unwrap() }).clone(), // different type
@ -519,7 +556,7 @@ fn test_hash_rrset() {
Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("a.iana-servers.net.", None).unwrap() }).clone(),
Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("b.iana-servers.net.", None).unwrap() }).clone()];
let filtered_hash = signer.hash_rrset(&rrsig, &rrset);
let filtered_hash = signer.hash_rrset_with_rrsig(&rrsig, &rrset);
assert!(!filtered_hash.is_empty());
assert_eq!(hash, filtered_hash);
}
@ -527,18 +564,19 @@ fn test_hash_rrset() {
#[test]
fn test_sign_and_verify_rrset() {
use ::rr::RecordType;
use ::rr::rdata::SIG;
let mut pkey = PKey::new();
pkey.gen(512);
let signer = Signer::new(Algorithm::RSASHA256, pkey, Name::root());
let signer = Signer::new(Algorithm::RSASHA256, pkey, Name::root(), u32::max_value(), 0);
let origin: Name = Name::parse("example.com.", None).unwrap();
let rrsig = Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::SIG{type_covered: RecordType::NS, algorithm: Algorithm::RSASHA256, num_labels: origin.num_labels(), original_ttl: 86400,
sig_expiration: 5, sig_inception: 0, key_tag: signer.calculate_key_tag(), signer_name: origin.clone(), sig: vec![]}).clone();
let rrsig = Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::SIG(SIG::new(RecordType::NS, Algorithm::RSASHA256, origin.num_labels(), 86400,
5, 0, signer.calculate_key_tag(), origin.clone(), vec![]))).clone();
let rrset = vec![Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("a.iana-servers.net.", None).unwrap() }).clone(),
Record::new().name(origin.clone()).ttl(86400).rr_type(RecordType::NS).dns_class(DNSClass::IN).rdata(RData::NS{ nsdname: Name::parse("b.iana-servers.net.", None).unwrap() }).clone()];
let hash = signer.hash_rrset(&rrsig, &rrset);
let hash = signer.hash_rrset_with_rrsig(&rrsig, &rrset);
let sig = signer.sign(&hash);
assert!(signer.verify(&hash, &sig));
@ -549,7 +587,7 @@ fn test_calculate_key_tag() {
let mut pkey = PKey::new();
pkey.gen(512);
println!("pkey: {:?}", pkey.save_pub());
let signer = Signer::new(Algorithm::RSASHA256, pkey, Name::root());
let signer = Signer::new(Algorithm::RSASHA256, pkey, Name::root(), u32::max_value(), 0);
let key_tag = signer.calculate_key_tag();
println!("key_tag: {}", key_tag);

View File

@ -118,7 +118,12 @@ impl Name {
pub fn num_labels(&self) -> u8 {
// it is illegal to have more than 256 labels.
self.labels.len() as u8
let num = self.labels.len() as u8;
if num > 0 && self[0] == "*" {
return num - 1
}
num
}
/// returns the length in bytes of the labels. '.' counts as 1
@ -424,6 +429,16 @@ mod tests {
]
}
#[test]
fn num_labels() {
assert_eq!(Name::new().label("*").num_labels(), 0);
assert_eq!(Name::new().label("a").num_labels(), 1);
assert_eq!(Name::new().label("*").label("b").num_labels(), 1);
assert_eq!(Name::new().label("a").label("b").num_labels(), 2);
assert_eq!(Name::new().label("*").label("b").label("c").num_labels(), 2);
assert_eq!(Name::new().label("a").label("b").label("c").num_labels(), 3);
}
#[test]
fn parse() {
test_read_data_set(get_data(), |ref mut d| Name::read(d));

View File

@ -18,7 +18,7 @@ pub mod dns_class;
pub mod resource;
pub mod record_data;
pub mod domain;
mod rdata;
pub mod rdata;
pub mod dnssec;
pub use self::record_type::RecordType;

View File

@ -34,3 +34,7 @@ pub mod sig;
pub mod soa;
pub mod srv;
pub mod txt;
pub use self::null::NULL;
pub use self::sig::SIG;
pub use self::soa::SOA;

View File

@ -33,33 +33,52 @@ use ::rr::record_data::RData;
// experimental extensions of the DNS.
//
// NULL { anything: Vec<u8> },
// TODO: length should be stored in the decoder, and guaranteed everywhere, right?
// TODO: use this for unknown record types in caching...
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<RData> {
let mut anything: Vec<u8> = Vec::with_capacity(rdata_length as usize);
for _ in 0..rdata_length {
if let Ok(byte) = decoder.pop() {
anything.push(byte);
} else {
return Err(DecodeError::EOF);
}
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct NULL { anything: Option<Vec<u8>> }
impl NULL {
pub fn new() -> NULL {
NULL { anything: None }
}
Ok(RData::NULL{ anything: anything })
pub fn with(anything: Vec<u8>) -> NULL {
NULL { anything: Some(anything) }
}
pub fn get_anything(&self) -> Option<&Vec<u8>> {
self.anything.as_ref()
}
}
pub fn emit(encoder: &mut BinEncoder, nil: &RData) -> EncodeResult {
if let RData::NULL{ref anything} = *nil {
for b in anything {
try!(encoder.emit(*b));
// TODO: length should be stored in the decoder, and guaranteed everywhere, right?
// TODO: use this for unknown record types in caching...
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<NULL> {
if rdata_length > 0 {
let mut anything: Vec<u8> = Vec::with_capacity(rdata_length as usize);
for _ in 0..rdata_length {
if let Ok(byte) = decoder.pop() {
anything.push(byte);
} else {
return Err(DecodeError::EOF);
}
}
Ok(())
Ok(NULL::with(anything))
} else {
panic!("wrong type here {:?}", nil);
Ok(NULL::new())
}
}
pub fn emit(encoder: &mut BinEncoder, nil: &NULL) -> EncodeResult {
if let Some(ref anything) = nil.get_anything() {
for b in anything.iter() {
try!(encoder.emit(*b));
}
}
Ok(())
}
#[allow(unused)]
pub fn parse(tokens: &Vec<Token>) -> ParseResult<RData> {
unimplemented!()
@ -67,7 +86,7 @@ pub fn parse(tokens: &Vec<Token>) -> ParseResult<RData> {
#[test]
pub fn test() {
let rdata = RData::NULL{ anything: vec![0,1,2,3,4,5,6,7] };
let rdata = NULL::with(vec![0,1,2,3,4,5,6,7]);
let mut bytes = Vec::new();
let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);

View File

@ -15,7 +15,7 @@
*/
use ::serialize::binary::*;
use ::error::*;
use ::rr::{Name, RecordType, RData};
use ::rr::{Name, RecordType};
use ::rr::dnssec::Algorithm;
// RFC 2535 & 2931 DNS Security Extensions March 1999
@ -300,7 +300,33 @@ use ::rr::dnssec::Algorithm;
// SIG { type_covered: u16, algorithm: SecAlgorithm, num_labels: u8, original_ttl: u32,
// sig_expiration: u32, sig_inception: u32, key_tag: u16, signer_name: Name, sig: Vec<u8> }
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<RData> {
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct SIG { type_covered: RecordType, algorithm: Algorithm, num_labels: u8, original_ttl: u32,
sig_expiration: u32, sig_inception: u32, key_tag: u16, signer_name: Name,
sig: Vec<u8> }
impl SIG {
pub fn new(type_covered: RecordType, algorithm: Algorithm, num_labels: u8, original_ttl: u32,
sig_expiration: u32, sig_inception: u32, key_tag: u16, signer_name: Name,
sig: Vec<u8>) -> SIG {
SIG { type_covered: type_covered, algorithm: algorithm, num_labels: num_labels,
original_ttl: original_ttl, sig_expiration: sig_expiration,
sig_inception: sig_inception, key_tag: key_tag, signer_name: signer_name,
sig: sig }
}
pub fn get_type_covered(&self) -> RecordType { self.type_covered }
pub fn get_algorithm(&self) -> Algorithm { self.algorithm }
pub fn get_num_labels(&self) -> u8 { self.num_labels }
pub fn get_original_ttl(&self) -> u32 { self.original_ttl }
pub fn get_sig_expiration(&self) -> u32 { self.sig_expiration }
pub fn get_sig_inception(&self) -> u32 { self.sig_inception }
pub fn get_key_tag(&self) -> u16 { self.key_tag }
pub fn get_signer_name(&self) -> &Name { &self.signer_name }
pub fn get_sig(&self) -> &[u8] { &self.sig }
}
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<SIG> {
let start_idx = decoder.index();
// TODO should we verify here? or elsewhere...
@ -317,67 +343,45 @@ pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<RData>
let bytes_read = decoder.index() - start_idx;
let sig = try!(decoder.read_vec(rdata_length as usize - bytes_read));
Ok(RData::SIG {
type_covered: type_covered,
algorithm: algorithm,
num_labels: num_labels,
original_ttl: original_ttl,
sig_expiration: sig_expiration,
sig_inception: sig_inception,
key_tag: key_tag,
signer_name: signer_name,
sig: sig,
})
Ok(SIG::new(type_covered, algorithm, num_labels, original_ttl, sig_expiration,
sig_inception, key_tag, signer_name, sig))
}
pub fn emit(encoder: &mut BinEncoder, sig: &RData) -> EncodeResult {
if let RData::SIG { type_covered, algorithm, num_labels, original_ttl, sig_expiration, sig_inception, key_tag, ref signer_name, ref sig } = *sig {
try!(type_covered.emit(encoder));
try!(algorithm.emit(encoder));
try!(encoder.emit(num_labels));
try!(encoder.emit_u32(original_ttl));
try!(encoder.emit_u32(sig_expiration));
try!(encoder.emit_u32(sig_inception));
try!(encoder.emit_u16(key_tag));
try!(signer_name.emit(encoder));
try!(encoder.emit_vec(sig));
Ok(())
} else {
panic!("wrong type here {:?}", sig);
}
pub fn emit(encoder: &mut BinEncoder, sig: &SIG) -> EncodeResult {
try!(sig.get_type_covered().emit(encoder));
try!(sig.get_algorithm().emit(encoder));
try!(encoder.emit(sig.get_num_labels()));
try!(encoder.emit_u32(sig.get_original_ttl()));
try!(encoder.emit_u32(sig.get_sig_expiration()));
try!(encoder.emit_u32(sig.get_sig_inception()));
try!(encoder.emit_u16(sig.get_key_tag()));
try!(sig.get_signer_name().emit(encoder));
try!(encoder.emit_vec(sig.get_sig()));
Ok(())
}
/// specifically for outputing the RData for an RRSIG, with signer_name in canonical form
pub fn emit_pre_sig(encoder: &mut BinEncoder, sig: &RData) -> EncodeResult {
if let RData::SIG { type_covered, algorithm, num_labels, original_ttl, sig_expiration, sig_inception, key_tag, ref signer_name, .. } = *sig {
try!(type_covered.emit(encoder));
try!(algorithm.emit(encoder));
try!(encoder.emit(num_labels));
try!(encoder.emit_u32(original_ttl));
try!(encoder.emit_u32(sig_expiration));
try!(encoder.emit_u32(sig_inception));
try!(encoder.emit_u16(key_tag));
try!(signer_name.emit_as_canonical(encoder, true));
Ok(())
} else {
panic!("wrong type here {:?}", sig);
}
pub fn emit_pre_sig(encoder: &mut BinEncoder, type_covered: RecordType, algorithm: Algorithm,
num_labels: u8, original_ttl: u32, sig_expiration: u32, sig_inception: u32,
key_tag: u16, signer_name: &Name) -> EncodeResult {
try!(type_covered.emit(encoder));
try!(algorithm.emit(encoder));
try!(encoder.emit(num_labels));
try!(encoder.emit_u32(original_ttl));
try!(encoder.emit_u32(sig_expiration));
try!(encoder.emit_u32(sig_inception));
try!(encoder.emit_u16(key_tag));
try!(signer_name.emit_as_canonical(encoder, true));
Ok(())
}
#[test]
fn test() {
let rdata = RData::SIG {
type_covered: RecordType::NULL,
algorithm: Algorithm::RSASHA256,
num_labels: 0,
original_ttl: 0,
sig_expiration: 2,
sig_inception: 1,
key_tag: 5,
signer_name: Name::new().label("www").label("example").label("com"),
sig: vec![ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15
,16,17,18,19,20,21,22,23,24,25,26,27,28,29,29,31], // 32 bytes for SHA256
};
let rdata = SIG::new(RecordType::NULL, Algorithm::RSASHA256, 0, 0, 2, 1, 5,
Name::new().label("www").label("example").label("com"),
vec![ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15
,16,17,18,19,20,21,22,23,24,25,26,27,28,29,29,31], // 32 bytes for SHA256
);
let mut bytes = Vec::new();
let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);

View File

@ -84,9 +84,70 @@ use ::rr::domain::Name;
// reason for this provison is to allow future dynamic update facilities to
// change the SOA RR with known semantics.
//
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct SOA { mname: Name, rname: Name, serial: u32,
refresh: i32, retry: i32, expire: i32,
minimum: u32, }
impl SOA {
pub fn new(mname: Name, rname: Name, serial: u32,
refresh: i32, retry: i32, expire: i32,
minimum: u32) -> Self {
SOA { mname: mname, rname: rname, serial: serial,
refresh: refresh, retry: retry, expire: expire,
minimum: minimum, }
}
/// Increments the serial number by one
pub fn increment_serial(&mut self) {
self.serial += 1;
}
/// # Return value
///
/// The `domain-name` of the name server that was the original or primary source of data for
/// this zone, i.e. the master name server.
pub fn get_mname(&self) -> &Name { &self.mname }
/// # Return value
///
/// A `domain-name` which specifies the mailbox of the person responsible for this zone, i.e.
/// the responsible name.
pub fn get_rname(&self) -> &Name { &self.rname }
/// # Return value
///
/// The unsigned 32 bit version number of the original copy of the zone. Zone transfers
/// preserve this value. This value wraps and should be compared using sequence space arithmetic.
pub fn get_serial(&self) -> u32 { self.serial }
/// # Return value
///
/// A 32 bit time interval before the zone should be refreshed, in seconds.
pub fn get_refresh(&self) -> i32 { self.refresh }
/// # Return value
///
/// A 32 bit time interval that should elapse before a failed refresh should be retried,
/// in seconds.
pub fn get_retry(&self) -> i32 { self.retry }
/// # Return value
///
/// A 32 bit time value that specifies the upper limit on the time interval that can elapse
/// before the zone is no longer authoritative, in seconds
pub fn get_expire(&self) -> i32 { self.expire }
/// # Return value
///
/// The unsigned 32 bit minimum TTL field that should be exported with any RR from this zone.
pub fn get_minimum(&self) -> u32 { self.minimum }
}
// SOA { mname: Name, rname: Name, serial: u32, refresh: i32, retry: i32, expire: i32, minimum: u32, },
pub fn read(decoder: &mut BinDecoder) -> DecodeResult<RData> {
Ok(RData::SOA{
pub fn read(decoder: &mut BinDecoder) -> DecodeResult<SOA> {
Ok(SOA{
mname: try!(Name::read(decoder)),
rname: try!(Name::read(decoder)),
serial: try!(decoder.read_u32()),
@ -97,19 +158,15 @@ pub fn read(decoder: &mut BinDecoder) -> DecodeResult<RData> {
})
}
pub fn emit(encoder: &mut BinEncoder, soa: &RData) -> EncodeResult {
if let RData::SOA { ref mname, ref rname, ref serial, ref refresh, ref retry, ref expire, ref minimum } = *soa {
try!(mname.emit(encoder));
try!(rname.emit(encoder));
try!(encoder.emit_u32(*serial));
try!(encoder.emit_i32(*refresh));
try!(encoder.emit_i32(*retry));
try!(encoder.emit_i32(*expire));
try!(encoder.emit_u32(*minimum));
Ok(())
} else {
panic!("wrong type here {:?}", soa);
}
pub fn emit(encoder: &mut BinEncoder, soa: &SOA) -> EncodeResult {
try!(soa.mname.emit(encoder));
try!(soa.rname.emit(encoder));
try!(encoder.emit_u32(soa.serial));
try!(encoder.emit_i32(soa.refresh));
try!(encoder.emit_i32(soa.retry));
try!(encoder.emit_i32(soa.expire));
try!(encoder.emit_u32(soa.minimum));
Ok(())
}
// VENERA Action\.domains (
@ -138,7 +195,7 @@ pub fn parse(tokens: &Vec<Token>, origin: Option<&Name>) -> ParseResult<RData> {
// let expire: i32 = try!(token.next().ok_or(ParseError::MissingToken("expire".to_string())).and_then(|t| if let &Token::CharData(ref s) = t {Ok(try!(s.parse()))} else {Err(ParseError::UnexpectedToken(t.clone()))} ));
// let minimum: u32 = try!(token.next().ok_or(ParseError::MissingToken("minimum".to_string())).and_then(|t| if let &Token::CharData(ref s) = t {Ok(try!(s.parse()))} else {Err(ParseError::UnexpectedToken(t.clone()))} ));
Ok(RData::SOA{
Ok(RData::SOA(SOA{
mname: mname,
rname: rname,
serial: serial,
@ -146,12 +203,12 @@ pub fn parse(tokens: &Vec<Token>, origin: Option<&Name>) -> ParseResult<RData> {
retry: retry,
expire: expire,
minimum: minimum,
})
}))
}
#[test]
fn test() {
let rdata = RData::SOA{
let rdata = SOA{
mname: Name::new().label("m").label("example").label("com"),
rname: Name::new().label("r").label("example").label("com"),
serial: 1,

View File

@ -25,6 +25,9 @@ use ::rr::dnssec::{Algorithm, DigestType, Nsec3HashAlgorithm};
use super::domain::Name;
use super::record_type::RecordType;
use super::rdata;
use super::rdata::NULL;
use super::rdata::SIG;
use super::rdata::SOA;
/// 3.3. Standard RRs
///
@ -252,7 +255,7 @@ pub enum RData {
// NULL records cause no additional section processing. NULL RRs are not
// allowed in master files. NULLs are used as placeholders in some
// experimental extensions of the DNS.
NULL { anything: Vec<u8> },
NULL(NULL),
// 3.3.11. NS RDATA format
//
@ -467,7 +470,7 @@ pub enum RData {
PTR { ptrdname: Name },
// RFC 2535 & 2931 DNS Security Extensions March 1999
// RFC 4034 DNSSEC Resource Records March 2005
// RFC 4034 DNSSEC Resource Records March 2005
//
// 3.1. RRSIG RDATA Wire Format
//
@ -496,8 +499,7 @@ pub enum RData {
// / Signature /
// / /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
SIG { type_covered: RecordType, algorithm: Algorithm, num_labels: u8, original_ttl: u32,
sig_expiration: u32, sig_inception: u32, key_tag: u16, signer_name: Name, sig: Vec<u8> },
SIG (SIG),
// 3.3.13. SOA RDATA format
//
@ -563,7 +565,8 @@ pub enum RData {
// when the zone is loaded from a master file or via a zone transfer. The
// reason for this provison is to allow future dynamic update facilities to
// change the SOA RR with known semantics.
SOA { mname: Name, rname: Name, serial: u32, refresh: i32, retry: i32, expire: i32, minimum: u32, },
//SOA { mname: Name, rname: Name, serial: u32, refresh: i32, retry: i32, expire: i32, minimum: u32, },
SOA (SOA),
// RFC 2782 DNS SRV RR February 2000
//
@ -664,16 +667,17 @@ impl RData {
RecordType::DS => {debug!("reading DS");rdata::ds::read(decoder, rdata_length)},
rt @ RecordType::IXFR => Err(DecodeError::UnknownRecordTypeValue(rt.into())),
RecordType::MX => {debug!("reading MX"); rdata::mx::read(decoder)},
RecordType::NULL => {debug!("reading NULL"); rdata::null::read(decoder, rdata_length)},
RecordType::NULL => {debug!("reading NULL"); Ok(RData::NULL(try!(rdata::null::read(decoder, rdata_length)))) },
RecordType::NS => {debug!("reading NS"); rdata::ns::read(decoder)},
RecordType::NSEC => {debug!("reading NSEC");rdata::nsec::read(decoder, rdata_length)},
RecordType::NSEC3 => {debug!("reading NSEC3");rdata::nsec3::read(decoder, rdata_length)},
RecordType::NSEC3PARAM => {debug!("reading NSEC3PARAM");rdata::nsec3param::read(decoder)},
RecordType::OPT => {debug!("reading OPT"); rdata::opt::read(decoder, rdata_length)},
RecordType::PTR => {debug!("reading PTR"); rdata::ptr::read(decoder)},
RecordType::RRSIG => {debug!("reading RRSIG"); rdata::sig::read(decoder, rdata_length)},
RecordType::SIG => {debug!("reading SIG"); rdata::sig::read(decoder, rdata_length)},
RecordType::SOA => {debug!("reading SOA"); rdata::soa::read(decoder)},
RecordType::RRSIG => {debug!("reading RRSIG"); Ok(RData::SIG(try!(rdata::sig::read(decoder, rdata_length)))) },
RecordType::SIG => {debug!("reading SIG"); Ok(RData::SIG(try!(rdata::sig::read(decoder, rdata_length)))) },
// TODO: this wrap in Ok() should go away when all RData types are converted to strong types
RecordType::SOA => {debug!("reading SOA"); Ok(RData::SOA(try!(rdata::soa::read(decoder)))) },
RecordType::SRV => {debug!("reading SRV"); rdata::srv::read(decoder)},
RecordType::TXT => {debug!("reading TXT"); rdata::txt::read(decoder, rdata_length)},
});
@ -694,15 +698,15 @@ impl RData {
RData::DS{..} => rdata::ds::emit(encoder, self),
RData::DNSKEY{..} => rdata::dnskey::emit(encoder, self),
RData::MX{..} => rdata::mx::emit(encoder, self),
RData::NULL{..} => rdata::null::emit(encoder, self),
RData::NULL(ref null) => rdata::null::emit(encoder, null),
RData::NS{..} => rdata::ns::emit(encoder, self),
RData::NSEC{..} => rdata::nsec::emit(encoder, self),
RData::NSEC3{..} => rdata::nsec3::emit(encoder, self),
RData::NSEC3PARAM{..} => rdata::nsec3param::emit(encoder, self),
RData::OPT{..} => rdata::opt::emit(encoder, self),
RData::PTR{..} => rdata::ptr::emit(encoder, self),
RData::SIG{..} => rdata::sig::emit(encoder, self),
RData::SOA{..} => rdata::soa::emit(encoder, self),
RData::SIG(ref sig) => rdata::sig::emit(encoder, sig),
RData::SOA(ref soa) => rdata::soa::emit(encoder, soa),
RData::SRV{..} => rdata::srv::emit(encoder, self),
RData::TXT{..} => rdata::txt::emit(encoder, self),
}
@ -724,11 +728,11 @@ impl<'a> From<&'a RData> for RecordType {
RData::NSEC{..} => RecordType::NSEC,
RData::NSEC3{..} => RecordType::NSEC3,
RData::NSEC3PARAM{..} => RecordType::NSEC3PARAM,
RData::NULL{..} => RecordType::NULL,
RData::NULL(..) => RecordType::NULL,
RData::OPT{..} => RecordType::OPT,
RData::PTR{..} => RecordType::PTR,
RData::SIG{..} => RecordType::SIG,
RData::SOA{..} => RecordType::SOA,
RData::SIG(..) => RecordType::SIG,
RData::SOA(..) => RecordType::SOA,
RData::SRV{..} => RecordType::SRV,
RData::TXT{..} => RecordType::TXT,
}
@ -777,6 +781,7 @@ mod tests {
use ::serialize::binary::*;
use ::serialize::binary::bin_tests::test_emit_data_set;
use ::rr::domain::Name;
use ::rr::rdata::SOA;
fn get_data() -> Vec<(RData, Vec<u8>)> {
vec![
@ -784,9 +789,9 @@ mod tests {
(RData::MX{preference: 256, exchange: Name::with_labels(vec!["n".to_string()])}, vec![1,0,1,b'n',0]),
(RData::NS{nsdname: Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()])}, vec![3,b'w',b'w',b'w',7,b'e',b'x',b'a',b'm',b'p',b'l',b'e',3,b'c',b'o',b'm',0]),
(RData::PTR{ptrdname: Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()])}, vec![3,b'w',b'w',b'w',7,b'e',b'x',b'a',b'm',b'p',b'l',b'e',3,b'c',b'o',b'm',0]),
(RData::SOA{mname: Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()]),
rname: Name::with_labels(vec!["xxx".to_string(),"example".to_string(),"com".to_string()]),
serial: u32::max_value(), refresh: -1 as i32, retry: -1 as i32, expire: -1 as i32, minimum: u32::max_value()},
(RData::SOA(SOA::new(Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()]),
Name::with_labels(vec!["xxx".to_string(),"example".to_string(),"com".to_string()]),
u32::max_value(), -1 as i32, -1 as i32, -1 as i32, u32::max_value())),
vec![3,b'w',b'w',b'w',7,b'e',b'x',b'a',b'm',b'p',b'l',b'e',3,b'c',b'o',b'm',0,
3,b'x',b'x',b'x',0xC0, 0x04,
0xFF,0xFF,0xFF,0xFF,
@ -813,9 +818,9 @@ mod tests {
RData::CNAME{cname: Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()])},
RData::PTR{ptrdname: Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()])},
RData::NS{nsdname: Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()])},
RData::SOA{mname: Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()]),
rname: Name::with_labels(vec!["xxx".to_string(),"example".to_string(),"com".to_string()]),
serial: u32::max_value(), refresh: -1 as i32, retry: -1 as i32, expire: -1 as i32, minimum: u32::max_value()},
RData::SOA(SOA::new(Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()]),
Name::with_labels(vec!["xxx".to_string(),"example".to_string(),"com".to_string()]),
u32::max_value(), -1 as i32, -1 as i32, -1 as i32, u32::max_value())),
RData::TXT{txt_data: vec!["abcdef".to_string(), "ghi".to_string(), "".to_string(), "j".to_string()]},
];
let mut unordered = vec![
@ -823,9 +828,9 @@ mod tests {
RData::MX{preference: 256, exchange: Name::with_labels(vec!["n".to_string()])},
RData::PTR{ptrdname: Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()])},
RData::NS{nsdname: Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()])},
RData::SOA{mname: Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()]),
rname: Name::with_labels(vec!["xxx".to_string(),"example".to_string(),"com".to_string()]),
serial: u32::max_value(), refresh: -1 as i32, retry: -1 as i32, expire: -1 as i32, minimum: u32::max_value()},
RData::SOA(SOA::new(Name::with_labels(vec!["www".to_string(),"example".to_string(),"com".to_string()]),
Name::with_labels(vec!["xxx".to_string(),"example".to_string(),"com".to_string()]),
u32::max_value(), -1 as i32, -1 as i32, -1 as i32, u32::max_value())),
RData::TXT{txt_data: vec!["abcdef".to_string(), "ghi".to_string(), "".to_string(), "j".to_string()]},
RData::A{ address: Ipv4Addr::from_str("0.0.0.0").unwrap()},
RData::AAAA{ address: Ipv6Addr::from_str("::").unwrap()},

View File

@ -19,6 +19,7 @@ use std::cmp::Ordering;
use ::serialize::binary::*;
use ::error::*;
use ::rr::rdata::NULL;
use super::record_data::RData;
use super::record_type::RecordType;
use super::dns_class::DNSClass;
@ -106,6 +107,16 @@ impl Record {
}
}
pub fn with(name: domain::Name, rr_type: RecordType, ttl: u32) -> Record {
Record {
name_labels: name,
rr_type: rr_type,
dns_class: DNSClass::IN,
ttl: ttl,
rdata: RData::NULL(NULL::new()),
}
}
pub fn name(&mut self, name: domain::Name) -> &mut Self { self.name_labels = name; self }
pub fn add_name(&mut self, label: String) -> &mut Self { self.name_labels.add_label(Rc::new(label)); self }
pub fn rr_type(&mut self, rr_type: RecordType) -> &mut Self { self.rr_type = rr_type; self }
@ -118,6 +129,7 @@ impl Record {
pub fn get_dns_class(&self) -> DNSClass { self.dns_class }
pub fn get_ttl(&self) -> u32 { self.ttl }
pub fn get_rdata(&self) -> &RData { &self.rdata }
pub fn get_rdata_mut(&mut self) -> &mut RData { &mut self.rdata }
// returns the len of this record in bytes
// pub fn len(&self) -> usize {
@ -174,7 +186,7 @@ impl BinSerializable<Record> for Record {
// this is to handle updates, RFC 2136, which uses 0 to indicate certain aspects of
// pre-requisites
let rdata: RData = if rd_length == 0 {
RData::NULL{ anything: vec![] }
RData::NULL(NULL::new())
} else {
// RDATA a variable length string of octets that describes the
// resource. The format of this information varies

View File

@ -259,9 +259,10 @@ impl Parser {
RecordType::SOA => {
// TTL for the SOA is set internally...
// expire is for the SOA, minimum is default for records
if let RData::SOA { ref expire, ref minimum, ..} = rdata {
record.ttl(*expire as u32); // the spec seems a little inaccurate with u32 and i32
if ttl.is_none() { ttl = Some(*minimum); } // TODO: should this only set it if it's not set?
if let RData::SOA(ref soa) = rdata {
// TODO, this looks wrong, get_expire() should be get_minimum(), right?
record.ttl(soa.get_expire() as u32); // the spec seems a little inaccurate with u32 and i32
if ttl.is_none() { ttl = Some(soa.get_minimum()); } // TODO: should this only set it if it's not set?
} else { assert!(false, "Invalid RData here, expected SOA: {:?}", rdata); }
},
_ => {

View File

@ -51,15 +51,15 @@ VENERA A 10.1.0.52
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());
assert_eq!(DNSClass::IN, soa_record.get_dns_class());
if let RData::SOA { ref mname, ref rname, serial, refresh, retry, expire, minimum } = *soa_record.get_rdata() {
if let RData::SOA(ref soa) = *soa_record.get_rdata() {
// this should all be lowercased
assert_eq!(&Name::new().label("venera").label("isi").label("edu"), mname);
assert_eq!(&Name::new().label("action.domains").label("isi").label("edu"), rname);
assert_eq!(20, serial);
assert_eq!(7200, refresh);
assert_eq!(600, retry);
assert_eq!(3600000, expire);
assert_eq!(60, minimum);
assert_eq!(&Name::new().label("venera").label("isi").label("edu"), soa.get_mname());
assert_eq!(&Name::new().label("action.domains").label("isi").label("edu"), soa.get_rname());
assert_eq!(20, soa.get_serial());
assert_eq!(7200, soa.get_refresh());
assert_eq!(600, soa.get_retry());
assert_eq!(3600000, soa.get_expire());
assert_eq!(60, soa.get_minimum());
} else {
panic!("Not an SOA record!!!")
}

View File

@ -232,7 +232,7 @@ impl Handler for Server {
Err(e) => error!("could not register stream: {:?} cause: {}", stream, e),
Ok(()) => {
info!("accepted tcp connection from: {:?} on {:?}", addr, stream.local_addr().ok());
self.tcp_handlers.insert(token, TcpHandler::new_server_handler(stream, self.catalog.clone()));
self.tcp_handlers.insert(token, TcpHandler::new_server_handler(stream));
}
}
},

View File

@ -16,34 +16,30 @@
use std::io;
use std::io::{Write, Read};
use std::mem;
use std::sync::Arc;
use mio::tcp::TcpStream;
use mio::EventSet; // not * b/c don't want confusion with std::net
use ::authority::Catalog;
pub struct TcpHandler {
tcp_type: TcpType,
state: TcpState, // current state of the handler and stream, i.e. are we reading from the client? or writing back to it?
buffer: Vec<u8>, // current location and buffer we are reading into or writing from
stream: TcpStream,
catalog: Option<Arc<Catalog>>,
}
impl TcpHandler {
/// initializes this handler with the intention to write first
pub fn new_client_handler(stream: TcpStream, catalog: Option<Arc<Catalog>>) -> Self {
Self::new(TcpType::Client, TcpState::WillWriteLength, vec![], stream, catalog)
pub fn new_client_handler(stream: TcpStream) -> Self {
Self::new(TcpType::Client, TcpState::WillWriteLength, vec![], stream)
}
/// initializes this handler with the intention to read first
pub fn new_server_handler(stream: TcpStream, catalog: Arc<Catalog>) -> Self {
Self::new(TcpType::Server, TcpState::WillReadLength, Vec::with_capacity(512), stream, Some(catalog))
pub fn new_server_handler(stream: TcpStream) -> Self {
Self::new(TcpType::Server, TcpState::WillReadLength, Vec::with_capacity(512), stream)
}
fn new(tcp_type: TcpType, state: TcpState, buffer: Vec<u8>, stream: TcpStream, catalog: Option<Arc<Catalog>>) -> Self {
TcpHandler{ tcp_type: tcp_type, state: state, buffer: buffer, stream: stream, catalog: catalog }
fn new(tcp_type: TcpType, state: TcpState, buffer: Vec<u8>, stream: TcpStream) -> Self {
TcpHandler{ tcp_type: tcp_type, state: state, buffer: buffer, stream: stream }
}
pub fn get_stream(&self) -> &TcpStream {

View File

@ -45,7 +45,7 @@ impl TcpClientConnection {
// ideally this would not be added to the event loop until the client connection request.
try!(event_loop.register(&stream, RESPONSE, EventSet::all(), PollOpt::all()));
Ok(TcpClientConnection{ handler: Some(TcpHandler::new_client_handler(stream, None)), event_loop: event_loop, error: None })
Ok(TcpClientConnection{ handler: Some(TcpHandler::new_client_handler(stream)), event_loop: event_loop, error: None })
}
}