]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/bind9/bin/check/check-tool.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / bind9 / bin / check / check-tool.c
1 /*
2  * Copyright (C) 2004-2008  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2002  Internet Software Consortium.
4  *
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.
8  *
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.
16  */
17
18 /* $Id: check-tool.c,v 1.10.18.20 2008/10/24 01:43:17 tbox Exp $ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <stdio.h>
25
26 #include "check-tool.h"
27 #include <isc/util.h>
28
29 #include <isc/buffer.h>
30 #include <isc/log.h>
31 #include <isc/net.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>
37
38 #include <dns/fixedname.h>
39 #include <dns/log.h>
40 #include <dns/name.h>
41 #include <dns/rdata.h>
42 #include <dns/rdataclass.h>
43 #include <dns/rdataset.h>
44 #include <dns/types.h>
45 #include <dns/zone.h>
46
47 #include <isccfg/log.h>
48
49 #ifndef CHECK_SIBLING
50 #define CHECK_SIBLING 1
51 #endif
52
53 #ifndef CHECK_LOCAL
54 #define CHECK_LOCAL 1
55 #endif
56
57 #ifdef HAVE_ADDRINFO
58 #ifdef HAVE_GETADDRINFO
59 #ifdef HAVE_GAISTRERROR
60 #define USE_GETADDRINFO
61 #endif
62 #endif
63 #endif
64
65 #define CHECK(r) \
66         do { \
67                 result = (r); \
68                 if (result != ISC_R_SUCCESS) \
69                         goto cleanup; \
70         } while (0)
71
72 static const char *dbtype[] = { "rbt" };
73
74 int debug = 0;
75 isc_boolean_t nomerge = ISC_TRUE;
76 #if CHECK_LOCAL
77 isc_boolean_t docheckmx = ISC_TRUE;
78 isc_boolean_t dochecksrv = ISC_TRUE;
79 isc_boolean_t docheckns = ISC_TRUE;
80 #else
81 isc_boolean_t docheckmx = ISC_FALSE;
82 isc_boolean_t dochecksrv = ISC_FALSE;
83 isc_boolean_t docheckns = ISC_FALSE;
84 #endif
85 unsigned int zone_options = DNS_ZONEOPT_CHECKNS |
86                             DNS_ZONEOPT_CHECKMX |
87                             DNS_ZONEOPT_MANYERRORS |
88                             DNS_ZONEOPT_CHECKNAMES |
89                             DNS_ZONEOPT_CHECKINTEGRITY |
90 #if CHECK_SIBLING
91                             DNS_ZONEOPT_CHECKSIBLING |
92 #endif
93                             DNS_ZONEOPT_CHECKWILDCARD |
94                             DNS_ZONEOPT_WARNMXCNAME |
95                             DNS_ZONEOPT_WARNSRVCNAME;
96
97 /*
98  * This needs to match the list in bin/named/log.c.
99  */
100 static isc_logcategory_t categories[] = {
101         { "",                0 },
102         { "client",          0 },
103         { "network",         0 },
104         { "update",          0 },
105         { "queries",         0 },
106         { "unmatched",       0 },
107         { "update-security", 0 },
108         { NULL,              0 }
109 };
110
111 static isc_boolean_t
112 checkns(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner,
113         dns_rdataset_t *a, dns_rdataset_t *aaaa)
114 {
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;
123         isc_boolean_t match;
124         const char *type;
125         void *ptr = NULL;
126         int result;
127
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;
137
138         dns_name_format(name, namebuf, sizeof(namebuf) - 1);
139         /*
140          * Turn off search.
141          */
142         if (dns_name_countlabels(name) > 1U)
143                 strcat(namebuf, ".");
144         dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
145
146         result = getaddrinfo(namebuf, NULL, &hints, &ai);
147         dns_name_format(name, namebuf, sizeof(namebuf) - 1);
148         switch (result) {
149         case 0:
150                 /*
151                  * Work around broken getaddrinfo() implementations that
152                  * fail to set ai_canonname on first entry.
153                  */
154                 cur = ai;
155                 while (cur != NULL && cur->ai_canonname == NULL &&
156                        cur->ai_next != NULL)
157                         cur = cur->ai_next;
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)",
163                                      ownerbuf, namebuf);
164                         /* XXX950 make fatal for 9.5.0 */
165                         /* answer = ISC_FALSE; */
166                 }
167                 break;
168         case EAI_NONAME:
169 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
170         case EAI_NODATA:
171 #endif
172                 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' (out of zone) "
173                              "has no addresses records (A or AAAA)",
174                              ownerbuf, namebuf);
175                 /* XXX950 make fatal for 9.5.0 */
176                 return (ISC_TRUE);
177
178         default:
179                 dns_zone_log(zone, ISC_LOG_WARNING,
180                              "getaddrinfo(%s) failed: %s",
181                              namebuf, gai_strerror(result));
182                 return (ISC_TRUE);
183         }
184         if (a == NULL || aaaa == NULL)
185                 return (answer);
186         /*
187          * Check that all glue records really exist.
188          */
189         if (!dns_rdataset_isassociated(a))
190                 goto checkaaaa;
191         result = dns_rdataset_first(a);
192         while (result == ISC_R_SUCCESS) {
193                 dns_rdataset_current(a, &rdata);
194                 match = ISC_FALSE;
195                 for (cur = ai; cur != NULL; cur = cur->ai_next) {
196                         if (cur->ai_family != AF_INET)
197                                 continue;
198                         ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
199                         if (memcmp(ptr, rdata.data, rdata.length) == 0) {
200                                 match = ISC_TRUE;
201                                 break;
202                         }
203                 }
204                 if (!match) {
205                         dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
206                                      "extra GLUE A record (%s)",
207                                      ownerbuf, namebuf,
208                                      inet_ntop(AF_INET, rdata.data,
209                                                addrbuf, sizeof(addrbuf)));
210                         /* XXX950 make fatal for 9.5.0 */
211                         /* answer = ISC_FALSE; */
212                 }
213                 dns_rdata_reset(&rdata);
214                 result = dns_rdataset_next(a);
215         }
216
217  checkaaaa:
218         if (!dns_rdataset_isassociated(aaaa))
219                 goto checkmissing;
220         result = dns_rdataset_first(aaaa);
221         while (result == ISC_R_SUCCESS) {
222                 dns_rdataset_current(aaaa, &rdata);
223                 match = ISC_FALSE;
224                 for (cur = ai; cur != NULL; cur = cur->ai_next) {
225                         if (cur->ai_family != AF_INET6)
226                                 continue;
227                         ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
228                         if (memcmp(ptr, rdata.data, rdata.length) == 0) {
229                                 match = ISC_TRUE;
230                                 break;
231                         }
232                 }
233                 if (!match) {
234                         dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
235                                      "extra GLUE AAAA record (%s)",
236                                      ownerbuf, namebuf,
237                                      inet_ntop(AF_INET6, rdata.data,
238                                                addrbuf, sizeof(addrbuf)));
239                         /* XXX950 make fatal for 9.5.0. */
240                         /* answer = ISC_FALSE; */
241                 }
242                 dns_rdata_reset(&rdata);
243                 result = dns_rdataset_next(aaaa);
244         }
245
246  checkmissing:
247         /*
248          * Check that all addresses appear in the glue.
249          */
250         for (cur = ai; cur != NULL; cur = cur->ai_next) {
251                 switch (cur->ai_family) {
252                 case AF_INET:
253                         rdataset = a;
254                         ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
255                         type = "A";
256                         break;
257                 case AF_INET6:
258                         rdataset = aaaa;
259                         ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
260                         type = "AAAA";
261                         break;
262                 default:
263                          continue;
264                 }
265                 match = ISC_FALSE;
266                 if (dns_rdataset_isassociated(rdataset))
267                         result = dns_rdataset_first(rdataset);
268                 else
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)
273                                 match = ISC_TRUE;
274                         dns_rdata_reset(&rdata);
275                         result = dns_rdataset_next(rdataset);
276                 }
277                 if (!match) {
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; */
285                 }
286         }
287         freeaddrinfo(ai);
288         return (answer);
289 #else
290         return (ISC_TRUE);
291 #endif
292 }
293
294 static isc_boolean_t
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];
300         int result;
301         int level = ISC_LOG_ERROR;
302         isc_boolean_t answer = ISC_TRUE;
303
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;
309
310         dns_name_format(name, namebuf, sizeof(namebuf) - 1);
311         /*
312          * Turn off search.
313          */
314         if (dns_name_countlabels(name) > 1U)
315                 strcat(namebuf, ".");
316         dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
317
318         result = getaddrinfo(namebuf, NULL, &hints, &ai);
319         dns_name_format(name, namebuf, sizeof(namebuf) - 1);
320         switch (result) {
321         case 0:
322                 /*
323                  * Work around broken getaddrinfo() implementations that
324                  * fail to set ai_canonname on first entry.
325                  */
326                 cur = ai;
327                 while (cur != NULL && cur->ai_canonname == NULL &&
328                        cur->ai_next != NULL)
329                         cur = cur->ai_next;
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)",
338                                              ownerbuf, namebuf);
339                                 if (level == ISC_LOG_ERROR)
340                                         answer = ISC_FALSE;
341                         }
342                 }
343                 freeaddrinfo(ai);
344                 return (answer);
345
346         case EAI_NONAME:
347 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
348         case EAI_NODATA:
349 #endif
350                 dns_zone_log(zone, ISC_LOG_ERROR, "%s/MX '%s' (out of zone) "
351                              "has no addresses records (A or AAAA)",
352                              ownerbuf, namebuf);
353                 /* XXX950 make fatal for 9.5.0. */
354                 return (ISC_TRUE);
355
356         default:
357                 dns_zone_log(zone, ISC_LOG_WARNING,
358                              "getaddrinfo(%s) failed: %s",
359                              namebuf, gai_strerror(result));
360                 return (ISC_TRUE);
361         }
362 #else
363         return (ISC_TRUE);
364 #endif
365 }
366
367 static isc_boolean_t
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];
373         int result;
374         int level = ISC_LOG_ERROR;
375         isc_boolean_t answer = ISC_TRUE;
376
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;
382
383         dns_name_format(name, namebuf, sizeof(namebuf) - 1);
384         /*
385          * Turn off search.
386          */
387         if (dns_name_countlabels(name) > 1U)
388                 strcat(namebuf, ".");
389         dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
390
391         result = getaddrinfo(namebuf, NULL, &hints, &ai);
392         dns_name_format(name, namebuf, sizeof(namebuf) - 1);
393         switch (result) {
394         case 0:
395                 /*
396                  * Work around broken getaddrinfo() implementations that
397                  * fail to set ai_canonname on first entry.
398                  */
399                 cur = ai;
400                 while (cur != NULL && cur->ai_canonname == NULL &&
401                        cur->ai_next != NULL)
402                         cur = cur->ai_next;
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)",
411                                              ownerbuf, namebuf);
412                                 if (level == ISC_LOG_ERROR)
413                                         answer = ISC_FALSE;
414                         }
415                 }
416                 freeaddrinfo(ai);
417                 return (answer);
418
419         case EAI_NONAME:
420 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
421         case EAI_NODATA:
422 #endif
423                 dns_zone_log(zone, ISC_LOG_ERROR, "%s/SRV '%s' (out of zone) "
424                              "has no addresses records (A or AAAA)",
425                              ownerbuf, namebuf);
426                 /* XXX950 make fatal for 9.5.0. */
427                 return (ISC_TRUE);
428
429         default:
430                 dns_zone_log(zone, ISC_LOG_WARNING,
431                              "getaddrinfo(%s) failed: %s",
432                              namebuf, gai_strerror(result));
433                 return (ISC_TRUE);
434         }
435 #else
436         return (ISC_TRUE);
437 #endif
438 }
439
440 isc_result_t
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;
445
446         RUNTIME_CHECK(isc_log_create(mctx, &log, &logconfig) == ISC_R_SUCCESS);
447         isc_log_registercategories(log, categories);
448         isc_log_setcontext(log);
449         dns_log_init(log);
450         dns_log_setcontext(log);
451         cfg_log_init(log);
452
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",
458                                        ISC_LOG_TOFILEDESC,
459                                        ISC_LOG_DYNAMIC,
460                                        &destination, 0) == ISC_R_SUCCESS);
461         RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr",
462                                          NULL, NULL) == ISC_R_SUCCESS);
463
464         *logp = log;
465         return (ISC_R_SUCCESS);
466 }
467
468 /*% load the zone */
469 isc_result_t
470 load_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
471           dns_masterformat_t fileformat, const char *classname,
472           dns_zone_t **zonep)
473 {
474         isc_result_t result;
475         dns_rdataclass_t rdclass;
476         isc_textregion_t region;
477         isc_buffer_t buffer;
478         dns_fixedname_t fixorigin;
479         dns_name_t *origin;
480         dns_zone_t *zone = NULL;
481
482         REQUIRE(zonep == NULL || *zonep == NULL);
483
484         if (debug)
485                 fprintf(stderr, "loading \"%s\" from \"%s\" class \"%s\"\n",
486                         zonename, filename, classname);
487
488         CHECK(dns_zone_create(&zone, mctx));
489
490         dns_zone_settype(zone, dns_zone_master);
491
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,
497                                 ISC_FALSE, NULL));
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));
501
502         DE_CONST(classname, region.base);
503         region.length = strlen(classname);
504         CHECK(dns_rdataclass_fromtext(&rdclass, &region));
505
506         dns_zone_setclass(zone, rdclass);
507         dns_zone_setoption(zone, zone_options, ISC_TRUE);
508         dns_zone_setoption(zone, DNS_ZONEOPT_NOMERGE, nomerge);
509         if (docheckmx)
510                 dns_zone_setcheckmx(zone, checkmx);
511         if (docheckns)
512                 dns_zone_setcheckns(zone, checkns);
513         if (dochecksrv)
514                 dns_zone_setchecksrv(zone, checksrv);
515
516         CHECK(dns_zone_load(zone));
517         if (zonep != NULL) {
518                 *zonep = zone;
519                 zone = NULL;
520         }
521
522  cleanup:
523         if (zone != NULL)
524                 dns_zone_detach(&zone);
525         return (result);
526 }
527
528 /*% dump the zone */
529 isc_result_t
530 dump_zone(const char *zonename, dns_zone_t *zone, const char *filename,
531           dns_masterformat_t fileformat, const dns_master_style_t *style)
532 {
533         isc_result_t result;
534         FILE *output = stdout;
535
536         if (debug) {
537                 if (filename != NULL)
538                         fprintf(stderr, "dumping \"%s\" to \"%s\"\n",
539                                 zonename, filename);
540                 else
541                         fprintf(stderr, "dumping \"%s\"\n", zonename);
542         }
543
544         if (filename != NULL) {
545                 result = isc_stdio_open(filename, "w+", &output);
546
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);
551                 }
552         }
553
554         result = dns_zone_dumptostream2(zone, output, fileformat, style);
555
556         if (filename != NULL)
557                 (void)isc_stdio_close(output);
558
559         return (result);
560 }