]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
3008. [func] Response policy zones (RPZ) support. [RT #21726]
authorMark Andrews <marka@isc.org>
Thu, 13 Jan 2011 01:59:28 +0000 (01:59 +0000)
committerMark Andrews <marka@isc.org>
Thu, 13 Jan 2011 01:59:28 +0000 (01:59 +0000)
45 files changed:
CHANGES
bin/named/include/named/query.h
bin/named/query.c
bin/named/server.c
bin/tests/system/Makefile.in
bin/tests/system/README
bin/tests/system/conf.sh.in
bin/tests/system/rpz/Makefile.in [new file with mode: 0644]
bin/tests/system/rpz/clean.sh [new file with mode: 0644]
bin/tests/system/rpz/ns1/named.conf [new file with mode: 0644]
bin/tests/system/rpz/ns1/root.db [new file with mode: 0644]
bin/tests/system/rpz/ns2/hints [new file with mode: 0644]
bin/tests/system/rpz/ns2/named.conf [new file with mode: 0644]
bin/tests/system/rpz/ns2/tld2.db [new file with mode: 0644]
bin/tests/system/rpz/ns3/base.db [new file with mode: 0644]
bin/tests/system/rpz/ns3/hints [new file with mode: 0644]
bin/tests/system/rpz/ns3/named.conf [new file with mode: 0644]
bin/tests/system/rpz/rpz.c [new file with mode: 0644]
bin/tests/system/rpz/setup.sh [new file with mode: 0644]
bin/tests/system/rpz/test1 [new file with mode: 0644]
bin/tests/system/rpz/test2 [new file with mode: 0644]
bin/tests/system/rpz/test3 [new file with mode: 0644]
bin/tests/system/rpz/test4 [new file with mode: 0644]
bin/tests/system/rpz/test5 [new file with mode: 0644]
bin/tests/system/rpz/tests.sh [new file with mode: 0644]
config.h.in
configure.in
doc/arm/Bv9ARM-book.xml
lib/dns/Makefile.in
lib/dns/db.c
lib/dns/include/dns/db.h
lib/dns/include/dns/name.h
lib/dns/include/dns/rpz.h [new file with mode: 0644]
lib/dns/include/dns/view.h
lib/dns/name.c
lib/dns/rbtdb.c
lib/dns/rpz.c [new file with mode: 0644]
lib/dns/sdb.c
lib/dns/sdlz.c
lib/dns/view.c
lib/dns/win32/libdns.def
lib/dns/win32/libdns.dsp
lib/dns/win32/libdns.mak
lib/dns/zone.c
lib/isccfg/namedconf.c

diff --git a/CHANGES b/CHANGES
index 40cc370f86d79d9292fb2369e066f95db04026a6..e8ea884406153ea7cb5ad33195acc583319d64b3 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,5 @@
+3008.  [func]          Response policy zones (RPZ) support. [RT #21726]
+
 3007.  [bug]           Named failed to preserve the case of domain names in
                        rdata which is no compressable when writing master
                        files.  [RT #22863]
index 2f65df3b7995623d9950380993e30dbfb62066df..b5bb461f1ac90b9bf6050fdde7881bbd10ed48bb 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: query.h,v 1.43 2010/12/08 02:46:15 marka Exp $ */
+/* $Id: query.h,v 1.44 2011/01/13 01:59:25 marka Exp $ */
 
 #ifndef NAMED_QUERY_H
 #define NAMED_QUERY_H 1
@@ -26,8 +26,9 @@
 #include <isc/buffer.h>
 #include <isc/netaddr.h>
 
-#include <dns/types.h>
 #include <dns/rdataset.h>
+#include <dns/rpz.h>
+#include <dns/types.h>
 
 #include <named/types.h>
 
@@ -35,6 +36,7 @@
 typedef struct ns_dbversion {
        dns_db_t                        *db;
        dns_dbversion_t                 *version;
+       isc_boolean_t                   acl_checked;
        isc_boolean_t                   queryok;
        ISC_LINK(struct ns_dbversion)   link;
 } ns_dbversion_t;
@@ -55,6 +57,7 @@ struct ns_query {
        isc_boolean_t                   isreferral;
        isc_mutex_t                     fetchlock;
        dns_fetch_t *                   fetch;
+       dns_rpz_st_t *                  rpz_st;
        isc_bufferlist_t                namebufs;
        ISC_LIST(ns_dbversion_t)        activeversions;
        ISC_LIST(ns_dbversion_t)        freeversions;
index 558ec3908aaa781741b1258ee1e278f9e2d62d23..05f3617c3aa8086c7fc85d1c4d9d10f219990dcf 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: query.c,v 1.350 2010/12/24 02:20:47 each Exp $ */
+/* $Id: query.c,v 1.351 2011/01/13 01:59:25 marka Exp $ */
 
 /*! \file */
 
 #define DNS_GETDB_NOEXACT 0x01U
 #define DNS_GETDB_NOLOG 0x02U
 #define DNS_GETDB_PARTIAL 0x04U
+#define DNS_GETDB_IGNOREACL 0x08U
 
 #define PENDINGOK(x)   (((x) & DNS_DBFIND_PENDINGOK) != 0)
 
@@ -159,6 +160,9 @@ query_findclosestnsec3(dns_name_t *qname, dns_db_t *db,
 static inline void
 log_queryerror(ns_client_t *client, isc_result_t result, int line, int level);
 
+static void
+rpz_st_clear(ns_client_t *client);
+
 /*%
  * Increment query statistics counters.
  */
@@ -353,6 +357,14 @@ query_reset(ns_client_t *client, isc_boolean_t everything) {
                                    NS_QUERYATTR_SECURE);
        client->query.restarts = 0;
        client->query.timerset = ISC_FALSE;
+       if (client->query.rpz_st != NULL) {
+               rpz_st_clear(client);
+               if (everything) {
+                       isc_mem_put(client->mctx, client->query.rpz_st,
+                                   sizeof(*client->query.rpz_st));
+                       client->query.rpz_st = NULL;
+               }
+       }
        client->query.origqname = NULL;
        client->query.dboptions = 0;
        client->query.fetchoptions = 0;
@@ -570,6 +582,7 @@ ns_query_init(ns_client_t *client) {
        ISC_LIST_INIT(client->query.freeversions);
        client->query.restarts = 0;
        client->query.timerset = ISC_FALSE;
+       client->query.rpz_st = NULL;
        client->query.qname = NULL;
        result = isc_mutex_init(&client->query.fetchlock);
        if (result != ISC_R_SUCCESS)
@@ -597,8 +610,7 @@ ns_query_init(ns_client_t *client) {
 }
 
 static inline ns_dbversion_t *
-query_findversion(ns_client_t *client, dns_db_t *db,
-                 isc_boolean_t *newzonep)
+query_findversion(ns_client_t *client, dns_db_t *db)
 {
        ns_dbversion_t *dbversion;
 
@@ -624,12 +636,10 @@ query_findversion(ns_client_t *client, dns_db_t *db,
                        return (NULL);
                dns_db_attach(db, &dbversion->db);
                dns_db_currentversion(db, &dbversion->version);
-               dbversion->queryok = ISC_FALSE;
+               dbversion->acl_checked = ISC_FALSE;
                ISC_LIST_APPEND(client->query.activeversions,
                                dbversion, link);
-               *newzonep = ISC_TRUE;
-       } else
-               *newzonep = ISC_FALSE;
+       }
 
        return (dbversion);
 }
@@ -641,7 +651,6 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
                     dns_dbversion_t **versionp)
 {
        isc_result_t result;
-       isc_boolean_t check_acl, new_zone;
        dns_acl_t *queryacl;
        ns_dbversion_t *dbversion;
 
@@ -657,7 +666,7 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
        if (!client->view->additionalfromauth &&
            client->query.authdbset &&
            db != client->query.authdb)
-               goto refuse;
+               return (DNS_R_REFUSED);
 
        /*
         * Non recursive query to a static-stub zone is prohibited; its
@@ -666,7 +675,7 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
         */
        if (dns_zone_gettype(zone) == dns_zone_staticstub &&
            !RECURSIONOK(client)) {
-               goto refuse;
+               return (DNS_R_REFUSED);
        }
 
        /*
@@ -677,23 +686,19 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
         * Also, get the database version to use.
         */
 
-       check_acl = ISC_TRUE;   /* Keep compiler happy. */
-       queryacl = NULL;
-
        /*
         * Get the current version of this database.
         */
-       dbversion = query_findversion(client, db, &new_zone);
-       if (dbversion == NULL) {
-               result = DNS_R_SERVFAIL;
-               goto fail;
-       }
-       if (new_zone) {
-               check_acl = ISC_TRUE;
-       } else if (!dbversion->queryok) {
-               goto refuse;
-       } else {
-               check_acl = ISC_FALSE;
+       dbversion = query_findversion(client, db);
+       if (dbversion == NULL)
+               return (DNS_R_SERVFAIL);
+
+       if ((options & DNS_GETDB_IGNOREACL) != 0)
+               goto approved;
+       if (dbversion->acl_checked) {
+               if (!dbversion->queryok)
+                       return (DNS_R_REFUSED);
+               goto approved;
        }
 
        queryacl = dns_zone_getqueryacl(zone);
@@ -707,88 +712,68 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
                         * allowed to make queries, otherwise the query should
                         * be refused.
                         */
-                       check_acl = ISC_FALSE;
+                       dbversion->acl_checked = ISC_TRUE;
                        if ((client->query.attributes &
-                            NS_QUERYATTR_QUERYOK) == 0)
-                               goto refuse;
-               } else {
-                       /*
-                        * We haven't evaluated the view's queryacl yet.
-                        */
-                       check_acl = ISC_TRUE;
+                            NS_QUERYATTR_QUERYOK) == 0) {
+                               dbversion->queryok = ISC_FALSE;
+                               return (DNS_R_REFUSED);
+                       }
+                       dbversion->queryok = ISC_TRUE;
+                       goto approved;
                }
        }
 
-       if (check_acl) {
-               isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0);
-
-               result = ns_client_checkaclsilent(client, NULL, queryacl,
-                                                 ISC_TRUE);
-               if (log) {
-                       char msg[NS_CLIENT_ACLMSGSIZE("query")];
-                       if (result == ISC_R_SUCCESS) {
-                               if (isc_log_wouldlog(ns_g_lctx,
-                                                    ISC_LOG_DEBUG(3)))
-                               {
-                                       ns_client_aclmsg("query", name, qtype,
-                                                        client->view->rdclass,
-                                                        msg, sizeof(msg));
-                                       ns_client_log(client,
-                                                     DNS_LOGCATEGORY_SECURITY,
-                                                     NS_LOGMODULE_QUERY,
-                                                     ISC_LOG_DEBUG(3),
-                                                     "%s approved", msg);
-                               }
-                       } else {
+       result = ns_client_checkaclsilent(client, NULL, queryacl, ISC_TRUE);
+       if ((options & DNS_GETDB_NOLOG) == 0) {
+               char msg[NS_CLIENT_ACLMSGSIZE("query")];
+               if (result == ISC_R_SUCCESS) {
+                       if (isc_log_wouldlog(ns_g_lctx, ISC_LOG_DEBUG(3))) {
                                ns_client_aclmsg("query", name, qtype,
                                                 client->view->rdclass,
                                                 msg, sizeof(msg));
-                               ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
-                                             NS_LOGMODULE_QUERY, ISC_LOG_INFO,
-                                             "%s denied", msg);
+                               ns_client_log(client,
+                                             DNS_LOGCATEGORY_SECURITY,
+                                             NS_LOGMODULE_QUERY,
+                                             ISC_LOG_DEBUG(3),
+                                             "%s approved", msg);
                        }
+               } else {
+                       ns_client_aclmsg("query", name, qtype,
+                                        client->view->rdclass,
+                                        msg, sizeof(msg));
+                       ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+                                     NS_LOGMODULE_QUERY, ISC_LOG_INFO,
+                                     "%s denied", msg);
                }
+       }
 
-               if (queryacl == client->view->queryacl) {
-                       if (result == ISC_R_SUCCESS) {
-                               /*
-                                * We were allowed by the default
-                                * "allow-query" ACL.  Remember this so we
-                                * don't have to check again.
-                                */
-                               client->query.attributes |=
-                                       NS_QUERYATTR_QUERYOK;
-                       }
+       if (queryacl == client->view->queryacl) {
+               if (result == ISC_R_SUCCESS) {
                        /*
-                        * We've now evaluated the view's query ACL, and
-                        * the NS_QUERYATTR_QUERYOK attribute is now valid.
+                        * We were allowed by the default
+                        * "allow-query" ACL.  Remember this so we
+                        * don't have to check again.
                         */
-                       client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
+                       client->query.attributes |= NS_QUERYATTR_QUERYOK;
                }
-
-               if (result != ISC_R_SUCCESS)
-                       goto refuse;
+               /*
+                * We've now evaluated the view's query ACL, and
+                * the NS_QUERYATTR_QUERYOK attribute is now valid.
+                */
+               client->query.attributes |= NS_QUERYATTR_QUERYOKVALID;
        }
 
-       /* Approved. */
-
-       /*
-        * Remember the result of the ACL check so we
-        * don't have to check again.
-        */
-       dbversion->queryok = ISC_TRUE;
+       dbversion->acl_checked = ISC_TRUE;
+       if (result != ISC_R_SUCCESS) {
+               dbversion->queryok = ISC_FALSE;
+               return (DNS_R_REFUSED);
+       }
 
+ approved:
        /* Transfer ownership, if necessary. */
        if (versionp != NULL)
                *versionp = dbversion->version;
-
        return (ISC_R_SUCCESS);
-
- refuse:
-       return (DNS_R_REFUSED);
-
- fail:
-       return (result);
 }
 
 static inline isc_result_t
@@ -844,6 +829,97 @@ query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
        return (result);
 }
 
+static void
+rpz_log(ns_client_t *client) {
+       char namebuf1[DNS_NAME_FORMATSIZE];
+       char namebuf2[DNS_NAME_FORMATSIZE];
+       dns_rpz_st_t *st;
+       const char *pat;
+
+       if (!ns_g_server->log_queries ||
+           !isc_log_wouldlog(ns_g_lctx, DNS_RPZ_INFO_LEVEL))
+               return;
+
+       st = client->query.rpz_st;
+       dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1));
+       dns_name_format(st->qname, namebuf2, sizeof(namebuf2));
+
+       switch (st->m.policy) {
+       case DNS_RPZ_POLICY_NO_OP:
+               pat ="response policy %s rewrite %s NO-OP using %s";
+               break;
+       case DNS_RPZ_POLICY_NXDOMAIN:
+               pat = "response policy %s rewrite %s to NXDOMAIN using %s";
+               break;
+       case DNS_RPZ_POLICY_NODATA:
+               pat = "response policy %s rewrite %s to NODATA using %s";
+               break;
+       case DNS_RPZ_POLICY_RECORD:
+       case DNS_RPZ_POLICY_CNAME:
+               pat = "response policy %s rewrite %s using %s";
+               break;
+       default:
+               INSIST(0);
+       }
+       ns_client_log(client, NS_LOGCATEGORY_QUERIES, NS_LOGMODULE_QUERY,
+                     DNS_RPZ_INFO_LEVEL, pat, dns_rpz_type2str(st->m.type),
+                     namebuf1, namebuf2);
+}
+
+static void
+rpz_fail_log(ns_client_t *client, int level, dns_rpz_type_t rpz_type,
+            dns_name_t *name, const char *str, isc_result_t result)
+{
+       char namebuf1[DNS_NAME_FORMATSIZE];
+       char namebuf2[DNS_NAME_FORMATSIZE];
+
+       if (!ns_g_server->log_queries || !isc_log_wouldlog(ns_g_lctx, level))
+               return;
+
+       dns_name_format(client->query.qname, namebuf1, sizeof(namebuf1));
+       dns_name_format(name, namebuf2, sizeof(namebuf2));
+       ns_client_log(client, NS_LOGCATEGORY_QUERY_EERRORS,
+                     NS_LOGMODULE_QUERY, level,
+                     "response policy %s rewrite %s via %s %sfailed: %s",
+                     dns_rpz_type2str(rpz_type),
+                     namebuf1, namebuf2, str, isc_result_totext(result));
+}
+
+/*
+ * Get a policy rewrite zone database.
+ */
+static isc_result_t
+rpz_getdb(ns_client_t *client, dns_rpz_type_t rpz_type,
+         dns_name_t *rpz_qname, dns_zone_t **zonep,
+         dns_db_t **dbp, dns_dbversion_t **versionp)
+{
+       char namebuf1[DNS_NAME_FORMATSIZE];
+       char namebuf2[DNS_NAME_FORMATSIZE];
+       dns_dbversion_t *rpz_version = NULL;
+       isc_result_t result;
+
+       result = query_getzonedb(client, rpz_qname, dns_rdatatype_any,
+                                DNS_GETDB_IGNOREACL, zonep, dbp, &rpz_version);
+       if (result == ISC_R_SUCCESS) {
+               if (ns_g_server->log_queries &&
+                   isc_log_wouldlog(ns_g_lctx, DNS_RPZ_DEBUG_LEVEL2)) {
+                       dns_name_format(client->query.qname, namebuf1,
+                                       sizeof(namebuf1));
+                       dns_name_format(rpz_qname, namebuf2, sizeof(namebuf2));
+                       ns_client_log(client, NS_LOGCATEGORY_QUERIES,
+                                     NS_LOGMODULE_QUERY, DNS_RPZ_DEBUG_LEVEL2,
+                                     "try rpz %s rewrite %s via %s",
+                                     dns_rpz_type2str(rpz_type),
+                                     namebuf1, namebuf2);
+               }
+               *versionp = rpz_version;
+               return (ISC_R_SUCCESS);
+       }
+       rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, rpz_type, rpz_qname,
+                    "query_getzonedb() ", result);
+       return (result);
+}
+
 static inline isc_result_t
 query_getcachedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
                 dns_db_t **dbp, unsigned int options)
@@ -2608,67 +2684,79 @@ query_addns(ns_client_t *client, dns_db_t *db, dns_dbversion_t *version) {
        return (eresult);
 }
 
-static inline isc_result_t
-query_addcnamelike(ns_client_t *client, dns_name_t *qname, dns_name_t *tname,
-                  dns_rdataset_t *dname, dns_name_t **anamep,
-                  dns_rdatatype_t type)
+static isc_result_t
+query_add_cname(ns_client_t *client, dns_name_t *qname, dns_name_t *tname,
+               dns_trust_t trust, dns_ttl_t ttl)
 {
        dns_rdataset_t *rdataset;
        dns_rdatalist_t *rdatalist;
        dns_rdata_t *rdata;
-       isc_result_t result;
        isc_region_t r;
+       dns_name_t *aname;
+       isc_result_t result;
 
        /*
         * We assume the name data referred to by tname won't go away.
         */
 
-       REQUIRE(anamep != NULL);
+       aname = NULL;
+       result = dns_message_gettempname(client->message, &aname);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       result = dns_name_dup(qname, client->mctx, aname);
+       if (result != ISC_R_SUCCESS) {
+               dns_message_puttempname(client->message, &aname);
+               return (result);
+       }
 
        rdatalist = NULL;
        result = dns_message_gettemprdatalist(client->message, &rdatalist);
-       if (result != ISC_R_SUCCESS)
+       if (result != ISC_R_SUCCESS) {
+               dns_message_puttempname(client->message, &aname);
                return (result);
+       }
        rdata = NULL;
        result = dns_message_gettemprdata(client->message, &rdata);
-       if (result != ISC_R_SUCCESS)
+       if (result != ISC_R_SUCCESS) {
+               dns_message_puttempname(client->message, &aname);
+               dns_message_puttemprdatalist(client->message, &rdatalist);
                return (result);
+       }
        rdataset = NULL;
        result = dns_message_gettemprdataset(client->message, &rdataset);
-       if (result != ISC_R_SUCCESS)
-               return (result);
-       dns_rdataset_init(rdataset);
-       result = dns_name_dup(qname, client->mctx, *anamep);
        if (result != ISC_R_SUCCESS) {
-               dns_message_puttemprdataset(client->message, &rdataset);
+               dns_message_puttempname(client->message, &aname);
+               dns_message_puttemprdatalist(client->message, &rdatalist);
+               dns_message_puttemprdata(client->message, &rdata);
                return (result);
        }
-
-       rdatalist->type = type;
+       dns_rdataset_init(rdataset);
+       rdatalist->type = dns_rdatatype_cname;
        rdatalist->covers = 0;
        rdatalist->rdclass = client->message->rdclass;
-       rdatalist->ttl = dname->ttl;
+       rdatalist->ttl = ttl;
 
        dns_name_toregion(tname, &r);
        rdata->data = r.base;
        rdata->length = r.length;
        rdata->rdclass = client->message->rdclass;
-       rdata->type = type;
+       rdata->type = dns_rdatatype_cname;
 
        ISC_LIST_INIT(rdatalist->rdata);
        ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
        RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset)
                      == ISC_R_SUCCESS);
-       rdataset->trust = dname->trust;
+       rdataset->trust = trust;
 
-       query_addrrset(client, anamep, &rdataset, NULL, NULL,
+       query_addrrset(client, &aname, &rdataset, NULL, NULL,
                       DNS_SECTION_ANSWER);
-
        if (rdataset != NULL) {
                if (dns_rdataset_isassociated(rdataset))
                        dns_rdataset_disassociate(rdataset);
                dns_message_puttemprdataset(client->message, &rdataset);
        }
+       if (aname != NULL)
+               dns_message_puttempname(client->message, &aname);
 
        return (ISC_R_SUCCESS);
 }
@@ -3558,8 +3646,9 @@ query_resume(isc_task_t *task, isc_event_t *event) {
 }
 
 static isc_result_t
