use alloc::{borrow::ToOwned, vec::Vec};
use time::OffsetDateTime;
use super::Algorithm;
use crate::{
error::{ProtoError, ProtoResult},
rr::{DNSClass, Name, Record, RecordSet, RecordType, SerialNumber},
serialize::binary::{BinEncodable, BinEncoder, EncodeMode},
};
use super::{
SigSigner,
rdata::{RRSIG, SIG, sig},
};
pub struct TBS(Vec<u8>);
impl TBS {
pub fn from_rrsig<'a>(
rrsig: &Record<RRSIG>,
records: impl Iterator<Item = &'a Record>,
) -> ProtoResult<Self> {
Self::from_sig(rrsig.name(), rrsig.dns_class(), rrsig.data(), records)
}
pub fn from_sig<'a>(
name: &Name,
dns_class: DNSClass,
sig: &SIG,
records: impl Iterator<Item = &'a Record>,
) -> ProtoResult<Self> {
Self::new(
name,
dns_class,
sig.num_labels(),
sig.type_covered(),
sig.algorithm(),
sig.original_ttl(),
sig.sig_expiration(),
sig.sig_inception(),
sig.key_tag(),
sig.signer_name(),
records,
)
}
pub fn from_rrset(
rr_set: &RecordSet,
zone_class: DNSClass,
inception: OffsetDateTime,
expiration: OffsetDateTime,
signer: &SigSigner,
) -> ProtoResult<Self> {
Self::new(
rr_set.name(),
zone_class,
rr_set.name().num_labels(),
rr_set.record_type(),
signer.key().algorithm(),
rr_set.ttl(),
SerialNumber(expiration.unix_timestamp() as u32),
SerialNumber(inception.unix_timestamp() as u32),
signer.calculate_key_tag()?,
signer.signer_name(),
rr_set.records_without_rrsigs(),
)
}
#[allow(clippy::too_many_arguments)]
fn new<'a>(
name: &Name,
dns_class: DNSClass,
num_labels: u8,
type_covered: RecordType,
algorithm: Algorithm,
original_ttl: u32,
sig_expiration: SerialNumber,
sig_inception: SerialNumber,
key_tag: u16,
signer_name: &Name,
records: impl Iterator<Item = &'a Record>,
) -> ProtoResult<Self> {
let mut rrset: Vec<&Record> = Vec::new();
for record in records {
if dns_class == record.dns_class()
&& type_covered == record.record_type()
&& name == record.name()
{
rrset.push(record);
}
}
rrset.sort();
let name = determine_name(name, num_labels)?;
let mut buf: Vec<u8> = Vec::new();
{
let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut buf);
encoder.set_canonical_names(true);
sig::emit_pre_sig(
&mut encoder,
type_covered,
algorithm,
name.num_labels(),
original_ttl,
sig_expiration,
sig_inception,
key_tag,
signer_name,
)?;
for record in rrset {
name.to_lowercase().emit_as_canonical(&mut encoder, true)?;
type_covered.emit(&mut encoder)?;
dns_class.emit(&mut encoder)?;
encoder.emit_u32(original_ttl)?;
let rdata_length_place = encoder.place::<u16>()?;
record.data().emit(&mut encoder)?;
let length = u16::try_from(encoder.len_since_place(&rdata_length_place))
.map_err(|_| ProtoError::from("RDATA length exceeds u16::MAX"))?;
rdata_length_place.replace(&mut encoder, length)?;
}
}
Ok(Self(buf))
}
}
impl<'a> From<&'a [u8]> for TBS {
fn from(slice: &'a [u8]) -> Self {
Self(slice.to_owned())
}
}
impl AsRef<[u8]> for TBS {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
pub fn message_tbs<M: BinEncodable>(message: &M, pre_sig0: &SIG) -> ProtoResult<TBS> {
let mut buf: Vec<u8> = Vec::with_capacity(512);
let mut buf2: Vec<u8> = Vec::with_capacity(512);
{
let mut encoder: BinEncoder<'_> = BinEncoder::with_mode(&mut buf, EncodeMode::Normal);
sig::emit_pre_sig(
&mut encoder,
pre_sig0.type_covered(),
pre_sig0.algorithm(),
pre_sig0.num_labels(),
pre_sig0.original_ttl(),
pre_sig0.sig_expiration(),
pre_sig0.sig_inception(),
pre_sig0.key_tag(),
pre_sig0.signer_name(),
)?;
let mut encoder2: BinEncoder<'_> = BinEncoder::with_mode(&mut buf2, EncodeMode::Signing);
message.emit(&mut encoder2).unwrap(); }
buf.append(&mut buf2);
Ok(TBS(buf))
}
pub fn determine_name(name: &Name, num_labels: u8) -> Result<Name, ProtoError> {
let fqdn_labels = name.num_labels();
if fqdn_labels == num_labels {
return Ok(name.clone());
}
if num_labels < fqdn_labels {
let mut star_name: Name = Name::from_labels(vec![b"*" as &[u8]]).unwrap();
let rightmost = name.trim_to(num_labels as usize);
if !rightmost.is_root() {
star_name = star_name.append_name(&rightmost)?;
return Ok(star_name);
}
return Ok(star_name);
}
Err(format!("could not determine name from {name}").into())
}