- Warn for unused 'nodefault' local-zone configuration in

unbound-checkconf (related to #1416).
This commit is contained in:
Yorgos Thessalonikefs
2026-03-06 17:05:57 +01:00
parent 683241a2f5
commit 94ef1a8fee
5 changed files with 118 additions and 60 deletions
+2
View 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.
+67 -59
View File
@@ -56,6 +56,24 @@
* 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++) {
+3
View 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
+45
View 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))
+1 -1
View 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");