<property name="build.lib.dest" value="${build.dir}/lib" />
<property name="build.src" value="src" />
+ <property name="build.test.src" value="tests" />
+ <property name="build.test.dest" value="${build.dir}/tests/classes" />
+
<property name="packages" value="com.verisignlabs.dnssec.*" />
<property name="doc.dir" value="docs" />
<property name="javadoc.dest" value="${doc.dir}/javadoc" />
</path>
<property name="project.classpath" refid="project.classpath" />
+ <!-- set the classpath for the unit tests -->
+ <path id="test.classpath">
+ <pathelement location="${build.dest}" />
+ <fileset dir="${lib.dir}" includes="*.jar,*.zip" />
+ <path location="${build.test.dest}" />
+ <path location="${build.dest}" />
+ <path location="${build.test.src}/junit-3.8.1.jar" />
+ </path>
+
<target name="prepare-src">
<mkdir dir="${build.dest}" />
<mkdir dir="${build.lib.dest}" />
</target>
+ <target name="prepare-test">
+ <mkdir dir="${build.test.dest}" />
+ </target>
+
<target name="compile" depends="prepare-src" >
<javac srcdir="${build.src}"
destdir="${build.dest}"
</tar>
</target>
+ <target name="compile_tests" depends="prepare-test,compile">
+ <javac destdir="${build.test.dest}" debug="true"
+ classpathref="test.classpath"
+ includeantruntime="false"
+ target="1.4"
+ source="1.4">
+ <src path="${build.test.src}"/>
+ </javac>
+ </target>
+
+ <target name="test" depends="compile_tests">
+ <antcall target="_run_tests">
+ <param name="classpathref" value="tests.classpath" />
+ </antcall>
+ </target>
+
+ <target name="_run_tests">
+ <junit fork="yes" forkmode="perTest" dir="${basedir}"
+ haltonerror="on" haltonfailure="on"
+ includeantruntime="true">
+ <classpath>
+ <path location="${build.dest}" />
+ <fileset dir="${lib.dir}" includes="*.jar,*.zip" />
+ <path location="${build.test.dest}" />
+ <path location="${build.dest}" />
+ <path location="${build.test.src}/junit-3.8.1.jar" />
+ </classpath>
+
+ <formatter type="plain" usefile="no" />
+
+ <test name="${testcase}" if="testcase" />
+
+ <batchtest unless="testcase">
+ <fileset dir="${build.test.dest}">
+ <include name="**/*Test.class" />
+ </fileset>
+ </batchtest>
+ </junit>
+ </target>
+
<target name="clean" depends="usage">
<delete dir="${build.dest}" />
<delete dir="${build.lib.dest}" />
</target>
</project>
-
m.setStatus(SecurityStatus.BOGUS);
}
- // FIXME: write CNAME validation code.
- private void validateCNAMEResponse(SMessage message, SRRset key_rrset) {}
+ /**
+ * Given a "CNAME" response (i.e., a response that contains at
+ * least one CNAME, and qtype != CNAME). This largely consists of
+ * validating each CNAME RRset until the CNAME chain goes "out of
+ * zone". Note that out-of-order CNAME chains will have been
+ * cleaned up via normalize(). When traversing the CNAME chain,
+ * we detect if the CNAME were generated from a wildcard, and we
+ * detect when the chain goes "out-of-zone". If the chain doesn't
+ * go out-of-zone, we then determine if the CNAME response was
+ * positive or negative (i.e., did it end with a non-CNAME
+ * RRset?). For each in-zone wildcard generated CNAME, we check
+ * for a proof that the alias (the owner of each cname) doesn't
+ * exist. If the response is negative (i.e., remains in-zone and
+ * results in no RRset, we do a NODATA or NXDOMAIN proof based on
+ * the actual RCODE.
+ *
+ * Note that once the CNAME chain goes out of zone, any further
+ * CNAMEs are not DNSSEC validated (we would need more trusted
+ * keysets for that), so this isn't useful in all cases (i.e., for
+ * testing a nameserver, like BIND, which generates CNAME chains
+ * across zones.)
+ *
+ * Note that by the time this method is called, the process of
+ * finding the trusted DNSKEY rrset that signs this reponse must
+ * already have been completed.
+ */
+ private void validateCNAMEResponse(SMessage message, SRRset key_rrset)
+ {
+ Name qname = message.getQName();
+ int qtype = message.getQType();
+
+ Name sname = qname; // this is the "current" name in the chain
+ boolean dname = false; // a flag indicating that prev iteration was a dname
+ boolean inZone = true; // a flag telling us if we ended up in zone.
+ boolean positive = false; // a flag telling us if we ended with a positive answer
+ List<Name> wildcards = new Vector<Name>();
+ Name wc = null;
+ Name zone = key_rrset.getName();
+
+ SRRset[] rrsets = message.getSectionRRsets(Section.ANSWER);
+
+ // Validate the ANSWER section RRsets.
+ for (int i = 0; i < rrsets.length; i++) {
+
+ int rtype = rrsets[i].getType();
+ Name rname = rrsets[i].getName();
+
+ // Follow the CNAME chain
+ if (rtype == Type.CNAME) {
+ // If we've gotten off track... Note: this should be
+ // impossible with normalization in effect.
+ if (!sname.equals(rname)) {
+ mErrorList.add("CNAME chain is broken: expected owner name of " +
+ sname + " got: " + rname);
+ message.setStatus(SecurityStatus.BOGUS);
+ return;
+ }
+
+ sname = ((CNAMERecord) rrsets[i].first()).getAlias();
+
+ // Check to see if the CNAME was generated by a
+ // wildcard. We store the generated name instead of
+ // the wildcard value, as we need to prove that the
+ // wildcard wasn't blocked.
+ wc = ValUtils.rrsetWildcard(rrsets[i]);
+ if (wc != null) {
+ wildcards.add(sname);
+ }
+ }
+
+ // Note when we see a DNAME.
+ if (rtype == Type.DNAME) {
+ dname = true;
+ wc = ValUtils.rrsetWildcard(rrsets[i]);
+ if (wc != null) {
+ mErrorList.add("Illegal wildcard DNAME found: " + rrsets[i]);
+ }
+ }
+
+ // Skip validation of CNAMEs following DNAMEs. The
+ // normalization step will have synthesized an unsigned
+ // CNAME RRset.
+ if (dname && rtype == Type.CNAME) {
+ dname = false;
+ continue;
+ }
+
+ if (rtype == qtype) {
+ positive = true;
+ }
+
+ // Once we've gone off the reservation, avoid further
+ // validation.
+ if (! rname.subdomain(zone)) {
+ inZone = false;
+ break;
+ }
+
+ int status = mValUtils.verifySRRset(rrsets[i], key_rrset);
+
+ if (status != SecurityStatus.SECURE) {
+ mErrorList.add("CNAME response has a failed ANSWER rrset: " +
+ rrsets[i]);
+ message.setStatus(SecurityStatus.BOGUS);
+
+ return;
+ }
+ }
+
+
+ // Validate the AUTHORITY section.
+ rrsets = message.getSectionRRsets(Section.ANSWER);
+ for (int i = 0; i < rrsets.length; i++) {
+
+
+ }
+
+ log.trace("Successfully validated CNAME response");
+ message.setStatus(SecurityStatus.SECURE);
+ }
/**
* Given an "ANY" response -- a response that contains an answer
--- /dev/null
+package com.verisign.tat.dnssec;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+
+public class CaptiveValidatorTest
+{
+
+ public static class Test_init extends TestCase
+ {
+ protected void setUp() {
+ // Nothing to do yet.
+ }
+
+ public void test_0arg()
+ {
+ CaptiveValidator v = new CaptiveValidator();
+ assertNotNull(v);
+ }
+ }
+
+ public static class Test_Validate extends TestCase
+ {
+ Message baseMessage;
+
+ // Set up a base Response message
+ protected void setUp() {
+ baseMessage = new Message();
+
+ // set up our response header; note that the captive
+ // validator code doesn't actually look at anything in the
+ // header but the RCODE, really, so the flag values
+ // probably don't matter. But make them realistic anyway.
+ Header hdr = new Header();
+ hdr.setOpcode(DNS.Opcode.QUERY);
+ hdr.setRcode(DNS.Rcode.NOERROR);
+ hdr.setFlag(DNS.Flags.QR);
+ hdr.setFlag(DNS.Flags.AA);
+ hdr.setFlag(DNS.Flags.RD);
+ baseMessage.setHeader(hdr);
+
+
+ }
+
+ public void test_positive()
+ {
+ Message m = new Message();
+ }
+
+ public void test_referral()
+ {
+ }
+
+ public void test_nodata()
+ {
+ }
+
+ public void test_nameerror()
+ {
+ }
+
+ public void test_cname()
+ {
+ }
+
+ public void test_any()
+ {
+ }
+ }
+
+
+ public static Test suite()
+ {
+ TestSuite s = new TestSuite();
+ s.addTestSuite(Test_init.class);
+ s.addTestSuite(Test_Validate.class);
+ return s;
+ }
+
+}