-query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain,
-             dns_rdataset_t *nameservers, isc_boolean_t resuming)
+query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
+             dns_name_t *qdomain, dns_rdataset_t *nameservers,
+             isc_boolean_t resuming)
 {
        isc_result_t result;
        dns_rdataset_t *rdataset, *sigrdataset;
@@ -3659,8 +3748,7 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain,
        else
                peeraddr = NULL;
        result = dns_resolver_createfetch2(client->view->resolver,
-                                          client->query.qname,
-                                          qtype, qdomain, nameservers,
+                                          qname, qtype, qdomain, nameservers,
                                           NULL, peeraddr, client->message->id,
                                           client->query.fetchoptions,
                                           client->task,
@@ -3683,6 +3771,685 @@ query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qdomain,
        return (result);
 }
 
+static inline void
+rpz_clean(dns_zone_t **zonep, dns_db_t **dbp, dns_dbnode_t **nodep,
+         dns_rdataset_t **rdatasetp)
+{
+       if (nodep != NULL && *nodep != NULL) {
+               REQUIRE(dbp != NULL && *dbp != NULL);
+               dns_db_detachnode(*dbp, nodep);
+       }
+       if (dbp != NULL && *dbp != NULL)
+               dns_db_detach(dbp);
+       if (zonep != NULL && *zonep != NULL)
+               dns_zone_detach(zonep);
+       if (rdatasetp != NULL && *rdatasetp != NULL &&
+           dns_rdataset_isassociated(*rdatasetp))
+               dns_rdataset_disassociate(*rdatasetp);
+}
+
+static inline isc_result_t
+rpz_ready(ns_client_t *client, dns_zone_t **zonep, dns_db_t **dbp,
+         dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp)
+{
+       REQUIRE(rdatasetp != NULL);
+
+       rpz_clean(zonep, dbp, nodep, rdatasetp);
+       if (*rdatasetp == NULL) {
+               *rdatasetp = query_newrdataset(client);
+               if (*rdatasetp == NULL)
+                       return (DNS_R_SERVFAIL);
+       }
+       return (ISC_R_SUCCESS);
+}
+
+static void
+rpz_st_clear(ns_client_t *client) {
+       dns_rpz_st_t *st = client->query.rpz_st;
+
+       rpz_clean(&st->m.zone, &st->m.db, &st->m.node, NULL);
+       if (st->m.rdataset != NULL)
+               query_putrdataset(client, &st->m.rdataset);
+
+       rpz_clean(NULL, &st->ns.db, NULL, NULL);
+       if (st->ns.ns_rdataset != NULL)
+               query_putrdataset(client, &st->ns.ns_rdataset);
+       if (st->ns.r_rdataset != NULL)
+               query_putrdataset(client, &st->ns.r_rdataset);
+
+       rpz_clean(&st->q.zone, &st->q.db, &st->q.node, NULL);
+       if (st->q.rdataset != NULL)
+               query_putrdataset(client, &st->q.rdataset);
+       if (st->q.sigrdataset != NULL)
+               query_putrdataset(client, &st->q.sigrdataset);
+       st->state = 0;
+}
+
+/*
+ * Get NS, A, or AAAA rrset for rpz nsdname or nsip checking.
+ */
+static isc_result_t
+rpz_ns_find(ns_client_t *client, dns_name_t *name, dns_rdatatype_t type,
+           dns_db_t **dbp, dns_dbversion_t *version,
+           dns_rdataset_t **rdatasetp, isc_boolean_t resuming)
+{
+       dns_rpz_st_t *st;
+       isc_boolean_t is_zone;
+       dns_dbnode_t *node;
+       dns_fixedname_t fixed;
+       dns_name_t *found;
+       isc_result_t result;
+
+       st = client->query.rpz_st;
+       if ((st->state & DNS_RPZ_RECURSING) != 0) {
+               INSIST(st->ns.r_type == type);
+               INSIST(dns_name_equal(name, st->r_name));
+               INSIST(*rdatasetp == NULL ||
+                      !dns_rdataset_isassociated(*rdatasetp));
+               st->state &= ~DNS_RPZ_RECURSING;
+               *dbp = st->ns.db;
+               st->ns.db = NULL;
+               if (*rdatasetp != NULL)
+                       query_putrdataset(client, rdatasetp);
+               *rdatasetp = st->ns.r_rdataset;
+               st->ns.r_rdataset = NULL;
+               result = st->ns.r_result;
+               if (result == DNS_R_DELEGATION) {
+                       rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL,
+                                    DNS_RPZ_TYPE_NSIP, name,
+                                    "rpz_ns_find() ", result);
+                       st->m.policy = DNS_RPZ_POLICY_ERROR;
+                       result = DNS_R_SERVFAIL;
+               }
+               return (result);
+       }
+
+       result = rpz_ready(client, NULL, NULL, NULL, rdatasetp);
+       if (result != ISC_R_SUCCESS) {
+               st->m.policy = DNS_RPZ_POLICY_ERROR;
+               return (result);
+       }
+       if (*dbp != NULL) {
+               is_zone = ISC_FALSE;
+       } else {
+               dns_zone_t *zone;
+
+               version = NULL;
+               zone = NULL;
+               result = query_getdb(client, name, type, 0, &zone, dbp,
+                                    &version, &is_zone);
+               if (result != ISC_R_SUCCESS) {
+                       rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL,
+                                    DNS_RPZ_TYPE_NSIP, name, "NS getdb() ",
+                                    result);
+                       st->m.policy = DNS_RPZ_POLICY_ERROR;
+                       if (zone != NULL)
+                               dns_zone_detach(&zone);
+                       return (result);
+               }
+               if (zone != NULL)
+                       dns_zone_detach(&zone);
+       }
+
+       node = NULL;
+       dns_fixedname_init(&fixed);
+       found = dns_fixedname_name(&fixed);
+       result = dns_db_find(*dbp, name, version, type, 0, client->now, &node,
+                            found, *rdatasetp, NULL);
+       if (result == DNS_R_DELEGATION && is_zone && USECACHE(client)) {
+               /*
+                * Try the cache if we're authoritative for an
+                * ancestor but not the domain itself.
+                */
+               rpz_clean(NULL, dbp, &node, rdatasetp);
+               version = NULL;
+               dns_db_attach(client->view->cachedb, dbp);
+               result = dns_db_find(*dbp, name, version, dns_rdatatype_ns,
+                                    0, client->now, &node, found,
+                                    *rdatasetp, NULL);
+       }
+       rpz_clean(NULL, dbp, &node, NULL);
+       if (result == DNS_R_DELEGATION) {
+               /*
+                * Recurse to get NS rrset or A or AAAA rrset for an NS name.
+                */
+               rpz_clean(NULL, NULL, NULL, rdatasetp);
+               dns_name_copy(name, st->r_name, NULL);
+               result = query_recurse(client, type, st->r_name, NULL, NULL,
+                                      resuming);
+               if (result == ISC_R_SUCCESS) {
+                       st->state |= DNS_RPZ_RECURSING;
+                       result = DNS_R_DELEGATION;
+               }
+       }
+       return (result);
+}
+
+/*
+ * Check the IP address in an A or AAAA rdataset against
+ * the IP or NSIP response policy rules of a view.
+ */
+static isc_result_t
+rpz_rewrite_ip(ns_client_t *client, dns_rdataset_t *rdataset,
+              dns_rpz_type_t rpz_type)
+{
+       dns_rpz_st_t *st;
+       dns_dbversion_t *version;
+       dns_zone_t *zone;
+       dns_db_t *db;
+       dns_rpz_zone_t *new_rpz;
+       isc_result_t result;
+
+       st = client->query.rpz_st;
+       if (st->m.rdataset == NULL) {
+               st->m.rdataset = query_newrdataset(client);
+               if (st->m.rdataset == NULL)
+                       return (DNS_R_SERVFAIL);
+       }
+       zone = NULL;
+       db = NULL;
+       for (new_rpz = ISC_LIST_HEAD(client->view->rpz_zones);
+            new_rpz != NULL;
+            new_rpz = ISC_LIST_NEXT(new_rpz, link)) {
+               version = NULL;
+
+               /*
+                * Find the database for this policy zone to get its
+                * radix tree.
+                */
+               result = rpz_getdb(client, rpz_type, &new_rpz->origin,
+                                  &zone, &db, &version);
+               if (result != ISC_R_SUCCESS) {
+                       rpz_clean(&zone, &db, NULL, NULL);
+                       continue;
+               }
+               /*
+                * Look for a better (e.g. longer prefix) hit for an IP address
+                * in this rdataset in this radix tree than than the previous
+                * hit, if any.  Note the domain name and quality of the
+                * best hit.
+                */
+               result = dns_db_rpz_findips(new_rpz, rpz_type, zone, db,
+                                           version, rdataset, st);
+               RUNTIME_CHECK(result == ISC_R_SUCCESS);
+               rpz_clean(&zone, &db, NULL, NULL);
+       }
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rpz_rewrite_nsip(ns_client_t *client, dns_rdatatype_t type, dns_name_t *name,
+                dns_db_t **dbp, dns_dbversion_t *version,
+                dns_rdataset_t **rdatasetp, isc_boolean_t resuming)
+{
+       isc_result_t result;
+
+       result = rpz_ns_find(client, name, type, dbp, version, rdatasetp,
+                            resuming);
+       switch (result) {
+       case ISC_R_SUCCESS:
+               result = rpz_rewrite_ip(client, *rdatasetp, DNS_RPZ_TYPE_NSIP);
+               break;
+       case DNS_R_EMPTYNAME:
+       case DNS_R_EMPTYWILD:
+       case DNS_R_NXDOMAIN:
+       case DNS_R_NCACHENXDOMAIN:
+       case DNS_R_NXRRSET:
+       case DNS_R_NCACHENXRRSET:
+               result = ISC_R_SUCCESS;
+               break;
+       case DNS_R_DELEGATION:
+       case DNS_R_DUPLICATE:
+       case DNS_R_DROP:
+               break;
+       default:
+               if (client->query.rpz_st->m.policy != DNS_RPZ_POLICY_ERROR) {
+                       client->query.rpz_st->m.policy = DNS_RPZ_POLICY_ERROR;
+                       rpz_fail_log(client, ISC_LOG_WARNING, DNS_RPZ_TYPE_NSIP,
+                                    name, "NS address rewrite nsip ", result);
+               }
+               break;
+       }
+       return (result);
+}
+
+/*
+ * Get the rrset from a response policy zone.
+ */
+static isc_result_t
+rpz_find(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qnamef,
+        dns_name_t *sname, dns_rpz_type_t rpz_type, dns_zone_t **zonep,
+        dns_db_t **dbp, dns_dbnode_t **nodep, dns_rdataset_t **rdatasetp,
+        dns_rpz_policy_t *policyp)
+{
+       dns_dbversion_t *version;
+       dns_rpz_policy_t policy;
+       dns_fixedname_t fixed;
+       dns_name_t *found;
+       isc_result_t result;
+
+       result = rpz_ready(client, zonep, dbp, nodep, rdatasetp);
+       if (result != ISC_R_SUCCESS) {
+               *policyp = DNS_RPZ_POLICY_ERROR;
+               return (result);
+       }
+
+       /*
+        * Try to get either a CNAME or the type of record demanded by the
+        * request from the policy zone.
+        */
+       version = NULL;
+       result = rpz_getdb(client, rpz_type, qnamef, zonep, dbp, &version);
+       if (result != ISC_R_SUCCESS) {
+               *policyp = DNS_RPZ_POLICY_ERROR;
+               return (DNS_R_SERVFAIL);
+       }
+
+       dns_fixedname_init(&fixed);
+       found = dns_fixedname_name(&fixed);
+       result = dns_db_find(*dbp, qnamef, version, dns_rdatatype_any, 0,
+                            client->now, nodep, found, *rdatasetp, NULL);
+       if (result == ISC_R_SUCCESS) {
+               dns_rdatasetiter_t *rdsiter;
+
+               rdsiter = NULL;
+               result = dns_db_allrdatasets(*dbp, *nodep, version, 0,
+                                            &rdsiter);
+               if (result != ISC_R_SUCCESS) {
+                       dns_db_detachnode(*dbp, nodep);
+                       rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, rpz_type,
+                                    qnamef, "allrdatasets()", result);
+                       *policyp = DNS_RPZ_POLICY_ERROR;
+                       return (DNS_R_SERVFAIL);
+               }
+               for (result = dns_rdatasetiter_first(rdsiter);
+                    result == ISC_R_SUCCESS;
+                    result = dns_rdatasetiter_next(rdsiter)) {
+                       dns_rdatasetiter_current(rdsiter, *rdatasetp);
+                       if ((*rdatasetp)->type == dns_rdatatype_cname ||
+                           (*rdatasetp)->type == qtype)
+                               break;
+                       dns_rdataset_disassociate(*rdatasetp);
+               }
+               dns_rdatasetiter_destroy(&rdsiter);
+               if (result != ISC_R_SUCCESS) {
+                       if (result != ISC_R_NOMORE) {
+                               rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL,
+                                            rpz_type, qnamef, "rdatasetiter",
+                                            result);
+                               *policyp = DNS_RPZ_POLICY_ERROR;
+                               return (DNS_R_SERVFAIL);
+                       }
+                       /*
+                        * Ask again to get the right DNS_R_DNAME/NXRRSET/...
+                        * result if there is neither a CNAME nor target type.
+                        */
+                       if (dns_rdataset_isassociated(*rdatasetp))
+                               dns_rdataset_disassociate(*rdatasetp);
+                       dns_db_detachnode(*dbp, nodep);
+                       result = dns_db_find(*dbp, qnamef, version, qtype, 0,
+                                            client->now, nodep, found,
+                                            *rdatasetp, NULL);
+               }
+       }
+       switch (result) {
+       case ISC_R_SUCCESS:
+               if ((*rdatasetp)->type != dns_rdatatype_cname) {
+                       policy = DNS_RPZ_POLICY_RECORD;
+               } else {
+                       policy = dns_rpz_decode_cname(*rdatasetp, sname);
+                       if (policy == DNS_RPZ_POLICY_RECORD &&
+                           qtype != dns_rdatatype_cname &&
+                           qtype != dns_rdatatype_any)
+                               result = DNS_R_CNAME;
+               }
+               break;
+       case DNS_R_DNAME:
+               policy = DNS_RPZ_POLICY_RECORD;
+               break;
+       case DNS_R_NXRRSET:
+               policy = DNS_RPZ_POLICY_NODATA;
+               break;
+       case DNS_R_NXDOMAIN:
+       case DNS_R_EMPTYNAME:
+               /*
+                * If we don't get a qname hit,
+                * see if it is worth looking for other types.
+                */
+               dns_db_rpz_enabled(*dbp, client->query.rpz_st);
+               dns_db_detach(dbp);
+               dns_zone_detach(zonep);
+               policy = DNS_RPZ_POLICY_MISS;
+               break;
+       default:
+               dns_db_detach(dbp);
+               dns_zone_detach(zonep);
+               rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL, rpz_type, qnamef,
+                            "", result);
+               policy = DNS_RPZ_POLICY_ERROR;
+               result = DNS_R_SERVFAIL;
+               break;
+       }
+
+       *policyp = policy;
+       return (result);
+}
+
+/*
+ * Build and look for a QNAME or NSDNAME owner name in a response policy zone.
+ */
+static isc_result_t
+rpz_rewrite_name(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
+                dns_rpz_type_t rpz_type, dns_rdataset_t **rdatasetp)
+{
+       dns_rpz_st_t *st;
+       dns_rpz_zone_t *rpz;
+       dns_fixedname_t prefixf, rpz_qnamef;
+       dns_name_t *prefix, *suffix, *rpz_qname;
+       dns_zone_t *zone;
+       dns_db_t *db;
+       dns_dbnode_t *node;
+       dns_rpz_policy_t policy;
+       unsigned int labels;
+       isc_result_t result;
+
+       st = client->query.rpz_st;
+       zone = NULL;
+       db = NULL;
+       node = NULL;
+
+       for (rpz = ISC_LIST_HEAD(client->view->rpz_zones);
+            rpz != NULL;
+            rpz = ISC_LIST_NEXT(rpz, link)) {
+               /*
+                * Construct the rule's owner name.
+                */
+               dns_fixedname_init(&prefixf);
+               prefix = dns_fixedname_name(&prefixf);
+               dns_name_split(qname, 1, prefix, NULL);
+               if (rpz_type == DNS_RPZ_TYPE_NSDNAME)
+                       suffix = &rpz->nsdname;
+               else
+                       suffix = &rpz->origin;
+               dns_fixedname_init(&rpz_qnamef);
+               rpz_qname = dns_fixedname_name(&rpz_qnamef);
+               for (;;) {
+                       result = dns_name_concatenate(prefix, suffix,
+                                                     rpz_qname, NULL);
+                       if (result == ISC_R_SUCCESS)
+                               break;
+                       INSIST(result == DNS_R_NAMETOOLONG);
+                       labels = dns_name_countlabels(prefix);
+                       if (labels < 2) {
+                               rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL,
+                                            rpz_type, suffix,
+                                            "concatentate() ", result);
+                               return (ISC_R_SUCCESS);
+                       }
+                       if (labels+1 == dns_name_countlabels(qname)) {
+                               rpz_fail_log(client, DNS_RPZ_DEBUG_LEVEL1,
+                                            rpz_type, suffix,
+                                            "concatentate() ", result);
+                       }
+                       dns_name_split(prefix, labels - 1, NULL, prefix);
+               }
+
+               /*
+                * See if the qname rule (or RR) exists.
+                */
+               result = rpz_find(client, qtype, rpz_qname, qname, rpz_type,
+                                 &zone, &db, &node, rdatasetp, &policy);
+               switch (result) {
+               case DNS_R_NXDOMAIN:
+               case DNS_R_EMPTYNAME:
+                       break;
+               case DNS_R_SERVFAIL:
+                       rpz_clean(&zone, &db, &node, rdatasetp);
+                       st->m.policy = DNS_RPZ_POLICY_ERROR;
+                       return (DNS_R_SERVFAIL);
+               default:
+                       /*
+                        * when more than one name or address hits a rule,
+                        * prefer the first set of names (qname or NS),
+                        * the first policy zone, and the smallest name
+                        */
+                       if (st->m.type == rpz_type &&
+                           rpz->num > st->m.rpz->num &&
+                           0 <= dns_name_compare(rpz_qname, st->qname))
+                               continue;
+                       rpz_clean(&st->m.zone, &st->m.db, &st->m.node,
+                                 &st->m.rdataset);
+                       st->m.rpz = rpz;
+                       st->m.type = rpz_type;
+                       st->m.prefix = 0;
+                       st->m.policy = policy;
+                       st->m.result = result;
+                       dns_name_copy(rpz_qname, st->qname, NULL);
+                       if (dns_rdataset_isassociated(*rdatasetp)) {
+                               dns_rdataset_t *trdataset;
+
+                               trdataset = st->m.rdataset;
+                               st->m.rdataset = *rdatasetp;
+                               *rdatasetp = trdataset;
+                               st->m.ttl = st->m.rdataset->ttl;
+                       } else {
+                               st->m.ttl = DNS_RPZ_TTL_DEFAULT;
+                       }
+                       st->m.node = node;
+                       node = NULL;
+                       st->m.db = db;
+                       db = NULL;
+                       st->m.zone = zone;
+                       zone = NULL;
+               }
+       }
+
+       rpz_clean(&zone, &db, &node, rdatasetp);
+       return (ISC_R_SUCCESS);
+}
+
+/*
+ * Look for response policy zone NSIP and NSDNAME rewriting.
+ */
+static isc_result_t
+rpz_rewrite(ns_client_t *client, dns_rdatatype_t qtype,
+           isc_boolean_t resuming)
+{
+       dns_rpz_st_t *st;
+       dns_db_t *ipdb;
+       dns_rdataset_t *rdataset;
+       dns_fixedname_t nsnamef;
+       dns_name_t *nsname;
+       dns_dbversion_t *version;
+       isc_result_t result;
+
+       ipdb = NULL;
+       rdataset = NULL;
+
+       st = client->query.rpz_st;
+       if (st == NULL) {
+               st = isc_mem_get(client->mctx, sizeof(*st));
+               if (st == NULL)
+                       return (ISC_R_NOMEMORY);
+               st->state = 0;
+               memset(&st->m, 0, sizeof(st->m));
+               memset(&st->ns, 0, sizeof(st->ns));
+               memset(&st->q, 0, sizeof(st->q));
+               dns_fixedname_init(&st->_qnamef);
+               dns_fixedname_init(&st->_r_namef);
+               dns_fixedname_init(&st->_fnamef);
+               st->qname = dns_fixedname_name(&st->_qnamef);
+               st->r_name = dns_fixedname_name(&st->_r_namef);
+               st->fname = dns_fixedname_name(&st->_fnamef);
+               client->query.rpz_st = st;
+       }
+       if ((st->state & DNS_RPZ_DONE_QNAME) == 0) {
+               st->state = DNS_RPZ_DONE_QNAME;
+               st->m.type = DNS_RPZ_TYPE_BAD;
+               st->m.policy = DNS_RPZ_POLICY_MISS;
+
+               /*
+                * Check rules for the name if this it the first time,
+                * i.e. we've not been recursing.
+                */
+               result = DNS_R_SERVFAIL;
+               st->state &= ~(DNS_RPZ_HAVE_IP | DNS_RPZ_HAVE_NSIPv4 |
+                              DNS_RPZ_HAVE_NSIPv6 | DNS_RPZ_HAD_NSDNAME);
+               result = rpz_rewrite_name(client, qtype, client->query.qname,
+                                         DNS_RPZ_TYPE_QNAME, &rdataset);
+               if (st->m.policy != DNS_RPZ_POLICY_MISS)
+                       goto cleanup;
+               if ((st->state & (DNS_RPZ_HAVE_NSIPv4 | DNS_RPZ_HAVE_NSIPv6 |
+                                 DNS_RPZ_HAD_NSDNAME)) == 0)
+                       goto cleanup;
+               st->ns.label = dns_name_countlabels(client->query.qname);
+       }
+
+       dns_fixedname_init(&nsnamef);
+       dns_name_clone(client->query.qname, dns_fixedname_name(&nsnamef));
+       while (st->ns.label > 1 && st->m.policy == DNS_RPZ_POLICY_MISS) {
+               if (st->ns.label == dns_name_countlabels(client->query.qname)) {
+                       nsname = client->query.qname;
+               } else {
+                       nsname = dns_fixedname_name(&nsnamef);
+                       dns_name_split(client->query.qname, st->ns.label,
+                                      NULL, nsname);
+               }
+               if (st->ns.ns_rdataset == NULL ||
+                   !dns_rdataset_isassociated(st->ns.ns_rdataset)) {
+                       dns_db_t *db = NULL;
+                       result = rpz_ns_find(client, nsname, dns_rdatatype_ns,
+                                            &db, NULL, &st->ns.ns_rdataset,
+                                            resuming);
+                       if (db != NULL)
+                               dns_db_detach(&db);
+                       if (result != ISC_R_SUCCESS) {
+                               if (result == DNS_R_DELEGATION)
+                                       goto cleanup;
+                               if (result == DNS_R_EMPTYNAME ||
+                                   result == DNS_R_NXRRSET ||
+                                   result == DNS_R_EMPTYWILD ||
+                                   result == DNS_R_NXDOMAIN ||
+                                   result == DNS_R_NCACHENXDOMAIN ||
+                                   result == DNS_R_NCACHENXRRSET ||
+                                   result == DNS_R_CNAME ||
+                                   result == DNS_R_DNAME) {
+                                       rpz_fail_log(client,
+                                                    DNS_RPZ_DEBUG_LEVEL2,
+                                                    DNS_RPZ_TYPE_NSIP, nsname,
+                                                    "NS db_find() ", result);
+                                       dns_rdataset_disassociate(st->ns.
+                                                       ns_rdataset);
+                                       st->ns.label--;
+                                       continue;
+                               }
+                               if (st->m.policy != DNS_RPZ_POLICY_ERROR) {
+                                       rpz_fail_log(client, DNS_RPZ_INFO_LEVEL,
+                                                    DNS_RPZ_TYPE_NSIP, nsname,
+                                                    "NS db_find() ", result);
+                                       st->m.policy = DNS_RPZ_POLICY_ERROR;
+                               }
+                               goto cleanup;
+                       }
+                       result = dns_rdataset_first(st->ns.ns_rdataset);
+                       if (result != ISC_R_SUCCESS)
+                               goto cleanup;
+               }
+               /*
+                * Check all NS names.
+                */
+               do {
+                       dns_rdata_ns_t ns;
+                       dns_rdata_t nsrdata = DNS_RDATA_INIT;
+
+                       dns_rdataset_current(st->ns.ns_rdataset, &nsrdata);
+                       result = dns_rdata_tostruct(&nsrdata, &ns, NULL);
+                       dns_rdata_reset(&nsrdata);
+                       if (result != ISC_R_SUCCESS) {
+                               rpz_fail_log(client, DNS_RPZ_ERROR_LEVEL,
+                                            DNS_RPZ_TYPE_NSIP, nsname,
+                                            "rdata_tostruct() ", result);
+                               st->m.policy = DNS_RPZ_POLICY_ERROR;
+                               goto cleanup;
+                       }
+                       if ((st->state & DNS_RPZ_HAD_NSDNAME) != 0) {
+                               result = rpz_rewrite_name(client, qtype,
+                                                       &ns.name,
+                                                       DNS_RPZ_TYPE_NSDNAME,
+                                                       &rdataset);
+                               if (result != ISC_R_SUCCESS) {
+                                       dns_rdata_freestruct(&ns);
+                                       goto cleanup;
+                               }
+                       }
+                       /*
+                        * Check all IP addresses for this NS name, but don't
+                        * bother without NSIP rules or with a NSDNAME hit.
+                        */
+                       version = NULL;
+                       if ((st->state & DNS_RPZ_HAVE_NSIPv4) != 0 &&
+                           st->m.type != DNS_RPZ_TYPE_NSDNAME &&
+                           (st->state & DNS_RPZ_DONE_A) == 0) {
+                               result = rpz_rewrite_nsip(client,
+                                                         dns_rdatatype_a,
+                                                         &ns.name, &ipdb,
+                                                         version, &rdataset,
+                                                         resuming);
+                               if (result == ISC_R_SUCCESS)
+                                       st->state |= DNS_RPZ_DONE_A;
+                       }
+                       if (result == ISC_R_SUCCESS &&
+                           (st->state & DNS_RPZ_HAVE_NSIPv6) != 0 &&
+                           st->m.type != DNS_RPZ_TYPE_NSDNAME) {
+                               result = rpz_rewrite_nsip(client,
+                                                       dns_rdatatype_aaaa,
+                                                       &ns.name, &ipdb, version,
+                                                       &rdataset, resuming);
+                       }
+                       dns_rdata_freestruct(&ns);
+                       if (ipdb != NULL)
+                               dns_db_detach(&ipdb);
+                       if (result != ISC_R_SUCCESS)
+                               goto cleanup;
+                       st->state &= ~DNS_RPZ_DONE_A;
+                       result = dns_rdataset_next(st->ns.ns_rdataset);
+               } while (result == ISC_R_SUCCESS);
+               dns_rdataset_disassociate(st->ns.ns_rdataset);
+               st->ns.label--;
+       }
+
+       /*
+        * Use the best, if any, hit.
+        */
+       result = ISC_R_SUCCESS;
+
+cleanup:
+       if (st->m.policy != DNS_RPZ_POLICY_MISS &&
+           st->m.policy != DNS_RPZ_POLICY_NO_OP &&
+           st->m.policy != DNS_RPZ_POLICY_ERROR &&
+           st->m.rpz->policy != DNS_RPZ_POLICY_GIVEN)
+               st->m.policy = st->m.rpz->policy;
+       if (st->m.policy == DNS_RPZ_POLICY_NO_OP)
+               rpz_log(client);
+       if (st->m.policy == DNS_RPZ_POLICY_MISS ||
+           st->m.policy == DNS_RPZ_POLICY_NO_OP ||
+           st->m.policy == DNS_RPZ_POLICY_ERROR)
+               rpz_clean(&st->m.zone, &st->m.db, &st->m.node, &st->m.rdataset);
+       if (st->m.policy != DNS_RPZ_POLICY_MISS)
+               st->state |= DNS_RPZ_REWRITTEN;
+       if (st->m.policy == DNS_RPZ_POLICY_ERROR) {
+               st->m.type = DNS_RPZ_TYPE_BAD;
+               result = DNS_R_SERVFAIL;
+       }
+       if (rdataset != NULL)
+               query_putrdataset(client, &rdataset);
+       if ((st->state & DNS_RPZ_RECURSING) == 0) {
+               rpz_clean(NULL, &st->ns.db, NULL, &st->ns.ns_rdataset);
+       }
+
+       return (result);
+}
+
 #define MAX_RESTARTS 16
 
 #define QUERY_ERROR(r) \
