Added NSEC record type support

This commit is contained in:
Benjamin Fry 2016-03-14 23:59:49 -07:00
parent c388512d66
commit cac5bf3cc9
6 changed files with 224 additions and 41 deletions

View File

@ -4,10 +4,11 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
- EDNS support
- Binary serialization and deserialization of all DNSSec record types
- NSEC3 parsing support
- DNSSec validation of RRSIG and DNSKEY records back to root cert
- Integration with OpenSSL (depends on fork until rust-openssl 0.7.6+ is cut)
- Binary serialization and deserialization of all DNSSec RFC4034 record types
- EDNS support
- Coveralls support added
- Partial implementation of SIG0 support for dynamic update
- SRV record support
@ -22,7 +23,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Travis build failing
### Deprecated
- See updated Client API
- See updated trust_dns::client::Client API
## 0.4.0 2015-10-17
### Added
@ -35,7 +36,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
## 0.3.1 2015-10-04
### Fixed
- Removed buffer clone during label pointer decoding (speed/memory)
- Removed a lot of superfluos clones, heavier use of Rc
- Removed a lot of unnecessary clones, heavier use of Rc
- Binary server bugs (fully functional)
## 0.3.0 2015-09-27

View File

@ -25,6 +25,7 @@ pub mod dnskey;
pub mod mx;
pub mod null;
pub mod ns;
pub mod nsec;
pub mod nsec3;
pub mod nsec3param;
pub mod opt;

150
src/rr/rdata/nsec.rs Normal file
View File

