2 * Copyright (C) 2004-2012 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.44 2011/12/22 07:32:39 each 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);
200 if (a == NULL || aaaa == NULL)
203 memset(&hints, 0, sizeof(hints));
204 hints.ai_flags = AI_CANONNAME;
205 hints.ai_family = PF_UNSPEC;
206 hints.ai_socktype = SOCK_STREAM;
207 hints.ai_protocol = IPPROTO_TCP;
209 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
213 if (dns_name_countlabels(name) > 1U)
214 strcat(namebuf, ".");
215 dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
217 result = getaddrinfo(namebuf, NULL, &hints, &ai);
218 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
222 * Work around broken getaddrinfo() implementations that
223 * fail to set ai_canonname on first entry.
226 while (cur != NULL && cur->ai_canonname == NULL &&
227 cur->ai_next != NULL)
229 if (cur != NULL && cur->ai_canonname != NULL &&
230 strcasecmp(cur->ai_canonname, namebuf) != 0 &&
231 !logged(namebuf, ERR_IS_CNAME)) {
232 dns_zone_log(zone, ISC_LOG_ERROR,
233 "%s/NS '%s' (out of zone) "
234 "is a CNAME '%s' (illegal)",
237 /* XXX950 make fatal for 9.5.0 */
238 /* answer = ISC_FALSE; */
239 add(namebuf, ERR_IS_CNAME);
243 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
246 if (!logged(namebuf, ERR_NO_ADDRESSES)) {
247 dns_zone_log(zone, ISC_LOG_ERROR,
248 "%s/NS '%s' (out of zone) "
249 "has no addresses records (A or AAAA)",
251 add(namebuf, ERR_NO_ADDRESSES);
253 /* XXX950 make fatal for 9.5.0 */
257 if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
258 dns_zone_log(zone, ISC_LOG_WARNING,
259 "getaddrinfo(%s) failed: %s",
260 namebuf, gai_strerror(result));
261 add(namebuf, ERR_LOOKUP_FAILURE);
267 * Check that all glue records really exist.
269 if (!dns_rdataset_isassociated(a))
271 result = dns_rdataset_first(a);
272 while (result == ISC_R_SUCCESS) {
273 dns_rdataset_current(a, &rdata);
275 for (cur = ai; cur != NULL; cur = cur->ai_next) {
276 if (cur->ai_family != AF_INET)
278 ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
279 if (memcmp(ptr, rdata.data, rdata.length) == 0) {
284 if (!match && !logged(namebuf, ERR_EXTRA_A)) {
285 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
286 "extra GLUE A record (%s)",
288 inet_ntop(AF_INET, rdata.data,
289 addrbuf, sizeof(addrbuf)));
290 add(namebuf, ERR_EXTRA_A);
291 /* XXX950 make fatal for 9.5.0 */
292 /* answer = ISC_FALSE; */
294 dns_rdata_reset(&rdata);
295 result = dns_rdataset_next(a);
299 if (!dns_rdataset_isassociated(aaaa))
301 result = dns_rdataset_first(aaaa);
302 while (result == ISC_R_SUCCESS) {
303 dns_rdataset_current(aaaa, &rdata);
305 for (cur = ai; cur != NULL; cur = cur->ai_next) {
306 if (cur->ai_family != AF_INET6)
308 ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
309 if (memcmp(ptr, rdata.data, rdata.length) == 0) {
314 if (!match && !logged(namebuf, ERR_EXTRA_AAAA)) {
315 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
316 "extra GLUE AAAA record (%s)",
318 inet_ntop(AF_INET6, rdata.data,
319 addrbuf, sizeof(addrbuf)));
320 add(namebuf, ERR_EXTRA_AAAA);
321 /* XXX950 make fatal for 9.5.0. */
322 /* answer = ISC_FALSE; */
324 dns_rdata_reset(&rdata);
325 result = dns_rdataset_next(aaaa);
330 * Check that all addresses appear in the glue.
332 if (!logged(namebuf, ERR_MISSING_GLUE)) {
333 isc_boolean_t missing_glue = ISC_FALSE;
334 for (cur = ai; cur != NULL; cur = cur->ai_next) {
335 switch (cur->ai_family) {
338 ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
343 ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
350 if (dns_rdataset_isassociated(rdataset))
351 result = dns_rdataset_first(rdataset);
353 result = ISC_R_FAILURE;
354 while (result == ISC_R_SUCCESS && !match) {
355 dns_rdataset_current(rdataset, &rdata);
356 if (memcmp(ptr, rdata.data, rdata.length) == 0)
358 dns_rdata_reset(&rdata);
359 result = dns_rdataset_next(rdataset);
362 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
363 "missing GLUE %s record (%s)",
364 ownerbuf, namebuf, type,
365 inet_ntop(cur->ai_family, ptr,
366 addrbuf, sizeof(addrbuf)));
367 /* XXX950 make fatal for 9.5.0. */
368 /* answer = ISC_FALSE; */
369 missing_glue = ISC_TRUE;
373 add(namebuf, ERR_MISSING_GLUE);
383 checkmx(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner) {
384 #ifdef USE_GETADDRINFO
385 struct addrinfo hints, *ai, *cur;
386 char namebuf[DNS_NAME_FORMATSIZE + 1];
387 char ownerbuf[DNS_NAME_FORMATSIZE];
389 int level = ISC_LOG_ERROR;
390 isc_boolean_t answer = ISC_TRUE;
392 memset(&hints, 0, sizeof(hints));
393 hints.ai_flags = AI_CANONNAME;
394 hints.ai_family = PF_UNSPEC;
395 hints.ai_socktype = SOCK_STREAM;
396 hints.ai_protocol = IPPROTO_TCP;
398 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
402 if (dns_name_countlabels(name) > 1U)
403 strcat(namebuf, ".");
404 dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
406 result = getaddrinfo(namebuf, NULL, &hints, &ai);
407 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
411 * Work around broken getaddrinfo() implementations that
412 * fail to set ai_canonname on first entry.
415 while (cur != NULL && cur->ai_canonname == NULL &&
416 cur->ai_next != NULL)
418 if (cur != NULL && cur->ai_canonname != NULL &&
419 strcasecmp(cur->ai_canonname, namebuf) != 0) {
420 if ((zone_options & DNS_ZONEOPT_WARNMXCNAME) != 0)
421 level = ISC_LOG_WARNING;
422 if ((zone_options & DNS_ZONEOPT_IGNOREMXCNAME) == 0) {
423 if (!logged(namebuf, ERR_IS_MXCNAME)) {
424 dns_zone_log(zone, level,
425 "%s/MX '%s' (out of zone)"
430 add(namebuf, ERR_IS_MXCNAME);
432 if (level == ISC_LOG_ERROR)
440 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
443 if (!logged(namebuf, ERR_NO_ADDRESSES)) {
444 dns_zone_log(zone, ISC_LOG_ERROR,
445 "%s/MX '%s' (out of zone) "
446 "has no addresses records (A or AAAA)",
448 add(namebuf, ERR_NO_ADDRESSES);
450 /* XXX950 make fatal for 9.5.0. */
454 if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
455 dns_zone_log(zone, ISC_LOG_WARNING,
456 "getaddrinfo(%s) failed: %s",
457 namebuf, gai_strerror(result));
458 add(namebuf, ERR_LOOKUP_FAILURE);
468 checksrv(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner) {
469 #ifdef USE_GETADDRINFO
470 struct addrinfo hints, *ai, *cur;
471 char namebuf[DNS_NAME_FORMATSIZE + 1];
472 char ownerbuf[DNS_NAME_FORMATSIZE];
474 int level = ISC_LOG_ERROR;
475 isc_boolean_t answer = ISC_TRUE;
477 memset(&hints, 0, sizeof(hints));
478 hints.ai_flags = AI_CANONNAME;
479 hints.ai_family = PF_UNSPEC;
480 hints.ai_socktype = SOCK_STREAM;
481 hints.ai_protocol = IPPROTO_TCP;
483 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
487 if (dns_name_countlabels(name) > 1U)
488 strcat(namebuf, ".");
489 dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
491 result = getaddrinfo(namebuf, NULL, &hints, &ai);
492 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
496 * Work around broken getaddrinfo() implementations that
497 * fail to set ai_canonname on first entry.
500 while (cur != NULL && cur->ai_canonname == NULL &&
501 cur->ai_next != NULL)
503 if (cur != NULL && cur->ai_canonname != NULL &&
504 strcasecmp(cur->ai_canonname, namebuf) != 0) {
505 if ((zone_options & DNS_ZONEOPT_WARNSRVCNAME) != 0)
506 level = ISC_LOG_WARNING;
507 if ((zone_options & DNS_ZONEOPT_IGNORESRVCNAME) == 0) {
508 if (!logged(namebuf, ERR_IS_SRVCNAME)) {
509 dns_zone_log(zone, level, "%s/SRV '%s'"
510 " (out of zone) is a "
511 "CNAME '%s' (illegal)",
514 add(namebuf, ERR_IS_SRVCNAME);
516 if (level == ISC_LOG_ERROR)
524 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
527 if (!logged(namebuf, ERR_NO_ADDRESSES)) {
528 dns_zone_log(zone, ISC_LOG_ERROR,
529 "%s/SRV '%s' (out of zone) "
530 "has no addresses records (A or AAAA)",
532 add(namebuf, ERR_NO_ADDRESSES);
534 /* XXX950 make fatal for 9.5.0. */
538 if (!logged(namebuf, ERR_LOOKUP_FAILURE)) {
539 dns_zone_log(zone, ISC_LOG_WARNING,
540 "getaddrinfo(%s) failed: %s",
541 namebuf, gai_strerror(result));
542 add(namebuf, ERR_LOOKUP_FAILURE);
552 setup_logging(isc_mem_t *mctx, FILE *errout, isc_log_t **logp) {
553 isc_logdestination_t destination;
554 isc_logconfig_t *logconfig = NULL;
555 isc_log_t *log = NULL;
557 RUNTIME_CHECK(isc_log_create(mctx, &log, &logconfig) == ISC_R_SUCCESS);
558 isc_log_registercategories(log, categories);
559 isc_log_setcontext(log);
561 dns_log_setcontext(log);
564 destination.file.stream = errout;
565 destination.file.name = NULL;
566 destination.file.versions = ISC_LOG_ROLLNEVER;
567 destination.file.maximum_size = 0;
568 RUNTIME_CHECK(isc_log_createchannel(logconfig, "stderr",
571 &destination, 0) == ISC_R_SUCCESS);
572 RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr",
573 NULL, NULL) == ISC_R_SUCCESS);
576 return (ISC_R_SUCCESS);
581 load_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
582 dns_masterformat_t fileformat, const char *classname,
586 dns_rdataclass_t rdclass;
587 isc_textregion_t region;
589 dns_fixedname_t fixorigin;
591 dns_zone_t *zone = NULL;
593 REQUIRE(zonep == NULL || *zonep == NULL);
596 fprintf(stderr, "loading \"%s\" from \"%s\" class \"%s\"\n",
597 zonename, filename, classname);
599 CHECK(dns_zone_create(&zone, mctx));
601 dns_zone_settype(zone, dns_zone_master);
603 isc_buffer_constinit(&buffer, zonename, strlen(zonename));
604 isc_buffer_add(&buffer, strlen(zonename));
605 dns_fixedname_init(&fixorigin);
606 origin = dns_fixedname_name(&fixorigin);
607 CHECK(dns_name_fromtext(origin, &buffer, dns_rootname, 0, NULL));
608 CHECK(dns_zone_setorigin(zone, origin));
609 CHECK(dns_zone_setdbtype(zone, 1, (const char * const *) dbtype));
610 CHECK(dns_zone_setfile2(zone, filename, fileformat));
612 DE_CONST(classname, region.base);
613 region.length = strlen(classname);
614 CHECK(dns_rdataclass_fromtext(&rdclass, ®ion));
616 dns_zone_setclass(zone, rdclass);
617 dns_zone_setoption(zone, zone_options, ISC_TRUE);
618 dns_zone_setoption(zone, DNS_ZONEOPT_NOMERGE, nomerge);
620 dns_zone_setcheckmx(zone, checkmx);
622 dns_zone_setcheckns(zone, checkns);
624 dns_zone_setchecksrv(zone, checksrv);
626 CHECK(dns_zone_load(zone));
634 dns_zone_detach(&zone);
640 dump_zone(const char *zonename, dns_zone_t *zone, const char *filename,
641 dns_masterformat_t fileformat, const dns_master_style_t *style,
642 const isc_uint32_t rawversion)
645 FILE *output = stdout;
648 flags = (fileformat == dns_masterformat_text) ? "w+" : "wb+";
651 if (filename != NULL && strcmp(filename, "-") != 0)
652 fprintf(stderr, "dumping \"%s\" to \"%s\"\n",
655 fprintf(stderr, "dumping \"%s\"\n", zonename);
658 if (filename != NULL && strcmp(filename, "-") != 0) {
659 result = isc_stdio_open(filename, flags, &output);
661 if (result != ISC_R_SUCCESS) {
662 fprintf(stderr, "could not open output "
663 "file \"%s\" for writing\n", filename);
664 return (ISC_R_FAILURE);
668 result = dns_zone_dumptostream3(zone, output, fileformat, style,
670 if (output != stdout)
671 (void)isc_stdio_close(output);
679 WORD wVersionRequested;
683 wVersionRequested = MAKEWORD(2, 0);
685 err = WSAStartup( wVersionRequested, &wsaData );
687 fprintf(stderr, "WSAStartup() failed: %d\n", err);
693 DestroySockets(void) {