2 * Copyright (C) 2004-2008 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.10.18.20 2008/10/24 01:43:17 tbox Exp $ */
26 #include "check-tool.h"
29 #include <isc/buffer.h>
32 #include <isc/netdb.h>
33 #include <isc/region.h>
34 #include <isc/stdio.h>
35 #include <isc/string.h>
36 #include <isc/types.h>
38 #include <dns/fixedname.h>
41 #include <dns/rdata.h>
42 #include <dns/rdataclass.h>
43 #include <dns/rdataset.h>
44 #include <dns/types.h>
47 #include <isccfg/log.h>
50 #define CHECK_SIBLING 1
58 #ifdef HAVE_GETADDRINFO
59 #ifdef HAVE_GAISTRERROR
60 #define USE_GETADDRINFO
68 if (result != ISC_R_SUCCESS) \
72 static const char *dbtype[] = { "rbt" };
75 isc_boolean_t nomerge = ISC_TRUE;
77 isc_boolean_t docheckmx = ISC_TRUE;
78 isc_boolean_t dochecksrv = ISC_TRUE;
79 isc_boolean_t docheckns = ISC_TRUE;
81 isc_boolean_t docheckmx = ISC_FALSE;
82 isc_boolean_t dochecksrv = ISC_FALSE;
83 isc_boolean_t docheckns = ISC_FALSE;
85 unsigned int zone_options = DNS_ZONEOPT_CHECKNS |
87 DNS_ZONEOPT_MANYERRORS |
88 DNS_ZONEOPT_CHECKNAMES |
89 DNS_ZONEOPT_CHECKINTEGRITY |
91 DNS_ZONEOPT_CHECKSIBLING |
93 DNS_ZONEOPT_CHECKWILDCARD |
94 DNS_ZONEOPT_WARNMXCNAME |
95 DNS_ZONEOPT_WARNSRVCNAME;
98 * This needs to match the list in bin/named/log.c.
100 static isc_logcategory_t categories[] = {
107 { "update-security", 0 },
112 checkns(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner,
113 dns_rdataset_t *a, dns_rdataset_t *aaaa)
115 #ifdef USE_GETADDRINFO
116 dns_rdataset_t *rdataset;
117 dns_rdata_t rdata = DNS_RDATA_INIT;
118 struct addrinfo hints, *ai, *cur;
119 char namebuf[DNS_NAME_FORMATSIZE + 1];
120 char ownerbuf[DNS_NAME_FORMATSIZE];
121 char addrbuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
122 isc_boolean_t answer = ISC_TRUE;
128 REQUIRE(a == NULL || !dns_rdataset_isassociated(a) ||
129 a->type == dns_rdatatype_a);
130 REQUIRE(aaaa == NULL || !dns_rdataset_isassociated(aaaa) ||
131 aaaa->type == dns_rdatatype_aaaa);
132 memset(&hints, 0, sizeof(hints));
133 hints.ai_flags = AI_CANONNAME;
134 hints.ai_family = PF_UNSPEC;
135 hints.ai_socktype = SOCK_STREAM;
136 hints.ai_protocol = IPPROTO_TCP;
138 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
142 if (dns_name_countlabels(name) > 1U)
143 strcat(namebuf, ".");
144 dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
146 result = getaddrinfo(namebuf, NULL, &hints, &ai);
147 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
151 * Work around broken getaddrinfo() implementations that
152 * fail to set ai_canonname on first entry.
155 while (cur != NULL && cur->ai_canonname == NULL &&
156 cur->ai_next != NULL)
158 if (cur != NULL && cur->ai_canonname != NULL &&
159 strcasecmp(ai->ai_canonname, namebuf) != 0) {
160 dns_zone_log(zone, ISC_LOG_ERROR,
161 "%s/NS '%s' (out of zone) "
162 "is a CNAME (illegal)",
164 /* XXX950 make fatal for 9.5.0 */
165 /* answer = ISC_FALSE; */
169 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
172 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' (out of zone) "
173 "has no addresses records (A or AAAA)",
175 /* XXX950 make fatal for 9.5.0 */
179 dns_zone_log(zone, ISC_LOG_WARNING,
180 "getaddrinfo(%s) failed: %s",
181 namebuf, gai_strerror(result));
184 if (a == NULL || aaaa == NULL)
187 * Check that all glue records really exist.
189 if (!dns_rdataset_isassociated(a))
191 result = dns_rdataset_first(a);
192 while (result == ISC_R_SUCCESS) {
193 dns_rdataset_current(a, &rdata);
195 for (cur = ai; cur != NULL; cur = cur->ai_next) {
196 if (cur->ai_family != AF_INET)
198 ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
199 if (memcmp(ptr, rdata.data, rdata.length) == 0) {
205 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
206 "extra GLUE A record (%s)",
208 inet_ntop(AF_INET, rdata.data,
209 addrbuf, sizeof(addrbuf)));
210 /* XXX950 make fatal for 9.5.0 */
211 /* answer = ISC_FALSE; */
213 dns_rdata_reset(&rdata);
214 result = dns_rdataset_next(a);
218 if (!dns_rdataset_isassociated(aaaa))
220 result = dns_rdataset_first(aaaa);
221 while (result == ISC_R_SUCCESS) {
222 dns_rdataset_current(aaaa, &rdata);
224 for (cur = ai; cur != NULL; cur = cur->ai_next) {
225 if (cur->ai_family != AF_INET6)
227 ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
228 if (memcmp(ptr, rdata.data, rdata.length) == 0) {
234 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
235 "extra GLUE AAAA record (%s)",
237 inet_ntop(AF_INET6, rdata.data,
238 addrbuf, sizeof(addrbuf)));
239 /* XXX950 make fatal for 9.5.0. */
240 /* answer = ISC_FALSE; */
242 dns_rdata_reset(&rdata);
243 result = dns_rdataset_next(aaaa);
248 * Check that all addresses appear in the glue.
250 for (cur = ai; cur != NULL; cur = cur->ai_next) {
251 switch (cur->ai_family) {
254 ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
259 ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
266 if (dns_rdataset_isassociated(rdataset))
267 result = dns_rdataset_first(rdataset);
269 result = ISC_R_FAILURE;
270 while (result == ISC_R_SUCCESS && !match) {
271 dns_rdataset_current(rdataset, &rdata);
272 if (memcmp(ptr, rdata.data, rdata.length) == 0)
274 dns_rdata_reset(&rdata);
275 result = dns_rdataset_next(rdataset);
278 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
279 "missing GLUE %s record (%s)",
280 ownerbuf, namebuf, type,
281 inet_ntop(cur->ai_family, ptr,
282 addrbuf, sizeof(addrbuf)));
283 /* XXX950 make fatal for 9.5.0. */
284 /* answer = ISC_FALSE; */
295 checkmx(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner) {
296 #ifdef USE_GETADDRINFO
297 struct addrinfo hints, *ai, *cur;
298 char namebuf[DNS_NAME_FORMATSIZE + 1];
299 char ownerbuf[DNS_NAME_FORMATSIZE];
301 int level = ISC_LOG_ERROR;
302 isc_boolean_t answer = ISC_TRUE;
304 memset(&hints, 0, sizeof(hints));
305 hints.ai_flags = AI_CANONNAME;
306 hints.ai_family = PF_UNSPEC;
307 hints.ai_socktype = SOCK_STREAM;
308 hints.ai_protocol = IPPROTO_TCP;
310 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
314 if (dns_name_countlabels(name) > 1U)
315 strcat(namebuf, ".");
316 dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
318 result = getaddrinfo(namebuf, NULL, &hints, &ai);
319 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
323 * Work around broken getaddrinfo() implementations that
324 * fail to set ai_canonname on first entry.
327 while (cur != NULL && cur->ai_canonname == NULL &&
328 cur->ai_next != NULL)
330 if (cur != NULL && cur->ai_canonname != NULL &&
331 strcasecmp(cur->ai_canonname, namebuf) != 0) {
332 if ((zone_options & DNS_ZONEOPT_WARNMXCNAME) != 0)
333 level = ISC_LOG_WARNING;
334 if ((zone_options & DNS_ZONEOPT_IGNOREMXCNAME) == 0) {
335 dns_zone_log(zone, ISC_LOG_WARNING,
336 "%s/MX '%s' (out of zone) "
337 "is a CNAME (illegal)",
339 if (level == ISC_LOG_ERROR)
347 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
350 dns_zone_log(zone, ISC_LOG_ERROR, "%s/MX '%s' (out of zone) "
351 "has no addresses records (A or AAAA)",
353 /* XXX950 make fatal for 9.5.0. */
357 dns_zone_log(zone, ISC_LOG_WARNING,
358 "getaddrinfo(%s) failed: %s",
359 namebuf, gai_strerror(result));
368 checksrv(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner) {
369 #ifdef USE_GETADDRINFO
370 struct addrinfo hints, *ai, *cur;
371 char namebuf[DNS_NAME_FORMATSIZE + 1];
372 char ownerbuf[DNS_NAME_FORMATSIZE];
374 int level = ISC_LOG_ERROR;
375 isc_boolean_t answer = ISC_TRUE;
377 memset(&hints, 0, sizeof(hints));
378 hints.ai_flags = AI_CANONNAME;
379 hints.ai_family = PF_UNSPEC;
380 hints.ai_socktype = SOCK_STREAM;
381 hints.ai_protocol = IPPROTO_TCP;
383 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
387 if (dns_name_countlabels(name) > 1U)
388 strcat(namebuf, ".");
389 dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
391 result = getaddrinfo(namebuf, NULL, &hints, &ai);
392 dns_name_format(name, namebuf, sizeof(namebuf) - 1);
396 * Work around broken getaddrinfo() implementations that
397 * fail to set ai_canonname on first entry.
400 while (cur != NULL && cur->ai_canonname == NULL &&
401 cur->ai_next != NULL)
403 if (cur != NULL && cur->ai_canonname != NULL &&
404 strcasecmp(cur->ai_canonname, namebuf) != 0) {
405 if ((zone_options & DNS_ZONEOPT_WARNSRVCNAME) != 0)
406 level = ISC_LOG_WARNING;
407 if ((zone_options & DNS_ZONEOPT_IGNORESRVCNAME) == 0) {
408 dns_zone_log(zone, level,
409 "%s/SRV '%s' (out of zone) "
410 "is a CNAME (illegal)",
412 if (level == ISC_LOG_ERROR)
420 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
423 dns_zone_log(zone, ISC_LOG_ERROR, "%s/SRV '%s' (out of zone) "
424 "has no addresses records (A or AAAA)",
426 /* XXX950 make fatal for 9.5.0. */
430 dns_zone_log(zone, ISC_LOG_WARNING,
431 "getaddrinfo(%s) failed: %s",
432 namebuf, gai_strerror(result));
441 setup_logging(isc_mem_t *mctx, isc_log_t **logp) {
442 isc_logdestination_t destination;
443 isc_logconfig_t *logconfig = NULL;
444 isc_log_t *log = NULL;
446 RUNTIME_CHECK(isc_log_create(mctx, &log, &logconfig) == ISC_R_SUCCESS);
447 isc_log_registercategories(log, categories);
448 isc_log_setcontext(log);
450 dns_log_setcontext(log);
453 destination.file.stream = stdout;
454 destination.file.name = NULL;
455 destination.file.versions = ISC_LOG_ROLLNEVER;
456 destination.file.maximum_size = 0;
457 RUNTIME_CHECK(isc_log_createchannel(logconfig, "stderr",
460 &destination, 0) == ISC_R_SUCCESS);
461 RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr",
462 NULL, NULL) == ISC_R_SUCCESS);
465 return (ISC_R_SUCCESS);
470 load_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
471 dns_masterformat_t fileformat, const char *classname,
475 dns_rdataclass_t rdclass;
476 isc_textregion_t region;
478 dns_fixedname_t fixorigin;
480 dns_zone_t *zone = NULL;
482 REQUIRE(zonep == NULL || *zonep == NULL);
485 fprintf(stderr, "loading \"%s\" from \"%s\" class \"%s\"\n",
486 zonename, filename, classname);
488 CHECK(dns_zone_create(&zone, mctx));
490 dns_zone_settype(zone, dns_zone_master);
492 isc_buffer_init(&buffer, zonename, strlen(zonename));
493 isc_buffer_add(&buffer, strlen(zonename));
494 dns_fixedname_init(&fixorigin);
495 origin = dns_fixedname_name(&fixorigin);
496 CHECK(dns_name_fromtext(origin, &buffer, dns_rootname,
498 CHECK(dns_zone_setorigin(zone, origin));
499 CHECK(dns_zone_setdbtype(zone, 1, (const char * const *) dbtype));
500 CHECK(dns_zone_setfile2(zone, filename, fileformat));
502 DE_CONST(classname, region.base);
503 region.length = strlen(classname);
504 CHECK(dns_rdataclass_fromtext(&rdclass, ®ion));
506 dns_zone_setclass(zone, rdclass);
507 dns_zone_setoption(zone, zone_options, ISC_TRUE);
508 dns_zone_setoption(zone, DNS_ZONEOPT_NOMERGE, nomerge);
510 dns_zone_setcheckmx(zone, checkmx);
512 dns_zone_setcheckns(zone, checkns);
514 dns_zone_setchecksrv(zone, checksrv);
516 CHECK(dns_zone_load(zone));
524 dns_zone_detach(&zone);
530 dump_zone(const char *zonename, dns_zone_t *zone, const char *filename,
531 dns_masterformat_t fileformat, const dns_master_style_t *style)
534 FILE *output = stdout;
537 if (filename != NULL)
538 fprintf(stderr, "dumping \"%s\" to \"%s\"\n",
541 fprintf(stderr, "dumping \"%s\"\n", zonename);
544 if (filename != NULL) {
545 result = isc_stdio_open(filename, "w+", &output);
547 if (result != ISC_R_SUCCESS) {
548 fprintf(stderr, "could not open output "
549 "file \"%s\" for writing\n", filename);
550 return (ISC_R_FAILURE);
554 result = dns_zone_dumptostream2(zone, output, fileformat, style);
556 if (filename != NULL)
557 (void)isc_stdio_close(output);