@@ -4161,6 +4928,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
        unsigned int options;
        isc_boolean_t empty_wild;
        dns_rdataset_t *noqname;
+       dns_rpz_st_t *rpz_st;
        isc_boolean_t resuming;
        int line = -1;
        isc_boolean_t dns64_exclude, dns64;
@@ -4201,17 +4969,47 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                 * and resume.
                 */
                want_restart = ISC_FALSE;
-               authoritative = ISC_FALSE;
 
-               qtype = event->qtype;
+                rpz_st = client->query.rpz_st;
+                if (rpz_st != NULL &&
+                   (rpz_st->state & DNS_RPZ_RECURSING) != 0) {
+                        is_zone = rpz_st->q.is_zone;
+                        authoritative = rpz_st->q.authoritative;
+                        zone = rpz_st->q.zone;
+                        rpz_st->q.zone = NULL;
+                        node = rpz_st->q.node;
+                        rpz_st->q.node = NULL;
+                        db = rpz_st->q.db;
+                        rpz_st->q.db = NULL;
+                        rdataset = rpz_st->q.rdataset;
+                        rpz_st->q.rdataset = NULL;
+                        sigrdataset = rpz_st->q.sigrdataset;
+                        rpz_st->q.sigrdataset = NULL;
+                        qtype = rpz_st->q.qtype;
+                        if (event->node != NULL)
+                                dns_db_detachnode(db, &event->node);
+                        rpz_st->ns.db = event->db;
+                        rpz_st->ns.r_type = event->qtype;
+                        rpz_st->ns.r_rdataset = event->rdataset;
+                        if (event->sigrdataset != NULL &&
+                            dns_rdataset_isassociated(event->sigrdataset))
+                                dns_rdataset_disassociate(event->sigrdataset);
+                } else {
+                        authoritative = ISC_FALSE;
+                        qtype = event->qtype;
+                        db = event->db;
+                        node = event->node;
+                        rdataset = event->rdataset;
+                        sigrdataset = event->sigrdataset;
+                }
+
                if (qtype == dns_rdatatype_rrsig || qtype == dns_rdatatype_sig)
                        type = dns_rdatatype_any;
                else
                        type = qtype;
-               db = event->db;
-               node = event->node;
-               rdataset = event->rdataset;
-               sigrdataset = event->sigrdataset;
+
                if (DNS64(client)) {
                        client->query.attributes &= ~NS_QUERYATTR_DNS64;
                        dns64 = ISC_TRUE;
@@ -4234,14 +5032,25 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                        QUERY_ERROR(DNS_R_SERVFAIL);
                        goto cleanup;
                }
-               tname = dns_fixedname_name(&event->foundname);
+               if (rpz_st != NULL &&
+                   (rpz_st->state & DNS_RPZ_RECURSING) != 0) {
+                       tname = rpz_st->fname;
+               } else {
+                       tname = dns_fixedname_name(&event->foundname);
+               }
                result = dns_name_copy(tname, fname, NULL);
                if (result != ISC_R_SUCCESS) {
                        QUERY_ERROR(DNS_R_SERVFAIL);
                        goto cleanup;
                }
-
-               result = event->result;
+               if (rpz_st != NULL && 
+                   (rpz_st->state & DNS_RPZ_RECURSING) != 0) {
+                       rpz_st->ns.r_result = event->result;
+                       result = rpz_st->q.result;
+                       isc_event_free(ISC_EVENT_PTR(&event));
+               } else {
+                       result = event->result;
+               }
                resuming = ISC_TRUE;
                goto resume;
        }
@@ -4399,6 +5208,118 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
 
  resume:
        CTRACE("query_find: resume");
+
+       if (!ISC_LIST_EMPTY(client->view->rpz_zones) &&
+           RECURSIONOK(client) && !RECURSING(client) &&
+           result != DNS_R_DELEGATION && result != ISC_R_NOTFOUND &&
+           (client->query.rpz_st == NULL ||
+            (client->query.rpz_st->state & DNS_RPZ_REWRITTEN) == 0) &&
+           !dns_name_equal(client->query.qname, dns_rootname)) {
+               isc_result_t rresult;
+
+               rresult = rpz_rewrite(client, qtype, resuming);
+               rpz_st = client->query.rpz_st;
+               switch (rresult) {
+               case ISC_R_SUCCESS:
+                       break;
+               case DNS_R_DELEGATION:
+                       /*
+                        * recursing for NS names or addresses,
+                        * so save the main query state
+                        */
+                       rpz_st->q.qtype = qtype;
+                       rpz_st->q.is_zone = is_zone;
+                       rpz_st->q.authoritative = authoritative;
+                       rpz_st->q.zone = zone;
+                       zone = NULL;
+                       rpz_st->q.db = db;
+                       db = NULL;
+                       rpz_st->q.node = node;
+                       node = NULL;
+                       rpz_st->q.rdataset = rdataset;
+                       rdataset = NULL;
+                       rpz_st->q.sigrdataset = sigrdataset;
+                       sigrdataset = NULL;
+                       dns_name_copy(fname, rpz_st->fname, NULL);
+                       rpz_st->q.result = result;
+                       client->query.attributes |= NS_QUERYATTR_RECURSING;
+                       result = ISC_R_SUCCESS;
+                       goto cleanup;
+               default:
+                       RECURSE_ERROR(rresult);
+                       goto cleanup;
+               }
+               if (rpz_st->m.policy != DNS_RPZ_POLICY_MISS &&
+                   rpz_st->m.policy != DNS_RPZ_POLICY_NO_OP) {
+                       result = dns_name_copy(client->query.qname, fname,
+                                              NULL);
+                       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+ finish_rewrite:
+                       rpz_clean(&zone, &db, &node, NULL);
+                       if (rpz_st->m.rdataset != NULL) {
+                               if (rdataset != NULL)
+                                       query_putrdataset(client, &rdataset);
+                               rdataset = rpz_st->m.rdataset;
+                               rpz_st->m.rdataset = NULL;
+                       } else if (rdataset != NULL &&
+                                  dns_rdataset_isassociated(rdataset)) {
+                               dns_rdataset_disassociate(rdataset);
+                       }
+                       node = rpz_st->m.node;
+                       rpz_st->m.node = NULL;
+                       db = rpz_st->m.db;
+                       rpz_st->m.db = NULL;
+                       zone = rpz_st->m.zone;
+                       rpz_st->m.zone = NULL;
+
+                       result = rpz_st->m.result;
+                       switch (rpz_st->m.policy) {
+                       case DNS_RPZ_POLICY_NXDOMAIN:
+                               result = DNS_R_NXDOMAIN;
+                               break;
+                       case DNS_RPZ_POLICY_NODATA:
+                               result = DNS_R_NXRRSET;
+                               break;
+                       case DNS_RPZ_POLICY_RECORD:
+                               if (type == dns_rdatatype_any &&
+                                   dns_rdataset_isassociated(rdataset))
+                                       dns_rdataset_disassociate(rdataset);
+                               break;
+                       case DNS_RPZ_POLICY_CNAME:
+                               result = dns_name_copy(&rpz_st->m.rpz->cname,
+                                                      fname, NULL);
+                               RUNTIME_CHECK(result == ISC_R_SUCCESS);
+                               query_keepname(client, fname, dbuf);
+                               result = query_add_cname(client,
+                                                       client->query.qname,
+                                                       fname,
+                                                       dns_trust_authanswer,
+                                                       rpz_st->m.ttl);
+                               if (result != ISC_R_SUCCESS)
+                                       goto cleanup;
+                               ns_client_qnamereplace(client, fname);
+                               fname = NULL;
+                               client->attributes &= ~NS_CLIENTATTR_WANTDNSSEC;
+                               rpz_log(client);
+                               want_restart = ISC_TRUE;
+                               goto cleanup;
+                       default:
+                               INSIST(0);
+                       }
+
+                       /*
+                        * Turn off DNSSEC because the results of a
+                        * response policy zone cannot verify.
+                        */
+                       client->attributes &= ~NS_CLIENTATTR_WANTDNSSEC;
+                       if (sigrdataset != NULL &&
+                           dns_rdataset_isassociated(sigrdataset))
+                               dns_rdataset_disassociate(sigrdataset);
+                       is_zone = ISC_TRUE;
+                       rpz_log(client);
+               }
+       }
+
        switch (result) {
        case ISC_R_SUCCESS:
                /*
@@ -4451,6 +5372,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                         */
                        if (RECURSIONOK(client)) {
                                result = query_recurse(client, qtype,
+                                                      client->query.qname,
                                                       NULL, NULL, resuming);
                                if (result == ISC_R_SUCCESS) {
                                        client->query.attributes |=
@@ -4642,17 +5564,18 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                 */
                                if (dns_rdatatype_atparent(type))
                                        result = query_recurse(client, qtype,
-                                                              NULL, NULL,
-                                                              resuming);
+                                                        client->query.qname,
+                                                        NULL, NULL, resuming);
                                else if (dns64)
                                        result = query_recurse(client,
-                                                              dns_rdatatype_a,
-                                                              NULL, NULL,
-                                                              resuming);
+                                                        dns_rdatatype_a,
+                                                        client->query.qname,
+                                                        NULL, NULL, resuming);
                                else
                                        result = query_recurse(client, qtype,
-                                                              fname, rdataset,
-                                                              resuming);
+                                                        client->query.qname,
+                                                        fname, rdataset,
+                                                        resuming);
 
                                if (result == ISC_R_SUCCESS) {
                                        client->query.attributes |=
@@ -4795,10 +5718,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                 * instead? If so add the nearest to the
                                 * closest provable encloser.
                                 */
-                               if (found &&
-                                   dns_rdataset_isassociated(rdataset) &&
-                                   !dns_name_equal(qname, found))
-                               {
+                               if (dns_rdataset_isassociated(rdataset) &&
+                                   !dns_name_equal(qname, found)) {
                                        unsigned int count;
                                        unsigned int skip;
 
@@ -5161,11 +6082,11 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                        dns_message_puttempname(client->message, &tname);
                        goto cleanup;
                }
-               dns_name_init(tname, NULL);
                dns_name_clone(&dname.dname, tname);
                dns_rdata_freestruct(&dname);
                /*
-                * Construct the new qname.
+                * Construct the new qname consisting of
+                * <found name prefix>.<dname target>
                 */
                dns_fixedname_init(&fixed);
                prefix = dns_fixedname_name(&fixed);
@@ -5182,8 +6103,8 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                        goto cleanup;
                }
                result = dns_name_concatenate(prefix, tname, fname, NULL);
+               dns_message_puttempname(client->message, &tname);
                if (result != ISC_R_SUCCESS) {
-                       dns_message_puttempname(client->message, &tname);
                        if (result == ISC_R_NOSPACE) {
                                /*
                                 * RFC2672, section 4.1, subsection 3c says
@@ -5196,11 +6117,12 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                }
                query_keepname(client, fname, dbuf);
                /*
-                * Synthesize a CNAME for this DNAME.
+                * Synthesize a CNAME consisting of
+                *   <old qname> <dname ttl> CNAME <new qname>
+                *          with <dname trust value>
                 *
-                * We want to synthesize a CNAME since if we don't
-                * then older software that doesn't understand DNAME
-                * will not chain like it should.
+                * Synthesize a CNAME so old old clients that don't understand
+                * DNAME can chain.
                 *
                 * We do not try to synthesize a signature because we hope
                 * that security aware servers will understand DNAME.  Also,
@@ -5208,12 +6130,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                 * on-the-fly is costly, and not really legitimate anyway
                 * since the synthesized CNAME is NOT in the zone.
                 */
-               dns_name_init(tname, NULL);
-               (void)query_addcnamelike(client, client->query.qname, fname,
-                                        trdataset, &tname,
-                                        dns_rdatatype_cname);
-               if (tname != NULL)
-                       dns_message_puttempname(client->message, &tname);
+               result = query_add_cname(client, client->query.qname, fname,
+                                        trdataset->trust, trdataset->ttl);
+               if (result != ISC_R_SUCCESS)
+                       goto cleanup;
                /*
                 * Switch to the new qname and restart.
                 */
@@ -5273,6 +6193,54 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                        QUERY_ERROR(DNS_R_SERVFAIL);
                        goto cleanup;
                }
+
+               /*
+                * Check all A and AAAA records in all response policy
+                * IP address zones
+                */
+               rpz_st = client->query.rpz_st;
+               if (rpz_st != NULL &&
+                   (rpz_st->state & DNS_RPZ_DONE_QNAME) != 0 &&
+                   (rpz_st->state & DNS_RPZ_REWRITTEN) == 0 &&
+                   RECURSIONOK(client) && !RECURSING(client) &&
+                   (rpz_st->state & DNS_RPZ_HAVE_IP) != 0) {
+                       for (result = dns_rdatasetiter_first(rdsiter);
+                            result == ISC_R_SUCCESS;
+                            result = dns_rdatasetiter_next(rdsiter)) {
+                               dns_rdatasetiter_current(rdsiter, rdataset);
+                               if (rdataset->type == dns_rdatatype_a ||
+                                   rdataset->type == dns_rdatatype_aaaa)
+                                       result = rpz_rewrite_ip(client,
+                                                             rdataset,
+                                                             DNS_RPZ_TYPE_IP);
+                               dns_rdataset_disassociate(rdataset);
+                               if (result != ISC_R_SUCCESS)
+                                       break;
+                       }
+                       if (result != ISC_R_NOMORE) {
+                               dns_rdatasetiter_destroy(&rdsiter);
+                               QUERY_ERROR(DNS_R_SERVFAIL);
+                               goto cleanup;
+                       }
+                       switch (rpz_st->m.policy) {
+                       case DNS_RPZ_POLICY_MISS:
+                               break;
+                       case DNS_RPZ_POLICY_NO_OP:
+                               rpz_log(client);
+                               rpz_st->state |= DNS_RPZ_REWRITTEN;
+                               break;
+                       case DNS_RPZ_POLICY_NXDOMAIN:
+                       case DNS_RPZ_POLICY_NODATA:
+                       case DNS_RPZ_POLICY_RECORD:
+                       case DNS_RPZ_POLICY_CNAME:
+                               dns_rdatasetiter_destroy(&rdsiter);
+                               rpz_st->state |= DNS_RPZ_REWRITTEN;
+                               goto finish_rewrite;
+                       default:
+                               INSIST(0);
+                       }
+               }
+
                /*
                 * Calling query_addrrset() with a non-NULL dbuf is going
                 * to either keep or release the name.  We don't want it to
@@ -5389,10 +6357,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                        dns_rdatasetiter_destroy(&rdsiter);
                                        if (RECURSIONOK(client)) {
                                                result = query_recurse(client,
-                                                                      qtype,
-                                                                      NULL,
-                                                                      NULL,
-                                                                      resuming);
+                                                           qtype,
+                                                           client->query.qname,
+                                                           NULL, NULL,
+                                                           resuming);
                                                if (result == ISC_R_SUCCESS)
                                                    client->query.attributes |=
                                                        NS_QUERYATTR_RECURSING;
@@ -5431,6 +6399,49 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                 * This is the "normal" case -- an ordinary question to which
                 * we know the answer.
                 */
+
+               /*
+                * Check all A and AAAA records in all response policy
+                * IP address zones
+                */
+               rpz_st = client->query.rpz_st;
+               if (rpz_st != NULL &&
+                   (rpz_st->state & DNS_RPZ_DONE_QNAME) != 0 &&
+                   (rpz_st->state & DNS_RPZ_REWRITTEN) == 0 &&
+                   RECURSIONOK(client) && !RECURSING(client) &&
+                   (rpz_st->state & DNS_RPZ_HAVE_IP) != 0 &&
+                   (qtype == dns_rdatatype_aaaa || qtype == dns_rdatatype_a)) {
+                       result = rpz_rewrite_ip(client, rdataset,
+                                               DNS_RPZ_TYPE_IP);
+                       if (result != ISC_R_SUCCESS) {
+                               QUERY_ERROR(DNS_R_SERVFAIL);
+                               goto cleanup;
+                       }
+                       /*
+                        * After a hit in the radix tree for the policy domain,
+                        * either stop trying to rewrite (DNS_RPZ_POLICY_NO_OP)
+                        * or restart to ask the ordinary database of the
+                        * policy zone for the DNS record corresponding to the
+                        * record in the radix tree.
+                        */
+                       switch (rpz_st->m.policy) {
+                       case DNS_RPZ_POLICY_MISS:
+                               break;
+                       case DNS_RPZ_POLICY_NO_OP:
+                               rpz_log(client);
+                               rpz_st->state |= DNS_RPZ_REWRITTEN;
+                               break;
+                       case DNS_RPZ_POLICY_NXDOMAIN:
+                       case DNS_RPZ_POLICY_NODATA:
+                       case DNS_RPZ_POLICY_RECORD:
+                       case DNS_RPZ_POLICY_CNAME:
+                               rpz_st->state |= DNS_RPZ_REWRITTEN;
+                               goto finish_rewrite;
+                       default:
+                               INSIST(0);
+                       }
+               }
+
 #ifdef ALLOW_FILTER_AAAA_ON_V4
                /*
                 * Optionally hide AAAAs from IPv4 clients if there is an A.
@@ -5492,6 +6503,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                                         * if the recursion for the A succeeds.
                                         */
                                        result = query_recurse(client,
+                                                       client->query.qname,
                                                        dns_rdatatype_a,
                                                        NULL, NULL, resuming);
                                        if (result == ISC_R_SUCCESS) {
@@ -5637,6 +6649,10 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
        /*
         * General cleanup.
         */
+       rpz_st = client->query.rpz_st;
+       if (rpz_st != NULL && (rpz_st->state & DNS_RPZ_RECURSING) == 0)
+               rpz_clean(&rpz_st->m.zone, &rpz_st->m.db, &rpz_st->m.node,
+                         &rpz_st->m.rdataset);
        if (rdataset != NULL)
                query_putrdataset(client, &rdataset);
        if (sigrdataset != NULL)
@@ -5697,7 +6713,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
                        INSIST(line >= 0);
                        query_error(client, eresult, line);
                }
-               ns_client_detach(&client);
+               ns_client_detach(&client);
        } else if (!RECURSING(client)) {
                /*
                 * We are done.  Set up sortlist data for the message
index 030dc40e211bf120eab05a0c94ae5011ba526a40..5ac2148119419fb47eecd847dce104a50fe46915 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: server.c,v 1.597 2011/01/11 23:47:12 tbox Exp $ */
+/* $Id: server.c,v 1.598 2011/01/13 01:59:25 marka Exp $ */
 
 /*! \file */
 
@@ -1438,6 +1438,114 @@ cleanup:
        return (result);
 }
 
+static isc_result_t
+configure_rpz(dns_view_t *view, const cfg_listelt_t *element) {
+       const cfg_obj_t *rpz_obj, *policy_obj;
+       const char *str;
+       dns_fixedname_t fixed;
+       dns_name_t *origin;
+       dns_rpz_zone_t *old, *new;
+       dns_zone_t *zone;
+       isc_result_t result;
+       unsigned int l1, l2;
+
+       new = isc_mem_get(view->mctx, sizeof(*new));
+       if (new == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+       memset(new, 0, sizeof(*new));
+       dns_name_init(&new->nsdname, NULL);
+       dns_name_init(&new->origin, NULL);
+       dns_name_init(&new->cname, NULL);
+       ISC_LIST_INITANDAPPEND(view->rpz_zones, new, link);
+
+       rpz_obj = cfg_listelt_value(element);
+       policy_obj = cfg_tuple_get(rpz_obj, "policy");
+       if (cfg_obj_isvoid(policy_obj)) {
+               new->policy = DNS_RPZ_POLICY_GIVEN;
+       } else {
+               str = cfg_obj_asstring(policy_obj);
+               new->policy = dns_rpz_str2policy(str);
+               INSIST(new->policy != DNS_RPZ_POLICY_ERROR);
+       }
+
+       dns_fixedname_init(&fixed);
+       origin = dns_fixedname_name(&fixed);
+       str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "name"));
+       result = dns_name_fromstring(origin, str, DNS_NAME_DOWNCASE, NULL);
+       if (result != ISC_R_SUCCESS) {
+               cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+                           "invalid zone '%s'", str);
+               goto cleanup;
+       }
+
+       result = dns_name_fromstring2(&new->nsdname, DNS_RPZ_NSDNAME_ZONE,
+                                     origin, DNS_NAME_DOWNCASE, view->mctx);
+       if (result != ISC_R_SUCCESS) {
+               cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+                           "invalid zone '%s'", str);
+               goto cleanup;
+       }
+
+       /*
+        * The origin is part of 'nsdname' so we don't need to keep it
+        * seperately.
+        */
+       l1 = dns_name_countlabels(&new->nsdname);
+       l2 = dns_name_countlabels(origin);
+       dns_name_getlabelsequence(&new->nsdname, l1 - l2, l2, &new->origin);
+
+       /*
+        * Are we configured to with the reponse policy zone?
+        */
+       result = dns_view_findzone(view, &new->origin, &zone);
+       if (result != ISC_R_SUCCESS) {
+               cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+                           "unknown zone '%s'", str);
+               goto cleanup;
+       }
+
+       if (dns_zone_gettype(zone) != dns_zone_master &&
+           dns_zone_gettype(zone) != dns_zone_slave) {
+               cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+                            "zone '%s' is neither master nor slave", str);
+               dns_zone_detach(&zone);
+               result = DNS_R_NOTMASTER;
+               goto cleanup;
+       }
+       dns_zone_detach(&zone);
+
+       for (old = ISC_LIST_HEAD(view->rpz_zones);
+            old != new;
+            old = ISC_LIST_NEXT(old, link)) {
+               ++new->num;
+               if (dns_name_equal(&old->origin, &new->origin)) {
+                       cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+                                   "duplicate '%s'", str);
+                       result = DNS_R_DUPLICATE;
+                       goto cleanup;
+               }
+       }
+
+       if (new->policy == DNS_RPZ_POLICY_CNAME) {
+               str = cfg_obj_asstring(cfg_tuple_get(rpz_obj, "cname"));
+               result = dns_name_fromstring(&new->cname, str, 0, view->mctx);
+               if (result != ISC_R_SUCCESS) {
+                       cfg_obj_log(rpz_obj, ns_g_lctx, DNS_RPZ_ERROR_LEVEL,
+                                   "invalid cname '%s'", str);
+                       goto cleanup;
+               }
+       }
+
+       return (ISC_R_SUCCESS);
+
+ cleanup:
+       dns_rpz_view_destroy(view);
+       return (result);
+}
+
 /*
  * Configure 'view' according to 'vconfig', taking defaults from 'config'
  * where values are missing in 'vconfig'.
@@ -2781,6 +2889,29 @@ configure_view(dns_view_t *view, cfg_parser_t* parser,
                }
        }
 
+       /*
+        * Make the list of response policy zone names for views that
+        * are used for real lookups and so care about hints.
+        */
+       zonelist = NULL;
+       if (view->rdclass == dns_rdataclass_in && need_hints) {
+               obj = NULL;
+               result = ns_config_get(maps, "response-policy", &obj);
+               if (result == ISC_R_SUCCESS)
+                       cfg_map_get(obj, "zone", &zonelist);
+       }
+       if (zonelist != NULL) {
+
+               for (element = cfg_list_first(zonelist);
+                    element != NULL;
+                    element = cfg_list_next(element)) {
+                       result = configure_rpz(view, element);
+                       if (result != ISC_R_SUCCESS)
+                               goto cleanup;
+                       dns_rpz_set_need(ISC_TRUE);
+               }
+       }
+
        result = ISC_R_SUCCESS;
 
  cleanup:
