remove some warnings by using java 5 features
authorDavid Blacka <david@blacka.com>
Sun, 19 Apr 2009 21:12:48 +0000 (17:12 -0400)
committerDavid Blacka <david@blacka.com>
Sun, 19 Apr 2009 21:12:48 +0000 (17:12 -0400)
src/com/versign/tat/dnssec/CaptiveValidator.java
src/com/versign/tat/dnssec/DnsSecVerifier.java
src/com/versign/tat/dnssec/NSEC3ValUtils.java
src/com/versign/tat/dnssec/SMessage.java
src/com/versign/tat/dnssec/SRRset.java
src/com/versign/tat/dnssec/SignUtils.java
src/com/versign/tat/dnssec/TrustAnchorStore.java
src/com/versign/tat/dnssec/Util.java
src/com/versign/tat/dnssec/ValUtils.java

index 4e808f5..6c51d35 100644 (file)
@@ -298,6 +298,14 @@ public class CaptiveValidator {
         m.setStatus(SecurityStatus.SECURE);
     }
 
+    private void validateReferral(SMessage message, SRRset key_rrset) {
+        
+    }
+    
+    private void validateCNAMEResponse(SMessage message, SRRset key_rrset) {
+        
+    }
+    
     /**
      * Given an "ANY" response -- a response that contains an answer to a
      * qtype==ANY question, with answers. This consists of simply verifying all
@@ -675,34 +683,38 @@ public class CaptiveValidator {
 //    }
 
 
-    public byte validateMessage(SMessage message) {
+    public byte validateMessage(SMessage message, Name zone) {
 
         SRRset key_rrset = findKeys(message);
         if (key_rrset == null) {
             return SecurityStatus.BOGUS;
         }
         
-        int subtype = ValUtils.classifyResponse(message);
+        ValUtils.ResponseType subtype = ValUtils.classifyResponse(message, zone);
 
         switch (subtype) {
-        case ValUtils.POSITIVE:
+        case POSITIVE:
             // log.trace("Validating a positive response");
             validatePositiveResponse(message, key_rrset);
             break;
-        case ValUtils.NODATA:
+        case REFERRAL:
+            validateReferral(message, key_rrset);
+            break;
+        case NODATA:
             // log.trace("Validating a nodata response");
             validateNodataResponse(message, key_rrset);
             break;
-        case ValUtils.NAMEERROR:
+        case NAMEERROR:
             // log.trace("Validating a nxdomain response");
             validateNameErrorResponse(message, key_rrset);
             break;
-        case ValUtils.CNAME:
+        case CNAME:
             // log.trace("Validating a cname response");
             // forward on to the special CNAME state for this.
 //            state.state = ValEventState.CNAME_STATE;
+            validateCNAMEResponse(message, key_rrset);
             break;
-        case ValUtils.ANY:
+        case ANY:
             // log.trace("Validating a postive ANY response");
             validateAnyResponse(message, key_rrset);
             break;
index 5f21dc6..e43b987 100644 (file)
@@ -1,7 +1,5 @@
 /*
- * $Id$
- *
- * Copyright (c) 2005 VeriSign, Inc. All rights reserved.
+ * Copyright (c) 2009 VeriSign, Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -56,7 +54,7 @@ public class DnsSecVerifier
    * This is a mapping of DNSSEC algorithm numbers/private identifiers to JCA
    * algorithm identifiers.
    */
