]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - contrib/bind9/bin/check/named-checkconf.c
Update BIND to 9.9.6-P1
[FreeBSD/stable/9.git] / contrib / bind9 / bin / check / named-checkconf.c
1 /*
2  * Copyright (C) 2004-2007, 2009-2014  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1999-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: named-checkconf.c,v 1.56 2011/03/12 04:59:46 tbox Exp $ */
19
20 /*! \file */
21
22 #include <config.h>
23
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27
28 #include <isc/commandline.h>
29 #include <isc/dir.h>
30 #include <isc/entropy.h>
31 #include <isc/hash.h>
32 #include <isc/log.h>
33 #include <isc/mem.h>
34 #include <isc/result.h>
35 #include <isc/string.h>
36 #include <isc/util.h>
37
38 #include <isccfg/namedconf.h>
39
40 #include <bind9/check.h>
41
42 #include <dns/db.h>
43 #include <dns/fixedname.h>
44 #include <dns/log.h>
45 #include <dns/name.h>
46 #include <dns/rdataclass.h>
47 #include <dns/result.h>
48 #include <dns/rootns.h>
49 #include <dns/zone.h>
50
51 #include "check-tool.h"
52
53 static const char *program = "named-checkconf";
54
55 isc_log_t *logc = NULL;
56
57 #define CHECK(r)\
58         do { \
59                 result = (r); \
60                 if (result != ISC_R_SUCCESS) \
61                         goto cleanup; \
62         } while (0)
63
64 /*% usage */
65 ISC_PLATFORM_NORETURN_PRE static void
66 usage(void) ISC_PLATFORM_NORETURN_POST;
67
68 static void
69 usage(void) {
70         fprintf(stderr, "usage: %s [-h] [-j] [-p] [-v] [-z] [-t directory] "
71                 "[named.conf]\n", program);
72         exit(1);
73 }
74
75 /*% directory callback */
76 static isc_result_t
77 directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
78         isc_result_t result;
79         const char *directory;
80
81         REQUIRE(strcasecmp("directory", clausename) == 0);
82
83         UNUSED(arg);
84         UNUSED(clausename);
85
86         /*
87          * Change directory.
88          */
89         directory = cfg_obj_asstring(obj);
90         result = isc_dir_chdir(directory);
91         if (result != ISC_R_SUCCESS) {
92                 cfg_obj_log(obj, logc, ISC_LOG_ERROR,
93                             "change directory to '%s' failed: %s\n",
94                             directory, isc_result_totext(result));
95                 return (result);
96         }
97
98         return (ISC_R_SUCCESS);
99 }
100
101 static isc_boolean_t
102 get_maps(const cfg_obj_t **maps, const char *name, const cfg_obj_t **obj) {
103         int i;
104         for (i = 0;; i++) {
105                 if (maps[i] == NULL)
106                         return (ISC_FALSE);
107                 if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS)
108                         return (ISC_TRUE);
109         }
110 }
111
112 static isc_boolean_t
113 get_checknames(const cfg_obj_t **maps, const cfg_obj_t **obj) {
114         const cfg_listelt_t *element;
115         const cfg_obj_t *checknames;
116         const cfg_obj_t *type;
117         const cfg_obj_t *value;
118         isc_result_t result;
119         int i;
120
121         for (i = 0;; i++) {
122                 if (maps[i] == NULL)
123                         return (ISC_FALSE);
124                 checknames = NULL;
125                 result = cfg_map_get(maps[i], "check-names", &checknames);
126                 if (result != ISC_R_SUCCESS)
127                         continue;
128                 if (checknames != NULL && !cfg_obj_islist(checknames)) {
129                         *obj = checknames;
130                         return (ISC_TRUE);
131                 }
132                 for (element = cfg_list_first(checknames);
133                      element != NULL;
134                      element = cfg_list_next(element)) {
135                         value = cfg_listelt_value(element);
136                         type = cfg_tuple_get(value, "type");
137                         if (strcasecmp(cfg_obj_asstring(type), "master") != 0)
138                                 continue;
139                         *obj = cfg_tuple_get(value, "mode");
140                         return (ISC_TRUE);
141                 }
142         }
143 }
144
145 static isc_result_t
146 configure_hint(const char *zfile, const char *zclass, isc_mem_t *mctx) {
147         isc_result_t result;
148         dns_db_t *db = NULL;
149         dns_rdataclass_t rdclass;
150         isc_textregion_t r;
151
152         if (zfile == NULL)
153                 return (ISC_R_FAILURE);
154
155         DE_CONST(zclass, r.base);
156         r.length = strlen(zclass);
157         result = dns_rdataclass_fromtext(&rdclass, &r);
158         if (result != ISC_R_SUCCESS)
159                 return (result);
160
161         result = dns_rootns_create(mctx, rdclass, zfile, &db);
162         if (result != ISC_R_SUCCESS)
163                 return (result);
164
165         dns_db_detach(&db);
166         return (ISC_R_SUCCESS);
167 }
168
169 /*% configure the zone */
170 static isc_result_t
171 configure_zone(const char *vclass, const char *view,
172                const cfg_obj_t *zconfig, const cfg_obj_t *vconfig,
173                const cfg_obj_t *config, isc_mem_t *mctx)
174 {
175         int i = 0;
176         isc_result_t result;
177         const char *zclass;
178         const char *zname;
179         const char *zfile = NULL;
180         const cfg_obj_t *maps[4];
181         const cfg_obj_t *mastersobj = NULL;
182         const cfg_obj_t *zoptions = NULL;
183         const cfg_obj_t *classobj = NULL;
184         const cfg_obj_t *typeobj = NULL;
185         const cfg_obj_t *fileobj = NULL;
186         const cfg_obj_t *dbobj = NULL;
187         const cfg_obj_t *obj = NULL;
188         const cfg_obj_t *fmtobj = NULL;
189         dns_masterformat_t masterformat;
190
191         zone_options = DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_MANYERRORS;
192
193         zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
194         classobj = cfg_tuple_get(zconfig, "class");
195         if (!cfg_obj_isstring(classobj))
196                 zclass = vclass;
197         else
198                 zclass = cfg_obj_asstring(classobj);
199
200         zoptions = cfg_tuple_get(zconfig, "options");
201         maps[i++] = zoptions;
202         if (vconfig != NULL)
203                 maps[i++] = cfg_tuple_get(vconfig, "options");
204         if (config != NULL) {
205                 cfg_map_get(config, "options", &obj);
206                 if (obj != NULL)
207                         maps[i++] = obj;
208         }
209         maps[i] = NULL;
210
211         cfg_map_get(zoptions, "type", &typeobj);
212         if (typeobj == NULL)
213                 return (ISC_R_FAILURE);
214
215         /*
216          * Skip checks when using an alternate data source.
217          */
218         cfg_map_get(zoptions, "database", &dbobj);
219         if (dbobj != NULL &&
220             strcmp("rbt", cfg_obj_asstring(dbobj)) != 0 &&
221             strcmp("rbt64", cfg_obj_asstring(dbobj)) != 0)
222                 return (ISC_R_SUCCESS);
223
224         cfg_map_get(zoptions, "file", &fileobj);
225         if (fileobj != NULL)
226                 zfile = cfg_obj_asstring(fileobj);
227
228         /*
229          * Check hints files for hint zones.
230          * Skip loading checks for any type other than
231          * master and redirect
232          */
233         if (strcasecmp(cfg_obj_asstring(typeobj), "hint") == 0)
234                 return (configure_hint(zfile, zclass, mctx));
235         else if ((strcasecmp(cfg_obj_asstring(typeobj), "master") != 0) &&
236                   (strcasecmp(cfg_obj_asstring(typeobj), "redirect") != 0))
237                 return (ISC_R_SUCCESS);
238
239         /*
240          * Is the redirect zone configured as a slave?
241          */
242         if (strcasecmp(cfg_obj_asstring(typeobj), "redirect") == 0) {
243                 cfg_map_get(zoptions, "masters", &mastersobj);
244                 if (mastersobj != NULL)
245                         return (ISC_R_SUCCESS);
246         }
247
248         if (zfile == NULL)
249                 return (ISC_R_FAILURE);
250
251         obj = NULL;
252         if (get_maps(maps, "check-dup-records", &obj)) {
253                 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
254                         zone_options |= DNS_ZONEOPT_CHECKDUPRR;
255                         zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL;
256                 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
257                         zone_options |= DNS_ZONEOPT_CHECKDUPRR;
258                         zone_options |= DNS_ZONEOPT_CHECKDUPRRFAIL;
259                 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
260                         zone_options &= ~DNS_ZONEOPT_CHECKDUPRR;
261                         zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL;
262                 } else
263                         INSIST(0);
264         } else {
265                 zone_options |= DNS_ZONEOPT_CHECKDUPRR;
266                 zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL;
267         }
268
269         obj = NULL;
270         if (get_maps(maps, "check-mx", &obj)) {
271                 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
272                         zone_options |= DNS_ZONEOPT_CHECKMX;
273                         zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
274                 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
275                         zone_options |= DNS_ZONEOPT_CHECKMX;
276                         zone_options |= DNS_ZONEOPT_CHECKMXFAIL;
277                 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
278                         zone_options &= ~DNS_ZONEOPT_CHECKMX;
279                         zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
280                 } else
281                         INSIST(0);
282         } else {
283                 zone_options |= DNS_ZONEOPT_CHECKMX;
284                 zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
285         }
286
287         obj = NULL;
288         if (get_maps(maps, "check-integrity", &obj)) {
289                 if (cfg_obj_asboolean(obj))
290                         zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
291                 else
292                         zone_options &= ~DNS_ZONEOPT_CHECKINTEGRITY;
293         } else
294                 zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
295
296         obj = NULL;
297         if (get_maps(maps, "check-mx-cname", &obj)) {
298                 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
299                         zone_options |= DNS_ZONEOPT_WARNMXCNAME;
300                         zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
301                 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
302                         zone_options &= ~DNS_ZONEOPT_WARNMXCNAME;
303                         zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
304                 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
305                         zone_options |= DNS_ZONEOPT_WARNMXCNAME;
306                         zone_options |= DNS_ZONEOPT_IGNOREMXCNAME;
307                 } else
308                         INSIST(0);
309         } else {
310                 zone_options |= DNS_ZONEOPT_WARNMXCNAME;
311                 zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
312         }
313
314         obj = NULL;
315         if (get_maps(maps, "check-srv-cname", &obj)) {
316                 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
317                         zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
318                         zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
319                 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
320                         zone_options &= ~DNS_ZONEOPT_WARNSRVCNAME;
321                         zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
322                 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
323                         zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
324                         zone_options |= DNS_ZONEOPT_IGNORESRVCNAME;
325                 } else
326                         INSIST(0);
327         } else {
328                 zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
329                 zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
330         }
331
332         obj = NULL;
333         if (get_maps(maps, "check-sibling", &obj)) {
334                 if (cfg_obj_asboolean(obj))
335                         zone_options |= DNS_ZONEOPT_CHECKSIBLING;
336                 else
337                         zone_options &= ~DNS_ZONEOPT_CHECKSIBLING;
338         }
339
340         obj = NULL;
341         if (get_maps(maps, "check-spf", &obj)) {
342                 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
343                         zone_options |= DNS_ZONEOPT_CHECKSPF;
344                 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
345                         zone_options &= ~DNS_ZONEOPT_CHECKSPF;
346                 } else
347                         INSIST(0);
348         } else {
349                 zone_options |= DNS_ZONEOPT_CHECKSPF;
350         }
351
352         obj = NULL;
353         if (get_checknames(maps, &obj)) {
354                 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
355                         zone_options |= DNS_ZONEOPT_CHECKNAMES;
356                         zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
357                 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
358                         zone_options |= DNS_ZONEOPT_CHECKNAMES;
359                         zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL;
360                 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
361                         zone_options &= ~DNS_ZONEOPT_CHECKNAMES;
362                         zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
363                 } else
364                         INSIST(0);
365         } else {
366                zone_options |= DNS_ZONEOPT_CHECKNAMES;
367                zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL;
368         }
369
370         masterformat = dns_masterformat_text;
371         fmtobj = NULL;
372         if (get_maps(maps, "masterfile-format", &fmtobj)) {
373                 const char *masterformatstr = cfg_obj_asstring(fmtobj);
374                 if (strcasecmp(masterformatstr, "text") == 0)
375                         masterformat = dns_masterformat_text;
376                 else if (strcasecmp(masterformatstr, "raw") == 0)
377                         masterformat = dns_masterformat_raw;
378                 else
379                         INSIST(0);
380         }
381
382         result = load_zone(mctx, zname, zfile, masterformat, zclass, NULL);
383         if (result != ISC_R_SUCCESS)
384                 fprintf(stderr, "%s/%s/%s: %s\n", view, zname, zclass,
385                         dns_result_totext(result));
386         return (result);
387 }
388
389 /*% configure a view */
390 static isc_result_t
391 configure_view(const char *vclass, const char *view, const cfg_obj_t *config,
392                const cfg_obj_t *vconfig, isc_mem_t *mctx)
393 {
394         const cfg_listelt_t *element;
395         const cfg_obj_t *voptions;
396         const cfg_obj_t *zonelist;
397         isc_result_t result = ISC_R_SUCCESS;
398         isc_result_t tresult;
399
400         voptions = NULL;
401         if (vconfig != NULL)
402                 voptions = cfg_tuple_get(vconfig, "options");
403
404         zonelist = NULL;
405         if (voptions != NULL)
406                 (void)cfg_map_get(voptions, "zone", &zonelist);
407         else
408                 (void)cfg_map_get(config, "zone", &zonelist);
409
410         for (element = cfg_list_first(zonelist);
411              element != NULL;
412              element = cfg_list_next(element))
413         {
414                 const cfg_obj_t *zconfig = cfg_listelt_value(element);
415                 tresult = configure_zone(vclass, view, zconfig, vconfig,
416                                          config, mctx);
417                 if (tresult != ISC_R_SUCCESS)
418                         result = tresult;
419         }
420         return (result);
421 }
422
423
424 /*% load zones from the configuration */
425 static isc_result_t
426 load_zones_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx) {
427         const cfg_listelt_t *element;
428         const cfg_obj_t *classobj;
429         const cfg_obj_t *views;
430         const cfg_obj_t *vconfig;
431         const char *vclass;
432         isc_result_t result = ISC_R_SUCCESS;
433         isc_result_t tresult;
434
435         views = NULL;
436
437         (void)cfg_map_get(config, "view", &views);
438         for (element = cfg_list_first(views);
439              element != NULL;
440              element = cfg_list_next(element))
441         {
442                 const char *vname;
443
444                 vclass = "IN";
445                 vconfig = cfg_listelt_value(element);
446                 if (vconfig != NULL) {
447                         classobj = cfg_tuple_get(vconfig, "class");
448                         if (cfg_obj_isstring(classobj))
449                                 vclass = cfg_obj_asstring(classobj);
450                 }
451                 vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
452                 tresult = configure_view(vclass, vname, config, vconfig, mctx);
453                 if (tresult != ISC_R_SUCCESS)
454                         result = tresult;
455         }
456
457         if (views == NULL) {
458                 tresult = configure_view("IN", "_default", config, NULL, mctx);
459                 if (tresult != ISC_R_SUCCESS)
460                         result = tresult;
461         }
462         return (result);
463 }
464
465 static void
466 output(void *closure, const char *text, int textlen) {
467         UNUSED(closure);
468         if (fwrite(text, 1, textlen, stdout) != (size_t)textlen) {
469                 perror("fwrite");
470                 exit(1);
471         }
472 }
473
474 /*% The main processing routine */
475 int
476 main(int argc, char **argv) {
477         int c;
478         cfg_parser_t *parser = NULL;
479         cfg_obj_t *config = NULL;
480         const char *conffile = NULL;
481         isc_mem_t *mctx = NULL;
482         isc_result_t result;
483         int exit_status = 0;
484         isc_entropy_t *ectx = NULL;
485         isc_boolean_t load_zones = ISC_FALSE;
486         isc_boolean_t print = ISC_FALSE;
487         unsigned int flags = 0;
488
489         isc_commandline_errprint = ISC_FALSE;
490
491         while ((c = isc_commandline_parse(argc, argv, "dhjt:pvxz")) != EOF) {
492                 switch (c) {
493                 case 'd':
494                         debug++;
495                         break;
496
497                 case 'j':
498                         nomerge = ISC_FALSE;
499                         break;
500
501                 case 't':
502                         result = isc_dir_chroot(isc_commandline_argument);
503                         if (result != ISC_R_SUCCESS) {
504                                 fprintf(stderr, "isc_dir_chroot: %s\n",
505                                         isc_result_totext(result));
506                                 exit(1);
507                         }
508                         break;
509
510                 case 'p':
511                         print = ISC_TRUE;
512                         break;
513
514                 case 'v':
515                         printf(VERSION "\n");
516                         exit(0);
517
518                 case 'x':
519                         flags |= CFG_PRINTER_XKEY;
520                         break;
521
522                 case 'z':
523                         load_zones = ISC_TRUE;
524                         docheckmx = ISC_FALSE;
525                         docheckns = ISC_FALSE;
526                         dochecksrv = ISC_FALSE;
527                         break;
528
529                 case '?':
530                         if (isc_commandline_option != '?')
531                                 fprintf(stderr, "%s: invalid argument -%c\n",
532                                         program, isc_commandline_option);
533                         /* FALLTHROUGH */
534                 case 'h':
535                         usage();
536
537                 default:
538                         fprintf(stderr, "%s: unhandled option -%c\n",
539                                 program, isc_commandline_option);
540                         exit(1);
541                 }
542         }
543
544         if (((flags & CFG_PRINTER_XKEY) != 0) && !print) {
545                 fprintf(stderr, "%s: -x cannot be used without -p\n", program);
546                 exit(1);
547         }
548
549         if (isc_commandline_index + 1 < argc)
550                 usage();
551         if (argv[isc_commandline_index] != NULL)
552                 conffile = argv[isc_commandline_index];
553         if (conffile == NULL || conffile[0] == '\0')
554                 conffile = NAMED_CONFFILE;
555
556 #ifdef _WIN32
557         InitSockets();
558 #endif
559
560         RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);
561
562         RUNTIME_CHECK(setup_logging(mctx, stdout, &logc) == ISC_R_SUCCESS);
563
564         RUNTIME_CHECK(isc_entropy_create(mctx, &ectx) == ISC_R_SUCCESS);
565         RUNTIME_CHECK(isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE)
566                       == ISC_R_SUCCESS);
567
568         dns_result_register();
569
570         RUNTIME_CHECK(cfg_parser_create(mctx, logc, &parser) == ISC_R_SUCCESS);
571
572         cfg_parser_setcallback(parser, directory_callback, NULL);
573
574         if (cfg_parse_file(parser, conffile, &cfg_type_namedconf, &config) !=
575             ISC_R_SUCCESS)
576                 exit(1);
577
578         result = bind9_check_namedconf(config, logc, mctx);
579         if (result != ISC_R_SUCCESS)
580                 exit_status = 1;
581
582         if (result == ISC_R_SUCCESS && load_zones) {
583                 result = load_zones_fromconfig(config, mctx);
584                 if (result != ISC_R_SUCCESS)
585                         exit_status = 1;
586         }
587
588         if (print && exit_status == 0)
589                 cfg_printx(config, flags, output, NULL);
590         cfg_obj_destroy(parser, &config);
591
592         cfg_parser_destroy(&parser);
593
594         dns_name_destroy();
595
596         isc_log_destroy(&logc);
597
598         isc_hash_destroy();
599         isc_entropy_detach(&ectx);
600
601         isc_mem_destroy(&mctx);
602
603 #ifdef _WIN32
604         DestroySockets();
605 #endif
606
607         return (exit_status);
608 }