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
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 *rdataset;
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 ownerbuf[DNS_NAME_FORMATSIZE];
char addrbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
bool match;
const char *type;
void *ptr = NULL;
- int result;
+ int eai;
REQUIRE(a == NULL || !dns_rdataset_isassociated(a) ||
a->type == dns_rdatatype_a);
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);
/*
* Turn off search.
}
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);
- switch (result) {
+ switch (eai) {
case 0:
/*
* Work around broken getaddrinfo() implementations that
if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
dns_zone_log(zone, ISC_LOG_WARNING,
"getaddrinfo(%s) failed: %s", namebuf,
- gai_strerror(result));
+ gai_strerror(eai));
add(namebuf, ERR_LOOKUP_FAILURE);
}
return true;
add(namebuf, ERR_MISSING_GLUE);
}
}
- freeaddrinfo(ai);
+ if (ai != NULL) {
+ freeaddrinfo(ai);
+ }
return answer;
}
static bool
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 ownerbuf[DNS_NAME_FORMATSIZE];
- int result;
+ int eai;
int level = ISC_LOG_ERROR;
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);
/*
* Turn off search.
}
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);
- switch (result) {
+ switch (eai) {
case 0:
/*
* Work around broken getaddrinfo() implementations that
}
}
}
- freeaddrinfo(ai);
+ if (ai != NULL) {
+ freeaddrinfo(ai);
+ }
return answer;
case EAI_NONAME:
if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
dns_zone_log(zone, ISC_LOG_WARNING,
"getaddrinfo(%s) failed: %s", namebuf,
- gai_strerror(result));
+ gai_strerror(eai));
add(namebuf, ERR_LOOKUP_FAILURE);
}
return true;
static bool
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 ownerbuf[DNS_NAME_FORMATSIZE];
- int result;
+ int eai;
int level = ISC_LOG_ERROR;
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);
/*
* Turn off search.
}
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);
- switch (result) {
+ switch (eai) {
case 0:
/*
* Work around broken getaddrinfo() implementations that
}
}
}
- freeaddrinfo(ai);
+ if (ai != NULL) {
+ freeaddrinfo(ai);
+ }
return answer;
case EAI_NONAME:
if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
dns_zone_log(zone, ISC_LOG_WARNING,
"getaddrinfo(%s) failed: %s", namebuf,
- gai_strerror(result));
+ gai_strerror(eai));
add(namebuf, ERR_LOOKUP_FAILURE);
}
return true;
}
if (docheckns) {
dns_zone_setcheckns(zone, checkns);
+ dns_zone_setcheckisservedby(zone, checkisservedby);
}
if (dochecksrv) {
dns_zone_setchecksrv(zone, checksrv);
(both in-zone and out-of-zone hostnames). Mode ``local`` only
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
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.
+
Mode ``local`` only checks NS records which refer to in-zone
hostnames or verifies that some required glue exists, i.e., when the
name server is in a child zone.
const dns_name_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 *,
const isc_sockaddr_t *, const isc_sockaddr_t *,
dns_rdataclass_t, void *);
* '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
dns_zone_setnotifydelay(dns_zone_t *zone, uint32_t delay);
/*%<
dns_checkmxfunc_t checkmx;
dns_checksrvfunc_t checksrv;
dns_checknsfunc_t checkns;
+ dns_checkisservedbyfunc_t checkisservedby;
/*%
* Zones in certain states such as "waiting for zone transfer"
* or "zone transfer in progress" are kept on per-state linked lists
}
static bool
-zone_check_glue(dns_zone_t *zone, dns_db_t *db, dns_name_t *name,
- dns_name_t *owner) {
+zone_check_glue(dns_zone_t *zone, dns_db_t *db, bool *has_a, bool *has_aaaa,
+ dns_name_t *name, dns_name_t *owner) {
bool answer = true;
isc_result_t result, tresult;
char ownerbuf[DNS_NAME_FORMATSIZE];
NULL);
}
if (result == ISC_R_SUCCESS) {
+ SET_IF_NOT_NULL(has_a, true);
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;
+ } else if (result == DNS_R_GLUE && has_a != NULL) {
+ *has_a = true;
} else if (result == DNS_R_DELEGATION) {
dns_rdataset_disassociate(&a);
}
if (dns_rdataset_isassociated(&a)) {
dns_rdataset_disassociate(&a);
}
+ SET_IF_NOT_NULL(has_aaaa, true);
dns_rdataset_disassociate(&aaaa);
return true;
}
if (tresult == DNS_R_DELEGATION || tresult == DNS_R_DNAME) {
dns_rdataset_disassociate(&aaaa);
}
+ if (tresult == DNS_R_GLUE && has_aaaa != NULL) {
+ *has_aaaa = true;
+ }
if (result == DNS_R_GLUE || tresult == DNS_R_GLUE) {
/*
* Check glue against child zone.
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
integrity_checks(dns_zone_t *zone, dns_db_t *db) {
dns_dbiterator_t *dbiterator = NULL;
dns_name_t *bottom;
isc_result_t result;
bool ok = true, have_spf, have_txt;
+ bool has_a = false;
+ bool has_aaaa = false;
int level;
char namebuf[DNS_NAME_FORMATSIZE];
dns_rdataset_current(&rdataset, &rdata);
result = dns_rdata_tostruct(&rdata, &ns, NULL);
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;
}
dns_rdata_reset(&rdata);
result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_srv,
0, 0, &rdataset, NULL);
if (result != ISC_R_SUCCESS) {
- goto checkspf;
+ goto checkforaaaa;
}
result = dns_rdataset_first(&rdataset);
while (result == ISC_R_SUCCESS) {
}
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
* SPF-formatted type TXT record also being present.
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:
if (node != NULL) {
dns_db_detachnode(db, &node);
zone->checkns = checkns;
}
+void
+dns_zone_setcheckisservedby(dns_zone_t *zone,
+ dns_checkisservedbyfunc_t checkisservedby) {
+ REQUIRE(DNS_ZONE_VALID(zone));
+ zone->checkisservedby = checkisservedby;
+}
+
void
dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg) {
REQUIRE(DNS_ZONE_VALID(zone));