2 * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2002 Internet Software Consortium.
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: check-tool.c,v 1.35.36.3 2009/01/20 02:03:18 marka Exp $ */
26 #include "check-tool.h"
27 #include <isc/buffer.h>
30 #include <isc/netdb.h>
32 #include <isc/region.h>
33 #include <isc/stdio.h>
34 #include <isc/string.h>
35 #include <isc/symtab.h>
36 #include <isc/types.h>
39 #include <dns/fixedname.h>
42 #include <dns/rdata.h>
43 #include <dns/rdataclass.h>
44 #include <dns/rdataset.h>
45 #include <dns/types.h>
48 #include <isccfg/log.h>
51 #define CHECK_SIBLING 1
59 #ifdef HAVE_GETADDRINFO
60 #ifdef HAVE_GAISTRERROR
61 #define USE_GETADDRINFO
69 if (result != ISC_R_SUCCESS) \
73 #define ERR_IS_CNAME 1
74 #define ERR_NO_ADDRESSES 2
75 #define ERR_LOOKUP_FAILURE 3
77 #define ERR_EXTRA_AAAA 5
78 #define ERR_MISSING_GLUE 5
79 #define ERR_IS_MXCNAME 6
80 #define ERR_IS_SRVCNAME 7
82 static const char *dbtype[] = { "rbt" };
85 isc_boolean_t nomerge = ISC_TRUE;
87 isc_boolean_t docheckmx = ISC_TRUE;
88 isc_boolean_t dochecksrv = ISC_TRUE;
89 isc_boolean_t docheckns = ISC_TRUE;
91 isc_boolean_t docheckmx = ISC_FALSE;
92 isc_boolean_t dochecksrv = ISC_FALSE;
93 isc_boolean_t docheckns = ISC_FALSE;
95 unsigned int zone_options = DNS_ZONEOPT_CHECKNS |
97 DNS_ZONEOPT_MANYERRORS |
98 DNS_ZONEOPT_CHECKNAMES |
99 DNS_ZONEOPT_CHECKINTEGRITY |
101 DNS_ZONEOPT_CHECKSIBLING |
103 DNS_ZONEOPT_CHECKWILDCARD |
104 DNS_ZONEOPT_WARNMXCNAME |
105 DNS_ZONEOPT_WARNSRVCNAME;
108 * This needs to match the list in bin/named/log.c.
110 static isc_logcategory_t categories[] = {
117 { "update-security", 0 },
118 { "query-errors", 0 },
122 static isc_symtab_t *symtab = NULL;
123 static isc_mem_t *sym_mctx;
126 freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
129 isc_mem_free(userarg, key);
133 add(char *key, int value) {
135 isc_symvalue_t symvalue;
137 if (sym_mctx == NULL) {
138 result = isc_mem_create(0, 0, &sym_mctx);
139 if (result != ISC_R_SUCCESS)
143 if (symtab == NULL) {
144 result = isc_symtab_create(sym_mctx, 100, freekey, sym_mctx,
146 if (result != ISC_R_SUCCESS)
150 key = isc_mem_strdup(sym_mctx, key);
154 symvalue.as_pointer = NULL;
155 result = isc_symtab_define(symtab, key, value, symvalue,
156 isc_symexists_reject);
157 if (result != ISC_R_SUCCESS)
158 isc_mem_free(sym_mctx, key);
162 logged(char *key, int value) {
168 result = isc_symtab_lookup(symtab, key, value, NULL);
169 if (result == ISC_R_SUCCESS)
175 checkns(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner,
176 dns_rdataset_t *a, dns_rdataset_t *aaaa)
178 #ifdef USE_GETADDRINFO
179 dns_rdataset_t *rdataset;
180 dns_rdata_t rdata = DNS_RDATA_INIT;
181 struct addrinfo hints, *ai, *cur;
182 char namebuf[DNS_NAME_FORMATSIZE + 1];
183 char ownerbuf[DNS_NAME_FORMATSIZE];
184 char addrbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
185 isc_boolean_t answer = ISC_TRUE;
191 REQUIRE(a == NULL || !dns_rdataset_isassociated(a) ||
192 a->type == dns_rdatatype_a);
193 REQUIRE(aaaa == NULL || !dns_rdataset_isassociated(aaaa) ||
194 aaaa->type == dns_rdatatype_aaaa);
195 memset(&hints, 0, sizeof(hints));
196 hints.ai_flags = AI_CANONNAME;
197 hints.ai_family = PF_UNSPEC;
198 hints.ai_socktype = SOCK_STREAM;
199 hints.ai_protocol = IPPROTO_TCP;
201 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
205 if (dns_name_countlabels(name) > 1U)
206 strcat(namebuf, ".");
207 dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
209 result = getaddrinfo(namebuf, NULL, &hints, &ai);
210 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
214 * Work around broken getaddrinfo() implementations that
215 * fail to set ai_canonname on first entry.
218 while (cur != NULL && cur->ai_canonname == NULL &&
219 cur->ai_next != NULL)
221 if (cur != NULL && cur->ai_canonname != NULL &&
222 strcasecmp(cur->ai_canonname, namebuf) != 0 &&
223 !logged(namebuf, ERR_IS_CNAME)) {
224 dns_zone_log(zone, ISC_LOG_ERROR,
225 "%s/NS '%s' (out of zone) "
226 "is a CNAME '%s' (illegal)",
229 /* XXX950 make fatal for 9.5.0 */
230 /* answer = ISC_FALSE; */
231 add(namebuf, ERR_IS_CNAME);
235 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
238 if (!logged(namebuf, ERR_NO_ADDRESSES)) {
239 dns_zone_log(zone, ISC_LOG_ERROR,
240 "%s/NS '%s' (out of zone) "
241 "has no addresses records (A or AAAA)",
243 add(namebuf, ERR_NO_ADDRESSES);
245 /* XXX950 make fatal for 9.5.0 */
249 if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
250 dns_zone_log(zone, ISC_LOG_WARNING,
251 "getaddrinfo(%s) failed: %s",
252 namebuf, gai_strerror(result));
253 add(namebuf, ERR_LOOKUP_FAILURE);
257 if (a == NULL || aaaa == NULL)
260 * Check that all glue records really exist.
262 if (!dns_rdataset_isassociated(a))
264 result = dns_rdataset_first(a);
265 while (result == ISC_R_SUCCESS) {
266 dns_rdataset_current(a, &rdata);
268 for (cur = ai; cur != NULL; cur = cur->ai_next) {
269 if (cur->ai_family != AF_INET)
271 ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
272 if (memcmp(ptr, rdata.data, rdata.length) == 0) {
277 if (!match && !logged(namebuf, ERR_EXTRA_A)) {
278 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
279 "extra GLUE A record (%s)",
281 inet_ntop(AF_INET, rdata.data,
282 addrbuf, sizeof(addrbuf)));
283 add(namebuf, ERR_EXTRA_A);
284 /* XXX950 make fatal for 9.5.0 */
285 /* answer = ISC_FALSE; */
287 dns_rdata_reset(&rdata);
288 result = dns_rdataset_next(a);
292 if (!dns_rdataset_isassociated(aaaa))
294 result = dns_rdataset_first(aaaa);
295 while (result == ISC_R_SUCCESS) {
296 dns_rdataset_current(aaaa, &rdata);
298 for (cur = ai; cur != NULL; cur = cur->ai_next) {
299 if (cur->ai_family != AF_INET6)
301 ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
302 if (memcmp(ptr, rdata.data, rdata.length) == 0) {
307 if (!match && !logged(namebuf, ERR_EXTRA_AAAA)) {
308 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
309 "extra GLUE AAAA record (%s)",
311 inet_ntop(AF_INET6, rdata.data,
312 addrbuf, sizeof(addrbuf)));
313 add(namebuf, ERR_EXTRA_AAAA);
314 /* XXX950 make fatal for 9.5.0. */
315 /* answer = ISC_FALSE; */
317 dns_rdata_reset(&rdata);
318 result = dns_rdataset_next(aaaa);
323 * Check that all addresses appear in the glue.
325 if (!logged(namebuf, ERR_MISSING_GLUE)) {
326 isc_boolean_t missing_glue = ISC_FALSE;
327 for (cur = ai; cur != NULL; cur = cur->ai_next) {
328 switch (cur->ai_family) {
331 ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
336 ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
343 if (dns_rdataset_isassociated(rdataset))
344 result = dns_rdataset_first(rdataset);
346 result = ISC_R_FAILURE;
347 while (result == ISC_R_SUCCESS && !match) {
348 dns_rdataset_current(rdataset, &rdata);
349 if (memcmp(ptr, rdata.data, rdata.length) == 0)
351 dns_rdata_reset(&rdata);
352 result = dns_rdataset_next(rdataset);
355 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
356 "missing GLUE %s record (%s)",
357 ownerbuf, namebuf, type,
358 inet_ntop(cur->ai_family, ptr,
359 addrbuf, sizeof(addrbuf)));
360 /* XXX950 make fatal for 9.5.0. */
361 /* answer = ISC_FALSE; */
362 missing_glue = ISC_TRUE;
366 add(namebuf, ERR_MISSING_GLUE);
376 checkmx(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner) {
377 #ifdef USE_GETADDRINFO
378 struct addrinfo hints, *ai, *cur;
379 char namebuf[DNS_NAME_FORMATSIZE + 1];
380 char ownerbuf[DNS_NAME_FORMATSIZE];
382 int level = ISC_LOG_ERROR;
383 isc_boolean_t answer = ISC_TRUE;
385 memset(&hints, 0, sizeof(hints));
386 hints.ai_flags = AI_CANONNAME;
387 hints.ai_family = PF_UNSPEC;
388 hints.ai_socktype = SOCK_STREAM;
389 hints.ai_protocol = IPPROTO_TCP;
391 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
395 if (dns_name_countlabels(name) > 1U)
396 strcat(namebuf, ".");
397 dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
399 result = getaddrinfo(namebuf, NULL, &hints, &ai);
400 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
404 * Work around broken getaddrinfo() implementations that
405 * fail to set ai_canonname on first entry.
408 while (cur != NULL && cur->ai_canonname == NULL &&
409 cur->ai_next != NULL)
411 if (cur != NULL && cur->ai_canonname != NULL &&
412 strcasecmp(cur->ai_canonname, namebuf) != 0) {
413 if ((zone_options & DNS_ZONEOPT_WARNMXCNAME) != 0)
414 level = ISC_LOG_WARNING;
415 if ((zone_options & DNS_ZONEOPT_IGNOREMXCNAME) == 0) {
416 if (!logged(namebuf, ERR_IS_MXCNAME)) {
417 dns_zone_log(zone, level,
418 "%s/MX '%s' (out of zone)"
423 add(namebuf, ERR_IS_MXCNAME);
425 if (level == ISC_LOG_ERROR)
433 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
436 if (!logged(namebuf, ERR_NO_ADDRESSES)) {
437 dns_zone_log(zone, ISC_LOG_ERROR,
438 "%s/MX '%s' (out of zone) "
439 "has no addresses records (A or AAAA)",
441 add(namebuf, ERR_NO_ADDRESSES);
443 /* XXX950 make fatal for 9.5.0. */
447 if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
448 dns_zone_log(zone, ISC_LOG_WARNING,
449 "getaddrinfo(%s) failed: %s",
450 namebuf, gai_strerror(result));
451 add(namebuf, ERR_LOOKUP_FAILURE);
461 checksrv(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner) {
462 #ifdef USE_GETADDRINFO
463 struct addrinfo hints, *ai, *cur;
464 char namebuf[DNS_NAME_FORMATSIZE + 1];
465 char ownerbuf[DNS_NAME_FORMATSIZE];
467 int level = ISC_LOG_ERROR;
468 isc_boolean_t answer = ISC_TRUE;
470 memset(&hints, 0, sizeof(hints));
471 hints.ai_flags = AI_CANONNAME;
472 hints.ai_family = PF_UNSPEC;
473 hints.ai_socktype = SOCK_STREAM;
474 hints.ai_protocol = IPPROTO_TCP;
476 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
480 if (dns_name_countlabels(name) > 1U)
481 strcat(namebuf, ".");
482 dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
484 result = getaddrinfo(namebuf, NULL, &hints, &ai);
485 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
489 * Work around broken getaddrinfo() implementations that
490 * fail to set ai_canonname on first entry.
493 while (cur != NULL && cur->ai_canonname == NULL &&
494 cur->ai_next != NULL)
496 if (cur != NULL && cur->ai_canonname != NULL &&
497 strcasecmp(cur->ai_canonname, namebuf) != 0) {
498 if ((zone_options & DNS_ZONEOPT_WARNSRVCNAME) != 0)
499 level = ISC_LOG_WARNING;
500 if ((zone_options & DNS_ZONEOPT_IGNORESRVCNAME) == 0) {
501 if (!logged(namebuf, ERR_IS_SRVCNAME)) {
502 dns_zone_log(zone, level, "%s/SRV '%s'"
503 " (out of zone) is a "
504 "CNAME '%s' (illegal)",
507 add(namebuf, ERR_IS_SRVCNAME);
509 if (level == ISC_LOG_ERROR)
517 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
520 if (!logged(namebuf, ERR_NO_ADDRESSES)) {
521 dns_zone_log(zone, ISC_LOG_ERROR,
522 "%s/SRV '%s' (out of zone) "
523 "has no addresses records (A or AAAA)",
525 add(namebuf, ERR_NO_ADDRESSES);
527 /* XXX950 make fatal for 9.5.0. */
531 if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
532 dns_zone_log(zone, ISC_LOG_WARNING,
533 "getaddrinfo(%s) failed: %s",
534 namebuf, gai_strerror(result));
535 add(namebuf, ERR_LOOKUP_FAILURE);
545 setup_logging(isc_mem_t *mctx, FILE *errout, isc_log_t **logp) {
546 isc_logdestination_t destination;
547 isc_logconfig_t *logconfig = NULL;
548 isc_log_t *log = NULL;
550 RUNTIME_CHECK(isc_log_create(mctx, &log, &logconfig) == ISC_R_SUCCESS);
551 isc_log_registercategories(log, categories);
552 isc_log_setcontext(log);
554 dns_log_setcontext(log);
557 destination.file.stream = errout;
558 destination.file.name = NULL;
559 destination.file.versions = ISC_LOG_ROLLNEVER;
560 destination.file.maximum_size = 0;
561 RUNTIME_CHECK(isc_log_createchannel(logconfig, "stderr",
564 &destination, 0) == ISC_R_SUCCESS);
565 RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr",
566 NULL, NULL) == ISC_R_SUCCESS);
569 return (ISC_R_SUCCESS);
574 load_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
575 dns_masterformat_t fileformat, const char *classname,
579 dns_rdataclass_t rdclass;
580 isc_textregion_t region;
582 dns_fixedname_t fixorigin;
584 dns_zone_t *zone = NULL;
586 REQUIRE(zonep == NULL || *zonep == NULL);
589 fprintf(stderr, "loading \"%s\" from \"%s\" class \"%s\"\n",
590 zonename, filename, classname);
592 CHECK(dns_zone_create(&zone, mctx));
594 dns_zone_settype(zone, dns_zone_master);
596 isc_buffer_init(&buffer, zonename, strlen(zonename));
597 isc_buffer_add(&buffer, strlen(zonename));
598 dns_fixedname_init(&fixorigin);
599 origin = dns_fixedname_name(&fixorigin);
600 CHECK(dns_name_fromtext(origin, &buffer, dns_rootname,
602 CHECK(dns_zone_setorigin(zone, origin));
603 CHECK(dns_zone_setdbtype(zone, 1, (const char * const *) dbtype));
604 CHECK(dns_zone_setfile2(zone, filename, fileformat));
606 DE_CONST(classname, region.base);
607 region.length = strlen(classname);
608 CHECK(dns_rdataclass_fromtext(&rdclass, ®ion));
610 dns_zone_setclass(zone, rdclass);
611 dns_zone_setoption(zone, zone_options, ISC_TRUE);
612 dns_zone_setoption(zone, DNS_ZONEOPT_NOMERGE, nomerge);
614 dns_zone_setcheckmx(zone, checkmx);
616 dns_zone_setcheckns(zone, checkns);
618 dns_zone_setchecksrv(zone, checksrv);
620 CHECK(dns_zone_load(zone));
628 dns_zone_detach(&zone);
634 dump_zone(const char *zonename, dns_zone_t *zone, const char *filename,
635 dns_masterformat_t fileformat, const dns_master_style_t *style)
638 FILE *output = stdout;
641 if (filename != NULL && strcmp(filename, "-") != 0)
642 fprintf(stderr, "dumping \"%s\" to \"%s\"\n",
645 fprintf(stderr, "dumping \"%s\"\n", zonename);
648 if (filename != NULL && strcmp(filename, "-") != 0) {
649 result = isc_stdio_open(filename, "w+", &output);
651 if (result != ISC_R_SUCCESS) {
652 fprintf(stderr, "could not open output "
653 "file \"%s\" for writing\n", filename);
654 return (ISC_R_FAILURE);
658 result = dns_zone_dumptostream2(zone, output, fileformat, style);
660 if (output != stdout)
661 (void)isc_stdio_close(output);