4 * Copyright (C) 2004-2009 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000-2002 Internet Software Consortium.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
20 /* Id: check-tool.c,v 1.39 2009/09/01 00:22:24 jinmei Exp */
28 #include "check-tool.h"
29 #include <isc/buffer.h>
32 #include <isc/netdb.h>
34 #include <isc/region.h>
35 #include <isc/stdio.h>
36 #include <isc/string.h>
37 #include <isc/symtab.h>
38 #include <isc/types.h>
41 #include <dns/fixedname.h>
44 #include <dns/rdata.h>
45 #include <dns/rdataclass.h>
46 #include <dns/rdataset.h>
47 #include <dns/types.h>
50 #include <isccfg/log.h>
53 #define CHECK_SIBLING 1
61 #ifdef HAVE_GETADDRINFO
62 #ifdef HAVE_GAISTRERROR
63 #define USE_GETADDRINFO
71 if (result != ISC_R_SUCCESS) \
75 #define ERR_IS_CNAME 1
76 #define ERR_NO_ADDRESSES 2
77 #define ERR_LOOKUP_FAILURE 3
79 #define ERR_EXTRA_AAAA 5
80 #define ERR_MISSING_GLUE 5
81 #define ERR_IS_MXCNAME 6
82 #define ERR_IS_SRVCNAME 7
84 static const char *dbtype
[] = { "rbt" };
87 isc_boolean_t nomerge
= ISC_TRUE
;
89 isc_boolean_t docheckmx
= ISC_TRUE
;
90 isc_boolean_t dochecksrv
= ISC_TRUE
;
91 isc_boolean_t docheckns
= ISC_TRUE
;
93 isc_boolean_t docheckmx
= ISC_FALSE
;
94 isc_boolean_t dochecksrv
= ISC_FALSE
;
95 isc_boolean_t docheckns
= ISC_FALSE
;
97 unsigned int zone_options
= DNS_ZONEOPT_CHECKNS
|
99 DNS_ZONEOPT_MANYERRORS
|
100 DNS_ZONEOPT_CHECKNAMES
|
101 DNS_ZONEOPT_CHECKINTEGRITY
|
103 DNS_ZONEOPT_CHECKSIBLING
|
105 DNS_ZONEOPT_CHECKWILDCARD
|
106 DNS_ZONEOPT_WARNMXCNAME
|
107 DNS_ZONEOPT_WARNSRVCNAME
;
110 * This needs to match the list in bin/named/log.c.
112 static isc_logcategory_t categories
[] = {
119 { "update-security", 0 },
120 { "query-errors", 0 },
124 static isc_symtab_t
*symtab
= NULL
;
125 static isc_mem_t
*sym_mctx
;
128 freekey(char *key
, unsigned int type
, isc_symvalue_t value
, void *userarg
) {
131 isc_mem_free(userarg
, key
);
135 add(char *key
, int value
) {
137 isc_symvalue_t symvalue
;
139 if (sym_mctx
== NULL
) {
140 result
= isc_mem_create(0, 0, &sym_mctx
);
141 if (result
!= ISC_R_SUCCESS
)
145 if (symtab
== NULL
) {
146 result
= isc_symtab_create(sym_mctx
, 100, freekey
, sym_mctx
,
148 if (result
!= ISC_R_SUCCESS
)
152 key
= isc_mem_strdup(sym_mctx
, key
);
156 symvalue
.as_pointer
= NULL
;
157 result
= isc_symtab_define(symtab
, key
, value
, symvalue
,
158 isc_symexists_reject
);
159 if (result
!= ISC_R_SUCCESS
)
160 isc_mem_free(sym_mctx
, key
);
164 logged(char *key
, int value
) {
170 result
= isc_symtab_lookup(symtab
, key
, value
, NULL
);
171 if (result
== ISC_R_SUCCESS
)
177 checkns(dns_zone_t
*zone
, dns_name_t
*name
, dns_name_t
*owner
,
178 dns_rdataset_t
*a
, dns_rdataset_t
*aaaa
)
180 #ifdef USE_GETADDRINFO
181 dns_rdataset_t
*rdataset
;
182 dns_rdata_t rdata
= DNS_RDATA_INIT
;
183 struct addrinfo hints
, *ai
, *cur
;
184 char namebuf
[DNS_NAME_FORMATSIZE
+ 1];
185 char ownerbuf
[DNS_NAME_FORMATSIZE
];
186 char addrbuf
[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
187 isc_boolean_t answer
= ISC_TRUE
;
193 REQUIRE(a
== NULL
|| !dns_rdataset_isassociated(a
) ||
194 a
->type
== dns_rdatatype_a
);
195 REQUIRE(aaaa
== NULL
|| !dns_rdataset_isassociated(aaaa
) ||
196 aaaa
->type
== dns_rdatatype_aaaa
);
197 memset(&hints
, 0, sizeof(hints
));
198 hints
.ai_flags
= AI_CANONNAME
;
199 hints
.ai_family
= PF_UNSPEC
;
200 hints
.ai_socktype
= SOCK_STREAM
;
201 hints
.ai_protocol
= IPPROTO_TCP
;
203 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
207 if (dns_name_countlabels(name
) > 1U)
208 strcat(namebuf
, ".");
209 dns_name_format(owner
, ownerbuf
, sizeof(ownerbuf
));
211 result
= getaddrinfo(namebuf
, NULL
, &hints
, &ai
);
212 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
216 * Work around broken getaddrinfo() implementations that
217 * fail to set ai_canonname on first entry.
220 while (cur
!= NULL
&& cur
->ai_canonname
== NULL
&&
221 cur
->ai_next
!= NULL
)
223 if (cur
!= NULL
&& cur
->ai_canonname
!= NULL
&&
224 strcasecmp(cur
->ai_canonname
, namebuf
) != 0 &&
225 !logged(namebuf
, ERR_IS_CNAME
)) {
226 dns_zone_log(zone
, ISC_LOG_ERROR
,
227 "%s/NS '%s' (out of zone) "
228 "is a CNAME '%s' (illegal)",
231 /* XXX950 make fatal for 9.5.0 */
232 /* answer = ISC_FALSE; */
233 add(namebuf
, ERR_IS_CNAME
);
237 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
240 if (!logged(namebuf
, ERR_NO_ADDRESSES
)) {
241 dns_zone_log(zone
, ISC_LOG_ERROR
,
242 "%s/NS '%s' (out of zone) "
243 "has no addresses records (A or AAAA)",
245 add(namebuf
, ERR_NO_ADDRESSES
);
247 /* XXX950 make fatal for 9.5.0 */
251 if (!logged(namebuf
, ERR_LOOKUP_FAILURE
)) {
252 dns_zone_log(zone
, ISC_LOG_WARNING
,
253 "getaddrinfo(%s) failed: %s",
254 namebuf
, gai_strerror(result
));
255 add(namebuf
, ERR_LOOKUP_FAILURE
);
259 if (a
== NULL
|| aaaa
== NULL
)
262 * Check that all glue records really exist.
264 if (!dns_rdataset_isassociated(a
))
266 result
= dns_rdataset_first(a
);
267 while (result
== ISC_R_SUCCESS
) {
268 dns_rdataset_current(a
, &rdata
);
270 for (cur
= ai
; cur
!= NULL
; cur
= cur
->ai_next
) {
271 if (cur
->ai_family
!= AF_INET
)
273 ptr
= &((struct sockaddr_in
*)(cur
->ai_addr
))->sin_addr
;
274 if (memcmp(ptr
, rdata
.data
, rdata
.length
) == 0) {
279 if (!match
&& !logged(namebuf
, ERR_EXTRA_A
)) {
280 dns_zone_log(zone
, ISC_LOG_ERROR
, "%s/NS '%s' "
281 "extra GLUE A record (%s)",
283 inet_ntop(AF_INET
, rdata
.data
,
284 addrbuf
, sizeof(addrbuf
)));
285 add(namebuf
, ERR_EXTRA_A
);
286 /* XXX950 make fatal for 9.5.0 */
287 /* answer = ISC_FALSE; */
289 dns_rdata_reset(&rdata
);
290 result
= dns_rdataset_next(a
);
294 if (!dns_rdataset_isassociated(aaaa
))
296 result
= dns_rdataset_first(aaaa
);
297 while (result
== ISC_R_SUCCESS
) {
298 dns_rdataset_current(aaaa
, &rdata
);
300 for (cur
= ai
; cur
!= NULL
; cur
= cur
->ai_next
) {
301 if (cur
->ai_family
!= AF_INET6
)
303 ptr
= &((struct sockaddr_in6
*)(cur
->ai_addr
))->sin6_addr
;
304 if (memcmp(ptr
, rdata
.data
, rdata
.length
) == 0) {
309 if (!match
&& !logged(namebuf
, ERR_EXTRA_AAAA
)) {
310 dns_zone_log(zone
, ISC_LOG_ERROR
, "%s/NS '%s' "
311 "extra GLUE AAAA record (%s)",
313 inet_ntop(AF_INET6
, rdata
.data
,
314 addrbuf
, sizeof(addrbuf
)));
315 add(namebuf
, ERR_EXTRA_AAAA
);
316 /* XXX950 make fatal for 9.5.0. */
317 /* answer = ISC_FALSE; */
319 dns_rdata_reset(&rdata
);
320 result
= dns_rdataset_next(aaaa
);
325 * Check that all addresses appear in the glue.
327 if (!logged(namebuf
, ERR_MISSING_GLUE
)) {
328 isc_boolean_t missing_glue
= ISC_FALSE
;
329 for (cur
= ai
; cur
!= NULL
; cur
= cur
->ai_next
) {
330 switch (cur
->ai_family
) {
333 ptr
= &((struct sockaddr_in
*)(cur
->ai_addr
))->sin_addr
;
338 ptr
= &((struct sockaddr_in6
*)(cur
->ai_addr
))->sin6_addr
;
345 if (dns_rdataset_isassociated(rdataset
))
346 result
= dns_rdataset_first(rdataset
);
348 result
= ISC_R_FAILURE
;
349 while (result
== ISC_R_SUCCESS
&& !match
) {
350 dns_rdataset_current(rdataset
, &rdata
);
351 if (memcmp(ptr
, rdata
.data
, rdata
.length
) == 0)
353 dns_rdata_reset(&rdata
);
354 result
= dns_rdataset_next(rdataset
);
357 dns_zone_log(zone
, ISC_LOG_ERROR
, "%s/NS '%s' "
358 "missing GLUE %s record (%s)",
359 ownerbuf
, namebuf
, type
,
360 inet_ntop(cur
->ai_family
, ptr
,
361 addrbuf
, sizeof(addrbuf
)));
362 /* XXX950 make fatal for 9.5.0. */
363 /* answer = ISC_FALSE; */
364 missing_glue
= ISC_TRUE
;
368 add(namebuf
, ERR_MISSING_GLUE
);
378 checkmx(dns_zone_t
*zone
, dns_name_t
*name
, dns_name_t
*owner
) {
379 #ifdef USE_GETADDRINFO
380 struct addrinfo hints
, *ai
, *cur
;
381 char namebuf
[DNS_NAME_FORMATSIZE
+ 1];
382 char ownerbuf
[DNS_NAME_FORMATSIZE
];
384 int level
= ISC_LOG_ERROR
;
385 isc_boolean_t answer
= ISC_TRUE
;
387 memset(&hints
, 0, sizeof(hints
));
388 hints
.ai_flags
= AI_CANONNAME
;
389 hints
.ai_family
= PF_UNSPEC
;
390 hints
.ai_socktype
= SOCK_STREAM
;
391 hints
.ai_protocol
= IPPROTO_TCP
;
393 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
397 if (dns_name_countlabels(name
) > 1U)
398 strcat(namebuf
, ".");
399 dns_name_format(owner
, ownerbuf
, sizeof(ownerbuf
));
401 result
= getaddrinfo(namebuf
, NULL
, &hints
, &ai
);
402 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
406 * Work around broken getaddrinfo() implementations that
407 * fail to set ai_canonname on first entry.
410 while (cur
!= NULL
&& cur
->ai_canonname
== NULL
&&
411 cur
->ai_next
!= NULL
)
413 if (cur
!= NULL
&& cur
->ai_canonname
!= NULL
&&
414 strcasecmp(cur
->ai_canonname
, namebuf
) != 0) {
415 if ((zone_options
& DNS_ZONEOPT_WARNMXCNAME
) != 0)
416 level
= ISC_LOG_WARNING
;
417 if ((zone_options
& DNS_ZONEOPT_IGNOREMXCNAME
) == 0) {
418 if (!logged(namebuf
, ERR_IS_MXCNAME
)) {
419 dns_zone_log(zone
, level
,
420 "%s/MX '%s' (out of zone)"
425 add(namebuf
, ERR_IS_MXCNAME
);
427 if (level
== ISC_LOG_ERROR
)
435 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
438 if (!logged(namebuf
, ERR_NO_ADDRESSES
)) {
439 dns_zone_log(zone
, ISC_LOG_ERROR
,
440 "%s/MX '%s' (out of zone) "
441 "has no addresses records (A or AAAA)",
443 add(namebuf
, ERR_NO_ADDRESSES
);
445 /* XXX950 make fatal for 9.5.0. */
449 if (!logged(namebuf
, ERR_LOOKUP_FAILURE
)) {
450 dns_zone_log(zone
, ISC_LOG_WARNING
,
451 "getaddrinfo(%s) failed: %s",
452 namebuf
, gai_strerror(result
));
453 add(namebuf
, ERR_LOOKUP_FAILURE
);
463 checksrv(dns_zone_t
*zone
, dns_name_t
*name
, dns_name_t
*owner
) {
464 #ifdef USE_GETADDRINFO
465 struct addrinfo hints
, *ai
, *cur
;
466 char namebuf
[DNS_NAME_FORMATSIZE
+ 1];
467 char ownerbuf
[DNS_NAME_FORMATSIZE
];
469 int level
= ISC_LOG_ERROR
;
470 isc_boolean_t answer
= ISC_TRUE
;
472 memset(&hints
, 0, sizeof(hints
));
473 hints
.ai_flags
= AI_CANONNAME
;
474 hints
.ai_family
= PF_UNSPEC
;
475 hints
.ai_socktype
= SOCK_STREAM
;
476 hints
.ai_protocol
= IPPROTO_TCP
;
478 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
482 if (dns_name_countlabels(name
) > 1U)
483 strcat(namebuf
, ".");
484 dns_name_format(owner
, ownerbuf
, sizeof(ownerbuf
));
486 result
= getaddrinfo(namebuf
, NULL
, &hints
, &ai
);
487 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
491 * Work around broken getaddrinfo() implementations that
492 * fail to set ai_canonname on first entry.
495 while (cur
!= NULL
&& cur
->ai_canonname
== NULL
&&
496 cur
->ai_next
!= NULL
)
498 if (cur
!= NULL
&& cur
->ai_canonname
!= NULL
&&
499 strcasecmp(cur
->ai_canonname
, namebuf
) != 0) {
500 if ((zone_options
& DNS_ZONEOPT_WARNSRVCNAME
) != 0)
501 level
= ISC_LOG_WARNING
;
502 if ((zone_options
& DNS_ZONEOPT_IGNORESRVCNAME
) == 0) {
503 if (!logged(namebuf
, ERR_IS_SRVCNAME
)) {
504 dns_zone_log(zone
, level
, "%s/SRV '%s'"
505 " (out of zone) is a "
506 "CNAME '%s' (illegal)",
509 add(namebuf
, ERR_IS_SRVCNAME
);
511 if (level
== ISC_LOG_ERROR
)
519 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
522 if (!logged(namebuf
, ERR_NO_ADDRESSES
)) {
523 dns_zone_log(zone
, ISC_LOG_ERROR
,
524 "%s/SRV '%s' (out of zone) "
525 "has no addresses records (A or AAAA)",
527 add(namebuf
, ERR_NO_ADDRESSES
);
529 /* XXX950 make fatal for 9.5.0. */
533 if (!logged(namebuf
, ERR_LOOKUP_FAILURE
)) {
534 dns_zone_log(zone
, ISC_LOG_WARNING
,
535 "getaddrinfo(%s) failed: %s",
536 namebuf
, gai_strerror(result
));
537 add(namebuf
, ERR_LOOKUP_FAILURE
);
547 setup_logging(isc_mem_t
*mctx
, FILE *errout
, isc_log_t
**logp
) {
548 isc_logdestination_t destination
;
549 isc_logconfig_t
*logconfig
= NULL
;
550 isc_log_t
*log
= NULL
;
552 RUNTIME_CHECK(isc_log_create(mctx
, &log
, &logconfig
) == ISC_R_SUCCESS
);
553 isc_log_registercategories(log
, categories
);
554 isc_log_setcontext(log
);
556 dns_log_setcontext(log
);
559 destination
.file
.stream
= errout
;
560 destination
.file
.name
= NULL
;
561 destination
.file
.versions
= ISC_LOG_ROLLNEVER
;
562 destination
.file
.maximum_size
= 0;
563 RUNTIME_CHECK(isc_log_createchannel(logconfig
, "stderr",
566 &destination
, 0) == ISC_R_SUCCESS
);
567 RUNTIME_CHECK(isc_log_usechannel(logconfig
, "stderr",
568 NULL
, NULL
) == ISC_R_SUCCESS
);
571 return (ISC_R_SUCCESS
);
576 load_zone(isc_mem_t
*mctx
, const char *zonename
, const char *filename
,
577 dns_masterformat_t fileformat
, const char *classname
,
581 dns_rdataclass_t rdclass
;
582 isc_textregion_t region
;
584 dns_fixedname_t fixorigin
;
586 dns_zone_t
*zone
= NULL
;
588 REQUIRE(zonep
== NULL
|| *zonep
== NULL
);
591 fprintf(stderr
, "loading \"%s\" from \"%s\" class \"%s\"\n",
592 zonename
, filename
, classname
);
594 CHECK(dns_zone_create(&zone
, mctx
));
596 dns_zone_settype(zone
, dns_zone_master
);
598 isc_buffer_init(&buffer
, zonename
, strlen(zonename
));
599 isc_buffer_add(&buffer
, strlen(zonename
));
600 dns_fixedname_init(&fixorigin
);
601 origin
= dns_fixedname_name(&fixorigin
);
602 CHECK(dns_name_fromtext(origin
, &buffer
, dns_rootname
, 0, NULL
));
603 CHECK(dns_zone_setorigin(zone
, origin
));
604 CHECK(dns_zone_setdbtype(zone
, 1, (const char * const *) dbtype
));
605 CHECK(dns_zone_setfile2(zone
, filename
, fileformat
));
607 DE_CONST(classname
, region
.base
);
608 region
.length
= strlen(classname
);
609 CHECK(dns_rdataclass_fromtext(&rdclass
, ®ion
));
611 dns_zone_setclass(zone
, rdclass
);
612 dns_zone_setoption(zone
, zone_options
, ISC_TRUE
);
613 dns_zone_setoption(zone
, DNS_ZONEOPT_NOMERGE
, nomerge
);
615 dns_zone_setcheckmx(zone
, checkmx
);
617 dns_zone_setcheckns(zone
, checkns
);
619 dns_zone_setchecksrv(zone
, checksrv
);
621 CHECK(dns_zone_load(zone
));
629 dns_zone_detach(&zone
);
635 dump_zone(const char *zonename
, dns_zone_t
*zone
, const char *filename
,
636 dns_masterformat_t fileformat
, const dns_master_style_t
*style
)
639 FILE *output
= stdout
;
642 if (filename
!= NULL
&& strcmp(filename
, "-") != 0)
643 fprintf(stderr
, "dumping \"%s\" to \"%s\"\n",
646 fprintf(stderr
, "dumping \"%s\"\n", zonename
);
649 if (filename
!= NULL
&& strcmp(filename
, "-") != 0) {
650 result
= isc_stdio_open(filename
, "w+", &output
);
652 if (result
!= ISC_R_SUCCESS
) {
653 fprintf(stderr
, "could not open output "
654 "file \"%s\" for writing\n", filename
);
655 return (ISC_R_FAILURE
);
659 result
= dns_zone_dumptostream2(zone
, output
, fileformat
, style
);
661 if (output
!= stdout
)
662 (void)isc_stdio_close(output
);