parse RRSIG record & complete signed NS test
This commit is contained in:
parent
037cf4f698
commit
2bcad2a25c
|
@ -67,6 +67,7 @@ impl Recurse {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DigOutput {
|
||||
pub flags: DigFlags,
|
||||
pub status: DigStatus,
|
||||
|
|
|
@ -359,7 +359,6 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "FIXME need to parse RRSIG record in dig's output"]
|
||||
fn signed() -> Result<()> {
|
||||
let tld_ns = NameServer::new(FQDN::ROOT)?.sign()?;
|
||||
|
||||
|
@ -382,6 +381,15 @@ mod tests {
|
|||
|
||||
assert!(output.status.is_noerror());
|
||||
|
||||
let [soa, rrsig] = output
|
||||
.answer
|
||||
.try_into()
|
||||
.expect("two records in answer section");
|
||||
|
||||
assert!(soa.is_soa());
|
||||
let rrsig = rrsig.try_into_rrsig().unwrap();
|
||||
assert_eq!(RecordType::SOA, rrsig.type_covered);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -392,6 +400,7 @@ mod tests {
|
|||
let key: Key = input.parse()?;
|
||||
|
||||
assert_eq!(1024, key.bits);
|
||||
assert_eq!(24975, key.id);
|
||||
let expected = "AwEAAdIpMlio4GJas7GbIZ9xRpzpB2pf4SxBJcsquN/0yNBPGNE2rzcFykqMAKmLwypk1/1q/EdHVa4tQ5RlK0w09CRhgSXfCaph+yLNJKpiPyuVcXKl2k0RnO4p835sgVEUIvx8qGTDo7c7DA9UBje+/3ViFKqVhOBaWyT6gHAmNVpb";
|
||||
assert_eq!(expected, key.encoded);
|
||||
|
||||
|
|
119
src/record.rs
119
src/record.rs
|
@ -8,6 +8,7 @@ use std::net::Ipv4Addr;
|
|||
use crate::{Error, Result, FQDN};
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum RecordType {
|
||||
A,
|
||||
NS,
|
||||
|
@ -24,10 +25,26 @@ impl RecordType {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for RecordType {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(input: &str) -> CoreResult<Self, Self::Err> {
|
||||
let record_type = match input {
|
||||
"A" => Self::A,
|
||||
"SOA" => Self::SOA,
|
||||
"NS" => Self::NS,
|
||||
_ => return Err(format!("unknown record type: {input}").into()),
|
||||
};
|
||||
|
||||
Ok(record_type)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub enum Record {
|
||||
A(A),
|
||||
RRSIG(RRSIG),
|
||||
SOA(SOA),
|
||||
}
|
||||
|
||||
|
@ -39,6 +56,18 @@ impl Record {
|
|||
Err(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_into_rrsig(self) -> CoreResult<RRSIG, Self> {
|
||||
if let Self::RRSIG(v) = self {
|
||||
Ok(v)
|
||||
} else {
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_soa(&self) -> bool {
|
||||
matches!(self, Self::SOA(..))
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Record {
|
||||
|
@ -53,6 +82,7 @@ impl FromStr for Record {
|
|||
let record = match record_type {
|
||||
"A" => Record::A(input.parse()?),
|
||||
"NS" => todo!(),
|
||||
"RRSIG" => Record::RRSIG(input.parse()?),
|
||||
"SOA" => Record::SOA(input.parse()?),
|
||||
_ => return Err(format!("unknown record type: {record_type}").into()),
|
||||
};
|
||||
|
@ -80,8 +110,11 @@ impl FromStr for A {
|
|||
return Err("expected 5 columns".into());
|
||||
};
|
||||
|
||||
if record_type != "A" {
|
||||
return Err(format!("tried to parse `{record_type}` record as an A record").into());
|
||||
let expected = "A";
|
||||
if record_type != expected {
|
||||
return Err(
|
||||
format!("tried to parse `{record_type}` record as an {expected} record").into(),
|
||||
);
|
||||
}
|
||||
|
||||
if class != "IN" {
|
||||
|
@ -96,6 +129,67 @@ impl FromStr for A {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Debug)]
|
||||
pub struct RRSIG {
|
||||
pub fqdn: FQDN<'static>,
|
||||
pub ttl: u32,
|
||||
pub type_covered: RecordType,
|
||||
pub algorithm: u32,
|
||||
pub labels: u32,
|
||||
pub original_ttl: u32,
|
||||
pub signature_expiration: u64,
|
||||
pub signature_inception: u64,
|
||||
pub key_tag: u32,
|
||||
pub signer_name: FQDN<'static>,
|
||||
/// base64 encoded
|
||||
pub signature: String,
|
||||
}
|
||||
|
||||
impl FromStr for RRSIG {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(input: &str) -> CoreResult<Self, Self::Err> {
|
||||
let mut columns = input.split_whitespace();
|
||||
|
||||
let [Some(fqdn), Some(ttl), Some(class), Some(record_type), Some(type_covered), Some(algorithm), Some(labels), Some(original_ttl), Some(signature_expiration), Some(signature_inception), Some(key_tag), Some(signer_name)] =
|
||||
array::from_fn(|_| columns.next())
|
||||
else {
|
||||
return Err("expected at least 12 columns".into());
|
||||
};
|
||||
|
||||
let expected = "RRSIG";
|
||||
if record_type != expected {
|
||||
return Err(
|
||||
format!("tried to parse `{record_type}` record as a {expected} record").into(),
|
||||
);
|
||||
}
|
||||
|
||||
if class != "IN" {
|
||||
return Err(format!("unknown class: {class}").into());
|
||||
}
|
||||
|
||||
let mut signature = String::new();
|
||||
for column in columns {
|
||||
signature.push_str(column);
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
fqdn: fqdn.parse()?,
|
||||
ttl: ttl.parse()?,
|
||||
type_covered: type_covered.parse()?,
|
||||
algorithm: algorithm.parse()?,
|
||||
labels: labels.parse()?,
|
||||
original_ttl: original_ttl.parse()?,
|
||||
signature_expiration: signature_expiration.parse()?,
|
||||
signature_inception: signature_inception.parse()?,
|
||||
key_tag: key_tag.parse()?,
|
||||
signer_name: signer_name.parse()?,
|
||||
signature,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
#[derive(Debug)]
|
||||
pub struct SOA {
|
||||
|
@ -178,4 +272,25 @@ mod tests {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_parse_rrsig_record() -> Result<()> {
|
||||
let input = ". 1800 IN RRSIG SOA 7 0 1800 20240306132701 20240207132701 11264 . wXpRU4elJPGYm2kgVVsIwGf1IkYJcQ3UE4mwmItWdxj0XWSWY07MO4Ll DMJgsE0u64Q/345Ck7+aQ904uLebwCvpFnsmkyCxk82XIAfHN9FiwzSy qoR/zZEvBONaej3vrvsqPwh8q/pvypLft9647HcFdwY0juzZsbrAaDAX 8WY=";
|
||||
|
||||
let rrsig: RRSIG = input.parse()?;
|
||||
|
||||
assert_eq!(FQDN::ROOT, rrsig.fqdn);
|
||||
assert_eq!(1800, rrsig.ttl);
|
||||
assert_eq!(RecordType::SOA, rrsig.type_covered);
|
||||
assert_eq!(7, rrsig.algorithm);
|
||||
assert_eq!(0, rrsig.labels);
|
||||
assert_eq!(20240306132701, rrsig.signature_expiration);
|
||||
assert_eq!(20240207132701, rrsig.signature_inception);
|
||||
assert_eq!(11264, rrsig.key_tag);
|
||||
assert_eq!(FQDN::ROOT, rrsig.signer_name);
|
||||
let expected = "wXpRU4elJPGYm2kgVVsIwGf1IkYJcQ3UE4mwmItWdxj0XWSWY07MO4LlDMJgsE0u64Q/345Ck7+aQ904uLebwCvpFnsmkyCxk82XIAfHN9FiwzSyqoR/zZEvBONaej3vrvsqPwh8q/pvypLft9647HcFdwY0juzZsbrAaDAX8WY=";
|
||||
assert_eq!(expected, rrsig.signature);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user