]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add a RPZ checking mode to named-checkzone
authorMark Andrews <marka@isc.org>
Thu, 23 Jul 2026 13:51:51 +0000 (23:51 +1000)
committerMark Andrews <marka@isc.org>
Sun, 2 Aug 2026 23:30:58 +0000 (09:30 +1000)
Check that the zone will not produce errors when it is loaded.

bin/check/check-tool.c
bin/check/check-tool.h
bin/check/named-checkzone.c
bin/check/named-checkzone.rst
lib/dns/include/dns/rpz.h
lib/dns/rpz.c

index f0db14c7717dd7ad44b86facd67b6b7f555c8c8b..39ba810fb2b092ce7935f72f010801523e3f8982 100644 (file)
@@ -78,6 +78,7 @@ bool docheckmx = false;
 bool dochecksrv = false;
 bool docheckns = false;
 #endif /* if CHECK_LOCAL */
+bool docheckrpz = false;
 dns_zoneopt_t zone_options = DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_CHECKMX |
                             DNS_ZONEOPT_CHECKDUPRR | DNS_ZONEOPT_CHECKSPF |
                             DNS_ZONEOPT_MANYERRORS | DNS_ZONEOPT_CHECKNAMES |
@@ -678,6 +679,14 @@ load_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
 
        CHECK(dns_zone_load(zone, false));
 
