sync with external work.
[captive-validator.git] / src / com / versign / tat / dnssec / ValUtils.java
index 873733f..5d05e98 100644 (file)
@@ -1,66 +1,48 @@
-/*
- * 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
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- *    derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- */
+/***************************** -*- Java -*- ********************************\
+ *                                                                         *
+ *   Copyright (c) 2009 VeriSign, Inc. All rights reserved.                *
+ *                                                                         *
+ * This software is provided solely in connection with the terms of the    *
+ * license agreement.  Any other use without the prior express written     *
+ * permission of VeriSign is completely prohibited.  The software and      *
+ * documentation are "Commercial Items", as that term is defined in 48     *
+ * C.F.R.  section 2.101, consisting of "Commercial Computer Software" and *
+ * "Commercial Computer Software Documentation" as such terms are defined  *
+ * in 48 C.F.R. section 252.227-7014(a)(5) and 48 C.F.R. section           *
+ * 252.227-7014(a)(1), and used in 48 C.F.R. section 12.212 and 48 C.F.R.  *
+ * section 227.7202, as applicable.  Pursuant to the above and other       *
+ * relevant sections of the Code of Federal Regulations, as applicable,    *
+ * VeriSign's publications, commercial computer software, and commercial   *
+ * computer software documentation are distributed and licensed to United  *
+ * States Government end users with only those rights as granted to all    *
+ * other end users, according to the terms and conditions contained in the *
+ * license agreement(s) that accompany the products and software           *
+ * documentation.                                                          *
+ *                                                                         *
+\***************************************************************************/
+
+package com.verisign.tat.dnssec;
+
+import org.apache.log4j.Logger;
 
-package com.versign.tat.dnssec;
+import org.xbill.DNS.*;
 
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
+
 import java.util.Iterator;
 
-import org.xbill.DNS.*;
 
 /**
  * This is a collection of routines encompassing the logic of validating
  * different message types.
-*/
-
+ */
 public class ValUtils {
-
-    // These are response subtypes. They are necessary for determining the
-    // validation strategy. They have no bearing on the iterative resolution
-    // algorithm, so they are confined here.
-
-    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)
-    }
+    private static Logger st_log = Logger.getLogger(ValUtils.class);
+    private Logger        log    = Logger.getLogger(this.getClass());
 
     /** A local copy of the verifier object. */
