Added NSEC record type support
This commit is contained in:
parent
c388512d66
commit
cac5bf3cc9
@ -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
|
||||
|
@ -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
150
src/rr/rdata/nsec.rs
Normal 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());
|
||||
}
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user