Merge branch 'vendor/EXPAT'
[dragonfly.git] / contrib / bind / bin / check / named-checkconf.c
1 /*
2  * Copyright (C) 2004-2007, 2009  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.46.18.2 2009/02/16 23:46:44 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/fixedname.h>
43 #include <dns/log.h>
44 #include <dns/name.h>
45 #include <dns/result.h>
46 #include <dns/zone.h>
47
48 #include "check-tool.h"
49
50 static const char *program = "named-checkconf";
51
52 isc_log_t *logc = NULL;
53
54 #define CHECK(r)\
55         do { \
56                 result = (r); \
57                 if (result != ISC_R_SUCCESS) \
58                         goto cleanup; \
59         } while (0)
60
61 /*% usage */
62 static void
63 usage(void) {
64         fprintf(stderr, "usage: %s [-h] [-j] [-v] [-z] [-t directory] "
65                 "[named.conf]\n", program);
66         exit(1);
67 }
68
69 /*% directory callback */
70 static isc_result_t
71 directory_callback(const char *clausename, const cfg_obj_t *obj, void *arg) {
72         isc_result_t result;
73         const char *directory;
74
75         REQUIRE(strcasecmp("directory", clausename) == 0);
76
77         UNUSED(arg);
78         UNUSED(clausename);
79
80         /*
81          * Change directory.
82          */
83         directory = cfg_obj_asstring(obj);
84         result = isc_dir_chdir(directory);
85         if (result != ISC_R_SUCCESS) {
86                 cfg_obj_log(obj, logc, ISC_LOG_ERROR,
87                             "change directory to '%s' failed: %s\n",
88                             directory, isc_result_totext(result));
89                 return (result);
90         }
91
92         return (ISC_R_SUCCESS);
93 }
94
95 static isc_boolean_t
96 get_maps(const cfg_obj_t **maps, const char *name, const cfg_obj_t **obj) {
97         int i;
98         for (i = 0;; i++) {
99                 if (maps[i] == NULL)
100                         return (ISC_FALSE);
101                 if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS)
102                         return (ISC_TRUE);
103         }
104 }
105
106 static isc_boolean_t
107 get_checknames(const cfg_obj_t **maps, const cfg_obj_t **obj) {
108         const cfg_listelt_t *element;
109         const cfg_obj_t *checknames;
110         const cfg_obj_t *type;
111         const cfg_obj_t *value;
112         isc_result_t result;
113         int i;
114
115         for (i = 0;; i++) {
116                 if (maps[i] == NULL)
117                         return (ISC_FALSE);
118                 checknames = NULL;
119                 result = cfg_map_get(maps[i], "check-names", &checknames);
120                 if (result != ISC_R_SUCCESS)
121                         continue;
122                 if (checknames != NULL && !cfg_obj_islist(checknames)) {
123                         *obj = checknames;
124                         return (ISC_TRUE);
125                 }
126                 for (element = cfg_list_first(checknames);
127                      element != NULL;
128                      element = cfg_list_next(element)) {
129                         value = cfg_listelt_value(element);
130                         type = cfg_tuple_get(value, "type");
131                         if (strcasecmp(cfg_obj_asstring(type), "master") != 0)
132                                 continue;
133                         *obj = cfg_tuple_get(value, "mode");
134                         return (ISC_TRUE);
135                 }
136         }
137 }
138
139 static isc_result_t
140 config_get(const cfg_obj_t **maps, const char *name, const cfg_obj_t **obj) {
141         int i;
142
143         for (i = 0;; i++) {
144                 if (maps[i] == NULL)
145                         return (ISC_R_NOTFOUND);
146                 if (cfg_map_get(maps[i], name, obj) == ISC_R_SUCCESS)
147                         return (ISC_R_SUCCESS);
148         }
149 }
150
151 /*% configure the zone */
152 static isc_result_t
153 configure_zone(const char *vclass, const char *view,
154                const cfg_obj_t *zconfig, const cfg_obj_t *vconfig,
155                const cfg_obj_t *config, isc_mem_t *mctx)
156 {
157         int i = 0;
158         isc_result_t result;
159         const char *zclass;
160         const char *zname;
161         const char *zfile;
162         const cfg_obj_t *maps[4];
163         const cfg_obj_t *zoptions = NULL;
164         const cfg_obj_t *classobj = NULL;
165         const cfg_obj_t *typeobj = NULL;
166         const cfg_obj_t *fileobj = NULL;
167         const cfg_obj_t *dbobj = NULL;
168         const cfg_obj_t *obj = NULL;
169         const cfg_obj_t *fmtobj = NULL;
170         dns_masterformat_t masterformat;
171
172         zone_options = DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_MANYERRORS;
173
174         zname = cfg_obj_asstring(cfg_tuple_get(zconfig, "name"));
175         classobj = cfg_tuple_get(zconfig, "class");
176         if (!cfg_obj_isstring(classobj))
177                 zclass = vclass;
178         else
179                 zclass = cfg_obj_asstring(classobj);
180
181         zoptions = cfg_tuple_get(zconfig, "options");
182         maps[i++] = zoptions;
183         if (vconfig != NULL)
184                 maps[i++] = cfg_tuple_get(vconfig, "options");
185         if (config != NULL) {
186                 cfg_map_get(config, "options", &obj);
187                 if (obj != NULL)
188                         maps[i++] = obj;
189         }
190         maps[i++] = NULL;
191
192         cfg_map_get(zoptions, "type", &typeobj);
193         if (typeobj == NULL)
194                 return (ISC_R_FAILURE);
195         if (strcasecmp(cfg_obj_asstring(typeobj), "master") != 0)
196                 return (ISC_R_SUCCESS);
197         cfg_map_get(zoptions, "database", &dbobj);
198         if (dbobj != NULL)
199                 return (ISC_R_SUCCESS);
200         cfg_map_get(zoptions, "file", &fileobj);
201         if (fileobj == NULL)
202                 return (ISC_R_FAILURE);
203         zfile = cfg_obj_asstring(fileobj);
204
205         obj = NULL;
206         if (get_maps(maps, "check-mx", &obj)) {
207                 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
208                         zone_options |= DNS_ZONEOPT_CHECKMX;
209                         zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
210                 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
211                         zone_options |= DNS_ZONEOPT_CHECKMX;
212                         zone_options |= DNS_ZONEOPT_CHECKMXFAIL;
213                 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
214                         zone_options &= ~DNS_ZONEOPT_CHECKMX;
215                         zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
216                 } else
217                         INSIST(0);
218         } else {
219                 zone_options |= DNS_ZONEOPT_CHECKMX;
220                 zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
221         }
222
223         obj = NULL;
224         if (get_maps(maps, "check-integrity", &obj)) {
225                 if (cfg_obj_asboolean(obj))
226                         zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
227                 else
228                         zone_options &= ~DNS_ZONEOPT_CHECKINTEGRITY;
229         } else
230                 zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
231
232         obj = NULL;
233         if (get_maps(maps, "check-mx-cname", &obj)) {
234                 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
235                         zone_options |= DNS_ZONEOPT_WARNMXCNAME;
236                         zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
237                 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
238                         zone_options &= ~DNS_ZONEOPT_WARNMXCNAME;
239                         zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
240                 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
241                         zone_options |= DNS_ZONEOPT_WARNMXCNAME;
242                         zone_options |= DNS_ZONEOPT_IGNOREMXCNAME;
243                 } else
244                         INSIST(0);
245         } else {
246                 zone_options |= DNS_ZONEOPT_WARNMXCNAME;
247                 zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
248         }
249
250         obj = NULL;
251         if (get_maps(maps, "check-srv-cname", &obj)) {
252                 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
253                         zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
254                         zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
255                 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
256                         zone_options &= ~DNS_ZONEOPT_WARNSRVCNAME;
257                         zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
258                 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
259                         zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
260                         zone_options |= DNS_ZONEOPT_IGNORESRVCNAME;
261                 } else
262                         INSIST(0);
263         } else {
264                 zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
265                 zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
266         }
267
268         obj = NULL;
269         if (get_maps(maps, "check-sibling", &obj)) {
270                 if (cfg_obj_asboolean(obj))
271                         zone_options |= DNS_ZONEOPT_CHECKSIBLING;
272                 else
273                         zone_options &= ~DNS_ZONEOPT_CHECKSIBLING;
274         }
275
276         obj = NULL;
277         if (get_checknames(maps, &obj)) {
278                 if (strcasecmp(cfg_obj_asstring(obj), "warn") == 0) {
279                         zone_options |= DNS_ZONEOPT_CHECKNAMES;
280                         zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
281                 } else if (strcasecmp(cfg_obj_asstring(obj), "fail") == 0) {
282                         zone_options |= DNS_ZONEOPT_CHECKNAMES;
283                         zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL;
284                 } else if (strcasecmp(cfg_obj_asstring(obj), "ignore") == 0) {
285                         zone_options &= ~DNS_ZONEOPT_CHECKNAMES;
286                         zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
287                 } else
288                         INSIST(0);
289         } else {
290                zone_options |= DNS_ZONEOPT_CHECKNAMES;
291                zone_options |= DNS_ZONEOPT_CHECKNAMESFAIL;
292         }
293
294         masterformat = dns_masterformat_text;
295         fmtobj = NULL;
296         result = config_get(maps, "masterfile-format", &fmtobj);
297         if (result == ISC_R_SUCCESS) {
298                 const char *masterformatstr = cfg_obj_asstring(fmtobj);
299                 if (strcasecmp(masterformatstr, "text") == 0)
300                         masterformat = dns_masterformat_text;
301                 else if (strcasecmp(masterformatstr, "raw") == 0)
302                         masterformat = dns_masterformat_raw;
303                 else
304                         INSIST(0);
305         }
306
307         result = load_zone(mctx, zname, zfile, masterformat, zclass, NULL);
308         if (result != ISC_R_SUCCESS)
309                 fprintf(stderr, "%s/%s/%s: %s\n", view, zname, zclass,
310                         dns_result_totext(result));
311         return(result);
312 }
313
314 /*% configure a view */
315 static isc_result_t
316 configure_view(const char *vclass, const char *view, const cfg_obj_t *config,
317                const cfg_obj_t *vconfig, isc_mem_t *mctx)
318 {
319         const cfg_listelt_t *element;
320         const cfg_obj_t *voptions;
321         const cfg_obj_t *zonelist;
322         isc_result_t result = ISC_R_SUCCESS;
323         isc_result_t tresult;
324
325         voptions = NULL;
326         if (vconfig != NULL)
327                 voptions = cfg_tuple_get(vconfig, "options");
328
329         zonelist = NULL;
330         if (voptions != NULL)
331                 (void)cfg_map_get(voptions, "zone", &zonelist);
332         else
333                 (void)cfg_map_get(config, "zone", &zonelist);
334
335         for (element = cfg_list_first(zonelist);
336              element != NULL;
337              element = cfg_list_next(element))
338         {
339                 const cfg_obj_t *zconfig = cfg_listelt_value(element);
340                 tresult = configure_zone(vclass, view, zconfig, vconfig,
341                                          config, mctx);
342                 if (tresult != ISC_R_SUCCESS)
343                         result = tresult;
344         }
345         return (result);
346 }
347
348
349 /*% load zones from the configuration */
350 static isc_result_t
351 load_zones_fromconfig(const cfg_obj_t *config, isc_mem_t *mctx) {
352         const cfg_listelt_t *element;
353         const cfg_obj_t *classobj;
354         const cfg_obj_t *views;
355         const cfg_obj_t *vconfig;
356         const char *vclass;
357         isc_result_t result = ISC_R_SUCCESS;
358         isc_result_t tresult;
359
360         views = NULL;
361
362         (void)cfg_map_get(config, "view", &views);
363         for (element = cfg_list_first(views);
364              element != NULL;
365              element = cfg_list_next(element))
366         {
367                 const char *vname;
368
369                 vclass = "IN";
370                 vconfig = cfg_listelt_value(element);
371                 if (vconfig != NULL) {
372                         classobj = cfg_tuple_get(vconfig, "class");
373                         if (cfg_obj_isstring(classobj))
374                                 vclass = cfg_obj_asstring(classobj);
375                 }
376                 vname = cfg_obj_asstring(cfg_tuple_get(vconfig, "name"));
377                 tresult = configure_view(vclass, vname, config, vconfig, mctx);
378                 if (tresult != ISC_R_SUCCESS)
379                         result = tresult;
380         }
381
382         if (views == NULL) {
383                 tresult = configure_view("IN", "_default", config, NULL, mctx);
384                 if (tresult != ISC_R_SUCCESS)
385                         result = tresult;
386         }
387         return (result);
388 }
389
390 /*% The main processing routine */
391 int
392 main(int argc, char **argv) {
393         int c;
394         cfg_parser_t *parser = NULL;
395         cfg_obj_t *config = NULL;
396         const char *conffile = NULL;
397         isc_mem_t *mctx = NULL;
398         isc_result_t result;
399         int exit_status = 0;
400         isc_entropy_t *ectx = NULL;
401         isc_boolean_t load_zones = ISC_FALSE;
402
403         isc_commandline_errprint = ISC_FALSE;
404
405         while ((c = isc_commandline_parse(argc, argv, "dhjt:vz")) != EOF) {
406                 switch (c) {
407                 case 'd':
408                         debug++;
409                         break;
410
411                 case 'j':
412                         nomerge = ISC_FALSE;
413                         break;
414
415                 case 't':
416                         result = isc_dir_chroot(isc_commandline_argument);
417                         if (result != ISC_R_SUCCESS) {
418                                 fprintf(stderr, "isc_dir_chroot: %s\n",
419                                         isc_result_totext(result));
420                                 exit(1);
421                         }
422                         break;
423
424                 case 'v':
425                         printf(VERSION "\n");
426                         exit(0);
427
428                 case 'z':
429                         load_zones = ISC_TRUE;
430                         docheckmx = ISC_FALSE;
431                         docheckns = ISC_FALSE;
432                         dochecksrv = ISC_FALSE;
433                         break;
434
435                 case '?':
436                         if (isc_commandline_option != '?')
437                                 fprintf(stderr, "%s: invalid argument -%c\n",
438                                         program, isc_commandline_option);
439                 case 'h':
440                         usage();
441
442                 default:
443                         fprintf(stderr, "%s: unhandled option -%c\n",
444                                 program, isc_commandline_option);
445                         exit(1);
446                 }
447         }
448
449         if (isc_commandline_index + 1 < argc)
450                 usage();
451         if (argv[isc_commandline_index] != NULL)
452                 conffile = argv[isc_commandline_index];
453         if (conffile == NULL || conffile[0] == '\0')
454                 conffile = NAMED_CONFFILE;
455
456         RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);
457
458         RUNTIME_CHECK(setup_logging(mctx, stdout, &logc) == ISC_R_SUCCESS);
459
460         RUNTIME_CHECK(isc_entropy_create(mctx, &ectx) == ISC_R_SUCCESS);
461         RUNTIME_CHECK(isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE)
462                       == ISC_R_SUCCESS);
463
464         dns_result_register();
465
466         RUNTIME_CHECK(cfg_parser_create(mctx, logc, &parser) == ISC_R_SUCCESS);
467
468         cfg_parser_setcallback(parser, directory_callback, NULL);
469
470         if (cfg_parse_file(parser, conffile, &cfg_type_namedconf, &config) !=
471             ISC_R_SUCCESS)
472                 exit(1);
473
474         result = bind9_check_namedconf(config, logc, mctx);
475         if (result != ISC_R_SUCCESS)
476                 exit_status = 1;
477
478         if (result == ISC_R_SUCCESS && load_zones) {
479                 result = load_zones_fromconfig(config, mctx);
480                 if (result != ISC_R_SUCCESS)
481                         exit_status = 1;
482         }
483
484         cfg_obj_destroy(parser, &config);
485
486         cfg_parser_destroy(&parser);
487
488         dns_name_destroy();
489
490         isc_log_destroy(&logc);
491
492         isc_hash_destroy();
493         isc_entropy_detach(&ectx);
494
495         isc_mem_destroy(&mctx);
496
497         return (exit_status);
498 }