]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Warn for unused 'nodefault' local-zone configuration in
authorYorgos Thessalonikefs <yorgos@nlnetlabs.nl>
Fri, 6 Mar 2026 16:05:57 +0000 (17:05 +0100)
committerYorgos Thessalonikefs <yorgos@nlnetlabs.nl>
Fri, 6 Mar 2026 16:05:57 +0000 (17:05 +0100)
  unbound-checkconf (related to #1416).

doc/Changelog
services/localzone.c
services/localzone.h
smallapp/unbound-checkconf.c
util/configparser.y

index eaa8e6f83770ea0c624859f0a1143240530135c2..90223791ca72164ebab04b2a1781b2d370c80a48 100644 (file)
@@ -4,6 +4,8 @@
 6 March 2026: Yorgos
        - Document the suggestion for a higher value for 'outgoing-range';
          helps when the request list is full.
+       - Warn for unused 'nodefault' local-zone configuration in
+         unbound-checkconf (related to #1416).
 
 5 March 2026: Wouter
        - Fix for DNS Rebinding Bypass via SVCB/HTTPS Records in Unbound.
index ccbe0d522a6a061503041f8c2f95993d3e30606c..52166ae2d2bd0066244f965c39caec9f3b7b3ca4 100644 (file)
  * with 16 bytes for an A record, a 64K packet has about 4000 max */
 #define LOCALZONE_RRSET_COUNT_MAX 4096
 
+static const char* default_zones_reverse_array[] = {
+       "127.in-addr.arpa.", /* reverse ip4 zone */
+       "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", /* reverse ip6 zone */
+       0
+};
+const char** local_zones_default_reverse = default_zones_reverse_array;
+
+static const char* default_zones_special_array[] = {
+       "test.",                /* RFC 6761 */
+       "invalid.",             /* RFC 6761 */
+       "onion.",               /* RFC 7686 */
+       "home.arpa.",           /* RFC 8375 */
+       "resolver.arpa.",       /* RFC 9462 */
+       "service.arpa.",        /* RFC 9665 */
+       0
+};
+const char** local_zones_default_special = default_zones_special_array;
+
 /** print all RRsets in local zone */
 static void
 local_zone_out(struct local_zone* z)
@@ -834,7 +852,7 @@ lz_nodefault(struct config_file* cfg, const char* name)
 
        for(p = cfg->local_zones_nodefault; p; p = p->next) {
                /* compare zone name, lowercase, compare without ending . */
-               if(strncasecmp(p->str, name, len) == 0 && 
+               if(strncasecmp(p->str, name, len) == 0 &&
                        (strlen(p->str) == len || (strlen(p->str)==len+1 &&
                        p->str[len] == '.')))
                        return 1;
@@ -842,6 +860,45 @@ lz_nodefault(struct config_file* cfg, const char* name)
        return 0;
 }
 
+/** enter reverse default zone */
+static int
+add_reverse_default(struct local_zones* zones, struct config_file* cfg,
+        const char* name)
+{
+       struct local_zone* z;
+       char str[1024]; /* known long enough */
+       if(lz_exists(zones, name) || lz_nodefault(cfg, name))
+               return 1; /* do not enter default content */
+       if(!(z=lz_enter_zone(zones, name, "static", LDNS_RR_CLASS_IN)))
+               return 0;
+       snprintf(str, sizeof(str), "%s 10800 IN SOA localhost. "
+               "nobody.invalid. 1 3600 1200 604800 10800", name);
+       if(!lz_enter_rr_into_zone(z, str)) {
+               lock_rw_unlock(&z->lock);
+               return 0;
+       }
+       snprintf(str, sizeof(str), "%s 10800 IN NS localhost. ", name);
+       if(!lz_enter_rr_into_zone(z, str)) {
+               lock_rw_unlock(&z->lock);
+               return 0;
+       }
+       if(strncasecmp("127.in-addr.arpa.", name, 17) ==  0) {
+               if(!lz_enter_rr_into_zone(z,
+                       "1.0.0.127.in-addr.arpa. 10800 IN PTR localhost.")) {
+                       lock_rw_unlock(&z->lock);
+                       return 0;
+               }
+       } else if(strncasecmp("1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", name, 73) ==  0) {
+               snprintf(str, sizeof(str), "%s 10800 IN PTR localhost.", name);
+               if(!lz_enter_rr_into_zone(z, str)) {
+                       lock_rw_unlock(&z->lock);
+                       return 0;
+               }
+       }
+       lock_rw_unlock(&z->lock);
+       return 1;
+}
+
 /** enter (AS112) empty default zone */
 static int
 add_empty_default(struct local_zones* zones, struct config_file* cfg,
@@ -902,72 +959,23 @@ int local_zone_enter_defaults(struct local_zones* zones, struct config_file* cfg
                }
                lock_rw_unlock(&z->lock);
        }
-       /* reverse ip4 zone */
-       if(!lz_exists(zones, "127.in-addr.arpa.") &&
-               !lz_nodefault(cfg, "127.in-addr.arpa.")) {
-               if(!(z=lz_enter_zone(zones, "127.in-addr.arpa.", "static", 
-                       LDNS_RR_CLASS_IN)) ||
-                  !lz_enter_rr_into_zone(z,
-                       "127.in-addr.arpa. 10800 IN NS localhost.") ||
-                  !lz_enter_rr_into_zone(z,
-                       "127.in-addr.arpa. 10800 IN SOA localhost. "
-                       "nobody.invalid. 1 3600 1200 604800 10800") ||
-                  !lz_enter_rr_into_zone(z,
-                       "1.0.0.127.in-addr.arpa. 10800 IN PTR localhost.")) {
+
+       /* ip4 and ip6 reverse */
+       for(zstr = local_zones_default_reverse; *zstr; zstr++) {
+               if(!add_reverse_default(zones, cfg, *zstr)) {
                        log_err("out of memory adding default zone");
-                       if(z) { lock_rw_unlock(&z->lock); }
                        return 0;
                }
-               lock_rw_unlock(&z->lock);
        }
-       /* reverse ip6 zone */
-       if(!lz_exists(zones, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.") &&
-               !lz_nodefault(cfg, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.")) {
-               if(!(z=lz_enter_zone(zones, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.", "static", 
-                       LDNS_RR_CLASS_IN)) ||
-                  !lz_enter_rr_into_zone(z,
-                       "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN NS localhost.") ||
-                  !lz_enter_rr_into_zone(z,
-                       "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN SOA localhost. "
-                       "nobody.invalid. 1 3600 1200 604800 10800") ||
-                  !lz_enter_rr_into_zone(z,
-                       "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa. 10800 IN PTR localhost.")) {
+
+       /* special-use zones */
+       for(zstr = local_zones_default_special; *zstr; zstr++) {
+               if(!add_empty_default(zones, cfg, *zstr)) {
                        log_err("out of memory adding default zone");
-                       if(z) { lock_rw_unlock(&z->lock); }
                        return 0;
                }
-               lock_rw_unlock(&z->lock);
-       }
-       /* home.arpa. zone (RFC 8375) */
-       if(!add_empty_default(zones, cfg, "home.arpa.")) {
-               log_err("out of memory adding default zone");
-               return 0;
-       }
-       /* resolver.arpa. zone (RFC 9462) */
-       if(!add_empty_default(zones, cfg, "resolver.arpa.")) {
-               log_err("out of memory adding default zone");
-               return 0;
-       }
-       /* service.arpa. zone (draft-ietf-dnssd-srp-25) */
-       if(!add_empty_default(zones, cfg, "service.arpa.")) {
-               log_err("out of memory adding default zone");
-               return 0;
-       }
-       /* onion. zone (RFC 7686) */
-       if(!add_empty_default(zones, cfg, "onion.")) {
-               log_err("out of memory adding default zone");
-               return 0;
-       }
-       /* test. zone (RFC 6761) */
-       if(!add_empty_default(zones, cfg, "test.")) {
-               log_err("out of memory adding default zone");
-               return 0;
-       }
-       /* invalid. zone (RFC 6761) */
-       if(!add_empty_default(zones, cfg, "invalid.")) {
-               log_err("out of memory adding default zone");
-               return 0;
        }
+
        /* block AS112 zones, unless asked not to */
        if(!cfg->unblock_lan_zones) {
                for(zstr = as112_zones; *zstr; zstr++) {
index 3dc89b05882bbcb5d1967fb86c51c82a0501b310..76c011836030afbb6bc5b4773a588970ae56987f 100644 (file)
@@ -57,6 +57,9 @@ struct sldns_buffer;
 struct comm_reply;
 struct config_strlist;
 
+extern const char** local_zones_default_special;
+extern const char** local_zones_default_reverse;
+
 /**
  * Local zone type
  * This type determines processing for queries that did not match
index 91bc558dd10faa1412765920178c08fe660b969d..399c2fce9cd85ba1379d56ed37e5e4208098137e 100644 (file)
@@ -44,6 +44,7 @@
 
 #include "config.h"
 #include <ctype.h>
+#include "util/as112.h"
 #include "util/log.h"
 #include "util/config_file.h"
 #include "util/module.h"
@@ -188,11 +189,55 @@ donotquerylocalhostcheck(struct config_file* cfg)
        }
 }
 
+static void
+nodefaultzonescheck(struct config_file* cfg)
+{
+       struct config_strlist* d;
+       const char** zstr;
+       size_t len;
+
+#define COMPARE_ZONE_NAME(confname, builtname, len)            \
+       (strncasecmp(confname, builtname, (len)) == 0 &&        \
+       (strlen(confname) == (len) ||                           \
+       (strlen(confname) == (len) + 1                          \
+       && confname[(len)] == '.')))
+
+       for(d = cfg->local_zones_nodefault; d; d = d->next) {
+               if(!cfg->unblock_lan_zones) {
+                       for(zstr = as112_zones; *zstr; zstr++) {
+                               len = strlen(*zstr) - 1; /* trailing '.' */
+                               if(COMPARE_ZONE_NAME(d->str, *zstr, len))
+                                       goto default_continue;
+                       }
+               }
+               for(zstr = local_zones_default_special; *zstr; zstr++) {
+                       len = strlen(*zstr) - 1; /* trailing '.' */
+                       if(COMPARE_ZONE_NAME(d->str, *zstr, len))
+                               goto default_continue;
+               }
+               for(zstr = local_zones_default_reverse; *zstr; zstr++) {
+                       len = strlen(*zstr) - 1; /* trailing '.' */
+                       if(COMPARE_ZONE_NAME(d->str, *zstr, len))
+                               goto default_continue;
+               }
+               if(COMPARE_ZONE_NAME(d->str, "localhost.", 10 - 1))
+                       goto default_continue;
+               fprintf(stderr, "unbound-checkconf: warning: local-zone: '%s' "
+                       "is configured as 'nodefault' but there is no such "
+                       "default local-zone. Check the unbound.conf "
+                       "documentation for default configured local-zones.\n",
+                       d->str);
+default_continue:
+       }
+#undef COMPARE_ZONE_NAME
+}
+
 /** check localzones */
 static void
 localzonechecks(struct config_file* cfg)
 {
        struct local_zones* zs;
+       nodefaultzonescheck(cfg);
        if(!(zs = local_zones_create()))
                fatal_exit("out of memory");
        if(!local_zones_apply_cfg(zs, cfg))
index d9a7cd839264b5adc1b3c20feeadad19a3067cd8..aa787fdcefdfaa34316b6df29653c765a8196160 100644 (file)
@@ -2399,7 +2399,7 @@ server_local_zone: VAR_LOCAL_ZONE STRING_ARG STRING_ARG
                        yyerror("local-zone type: expected static, deny, "
                                "refuse, redirect, transparent, "
                                "typetransparent, inform, inform_deny, "
-                               "inform_redirect, always_transparent, block_a,"
+                               "inform_redirect, always_transparent, block_a, "
                                "always_refuse, always_nxdomain, "
                                "always_nodata, always_deny, always_null, "
                                "noview, nodefault or ipset");