From c5223c9cb7c22620d5ee6611228673e95b48a270 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 19 May 2005 04:59:05 +0000 Subject: [PATCH] 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] --- CHANGES | 7 + bin/check/check-tool.c | 308 ++++++++++++++- bin/check/check-tool.h | 5 +- bin/check/named-checkconf.c | 118 +++++- bin/check/named-checkzone.c | 87 ++++- bin/check/named-checkzone.docbook | 52 ++- bin/named/config.c | 4 +- bin/named/named.conf.docbook | 8 +- bin/named/update.c | 111 +++++- bin/named/zoneconf.c | 66 +++- bin/tests/system/nsupdate/ns1/named.conf | 4 +- doc/arm/Bv9ARM-book.xml | 56 ++- lib/bind9/check.c | 4 +- lib/dns/include/dns/master.h | 4 +- lib/dns/include/dns/name.h | 4 +- lib/dns/include/dns/rdata.h | 4 +- lib/dns/include/dns/result.h | 5 +- lib/dns/include/dns/types.h | 14 +- lib/dns/include/dns/zone.h | 35 +- lib/dns/master.c | 12 +- lib/dns/rdata.c | 22 +- lib/dns/rdata/generic/mx_15.c | 33 +- lib/dns/result.c | 5 +- lib/dns/zone.c | 459 +++++++++++++++++++++-- lib/isccfg/namedconf.c | 4 +- 25 files changed, 1359 insertions(+), 72 deletions(-) diff --git a/CHANGES b/CHANGES index 886a590102..906c727d75 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +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 diff --git a/bin/check/check-tool.c b/bin/check/check-tool.c index dacd56ac55..305bb5eefd 100644 --- a/bin/check/check-tool.c +++ b/bin/check/check-tool.c @@ -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 #include +#include +#include #include #include #include @@ -36,24 +38,39 @@ #include #include #include +#include #include +#include #include #include +#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){ diff --git a/bin/check/check-tool.h b/bin/check/check-tool.h index 54c9bfe282..bfd8a409e2 100644 --- a/bin/check/check-tool.h +++ b/bin/check/check-tool.h @@ -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 diff --git a/bin/check/named-checkconf.c b/bin/check/named-checkconf.c index e6d165e0a3..c730675d17 100644 --- a/bin/check/named-checkconf.c +++ b/bin/check/named-checkconf.c @@ -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 #include +#include #include +#include #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(); diff --git a/bin/check/named-checkzone.c b/bin/check/named-checkzone.c index f8bdfae90c..99c405f28f 100644 --- a/bin/check/named-checkzone.c +++ b/bin/check/named-checkzone.c @@ -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 #include #include +#include #include #include #include @@ -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,20 +105,33 @@ main(int argc, char **argv) { debug++; break; - case 'j': - nomerge = ISC_FALSE; + 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; + } else { + fprintf(stderr, "invalid argument to -i: %s\n", + isc_commandline_argument); + exit(1); + } break; - case 'n': - if (!strcmp(isc_commandline_argument, "ignore")) - zone_options &= ~(DNS_ZONEOPT_CHECKNS| - DNS_ZONEOPT_FATALNS); - else if (!strcmp(isc_commandline_argument, "warn")) { - zone_options |= DNS_ZONEOPT_CHECKNS; - zone_options &= ~DNS_ZONEOPT_FATALNS; - } else if (!strcmp(isc_commandline_argument, "fail")) - zone_options |= DNS_ZONEOPT_CHECKNS| - DNS_ZONEOPT_FATALNS; + case 'j': + nomerge = ISC_FALSE; break; case 'k': @@ -129,6 +146,46 @@ main(int argc, char **argv) { "ignore")) { zone_options &= ~(DNS_ZONEOPT_CHECKNAMES | DNS_ZONEOPT_CHECKNAMESFAIL); + } else { + fprintf(stderr, "invalid argument to -k: %s\n", + isc_commandline_argument); + exit(1); + } + break; + + case 'n': + if (!strcmp(isc_commandline_argument, "ignore")) { + zone_options &= ~(DNS_ZONEOPT_CHECKNS| + DNS_ZONEOPT_FATALNS); + } else if (!strcmp(isc_commandline_argument, "warn")) { + zone_options |= DNS_ZONEOPT_CHECKNS; + zone_options &= ~DNS_ZONEOPT_FATALNS; + } else if (!strcmp(isc_commandline_argument, "fail")) { + zone_options |= DNS_ZONEOPT_CHECKNS| + DNS_ZONEOPT_FATALNS; + } else { + fprintf(stderr, "invalid argument to -n: %s\n", + isc_commandline_argument); + exit(1); + } + break; + + case 'm': + if (!strcmp(isc_commandline_argument, "warn")) { + zone_options |= DNS_ZONEOPT_CHECKMX; + zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL; + } else if (!strcmp(isc_commandline_argument, + "fail")) { + zone_options |= DNS_ZONEOPT_CHECKMX | + DNS_ZONEOPT_CHECKMXFAIL; + } else if (!strcmp(isc_commandline_argument, + "ignore")) { + zone_options &= ~(DNS_ZONEOPT_CHECKMX | + DNS_ZONEOPT_CHECKMXFAIL); + } else { + fprintf(stderr, "invalid argument to -m: %s\n", + isc_commandline_argument); + exit(1); } break; diff --git a/bin/check/named-checkzone.docbook b/bin/check/named-checkzone.docbook index 8c4340f2d8..121b2bc94c 100644 --- a/bin/check/named-checkzone.docbook +++ b/bin/check/named-checkzone.docbook @@ -18,7 +18,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + June 13, 2000 @@ -57,7 +57,9 @@ + + @@ -129,6 +131,42 @@ + + -i mode + + + Perform post load zone integrity checks. Possible modes are + "full" (default), + "local" and + "none". + + + Mode "full" checks that MX records + refer to A or AAAA record (both in-zone and out-of-zone + hostnames). Mode "local" only + checks MX records which refer to in-zone hostnames. + + + Mode "full" checks that SRV records + refer to A or AAAA record (both in-zone and out-of-zone + hostnames). Mode "local" only + checks SRV records which refer to in-zone hostnames. + + + Mode "full" checks that delegation NS + records refer to A or AAAA record (both in-zone and out-of-zone + hostnames). It also checks that glue addresses records + in the zone match those advertised by the child. + Mode "local" only checks NS records which + refer to in-zone hostnames or that some required glue exists, + that is when the nameserver is in a child zone. + + + Mode "none" disables the checks. + + + + -k mode @@ -142,6 +180,18 @@ + + -m mode + + + Specify whether MX records should be checked to see if they + are addresses. Possible modes are "fail", + "warn" (default) and + "ignore". + + + + -n mode diff --git a/bin/named/config.c b/bin/named/config.c index f4e5966d8c..87b8a2f22e 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: config.c,v 1.56 2005/04/27 04:55:49 sra Exp $ */ +/* $Id: config.c,v 1.57 2005/05/19 04:59:00 marka Exp $ */ /*! \file */ @@ -128,10 +128,12 @@ options {\n\ check-names master fail;\n\ check-names slave warn;\n\ check-names response ignore;\n\ + check-mx warn;\n\ use-additional-cache true;\n\ acache-cleaning-interval 60;\n\ max-acache-size 0;\n\ dnssec-enable no; /* Make yes for 9.4. */ \n\ + integrity-check yes;\n\ " " /* zone */\n\ diff --git a/bin/named/named.conf.docbook b/bin/named/named.conf.docbook index 5cad2dc242..1534681b4a 100644 --- a/bin/named/named.conf.docbook +++ b/bin/named/named.conf.docbook @@ -17,7 +17,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + Aug 13, 2004 @@ -240,6 +240,8 @@ options { max-cache-size size_no_default; check-names ( master | slave | response ) ( fail | warn | ignore ); + check-mx ( fail | warn | ignore ); + integrity-check boolean; cache-file quoted_string; suppress-initial-notify boolean; // not yet implemented preferred-glue string; @@ -370,6 +372,8 @@ view string optional_class max-cache-size size_no_default; check-names ( master | slave | response ) ( fail | warn | ignore ); + check-mx ( fail | warn | ignore ); + integrity-check boolean; cache-file quoted_string; suppress-initial-notify boolean; // not yet implemented preferred-glue string; @@ -458,6 +462,8 @@ zone string optional_class database string; delegation-only boolean; check-names ( fail | warn | ignore ); + check-mx ( fail | warn | ignore ); + integrity-check boolean; dialup dialuptype; ixfr-from-differences boolean; journal quoted_string; diff --git a/bin/named/update.c b/bin/named/update.c index d7ed3c9886..a2c0794084 100644 --- a/bin/named/update.c +++ b/bin/named/update.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: update.c,v 1.121 2005/04/27 04:55:55 sra Exp $ */ +/* $Id: update.c,v 1.122 2005/05/19 04:59:00 marka Exp $ */ #include @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -2129,6 +2130,112 @@ remove_orphaned_ds(dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff) { return (result); } +/* + * This implements the post load integrity checks for mx records. + */ +static isc_result_t +check_mx(ns_client_t *client, dns_zone_t *zone, + dns_db_t *db, dns_dbversion_t *newver, dns_diff_t *diff) +{ + char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123.")]; + char ownerbuf[DNS_NAME_FORMATSIZE]; + char namebuf[DNS_NAME_FORMATSIZE]; + char altbuf[DNS_NAME_FORMATSIZE]; + dns_difftuple_t *t; + dns_fixedname_t fixed; + dns_name_t *foundname; + dns_rdata_mx_t mx; + dns_rdata_t rdata; + isc_boolean_t ok = ISC_TRUE; + isc_boolean_t isaddress; + isc_result_t result; + struct in6_addr addr6; + struct in_addr addr; + unsigned int options; + + dns_fixedname_init(&fixed); + foundname = dns_fixedname_name(&fixed); + dns_rdata_init(&rdata); + options = dns_zone_getoptions(zone); + + for (t = ISC_LIST_HEAD(diff->tuples); + t != NULL; + t = ISC_LIST_NEXT(t, link)) { + if (t->op != DNS_DIFFOP_DEL || + t->rdata.type != dns_rdatatype_mx) + continue; + + result = dns_rdata_tostruct(&t->rdata, &mx, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + /* + * Check if we will error out if we attempt to reload the + * zone. + */ + dns_name_format(&mx.mx, namebuf, sizeof(namebuf)); + dns_name_format(&t->name, ownerbuf, sizeof(ownerbuf)); + isaddress = ISC_FALSE; + if ((options & DNS_RDATA_CHECKMX) != 0 && + strlcpy(tmp, namebuf, sizeof(tmp)) < sizeof(tmp)) { + if (tmp[strlen(tmp) - 1] == '.') + tmp[strlen(tmp) - 1] = '\0'; + if (inet_aton(tmp, &addr) == 1 || + inet_pton(AF_INET6, tmp, &addr6) == 1) + isaddress = ISC_TRUE; + } + + if (isaddress && (options & DNS_RDATA_CHECKMXFAIL) != 0) { + update_log(client, zone, ISC_LOG_ERROR, + "%s/MX: '%s': %s", + ownerbuf, namebuf, + dns_result_totext(DNS_R_MXISADDRESS)); + ok = ISC_FALSE; + } else if (isaddress) { + update_log(client, zone, ISC_LOG_WARNING, + "%s/MX: warning: '%s': %s", + ownerbuf, namebuf, + dns_result_totext(DNS_R_MXISADDRESS)); + } + + /* + * Check zone integrity checks. + */ + if ((options & DNS_ZONEOPT_INTEGRITYCHECK) == 0) + continue; + result = dns_db_find(db, &mx.mx, newver, dns_rdatatype_a, + 0, 0, NULL, foundname, NULL, NULL); + if (result == ISC_R_SUCCESS) + continue; + + if (result == DNS_R_NXRRSET) { + result = dns_db_find(db, &mx.mx, newver, + dns_rdatatype_aaaa, + 0, 0, NULL, foundname, + NULL, NULL); + if (result == ISC_R_SUCCESS) + continue; + } + + if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN) { + update_log(client, zone, ISC_LOG_ERROR, + "%s/MX '%s' has no address records " + "(A or AAAA)", ownerbuf, namebuf); + ok = ISC_FALSE; + } else if (result == DNS_R_CNAME) { + update_log(client, zone, ISC_LOG_ERROR, + "%s/MX '%s' is a CNAME (illegal)", + ownerbuf, namebuf); + ok = ISC_FALSE; + } else if (result == DNS_R_DNAME) { + dns_name_format(foundname, altbuf, sizeof altbuf); + update_log(client, zone, ISC_LOG_ERROR, + "%s/MX '%s' is below a DNAME '%s' (illegal)", + ownerbuf, namebuf, altbuf); + ok = ISC_FALSE; + } + } + return (ok ? ISC_R_SUCCESS : DNS_R_REFUSED); +} + static void update_action(isc_task_t *task, isc_event_t *event) { update_event_t *uev = (update_event_t *) event; @@ -2628,6 +2735,8 @@ update_action(isc_task_t *task, isc_event_t *event) { CHECK(increment_soa_serial(db, ver, &diff, mctx)); } + CHECK(check_mx(client, zone, db, ver, &diff)); + CHECK(remove_orphaned_ds(db, ver, &diff)); if (dns_db_issecure(db)) { diff --git a/bin/named/zoneconf.c b/bin/named/zoneconf.c index cdbc6d8a1f..d64bd7f55b 100644 --- a/bin/named/zoneconf.c +++ b/bin/named/zoneconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zoneconf.c,v 1.120 2005/04/27 04:55:56 sra Exp $ */ +/* $Id: zoneconf.c,v 1.121 2005/05/19 04:59:01 marka Exp $ */ /*% */ @@ -613,6 +613,7 @@ ns_zone_configure(cfg_obj_t *config, cfg_obj_t *vconfig, cfg_obj_t *zconfig, } RETERR(dns_zone_setkeydirectory(zone, filename)); } + obj = NULL; result = ns_config_get(maps, "check-wildcard", &obj); if (result == ISC_R_SUCCESS) @@ -620,6 +621,69 @@ ns_zone_configure(cfg_obj_t *config, cfg_obj_t *vconfig, cfg_obj_t *zconfig, else check = ISC_FALSE; dns_zone_setoption(zone, DNS_ZONEOPT_CHECKWILDCARD, check); + + obj = NULL; + result = ns_config_get(maps, "check-mx", &obj); + INSIST(obj != NULL); + if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) { + fail = ISC_FALSE; + check = ISC_TRUE; + } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) { + fail = check = ISC_TRUE; + } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) { + fail = check = ISC_FALSE; + } else + INSIST(0); + dns_zone_setoption(zone, DNS_ZONEOPT_CHECKMX, check); + dns_zone_setoption(zone, DNS_ZONEOPT_CHECKMXFAIL, fail); + + obj = NULL; + result = ns_config_get(maps, "integrity-check", &obj); + INSIST(obj != NULL); + dns_zone_setoption(zone, DNS_ZONEOPT_INTEGRITYCHECK, + cfg_obj_asboolean(obj)); + } + + /* + * Configure update-related options. These apply to + * primary masters only. + */ + if (ztype == dns_zone_master) { + dns_acl_t *updateacl; + RETERR(configure_zone_acl(zconfig, vconfig, config, + "allow-update", ac, zone, + dns_zone_setupdateacl, + dns_zone_clearupdateacl)); + + updateacl = dns_zone_getupdateacl(zone); + if (updateacl != NULL && dns_acl_isinsecure(updateacl)) + isc_log_write(ns_g_lctx, DNS_LOGCATEGORY_SECURITY, + NS_LOGMODULE_SERVER, ISC_LOG_WARNING, + "zone '%s' allows updates by IP " + "address, which is insecure", + zname); + + RETERR(configure_zone_ssutable(zoptions, zone)); + + obj = NULL; + result = ns_config_get(maps, "sig-validity-interval", &obj); + INSIST(result == ISC_R_SUCCESS); + dns_zone_setsigvalidityinterval(zone, + cfg_obj_asuint32(obj) * 86400); + + obj = NULL; + result = ns_config_get(maps, "key-directory", &obj); + if (result == ISC_R_SUCCESS) { + filename = cfg_obj_asstring(obj); + if (!isc_file_isabsolute(filename)) { + cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR, + "key-directory '%s' " + "is not absolute", filename); + return (ISC_R_FAILURE); + } + RETERR(dns_zone_setkeydirectory(zone, filename)); + } + } else if (ztype == dns_zone_slave) { RETERR(configure_zone_acl(zconfig, vconfig, config, "allow-update-forwarding", ac, zone, diff --git a/bin/tests/system/nsupdate/ns1/named.conf b/bin/tests/system/nsupdate/ns1/named.conf index 20f678c42a..03b100ba05 100644 --- a/bin/tests/system/nsupdate/ns1/named.conf +++ b/bin/tests/system/nsupdate/ns1/named.conf @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: named.conf,v 1.12 2004/03/05 05:01:58 marka Exp $ */ +/* $Id: named.conf,v 1.13 2005/05/19 04:59:01 marka Exp $ */ controls { /* empty */ }; @@ -43,6 +43,7 @@ controls { zone "example.nil" { type master; file "example.db"; + integrity-check no; allow-update { any; }; allow-transfer { any; }; }; @@ -50,6 +51,7 @@ zone "example.nil" { zone "update.nil" { type master; file "update.db"; + integrity-check no; allow-update { any; }; allow-transfer { any; }; also-notify { 10.53.0.2; }; diff --git a/doc/arm/Bv9ARM-book.xml b/doc/arm/Bv9ARM-book.xml index e15656adae..35af0efb28 100644 --- a/doc/arm/Bv9ARM-book.xml +++ b/doc/arm/Bv9ARM-book.xml @@ -18,7 +18,7 @@ - PERFORMANCE OF THIS SOFTWARE. --> - + BIND 9 Administrator Reference Manual @@ -4307,7 +4307,9 @@ category notify { null; }; forwarders { ip_addr port ip_port ; ip_addr port ip_port ; ... }; dual-stack-servers port ip_port { ( domain_name port ip_port | ip_addr port ip_port ) ; ... }; check-names ( master | slave | response )( warn | fail | ignore ); + check-mx ( warn | fail | ignore ); check-wildcard yes_or_no; + integrity-checks yes_or_no; allow-notify { address_match_list }; allow-query { address_match_list }; allow-query-cache { address_match_list }; @@ -5423,6 +5425,18 @@ options { + + check-mx + + + Check whether the MX record appears to refer to a IP address. + The default is to warn. Other possible + values are fail and + ignore. + + + + check-wildcard @@ -5438,6 +5452,24 @@ options { + + integrity-check + + + Perform post load zone integrity checks on master + zones. This checks that MX and SRV records refer + to address (A or AAAA) records and that glue + address records exist for delegated zones. For + MX and SRV records only in-zone hostnames are + checked (for out-of-zone hostnames use named-checkzone). + For NS records only names below top of zone are + checked (for out-of-zone names and glue consistancy + checks use named-checkzone). The default is + yes. + + + + @@ -7497,7 +7529,9 @@ view "external" { allow-update-forwarding { address_match_list } ; also-notify { ip_addr port ip_port ; ip_addr port ip_port ; ... }; check-names (warn|fail|ignore) ; + check-mx (warn|fail|ignore) ; check-wildcard yes_or_no; + integrity-checks yes_or_no ; dialup dialup_option ; delegation-only yes_or_no ; file string ; @@ -7884,6 +7918,16 @@ view "external" { + + check-mx + + + See the description of + check-mx in . + + + + check-wildcard @@ -7894,6 +7938,16 @@ view "external" { + + integrity-check + + + See the description of + integrity-check in . + + + + database diff --git a/lib/bind9/check.c b/lib/bind9/check.c index b30745787d..af63de774f 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: check.c,v 1.57 2005/04/27 04:56:43 sra Exp $ */ +/* $Id: check.c,v 1.58 2005/05/19 04:59:02 marka Exp $ */ /*! \file */ @@ -799,6 +799,8 @@ check_zoneconf(cfg_obj_t *zconfig, cfg_obj_t *voptions, cfg_obj_t *config, { "database", MASTERZONE | SLAVEZONE | STUBZONE }, { "key-directory", MASTERZONE }, { "check-wildcard", MASTERZONE }, + { "check-mx", MASTERZONE }, + { "integrity-check", MASTERZONE }, }; static optionstable dialups[] = { diff --git a/lib/dns/include/dns/master.h b/lib/dns/include/dns/master.h index 2ce21ed756..7a9661f2b2 100644 --- a/lib/dns/include/dns/master.h +++ b/lib/dns/include/dns/master.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: master.h,v 1.42 2005/04/27 04:56:56 sra Exp $ */ +/* $Id: master.h,v 1.43 2005/05/19 04:59:04 marka Exp $ */ #ifndef DNS_MASTER_H #define DNS_MASTER_H 1 @@ -52,6 +52,8 @@ #define DNS_MASTER_CHECKNAMES 0x00000100 #define DNS_MASTER_CHECKNAMESFAIL 0x00000200 #define DNS_MASTER_CHECKWILDCARD 0x00000400 /* Check for internal wildcards. */ +#define DNS_MASTER_CHECKMX 0x00000800 +#define DNS_MASTER_CHECKMXFAIL 0x00001000 ISC_LANG_BEGINDECLS diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h index d8237f6dcc..656ac08857 100644 --- a/lib/dns/include/dns/name.h +++ b/lib/dns/include/dns/name.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: name.h,v 1.115 2005/04/27 04:56:57 sra Exp $ */ +/* $Id: name.h,v 1.116 2005/05/19 04:59:04 marka Exp $ */ #ifndef DNS_NAME_H #define DNS_NAME_H 1 @@ -145,6 +145,8 @@ struct dns_name { #define DNS_NAME_CHECKNAMES 0x0002 /*%< Used by rdata. */ #define DNS_NAME_CHECKNAMESFAIL 0x0004 /*%< Used by rdata. */ #define DNS_NAME_CHECKREVERSE 0x0008 /*%< Used by rdata. */ +#define DNS_NAME_CHECKMX 0x0010 /*%< Used by rdata. */ +#define DNS_NAME_CHECKMXFAIL 0x0020 /*%< Used by rdata. */ LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_rootname; LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_wildcardname; diff --git a/lib/dns/include/dns/rdata.h b/lib/dns/include/dns/rdata.h index 9cf8f7b8b9..296cf61c39 100644 --- a/lib/dns/include/dns/rdata.h +++ b/lib/dns/include/dns/rdata.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdata.h,v 1.62 2005/04/29 00:23:01 marka Exp $ */ +/* $Id: rdata.h,v 1.63 2005/05/19 04:59:04 marka Exp $ */ #ifndef DNS_RDATA_H #define DNS_RDATA_H 1 @@ -143,6 +143,8 @@ struct dns_rdata { #define DNS_RDATA_CHECKNAMES DNS_NAME_CHECKNAMES #define DNS_RDATA_CHECKNAMESFAIL DNS_NAME_CHECKNAMESFAIL #define DNS_RDATA_CHECKREVERSE DNS_NAME_CHECKREVERSE +#define DNS_RDATA_CHECKMX DNS_NAME_CHECKMX +#define DNS_RDATA_CHECKMXFAIL DNS_NAME_CHECKMXFAIL /*** *** Initialization diff --git a/lib/dns/include/dns/result.h b/lib/dns/include/dns/result.h index 684322b11f..5e6d5c61ef 100644 --- a/lib/dns/include/dns/result.h +++ b/lib/dns/include/dns/result.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: result.h,v 1.108 2005/04/29 00:23:02 marka Exp $ */ +/* $Id: result.h,v 1.109 2005/05/19 04:59:04 marka Exp $ */ #ifndef DNS_RESULT_H #define DNS_RESULT_H 1 @@ -145,8 +145,9 @@ #define DNS_R_UNKNOWNCOMMAND (ISC_RESULTCLASS_DNS + 99) #define DNS_R_MUSTBESECURE (ISC_RESULTCLASS_DNS + 100) #define DNS_R_COVERINGNSEC (ISC_RESULTCLASS_DNS + 101) +#define DNS_R_MXISADDRESS (ISC_RESULTCLASS_DNS + 102) -#define DNS_R_NRESULTS 102 /*%< Number of results */ +#define DNS_R_NRESULTS 103 /*%< Number of results */ /* * DNS wire format rcodes. diff --git a/lib/dns/include/dns/types.h b/lib/dns/include/dns/types.h index 15031b102b..d3aec3bad3 100644 --- a/lib/dns/include/dns/types.h +++ b/lib/dns/include/dns/types.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: types.h,v 1.114 2005/04/27 04:57:00 sra Exp $ */ +/* $Id: types.h,v 1.115 2005/05/19 04:59:04 marka Exp $ */ #ifndef DNS_TYPES_H #define DNS_TYPES_H 1 @@ -298,7 +298,17 @@ typedef void (*dns_updatecallback_t)(void *, isc_result_t, dns_message_t *); typedef int -(*dns_rdatasetorderfunc_t)(dns_rdata_t *rdata, void *arg); +(*dns_rdatasetorderfunc_t)(dns_rdata_t *, void *); + +typedef isc_boolean_t +(*dns_checkmxfunc_t)(dns_zone_t *, dns_name_t *, dns_name_t *); + +typedef isc_boolean_t +(*dns_checksrvfunc_t)(dns_zone_t *, dns_name_t *, dns_name_t *); + +typedef isc_boolean_t +(*dns_checknsfunc_t)(dns_zone_t *, dns_name_t *, dns_name_t *, + dns_rdataset_t *, dns_rdataset_t *); typedef isc_boolean_t (*dns_isselffunc_t)(dns_view_t *, dns_tsigkey_t *, isc_sockaddr_t *, diff --git a/lib/dns/include/dns/zone.h b/lib/dns/include/dns/zone.h index 75f2f63148..cf70fba749 100644 --- a/lib/dns/include/dns/zone.h +++ b/lib/dns/include/dns/zone.h @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.h,v 1.133 2005/04/27 04:57:01 sra Exp $ */ +/* $Id: zone.h,v 1.134 2005/05/19 04:59:05 marka Exp $ */ #ifndef DNS_ZONE_H #define DNS_ZONE_H 1 @@ -55,6 +55,9 @@ typedef enum { #define DNS_ZONEOPT_CHECKNAMES 0x00000800U /*%< check-names */ #define DNS_ZONEOPT_CHECKNAMESFAIL 0x00001000U /*%< fatal check-name failures */ #define DNS_ZONEOPT_CHECKWILDCARD 0x00002000U /*%< check for internal wildcards */ +#define DNS_ZONEOPT_CHECKMX 0x00004000U /*%< check-mx */ +#define DNS_ZONEOPT_CHECKMXFAIL 0x00008000U /*%< fatal check-mx failures */ +#define DNS_ZONEOPT_INTEGRITYCHECK 0x00010000U /*%< perform integrity checks */ #ifndef NOMINUM_PUBLIC /* @@ -1448,6 +1451,36 @@ dns_zone_setacache(dns_zone_t *zone, dns_acache_t *acache); * 'zone' will have a reference to 'acache' */ +void +dns_zone_setcheckmx(dns_zone_t *zone, dns_checkmxfunc_t checkmx); +/* + * Set the post load integrity callback function 'checkmx'. + * 'checkmx' will be called if the MX is not within the zone. + * + * Require: + * 'zone' to be a valid zone. + */ + +void +dns_zone_setchecksrv(dns_zone_t *zone, dns_checkmxfunc_t checksrv); +/* + * Set the post load integrity callback function 'checksrv'. + * 'checksrv' will be called if the SRV TARGET is not within the zone. + * + * Require: + * 'zone' to be a valid zone. + */ + +void +dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns); +/* + * Set the post load integrity callback function 'checkmx'. + * 'checkmx' will be called if the MX is not within the zone. + * + * Require: + * 'zone' to be a valid zone. + */ + void dns_zone_setnotifydelay(dns_zone_t *zone, isc_uint32_t delay); /* diff --git a/lib/dns/master.c b/lib/dns/master.c index 6ce43f46d9..beaccbd8a9 100644 --- a/lib/dns/master.c +++ b/lib/dns/master.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: master.c,v 1.152 2005/04/27 04:56:47 sra Exp $ */ +/* $Id: master.c,v 1.153 2005/05/19 04:59:02 marka Exp $ */ /*! \file */ @@ -959,11 +959,16 @@ load(dns_loadctx_t *lctx) { options |= DNS_RDATA_CHECKNAMES; if ((lctx->options & DNS_MASTER_CHECKNAMESFAIL) != 0) options |= DNS_RDATA_CHECKNAMESFAIL; + if ((lctx->options & DNS_MASTER_CHECKMX) != 0) + options |= DNS_RDATA_CHECKMX; + if ((lctx->options & DNS_MASTER_CHECKMXFAIL) != 0) + options |= DNS_RDATA_CHECKMXFAIL; source = isc_lex_getsourcename(lctx->lex); do { initialws = ISC_FALSE; line = isc_lex_getsourceline(lctx->lex); - GETTOKEN(lctx->lex, ISC_LEXOPT_INITIALWS, &token, ISC_TRUE); + GETTOKEN(lctx->lex, ISC_LEXOPT_INITIALWS | ISC_LEXOPT_QSTRING, + &token, ISC_TRUE); line = isc_lex_getsourceline(lctx->lex); if (token.type == isc_tokentype_eof) { @@ -999,7 +1004,8 @@ load(dns_loadctx_t *lctx) { * Still working on the same name. */ initialws = ISC_TRUE; - } else if (token.type == isc_tokentype_string) { + } else if (token.type == isc_tokentype_string || + token.type == isc_tokentype_qstring) { /* * "$" Support. diff --git a/lib/dns/rdata.c b/lib/dns/rdata.c index 64b630942f..08f3c46f87 100644 --- a/lib/dns/rdata.c +++ b/lib/dns/rdata.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: rdata.c,v 1.188 2005/04/27 04:56:50 sra Exp $ */ +/* $Id: rdata.c,v 1.189 2005/05/19 04:59:03 marka Exp $ */ /*! \file */ @@ -197,6 +197,10 @@ static void warn_badname(dns_name_t *name, isc_lex_t *lexer, dns_rdatacallbacks_t *callbacks); +static void +warn_badmx(isc_token_t *token, isc_lex_t *lexer, + dns_rdatacallbacks_t *callbacks); + static inline int getquad(const void *src, struct in_addr *dst, isc_lex_t *lexer, dns_rdatacallbacks_t *callbacks) @@ -1578,6 +1582,22 @@ fromtext_warneof(isc_lex_t *lexer, dns_rdatacallbacks_t *callbacks) { } } +static void +warn_badmx(isc_token_t *token, isc_lex_t *lexer, + dns_rdatacallbacks_t *callbacks) +{ + const char *file; + unsigned long line; + + if (lexer != NULL) { + file = isc_lex_getsourcename(lexer); + line = isc_lex_getsourceline(lexer); + (*callbacks->warn)(callbacks, "%s:%u: warning: '%s': %s", + file, line, DNS_AS_STR(*token), + dns_result_totext(DNS_R_MXISADDRESS)); + } +} + static void warn_badname(dns_name_t *name, isc_lex_t *lexer, dns_rdatacallbacks_t *callbacks) diff --git a/lib/dns/rdata/generic/mx_15.c b/lib/dns/rdata/generic/mx_15.c index 211a0af656..92bda74fe6 100644 --- a/lib/dns/rdata/generic/mx_15.c +++ b/lib/dns/rdata/generic/mx_15.c @@ -15,15 +15,37 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: mx_15.c,v 1.52 2004/03/05 05:10:15 marka Exp $ */ +/* $Id: mx_15.c,v 1.53 2005/05/19 04:59:05 marka Exp $ */ /* reviewed: Wed Mar 15 18:05:46 PST 2000 by brister */ #ifndef RDATA_GENERIC_MX_15_C #define RDATA_GENERIC_MX_15_C +#include + +#include + #define RRTYPE_MX_ATTRIBUTES (0) +static isc_boolean_t +check_mx(isc_token_t *token) { + char tmp[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123.")]; + struct in_addr addr; + struct in6_addr addr6; + + if (strlcpy(tmp, DNS_AS_STR(*token), sizeof(tmp)) >= sizeof(tmp)) + return (ISC_TRUE); + + if (tmp[strlen(tmp) - 1] == '.') + tmp[strlen(tmp) - 1] = '\0'; + if (inet_aton(tmp, &addr) == 1 || + inet_pton(AF_INET6, tmp, &addr6) == 1) + return (ISC_FALSE); + + return (ISC_TRUE); +} + static inline isc_result_t fromtext_mx(ARGS_FROMTEXT) { isc_token_t token; @@ -45,6 +67,15 @@ fromtext_mx(ARGS_FROMTEXT) { RETERR(isc_lex_getmastertoken(lexer, &token, isc_tokentype_string, ISC_FALSE)); + + ok = ISC_TRUE; + if ((options & DNS_RDATA_CHECKMX) != 0) + ok = check_mx(&token); + if (!ok && (options & DNS_RDATA_CHECKMXFAIL) != 0) + RETTOK(DNS_R_MXISADDRESS); + if (!ok && callbacks != NULL) + warn_badmx(&token, lexer, callbacks); + dns_name_init(&name, NULL); buffer_fromregion(&buffer, &token.value.as_region); origin = (origin != NULL) ? origin : dns_rootname; diff --git a/lib/dns/result.c b/lib/dns/result.c index a211ed5df6..9885666a70 100644 --- a/lib/dns/result.c +++ b/lib/dns/result.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: result.c,v 1.119 2005/04/29 00:22:51 marka Exp $ */ +/* $Id: result.c,v 1.120 2005/05/19 04:59:03 marka Exp $ */ /*! \file */ @@ -153,7 +153,8 @@ static const char *text[DNS_R_NRESULTS] = { "unknown command", /*%< 99 DNS_R_UNKNOWNCOMMAND */ "must-be-secure", /*%< 100 DNS_R_MUSTBESECURE */ - "covering NSEC record returned" /*%< 101 DNS_R_COVERINGNSEC */ + "covering NSEC record returned", /*%< 101 DNS_R_COVERINGNSEC */ + "MX is an address" /*%< 102 DNS_R_MXISADDRESS */ }; static const char *rcode_text[DNS_R_NRCODERESULTS] = { diff --git a/lib/dns/zone.c b/lib/dns/zone.c index a2a1d7d548..41aa8bbdb2 100644 --- a/lib/dns/zone.c +++ b/lib/dns/zone.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: zone.c,v 1.433 2005/04/27 04:56:53 sra Exp $ */ +/* $Id: zone.c,v 1.434 2005/05/19 04:59:03 marka Exp $ */ /*! \file */ @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -179,13 +180,13 @@ struct dns_zone { unsigned int notifycnt; isc_sockaddr_t notifyfrom; isc_task_t *task; - isc_sockaddr_t notifysrc4; - isc_sockaddr_t notifysrc6; - isc_sockaddr_t xfrsource4; - isc_sockaddr_t xfrsource6; - isc_sockaddr_t altxfrsource4; - isc_sockaddr_t altxfrsource6; - isc_sockaddr_t sourceaddr; + isc_sockaddr_t notifysrc4; + isc_sockaddr_t notifysrc6; + isc_sockaddr_t xfrsource4; + isc_sockaddr_t xfrsource6; + isc_sockaddr_t altxfrsource4; + isc_sockaddr_t altxfrsource6; + isc_sockaddr_t sourceaddr; dns_xfrin_ctx_t *xfr; /* task locked */ dns_tsigkey_t *tsigkey; /* key used for xfr */ /* Access Control Lists */ @@ -211,6 +212,9 @@ struct dns_zone { isc_uint32_t sigvalidityinterval; dns_view_t *view; dns_acache_t *acache; + dns_checkmxfunc_t checkmx; + dns_checksrvfunc_t checksrv; + dns_checknsfunc_t checkns; /*% * Zones in certain states such as "waiting for zone transfer" * or "zone transfer in progress" are kept on per-state linked lists @@ -586,6 +590,9 @@ dns_zone_create(dns_zone_t **zonep, isc_mem_t *mctx) { zone->sigvalidityinterval = 30 * 24 * 3600; zone->view = NULL; zone->acache = NULL; + zone->checkmx = NULL; + zone->checksrv = NULL; + zone->checkns = NULL; ISC_LINK_INIT(zone, statelink); zone->statelist = NULL; zone->counters = NULL; @@ -1025,24 +1032,34 @@ zone_load(dns_zone_t *zone, unsigned int flags) { goto cleanup; } + + /* + * Store the current time before the zone is loaded, so that if the + * file changes between the time of the load and the time that + * zone->loadtime is set, then the file will still be reloaded + * the next time dns_zone_load is called. + */ + TIME_NOW(&loadtime); + /* * Don't do the load if the file that stores the zone is older * than the last time the zone was loaded. If the zone has not * been loaded yet, zone->loadtime will be the epoch. */ - if (zone->masterfile != NULL && ! isc_time_isepoch(&zone->loadtime)) { + if (zone->masterfile != NULL) { /* * The file is already loaded. If we are just doing a * "rndc reconfig", we are done. */ - if ((flags & DNS_ZONELOADFLAG_NOSTAT) != 0) { + if (!isc_time_isepoch(&zone->loadtime) && + (flags & DNS_ZONELOADFLAG_NOSTAT) != 0) { result = ISC_R_SUCCESS; goto cleanup; } - if (! DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE)) { - result = isc_file_getmodtime(zone->masterfile, - &filetime); - if (result == ISC_R_SUCCESS && + + result = isc_file_getmodtime(zone->masterfile, &filetime); + if (result == ISC_R_SUCCESS) { + if (!DNS_ZONE_FLAG(zone, DNS_ZONEFLG_HASINCLUDE) && isc_time_compare(&filetime, &zone->loadtime) < 0) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "skipping load: master file older " @@ -1050,6 +1067,7 @@ zone_load(dns_zone_t *zone, unsigned int flags) { result = DNS_R_UPTODATE; goto cleanup; } + loadtime = filetime; } } @@ -1073,14 +1091,6 @@ zone_load(dns_zone_t *zone, unsigned int flags) { dns_zone_log(zone, ISC_LOG_DEBUG(1), "starting load"); - /* - * Store the current time before the zone is loaded, so that if the - * file changes between the time of the load and the time that - * zone->loadtime is set, then the file will still be reloaded - * the next time dns_zone_load is called. - */ - TIME_NOW(&loadtime); - result = dns_db_create(zone->mctx, zone->db_argv[0], &zone->origin, (zone->type == dns_zone_stub) ? dns_dbtype_stub : dns_dbtype_zone, @@ -1161,6 +1171,10 @@ zone_gotreadhandle(isc_task_t *task, isc_event_t *event) { options |= DNS_MASTER_CHECKNAMES; if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKNAMESFAIL)) options |= DNS_MASTER_CHECKNAMESFAIL; + if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKMX)) + options |= DNS_MASTER_CHECKMX; + if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKMXFAIL)) + options |= DNS_MASTER_CHECKMXFAIL; if (DNS_ZONE_OPTION(load->zone, DNS_ZONEOPT_CHECKWILDCARD)) options |= DNS_MASTER_CHECKWILDCARD; result = dns_master_loadfileinc(load->zone->masterfile, @@ -1233,6 +1247,10 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { options |= DNS_MASTER_CHECKNAMES; if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKNAMESFAIL)) options |= DNS_MASTER_CHECKNAMESFAIL; + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMX)) + options |= DNS_MASTER_CHECKMX; + if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKMXFAIL)) + options |= DNS_MASTER_CHECKMXFAIL; if (DNS_ZONE_OPTION(zone, DNS_ZONEOPT_CHECKWILDCARD)) options |= DNS_MASTER_CHECKWILDCARD; @@ -1293,6 +1311,375 @@ zone_startload(dns_db_t *db, dns_zone_t *zone, isc_time_t loadtime) { return (result); } +static isc_boolean_t +zone_check_mx(dns_zone_t *zone, dns_db_t *db, dns_name_t *name, + dns_name_t *owner) +{ + isc_result_t result; + char ownerbuf[DNS_NAME_FORMATSIZE]; + char namebuf[DNS_NAME_FORMATSIZE]; + char altbuf[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fixed; + dns_name_t *foundname; + int level; + + /* + * Outside of zone. + */ + if (!dns_name_issubdomain(name, &zone->origin)) { + if (zone->checkmx != NULL) + return ((zone->checkmx)(zone, name, owner)); + return (ISC_TRUE); + } + + if (zone->type == dns_zone_master) + level = ISC_LOG_ERROR; + else + level = ISC_LOG_WARNING; + + dns_fixedname_init(&fixed); + foundname = dns_fixedname_name(&fixed); + + result = dns_db_find(db, name, NULL, dns_rdatatype_a, + 0, 0, NULL, foundname, NULL, NULL); + if (result == ISC_R_SUCCESS) + return (ISC_TRUE); + + if (result == DNS_R_NXRRSET) { + result = dns_db_find(db, name, NULL, dns_rdatatype_aaaa, + 0, 0, NULL, foundname, NULL, NULL); + if (result == ISC_R_SUCCESS) + return (ISC_TRUE); + } + + dns_name_format(owner, ownerbuf, sizeof ownerbuf); + dns_name_format(name, namebuf, sizeof namebuf); + if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN || + result == DNS_R_EMPTYNAME) { + dns_zone_log(zone, level, + "%s/MX '%s' has no address records (A or AAAA)", + ownerbuf, namebuf); + return (ISC_FALSE); + } + + if (result == DNS_R_CNAME) { + dns_zone_log(zone, level, "%s/MX '%s' is a CNAME (illegal)", + ownerbuf, namebuf); + return (ISC_FALSE); + } + + if (result == DNS_R_DNAME) { + dns_name_format(foundname, altbuf, sizeof altbuf); + dns_zone_log(zone, level, + "%s/MX '%s' is below a DNAME '%s' (illegal)", + ownerbuf, namebuf, altbuf); + return (ISC_FALSE); + } + + if (zone->checkmx != NULL && result == DNS_R_DELEGATION) + return ((zone->checkmx)(zone, name, owner)); + + return (ISC_TRUE); +} + +static isc_boolean_t +zone_check_srv(dns_zone_t *zone, dns_db_t *db, dns_name_t *name, + dns_name_t *owner) +{ + isc_result_t result; + char ownerbuf[DNS_NAME_FORMATSIZE]; + char namebuf[DNS_NAME_FORMATSIZE]; + char altbuf[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fixed; + dns_name_t *foundname; + int level; + + /* + * Outside of zone. + */ + if (!dns_name_issubdomain(name, &zone->origin)) { + if (zone->checksrv != NULL) + return ((zone->checksrv)(zone, name, owner)); + return (ISC_TRUE); + } + + if (zone->type == dns_zone_master) + level = ISC_LOG_ERROR; + else + level = ISC_LOG_WARNING; + + dns_fixedname_init(&fixed); + foundname = dns_fixedname_name(&fixed); + + result = dns_db_find(db, name, NULL, dns_rdatatype_a, + 0, 0, NULL, foundname, NULL, NULL); + if (result == ISC_R_SUCCESS) + return (ISC_TRUE); + + if (result == DNS_R_NXRRSET) { + result = dns_db_find(db, name, NULL, dns_rdatatype_aaaa, + 0, 0, NULL, foundname, NULL, NULL); + if (result == ISC_R_SUCCESS) + return (ISC_TRUE); + } + + dns_name_format(owner, ownerbuf, sizeof ownerbuf); + dns_name_format(name, namebuf, sizeof namebuf); + if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN || + result == DNS_R_EMPTYNAME) { + dns_zone_log(zone, level, + "%s/SRV '%s' has no address records (A or AAAA)", + ownerbuf, namebuf); + return (ISC_FALSE); + } + + if (result == DNS_R_CNAME) { + dns_zone_log(zone, level, "%s/SRV '%s' is a CNAME (illegal)", + ownerbuf, namebuf); + return (ISC_FALSE); + } + + if (result == DNS_R_DNAME) { + dns_name_format(foundname, altbuf, sizeof altbuf); + dns_zone_log(zone, level, + "%s/SRV '%s' is below a DNAME '%s' (illegal)", + ownerbuf, namebuf, altbuf); + return (ISC_FALSE); + } + + if (zone->checksrv != NULL && result == DNS_R_DELEGATION) + return ((zone->checksrv)(zone, name, owner)); + + return (ISC_TRUE); +} + +static isc_boolean_t +zone_check_glue(dns_zone_t *zone, dns_db_t *db, dns_name_t *name, + dns_name_t *owner) +{ + isc_boolean_t answer = ISC_TRUE; + isc_result_t result, tresult; + char ownerbuf[DNS_NAME_FORMATSIZE]; + char namebuf[DNS_NAME_FORMATSIZE]; + char altbuf[DNS_NAME_FORMATSIZE]; + dns_fixedname_t fixed; + dns_name_t *foundname; + dns_rdataset_t a; + dns_rdataset_t aaaa; + int level; + + /* + * Outside of zone. + */ + if (!dns_name_issubdomain(name, &zone->origin)) { + if (zone->checkns != NULL) + return ((zone->checkns)(zone, name, owner, NULL, NULL)); + return (ISC_TRUE); + } + + if (zone->type == dns_zone_master) + level = ISC_LOG_ERROR; + else + level = ISC_LOG_WARNING; + + dns_fixedname_init(&fixed); + foundname = dns_fixedname_name(&fixed); + dns_rdataset_init(&a); + dns_rdataset_init(&aaaa); + + result = dns_db_find(db, name, NULL, dns_rdatatype_a, + DNS_DBFIND_GLUEOK, 0, NULL, + foundname, &a, NULL); + + if (result == ISC_R_SUCCESS) { + dns_rdataset_disassociate(&a); + return (ISC_TRUE); + } else if (result == DNS_R_DELEGATION) + dns_rdataset_disassociate(&a); + + if (result == DNS_R_NXRRSET || result == DNS_R_DELEGATION || + result == DNS_R_GLUE) { + tresult = dns_db_find(db, name, NULL, dns_rdatatype_aaaa, + DNS_DBFIND_GLUEOK, 0, NULL, + foundname, &aaaa, NULL); + if (tresult == ISC_R_SUCCESS) { + dns_rdataset_disassociate(&aaaa); + return (ISC_TRUE); + } + if (tresult == DNS_R_DELEGATION) + dns_rdataset_disassociate(&aaaa); + if (result == DNS_R_GLUE || tresult == DNS_R_GLUE) { + /* + * Check glue against child zone. + */ + if (zone->checkns != NULL) + answer = (zone->checkns)(zone, name, owner, + &a, &aaaa); + if (dns_rdataset_isassociated(&a)) + dns_rdataset_disassociate(&a); + if (dns_rdataset_isassociated(&aaaa)) + dns_rdataset_disassociate(&aaaa); + return (answer); + } + } else + tresult = result; + + dns_name_format(owner, ownerbuf, sizeof ownerbuf); + dns_name_format(name, namebuf, sizeof namebuf); + if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN || + result == DNS_R_EMPTYNAME || result == DNS_R_DELEGATION) { + const char *what; + if (dns_name_issubdomain(name, owner)) + what = "REQUIRED GLUE "; + else if (result == DNS_R_DELEGATION) + what = "SIBLING GLUE "; + else + what = ""; + dns_zone_log(zone, level, + "%s/NS '%s' has no %saddress records (A or AAAA)", + ownerbuf, namebuf, what); + /* + * Log missing address record. + */ + if (result == DNS_R_DELEGATION && zone->checkns != NULL) + answer = (zone->checkns)(zone, name, owner, &a, &aaaa); + answer = ISC_FALSE; + } else if (result == DNS_R_CNAME) { + dns_zone_log(zone, level, "%s/NS '%s' is a CNAME (illegal)", + ownerbuf, namebuf); + answer = ISC_FALSE; + } else if (result == DNS_R_DNAME) { + dns_name_format(foundname, altbuf, sizeof altbuf); + dns_zone_log(zone, level, + "%s/NS '%s' is below a DNAME '%s' (illegal)", + ownerbuf, namebuf, altbuf); + answer = ISC_FALSE; + } + + if (dns_rdataset_isassociated(&a)) + dns_rdataset_disassociate(&a); + if (dns_rdataset_isassociated(&aaaa)) + dns_rdataset_disassociate(&aaaa); + return (answer); +} + +static isc_boolean_t +integrity_checks(dns_zone_t *zone, dns_db_t *db) { + dns_dbiterator_t *dbiterator = NULL; + dns_dbnode_t *node = NULL; + dns_rdataset_t rdataset; + dns_fixedname_t fixed; + dns_fixedname_t fixedbottom; + dns_rdata_mx_t mx; + dns_rdata_ns_t ns; + dns_rdata_in_srv_t srv; + dns_rdata_t rdata; + dns_name_t *name; + dns_name_t *bottom; + isc_result_t result; + isc_boolean_t ok = ISC_TRUE; + + dns_fixedname_init(&fixed); + name = dns_fixedname_name(&fixed); + dns_fixedname_init(&fixedbottom); + bottom = dns_fixedname_name(&fixedbottom); + dns_rdataset_init(&rdataset); + dns_rdata_init(&rdata); + + result = dns_db_createiterator(db, ISC_FALSE, &dbiterator); + if (result != ISC_R_SUCCESS) + return (ISC_TRUE); + + result = dns_dbiterator_first(dbiterator); + while (result == ISC_R_SUCCESS) { + result = dns_dbiterator_current(dbiterator, &node, name); + if (result != ISC_R_SUCCESS) + goto cleanup; + + /* + * Is this name visible in the zone? + */ + if (!dns_name_issubdomain(name, &zone->origin) || + (dns_name_countlabels(bottom) > 0 && + dns_name_issubdomain(name, bottom))) + goto next; + + /* + * Don't check the NS records at the origin. + */ + if (dns_name_equal(name, &zone->origin)) + goto checkmx; + + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_ns, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) + goto checkmx; + /* + * Remember bottom of zone. + */ + dns_name_dup(name, NULL, bottom); + + 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); + if (!zone_check_glue(zone, db, &ns.name, name)) + ok = ISC_FALSE; + dns_rdata_reset(&rdata); + result = dns_rdataset_next(&rdataset); + } + dns_rdataset_disassociate(&rdataset); + + checkmx: + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_mx, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) + goto checksrv; + result = dns_rdataset_first(&rdataset); + while (result == ISC_R_SUCCESS) { + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &mx, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (!zone_check_mx(zone, db, &mx.mx, name)) + ok = ISC_FALSE; + dns_rdata_reset(&rdata); + result = dns_rdataset_next(&rdataset); + } + dns_rdataset_disassociate(&rdataset); + + checksrv: + if (zone->rdclass != dns_rdataclass_in) + goto next; + result = dns_db_findrdataset(db, node, NULL, dns_rdatatype_srv, + 0, 0, &rdataset, NULL); + if (result != ISC_R_SUCCESS) + goto next; + result = dns_rdataset_first(&rdataset); + while (result == ISC_R_SUCCESS) { + dns_rdataset_current(&rdataset, &rdata); + result = dns_rdata_tostruct(&rdata, &srv, NULL); + RUNTIME_CHECK(result == ISC_R_SUCCESS); + if (!zone_check_srv(zone, db, &srv.target, name)) + ok = ISC_FALSE; + dns_rdata_reset(&rdata); + result = dns_rdataset_next(&rdataset); + } + dns_rdataset_disassociate(&rdataset); + + next: + dns_db_detachnode(db, &node); + result = dns_dbiterator_next(dbiterator); + } + + cleanup: + if (node != NULL) + dns_db_detachnode(db, &node); + dns_dbiterator_destroy(&dbiterator); + + return (ok); +} + static isc_result_t zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, isc_result_t result) @@ -1410,6 +1797,13 @@ zone_postload(dns_zone_t *zone, dns_db_t *db, isc_time_t loadtime, result = DNS_R_BADZONE; goto cleanup; } + if (zone->type == dns_zone_master && + DNS_ZONE_OPTION(zone, DNS_ZONEOPT_INTEGRITYCHECK) && + !integrity_checks(zone, db)) { + result = DNS_R_BADZONE; + goto cleanup; + } + if (zone->db != NULL) { if (!isc_serial_ge(serial, zone->serial)) { dns_zone_log(zone, ISC_LOG_ERROR, @@ -1555,7 +1949,8 @@ zone_check_ns(dns_zone_t *zone, dns_db_t *db, dns_name_t *name) { } dns_name_format(name, namebuf, sizeof namebuf); - if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN) { + if (result == DNS_R_NXRRSET || result == DNS_R_NXDOMAIN || + result == DNS_R_EMPTYNAME) { dns_zone_log(zone, level, "NS '%s' has no address records (A or AAAA)", namebuf); @@ -7062,6 +7457,24 @@ dns_zone_checknames(dns_zone_t *zone, dns_name_t *name, dns_rdata_t *rdata) { return (ISC_R_SUCCESS); } +void +dns_zone_setcheckmx(dns_zone_t *zone, dns_checkmxfunc_t checkmx) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->checkmx = checkmx; +} + +void +dns_zone_setchecksrv(dns_zone_t *zone, dns_checksrvfunc_t checksrv) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->checksrv = checksrv; +} + +void +dns_zone_setcheckns(dns_zone_t *zone, dns_checknsfunc_t checkns) { + REQUIRE(DNS_ZONE_VALID(zone)); + zone->checkns = checkns; +} + void dns_zone_setisself(dns_zone_t *zone, dns_isselffunc_t isself, void *arg) { REQUIRE(DNS_ZONE_VALID(zone)); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 44e355f4b9..593eb87209 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -15,7 +15,7 @@ * PERFORMANCE OF THIS SOFTWARE. */ -/* $Id: namedconf.c,v 1.49 2005/05/12 04:38:23 marka Exp $ */ +/* $Id: namedconf.c,v 1.50 2005/05/19 04:59:05 marka Exp $ */ /*! \file */ @@ -793,6 +793,8 @@ zone_clauses[] = { { "zone-statistics", &cfg_type_boolean, 0 }, { "key-directory", &cfg_type_qstring, 0 }, { "check-wildcard", &cfg_type_boolean, 0 }, + { "integrity-check", &cfg_type_boolean, 0 }, + { "check-mx", &cfg_type_checkmode, 0 }, { NULL, NULL, 0 } };