@ -0,0 +1,150 @@
/*
* Copyright (C) 2015 Benjamin Fry <benjaminfry@me.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
use ::serialize::binary::*;
use ::error::*;
use ::rr::{RData, Name};
use ::rr::rdata::nsec3;
// RFC 4034 DNSSEC Resource Records March 2005
//
// 4.1. NSEC RDATA Wire Format
//
// The RDATA of the NSEC RR is as shown below:
//
// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// / Next Domain Name /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// / Type Bit Maps /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//
// 4.1.1. The Next Domain Name Field
//
// The Next Domain field contains the next owner name (in the canonical
// ordering of the zone) that has authoritative data or contains a
// delegation point NS RRset; see Section 6.1 for an explanation of
// canonical ordering. The value of the Next Domain Name field in the
// last NSEC record in the zone is the name of the zone apex (the owner
// name of the zone's SOA RR). This indicates that the owner name of
// the NSEC RR is the last name in the canonical ordering of the zone.
//
// A sender MUST NOT use DNS name compression on the Next Domain Name
// field when transmitting an NSEC RR.
//
// Owner names of RRsets for which the given zone is not authoritative
// (such as glue records) MUST NOT be listed in the Next Domain Name
// unless at least one authoritative RRset exists at the same owner
// name.
//
// 4.1.2. The Type Bit Maps Field
//
// The Type Bit Maps field identifies the RRset types that exist at the
// NSEC RR's owner name.
//
// The RR type space is split into 256 window blocks, each representing
// the low-order 8 bits of the 16-bit RR type space. Each block that
// has at least one active RR type is encoded using a single octet
// window number (from 0 to 255), a single octet bitmap length (from 1
// to 32) indicating the number of octets used for the window block's
// bitmap, and up to 32 octets (256 bits) of bitmap.
//
// Blocks are present in the NSEC RR RDATA in increasing numerical
// order.
//
// Type Bit Maps Field = ( Window Block # | Bitmap Length | Bitmap )+
//
// where "|" denotes concatenation.
//
// Each bitmap encodes the low-order 8 bits of RR types within the
// window block, in network bit order. The first bit is bit 0. For
// window block 0, bit 1 corresponds to RR type 1 (A), bit 2 corresponds
// to RR type 2 (NS), and so forth. For window block 1, bit 1
// corresponds to RR type 257, and bit 2 to RR type 258. If a bit is
// set, it indicates that an RRset of that type is present for the NSEC
// RR's owner name. If a bit is clear, it indicates that no RRset of
// that type is present for the NSEC RR's owner name.
//
// Bits representing pseudo-types MUST be clear, as they do not appear
// in zone data. If encountered, they MUST be ignored upon being read.
//
// Blocks with no types present MUST NOT be included. Trailing zero
// octets in the bitmap MUST be omitted. The length of each block's
// bitmap is determined by the type code with the largest numerical
// value, within that block, among the set of RR types present at the
// NSEC RR's owner name. Trailing zero octets not specified MUST be
// interpreted as zero octets.
//
// The bitmap for the NSEC RR at a delegation point requires special
// attention. Bits corresponding to the delegation NS RRset and the RR
// types for which the parent zone has authoritative data MUST be set;
// bits corresponding to any non-NS RRset for which the parent is not
// authoritative MUST be clear.
//
// A zone MUST NOT include an NSEC RR for any domain name that only
// holds glue records.
//
// 4.1.3. Inclusion of Wildcard Names in NSEC RDATA
//
// If a wildcard owner name appears in a zone, the wildcard label ("*")
// is treated as a literal symbol and is treated the same as any other
// owner name for the purposes of generating NSEC RRs. Wildcard owner
// names appear in the Next Domain Name field without any wildcard
// expansion. [RFC4035] describes the impact of wildcards on
// authenticated denial of existence.
//
// NSEC { next_domain_name: Name, type_bit_maps: Vec<RecordType> },
pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<RData> {
let start_idx = decoder.index();
let next_domain_name = try!(Name::read(decoder));
let bit_map_len = rdata_length as usize - (decoder.index() - start_idx);
let record_types = try!(nsec3::decode_type_bit_maps(decoder, bit_map_len));
Ok(RData::NSEC{ next_domain_name: next_domain_name, type_bit_maps: record_types })
}
pub fn emit(encoder: &mut BinEncoder, rdata: &RData) -> EncodeResult {
if let RData::NSEC{ ref next_domain_name, ref type_bit_maps } = *rdata {
try!(next_domain_name.emit(encoder));
try!(nsec3::encode_bit_maps(encoder, type_bit_maps));
Ok(())
} else {
panic!("wrong type here {:?}", rdata);
}
}
#[test]
pub fn test() {
use ::rr::RecordType;
let rdata = RData::NSEC{ next_domain_name: Name::new().label("www").label("example").label("com"),
type_bit_maps: vec![RecordType::A, RecordType::AAAA, RecordType::DS, RecordType::RRSIG] };
let mut bytes = Vec::new();
let mut encoder: BinEncoder = BinEncoder::new(&mut bytes);
assert!(emit(&mut encoder, &rdata).is_ok());
let bytes = encoder.as_bytes();
println!("bytes: {:?}", bytes);
let mut decoder: BinDecoder = BinDecoder::new(bytes);
let read_rdata = read(&mut decoder, bytes.len() as u16);
assert!(read_rdata.is_ok(), format!("error decoding: {:?}", read_rdata.unwrap_err()));
assert_eq!(rdata, read_rdata.unwrap());
}

View File

@ -67,7 +67,6 @@ use ::rr::dnssec::Nsec3HashAlgorithm;
// does not include the name of the containing zone. The length of this
// field is determined by the preceding Hash Length field.
//
//
// NSEC3{ hash_algorithm: Nsec3HashAlgorithm, opt_out: bool, iterations: u16, salt: Vec<u8>,
// next_hashed_owner_name: Vec<u8>, type_bit_maps: Vec<RecordType>},
@ -85,6 +84,15 @@ pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<RData>
let hash_len: u8 = try!(decoder.read_u8());
let next_hashed_owner_name: Vec<u8> = try!(decoder.read_vec(hash_len as usize));
let bit_map_len = rdata_length as usize - (decoder.index() - start_idx);
let record_types = try!(decode_type_bit_maps(decoder, bit_map_len));
Ok(RData::NSEC3{ hash_algorithm: hash_algorithm, opt_out: opt_out, iterations: iterations,
salt: salt, next_hashed_owner_name: next_hashed_owner_name,
type_bit_maps: record_types })
}
pub fn decode_type_bit_maps(decoder: &mut BinDecoder, bit_map_len: usize) -> DecodeResult<Vec<RecordType>> {
// 3.2.1. Type Bit Maps Encoding
//
// The encoding of the Type Bit Maps field is the same as that used by
@ -130,7 +138,6 @@ pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<RData>
// value, within that block, among the set of RR types present at the
// original owner name of the NSEC3 RR. Trailing octets not specified
// MUST be interpreted as zero octets.
let bit_map_len = rdata_length as usize - (decoder.index() - start_idx);
let mut record_types: Vec<RecordType> = Vec::new();
let mut state: BitMapState = BitMapState::ReadWindow;
@ -173,9 +180,7 @@ pub fn read(decoder: &mut BinDecoder, rdata_length: u16) -> DecodeResult<RData>
};
}
Ok(RData::NSEC3{ hash_algorithm: hash_algorithm, opt_out: opt_out, iterations: iterations,
salt: salt, next_hashed_owner_name: next_hashed_owner_name,
type_bit_maps: record_types })
Ok(record_types)
}
enum BitMapState {
@ -197,36 +202,7 @@ pub fn emit(encoder: &mut BinEncoder, rdata: &RData) -> EncodeResult {
try!(encoder.emit_vec(salt));
try!(encoder.emit(next_hashed_owner_name.len() as u8));
try!(encoder.emit_vec(next_hashed_owner_name));
let mut hash: HashMap<u8, Vec<u8>> = HashMap::new();
// collect the bitmaps
for rr_type in type_bit_maps {
let code: u16 = (*rr_type).into();
let window: u8 = (code >> 8) as u8;
let low: u8 = (code & 0x00FF) as u8;
let bit_map: &mut Vec<u8> = hash.entry(window).or_insert(Vec::new());
// len + left is the block in the bitmap, divided by 8 for the bits, + the bit in the current_byte
let index: u8 = low / 8;
let bit: u8 = 0b1000_0000 >> (low % 8);
for _ in 0..((index as usize + 1) - bit_map.len()) {
bit_map.push(0);
}
bit_map[index as usize] |= bit;
}
// output bitmaps
for (window, bitmap) in hash {
try!(encoder.emit(window));
// the hashset should never be larger that 255 based on above logic.
try!(encoder.emit(bitmap.len() as u8));
for bits in bitmap {
try!(encoder.emit(bits));
}
}
try!(encode_bit_maps(encoder, type_bit_maps));
Ok(())
} else {
@ -234,6 +210,40 @@ pub fn emit(encoder: &mut BinEncoder, rdata: &RData) -> EncodeResult {
}
}
pub fn encode_bit_maps(encoder: &mut BinEncoder, type_bit_maps: &[RecordType]) -> EncodeResult {
let mut hash: HashMap<u8, Vec<u8>> = HashMap::new();
// collect the bitmaps
for rr_type in type_bit_maps {
let code: u16 = (*rr_type).into();
let window: u8 = (code >> 8) as u8;
let low: u8 = (code & 0x00FF) as u8;
let bit_map: &mut Vec<u8> = hash.entry(window).or_insert(Vec::new());
// len + left is the block in the bitmap, divided by 8 for the bits, + the bit in the current_byte
let index: u8 = low / 8;
let bit: u8 = 0b1000_0000 >> (low % 8);
for _ in 0..((index as usize + 1) - bit_map.len()) {
bit_map.push(0);
}
bit_map[index as usize] |= bit;
}
// output bitmaps
for (window, bitmap) in hash {
try!(encoder.emit(window));
// the hashset should never be larger that 255 based on above logic.
try!(encoder.emit(bitmap.len() as u8));
for bits in bitmap {
try!(encoder.emit(bits));
}
}
Ok(())
}
#[test]
pub fn test() {
let rdata = RData::NSEC3{ hash_algorithm: Nsec3HashAlgorithm::SHA1, opt_out: true, iterations: 2,

View File

@ -278,6 +278,21 @@ pub enum RData {
// class information are normally queried using IN class protocols.
NS { nsdname: Name },
// RFC 4034 DNSSEC Resource Records March 2005
//
// 4.1. NSEC RDATA Wire Format
//
// The RDATA of the NSEC RR is as shown below:
//
// 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// / Next Domain Name /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// / Type Bit Maps /
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
NSEC { next_domain_name: Name, type_bit_maps: Vec<RecordType> },
// RFC 5155 NSEC3 March 2008
//
// 3.2. NSEC3 RDATA Wire Format
@ -588,6 +603,7 @@ impl RData {
RecordType::MX => rdata::mx::parse(tokens, origin),
RecordType::NULL => rdata::null::parse(tokens),
RecordType::NS => rdata::ns::parse(tokens, origin),
RecordType::NSEC => panic!("NSEC should be dynamically generated"),
RecordType::NSEC3 => panic!("NSEC3 should be dynamically generated"),
RecordType::NSEC3PARAM => panic!("NSEC3PARAM should be dynamically generated"),
RecordType::OPT => panic!("parsing OPT doesn't make sense"),
@ -650,6 +666,7 @@ impl RData {
RecordType::MX => {debug!("reading MX"); rdata::mx::read(decoder)},
RecordType::NULL => {debug!("reading NULL"); 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)},
@ -679,6 +696,7 @@ impl RData {
RData::MX{..} => rdata::mx::emit(encoder, self),
RData::NULL{..} => rdata::null::emit(encoder, self),
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),
@ -703,6 +721,7 @@ impl<'a> From<&'a RData> for RecordType {
RData::DNSKEY{..} => RecordType::DNSKEY,
RData::MX{..} => RecordType::MX,
RData::NS{..} => RecordType::NS,
RData::NSEC{..} => RecordType::NSEC,
RData::NSEC3{..} => RecordType::NSEC3,
RData::NSEC3PARAM{..} => RecordType::NSEC3PARAM,
RData::NULL{..} => RecordType::NULL,

View File

@ -49,8 +49,8 @@ pub enum RecordType {
MX, // 15 RFC 1035[1] Mail exchange record
// NAPTR, // 35 RFC 3403 Naming Authority Pointer
NS, // 2 RFC 1035[1] Name server record
NULL, // 0 RFC 1035[1] Null server record, for testing
// NSEC, // 47 RFC 4034 Next-Secure record
NULL, // 0 RFC 1035[1] Null server record, for testing
NSEC, // 47 RFC 4034 Next-Secure record
NSEC3, // 50 RFC 5155 NSEC record version 3
NSEC3PARAM, // 51 RFC 5155 NSEC3 parameters
OPT, // 41 RFC 6891 Option
@ -170,6 +170,7 @@ impl From<RecordType> for &'static str {
RecordType::MX => "MX",
RecordType::NULL => "NULL",
RecordType::NS => "NS",
RecordType::NSEC => "NSEC",
RecordType::NSEC3 => "NSEC3",
RecordType::NSEC3PARAM => "NSEC3PARAM",
RecordType::OPT => "OPT",
@ -206,6 +207,7 @@ impl From<RecordType> for u16 {
RecordType::MX => 15,
RecordType::NS => 2,
RecordType::NULL => 0,
RecordType::NSEC => 47,
RecordType::NSEC3 => 50,
RecordType::NSEC3PARAM => 51,
RecordType::OPT => 41,