use alloc::vec::Vec;
use core::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
dnssec::{Algorithm, DigestType},
error::ProtoResult,
rr::{RData, RecordData, RecordDataDecodable, RecordType},
serialize::binary::{
BinDecoder, BinEncodable, BinEncoder, DecodeError, Restrict, RestrictedMath,
},
};
use super::DNSSECRData;
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct CDS {
key_tag: u16,
algorithm: Option<Algorithm>,
digest_type: DigestType,
digest: Vec<u8>,
}
impl CDS {
pub fn new(
key_tag: u16,
algorithm: Option<Algorithm>,
digest_type: DigestType,
digest: Vec<u8>,
) -> Self {
Self {
key_tag,
algorithm,
digest_type,
digest,
}
}
pub fn key_tag(&self) -> u16 {
self.key_tag
}
pub fn algorithm(&self) -> Option<Algorithm> {
self.algorithm
}
pub fn is_delete(&self) -> bool {
self.algorithm.is_none()
}
pub fn digest_type(&self) -> DigestType {
self.digest_type
}
pub fn digest(&self) -> &[u8] {
&self.digest
}
}
impl From<CDS> for RData {
fn from(value: CDS) -> Self {
Self::DNSSEC(DNSSECRData::CDS(value))
}
}
impl BinEncodable for CDS {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
encoder.emit_u16(self.key_tag())?;
match self.algorithm() {
Some(algorithm) => algorithm.emit(encoder)?,
None => encoder.emit_u8(0)?,
}
encoder.emit(self.digest_type().into())?;
encoder.emit_vec(self.digest())?;
Ok(())
}
}
impl<'r> RecordDataDecodable<'r> for CDS {
fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> Result<Self, DecodeError> {
let start_idx = decoder.index();
let key_tag = decoder.read_u16()?.unverified();
let algorithm_value = decoder.read_u8()?.unverified();
let algorithm = match algorithm_value {
0 => None,
_ => Some(Algorithm::from_u8(algorithm_value)),
};
let digest_type =
DigestType::from(decoder.read_u8()?.unverified());
let bytes_read = decoder.index() - start_idx;
let left = length
.map(|u| u as usize)
.checked_sub(bytes_read)
.map_err(|len| DecodeError::IncorrectRDataLengthRead { read: bytes_read, len })?
.unverified();
let digest =
decoder.read_vec(left)?.unverified();
Ok(Self::new(key_tag, algorithm, digest_type, digest))
}
}
impl RecordData for CDS {
fn try_borrow(data: &RData) -> Option<&Self> {
match data {
RData::DNSSEC(DNSSECRData::CDS(cds)) => Some(cds),
_ => None,
}
}
fn record_type(&self) -> RecordType {
RecordType::CDS
}
fn into_rdata(self) -> RData {
RData::DNSSEC(DNSSECRData::CDS(self))
}
}
impl fmt::Display for CDS {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"{tag} {alg} {ty} {digest}",
tag = self.key_tag,
alg = self.algorithm.map(u8::from).unwrap_or(0),
ty = u8::from(self.digest_type),
digest = data_encoding::HEXUPPER_PERMISSIVE.encode(&self.digest)
)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]
use alloc::vec::Vec;
use std::println;
use crate::{
dnssec::{Algorithm, DigestType},
rr::RecordDataDecodable,
serialize::binary::{BinDecoder, BinEncodable, BinEncoder, Restrict},
};
use super::CDS;
#[test]
fn test() {
let rdata = CDS::new(
0xF00F,
Some(Algorithm::RSASHA256),
DigestType::SHA256,
vec![5, 6, 7, 8],
);
let mut bytes = Vec::new();
let mut encoder = BinEncoder::new(&mut bytes);
rdata.emit(&mut encoder).expect("error encoding");
let bytes = encoder.into_bytes();
println!("bytes: {bytes:?}");
let mut decoder = BinDecoder::new(bytes);
let read_rdata = CDS::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
.expect("error decoding");
assert_eq!(rdata, read_rdata);
}
#[test]
fn test_delete() {
let rdata = CDS::new(0, None, DigestType::Unknown(0), vec![0]);
let mut bytes = Vec::new();
let mut encoder = BinEncoder::new(&mut bytes);
rdata.emit(&mut encoder).expect("error encoding");
let bytes = encoder.into_bytes();
println!("bytes: {bytes:?}");
let mut decoder = BinDecoder::new(bytes);
let read_rdata = CDS::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
.expect("error decoding");
assert_eq!(rdata, read_rdata);
}
}