2 * Copyright (C) 2004-2007 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.18 2007/09/13 05:04:01 each 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 #ifdef HAVE_GETADDRINFO
51 #ifdef HAVE_GAISTRERROR
52 #define USE_GETADDRINFO
60 if (result != ISC_R_SUCCESS) \
64 static const char *dbtype
[] = { "rbt" };
67 isc_boolean_t nomerge
= ISC_TRUE
;
68 isc_boolean_t docheckmx
= ISC_TRUE
;
69 isc_boolean_t dochecksrv
= ISC_TRUE
;
70 isc_boolean_t docheckns
= ISC_TRUE
;
71 unsigned int zone_options
= DNS_ZONEOPT_CHECKNS
|
73 DNS_ZONEOPT_MANYERRORS
|
74 DNS_ZONEOPT_CHECKNAMES
|
75 DNS_ZONEOPT_CHECKINTEGRITY
|
76 DNS_ZONEOPT_CHECKWILDCARD
|
77 DNS_ZONEOPT_WARNMXCNAME
|
78 DNS_ZONEOPT_WARNSRVCNAME
;
81 * This needs to match the list in bin/named/log.c.
83 static isc_logcategory_t categories
[] = {
90 { "update-security", 0 },
95 checkns(dns_zone_t
*zone
, dns_name_t
*name
, dns_name_t
*owner
,
96 dns_rdataset_t
*a
, dns_rdataset_t
*aaaa
)
98 #ifdef USE_GETADDRINFO
99 dns_rdataset_t
*rdataset
;
100 dns_rdata_t rdata
= DNS_RDATA_INIT
;
101 struct addrinfo hints
, *ai
, *cur
;
102 char namebuf
[DNS_NAME_FORMATSIZE
+ 1];
103 char ownerbuf
[DNS_NAME_FORMATSIZE
];
104 char addrbuf
[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
105 isc_boolean_t answer
= ISC_TRUE
;
111 REQUIRE(a
== NULL
|| !dns_rdataset_isassociated(a
) ||
112 a
->type
== dns_rdatatype_a
);
113 REQUIRE(aaaa
== NULL
|| !dns_rdataset_isassociated(aaaa
) ||
114 aaaa
->type
== dns_rdatatype_aaaa
);
115 memset(&hints
, 0, sizeof(hints
));
116 hints
.ai_flags
= AI_CANONNAME
;
117 hints
.ai_family
= PF_UNSPEC
;
118 hints
.ai_socktype
= SOCK_STREAM
;
119 hints
.ai_protocol
= IPPROTO_TCP
;
121 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
125 if (dns_name_countlabels(name
) > 1U)
126 strcat(namebuf
, ".");
127 dns_name_format(owner
, ownerbuf
, sizeof(ownerbuf
));
129 result
= getaddrinfo(namebuf
, NULL
, &hints
, &ai
);
130 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
134 * Work around broken getaddrinfo() implementations that
135 * fail to set ai_canonname on first entry.
138 while (cur
!= NULL
&& cur
->ai_canonname
== NULL
&&
139 cur
->ai_next
!= NULL
)
141 if (cur
!= NULL
&& cur
->ai_canonname
!= NULL
&&
142 strcasecmp(ai
->ai_canonname
, namebuf
) != 0) {
143 dns_zone_log(zone
, ISC_LOG_ERROR
,
144 "%s/NS '%s' (out of zone) "
145 "is a CNAME (illegal)",
147 /* XXX950 make fatal for 9.5.0 */
148 /* answer = ISC_FALSE; */
152 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
155 dns_zone_log(zone
, ISC_LOG_ERROR
, "%s/NS '%s' (out of zone) "
156 "has no addresses records (A or AAAA)",
158 /* XXX950 make fatal for 9.5.0 */
162 dns_zone_log(zone
, ISC_LOG_WARNING
,
163 "getaddrinfo(%s) failed: %s",
164 namebuf
, gai_strerror(result
));
167 if (a
== NULL
|| aaaa
== NULL
)
170 * Check that all glue records really exist.
172 if (!dns_rdataset_isassociated(a
))
174 result
= dns_rdataset_first(a
);
175 while (result
== ISC_R_SUCCESS
) {
176 dns_rdataset_current(a
, &rdata
);
178 for (cur
= ai
; cur
!= NULL
; cur
= cur
->ai_next
) {
179 if (cur
->ai_family
!= AF_INET
)
181 ptr
= &((struct sockaddr_in
*)(cur
->ai_addr
))->sin_addr
;
182 if (memcmp(ptr
, rdata
.data
, rdata
.length
) == 0) {
188 dns_zone_log(zone
, ISC_LOG_ERROR
, "%s/NS '%s' "
189 "extra GLUE A record (%s)",
191 inet_ntop(AF_INET
, rdata
.data
,
192 addrbuf
, sizeof(addrbuf
)));
193 /* XXX950 make fatal for 9.5.0 */
194 /* answer = ISC_FALSE; */
196 dns_rdata_reset(&rdata
);
197 result
= dns_rdataset_next(a
);
201 if (!dns_rdataset_isassociated(aaaa
))
203 result
= dns_rdataset_first(aaaa
);
204 while (result
== ISC_R_SUCCESS
) {
205 dns_rdataset_current(aaaa
, &rdata
);
207 for (cur
= ai
; cur
!= NULL
; cur
= cur
->ai_next
) {
208 if (cur
->ai_family
!= AF_INET6
)
210 ptr
= &((struct sockaddr_in6
*)(cur
->ai_addr
))->sin6_addr
;
211 if (memcmp(ptr
, rdata
.data
, rdata
.length
) == 0) {
217 dns_zone_log(zone
, ISC_LOG_ERROR
, "%s/NS '%s' "
218 "extra GLUE AAAA record (%s)",
220 inet_ntop(AF_INET6
, rdata
.data
,
221 addrbuf
, sizeof(addrbuf
)));
222 /* XXX950 make fatal for 9.5.0. */
223 /* answer = ISC_FALSE; */
225 dns_rdata_reset(&rdata
);
226 result
= dns_rdataset_next(aaaa
);
231 * Check that all addresses appear in the glue.
233 for (cur
= ai
; cur
!= NULL
; cur
= cur
->ai_next
) {
234 switch (cur
->ai_family
) {
237 ptr
= &((struct sockaddr_in
*)(cur
->ai_addr
))->sin_addr
;
242 ptr
= &((struct sockaddr_in6
*)(cur
->ai_addr
))->sin6_addr
;
249 if (dns_rdataset_isassociated(rdataset
))
250 result
= dns_rdataset_first(rdataset
);
252 result
= ISC_R_FAILURE
;
253 while (result
== ISC_R_SUCCESS
&& !match
) {
254 dns_rdataset_current(rdataset
, &rdata
);
255 if (memcmp(ptr
, rdata
.data
, rdata
.length
) == 0)
257 dns_rdata_reset(&rdata
);
258 result
= dns_rdataset_next(rdataset
);
261 dns_zone_log(zone
, ISC_LOG_ERROR
, "%s/NS '%s' "
262 "missing GLUE %s record (%s)",
263 ownerbuf
, namebuf
, type
,
264 inet_ntop(cur
->ai_family
, ptr
,
265 addrbuf
, sizeof(addrbuf
)));
266 /* XXX950 make fatal for 9.5.0. */
267 /* answer = ISC_FALSE; */
278 checkmx(dns_zone_t
*zone
, dns_name_t
*name
, dns_name_t
*owner
) {
279 #ifdef USE_GETADDRINFO
280 struct addrinfo hints
, *ai
, *cur
;
281 char namebuf
[DNS_NAME_FORMATSIZE
+ 1];
282 char ownerbuf
[DNS_NAME_FORMATSIZE
];
284 int level
= ISC_LOG_ERROR
;
285 isc_boolean_t answer
= ISC_TRUE
;
287 memset(&hints
, 0, sizeof(hints
));
288 hints
.ai_flags
= AI_CANONNAME
;
289 hints
.ai_family
= PF_UNSPEC
;
290 hints
.ai_socktype
= SOCK_STREAM
;
291 hints
.ai_protocol
= IPPROTO_TCP
;
293 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
297 if (dns_name_countlabels(name
) > 1U)
298 strcat(namebuf
, ".");
299 dns_name_format(owner
, ownerbuf
, sizeof(ownerbuf
));
301 result
= getaddrinfo(namebuf
, NULL
, &hints
, &ai
);
302 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
306 * Work around broken getaddrinfo() implementations that
307 * fail to set ai_canonname on first entry.
310 while (cur
!= NULL
&& cur
->ai_canonname
== NULL
&&
311 cur
->ai_next
!= NULL
)
313 if (cur
!= NULL
&& cur
->ai_canonname
!= NULL
&&
314 strcasecmp(cur
->ai_canonname
, namebuf
) != 0) {
315 if ((zone_options
& DNS_ZONEOPT_WARNMXCNAME
) != 0)
316 level
= ISC_LOG_WARNING
;
317 if ((zone_options
& DNS_ZONEOPT_IGNOREMXCNAME
) == 0) {
318 dns_zone_log(zone
, ISC_LOG_WARNING
,
319 "%s/MX '%s' (out of zone) "
320 "is a CNAME (illegal)",
322 if (level
== ISC_LOG_ERROR
)
330 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
333 dns_zone_log(zone
, ISC_LOG_ERROR
, "%s/MX '%s' (out of zone) "
334 "has no addresses records (A or AAAA)",
336 /* XXX950 make fatal for 9.5.0. */
340 dns_zone_log(zone
, ISC_LOG_WARNING
,
341 "getaddrinfo(%s) failed: %s",
342 namebuf
, gai_strerror(result
));
351 checksrv(dns_zone_t
*zone
, dns_name_t
*name
, dns_name_t
*owner
) {
352 #ifdef USE_GETADDRINFO
353 struct addrinfo hints
, *ai
, *cur
;
354 char namebuf
[DNS_NAME_FORMATSIZE
+ 1];
355 char ownerbuf
[DNS_NAME_FORMATSIZE
];
357 int level
= ISC_LOG_ERROR
;
358 isc_boolean_t answer
= ISC_TRUE
;
360 memset(&hints
, 0, sizeof(hints
));
361 hints
.ai_flags
= AI_CANONNAME
;
362 hints
.ai_family
= PF_UNSPEC
;
363 hints
.ai_socktype
= SOCK_STREAM
;
364 hints
.ai_protocol
= IPPROTO_TCP
;
366 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
370 if (dns_name_countlabels(name
) > 1U)
371 strcat(namebuf
, ".");
372 dns_name_format(owner
, ownerbuf
, sizeof(ownerbuf
));
374 result
= getaddrinfo(namebuf
, NULL
, &hints
, &ai
);
375 dns_name_format(name
, namebuf
, sizeof(namebuf
) - 1);
379 * Work around broken getaddrinfo() implementations that
380 * fail to set ai_canonname on first entry.
383 while (cur
!= NULL
&& cur
->ai_canonname
== NULL
&&
384 cur
->ai_next
!= NULL
)
386 if (cur
!= NULL
&& cur
->ai_canonname
!= NULL
&&
387 strcasecmp(cur
->ai_canonname
, namebuf
) != 0) {
388 if ((zone_options
& DNS_ZONEOPT_WARNSRVCNAME
) != 0)
389 level
= ISC_LOG_WARNING
;
390 if ((zone_options
& DNS_ZONEOPT_IGNORESRVCNAME
) == 0) {
391 dns_zone_log(zone
, level
,
392 "%s/SRV '%s' (out of zone) "
393 "is a CNAME (illegal)",
395 if (level
== ISC_LOG_ERROR
)
403 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
406 dns_zone_log(zone
, ISC_LOG_ERROR
, "%s/SRV '%s' (out of zone) "
407 "has no addresses records (A or AAAA)",
409 /* XXX950 make fatal for 9.5.0. */
413 dns_zone_log(zone
, ISC_LOG_WARNING
,
414 "getaddrinfo(%s) failed: %s",
415 namebuf
, gai_strerror(result
));
424 setup_logging(isc_mem_t
*mctx
, isc_log_t
**logp
) {
425 isc_logdestination_t destination
;
426 isc_logconfig_t
*logconfig
= NULL
;
427 isc_log_t
*log
= NULL
;
429 RUNTIME_CHECK(isc_log_create(mctx
, &log
, &logconfig
) == ISC_R_SUCCESS
);
430 isc_log_registercategories(log
, categories
);
431 isc_log_setcontext(log
);
433 dns_log_setcontext(log
);
436 destination
.file
.stream
= stdout
;
437 destination
.file
.name
= NULL
;
438 destination
.file
.versions
= ISC_LOG_ROLLNEVER
;
439 destination
.file
.maximum_size
= 0;
440 RUNTIME_CHECK(isc_log_createchannel(logconfig
, "stderr",
443 &destination
, 0) == ISC_R_SUCCESS
);
444 RUNTIME_CHECK(isc_log_usechannel(logconfig
, "stderr",
445 NULL
, NULL
) == ISC_R_SUCCESS
);
448 return (ISC_R_SUCCESS
);
453 load_zone(isc_mem_t
*mctx
, const char *zonename
, const char *filename
,
454 dns_masterformat_t fileformat
, const char *classname
,
458 dns_rdataclass_t rdclass
;
459 isc_textregion_t region
;
461 dns_fixedname_t fixorigin
;
463 dns_zone_t
*zone
= NULL
;
465 REQUIRE(zonep
== NULL
|| *zonep
== NULL
);
468 fprintf(stderr
, "loading \"%s\" from \"%s\" class \"%s\"\n",
469 zonename
, filename
, classname
);
471 CHECK(dns_zone_create(&zone
, mctx
));
473 dns_zone_settype(zone
, dns_zone_master
);
475 isc_buffer_init(&buffer
, zonename
, strlen(zonename
));
476 isc_buffer_add(&buffer
, strlen(zonename
));
477 dns_fixedname_init(&fixorigin
);
478 origin
= dns_fixedname_name(&fixorigin
);
479 CHECK(dns_name_fromtext(origin
, &buffer
, dns_rootname
,
481 CHECK(dns_zone_setorigin(zone
, origin
));
482 CHECK(dns_zone_setdbtype(zone
, 1, (const char * const *) dbtype
));
483 CHECK(dns_zone_setfile2(zone
, filename
, fileformat
));
485 DE_CONST(classname
, region
.base
);
486 region
.length
= strlen(classname
);
487 CHECK(dns_rdataclass_fromtext(&rdclass
, ®ion
));
489 dns_zone_setclass(zone
, rdclass
);
490 dns_zone_setoption(zone
, zone_options
, ISC_TRUE
);
491 dns_zone_setoption(zone
, DNS_ZONEOPT_NOMERGE
, nomerge
);
493 dns_zone_setcheckmx(zone
, checkmx
);
495 dns_zone_setcheckns(zone
, checkns
);
497 dns_zone_setchecksrv(zone
, checksrv
);
499 CHECK(dns_zone_load(zone
));
507 dns_zone_detach(&zone
);
513 dump_zone(const char *zonename
, dns_zone_t
*zone
, const char *filename
,
514 dns_masterformat_t fileformat
, const dns_master_style_t
*style
)
517 FILE *output
= stdout
;
520 if (filename
!= NULL
)
521 fprintf(stderr
, "dumping \"%s\" to \"%s\"\n",
524 fprintf(stderr
, "dumping \"%s\"\n", zonename
);
527 if (filename
!= NULL
) {
528 result
= isc_stdio_open(filename
, "w+", &output
);
530 if (result
!= ISC_R_SUCCESS
) {
531 fprintf(stderr
, "could not open output "
532 "file \"%s\" for writing\n", filename
);
533 return (ISC_R_FAILURE
);
537 result
= dns_zone_dumptostream2(zone
, output
, fileformat
, style
);
539 if (filename
!= NULL
)
540 (void)isc_stdio_close(output
);