named-checkconf.c revision 218384
1/*
2 * Copyright (C) 2004-2007, 2009, 2010  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.222.4 2010-09-07 23:46:05 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
50static const char *program = "named-checkconf";
51
52isc_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 */
62static void
63usage(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 */
70static isc_result_t
71directory_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
95static isc_boolean_t
96get_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
106static isc_boolean_t
107get_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
139static isc_result_t
140config_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 */
152static isc_result_t
153configure_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 */
315static isc_result_t
316configure_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 */
350static isc_result_t
351load_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 */
391int
392main(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#ifdef _WIN32
457	InitSockets();
458#endif
459
460	RUNTIME_CHECK(isc_mem_create(0, 0, &mctx) == ISC_R_SUCCESS);
461
462	RUNTIME_CHECK(setup_logging(mctx, stdout, &logc) == ISC_R_SUCCESS);
463
464	RUNTIME_CHECK(isc_entropy_create(mctx, &ectx) == ISC_R_SUCCESS);
465	RUNTIME_CHECK(isc_hash_create(mctx, ectx, DNS_NAME_MAXWIRE)
466		      == ISC_R_SUCCESS);
467
468	dns_result_register();
469
470	RUNTIME_CHECK(cfg_parser_create(mctx, logc, &parser) == ISC_R_SUCCESS);
471
472	cfg_parser_setcallback(parser, directory_callback, NULL);
473
474	if (cfg_parse_file(parser, conffile, &cfg_type_namedconf, &config) !=
475	    ISC_R_SUCCESS)
476		exit(1);
477
478	result = bind9_check_namedconf(config, logc, mctx);
479	if (result != ISC_R_SUCCESS)
480		exit_status = 1;
481
482	if (result == ISC_R_SUCCESS && load_zones) {
483		result = load_zones_fromconfig(config, mctx);
484		if (result != ISC_R_SUCCESS)
485			exit_status = 1;
486	}
487
488	cfg_obj_destroy(parser, &config);
489
490	cfg_parser_destroy(&parser);
491
492	dns_name_destroy();
493
494	isc_log_destroy(&logc);
495
496	isc_hash_destroy();
497	isc_entropy_detach(&ectx);
498
499	isc_mem_destroy(&mctx);
500
501#ifdef _WIN32
502	DestroySockets();
503#endif
504
505	return (exit_status);
506}
507