nsec record creation
This commit is contained in:
parent
d4f79c9007
commit
18eb2dc707
@ -8,6 +8,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- Authority zone signing now complete, still need to load/save private keys
|
||||
- DNSKEYs auto inserted for added private keys
|
||||
- New mocked network client tests, to verify zone signing
|
||||
- NSEC record creation for zone
|
||||
|
||||
### Fixed
|
||||
- Added loop on TCP accept requests
|
||||
|
@ -13,14 +13,15 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
use std::collections::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use chrono::offset::utc::UTC;
|
||||
|
||||
use ::authority::{UpdateResult, ZoneType, RRSet};
|
||||
use ::op::{UpdateMessage, ResponseCode};
|
||||
use ::rr::{DNSClass, Name, RData, Record, RecordType};
|
||||
use ::rr::rdata::SIG;
|
||||
use ::rr::rdata::{NSEC, SIG};
|
||||
use ::rr::dnssec::Signer;
|
||||
|
||||
/// Accessor key for RRSets in the Authority.
|
||||
@ -43,11 +44,28 @@ impl RrKey {
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for RrKey {
|
||||
fn partial_cmp(&self, other: &RrKey) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for RrKey {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
let order = self.name.cmp(&other.name);
|
||||
if order == Ordering::Equal {
|
||||
self.record_type.cmp(&other.record_type)
|
||||
} else {
|
||||
order
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Authority is the storage method for all resource records
|
||||
pub struct Authority {
|
||||
origin: Name,
|
||||
class: DNSClass,
|
||||
records: HashMap<RrKey, RRSet>,
|
||||
records: BTreeMap<RrKey, RRSet>,
|
||||
zone_type: ZoneType,
|
||||
allow_update: bool,
|
||||
// Private key mapped to the Record of the DNSKey
|
||||
@ -76,7 +94,7 @@ impl Authority {
|
||||
/// # Return value
|
||||
///
|
||||
/// The new `Authority`.
|
||||
pub fn new(origin: Name, records: HashMap<RrKey, RRSet>, zone_type: ZoneType, allow_update: bool) -> Authority {
|
||||
pub fn new(origin: Name, records: BTreeMap<RrKey, RRSet>, zone_type: ZoneType, allow_update: bool) -> Authority {
|
||||
Authority{ origin: origin, class: DNSClass::IN, records: records, zone_type: zone_type,
|
||||
allow_update: allow_update, secure_keys: Vec::new() }
|
||||
}
|
||||
@ -588,6 +606,9 @@ impl Authority {
|
||||
|
||||
// update the serial...
|
||||
if updated {
|
||||
// TODO: only call nsec_zone after adds/deletes
|
||||
// needs to be called before incrementing the soa serial, to make sur IXFR works properly
|
||||
self.nsec_zone();
|
||||
|
||||
// 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.
|
||||
@ -724,13 +745,63 @@ impl Authority {
|
||||
if result.is_empty() { None } else { Some(result) }
|
||||
}
|
||||
|
||||
/// Creates all nsec records needed for the zone, replaces any existing records.
|
||||
fn nsec_zone(&mut self) {
|
||||
// only create nsec records for secure zones
|
||||
if self.secure_keys.is_empty() { return }
|
||||
debug!("generating nsec records: {}", self.origin);
|
||||
|
||||
// first remove all existing nsec records
|
||||
let delete_keys: Vec<RrKey> = self.records.keys()
|
||||
.filter(|k| k.record_type == RecordType::NSEC)
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
for key in delete_keys {
|
||||
self.records.remove(&key);
|
||||
}
|
||||
|
||||
// now go through and generate the nsec records
|
||||
let ttl = self.get_soa(false).map_or(0, |soa| if let &RData::SOA(ref rdata) = soa.get_rdata() { rdata.get_minimum() } else { 0 });
|
||||
let serial = self.get_serial();
|
||||
let mut records: Vec<Record> = vec![];
|
||||
|
||||
{
|
||||
let mut nsec_info: Option<(&Name, Vec<RecordType>)> = None;
|
||||
for key in self.records.keys() {
|
||||
match nsec_info {
|
||||
None => nsec_info = Some((&key.name, vec![key.record_type])),
|
||||
Some((name, ref mut vec)) if name == &key.name => { vec.push(key.record_type) },
|
||||
Some((name, vec)) => {
|
||||
// names aren't equal, create the NSEC record
|
||||
let mut record = Record::with(name.clone(), RecordType::NSEC, ttl);
|
||||
let rdata = NSEC::new(key.name.clone(), vec);
|
||||
record.rdata(RData::NSEC(rdata));
|
||||
records.push(record);
|
||||
|
||||
// new record...
|
||||
nsec_info = Some((&key.name, vec![key.record_type]))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// the last record
|
||||
if let Some((name, vec)) = nsec_info {
|
||||
// names aren't equal, create the NSEC record
|
||||
let mut record = Record::with(name.clone(), RecordType::NSEC, ttl);
|
||||
let rdata = NSEC::new(self.get_origin().clone(), vec);
|
||||
record.rdata(RData::NSEC(rdata));
|
||||
records.push(record);
|
||||
}
|
||||
}
|
||||
|
||||
// insert all the nsec records
|
||||
for record in records {
|
||||
self.upsert(record, serial);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
debug!("signing zone: {}", self.origin);
|
||||
let now = UTC::now().timestamp() as u32;
|
||||
@ -794,7 +865,7 @@ impl Authority {
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod authority_tests {
|
||||
use std::collections::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
use std::net::{Ipv4Addr,Ipv6Addr};
|
||||
|
||||
use ::authority::ZoneType;
|
||||
@ -805,7 +876,7 @@ pub mod authority_tests {
|
||||
|
||||
pub fn create_example() -> Authority {
|
||||
let origin: Name = Name::parse("example.com.", None,).unwrap();
|
||||
let mut records: Authority = Authority::new(origin.clone(), HashMap::new(), ZoneType::Master, false);
|
||||
let mut records: Authority = Authority::new(origin.clone(), BTreeMap::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(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);
|
||||
|
||||
|
@ -122,47 +122,54 @@ impl Catalog {
|
||||
self.authorities.insert(name, RwLock::new(authority));
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 2136 DNS Update April 1997
|
||||
*
|
||||
* 3.1 - Process Zone Section
|
||||
*
|
||||
* 3.1.1. The Zone Section is checked to see that there is exactly one
|
||||
* RR therein and that the RR's ZTYPE is SOA, else signal FORMERR to the
|
||||
* requestor. Next, the ZNAME and ZCLASS are checked to see if the zone
|
||||
* so named is one of this server's authority zones, else signal NOTAUTH
|
||||
* to the requestor. If the server is a zone slave, the request will be
|
||||
* forwarded toward the primary master.
|
||||
*
|
||||
* 3.1.2 - Pseudocode For Zone Section Processing
|
||||
*
|
||||
* if (zcount != 1 || ztype != SOA)
|
||||
* return (FORMERR)
|
||||
* if (zone_type(zname, zclass) == SLAVE)
|
||||
* return forward()
|
||||
* if (zone_type(zname, zclass) == MASTER)
|
||||
* return update()
|
||||
* return (NOTAUTH)
|
||||
*
|
||||
* Sections 3.2 through 3.8 describe the primary master's behaviour,
|
||||
* whereas Section 6 describes a forwarder's behaviour.
|
||||
*
|
||||
* 3.8 - Response
|
||||
*
|
||||
* At the end of UPDATE processing, a response code will be known. A
|
||||
* response message is generated by copying the ID and Opcode fields
|
||||
* from the request, and either copying the ZOCOUNT, PRCOUNT, UPCOUNT,
|
||||
* and ADCOUNT fields and associated sections, or placing zeros (0) in
|
||||
* the these "count" fields and not including any part of the original
|
||||
* update. The QR bit is set to one (1), and the response is sent back
|
||||
* to the requestor. If the requestor used UDP, then the response will
|
||||
* be sent to the requestor's source UDP port. If the requestor used
|
||||
* TCP, then the response will be sent back on the requestor's open TCP
|
||||
* connection.
|
||||
*/
|
||||
/// Update the zone given the Update request.
|
||||
///
|
||||
/// [RFC 2136](https://tools.ietf.org/html/rfc2136), DNS Update, April 1997
|
||||
///
|
||||
/// ```text
|
||||
/// 3.1 - Process Zone Section
|
||||
///
|
||||
/// 3.1.1. The Zone Section is checked to see that there is exactly one
|
||||
/// RR therein and that the RR's ZTYPE is SOA, else signal FORMERR to the
|
||||
/// requestor. Next, the ZNAME and ZCLASS are checked to see if the zone
|
||||
/// so named is one of this server's authority zones, else signal NOTAUTH
|
||||
/// to the requestor. If the server is a zone slave, the request will be
|
||||
/// forwarded toward the primary master.
|
||||
///
|
||||
/// 3.1.2 - Pseudocode For Zone Section Processing
|
||||
///
|
||||
/// if (zcount != 1 || ztype != SOA)
|
||||
/// return (FORMERR)
|
||||
/// if (zone_type(zname, zclass) == SLAVE)
|
||||
/// return forward()
|
||||
/// if (zone_type(zname, zclass) == MASTER)
|
||||
/// return update()
|
||||
/// return (NOTAUTH)
|
||||
///
|
||||
/// Sections 3.2 through 3.8 describe the primary master's behaviour,
|
||||
/// whereas Section 6 describes a forwarder's behaviour.
|
||||
///
|
||||
/// 3.8 - Response
|
||||
///
|
||||
/// At the end of UPDATE processing, a response code will be known. A
|
||||
/// response message is generated by copying the ID and Opcode fields
|
||||
/// from the request, and either copying the ZOCOUNT, PRCOUNT, UPCOUNT,
|
||||
/// and ADCOUNT fields and associated sections, or placing zeros (0) in
|
||||
/// the these "count" fields and not including any part of the original
|
||||
/// update. The QR bit is set to one (1), and the response is sent back
|
||||
/// to the requestor. If the requestor used UDP, then the response will
|
||||
/// be sent to the requestor's source UDP port. If the requestor used
|
||||
/// TCP, then the response will be sent back on the requestor's open TCP
|
||||
/// connection.
|
||||
/// ```
|
||||
///
|
||||
/// The "request" should be an update formatted message.
|
||||
/// The response will be in the alternate, all 0's format described in RFC 2136 section 3.8
|
||||
/// as this is more efficient.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `request` - an update message
|
||||
pub fn update(&self, request: &Message) -> Message {
|
||||
let mut response: Message = Message::new();
|
||||
response.id(request.get_id());
|
||||
@ -349,7 +356,7 @@ 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);
|
||||
let mut records: Authority = Authority::new(origin.clone(), BTreeMap::new(), ZoneType::Master, false);
|
||||
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(Name::parse("a.iana-servers.net.", None).unwrap()) ).clone(), 0);
|
||||
|
@ -355,37 +355,39 @@ impl PartialOrd<Name> for Name {
|
||||
}
|
||||
|
||||
impl Ord for Name {
|
||||
// RFC 4034 DNSSEC Resource Records March 2005
|
||||
//
|
||||
// 6.1. Canonical DNS Name Order
|
||||
//
|
||||
// For the purposes of DNS security, owner names are ordered by treating
|
||||
// individual labels as unsigned left-justified octet strings. The
|
||||
// absence of a octet sorts before a zero value octet, and uppercase
|
||||
// US-ASCII letters are treated as if they were lowercase US-ASCII
|
||||
// letters.
|
||||
//
|
||||
// To compute the canonical ordering of a set of DNS names, start by
|
||||
// sorting the names according to their most significant (rightmost)
|
||||
// labels. For names in which the most significant label is identical,
|
||||
// continue sorting according to their next most significant label, and
|
||||
// so forth.
|
||||
//
|
||||
// For example, the following names are sorted in canonical DNS name
|
||||
// order. The most significant label is "example". At this level,
|
||||
// "example" sorts first, followed by names ending in "a.example", then
|
||||
// by names ending "z.example". The names within each level are sorted
|
||||
// in the same way.
|
||||
//
|
||||
// example
|
||||
// a.example
|
||||
// yljkjljk.a.example
|
||||
// Z.a.example
|
||||
// zABC.a.EXAMPLE
|
||||
// z.example
|
||||
// \001.z.example
|
||||
// *.z.example
|
||||
// \200.z.example
|
||||
/// RFC 4034 DNSSEC Resource Records March 2005
|
||||
///
|
||||
/// ```text
|
||||
/// 6.1. Canonical DNS Name Order
|
||||
///
|
||||
/// For the purposes of DNS security, owner names are ordered by treating
|
||||
/// individual labels as unsigned left-justified octet strings. The
|
||||
/// absence of a octet sorts before a zero value octet, and uppercase
|
||||
/// US-ASCII letters are treated as if they were lowercase US-ASCII
|
||||
/// letters.
|
||||
///
|
||||
/// To compute the canonical ordering of a set of DNS names, start by
|
||||
/// sorting the names according to their most significant (rightmost)
|
||||
/// labels. For names in which the most significant label is identical,
|
||||
/// continue sorting according to their next most significant label, and
|
||||
/// so forth.
|
||||
///
|
||||
/// For example, the following names are sorted in canonical DNS name
|
||||
/// order. The most significant label is "example". At this level,
|
||||
/// "example" sorts first, followed by names ending in "a.example", then
|
||||
/// by names ending "z.example". The names within each level are sorted
|
||||
/// in the same way.
|
||||
///
|
||||
/// example
|
||||
/// a.example
|
||||
/// yljkjljk.a.example
|
||||
/// Z.a.example
|
||||
/// zABC.a.EXAMPLE
|
||||
/// z.example
|
||||
/// \001.z.example
|
||||
/// *.z.example
|
||||
/// \200.z.example
|
||||
/// ```
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
if self.labels.is_empty() && other.labels.is_empty() { return Ordering::Equal }
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
use std::collections::HashMap;
|
||||
use std::collections::BTreeMap;
|
||||
use std::io::Read;
|
||||
use std::fs::File;
|
||||
|
||||
@ -23,104 +23,106 @@ use ::authority::{Authority, RrKey, ZoneType, RRSet};
|
||||
|
||||
use super::master_lex::{Lexer, Token};
|
||||
|
||||
// 5. MASTER FILES
|
||||
//
|
||||
// Master files are text files that contain RRs in text form. Since the
|
||||
// contents of a zone can be expressed in the form of a list of RRs a
|
||||
// master file is most often used to define a zone, though it can be used
|
||||
// to list a cache's contents. Hence, this section first discusses the
|
||||
// format of RRs in a master file, and then the special considerations when
|
||||
// a master file is used to create a zone in some name server.
|
||||
//
|
||||
// 5.1. Format
|
||||
//
|
||||
// The format of these files is a sequence of entries. Entries are
|
||||
// predominantly line-oriented, though parentheses can be used to continue
|
||||
// a list of items across a line boundary, and text literals can contain
|
||||
// CRLF within the text. Any combination of tabs and spaces act as a
|
||||
// delimiter between the separate items that make up an entry. The end of
|
||||
// any line in the master file can end with a comment. The comment starts
|
||||
// with a ";" (semicolon).
|
||||
//
|
||||
// The following entries are defined:
|
||||
//
|
||||
// <blank>[<comment>]
|
||||
//
|
||||
// $ORIGIN <domain-name> [<comment>]
|
||||
//
|
||||
// $INCLUDE <file-name> [<domain-name>] [<comment>]
|
||||
//
|
||||
// <domain-name><rr> [<comment>]
|
||||
//
|
||||
// <blank><rr> [<comment>]
|
||||
//
|
||||
// Blank lines, with or without comments, are allowed anywhere in the file.
|
||||
//
|
||||
// Two control entries are defined: $ORIGIN and $INCLUDE. $ORIGIN is
|
||||
// followed by a domain name, and resets the current origin for relative
|
||||
// domain names to the stated name. $INCLUDE inserts the named file into
|
||||
// the current file, and may optionally specify a domain name that sets the
|
||||
// relative domain name origin for the included file. $INCLUDE may also
|
||||
// have a comment. Note that a $INCLUDE entry never changes the relative
|
||||
// origin of the parent file, regardless of changes to the relative origin
|
||||
// made within the included file.
|
||||
//
|
||||
// The last two forms represent RRs. If an entry for an RR begins with a
|
||||
// blank, then the RR is assumed to be owned by the last stated owner. If
|
||||
// an RR entry begins with a <domain-name>, then the owner name is reset.
|
||||
//
|
||||
// <rr> contents take one of the following forms:
|
||||
//
|
||||
// [<TTL>] [<class>] <type> <RDATA>
|
||||
//
|
||||
// [<class>] [<TTL>] <type> <RDATA>
|
||||
//
|
||||
// The RR begins with optional TTL and class fields, followed by a type and
|
||||
// RDATA field appropriate to the type and class. Class and type use the
|
||||
// standard mnemonics, TTL is a decimal integer. Omitted class and TTL
|
||||
// values are default to the last explicitly stated values. Since type and
|
||||
// class mnemonics are disjoint, the parse is unique. (Note that this
|
||||
// order is different from the order used in examples and the order used in
|
||||
// the actual RRs; the given order allows easier parsing and defaulting.)
|
||||
//
|
||||
// <domain-name>s make up a large share of the data in the master file.
|
||||
// The labels in the domain name are expressed as character strings and
|
||||
// separated by dots. Quoting conventions allow arbitrary characters to be
|
||||
// stored in domain names. Domain names that end in a dot are called
|
||||
// absolute, and are taken as complete. Domain names which do not end in a
|
||||
// dot are called relative; the actual domain name is the concatenation of
|
||||
// the relative part with an origin specified in a $ORIGIN, $INCLUDE, or as
|
||||
// an argument to the master file loading routine. A relative name is an
|
||||
// error when no origin is available.
|
||||
//
|
||||
// <character-string> is expressed in one or two ways: as a contiguous set
|
||||
// of characters without interior spaces, or as a string beginning with a "
|
||||
// and ending with a ". Inside a " delimited string any character can
|
||||
// occur, except for a " itself, which must be quoted using \ (back slash).
|
||||
//
|
||||
// Because these files are text files several special encodings are
|
||||
// necessary to allow arbitrary data to be loaded. In particular:
|
||||
//
|
||||
// of the root.
|
||||
//
|
||||
// @ A free standing @ is used to denote the current origin.
|
||||
//
|
||||
// \X where X is any character other than a digit (0-9), is
|
||||
// used to quote that character so that its special meaning
|
||||
// does not apply. For example, "\." can be used to place
|
||||
// a dot character in a label.
|
||||
//
|
||||
// \DDD where each D is a digit is the octet corresponding to
|
||||
// the decimal number described by DDD. The resulting
|
||||
// octet is assumed to be text and is not checked for
|
||||
// special meaning.
|
||||
//
|
||||
// ( ) Parentheses are used to group data that crosses a line
|
||||
// boundary. In effect, line terminations are not
|
||||
// recognized within parentheses.
|
||||
//
|
||||
// ; Semicolon is used to start a comment; the remainder of
|
||||
// the line is ignored.
|
||||
/// ```text
|
||||
/// 5. MASTER FILES
|
||||
///
|
||||
/// Master files are text files that contain RRs in text form. Since the
|
||||
/// contents of a zone can be expressed in the form of a list of RRs a
|
||||
/// master file is most often used to define a zone, though it can be used
|
||||
/// to list a cache's contents. Hence, this section first discusses the
|
||||
/// format of RRs in a master file, and then the special considerations when
|
||||
/// a master file is used to create a zone in some name server.
|
||||
///
|
||||
/// 5.1. Format
|
||||
///
|
||||
/// The format of these files is a sequence of entries. Entries are
|
||||
/// predominantly line-oriented, though parentheses can be used to continue
|
||||
/// a list of items across a line boundary, and text literals can contain
|
||||
/// CRLF within the text. Any combination of tabs and spaces act as a
|
||||
/// delimiter between the separate items that make up an entry. The end of
|
||||
/// any line in the master file can end with a comment. The comment starts
|
||||
/// with a ";" (semicolon).
|
||||
///
|
||||
/// The following entries are defined:
|
||||
///
|
||||
/// <blank>[<comment>]
|
||||
///
|
||||
/// $ORIGIN <domain-name> [<comment>]
|
||||
///
|
||||
/// $INCLUDE <file-name> [<domain-name>] [<comment>]
|
||||
///
|
||||
/// <domain-name><rr> [<comment>]
|
||||
///
|
||||
/// <blank><rr> [<comment>]
|
||||
///
|
||||
/// Blank lines, with or without comments, are allowed anywhere in the file.
|
||||
///
|
||||
/// Two control entries are defined: $ORIGIN and $INCLUDE. $ORIGIN is
|
||||
/// followed by a domain name, and resets the current origin for relative
|
||||
/// domain names to the stated name. $INCLUDE inserts the named file into
|
||||
/// the current file, and may optionally specify a domain name that sets the
|
||||
/// relative domain name origin for the included file. $INCLUDE may also
|
||||
/// have a comment. Note that a $INCLUDE entry never changes the relative
|
||||
/// origin of the parent file, regardless of changes to the relative origin
|
||||
/// made within the included file.
|
||||
///
|
||||
/// The last two forms represent RRs. If an entry for an RR begins with a
|
||||
/// blank, then the RR is assumed to be owned by the last stated owner. If
|
||||
/// an RR entry begins with a <domain-name>, then the owner name is reset.
|
||||
///
|
||||
/// <rr> contents take one of the following forms:
|
||||
///
|
||||
/// [<TTL>] [<class>] <type> <RDATA>
|
||||
///
|
||||
/// [<class>] [<TTL>] <type> <RDATA>
|
||||
///
|
||||
/// The RR begins with optional TTL and class fields, followed by a type and
|
||||
/// RDATA field appropriate to the type and class. Class and type use the
|
||||
/// standard mnemonics, TTL is a decimal integer. Omitted class and TTL
|
||||
/// values are default to the last explicitly stated values. Since type and
|
||||
/// class mnemonics are disjoint, the parse is unique. (Note that this
|
||||
/// order is different from the order used in examples and the order used in
|
||||
/// the actual RRs; the given order allows easier parsing and defaulting.)
|
||||
///
|
||||
/// <domain-name>s make up a large share of the data in the master file.
|
||||
/// The labels in the domain name are expressed as character strings and
|
||||
/// separated by dots. Quoting conventions allow arbitrary characters to be
|
||||
/// stored in domain names. Domain names that end in a dot are called
|
||||
/// absolute, and are taken as complete. Domain names which do not end in a
|
||||
/// dot are called relative; the actual domain name is the concatenation of
|
||||
/// the relative part with an origin specified in a $ORIGIN, $INCLUDE, or as
|
||||
/// an argument to the master file loading routine. A relative name is an
|
||||
/// error when no origin is available.
|
||||
///
|
||||
/// <character-string> is expressed in one or two ways: as a contiguous set
|
||||
/// of characters without interior spaces, or as a string beginning with a "
|
||||
/// and ending with a ". Inside a " delimited string any character can
|
||||
/// occur, except for a " itself, which must be quoted using \ (back slash).
|
||||
///
|
||||
/// Because these files are text files several special encodings are
|
||||
/// necessary to allow arbitrary data to be loaded. In particular:
|
||||
///
|
||||
/// of the root.
|
||||
///
|
||||
/// @ A free standing @ is used to denote the current origin.
|
||||
///
|
||||
/// \X where X is any character other than a digit (0-9), is
|
||||
/// used to quote that character so that its special meaning
|
||||
/// does not apply. For example, "\." can be used to place
|
||||
/// a dot character in a label.
|
||||
///
|
||||
/// \DDD where each D is a digit is the octet corresponding to
|
||||
/// the decimal number described by DDD. The resulting
|
||||
/// octet is assumed to be text and is not checked for
|
||||
/// special meaning.
|
||||
///
|
||||
/// ( ) Parentheses are used to group data that crosses a line
|
||||
/// boundary. In effect, line terminations are not
|
||||
/// recognized within parentheses.
|
||||
///
|
||||
/// ; Semicolon is used to start a comment; the remainder of
|
||||
/// the line is ignored.
|
||||
/// ```
|
||||
pub struct Parser;
|
||||
|
||||
impl Parser {
|
||||
@ -141,7 +143,7 @@ impl Parser {
|
||||
|
||||
pub fn parse(&mut self, lexer: Lexer, origin: Option<Name>, zone_type: ZoneType, allow_update: bool) -> ParseResult<Authority> {
|
||||
let mut lexer = lexer;
|
||||
let mut records: HashMap<RrKey, RRSet> = HashMap::new();
|
||||
let mut records: BTreeMap<RrKey, RRSet> = BTreeMap::new();
|
||||
|
||||
let mut origin: Option<Name> = origin;
|
||||
let mut current_name: Option<Name> = None;
|
||||
@ -303,7 +305,6 @@ impl Parser {
|
||||
|
||||
//
|
||||
// build the Authority and return.
|
||||
records.shrink_to_fit(); // this shouldn't change once stored (replacement instead)
|
||||
Ok(Authority::new(try!(origin.ok_or(ParseError::OriginIsUndefined)), records, zone_type, allow_update))
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user