2 * Copyright (C) 2004-2010 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.24.2 2010/09/07 23:46:25 tbox Exp $ */
30 #include "check-tool.h"
31 #include <isc/buffer.h>
34 #include <isc/netdb.h>
36 #include <isc/region.h>
37 #include <isc/stdio.h>
38 #include <isc/string.h>
39 #include <isc/symtab.h>
40 #include <isc/types.h>
43 #include <dns/fixedname.h>
46 #include <dns/rdata.h>
47 #include <dns/rdataclass.h>
48 #include <dns/rdataset.h>
49 #include <dns/types.h>
52 #include <isccfg/log.h>
55 #define CHECK_SIBLING 1
63 #ifdef HAVE_GETADDRINFO
64 #ifdef HAVE_GAISTRERROR
65 #define USE_GETADDRINFO
73 if (result != ISC_R_SUCCESS) \
77 #define ERR_IS_CNAME 1
78 #define ERR_NO_ADDRESSES 2
79 #define ERR_LOOKUP_FAILURE 3
81 #define ERR_EXTRA_AAAA 5
82 #define ERR_MISSING_GLUE 5
83 #define ERR_IS_MXCNAME 6
84 #define ERR_IS_SRVCNAME 7
86 static const char *dbtype[] = { "rbt" };
89 isc_boolean_t nomerge = ISC_TRUE;
91 isc_boolean_t docheckmx = ISC_TRUE;
92 isc_boolean_t dochecksrv = ISC_TRUE;
93 isc_boolean_t docheckns = ISC_TRUE;
95 isc_boolean_t docheckmx = ISC_FALSE;
96 isc_boolean_t dochecksrv = ISC_FALSE;
97 isc_boolean_t docheckns = ISC_FALSE;
99 unsigned int zone_options = DNS_ZONEOPT_CHECKNS |
100 DNS_ZONEOPT_CHECKMX |
101 DNS_ZONEOPT_MANYERRORS |
102 DNS_ZONEOPT_CHECKNAMES |
103 DNS_ZONEOPT_CHECKINTEGRITY |
105 DNS_ZONEOPT_CHECKSIBLING |
107 DNS_ZONEOPT_CHECKWILDCARD |
108 DNS_ZONEOPT_WARNMXCNAME |
109 DNS_ZONEOPT_WARNSRVCNAME;
112 * This needs to match the list in bin/named/log.c.
114 static isc_logcategory_t categories[] = {
121 { "update-security", 0 },
122 { "query-errors", 0 },
126 static isc_symtab_t *symtab = NULL;
127 static isc_mem_t *sym_mctx;
130 freekey(char *key, unsigned int type, isc_symvalue_t value, void *userarg) {
133 isc_mem_free(userarg, key);
137 add(char *key, int value) {
139 isc_symvalue_t symvalue;
141 if (sym_mctx == NULL) {
142 result = isc_mem_create(0, 0, &sym_mctx);
143 if (result != ISC_R_SUCCESS)
147 if (symtab == NULL) {
148 result = isc_symtab_create(sym_mctx, 100, freekey, sym_mctx,
150 if (result != ISC_R_SUCCESS)
154 key = isc_mem_strdup(sym_mctx, key);
158 symvalue.as_pointer = NULL;
159 result = isc_symtab_define(symtab, key, value, symvalue,
160 isc_symexists_reject);
161 if (result != ISC_R_SUCCESS)
162 isc_mem_free(sym_mctx, key);
166 logged(char *key, int value) {
172 result = isc_symtab_lookup(symtab, key, value, NULL);
173 if (result == ISC_R_SUCCESS)
179 checkns(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner,
180 dns_rdataset_t *a, dns_rdataset_t *aaaa)
182 #ifdef USE_GETADDRINFO
183 dns_rdataset_t *rdataset;
184 dns_rdata_t rdata = DNS_RDATA_INIT;
185 struct addrinfo hints, *ai, *cur;
186 char namebuf[DNS_NAME_FORMATSIZE + 1];
187 char ownerbuf[DNS_NAME_FORMATSIZE];
188 char addrbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
189 isc_boolean_t answer = ISC_TRUE;
195 REQUIRE(a == NULL || !dns_rdataset_isassociated(a) ||
196 a->type == dns_rdatatype_a);
197 REQUIRE(aaaa == NULL || !dns_rdataset_isassociated(aaaa) ||
198 aaaa->type == dns_rdatatype_aaaa);
199 memset(&hints, 0, sizeof(hints));
200 hints.ai_flags = AI_CANONNAME;
201 hints.ai_family = PF_UNSPEC;
202 hints.ai_socktype = SOCK_STREAM;
203 hints.ai_protocol = IPPROTO_TCP;
205 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
209 if (dns_name_countlabels(name) > 1U)
210 strcat(namebuf, ".");
211 dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
213 result = getaddrinfo(namebuf, NULL, &hints, &ai);
214 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
218 * Work around broken getaddrinfo() implementations that
219 * fail to set ai_canonname on first entry.
222 while (cur != NULL && cur->ai_canonname == NULL &&
223 cur->ai_next != NULL)
225 if (cur != NULL && cur->ai_canonname != NULL &&
226 strcasecmp(cur->ai_canonname, namebuf) != 0 &&
227 !logged(namebuf, ERR_IS_CNAME)) {
228 dns_zone_log(zone, ISC_LOG_ERROR,
229 "%s/NS '%s' (out of zone) "
230 "is a CNAME '%s' (illegal)",
233 /* XXX950 make fatal for 9.5.0 */
234 /* answer = ISC_FALSE; */
235 add(namebuf, ERR_IS_CNAME);
239 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
242 if (!logged(namebuf, ERR_NO_ADDRESSES)) {
243 dns_zone_log(zone, ISC_LOG_ERROR,
244 "%s/NS '%s' (out of zone) "
245 "has no addresses records (A or AAAA)",
247 add(namebuf, ERR_NO_ADDRESSES);
249 /* XXX950 make fatal for 9.5.0 */
253 if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
254 dns_zone_log(zone, ISC_LOG_WARNING,
255 "getaddrinfo(%s) failed: %s",
256 namebuf, gai_strerror(result));
257 add(namebuf, ERR_LOOKUP_FAILURE);
261 if (a == NULL || aaaa == NULL)
264 * Check that all glue records really exist.
266 if (!dns_rdataset_isassociated(a))
268 result = dns_rdataset_first(a);
269 while (result == ISC_R_SUCCESS) {
270 dns_rdataset_current(a, &rdata);
272 for (cur = ai; cur != NULL; cur = cur->ai_next) {
273 if (cur->ai_family != AF_INET)
275 ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
276 if (memcmp(ptr, rdata.data, rdata.length) == 0) {
281 if (!match && !logged(namebuf, ERR_EXTRA_A)) {
282 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
283 "extra GLUE A record (%s)",
285 inet_ntop(AF_INET, rdata.data,
286 addrbuf, sizeof(addrbuf)));
287 add(namebuf, ERR_EXTRA_A);
288 /* XXX950 make fatal for 9.5.0 */
289 /* answer = ISC_FALSE; */
291 dns_rdata_reset(&rdata);
292 result = dns_rdataset_next(a);
296 if (!dns_rdataset_isassociated(aaaa))
298 result = dns_rdataset_first(aaaa);
299 while (result == ISC_R_SUCCESS) {
300 dns_rdataset_current(aaaa, &rdata);
302 for (cur = ai; cur != NULL; cur = cur->ai_next) {
303 if (cur->ai_family != AF_INET6)
305 ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
306 if (memcmp(ptr, rdata.data, rdata.length) == 0) {
311 if (!match && !logged(namebuf, ERR_EXTRA_AAAA)) {
312 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
313 "extra GLUE AAAA record (%s)",
315 inet_ntop(AF_INET6, rdata.data,
316 addrbuf, sizeof(addrbuf)));
317 add(namebuf, ERR_EXTRA_AAAA);
318 /* XXX950 make fatal for 9.5.0. */
319 /* answer = ISC_FALSE; */
321 dns_rdata_reset(&rdata);
322 result = dns_rdataset_next(aaaa);
327 * Check that all addresses appear in the glue.
329 if (!logged(namebuf, ERR_MISSING_GLUE)) {
330 isc_boolean_t missing_glue = ISC_FALSE;
331 for (cur = ai; cur != NULL; cur = cur->ai_next) {
332 switch (cur->ai_family) {
335 ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
340 ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
347 if (dns_rdataset_isassociated(rdataset))
348 result = dns_rdataset_first(rdataset);
350 result = ISC_R_FAILURE;
351 while (result == ISC_R_SUCCESS && !match) {
352 dns_rdataset_current(rdataset, &rdata);
353 if (memcmp(ptr, rdata.data, rdata.length) == 0)
355 dns_rdata_reset(&rdata);
356 result = dns_rdataset_next(rdataset);
359 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
360 "missing GLUE %s record (%s)",
361 ownerbuf, namebuf, type,
362 inet_ntop(cur->ai_family, ptr,
363 addrbuf, sizeof(addrbuf)));
364 /* XXX950 make fatal for 9.5.0. */
365 /* answer = ISC_FALSE; */
366 missing_glue = ISC_TRUE;
370 add(namebuf, ERR_MISSING_GLUE);
380 checkmx(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner) {
381 #ifdef USE_GETADDRINFO
382 struct addrinfo hints, *ai, *cur;
383 char namebuf[DNS_NAME_FORMATSIZE + 1];
384 char ownerbuf[DNS_NAME_FORMATSIZE];
386 int level = ISC_LOG_ERROR;
387 isc_boolean_t answer = ISC_TRUE;
389 memset(&hints, 0, sizeof(hints));
390 hints.ai_flags = AI_CANONNAME;
391 hints.ai_family = PF_UNSPEC;
392 hints.ai_socktype = SOCK_STREAM;
393 hints.ai_protocol = IPPROTO_TCP;
395 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
399 if (dns_name_countlabels(name) > 1U)
400 strcat(namebuf, ".");
401 dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
403 result = getaddrinfo(namebuf, NULL, &hints, &ai);
404 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
408 * Work around broken getaddrinfo() implementations that
409 * fail to set ai_canonname on first entry.
412 while (cur != NULL && cur->ai_canonname == NULL &&
413 cur->ai_next != NULL)
415 if (cur != NULL && cur->ai_canonname != NULL &&
416 strcasecmp(cur->ai_canonname, namebuf) != 0) {
417 if ((zone_options & DNS_ZONEOPT_WARNMXCNAME) != 0)
418 level = ISC_LOG_WARNING;
419 if ((zone_options & DNS_ZONEOPT_IGNOREMXCNAME) == 0) {
420 if (!logged(namebuf, ERR_IS_MXCNAME)) {
421 dns_zone_log(zone, level,
422 "%s/MX '%s' (out of zone)"
427 add(namebuf, ERR_IS_MXCNAME);
429 if (level == ISC_LOG_ERROR)
437 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
440 if (!logged(namebuf, ERR_NO_ADDRESSES)) {
441 dns_zone_log(zone, ISC_LOG_ERROR,
442 "%s/MX '%s' (out of zone) "
443 "has no addresses records (A or AAAA)",
445 add(namebuf, ERR_NO_ADDRESSES);
447 /* XXX950 make fatal for 9.5.0. */
451 if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
452 dns_zone_log(zone, ISC_LOG_WARNING,
453 "getaddrinfo(%s) failed: %s",
454 namebuf, gai_strerror(result));
455 add(namebuf, ERR_LOOKUP_FAILURE);
465 checksrv(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner) {
466 #ifdef USE_GETADDRINFO
467 struct addrinfo hints, *ai, *cur;
468 char namebuf[DNS_NAME_FORMATSIZE + 1];
469 char ownerbuf[DNS_NAME_FORMATSIZE];
471 int level = ISC_LOG_ERROR;
472 isc_boolean_t answer = ISC_TRUE;
474 memset(&hints, 0, sizeof(hints));
475 hints.ai_flags = AI_CANONNAME;
476 hints.ai_family = PF_UNSPEC;
477 hints.ai_socktype = SOCK_STREAM;
478 hints.ai_protocol = IPPROTO_TCP;
480 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
484 if (dns_name_countlabels(name) > 1U)
485 strcat(namebuf, ".");
486 dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
488 result = getaddrinfo(namebuf, NULL, &hints, &ai);
489 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
493 * Work around broken getaddrinfo() implementations that
494 * fail to set ai_canonname on first entry.
497 while (cur != NULL && cur->ai_canonname == NULL &&
498 cur->ai_next != NULL)
500 if (cur != NULL && cur->ai_canonname != NULL &&
501 strcasecmp(cur->ai_canonname, namebuf) != 0) {
502 if ((zone_options & DNS_ZONEOPT_WARNSRVCNAME) != 0)
503 level = ISC_LOG_WARNING;
504 if ((zone_options & DNS_ZONEOPT_IGNORESRVCNAME) == 0) {
505 if (!logged(namebuf, ERR_IS_SRVCNAME)) {
506 dns_zone_log(zone, level, "%s/SRV '%s'"
507 " (out of zone) is a "
508 "CNAME '%s' (illegal)",
511 add(namebuf, ERR_IS_SRVCNAME);
513 if (level == ISC_LOG_ERROR)
521 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
524 if (!logged(namebuf, ERR_NO_ADDRESSES)) {
525 dns_zone_log(zone, ISC_LOG_ERROR,
526 "%s/SRV '%s' (out of zone) "
527 "has no addresses records (A or AAAA)",
529 add(namebuf, ERR_NO_ADDRESSES);
531 /* XXX950 make fatal for 9.5.0. */
535 if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
536 dns_zone_log(zone, ISC_LOG_WARNING,
537 "getaddrinfo(%s) failed: %s",
538 namebuf, gai_strerror(result));
539 add(namebuf, ERR_LOOKUP_FAILURE);
549 setup_logging(isc_mem_t *mctx, FILE *errout, isc_log_t **logp) {
550 isc_logdestination_t destination;
551 isc_logconfig_t *logconfig = NULL;
552 isc_log_t *log = NULL;
554 RUNTIME_CHECK(isc_log_create(mctx, &log, &logconfig) == ISC_R_SUCCESS);
555 isc_log_registercategories(log, categories);
556 isc_log_setcontext(log);
558 dns_log_setcontext(log);
561 destination.file.stream = errout;
562 destination.file.name = NULL;
563 destination.file.versions = ISC_LOG_ROLLNEVER;
564 destination.file.maximum_size = 0;
565 RUNTIME_CHECK(isc_log_createchannel(logconfig, "stderr",
568 &destination, 0) == ISC_R_SUCCESS);
569 RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr",
570 NULL, NULL) == ISC_R_SUCCESS);
573 return (ISC_R_SUCCESS);
578 load_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
579 dns_masterformat_t fileformat, const char *classname,
583 dns_rdataclass_t rdclass;
584 isc_textregion_t region;
586 dns_fixedname_t fixorigin;
588 dns_zone_t *zone = NULL;
590 REQUIRE(zonep == NULL || *zonep == NULL);
593 fprintf(stderr, "loading \"%s\" from \"%s\" class \"%s\"\n",
594 zonename, filename, classname);
596 CHECK(dns_zone_create(&zone, mctx));
598 dns_zone_settype(zone, dns_zone_master);
600 isc_buffer_init(&buffer, zonename, strlen(zonename));
601 isc_buffer_add(&buffer, strlen(zonename));
602 dns_fixedname_init(&fixorigin);
603 origin = dns_fixedname_name(&fixorigin);
604 CHECK(dns_name_fromtext(origin, &buffer, dns_rootname,
606 CHECK(dns_zone_setorigin(zone, origin));
607 CHECK(dns_zone_setdbtype(zone, 1, (const char * const *) dbtype));
608 CHECK(dns_zone_setfile2(zone, filename, fileformat));
610 DE_CONST(classname, region.base);
611 region.length = strlen(classname);
612 CHECK(dns_rdataclass_fromtext(&rdclass, ®ion));
614 dns_zone_setclass(zone, rdclass);
615 dns_zone_setoption(zone, zone_options, ISC_TRUE);
616 dns_zone_setoption(zone, DNS_ZONEOPT_NOMERGE, nomerge);
618 dns_zone_setcheckmx(zone, checkmx);
620 dns_zone_setcheckns(zone, checkns);
622 dns_zone_setchecksrv(zone, checksrv);
624 CHECK(dns_zone_load(zone));
632 dns_zone_detach(&zone);
638 dump_zone(const char *zonename, dns_zone_t *zone, const char *filename,
639 dns_masterformat_t fileformat, const dns_master_style_t *style)
642 FILE *output = stdout;
645 if (filename != NULL && strcmp(filename, "-") != 0)
646 fprintf(stderr, "dumping \"%s\" to \"%s\"\n",
649 fprintf(stderr, "dumping \"%s\"\n", zonename);
652 if (filename != NULL && strcmp(filename, "-") != 0) {
653 result = isc_stdio_open(filename, "w+", &output);
655 if (result != ISC_R_SUCCESS) {
656 fprintf(stderr, "could not open output "
657 "file \"%s\" for writing\n", filename);
658 return (ISC_R_FAILURE);
662 result = dns_zone_dumptostream2(zone, output, fileformat, style);
664 if (output != stdout)
665 (void)isc_stdio_close(output);
673 WORD wVersionRequested;
677 wVersionRequested = MAKEWORD(2, 0);
679 err = WSAStartup( wVersionRequested, &wsaData );
681 fprintf(stderr, "WSAStartup() failed: %d\n", err);
687 DestroySockets(void) {