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.31.62.5 2009/01/27 21:17:39 jinmei 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 (illegal)",
228 /* XXX950 make fatal for 9.5.0 */
229 /* answer = ISC_FALSE; */
230 add(namebuf
, ERR_IS_CNAME
);
234 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
237 if (!logged(namebuf
, ERR_NO_ADDRESSES
)) {
238 dns_zone_log(zone
, ISC_LOG_ERROR
,
239 "%s/NS '%s' (out of zone) "
240 "has no addresses records (A or AAAA)",
242 add(namebuf
, ERR_NO_ADDRESSES
);
244 /* XXX950 make fatal for 9.5.0 */
248 if (!logged(namebuf
, ERR_LOOKUP_FAILURE
)) {
249 dns_zone_log(zone
, ISC_LOG_WARNING
,
250 "getaddrinfo(%s) failed: %s",
251 namebuf
, gai_strerror(result
));
252 add(namebuf
, ERR_LOOKUP_FAILURE
);
256 if (a
== NULL
|| aaaa
== NULL
)
259 * Check that all glue records really exist.
261 if (!dns_rdataset_isassociated(a
))
263 result
= dns_rdataset_first(a
);
264 while (result
== ISC_R_SUCCESS
) {
265 dns_rdataset_current(a
, &rdata
);
267 for (cur
= ai
; cur
!= NULL
; cur
= cur
->ai_next
) {
268 if (cur
->ai_family
!= AF_INET
)
270 ptr
= &((struct sockaddr_in
*)(cur
->ai_addr
))->sin_addr
;
271 if (memcmp(ptr
, rdata
.data
, rdata
.length
) == 0) {
276 if (!match
&& !logged(namebuf
, ERR_EXTRA_A
)) {
277 dns_zone_log(zone
, ISC_LOG_ERROR
, "%s/NS '%s' "
278 "extra GLUE A record (%s)",
280 inet_ntop(AF_INET
, rdata
.data
,
281 addrbuf
, sizeof(addrbuf
)));
282 add(namebuf
, ERR_EXTRA_A
);
283 /* XXX950 make fatal for 9.5.0 */
284 /* answer = ISC_FALSE; */
286 dns_rdata_reset(&rdata
);
287 result
= dns_rdataset_next(a
);
291 if (!dns_rdataset_isassociated(aaaa
))
293 result
= dns_rdataset_first(aaaa
);
294 while (result
== ISC_R_SUCCESS
) {
295 dns_rdataset_current(aaaa
, &rdata
);
297 for (cur
= ai
; cur
!= NULL
; cur
= cur
->ai_next
) {
298 if (cur
->ai_family
!= AF_INET6
)
300 ptr
= &((struct sockaddr_in6
*)(cur
->ai_addr
))->sin6_addr
;
301 if (memcmp(ptr
, rdata
.data
, rdata
.length
) == 0) {
306 if (!match
&& !logged(namebuf
, ERR_EXTRA_AAAA
)) {
307 dns_zone_log(zone
, ISC_LOG_ERROR
, "%s/NS '%s' "
308 "extra GLUE AAAA record (%s)",
310 inet_ntop(AF_INET6
, rdata
.data
,
311 addrbuf
, sizeof(addrbuf
)));
312 add(namebuf
, ERR_EXTRA_AAAA
);
313 /* XXX950 make fatal for 9.5.0. */
314 /* answer = ISC_FALSE; */
316 dns_rdata_reset(&rdata
);
317 result
= dns_rdataset_next(aaaa
);
322 * Check that all addresses appear in the glue.
324 if (!logged(namebuf
, ERR_MISSING_GLUE
)) {
325 isc_boolean_t missing_glue
= ISC_FALSE
;
326 for (cur
= ai
; cur
!= NULL
; cur
= cur
->ai_next
) {
327 switch (cur
->ai_family
) {
330 ptr
= &((struct sockaddr_in
*)(cur
->ai_addr
))->sin_addr
;
335 ptr
= &((struct sockaddr_in6
*)(cur
->ai_addr
))->sin6_addr
;
342 if (dns_rdataset_isassociated(rdataset
))
343 result
= dns_rdataset_first(rdataset
);
345 result
= ISC_R_FAILURE
;
346 while (result
== ISC_R_SUCCESS
&& !match
) {
347 dns_rdataset_current(rdataset
, &rdata
);
348 if (memcmp(ptr
, rdata
.data
, rdata
.length
) == 0)
350 dns_rdata_reset(&rdata
);
351 result
= dns_rdataset_next(rdataset
);
354 dns_zone_log(zone
, ISC_LOG_ERROR
, "%s/NS '%s' "
355 "missing GLUE %s record (%s)",
356 ownerbuf
, namebuf
, type
,
357 inet_ntop(cur
->ai_family
, ptr
,
358 addrbuf
, sizeof(addrbuf
)));
359 /* XXX950 make fatal for 9.5.0. */
360 /* answer = ISC_FALSE; */
361 missing_glue
= ISC_TRUE
;
365 add(namebuf
, ERR_MISSING_GLUE
);
375 checkmx(dns_zone_t
*zone
, dns_name_t
*name
, dns_name_t
*owner
) {
376 #ifdef USE_GETADDRINFO
377 struct addrinfo hints
, *ai
, *cur
;
378 char namebuf
[DNS_NAME_FORMATSIZE
+ 1];
379 char ownerbuf
[DNS_NAME_FORMATSIZE
];
381 int level
= ISC_LOG_ERROR
;
382 isc_boolean_t answer
= ISC_TRUE
;
384 memset(&hints
, 0, sizeof(hints
));
385 hints
.ai_flags
= AI_CANONNAME
;
386 hints
.ai_family
= PF_UNSPEC
;
387 hints
.ai_socktype
= SOCK_STREAM
;
388 hints
.ai_protocol
= IPPROTO_TCP
;
390 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
394 if (dns_name_countlabels(name
) > 1U)
395 strcat(namebuf
, ".");
396 dns_name_format(owner
, ownerbuf
, sizeof(ownerbuf
));
398 result
= getaddrinfo(namebuf
, NULL
, &hints
, &ai
);
399 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
403 * Work around broken getaddrinfo() implementations that
404 * fail to set ai_canonname on first entry.
407 while (cur
!= NULL
&& cur
->ai_canonname
== NULL
&&
408 cur
->ai_next
!= NULL
)
410 if (cur
!= NULL
&& cur
->ai_canonname
!= NULL
&&
411 strcasecmp(cur
->ai_canonname
, namebuf
) != 0) {
412 if ((zone_options
& DNS_ZONEOPT_WARNMXCNAME
) != 0)
413 level
= ISC_LOG_WARNING
;
414 if ((zone_options
& DNS_ZONEOPT_IGNOREMXCNAME
) == 0) {
415 if (!logged(namebuf
, ERR_IS_MXCNAME
)) {
416 dns_zone_log(zone
, level
,
417 "%s/MX '%s' (out of zone)"
418 " is a CNAME (illegal)",
420 add(namebuf
, ERR_IS_MXCNAME
);
422 if (level
== ISC_LOG_ERROR
)
430 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
433 if (!logged(namebuf
, ERR_NO_ADDRESSES
)) {
434 dns_zone_log(zone
, ISC_LOG_ERROR
,
435 "%s/MX '%s' (out of zone) "
436 "has no addresses records (A or AAAA)",
438 add(namebuf
, ERR_NO_ADDRESSES
);
440 /* XXX950 make fatal for 9.5.0. */
444 if (!logged(namebuf
, ERR_LOOKUP_FAILURE
)) {
445 dns_zone_log(zone
, ISC_LOG_WARNING
,
446 "getaddrinfo(%s) failed: %s",
447 namebuf
, gai_strerror(result
));
448 add(namebuf
, ERR_LOOKUP_FAILURE
);
458 checksrv(dns_zone_t
*zone
, dns_name_t
*name
, dns_name_t
*owner
) {
459 #ifdef USE_GETADDRINFO
460 struct addrinfo hints
, *ai
, *cur
;
461 char namebuf
[DNS_NAME_FORMATSIZE
+ 1];
462 char ownerbuf
[DNS_NAME_FORMATSIZE
];
464 int level
= ISC_LOG_ERROR
;
465 isc_boolean_t answer
= ISC_TRUE
;
467 memset(&hints
, 0, sizeof(hints
));
468 hints
.ai_flags
= AI_CANONNAME
;
469 hints
.ai_family
= PF_UNSPEC
;
470 hints
.ai_socktype
= SOCK_STREAM
;
471 hints
.ai_protocol
= IPPROTO_TCP
;
473 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
477 if (dns_name_countlabels(name
) > 1U)
478 strcat(namebuf
, ".");
479 dns_name_format(owner
, ownerbuf
, sizeof(ownerbuf
));
481 result
= getaddrinfo(namebuf
, NULL
, &hints
, &ai
);
482 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
486 * Work around broken getaddrinfo() implementations that
487 * fail to set ai_canonname on first entry.
490 while (cur
!= NULL
&& cur
->ai_canonname
== NULL
&&
491 cur
->ai_next
!= NULL
)
493 if (cur
!= NULL
&& cur
->ai_canonname
!= NULL
&&
494 strcasecmp(cur
->ai_canonname
, namebuf
) != 0) {
495 if ((zone_options
& DNS_ZONEOPT_WARNSRVCNAME
) != 0)
496 level
= ISC_LOG_WARNING
;
497 if ((zone_options
& DNS_ZONEOPT_IGNORESRVCNAME
) == 0) {
498 if (!logged(namebuf
, ERR_IS_SRVCNAME
)) {
499 dns_zone_log(zone
, level
, "%s/SRV '%s'"
500 " (out of zone) is a "
503 add(namebuf
, ERR_IS_SRVCNAME
);
505 if (level
== ISC_LOG_ERROR
)
513 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
516 if (!logged(namebuf
, ERR_NO_ADDRESSES
)) {
517 dns_zone_log(zone
, ISC_LOG_ERROR
,
518 "%s/SRV '%s' (out of zone) "
519 "has no addresses records (A or AAAA)",
521 add(namebuf
, ERR_NO_ADDRESSES
);
523 /* XXX950 make fatal for 9.5.0. */
527 if (!logged(namebuf
, ERR_LOOKUP_FAILURE
)) {
528 dns_zone_log(zone
, ISC_LOG_WARNING
,
529 "getaddrinfo(%s) failed: %s",
530 namebuf
, gai_strerror(result
));
531 add(namebuf
, ERR_LOOKUP_FAILURE
);
541 setup_logging(isc_mem_t
*mctx
, FILE *errout
, isc_log_t
**logp
) {
542 isc_logdestination_t destination
;
543 isc_logconfig_t
*logconfig
= NULL
;
544 isc_log_t
*log
= NULL
;
546 RUNTIME_CHECK(isc_log_create(mctx
, &log
, &logconfig
) == ISC_R_SUCCESS
);
547 isc_log_registercategories(log
, categories
);
548 isc_log_setcontext(log
);
550 dns_log_setcontext(log
);
553 destination
.file
.stream
= errout
;
554 destination
.file
.name
= NULL
;
555 destination
.file
.versions
= ISC_LOG_ROLLNEVER
;
556 destination
.file
.maximum_size
= 0;
557 RUNTIME_CHECK(isc_log_createchannel(logconfig
, "stderr",
560 &destination
, 0) == ISC_R_SUCCESS
);
561 RUNTIME_CHECK(isc_log_usechannel(logconfig
, "stderr",
562 NULL
, NULL
) == ISC_R_SUCCESS
);
565 return (ISC_R_SUCCESS
);
570 load_zone(isc_mem_t
*mctx
, const char *zonename
, const char *filename
,
571 dns_masterformat_t fileformat
, const char *classname
,
575 dns_rdataclass_t rdclass
;
576 isc_textregion_t region
;
578 dns_fixedname_t fixorigin
;
580 dns_zone_t
*zone
= NULL
;
582 REQUIRE(zonep
== NULL
|| *zonep
== NULL
);
585 fprintf(stderr
, "loading \"%s\" from \"%s\" class \"%s\"\n",
586 zonename
, filename
, classname
);
588 CHECK(dns_zone_create(&zone
, mctx
));
590 dns_zone_settype(zone
, dns_zone_master
);
592 isc_buffer_init(&buffer
, zonename
, strlen(zonename
));
593 isc_buffer_add(&buffer
, strlen(zonename
));
594 dns_fixedname_init(&fixorigin
);
595 origin
= dns_fixedname_name(&fixorigin
);
596 CHECK(dns_name_fromtext(origin
, &buffer
, dns_rootname
,
598 CHECK(dns_zone_setorigin(zone
, origin
));
599 CHECK(dns_zone_setdbtype(zone
, 1, (const char * const *) dbtype
));
600 CHECK(dns_zone_setfile2(zone
, filename
, fileformat
));
602 DE_CONST(classname
, region
.base
);
603 region
.length
= strlen(classname
);
604 CHECK(dns_rdataclass_fromtext(&rdclass
, ®ion
));
606 dns_zone_setclass(zone
, rdclass
);
607 dns_zone_setoption(zone
, zone_options
, ISC_TRUE
);
608 dns_zone_setoption(zone
, DNS_ZONEOPT_NOMERGE
, nomerge
);
610 dns_zone_setcheckmx(zone
, checkmx
);
612 dns_zone_setcheckns(zone
, checkns
);
614 dns_zone_setchecksrv(zone
, checksrv
);
616 CHECK(dns_zone_load(zone
));
624 dns_zone_detach(&zone
);
630 dump_zone(const char *zonename
, dns_zone_t
*zone
, const char *filename
,
631 dns_masterformat_t fileformat
, const dns_master_style_t
*style
)
634 FILE *output
= stdout
;
637 if (filename
!= NULL
&& strcmp(filename
, "-") != 0)
638 fprintf(stderr
, "dumping \"%s\" to \"%s\"\n",
641 fprintf(stderr
, "dumping \"%s\"\n", zonename
);
644 if (filename
!= NULL
&& strcmp(filename
, "-") != 0) {
645 result
= isc_stdio_open(filename
, "w+", &output
);
647 if (result
!= ISC_R_SUCCESS
) {
648 fprintf(stderr
, "could not open output "
649 "file \"%s\" for writing\n", filename
);
650 return (ISC_R_FAILURE
);
654 result
= dns_zone_dumptostream2(zone
, output
, fileformat
, style
);
656 if (output
!= stdout
)
657 (void)isc_stdio_close(output
);