+       if (docheckrpz) {
+               dns_db_t *db = NULL;
+               dns_zone_getdb(zone, &db);
+               result = dns_rpz_checkdb(db, mctx);
+               dns_db_detach(&db);
+               CHECK(result);
+       }
+
        if (zonep != NULL) {
                *zonep = zone;
                zone = NULL;
index 7e8f946268ed698f92a2971e5e7ec57c44f5c748..3236fdf02aabec42a0c7a1c9e03a9f5ef1ea625d 100644 (file)
@@ -44,4 +44,5 @@ extern bool nomerge;
 extern bool docheckmx;
 extern bool docheckns;
 extern bool dochecksrv;
+extern bool docheckrpz;
 extern dns_zoneopt_t zone_options;
index 40b4eabd1f9cd9f6b1c794f9e67898ea7750b9c4..8833062d95b3b76a5444084ddbcd1271b7e5e321 100644 (file)
@@ -67,14 +67,14 @@ usage(int ret);
 static void
 usage(int ret) {
        fprintf(stderr,
-               "usage: %s [-djqvD] [-c class] "
+               "usage: %s [-djqvDP] [-c class] "
                "[-f inputformat] [-F outputformat] [-J filename] "
                "[-s (full|relative)] [-t directory] [-w directory] "
                "[-k (ignore|warn|fail)] [-m (ignore|warn|fail)] "
                "[-n (ignore|warn|fail)] [-r (ignore|warn|fail)] "
                "[-i (full|full-sibling|local|local-sibling|none)] "
-               "[-M (ignore|warn|fail)] [-S (ignore|warn|fail)] "
-               "[-W (ignore|warn)] "
+               "[-M (ignore|warn|fail)] [-R (ignore|fail)] "
+               "[-S (ignore|warn|fail)] [-W (ignore|warn)] "
                "%s zonename [ (filename|-) ]\n",
                isc_commandline_progname,
                progmode == progmode_check ? "[-o filename]" : "-o filename");
@@ -137,7 +137,7 @@ main(int argc, char **argv) {
 
        while ((c = isc_commandline_parse(argc, argv,
                                          "c:df:hi:jJ:k:L:l:m:n:qr:s:t:o:vw:C:"
-                                         "DF:M:R:S:T:W:")) != EOF)
+                                         "DF:M:PR:S:T:W:")) != EOF)
        {
                switch (c) {
                case 'c':
@@ -281,6 +281,10 @@ main(int argc, char **argv) {
                        output_filename = isc_commandline_argument;
                        break;
 
+               case 'P':
+                       docheckrpz = true;
+                       break;
+
                case 'q':
                        quiet++;
                        break;
index 2e57020769f3559340ed716e2d93202d5a640631..8e79bdd52905779fa1915326358422e50a5e8773 100644 (file)
@@ -174,6 +174,10 @@ Options
    This option writes the zone output to ``filename``. If ``filename`` is ``-``, then
    the zone output is written to standard output.
 
+.. option:: -P
+
+   This option checks the zone for RPZ errors.
+
 .. option:: -r mode
 
    This option checks for records that are treated as different by DNSSEC but are
index 4904a470da47f6044b5c445c4f57ada95f40e3c6..78b464d00be5cb7708b6181806114bbb082d8f27 100644 (file)
@@ -421,3 +421,6 @@ dns_rpz_find_ip(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
 dns_rpz_zbits_t
 dns_rpz_find_name(dns_rpz_zones_t *rpzs, dns_rpz_type_t rpz_type,
                  dns_rpz_zbits_t zbits, dns_name_t *trig_name);
+
+isc_result_t
+dns_rpz_checkdb(dns_db_t *db, isc_mem_t *mctx);
index 684f25675215b58afee0ef882ef0799d7f5abbfe..e30a71be67cb58fec1b26184aa33563b11e24207 100644 (file)
@@ -194,7 +194,7 @@ ISC_REFCOUNT_DECL(nmdata);
 #endif
 
 static isc_result_t
-rpz_add(dns_rpz_zone_t *rpz, const dns_name_t *src_name);
+rpz_add(dns_rpz_zone_t *rpz, const dns_name_t *src_name, bool fail);
 static void
 rpz_del(dns_rpz_zone_t *rpz, const dns_name_t *src_name);
 
@@ -1301,7 +1301,7 @@ search(dns_rpz_zones_t *rpzs, const dns_rpz_cidr_key_t *tgt_ip,
  */
 static isc_result_t
 add_cidr(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
-        const dns_name_t *src_name) {
+        const dns_name_t *src_name, bool fail) {
        dns_rpz_cidr_key_t tgt_ip;
        dns_rpz_prefix_t tgt_prefix;
        dns_rpz_addr_zbits_t set;
@@ -1314,7 +1314,7 @@ add_cidr(dns_rpz_zone_t *rpz, dns_rpz_type_t rpz_type,
         * Log complaints about bad owner names but let the zone load.
         */
        if (result != ISC_R_SUCCESS) {
-               return ISC_R_SUCCESS;
+               return fail ? result : ISC_R_SUCCESS;
        }
 
        RWLOCK(&rpz->rpzs->search_lock, isc_rwlocktype_write);
@@ -1689,6 +1689,82 @@ update_rpz_done_cb(void *data, isc_result_t result) {
        dns_rpz_zones_unref(rpz->rpzs);
 }
 
+isc_result_t
+dns_rpz_checkdb(dns_db_t *db, isc_mem_t *mctx) {
+       dns_dbiterator_t *dbit = NULL;
+       dns_dbnode_t *node = NULL;
+       dns_fixedname_t fixedname;
+       dns_name_t *name = dns_fixedname_initname(&fixedname);
+       dns_rdatasetiter_t *rdsiter = NULL;
+       dns_rpz_zone_t *rpz = NULL;
+       dns_rpz_zones_t *rpzs = NULL;
+       dns_view_t *view = NULL;
+       isc_result_t result, aresult = ISC_R_SUCCESS;
+
+       dns_view_create(mctx, NULL, dns_rdataclass_in, "view", &view);
+       CHECK(dns_rpz_new_zones(view, &rpzs, true));
+       CHECK(dns_rpz_new_zone(rpzs, &rpz));
+
+       dns_name_dup(dns_db_origin(db), mctx, &rpz->origin);
+
+       CHECK(dns_name_fromstring(&rpz->client_ip, DNS_RPZ_CLIENT_IP_ZONE,
+                                 &rpz->origin, DNS_NAME_DOWNCASE, mctx));
+       CHECK(dns_name_fromstring(&rpz->ip, DNS_RPZ_IP_ZONE, &rpz->origin,
+                                 DNS_NAME_DOWNCASE, mctx));
+       CHECK(dns_name_fromstring(&rpz->nsdname, DNS_RPZ_NSDNAME_ZONE,
+                                 &rpz->origin, DNS_NAME_DOWNCASE, mctx));
+       CHECK(dns_name_fromstring(&rpz->nsip, DNS_RPZ_NSIP_ZONE, &rpz->origin,
+                                 DNS_NAME_DOWNCASE, mctx));
+
+       CHECK(dns_name_fromstring(&rpz->passthru, DNS_RPZ_PASSTHRU_NAME,
+                                 dns_rootname, DNS_NAME_DOWNCASE, mctx));
+       CHECK(dns_name_fromstring(&rpz->drop, DNS_RPZ_DROP_NAME, dns_rootname,
+                                 DNS_NAME_DOWNCASE, mctx));
+       CHECK(dns_name_fromstring(&rpz->tcp_only, DNS_RPZ_TCP_ONLY_NAME,
+                                 dns_rootname, DNS_NAME_DOWNCASE, mctx));
+
+       CHECK(dns_db_createiterator(db, DNS_DB_NONSEC3, &dbit));
+       DNS_DBITERATOR_FOREACH(dbit) {
+               CHECK(dns_dbiterator_current(dbit, &node, name));
+               CHECK(dns_db_allrdatasets(db, node, NULL, 0, 0, &rdsiter));
+               result = dns_rdatasetiter_first(rdsiter);
+               if (result == ISC_R_SUCCESS) {
+                       result = rpz_add(rpz, name, true);
+                       /* Remember rpz_add errors. */
+                       if (result != ISC_R_SUCCESS) {
+                               aresult = result;
+                       }
+               }
+               dns_rdatasetiter_destroy(&rdsiter);
+               dns_db_detachnode(&node);
+       }
+
+       result = ISC_R_SUCCESS;
+
+cleanup:
+       if (rdsiter != NULL) {
+               dns_rdatasetiter_destroy(&rdsiter);
+       }
+       if (node != NULL) {
+               dns_db_detachnode(&node);
+       }
+       if (dbit != NULL) {
+               dns_dbiterator_destroy(&dbit);
+       }
+       if (rpzs != NULL) {
+               dns_rpz_zones_shutdown(rpzs);
+               dns_rpz_zones_detach(&rpzs);
+       }
+       if (view != NULL) {
+               dns_view_detach(&view);
+       }
+       /* Report rpz_add errors */
+       if (aresult != ISC_R_SUCCESS) {
+               result = aresult;
+       }
+       return result;
+}
+
 static isc_result_t
 update_nodes(dns_rpz_zone_t *rpz, isc_ht_t *newnodes) {
        isc_result_t result;
@@ -1800,7 +1876,7 @@ update_nodes(dns_rpz_zone_t *rpz, isc_ht_t *newnodes) {
                 * different rpz zones at the same time
                 */
                LOCK(&rpz->rpzs->maint_lock);
-               result = rpz_add(rpz, name);
+               result = rpz_add(rpz, name, false);
                UNLOCK(&rpz->rpzs->maint_lock);
 
                if (result != ISC_R_SUCCESS) {
@@ -2116,7 +2192,7 @@ ISC_REFCOUNT_IMPL(dns_rpz_zones, dns__rpz_zones_destroy);
  * Add an IP address to the radix tree or a name to the summary database.
  */
 static isc_result_t
-rpz_add(dns_rpz_zone_t *rpz, const dns_name_t *src_name) {
+rpz_add(dns_rpz_zone_t *rpz, const dns_name_t *src_name, bool fail) {
        dns_rpz_type_t rpz_type;
        isc_result_t result = ISC_R_FAILURE;
        dns_rpz_zones_t *rpzs = NULL;
@@ -2138,7 +2214,7 @@ rpz_add(dns_rpz_zone_t *rpz, const dns_name_t *src_name) {
        case DNS_RPZ_TYPE_CLIENT_IP:
        case DNS_RPZ_TYPE_IP:
        case DNS_RPZ_TYPE_NSIP:
-               result = add_cidr(rpz, rpz_type, src_name);
+               result = add_cidr(rpz, rpz_type, src_name, fail);
                break;
        case DNS_RPZ_TYPE_BAD:
                break;