index 4001666f09b3afcdb885ee1424f7a84ec3ef50ab..0d171e319ffda332f4f67c06afbc011ce7168b2a 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.33 2010/06/23 23:46:58 tbox Exp $
+# $Id: Makefile.in,v 1.34 2011/01/13 01:59:25 marka Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -21,7 +21,7 @@ top_srcdir =  @top_srcdir@
 
 @BIND9_MAKE_INCLUDES@
 
-SUBDIRS =      filter-aaaa lwresd tkey 
+SUBDIRS =      filter-aaaa lwresd rpz tkey
 TARGETS =
 
 @BIND9_MAKE_RULES@
index 53aebeb0ccbecbeaaff796de2c4a35cef9e5b1c7..8f63e5f740f60e092fd5059b0255827bfeedd875 100644 (file)
@@ -17,6 +17,7 @@ involving a different DNS setup.  They are:
   nsupdate/    Dynamic update and IXFR tests
   resolver/     Regression tests for resolver bugs that have been fixed
                (not a complete resolver test suite)
+  rpz/         Tests of response policy zone (RPZ) rewriting
   stub/                Tests of stub zone functionality
   unknown/     Unknown type and class tests
   upforwd/     Update forwarding tests
@@ -57,4 +58,4 @@ The tests can be run individually like this:
 
 To run all the tests, just type "make test".
 
-$Id: README,v 1.14 2010/08/25 23:46:37 tbox Exp $
+$Id: README,v 1.15 2011/01/13 01:59:25 marka Exp $
index c4a0f28a8d960a9019db1c478f45a9353e729780..d3e80266a0ca9929d286b6d92cd9ea40c1c36789 100644 (file)
@@ -15,7 +15,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: conf.sh.in,v 1.57 2010/12/23 04:07:59 marka Exp $
+# $Id: conf.sh.in,v 1.58 2011/01/13 01:59:26 marka Exp $
 
 #
 # Common configuration data for system tests, to be sourced into
@@ -55,7 +55,7 @@ JOURNALPRINT=$TOP/bin/tools/named-journalprint
 SUBDIRS="acl allow_query addzone autosign cacheclean checkconf checknames
     dlv @DLZ_SYSTEM_TEST@ dlzexternal dns64 dnssec forward glue gost ixfr limits
     lwresd masterfile masterformat metadata notify nsupdate pending pkcs11
-    resolver rrsetorder sortlist smartsign staticstub stub tkey
+    resolver rpz rrsetorder sortlist smartsign staticstub stub tkey
     tsig tsiggss unknown upforwd views xfer xferquota zonechecks"
 
 # PERL will be an empty string if no perl interpreter was found.
diff --git a/bin/tests/system/rpz/Makefile.in b/bin/tests/system/rpz/Makefile.in
new file mode 100644 (file)
index 0000000..557e8ac
--- /dev/null
@@ -0,0 +1,55 @@
+# Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: Makefile.in,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+srcdir =       @srcdir@
+VPATH =                @srcdir@
+top_srcdir =   @top_srcdir@
+
+@BIND9_VERSION@
+
+@BIND9_MAKE_INCLUDES@
+
+CINCLUDES =
+
+CDEFINES =
+CWARNINGS =
+
+DNSLIBS =
+ISCLIBS =      .
+
+DNSDEPLIBS =
+ISCDEPLIBS =
+
+DEPLIBS =
+
+LIBS =         @LIBS@
+
+TARGETS =      rpz@EXEEXT@
+
+RPZOBJS =      rpz.@O@
+
+SRCS =         rpz.c
+
+@BIND9_MAKE_RULES@
+
+all: rpz@EXEEXT@
+
+rpz@EXEEXT@: ${RPZOBJS}
+       ${LIBTOOL_MODE_LINK} ${PURIFY} ${CC} ${CFLAGS} ${LDFLAGS} -o $@ ${RPZOBJS} ${LIBS}
+
+clean distclean::
+       rm -f ${TARGETS}
+
diff --git a/bin/tests/system/rpz/clean.sh b/bin/tests/system/rpz/clean.sh
new file mode 100644 (file)
index 0000000..bc8ec84
--- /dev/null
@@ -0,0 +1,22 @@
+# Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: clean.sh,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+
+# Clean up after rpz tests.
+
+rm -f dig.out* nsupdate.tmp
+rm -f  */named.memstats */named.run */session.key
+rm -f ns3/bl*.db */*.jnl
diff --git a/bin/tests/system/rpz/ns1/named.conf b/bin/tests/system/rpz/ns1/named.conf
new file mode 100644 (file)
index 0000000..7318180
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: named.conf,v 1.2 2011/01/13 01:59:26 marka Exp $ */
+
+controls { /* empty */ };
+
+options {
+       query-source address 10.53.0.1;
+       notify-source 10.53.0.1;
+       transfer-source 10.53.0.1;
+       port 5300;
+       session-keyfile "session.key";
+       pid-file "named.pid";
+       listen-on { 10.53.0.1; };
+       listen-on-v6 { none; };
+       notify no;
+};
+
+zone "." {type master; file "root.db";};
diff --git a/bin/tests/system/rpz/ns1/root.db b/bin/tests/system/rpz/ns1/root.db
new file mode 100644 (file)
index 0000000..86fc1b6
--- /dev/null
@@ -0,0 +1,28 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: root.db,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+$TTL   120
+@              SOA     s1. hostmaster.ns.s1. ( 1 3600 1200 604800 60 )
+@              NS      s1
+s1.            A       10.53.0.1
+
+; rewrite responses from this zone
+tld2.          NS      ns.tld2.
+ns.tld2.       A       10.53.0.2
+
+; requests come from here
+tld3.          NS      ns.tld3.
+ns.tld3.       A       10.53.0.3
diff --git a/bin/tests/system/rpz/ns2/hints b/bin/tests/system/rpz/ns2/hints
new file mode 100644 (file)
index 0000000..66ce619
--- /dev/null
@@ -0,0 +1,18 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: hints,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+.      0       NS      s1.
+s1.    0       A       10.53.0.1
diff --git a/bin/tests/system/rpz/ns2/named.conf b/bin/tests/system/rpz/ns2/named.conf
new file mode 100644 (file)
index 0000000..e3aef74
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: named.conf,v 1.2 2011/01/13 01:59:26 marka Exp $ */
+
+controls { /* empty */ };
+
+options {
+       query-source address 10.53.0.2;
+       notify-source 10.53.0.2;
+       transfer-source 10.53.0.2;
+       port 5300;
+       pid-file "named.pid";
+       session-keyfile "session.key";
+       listen-on { 10.53.0.2; };
+       listen-on-v6 { none; };
+       notify no;
+};
+
+zone "." { type hint; file "hints"; };
+
+zone "tld2." {type master; file "tld2.db";};
+zone "sub1.tld2." {type master; file "tld2.db";};
+zone "sub2.sub1.tld2." {type master; file "tld2.db";};
diff --git a/bin/tests/system/rpz/ns2/tld2.db b/bin/tests/system/rpz/ns2/tld2.db
new file mode 100644 (file)
index 0000000..cd2b37d
--- /dev/null
@@ -0,0 +1,57 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: tld2.db,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+; RPZ rewrite responses from this zone
+
+$TTL   120
+@              SOA     tld2.  hostmaster.ns.tld2. ( 1 3600 1200 604800 60 )
+               NS      @
+               A       10.53.0.2
+
+nodata         TXT     "nodata"
+a12            A       12.12.12.12
+
+a0-1           A       192.168.0.1
+               AAAA    2001:2::1
+               TXT     "a0-1 text"
+
+a3-1           A       192.168.3.1
+               AAAA    2001:2:3::1
+               TXT     "a3-1 text"
+
+a3-2           A       192.168.3.2
+               AAAA    2001:2:3::2
+               TXT     "a3-2 text"
+
+a4-1           A       192.168.4.1
+               AAAA    2001:2:4::1
+               TXT     "a4-1 text"
+a4-1-aaaa      AAAA    2001:2:4::1
+
+a4-2           A       192.168.4.2
+               AAAA    2001:2:4::2
+               TXT     "a4-2 text"
+
+a4-3           A       192.168.4.3
+               AAAA    2001:2:4::3
+               TXT     "a4-3 text"
+
+a4-4           A       192.168.4.4
+               AAAA    2001:2:4::4
+               TXT     "a4-4 text"
+
+a4-5           CNAME   a12
+
diff --git a/bin/tests/system/rpz/ns3/base.db b/bin/tests/system/rpz/ns3/base.db
new file mode 100644 (file)
index 0000000..7b4e4b2
--- /dev/null
@@ -0,0 +1,31 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: base.db,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+; RPZ test
+
+$TTL   120
+@              SOA     tld3.  hostmaster.ns.tld3. ( 1 3600 1200 604800 60 )
+@              NS      ns.utld.
+
+; Poke the radix tree a little.
+128.1111.2222.3333.4444.5555.6666.7777.8888.rpz-ip     CNAME   .
+128.1111.2222.3333.4444.5555.6666.zz.rpz-ip            CNAME   .
+128.1111.2222.3333.4444.5555.zz.8888.rpz-ip            CNAME   .
+128.1111.2222.3333.4444.zz.8888.rpz-ip                 CNAME   .
+128.zz.3333.4444.0.0.8888.rpz-ip                       CNAME   .
+128.zz.3333.4444.0.7777.8888.rpz-ip                    CNAME   .
+128.zz.3333.4444.0.8777.8888.rpz-ip                    CNAME   .
+127.zz.3333.4444.0.8777.8888.rpz-ip                    CNAME   .
diff --git a/bin/tests/system/rpz/ns3/hints b/bin/tests/system/rpz/ns3/hints
new file mode 100644 (file)
index 0000000..a47fc16
--- /dev/null
@@ -0,0 +1,18 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: hints,v 1.2 2011/01/13 01:59:27 marka Exp $
+
+.      0       NS      s1.
+s1.    0       A       10.53.0.1
diff --git a/bin/tests/system/rpz/ns3/named.conf b/bin/tests/system/rpz/ns3/named.conf
new file mode 100644 (file)
index 0000000..3d377dd
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: named.conf,v 1.2 2011/01/13 01:59:27 marka Exp $ */
+
+controls { /* empty */ };
+
+options {
+       query-source address 10.53.0.3;
+       notify-source 10.53.0.3;
+       transfer-source 10.53.0.3;
+       port 5300;
+       pid-file "named.pid";
+       session-keyfile "session.key";
+       listen-on { 10.53.0.3; };
+       listen-on-v6 { none; };
+       notify no;
+
+       response-policy {
+           zone "bl";
+           zone "bl-given" policy given;
+           zone "bl-no-op" policy no-op;
+           zone "bl-nodata" policy nodata;
+           zone "bl-nxdomain" policy nxdomain;
+           zone "bl-cname" policy cname nodata.tld2.;
+       };
+};
+
+key rndc_key {
+        secret "1234abcd8765";
+        algorithm hmac-md5;
+};
+controls {
+        inet 10.53.0.3 port 9953 allow { any; } keys { rndc_key; };
+};
+
+logging {
+       category queries { default_stderr; };
+       category query-errors { default_stderr; };
+};
+
+
+zone "." { type hint; file "hints"; };
+
+
+zone "bl."         {type master; file "bl.db";
+                       allow-update {any;};
+};
+zone "bl-given."    {type master; file "bl-given.db";
+                       allow-update {any;};
+};
+zone "bl-no-op."    {type master; file "bl-no-op.db";
+                       allow-update {any;};
+};
+zone "bl-nodata."   {type master; file "bl-nodata.db";
+                       allow-update {any;};
+};
+zone "bl-nxdomain." {type master; file "bl-nxdomain.db";
+                       allow-update {any;};
+};
+zone "bl-cname."    {type master; file "bl-cname.db";
+                       allow-update {any;};
+};
+
diff --git a/bin/tests/system/rpz/rpz.c b/bin/tests/system/rpz/rpz.c
new file mode 100644 (file)
index 0000000..a9d35a6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: rpz.c,v 1.2 2011/01/13 01:59:26 marka Exp $ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+
+#define USAGE "usage: nsip | nsdname\n"
+
+int
+main(int argc, char **argv)
+{
+       if (argc != 2) {
+               fputs(USAGE, stderr);
+               return (1);
+       }
+
+       if (!strcasecmp(argv[1], "nsip")) {
+#ifdef ENABLE_RPZ_NSIP
+               return (0);
+#else
+               return (1);
+#endif
+           }
+
+       if (!strcasecmp(argv[1], "nsdname")) {
+#ifdef ENABLE_RPZ_NSDNAME
+               return (0);
+#else
+               return (1);
+#endif
+           }
+
+           fputs(USAGE, stderr);
+           return (1);
+}
diff --git a/bin/tests/system/rpz/setup.sh b/bin/tests/system/rpz/setup.sh
new file mode 100644 (file)
index 0000000..8b0f878
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: setup.sh,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+sh clean.sh
+
+for NM in '' -given -no-op -nodata -nxdomain -cname; do
+    cp -f ns3/base.db ns3/bl$NM.db
+done
diff --git a/bin/tests/system/rpz/test1 b/bin/tests/system/rpz/test1
new file mode 100644 (file)
index 0000000..ec3bb95
--- /dev/null
@@ -0,0 +1,24 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: test1,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+
+server 10.53.0.3 5300
+
+update add  a0-1.tld2.bl.      300 CNAME .
+update add  a3-1.tld2.bl.      300 CNAME *.
+update add  *.sub1.tld2.bl.    300 A 12.12.12.12
+
+send
diff --git a/bin/tests/system/rpz/test2 b/bin/tests/system/rpz/test2
new file mode 100644 (file)
index 0000000..57565d0
--- /dev/null
@@ -0,0 +1,35 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: test2,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+
+server 10.53.0.3 5300
+
+; NODATA a3-1.tld2
+update add  32.1.3.168.192.rpz-ip.bl   300 CNAME *.
+
+; NXDOMAIN for network of a4-1.tld2
+update add  24.0.4.168.192.rpz-ip.bl   300 CNAME .
+
+; poke hole in NXDOMAIN CIDR block to leave a4-1.tld2 unchanged
+update add  32.1.4.168.192.rpz-ip.bl   300 CNAME 32.1.4.168.192
+
+; NODATA a4-3.tld2
+update add  32.3.4.168.192.rpz-ip.bl   300 CNAME *.
+
+; NXDOMAIN for IPv6 a3-1.tld2
+update add  128.1.zz.3.2.2001.rpz-ip.bl        300 CNAME .
+
+send
diff --git a/bin/tests/system/rpz/test3 b/bin/tests/system/rpz/test3
new file mode 100644 (file)
index 0000000..fa37e01
--- /dev/null
@@ -0,0 +1,22 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: test3,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+
+server 10.53.0.3 5300
+
+update add  *.tld2.rpz-nsdname.bl.     300 CNAME .
+
+send
diff --git a/bin/tests/system/rpz/test4 b/bin/tests/system/rpz/test4
new file mode 100644 (file)
index 0000000..86c3cee
--- /dev/null
@@ -0,0 +1,22 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: test4,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+
+server 10.53.0.3 5300
+
+update add  32.2.0.53.10.rpz-nsip.bl.  300 CNAME .
+
+send
diff --git a/bin/tests/system/rpz/test5 b/bin/tests/system/rpz/test5
new file mode 100644 (file)
index 0000000..fff20e5
--- /dev/null
@@ -0,0 +1,36 @@
+; Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+;
+; Permission to use, copy, modify, and/or distribute this software for any
+; purpose with or without fee is hereby granted, provided that the above
+; copyright notice and this permission notice appear in all copies.
+;
+; THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+; REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+; AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+; INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+; LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+; OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+; PERFORMANCE OF THIS SOFTWARE.
+
+; $Id: test5,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+
+server 10.53.0.3 5300
+update add  a3-1.tld2.bl-given.            300 CNAME .
+send
+
+server 10.53.0.3 5300
+update add  a3-2.tld2.bl-no-op.            300 CNAME .
+send
+
+server 10.53.0.3 5300
+update add  a3-3.tld2.bl-nodata.    300 CNAME .
+send
+
+server 10.53.0.3 5300
+update add  a3-4.tld2.bl-nxdomain.  300 CNAME *.
+send
+
+server 10.53.0.3 5300
+update add  a3-5.tld2.bl-cname.            300 CNAME .
+send
diff --git a/bin/tests/system/rpz/tests.sh b/bin/tests/system/rpz/tests.sh
new file mode 100644 (file)
index 0000000..6e59a05
--- /dev/null
@@ -0,0 +1,223 @@
+# Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+# PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: tests.sh,v 1.2 2011/01/13 01:59:26 marka Exp $
+
+# test response policy zones (RPZ)
+
+SYSTEMTESTTOP=..
+. $SYSTEMTESTTOP/conf.sh
+
+root=10.53.0.1
+s2=10.53.0.2
+s3=10.53.0.3
+
+DIGCMD="$DIG +noadd +nosea +nocmd -p 5300"
+
+
+USAGE="$0: [-x]"
+while getopts "x" c; do
+    case $c in
+       x) set -x;;
+       *) echo "$USAGE" 1>&2; exit 1;;
+    esac
+done
+shift `expr $OPTIND - 1 || true`
+if test "$#" -ne 0; then
+    echo "$USAGE" 1>&2
+    exit 1
+fi
+# really quit on control-C
+trap 'exit 1' 1 2 15
+
+
+# set DIGNM=file name for dig output
+# $1=target domain  $2=optional query type  $3=optional string
+dignm () {
+    DIGNM=dig.out-$DIGNM_SUB-$1
+    if test -n "$3"; then
+       DIGNM=$DIGNM-$3
+    fi
+    if test -n "$2"; then
+       DIGNM=$DIGNM-`expr "x$2" : 'x-t *\(.*\)'`
+    fi
+}
+
+setret () {
+    ret=1
+    echo "$*"
+}
+
+# check rewrite to NXDOMAIN
+# $1=target domain  $2=optional query type
+nxdomain () {
+    dignm $1 "$2"
+    $DIGCMD +noauth $1 $2 @$s3 >$DIGNM
+    $PERL ../digcomp.pl dig.out-nxdomain $DIGNM || setret "   in $DIGNM"
+}
+
+# check rewrite to NODATA
+# $1=target domain  $2=optional query type
+nodata () {
+    dignm $1 "$2"
+    $DIGCMD +noauth $1 $2 @$s3 >$DIGNM
+    $PERL ../digcomp.pl dig.out-nodata $DIGNM ||  setret "   in $DIGNM"
+}
+
+# check rewrite to "A 12.12.12.12"
+#   modify the output so that it is easily matched, but save the original line
+# $1=target domain  $2=optional query type
+a12 () {
+    dignm $1 "$2"
+    $DIGCMD +noauth $1 $2 @$s3                                         \
+       | sed -e "/^$1\.        /{"                                     \
+           -e "s/.*/;xxx &/p" -e "s/^;xxx $1/a12.tld2/" -e '}'         \
+       >$DIGNM
+    $PERL ../digcomp.pl dig.out-a12 $DIGNM || ret=1
+}
+
+# check that a response is not rewritten
+# $1=target domain  $2=optional query type
+nochange () {
+    dignm $1 "$2" ok
+    DIGNM_OK=$DIGNM
+    dignm $1 "$2"
+    $DIGCMD $1 $2 @$s3 >$DIGNM
+    $DIGCMD $1 $2 @$s2 >$DIGNM_OK
+    $PERL ../digcomp.pl $DIGNM_OK $DIGNM || ret=1
+}
+
+flush_db () {
+    if $RNDC -c ../common/rndc.conf -s $s3 -p 9953 freeze; then : ; else
+       echo "I:failed to freeze policy zone $1"
+       exit 1
+    fi
+    if $RNDC -c ../common/rndc.conf -s $s3 -p 9953 thaw; then : ; else
+       echo "I:failed to thaw policy zone $1"
+       exit 1
+    fi
+}
+
+# $1=message  $2=test file
+start_test () {
+    ret=0
+    if test -n "$1"; then
+       echo "I:checking $1"
+    fi
+    PREV_FILE=$2
+    if test -n "$2"; then
+       DIGNM_SUB=`expr "$2" : 'test\(.\)'`
+       if $NSUPDATE -v $PREV_FILE; then : ; else
+           echo "I:failed to update policy zone $1 with $2"
+           exit 1
+       fi
+       #flush_db
+    else
+       DIGNM_SUB="${DIGNM_SUB}x"
+    fi
+}
+
+end_test () {
+    if test $ret != 0; then
+       echo "I:failed"
+    else
+       rm -f dig.out-${DIGNM_SUB}*
+    fi
+    if test -n "$PREV_FILE"; then
+       sed -e 's/ add / delete /' $PREV_FILE | $NSUPDATE
+       status=`expr $status + $ret`
+       #flush_db
+    fi
+}
+
+
+# make NXDOMAIN and NODATA prototypes
+echo "I:making prototype RPZ NXDOMAIN, NODATA, and CNAME results"
+$DIGCMD +noauth nonexistent @$s2 >dig.out-nxdomain
+$DIGCMD +noauth nodata.tld2 @$s2 >dig.out-nodata
+$DIGCMD +noauth a12.tld2    @$s2 >dig.out-a12
+
+status=0
+
+start_test "RPZ QNAME rewrites" test1
+nxdomain a0-1.tld2
+nodata a3-1.tld2
+a12 a4-1.sub1.tld2
+end_test
+
+start_test "RPZ IP rewrites" test2
+nodata a3-1.tld2
+nochange a3-2.tld2
+nxdomain a3-99.tld2
+nochange a4-1.tld2
+nxdomain a4-2.tld2
+nochange a4-2.tld2 -taaaa
+nochange a4-2.tld2 -ttxt
+nxdomain a4-2.tld2 -tany
+nodata a4-3.tld2
+nxdomain a3-1.tld2 -tAAAA
+nochange a4-1-aaaa.tld2 -tAAAA
+end_test
+
+start_test "RPZ radix tree deletions"
+nochange a3-1.tld2
+nochange a3-2.tld2
+nochange a4-1.tld2
+nochange a4-2.tld2
+nochange a4-2.tld2 -taaaa
+nochange a4-2.tld2 -ttxt
+nochange a4-2.tld2 -tany
+nochange a4-3.tld2
+nochange a3-1.tld2 -tAAAA
+nochange a4-1-aaaa.tld2 -tAAAA
+end_test
+
+if ./rpz nsdname; then
+    start_test "RPZ NSDNAME rewrites" test3
+    nochange a3-1.tld2
+    nxdomain a3-1.sub1.tld2
+    nxdomain a3-1.sub2.sub1.tld2
+    end_test
+else
+    echo "I:RPZ NSDNAME not checked; named was not built with --enable-rpz-nsdname"
+fi
+
+if ./rpz nsip; then
+    start_test "RPZ NSIP rewrites" test4
+    nxdomain a3-1.tld2
+    nochange .
+    end_test
+else
+    echo "I:RPZ NSIP not checked; named was not built with --enable-rpz-nsip"
+fi
+
+start_test "RPZ policy overrides" test5
+nxdomain a3-1.tld2
+nochange a3-2.tld2
+nodata a3-3.tld2
+nxdomain a3-4.tld2
+dignm a3-5.tld2 -tany
+$DIGCMD +noauth a3-5.tld2 -tany @$s3 >$DIGNM
+if grep CNAME $DIGNM >/dev/null; then : ; else
+    echo "'policy cname' failed"
+    ret=1
+fi
+end_test
+
+if test "$status" -eq 0; then
+    rm -f dig.out*
+fi
+
+echo "I:exit status: $status"
+exit $status
index f261974c3b0afb20b7f6a3fac89d3e5b62f2bc86..3f7c8a9c9b67a2b5a7ce2023fb72e96f33c8dc84 100644 (file)
@@ -16,7 +16,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: config.h.in,v 1.141 2010/12/23 04:09:28 marka Exp $ */
+/* $Id: config.h.in,v 1.142 2011/01/13 01:59:25 marka Exp $ */
 
 /*! \file */
 
@@ -144,9 +144,6 @@ int sigwait(const unsigned int *set, int *sig);
 /* Define if threads need PTHREAD_SCOPE_SYSTEM */
 #undef NEED_PTHREAD_SCOPE_SYSTEM
 
-/* Define if building universal (internal helper macro) */
-#undef AC_APPLE_UNIVERSAL_BUILD
-
 /* Define to enable the "filter-aaaa-on-v4" option. */
 #undef ALLOW_FILTER_AAAA_ON_V4
 
@@ -160,6 +157,12 @@ int sigwait(const unsigned int *set, int *sig);
 /* Define to enable "rrset-order fixed" syntax. */
 #undef DNS_RDATASET_FIXED
 
+/* Define to enable rpz-nsdname rules. */
+#undef ENABLE_RPZ_NSDNAME
+
+/* Define to enable rpz-nsip rules. */
+#undef ENABLE_RPZ_NSIP
+
 /* Solaris hack to get select_large_fdset. */
 #undef FD_SETSIZE
 
