blob: 3fede140a16880ccd0c796164e5df385c0516a44 [file] [log] [blame]
// Copyright 2015-2021 Benjamin Fry <benjaminfry@me.com>
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
//! type bit map helper definitions
use crate::error::*;
use crate::rr::RecordType;
use crate::serialize::binary::*;
use std::collections::BTreeMap;
enum BitMapReadState {
Window,
Len {
window: u8,
},
RecordType {
window: u8,
len: Restrict<u8>,
left: Restrict<u8>,
},
}
/// Encode the bit map
///
/// # Arguments
///
/// * `encoder` - the encoder to write to
/// * `type_bit_maps` - types to encode into the bitmap
pub(crate) fn encode_type_bit_maps(
encoder: &mut BinEncoder<'_>,
type_bit_maps: &[RecordType],
) -> ProtoResult<()> {
let mut hash: BTreeMap<u8, Vec<u8>> = BTreeMap::new();
let mut type_bit_maps = type_bit_maps.to_vec();
type_bit_maps.sort();
// 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_with(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);
// adding necessary space to the vector
if bit_map.len() < (index as usize + 1) {
bit_map.resize(index as usize + 1, 0_u8);
}
bit_map[index as usize] |= bit;
}
// output bitmaps
for (window, bitmap) in hash {
encoder.emit(window)?;
// the hashset should never be larger that 255 based on above logic.
encoder.emit(bitmap.len() as u8)?;
for bits in bitmap {
encoder.emit(bits)?;
}
}
Ok(())
}
/// Decodes the array of RecordTypes covered by this NSEC record
///
/// # Arguments
///
/// * `decoder` - decoder to read from
/// * `bit_map_len` - the number bytes in the bit map
///
/// # Returns
///
/// The Array of covered types
pub(crate) fn decode_type_bit_maps(
decoder: &mut BinDecoder<'_>,
bit_map_len: Restrict<usize>,
) -> ProtoResult<Vec<RecordType>> {
// 3.2.1. Type Bit Maps Encoding
//
// The encoding of the Type Bit Maps field is the same as that used by
// the NSEC RR, described in [RFC4034]. It is explained and clarified
// here for clarity.
//
// 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 bitmap of the
// window block, and up to 32 octets (256 bits) of bitmap.
//
// Blocks are present in the NSEC3 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, bit 2 to RR type 258. If a bit is set to
// 1, it indicates that an RRSet of that type is present for the
// original owner name of the NSEC3 RR. If a bit is set to 0, it
// indicates that no RRSet of that type is present for the original
// owner name of the NSEC3 RR.
//
// Since bit 0 in window block 0 refers to the non-existing RR type 0,
// it MUST be set to 0. After verification, the validator MUST ignore
// the value of bit 0 in window block 0.
//
// Bits representing Meta-TYPEs or QTYPEs as specified in Section 3.1 of
// [RFC2929] or within the range reserved for assignment only to QTYPEs
// and Meta-TYPEs MUST be set to 0, since they do not appear in zone
// data. If encountered, they must be ignored upon reading.
//
// Blocks with no types present MUST NOT be included. Trailing zero
// octets in the bitmap MUST be omitted. The length of the bitmap of
// each block is determined by the type code with the largest numerical
// 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 mut record_types: Vec<RecordType> = Vec::new();
let mut state: BitMapReadState = BitMapReadState::Window;
// loop through all the bytes in the bitmap
for _ in 0..bit_map_len.unverified(/*bounded over any length of u16*/) {
let current_byte = decoder.read_u8()?;
state = match state {
BitMapReadState::Window => BitMapReadState::Len {
window: current_byte.unverified(/*window is any valid u8,*/),
},
BitMapReadState::Len { window } => BitMapReadState::RecordType {
window,
len: current_byte,
left: current_byte,
},
BitMapReadState::RecordType { window, len, left } => {
// window is the Window Block # from above
// len is the Bitmap Length
// current_byte is the Bitmap
let mut bit_map = current_byte.unverified(/*validated and restricted in usage in following usage*/);
// for all the bits in the current_byte
for i in 0..8 {
// if the current_bytes most significant bit is set
if bit_map & 0b1000_0000 == 0b1000_0000 {
// len - left is the block in the bitmap, times 8 for the bits, + the bit in the current_byte
let low_byte: u8 = len
.checked_sub(left.unverified(/*will fail as param in this call if invalid*/))
.checked_mul(8)
.checked_add(i)
.map_err(|_| "block len or left out of bounds in NSEC(3)")?
.unverified(/*any u8 is valid at this point*/);
let rr_type: u16 = (u16::from(window) << 8) | u16::from(low_byte);
record_types.push(RecordType::from(rr_type));
}
// shift left and look at the next bit
bit_map <<= 1;
}
// move to the next section of the bit_map
let left = left
.checked_sub(1)
.map_err(|_| ProtoError::from("block left out of bounds in NSEC(3)"))?;
if left.unverified(/*comparison is safe*/) == 0 {
// we've exhausted this Window, move to the next
BitMapReadState::Window
} else {
// continue reading this Window
BitMapReadState::RecordType { window, len, left }
}
}
};
}
Ok(record_types)
}
#[cfg(test)]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]
use super::*;
#[test]
fn test_encode_decode() {
let types = vec![RecordType::A, RecordType::NS];
let mut bytes = Vec::new();
let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
assert!(encode_type_bit_maps(&mut encoder, &types).is_ok());
let bytes = encoder.into_bytes();
let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
let restrict = Restrict::new(bytes.len());
let read_bit_map = decode_type_bit_maps(&mut decoder, restrict).expect("Decoding error");
assert_eq!(types, read_bit_map);
}
}