From d3e8c4c913649490abddd610e95b3049c47f1fa9 Mon Sep 17 00:00:00 2001 From: David Blacka Date: Sat, 26 May 2012 23:14:12 -0400 Subject: [PATCH] Add duplicate RR detection to jdnssec-verifyzone, and a command line option to disable it. --- .../verisignlabs/dnssec/cl/VerifyZone.java | 11 ++++ .../dnssec/security/ZoneVerifier.java | 62 +++++++++++++++---- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/com/verisignlabs/dnssec/cl/VerifyZone.java b/src/com/verisignlabs/dnssec/cl/VerifyZone.java index a203e71..cbaae5d 100644 --- a/src/com/verisignlabs/dnssec/cl/VerifyZone.java +++ b/src/com/verisignlabs/dnssec/cl/VerifyZone.java @@ -48,6 +48,7 @@ public class VerifyZone extends CLBase public int startfudge = 0; public int expirefudge = 0; public boolean ignoreTime = false; + public boolean ignoreDups = false; public CLIState() { @@ -71,6 +72,10 @@ public class VerifyZone extends CLBase OptionBuilder.withLongOpt("ignore-time"); OptionBuilder.withDescription("Ignore RRSIG inception and expiration time errors."); opts.addOption(OptionBuilder.create()); + + OptionBuilder.withLongOpt("ignore-duplicate-rrs"); + OptionBuilder.withDescription("Ignore duplicate record errors."); + opts.addOption(OptionBuilder.create()); } protected void processOptions(CommandLine cli) @@ -80,6 +85,11 @@ public class VerifyZone extends CLBase ignoreTime = true; } + if (cli.hasOption("ignore-duplicate-rrs")) + { + ignoreDups = true; + } + String optstr = null; if ((optstr = cli.getOptionValue('S')) != null) { @@ -126,6 +136,7 @@ public class VerifyZone extends CLBase zoneverifier.getVerifier().setStartFudge(state.startfudge); zoneverifier.getVerifier().setExpireFudge(state.expirefudge); zoneverifier.getVerifier().setIgnoreTime(state.ignoreTime); + zoneverifier.setIgnoreDuplicateRRs(state.ignoreDups); List records = ZoneUtils.readZoneFile(state.zonefile, null); diff --git a/src/com/verisignlabs/dnssec/security/ZoneVerifier.java b/src/com/verisignlabs/dnssec/security/ZoneVerifier.java index f376643..9a50253 100644 --- a/src/com/verisignlabs/dnssec/security/ZoneVerifier.java +++ b/src/com/verisignlabs/dnssec/security/ZoneVerifier.java @@ -48,7 +48,7 @@ import org.xbill.DNS.utils.base32; * A class for whole zone DNSSEC verification. Along with cryptographically * verifying signatures, this class will also detect invalid NSEC and NSEC3 * chains. - * + * * @author David Blacka (original) * @author $Author: davidb $ * @version $Revision: 172 $ @@ -63,6 +63,7 @@ public class ZoneVerifier private Name mZoneName; private DNSSECType mDNSSECType; private NSEC3PARAMRecord mNSEC3params; + private boolean mIgnoreDuplicateRRs; private DnsSecVerifier mVerifier; private base32 mBase32; @@ -107,22 +108,48 @@ public class ZoneVerifier mVerifier = new DnsSecVerifier(); mBase32 = new base32(base32.Alphabet.BASE32HEX, false, true); mBAcmp = new ByteArrayComparator(); + mIgnoreDuplicateRRs = false; } + /** @return the DnsSecVerifier object used to verify individual RRsets. */ public DnsSecVerifier getVerifier() { return mVerifier; } + public void setIgnoreDuplicateRRs(boolean value) + { + mIgnoreDuplicateRRs = value; + } + private static String key(Name n, int type) { return n.toString() + ':' + type; } + @SuppressWarnings("rawtypes") + private boolean addRRtoRRset(RRset rrset, Record rr) + { + if (mIgnoreDuplicateRRs) + { + rrset.addRR(rr); + return true; + } + + for (Iterator i = rrset.rrs(); i.hasNext(); ) + { + Record record = (Record) i.next(); + if (rr.equals(record)) return false; + } + rrset.addRR(rr); + return true; + } + /** * Add a record to the various maps. + * @return TODO */ - private void addRR(Record r) + private boolean addRR(Record r) { Name r_name = r.getName(); int r_type = r.getType(); @@ -138,8 +165,8 @@ public class ZoneVerifier rrset = new MarkRRset(); mNSECMap.put(r_name, rrset); } - rrset.addRR(r); - return; + + return addRRtoRRset(rrset, r); } if (r_type == Type.NSEC3) @@ -151,8 +178,8 @@ public class ZoneVerifier rrset = new MarkRRset(); mNSEC3Map.put(r_name, rrset); } - rrset.addRR(r); - return; + + return addRRtoRRset(rrset, r); } // Add the name and type to the node map @@ -172,7 +199,7 @@ public class ZoneVerifier rrset = new RRset(); mRRsetMap.put(k, rrset); } - rrset.addRR(r); + return addRRtoRRset(rrset, r); } /** @@ -198,10 +225,11 @@ public class ZoneVerifier /** * Given an unsorted list of records, load the node and rrset maps, as well as * determine the NSEC3 parameters and signing type. - * + * * @param records + * @return TODO */ - private void calculateNodes(List records) + private int calculateNodes(List records) { mNodeMap = new TreeMap>(); mRRsetMap = new HashMap(); @@ -209,13 +237,19 @@ public class ZoneVerifier // The zone is unsigned until we get a clue otherwise. mDNSSECType = DNSSECType.UNSIGNED; + int errors = 0; for (Record r : records) { Name r_name = r.getName(); int r_type = r.getType(); // Add the record to the various maps. - addRR(r); + boolean res = addRR(r); + if (!res) + { + log.warning("Record '" + r + "' detected as a duplicate"); + errors++; + } // Learn some things about the zone as we do this pass. if (r_type == Type.SOA) mZoneName = r_name; @@ -229,6 +263,8 @@ public class ZoneVerifier if (mDNSSECType == DNSSECType.UNSIGNED) mDNSSECType = determineDNSSECType(r); } + + return errors; } /** @@ -290,7 +326,7 @@ public class ZoneVerifier last_cut = n; } - // check all of the RRset that should be signed + // check all of the RRsets that should be signed for (int type : typeset) { if (type == Type.RRSIG) continue; @@ -709,9 +745,9 @@ public class ZoneVerifier { int errors = 0; - calculateNodes(records); + errors += calculateNodes(records); - errors = processNodes(); + errors += processNodes(); if (mDNSSECType == DNSSECType.NSEC) {