Commit c5223c9c authored by Mark Andrews's avatar Mark Andrews

1862. [func] Add additional zone data constancy checks.

                        named-checkzone has extended checking of NS, MX and
                        SRV record and the hosts they reference.
                        named has extended post zone load checks.
                        New zone options: check-mx and integrity-check.
                        [RT #4940]
parent d73541ea
1862. [func] Add additional zone data constancy checks.
named-checkzone has extended checking of NS, MX and
SRV record and the hosts they reference.
named has extended post zone load checks.
New zone options: check-mx and integrity-check.
[RT #4940]
1861. [placeholder] rt14801
1860. [placeholder] rt14775
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: check-tool.c,v 1.16 2005/04/27 04:55:42 sra Exp $ */
/* $Id: check-tool.c,v 1.17 2005/05/19 04:58:59 marka Exp $ */
/*! \file */
......@@ -29,6 +29,8 @@
#include <isc/buffer.h>
#include <isc/log.h>
#include <isc/net.h>
#include <isc/netdb.h>
#include <isc/region.h>
#include <isc/stdio.h>
#include <isc/types.h>
......@@ -36,24 +38,39 @@
#include <dns/fixedname.h>
#include <dns/log.h>
#include <dns/name.h>
#include <dns/rdata.h>
#include <dns/rdataclass.h>
#include <dns/rdataset.h>
#include <dns/types.h>
#include <dns/zone.h>
#ifdef HAVE_ADDRINFO
#ifdef HAVE_GETADDRINFO
#ifdef HAVE_GAISTRERROR
#define USE_GETADDRINFO
#endif
#endif
#endif
#define CHECK(r) \
do { \
do { \
result = (r); \
if (result != ISC_R_SUCCESS) \
goto cleanup; \
} while (0)
if (result != ISC_R_SUCCESS) \
goto cleanup; \
} while (0)
static const char *dbtype[] = { "rbt" };
int debug = 0;
isc_boolean_t nomerge = ISC_TRUE;
isc_boolean_t docheckmx = ISC_TRUE;
isc_boolean_t dochecksrv = ISC_TRUE;
isc_boolean_t docheckns = ISC_TRUE;
unsigned int zone_options = DNS_ZONEOPT_CHECKNS |
DNS_ZONEOPT_CHECKMX |
DNS_ZONEOPT_MANYERRORS |
DNS_ZONEOPT_CHECKNAMES |
DNS_ZONEOPT_INTEGRITYCHECK |
DNS_ZONEOPT_CHECKWILDCARD;
/*
......@@ -70,6 +87,279 @@ static isc_logcategory_t categories[] = {
{ NULL, 0 }
};
static isc_boolean_t
checkns(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner,
dns_rdataset_t *a, dns_rdataset_t *aaaa)
{
#ifdef USE_GETADDRINFO
dns_rdataset_t *rdataset;
dns_rdata_t rdata = DNS_RDATA_INIT;
struct addrinfo hints, *ai, *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")];
isc_boolean_t answer = ISC_TRUE;
isc_boolean_t match;
const char *type;
void *ptr = NULL;
int result;
REQUIRE(a == NULL || !dns_rdataset_isassociated(a) ||
a->type == dns_rdatatype_a);
REQUIRE(aaaa == NULL || !dns_rdataset_isassociated(aaaa) ||
aaaa->type == dns_rdatatype_aaaa);
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.
*/
if (dns_name_countlabels(name) > 1U)
strcat(namebuf, ".");
dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
result = getaddrinfo(namebuf, NULL, &hints, &ai);
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
switch (result) {
case 0:
if (strcasecmp(ai->ai_canonname, namebuf) != 0) {
dns_zone_log(zone, ISC_LOG_ERROR,
"%s/NS '%s' (out of zone) "
"is a CNAME (illegal)",
ownerbuf, namebuf);
answer = ISC_FALSE;
}
break;
case EAI_NONAME:
#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
case EAI_NODATA:
#endif
dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' (out of zone) "
"has no addresses records (A or AAAA)",
ownerbuf, namebuf);
return (ISC_FALSE);
default:
dns_zone_log(zone, ISC_LOG_WARNING,
"getaddrinfo(%s) failed: %s",
namebuf, gai_strerror(result));
return (ISC_TRUE);
}
if (a == NULL || aaaa == NULL)
return (answer);
/*
* Check that all glue records really exist.
*/
if (!dns_rdataset_isassociated(a))
goto checkaaaa;
result = dns_rdataset_first(a);
while (result == ISC_R_SUCCESS) {
dns_rdataset_current(a, &rdata);
match = ISC_FALSE;
for (cur = ai; cur != NULL; cur = cur->ai_next) {
if (cur->ai_family != AF_INET)
continue;
ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
if (memcmp(ptr, rdata.data, rdata.length) == 0) {
match = ISC_TRUE;
break;
}
}
if (!match) {
dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
"extra GLUE A record (%s)",
ownerbuf, namebuf,
inet_ntop(AF_INET, rdata.data,
addrbuf, sizeof(addrbuf)));
answer = ISC_FALSE;
}
dns_rdata_reset(&rdata);
result = dns_rdataset_next(a);
}
checkaaaa:
if (!dns_rdataset_isassociated(aaaa))
goto checkmissing;
result = dns_rdataset_first(aaaa);
while (result == ISC_R_SUCCESS) {
dns_rdataset_current(aaaa, &rdata);
match = ISC_FALSE;
for (cur = ai; cur != NULL; cur = cur->ai_next) {
if (cur->ai_family != AF_INET6)
continue;
ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
if (memcmp(ptr, rdata.data, rdata.length) == 0) {
match = ISC_TRUE;
break;
}
}
if (!match) {
dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
"extra GLUE AAAA record (%s)",
ownerbuf, namebuf,
inet_ntop(AF_INET6, rdata.data,
addrbuf, sizeof(addrbuf)));
answer = ISC_FALSE;
}
dns_rdata_reset(&rdata);
result = dns_rdataset_next(aaaa);
}
checkmissing:
/*
* Check that all addresses appear in the glue.
*/
for (cur = ai; cur != NULL; cur = cur->ai_next) {
switch (cur->ai_family) {
case AF_INET:
rdataset = a;
ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
type = "A";
break;
case AF_INET6:
rdataset = aaaa;
ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
type = "AAAA";
break;
default:
continue;
}
match = ISC_FALSE;
if (dns_rdataset_isassociated(rdataset))
result = dns_rdataset_first(rdataset);
else
result = ISC_R_FAILURE;
while (result == ISC_R_SUCCESS && !match) {
dns_rdataset_current(rdataset, &rdata);
if (memcmp(ptr, rdata.data, rdata.length) == 0)
match = ISC_TRUE;
dns_rdata_reset(&rdata);
result = dns_rdataset_next(rdataset);
}
if (!match) {
dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
"missing GLUE %s record (%s)",
ownerbuf, namebuf, type,
inet_ntop(cur->ai_family, ptr,
addrbuf, sizeof(addrbuf)));
answer = ISC_FALSE;
}
}
freeaddrinfo(ai);
return (answer);
#else
return (ISC_TRUE);
#endif
}
static isc_boolean_t
checkmx(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner) {
#ifdef USE_GETADDRINFO
struct addrinfo hints, *ai;
char namebuf[DNS_NAME_FORMATSIZE + 1];
char ownerbuf[DNS_NAME_FORMATSIZE];
int result;
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.
*/
if (dns_name_countlabels(name) > 1U)
strcat(namebuf, ".");
dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
result = getaddrinfo(namebuf, NULL, &hints, &ai);
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
switch (result) {
case 0:
if (strcasecmp(ai->ai_canonname, namebuf) != 0)
dns_zone_log(zone, ISC_LOG_WARNING,
"%s/MX '%s' (out of zone) "
"is a CNAME (illegal)",
ownerbuf, namebuf);
freeaddrinfo(ai);
break;
case EAI_NONAME:
#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
case EAI_NODATA:
#endif
dns_zone_log(zone, ISC_LOG_ERROR, "%s/MX '%s' (out of zone) "
"has no addresses records (A or AAAA)",
ownerbuf, namebuf);
return (ISC_FALSE);
default:
dns_zone_log(zone, ISC_LOG_WARNING,
"getaddrinfo(%s) failed: %s",
namebuf, gai_strerror(result));
return (ISC_TRUE);
}
#endif
return (ISC_TRUE);
}
static isc_boolean_t
checksrv(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner) {
#ifdef USE_GETADDRINFO
struct addrinfo hints, *ai;
char namebuf[DNS_NAME_FORMATSIZE + 1];
char ownerbuf[DNS_NAME_FORMATSIZE];
int result;
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.
*/
if (dns_name_countlabels(name) > 1U)
strcat(namebuf, ".");
dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
result = getaddrinfo(namebuf, NULL, &hints, &ai);
dns_name_format(name, namebuf, sizeof(namebuf) - 1);
switch (result) {
case 0:
if (strcasecmp(ai->ai_canonname, namebuf) != 0)
dns_zone_log(zone, ISC_LOG_WARNING,
"%s/SRV '%s' (out of zone) "
"is a CNAME (illegal)",
ownerbuf, namebuf);
freeaddrinfo(ai);
break;
case EAI_NONAME:
#if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
case EAI_NODATA:
#endif
dns_zone_log(zone, ISC_LOG_ERROR, "%s/SRV '%s' (out of zone) "
"has no addresses records (A or AAAA)",
ownerbuf, namebuf);
return (ISC_FALSE);
default:
dns_zone_log(zone, ISC_LOG_WARNING,
"getaddrinfo(%s) failed: %s",
namebuf, gai_strerror(result));
return (ISC_TRUE);
}
#endif
return (ISC_TRUE);
}
isc_result_t
setup_logging(isc_mem_t *mctx, isc_log_t **logp) {
isc_logdestination_t destination;
......@@ -124,7 +414,7 @@ load_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
dns_fixedname_init(&fixorigin);
origin = dns_fixedname_name(&fixorigin);
CHECK(dns_name_fromtext(origin, &buffer, dns_rootname,
ISC_FALSE, NULL));
ISC_FALSE, NULL));
CHECK(dns_zone_setorigin(zone, origin));
CHECK(dns_zone_setdbtype(zone, 1, (const char * const *) dbtype));
CHECK(dns_zone_setfile(zone, filename));
......@@ -136,6 +426,12 @@ load_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
dns_zone_setclass(zone, rdclass);
dns_zone_setoption(zone, zone_options, ISC_TRUE);
dns_zone_setoption(zone, DNS_ZONEOPT_NOMERGE, nomerge);
if (docheckmx)
dns_zone_setcheckmx(zone, checkmx);
if (docheckns)
dns_zone_setcheckns(zone, checkns);
if (dochecksrv)
dns_zone_setchecksrv(zone, checksrv);
CHECK(dns_zone_load(zone));
if (zonep != NULL){
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: check-tool.h,v 1.9 2005/04/29 00:22:24 marka Exp $ */
/* $Id: check-tool.h,v 1.10 2005/05/19 04:58:59 marka Exp $ */
#ifndef CHECK_TOOL_H
#define CHECK_TOOL_H
......@@ -41,6 +41,9 @@ dump_zone(const char *zonename, dns_zone_t *zone, const char *filename);
extern int debug;
extern isc_boolean_t nomerge;
extern isc_boolean_t docheckmx;
extern isc_boolean_t docheckns;
extern isc_boolean_t dochecksrv;
extern unsigned int zone_options;
ISC_LANG_ENDDECLS
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: named-checkconf.c,v 1.31 2005/04/27 04:55:43 sra Exp $ */
/* $Id: named-checkconf.c,v 1.32 2005/05/19 04:58:59 marka Exp $ */
/*! \file */
......@@ -41,11 +41,14 @@
#include <dns/fixedname.h>
#include <dns/log.h>
#include <dns/name.h>
#include <dns/result.h>
#include <dns/zone.h>
#include "check-tool.h"
isc_log_t *logc = NULL;
static isc_entropy_t *ectx = NULL;
#define CHECK(r)\
do { \
......@@ -88,10 +91,54 @@ directory_callback(const char *clausename, cfg_obj_t *obj, void *arg) {
return (ISC_R_SUCCESS);
}
static isc_boolean_t
get_maps(cfg_obj_t **maps, const char *name, cfg_obj_t **obj) {
int i;
for (i = 0;; i++) {
if (maps[i] == NULL)
return (ISC_FALSE);
if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS)
return (ISC_TRUE);
}
}
static isc_boolean_t
get_checknames(cfg_obj_t **maps, cfg_obj_t **obj) {
cfg_listelt_t *element;
cfg_obj_t *checknames;
cfg_obj_t *type;
cfg_obj_t *value;
isc_result_t result;
int i;
for (i = 0;; i++) {
if (maps[i] == NULL)
return (ISC_FALSE);
checknames = NULL;
result = cfg_map_get(maps[i], "check-names", &checknames);
if (result != ISC_R_SUCCESS)
continue;
if (checknames != NULL && !cfg_obj_islist(checknames)) {
*obj = checknames;
return (ISC_TRUE);
}
for (element = cfg_list_first(checknames);
element != NULL;
element = cfg_list_next(element)) {
value = cfg_listelt_value(element);
type = cfg_tuple_get(value, "type");
if (strcasecmp(cfg_obj_asstring(type), "master") != 0)
continue;
*obj = cfg_tuple_get(value, "mode");
return (ISC_TRUE);
}
}
}
/*% configure the zone */
static isc_result_t
configure_zone(const char *vclass, const char *view, cfg_obj_t *zconfig,
isc_mem_t *mctx)
cfg_obj_t *vconfig, cfg_obj_t *config, isc_mem_t *mctx)
{
isc_result_t result;
const char *zclass;
......@@ -102,6 +149,13 @@ configure_zone(const char *vclass, const char *view, cfg_obj_t *zconfig,
cfg_obj_t *typeobj = NULL;
cfg_obj_t *fileobj = NULL;
cfg_obj_t *dbobj = NULL;
cfg_obj_t *obj = NULL;
cfg_obj_t *maps[4];
int i = 0;
zone_options = DNS_ZONEOPT_CHECKNS |
DNS_ZONEOPT_MANYERRORS |
DNS_ZONEOPT_INTEGRITYCHECK;
zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
classobj = cfg_tuple_get(zconfig, "class");
......@@ -109,7 +163,18 @@ configure_zone(const char *vclass, const char *view, cfg_obj_t *zconfig,
zclass = vclass;
else
zclass = cfg_obj_asstring(classobj);
zoptions = cfg_tuple_get(zconfig, "options");
maps[i++] = zoptions;
if (vconfig != NULL)
maps[i++] = cfg_tuple_get(vconfig, "options");
if (config != NULL) {
cfg_map_get(config, "options", &obj);
if (obj != NULL)
maps[i++] = obj;
}
maps[i++] = NULL;
cfg_map_get(zoptions, "type", &typeobj);
if (typeobj == NULL)
return (ISC_R_FAILURE);
......@@ -122,6 +187,43 @@ configure_zone(const char *vclass, const char *view, cfg_obj_t *zconfig,
if (fileobj == NULL)
return (ISC_R_FAILURE);
zfile = cfg_obj_asstring(fileobj);
obj = NULL;
if (get_maps(maps, "check-mx", &obj)) {
if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
zone_options |= DNS_ZONEOPT_CHECKMX;
zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
zone_options |= DNS_ZONEOPT_CHECKMX;
zone_options |= DNS_ZONEOPT_CHECKMXFAIL;
} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
zone_options &= ~DNS_ZONEOPT_CHECKMX;
zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
} else
INSIST(0);
} else {
zone_options |= DNS_ZONEOPT_CHECKMX;
zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
}
obj = NULL;
if (get_checknames(maps, &obj)) {
if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
zone_options |= DNS_ZONEOPT_CHECKNAMES;
zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
} else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
zone_options |= DNS_ZONEOPT_CHECKNAMES;
zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL;
} else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
zone_options &= ~DNS_ZONEOPT_CHECKNAMES;
zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
} else
INSIST(0);
} else {
zone_options |= DNS_ZONEOPT_CHECKNAMES;
zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL;
}
result = load_zone(mctx, zname, zfile, zclass, NULL);
if (result != ISC_R_SUCCESS)
fprintf(stderr, "%s/%s/%s: %s\n", view, zname, zclass,
......@@ -155,7 +257,8 @@ configure_view(const char *vclass, const char *view, cfg_obj_t *config,
element = cfg_list_next(element))
{
cfg_obj_t *zconfig = cfg_listelt_value(element);
tresult = configure_zone(vclass, view, zconfig, mctx);
tresult = configure_zone(vclass, view, zconfig, vconfig,
config, mctx);
if (tresult != ISC_R_SUCCESS)
result = tresult;
}
......@@ -248,6 +351,9 @@ main(int argc, char **argv) {
case 'z':
load_zones = ISC_TRUE;
docheckmx = ISC_FALSE;
docheckns = ISC_FALSE;
dochecksrv = ISC_FALSE;
break;
default:
......@@ -268,6 +374,10 @@ main(int argc, char **argv) {
RUNTIME_CHECK(isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE)
== ISC_R_SUCCESS);
RUNTIME_CHECK(isc_entropy_create(mctx, &ectx) == ISC_R_SUCCESS);
RUNTIME_CHECK(isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE)
== ISC_R_SUCCESS);
dns_result_register();
RUNTIME_CHECK(cfg_parser_create(mctx, logc, &parser) == ISC_R_SUCCESS);
......@@ -294,6 +404,8 @@ main(int argc, char **argv) {
cfg_parser_destroy(&parser);
isc_hash_destroy();
isc_log_destroy(&logc);
isc_hash_destroy();
......
......@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: named-checkzone.c,v 1.34 2005/04/27 04:55:43 sra Exp $ */
/* $Id: named-checkzone.c,v 1.35 2005/05/19 04:58:59 marka Exp $ */
/*! \file */
......@@ -39,6 +39,7 @@
#include <dns/db.h>
#include <dns/fixedname.h>
#include <dns/log.h>
#include <dns/name.h>
#include <dns/rdataclass.h>
#include <dns/rdataset.h>
#include <dns/result.h>
......@@ -69,7 +70,9 @@ usage(void) {
fprintf(stderr,
"usage: named-checkzone [-djqvD] [-c class] [-o output] "
"[-t directory] [-w directory] [-k (ignore|warn|fail)] "
"[-n (ignore|warn|fail)] [-W (ignore|warn)] zonename filename\n");
"[-n (ignore|warn|fail)] [-m (ignore|warn|fail)] "
"[-i (full|local|none)] [-W (ignore|warn)] "
"zonename filename\n");
exit(1);
}
......@@ -91,7 +94,8 @@ main(int argc, char **argv) {
char *classname = classname_in;
const char *workdir = NULL;
while ((c = isc_commandline_parse(argc, argv, "c:dijk:n:qst:o:vw:DW:")) != EOF) {
while ((c = isc_commandline_parse(argc, argv,
"c:di:jk:m:n:qst:o:vw:DW:")) != EOF) {
switch (c) {
case 'c':
classname = isc_commandline_argument;
......@@ -101,34 +105,87 @@ main(int argc, char **argv) {
debug++;
break;
case 'i':
if (!strcmp(isc_commandline_argument, "full")) {
zone_options |= DNS_ZONEOPT_INTEGRITYCHECK;
docheckmx = ISC_TRUE;
docheckns = ISC_TRUE;
dochecksrv = ISC_TRUE;
} else if (!strcmp(isc_commandline_argument,
"local")) {
zone_options |= DNS_ZONEOPT_INTEGRITYCHECK;
docheckmx = ISC_FALSE;
docheckns = ISC_FALSE;
dochecksrv = ISC_FALSE;
} else if (!strcmp(isc_commandline_argument,
"none")) {
zone_options &= ~DNS_ZONEOPT_INTEGRITYCHECK;
docheckmx = ISC_FALSE;
docheckns = ISC_FALSE;
dochecksrv = ISC_FALSE;