@@ -374,9 +377,6 @@ int sigwait(const unsigned int *set, int *sig);
 /* Define to the one symbol short name of this package. */
 #undef PACKAGE_TARNAME
 
-/* Define to the home page for this package. */
-#undef PACKAGE_URL
-
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
 
@@ -397,26 +397,15 @@ int sigwait(const unsigned int *set, int *sig);
 /* define if idnkit support is to be included. */
 #undef WITH_IDN
 
-/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
-   significant byte first (like Motorola and SPARC, unlike Intel). */
-#if defined AC_APPLE_UNIVERSAL_BUILD
-# if defined __BIG_ENDIAN__
-#  define WORDS_BIGENDIAN 1
-# endif
-#else
-# ifndef WORDS_BIGENDIAN
-#  undef WORDS_BIGENDIAN
-# endif
-#endif
+/* Define to 1 if your processor stores words with the most significant byte
+   first (like Motorola and SPARC, unlike Intel and VAX). */
+#undef WORDS_BIGENDIAN
 
 /* Define to empty if `const' does not conform to ANSI C. */
 #undef const
 
-/* Define to `__inline__' or `__inline' if that's what the C compiler
-   calls it, or to nothing if 'inline' is not supported under any name.  */
-#ifndef __cplusplus
+/* Define to empty if your compiler does not support "static inline". */
 #undef inline
-#endif
 
 /* Define to `unsigned int' if <sys/types.h> does not define. */
 #undef size_t
index 9035a3d500e9e2d64e5dadcf51bc6839eb9d09af..393dd8db8aee09029dcf4573ab6fc3c84757b024 100644 (file)
@@ -18,7 +18,7 @@ AC_DIVERT_PUSH(1)dnl
 esyscmd([sed "s/^/# /" COPYRIGHT])dnl
 AC_DIVERT_POP()dnl
 
-AC_REVISION($Revision: 1.511 $)
+AC_REVISION($Revision: 1.512 $)
 
 AC_INIT(lib/dns/name.c)
 AC_PREREQ(2.59)
@@ -308,7 +308,7 @@ AC_TRY_COMPILE(, [
        ],
        [AC_MSG_RESULT(no)],
        [AC_MSG_RESULT(yes)
-        AC_DEFINE(inline, )])
+        AC_DEFINE(inline, ,[Define to empty if your compiler does not support "static inline".])])
 
 AC_TYPE_SIZE_T
 AC_CHECK_TYPE(ssize_t, int)
@@ -2729,6 +2729,42 @@ case "$enable_fixed" in
                ;;
 esac
 
+#
+# Enable response policy rewriting using NS IP addresses
+#
+AC_ARG_ENABLE(rpz-nsip,
+       [  --enable-rpz-nsip      enable rpz-nsip rules [[default=no]]],
+                       enable_nsip="$enableval",
+                       enable_nsip="no")
+case "$enable_nsip" in
+       yes)
+               AC_DEFINE(ENABLE_RPZ_NSIP, 1,
+                         [Define to enable rpz-nsip rules.])
+               ;;
+       no)
+               ;;
+       *)
+               ;;
+esac
+
+#
+# Enable response policy rewriting using NS name
+#
+AC_ARG_ENABLE(rpz-nsdname,
+       [  --enable-rpz-nsdname   enable rpz-nsdname rules [[default=no]]],
+                       enable_nsdname="$enableval",
+                       enable_nsdname="no")
+case "$enable_nsdname" in
+       yes)
+               AC_DEFINE(ENABLE_RPZ_NSDNAME, 1,
+                         [Define to enable rpz-nsdname rules.])
+               ;;
+       no)
+               ;;
+       *)
+               ;;
+esac
+
 #
 # Activate "filter-aaaa-on-v4" or not?
 #
@@ -3269,6 +3305,7 @@ AC_CONFIG_FILES([
        bin/tests/system/filter-aaaa/Makefile
        bin/tests/system/gost/prereq.sh
        bin/tests/system/lwresd/Makefile
+       bin/tests/system/rpz/Makefile
        bin/tests/system/tkey/Makefile
        bin/tests/tasks/Makefile
        bin/tests/timers/Makefile
index 56f4a9ebe0ff20445d759bdbbe083c13d3b60826..a3175b8f8ae242beaf0e93a00731473caeecc37e 100644 (file)
@@ -18,7 +18,7 @@
  - PERFORMANCE OF THIS SOFTWARE.
 -->
 
-<!-- File: $Id: Bv9ARM-book.xml,v 1.475 2011/01/07 04:31:38 marka Exp $ -->
+<!-- File: $Id: Bv9ARM-book.xml,v 1.476 2011/01/13 01:59:27 marka Exp $ -->
 <book xmlns:xi="http://www.w3.org/2001/XInclude">
   <title>BIND 9 Administrator Reference Manual</title>
 
@@ -5165,6 +5165,7 @@ badresp:1,adberr:0,findfail:0,valfail:0]
     <optional> zero-no-soa-ttl-cache <replaceable>yes_or_no</replaceable> ; </optional>
     <optional> deny-answer-addresses { <replaceable>address_match_list</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
     <optional> deny-answer-aliases { <replaceable>namelist</replaceable> } <optional> except-from { <replaceable>namelist</replaceable> } </optional>;</optional>
+    <optional> response-policy { <replaceable>zone_name</replaceable> <optional> policy <replaceable>given</replaceable> | <replaceable>no-op</replaceable> | <replaceable>nxdomain</replaceable> | <replaceable>nodata</replaceable> | <replaceable>cname domain</replaceable> </optional> ; } ; </optional>
 };
 </programlisting>
 
@@ -9183,6 +9184,143 @@ deny-answer-aliases { "example.net"; };
            spuriously can break such applications.
          </para>
        </sect3>
+
+        <sect3>
+         <title>Response Policy Zone (RPZ) Rewriting</title>
+         <para>
+           <acronym>BIND</acronym> 9 includes an intentionally limited
+            mechanism to modify DNS responses for recursive requests
+            similar to email anti-spam DNS blacklists.
+            All response policy zones are named in the
+            <command>response-policy</command> option for the view or among the
+            global options if there is no response-policy option for the view.
+          </para>
+
+          <para>
+            The rules encoded in a response policy zone (RPZ) are applied
+            only to responses to queries that ask for recursion (RD=1).
+            RPZs are normal DNS zones containing largely valid RRsets
+            that can be queried normal if allowed.
+            It is usually best to restrict those queries with something like
+            <command>allow-query {none; };</command> or
+            <command>allow-query { 127.0.0.1; };</command>.
+          </para>
+
+          <para>
+            There are four kinds of RPZ rewrite rules.  QNAME rules are
+            applied to query names in requests and to targets of CNAME
+            records resolved in the process of generating the response.
+            The owner name of a QNAME rule is the query name relativized
+            to the RPZ.
+          </para>
+
+          <para>
+            IP rules are triggered by addresses in A and AAAA records.
+            All IP addresses in A or AAAA RRsets are tested and the rule
+            longest prefix is applied.  Ties between rules with equal prefixes
+            are broken in favor of the first RPZ mentioned in the
+            response-policy option.
+            The rule matching the smallest IP address is chosen among equal
+            prefix rules from a single RPZ.
+            IP rules are expressed in RRsets with owner names that are
+            subdomains of rpz-ip and encoding an IP address block, reversed
+            as in IN-ARPA.
+            prefix.B.B.B.B with prefix between 1 and 32 and B between 1 and 255
+            encodes an IPv4 address.
+            IPv6 addresses are encoded by with prefix.W.W.W.W.W.W.W.W or
+            prefix.WORDS.zz.WORDS.  The words in the standard IPv6 text
+            representation are reversed, "::" is replaced with ".zz.",
+            and ":" becomes ".".
+          </para>
+
+          <para>
+            NSDNAME rules match names in NS RRsets for the response or a
+            parent.  They are encoded as subdomains of rpz-nsdomain relativized
+            to the RPZ origin name.
+          </para>
+
+          <para>
+            NSIP rules match IP addresses in A and AAAA RRsets for names of
+            responsible servers or the names that can be matched by NSDNAME
+            rules.  The are encoded like IP rules except as subdomains of
+            rpz-nsip.
+         </para>
+
+         <para>
+            Authority verification issues and variations in authority data in
+           the current version of <acronym>BIND</acronym> 9 can cause
+           inconsistent results from NSIP and NSDNAME.  So they are available
+           only when <acronym>BIND</acronym> is built with the
+           <userinput>--enable-rpz-nsip</userinput> or
+           <userinput>--enable-rpz-nsdname</userinput> options
+            on the "configure" command line.
+          </para>
+
+          <para>
+            Four policies can be expressed.
+            The <command>NXDOMAIN</command> policy causes a NXDOMAIN response
+            and is expressed with an RRset consisting of a single CNAME
+            whose target is the root domain (.).
+            <command>NODATA</command> generates NODATA or ANCOUNT=1 regardless
+            of query type.
+            It is expressed with a CNAME whose target is the wildcard
+            top-level domain (*.).
+            The <command>NO-OP</command> policy does not change the response
+            and is used to "poke holes" in policies for larger CIDR blocks or in
+            zones named later in the <command>response-policy</command> option.
+            The NO-OP policy is expressed by a CNAME with a target consisting
+            of the variable part of the owner name, such as "example.com." for
+            a QNAME rule or "128.1.0.0.127." for an IP rule.
+            The <command>CNAME</command> policy is used to replace the RRsets
+            of response.
+            A and AAAA RRsets are most common and useful to capture
+            an evil domain in a walled garden, but any valid set of RRsets
+            is possible.
+          </para>
+
+          <para>
+            All of the policies in an RPZ can be overridden with a
+            <command>policy</command> clause.
+            <command>given</command> says "do not override."
+            <command>no-op</command> says "do nothing" regardless of the policy
+            in RPZ records.
+            <command>nxdomain</command> causes all RPZ rules to generate
+            NXDOMAIN results.
+            <command>nodata</command> gives nodata.
+            <command>cname domain</command> causes all RPZ rules to act as if
+            the consisted of a "cname domain" record.
+          </para>
+
+          <para>
+            For example, you might use this option statement
+          </para>
+<programlisting>response-policy { zone "bl"; };</programlisting>
+          <para>
+            and this zone statement
+          </para>
+<programlisting>zone "bl" {type master; file "example/bl"; allow-query {none;}; };</programlisting>
+          <para>
+            with this zone file
+          </para>
+<programlisting>$TTL 1H
+@                   SOA LOCALHOST. named-mgr.example.com (1 1h 15m 30d 2h)
+
+; QNAME rules
+nxdomain.domain.com         CNAME   .
+nodata.domain.com           CNAME   *.
+bad.domain.com              A       10.0.0.1
+                            AAAA    2001:2::1
+ok.domain.com               CNAME   ok.domain.com.
+
+; IP rules rewriting all answers for 127/8 except 127.0.0.1
+8.0.0.0.127.ip              CNAME   .
+32.1.0.0.127.ip             CNAME   32.1.0.0.127.
+
+; NSDNAME and NSIP rules
+ns.domain.com.rpz-nsdname   CNAME   .
+48.zz.2.2001.rpz-nsip       CNAME   .
+</programlisting>
+       </sect3>
       </sect2>
 
       <sect2 id="server_statement_grammar">
index 20a91b2a545d937cab2c017f66d9b857fb983224..65dca3a3c3593cb32e61403a90070ec9fd177284 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.175 2011/01/06 23:47:00 tbox Exp $
+# $Id: Makefile.in,v 1.176 2011/01/13 01:59:27 marka Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -64,9 +64,9 @@ DNSOBJS =     acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \
                name.@O@ ncache.@O@ nsec.@O@ nsec3.@O@ order.@O@ peer.@O@ \
                portlist.@O@ private.@O@ \
                rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \
-               rdatalist.@O@ \
-               rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ request.@O@ \
-               resolver.@O@ result.@O@ rootns.@O@ rriterator.@O@ sdb.@O@ \
+               rdatalist.@O@ rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ \
+               request.@O@ resolver.@O@ result.@O@ rootns.@O@ rpz.@O@ \
+               rriterator.@O@ sdb.@O@ \
                sdlz.@O@ soa.@O@ ssu.@O@ ssu_external.@O@ \
                stats.@O@ tcpmsg.@O@ time.@O@ timer.@O@ tkey.@O@ \
                tsec.@O@ tsig.@O@ ttl.@O@ validator.@O@ \
@@ -90,11 +90,10 @@ DNSSRCS =   acache.c acl.c adb.c byaddr.c \
                keydata.c keytable.c lib.c log.c lookup.c \
                master.c masterdump.c message.c \
                name.c ncache.c nsec.c nsec3.c order.c peer.c portlist.c \
-               rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c \
-               rdatalist.c \
+               rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c rdatalist.c \
                rdataset.c rdatasetiter.c rdataslab.c request.c \
-               resolver.c result.c rootns.c rriterator.c sdb.c sdlz.c \
-               soa.c ssu.c ssu_external.c \
+               resolver.c result.c rootns.c rpz.c rriterator.c \
+               sdb.c sdlz.c soa.c ssu.c ssu_external.c \
                stats.c tcpmsg.c time.c timer.c tkey.c \
                tsec.c tsig.c ttl.c validator.c \
                version.c view.c xfrin.c zone.c zonekey.c zt.c ${OTHERSRCS}
index f1ac0043011e4f62a4f1b57204f9faadd947675b..133891188a58f2ce6768aa07ff25b326a27cb1e0 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: db.c,v 1.95 2009/10/08 23:13:06 marka Exp $ */
+/* $Id: db.c,v 1.96 2011/01/13 01:59:27 marka Exp $ */
 
 /*! \file */
 
@@ -944,3 +944,21 @@ dns_db_resigned(dns_db_t *db, dns_rdataset_t *rdataset,
        if (db->methods->resigned != NULL)
                (db->methods->resigned)(db, rdataset, version);
 }
+
+void
+dns_db_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st)
+{
+       if (db->methods->rpz_enabled != NULL)
+               (db->methods->rpz_enabled)(db, st);
+}
+
+isc_result_t
+dns_db_rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
+                  dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
+                  dns_rdataset_t *ardataset, dns_rpz_st_t *st)
+{
+       if (db->methods->rpz_findips == NULL)
+               return (ISC_R_NOTIMPLEMENTED);
+       return ((db->methods->rpz_findips)(rpz, rpz_type, zone, db, version,
+                                          ardataset, st));
+}
index 234f7b5e41ced2ec2b8e3d88874af4945e943f45..07d94dcae0c560018903659dda452e10077f160a 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: db.h,v 1.102 2009/11/25 23:49:22 tbox Exp $ */
+/* $Id: db.h,v 1.103 2011/01/13 01:59:28 marka Exp $ */
 
 #ifndef DNS_DB_H
 #define DNS_DB_H 1
@@ -63,6 +63,7 @@
 #include <dns/name.h>
 #include <dns/rdata.h>
 #include <dns/rdataset.h>
+#include <dns/rpz.h>
 #include <dns/types.h>
 
 ISC_LANG_BEGINDECLS
@@ -170,6 +171,13 @@ typedef struct dns_dbmethods {
                                           dns_dbversion_t *version);
        isc_boolean_t   (*isdnssec)(dns_db_t *db);
        dns_stats_t     *(*getrrsetstats)(dns_db_t *db);
+       void            (*rpz_enabled)(dns_db_t *db, dns_rpz_st_t *st);
+       isc_result_t    (*rpz_findips)(dns_rpz_zone_t *rpz,
+                                      dns_rpz_type_t rpz_type,
+                                      dns_zone_t *zone, dns_db_t *db,
+                                      dns_dbversion_t *version,
+                                      dns_rdataset_t *ardataset,
+                                      dns_rpz_st_t *st);
 } dns_dbmethods_t;
 
 typedef isc_result_t
@@ -1487,6 +1495,31 @@ dns_db_getrrsetstats(dns_db_t *db);
  *     dns_rdatasetstats_create(); otherwise NULL.
  */
 
+void
+dns_db_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st);
+/*%<
+ * See if a policy database has DNS_RPZ_TYPE_IP, DNS_RPZ_TYPE_NSIP, or
+ * DNS_RPZ_TYPE_NSDNAME records.
+ */
+
+isc_result_t
+dns_db_rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
+                  dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
+                  dns_rdataset_t *ardataset, dns_rpz_st_t *st);
+/*%<
+ * Search the CDIR block tree of a response policy tree of trees for the best
+ * match to any of the IP addresses in an A or AAAA rdataset.
+ *
+ * Requires:
+ * \li search in policy zone 'rpz' for a match of 'rpz_type' either
+ *         DNS_RPZ_TYPE_IP or DNS_RPZ_TYPE_NSIP
+ * \li 'zone' and 'db' are the database corresponding to 'rpz'
+ * \li 'version' is the required version of the database
+ * \li 'ardataset' is an A or AAAA rdataset of addresses to check
+ * \li 'found' specifies the previous best match if any or
+ *         or NULL, an empty name, 0, DNS_RPZ_POLICY_MISS, and 0
+ */
+
 ISC_LANG_ENDDECLS
 
 #endif /* DNS_DB_H */
index e00167bf1ae6b6fe30197cc7ba2eff5161f6e644..bf8ba35cb57a5073bda2a6183a7d353b78fa2516 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: name.h,v 1.135 2010/07/09 23:46:51 tbox Exp $ */
+/* $Id: name.h,v 1.136 2011/01/13 01:59:28 marka Exp $ */
 
 #ifndef DNS_NAME_H
 #define DNS_NAME_H 1
@@ -756,7 +756,7 @@ dns_name_towire(const dns_name_t *name, dns_compress_t *cctx,
 
 isc_result_t
 dns_name_fromtext(dns_name_t *name, isc_buffer_t *source,
-                 dns_name_t *origin, unsigned int options,
+                 const dns_name_t *origin, unsigned int options,
                  isc_buffer_t *target);
 /*%<
  * Convert the textual representation of a DNS name at source
@@ -1168,11 +1168,18 @@ dns_name_tostring(dns_name_t *source, char **target, isc_mem_t *mctx);
 isc_result_t
 dns_name_fromstring(dns_name_t *target, const char *src, unsigned int options,
                    isc_mem_t *mctx);
+isc_result_t
+dns_name_fromstring2(dns_name_t *target, const char *src,
+                    const dns_name_t *origin, unsigned int options,
+                    isc_mem_t *mctx);
 /*%<
  * Convert a string to a name and place it in target, allocating memory
  * as necessary.  'options' has the same semantics as that of
  * dns_name_fromtext().
  *
+ * If 'target' has a buffer then the name will be copied into it rather than
+ * memory being allocated.
+ *
  * Requires:
  *
  * \li 'target' is a valid name that is not read-only.
diff --git a/lib/dns/include/dns/rpz.h b/lib/dns/include/dns/rpz.h
new file mode 100644 (file)
index 0000000..3533c76
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: rpz.h,v 1.2 2011/01/13 01:59:28 marka Exp $ */
+
+#ifndef DNS_RPZ_H
+#define DNS_RPZ_H 1
+
+#include <isc/lang.h>
+
+#include <dns/fixedname.h>
+#include <dns/rdata.h>
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_RPZ_IP_ZONE                "rpz-ip"
+#define DNS_RPZ_NSIP_ZONE      "rpz-nsip"
+#define DNS_RPZ_NSDNAME_ZONE   "rpz-nsdname"
+
+typedef isc_uint8_t            dns_rpz_cidr_bits_t;
+
+typedef enum {
+       DNS_RPZ_TYPE_BAD,
+       DNS_RPZ_TYPE_QNAME,
+       DNS_RPZ_TYPE_IP,
+       DNS_RPZ_TYPE_NSIP,
+       DNS_RPZ_TYPE_NSDNAME
+} dns_rpz_type_t;
+
+/*
+ * Require DNS_RPZ_POLICY_NO_OP < DNS_RPZ_POLICY_NXDOMAIN <
+ *        DNS_RPZ_POLICY_NODATA < DNS_RPZ_POLICY_CNAME.
+ */
+typedef enum {
+       DNS_RPZ_POLICY_GIVEN = 0,       /* 'given': what something else says */
+       DNS_RPZ_POLICY_NO_OP = 1,       /* 'no-op': do not rewrite */
+       DNS_RPZ_POLICY_NXDOMAIN = 2,    /* 'nxdomain': answer with NXDOMAIN */
+       DNS_RPZ_POLICY_NODATA = 3,      /* 'nodata': answer with ANCOUNT=0 */
+       DNS_RPZ_POLICY_CNAME = 4,       /* 'cname x': answer with x's rrsets */
+       DNS_RPZ_POLICY_RECORD = 5,
+       DNS_RPZ_POLICY_MISS,
+       DNS_RPZ_POLICY_ERROR
+} dns_rpz_policy_t;
+
+/*
+ * Specify a response policy zone.
+ */
+typedef struct dns_rpz_zone dns_rpz_zone_t;
+
+struct dns_rpz_zone {
+       ISC_LINK(dns_rpz_zone_t) link;
+       int                      num;
+       dns_name_t               origin;  /* Policy zone name */
+       dns_name_t               nsdname; /* RPZ_NSDNAME_ZONE.origin */
+       dns_rpz_policy_t         policy;  /* RPZ_POLICY_GIVEN or override */
+       dns_name_t               cname;   /* override name for
+                                            RPZ_POLICY_CNAME */
+};
+
+/*
+ * Radix trees for response policy IP addresses.
+ */
+typedef struct dns_rpz_cidr    dns_rpz_cidr_t;
+
+/*
+ * context for finding the best policy
+ */
+typedef struct {
+       unsigned int            state;
+# define DNS_RPZ_REWRITTEN     0x0001
+# define DNS_RPZ_DONE_QNAME    0x0002
+# define DNS_RPZ_DONE_A                0x0004
+# define DNS_RPZ_RECURSING     0x0008
+# define DNS_RPZ_HAVE_IP       0x0010
+# define DNS_RPZ_HAVE_NSIPv4   0x0020
+# define DNS_RPZ_HAVE_NSIPv6   0x0040
+# define DNS_RPZ_HAD_NSDNAME   0x0080
+       /*
+        * Best match so far.
+        */
+       struct {
+               dns_rpz_type_t          type;
+               dns_rpz_zone_t          *rpz;
+               dns_rpz_cidr_bits_t     prefix;
+               dns_rpz_policy_t        policy;
+               dns_ttl_t               ttl;
+               isc_result_t            result;
+               dns_zone_t              *zone;
+               dns_db_t                *db;
+               dns_dbnode_t            *node;
+               dns_rdataset_t          *rdataset;
+       } m;
+       /*
+        * State for chasing NS names and addresses including recursion.
+        */
+       struct {
+               unsigned int            label;
+               dns_db_t                *db;
+               dns_rdataset_t          *ns_rdataset;
+               dns_rdatatype_t         r_type;
+               isc_result_t            r_result;
+               dns_rdataset_t          *r_rdataset;
+       } ns;
+       /*
+        * State of real query while recursing for NSIP or NSDNAME.
+        */
+       struct {
+               isc_result_t            result;
+               isc_boolean_t           is_zone;
+               isc_boolean_t           authoritative;
+               dns_zone_t              *zone;
+               dns_db_t                *db;
+               dns_dbnode_t            *node;
+               dns_rdataset_t          *rdataset;
+               dns_rdataset_t          *sigrdataset;
+               dns_rdatatype_t         qtype;
+       } q;
+       dns_name_t              *qname;
+       dns_name_t              *r_name;
+       dns_name_t              *fname;
+       dns_fixedname_t         _qnamef;
+       dns_fixedname_t         _r_namef;
+       dns_fixedname_t         _fnamef;
+} dns_rpz_st_t;
+
+#define DNS_RPZ_TTL_DEFAULT            5
+
+/*
+ * So various response policy zone messages can be turned up or down.
+ */
+#define DNS_RPZ_ERROR_LEVEL    ISC_LOG_WARNING
+#define DNS_RPZ_INFO_LEVEL     ISC_LOG_INFO
+#define DNS_RPZ_DEBUG_LEVEL1   ISC_LOG_DEBUG(1)
+#define DNS_RPZ_DEBUG_LEVEL2   ISC_LOG_DEBUG(2)
+
+const char *
+dns_rpz_type2str(dns_rpz_type_t type);
+
+dns_rpz_policy_t 
+dns_rpz_str2policy(const char *str);
+
+void 
+dns_rpz_set_need(isc_boolean_t need);
+
+isc_boolean_t
+dns_rpz_needed(void);
+
+void
+dns_rpz_cidr_free(dns_rpz_cidr_t **cidr);
+
+void
+dns_rpz_view_destroy(dns_view_t *view);
+
+isc_result_t
+dns_rpz_new_cidr(isc_mem_t *mctx, dns_name_t *origin,
+                dns_rpz_cidr_t **rbtdb_cidr);
+void
+dns_rpz_enabled(dns_rpz_cidr_t *cidr, dns_rpz_st_t *st);
+
+void
+dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name);
+
+void
+dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name);
+
+isc_result_t
+dns_rpz_cidr_find(dns_rpz_cidr_t *cidr, const isc_netaddr_t *netaddr,
+                 dns_rpz_type_t type, dns_name_t *canon_name,
+                 dns_name_t *search_name, dns_rpz_cidr_bits_t *prefix);
+
+dns_rpz_policy_t
+dns_rpz_decode_cname(dns_rdataset_t *, dns_name_t *selfname);
+
+#endif /* DNS_RPZ_H */
+
index d52e4667856376d4e320304df7f65352048ac04d..153c52e5c261dc2b1123c8d07b6d8193081db8be 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: view.h,v 1.131 2011/01/11 23:47:13 tbox Exp $ */
+/* $Id: view.h,v 1.132 2011/01/13 01:59:28 marka Exp $ */
 
 #ifndef DNS_VIEW_H
 #define DNS_VIEW_H 1