-  private HashMap mAlgorithmMap;
+  private HashMap<Integer, AlgEntry> mAlgorithmMap;
 
   private static class AlgEntry
   {
@@ -74,7 +72,7 @@ public class DnsSecVerifier
 
   public DnsSecVerifier()
   {
-    mAlgorithmMap = new HashMap();
+    mAlgorithmMap = new HashMap<Integer, AlgEntry>();
 
     // set the default algorithm map.
     mAlgorithmMap.put(new Integer(DNSSEC.RSAMD5), new AlgEntry("MD5withRSA",
@@ -105,12 +103,9 @@ public class DnsSecVerifier
 
     // For now, we just accept new identifiers for existing algoirthms.
     // FIXME: handle private identifiers.
-    List aliases = Util.parseConfigPrefix(config, "dns.algorithm.");
-
-    for (Iterator i = aliases.iterator(); i.hasNext();)
-    {
-      Util.ConfigEntry entry = (Util.ConfigEntry) i.next();
+    List<Util.ConfigEntry> aliases = Util.parseConfigPrefix(config, "dns.algorithm.");
 
+    for (Util.ConfigEntry entry : aliases) {
       Integer alg_alias = new Integer(Util.parseInt(entry.key, -1));
       Integer alg_orig = new Integer(Util.parseInt(entry.value, -1));
 
@@ -132,16 +127,14 @@ public class DnsSecVerifier
     }
 
     // for debugging purposes, log the entire algorithm map table.
-    for (Iterator i = mAlgorithmMap.keySet().iterator(); i.hasNext(); )
-    {
-      Integer alg = (Integer) i.next();
-      AlgEntry entry = (AlgEntry) mAlgorithmMap.get(alg);
+//    for (Integer alg : mAlgorithmMap.keySet()) {
+//      AlgEntry entry =  mAlgorithmMap.get(alg);
 //      if (entry == null) 
 //        log.warn("DNSSEC alg " + alg + " has a null entry!");
 //      else
 //        log.debug("DNSSEC alg " + alg + " maps to " + entry.jcaName
 //            + " (" + entry.dnssecAlg + ")");
-    }
+//    }
   }
 
   /**
@@ -154,7 +147,8 @@ public class DnsSecVerifier
    * @return A List contains a one or more DNSKEYRecord objects, or null if a
    *         matching DNSKEY could not be found.
    */
-  private List findKey(RRset dnskey_rrset, RRSIGRecord signature)
+  @SuppressWarnings("unchecked")
+private List<DNSKEYRecord> findKey(RRset dnskey_rrset, RRSIGRecord signature)
   {
     if (!signature.getSigner().equals(dnskey_rrset.getName()))
     {
@@ -167,7 +161,7 @@ public class DnsSecVerifier
     int keyid = signature.getFootprint();
     int alg = signature.getAlgorithm();
 
-    List res = new ArrayList(dnskey_rrset.size());
+    List<DNSKEYRecord> res = new ArrayList<DNSKEYRecord>(dnskey_rrset.size());
 
     for (Iterator i = dnskey_rrset.rrs(); i.hasNext();)
     {
@@ -325,7 +319,7 @@ public class DnsSecVerifier
     byte result = checkSignature(rrset, sigrec);
     if (result != SecurityStatus.SECURE) return result;
 
-    List keys = findKey(key_rrset, sigrec);
+    List<DNSKEYRecord> keys = findKey(key_rrset, sigrec);
 
     if (keys == null)
     {
@@ -335,9 +329,7 @@ public class DnsSecVerifier
 
     byte status = SecurityStatus.UNCHECKED;
 
-    for (Iterator i = keys.iterator(); i.hasNext();)
-    {
-      DNSKEYRecord key = (DNSKEYRecord) i.next();
+    for (DNSKEYRecord key : keys) {
       status = verifySignature(rrset, sigrec, key);
 
       if (status == SecurityStatus.SECURE) break;
@@ -354,7 +346,8 @@ public class DnsSecVerifier
    * @return SecurityStatus.SECURE if the rrest verified positively,
    *         SecurityStatus.BOGUS otherwise.
    */
-  public byte verify(RRset rrset, RRset key_rrset)
+  @SuppressWarnings("unchecked")
+public byte verify(RRset rrset, RRset key_rrset)
   {
     Iterator i = rrset.sigs();
 
@@ -386,7 +379,8 @@ public class DnsSecVerifier
    * @param dnskey The DNSKEY to verify with.
    * @return SecurityStatus.SECURE if the rrset verified, BOGUS otherwise.
    */
-  public byte verify(RRset rrset, DNSKEYRecord dnskey)
+  @SuppressWarnings("unchecked")
+public byte verify(RRset rrset, DNSKEYRecord dnskey)
   {
     // Iterate over RRSIGS
 
index 72674d5..8e77136 100644 (file)
@@ -37,829 +37,720 @@ import org.xbill.DNS.utils.base32;
 
 import com.versign.tat.dnssec.SignUtils.ByteArrayComparator;
 
-
-
-public class NSEC3ValUtils
-{
-
-  // FIXME: should probably refactor to handle different NSEC3 parameters more
-  // efficiently.
-  // Given a list of NSEC3 RRs, they should be grouped according to
-  // parameters. The idea is to hash and compare for each group independently,
-  // instead of having to skip NSEC3 RRs with the wrong parameters.
-
-
-  private static Name   asterisk_label = Name.fromConstantString("*");
-
-  /**
-   * This is a class to encapsulate a unique set of NSEC3 parameters:
-   * algorithm, iterations, and salt.
-   */
-  private static class NSEC3Parameters
-  {
-    public byte   alg;
-    public byte[] salt;
-    public int    iterations;
-
-    public NSEC3Parameters(NSEC3Record r)
-    {
-      alg = r.getHashAlgorithm();
-      salt = r.getSalt();
-      iterations = r.getIterations();
-    }
-
-    public boolean match(NSEC3Record r, ByteArrayComparator bac)
-    {
-      if (r.getHashAlgorithm() != alg) return false;
-      if (r.getIterations() != iterations) return false;
-
-      if (salt == null && r.getSalt() != null) return false;
-
-      if (bac == null) bac = new ByteArrayComparator();
-      return bac.compare(r.getSalt(), salt) == 0;
-    }
-  }
-
-  /**
-   * This is just a simple class to enapsulate the response to a closest
-   * encloser proof.
-   */
-  private static class CEResponse
-  {
-    public Name        closestEncloser;
-    public NSEC3Record ce_nsec3;
-    public NSEC3Record nc_nsec3;
-
-    public CEResponse(Name ce, NSEC3Record nsec3)
-    {
-      this.closestEncloser = ce;
-      this.ce_nsec3 = nsec3;
-    }
-  }
-
-  public static boolean supportsHashAlgorithm(int alg)
-  {
-    if (alg == NSEC3Record.SHA1_DIGEST_ID) return true;
-    return false;
-  }
-  
-  public static void stripUnknownAlgNSEC3s(List nsec3s)
-  {
-    if (nsec3s == null) return;
-    for (ListIterator i = nsec3s.listIterator(); i.hasNext(); )
-    {
-      NSEC3Record nsec3 = (NSEC3Record) i.next();
-      if (!supportsHashAlgorithm(nsec3.getHashAlgorithm()))
-      {
-        i.remove();
-      }
-    }
-  }
-  
-  /**
-   * Given a list of NSEC3Records that are part of a message, determine the
-   * NSEC3 parameters (hash algorithm, iterations, and salt) present. If there
-   * is more than one distinct grouping, return null;
-   * 
-   * @param nsec3s A list of NSEC3Record object.
-   * @return A set containing a number of objects (NSEC3Parameter objects)
-   *         that correspond to each distinct set of parameters, or null if
-   *         the nsec3s list was empty.
-   */
-  public static NSEC3Parameters nsec3Parameters(List nsec3s)
-  {
-    if (nsec3s == null || nsec3s.size() == 0) return null;
-
-    NSEC3Parameters params = new NSEC3Parameters((NSEC3Record) nsec3s.get(0));
-    ByteArrayComparator bac = new ByteArrayComparator();
-    
-    for (Iterator i = nsec3s.iterator(); i.hasNext();)
-    {
-      if (! params.match((NSEC3Record) i.next(), bac))
-      {
-        return null;
-      }
-    }  
-    return params;
-  }
-
-  /**
-   * In a list of NSEC3Record object pulled from a given message, find the
-   * NSEC3 that directly matches a given name, without hashing.
-   * 
-   * @param n The name in question.
-   * @param nsec3s A list of NSEC3Records from a given message.
-   * @return The matching NSEC3Record, or null if there wasn't one.
-   */
-  // private static NSEC3Record findDirectMatchingNSEC3(Name n, List nsec3s)
-  // {
-  // if (n == null || nsec3s == null) return null;
-  //
-  // for (Iterator i = nsec3s.iterator(); i.hasNext();)
-  // {
-  // NSEC3Record nsec3 = (NSEC3Record) i.next();
-  // if (n.equals(nsec3.getName())) return nsec3;
-  // }
-  //
-  // return null;
-  // }
-  /**
-   * Given a hash and an a zone name, construct an NSEC3 ownername.
-   * 
-   * @param hash The hash of an original name.
-   * @param zonename The zone to use in constructing the NSEC3 name.
-   * @return The NSEC3 name.
-   */
-  private static Name hashName(byte[] hash, Name zonename)
-  {
-    try
-    {
-      return new Name(base32.toString(hash).toLowerCase(), zonename);
-    }
-    catch (TextParseException e)
-    {
-      // Note, this should never happen.
-      return null;
-    }
-  }
-
-  /**
-   * Given a set of NSEC3 parameters, hash a name.
-   * 
-   * @param name The name to hash.
-   * @param params The parameters to hash with.
-   * @return The hash.
-   */
-  private static byte[] hash(Name name, NSEC3Parameters params)
-  {
-    try
-    {
-      return NSEC3Record.hash(name,
-          params.alg,
-          params.iterations,
-          params.salt);
-    }
-    catch (NoSuchAlgorithmException e)
-    {
-//      st_log.debug("Did not recognize hash algorithm: " + params.alg);
-      return null;
-    }
-  }
-
-  /**
-   * Given the name of a closest encloser, return the name *.closest_encloser.
-   * 
-   * @param closestEncloser The name to start with.
-   * @return The wildcard name.
-   */
-  private static Name ceWildcard(Name closestEncloser)
-  {
-    try
-    {
-      Name wc = Name.concatenate(asterisk_label, closestEncloser);
-      return wc;
-    }
-    catch (NameTooLongException e)
-    {
-      return null;
-    }
-  }
-
-  /**
-   * Given a qname and its proven closest encloser, calculate the "next
-   * closest" name. Basically, this is the name that is one label longer than
-   * the closest encloser that is still a subdomain of qname.
-   * 
-   * @param qname The qname.
-   * @param closestEncloser The closest encloser name.
-   * @return The next closer name.
-   */
-  private static Name nextClosest(Name qname, Name closestEncloser)
-  {
-    int strip = qname.labels() - closestEncloser.labels() - 1;
-    return (strip > 0) ? new Name(qname, strip) : qname;
-  }
-
-  /**
-   * Find the NSEC3Record that matches a hash of a name.
-   * 
-   * @param hash The pre-calculated hash of a name.
-   * @param zonename The name of the zone that the NSEC3s are from.
-   * @param nsec3s A list of NSEC3Records from a given message.
-   * @param params The parameters used for calculating the hash.
-   * @param bac An already allocated ByteArrayComparator, for reuse. This may
-   *          be null.
-   * 
-   * @return The matching NSEC3Record, if one is present.
-   */
-  private static NSEC3Record findMatchingNSEC3(byte[] hash, Name zonename,
-      List nsec3s, NSEC3Parameters params, ByteArrayComparator bac)
-  {
-    Name n = hashName(hash, zonename);
-
-    for (Iterator i = nsec3s.iterator(); i.hasNext();)
-    {
-      NSEC3Record nsec3 = (NSEC3Record) i.next();
-      // Skip nsec3 records that are using different parameters.
-      if (!params.match(nsec3, bac)) continue;
-      if (n.equals(nsec3.getName())) return nsec3;
-    }
-    return null;
-  }
-
-  /**
-   * Given a hash and a candidate NSEC3Record, determine if that NSEC3Record
-   * covers the hash. Covers specifically means that the hash is in between
-   * the owner and next hashes and does not equal either.
-   * 
-   * @param nsec3 The candidate NSEC3Record.
-   * @param hash The precalculated hash.
-   * @param bac An already allocated comparator. This may be null.
-   * @return True if the NSEC3Record covers the hash.
-   */
-  private static boolean nsec3Covers(NSEC3Record nsec3, byte[] hash,
-      ByteArrayComparator bac)
-  {
-    byte[] owner = nsec3.getOwner();
-    byte[] next = nsec3.getNext();
-
-    // This is the "normal case: owner < next and owner < hash < next
-    if (bac.compare(owner, hash) < 0 && bac.compare(hash, next) < 0)
-      return true;
-
-    // this is the end of zone case: next < owner && hash > owner || hash <
-    // next
-    if (bac.compare(next, owner) <= 0
-        && (bac.compare(hash, next) < 0 || bac.compare(owner, hash) < 0))
-      return true;
-     
-    // Otherwise, the NSEC3 does not cover the hash.
-    return false;
-  }
-
-  /**
-   * Given a pre-hashed name, find a covering NSEC3 from among a list of
-   * NSEC3s.
-   * 
-   * @param hash The hash to consider.
-   * @param zonename The name of the zone.
-   * @param nsec3s The list of NSEC3s present in a message.
-   * @param params The NSEC3 parameters used to generate the hash -- NSEC3s
-   *          that do not use those parameters will be skipped.
-   * 
-   * @return A covering NSEC3 if one is present, null otherwise.
-   */
-  private static NSEC3Record findCoveringNSEC3(byte[] hash, Name zonename,
-      List nsec3s, NSEC3Parameters params, ByteArrayComparator bac)
-  {
-    ByteArrayComparator comparator = new ByteArrayComparator();
-
-    for (Iterator i = nsec3s.iterator(); i.hasNext();)
-    {
-      NSEC3Record nsec3 = (NSEC3Record) i.next();
-      if (!params.match(nsec3, bac)) continue;
-
-      if (nsec3Covers(nsec3, hash, comparator)) return nsec3;
-    }
-
-    return null;
-  }
-
-
-  /**
-   * Given a name and a list of NSEC3s, find the candidate closest encloser.
-   * This will be the first ancestor of 'name' (including itself) to have a
-   * matching NSEC3 RR.
-   * 
-   * @param name The name the start with.
-   * @param zonename The name of the zone that the NSEC3s came from.
-   * @param nsec3s The list of NSEC3s.
-   * @param nsec3params The NSEC3 parameters.
-   * @param bac A pre-allocated comparator. May be null.
-   * 
-   * @return A CEResponse containing the closest encloser name and the NSEC3
-   *         RR that matched it, or null if there wasn't one.
-   */
-  private static CEResponse findClosestEncloser(Name name, Name zonename,
-      List nsec3s, NSEC3Parameters params, ByteArrayComparator bac)
-  {
-    Name n = name;
-
-    NSEC3Record nsec3;
-
-    // This scans from longest name to shortest, so the first match we find is
-    // the only viable candidate.
-    // FIXME: modify so that the NSEC3 matching the zone apex need not be
-    // present.
-    while (n.labels() >= zonename.labels())
-    {
-      nsec3 = findMatchingNSEC3(hash(n, params), zonename, nsec3s, params, bac);
-      if (nsec3 != null) return new CEResponse(n, nsec3);
-      n = new Name(n, 1);
-    }
-
-    return null;
-  }
-
-  /**
-   * Given a List of nsec3 RRs, find and prove the closest encloser to qname.
-   * 
-   * @param qname The qname in question.
-   * @param zonename The name of the zone that the NSEC3 RRs come from.
-   * @param nsec3s The list of NSEC3s found the this response (already
-   *          verified).
-   * @param params The NSEC3 parameters found in the response.
-   * @param bac A pre-allocated comparator. May be null.
-   * @param proveDoesNotExist If true, then if the closest encloser turns out
-   *          to be qname, then null is returned.
-   * @return null if the proof isn't completed. Otherwise, return a CEResponse
-   *         object which contains the closest encloser name and the NSEC3
-   *         that matches it.
-   */
-  private static CEResponse proveClosestEncloser(Name qname, Name zonename,
-      List nsec3s, NSEC3Parameters params, ByteArrayComparator bac,
-      boolean proveDoesNotExist)
-  {
-    CEResponse candidate = findClosestEncloser(qname,
-        zonename,
-        nsec3s,
-        params,
-        bac);
-
-    if (candidate == null)
-    {
-//      st_log.debug("proveClosestEncloser: could not find a "
-//          + "candidate for the closest encloser.");
-      return null;
+public class NSEC3ValUtils {
+
+    // FIXME: should probably refactor to handle different NSEC3 parameters more
+    // efficiently.
+    // Given a list of NSEC3 RRs, they should be grouped according to
+    // parameters. The idea is to hash and compare for each group independently,
+    // instead of having to skip NSEC3 RRs with the wrong parameters.
+
+    private static Name asterisk_label = Name.fromConstantString("*");
+
+    /**
+     * This is a class to encapsulate a unique set of NSEC3 parameters:
+     * algorithm, iterations, and salt.
+     */
+    private static class NSEC3Parameters {
+        public byte   alg;
+        public byte[] salt;
+        public int    iterations;
+
+        public NSEC3Parameters(NSEC3Record r) {
+            alg = r.getHashAlgorithm();
+            salt = r.getSalt();
+            iterations = r.getIterations();
+        }
+
+        public boolean match(NSEC3Record r, ByteArrayComparator bac) {
+            if (r.getHashAlgorithm() != alg) return false;
+            if (r.getIterations() != iterations) return false;
+
+            if (salt == null && r.getSalt() != null) return false;
+
+            if (bac == null) bac = new ByteArrayComparator();
+            return bac.compare(r.getSalt(), salt) == 0;
+        }
+    }
+
+    /**
+     * This is just a simple class to encapsulate the response to a closest
+     * encloser proof.
+     */
+    private static class CEResponse {
+        public Name        closestEncloser;
+        public NSEC3Record ce_nsec3;
+        public NSEC3Record nc_nsec3;
+
+        public CEResponse(Name ce, NSEC3Record nsec3) {
+            this.closestEncloser = ce;
+            this.ce_nsec3 = nsec3;
+        }
+    }
+
+    public static boolean supportsHashAlgorithm(int alg) {
+        if (alg == NSEC3Record.SHA1_DIGEST_ID) return true;
+        return false;
     }
 
-    if (candidate.closestEncloser.equals(qname))
-    {
-      if (proveDoesNotExist)
-      {
-//        st_log.debug("proveClosestEncloser: proved that qname existed!");
+    public static void stripUnknownAlgNSEC3s(List<NSEC3Record> nsec3s) {
+        if (nsec3s == null) return;
+        for (ListIterator<NSEC3Record> i = nsec3s.listIterator(); i.hasNext();) {
+            NSEC3Record nsec3 = i.next();
+            if (!supportsHashAlgorithm(nsec3.getHashAlgorithm())) {
+                i.remove();
+            }
+        }
+    }
+
+    /**
+     * Given a list of NSEC3Records that are part of a message, determine the
+     * NSEC3 parameters (hash algorithm, iterations, and salt) present. If there
+     * is more than one distinct grouping, return null;
+     * 
+     * @param nsec3s
+     *            A list of NSEC3Record object.
+     * @return A set containing a number of objects (NSEC3Parameter objects)
+     *         that correspond to each distinct set of parameters, or null if
+     *         the nsec3s list was empty.
+     */
+    public static NSEC3Parameters nsec3Parameters(List<NSEC3Record> nsec3s) {
+        if (nsec3s == null || nsec3s.size() == 0) return null;
+
+        NSEC3Parameters params = new NSEC3Parameters(
+                (NSEC3Record) nsec3s.get(0));
+        ByteArrayComparator bac = new ByteArrayComparator();
+
+        for (NSEC3Record nsec3 : nsec3s) {
+            if (!params.match(nsec3, bac)) return null;
+        }
+
+        return params;
+    }
+
+
+    /**
+     * Given a hash and an a zone name, construct an NSEC3 ownername.
+     * 
+     * @param hash
+     *            The hash of an original name.
+     * @param zonename
+     *            The zone to use in constructing the NSEC3 name.
+     * @return The NSEC3 name.
+     */
+    private static Name hashName(byte[] hash, Name zonename) {
+        try {
+            return new Name(base32.toString(hash).toLowerCase(), zonename);
+        } catch (TextParseException e) {
+            // Note, this should never happen.
+            return null;
+        }
+    }
+
+    /**
+     * Given a set of NSEC3 parameters, hash a name.
+     * 
+     * @param name
+     *            The name to hash.
+     * @param params
+     *            The parameters to hash with.
+     * @return The hash.
+     */
+    private static byte[] hash(Name name, NSEC3Parameters params) {
+        try {
+            return NSEC3Record.hash(name, params.alg, params.iterations,
+                                    params.salt);
+        } catch (NoSuchAlgorithmException e) {
+            // st_log.debug("Did not recognize hash algorithm: " + params.alg);
+            return null;
+        }
+    }
+
+    /**
+     * Given the name of a closest encloser, return the name *.closest_encloser.
+     * 
+     * @param closestEncloser
+     *            The name to start with.
+     * @return The wildcard name.
+     */
+    private static Name ceWildcard(Name closestEncloser) {
+        try {
+            Name wc = Name.concatenate(asterisk_label, closestEncloser);
+            return wc;
+        } catch (NameTooLongException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Given a qname and its proven closest encloser, calculate the "next
+     * closest" name. Basically, this is the name that is one label longer than
+     * the closest encloser that is still a subdomain of qname.
+     * 
+     * @param qname
+     *            The qname.
+     * @param closestEncloser
+     *            The closest encloser name.
+     * @return The next closer name.
+     */
+    private static Name nextClosest(Name qname, Name closestEncloser) {
+        int strip = qname.labels() - closestEncloser.labels() - 1;
+        return (strip > 0) ? new Name(qname, strip) : qname;
+    }
+
+    /**
+     * Find the NSEC3Record that matches a hash of a name.
+     * 
+     * @param hash
+     *            The pre-calculated hash of a name.
+     * @param zonename
+     *            The name of the zone that the NSEC3s are from.
+     * @param nsec3s
+     *            A list of NSEC3Records from a given message.
+     * @param params
+     *            The parameters used for calculating the hash.
+     * @param bac
+     *            An already allocated ByteArrayComparator, for reuse. This may
+     *            be null.
+     * 
+     * @return The matching NSEC3Record, if one is present.
+     */
+    private static NSEC3Record findMatchingNSEC3(byte[] hash, Name zonename,
+                                                 List<NSEC3Record> nsec3s,
+                                                 NSEC3Parameters params,
+                                                 ByteArrayComparator bac) {
+        Name n = hashName(hash, zonename);
+
+        for (NSEC3Record nsec3 : nsec3s) {
+            // Skip nsec3 records that are using different parameters.
+            if (!params.match(nsec3, bac)) continue;
+            if (n.equals(nsec3.getName())) return nsec3;
+        }
         return null;
-      }
-      // otherwise, we need to nothing else to prove that qname is its own
-      // closest encloser.
-      return candidate;
     }
 
-    // If the closest encloser is actually a delegation, then the response
-    // should have been a referral. If it is a DNAME, then it should have been
-    // a DNAME response.
-    if (candidate.ce_nsec3.hasType(Type.NS)
-        && !candidate.ce_nsec3.hasType(Type.SOA))
-    {
-//      st_log.debug("proveClosestEncloser: closest encloser "
-//          + "was a delegation!");
-      return null;
-    }
-    if (candidate.ce_nsec3.hasType(Type.DNAME))
-    {
-//      st_log.debug("proveClosestEncloser: closest encloser was a DNAME!");
-      return null;
-    }
-
-    // Otherwise, we need to show that the next closer name is covered.
-    Name nextClosest = nextClosest(qname, candidate.closestEncloser);
-    
-    byte[] nc_hash = hash(nextClosest, params);
-    candidate.nc_nsec3 = findCoveringNSEC3(nc_hash,
-        zonename,
-        nsec3s,
-        params,
-        bac);
-    if (candidate.nc_nsec3 == null)
-    {
-//      st_log.debug("Could not find proof that the "
-//          + "closest encloser was the closest encloser");
-      return null;
-    }
-
-    return candidate;
-  }
-
-  private static int maxIterations(int baseAlg, int keysize)
-  {
-    switch (baseAlg)
-    {
-      case DnsSecVerifier.RSA:
-        if (keysize == 0) return 2500; // the max at 4096
-        if (keysize > 2048) return 2500;
-        if (keysize > 1024) return 500;
-        if (keysize > 0) return 150;
-        break;
-      case DnsSecVerifier.DSA:
-        if (keysize == 0) return 5000; // the max at 2048;
-        if (keysize > 1024) return 5000;
-        if (keysize > 0) return 1500;
-        break;
-    }
-    return -1;
-  }
-  
-  private static boolean validIterations(NSEC3Parameters nsec3params, 
-      RRset dnskey_rrset, DnsSecVerifier verifier)
-  {
-    // for now, we return the maximum iterations based simply on the key
-    // algorithms that may have been used to sign the NSEC3 RRsets.
-    
-    int max_iterations = 0;
-    for (Iterator i = dnskey_rrset.rrs(); i.hasNext();)
-    {
-      DNSKEYRecord dnskey = (DNSKEYRecord) i.next();
-      int baseAlg = verifier.baseAlgorithm(dnskey.getAlgorithm());
-      int iters = maxIterations(baseAlg, 0);
-      max_iterations = max_iterations < iters ? iters : max_iterations;
-    }
-    
-    if (nsec3params.iterations > max_iterations) return false;
-    
-   return true; 
-  }
-  
-  /**
-   * Determine if all of the NSEC3s in a response are legally ignoreable
-   * (i.e., their presence should lead to an INSECURE result). Currently, this
-   * is solely based on iterations.
-   * 
-   * @param nsec3s The list of NSEC3s. If there is more than one set of NSEC3
-   *          parameters present, this test will not be performed.
-   * @param dnskey_rrset The set of validating DNSKEYs.
-   * @param verifier The verifier used to verify the NSEC3 RRsets. This is
-   *          solely used to map algorithm aliases.
-   * @return true if all of the NSEC3s can be legally ignored, false if not.
-   */
-  public static boolean allNSEC3sIgnoreable(List nsec3s, RRset dnskey_rrset, DnsSecVerifier verifier)
-  {
-    NSEC3Parameters params = nsec3Parameters(nsec3s);
-    if (params == null) return false;
-    
-    return !validIterations(params, dnskey_rrset, verifier);
-  }
-  
-  /**
-   * Determine if the set of NSEC3 records provided with a response prove NAME
-   * ERROR. This means that the NSEC3s prove a) the closest encloser exists,
-   * b) the direct child of the closest encloser towards qname doesn't exist,
-   * and c) *.closest encloser does not exist.
-   * 
-   * @param nsec3s The list of NSEC3s.
-   * @param qname The query name to check against.
-   * @param zonename This is the name of the zone that the NSEC3s belong to.
-   *          This may be discovered in any number of ways. A good one is to
-   *          use the signerName from the NSEC3 record's RRSIG.
-   * @return SecurityStatus.SECURE of the Name Error is proven by the NSEC3
-   *         RRs, BOGUS if not, INSECURE if all of the NSEC3s could be validly
-   *         ignored.
-   */
-  public static boolean proveNameError(List nsec3s, Name qname, Name zonename)
-  {
-    if (nsec3s == null || nsec3s.size() == 0) return false;
-
-    NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
-    if (nsec3params == null)
-    {
-//      st_log.debug("Could not find a single set of " +
-//          "NSEC3 parameters (multiple parameters present).");
-      return false;
-    }
-    
-    ByteArrayComparator bac = new ByteArrayComparator();
-
-    // First locate and prove the closest encloser to qname. We will use the
-    // variant that fails if the closest encloser turns out to be qname.
-    CEResponse ce = proveClosestEncloser(qname,
-        zonename,
-        nsec3s,
-        nsec3params,
-        bac,
-        true);
-
-    if (ce == null)
-    {
-//      st_log.debug("proveNameError: failed to prove a closest encloser.");
-      return false;
-    }
-
-    // At this point, we know that qname does not exist. Now we need to prove
-    // that the wildcard does not exist.
-    Name wc = ceWildcard(ce.closestEncloser);
-    byte[] wc_hash = hash(wc, nsec3params);
-    NSEC3Record nsec3 = findCoveringNSEC3(wc_hash,
-        zonename,
-        nsec3s,
-        nsec3params,
-        bac);
-    if (nsec3 == null)
-    {
-//      st_log.debug("proveNameError: could not prove that the "
-//          + "applicable wildcard did not exist.");
-      return false;
-    }
-
-    return true;
-  }
-
-  /**
-   * Determine if the set of NSEC3 records provided with a response prove NAME
-   * ERROR when qtype = NSEC3. This is a special case, and (currently anyway)
-   * it suffices to simply prove that the NSEC3 RRset itself does not exist,
-   * without proving that no wildcard could have generated it, etc..
-   * 
-   * @param nsec3s The list of NSEC3s.
-   * @param qname The query name to check against.
-   * @param zonename This is the name of the zone that the NSEC3s belong to.
-   *          This may be discovered in any number of ways. A good one is to
-   *          use the signerName from the NSEC3 record's RRSIG.
-   * @return true of the Name Error is proven by the NSEC3 RRs, false if not.
-   */
-  // public static boolean proveNSEC3NameError(List nsec3s, Name qname,
-  // Name zonename)
-  // {
-  // if (nsec3s == null || nsec3s.size() == 0) return false;
-  //
-  // for (Iterator i = nsec3s.iterator(); i.hasNext(); )
-  // {
-  // NSEC3Record nsec3 = (NSEC3Record) i.next();
-  //      
-  // // Convert owner and next into Names.
-  // Name owner = nsec3.getName();
-  // Name next = null;
-  // try
-  // {
-  // next = new Name(base32.toString(nsec3.getNext()), zonename);
-  // }
-  // catch (TextParseException e)
-  // {
-  // continue;
-  // }
-  //      
-  // // Now see if qname is covered by the NSEC3.
-  //      
-  // // normal case, owner < qname < next.
-  // if (owner.compareTo(next) < 0 && owner.compareTo(qname) < 0 &&
-  // next.compareTo(qname) > 0)
-  // {
-  // st_log.debug("proveNSEC3NameError: found a covering NSEC3: " + nsec3);
-  // return true;
-  // }
-  // // end-of-zone case: next < owner and qname > owner || qname < next.
-  // if (owner.compareTo(next) > 0 && (owner.compareTo(qname) < 0 ||
-  // next.compareTo(qname) > 0))
-  // {
-  // st_log.debug("proveNSEC3NameError: found a covering NSEC3: " + nsec3);
-  // return true;
-  // }
-  // }
-  //    
-  // st_log.debug("proveNSEC3NameError: did not find a covering NSEC3");
-  // return false;
-  // }
-  /**
-   * Determine if the NSEC3s provided in a response prove the NOERROR/NODATA
-   * status. There are a number of different variants to this:
-   * 
-   * 1) Normal NODATA -- qname is matched to an NSEC3 record, type is not
-   * present.
-   * 
-   * 2) ENT NODATA -- because there must be NSEC3 record for
-   * empty-non-terminals, this is the same as #1.
-   * 
-   * 3) NSEC3 ownername NODATA -- qname matched an existing, lone NSEC3
-   * ownername, but qtype was not NSEC3. NOTE: as of nsec-05, this case no
-   * longer exists.
-   * 
-   * 4) Wildcard NODATA -- A wildcard matched the name, but not the type.
-   * 
-   * 5) Opt-In DS NODATA -- the qname is covered by an opt-in span and qtype ==
-   * DS. (or maybe some future record with the same parent-side-only property)
-   * 
-   * @param nsec3s The NSEC3Records to consider.
-   * @param qname The qname in question.
-   * @param qtype The qtype in question.
-   * @param zonename The name of the zone that the NSEC3s came from.
-   * @return true if the NSEC3s prove the proposition.
-   */
-  public static boolean proveNodata(List nsec3s, Name qname, int qtype,
-      Name zonename)
-  {
-    if (nsec3s == null || nsec3s.size() == 0) return false;
-
-    NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
-    if (nsec3params == null)
-    {
-//      st_log.debug("could not find a single set of "
-//          + "NSEC3 parameters (multiple parameters present)");
-      return false;
-    }
-    ByteArrayComparator bac = new ByteArrayComparator();
-
-    NSEC3Record nsec3 = findMatchingNSEC3(hash(qname, nsec3params),
-        zonename,
-        nsec3s,
-        nsec3params,
-        bac);
-    // Cases 1 & 2.
-    if (nsec3 != null)
-    {
-      if (nsec3.hasType(qtype))
-      {
-//        st_log.debug("proveNodata: Matching NSEC3 proved that type existed!");
-        return false;
-      }
-      if (nsec3.hasType(Type.CNAME))
-      {
-//        st_log.debug("proveNodata: Matching NSEC3 proved "
-//            + "that a CNAME existed!");
+    /**
+     * Given a hash and a candidate NSEC3Record, determine if that NSEC3Record
+     * covers the hash. Covers specifically means that the hash is in between
+     * the owner and next hashes and does not equal either.
+     * 
+     * @param nsec3
+     *            The candidate NSEC3Record.
+     * @param hash
+     *            The precalculated hash.
+     * @param bac
+     *            An already allocated comparator. This may be null.
+     * @return True if the NSEC3Record covers the hash.
+     */
+    private static boolean nsec3Covers(NSEC3Record nsec3, byte[] hash,
+                                       ByteArrayComparator bac) {
+        byte[] owner = nsec3.getOwner();
+        byte[] next = nsec3.getNext();
+
+        // This is the "normal case: owner < next and owner < hash < next
+        if (bac.compare(owner, hash) < 0 && bac.compare(hash, next) < 0)
+            return true;
+
+        // this is the end of zone case: next < owner && hash > owner || hash <
+        // next
+        if (bac.compare(next, owner) <= 0
+            && (bac.compare(hash, next) < 0 || bac.compare(owner, hash) < 0))
+            return true;
+
+        // Otherwise, the NSEC3 does not cover the hash.
         return false;
-      }
-      return true;
     }
 
-    // For cases 3 - 5, we need the proven closest encloser, and it can't
-    // match qname. Although, at this point, we know that it won't since we
-    // just checked that.
-    CEResponse ce = proveClosestEncloser(qname,
-        zonename,
-        nsec3s,
-        nsec3params,
-        bac,
-        true);
-
-    // At this point, not finding a match or a proven closest encloser is a
-    // problem.
-    if (ce == null)
-    {
-//      st_log.debug("proveNodata: did not match qname, "
-//          + "nor found a proven closest encloser.");
-      return false;
-    }
+    /**
+     * Given a pre-hashed name, find a covering NSEC3 from among a list of
+     * NSEC3s.
+     * 
+     * @param hash
+     *            The hash to consider.
+     * @param zonename
+     *            The name of the zone.
+     * @param nsec3s
+     *            The list of NSEC3s present in a message.
+     * @param params
+     *            The NSEC3 parameters used to generate the hash -- NSEC3s that
+     *            do not use those parameters will be skipped.
+     * 
+     * @return A covering NSEC3 if one is present, null otherwise.
+     */
+    private static NSEC3Record findCoveringNSEC3(byte[] hash, Name zonename,
+                                                 List<NSEC3Record> nsec3s,
+                                                 NSEC3Parameters params,
+                                                 ByteArrayComparator bac) {
+        ByteArrayComparator comparator = new ByteArrayComparator();
+
+        for (NSEC3Record nsec3 : nsec3s) {
+            if (!params.match(nsec3, bac)) continue;
+            if (nsec3Covers(nsec3, hash, comparator)) return nsec3;
+        }
 
-    // Case 3: REMOVED
-
-    // Case 4:
-    Name wc = ceWildcard(ce.closestEncloser);
-    nsec3 = findMatchingNSEC3(hash(wc, nsec3params),
-        zonename,
-        nsec3s,
-        nsec3params,
-        bac);
-
-    if (nsec3 != null)
-    {
-      if (nsec3.hasType(qtype))
-      {
-//        st_log.debug("proveNodata: matching wildcard had qtype!");
-        return false;
-      }
-      return true;
+        return null;
     }
 
-    // Case 5.
-    if (qtype != Type.DS)
-    {
-//      st_log.debug("proveNodata: could not find matching NSEC3, "
-//          + "nor matching wildcard, and qtype is not DS -- no more options.");
-      return false;
-    }
+    /**
+     * Given a name and a list of NSEC3s, find the candidate closest encloser.
+     * This will be the first ancestor of 'name' (including itself) to have a
+     * matching NSEC3 RR.
+     * 
+     * @param name
+     *            The name the start with.
+     * @param zonename
+     *            The name of the zone that the NSEC3s came from.
+     * @param nsec3s
+     *            The list of NSEC3s.
+     * @param nsec3params
+     *            The NSEC3 parameters.
+     * @param bac
+     *            A pre-allocated comparator. May be null.
+     * 
+     * @return A CEResponse containing the closest encloser name and the NSEC3
+     *         RR that matched it, or null if there wasn't one.
+     */
+    private static CEResponse findClosestEncloser(Name name, Name zonename,
+                                                  List<NSEC3Record> nsec3s,
+                                                  NSEC3Parameters params,
+                                                  ByteArrayComparator bac) {
+        Name n = name;
+
+        NSEC3Record nsec3;
+
+        // This scans from longest name to shortest, so the first match we find
+        // is
+        // the only viable candidate.
+        // FIXME: modify so that the NSEC3 matching the zone apex need not be
+        // present.
+        while (n.labels() >= zonename.labels()) {
+            nsec3 = findMatchingNSEC3(hash(n, params), zonename, nsec3s,
+                                      params, bac);
+            if (nsec3 != null) return new CEResponse(n, nsec3);
+            n = new Name(n, 1);
+        }
 
-    // We need to make sure that the covering NSEC3 is opt-in.
-    if (!ce.nc_nsec3.getOptInFlag())
-    {
-//      st_log.debug("proveNodata: covering NSEC3 was not "
-//          + "opt-in in an opt-in DS NOERROR/NODATA case.");
-      return false;
+        return null;
     }
 
-    return true;
-  }
-
-  /**
-   * Prove that a positive wildcard match was appropriate (no direct match
-   * RRset).
-   * 
-   * @param nsec3s The NSEC3 records to work with.
-   * @param qname The qname that was matched to the wildard
-   * @param zonename The name of the zone that the NSEC3s come from.
-   * @param wildcard The purported wildcard that matched.
-   * @return true if the NSEC3 records prove this case.
-   */
-  public static boolean proveWildcard(List nsec3s, Name qname, Name zonename,
-      Name wildcard)
-  {
-    if (nsec3s == null || nsec3s.size() == 0) return false;
-    if (qname == null || wildcard == null) return false;
-
-    NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
-    if (nsec3params == null) 
-    {
-//      st_log.debug("couldn't find a single set of NSEC3 parameters (multiple parameters present).");
-      return false;
-    }
-    
-    ByteArrayComparator bac = new ByteArrayComparator();
-
-    // We know what the (purported) closest encloser is by just looking at the
-    // supposed generating wildcard.
-    CEResponse candidate = new CEResponse(new Name(wildcard, 1), null);
-
-    // Now we still need to prove that the original data did not exist.
-    // Otherwise, we need to show that the next closer name is covered.
-    Name nextClosest = nextClosest(qname, candidate.closestEncloser);
-    candidate.nc_nsec3 = findCoveringNSEC3(hash(nextClosest, nsec3params),
-        zonename,
-        nsec3s,
-        nsec3params,
-        bac);
-
-    if (candidate.nc_nsec3 == null)
-    {
-//      st_log.debug("proveWildcard: did not find a covering NSEC3 "
-//          + "that covered the next closer name to " + qname + " from "
-//          + candidate.closestEncloser + " (derived from wildcard " + wildcard
-//          + ")");
-      return false;
-    }
+    /**
+     * Given a List of nsec3 RRs, find and prove the closest encloser to qname.
+     * 
+     * @param qname
+     *            The qname in question.
+     * @param zonename
+     *            The name of the zone that the NSEC3 RRs come from.
+     * @param nsec3s
+     *            The list of NSEC3s found the this response (already verified).
+     * @param params
+     *            The NSEC3 parameters found in the response.
+     * @param bac
+     *            A pre-allocated comparator. May be null.
+     * @param proveDoesNotExist
+     *            If true, then if the closest encloser turns out to be qname,
+     *            then null is returned.
+     * @return null if the proof isn't completed. Otherwise, return a CEResponse
+     *         object which contains the closest encloser name and the NSEC3
+     *         that matches it.
+     */
+    private static CEResponse proveClosestEncloser(Name qname, Name zonename,
+                                                   List<NSEC3Record> nsec3s,
+                                                   NSEC3Parameters params,
+                                                   ByteArrayComparator bac,
+                                                   boolean proveDoesNotExist) {
+        CEResponse candidate = findClosestEncloser(qname, zonename, nsec3s,
+                                                   params, bac);
+
+        if (candidate == null) {
+            // st_log.debug("proveClosestEncloser: could not find a "
+            // + "candidate for the closest encloser.");
+            return null;
+        }
+
+        if (candidate.closestEncloser.equals(qname)) {
+            if (proveDoesNotExist) {
+                // st_log.debug("proveClosestEncloser: proved that qname existed!");
+                return null;
+            }
+            // otherwise, we need to nothing else to prove that qname is its own
+            // closest encloser.
+            return candidate;
+        }
+
+        // If the closest encloser is actually a delegation, then the response
+        // should have been a referral. If it is a DNAME, then it should have
+        // been
+        // a DNAME response.
+        if (candidate.ce_nsec3.hasType(Type.NS)
+            && !candidate.ce_nsec3.hasType(Type.SOA)) {
+            // st_log.debug("proveClosestEncloser: closest encloser "
+            // + "was a delegation!");
+            return null;
+        }
+        if (candidate.ce_nsec3.hasType(Type.DNAME)) {
+            // st_log.debug("proveClosestEncloser: closest encloser was a DNAME!");
+            return null;
+        }
+
+        // Otherwise, we need to show that the next closer name is covered.
+        Name nextClosest = nextClosest(qname, candidate.closestEncloser);
+
+        byte[] nc_hash = hash(nextClosest, params);
+        candidate.nc_nsec3 = findCoveringNSEC3(nc_hash, zonename, nsec3s,
+                                               params, bac);
+        if (candidate.nc_nsec3 == null) {
+            // st_log.debug("Could not find proof that the "
+            // + "closest encloser was the closest encloser");
+            return null;
+        }
+
+        return candidate;
+    }
+
+    private static int maxIterations(int baseAlg, int keysize) {
+        switch (baseAlg) {
+        case DnsSecVerifier.RSA:
+            if (keysize == 0) return 2500; // the max at 4096
+            if (keysize > 2048) return 2500;
+            if (keysize > 1024) return 500;
+            if (keysize > 0) return 150;
+            break;
+        case DnsSecVerifier.DSA:
+            if (keysize == 0) return 5000; // the max at 2048;
+            if (keysize > 1024) return 5000;
+            if (keysize > 0) return 1500;
+            break;
+        }
+        return -1;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static boolean validIterations(NSEC3Parameters nsec3params,
+                                           RRset dnskey_rrset,
+                                           DnsSecVerifier verifier) {
+        // for now, we return the maximum iterations based simply on the key
+        // algorithms that may have been used to sign the NSEC3 RRsets.
+
+        int max_iterations = 0;
+        for (Iterator i = dnskey_rrset.rrs(); i.hasNext();) {
+            DNSKEYRecord dnskey = (DNSKEYRecord) i.next();
+            int baseAlg = verifier.baseAlgorithm(dnskey.getAlgorithm());
+            int iters = maxIterations(baseAlg, 0);
+            max_iterations = max_iterations < iters ? iters : max_iterations;
+        }
+
+        if (nsec3params.iterations > max_iterations) return false;
+
+        return true;
+    }
+
+    /**
+     * Determine if all of the NSEC3s in a response are legally ignoreable
+     * (i.e., their presence should lead to an INSECURE result). Currently, this
+     * is solely based on iterations.
+     * 
+     * @param nsec3s
+     *            The list of NSEC3s. If there is more than one set of NSEC3
+     *            parameters present, this test will not be performed.
+     * @param dnskey_rrset
+     *            The set of validating DNSKEYs.
+     * @param verifier
+     *            The verifier used to verify the NSEC3 RRsets. This is solely
+     *            used to map algorithm aliases.
+     * @return true if all of the NSEC3s can be legally ignored, false if not.
+     */
+    public static boolean allNSEC3sIgnoreable(List<NSEC3Record> nsec3s,
+                                              RRset dnskey_rrset,
+                                              DnsSecVerifier verifier) {
+        NSEC3Parameters params = nsec3Parameters(nsec3s);
+        if (params == null) return false;
+
+        return !validIterations(params, dnskey_rrset, verifier);
+    }
+
+    /**
+     * Determine if the set of NSEC3 records provided with a response prove NAME
+     * ERROR. This means that the NSEC3s prove a) the closest encloser exists,
+     * b) the direct child of the closest encloser towards qname doesn't exist,
+     * and c) *.closest encloser does not exist.
+     * 
+     * @param nsec3s
+     *            The list of NSEC3s.
+     * @param qname
+     *            The query name to check against.
+     * @param zonename
+     *            This is the name of the zone that the NSEC3s belong to. This
+     *            may be discovered in any number of ways. A good one is to use
+     *            the signerName from the NSEC3 record's RRSIG.
+     * @return SecurityStatus.SECURE of the Name Error is proven by the NSEC3
+     *         RRs, BOGUS if not, INSECURE if all of the NSEC3s could be validly
+     *         ignored.
+     */
+    public static boolean proveNameError(List<NSEC3Record> nsec3s, Name qname,
+                                         Name zonename) {
+        if (nsec3s == null || nsec3s.size() == 0) return false;
+
+        NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
+        if (nsec3params == null) {
+            // st_log.debug("Could not find a single set of " +
+            // "NSEC3 parameters (multiple parameters present).");
+            return false;
+        }
+
+        ByteArrayComparator bac = new ByteArrayComparator();
+
+        // First locate and prove the closest encloser to qname. We will use the
+        // variant that fails if the closest encloser turns out to be qname.
+        CEResponse ce = proveClosestEncloser(qname, zonename, nsec3s,
+                                             nsec3params, bac, true);
+
+        if (ce == null) {
+            // st_log.debug("proveNameError: failed to prove a closest encloser.");
+            return false;
+        }
+
+        // At this point, we know that qname does not exist. Now we need to
+        // prove
+        // that the wildcard does not exist.
+        Name wc = ceWildcard(ce.closestEncloser);
+        byte[] wc_hash = hash(wc, nsec3params);
+        NSEC3Record nsec3 = findCoveringNSEC3(wc_hash, zonename, nsec3s,
+                                              nsec3params, bac);
+        if (nsec3 == null) {
+            // st_log.debug("proveNameError: could not prove that the "
+            // + "applicable wildcard did not exist.");
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Determine if the NSEC3s provided in a response prove the NOERROR/NODATA
+     * status. There are a number of different variants to this:
+     * 
+     * 1) Normal NODATA -- qname is matched to an NSEC3 record, type is not
+     * present.
+     * 
+     * 2) ENT NODATA -- because there must be NSEC3 record for
+     * empty-non-terminals, this is the same as #1.
+     * 
+     * 3) NSEC3 ownername NODATA -- qname matched an existing, lone NSEC3
+     * ownername, but qtype was not NSEC3. NOTE: as of nsec-05, this case no
+     * longer exists.
+     * 
+     * 4) Wildcard NODATA -- A wildcard matched the name, but not the type.
+     * 
+     * 5) Opt-In DS NODATA -- the qname is covered by an opt-in span and qtype
+     * == DS. (or maybe some future record with the same parent-side-only
+     * property)
+     * 
+     * @param nsec3s
+     *            The NSEC3Records to consider.
+     * @param qname
+     *            The qname in question.
+     * @param qtype
+     *            The qtype in question.
+     * @param zonename
+     *            The name of the zone that the NSEC3s came from.
+     * @return true if the NSEC3s prove the proposition.
+     */
+    public static boolean proveNodata(List<NSEC3Record> nsec3s, Name qname,
+                                      int qtype, Name zonename) {
+        if (nsec3s == null || nsec3s.size() == 0) return false;
+
+        NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
+        if (nsec3params == null) {
+            // st_log.debug("could not find a single set of "
+            // + "NSEC3 parameters (multiple parameters present)");
+            return false;
+        }
+        ByteArrayComparator bac = new ByteArrayComparator();
+
+        NSEC3Record nsec3 = findMatchingNSEC3(hash(qname, nsec3params),
+                                              zonename, nsec3s, nsec3params,
+                                              bac);
+        // Cases 1 & 2.
+        if (nsec3 != null) {
+            if (nsec3.hasType(qtype)) {
+                // st_log.debug("proveNodata: Matching NSEC3 proved that type existed!");
+                return false;
+            }
+            if (nsec3.hasType(Type.CNAME)) {
+                // st_log.debug("proveNodata: Matching NSEC3 proved "
+                // + "that a CNAME existed!");
+                return false;
+            }
+            return true;
+        }
+
+        // For cases 3 - 5, we need the proven closest encloser, and it can't
+        // match qname. Although, at this point, we know that it won't since we
+        // just checked that.
+        CEResponse ce = proveClosestEncloser(qname, zonename, nsec3s,
+                                             nsec3params, bac, true);
+
+        // At this point, not finding a match or a proven closest encloser is a
+        // problem.
+        if (ce == null) {
+            // st_log.debug("proveNodata: did not match qname, "
+            // + "nor found a proven closest encloser.");
+            return false;
+        }
+
+        // Case 3: REMOVED
+
+        // Case 4:
+        Name wc = ceWildcard(ce.closestEncloser);
+        nsec3 = findMatchingNSEC3(hash(wc, nsec3params), zonename, nsec3s,
+                                  nsec3params, bac);
+
+        if (nsec3 != null) {
+            if (nsec3.hasType(qtype)) {
+                // st_log.debug("proveNodata: matching wildcard had qtype!");
+                return false;
+            }
+            return true;
+        }
+
+        // Case 5.
+        if (qtype != Type.DS) {
+            // st_log.debug("proveNodata: could not find matching NSEC3, "
+            // +
+            // "nor matching wildcard, and qtype is not DS -- no more options.");
+            return false;
+        }
+
+        // We need to make sure that the covering NSEC3 is opt-in.
+        if (!ce.nc_nsec3.getOptInFlag()) {
+            // st_log.debug("proveNodata: covering NSEC3 was not "
+            // + "opt-in in an opt-in DS NOERROR/NODATA case.");
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Prove that a positive wildcard match was appropriate (no direct match
+     * RRset).
+     * 
+     * @param nsec3s
+     *            The NSEC3 records to work with.
+     * @param qname
+     *            The qname that was matched to the wildcard
+     * @param zonename
+     *            The name of the zone that the NSEC3s come from.
+     * @param wildcard
+     *            The purported wildcard that matched.
+     * @return true if the NSEC3 records prove this case.
+     */
+    public static boolean proveWildcard(List<NSEC3Record> nsec3s, Name qname,
+                                        Name zonename, Name wildcard) {
+        if (nsec3s == null || nsec3s.size() == 0) return false;
+        if (qname == null || wildcard == null) return false;
+
+        NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
+        if (nsec3params == null) {
+            // st_log.debug("couldn't find a single set of NSEC3 parameters (multiple parameters present).");
+            return false;
+        }
+
+        ByteArrayComparator bac = new ByteArrayComparator();
+
+        // We know what the (purported) closest encloser is by just looking at
+        // the
+        // supposed generating wildcard.
+        CEResponse candidate = new CEResponse(new Name(wildcard, 1), null);
+
+        // Now we still need to prove that the original data did not exist.
+        // Otherwise, we need to show that the next closer name is covered.
+        Name nextClosest = nextClosest(qname, candidate.closestEncloser);
+        candidate.nc_nsec3 = findCoveringNSEC3(hash(nextClosest, nsec3params),
+                                               zonename, nsec3s, nsec3params,
+                                               bac);
+
+        if (candidate.nc_nsec3 == null) {
+            // st_log.debug("proveWildcard: did not find a covering NSEC3 "
+            // + "that covered the next closer name to " + qname + " from "
+            // + candidate.closestEncloser + " (derived from wildcard " +
+            // wildcard
+            // + ")");
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Prove that a DS response either had no DS, or wasn't a delegation point.
+     * 
+     * Fundamentally there are two cases here: normal NODATA and Opt-In NODATA.
+     * 
+     * @param nsec3s
+     *            The NSEC3 RRs to examine.
+     * @param qname
+     *            The name of the DS in question.
+     * @param zonename
+     *            The name of the zone that the NSEC3 RRs come from.
+     * 
+     * @return SecurityStatus.SECURE if it was proven that there is no DS in a
+     *         secure (i.e., not opt-in) way, SecurityStatus.INSECURE if there
+     *         was no DS in an insecure (i.e., opt-in) way,
+     *         SecurityStatus.INDETERMINATE if it was clear that this wasn't a
+     *         delegation point, and SecurityStatus.BOGUS if the proofs don't
+     *         work out.
+     */
+    public static int proveNoDS(List<NSEC3Record> nsec3s, Name qname,
+                                Name zonename) {
+        if (nsec3s == null || nsec3s.size() == 0) return SecurityStatus.BOGUS;
+
+        NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
+        if (nsec3params == null) {
+            // st_log.debug("couldn't find a single set of " +
+            // "NSEC3 parameters (multiple parameters present).");
+            return SecurityStatus.BOGUS;
+        }
+        ByteArrayComparator bac = new ByteArrayComparator();
+
+        // Look for a matching NSEC3 to qname -- this is the normal NODATA case.
+        NSEC3Record nsec3 = findMatchingNSEC3(hash(qname, nsec3params),
+                                              zonename, nsec3s, nsec3params,
+                                              bac);
+
+        if (nsec3 != null) {
+            // If the matching NSEC3 has the SOA bit set, it is from the wrong
+            // zone
+            // (the child instead of the parent). If it has the DS bit set, then
+            // we
+            // were lied to.
+            if (nsec3.hasType(Type.SOA) || nsec3.hasType(Type.DS)) {
+                return SecurityStatus.BOGUS;
+            }
+            // If the NSEC3 RR doesn't have the NS bit set, then this wasn't a
+            // delegation point.
+            if (!nsec3.hasType(Type.NS)) return SecurityStatus.INDETERMINATE;
+
+            // Otherwise, this proves no DS.
+            return SecurityStatus.SECURE;
+        }
+
+        // Otherwise, we are probably in the opt-in case.
+        CEResponse ce = proveClosestEncloser(qname, zonename, nsec3s,
+                                             nsec3params, bac, true);
+        if (ce == null) {
+            return SecurityStatus.BOGUS;
+        }
+
+        // If we had the closest encloser proof, then we need to check that the
+        // covering NSEC3 was opt-in -- the proveClosestEncloser step already
+        // checked to see if the closest encloser was a delegation or DNAME.
+        if (ce.nc_nsec3.getOptInFlag()) {
+            return SecurityStatus.SECURE;
+        }
 
-    return true;
-  }
-
-  /**
-   * Prove that a DS response either had no DS, or wasn't a delegation point.
-   * 
-   * Fundamentally there are two cases here: normal NODATA and Opt-In NODATA.
-   * 
-   * @param nsec3s The NSEC3 RRs to examine.
-   * @param qname The name of the DS in question.
-   * @param zonename The name of the zone that the NSEC3 RRs come from.
-   * 
-   * @return SecurityStatus.SECURE if it was proven that there is no DS in a
-   *         secure (i.e., not opt-in) way, SecurityStatus.INSECURE if there
-   *         was no DS in an insecure (i.e., opt-in) way,
-   *         SecurityStatus.INDETERMINATE if it was clear that this wasn't a
-   *         delegation point, and SecurityStatus.BOGUS if the proofs don't
-   *         work out.
-   */
-  public static int proveNoDS(List nsec3s, Name qname, Name zonename)
-  {
-    if (nsec3s == null || nsec3s.size() == 0) return SecurityStatus.BOGUS;
-
-    NSEC3Parameters nsec3params = nsec3Parameters(nsec3s);
-    if (nsec3params == null)
-    {
-//      st_log.debug("couldn't find a single set of " +
-//          "NSEC3 parameters (multiple parameters present).");
-      return SecurityStatus.BOGUS;      
-    }
-    ByteArrayComparator bac = new ByteArrayComparator();
-
-    // Look for a matching NSEC3 to qname -- this is the normal NODATA case.
-    NSEC3Record nsec3 = findMatchingNSEC3(hash(qname, nsec3params),
-        zonename,
-        nsec3s,
-        nsec3params,
-        bac);
-
-    if (nsec3 != null)
-    {
-      // If the matching NSEC3 has the SOA bit set, it is from the wrong zone
-      // (the child instead of the parent). If it has the DS bit set, then we
-      // were lied to.
-      if (nsec3.hasType(Type.SOA) || nsec3.hasType(Type.DS))
-      {
         return SecurityStatus.BOGUS;
-      }
-      // If the NSEC3 RR doesn't have the NS bit set, then this wasn't a
-      // delegation point.
-      if (!nsec3.hasType(Type.NS)) return SecurityStatus.INDETERMINATE;
-
-      // Otherwise, this proves no DS.
-      return SecurityStatus.SECURE;
-    }
-
-    // Otherwise, we are probably in the opt-in case.
-    CEResponse ce = proveClosestEncloser(qname,
-        zonename,
-        nsec3s,
-        nsec3params,
-        bac,
-        true);
-    if (ce == null)
-    {
-      return SecurityStatus.BOGUS;
     }
 
-    // If we had the closest encloser proof, then we need to check that the
-    // covering NSEC3 was opt-in -- the proveClosestEncloser step already
-    // checked to see if the closest encloser was a delegation or DNAME.
-    if (ce.nc_nsec3.getOptInFlag())
-    {
-      return SecurityStatus.SECURE;
-    }
-
-    return SecurityStatus.BOGUS;
-  }
-
 }
index 6525117..aca257b 100644 (file)
@@ -36,369 +36,324 @@ import org.xbill.DNS.*;
 /**
  * This class represents a DNS message with resolver/validator state.
  */
-public class SMessage
-{
-  private Header          mHeader;
-
-  private Record          mQuestion;
-  private OPTRecord       mOPTRecord;
-  private List[]          mSection;
-  private SecurityStatus  mSecurityStatus;
-
-  private static SRRset[] empty_srrset_array = new SRRset[0];
-
-  public SMessage(Header h)
-  {
-    mSection = new List[3];
-    mHeader = h;
-    mSecurityStatus = new SecurityStatus();
-  }
-
-  public SMessage(int id)
-  {
-    this(new Header(id));
-  }
-
-  public SMessage()
-  {
-    this(new Header(0));
-  }
-
-  public SMessage(Message m)
-  {
-    this(m.getHeader());
-    mQuestion = m.getQuestion();
-    mOPTRecord = m.getOPT();
-
-    for (int i = Section.ANSWER; i <= Section.ADDITIONAL; i++)
-    {
-      RRset[] rrsets = m.getSectionRRsets(i);
-
-      for (int j = 0; j < rrsets.length; j++)
-      {
-        addRRset(rrsets[j], i);
-      }
-    }
-  }
-
-  public Header getHeader()
-  {
-    return mHeader;
-  }
-
-  public void setHeader(Header h)
-  {
-    mHeader = h;
-  }
-
-  public void setQuestion(Record r)
-  {
-    mQuestion = r;
-  }
-
-  public Record getQuestion()
-  {
-    return mQuestion;
-  }
-
-  public Name getQName() {
-      return getQuestion().getName();
-  }
-  
-  public int getQType() {
-      return getQuestion().getType();
-  }
-  
-  public int getQClass() {
-      return getQuestion().getDClass();
-  }
-  
-  public void setOPT(OPTRecord r)
-  {
-    mOPTRecord = r;
-  }
-
-  public OPTRecord getOPT()
-  {
-    return mOPTRecord;
-  }
-
-  public List getSectionList(int section)
-  {
-    if (section <= Section.QUESTION || section > Section.ADDITIONAL)
-      throw new IllegalArgumentException("Invalid section.");
-
-    if (mSection[section - 1] == null)
-    {
-      mSection[section - 1] = new LinkedList();
-    }
-
-    return mSection[section - 1];
-  }
-
-  public void addRRset(SRRset srrset, int section)
-  {
-    if (section <= Section.QUESTION || section > Section.ADDITIONAL)
-      throw new IllegalArgumentException("Invalid section");
-
-    if (srrset.getType() == Type.OPT)
-    {
-      mOPTRecord = (OPTRecord) srrset.first();
-      return;
-    }
-
-    List sectionList = getSectionList(section);
-    sectionList.add(srrset);
-  }
-
-  public void addRRset(RRset rrset, int section)
-  {
-    if (rrset instanceof SRRset)
-    {
-      addRRset((SRRset) rrset, section);
-      return;
-    }
-
-    SRRset srrset = new SRRset(rrset);
-    addRRset(srrset, section);
-  }
-
-  public void prependRRsets(List rrsets, int section)
-  {
-    if (section <= Section.QUESTION || section > Section.ADDITIONAL)
-      throw new IllegalArgumentException("Invalid section");
-
-    List sectionList = getSectionList(section);
-    sectionList.addAll(0, rrsets);
-  }
-
-  public SRRset[] getSectionRRsets(int section)
-  {
-    List slist = getSectionList(section);
-
-    return (SRRset[]) slist.toArray(empty_srrset_array);
-  }
-
-  public SRRset[] getSectionRRsets(int section, int qtype)
-  {
-    List slist = getSectionList(section);
-
-    if (slist.size() == 0) return new SRRset[0];
-
-    ArrayList result = new ArrayList(slist.size());
-    for (Iterator i = slist.iterator(); i.hasNext();)
-    {
-      SRRset rrset = (SRRset) i.next();
-      if (rrset.getType() == qtype) result.add(rrset);
-    }
-
-    return (SRRset[]) result.toArray(empty_srrset_array);
-  }
-
-  public void deleteRRset(SRRset rrset, int section)
-  {
-    List slist = getSectionList(section);
-
-    if (slist.size() == 0) return;
-
-    slist.remove(rrset);
-  }
-
-  public void clear(int section)
-  {
-    if (section < Section.QUESTION || section > Section.ADDITIONAL)
-      throw new IllegalArgumentException("Invalid section.");
-
-    if (section == Section.QUESTION)
-    {
-      mQuestion = null;
-      return;
-    }
-    if (section == Section.ADDITIONAL)
-    {
-      mOPTRecord = null;
-    }
-
-    mSection[section - 1] = null;
-  }
-
-  public void clear()
-  {
-    for (int s = Section.QUESTION; s <= Section.ADDITIONAL; s++)
-    {
-      clear(s);
-    }
-  }
-
-  public int getRcode()
-  {
-    // FIXME: might want to do what Message does and handle extended rcodes.
-    return mHeader.getRcode();
-  }
-
-  public int getStatus()
-  {
-    return mSecurityStatus.getStatus();
-  }
-
-  public void setStatus(byte status)
-  {
-    mSecurityStatus.setStatus(status);
-  }
-
-  public SecurityStatus getSecurityStatus()
-  {
-    return mSecurityStatus;
-  }
-  public void setSecurityStatus(SecurityStatus s)
-  {
-    if (s == null) return;
-    mSecurityStatus = s;
-  }
-  
-  public Message getMessage()
-  {
-    // Generate our new message.
-    Message m = new Message(mHeader.getID());
-
-    // Convert the header
-    // We do this for two reasons: 1) setCount() is package scope, so we can't
-    // do that, and 2) setting the header on a message after creating the
-    // message frequently gets stuff out of sync, leading to malformed wire
-    // format messages.
-    Header h = m.getHeader();
-    h.setOpcode(mHeader.getOpcode());
-    h.setRcode(mHeader.getRcode());
-    for (int i = 0; i < 16; i++)
-    {
-      if (Flags.isFlag(i)) {
-          if (mHeader.getFlag(i)) {
-              h.setFlag(i);
-          } else {
-              h.unsetFlag(i);
-          }
-      }
-    }
-
-    // Add all the records. -- this will set the counts correctly in the
-    // message header.
-
-    if (mQuestion != null)
-    {
-      m.addRecord(mQuestion, Section.QUESTION);
-    }
-
-    for (int sec = Section.ANSWER; sec <= Section.ADDITIONAL; sec++)
-    {
-      List slist = getSectionList(sec);
-      for (Iterator i = slist.iterator(); i.hasNext();)
-      {
-        SRRset rrset = (SRRset) i.next();
-        for (Iterator j = rrset.rrs(); j.hasNext();)
-        {
-          m.addRecord((Record) j.next(), sec);
+public class SMessage {
+    private Header          mHeader;
+
+    private Record          mQuestion;
+    private OPTRecord       mOPTRecord;
+    private List<SRRset>[]          mSection;
+    private SecurityStatus  mSecurityStatus;
+
+    private static SRRset[] empty_srrset_array = new SRRset[0];
+
+    @SuppressWarnings("unchecked")
+    public SMessage(Header h) {
+        mSection = (List<SRRset>[]) new List[3];
+        mHeader = h;
+        mSecurityStatus = new SecurityStatus();
+    }
+
+    public SMessage(int id) {
+        this(new Header(id));
+    }
+
+    public SMessage() {
+        this(new Header(0));
+    }
+
+    public SMessage(Message m) {
+        this(m.getHeader());
+        mQuestion = m.getQuestion();
+        mOPTRecord = m.getOPT();
+
+        for (int i = Section.ANSWER; i <= Section.ADDITIONAL; i++) {
+            RRset[] rrsets = m.getSectionRRsets(i);
+
+            for (int j = 0; j < rrsets.length; j++) {
+                addRRset(rrsets[j], i);
+            }
         }
-        for (Iterator j = rrset.sigs(); j.hasNext();)
-        {
-          m.addRecord((Record) j.next(), sec);
+    }
+
+    public Header getHeader() {
+        return mHeader;
+    }
+
+    public void setHeader(Header h) {
+        mHeader = h;
+    }
+
+    public void setQuestion(Record r) {
+        mQuestion = r;
+    }
+
+    public Record getQuestion() {
+        return mQuestion;
+    }
+
+    public Name getQName() {
+        return getQuestion().getName();
+    }
+
+    public int getQType() {
+        return getQuestion().getType();
+    }
+
+    public int getQClass() {
+        return getQuestion().getDClass();
+    }
+
+    public void setOPT(OPTRecord r) {
+        mOPTRecord = r;
+    }
+
+    public OPTRecord getOPT() {
+        return mOPTRecord;
+    }
+
+    public List<SRRset> getSectionList(int section) {
+        if (section <= Section.QUESTION || section > Section.ADDITIONAL)
+            throw new IllegalArgumentException("Invalid section.");
+
+        if (mSection[section - 1] == null) {
+            mSection[section - 1] = new LinkedList<SRRset>();
+        }
+
+        return (List<SRRset>) mSection[section - 1];
+    }
+
+    public void addRRset(SRRset srrset, int section) {
+        if (section <= Section.QUESTION || section > Section.ADDITIONAL)
+            throw new IllegalArgumentException("Invalid section");
+
+        if (srrset.getType() == Type.OPT) {
+            mOPTRecord = (OPTRecord) srrset.first();
+            return;
+        }
+
+        List<SRRset> sectionList = getSectionList(section);
+        sectionList.add(srrset);
+    }
+
+    public void addRRset(RRset rrset, int section) {
+        if (rrset instanceof SRRset) {
+            addRRset((SRRset) rrset, section);
+            return;
+        }
+
+        SRRset srrset = new SRRset(rrset);
+        addRRset(srrset, section);
+    }
+
+    public void prependRRsets(List<SRRset> rrsets, int section) {
+        if (section <= Section.QUESTION || section > Section.ADDITIONAL)
+            throw new IllegalArgumentException("Invalid section");
+
+        List<SRRset> sectionList = getSectionList(section);
+        sectionList.addAll(0, rrsets);
+    }
+
+    public SRRset[] getSectionRRsets(int section) {
+        List<SRRset> slist = getSectionList(section);
+
+        return (SRRset[]) slist.toArray(empty_srrset_array);
+    }
+
+    public SRRset[] getSectionRRsets(int section, int qtype) {
+        List<SRRset> slist = getSectionList(section);
+
+        if (slist.size() == 0) return new SRRset[0];
+
+        ArrayList<SRRset> result = new ArrayList<SRRset>(slist.size());
+        for (SRRset rrset : slist) {
+            if (rrset.getType() == qtype) result.add(rrset);
         }
-      }
-    }
-
-    if (mOPTRecord != null)
-    {
-      m.addRecord(mOPTRecord, Section.ADDITIONAL);
-    }
-
-    return m;
-  }
-
-  public int getCount(int section)
-  {
-    if (section == Section.QUESTION)
-    {
-      return mQuestion == null ? 0 : 1;
-    }
-    List sectionList = getSectionList(section);
-    if (sectionList == null) return 0;
-    if (sectionList.size() == 0) return 0;
-
-    int count = 0;
-    for (Iterator i = sectionList.iterator(); i.hasNext(); )
-    {
-      SRRset sr = (SRRset) i.next();
-      count += sr.totalSize();
-    }
-    return count;
-  }
-  
-  public String toString()
-  {
-    return getMessage().toString();
-  }
-
-  /**
-   * Find a specific (S)RRset in a given section.
-   * 
-   * @param name the name of the RRset.
-   * @param type the type of the RRset.
-   * @param dclass the class of the RRset.
-   * @param section the section to look in (ANSWER -> ADDITIONAL)
-   * 
-   * @return The SRRset if found, null otherwise.
-   */
-  public SRRset findRRset(Name name, int type, int dclass, int section)
-  {
-    if (section <= Section.QUESTION || section > Section.ADDITIONAL)
-      throw new IllegalArgumentException("Invalid section.");
-
-    SRRset[] rrsets = getSectionRRsets(section);
-
-    for (int i = 0; i < rrsets.length; i++)
-    {
-      if (rrsets[i].getName().equals(name) && rrsets[i].getType() == type
-          && rrsets[i].getDClass() == dclass)
-      {
-        return rrsets[i];
-      }
-    }
-
-    return null;
-  }
-
-  /**
-   * Find an "answer" RRset. This will look for RRsets in the ANSWER section
-   * that match the <qname,qtype,qclass>, taking into consideration CNAMEs.
-   * 
-   * @param qname The starting search name.
-   * @param qtype The search type.
-   * @param qclass The search class.
-   * 
-   * @return a SRRset matching the query. This SRRset may have a different
-   *         name from qname, due to following a CNAME chain.
-   */
-  public SRRset findAnswerRRset(Name qname, int qtype, int qclass)
-  {
-    SRRset[] srrsets = getSectionRRsets(Section.ANSWER);
-
-    for (int i = 0; i < srrsets.length; i++)
-    {
-      if (srrsets[i].getName().equals(qname)
-          && srrsets[i].getType() == Type.CNAME)
-      {
-        CNAMERecord cname = (CNAMERecord) srrsets[i].first();
-        qname = cname.getTarget();
-        continue;
-      }
-
-      if (srrsets[i].getName().equals(qname) && srrsets[i].getType() == qtype
-          && srrsets[i].getDClass() == qclass)
-      {
-        return srrsets[i];
-      }
-    }
-
-    return null;
-  }
+
+        return (SRRset[]) result.toArray(empty_srrset_array);
+    }
+
+    public void deleteRRset(SRRset rrset, int section) {
+        List<SRRset> slist = getSectionList(section);
+
+        if (slist.size() == 0) return;
+
+        slist.remove(rrset);
+    }
+
+    public void clear(int section) {
+        if (section < Section.QUESTION || section > Section.ADDITIONAL)
+            throw new IllegalArgumentException("Invalid section.");
+
+        if (section == Section.QUESTION) {
+            mQuestion = null;
+            return;
+        }
+        if (section == Section.ADDITIONAL) {
+            mOPTRecord = null;
+        }
+
+        mSection[section - 1] = null;
+    }
+
+    public void clear() {
+        for (int s = Section.QUESTION; s <= Section.ADDITIONAL; s++) {
+            clear(s);
+        }
+    }
+
+    public int getRcode() {
+        // FIXME: might want to do what Message does and handle extended rcodes.
+        return mHeader.getRcode();
+    }
+
+    public int getStatus() {
+        return mSecurityStatus.getStatus();
+    }
+
+    public void setStatus(byte status) {
+        mSecurityStatus.setStatus(status);
+    }
+
+    public SecurityStatus getSecurityStatus() {
+        return mSecurityStatus;
+    }
+
+    public void setSecurityStatus(SecurityStatus s) {
+        if (s == null) return;
+        mSecurityStatus = s;
+    }
+
+    public Message getMessage() {
+        // Generate our new message.
+        Message m = new Message(mHeader.getID());
+
+        // Convert the header
+        // We do this for two reasons: 1) setCount() is package scope, so we
+        // can't do that, and 2) setting the header on a message after creating
+        // the message frequently gets stuff out of sync, leading to malformed
+        // wire format messages.
+        Header h = m.getHeader();
+        h.setOpcode(mHeader.getOpcode());
+        h.setRcode(mHeader.getRcode());
+        for (int i = 0; i < 16; i++) {
+            if (Flags.isFlag(i)) {
+                if (mHeader.getFlag(i)) {
+                    h.setFlag(i);
+                } else {
+                    h.unsetFlag(i);
+                }
+            }
+        }
+
+        // Add all the records. -- this will set the counts correctly in the
+        // message header.
+
+        if (mQuestion != null) {
+            m.addRecord(mQuestion, Section.QUESTION);
+        }
+
+        for (int sec = Section.ANSWER; sec <= Section.ADDITIONAL; sec++) {
+            List<SRRset> slist = getSectionList(sec);
+            for (SRRset rrset : slist) {
+                for (Iterator<Record> j = rrset.rrs(); j.hasNext(); ) {
+                    m.addRecord(j.next(), sec);
+                }
+                for (Iterator<RRSIGRecord> j = rrset.sigs(); j.hasNext(); ) {
+                    m.addRecord(j.next(), sec);
+                }
+            }
+        }
+
+        if (mOPTRecord != null) {
+            m.addRecord(mOPTRecord, Section.ADDITIONAL);
+        }
+
+        return m;
+    }
+
+    public int getCount(int section) {
+        if (section == Section.QUESTION) {
+            return mQuestion == null ? 0 : 1;
+        }
+        List<SRRset> sectionList = getSectionList(section);
+        if (sectionList == null) return 0;
+        if (sectionList.size() == 0) return 0;
+
+        int count = 0;
+        for (SRRset sr : sectionList) {
+            count += sr.totalSize();
+        }
+
+        return count;
+    }
+
+    public String toString() {
+        return getMessage().toString();
+    }
+
+    /**
+     * Find a specific (S)RRset in a given section.
+     * 
+     * @param name
+     *            the name of the RRset.
+     * @param type
+     *            the type of the RRset.
+     * @param dclass
+     *            the class of the RRset.
+     * @param section
+     *            the section to look in (ANSWER -> ADDITIONAL)
+     * 
+     * @return The SRRset if found, null otherwise.
+     */
+    public SRRset findRRset(Name name, int type, int dclass, int section) {
+        if (section <= Section.QUESTION || section > Section.ADDITIONAL)
+            throw new IllegalArgumentException("Invalid section.");
+
+        SRRset[] rrsets = getSectionRRsets(section);
+
+        for (int i = 0; i < rrsets.length; i++) {
+            if (rrsets[i].getName().equals(name) && rrsets[i].getType() == type
+                && rrsets[i].getDClass() == dclass) {
+                return rrsets[i];
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Find an "answer" RRset. This will look for RRsets in the ANSWER section
+     * that match the <qname,qtype,qclass>, taking into consideration CNAMEs.
+     * 
+     * @param qname
+     *            The starting search name.
+     * @param qtype
+     *            The search type.
+     * @param qclass
+     *            The search class.
+     * 
+     * @return a SRRset matching the query. This SRRset may have a different
+     *         name from qname, due to following a CNAME chain.
+     */
+    public SRRset findAnswerRRset(Name qname, int qtype, int qclass) {
+        SRRset[] srrsets = getSectionRRsets(Section.ANSWER);
+
+        for (int i = 0; i < srrsets.length; i++) {
+            if (srrsets[i].getName().equals(qname)
+                && srrsets[i].getType() == Type.CNAME) {
+                CNAMERecord cname = (CNAMERecord) srrsets[i].first();
+                qname = cname.getTarget();
+                continue;
+            }
+
+            if (srrsets[i].getName().equals(qname)
+                && srrsets[i].getType() == qtype
+                && srrsets[i].getDClass() == qclass) {
+                return srrsets[i];
+            }
+        }
+
+        return null;
+    }
 
 }
\ No newline at end of file
index 18b16b2..71880a0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005 VeriSign. All rights reserved.
+ * Copyright (c) 2009 VeriSign. All rights reserved.
  * 
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions are met:
@@ -34,152 +34,103 @@ import org.xbill.DNS.*;
 /**
  * A version of the RRset class overrides the standard security status.
  */
-public class SRRset extends RRset
-{
-  private SecurityStatus mSecurityStatus;
-  
-  /** Create a new, blank SRRset. */
-  public SRRset()
-  {
-    super();
-    mSecurityStatus = new SecurityStatus();
-  }
-
-  /**
-   * Create a new SRRset from an existing RRset. This SRRset will contain that
-   * same internal Record objects as the original RRset.
-   */
-  @SuppressWarnings("unchecked") // org.xbill.DNS.RRset isn't typesafe-aware.
-public SRRset(RRset r)
-  {
-    this();
-
-    for (Iterator i = r.rrs(); i.hasNext();)
-    {
-      addRR((Record) i.next());
+public class SRRset extends RRset {
+    private SecurityStatus mSecurityStatus;
+
+    /** Create a new, blank SRRset. */
+    public SRRset() {
+        super();
+        mSecurityStatus = new SecurityStatus();
+    }
+
+    
+    /**
+     * Create a new SRRset from an existing RRset. This SRRset will contain that
+     * same internal Record objects as the original RRset.
+     */
+    @SuppressWarnings("unchecked")
+    // org.xbill.DNS.RRset isn't typesafe-aware.
+    public SRRset(RRset r) {
+        this();
+
+        for (Iterator i = r.rrs(); i.hasNext();) {
+            addRR((Record) i.next());
+        }
+
+        for (Iterator i = r.sigs(); i.hasNext();) {
+            addRR((Record) i.next());
+        }
+    }
+
+    /**
+     * Return the current security status (generally: UNCHECKED, BOGUS, or
+     * SECURE).
+     */
+    public int getSecurity() {
+        return getSecurityStatus();
+    }
+
+    /**
+     * Return the current security status (generally: UNCHECKED, BOGUS, or
+     * SECURE).
+     */
+    public byte getSecurityStatus() {
+        return mSecurityStatus.getStatus();
+    }
+
+    /**
+     * Set the current security status for this SRRset. This status will be
+     * shared amongst all copies of this SRRset (created with cloneSRRset())
+     */
+    public void setSecurityStatus(byte status) {
+        mSecurityStatus.setStatus(status);
     }
 
-    for (Iterator i = r.sigs(); i.hasNext();)
-    {
-      addRR((Record) i.next());
+    public Iterator<Record> rrs() {
+        return (Iterator<Record>) rrs();
     }
-  }
-
-  /**
-   * Clone this SRRset, giving the copy a new TTL. The copy is independent
-   * from the original except for the security status.
-   * 
-   * @param withNewTTL The new TTL to apply to the RRset. This applies to
-   *          contained RRsig records as well.
-   * @return The cloned SRRset.
-   */
-//  public SRRset cloneSRRset(long withNewTTL)
-//  {
-//    SRRset nr = new SRRset();
-//
-//    for (Iterator i = rrs(); i.hasNext();)
-//    {
-//      nr.addRR(((Record) i.next()).withTTL(withNewTTL));
-//    }
-//    for (Iterator i = sigs(); i.hasNext();)
-//    {
-//      nr.addRR(((Record) i.next()).withTTL(withNewTTL));
-//    }
-//
-//    nr.mSecurityStatus = mSecurityStatus;
-//
-//    return nr;
-//  }
-
-  public SRRset cloneSRRsetNoSigs()
-  {
-    SRRset nr = new SRRset();
-    for (Iterator i = rrs(); i.hasNext();)
-    {
-      // NOTE: should this clone the records as well?
-      nr.addRR((Record) i.next());
+    
+    public Iterator<RRSIGRecord> sigs() {
+        return (Iterator<RRSIGRecord>) sigs();
     }
-    // Do not copy the SecurityStatus reference
     
-    return nr;
-  }
-  
-  
-  /**
-   * Return the current security status (generally: UNCHECKED, BOGUS, or
-   * SECURE).
-   */
-  public int getSecurity()
-  {
-    return getSecurityStatus();
-  }
-
-  /**
-   * Return the current security status (generally: UNCHECKED, BOGUS, or
-   * SECURE).
-   */
-  public int getSecurityStatus()
-  {
-    return mSecurityStatus.getStatus();
-  }
-
-  /**
-   * Set the current security status for this SRRset. This status will be
-   * shared amongst all copies of this SRRset (created with cloneSRRset())
-   */
-  public void setSecurityStatus(byte status)
-  {
-    mSecurityStatus.setStatus(status);
-  }
-
-  public int totalSize() {
-      int num_sigs = 0;
-      for (Iterator i = sigs(); i.hasNext(); ) {
-          num_sigs++;
-      }
-      return size() + num_sigs;
-  }
-  
-  /**
-   * @return The total number of records (data + sigs) in the SRRset.
-   */
-  public int getNumRecords()
-  {
-    return totalSize();
-  }
-
-  public RRSIGRecord firstSig() {
-      for (Iterator i = sigs(); i.hasNext(); ) {
-          return (RRSIGRecord) i.next();
-      }
-      return null;
-  }
-  /**
-   * @return true if this RRset has RRSIG records that cover data records.
-   *         (i.e., RRSIG SRRsets return false)
-   */
-  public boolean isSigned()
-  {
-    if (getType() == Type.RRSIG) return false;
-    return firstSig() != null;
-  }
-
-  /**
-   * @return The "signer" name for this SRRset, if signed, or null if not.
-   */
-  public Name getSignerName()
-  {
-    RRSIGRecord sig = (RRSIGRecord) firstSig();
-    if (sig == null) return null;
-    return sig.getSigner();
-  }
-  
-//  public void setTTL(long ttl)
-//  {
-//    if (ttl < 0)
-//    {
-//      throw new IllegalArgumentException("ttl can't be less than zero, stupid! was " + ttl);
-//    }
-//    super.setTTL(ttl);
-//  }
+    public int totalSize() {
+        int num_sigs = 0;
+        for (Iterator<RRSIGRecord> i = sigs(); i.hasNext();) {
+            num_sigs++;
+        }
+        return size() + num_sigs;
+    }
+
+    /**
+     * @return The total number of records (data + sigs) in the SRRset.
+     */
+    public int getNumRecords() {
+        return totalSize();
+    }
+
+    public RRSIGRecord firstSig() {
+        for (Iterator<RRSIGRecord> i = sigs(); i.hasNext();) {
+            return i.next();
+        }
+        return null;
+    }
+
+    /**
+     * @return true if this RRset has RRSIG records that cover data records.
+     *         (i.e., RRSIG SRRsets return false)
+     */
+    public boolean isSigned() {
+        if (getType() == Type.RRSIG) return false;
+        return firstSig() != null;
+    }
+
+    /**
+     * @return The "signer" name for this SRRset, if signed, or null if not.
+     */
+    public Name getSignerName() {
+        RRSIGRecord sig = (RRSIGRecord) firstSig();
+        if (sig == null) return null;
+        return sig.getSigner();
+    }
 }
index ac775ed..8316407 100644 (file)
@@ -47,428 +47,430 @@ import org.xbill.DNS.Name;
 import org.xbill.DNS.RRSIGRecord;
 import org.xbill.DNS.RRset;
 import org.xbill.DNS.Record;
-import org.xbill.DNS.utils.base64;
 
 /**
  * This class contains a bunch of utility methods that are generally useful in
  * signing and verifying rrsets.
- * 
- * @author David Blacka (original)
- * @author $Author$
- * @version $Revision$
  */
 
-public class SignUtils
-{
-
-  /**
-   * This class implements a basic comparitor for byte arrays. It is primarily
-   * useful for comparing RDATA portions of DNS records in doing DNSSEC
-   * canonical ordering.
-   * 
-   * @author David Blacka (original)
-   */
-  public static class ByteArrayComparator implements Comparator
-  {
-    private int     mOffset = 0;
-    private boolean mDebug  = false;
-
-    public ByteArrayComparator()
-    {
-    }
+public class SignUtils {
 
-    public ByteArrayComparator(int offset, boolean debug)
-    {
-      mOffset = offset;
-      mDebug = debug;
-    }
+    /**
+     * This class implements a basic comparator for byte arrays. It is primarily
+     * useful for comparing RDATA portions of DNS records in doing DNSSEC
+     * canonical ordering.
+     */
+    public static class ByteArrayComparator implements Comparator<byte[]> {
+        private int     mOffset = 0;
+        private boolean mDebug  = false;
+
+        public ByteArrayComparator() {
+        }
 
-    public int compare(Object o1, Object o2) throws ClassCastException
-    {
-      byte[] b1 = (byte[]) o1;
-      byte[] b2 = (byte[]) o2;
-
-      for (int i = mOffset; i < b1.length && i < b2.length; i++)
-      {
-        if (b1[i] != b2[i])
-        {
-          if (mDebug)
-          {
-            System.out.println("offset " + i + " differs (this is "
-                + (i - mOffset) + " bytes in from our offset.)");
-          }
-          return (b1[i] & 0xFF) - (b2[i] & 0xFF);
+        public ByteArrayComparator(int offset, boolean debug) {
+            mOffset = offset;
+            mDebug = debug;
         }
-      }
 
-      return b1.length - b2.length;
+        public int compare(byte[] b1, byte[] b2) throws ClassCastException {
+            for (int i = mOffset; i < b1.length && i < b2.length; i++) {
+                if (b1[i] != b2[i]) {
+                    if (mDebug) {
+                        System.out.println("offset " + i + " differs (this is "
+                                           + (i - mOffset)
+                                           + " bytes in from our offset.)");
+                    }
+                    return (b1[i] & 0xFF) - (b2[i] & 0xFF);
+                }
+            }
+
+            return b1.length - b2.length;
+        }
     }
-  }
-
-  // private static final int DSA_SIGNATURE_LENGTH = 20;
-  private static final int ASN1_INT      = 0x02;
-  private static final int ASN1_SEQ      = 0x30;
-
-  public static final int  RR_NORMAL     = 0;
-  public static final int  RR_DELEGATION = 1;
-  public static final int  RR_GLUE       = 2;
-  public static final int  RR_INVALID    = 3;
-
-  /**
-   * Generate from some basic information a prototype SIG RR containing
-   * everything but the actual signature itself.
-   * 
-   * @param rrset the RRset being signed.
-   * @param signer the name of the signing key
-   * @param alg the algorithm of the signing key
-   * @param keyid the keyid (or footprint) of the signing key
-   * @param start the SIG inception time.
-   * @param expire the SIG expiration time.
-   * @param sig_ttl the TTL of the resulting SIG record.
-   * @return a prototype signature based on the RRset and key information.
-   */
-  public static RRSIGRecord generatePreRRSIG(RRset rrset, Name signer,
-      int alg, int keyid, Date start, Date expire, long sig_ttl)
-  {
-    return new RRSIGRecord(rrset.getName(), rrset.getDClass(), sig_ttl, rrset
-        .getType(), alg, rrset.getTTL(), expire, start, keyid, signer, null);
-  }
-
-  /**
-   * Generate from some basic information a prototype SIG RR containing
-   * everything but the actual signature itself.
-   * 
-   * @param rrset the RRset being signed.
-   * @param key the public KEY RR counterpart to the key being used to sign
-   *          the RRset
-   * @param start the SIG inception time.
-   * @param expire the SIG expiration time.
-   * @param sig_ttl the TTL of the resulting SIG record.
-   * @return a prototype signature based on the RRset and key information.
-   */
-  public static RRSIGRecord generatePreRRSIG(RRset rrset, DNSKEYRecord key,
-      Date start, Date expire, long sig_ttl)
-  {
-    return generatePreRRSIG(rrset, key.getName(), key.getAlgorithm(), key
-        .getFootprint(), start, expire, sig_ttl);
-  }
-
-  /**
-   * Generate from some basic information a prototype SIG RR containing
-   * everything but the actual signature itself.
-   * 
-   * @param rec the DNS record being signed (forming an entire RRset).
-   * @param key the public KEY RR counterpart to the key signing the record.
-   * @param start the SIG inception time.
-   * @param expire the SIG expiration time.
-   * @param sig_ttl the TTL of the result SIG record.
-   * @return a prototype signature based on the Record and key information.
-   */
-  public static RRSIGRecord generatePreRRSIG(Record rec, DNSKEYRecord key,
-      Date start, Date expire, long sig_ttl)
-  {
-    return new RRSIGRecord(rec.getName(), rec.getDClass(), sig_ttl, rec
-        .getType(), key.getAlgorithm(), rec.getTTL(), expire, start, key
-        .getFootprint(), key.getName(), null);
-  }
-
-  /**
-   * Generate the binary image of the prototype SIG RR.
-   * 
-   * @param presig the SIG RR prototype.
-   * @return the RDATA portion of the prototype SIG record. This forms the
-   *         first part of the data to be signed.
-   */
-  private static byte[] generatePreSigRdata(RRSIGRecord presig)
-  {
-    // Generate the binary image;
-    DNSOutput image = new DNSOutput();
-
-    // precalc some things
-    int start_time = (int) (presig.getTimeSigned().getTime() / 1000);
-    int expire_time = (int) (presig.getExpire().getTime() / 1000);
-    Name signer = presig.getSigner();
-
-    // first write out the partial SIG record (this is the SIG RDATA
-    // minus the actual signature.
-    image.writeU16(presig.getTypeCovered());
-    image.writeU8(presig.getAlgorithm());
-    image.writeU8(presig.getLabels());
-    image.writeU32((int) presig.getOrigTTL());
-    image.writeU32(expire_time);
-    image.writeU32(start_time);
-    image.writeU16(presig.getFootprint());
-    image.writeByteArray(signer.toWireCanonical());
-
-    return image.toByteArray();
-  }
-
-  /**
-   * Calculate the canonical wire line format of the RRset.
-   * 
-   * @param rrset the RRset to convert.
-   * @param ttl the TTL to use when canonicalizing -- this is generally the
-   *          TTL of the signature if there is a pre-existing signature. If
-   *          not it is just the ttl of the rrset itself.
-   * @param labels the labels field of the signature, or 0.
-   * @return the canonical wire line format of the rrset. This is the second
-   *         part of data to be signed.
-   */
-  public static byte[] generateCanonicalRRsetData(RRset rrset, long ttl,
-      int labels)
-  {
-    DNSOutput image = new DNSOutput();
-
-    if (ttl == 0) ttl = rrset.getTTL();
-    Name n = rrset.getName();
-    if (labels == 0)
-    {
-      labels = n.labels();
+
+    // private static final int DSA_SIGNATURE_LENGTH = 20;
+    private static final int ASN1_INT      = 0x02;
+    private static final int ASN1_SEQ      = 0x30;
+
+    public static final int  RR_NORMAL     = 0;
+    public static final int  RR_DELEGATION = 1;
+    public static final int  RR_GLUE       = 2;
+    public static final int  RR_INVALID    = 3;
+
+    /**
+     * Generate from some basic information a prototype SIG RR containing
+     * everything but the actual signature itself.
+     * 
+     * @param rrset
+     *            the RRset being signed.
+     * @param signer
+     *            the name of the signing key
+     * @param alg
+     *            the algorithm of the signing key
+     * @param keyid
+     *            the keyid (or footprint) of the signing key
+     * @param start
+     *            the SIG inception time.
+     * @param expire
+     *            the SIG expiration time.
+     * @param sig_ttl
+     *            the TTL of the resulting SIG record.
+     * @return a prototype signature based on the RRset and key information.
+     */
+    public static RRSIGRecord generatePreRRSIG(RRset rrset, Name signer,
+                                               int alg, int keyid, Date start,
+                                               Date expire, long sig_ttl) {
+        return new RRSIGRecord(rrset.getName(), rrset.getDClass(), sig_ttl,
+                rrset.getType(), alg, rrset.getTTL(), expire, start, keyid,
+                signer, null);
     }
-    else
-    {
-      // correct for Name()'s conception of label count.
-      labels++;
+
+    /**
+     * Generate from some basic information a prototype SIG RR containing
+     * everything but the actual signature itself.
+     * 
+     * @param rrset
+     *            the RRset being signed.
+     * @param key
+     *            the public KEY RR counterpart to the key being used to sign
+     *            the RRset
+     * @param start
+     *            the SIG inception time.
+     * @param expire
+     *            the SIG expiration time.
+     * @param sig_ttl
+     *            the TTL of the resulting SIG record.
+     * @return a prototype signature based on the RRset and key information.
+     */
+    public static RRSIGRecord generatePreRRSIG(RRset rrset, DNSKEYRecord key,
+                                               Date start, Date expire,
+                                               long sig_ttl) {
+        return generatePreRRSIG(rrset, key.getName(), key.getAlgorithm(),
+                                key.getFootprint(), start, expire, sig_ttl);
     }
-    boolean wildcardName = false;
-    if (n.labels() != labels)
-    {
-      n = n.wild(n.labels() - labels);
-      wildcardName = true;
-//      log.trace("Detected wildcard expansion: " + rrset.getName() + " changed to " + n);
+
+    /**
+     * Generate from some basic information a prototype SIG RR containing
+     * everything but the actual signature itself.
+     * 
+     * @param rec
+     *            the DNS record being signed (forming an entire RRset).
+     * @param key
+     *            the public KEY RR counterpart to the key signing the record.
+     * @param start
+     *            the SIG inception time.
+     * @param expire
+     *            the SIG expiration time.
+     * @param sig_ttl
+     *            the TTL of the result SIG record.
+     * @return a prototype signature based on the Record and key information.
+     */
+    public static RRSIGRecord generatePreRRSIG(Record rec, DNSKEYRecord key,
+                                               Date start, Date expire,
+                                               long sig_ttl) {
+        return new RRSIGRecord(rec.getName(), rec.getDClass(), sig_ttl,
+                rec.getType(), key.getAlgorithm(), rec.getTTL(), expire, start,
+                key.getFootprint(), key.getName(), null);
     }
 
-    // now convert load the wire format records in the RRset into a
-    // list of byte arrays.
-    ArrayList canonical_rrs = new ArrayList();
-    for (Iterator i = rrset.rrs(); i.hasNext();)
-    {
-      Record r = (Record) i.next();
-      if (r.getTTL() != ttl || wildcardName)
-      {
-        // If necessary, we need to create a new record with a new ttl or ownername.
-        // In the TTL case, this avoids changing the ttl in the response.
-        r = Record.newRecord(n, r.getType(), r.getDClass(), ttl, r
-            .rdataToWireCanonical());
-      }
-      byte[] wire_fmt = r.toWireCanonical();
-      canonical_rrs.add(wire_fmt);
+    /**
+     * Generate the binary image of the prototype SIG RR.
+     * 
+     * @param presig
+     *            the SIG RR prototype.
+     * @return the RDATA portion of the prototype SIG record. This forms the
+     *         first part of the data to be signed.
+     */
+    private static byte[] generatePreSigRdata(RRSIGRecord presig) {
+        // Generate the binary image;
+        DNSOutput image = new DNSOutput();
+
+        // precalculate some things
+        int start_time = (int) (presig.getTimeSigned().getTime() / 1000);
+        int expire_time = (int) (presig.getExpire().getTime() / 1000);
+        Name signer = presig.getSigner();
+
+        // first write out the partial SIG record (this is the SIG RDATA
+        // minus the actual signature.
+        image.writeU16(presig.getTypeCovered());
+        image.writeU8(presig.getAlgorithm());
+        image.writeU8(presig.getLabels());
+        image.writeU32((int) presig.getOrigTTL());
+        image.writeU32(expire_time);
+        image.writeU32(start_time);
+        image.writeU16(presig.getFootprint());
+        image.writeByteArray(signer.toWireCanonical());
+
+        return image.toByteArray();
     }
 
-    // put the records into the correct ordering.
-    // Caculate the offset where the RDATA begins (we have to skip
-    // past the length byte)
+    /**
+     * Calculate the canonical wire line format of the RRset.
+     * 
+     * @param rrset
+     *            the RRset to convert.
+     * @param ttl
+     *            the TTL to use when canonicalizing -- this is generally the
+     *            TTL of the signature if there is a pre-existing signature. If
+     *            not it is just the ttl of the rrset itself.
+     * @param labels
+     *            the labels field of the signature, or 0.
+     * @return the canonical wire line format of the rrset. This is the second
+     *         part of data to be signed.
+     */
+    @SuppressWarnings("unchecked")
+    public static byte[] generateCanonicalRRsetData(RRset rrset, long ttl,
+                                                    int labels) {
+        DNSOutput image = new DNSOutput();
+
+        if (ttl == 0) ttl = rrset.getTTL();
+        Name n = rrset.getName();
+        if (labels == 0) {
+            labels = n.labels();
+        } else {
+            // correct for Name()'s conception of label count.
+            labels++;
+        }
+        boolean wildcardName = false;
+        if (n.labels() != labels) {
+            n = n.wild(n.labels() - labels);
+            wildcardName = true;
+            // log.trace("Detected wildcard expansion: " + rrset.getName() +
+            // " changed to " + n);
+        }
+
+        // now convert the wire format records in the RRset into a
+        // list of byte arrays.
+        ArrayList<byte[]> canonical_rrs = new ArrayList<byte[]>();
+        for (Iterator i = rrset.rrs(); i.hasNext();) {
+            Record r = (Record) i.next();
+            if (r.getTTL() != ttl || wildcardName) {
+                // If necessary, we need to create a new record with a new ttl
+                // or ownername.
+                // In the TTL case, this avoids changing the ttl in the
+                // response.
+                r = Record.newRecord(n, r.getType(), r.getDClass(), ttl,
+                                     r.rdataToWireCanonical());
+            }
+            byte[] wire_fmt = r.toWireCanonical();
+            canonical_rrs.add(wire_fmt);
+        }
+
+        // put the records into the correct ordering.
+        // Calculate the offset where the RDATA begins (we have to skip
+        // past the length byte)
+
+        int offset = rrset.getName().toWireCanonical().length + 10;
+        ByteArrayComparator bac = new ByteArrayComparator(offset, false);
+
+        Collections.sort(canonical_rrs, bac);
 
-    int offset = rrset.getName().toWireCanonical().length + 10;
-    ByteArrayComparator bac = new ByteArrayComparator(offset, false);
+        for (Iterator<byte[]> i = canonical_rrs.iterator(); i.hasNext();) {
+            byte[] wire_fmt_rec = i.next();
+            image.writeByteArray(wire_fmt_rec);
+        }
 
-    Collections.sort(canonical_rrs, bac);
+        return image.toByteArray();
+    }
 
-    for (Iterator i = canonical_rrs.iterator(); i.hasNext();)
-    {
-      byte[] wire_fmt_rec = (byte[]) i.next();
-      image.writeByteArray(wire_fmt_rec);
+    /**
+     * Given an RRset and the prototype signature, generate the canonical data
+     * that is to be signed.
+     * 
+     * @param rrset
+     *            the RRset to be signed.
+     * @param presig
+     *            a prototype SIG RR created using the same RRset.
+     * @return a block of data ready to be signed.
+     */
+    public static byte[] generateSigData(RRset rrset, RRSIGRecord presig)
+            throws IOException {
+        byte[] rrset_data = generateCanonicalRRsetData(rrset,
+                                                       presig.getOrigTTL(),
+                                                       presig.getLabels());
+
+        return generateSigData(rrset_data, presig);
     }
 
-    return image.toByteArray();
-  }
-
-  /**
-   * Given an RRset and the prototype signature, generate the canonical data
-   * that is to be signed.
-   * 
-   * @param rrset the RRset to be signed.
-   * @param presig a prototype SIG RR created using the same RRset.
-   * @return a block of data ready to be signed.
-   */
-  public static byte[] generateSigData(RRset rrset, RRSIGRecord presig)
-      throws IOException
-  {
-    byte[] rrset_data = generateCanonicalRRsetData(rrset, presig.getOrigTTL(), presig.getLabels());
-
-    return generateSigData(rrset_data, presig);
-  }
-
-  /**
-   * Given an RRset and the prototype signature, generate the canonical data
-   * that is to be signed.
-   * 
-   * @param rrset_data the RRset converted into canonical wire line format (as
-   *          per the canonicalization rules in RFC 2535).
-   * @param presig the prototype signature based on the same RRset represented
-   *          in <code>rrset_data</code>.
-   * @return a block of data ready to be signed.
-   */
-  public static byte[] generateSigData(byte[] rrset_data, RRSIGRecord presig)
-      throws IOException
-  {
-    byte[] sig_rdata = generatePreSigRdata(presig);
-
-    ByteArrayOutputStream image = new ByteArrayOutputStream(sig_rdata.length
-        + rrset_data.length);
-
-    image.write(sig_rdata);
-    image.write(rrset_data);
-
-    return image.toByteArray();
-  }
-
-  /**
-   * Given the acutal signature an the prototype signature, combine them and
-   * return the fully formed SIGRecord.
-   * 
-   * @param signature the cryptographic signature, in DNSSEC format.
-   * @param presig the prototype SIG RR to add the signature to.
-   * @return the fully formed SIG RR.
-   */
-  public static RRSIGRecord generateRRSIG(byte[] signature, RRSIGRecord presig)
-  {
-    return new RRSIGRecord(presig.getName(), presig.getDClass(), presig
-        .getTTL(), presig.getTypeCovered(), presig.getAlgorithm(), presig
-        .getOrigTTL(), presig.getExpire(), presig.getTimeSigned(), presig
-        .getFootprint(), presig.getSigner(), signature);
-  }
-
-  /**
-   * Converts from a RFC 2536 formatted DSA signature to a JCE (ASN.1)
-   * formatted signature.
-   * 
-   * <p>
-   * ASN.1 format = ASN1_SEQ . seq_length . ASN1_INT . Rlength . R . ANS1_INT .
-   * Slength . S
-   * </p>
-   * 
-   * The integers R and S may have a leading null byte to force the integer
-   * positive.
-   * 
-   * @param signature the RFC 2536 formatted DSA signature.
-   * @return The ASN.1 formatted DSA signature.
-   * @throws SignatureException if there was something wrong with the RFC 2536
-   *           formatted signature.
-   */
-  public static byte[] convertDSASignature(byte[] signature)
-      throws SignatureException
-  {
-    if (signature.length != 41)
-      throw new SignatureException("RFC 2536 signature not expected length.");
-
-    byte r_pad = 0;
-    byte s_pad = 0;
-
-    // handle initial null byte padding.
-    if (signature[1] < 0) r_pad++;
-    if (signature[21] < 0) s_pad++;
-
-    // ASN.1 length = R length + S length + (2 + 2 + 2), where each 2
-    // is for a ASN.1 type-length byte pair of which there are three
-    // (SEQ, INT, INT).
-    byte sig_length = (byte) (40 + r_pad + s_pad + 6);
-
-    byte sig[] = new byte[sig_length];
-    byte pos = 0;
-
-    sig[pos++] = ASN1_SEQ;
-    sig[pos++] = (byte) (sig_length - 2); // all but the SEQ type+length.
-    sig[pos++] = ASN1_INT;
-    sig[pos++] = (byte) (20 + r_pad);
-
-    // copy the value of R, leaving a null byte if necessary
-    if (r_pad == 1) sig[pos++] = 0;
-
-    System.arraycopy(signature, 1, sig, pos, 20);
-    pos += 20;
-
-    sig[pos++] = ASN1_INT;
-    sig[pos++] = (byte) (20 + s_pad);
-
-    // copy the value of S, leaving a null byte if necessary
-    if (s_pad == 1) sig[pos++] = 0;
-
-    System.arraycopy(signature, 21, sig, pos, 20);
-
-    return sig;
-  }
-
-  /**
-   * Converts from a JCE (ASN.1) formatted DSA signature to a RFC 2536
-   * compliant signature.
-   * 
-   * <p>
-   * rfc2536 format = T . R . S
-   * </p>
-   * 
-   * where T is a number between 0 and 8, which is based on the DSA key
-   * length, and R & S are formatted to be exactly 20 bytes each (no leading
-   * null bytes).
-   * 
-   * @param params the DSA parameters associated with the DSA key used to
-   *          generate the signature.
-   * @param signature the ASN.1 formatted DSA signature.
-   * @return a RFC 2536 formatted DSA signature.
-   * @throws SignatureException if something is wrong with the ASN.1 format.
-   */
-  public static byte[] convertDSASignature(DSAParams params, byte[] signature)
-      throws SignatureException
-  {
-    if (signature[0] != ASN1_SEQ || signature[2] != ASN1_INT)
-    {
-      throw new SignatureException(
-          "Invalid ASN.1 signature format: expected SEQ, INT");
+    /**
+     * Given an RRset and the prototype signature, generate the canonical data
+     * that is to be signed.
+     * 
+     * @param rrset_data
+     *            the RRset converted into canonical wire line format (as per
+     *            the canonicalization rules in RFC 2535).
+     * @param presig
+     *            the prototype signature based on the same RRset represented in
+     *            <code>rrset_data</code>.
+     * @return a block of data ready to be signed.
+     */
+    public static byte[] generateSigData(byte[] rrset_data, RRSIGRecord presig)
+            throws IOException {
+        byte[] sig_rdata = generatePreSigRdata(presig);
+
+        ByteArrayOutputStream image = new ByteArrayOutputStream(
+                sig_rdata.length + rrset_data.length);
+
+        image.write(sig_rdata);
+        image.write(rrset_data);
+
+        return image.toByteArray();
     }
 
-    byte r_pad = (byte) (signature[3] - 20);
+    /**
+     * Given the actual signature and the prototype signature, combine them and
+     * return the fully formed RRSIGRecord.
+     * 
+     * @param signature
+     *            the cryptographic signature, in DNSSEC format.
+     * @param presig
+     *            the prototype RRSIG RR to add the signature to.
+     * @return the fully formed RRSIG RR.
+     */
+    public static RRSIGRecord generateRRSIG(byte[] signature, RRSIGRecord presig) {
+        return new RRSIGRecord(presig.getName(), presig.getDClass(),
+                presig.getTTL(), presig.getTypeCovered(),
+                presig.getAlgorithm(), presig.getOrigTTL(), presig.getExpire(),
+                presig.getTimeSigned(), presig.getFootprint(),
+                presig.getSigner(), signature);
+    }
 
-    if (signature[24 + r_pad] != ASN1_INT)
-    {
-      throw new SignatureException(
-          "Invalid ASN.1 signature format: expected SEQ, INT, INT");
+    /**
+     * Converts from a RFC 2536 formatted DSA signature to a JCE (ASN.1)
+     * formatted signature.
+     * 
+     * <p>
+     * ASN.1 format = ASN1_SEQ . seq_length . ASN1_INT . Rlength . R . ANS1_INT
+     * . Slength . S
+     * </p>
+     * 
+     * The integers R and S may have a leading null byte to force the integer
+     * positive.
+     * 
+     * @param signature
+     *            the RFC 2536 formatted DSA signature.
+     * @return The ASN.1 formatted DSA signature.
+     * @throws SignatureException
+     *             if there was something wrong with the RFC 2536 formatted
+     *             signature.
+     */
+    public static byte[] convertDSASignature(byte[] signature)
+            throws SignatureException {
+        if (signature.length != 41)
+            throw new SignatureException(
+                    "RFC 2536 signature not expected length.");
+
+        byte r_pad = 0;
+        byte s_pad = 0;
+
+        // handle initial null byte padding.
+        if (signature[1] < 0) r_pad++;
+        if (signature[21] < 0) s_pad++;
+
+        // ASN.1 length = R length + S length + (2 + 2 + 2), where each 2
+        // is for a ASN.1 type-length byte pair of which there are three
+        // (SEQ, INT, INT).
+        byte sig_length = (byte) (40 + r_pad + s_pad + 6);
+
+        byte sig[] = new byte[sig_length];
+        byte pos = 0;
+
+        sig[pos++] = ASN1_SEQ;
+        sig[pos++] = (byte) (sig_length - 2); // all but the SEQ type+length.
+        sig[pos++] = ASN1_INT;
+        sig[pos++] = (byte) (20 + r_pad);
+
+        // copy the value of R, leaving a null byte if necessary
+        if (r_pad == 1) sig[pos++] = 0;
+
+        System.arraycopy(signature, 1, sig, pos, 20);
+        pos += 20;
+
+        sig[pos++] = ASN1_INT;
+        sig[pos++] = (byte) (20 + s_pad);
+
+        // copy the value of S, leaving a null byte if necessary
+        if (s_pad == 1) sig[pos++] = 0;
+
+        System.arraycopy(signature, 21, sig, pos, 20);
+
+        return sig;
     }
 
-//    log.trace("(start) ASN.1 DSA Sig:\n" + base64.toString(signature));
+    /**
+     * Converts from a JCE (ASN.1) formatted DSA signature to a RFC 2536
+     * compliant signature.
+     * 
+     * <p>
+     * rfc2536 format = T . R . S
+     * </p>
+     * 
+     * where T is a number between 0 and 8, which is based on the DSA key
+     * length, and R & S are formatted to be exactly 20 bytes each (no leading
+     * null bytes).
+     * 
+     * @param params
+     *            the DSA parameters associated with the DSA key used to
+     *            generate the signature.
+     * @param signature
+     *            the ASN.1 formatted DSA signature.
+     * @return a RFC 2536 formatted DSA signature.
+     * @throws SignatureException
+     *             if something is wrong with the ASN.1 format.
+     */
+    public static byte[] convertDSASignature(DSAParams params, byte[] signature)
+            throws SignatureException {
+        if (signature[0] != ASN1_SEQ || signature[2] != ASN1_INT) {
+            throw new SignatureException(
+                    "Invalid ASN.1 signature format: expected SEQ, INT");
+        }
 
-    byte s_pad = (byte) (signature[25 + r_pad] - 20);
+        byte r_pad = (byte) (signature[3] - 20);
 
-    byte[] sig = new byte[41]; // all rfc2536 signatures are 41 bytes.
+        if (signature[24 + r_pad] != ASN1_INT) {
+            throw new SignatureException(
+                    "Invalid ASN.1 signature format: expected SEQ, INT, INT");
+        }
 
-    // Calculate T:
-    sig[0] = (byte) ((params.getP().bitLength() - 512) / 64);
+        // log.trace("(start) ASN.1 DSA Sig:\n" + base64.toString(signature));
 
-    // copy R value
-    if (r_pad >= 0)
-    {
-      System.arraycopy(signature, 4 + r_pad, sig, 1, 20);
-    }
-    else
-    {
-      // R is shorter than 20 bytes, so right justify the number
-      // (r_pad is negative here, remember?).
-      Arrays.fill(sig, 1, 1 - r_pad, (byte) 0);
-      System.arraycopy(signature, 4, sig, 1 - r_pad, 20 + r_pad);
-    }
+        byte s_pad = (byte) (signature[25 + r_pad] - 20);
 
-    // copy S value
-    if (s_pad >= 0)
-    {
-      System.arraycopy(signature, 26 + r_pad + s_pad, sig, 21, 20);
-    }
-    else
-    {
-      // S is shorter than 20 bytes, so right justify the number
-      // (s_pad is negative here).
-      Arrays.fill(sig, 21, 21 - s_pad, (byte) 0);
-      System.arraycopy(signature, 26 + r_pad, sig, 21 - s_pad, 20 + s_pad);
-    }
+        byte[] sig = new byte[41]; // all rfc2536 signatures are 41 bytes.
+
+        // Calculate T:
+        sig[0] = (byte) ((params.getP().bitLength() - 512) / 64);
+
+        // copy R value
+        if (r_pad >= 0) {
+            System.arraycopy(signature, 4 + r_pad, sig, 1, 20);
+        } else {
+            // R is shorter than 20 bytes, so right justify the number
+            // (r_pad is negative here, remember?).
+            Arrays.fill(sig, 1, 1 - r_pad, (byte) 0);
+            System.arraycopy(signature, 4, sig, 1 - r_pad, 20 + r_pad);
+        }
 
-//    if (r_pad < 0 || s_pad < 0)
-//    {
-//      log.trace("(finish ***) RFC 2536 DSA Sig:\n" + base64.toString(sig));
-//
-//    }
-//    else
-//    {
-//      log.trace("(finish) RFC 2536 DSA Sig:\n" + base64.toString(sig));
-//    }
-
-    return sig;
-  }
+        // copy S value
+        if (s_pad >= 0) {
+            System.arraycopy(signature, 26 + r_pad + s_pad, sig, 21, 20);
+        } else {
+            // S is shorter than 20 bytes, so right justify the number
+            // (s_pad is negative here).
+            Arrays.fill(sig, 21, 21 - s_pad, (byte) 0);
+            System.arraycopy(signature, 26 + r_pad, sig, 21 - s_pad, 20 + s_pad);
+        }
+
+        // if (r_pad < 0 || s_pad < 0)
+        // {
+        // log.trace("(finish ***) RFC 2536 DSA Sig:\n" + base64.toString(sig));
+        //
+        // }
+        // else
+        // {
+        // log.trace("(finish) RFC 2536 DSA Sig:\n" + base64.toString(sig));
+        // }
+
+        return sig;
+    }
 }
index 0e95343..99922f4 100644 (file)
@@ -33,16 +33,12 @@ import java.util.Map;
 
 import org.xbill.DNS.Name;
 
-import com.versign.tat.dnssec.SRRset;
-import com.versign.tat.dnssec.SecurityStatus;
-
-
 /**
  *
  */
 public class TrustAnchorStore
 {
-  private Map mMap;
+  private Map<String, SRRset> mMap;
   
   public TrustAnchorStore()
   {
@@ -59,7 +55,7 @@ public class TrustAnchorStore
   {
     if (mMap == null)
     {
-      mMap = new HashMap();
+      mMap = new HashMap<String, SRRset>();
     }
     String k = key(rrset.getName(), rrset.getDClass());
     rrset.setSecurityStatus(SecurityStatus.SECURE);
@@ -70,7 +66,7 @@ public class TrustAnchorStore
   private SRRset lookup(String key)
   {
     if (mMap == null) return null;
-    return (SRRset) mMap.get(key);
+    return mMap.get(key);
   }
   
   public SRRset find(Name n, int dclass)
index 3466170..967be77 100644 (file)
@@ -31,15 +31,10 @@ package com.versign.tat.dnssec;
 
 import java.util.*;
 
-import org.xbill.DNS.Flags;
-import org.xbill.DNS.Header;
 import org.xbill.DNS.Name;
 
 /**
  * Some basic utility functions.
- * 
- * @author davidb
- * @version $Revision$
  */
 public class Util
 {
@@ -61,31 +56,6 @@ public class Util
     return n;
   }
 
-//  public static SMessage errorMessage(Request request, int rcode)
-//  {
-//    SMessage m = new SMessage(request.getID());
-//    Header h = m.getHeader();
-//    h.setRcode(rcode);
-//    h.setFlag(Flags.QR);
-//    m.setQuestion(request.getQuestion());
-//    m.setOPT(request.getOPT());
-//
-//    return m;
-//  }
-//
-//  public static SMessage errorMessage(SMessage message, int rcode)
-//  {
-//    Header h = message.getHeader();
-//    SMessage m = new SMessage(h.getID());
-//    h = m.getHeader();
-//    h.setRcode(rcode);
-//    h.setFlag(Flags.QR);
-//    m.setQuestion(message.getQuestion());
-//    m.setOPT(message.getOPT());
-//
-//    return m;
-//  }
-
   public static int parseInt(String s, int def)
   {
     if (s == null) return def;
@@ -123,25 +93,21 @@ public class Util
     }
   }
   
-  public static List parseConfigPrefix(Properties config, String prefix)
+  public static List<ConfigEntry> parseConfigPrefix(Properties config, String prefix)
   {
     if (! prefix.endsWith("."))
     {
       prefix = prefix + ".";
     }
     
-    List res = new ArrayList();
+    List<ConfigEntry> res = new ArrayList<ConfigEntry>();
     
-    for (Iterator i = config.entrySet().iterator(); i.hasNext(); )
-    {
-      Map.Entry entry = (Map.Entry) i.next();
-      String key = (String) entry.getKey();
-      if (key.startsWith(prefix))
-      {
-        key = key.substring(prefix.length());
-        
-        res.add(new ConfigEntry(key, (String) entry.getValue()));
-      }
+    for (Map.Entry<Object, Object> entry : config.entrySet()) {
+        String key = (String) entry.getKey();
+        if (key.startsWith(prefix)) {
+            key = key.substring(prefix.length());
+            res.add(new ConfigEntry(key, (String) entry.getValue()));
+        }
     }
     
     return res;
index ef12d34..037feb2 100644 (file)
@@ -45,26 +45,40 @@ public class ValUtils {
     // validation strategy. They have no bearing on the iterative resolution
     // algorithm, so they are confined here.
 
-    /** Not subtyped yet. */
-    public static final int UNTYPED   = 0;
-
-    /** Not a recognized subtype. */
-    public static final int UNKNOWN   = 1;
-
-    /** A postive, direct, response. */
-    public static final int POSITIVE  = 2;
-
-    /** A postive response, with a CNAME/DNAME chain. */
-    public static final int CNAME     = 3;
-
-    /** A NOERROR/NODATA response. */
-    public static final int NODATA    = 4;
-
-    /** A NXDOMAIN response. */
-    public static final int NAMEERROR = 5;
-
-    /** A response to a qtype=ANY query. */
-    public static final int ANY       = 6;
+    public enum ResponseType {
+        UNTYPED, // not sub typed yet
+        UNKNOWN, // not a recognized sub type
+        POSITIVE, // a positive response (no CNAME/DNAME chain)
+        CNAME, // a positive response with a CNAME/DNAME chain.
+        NODATA, // a NOERROR/NODATA response
+        NAMEERROR, // a NXDOMAIN response
+        ANY, // a response to a qtype=ANY query
+        REFERRAL,
+        // a referral response
+        THROWAWAY
+        // a throwaway response (i.e., an error)
+    }
+    
+//    /** Not subtyped yet. */
+//    public static final int UNTYPED   = 0;
+//
+//    /** Not a recognized subtype. */
+//    public static final int UNKNOWN   = 1;
+//
+//    /** A postive, direct, response. */
+//    public static final int POSITIVE  = 2;
+//
+//    /** A postive response, with a CNAME/DNAME chain. */
+//    public static final int CNAME     = 3;
+//
+//    /** A NOERROR/NODATA response. */
+//    public static final int NODATA    = 4;
+//
+//    /** A NXDOMAIN response. */
+//    public static final int NAMEERROR = 5;
+//
+//    /** A response to a qtype=ANY query. */
+//    public static final int ANY       = 6;
 
     /** A local copy of the verifier object. */
     private DnsSecVerifier  mVerifier;
@@ -81,18 +95,38 @@ public class ValUtils {
      * 
      * @return A subtype ranging from UNKNOWN to NAMEERROR.
      */
-    public static int classifyResponse(SMessage m) {
+    public static ResponseType classifyResponse(SMessage m, Name zone) {
+
+        SRRset[] rrsets;
         // Normal Name Error's are easy to detect -- but don't mistake a CNAME
         // chain ending in NXDOMAIN.
         if (m.getRcode() == Rcode.NXDOMAIN && m.getCount(Section.ANSWER) == 0) {
-            return NAMEERROR;
+            return ResponseType.NAMEERROR;
+        }
+
+        // If rcode isn't NXDOMAIN or NOERROR, it is a throwaway response.
+        if (m.getRcode() != Rcode.NOERROR) {
+            return ResponseType.THROWAWAY;
+        }
+
+        // Next is REFERRAL. These are distinguished by having:
+        // 1) nothing in the ANSWER section
+        // 2) an NS RRset in the AUTHORITY section that is a strict subdomain of
+        // 'zone' (the presumed queried zone).
+        if (zone != null && m.getCount(Section.ANSWER) == 0
+            && m.getCount(Section.AUTHORITY) > 0) {
+            rrsets = m.getSectionRRsets(Section.AUTHORITY);
+            for (int i = 0; i < rrsets.length; ++i) {
+                if (rrsets[i].getType() == Type.NS
+                    && strictSubdomain(rrsets[i].getName(), zone)) {
+                    return ResponseType.REFERRAL;
+                }
+            }
         }
 
         // Next is NODATA
-        // st_log.debug("classifyResponse: ancount = " +
-        // m.getCount(Section.ANSWER));
         if (m.getCount(Section.ANSWER) == 0) {
-            return NODATA;
+            return ResponseType.NODATA;
         }
 
         // We distinguish between CNAME response and other positive/negative
@@ -100,23 +134,22 @@ public class ValUtils {
         int qtype = m.getQuestion().getType();
 
         // We distinguish between ANY and CNAME or POSITIVE because ANY
-        // responses
-        // are validated differently.
+        // responses are validated differently.
         if (qtype == Type.ANY) {
-            return ANY;
+            return ResponseType.ANY;
         }
 
-        SRRset[] rrsets = m.getSectionRRsets(Section.ANSWER);
+        rrsets = m.getSectionRRsets(Section.ANSWER);
 
         // Note that DNAMEs will be ignored here, unless qtype=DNAME. Unless
         // qtype=CNAME, this will yield a CNAME response.
         for (int i = 0; i < rrsets.length; i++) {
-            if (rrsets[i].getType() == qtype) return POSITIVE;
-            if (rrsets[i].getType() == Type.CNAME) return CNAME;
+            if (rrsets[i].getType() == qtype) return ResponseType.POSITIVE;
+            if (rrsets[i].getType() == Type.CNAME) return ResponseType.CNAME;
         }
 
         // st_log.warn("Failed to classify response message:\n" + m);
-        return UNKNOWN;
+        return ResponseType.UNKNOWN;
     }
 
     /**
@@ -126,162 +159,25 @@ public class ValUtils {
      * 
      * @param m
      *            The response to analyze.
-     * @param request
-     *            The request that generated the response.
      * @return a signer name, if the response is signed (even partially), or
      *         null if the response isn't signed.
      */
     public Name findSigner(SMessage m) {
-        int subtype = classifyResponse(m);
-        Name qname = m.getQName();
+        // FIXME: this used to classify the message, then look in the pertinent
+        // section. Now we just find the first RRSIG in the ANSWER and AUTHORIY
+        // sections.
 
-        SRRset[] rrsets;
-
-        switch (subtype) {
-        case POSITIVE:
-        case CNAME:
-        case ANY:
-            // Check to see if the ANSWER section RRset
-            rrsets = m.getSectionRRsets(Section.ANSWER);
-            for (int i = 0; i < rrsets.length; i++) {
-                if (rrsets[i].getName().equals(qname)) {
-                    return rrsets[i].getSignerName();
-                }
-            }
-            return null;
-
-        case NAMEERROR:
-        case NODATA:
-            // Check to see if the AUTH section NSEC record(s) have rrsigs
-            rrsets = m.getSectionRRsets(Section.AUTHORITY);
-            for (int i = 0; i < rrsets.length; i++) {
-                if (rrsets[i].getType() == Type.NSEC
-                    || rrsets[i].getType() == Type.NSEC3) {
-                    return rrsets[i].getSignerName();
-                }
+        for (int section = Section.ANSWER; section < Section.ADDITIONAL; ++section) {
+            SRRset[] rrsets = m.getSectionRRsets(section);
+            for (int i = 0; i < rrsets.length; ++i) {
+                Name signerName = rrsets[i].getSignerName();
+                if (signerName != null) return signerName;
             }
-            return null;
-        default:
-            // log.debug("findSigner: could not find signer name "
-            // + "for unknown type response.");
-            return null;
         }
+        return null;
     }
 
-    public boolean dssetIsUsable(SRRset ds_rrset) {
-        for (Iterator i = ds_rrset.rrs(); i.hasNext();) {
-            DSRecord ds = (DSRecord) i.next();
-            if (supportsDigestID(ds.getDigestID())
-                && mVerifier.supportsAlgorithm(ds.getAlgorithm())) {
-                return true;
-            }
-        }
-
-        return false;
-    }
 
-    /**
-     * Given a DS rrset and a DNSKEY rrset, match the DS to a DNSKEY and verify
-     * the DNSKEY rrset with that key.
-     * 
-     * @param dnskey_rrset
-     *            The DNSKEY rrset to match against. The security status of this
-     *            rrset will be updated on a successful verification.
-     * @param ds_rrset
-     *            The DS rrset to match with. This rrset must already be
-     *            trusted.
-     * 
-     * @return a KeyEntry. This will either contain the now trusted
-     *         dnskey_rrset, a "null" key entry indicating that this DS
-     *         rrset/DNSKEY pair indicate an secure end to the island of trust
-     *         (i.e., unknown algorithms), or a "bad" KeyEntry if the dnskey
-     *         rrset fails to verify. Note that the "null" response should
-     *         generally only occur in a private algorithm scenario: normally
-     *         this sort of thing is checked before fetching the matching DNSKEY
-     *         rrset.
-     */
-    // public KeyEntry verifyNewDNSKEYs(SRRset dnskey_rrset, SRRset ds_rrset)
-    // {
-    // if (!dnskey_rrset.getName().equals(ds_rrset.getName()))
-    // {
-    // // log.debug("DNSKEY RRset did not match DS RRset by name!");
-    // return KeyEntry
-    // .newBadKeyEntry(ds_rrset.getName(), ds_rrset.getDClass());
-    // }
-    //
-    // // as long as this is false, we can consider this DS rrset to be
-    // // equivalent to no DS rrset.
-    // boolean hasUsefulDS = false;
-    //
-    // for (Iterator i = ds_rrset.rrs(); i.hasNext();)
-    // {
-    // DSRecord ds = (DSRecord) i.next();
-    //
-    // // Check to see if we can understand this DS.
-    // if (!supportsDigestID(ds.getDigestID())
-    // || !mVerifier.supportsAlgorithm(ds.getAlgorithm()))
-    // {
-    // continue;
-    // }
-    //
-    // // Once we see a single DS with a known digestID and algorithm, we
-    // // cannot return INSECURE (with a "null" KeyEntry).
-    // hasUsefulDS = true;
-    //
-    // DNSKEY : for (Iterator j = dnskey_rrset.rrs(); j.hasNext();)
-    // {
-    // DNSKEYRecord dnskey = (DNSKEYRecord) j.next();
-    //
-    // // Skip DNSKEYs that don't match the basic criteria.
-    // if (ds.getFootprint() != dnskey.getFootprint()
-    // || ds.getAlgorithm() != dnskey.getAlgorithm())
-    // {
-    // continue;
-    // }
-    //
-    // // Convert the candidate DNSKEY into a hash using the same DS hash
-    // // algorithm.
-    // byte[] key_hash = calculateDSHash(dnskey, ds.getDigestID());
-    // byte[] ds_hash = ds.getDigest();
-    //
-    // // see if there is a length mismatch (unlikely)
-    // if (key_hash.length != ds_hash.length)
-    // {
-    // continue DNSKEY;
-    // }
-    //
-    // for (int k = 0; k < key_hash.length; k++)
-    // {
-    // if (key_hash[k] != ds_hash[k]) continue DNSKEY;
-    // }
-    //
-    // // Otherwise, we have a match! Make sure that the DNSKEY verifies
-    // // *with this key*.
-    // byte res = mVerifier.verify(dnskey_rrset, dnskey);
-    // if (res == SecurityStatus.SECURE)
-    // {
-    // // log.trace("DS matched DNSKEY.");
-    // dnskey_rrset.setSecurityStatus(SecurityStatus.SECURE);
-    // return KeyEntry.newKeyEntry(dnskey_rrset);
-    // }
-    // // If it didn't validate with the DNSKEY, try the next one!
-    // }
-    // }
-    //
-    // // None of the DS's worked out.
-    //
-    // // If no DSs were understandable, then this is OK.
-    // if (!hasUsefulDS)
-    // {
-    // //
-    // log.debug("No usuable DS records were found -- treating as insecure.");
-    // return KeyEntry.newNullKeyEntry(ds_rrset.getName(), ds_rrset
-    // .getDClass(), ds_rrset.getTTL());
-    // }
-    // // If any were understandable, then it is bad.
-    // // log.debug("Failed to match any usable DS to a DNSKEY.");
-    // return KeyEntry.newBadKeyEntry(ds_rrset.getName(), ds_rrset.getDClass());
-    // }
     /**
      * Given a DNSKEY record, generate the DS record from it.
      * 
@@ -406,9 +302,9 @@ public class ValUtils {
      * @return The status (BOGUS or SECURE).
      */
     public byte verifySRRset(SRRset rrset, SRRset key_rrset) {
-        String rrset_name = rrset.getName() + "/"
-                            + Type.string(rrset.getType()) + "/"
-                            + DClass.string(rrset.getDClass());
+//        String rrset_name = rrset.getName() + "/"
+//                            + Type.string(rrset.getType()) + "/"
+//                            + DClass.string(rrset.getDClass());
 
         if (rrset.getSecurityStatus() == SecurityStatus.SECURE) {
             // log.trace("verifySRRset: rrset <" + rrset_name
@@ -433,7 +329,7 @@ public class ValUtils {
     }
 
     /**
-     * Determine if a given type map has a given typ.
+     * Determine if a given type map has a given type.
      * 
      * @param types
      *            The type map from the NSEC record.
@@ -448,6 +344,7 @@ public class ValUtils {
         return false;
     }
 
+    @SuppressWarnings("unchecked")
     public static RRSIGRecord rrsetFirstSig(RRset rrset) {
         for (Iterator i = rrset.sigs(); i.hasNext();) {
             return (RRSIGRecord) i.next();
@@ -517,7 +414,7 @@ public class ValUtils {
      * generating wildcard.
      * 
      * @param rrset
-     *            The rrset to chedck.
+     *            The rrset to check.
      * @return the wildcard name, if the rrset was synthesized from a wildcard.
      *         null if not.
      */
@@ -577,14 +474,11 @@ public class ValUtils {
 
         // If NSEC is a parent of qname, we need to check the type map
         // If the parent name has a DNAME or is a delegation point, then this
-        // NSEC
-        // is being misused.
-        if (qname.subdomain(owner)
-            && (typeMapHasType(nsec.getTypes(), Type.DNAME) || (typeMapHasType(
-                                                                               nsec.getTypes(),
-                                                                               Type.NS) && !typeMapHasType(
-                                                                                                           nsec.getTypes(),
-                                                                                                           Type.SOA)))) {
+        // NSEC is being misused.
+        boolean hasBadType = typeMapHasType(nsec.getTypes(), Type.DNAME)
+                             || (typeMapHasType(nsec.getTypes(), Type.NS) && !typeMapHasType(nsec.getTypes(),
+                                                                                             Type.SOA));
+        if (qname.subdomain(owner) && hasBadType) {
             return false;
         }