1238106Sdes/*
2238106Sdes * checkconf/unbound-checkconf.c - config file checker for unbound.conf file.
3238106Sdes *
4238106Sdes * Copyright (c) 2007, NLnet Labs. All rights reserved.
5238106Sdes *
6238106Sdes * This software is open source.
7238106Sdes *
8238106Sdes * Redistribution and use in source and binary forms, with or without
9238106Sdes * modification, are permitted provided that the following conditions
10238106Sdes * are met:
11238106Sdes *
12238106Sdes * Redistributions of source code must retain the above copyright notice,
13238106Sdes * this list of conditions and the following disclaimer.
14238106Sdes *
15238106Sdes * Redistributions in binary form must reproduce the above copyright notice,
16238106Sdes * this list of conditions and the following disclaimer in the documentation
17238106Sdes * and/or other materials provided with the distribution.
18238106Sdes *
19238106Sdes * Neither the name of the NLNET LABS nor the names of its contributors may
20238106Sdes * be used to endorse or promote products derived from this software without
21238106Sdes * specific prior written permission.
22238106Sdes *
23238106Sdes * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24269257Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25269257Sdes * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26269257Sdes * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27269257Sdes * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28269257Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29269257Sdes * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30269257Sdes * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31269257Sdes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32269257Sdes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33269257Sdes * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34238106Sdes */
35238106Sdes
36238106Sdes/**
37238106Sdes * \file
38238106Sdes *
39238106Sdes * The config checker checks for syntax and other errors in the unbound.conf
40238106Sdes * file, and can be used to check for errors before the server is started
41238106Sdes * or sigHUPped.
42238106Sdes * Exit status 1 means an error.
43238106Sdes */
44238106Sdes
45238106Sdes#include "config.h"
46238106Sdes#include "util/log.h"
47238106Sdes#include "util/config_file.h"
48238106Sdes#include "util/module.h"
49238106Sdes#include "util/net_help.h"
50238106Sdes#include "util/regional.h"
51238106Sdes#include "iterator/iterator.h"
52238106Sdes#include "iterator/iter_fwd.h"
53238106Sdes#include "iterator/iter_hints.h"
54238106Sdes#include "validator/validator.h"
55238106Sdes#include "services/localzone.h"
56291767Sdes#include "sldns/sbuffer.h"
57238106Sdes#ifdef HAVE_GETOPT_H
58238106Sdes#include <getopt.h>
59238106Sdes#endif
60238106Sdes#ifdef HAVE_PWD_H
61238106Sdes#include <pwd.h>
62238106Sdes#endif
63238106Sdes#ifdef HAVE_SYS_STAT_H
64238106Sdes#include <sys/stat.h>
65238106Sdes#endif
66238106Sdes#ifdef HAVE_GLOB_H
67238106Sdes#include <glob.h>
68238106Sdes#endif
69238106Sdes#ifdef WITH_PYTHONMODULE
70238106Sdes#include "pythonmod/pythonmod.h"
71238106Sdes#endif
72238106Sdes
73238106Sdes/** Give checkconf usage, and exit (1). */
74238106Sdesstatic void
75238106Sdesusage()
76238106Sdes{
77238106Sdes	printf("Usage:	unbound-checkconf [file]\n");
78238106Sdes	printf("	Checks unbound configuration file for errors.\n");
79238106Sdes	printf("file	if omitted %s is used.\n", CONFIGFILE);
80238106Sdes	printf("-o option	print value of option to stdout.\n");
81285206Sdes	printf("-f 		output full pathname with chroot applied, eg. with -o pidfile.\n");
82238106Sdes	printf("-h		show this usage help.\n");
83238106Sdes	printf("Version %s\n", PACKAGE_VERSION);
84238106Sdes	printf("BSD licensed, see LICENSE in source package for details.\n");
85238106Sdes	printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
86238106Sdes	exit(1);
87238106Sdes}
88238106Sdes
89238106Sdes/**
90238106Sdes * Print given option to stdout
91238106Sdes * @param cfg: config
92238106Sdes * @param opt: option name without trailing :.
93238106Sdes *	This is different from config_set_option.
94285206Sdes * @param final: if final pathname with chroot applied has to be printed.
95238106Sdes */
96238106Sdesstatic void
97285206Sdesprint_option(struct config_file* cfg, const char* opt, int final)
98238106Sdes{
99285206Sdes	if(strcmp(opt, "pidfile") == 0 && final) {
100285206Sdes		printf("%s\n", fname_after_chroot(cfg->pidfile, cfg, 1));
101285206Sdes		return;
102285206Sdes	}
103238106Sdes	if(!config_get_option(cfg, opt, config_print_func, stdout))
104238106Sdes		fatal_exit("cannot print option '%s'", opt);
105238106Sdes}
106238106Sdes
107238106Sdes/** check if module works with config */
108238106Sdesstatic void
109238106Sdescheck_mod(struct config_file* cfg, struct module_func_block* fb)
110238106Sdes{
111238106Sdes	struct module_env env;
112238106Sdes	memset(&env, 0, sizeof(env));
113238106Sdes	env.cfg = cfg;
114238106Sdes	env.scratch = regional_create();
115269257Sdes	env.scratch_buffer = sldns_buffer_new(BUFSIZ);
116238106Sdes	if(!env.scratch || !env.scratch_buffer)
117238106Sdes		fatal_exit("out of memory");
118238106Sdes	if(!(*fb->init)(&env, 0)) {
119238106Sdes		fatal_exit("bad config for %s module", fb->name);
120238106Sdes	}
121238106Sdes	(*fb->deinit)(&env, 0);
122269257Sdes	sldns_buffer_free(env.scratch_buffer);
123238106Sdes	regional_destroy(env.scratch);
124238106Sdes}
125238106Sdes
126238106Sdes/** check localzones */
127238106Sdesstatic void
128238106Sdeslocalzonechecks(struct config_file* cfg)
129238106Sdes{
130238106Sdes	struct local_zones* zs;
131238106Sdes	if(!(zs = local_zones_create()))
132238106Sdes		fatal_exit("out of memory");
133238106Sdes	if(!local_zones_apply_cfg(zs, cfg))
134238106Sdes		fatal_exit("failed local-zone, local-data configuration");
135238106Sdes	local_zones_delete(zs);
136238106Sdes}
137238106Sdes
138238106Sdes/** emit warnings for IP in hosts */
139238106Sdesstatic void
140238106Sdeswarn_hosts(const char* typ, struct config_stub* list)
141238106Sdes{
142238106Sdes	struct sockaddr_storage a;
143238106Sdes	socklen_t alen;
144238106Sdes	struct config_stub* s;
145238106Sdes	struct config_strlist* h;
146238106Sdes	for(s=list; s; s=s->next) {
147238106Sdes		for(h=s->hosts; h; h=h->next) {
148238106Sdes			if(extstrtoaddr(h->str, &a, &alen)) {
149238106Sdes				fprintf(stderr, "unbound-checkconf: warning:"
150238106Sdes				  " %s %s: \"%s\" is an IP%s address, "
151238106Sdes				  "and when looked up as a host name "
152238106Sdes				  "during use may not resolve.\n",
153238106Sdes				  s->name, typ, h->str,
154238106Sdes				  addr_is_ip6(&a, alen)?"6":"4");
155238106Sdes			}
156238106Sdes		}
157238106Sdes	}
158238106Sdes}
159238106Sdes
160238106Sdes/** check interface strings */
161238106Sdesstatic void
162238106Sdesinterfacechecks(struct config_file* cfg)
163238106Sdes{
164238106Sdes	struct sockaddr_storage a;
165238106Sdes	socklen_t alen;
166238106Sdes	int i, j;
167238106Sdes	for(i=0; i<cfg->num_ifs; i++) {
168238106Sdes		if(!extstrtoaddr(cfg->ifs[i], &a, &alen)) {
169238106Sdes			fatal_exit("cannot parse interface specified as '%s'",
170238106Sdes				cfg->ifs[i]);
171238106Sdes		}
172238106Sdes		for(j=0; j<cfg->num_ifs; j++) {
173238106Sdes			if(i!=j && strcmp(cfg->ifs[i], cfg->ifs[j])==0)
174238106Sdes				fatal_exit("interface: %s present twice, "
175238106Sdes					"cannot bind same ports twice.",
176238106Sdes					cfg->ifs[i]);
177238106Sdes		}
178238106Sdes	}
179238106Sdes	for(i=0; i<cfg->num_out_ifs; i++) {
180238106Sdes		if(!ipstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT,
181238106Sdes			&a, &alen)) {
182238106Sdes			fatal_exit("cannot parse outgoing-interface "
183238106Sdes				"specified as '%s'", cfg->out_ifs[i]);
184238106Sdes		}
185238106Sdes		for(j=0; j<cfg->num_out_ifs; j++) {
186238106Sdes			if(i!=j && strcmp(cfg->out_ifs[i], cfg->out_ifs[j])==0)
187238106Sdes				fatal_exit("outgoing-interface: %s present "
188238106Sdes					"twice, cannot bind same ports twice.",
189238106Sdes					cfg->out_ifs[i]);
190238106Sdes		}
191238106Sdes	}
192238106Sdes}
193238106Sdes
194238106Sdes/** check acl ips */
195238106Sdesstatic void
196238106Sdesaclchecks(struct config_file* cfg)
197238106Sdes{
198238106Sdes	int d;
199238106Sdes	struct sockaddr_storage a;
200238106Sdes	socklen_t alen;
201238106Sdes	struct config_str2list* acl;
202238106Sdes	for(acl=cfg->acls; acl; acl = acl->next) {
203238106Sdes		if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen,
204238106Sdes			&d)) {
205238106Sdes			fatal_exit("cannot parse access control address %s %s",
206238106Sdes				acl->str, acl->str2);
207238106Sdes		}
208238106Sdes	}
209238106Sdes}
210238106Sdes
211238106Sdes/** true if fname is a file */
212238106Sdesstatic int
213238106Sdesis_file(const char* fname)
214238106Sdes{
215238106Sdes	struct stat buf;
216238106Sdes	if(stat(fname, &buf) < 0) {
217238106Sdes		if(errno==EACCES) {
218238106Sdes			printf("warning: no search permission for one of the directories in path: %s\n", fname);
219238106Sdes			return 1;
220238106Sdes		}
221238106Sdes		perror(fname);
222238106Sdes		return 0;
223238106Sdes	}
224238106Sdes	if(S_ISDIR(buf.st_mode)) {
225238106Sdes		printf("%s is not a file\n", fname);
226238106Sdes		return 0;
227238106Sdes	}
228238106Sdes	return 1;
229238106Sdes}
230238106Sdes
231238106Sdes/** true if fname is a directory */
232238106Sdesstatic int
233238106Sdesis_dir(const char* fname)
234238106Sdes{
235238106Sdes	struct stat buf;
236238106Sdes	if(stat(fname, &buf) < 0) {
237238106Sdes		if(errno==EACCES) {
238238106Sdes			printf("warning: no search permission for one of the directories in path: %s\n", fname);
239238106Sdes			return 1;
240238106Sdes		}
241238106Sdes		perror(fname);
242238106Sdes		return 0;
243238106Sdes	}
244238106Sdes	if(!(S_ISDIR(buf.st_mode))) {
245238106Sdes		printf("%s is not a directory\n", fname);
246238106Sdes		return 0;
247238106Sdes	}
248238106Sdes	return 1;
249238106Sdes}
250238106Sdes
251238106Sdes/** get base dir of a fname */
252238106Sdesstatic char*
253238106Sdesbasedir(char* fname)
254238106Sdes{
255238106Sdes	char* rev;
256238106Sdes	if(!fname) fatal_exit("out of memory");
257238106Sdes	rev = strrchr(fname, '/');
258238106Sdes	if(!rev) return NULL;
259238106Sdes	if(fname == rev) return NULL;
260238106Sdes	rev[0] = 0;
261238106Sdes	return fname;
262238106Sdes}
263238106Sdes
264238106Sdes/** check chroot for a file string */
265238106Sdesstatic void
266238106Sdescheck_chroot_string(const char* desc, char** ss,
267238106Sdes	const char* chrootdir, struct config_file* cfg)
268238106Sdes{
269238106Sdes	char* str = *ss;
270238106Sdes	if(str && str[0]) {
271238106Sdes		*ss = fname_after_chroot(str, cfg, 1);
272238106Sdes		if(!*ss) fatal_exit("out of memory");
273238106Sdes		if(!is_file(*ss)) {
274238106Sdes			if(chrootdir && chrootdir[0])
275238106Sdes				fatal_exit("%s: \"%s\" does not exist in "
276238106Sdes					"chrootdir %s", desc, str, chrootdir);
277238106Sdes			else
278238106Sdes				fatal_exit("%s: \"%s\" does not exist",
279238106Sdes					desc, str);
280238106Sdes		}
281238106Sdes		/* put in a new full path for continued checking */
282238106Sdes		free(str);
283238106Sdes	}
284238106Sdes}
285238106Sdes
286238106Sdes/** check file list, every file must be inside the chroot location */
287238106Sdesstatic void
288238106Sdescheck_chroot_filelist(const char* desc, struct config_strlist* list,
289238106Sdes	const char* chrootdir, struct config_file* cfg)
290238106Sdes{
291238106Sdes	struct config_strlist* p;
292238106Sdes	for(p=list; p; p=p->next) {
293238106Sdes		check_chroot_string(desc, &p->str, chrootdir, cfg);
294238106Sdes	}
295238106Sdes}
296238106Sdes
297238106Sdes/** check file list, with wildcard processing */
298238106Sdesstatic void
299238106Sdescheck_chroot_filelist_wild(const char* desc, struct config_strlist* list,
300238106Sdes	const char* chrootdir, struct config_file* cfg)
301238106Sdes{
302238106Sdes	struct config_strlist* p;
303238106Sdes	for(p=list; p; p=p->next) {
304238106Sdes#ifdef HAVE_GLOB
305238106Sdes		if(strchr(p->str, '*') || strchr(p->str, '[') ||
306238106Sdes			strchr(p->str, '?') || strchr(p->str, '{') ||
307238106Sdes			strchr(p->str, '~')) {
308238106Sdes			char* s = p->str;
309238106Sdes			/* adjust whole pattern for chroot and check later */
310238106Sdes			p->str = fname_after_chroot(p->str, cfg, 1);
311238106Sdes			free(s);
312238106Sdes		} else
313238106Sdes#endif /* HAVE_GLOB */
314238106Sdes			check_chroot_string(desc, &p->str, chrootdir, cfg);
315238106Sdes	}
316238106Sdes}
317238106Sdes
318238106Sdes/** check configuration for errors */
319238106Sdesstatic void
320238106Sdesmorechecks(struct config_file* cfg, const char* fname)
321238106Sdes{
322238106Sdes	warn_hosts("stub-host", cfg->stubs);
323238106Sdes	warn_hosts("forward-host", cfg->forwards);
324238106Sdes	interfacechecks(cfg);
325238106Sdes	aclchecks(cfg);
326238106Sdes
327238106Sdes	if(cfg->verbosity < 0)
328238106Sdes		fatal_exit("verbosity value < 0");
329238106Sdes	if(cfg->num_threads <= 0 || cfg->num_threads > 10000)
330238106Sdes		fatal_exit("num_threads value weird");
331238106Sdes	if(!cfg->do_ip4 && !cfg->do_ip6)
332238106Sdes		fatal_exit("ip4 and ip6 are both disabled, pointless");
333238106Sdes	if(!cfg->do_udp && !cfg->do_tcp)
334238106Sdes		fatal_exit("udp and tcp are both disabled, pointless");
335238106Sdes	if(cfg->edns_buffer_size > cfg->msg_buffer_size)
336238106Sdes		fatal_exit("edns-buffer-size larger than msg-buffer-size, "
337238106Sdes			"answers will not fit in processing buffer");
338294190Sdes#ifdef UB_ON_WINDOWS
339294190Sdes	w_config_adjust_directory(cfg);
340294190Sdes#endif
341238106Sdes	if(cfg->chrootdir && cfg->chrootdir[0] &&
342238106Sdes		cfg->chrootdir[strlen(cfg->chrootdir)-1] == '/')
343238106Sdes		fatal_exit("chootdir %s has trailing slash '/' please remove.",
344238106Sdes			cfg->chrootdir);
345238106Sdes	if(cfg->chrootdir && cfg->chrootdir[0] &&
346238106Sdes		!is_dir(cfg->chrootdir)) {
347238106Sdes		fatal_exit("bad chroot directory");
348238106Sdes	}
349238106Sdes	if(cfg->chrootdir && cfg->chrootdir[0]) {
350238106Sdes		char buf[10240];
351238106Sdes		buf[0] = 0;
352238106Sdes		if(fname[0] != '/') {
353238106Sdes			if(getcwd(buf, sizeof(buf)) == NULL)
354238106Sdes				fatal_exit("getcwd: %s", strerror(errno));
355269257Sdes			(void)strlcat(buf, "/", sizeof(buf));
356238106Sdes		}
357269257Sdes		(void)strlcat(buf, fname, sizeof(buf));
358238106Sdes		if(strncmp(buf, cfg->chrootdir, strlen(cfg->chrootdir)) != 0)
359238106Sdes			fatal_exit("config file %s is not inside chroot %s",
360238106Sdes				buf, cfg->chrootdir);
361238106Sdes	}
362238106Sdes	if(cfg->directory && cfg->directory[0]) {
363238106Sdes		char* ad = fname_after_chroot(cfg->directory, cfg, 0);
364238106Sdes		if(!ad) fatal_exit("out of memory");
365238106Sdes		if(!is_dir(ad)) fatal_exit("bad chdir directory");
366238106Sdes		free(ad);
367238106Sdes	}
368238106Sdes	if( (cfg->chrootdir && cfg->chrootdir[0]) ||
369238106Sdes	    (cfg->directory && cfg->directory[0])) {
370238106Sdes		if(cfg->pidfile && cfg->pidfile[0]) {
371238106Sdes			char* ad = (cfg->pidfile[0]=='/')?strdup(cfg->pidfile):
372238106Sdes				fname_after_chroot(cfg->pidfile, cfg, 1);
373238106Sdes			char* bd = basedir(ad);
374238106Sdes			if(bd && !is_dir(bd))
375238106Sdes				fatal_exit("pidfile directory does not exist");
376238106Sdes			free(ad);
377238106Sdes		}
378238106Sdes		if(cfg->logfile && cfg->logfile[0]) {
379238106Sdes			char* ad = fname_after_chroot(cfg->logfile, cfg, 1);
380238106Sdes			char* bd = basedir(ad);
381238106Sdes			if(bd && !is_dir(bd))
382238106Sdes				fatal_exit("logfile directory does not exist");
383238106Sdes			free(ad);
384238106Sdes		}
385238106Sdes	}
386238106Sdes
387238106Sdes	check_chroot_filelist("file with root-hints",
388238106Sdes		cfg->root_hints, cfg->chrootdir, cfg);
389238106Sdes	check_chroot_filelist("trust-anchor-file",
390238106Sdes		cfg->trust_anchor_file_list, cfg->chrootdir, cfg);
391238106Sdes	check_chroot_filelist("auto-trust-anchor-file",
392238106Sdes		cfg->auto_trust_anchor_file_list, cfg->chrootdir, cfg);
393238106Sdes	check_chroot_filelist_wild("trusted-keys-file",
394238106Sdes		cfg->trusted_keys_file_list, cfg->chrootdir, cfg);
395238106Sdes	check_chroot_string("dlv-anchor-file", &cfg->dlv_anchor_file,
396238106Sdes		cfg->chrootdir, cfg);
397238106Sdes	/* remove chroot setting so that modules are not stripping pathnames*/
398238106Sdes	free(cfg->chrootdir);
399238106Sdes	cfg->chrootdir = NULL;
400238106Sdes
401238106Sdes	if(strcmp(cfg->module_conf, "iterator") != 0
402238106Sdes		&& strcmp(cfg->module_conf, "validator iterator") != 0
403285206Sdes		&& strcmp(cfg->module_conf, "dns64 validator iterator") != 0
404285206Sdes		&& strcmp(cfg->module_conf, "dns64 iterator") != 0
405238106Sdes#ifdef WITH_PYTHONMODULE
406238106Sdes		&& strcmp(cfg->module_conf, "python iterator") != 0
407238106Sdes		&& strcmp(cfg->module_conf, "python validator iterator") != 0
408238106Sdes		&& strcmp(cfg->module_conf, "validator python iterator") != 0
409285206Sdes		&& strcmp(cfg->module_conf, "dns64 python iterator") != 0
410285206Sdes		&& strcmp(cfg->module_conf, "dns64 python validator iterator") != 0
411285206Sdes		&& strcmp(cfg->module_conf, "dns64 validator python iterator") != 0
412285206Sdes		&& strcmp(cfg->module_conf, "python dns64 iterator") != 0
413285206Sdes		&& strcmp(cfg->module_conf, "python dns64 validator iterator") != 0
414238106Sdes#endif
415238106Sdes		) {
416238106Sdes		fatal_exit("module conf '%s' is not known to work",
417238106Sdes			cfg->module_conf);
418238106Sdes	}
419238106Sdes
420238106Sdes#ifdef HAVE_GETPWNAM
421238106Sdes	if(cfg->username && cfg->username[0]) {
422238106Sdes		if(getpwnam(cfg->username) == NULL)
423238106Sdes			fatal_exit("user '%s' does not exist.", cfg->username);
424238106Sdes		endpwent();
425238106Sdes	}
426238106Sdes#endif
427285206Sdes	if(cfg->remote_control_enable && cfg->remote_control_use_cert) {
428238106Sdes		check_chroot_string("server-key-file", &cfg->server_key_file,
429238106Sdes			cfg->chrootdir, cfg);
430238106Sdes		check_chroot_string("server-cert-file", &cfg->server_cert_file,
431238106Sdes			cfg->chrootdir, cfg);
432238106Sdes		if(!is_file(cfg->control_key_file))
433238106Sdes			fatal_exit("control-key-file: \"%s\" does not exist",
434238106Sdes				cfg->control_key_file);
435238106Sdes		if(!is_file(cfg->control_cert_file))
436238106Sdes			fatal_exit("control-cert-file: \"%s\" does not exist",
437238106Sdes				cfg->control_cert_file);
438238106Sdes	}
439238106Sdes
440238106Sdes	localzonechecks(cfg);
441238106Sdes}
442238106Sdes
443238106Sdes/** check forwards */
444238106Sdesstatic void
445238106Sdescheck_fwd(struct config_file* cfg)
446238106Sdes{
447238106Sdes	struct iter_forwards* fwd = forwards_create();
448238106Sdes	if(!fwd || !forwards_apply_cfg(fwd, cfg)) {
449238106Sdes		fatal_exit("Could not set forward zones");
450238106Sdes	}
451238106Sdes	forwards_delete(fwd);
452238106Sdes}
453238106Sdes
454238106Sdes/** check hints */
455238106Sdesstatic void
456238106Sdescheck_hints(struct config_file* cfg)
457238106Sdes{
458238106Sdes	struct iter_hints* hints = hints_create();
459238106Sdes	if(!hints || !hints_apply_cfg(hints, cfg)) {
460238106Sdes		fatal_exit("Could not set root or stub hints");
461238106Sdes	}
462238106Sdes	hints_delete(hints);
463238106Sdes}
464238106Sdes
465238106Sdes/** check config file */
466238106Sdesstatic void
467285206Sdescheckconf(const char* cfgfile, const char* opt, int final)
468238106Sdes{
469238106Sdes	struct config_file* cfg = config_create();
470238106Sdes	if(!cfg)
471238106Sdes		fatal_exit("out of memory");
472238106Sdes	if(!config_read(cfg, cfgfile, NULL)) {
473238106Sdes		/* config_read prints messages to stderr */
474238106Sdes		config_delete(cfg);
475238106Sdes		exit(1);
476238106Sdes	}
477285206Sdes	if(opt) {
478285206Sdes		print_option(cfg, opt, final);
479285206Sdes		config_delete(cfg);
480285206Sdes		return;
481285206Sdes	}
482238106Sdes	morechecks(cfg, cfgfile);
483238106Sdes	check_mod(cfg, iter_get_funcblock());
484238106Sdes	check_mod(cfg, val_get_funcblock());
485238106Sdes#ifdef WITH_PYTHONMODULE
486238106Sdes	if(strstr(cfg->module_conf, "python"))
487238106Sdes		check_mod(cfg, pythonmod_get_funcblock());
488238106Sdes#endif
489238106Sdes	check_fwd(cfg);
490238106Sdes	check_hints(cfg);
491285206Sdes	printf("unbound-checkconf: no errors in %s\n", cfgfile);
492238106Sdes	config_delete(cfg);
493238106Sdes}
494238106Sdes
495238106Sdes/** getopt global, in case header files fail to declare it. */
496238106Sdesextern int optind;
497238106Sdes/** getopt global, in case header files fail to declare it. */
498238106Sdesextern char* optarg;
499238106Sdes
500238106Sdes/** Main routine for checkconf */
501238106Sdesint main(int argc, char* argv[])
502238106Sdes{
503238106Sdes	int c;
504285206Sdes	int final = 0;
505238106Sdes	const char* f;
506238106Sdes	const char* opt = NULL;
507238106Sdes	const char* cfgfile = CONFIGFILE;
508238106Sdes	log_ident_set("unbound-checkconf");
509238106Sdes	log_init(NULL, 0, NULL);
510238106Sdes	checklock_start();
511238106Sdes#ifdef USE_WINSOCK
512238106Sdes	/* use registry config file in preference to compiletime location */
513238106Sdes	if(!(cfgfile=w_lookup_reg_str("Software\\Unbound", "ConfigFile")))
514238106Sdes		cfgfile = CONFIGFILE;
515238106Sdes#endif /* USE_WINSOCK */
516238106Sdes	/* parse the options */
517285206Sdes	while( (c=getopt(argc, argv, "fho:")) != -1) {
518238106Sdes		switch(c) {
519285206Sdes		case 'f':
520285206Sdes			final = 1;
521285206Sdes			break;
522238106Sdes		case 'o':
523238106Sdes			opt = optarg;
524238106Sdes			break;
525238106Sdes		case '?':
526238106Sdes		case 'h':
527238106Sdes		default:
528238106Sdes			usage();
529238106Sdes		}
530238106Sdes	}
531238106Sdes	argc -= optind;
532238106Sdes	argv += optind;
533238106Sdes	if(argc != 0 && argc != 1)
534238106Sdes		usage();
535238106Sdes	if(argc == 1)
536238106Sdes		f = argv[0];
537238106Sdes	else	f = cfgfile;
538285206Sdes	checkconf(f, opt, final);
539238106Sdes	checklock_stop();
540238106Sdes	return 0;
541238106Sdes}
542