@@ -74,6 +74,7 @@
 #include <dns/acl.h>
 #include <dns/fixedname.h>
 #include <dns/rdatastruct.h>
+#include <dns/rpz.h>
 #include <dns/types.h>
 
 ISC_LANG_BEGINDECLS
@@ -160,6 +161,7 @@ struct dns_view {
        dns_acl_t *                     v4_aaaa_acl;
        dns_dns64list_t                 dns64;
        unsigned int                    dns64cnt;
+       ISC_LIST(dns_rpz_zone_t)        rpz_zones;
 
        /*
         * Configurable data for server use only,
index 8442acef3cbe114e768b65fbfb35497fbcdf1068..0a18c8367761f8a70f853ad8a4a0709f99551edf 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: name.c,v 1.172 2010/07/09 05:13:15 each Exp $ */
+/* $Id: name.c,v 1.173 2011/01/13 01:59:27 marka Exp $ */
 
 /*! \file */
 
@@ -1021,7 +1021,7 @@ dns_name_toregion(dns_name_t *name, isc_region_t *r) {
 
 isc_result_t
 dns_name_fromtext(dns_name_t *name, isc_buffer_t *source,
-                 dns_name_t *origin, unsigned int options,
+                 const dns_name_t *origin, unsigned int options,
                  isc_buffer_t *target)
 {
        unsigned char *ndata, *label;
@@ -2395,6 +2395,14 @@ dns_name_tostring(dns_name_t *name, char **target, isc_mem_t *mctx) {
 isc_result_t
 dns_name_fromstring(dns_name_t *target, const char *src, unsigned int options,
                    isc_mem_t *mctx)
+{
+       return (dns_name_fromstring2(target, src, dns_rootname, options, mctx));
+}
+
+isc_result_t
+dns_name_fromstring2(dns_name_t *target, const char *src,
+                    const dns_name_t *origin, unsigned int options,
+                     isc_mem_t *mctx)
 {
        isc_result_t result;
        isc_buffer_t buf;
@@ -2405,14 +2413,19 @@ dns_name_fromstring(dns_name_t *target, const char *src, unsigned int options,
 
        isc_buffer_init(&buf, src, strlen(src));
        isc_buffer_add(&buf, strlen(src));
-       dns_fixedname_init(&fn);
-       name = dns_fixedname_name(&fn);
+       if (BINDABLE(target) && target->buffer != NULL)
+               name = target;
+       else {
+               dns_fixedname_init(&fn);
+               name = dns_fixedname_name(&fn);
+       }
 
-       result = dns_name_fromtext(name, &buf, dns_rootname, options, NULL);
+       result = dns_name_fromtext(name, &buf, origin, options, NULL);
        if (result != ISC_R_SUCCESS)
                return (result);
 
-       result = dns_name_dup(name, mctx, target);
+       if (name != target)
+               result = dns_name_dupwithoffsets(name, mctx, target);
        return (result);
 }
 
index 0b120d79e716fbb4533131fa058c1f7074f9f553..0c0f69e6902e7b2e182d445e695f018804700b7a 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: rbtdb.c,v 1.307 2010/12/02 04:58:13 marka Exp $ */
+/* $Id: rbtdb.c,v 1.308 2011/01/13 01:59:27 marka Exp $ */
 
 /*! \file */
 
@@ -53,6 +53,7 @@
 #include <dns/nsec.h>
 #include <dns/nsec3.h>
 #include <dns/rbt.h>
+#include <dns/rpz.h>
 #include <dns/rdata.h>
 #include <dns/rdataset.h>
 #include <dns/rdatasetiter.h>
@@ -437,6 +438,7 @@ typedef struct {
        dns_rbt_t *                     tree;
        dns_rbt_t *                     nsec;
        dns_rbt_t *                     nsec3;
+       dns_rpz_cidr_t *                rpz_cidr;
 
        /* Unlocked */
        unsigned int                    quantum;
@@ -953,6 +955,9 @@ free_rbtdb(dns_rbtdb_t *rbtdb, isc_boolean_t log, isc_event_t *event) {
        if (rbtdb->rrsetstats != NULL)
                dns_stats_detach(&rbtdb->rrsetstats);
 
+       if (rbtdb->rpz_cidr != NULL)
+               dns_rpz_cidr_free(&rbtdb->rpz_cidr);
+
        isc_mem_put(rbtdb->common.mctx, rbtdb->node_locks,
                    rbtdb->node_lock_count * sizeof(rbtdb_nodelock_t));
        isc_rwlock_destroy(&rbtdb->tree_lock);
@@ -1488,6 +1493,12 @@ delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node)
 
        switch (node->nsec) {
        case DNS_RBT_NSEC_NORMAL:
+               if (rbtdb->rpz_cidr != NULL) {
+                       dns_fixedname_init(&fname);
+                       name = dns_fixedname_name(&fname);
+                       dns_rbt_fullnamefromnode(node, name);
+                       dns_rpz_cidr_deleteip(rbtdb->rpz_cidr, name);
+               }
                result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
                break;
        case DNS_RBT_NSEC_HAS_NSEC:
@@ -1522,6 +1533,7 @@ delete_node(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node)
                        }
                }
                result = dns_rbt_deletenode(rbtdb->tree, node, ISC_FALSE);
+               dns_rpz_cidr_deleteip(rbtdb->rpz_cidr, name);
                break;
        case DNS_RBT_NSEC_NSEC:
                result = dns_rbt_deletenode(rbtdb->nsec, node, ISC_FALSE);
@@ -2495,6 +2507,15 @@ findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
                node = NULL;
                result = dns_rbt_addnode(rbtdb->tree, name, &node);
                if (result == ISC_R_SUCCESS) {
+                       if (rbtdb->rpz_cidr != NULL) {
+                               dns_fixedname_t fnamef;
+                               dns_name_t *fname;
+
+                               dns_fixedname_init(&fnamef);
+                               fname = dns_fixedname_name(&fnamef);
+                               dns_rbt_fullnamefromnode(node, fname);
+                               dns_rpz_cidr_addip(rbtdb->rpz_cidr, fname);
+                       }
                        dns_rbt_namefromnode(node, &nodename);
 #ifdef DNS_RBT_USEHASH
                        node->locknum = node->hashval % rbtdb->node_lock_count;
@@ -4510,6 +4531,198 @@ find_coveringnsec(rbtdb_search_t *search, dns_dbnode_t **nodep,
        return (result);
 }
 
+/*
+ * Mark a database for response policy rewriting.
+ */
+static void
+get_rpz_enabled(dns_db_t *db, dns_rpz_st_t *st)
+{
+       dns_rbtdb_t *rbtdb;
+
+       rbtdb = (dns_rbtdb_t *)db;
+       REQUIRE(VALID_RBTDB(rbtdb));
+       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       dns_rpz_enabled(rbtdb->rpz_cidr, st);
+       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+}
+
+/*
+ * Search the CDIR block tree of a response policy tree of trees for all of
+ * the IP addresses in an A or AAAA rdataset.
+ * Among the policies for all IPv4 and IPv6 addresses for a name, choose
+ * the longest prefix.  Among those with the longest prefix, the first
+ * configured policy.  Among answers for with the longest prefixes for
+ * two or more IP addresses in the A and AAAA rdatasets the lexically
+ * smallest address.
+ */
+static isc_result_t
+rpz_findips(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
+           dns_zone_t *zone, dns_db_t *db, dns_dbversion_t *version,
+           dns_rdataset_t *ardataset, dns_rpz_st_t *st)
+{
+       dns_rbtdb_t *rbtdb;
+       struct in_addr ina;
+       struct in6_addr in6a;
+       isc_netaddr_t netaddr;
+       dns_fixedname_t selfnamef, qnamef;
+       dns_name_t *selfname, *qname;
+       dns_rbtnode_t *node;
+       dns_rdataset_t zrdataset;
+       dns_rpz_cidr_bits_t prefix;
+       isc_result_t result;
+       dns_rpz_policy_t rpz_policy;
+       dns_ttl_t ttl;
+
+       rbtdb = (dns_rbtdb_t *)db;
+       REQUIRE(VALID_RBTDB(rbtdb));
+       RWLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+
+       if (rbtdb->rpz_cidr == NULL) {
+               RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+               dns_db_detach(&db);
+               dns_zone_detach(&zone);
+               return (ISC_R_UNEXPECTED);
+       }
+
+       dns_fixedname_init(&selfnamef);
+       dns_fixedname_init(&qnamef);
+       selfname = dns_fixedname_name(&selfnamef);
+       qname = dns_fixedname_name(&qnamef);
+
+       for (result = dns_rdataset_first(ardataset);
+            result == ISC_R_SUCCESS;
+            result = dns_rdataset_next(ardataset)) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+               dns_rdataset_current(ardataset, &rdata);
+               switch (rdata.type) {
+               case dns_rdatatype_a:
+                       INSIST(rdata.length == 4);
+                       memcpy(&ina.s_addr, rdata.data, 4);
+                       isc_netaddr_fromin(&netaddr, &ina);
+                       break;
+               case dns_rdatatype_aaaa:
+                       INSIST(rdata.length == 16);
+                       memcpy(in6a.s6_addr, rdata.data, 16);
+                       isc_netaddr_fromin6(&netaddr, &in6a);
+                       break;
+               default:
+                       continue;
+               }
+
+               result = dns_rpz_cidr_find(rbtdb->rpz_cidr, &netaddr, rpz_type,
+                                          selfname, qname, &prefix);
+               if (result != ISC_R_SUCCESS)
+                       continue;
+
+               /*
+                * Choose the policy with the longest matching prefix.
+                * Between policies with the same prefix, choose the first
+                * configured.
+                */
+               if (st->m.policy != DNS_RPZ_POLICY_MISS) {
+                       if (prefix < st->m.prefix)
+                               continue;
+                       if (prefix == st->m.prefix &&
+                           rpz->num > st->m.rpz->num)
+                               continue;
+               }
+
+               /*
+                * We have rpz_st an entry with a prefix at least as long as
+                * the prefix of the entry we had before.  Find the node
+                * corresponding to CDIR tree entry.
+                */
+               node = NULL;
+               result = dns_rbt_findnode(rbtdb->tree, qname, NULL,
+                                         &node, NULL, 0, NULL, NULL);
+               if (result != ISC_R_SUCCESS) {
+                       char namebuf[DNS_NAME_FORMATSIZE];
+
+                       dns_name_format(qname, namebuf, sizeof(namebuf));
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_CACHE, DNS_RPZ_ERROR_LEVEL,
+                                     "rpz_findips findnode(%s): %s",
+                                     namebuf, isc_result_totext(result));
+                       continue;
+               }
+               /*
+                * First look for a simple rewrite of the IP address.
+                * If that fails, look for a CNAME.  If we cannot find
+                * a CNAME or the CNAME is neither of the special forms
+                * "*" or ".", treat it like a real CNAME.
+                */
+               dns_rdataset_init(&zrdataset);
+               result = dns_db_findrdataset(db, node, version, ardataset->type,
+                                            0, 0, &zrdataset, NULL);
+               if (result != ISC_R_SUCCESS)
+                       result = dns_db_findrdataset(db, node, version,
+                                                    dns_rdatatype_cname,
+                                                    0, 0, &zrdataset, NULL);
+               if (result == ISC_R_SUCCESS) {
+                       if (zrdataset.type != dns_rdatatype_cname) {
+                               rpz_policy = DNS_RPZ_POLICY_RECORD;
+                       } else {
+                               rpz_policy = dns_rpz_decode_cname(&zrdataset,
+                                                                 selfname);
+                               if (rpz_policy == DNS_RPZ_POLICY_RECORD)
+                                       result = DNS_R_CNAME;
+                       }
+                       ttl = zrdataset.ttl;
+               } else {
+                       rpz_policy = DNS_RPZ_POLICY_RECORD;
+                       result = DNS_R_NXRRSET;
+                       ttl = DNS_RPZ_TTL_DEFAULT;
+               }
+
+               /*
+                * Use an overriding action specified in the configuration file
+                */
+               if (rpz->policy != DNS_RPZ_POLICY_GIVEN &&
+                   rpz_policy != DNS_RPZ_POLICY_NO_OP)
+                       rpz_policy = rpz->policy;
+
+               /*
+                * We know the new prefix is at least as long as the current.
+                * Prefer the new answer if the new prefix is longer.
+                * Prefer the zone configured first if the prefixes are equal.
+                * With two actions from the same zone, prefer the action
+                * on the "smallest" name.
+                */
+               if (st->m.policy == DNS_RPZ_POLICY_MISS ||
+                   prefix > st->m.prefix ||
+                   rpz->num <= st->m.rpz->num ||
+                   0 > dns_name_compare(qname, st->qname)) {
+                       if (dns_rdataset_isassociated(st->m.rdataset))
+                               dns_rdataset_disassociate(st->m.rdataset);
+                       if (st->m.node != NULL)
+                               dns_db_detachnode(st->m.db, &st->m.node);
+                       if (st->m.db != NULL)
+                               dns_db_detach(&st->m.db);
+                       if (st->m.zone != NULL)
+                               dns_zone_detach(&st->m.zone);
+                       st->m.rpz = rpz;
+                       st->m.type = rpz_type;
+                       st->m.prefix = prefix;
+                       st->m.policy = rpz_policy;
+                       st->m.ttl = ttl;
+                       st->m.result = result;
+                       dns_name_copy(qname, st->qname, NULL);
+                       if (rpz_policy == DNS_RPZ_POLICY_RECORD &&
+                           result != DNS_R_NXRRSET) {
+                               dns_rdataset_clone(&zrdataset,st->m.rdataset);
+                               dns_db_attachnode(db, node, &st->m.node);
+                       }
+                       dns_db_attach(db, &st->m.db);
+                       dns_zone_attach(zone, &st->m.zone);
+               }
+               if (dns_rdataset_isassociated(&zrdataset))
+                       dns_rdataset_disassociate(&zrdataset);
+       }
+
+       RWUNLOCK(&rbtdb->tree_lock, isc_rwlocktype_read);
+       return (ISC_R_SUCCESS);
+}
+
 static isc_result_t
 cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
           dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
@@ -6583,6 +6796,10 @@ loadnode(dns_rbtdb_t *rbtdb, dns_name_t *name, dns_rbtnode_t **nodep,
        dns_rbtnode_t *nsecnode;
 
        noderesult = dns_rbt_addnode(rbtdb->tree, name, nodep);
+
+       if (noderesult == ISC_R_SUCCESS)
+               dns_rpz_cidr_addip(rbtdb->rpz_cidr, name);
+
        if (!hasnsec)
                return (noderesult);
        if (noderesult == ISC_R_EXISTS) {
@@ -6694,7 +6911,7 @@ loading_addrdataset(void *arg, dns_name_t *name, dns_rdataset_t *rdataset) {
        }
        if (result != ISC_R_SUCCESS && result != ISC_R_EXISTS)
                return (result);
-       if (result != ISC_R_EXISTS) {
+       if (result == ISC_R_SUCCESS) {
                dns_name_t foundname;
                dns_name_init(&foundname, NULL);
                dns_rbt_namefromnode(node, &foundname);
@@ -7156,7 +7373,9 @@ static dns_dbmethods_t zone_methods = {
        getsigningtime,
        resigned,
        isdnssec,
-       NULL
+       NULL,
+       get_rpz_enabled,
+       rpz_findips
 };
 
 static dns_dbmethods_t cache_methods = {
@@ -7195,7 +7414,9 @@ static dns_dbmethods_t cache_methods = {
        NULL,
        NULL,
        isdnssec,
-       getrrsetstats
+       getrrsetstats,
+       NULL,
+       NULL
 };
 
 isc_result_t
@@ -7377,6 +7598,22 @@ dns_rbtdb_create
                return (result);
        }
 
+       /*
+        * Get ready for response policy IP address searching if at least one
+        * zone has been configured as a response policy zone and this
+        * is not a cache zone.
+        * It would be better to know that this database is for a policy
+        * zone named for a view, but that would require knowledge from
+        * above such as an argv[] set from data in the zone.
+        */
+       if (type == dns_dbtype_zone && !dns_name_equal(origin, dns_rootname)) {
+               result = dns_rpz_new_cidr(mctx, origin, &rbtdb->rpz_cidr);
+               if (result != ISC_R_SUCCESS) {
+                       free_rbtdb(rbtdb, ISC_FALSE, NULL);
+                       return (result);
+               }
+       }
+
        /*
         * In order to set the node callback bit correctly in zone databases,
         * we need to know if the node has the origin name of the zone.
diff --git a/lib/dns/rpz.c b/lib/dns/rpz.c
new file mode 100644 (file)
index 0000000..43d369d
--- /dev/null
@@ -0,0 +1,1166 @@
+/*
+ * Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+ * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* $Id: rpz.c,v 1.2 2011/01/13 01:59:27 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/net.h>
+#include <isc/netaddr.h>
+#include <isc/stdlib.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/result.h>
+#include <dns/rpz.h>
+#include <dns/view.h>
+
+
+/*
+ * Parallel radix trees for databases of response policy IP addresses
+ *
+ * The radix or Patricia trees are somewhat specialized to handle response
+ * policy addresses by representing the two test of IP IP addresses and name
+ * server IP addresses in a single tree.
+ *
+ * Each leaf indicates that an IP address is listed in the IP address or the
+ * name server IP address policy sub-zone (or both) of the corresponding
+ * response response zone.  The policy data such as a CNAME or an A record
+ * is kept in the policy zone.  After an IP address has been found in a radix
+ * tree, the node in the policy zone's database is found by converting
+ * the IP address to a domain name in a canonical form.
+ *
+ * The response policy zone canonical form of IPv6 addresses is one of:
+ *     prefix.W.W.W.W.W.W.W.W
+ *     prefix.WORDS.zz
+ *     prefix.WORDS.zz.WORDS
+ *     prefix.zz.WORDS
+ *  where
+ *     prefix  is the prefix length of the IPv6 address between 1 and 128
+ *     W       is a number between 0 and 65535
+ *     WORDS   is one or more numbers W separated with "."
+ *     zz      corresponds to :: in the standard IPv6 text representation
+ *
+ * The canonical form of IPv4 addresses is:
+ *     prefix.B.B.B.B
+ *  where
+ *     prefix  is the prefix length of the address between 1 and 32
+ *     B       is a number between 0 and 255
+ *
+ * IPv4 addresses are distinguished from IPv6 addresses by having
+ * 5 labels all of which are numbers, and a prefix between 1 and 32.
+ */
+
+
+/*
+ * Use a private definition of IPv6 addresses because s6_addr32 is not
+ * always defined and our IPv6 addresses are in non-standard byte order
+ */
+typedef isc_uint32_t           dns_rpz_cidr_word_t;
+#define DNS_RPZ_CIDR_WORD_BITS ((int)sizeof(dns_rpz_cidr_word_t)*8)
+#define DNS_RPZ_CIDR_KEY_BITS  ((int)sizeof(dns_rpz_cidr_key_t)*8)
+#define DNS_RPZ_CIDR_WORDS     (128/DNS_RPZ_CIDR_WORD_BITS)
+typedef struct {
+       dns_rpz_cidr_word_t     w[DNS_RPZ_CIDR_WORDS];
+} dns_rpz_cidr_key_t;
+
+#define ADDR_V4MAPPED          0xffff
+
+#define DNS_RPZ_WORD_MASK(b)                           \
+       ((b) == 0 ? (dns_rpz_cidr_word_t)(-1)           \
+                 : ((dns_rpz_cidr_word_t)(-1)          \
+                   << (DNS_RPZ_CIDR_WORD_BITS - (b))))
+
+#define DNS_RPZ_IP_BIT(ip, bitno) \
+       (1 & ((ip)->w[(bitno)/DNS_RPZ_CIDR_WORD_BITS] >> \
+           (DNS_RPZ_CIDR_WORD_BITS - 1 - ((bitno) % DNS_RPZ_CIDR_WORD_BITS))))
+
+typedef struct dns_rpz_cidr_node       dns_rpz_cidr_node_t;
+typedef isc_uint8_t                    dns_rpz_cidr_flags_t;
+struct dns_rpz_cidr_node {
+       dns_rpz_cidr_node_t             *parent;
+       dns_rpz_cidr_node_t             *child[2];
+       dns_rpz_cidr_key_t              ip;
+       dns_rpz_cidr_bits_t             bits;
+       dns_rpz_cidr_flags_t            flags;
+#define        DNS_RPZ_CIDR_FG_IP       0x01   /* has IP data or is parent of IP */
+#define        DNS_RPZ_CIDR_FG_IP_DATA  0x02   /* has IP data */
+#define        DNS_RPZ_CIDR_FG_NSIPv4   0x04   /* has or is parent of NSIPv4 data */
+#define        DNS_RPZ_CIDR_FG_NSIPv6   0x08   /* has or is parent of NSIPv6 data */
+#define        DNS_RPZ_CIDR_FG_NSIP_DATA 0x10  /* has NSIP data */
+};
+
+struct dns_rpz_cidr {
+       isc_mem_t               *mctx;
+       isc_boolean_t           had_nsdname;
+       dns_rpz_cidr_node_t     *root;
+       dns_name_t              ip_name;        /* RPZ_IP_ZONE.LOCALHOST. */
+       dns_name_t              nsip_name;      /* RPZ_NSIP_ZONE.LOCALHOST. */
+       dns_name_t              nsdname_name;   /* RPZ_NSDNAME_ZONE.LOCALHOST */
+};
+
+
+static isc_boolean_t           have_rpz_zones = ISC_FALSE;
+
+
+const char *
+dns_rpz_type2str(dns_rpz_type_t type)
+{
+       switch (type) {
+       case DNS_RPZ_TYPE_QNAME:
+               return ("QNAME");
+       case DNS_RPZ_TYPE_IP:
+               return ("IP");
+       case DNS_RPZ_TYPE_NSIP:
+               return ("NSIP");
+       case DNS_RPZ_TYPE_NSDNAME:
+               return ("NSDNAME");
+       case DNS_RPZ_TYPE_BAD:
+               break;
+       }
+       FATAL_ERROR(__FILE__, __LINE__,
+                   "impossible response policy zone type %d", type);
+       return ("impossible");
+}
+
+
+
+dns_rpz_policy_t
+dns_rpz_str2policy(const char *str)
+{
+       if (str == NULL)
+               return (DNS_RPZ_POLICY_ERROR);
+       if (!strcasecmp(str, "given"))
+               return (DNS_RPZ_POLICY_GIVEN);
+       if (!strcasecmp(str, "no-op"))
+               return (DNS_RPZ_POLICY_NO_OP);
+       if (!strcasecmp(str, "nxdomain"))
+               return (DNS_RPZ_POLICY_NXDOMAIN);
+       if (!strcasecmp(str, "nodata"))
+               return (DNS_RPZ_POLICY_NODATA);
+       if (!strcasecmp(str, "cname"))
+               return (DNS_RPZ_POLICY_CNAME);
+       return (DNS_RPZ_POLICY_ERROR);
+}
+
+
+
+/*
+ * Free the radix tree of a response policy database.
+ */
+void
+dns_rpz_cidr_free(dns_rpz_cidr_t **cidrp) {
+       dns_rpz_cidr_node_t *cur, *child, *parent;
+       dns_rpz_cidr_t *cidr;
+
+       REQUIRE(cidrp != NULL);
+
+       cidr = *cidrp;
+       if (cidr == NULL)
+               return;
+
+       cur = cidr->root;
+       while (cur != NULL) {
+               /* Depth first. */
+               child = cur->child[0];
+               if (child != NULL) {
+                       cur = child;
+                       continue;
+               }
+               child = cur->child[1];
+               if (child != NULL) {
+                       cur = child;
+                       continue;
+               }
+
+               /* Delete this leaf and go up. */
+               parent = cur->parent;
+               if (parent == NULL)
+                       cidr->root = NULL;
+               else
+                       parent->child[parent->child[1] == cur] = NULL;
+               isc_mem_put(cidr->mctx, cur, sizeof(*cur));
+               cur = parent;
+       }
+
+       dns_name_free(&cidr->ip_name, cidr->mctx);
+       dns_name_free(&cidr->nsip_name, cidr->mctx);
+       dns_name_free(&cidr->nsdname_name, cidr->mctx);
+       isc_mem_put(cidr->mctx, cidr, sizeof(*cidr));
+       *cidrp = NULL;
+}
+
+
+
+/*
+ * Forget a view's list of policy zones.
+ */
+void
+dns_rpz_view_destroy(dns_view_t *view) {
+       dns_rpz_zone_t *zone;
+
+       REQUIRE(view != NULL);
+
+       while (!ISC_LIST_EMPTY(view->rpz_zones)) {
+               zone = ISC_LIST_HEAD(view->rpz_zones);
+               ISC_LIST_UNLINK(view->rpz_zones, zone, link);
+               if (dns_name_dynamic(&zone->origin))
+                       dns_name_free(&zone->origin, view->mctx);
+               if (dns_name_dynamic(&zone->nsdname))
+                       dns_name_free(&zone->nsdname, view->mctx);
+               if (dns_name_dynamic(&zone->cname))
+                       dns_name_free(&zone->cname, view->mctx);
+               isc_mem_put(view->mctx, zone, sizeof(*zone));
+       }
+}
+
+/*
+ * Note that we have at least one response policy zone.
+ * It would be better for something to tell the rbtdb code that the
+ * zone is in at least one view's list of policy zones.
+ */
+void
+dns_rpz_set_need(isc_boolean_t need)
+{
+       have_rpz_zones = need;
+}
+
+
+isc_boolean_t
+dns_rpz_needed(void)
+{
+       return (have_rpz_zones);
+}
+
+
+
+/*
+ * Start a new radix tree for a response policy zone.
+ */
+isc_result_t
+dns_rpz_new_cidr(isc_mem_t *mctx, dns_name_t *origin,
+                dns_rpz_cidr_t **rbtdb_cidr)
+{
+       isc_result_t result;
+       dns_rpz_cidr_t *cidr;
+
+       REQUIRE(rbtdb_cidr != NULL && *rbtdb_cidr == NULL);
+
+       /*
+        * Only if there is at least one response policy zone.
+        */
+       if (!have_rpz_zones)
+               return (ISC_R_SUCCESS);
+
+       cidr = isc_mem_get(mctx, sizeof(*cidr));
+       if (cidr == NULL)
+               return (ISC_R_NOMEMORY);
+       memset(cidr, 0, sizeof(*cidr));
+       cidr->mctx = mctx;
+
+       dns_name_init(&cidr->ip_name, NULL);
+       result = dns_name_fromstring2(&cidr->ip_name, DNS_RPZ_IP_ZONE, origin,
+                                     DNS_NAME_DOWNCASE, mctx);
+       if (result != ISC_R_SUCCESS) {
+               isc_mem_put(mctx, cidr, sizeof(*cidr));
+               return (result);
+       }
+
+       dns_name_init(&cidr->nsip_name, NULL);
+       result = dns_name_fromstring2(&cidr->nsip_name, DNS_RPZ_NSIP_ZONE,
+                                     origin, DNS_NAME_DOWNCASE, mctx);
+       if (result != ISC_R_SUCCESS) {
+               dns_name_free(&cidr->ip_name, mctx);
+               isc_mem_put(mctx, cidr, sizeof(*cidr));
+               return (result);
+       }
+
+       dns_name_init(&cidr->nsdname_name, NULL);
+       result = dns_name_fromstring2(&cidr->nsdname_name, DNS_RPZ_NSDNAME_ZONE,
+                                     origin, DNS_NAME_DOWNCASE, mctx);
+       if (result != ISC_R_SUCCESS) {
+               dns_name_free(&cidr->nsip_name, mctx);
+               dns_name_free(&cidr->ip_name, mctx);
+               isc_mem_put(mctx, cidr, sizeof(*cidr));
+               return (result);
+       }
+
+       *rbtdb_cidr = cidr;
+       return (ISC_R_SUCCESS);
+}
+
+
+/*
+ * See if a policy zone has IP, NSIP, or NSDNAME rules or records.
+ */
+void
+dns_rpz_enabled(dns_rpz_cidr_t *cidr, dns_rpz_st_t *st) {
+       if (cidr->root != NULL &&
+           (cidr->root->flags & DNS_RPZ_CIDR_FG_IP) != 0)
+               st->state |= DNS_RPZ_HAVE_IP;
+       if (cidr->root != NULL &&
+           (cidr->root->flags & DNS_RPZ_CIDR_FG_NSIPv4) != 0)
+               st->state |= DNS_RPZ_HAVE_NSIPv4;
+       if (cidr->root != NULL &&
+           (cidr->root->flags & DNS_RPZ_CIDR_FG_NSIPv6) != 0)
+               st->state |= DNS_RPZ_HAVE_NSIPv6;
+       if (cidr->had_nsdname)
+               st->state |= DNS_RPZ_HAD_NSDNAME;
+}
+
+static inline dns_rpz_cidr_flags_t
+get_flags(const dns_rpz_cidr_key_t *ip, dns_rpz_cidr_bits_t prefix,
+       dns_rpz_type_t rpz_type)
+{
+       if (rpz_type == DNS_RPZ_TYPE_NSIP) {
+               if (prefix >= 96 &&
+                   ip->w[0] == 0 && ip->w[1] == 0 &&
+                   ip->w[2] == ADDR_V4MAPPED)
+                       return (DNS_RPZ_CIDR_FG_NSIP_DATA |
+                               DNS_RPZ_CIDR_FG_NSIPv4);
+               else
+                       return (DNS_RPZ_CIDR_FG_NSIP_DATA |
+                               DNS_RPZ_CIDR_FG_NSIPv6);
+       } else {
+               return (DNS_RPZ_CIDR_FG_IP | DNS_RPZ_CIDR_FG_IP_DATA);
+       }
+}
+
+
+
+/*
+ * Mark a node as having IP or NSIP data and all of its parents
+ * as members of the IP or NSIP tree.
+ */
+static void
+set_node_flags(dns_rpz_cidr_node_t *node, dns_rpz_type_t rpz_type) {
+       dns_rpz_cidr_flags_t flags;
+
+       flags = get_flags(&node->ip, node->bits, rpz_type);
+       node->flags |= flags;
+       flags &= ~(DNS_RPZ_CIDR_FG_NSIP_DATA | DNS_RPZ_CIDR_FG_IP_DATA);
+       for (;;) {
+               node = node->parent;
+               if (node == NULL)
+                       return;
+               node->flags |= flags;
+       }
+}
+
+
+
+/*
+ * Make a radix tree node.
+ */
+static dns_rpz_cidr_node_t *
+new_node(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *ip,
+        dns_rpz_cidr_bits_t bits, dns_rpz_cidr_flags_t flags)
+{
+       dns_rpz_cidr_node_t *node;
+       int i, words, wlen;
+
+       node = isc_mem_get(cidr->mctx, sizeof(*node));
+       if (node == NULL)
+               return (NULL);
+       memset(node, 0, sizeof(*node));
+
+       node->flags = flags & ~(DNS_RPZ_CIDR_FG_IP_DATA |
+                               DNS_RPZ_CIDR_FG_NSIP_DATA);
+
+       node->bits = bits;
+       words = bits / DNS_RPZ_CIDR_WORD_BITS;
+       wlen = bits % DNS_RPZ_CIDR_WORD_BITS;
+       i = 0;
+       while (i < words) {
+               node->ip.w[i] = ip->w[i];
+               ++i;
+       }
+       if (wlen != 0) {
+               node->ip.w[i] = ip->w[i] & DNS_RPZ_WORD_MASK(wlen);
+               ++i;
+       }
+       while (i < DNS_RPZ_CIDR_WORDS)
+               node->ip.w[i++] = 0;
+
+       return (node);
+}
+
+
+
+static void
+badname(int level, dns_name_t *name, const char *comment)
+{
+       char printname[DNS_NAME_FORMATSIZE];
+
+       if (isc_log_wouldlog(dns_lctx, level)) {
+               dns_name_format(name, printname, sizeof(printname));
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_RBTDB, level,
+                             "invalid response policy name \"%s\"%s",
+                             printname, comment);
+       }
+}
+
+
+
+/*
+ * Convert an IP address from radix tree binary (host byte order) to
+ * to its canonical response policy domain name and its name in the
+ * policy zone.
+ */
+static isc_result_t
+ip2name(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip,
+       dns_rpz_cidr_bits_t tgt_prefix, dns_rpz_type_t type,
+       dns_name_t *canon_name, dns_name_t *search_name)
+{
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+       int w[DNS_RPZ_CIDR_WORDS*2];
+       char str[1+8+1+INET6_ADDRSTRLEN+1];
+       isc_buffer_t buffer;
+       dns_name_t *name;
+       isc_result_t result;
+       isc_boolean_t zeros;
+       int i, n, len;
+
+       if (tgt_prefix > 96 &&
+           tgt_ip->w[0] == 0 &&
+           tgt_ip->w[1] == 0 &&
+           tgt_ip->w[2] == ADDR_V4MAPPED) {
+               len = snprintf(str, sizeof(str), "%d.%d.%d.%d.%d",
+                              tgt_prefix - 96,
+                              tgt_ip->w[3] & 0xff,
+                              (tgt_ip->w[3]>>8) & 0xff,
+                              (tgt_ip->w[3]>>16) & 0xff,
+                              (tgt_ip->w[3]>>24) & 0xff);
+               if (len == -1 || len > (int)sizeof(str))
+                       return (ISC_R_FAILURE);
+       } else {
+               for (i = 0; i < DNS_RPZ_CIDR_WORDS; i++) {
+                       w[i*2+1] = ((tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] >> 16)
+                                   & 0xffff);
+                       w[i*2] = tgt_ip->w[DNS_RPZ_CIDR_WORDS-1-i] & 0xffff;
+               }
+               zeros = ISC_FALSE;
+               len = snprintf(str, sizeof(str), "%d", tgt_prefix);
+               if (len == -1)
+                       return (ISC_R_FAILURE);
+               i = 0;
+               while (i < DNS_RPZ_CIDR_WORDS * 2) {
+                       if (w[i] != 0 || zeros
+                           || i >= DNS_RPZ_CIDR_WORDS * 2 - 1
+                           || w[i+1] != 0) {
+                               INSIST((size_t)len <= sizeof(str));
+                               n = snprintf(&str[len], sizeof(str) - len,
+                                            ".%x", w[i++]);
+                               if (n < 0)
+                                       return (ISC_R_FAILURE);
+                               len += n;
+                       } else {
+                               zeros = ISC_TRUE;
+                               INSIST((size_t)len <= sizeof(str));
+                               n = snprintf(&str[len], sizeof(str) - len,
+                                            ".zz");
+                               if (n < 0)
+                                       return (ISC_R_FAILURE);
+                               len += n;
+                               i += 2;
+                               while (i < DNS_RPZ_CIDR_WORDS * 2 && w[i] == 0)
+                                       ++i;
+                       }
+                       if (len > (int)sizeof(str))
+                               return (ISC_R_FAILURE);
+               }
+       }
+
+       if (canon_name != NULL) {
+               isc__buffer_init(&buffer, str, sizeof(str));
+               isc__buffer_add(&buffer, len);
+               result = dns_name_fromtext(canon_name, &buffer,
+                                          dns_rootname, 0, NULL);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+       }
+       if (search_name != NULL) {
+               isc__buffer_init(&buffer, str, sizeof(str));
+               isc__buffer_add(&buffer, len);
+               if (type == DNS_RPZ_TYPE_NSIP)
+                       name = &cidr->nsip_name;
+               else
+                       name = &cidr->ip_name;
+               result = dns_name_fromtext(search_name, &buffer, name, 0, NULL);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+       }
+       return (ISC_R_SUCCESS);
+}
+
+
+
+/*
+ * Decide which kind of IP address response policy zone a name is in.
+ */
+static dns_rpz_type_t
+set_type(dns_rpz_cidr_t *cidr, dns_name_t *name) {
+
+       if (dns_name_issubdomain(name, &cidr->ip_name))
+               return (DNS_RPZ_TYPE_IP);
+
+       /*
+        * Require `./configure --enable-rpz-nsip` and nsdname
+        * until consistency problems are resolved.
+        */
+#ifdef ENABLE_RPZ_NSIP
+       if (dns_name_issubdomain(name, &cidr->nsip_name))
+               return (DNS_RPZ_TYPE_NSIP);
+#endif
+
+#ifdef ENABLE_RPZ_NSDNAME
+       if (dns_name_issubdomain(name, &cidr->nsdname_name))
+               return (DNS_RPZ_TYPE_NSDNAME);
+#endif
+
+       return (DNS_RPZ_TYPE_QNAME);
+}
+
+
+
+/*
+ * Convert an IP address from canonical response policy domain name form
+ * to radix tree binary (host byte order).
+ */
+static isc_result_t
+name2ipkey(dns_rpz_cidr_t *cidr, int level, dns_name_t *src_name,
+          dns_rpz_type_t type, dns_rpz_cidr_key_t *tgt_ip,
+          dns_rpz_cidr_bits_t *tgt_prefix)
+{
+       isc_buffer_t buffer;
+       unsigned char data[DNS_NAME_MAXWIRE+1];
+       dns_fixedname_t fname;
+       dns_name_t *name;
+       const char *cp, *end;
+       char *cp2;
+       int ip_labels;
+       dns_rpz_cidr_bits_t bits;
+       unsigned long prefix, l;
+       int i;
+
+       /*
+        * Need at least enough labels for the shortest name,
+        * :: or 128.*.RPZ_x_ZONE.rpz.LOCALHOST.
+        */
+       ip_labels = dns_name_countlabels(src_name);
+       ip_labels -= dns_name_countlabels(&cidr->ip_name);
+       ip_labels--;
+       if (ip_labels < 1) {
+               badname(level, src_name, ", too short");
+               return (ISC_R_FAILURE);
+       }
+
+       /*
+        * Get text for the IP address without RPZ_x_ZONE.rpz.LOCALHOST.
+        */
+       dns_fixedname_init(&fname);
+       name = dns_fixedname_name(&fname);
+       dns_name_split(src_name, dns_name_countlabels(&cidr->ip_name),
+                      name, NULL);
+       isc_buffer_init(&buffer, data, sizeof(data));
+       dns_name_totext(name, ISC_TRUE, &buffer);
+       isc_buffer_putuint8(&buffer, '\0');
+       cp = isc_buffer_base(&buffer);
+
+       prefix = strtoul(cp, &cp2, 10);
+       if (prefix < 1 || prefix > 128 || *cp2 != '.') {
+               badname(level, src_name, ", bad prefix length");
+               return (ISC_R_FAILURE);
+       }
+       cp = cp2+1;
+
+       end = isc_buffer_used(&buffer);
+       if (ip_labels == 4 && !strchr(cp, 'z')) {
+               /*
+                * Convert an IPv4 address
+                * from the form "prefix.w.z.y.x"
+                */
+               if (prefix > 32) {
+                       badname(level, src_name, "; bad IPv4 prefix length");
+                       return (ISC_R_FAILURE);
+               }
+               prefix += 96;
+               *tgt_prefix = prefix;
+               tgt_ip->w[0] = 0;
+               tgt_ip->w[1] = 0;
+               tgt_ip->w[2] = ADDR_V4MAPPED;
+               tgt_ip->w[3] = 0;
+               for (i = 0; i < 32; i += 8) {
+                       l = strtoul(cp, &cp2, 10);
+                       if (l > 255 || (*cp2 != '.' && *cp2 != '\0')) {
+                               badname(level, src_name, "; bad IPv4 address");
+                               return (ISC_R_FAILURE);
+                       }
+                       tgt_ip->w[3] |= l << i;
+                       cp = cp2 + 1;
+               }
+       } else {
+               /*
+                * Convert a text IPv6 address.
+                */
+               *tgt_prefix = prefix;
+               for (i = 0;
+                    ip_labels > 0 && i < DNS_RPZ_CIDR_WORDS * 2;
+                    ip_labels--) {
+                       if (cp[0] == 'z' && cp[1] == 'z' &&
+                           (cp[2] == '.' || cp[2] == '\0') &&
+                           i <= 6) {
+                               do {
+                                       if ((i & 1) == 0)
+                                           tgt_ip->w[3-i/2] = 0;
+                                       ++i;
+                               } while (ip_labels + i <= 8);
+                               cp += 3;
+                       } else {
+                               l = strtoul(cp, &cp2, 16);
+                               if (l > 0xffff ||
+                                   (*cp2 != '.' && *cp2 != '\0')) {
+                                       badname(level, src_name, "");
+                                       return (ISC_R_FAILURE);
+                               }
+                               if ((i & 1) == 0)
+                                       tgt_ip->w[3-i/2] = l;
+                               else
+                                       tgt_ip->w[3-i/2] |= l << 16;
+                               i++;
+                               cp = cp2 + 1;
+                       }
+               }
+       }
+       if (cp != end) {
+               badname(level, src_name, "");
+               return (ISC_R_FAILURE);
+       }
+
+       /*
+        * Check for 1s after the prefix length.
+        */
+       bits = prefix;
+       while (bits < DNS_RPZ_CIDR_KEY_BITS) {
+               dns_rpz_cidr_word_t aword;
+
+               i = bits % DNS_RPZ_CIDR_WORD_BITS;
+               aword = tgt_ip->w[bits / DNS_RPZ_CIDR_WORD_BITS];
+               if ((aword & ~DNS_RPZ_WORD_MASK(i)) != 0) {
+                       badname(level, src_name, "; wrong prefix length");
+                       return (ISC_R_FAILURE);
+               }
+               bits -= i;
+               bits += DNS_RPZ_CIDR_WORD_BITS;
+       }
+
+       /*
+        * Convert the IPv6 address back to a canonical policy domain name
+        * to ensure that it is in canonical form.
+        */
+       if (ISC_R_SUCCESS != ip2name(cidr, tgt_ip, prefix, type, NULL, name) ||
+           !dns_name_equal(src_name, name)) {
+               badname(level, src_name, "; not canonical");
+               return (ISC_R_FAILURE);
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+
+
+/*
+ * find first differing bit
+ */
+static int
+ffbit(dns_rpz_cidr_word_t w) {
+       int bit;
+
+       if (w == 0)
+               return (DNS_RPZ_CIDR_WORD_BITS);
+       for (bit = 0; (w & (1 << (DNS_RPZ_CIDR_WORD_BITS-1))) == 0; bit++)
+               w <<= 1;
+       return (bit);
+}
+
+
+
+/*
+ * find the first differing bit in two keys
+ */
+static int
+diff_keys(const dns_rpz_cidr_key_t *key1, dns_rpz_cidr_bits_t bits1,
+         const dns_rpz_cidr_key_t *key2, dns_rpz_cidr_bits_t bits2)
+{
+       dns_rpz_cidr_word_t delta;
+       dns_rpz_cidr_bits_t maxbit, bit;
+       int i;
+
+       maxbit = ISC_MIN(bits1, bits2);
+
+       /*
+        * find the first differing words
+        */
+       for (i = 0, bit = 0;
+            bit <= maxbit;
+            i++, bit += DNS_RPZ_CIDR_WORD_BITS) {
+               delta = key1->w[i] ^ key2->w[i];
+               if (delta != 0) {
+                       bit += ffbit(delta);
+                       break;
+               }
+       }
+       return (ISC_MIN(bit, maxbit));
+}
+
+
+
+/*
+ * Search a radix tree for an IP address for ordinary lookup
+ *     or for a CIDR block adding or deleting an entry
+ * The tree read (for simple search) or write lock must be held by the caller.
+ *
+ * return ISC_R_SUCCESS, ISC_R_NOTFOUND, DNS_R_PARTIALMATCH, ISC_R_EXISTS,
+ *     ISC_R_NOMEMORY
+ */
+static isc_result_t
+search(dns_rpz_cidr_t *cidr, const dns_rpz_cidr_key_t *tgt_ip,
+       dns_rpz_cidr_bits_t tgt_prefix, dns_rpz_type_t type,
+       isc_boolean_t create,
+       dns_rpz_cidr_node_t **found)            /* NULL or longest match node */
+{
+       dns_rpz_cidr_node_t *cur, *parent, *child, *new_parent, *sibling;
+       int cur_num, child_num;
+       dns_rpz_cidr_bits_t dbit;
+       dns_rpz_cidr_flags_t flags, data_flag;
+       isc_result_t find_result;
+
+       flags = get_flags(tgt_ip, tgt_prefix, type);
+       data_flag = flags & (DNS_RPZ_CIDR_FG_IP_DATA |
+                            DNS_RPZ_CIDR_FG_NSIP_DATA);
+
+       find_result = ISC_R_NOTFOUND;
+       if (found != NULL)
+               *found = NULL;
+       cur = cidr->root;
+       parent = NULL;
+       cur_num = 0;
+       for (;;) {
+               if (cur == NULL) {
+                       /*
+                        * No child so we cannot go down.  Fail or
+                        * add the target as a child of the current parent.
+                        */
+                       if (!create)
+                               return (find_result);
+                       child = new_node(cidr, tgt_ip, tgt_prefix, 0);
+                       if (child == NULL)
+                               return (ISC_R_NOMEMORY);
+                       if (parent == NULL)
+                               cidr->root = child;
+                       else
+                               parent->child[cur_num] = child;
+                       child->parent = parent;
+                       set_node_flags(child, type);
+                       if (found != NULL)
+                               *found = cur;
+                       return (ISC_R_SUCCESS);
+               }
+
+               /*
+                * Pretend a node not in the correct tree does not exist
+                * if we are not adding to the tree,
+                * If we are adding, then continue down to eventually
+                * add a node and mark/put this node in the correct tree.
+                */
+               if ((cur->flags & flags) == 0 && !create)
+                       return (find_result);
+
+               dbit = diff_keys(tgt_ip, tgt_prefix, &cur->ip, cur->bits);
+               /*
+                * dbit <= tgt_prefix and dbit <= cur->bits always.
+                * We are finished searching if we matched all of the target.
+                */
+               if (dbit == tgt_prefix) {
+                       if (tgt_prefix == cur->bits) {
+                               /*
+                                * The current node matches the target exactly.
+                                * It is the answer if it has data.
+                                */
+                               if ((cur->flags & data_flag) != 0) {
+                                       if (create)
+                                               return (ISC_R_EXISTS);
+                                       if (found != NULL)
+                                               *found = cur;
+                                       return (ISC_R_SUCCESS);
+                               } else if (create) {
+                                       /*
+                                        * The node had no data but does now.
+                                        */
+                                       set_node_flags(cur, type);
+                                       if (found != NULL)
+                                               *found = cur;
+                                       return (ISC_R_SUCCESS);
+                               }
+                               return (find_result);
+                       }
+
+                       /*
+                        * We know tgt_prefix < cur_bits which means that
+                        * the target is shorter than the current node.
+                        * Add the target as the current node's parent.
+                        */
+                       if (!create)
+                               return (find_result);
+
+                       new_parent = new_node(cidr, tgt_ip, tgt_prefix,
+                                             cur->flags);
+                       if (new_parent == NULL)
+                               return (ISC_R_NOMEMORY);
+                       new_parent->parent = parent;
+                       if (parent == NULL)
+                               cidr->root = new_parent;
+                       else
+                               parent->child[cur_num] = new_parent;
+                       child_num = DNS_RPZ_IP_BIT(&cur->ip, tgt_prefix+1);
+                       new_parent->child[child_num] = cur;
+                       cur->parent = new_parent;
+                       set_node_flags(new_parent, type);
+                       if (found != NULL)
+                               *found = new_parent;
+                       return (ISC_R_SUCCESS);
+               }
+
+               if (dbit == cur->bits) {
+                       /*
+                        * We have a partial match by matching of all of the
+                        * current node but only part of the target.
+                        * Try to go down.
+                        */
+                       if ((cur->flags & data_flag) != 0) {
+                               find_result = DNS_R_PARTIALMATCH;
+                               if (found != NULL)
+                                       *found = cur;
+                       }
+
+                       parent = cur;
+                       cur_num = DNS_RPZ_IP_BIT(tgt_ip, dbit);
+                       cur = cur->child[cur_num];
+                       continue;
+               }
+
+
+               /*
+                * dbit < tgt_prefix and dbit < cur->bits,
+                * so we failed to match both the target and the current node.
+                * Insert a fork of a parent above the current node and
+                * add the target as a sibling of the current node
+                */
+               if (!create)
+                       return (find_result);
+
+               sibling = new_node(cidr, tgt_ip, tgt_prefix, 0);
+               if (sibling == NULL)
+                       return (ISC_R_NOMEMORY);
+               new_parent = new_node(cidr, tgt_ip, dbit, cur->flags);
+               if (new_parent == NULL) {
+                       isc_mem_put(cidr->mctx, sibling, sizeof(*sibling));
+                       return (ISC_R_NOMEMORY);
+               }
+               new_parent->parent = parent;
+               if (parent == NULL)
+                       cidr->root = new_parent;
+               else
+                       parent->child[cur_num] = new_parent;
+               child_num = DNS_RPZ_IP_BIT(tgt_ip, dbit);
+               new_parent->child[child_num] = sibling;
+               new_parent->child[1-child_num] = cur;
+               cur->parent = new_parent;
+               sibling->parent = new_parent;
+               set_node_flags(sibling, type);
+               if (found != NULL)
+                       *found = sibling;
+               return (ISC_R_SUCCESS);
+       }
+}
+
+
+
+/*
+ * Add an IP address to the radix tree of a response policy database.
+ *     The tree write lock must be held by the caller.
+ */
+void
+dns_rpz_cidr_addip(dns_rpz_cidr_t *cidr, dns_name_t *name)
+{
+       dns_rpz_cidr_key_t tgt_ip;
+       dns_rpz_cidr_bits_t tgt_prefix;
+       dns_rpz_type_t type;
+
+       if (cidr == NULL)
+               return;
+
+       /*
+        * no worries if the new name is not an IP address
+        */
+       type = set_type(cidr, name);
+       switch (type) {
+       case DNS_RPZ_TYPE_IP:
+       case DNS_RPZ_TYPE_NSIP:
+               break;
+       case DNS_RPZ_TYPE_NSDNAME:
+               cidr->had_nsdname = ISC_TRUE;
+               return;
+       case DNS_RPZ_TYPE_QNAME:
+       case DNS_RPZ_TYPE_BAD:
+               return;
+       }
+       if (ISC_R_SUCCESS != name2ipkey(cidr, DNS_RPZ_ERROR_LEVEL, name,
+                                       type, &tgt_ip, &tgt_prefix))
+               return;
+
+       if (ISC_R_EXISTS == search(cidr, &tgt_ip, tgt_prefix, type,
+                                  ISC_TRUE, NULL) &&
+           isc_log_wouldlog(dns_lctx, DNS_RPZ_ERROR_LEVEL)) {
+               char printname[DNS_NAME_FORMATSIZE];
+
+               dns_name_format(name, printname, sizeof(printname));
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                             "duplicate response policy name \"%s\"",
+                             printname);
+       }
+}
+
+
+
+/*
+ * Delete an IP address from the radix tree of a response policy database.
+ *     The tree write lock must be held by the caller.
+ */
+void
+dns_rpz_cidr_deleteip(dns_rpz_cidr_t *cidr, dns_name_t *name) {
+       dns_rpz_cidr_key_t tgt_ip;
+       dns_rpz_cidr_bits_t tgt_prefix;
+       dns_rpz_type_t type;
+       dns_rpz_cidr_node_t *tgt = NULL, *parent, *child;
+       dns_rpz_cidr_flags_t flags, data_flag;
+
+       if (cidr == NULL)
+               return;
+
+       /*
+        * Decide which kind of policy zone IP address it is, if either
+        * and then find its node.
+        */
+       type = set_type(cidr, name);
+       switch (type) {
+       case DNS_RPZ_TYPE_IP:
+       case DNS_RPZ_TYPE_NSIP:
+               break;
+       case DNS_RPZ_TYPE_NSDNAME:
+               /*
+                * We cannot easily count nsdnames because
+                * internal rbt nodes get deleted.
+                */
+               return;
+       case DNS_RPZ_TYPE_QNAME:
+       case DNS_RPZ_TYPE_BAD:
+               return;
+       }
+
+       /*
+        * Do not get excited about the deletion of interior rbt nodes.
+        */
+       if (ISC_R_SUCCESS != name2ipkey(cidr, DNS_RPZ_DEBUG_LEVEL2, name,
+                                       type, &tgt_ip, &tgt_prefix))
+               return;
+       if (ISC_R_SUCCESS != search(cidr, &tgt_ip, tgt_prefix, type,
+                                   ISC_FALSE, &tgt)) {
+               if (isc_log_wouldlog(dns_lctx, DNS_RPZ_ERROR_LEVEL)) {
+                       char printname[DNS_NAME_FORMATSIZE];
+
+                       dns_name_format(name, printname, sizeof(printname));
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_RBTDB, DNS_RPZ_ERROR_LEVEL,
+                                     "missing response policy node \"%s\"",
+                                     printname);
+               }
+               return;
+       }
+
+       /*
+        * Mark the node and its parents to reflect the deleted IP address.
+        */
+       flags = get_flags(&tgt_ip, tgt_prefix, type);
+       data_flag = flags & (DNS_RPZ_CIDR_FG_IP_DATA |
+                             DNS_RPZ_CIDR_FG_NSIP_DATA);
+       tgt->flags &= ~data_flag;
+       for (parent = tgt; parent != NULL; parent = parent->parent) {
+               if ((parent->flags & data_flag) != 0 ||
+                   (parent->child[0] != NULL &&
+                    (parent->child[0]->flags & flags) != 0) ||
+                   (parent->child[1] != NULL &&
+                    (parent->child[1]->flags & flags) != 0))
+                       break;
+               parent->flags &= ~flags;
+       }
+
+       /*
+        * We might need to delete 2 nodes.
+        */
+       do {
+               /*
+                * The node is now useless if it has no data of its own
+                * and 0 or 1 children.  We are finished if it is not useless.
+                */
+               if ((child = tgt->child[0]) != NULL) {
+                       if (tgt->child[1] != NULL)
+                               return;
+               } else {
+                       child = tgt->child[1];
+               }
+               if ((tgt->flags & (DNS_RPZ_CIDR_FG_IP_DATA |
+                                DNS_RPZ_CIDR_FG_NSIP_DATA)) != 0)
+                       return;
+
+               /*
+                * Replace the pointer to this node in the parent with
+                * the remaining child or NULL.
+                */
+               parent = tgt->parent;
+               if (parent == NULL) {
+                       cidr->root = child;
+               } else {
+                       parent->child[parent->child[1] == tgt] = child;
+               }
+               /*
+                * If the child exists fix up its parent pointer.
+                */
+               if (child != NULL)
+                       child->parent = parent;
+               isc_mem_put(cidr->mctx, tgt, sizeof(*tgt));
+
+               tgt = parent;
+       } while (tgt != NULL);
+}
+
+
+
+/*
+ * Caller must hold tree lock.
+ * Return  ISC_R_NOTFOUND
+ *     or ISC_R_SUCCESS and the found entry's canonical and search names
+ *         and its prefix length
+ */
+isc_result_t
+dns_rpz_cidr_find(dns_rpz_cidr_t *cidr, const isc_netaddr_t *netaddr,
+                 dns_rpz_type_t type, dns_name_t *canon_name,
+                 dns_name_t *search_name, dns_rpz_cidr_bits_t *prefix)
+{
+       dns_rpz_cidr_key_t tgt_ip;
+       isc_result_t result;
+       dns_rpz_cidr_node_t *found;
+       int i;
+
+       /*
+        * Convert IP address to CIDR tree key.
+        */
+       if (netaddr->family == AF_INET) {
+               tgt_ip.w[0] = 0;
+               tgt_ip.w[1] = 0;
+               tgt_ip.w[2] = ADDR_V4MAPPED;
+               tgt_ip.w[3] = ntohl(netaddr->type.in.s_addr);
+       } else if (netaddr->family == AF_INET6) {
+               dns_rpz_cidr_key_t src_ip6;
+
+               /*
+                * Given the int aligned struct in_addr member of netaddr->type
+                * one could cast netaddr->type.in6 to dns_rpz_cidr_key_t *,
+                * but there are objections.
+                */
+               memcpy(src_ip6.w, &netaddr->type.in6, sizeof(src_ip6.w));
+               for (i = 0; i < 4; i++) {
+                       tgt_ip.w[i] = ntohl(src_ip6.w[i]);
+               }
+       } else {
+               return (ISC_R_NOTFOUND);
+       }
+
+       result = search(cidr, &tgt_ip, 128, type, ISC_FALSE, &found);
+       if (result != ISC_R_SUCCESS && result != DNS_R_PARTIALMATCH)
+               return (result);
+
+       *prefix = found->bits;
+       return (ip2name(cidr, &found->ip, found->bits, type,
+                       canon_name, search_name));
+}
+
+
+
+/*
+ * Translate CNAME rdata to a QNAME response policy action.
+ */
+dns_rpz_policy_t
+dns_rpz_decode_cname(dns_rdataset_t *rdataset, dns_name_t *selfname) {
+       dns_rdata_t rdata = DNS_RDATA_INIT;
+       dns_rdata_cname_t cname;
+       isc_result_t result;
+
+       result = dns_rdataset_first(rdataset);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       dns_rdataset_current(rdataset, &rdata);
+       result = dns_rdata_tostruct(&rdata, &cname, NULL);
+       RUNTIME_CHECK(result == ISC_R_SUCCESS);
+       dns_rdata_reset(&rdata);
+
+       /*
+        * CNAME . means NXDOMAIN
+        */
+       if (dns_name_equal(&cname.cname, dns_rootname))
+               return (DNS_RPZ_POLICY_NXDOMAIN);
+
+       /*
+        * CNAME *. means NODATA
+        */
+       if (dns_name_countlabels(&cname.cname) == 2
+           && dns_name_iswildcard(&cname.cname))
+               return (DNS_RPZ_POLICY_NODATA);
+
+       /*
+        * 128.1.0.127.rpz-ip CNAME  128.1.0.0.127. means "do not rewrite"
+        */
+       if (selfname != NULL && dns_name_equal(&cname.cname, selfname))
+               return (DNS_RPZ_POLICY_NO_OP);
+
+       /*
+        * evil.com CNAME garden.net rewrites www.evil.com to www.garden.net.
+        */
+       return (DNS_RPZ_POLICY_RECORD);
+}
index 7d8dd745d348dde2763fb6cca05fcc1042d26a82..fff0337727250b14d1001cec14c9f322b5bfeff0 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: sdb.c,v 1.74 2010/08/16 04:46:16 marka Exp $ */
+/* $Id: sdb.c,v 1.75 2011/01/13 01:59:27 marka Exp $ */
 
 /*! \file */
 
@@ -1254,6 +1254,8 @@ static dns_dbmethods_t sdb_methods = {
        NULL,
        NULL,
        NULL,
+       NULL,
+       NULL
 };
 
 static isc_result_t
index 2e3179aaae8136cf2a1791a4bb7c9b086df43ff6..a464a56b6dc9a1826dc53e0e760b64888761b954 100644 (file)
@@ -50,7 +50,7 @@
  * USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: sdlz.c,v 1.28 2010/12/19 02:37:08 each Exp $ */
+/* $Id: sdlz.c,v 1.29 2011/01/13 01:59:28 marka Exp $ */
 
 /*! \file */
 
@@ -1237,6 +1237,8 @@ static dns_dbmethods_t sdlzdb_methods = {
        NULL,
        NULL,
        NULL,
+       NULL,
+       NULL,
        NULL
 };
 
index 7d795c2185e61fa750abe34bd93882eb030dba8e..5c9f3c013fefea9efadc62b8524369ace54c0801 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: view.c,v 1.176 2011/01/11 23:47:13 tbox Exp $ */
+/* $Id: view.c,v 1.177 2011/01/13 01:59:28 marka Exp $ */
 
 /*! \file */
 
@@ -53,6 +53,7 @@
 #include <dns/request.h>
 #include <dns/resolver.h>
 #include <dns/result.h>
+#include <dns/rpz.h>
 #include <dns/stats.h>
 #include <dns/tsig.h>
 #include <dns/zone.h>
@@ -191,6 +192,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
        view->maxudp = 0;
        view->v4_aaaa = dns_v4_aaaa_ok;
        view->v4_aaaa_acl = NULL;
+       ISC_LIST_INIT(view->rpz_zones);
        dns_fixedname_init(&view->dlv_fixed);
        view->managed_keys = NULL;
 #ifdef BIND9
@@ -326,6 +328,7 @@ destroy(dns_view_t *view) {
                dns_acache_detach(&view->acache);
        }
 #endif
+       dns_rpz_view_destroy(view);
        if (view->requestmgr != NULL)
                dns_requestmgr_detach(&view->requestmgr);
        if (view->task != NULL)
index 4c97e060283e70150c3cb5294334c77c4658780c..c886b8febe7874b8b3cf64e2a6bf6d4aaf17d7e3 100644 (file)
@@ -603,6 +603,18 @@ dns_result_register
 dns_result_torcode
 dns_result_totext
 dns_rootns_create
+dns_rpz_cidr_addip
+dns_rpz_cidr_deleteip
+dns_rpz_cidr_find
+dns_rpz_cidr_free
+dns_rpz_decode_cname
+dns_rpz_enabled
+dns_rpz_needed
+dns_rpz_new_cidr
+dns_rpz_set_need
+dns_rpz_str2policy
+dns_rpz_type2str
+dns_rpz_view_destroy
 dns_rriterator_current
 dns_rriterator_destroy
 dns_rriterator_first
index 4a7f3e06a9f7941f09cff48be45f8663f6bf468c..ef0a9ac067f2a1e057660e87055bd2ac9303bffe 100644 (file)
@@ -338,6 +338,10 @@ SOURCE=..\include\dns\rootns.h
 # End Source File
 # Begin Source File
 
+SOURCE=..\include\dns\rpz.h
+# End Source File
+# Begin Source File
+
 SOURCE=..\include\dns\rriterator.h
 # End Source File
 # Begin Source File
@@ -630,6 +634,10 @@ SOURCE=..\rootns.c
 # End Source File
 # Begin Source File
 
+SOURCE=..\rpz.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\rriterator.c
 # End Source File
 # Begin Source File
index c36b91c5503a9cf9ce0dab2325b54b7c1f112265..09abddcebbbdfb2ba062a9603862f30062ce2206 100644 (file)
@@ -181,6 +181,7 @@ CLEAN :
        -@erase "$(INTDIR)\resolver.obj"
        -@erase "$(INTDIR)\result.obj"
        -@erase "$(INTDIR)\rootns.obj"
+       -@erase "$(INTDIR)\rpz.obj"
        -@erase "$(INTDIR)\sdb.obj"
        -@erase "$(INTDIR)\sdlz.obj"
        -@erase "$(INTDIR)\soa.obj"
@@ -303,6 +304,7 @@ LINK32_OBJS= \
        "$(INTDIR)\resolver.obj" \
        "$(INTDIR)\result.obj" \
        "$(INTDIR)\rootns.obj" \
+       "$(INTDIR)\rpz.obj" \
        "$(INTDIR)\rriterator.obj" \
        "$(INTDIR)\sdb.obj" \
        "$(INTDIR)\sdlz.obj" \
@@ -491,6 +493,8 @@ CLEAN :
        -@erase "$(INTDIR)\result.sbr"
        -@erase "$(INTDIR)\rootns.obj"
        -@erase "$(INTDIR)\rootns.sbr"
+       -@erase "$(INTDIR)\rpz.obj"
+       -@erase "$(INTDIR)\rpz.sbr"
        -@erase "$(INTDIR)\rriterator.obj"
        -@erase "$(INTDIR)\rriterator.sbr"
        -@erase "$(INTDIR)\sdb.obj"
@@ -633,6 +637,7 @@ BSC32_SBRS= \
        "$(INTDIR)\resolver.sbr" \
        "$(INTDIR)\result.sbr" \
        "$(INTDIR)\rootns.sbr" \
+       "$(INTDIR)\rpz.sbr" \
        "$(INTDIR)\rriterator.sbr" \
        "$(INTDIR)\sdb.sbr" \
        "$(INTDIR)\sdlz.sbr" \
@@ -726,6 +731,7 @@ LINK32_OBJS= \
        "$(INTDIR)\resolver.obj" \
        "$(INTDIR)\result.obj" \
        "$(INTDIR)\rootns.obj" \
+       "$(INTDIR)\rpz.obj" \
        "$(INTDIR)\rriterator.obj" \
        "$(INTDIR)\sdb.obj" \
        "$(INTDIR)\sdlz.obj" \
@@ -1664,6 +1670,24 @@ SOURCE=..\rootns.c
        $(CPP) $(CPP_PROJ) $(SOURCE)
 
 
+!ENDIF 
+
+SOURCE=..\rpz.c
+
+!IF  "$(CFG)" == "libdns - Win32 Release"
+
+
+"$(INTDIR)\rpz.obj" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
+!ELSEIF  "$(CFG)" == "libdns - Win32 Debug"
+
+
+"$(INTDIR)\rpz.obj"    "$(INTDIR)\rpz.sbr" : $(SOURCE) "$(INTDIR)"
+       $(CPP) $(CPP_PROJ) $(SOURCE)
+
+
 !ENDIF 
 
 SOURCE=..\rriterator.c
index c1289447a707d275b9753ed33e6f62ad48812d25..fc0305876c04446306114a8f894b6cae56278cc1 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: zone.c,v 1.580 2010/12/18 01:56:22 each Exp $ */
+/* $Id: zone.c,v 1.581 2011/01/13 01:59:28 marka Exp $ */
 
 /*! \file */
 
@@ -322,6 +322,11 @@ struct dns_zone {
         * True if added by "rndc addzone"
         */
        isc_boolean_t           added;
+
+       /*%
+        * whether a rpz radix was needed when last loaded
+        */
+       isc_boolean_t           rpz_zone;
 };
 
 #define DNS_ZONE_FLAG(z,f) (ISC_TF(((z)->flags & (f)) != 0))
@@ -833,6 +838,7 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) {
        zone->nodes = 100;
        zone->privatetype = (dns_rdatatype_t)0xffffU;
        zone->added = ISC_FALSE;
+       zone->rpz_zone = ISC_FALSE;
 
        zone->magic = ZONE_MAGIC;
 
@@ -1434,7 +1440,8 @@ zone_load(dns_zone_t *zone, unsigned int flags) {
                 * "rndc reconfig", we are done.
                 */
                if (!isc_time_isepoch(&zone->loadtime) &&
-                   (flags & DNS_ZONELOADFLAG_NOSTAT) != 0) {
+                   (flags & DNS_ZONELOADFLAG_NOSTAT) != 0 &&
+                   zone->rpz_zone == dns_rpz_needed()) {
                        result = ISC_R_SUCCESS;
                        goto cleanup;
                }
@@ -1443,7 +1450,8 @@ zone_load(dns_zone_t *zone, unsigned int flags) {
                if (result == ISC_R_SUCCESS) {
                        if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_LOADED) &&
                            !DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE) &&
-                           isc_time_compare(&filetime, &zone->loadtime) <= 0) {
+                           isc_time_compare(&filetime, &zone->loadtime) <= 0 &&
+                           zone->rpz_zone == dns_rpz_needed()) {
                                dns_zone_log(zone, ISC_LOG_DEBUG(1),
                                             "skipping load: master file "
                                             "older than last load");
@@ -1451,6 +1459,7 @@ zone_load(dns_zone_t *zone, unsigned int flags) {
                                goto cleanup;
                        }
                        loadtime = filetime;
+                       zone->rpz_zone = dns_rpz_needed();
                }
        }
 
index 66c972bd8ad0f5df09a3ce9f9c158358b8cebdb9..bc12bc594629fa74986fb6dd7bd845e772886ea8 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: namedconf.c,v 1.130 2011/01/07 04:31:39 marka Exp $ */
+/* $Id: namedconf.c,v 1.131 2011/01/13 01:59:28 marka Exp $ */
 
 /*! \file */
 
@@ -1013,6 +1013,120 @@ static cfg_type_t cfg_type_masterformat = {
        &cfg_rep_string, &masterformat_enums
 };
 
+
+
+/*
+ *  response-policy {
+ *     zone <string> [ policy (given|no-op|nxdomain|nodata|cname <domain> ) ];
+ *  };
+ *
+ * this is a chimera of doc_optional_keyvalue() and cfg_doc_enum()
+ */
+static void
+doc_rpz_policies(cfg_printer_t *pctx, const cfg_type_t *type) {
+       const keyword_type_t *kw;
+       const char * const *p;
+
+       kw = type->of;
+       cfg_print_chars(pctx, "[ ", 2);
+       cfg_print_cstr(pctx, kw->name);
+       cfg_print_chars(pctx, " ", 1);
+
+       cfg_print_chars(pctx, "( ", 2);
+       for (p = kw->type->of; *p != NULL; p++) {
+               cfg_print_cstr(pctx, *p);
+               if (p[1] != NULL)
+                       cfg_print_chars(pctx, " | ", 3);
+       }
+}
+
+/*
+ * print_qstring() from parser.c
+ */
+static void
+print_rpz_cname(cfg_printer_t *pctx, const cfg_obj_t *obj)
+{
+       cfg_print_chars(pctx, "\"", 1);
+       cfg_print_ustring(pctx, obj);
+       cfg_print_chars(pctx, "\"", 1);
+}
+
+static void
+doc_rpz_cname(cfg_printer_t *pctx, const cfg_type_t *type) {
+       cfg_doc_terminal(pctx, type);
+       cfg_print_chars(pctx, " ) ]", 4);
+}
+
+static isc_result_t
+parse_rpz(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret) {
+       isc_result_t result;
+       cfg_obj_t *obj = NULL;
+       const cfg_tuplefielddef_t *fields = type->of;
+
+       CHECK(cfg_create_tuple(pctx, type, &obj));
+       CHECK(cfg_parse_obj(pctx, fields[0].type, &obj->value.tuple[0]));
+       CHECK(cfg_parse_obj(pctx, fields[1].type, &obj->value.tuple[1]));
+       /*
+        * parse cname domain only after "policy cname"
+        */
+       if (cfg_obj_isvoid(obj->value.tuple[1]) ||
+           strcasecmp("cname", cfg_obj_asstring(obj->value.tuple[1]))) {
+               CHECK(cfg_parse_void(pctx, NULL, &obj->value.tuple[2]));
+       } else {
+               CHECK(cfg_parse_obj(pctx, fields[2].type, &obj->value.tuple[2]));
+       }
+
+       *ret = obj;
+       return (ISC_R_SUCCESS);
+
+cleanup:
+       CLEANUP_OBJ(obj);
+       return (result);
+}
+
+static const char *rpz_policies[] = {
+       "given", "no-op", "nxdomain", "nodata", "cname", NULL
+};
+static cfg_type_t cfg_type_rpz_policylist = {
+       "policies", cfg_parse_enum, cfg_print_ustring, cfg_doc_enum,
+       &cfg_rep_string, &rpz_policies
+};
+static keyword_type_t rpz_policies_kw = {
+       "policy", &cfg_type_rpz_policylist
+};
+static cfg_type_t cfg_type_rpz_policy = {
+       "optional_policy", parse_optional_keyvalue, print_keyvalue,
+       doc_rpz_policies, &cfg_rep_string, &rpz_policies_kw
+};
+static cfg_type_t cfg_type_cname = {
+       "domain", cfg_parse_astring, print_rpz_cname, doc_rpz_cname,
+       &cfg_rep_string, NULL
+};
+static cfg_tuplefielddef_t rpzone_fields[] = {
+       { "name", &cfg_type_astring, 0 },
+       { "policy", &cfg_type_rpz_policy, 0 },
+       { "cname", &cfg_type_cname, 0 },
+       { NULL, NULL, 0 }
+};
+static cfg_type_t cfg_type_rpzone = {
+       "rpzone", parse_rpz, cfg_print_tuple, cfg_doc_tuple,
+       &cfg_rep_tuple, rpzone_fields
+};
+static cfg_clausedef_t rpz_clauses[] = {
+       { "zone", &cfg_type_rpzone, CFG_CLAUSEFLAG_MULTI },
+       { NULL, NULL, 0 }
+};
+static cfg_clausedef_t *rpz_clausesets[] = {
+       rpz_clauses,
+       NULL
+};
+static cfg_type_t cfg_type_rpz = {
+       "rpz", cfg_parse_map, cfg_print_map, cfg_doc_map,
+       &cfg_rep_map, rpz_clausesets
+};
+
+
+
 /*%
  * dnssec-lookaside
  */
@@ -1146,6 +1260,7 @@ view_clauses[] = {
        { "filter-aaaa-on-v4", &cfg_type_v4_aaaa,
           CFG_CLAUSEFLAG_NOTCONFIGURED },
 #endif
+       { "response-policy", &cfg_type_rpz, 0 },
        { NULL, NULL, 0 }
 };