mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-22 10:10:06 +00:00
new: usr: Add "Zone has [AAAA/A] records but is not served by IPv[6/4]" warnings
Check that zones with AAAA records are served by IPv6 servers and that zones with A records are served by IPv4 servers. Sometimes, IPv6 services are accidentally misconfigured and zones with IPv6 (AAAA) address records are not served by DNS servers with IPv6 addresses, which means they need to use translation devices to look up those IPv6 addresses. The reverse is also sometimes true: zones with A records are not resolvable over IPv4 when they should be. To prevent this, BIND now looks for these misconfigured zones and issues a warning if they are found. Closes #4370 Merge branch '4370-check-that-a-zone-is-served-by-ipv6-servers-if-it-has-aaaa-records' into 'main' See merge request isc-projects/bind9!8393
This commit is contained in:
commit
ef6dc36e53
@ -144,12 +144,97 @@ logged(char *key, int value) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
checkisservedby(dns_zone_t *zone, dns_rdatatype_t type,
|
||||||
|
const dns_name_t *name) {
|
||||||
|
char namebuf[DNS_NAME_FORMATSIZE + 1];
|
||||||
|
char ownerbuf[DNS_NAME_FORMATSIZE + 1];
|
||||||
|
/*
|
||||||
|
* Not all getaddrinfo implementations distinguish NODATA
|
||||||
|
* from NXDOMAIN with PF_INET6 so use PF_UNSPEC and look at
|
||||||
|
* the returned ai_family values.
|
||||||
|
*/
|
||||||
|
struct addrinfo hints = {
|
||||||
|
.ai_flags = AI_CANONNAME,
|
||||||
|
.ai_family = PF_UNSPEC,
|
||||||
|
.ai_socktype = SOCK_STREAM,
|
||||||
|
.ai_protocol = IPPROTO_TCP,
|
||||||
|
};
|
||||||
|
struct addrinfo *ai = NULL, *cur;
|
||||||
|
bool has_type = false;
|
||||||
|
int eai;
|
||||||
|
|
||||||
|
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
||||||
|
/*
|
||||||
|
* Turn off search.
|
||||||
|
*/
|
||||||
|
if (dns_name_countlabels(name) > 1U) {
|
||||||
|
strlcat(namebuf, ".", sizeof(namebuf));
|
||||||
|
}
|
||||||
|
eai = getaddrinfo(namebuf, NULL, &hints, &ai);
|
||||||
|
|
||||||
|
switch (eai) {
|
||||||
|
case 0:
|
||||||
|
cur = ai;
|
||||||
|
while (cur != NULL) {
|
||||||
|
if (cur->ai_family == AF_INET &&
|
||||||
|
type == dns_rdatatype_a)
|
||||||
|
{
|
||||||
|
has_type = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (cur->ai_family == AF_INET6 &&
|
||||||
|
type == dns_rdatatype_aaaa)
|
||||||
|
{
|
||||||
|
has_type = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cur = cur->ai_next;
|
||||||
|
}
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
return has_type;
|
||||||
|
#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
|
||||||
|
case EAI_NODATA:
|
||||||
|
#endif /* if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME) */
|
||||||
|
case EAI_NONAME:
|
||||||
|
if (!logged(namebuf, ERR_NO_ADDRESSES)) {
|
||||||
|
dns_name_format(dns_zone_getorigin(zone), ownerbuf,
|
||||||
|
sizeof(ownerbuf));
|
||||||
|
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
||||||
|
dns_zone_log(zone, ISC_LOG_ERROR,
|
||||||
|
"%s/NS '%s' (out of zone) "
|
||||||
|
"has no addresses records (A or AAAA)",
|
||||||
|
ownerbuf, namebuf);
|
||||||
|
add(namebuf, ERR_NO_ADDRESSES);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
|
||||||
|
dns_name_format(dns_zone_getorigin(zone), ownerbuf,
|
||||||
|
sizeof(ownerbuf));
|
||||||
|
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
||||||
|
dns_zone_log(zone, ISC_LOG_WARNING,
|
||||||
|
"getaddrinfo(%s) failed: %s", namebuf,
|
||||||
|
gai_strerror(eai));
|
||||||
|
add(namebuf, ERR_LOOKUP_FAILURE);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
checkns(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner,
|
checkns(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner,
|
||||||
dns_rdataset_t *a, dns_rdataset_t *aaaa) {
|
dns_rdataset_t *a, dns_rdataset_t *aaaa) {
|
||||||
dns_rdataset_t *rdataset;
|
dns_rdataset_t *rdataset;
|
||||||
dns_rdata_t rdata = DNS_RDATA_INIT;
|
dns_rdata_t rdata = DNS_RDATA_INIT;
|
||||||
struct addrinfo hints, *ai, *cur;
|
isc_result_t result;
|
||||||
|
struct addrinfo hints = {
|
||||||
|
.ai_flags = AI_CANONNAME,
|
||||||
|
.ai_family = PF_UNSPEC,
|
||||||
|
.ai_socktype = SOCK_STREAM,
|
||||||
|
.ai_protocol = IPPROTO_TCP,
|
||||||
|
};
|
||||||
|
struct addrinfo *ai = NULL, *cur;
|
||||||
char namebuf[DNS_NAME_FORMATSIZE + 1];
|
char namebuf[DNS_NAME_FORMATSIZE + 1];
|
||||||
char ownerbuf[DNS_NAME_FORMATSIZE];
|
char ownerbuf[DNS_NAME_FORMATSIZE];
|
||||||
char addrbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
|
char addrbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
|
||||||
@ -157,7 +242,7 @@ checkns(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner,
|
|||||||
bool match;
|
bool match;
|
||||||
const char *type;
|
const char *type;
|
||||||
void *ptr = NULL;
|
void *ptr = NULL;
|
||||||
int result;
|
int eai;
|
||||||
|
|
||||||
REQUIRE(a == NULL || !dns_rdataset_isassociated(a) ||
|
REQUIRE(a == NULL || !dns_rdataset_isassociated(a) ||
|
||||||
a->type == dns_rdatatype_a);
|
a->type == dns_rdatatype_a);
|
||||||
@ -168,12 +253,6 @@ checkns(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner,
|
|||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
|
||||||
hints.ai_flags = AI_CANONNAME;
|
|
||||||
hints.ai_family = PF_UNSPEC;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
hints.ai_protocol = IPPROTO_TCP;
|
|
||||||
|
|
||||||
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
||||||
/*
|
/*
|
||||||
* Turn off search.
|
* Turn off search.
|
||||||
@ -183,9 +262,9 @@ checkns(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner,
|
|||||||
}
|
}
|
||||||
dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
|
dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
|
||||||
|
|
||||||
result = getaddrinfo(namebuf, NULL, &hints, &ai);
|
eai = getaddrinfo(namebuf, NULL, &hints, &ai);
|
||||||
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
||||||
switch (result) {
|
switch (eai) {
|
||||||
case 0:
|
case 0:
|
||||||
/*
|
/*
|
||||||
* Work around broken getaddrinfo() implementations that
|
* Work around broken getaddrinfo() implementations that
|
||||||
@ -228,7 +307,7 @@ checkns(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner,
|
|||||||
if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
|
if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
|
||||||
dns_zone_log(zone, ISC_LOG_WARNING,
|
dns_zone_log(zone, ISC_LOG_WARNING,
|
||||||
"getaddrinfo(%s) failed: %s", namebuf,
|
"getaddrinfo(%s) failed: %s", namebuf,
|
||||||
gai_strerror(result));
|
gai_strerror(eai));
|
||||||
add(namebuf, ERR_LOOKUP_FAILURE);
|
add(namebuf, ERR_LOOKUP_FAILURE);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -358,25 +437,27 @@ checkmissing:
|
|||||||
add(namebuf, ERR_MISSING_GLUE);
|
add(namebuf, ERR_MISSING_GLUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
freeaddrinfo(ai);
|
if (ai != NULL) {
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
}
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
checkmx(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
|
checkmx(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
|
||||||
struct addrinfo hints, *ai, *cur;
|
struct addrinfo hints = {
|
||||||
|
.ai_flags = AI_CANONNAME,
|
||||||
|
.ai_family = PF_UNSPEC,
|
||||||
|
.ai_socktype = SOCK_STREAM,
|
||||||
|
.ai_protocol = IPPROTO_TCP,
|
||||||
|
};
|
||||||
|
struct addrinfo *ai = NULL, *cur;
|
||||||
char namebuf[DNS_NAME_FORMATSIZE + 1];
|
char namebuf[DNS_NAME_FORMATSIZE + 1];
|
||||||
char ownerbuf[DNS_NAME_FORMATSIZE];
|
char ownerbuf[DNS_NAME_FORMATSIZE];
|
||||||
int result;
|
int eai;
|
||||||
int level = ISC_LOG_ERROR;
|
int level = ISC_LOG_ERROR;
|
||||||
bool answer = true;
|
bool answer = true;
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
|
||||||
hints.ai_flags = AI_CANONNAME;
|
|
||||||
hints.ai_family = PF_UNSPEC;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
hints.ai_protocol = IPPROTO_TCP;
|
|
||||||
|
|
||||||
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
||||||
/*
|
/*
|
||||||
* Turn off search.
|
* Turn off search.
|
||||||
@ -386,9 +467,9 @@ checkmx(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
|
|||||||
}
|
}
|
||||||
dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
|
dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
|
||||||
|
|
||||||
result = getaddrinfo(namebuf, NULL, &hints, &ai);
|
eai = getaddrinfo(namebuf, NULL, &hints, &ai);
|
||||||
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
||||||
switch (result) {
|
switch (eai) {
|
||||||
case 0:
|
case 0:
|
||||||
/*
|
/*
|
||||||
* Work around broken getaddrinfo() implementations that
|
* Work around broken getaddrinfo() implementations that
|
||||||
@ -421,7 +502,9 @@ checkmx(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
freeaddrinfo(ai);
|
if (ai != NULL) {
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
}
|
||||||
return answer;
|
return answer;
|
||||||
|
|
||||||
case EAI_NONAME:
|
case EAI_NONAME:
|
||||||
@ -442,7 +525,7 @@ checkmx(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
|
|||||||
if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
|
if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
|
||||||
dns_zone_log(zone, ISC_LOG_WARNING,
|
dns_zone_log(zone, ISC_LOG_WARNING,
|
||||||
"getaddrinfo(%s) failed: %s", namebuf,
|
"getaddrinfo(%s) failed: %s", namebuf,
|
||||||
gai_strerror(result));
|
gai_strerror(eai));
|
||||||
add(namebuf, ERR_LOOKUP_FAILURE);
|
add(namebuf, ERR_LOOKUP_FAILURE);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -451,19 +534,19 @@ checkmx(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
checksrv(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
|
checksrv(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
|
||||||
struct addrinfo hints, *ai, *cur;
|
struct addrinfo hints = {
|
||||||
|
.ai_flags = AI_CANONNAME,
|
||||||
|
.ai_family = PF_UNSPEC,
|
||||||
|
.ai_socktype = SOCK_STREAM,
|
||||||
|
.ai_protocol = IPPROTO_TCP,
|
||||||
|
};
|
||||||
|
struct addrinfo *ai = NULL, *cur;
|
||||||
char namebuf[DNS_NAME_FORMATSIZE + 1];
|
char namebuf[DNS_NAME_FORMATSIZE + 1];
|
||||||
char ownerbuf[DNS_NAME_FORMATSIZE];
|
char ownerbuf[DNS_NAME_FORMATSIZE];
|
||||||
int result;
|
int eai;
|
||||||
int level = ISC_LOG_ERROR;
|
int level = ISC_LOG_ERROR;
|
||||||
bool answer = true;
|
bool answer = true;
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(hints));
|
|
||||||
hints.ai_flags = AI_CANONNAME;
|
|
||||||
hints.ai_family = PF_UNSPEC;
|
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
hints.ai_protocol = IPPROTO_TCP;
|
|
||||||
|
|
||||||
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
||||||
/*
|
/*
|
||||||
* Turn off search.
|
* Turn off search.
|
||||||
@ -473,9 +556,9 @@ checksrv(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
|
|||||||
}
|
}
|
||||||
dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
|
dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
|
||||||
|
|
||||||
result = getaddrinfo(namebuf, NULL, &hints, &ai);
|
eai = getaddrinfo(namebuf, NULL, &hints, &ai);
|
||||||
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
|
||||||
switch (result) {
|
switch (eai) {
|
||||||
case 0:
|
case 0:
|
||||||
/*
|
/*
|
||||||
* Work around broken getaddrinfo() implementations that
|
* Work around broken getaddrinfo() implementations that
|
||||||
@ -508,7 +591,9 @@ checksrv(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
freeaddrinfo(ai);
|
if (ai != NULL) {
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
}
|
||||||
return answer;
|
return answer;
|
||||||
|
|
||||||
case EAI_NONAME:
|
case EAI_NONAME:
|
||||||
@ -529,7 +614,7 @@ checksrv(dns_zone_t *zone, const dns_name_t *name, const dns_name_t *owner) {
|
|||||||
if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
|
if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
|
||||||
dns_zone_log(zone, ISC_LOG_WARNING,
|
dns_zone_log(zone, ISC_LOG_WARNING,
|
||||||
"getaddrinfo(%s) failed: %s", namebuf,
|
"getaddrinfo(%s) failed: %s", namebuf,
|
||||||
gai_strerror(result));
|
gai_strerror(eai));
|
||||||
add(namebuf, ERR_LOOKUP_FAILURE);
|
add(namebuf, ERR_LOOKUP_FAILURE);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -603,6 +688,7 @@ load_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
|
|||||||
}
|
}
|
||||||
if (docheckns) {
|
if (docheckns) {
|
||||||
dns_zone_setcheckns(zone, checkns);
|
dns_zone_setcheckns(zone, checkns);
|
||||||
|
dns_zone_setcheckisservedby(zone, checkisservedby);
|
||||||
}
|
}
|
||||||
if (dochecksrv) {
|
if (dochecksrv) {
|
||||||
dns_zone_setchecksrv(zone, checksrv);
|
dns_zone_setchecksrv(zone, checksrv);
|
||||||
|
@ -91,9 +91,13 @@ Options
|
|||||||
(both in-zone and out-of-zone hostnames). Mode ``local`` only
|
(both in-zone and out-of-zone hostnames). Mode ``local`` only
|
||||||
checks SRV records which refer to in-zone hostnames.
|
checks SRV records which refer to in-zone hostnames.
|
||||||
|
|
||||||
|
Mode ``full`` checks that a zone that has A or AAAA records it is served
|
||||||
|
by a server with the same type of address records.
|
||||||
|
|
||||||
Mode ``full`` checks that delegation NS records refer to A or AAAA
|
Mode ``full`` checks that delegation NS records refer to A or AAAA
|
||||||
records (both in-zone and out-of-zone hostnames). It also checks that
|
records (both in-zone and out-of-zone hostnames). It also checks that
|
||||||
glue address records in the zone match those advertised by the child.
|
glue address records in the zone match those advertised by the child.
|
||||||
|
|
||||||
Mode ``local`` only checks NS records which refer to in-zone
|
Mode ``local`` only checks NS records which refer to in-zone
|
||||||
hostnames or verifies that some required glue exists, i.e., when the
|
hostnames or verifies that some required glue exists, i.e., when the
|
||||||
name server is in a child zone.
|
name server is in a child zone.
|
||||||
|
@ -218,5 +218,41 @@ echo $lines
|
|||||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||||
status=$((status + ret))
|
status=$((status + ret))
|
||||||
|
|
||||||
|
echo_i "Checking for 'zone has A records but is not served by IPv4 servers' warning ($n)"
|
||||||
|
ret=0
|
||||||
|
$CHECKZONE example zones/warn.no-a.server.db >test.out1.$n 2>&1 || ret=1
|
||||||
|
grep "zone has A records but is not served by IPv4 servers" test.out1.$n >/dev/null || ret=1
|
||||||
|
grep "zone has AAAA records but is not served by IPv6 servers" test.out1.$n >/dev/null && ret=1
|
||||||
|
n=$((n + 1))
|
||||||
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||||
|
status=$((status + ret))
|
||||||
|
|
||||||
|
echo_i "Checking for 'zone has AAAA records but is not served by IPv6 servers' warning ($n)"
|
||||||
|
ret=0
|
||||||
|
$CHECKZONE example zones/warn.no-aaaa.server.db >test.out1.$n 2>&1 || ret=1
|
||||||
|
grep "zone has AAAA records but is not served by IPv6 servers" test.out1.$n >/dev/null || ret=1
|
||||||
|
grep "zone has A records but is not served by IPv4 servers" test.out1.$n >/dev/null && ret=1
|
||||||
|
n=$((n + 1))
|
||||||
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||||
|
status=$((status + ret))
|
||||||
|
|
||||||
|
echo_i "Checking for 'zone has A records but is not served by IPv4 servers' warning for glue ($n)"
|
||||||
|
ret=0
|
||||||
|
$CHECKZONE example zones/warn.no-a.server.glue.db >test.out1.$n 2>&1 || ret=1
|
||||||
|
grep "zone has A records but is not served by IPv4 servers" test.out1.$n >/dev/null || ret=1
|
||||||
|
grep "zone has AAAA records but is not served by IPv6 servers" test.out1.$n >/dev/null && ret=1
|
||||||
|
n=$((n + 1))
|
||||||
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||||
|
status=$((status + ret))
|
||||||
|
|
||||||
|
echo_i "Checking for 'zone has AAAA records but is not served by IPv6 servers' warning for glue ($n)"
|
||||||
|
ret=0
|
||||||
|
$CHECKZONE example zones/warn.no-aaaa.server.glue.db >test.out1.$n 2>&1 || ret=1
|
||||||
|
grep "zone has AAAA records but is not served by IPv6 servers" test.out1.$n >/dev/null || ret=1
|
||||||
|
grep "zone has A records but is not served by IPv4 servers" test.out1.$n >/dev/null && ret=1
|
||||||
|
n=$((n + 1))
|
||||||
|
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||||
|
status=$((status + ret))
|
||||||
|
|
||||||
echo_i "exit status: $status"
|
echo_i "exit status: $status"
|
||||||
[ $status -eq 0 ] || exit 1
|
[ $status -eq 0 ] || exit 1
|
||||||
|
22
bin/tests/system/checkzone/zones/warn.no-a.server.db
Normal file
22
bin/tests/system/checkzone/zones/warn.no-a.server.db
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
;
|
||||||
|
; SPDX-License-Identifier: MPL-2.0
|
||||||
|
;
|
||||||
|
; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
; file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
;
|
||||||
|
; See the COPYRIGHT file distributed with this work for additional
|
||||||
|
; information regarding copyright ownership.
|
||||||
|
|
||||||
|
@ 30 IN SOA ns1 hostmaster 2017052201 3600 600 604800 30
|
||||||
|
@ 7200 IN NS ns1
|
||||||
|
@ 7200 IN NS ns2
|
||||||
|
@ 7200 IN NS ns3
|
||||||
|
@ 7200 IN NS ns4
|
||||||
|
ns1 3600 IN AAAA fd92:7065:b8e:ffff::1
|
||||||
|
ns2 3600 IN AAAA fd92:7065:b8e:ffff::2
|
||||||
|
ns3 3600 IN AAAA fd92:7065:b8e:ffff::4
|
||||||
|
ns4 3600 IN AAAA fd92:7065:b8e:ffff::4
|
||||||
|
dualstack 300 IN AAAA 2001:db8::1
|
||||||
|
dualstack 300 IN A 10.53.0.5
|
23
bin/tests/system/checkzone/zones/warn.no-a.server.glue.db
Normal file
23
bin/tests/system/checkzone/zones/warn.no-a.server.glue.db
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
;
|
||||||
|
; SPDX-License-Identifier: MPL-2.0
|
||||||
|
;
|
||||||
|
; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
; file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
;
|
||||||
|
; See the COPYRIGHT file distributed with this work for additional
|
||||||
|
; information regarding copyright ownership.
|
||||||
|
|
||||||
|
@ 30 IN SOA ns1 hostmaster 2017052201 3600 600 604800 30
|
||||||
|
@ 7200 IN NS ns1
|
||||||
|
@ 7200 IN NS ns2
|
||||||
|
@ 7200 IN NS ns3
|
||||||
|
@ 7200 IN NS ns4
|
||||||
|
ns1 3600 IN AAAA fd92:7065:b8e:ffff::1
|
||||||
|
ns2 3600 IN AAAA fd92:7065:b8e:ffff::2
|
||||||
|
ns3 3600 IN AAAA fd92:7065:b8e:ffff::4
|
||||||
|
ns4 3600 IN AAAA fd92:7065:b8e:ffff::4
|
||||||
|
child 3600 IN NS ns1.child
|
||||||
|
ns1.child 300 IN AAAA 2001:db8::1
|
||||||
|
ns1.child 300 IN A 10.53.0.5
|
22
bin/tests/system/checkzone/zones/warn.no-aaaa.server.db
Normal file
22
bin/tests/system/checkzone/zones/warn.no-aaaa.server.db
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
;
|
||||||
|
; SPDX-License-Identifier: MPL-2.0
|
||||||
|
;
|
||||||
|
; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
; file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
;
|
||||||
|
; See the COPYRIGHT file distributed with this work for additional
|
||||||
|
; information regarding copyright ownership.
|
||||||
|
|
||||||
|
@ 30 IN SOA ns1 hostmaster 2017052201 3600 600 604800 30
|
||||||
|
@ 7200 IN NS ns1
|
||||||
|
@ 7200 IN NS ns2
|
||||||
|
@ 7200 IN NS ns3
|
||||||
|
@ 7200 IN NS ns4
|
||||||
|
ns1 3600 IN A 10.53.0.1
|
||||||
|
ns2 3600 IN A 10.53.0.2
|
||||||
|
ns3 3600 IN A 10.53.0.4
|
||||||
|
ns4 3600 IN A 10.53.0.4
|
||||||
|
dualstack 300 IN AAAA 2001:db8::1
|
||||||
|
dualstack 300 IN A 10.53.0.5
|
23
bin/tests/system/checkzone/zones/warn.no-aaaa.server.glue.db
Normal file
23
bin/tests/system/checkzone/zones/warn.no-aaaa.server.glue.db
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||||
|
;
|
||||||
|
; SPDX-License-Identifier: MPL-2.0
|
||||||
|
;
|
||||||
|
; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
; file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
;
|
||||||
|
; See the COPYRIGHT file distributed with this work for additional
|
||||||
|
; information regarding copyright ownership.
|
||||||
|
|
||||||
|
@ 30 IN SOA ns1 hostmaster 2017052201 3600 600 604800 30
|
||||||
|
@ 7200 IN NS ns1
|
||||||
|
@ 7200 IN NS ns2
|
||||||
|
@ 7200 IN NS ns3
|
||||||
|
@ 7200 IN NS ns4
|
||||||
|
ns1 3600 IN A 10.53.0.1
|
||||||
|
ns2 3600 IN A 10.53.0.2
|
||||||
|
ns3 3600 IN A 10.53.0.4
|
||||||
|
ns4 3600 IN A 10.53.0.4
|
||||||
|
child 3600 IN NS ns1.child
|
||||||
|
ns1.child 300 IN AAAA 2001:db8::1
|
||||||
|
ns1.child 300 IN A 10.53.0.5
|
@ -448,6 +448,9 @@ typedef bool (*dns_checknsfunc_t)(dns_zone_t *, const dns_name_t *,
|
|||||||
const dns_name_t *, dns_rdataset_t *,
|
const dns_name_t *, dns_rdataset_t *,
|
||||||
dns_rdataset_t *);
|
dns_rdataset_t *);
|
||||||
|
|
||||||
|
typedef bool (*dns_checkisservedbyfunc_t)(dns_zone_t *, dns_rdatatype_t type,
|
||||||
|
const dns_name_t *);
|
||||||
|
|
||||||
typedef bool (*dns_isselffunc_t)(dns_view_t *, dns_tsigkey_t *,
|
typedef bool (*dns_isselffunc_t)(dns_view_t *, dns_tsigkey_t *,
|
||||||
const isc_sockaddr_t *, const isc_sockaddr_t *,
|
const isc_sockaddr_t *, const isc_sockaddr_t *,
|
||||||
dns_rdataclass_t, void *);
|
dns_rdataclass_t, void *);
|
||||||
|
@ -2187,6 +2187,18 @@ dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns);
|
|||||||
* 'zone' to be a valid zone.
|
* 'zone' to be a valid zone.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
dns_zone_setcheckisservedby(dns_zone_t *zone,
|
||||||
|
dns_checkisservedbyfunc_t checkisserverby);
|
||||||
|
/*%<
|
||||||
|
* Set the post load integrity callback function 'checkisserverby'.
|
||||||
|
* 'checkisserverby' will be called if the NS TARGET is not within
|
||||||
|
* the zone and there are A or AAAA records in the the zone.
|
||||||
|
*
|
||||||
|
* Require:
|
||||||
|
* 'zone' to be a valid zone.
|
||||||
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
dns_zone_setnotifydelay(dns_zone_t *zone, uint32_t delay);
|
dns_zone_setnotifydelay(dns_zone_t *zone, uint32_t delay);
|
||||||
/*%<
|
/*%<
|
||||||
|
166
lib/dns/zone.c
166
lib/dns/zone.c
@ -377,6 +377,7 @@ struct dns_zone {
|
|||||||
dns_checkmxfunc_t checkmx;
|
dns_checkmxfunc_t checkmx;
|
||||||
dns_checksrvfunc_t checksrv;
|
dns_checksrvfunc_t checksrv;
|
||||||
dns_checknsfunc_t checkns;
|
dns_checknsfunc_t checkns;
|
||||||
|
dns_checkisservedbyfunc_t checkisservedby;
|
||||||
/*%
|
/*%
|
||||||
* Zones in certain states such as "waiting for zone transfer"
|
* Zones in certain states such as "waiting for zone transfer"
|
||||||
* or "zone transfer in progress" are kept on per-state linked lists
|
* or "zone transfer in progress" are kept on per-state linked lists
|
||||||
@ -2875,8 +2876,8 @@ zone_check_srv(dns_zone_t *zone, dns_db_t *db, dns_name_t *name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
zone_check_glue(dns_zone_t *zone, dns_db_t *db, dns_name_t *name,
|
zone_check_glue(dns_zone_t *zone, dns_db_t *db, bool *has_a, bool *has_aaaa,
|
||||||
dns_name_t *owner) {
|
dns_name_t *name, dns_name_t *owner) {
|
||||||
bool answer = true;
|
bool answer = true;
|
||||||
isc_result_t result, tresult;
|
isc_result_t result, tresult;
|
||||||
char ownerbuf[DNS_NAME_FORMATSIZE];
|
char ownerbuf[DNS_NAME_FORMATSIZE];
|
||||||
@ -2928,8 +2929,22 @@ zone_check_glue(dns_zone_t *zone, dns_db_t *db, dns_name_t *name,
|
|||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
if (result == ISC_R_SUCCESS) {
|
if (result == ISC_R_SUCCESS) {
|
||||||
|
SET_IF_NOT_NULL(has_a, true);
|
||||||
dns_rdataset_disassociate(&a);
|
dns_rdataset_disassociate(&a);
|
||||||
|
if (has_aaaa != NULL && !*has_aaaa) {
|
||||||
|
result = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
|
||||||
|
DNS_DBFIND_GLUEOK, 0, NULL,
|
||||||
|
foundname, &aaaa, NULL);
|
||||||
|
if (result == ISC_R_SUCCESS) {
|
||||||
|
*has_aaaa = true;
|
||||||
|
}
|
||||||
|
if (dns_rdataset_isassociated(&aaaa)) {
|
||||||
|
dns_rdataset_disassociate(&aaaa);
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
|
} else if (result == DNS_R_GLUE && has_a != NULL) {
|
||||||
|
*has_a = true;
|
||||||
} else if (result == DNS_R_DELEGATION) {
|
} else if (result == DNS_R_DELEGATION) {
|
||||||
dns_rdataset_disassociate(&a);
|
dns_rdataset_disassociate(&a);
|
||||||
}
|
}
|
||||||
@ -2944,12 +2959,16 @@ zone_check_glue(dns_zone_t *zone, dns_db_t *db, dns_name_t *name,
|
|||||||
if (dns_rdataset_isassociated(&a)) {
|
if (dns_rdataset_isassociated(&a)) {
|
||||||
dns_rdataset_disassociate(&a);
|
dns_rdataset_disassociate(&a);
|
||||||
}
|
}
|
||||||
|
SET_IF_NOT_NULL(has_aaaa, true);
|
||||||
dns_rdataset_disassociate(&aaaa);
|
dns_rdataset_disassociate(&aaaa);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (tresult == DNS_R_DELEGATION || tresult == DNS_R_DNAME) {
|
if (tresult == DNS_R_DELEGATION || tresult == DNS_R_DNAME) {
|
||||||
dns_rdataset_disassociate(&aaaa);
|
dns_rdataset_disassociate(&aaaa);
|
||||||
}
|
}
|
||||||
|
if (tresult == DNS_R_GLUE && has_aaaa != NULL) {
|
||||||
|
*has_aaaa = true;
|
||||||
|
}
|
||||||
if (result == DNS_R_GLUE || tresult == DNS_R_GLUE) {
|
if (result == DNS_R_GLUE || tresult == DNS_R_GLUE) {
|
||||||
/*
|
/*
|
||||||
* Check glue against child zone.
|
* Check glue against child zone.
|
||||||
@ -3173,6 +3192,46 @@ isspf(const dns_rdata_t *rdata) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
zone_is_served_by(dns_zone_t *zone, dns_db_t *db, dns_rdatatype_t type,
|
||||||
|
dns_name_t *name) {
|
||||||
|
dns_rdataset_t rdataset;
|
||||||
|
dns_fixedname_t found;
|
||||||
|
dns_name_t *foundname = dns_fixedname_initname(&found);
|
||||||
|
isc_result_t result;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Outside of zone, assume good when loading in named.
|
||||||
|
*/
|
||||||
|
if (!dns_name_issubdomain(name, &zone->origin)) {
|
||||||
|
if (zone->checkisservedby != NULL) {
|
||||||
|
return zone->checkisservedby(zone, type, name);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
dns_rdataset_init(&rdataset);
|
||||||
|
result = dns_db_find(db, name, NULL, type, 0, 0, NULL, foundname,
|
||||||
|
&rdataset, NULL);
|
||||||
|
if (dns_rdataset_isassociated(&rdataset)) {
|
||||||
|
dns_rdataset_disassociate(&rdataset);
|
||||||
|
}
|
||||||
|
switch (result) {
|
||||||
|
case DNS_R_DELEGATION:
|
||||||
|
if (zone->checkisservedby != NULL) {
|
||||||
|
return zone->checkisservedby(zone, type, name);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Treat as success.
|
||||||
|
*/
|
||||||
|
return true;
|
||||||
|
case ISC_R_SUCCESS:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
integrity_checks(dns_zone_t *zone, dns_db_t *db) {
|
integrity_checks(dns_zone_t *zone, dns_db_t *db) {
|
||||||
dns_dbiterator_t *dbiterator = NULL;
|
dns_dbiterator_t *dbiterator = NULL;
|
||||||
@ -3188,6 +3247,8 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) {
|
|||||||
dns_name_t *bottom;
|
dns_name_t *bottom;
|
||||||
isc_result_t result;
|
isc_result_t result;
|
||||||
bool ok = true, have_spf, have_txt;
|
bool ok = true, have_spf, have_txt;
|
||||||
|
bool has_a = false;
|
||||||
|
bool has_aaaa = false;
|
||||||
int level;
|
int level;
|
||||||
char namebuf[DNS_NAME_FORMATSIZE];
|
char namebuf[DNS_NAME_FORMATSIZE];
|
||||||
|
|
||||||
@ -3242,7 +3303,9 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) {
|
|||||||
dns_rdataset_current(&rdataset, &rdata);
|
dns_rdataset_current(&rdataset, &rdata);
|
||||||
result = dns_rdata_tostruct(&rdata, &ns, NULL);
|
result = dns_rdata_tostruct(&rdata, &ns, NULL);
|
||||||
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||||
if (!zone_check_glue(zone, db, &ns.name, name)) {
|
if (!zone_check_glue(zone, db, &has_a, &has_aaaa,
|
||||||
|
&ns.name, name))
|
||||||
|
{
|
||||||
ok = false;
|
ok = false;
|
||||||
}
|
}
|
||||||
dns_rdata_reset(&rdata);
|
dns_rdata_reset(&rdata);
|
||||||
@ -3306,7 +3369,7 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) {
|
|||||||
result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_srv,
|
result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_srv,
|
||||||
0, 0, &rdataset, NULL);
|
0, 0, &rdataset, NULL);
|
||||||
if (result != ISC_R_SUCCESS) {
|
if (result != ISC_R_SUCCESS) {
|
||||||
goto checkspf;
|
goto checkforaaaa;
|
||||||
}
|
}
|
||||||
result = dns_rdataset_first(&rdataset);
|
result = dns_rdataset_first(&rdataset);
|
||||||
while (result == ISC_R_SUCCESS) {
|
while (result == ISC_R_SUCCESS) {
|
||||||
@ -3321,7 +3384,29 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) {
|
|||||||
}
|
}
|
||||||
dns_rdataset_disassociate(&rdataset);
|
dns_rdataset_disassociate(&rdataset);
|
||||||
|
|
||||||
checkspf:
|
checkforaaaa:
|
||||||
|
/*
|
||||||
|
* Check if there is an A or AAAA RRset in the zone.
|
||||||
|
*/
|
||||||
|
if (!has_a) {
|
||||||
|
result = dns_db_findrdataset(db, node, NULL,
|
||||||
|
dns_rdatatype_a, 0, 0,
|
||||||
|
&rdataset, NULL);
|
||||||
|
if (result == ISC_R_SUCCESS) {
|
||||||
|
has_a = true;
|
||||||
|
dns_rdataset_disassociate(&rdataset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!has_aaaa) {
|
||||||
|
result = dns_db_findrdataset(db, node, NULL,
|
||||||
|
dns_rdatatype_aaaa, 0, 0,
|
||||||
|
&rdataset, NULL);
|
||||||
|
if (result == ISC_R_SUCCESS) {
|
||||||
|
has_aaaa = true;
|
||||||
|
dns_rdataset_disassociate(&rdataset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check if there is a type SPF record without an
|
* Check if there is a type SPF record without an
|
||||||
* SPF-formatted type TXT record also being present.
|
* SPF-formatted type TXT record also being present.
|
||||||
@ -3371,6 +3456,70 @@ integrity_checks(dns_zone_t *zone, dns_db_t *db) {
|
|||||||
result = dns_dbiterator_next(dbiterator);
|
result = dns_dbiterator_next(dbiterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (has_a) {
|
||||||
|
has_a = false;
|
||||||
|
result = dns_db_find(db, &zone->origin, NULL, dns_rdatatype_ns,
|
||||||
|
0, 0, NULL, name, &rdataset, NULL);
|
||||||
|
if (result != ISC_R_SUCCESS) {
|
||||||
|
if (dns_rdataset_isassociated(&rdataset)) {
|
||||||
|
dns_rdataset_disassociate(&rdataset);
|
||||||
|
}
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
result = dns_rdataset_first(&rdataset);
|
||||||
|
while (result == ISC_R_SUCCESS) {
|
||||||
|
dns_rdataset_current(&rdataset, &rdata);
|
||||||
|
result = dns_rdata_tostruct(&rdata, &ns, NULL);
|
||||||
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||||
|
dns_rdata_reset(&rdata);
|
||||||
|
if (zone_is_served_by(zone, db, dns_rdatatype_a,
|
||||||
|
&ns.name))
|
||||||
|
{
|
||||||
|
has_a = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = dns_rdataset_next(&rdataset);
|
||||||
|
}
|
||||||
|
dns_rdataset_disassociate(&rdataset);
|
||||||
|
if (!has_a) {
|
||||||
|
dns_zone_log(zone, ISC_LOG_WARNING,
|
||||||
|
"zone has A records but is not served "
|
||||||
|
"by IPv4 servers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_aaaa) {
|
||||||
|
has_aaaa = false;
|
||||||
|
result = dns_db_find(db, &zone->origin, NULL, dns_rdatatype_ns,
|
||||||
|
0, 0, NULL, name, &rdataset, NULL);
|
||||||
|
if (result != ISC_R_SUCCESS) {
|
||||||
|
if (dns_rdataset_isassociated(&rdataset)) {
|
||||||
|
dns_rdataset_disassociate(&rdataset);
|
||||||
|
}
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
result = dns_rdataset_first(&rdataset);
|
||||||
|
while (result == ISC_R_SUCCESS) {
|
||||||
|
dns_rdataset_current(&rdataset, &rdata);
|
||||||
|
result = dns_rdata_tostruct(&rdata, &ns, NULL);
|
||||||
|
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||||
|
dns_rdata_reset(&rdata);
|
||||||
|
if (zone_is_served_by(zone, db, dns_rdatatype_aaaa,
|
||||||
|
&ns.name))
|
||||||
|
{
|
||||||
|
has_aaaa = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
result = dns_rdataset_next(&rdataset);
|
||||||
|
}
|
||||||
|
dns_rdataset_disassociate(&rdataset);
|
||||||
|
if (!has_aaaa) {
|
||||||
|
dns_zone_log(zone, ISC_LOG_WARNING,
|
||||||
|
"zone has AAAA records but is not served "
|
||||||
|
"by IPv6 servers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (node != NULL) {
|
if (node != NULL) {
|
||||||
dns_db_detachnode(db, &node);
|
dns_db_detachnode(db, &node);
|
||||||
@ -20092,6 +20241,13 @@ dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns) {
|
|||||||
zone->checkns = checkns;
|
zone->checkns = checkns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
dns_zone_setcheckisservedby(dns_zone_t *zone,
|
||||||
|
dns_checkisservedbyfunc_t checkisservedby) {
|
||||||
|
REQUIRE(DNS_ZONE_VALID(zone));
|
||||||
|
zone->checkisservedby = checkisservedby;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg) {
|
dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg) {
|
||||||
REQUIRE(DNS_ZONE_VALID(zone));
|
REQUIRE(DNS_ZONE_VALID(zone));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user