]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bind9/bin/check/check-tool.c
add -n option to suppress clearing the build tree and add -DNO_CLEAN
[FreeBSD/FreeBSD.git] / contrib / bind9 / bin / check / check-tool.c
1 /*
2  * Copyright (C) 2004-2007  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.18 2007/09/13 05:04:01 each 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 #ifdef HAVE_ADDRINFO
50 #ifdef HAVE_GETADDRINFO
51 #ifdef HAVE_GAISTRERROR
52 #define USE_GETADDRINFO
53 #endif
54 #endif
55 #endif
56
57 #define CHECK(r) \
58         do { \
59                 result = (r); \
60                 if (result != ISC_R_SUCCESS) \
61                         goto cleanup; \
62         } while (0)   
63
64 static const char *dbtype[] = { "rbt" };
65
66 int debug = 0;
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 | 
72                             DNS_ZONEOPT_CHECKMX |
73                             DNS_ZONEOPT_MANYERRORS |
74                             DNS_ZONEOPT_CHECKNAMES |
75                             DNS_ZONEOPT_CHECKINTEGRITY |
76                             DNS_ZONEOPT_CHECKWILDCARD |
77                             DNS_ZONEOPT_WARNMXCNAME |
78                             DNS_ZONEOPT_WARNSRVCNAME;
79
80 /*
81  * This needs to match the list in bin/named/log.c.
82  */
83 static isc_logcategory_t categories[] = {
84         { "",                0 },
85         { "client",          0 },
86         { "network",         0 },
87         { "update",          0 },
88         { "queries",         0 },
89         { "unmatched",       0 },
90         { "update-security", 0 },
91         { NULL,              0 }
92 };
93
94 static isc_boolean_t
95 checkns(dns_zone_t *zone, dns_name_t *name, dns_name_t *owner,
96         dns_rdataset_t *a, dns_rdataset_t *aaaa)
97 {
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;
106         isc_boolean_t match;
107         const char *type;
108         void *ptr = NULL;
109         int result;
110
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;
120
121         dns_name_format(name, namebuf, sizeof(namebuf) - 1);
122         /*
123          * Turn off search.
124          */
125         if (dns_name_countlabels(name) > 1U)
126                 strcat(namebuf, ".");
127         dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
128         
129         result = getaddrinfo(namebuf, NULL, &hints, &ai);
130         dns_name_format(name, namebuf, sizeof(namebuf) - 1);
131         switch (result) {
132         case 0:
133                 /*
134                  * Work around broken getaddrinfo() implementations that
135                  * fail to set ai_canonname on first entry.
136                  */
137                 cur = ai;
138                 while (cur != NULL && cur->ai_canonname == NULL &&
139                        cur->ai_next != NULL)
140                         cur = cur->ai_next;
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)",
146                                      ownerbuf, namebuf);
147                         /* XXX950 make fatal for 9.5.0 */
148                         /* answer = ISC_FALSE; */
149                 }
150                 break;
151         case EAI_NONAME:
152 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
153         case EAI_NODATA:
154 #endif
155                 dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' (out of zone) "
156                              "has no addresses records (A or AAAA)",
157                              ownerbuf, namebuf);
158                 /* XXX950 make fatal for 9.5.0 */
159                 return (ISC_TRUE);
160
161         default:
162                 dns_zone_log(zone, ISC_LOG_WARNING,
163                              "getaddrinfo(%s) failed: %s",
164                              namebuf, gai_strerror(result));
165                 return (ISC_TRUE);
166         }
167         if (a == NULL || aaaa == NULL)
168                 return (answer);
169         /*
170          * Check that all glue records really exist.
171          */
172         if (!dns_rdataset_isassociated(a))
173                 goto checkaaaa;
174         result = dns_rdataset_first(a);
175         while (result == ISC_R_SUCCESS) {
176                 dns_rdataset_current(a, &rdata);
177                 match = ISC_FALSE;
178                 for (cur = ai; cur != NULL; cur = cur->ai_next) {
179                         if (cur->ai_family != AF_INET)
180                                 continue;
181                         ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
182                         if (memcmp(ptr, rdata.data, rdata.length) == 0) {
183                                 match = ISC_TRUE;
184                                 break;
185                         }
186                 }
187                 if (!match) {
188                         dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
189                                      "extra GLUE A record (%s)",
190                                      ownerbuf, namebuf,
191                                      inet_ntop(AF_INET, rdata.data,
192                                                addrbuf, sizeof(addrbuf)));
193                         /* XXX950 make fatal for 9.5.0 */
194                         /* answer = ISC_FALSE; */
195                 }
196                 dns_rdata_reset(&rdata);
197                 result = dns_rdataset_next(a);
198         }
199
200  checkaaaa:
201         if (!dns_rdataset_isassociated(aaaa))
202                 goto checkmissing;
203         result = dns_rdataset_first(aaaa);
204         while (result == ISC_R_SUCCESS) {
205                 dns_rdataset_current(aaaa, &rdata);
206                 match = ISC_FALSE;
207                 for (cur = ai; cur != NULL; cur = cur->ai_next) {
208                         if (cur->ai_family != AF_INET6)
209                                 continue;
210                         ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
211                         if (memcmp(ptr, rdata.data, rdata.length) == 0) {
212                                 match = ISC_TRUE;
213                                 break;
214                         }
215                 }
216                 if (!match) {
217                         dns_zone_log(zone, ISC_LOG_ERROR, "%s/NS '%s' "
218                                      "extra GLUE AAAA record (%s)",
219                                      ownerbuf, namebuf,
220                                      inet_ntop(AF_INET6, rdata.data,
221                                                addrbuf, sizeof(addrbuf)));
222                         /* XXX950 make fatal for 9.5.0. */
223                         /* answer = ISC_FALSE; */
224                 }
225                 dns_rdata_reset(&rdata);
226                 result = dns_rdataset_next(aaaa);
227         }
228
229  checkmissing:
230         /*
231          * Check that all addresses appear in the glue.
232          */
233         for (cur = ai; cur != NULL; cur = cur->ai_next) {
234                 switch (cur->ai_family) {
235                 case AF_INET:
236                         rdataset = a;
237                         ptr = &((struct sockaddr_in *)(cur->ai_addr))->sin_addr;
238                         type = "A";
239                         break;
240                 case AF_INET6:
241                         rdataset = aaaa;
242                         ptr = &((struct sockaddr_in6 *)(cur->ai_addr))->sin6_addr;
243                         type = "AAAA";
244                         break;
245                 default:
246                          continue;
247                 }
248                 match = ISC_FALSE;
249                 if (dns_rdataset_isassociated(rdataset))
250                         result = dns_rdataset_first(rdataset);
251                 else
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)
256                                 match = ISC_TRUE;
257                         dns_rdata_reset(&rdata);
258                         result = dns_rdataset_next(rdataset);
259                 }
260                 if (!match) {
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; */
268                 }
269         }
270         freeaddrinfo(ai);
271         return (answer);
272 #else
273         return (ISC_TRUE);
274 #endif
275 }
276
277 static isc_boolean_t
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];
283         int result;
284         int level = ISC_LOG_ERROR;
285         isc_boolean_t answer = ISC_TRUE;
286
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;
292
293         dns_name_format(name, namebuf, sizeof(namebuf) - 1);
294         /*
295          * Turn off search.
296          */
297         if (dns_name_countlabels(name) > 1U)
298                 strcat(namebuf, ".");
299         dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
300         
301         result = getaddrinfo(namebuf, NULL, &hints, &ai);
302         dns_name_format(name, namebuf, sizeof(namebuf) - 1);
303         switch (result) {
304         case 0:
305                 /*
306                  * Work around broken getaddrinfo() implementations that
307                  * fail to set ai_canonname on first entry.
308                  */
309                 cur = ai;
310                 while (cur != NULL && cur->ai_canonname == NULL &&
311                        cur->ai_next != NULL)
312                         cur = cur->ai_next;
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)",
321                                              ownerbuf, namebuf);
322                                 if (level == ISC_LOG_ERROR)
323                                         answer = ISC_FALSE;
324                         }
325                 }
326                 freeaddrinfo(ai);
327                 return (answer);
328
329         case EAI_NONAME:
330 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
331         case EAI_NODATA:
332 #endif
333                 dns_zone_log(zone, ISC_LOG_ERROR, "%s/MX '%s' (out of zone) "
334                              "has no addresses records (A or AAAA)",
335                              ownerbuf, namebuf);
336                 /* XXX950 make fatal for 9.5.0. */
337                 return (ISC_TRUE);
338
339         default:
340                 dns_zone_log(zone, ISC_LOG_WARNING,
341                              "getaddrinfo(%s) failed: %s",
342                              namebuf, gai_strerror(result));
343                 return (ISC_TRUE);
344         }
345 #else
346         return (ISC_TRUE);
347 #endif
348 }
349
350 static isc_boolean_t
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];
356         int result;
357         int level = ISC_LOG_ERROR;
358         isc_boolean_t answer = ISC_TRUE;
359
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;
365
366         dns_name_format(name, namebuf, sizeof(namebuf) - 1);
367         /*
368          * Turn off search.
369          */
370         if (dns_name_countlabels(name) > 1U)
371                 strcat(namebuf, ".");
372         dns_name_format(owner, ownerbuf, sizeof(ownerbuf));
373         
374         result = getaddrinfo(namebuf, NULL, &hints, &ai);
375         dns_name_format(name, namebuf, sizeof(namebuf) - 1);
376         switch (result) {
377         case 0:
378                 /*
379                  * Work around broken getaddrinfo() implementations that
380                  * fail to set ai_canonname on first entry.
381                  */
382                 cur = ai;
383                 while (cur != NULL && cur->ai_canonname == NULL &&
384                        cur->ai_next != NULL)
385                         cur = cur->ai_next;
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)",
394                                              ownerbuf, namebuf);
395                                 if (level == ISC_LOG_ERROR)
396                                         answer = ISC_FALSE;
397                         }
398                 }
399                 freeaddrinfo(ai);
400                 return (answer);
401
402         case EAI_NONAME:
403 #if defined(EAI_NODATA) && (EAI_NODATA != EAI_NONAME)
404         case EAI_NODATA:
405 #endif
406                 dns_zone_log(zone, ISC_LOG_ERROR, "%s/SRV '%s' (out of zone) "
407                              "has no addresses records (A or AAAA)",
408                              ownerbuf, namebuf);
409                 /* XXX950 make fatal for 9.5.0. */
410                 return (ISC_TRUE);
411
412         default:
413                 dns_zone_log(zone, ISC_LOG_WARNING,
414                              "getaddrinfo(%s) failed: %s",
415                              namebuf, gai_strerror(result));
416                 return (ISC_TRUE);
417         }
418 #else
419         return (ISC_TRUE);
420 #endif
421 }
422
423 isc_result_t
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;
428
429         RUNTIME_CHECK(isc_log_create(mctx, &log, &logconfig) == ISC_R_SUCCESS);
430         isc_log_registercategories(log, categories);
431         isc_log_setcontext(log);
432         dns_log_init(log);
433         dns_log_setcontext(log);
434         cfg_log_init(log);
435
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",
441                                        ISC_LOG_TOFILEDESC,
442                                        ISC_LOG_DYNAMIC,
443                                        &destination, 0) == ISC_R_SUCCESS);
444         RUNTIME_CHECK(isc_log_usechannel(logconfig, "stderr",
445                                          NULL, NULL) == ISC_R_SUCCESS);
446
447         *logp = log;
448         return (ISC_R_SUCCESS);
449 }
450
451 /*% load the zone */
452 isc_result_t
453 load_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
454           dns_masterformat_t fileformat, const char *classname,
455           dns_zone_t **zonep)
456 {
457         isc_result_t result;
458         dns_rdataclass_t rdclass;
459         isc_textregion_t region;
460         isc_buffer_t buffer;
461         dns_fixedname_t fixorigin;
462         dns_name_t *origin;
463         dns_zone_t *zone = NULL;
464
465         REQUIRE(zonep == NULL || *zonep == NULL);
466
467         if (debug)
468                 fprintf(stderr, "loading \"%s\" from \"%s\" class \"%s\"\n",
469                         zonename, filename, classname);
470
471         CHECK(dns_zone_create(&zone, mctx));
472
473         dns_zone_settype(zone, dns_zone_master);
474
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,
480                                 ISC_FALSE, NULL));
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));
484
485         DE_CONST(classname, region.base);
486         region.length = strlen(classname);
487         CHECK(dns_rdataclass_fromtext(&rdclass, &region));
488
489         dns_zone_setclass(zone, rdclass);
490         dns_zone_setoption(zone, zone_options, ISC_TRUE);
491         dns_zone_setoption(zone, DNS_ZONEOPT_NOMERGE, nomerge);
492         if (docheckmx)
493                 dns_zone_setcheckmx(zone, checkmx);
494         if (docheckns)
495                 dns_zone_setcheckns(zone, checkns);
496         if (dochecksrv)
497                 dns_zone_setchecksrv(zone, checksrv);
498
499         CHECK(dns_zone_load(zone));
500         if (zonep != NULL) {
501                 *zonep = zone;
502                 zone = NULL;
503         }
504
505  cleanup:
506         if (zone != NULL)
507                 dns_zone_detach(&zone);
508         return (result);
509 }
510
511 /*% dump the zone */
512 isc_result_t
513 dump_zone(const char *zonename, dns_zone_t *zone, const char *filename,
514           dns_masterformat_t fileformat, const dns_master_style_t *style)
515 {
516         isc_result_t result;
517         FILE *output = stdout;
518
519         if (debug) {
520                 if (filename != NULL)
521                         fprintf(stderr, "dumping \"%s\" to \"%s\"\n",
522                                 zonename, filename);
523                 else
524                         fprintf(stderr, "dumping \"%s\"\n", zonename);
525         }
526
527         if (filename != NULL) {
528                 result = isc_stdio_open(filename, "w+", &output);
529
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);
534                 }
535         }
536
537         result = dns_zone_dumptostream2(zone, output, fileformat, style);
538
539         if (filename != NULL)
540                 (void)isc_stdio_close(output);
541
542         return (result);
543 }