-    private DnsSecVerifier  mVerifier;
+    private DnsSecVerifier mVerifier;
 
     public ValUtils(DnsSecVerifier verifier) {
         mVerifier = verifier;
@@ -68,18 +50,19 @@ public class ValUtils {
 
     /**
      * Given a response, classify ANSWER responses into a subtype.
-     * 
+     *
      * @param m
      *            The response to classify.
-     * 
+     *
      * @return A subtype ranging from UNKNOWN to NAMEERROR.
      */
     public static ResponseType classifyResponse(SMessage m, Name zone) {
+        SRRset [] rrsets;
 
-        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) {
+        if ((m.getRcode() == Rcode.NXDOMAIN) &&
+                (m.getCount(Section.ANSWER) == 0)) {
             return ResponseType.NAMEERROR;
         }
 
@@ -92,12 +75,13 @@ public class ValUtils {
         // 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) {
+        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)) {
+                if ((rrsets[i].getType() == Type.NS) &&
+                        strictSubdomain(rrsets[i].getName(), zone)) {
                     return ResponseType.REFERRAL;
                 }
             }
@@ -123,11 +107,17 @@ public class ValUtils {
         // 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 ResponseType.POSITIVE;
-            if (rrsets[i].getType() == Type.CNAME) return ResponseType.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);
+        st_log.warn("Failed to classify response message:\n" + m);
+
         return ResponseType.UNKNOWN;
     }
 
@@ -135,7 +125,7 @@ public class ValUtils {
      * Given a response, determine the name of the "signer". This is primarily
      * to determine if the response is, in fact, signed at all, and, if so, what
      * is the name of the most pertinent keyset.
-     * 
+     *
      * @param m
      *            The response to analyze.
      * @return a signer name, if the response is signed (even partially), or
@@ -145,28 +135,32 @@ public class ValUtils {
         // 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.
+        for (int section = Section.ANSWER; section < Section.ADDITIONAL;
+                ++section) {
+            SRRset [] rrsets = m.getSectionRRsets(section);
 
-        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;
+
+                if (signerName != null) {
+                    return signerName;
+                }
             }
         }
+
         return null;
     }
 
-
     /**
      * Given a DNSKEY record, generate the DS record from it.
-     * 
+     *
      * @param keyrec
      *            the DNSKEY record in question.
      * @param ds_alg
      *            The DS digest algorithm in use.
      * @return the corresponding {@link org.xbill.DNS.DSRecord}
      */
-    public static byte[] calculateDSHash(DNSKEYRecord keyrec, int ds_alg) {
+    public static byte [] calculateDSHash(DNSKEYRecord keyrec, int ds_alg) {
         DNSOutput os = new DNSOutput();
 
         os.writeByteArray(keyrec.getName().toWireCanonical());
@@ -174,65 +168,81 @@ public class ValUtils {
 
         try {
             MessageDigest md = null;
+
             switch (ds_alg) {
-            case DSRecord.SHA1_DIGEST_ID:
-                md = MessageDigest.getInstance("SHA");
-                return md.digest(os.toByteArray());
-            case DSRecord.SHA256_DIGEST_ID:
-                md = MessageDigest.getInstance("SHA256");
-                return md.digest(os.toByteArray());
-            default:
-                // st_log.warn("Unknown DS algorithm: " + ds_alg);
-                return null;
-            }
+                case DSRecord.SHA1_DIGEST_ID:
+                    md = MessageDigest.getInstance("SHA");
+
+                    return md.digest(os.toByteArray());
+
+                case DSRecord.SHA256_DIGEST_ID:
+                    md = MessageDigest.getInstance("SHA256");
 
+                    return md.digest(os.toByteArray());
+
+                default:
+                    st_log.warn("Unknown DS algorithm: " + ds_alg);
+
+                    return null;
+            }
         } catch (NoSuchAlgorithmException e) {
-            // st_log.error("Error using DS algorithm: " + ds_alg, e);
+            st_log.error("Error using DS algorithm: " + ds_alg, e);
+
             return null;
         }
     }
 
     public static boolean supportsDigestID(int digest_id) {
-        if (digest_id == DSRecord.SHA1_DIGEST_ID) return true;
-        if (digest_id == DSRecord.SHA256_DIGEST_ID) return true;
+        if (digest_id == DSRecord.SHA1_DIGEST_ID) {
+            return true;
+        }
+
+        if (digest_id == DSRecord.SHA256_DIGEST_ID) {
+            return true;
+        }
+
         return false;
     }
 
     /**
      * Check to see if a type is a special DNSSEC type.
-     * 
+     *
      * @param type
      *            The type.
-     * 
+     *
      * @return true if the type is one of the special DNSSEC types.
      */
     public static boolean isDNSSECType(int type) {
         switch (type) {
-        case Type.DNSKEY:
-        case Type.NSEC:
-        case Type.DS:
-        case Type.RRSIG:
-        case Type.NSEC3:
-            return true;
-        default:
-            return false;
+            case Type.DNSKEY:
+            case Type.NSEC:
+            case Type.DS:
+            case Type.RRSIG:
+            case Type.NSEC3:
+                return true;
+
+            default:
+                return false;
         }
     }
 
     /**
      * Set the security status of a particular RRset. This will only upgrade the
      * security status.
-     * 
+     *
      * @param rrset
      *            The SRRset to update.
      * @param security
      *            The security status.
      */
     public static void setRRsetSecurity(SRRset rrset, byte security) {
-        if (rrset == null) return;
+        if (rrset == null) {
+            return;
+        }
 
         int cur_sec = rrset.getSecurityStatus();
-        if (cur_sec == SecurityStatus.UNCHECKED || security > cur_sec) {
+
+        if ((cur_sec == SecurityStatus.UNCHECKED) || (security > cur_sec)) {
             rrset.setSecurityStatus(security);
         }
     }
@@ -241,28 +251,33 @@ public class ValUtils {
      * Set the security status of a message and all of its RRsets. This will
      * only upgrade the status of the message (i.e., set to more secure, not
      * less) and all of the RRsets.
-     * 
+     *
      * @param m
      * @param security
      *            KeyEntry ke;
-     * 
+     *
      *            SMessage m = response.getSMessage(); SRRset ans_rrset =
      *            m.findAnswerRRset(qname, qtype, qclass);
-     * 
+     *
      *            ke = verifySRRset(ans_rrset, key_rrset); if
      *            (ans_rrset.getSecurityStatus() != SecurityStatus.SECURE) {
      *            return; } key_rrset = ke.getRRset();
      */
     public static void setMessageSecurity(SMessage m, byte security) {
-        if (m == null) return;
+        if (m == null) {
+            return;
+        }
 
         int cur_sec = m.getStatus();
-        if (cur_sec == SecurityStatus.UNCHECKED || security > cur_sec) {
+
+        if ((cur_sec == SecurityStatus.UNCHECKED) || (security > cur_sec)) {
             m.setStatus(security);
         }
 
-        for (int section = Section.ANSWER; section <= Section.ADDITIONAL; section++) {
-            SRRset[] rrsets = m.getSectionRRsets(section);
+        for (int section = Section.ANSWER; section <= Section.ADDITIONAL;
+                section++) {
+            SRRset [] rrsets = m.getSectionRRsets(section);
+
             for (int i = 0; i < rrsets.length; i++) {
                 setRRsetSecurity(rrsets[i], security);
             }
@@ -273,7 +288,7 @@ public class ValUtils {
      * Given an SRRset that is signed by a DNSKEY found in the key_rrset, verify
      * it. This will return the status (either BOGUS or SECURE) and set that
      * status in rrset.
-     * 
+     *
      * @param rrset
      *            The SRRset to verify.
      * @param key_rrset
@@ -281,45 +296,49 @@ 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
-            // + "> previously found to be SECURE");
+            log.trace("verifySRRset: rrset <" + rrset_name +
+                "> previously found to be SECURE");
+
             return SecurityStatus.SECURE;
         }
 
         byte status = mVerifier.verify(rrset, key_rrset);
+
         if (status != SecurityStatus.SECURE) {
-            // log.debug("verifySRRset: rrset <" + rrset_name +
-            // "> found to be BAD");
+            log.debug("verifySRRset: rrset <" + rrset_name +
+                "> found to be BAD");
             status = SecurityStatus.BOGUS;
+        } else {
+            log.trace("verifySRRset: rrset <" + rrset_name +
+                "> found to be SECURE");
         }
-        // else
-        // {
-        // log.trace("verifySRRset: rrset <" + rrset_name +
-        // "> found to be SECURE");
-        // }
 
         rrset.setSecurityStatus(status);
+
         return status;
     }
 
     /**
      * Determine if a given type map has a given type.
-     * 
+     *
      * @param types
      *            The type map from the NSEC record.
      * @param type
      *            The type to look for.
      * @return true if the type is present in the type map, false otherwise.
      */
-    public static boolean typeMapHasType(int[] types, int type) {
+    public static boolean typeMapHasType(int [] types, int type) {
         for (int i = 0; i < types.length; i++) {
-            if (types[i] == type) return true;
+            if (types[i] == type) {
+                return true;
+            }
         }
+
         return false;
     }
 
@@ -328,28 +347,33 @@ public class ValUtils {
         for (Iterator i = rrset.sigs(); i.hasNext();) {
             return (RRSIGRecord) i.next();
         }
+
         return null;
     }
 
     /**
      * Finds the longest common name between two domain names.
-     * 
+     *
      * @param domain1
      * @param domain2
      * @return
      */
     public static Name longestCommonName(Name domain1, Name domain2) {
-        if (domain1 == null || domain2 == null) return null;
+        if ((domain1 == null) || (domain2 == null)) {
+            return null;
+        }
+
         // for now, do this in a a fairly brute force way
         // FIXME: convert this to direct operations on the byte[]
-
         int d1_labels = domain1.labels();
         int d2_labels = domain2.labels();
 
-        int l = (d1_labels < d2_labels) ? d1_labels : d2_labels;
+        int l         = (d1_labels < d2_labels) ? d1_labels : d2_labels;
+
         for (int i = l; i > 0; i--) {
             Name n1 = new Name(domain1, d1_labels - i);
             Name n2 = new Name(domain2, d2_labels - i);
+
             if (n1.equals(n2)) {
                 return n1;
             }
@@ -361,26 +385,33 @@ public class ValUtils {
     public static boolean strictSubdomain(Name child, Name parent) {
         int clabels = child.labels();
         int plabels = parent.labels();
-        if (plabels >= clabels) return false;
+
+        if (plabels >= clabels) {
+            return false;
+        }
 
         Name n = new Name(child, clabels - plabels);
+
         return parent.equals(n);
     }
 
     /**
      * Determine by looking at a signed RRset whether or not the rrset name was
      * the result of a wildcard expansion.
-     * 
+     *
      * @param rrset
      *            The rrset to examine.
      * @return true if the rrset is a wildcard expansion. This will return false
      *         for all unsigned rrsets.
      */
     public static boolean rrsetIsWildcardExpansion(RRset rrset) {
-        if (rrset == null) return false;
+        if (rrset == null) {
+            return false;
+        }
+
         RRSIGRecord rrsig = rrsetFirstSig(rrset);
 
-        if (rrset.getName().labels() - 1 > rrsig.getLabels()) {
+        if ((rrset.getName().labels() - 1) > rrsig.getLabels()) {
             return true;
         }
 
@@ -391,23 +422,28 @@ public class ValUtils {
      * Determine by looking at a signed RRset whether or not the RRset name was
      * the result of a wildcard expansion. If so, return the name of the
      * generating wildcard.
-     * 
+     *
      * @param rrset
      *            The rrset to check.
      * @return the wildcard name, if the rrset was synthesized from a wildcard.
      *         null if not.
      */
     public static Name rrsetWildcard(RRset rrset) {
-        if (rrset == null) return null;
+        if (rrset == null) {
+            return null;
+        }
+
         RRSIGRecord rrsig = rrsetFirstSig(rrset);
 
         // if the RRSIG label count is shorter than the number of actual labels,
         // then this rrset was synthesized from a wildcard.
         // Note that the RRSIG label count doesn't count the root label.
         int label_diff = (rrset.getName().labels() - 1) - rrsig.getLabels();
+
         if (label_diff > 0) {
             return rrset.getName().wild(label_diff);
         }
+
         return null;
     }
 
@@ -430,7 +466,7 @@ public class ValUtils {
     /**
      * Determine if the given NSEC proves a NameError (NXDOMAIN) for a given
      * qname.
-     * 
+     *
      * @param nsec
      *            The NSEC to check.
      * @param qname
@@ -442,9 +478,9 @@ public class ValUtils {
      * @return true if the NSEC proves the condition.
      */
     public static boolean nsecProvesNameError(NSECRecord nsec, Name qname,
-                                              Name signerName) {
+        Name signerName) {
         Name owner = nsec.getName();
-        Name next = nsec.getNext();
+        Name next  = nsec.getNext();
 
         // If NSEC owner == qname, then this NSEC proves that qname exists.
         if (qname.equals(owner)) {
@@ -454,24 +490,26 @@ 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.
-        boolean hasBadType = typeMapHasType(nsec.getTypes(), Type.DNAME)
-                             || (typeMapHasType(nsec.getTypes(), Type.NS) && !typeMapHasType(nsec.getTypes(),
-                                                                                             Type.SOA));
+        boolean hasBadType = typeMapHasType(nsec.getTypes(), Type.DNAME) ||
+            (typeMapHasType(nsec.getTypes(), Type.NS) &&
+            !typeMapHasType(nsec.getTypes(), Type.SOA));
+
         if (qname.subdomain(owner) && hasBadType) {
             return false;
         }
 
-        if (qname.compareTo(owner) > 0 && (qname.compareTo(next) < 0)
-            || signerName.equals(next)) {
+        if (((qname.compareTo(owner) > 0) && (qname.compareTo(next) < 0)) ||
+                signerName.equals(next)) {
             return true;
         }
+
         return false;
     }
 
     /**
      * Determine if a NSEC record proves the non-existence of a wildcard that
      * could have produced qname.
-     * 
+     *
      * @param nsec
      *            The nsec to check.
      * @param qname
@@ -481,17 +519,18 @@ public class ValUtils {
      * @return true if the NSEC proves the condition.
      */
     public static boolean nsecProvesNoWC(NSECRecord nsec, Name qname,
-                                         Name signerName) {
-        Name owner = nsec.getName();
-        Name next = nsec.getNext();
+        Name signerName) {
+        Name owner         = nsec.getName();
+        Name next          = nsec.getNext();
 
-        int qname_labels = qname.labels();
-        int signer_labels = signerName.labels();
+        int  qname_labels  = qname.labels();
+        int  signer_labels = signerName.labels();
 
         for (int i = qname_labels - signer_labels; i > 0; i--) {
             Name wc_name = qname.wild(i);
-            if (wc_name.compareTo(owner) > 0
-                && (wc_name.compareTo(next) < 0 || signerName.equals(next))) {
+
+            if ((wc_name.compareTo(owner) > 0) &&
+                    ((wc_name.compareTo(next) < 0) || signerName.equals(next))) {
                 return true;
             }
         }
@@ -505,7 +544,7 @@ public class ValUtils {
      * wildcard case. If the ownername of 'nsec' is a wildcard, the validator
      * must still be provided proof that qname did not directly exist and that
      * the wildcard is, in fact, *.closest_encloser.
-     * 
+     *
      * @param nsec
      *            The NSEC to check
      * @param qname
@@ -515,7 +554,7 @@ public class ValUtils {
      * @return true if the NSEC proves the condition.
      */
     public static boolean nsecProvesNodata(NSECRecord nsec, Name qname,
-                                           int qtype) {
+        int qtype) {
         if (!nsec.getName().equals(qname)) {
             // wildcard checking.
 
@@ -532,10 +571,11 @@ public class ValUtils {
                 // The qname must be a strict subdomain of the closest encloser,
                 // and
                 // the qtype must be absent from the type map.
-                if (!strictSubdomain(qname, ce)
-                    || typeMapHasType(nsec.getTypes(), qtype)) {
+                if (!strictSubdomain(qname, ce) ||
+                        typeMapHasType(nsec.getTypes(), qtype)) {
                     return false;
                 }
+
                 return true;
             }
 
@@ -545,10 +585,11 @@ public class ValUtils {
             // be
             // less than qname, and the next name will be a child domain of the
             // qname.
-            if (strictSubdomain(nsec.getNext(), qname)
-                && qname.compareTo(nsec.getName()) > 0) {
+            if (strictSubdomain(nsec.getNext(), qname) &&
+                    (qname.compareTo(nsec.getName()) > 0)) {
                 return true;
             }
+
             // Otherwise, this NSEC does not prove ENT, so it does not prove
             // NODATA.
             return false;
@@ -569,8 +610,8 @@ public class ValUtils {
         // not a zone apex), then we should have gotten a referral (or we just
         // got
         // the wrong NSEC).
-        if (typeMapHasType(nsec.getTypes(), Type.NS)
-            && !typeMapHasType(nsec.getTypes(), Type.SOA)) {
+        if (typeMapHasType(nsec.getTypes(), Type.NS) &&
+                !typeMapHasType(nsec.getTypes(), Type.SOA)) {
             return false;
         }
 
@@ -579,7 +620,8 @@ public class ValUtils {
 
     public static byte nsecProvesNoDS(NSECRecord nsec, Name qname) {
         // Could check to make sure the qname is a subdomain of nsec
-        int[] types = nsec.getTypes();
+        int [] types = nsec.getTypes();
+
         if (typeMapHasType(types, Type.SOA) || typeMapHasType(types, Type.DS)) {
             // SOA present means that this is the NSEC from the child, not the
             // parent (so it is the wrong one)
@@ -594,8 +636,18 @@ public class ValUtils {
             // anything one way or the other.
             return SecurityStatus.INSECURE;
         }
+
         // Otherwise, this proves no DS.
         return SecurityStatus.SECURE;
     }
 
+    // These are response subtypes. They are necessary for determining the
+    // validation strategy. They have no bearing on the iterative resolution
+    // algorithm, so they are confined here.
+    public enum ResponseType {UNTYPED, UNKNOWN, POSITIVE, CNAME, NODATA, 
+        NAMEERROR, ANY, REFERRAL, 
+        // a referral response
+        THROWAWAY;
+        // a throwaway response (i.e., an error)
+    }
 }