mirror of
https://github.com/NLnetLabs/dnst.git
synced 2026-09-26 11:44:55 +02:00
Match ldns-signzone NSEC/NSEC3 RR ordering and NSEC3 formatting (#109)
This commit is contained in:
+123
-20
@@ -498,8 +498,8 @@ impl LdnsCommand for SignZone {
|
||||
hash_only: false,
|
||||
use_yyyymmddhhmmss_rrsig_format: true,
|
||||
preceed_zone_with_hash_list,
|
||||
order_rrsigs_after_the_rtype_they_cover: extra_comments,
|
||||
order_nsec3_rrs_by_unhashed_owner_name: extra_comments,
|
||||
order_rrsigs_after_the_rtype_they_cover: true,
|
||||
order_nsec3_rrs_by_unhashed_owner_name: true,
|
||||
zonefile_path,
|
||||
key_paths,
|
||||
invoked_as_ldns: true,
|
||||
@@ -970,7 +970,11 @@ impl SignZone {
|
||||
|
||||
let mut nsec3_hashes: Option<Nsec3HashMap> = None;
|
||||
|
||||
if self.use_nsec3 && (self.extra_comments || self.preceed_zone_with_hash_list) {
|
||||
if self.use_nsec3
|
||||
&& (self.extra_comments
|
||||
|| self.preceed_zone_with_hash_list
|
||||
|| self.order_nsec3_rrs_by_unhashed_owner_name)
|
||||
{
|
||||
// Create a collection of NSEC3 hashes that can later be used for
|
||||
// debug output.
|
||||
let mut hash_provider = Nsec3HashMap::new();
|
||||
@@ -1153,9 +1157,6 @@ impl SignZone {
|
||||
// compatibility with ldns-signzone, re-order them to be in canonical
|
||||
// order by unhashed owner name and so that hashed names come after
|
||||
// equivalent unhashed names.
|
||||
//
|
||||
// INCOMAPATIBILITY WARNING: Unlike ldns-signzone, we only apply this
|
||||
// ordering if `-b` is specified.
|
||||
let mut owner_rrs;
|
||||
let owner_rrs_iter: AnyOwnerRrsIter =
|
||||
if self.order_nsec3_rrs_by_unhashed_owner_name && nsec3_hashes.is_some() {
|
||||
@@ -1248,12 +1249,22 @@ impl SignZone {
|
||||
// we skip that, and we skip RRSIGs as they are output only after
|
||||
// the RRset that they cover.
|
||||
if self.order_rrsigs_after_the_rtype_they_cover {
|
||||
for rrset in owner_rrs
|
||||
.rrsets()
|
||||
.filter(|rrset| !matches!(rrset.rtype(), Rtype::SOA | Rtype::RRSIG))
|
||||
{
|
||||
for rrset in owner_rrs.rrsets().filter(|rrset| {
|
||||
!(matches!(rrset.rtype(), Rtype::SOA | Rtype::RRSIG)
|
||||
// If run as ldns-signzone we want to list the NSEC RR
|
||||
// at the end of the RRset of the apex. By default,
|
||||
// the NSEC RR would preceed the DNSKEY RRset, so we
|
||||
// need to filter it out here to manually reinsert it
|
||||
// later. This is only necessary for the NSEC RR at
|
||||
// the apex, as the ordering issue doesn't appear at
|
||||
// other locations than the apex.
|
||||
|| (self.invoked_as_ldns
|
||||
&& rrset.rtype() == Rtype::NSEC
|
||||
&& rrset.owner() == apex))
|
||||
}) {
|
||||
for rr in rrset.iter() {
|
||||
self.write_rr(&mut writer, rr)?;
|
||||
|
||||
match rr.data() {
|
||||
ZoneRecordData::Nsec3(nsec3) if self.extra_comments => {
|
||||
nsec3.comment(&mut writer, rr, nsec3_cs)?
|
||||
@@ -1276,13 +1287,52 @@ impl SignZone {
|
||||
for covering_rrsigs in owner_rrs
|
||||
.rrsets()
|
||||
.filter(|this_rrset| this_rrset.rtype() == Rtype::RRSIG)
|
||||
.map(|this_rrset| this_rrset.iter().filter(|rr| matches!(rr.data(), ZoneRecordData::Rrsig(rrsig) if rrsig.type_covered() == rrset.rtype())))
|
||||
.map(|this_rrset| {
|
||||
this_rrset.iter().filter(|rr| {
|
||||
matches!(rr.data(), ZoneRecordData::Rrsig(rrsig)
|
||||
if rrsig.type_covered() == rrset.rtype()
|
||||
&& if self.invoked_as_ldns && rr.owner() == apex {
|
||||
// Withhold an RRSIG that covers the NSEC of the apex
|
||||
// as we' reinserting them at the end of the apex' RRsets
|
||||
rrsig.type_covered() != Rtype::NSEC
|
||||
} else { true }
|
||||
)
|
||||
})
|
||||
})
|
||||
{
|
||||
for covering_rrsig_rr in covering_rrsigs {
|
||||
self.writeln_rr(&mut writer, covering_rrsig_rr)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If running as ldns-signzone, we've been withholding the NSEC and NSEC's RRSIG at
|
||||
// the apex above to reinsert them after all other RRsets at the apex. By default,
|
||||
// the DNSKEY RRset and it's RRSIG would take the rear of the RRsets at the apex.
|
||||
// This doesn't apply, if we're using NSEC3. Additionally, the NSEC RRs at other
|
||||
// places than the apex do not have the ordering issue.
|
||||
if self.invoked_as_ldns && !self.use_nsec3 && owner_rrs.owner() == apex {
|
||||
if let Some(nsec_rrset) = owner_rrs
|
||||
.rrsets()
|
||||
.find(|this_rrset| this_rrset.rtype() == Rtype::NSEC)
|
||||
{
|
||||
self.writeln_rr(&mut writer, nsec_rrset.first())?;
|
||||
}
|
||||
|
||||
if let Some(rrsig_rrset) = owner_rrs
|
||||
.rrsets()
|
||||
.find(|this_rrset| this_rrset.rtype() == Rtype::RRSIG)
|
||||
{
|
||||
for rr in rrsig_rrset.iter() {
|
||||
if matches!(rr.data(), ZoneRecordData::Rrsig(rrsig) if rrsig.type_covered() == Rtype::NSEC)
|
||||
{
|
||||
self.writeln_rr(&mut writer, rr)?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if self.extra_comments {
|
||||
writer.write_str(";\n")?;
|
||||
}
|
||||
@@ -1327,6 +1377,13 @@ impl SignZone {
|
||||
}
|
||||
}
|
||||
|
||||
if self.invoked_as_ldns {
|
||||
if let ZoneRecordData::Nsec3(nsec3) = rr.data() {
|
||||
let rr = Record::new(rr.owner(), rr.class(), rr.ttl(), LdnsNsec3(nsec3));
|
||||
return writer.write_fmt(format_args!("{}", rr.display_zonefile(DISPLAY_KIND)));
|
||||
}
|
||||
}
|
||||
|
||||
writer.write_fmt(format_args!("{}", rr.display_zonefile(DISPLAY_KIND)))
|
||||
}
|
||||
|
||||
@@ -2026,6 +2083,52 @@ impl<O, N> RecordData for YyyyMmDdHhMMSsRrsig<'_, O, N> {
|
||||
}
|
||||
}
|
||||
|
||||
//------------ LdnsNsec3 -----------------------------------------------------
|
||||
|
||||
/// A wrapper around Nsec3 to print the Nsec3 data in the exact format used by
|
||||
/// ldns-signzone with all its quirks.
|
||||
struct LdnsNsec3<'a, O>(&'a Nsec3<O>);
|
||||
|
||||
impl<O: AsRef<[u8]>> ZonefileFmt for LdnsNsec3<'_, O> {
|
||||
fn fmt(&self, p: &mut impl Formatter) -> zonefile_fmt::Result {
|
||||
// This block of code was copied from the `domain` crate impl of
|
||||
// `Zonefilefmt` for domain::rdata::nsec3::Nsec3 and adapted for
|
||||
// ldns output format.
|
||||
p.block(|p| {
|
||||
p.write_show(self.0.hash_algorithm())?;
|
||||
p.write_token(self.0.flags())?;
|
||||
p.write_comment(format_args!(
|
||||
"flags: {}",
|
||||
if self.0.opt_out() {
|
||||
"opt-out"
|
||||
} else {
|
||||
"<none>"
|
||||
}
|
||||
))?;
|
||||
p.write_token(self.0.iterations())?;
|
||||
p.write_comment("iterations")?;
|
||||
p.write_show(self.0.salt())?;
|
||||
p.write_token(format!(
|
||||
" {}",
|
||||
domain::utils::base32::encode_display_hex(&self.0.next_owner())
|
||||
.to_string()
|
||||
.to_lowercase()
|
||||
))?;
|
||||
p.write_show(self.0.types())?;
|
||||
// ldns-signzone ends its NSEC3 rtype bitmap with a trailing
|
||||
// space. Adding an empty token, because the formatter will add
|
||||
// a space as a delimiter.
|
||||
p.write_token("")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<O> RecordData for LdnsNsec3<'_, O> {
|
||||
fn rtype(&self) -> Rtype {
|
||||
Rtype::NSEC3
|
||||
}
|
||||
}
|
||||
|
||||
//-------------- Nsec3HashMap ------------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -2420,8 +2523,8 @@ mod test {
|
||||
hash_only: false,
|
||||
use_yyyymmddhhmmss_rrsig_format: true,
|
||||
preceed_zone_with_hash_list: false,
|
||||
order_rrsigs_after_the_rtype_they_cover: false,
|
||||
order_nsec3_rrs_by_unhashed_owner_name: false,
|
||||
order_rrsigs_after_the_rtype_they_cover: true,
|
||||
order_nsec3_rrs_by_unhashed_owner_name: true,
|
||||
zonefile_path: PathBuf::from("example.org.zone"),
|
||||
key_paths: Vec::from([PathBuf::from("anykey")]),
|
||||
invoked_as_ldns: true,
|
||||
@@ -2960,14 +3063,14 @@ m.root-servers.net.\t3600000\tIN\tAAAA\t2001:dc3::35
|
||||
|
||||
// (dnst) ldns-signzone -np -f - -e 20241127162422 -i 20241127162422 nsec3_optout1_example.org.zone ksk1 | grep NSEC3
|
||||
let ldns_dnst_output_stripped: &str = "\
|
||||
example.org.\t3600\tIN\tRRSIG\tNSEC3PARAM 15 2 3600 20241127162422 20241127162422 38873 example.org. 0XdDm1l2Mm8dyhtzbyQb91CmyNONs8lc9d22FUGvpjfqo8T2h0xs04x5MIfP0DjmiVnNqIyPK6sipnDqf6tCDg==\n\
|
||||
example.org.\t3600\tIN\tNSEC3PARAM\t1 1 1 -\n\
|
||||
example.org.\t3600\tIN\tRRSIG\tNSEC3PARAM 15 2 3600 20241127162422 20241127162422 38873 example.org. 0XdDm1l2Mm8dyhtzbyQb91CmyNONs8lc9d22FUGvpjfqo8T2h0xs04x5MIfP0DjmiVnNqIyPK6sipnDqf6tCDg==\n\
|
||||
93u63bg57ppj6649al2n31l92iedkjd6.example.org.\t240\tIN\tNSEC3\t1 1 1 - k71ku6aicr5jpdjoe9j7cdnlk6d5c3ue A NS SOA RRSIG DNSKEY NSEC3PARAM \n\
|
||||
93u63bg57ppj6649al2n31l92iedkjd6.example.org.\t240\tIN\tRRSIG\tNSEC3 15 3 240 20241127162422 20241127162422 38873 example.org. z4ceUmbSZiSnluFj8CDJ7B9fukCR2flTWgca4GE2xrw48+fiieH/04xCKhJmDRJUJTVkKtIYpB4p0Q4m60M1Cg==\n\
|
||||
93u63bg57ppj6649al2n31l92iedkjd6.example.org.\t240\tIN\tNSEC3\t1 1 1 - K71KU6AICR5JPDJOE9J7CDNLK6D5C3UE A NS SOA RRSIG DNSKEY NSEC3PARAM\n\
|
||||
k71ku6aicr5jpdjoe9j7cdnlk6d5c3ue.example.org.\t240\tIN\tNSEC3\t1 1 1 - ojicmhri4vp8po7h2kvej99sklqnj5p2 NS \n\
|
||||
k71ku6aicr5jpdjoe9j7cdnlk6d5c3ue.example.org.\t240\tIN\tRRSIG\tNSEC3 15 3 240 20241127162422 20241127162422 38873 example.org. HUrf7tOm3simXqpZj1oZeKX/P3eWoTTKc3fsyqfuLD6sGssXrBfpv1/LINBR9eEBjJ9rFbQXILgweS6huBL/Ag==\n\
|
||||
k71ku6aicr5jpdjoe9j7cdnlk6d5c3ue.example.org.\t240\tIN\tNSEC3\t1 1 1 - OJICMHRI4VP8PO7H2KVEJ99SKLQNJ5P2 NS\n\
|
||||
ojicmhri4vp8po7h2kvej99sklqnj5p2.example.org.\t240\tIN\tNSEC3\t1 1 1 - 93u63bg57ppj6649al2n31l92iedkjd6 NS DS RRSIG \n\
|
||||
ojicmhri4vp8po7h2kvej99sklqnj5p2.example.org.\t240\tIN\tRRSIG\tNSEC3 15 3 240 20241127162422 20241127162422 38873 example.org. NG/8jk3UHht1ZYNEjUZ4swaEHea1amF4l3jZ893oARi95oxtPVLKoinVbBbfVuoanicOgeZxUPpKWHMBR12XDA==\n\
|
||||
ojicmhri4vp8po7h2kvej99sklqnj5p2.example.org.\t240\tIN\tNSEC3\t1 1 1 - 93U63BG57PPJ6649AL2N31L92IEDKJD6 NS DS RRSIG\n\
|
||||
";
|
||||
|
||||
let res = FakeCmd::new([
|
||||
@@ -2996,14 +3099,14 @@ m.root-servers.net.\t3600000\tIN\tAAAA\t2001:dc3::35
|
||||
fn ldns_signzone_disables_minus_b_when_output_is_to_stdout() {
|
||||
let expected_output = r###"example.org.\t239\tIN\tSOA\texample.net. hostmaster.example.net. 1234567890 28800 7200 604800 238
|
||||
example.org.\t239\tIN\tRRSIG\tSOA 8 2 239 20241127162422 20241127162422 51331 example.org. XD5+Exk0KLfvLYA7y+Qs6jhF+JeESFONqZAjkSvznXdjod80W6cv9C77XeHqqod+5glGHlw9bXmVhuJ/5n056BbnDcMWF+AV4taFc/RrDcZb5A0tS6LnRWbpO9puKeLVK10FeAChCygct6/+GNiE12DDLnzKJFuyMuu+nLa2p88=
|
||||
example.org.\t238\tIN\tRRSIG\tNSEC 8 2 238 20241127162422 20241127162422 51331 example.org. AT4PDLEolpApcrYi7mcTXrqCQ6psXeZNdmFub08m6BJRs2jeW07fM11Amft53FXKgqbT23WILkEM7Raai8E8qPJoSdDCys6zYXW/NCU9Cf/oXIKdD4nxQXXWbnX4GCMN4XJy382dYnxTDssQK6lNIKKi4OvGYIxVUPthaLKJFU0=
|
||||
example.org.\t239\tIN\tDNSKEY\t257 3 8 AwEAAckp/oMmocs+pv4KsCkCciazIl2+SohAZ2/bH2viAMg3tHAPjw5YfPNErUBqMGvN4c23iBCnt9TktT5bVoQdpXyCJ+ZwmWrFxlXvXIqG8rpkwHi1xFoXWVZLrG9XYCqLVMq2cB+FgMIaX504XMGk7WQydtV1LAqLgP3B8JA2Fc1j ;{id = 51331 (ksk), size = 1024b}
|
||||
example.org.\t239\tIN\tRRSIG\tDNSKEY 8 2 239 20241127162422 20241127162422 51331 example.org. rLwqlu9fYkzAy0jM9crtw5du4rUaDVH9PI4m06lRwjSKhu1VQ1AHjRhlKy1OgUee/5LovXSRGcgNZi4wiTS5ZULTJw7UQTBRXaaNhVACENX/MoVw9SmYuDSTyvQboChmFmYSMch3Q/02VhgN+BT8F7+OdDVgsWqZUEKPVNixk/0=
|
||||
example.org.\t238\tIN\tNSEC\tsome.example.org. SOA RRSIG NSEC DNSKEY
|
||||
example.org.\t239\tIN\tDNSKEY\t257 3 8 AwEAAckp/oMmocs+pv4KsCkCciazIl2+SohAZ2/bH2viAMg3tHAPjw5YfPNErUBqMGvN4c23iBCnt9TktT5bVoQdpXyCJ+ZwmWrFxlXvXIqG8rpkwHi1xFoXWVZLrG9XYCqLVMq2cB+FgMIaX504XMGk7WQydtV1LAqLgP3B8JA2Fc1j ;{id = 51331 (ksk), size = 1024b}
|
||||
example.org.\t238\tIN\tRRSIG\tNSEC 8 2 238 20241127162422 20241127162422 51331 example.org. AT4PDLEolpApcrYi7mcTXrqCQ6psXeZNdmFub08m6BJRs2jeW07fM11Amft53FXKgqbT23WILkEM7Raai8E8qPJoSdDCys6zYXW/NCU9Cf/oXIKdD4nxQXXWbnX4GCMN4XJy382dYnxTDssQK6lNIKKi4OvGYIxVUPthaLKJFU0=
|
||||
some.example.org.\t240\tIN\tA\t1.2.3.4
|
||||
some.example.org.\t240\tIN\tRRSIG\tA 8 3 240 20241127162422 20241127162422 51331 example.org. xdVbhbaMXEyMySCOKy2yYQgU2URAOnu+jLU5py+4R8R3yVVvdl6yMjzdUD3vyxprHitJ+xLrXU/wHSQvtjSwmxVL53ztu+9wrnrhQm6nqXGLW+iw58LepdLVRlppz2WlV0CJAlLIQPJ8rw4hND3NYLJojnO8OdrgpHL89ajD4II=
|
||||
some.example.org.\t238\tIN\tRRSIG\tNSEC 8 3 238 20241127162422 20241127162422 51331 example.org. PP4tH4Y6JNymWSJebPd3zjvDrjyZXVBF8QTKxKAmbmtPacbWyIcRuI0L8+8Z1folAN2U5cUZmCaIbt5Ylaj6ab4UAYHiy0BrcF/zbNIeLRSTz4hOteencIooTDvqIqYuI9/xTVXcfJ+gVzzlIh2dJK2GW5O4+B1xR+CINLNJ/j8=
|
||||
some.example.org.\t238\tIN\tNSEC\texample.org. A RRSIG NSEC
|
||||
some.example.org.\t238\tIN\tRRSIG\tNSEC 8 3 238 20241127162422 20241127162422 51331 example.org. PP4tH4Y6JNymWSJebPd3zjvDrjyZXVBF8QTKxKAmbmtPacbWyIcRuI0L8+8Z1folAN2U5cUZmCaIbt5Ylaj6ab4UAYHiy0BrcF/zbNIeLRSTz4hOteencIooTDvqIqYuI9/xTVXcfJ+gVzzlIh2dJK2GW5O4+B1xR+CINLNJ/j8=
|
||||
"###.replace("\\t", "\t");
|
||||
|
||||
let zone_file_path =
|
||||
|
||||
Reference in New Issue
Block a user