]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/unbound/smallapp/unbound-checkconf.c
Upgrade Unbound to 1.6.4. More to follow.
[FreeBSD/FreeBSD.git] / contrib / unbound / smallapp / unbound-checkconf.c
1 /*
2  * checkconf/unbound-checkconf.c - config file checker for unbound.conf file.
3  *
4  * Copyright (c) 2007, NLnet Labs. All rights reserved.
5  *
6  * This software is open source.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * Redistributions of source code must retain the above copyright notice,
13  * this list of conditions and the following disclaimer.
14  *
15  * Redistributions in binary form must reproduce the above copyright notice,
16  * this list of conditions and the following disclaimer in the documentation
17  * and/or other materials provided with the distribution.
18  *
19  * Neither the name of the NLNET LABS nor the names of its contributors may
20  * be used to endorse or promote products derived from this software without
21  * specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35
36 /**
37  * \file
38  *
39  * The config checker checks for syntax and other errors in the unbound.conf
40  * file, and can be used to check for errors before the server is started
41  * or sigHUPped.
42  * Exit status 1 means an error.
43  */
44
45 #include "config.h"
46 #include "util/log.h"
47 #include "util/config_file.h"
48 #include "util/module.h"
49 #include "util/net_help.h"
50 #include "util/regional.h"
51 #include "iterator/iterator.h"
52 #include "iterator/iter_fwd.h"
53 #include "iterator/iter_hints.h"
54 #include "validator/validator.h"
55 #include "services/localzone.h"
56 #include "services/view.h"
57 #include "respip/respip.h"
58 #include "sldns/sbuffer.h"
59 #ifdef HAVE_GETOPT_H
60 #include <getopt.h>
61 #endif
62 #ifdef HAVE_PWD_H
63 #include <pwd.h>
64 #endif
65 #ifdef HAVE_SYS_STAT_H
66 #include <sys/stat.h>
67 #endif
68 #ifdef HAVE_GLOB_H
69 #include <glob.h>
70 #endif
71 #ifdef WITH_PYTHONMODULE
72 #include "pythonmod/pythonmod.h"
73 #endif
74
75 /** Give checkconf usage, and exit (1). */
76 static void
77 usage(void)
78 {
79         printf("Usage:  unbound-checkconf [file]\n");
80         printf("        Checks unbound configuration file for errors.\n");
81         printf("file    if omitted %s is used.\n", CONFIGFILE);
82         printf("-o option       print value of option to stdout.\n");
83         printf("-f              output full pathname with chroot applied, eg. with -o pidfile.\n");
84         printf("-h              show this usage help.\n");
85         printf("Version %s\n", PACKAGE_VERSION);
86         printf("BSD licensed, see LICENSE in source package for details.\n");
87         printf("Report bugs to %s\n", PACKAGE_BUGREPORT);
88         exit(1);
89 }
90
91 /**
92  * Print given option to stdout
93  * @param cfg: config
94  * @param opt: option name without trailing :.
95  *      This is different from config_set_option.
96  * @param final: if final pathname with chroot applied has to be printed.
97  */
98 static void
99 print_option(struct config_file* cfg, const char* opt, int final)
100 {
101         if(strcmp(opt, "pidfile") == 0 && final) {
102                 char *p = fname_after_chroot(cfg->pidfile, cfg, 1);
103                 if(!p) fatal_exit("out of memory");
104                 printf("%s\n", p);
105                 free(p);
106                 return;
107         }
108         if(!config_get_option(cfg, opt, config_print_func, stdout))
109                 fatal_exit("cannot print option '%s'", opt);
110 }
111
112 /** check if module works with config */
113 static void
114 check_mod(struct config_file* cfg, struct module_func_block* fb)
115 {
116         struct module_env env;
117         memset(&env, 0, sizeof(env));
118         env.cfg = cfg;
119         env.scratch = regional_create();
120         env.scratch_buffer = sldns_buffer_new(BUFSIZ);
121         if(!env.scratch || !env.scratch_buffer)
122                 fatal_exit("out of memory");
123         if(!edns_known_options_init(&env))
124                 fatal_exit("out of memory");
125         if(!(*fb->init)(&env, 0)) {
126                 fatal_exit("bad config for %s module", fb->name);
127         }
128         (*fb->deinit)(&env, 0);
129         sldns_buffer_free(env.scratch_buffer);
130         regional_destroy(env.scratch);
131         edns_known_options_delete(&env);
132 }
133
134 /** check localzones */
135 static void
136 localzonechecks(struct config_file* cfg)
137 {
138         struct local_zones* zs;
139         if(!(zs = local_zones_create()))
140                 fatal_exit("out of memory");
141         if(!local_zones_apply_cfg(zs, cfg))
142                 fatal_exit("failed local-zone, local-data configuration");
143         local_zones_delete(zs);
144 }
145
146 /** check view and response-ip configuration */
147 static void
148 view_and_respipchecks(struct config_file* cfg)
149 {
150         struct views* views = NULL;
151         struct respip_set* respip = NULL;
152         int ignored = 0;
153         if(!(views = views_create()))
154                 fatal_exit("Could not create views: out of memory");
155         if(!(respip = respip_set_create()))
156                 fatal_exit("Could not create respip set: out of memory");
157         if(!views_apply_cfg(views, cfg))
158                 fatal_exit("Could not set up views");
159         if(!respip_global_apply_cfg(respip, cfg))
160                 fatal_exit("Could not setup respip set");
161         if(!respip_views_apply_cfg(views, cfg, &ignored))
162                 fatal_exit("Could not setup per-view respip sets");
163         views_delete(views);
164         respip_set_delete(respip);
165 }
166
167 /** emit warnings for IP in hosts */
168 static void
169 warn_hosts(const char* typ, struct config_stub* list)
170 {
171         struct sockaddr_storage a;
172         socklen_t alen;
173         struct config_stub* s;
174         struct config_strlist* h;
175         for(s=list; s; s=s->next) {
176                 for(h=s->hosts; h; h=h->next) {
177                         if(extstrtoaddr(h->str, &a, &alen)) {
178                                 fprintf(stderr, "unbound-checkconf: warning:"
179                                   " %s %s: \"%s\" is an IP%s address, "
180                                   "and when looked up as a host name "
181                                   "during use may not resolve.\n",
182                                   s->name, typ, h->str,
183                                   addr_is_ip6(&a, alen)?"6":"4");
184                         }
185                 }
186         }
187 }
188
189 /** check interface strings */
190 static void
191 interfacechecks(struct config_file* cfg)
192 {
193         int d;
194         struct sockaddr_storage a;
195         socklen_t alen;
196         int i, j;
197         for(i=0; i<cfg->num_ifs; i++) {
198                 if(!extstrtoaddr(cfg->ifs[i], &a, &alen)) {
199                         fatal_exit("cannot parse interface specified as '%s'",
200                                 cfg->ifs[i]);
201                 }
202                 for(j=0; j<cfg->num_ifs; j++) {
203                         if(i!=j && strcmp(cfg->ifs[i], cfg->ifs[j])==0)
204                                 fatal_exit("interface: %s present twice, "
205                                         "cannot bind same ports twice.",
206                                         cfg->ifs[i]);
207                 }
208         }
209         for(i=0; i<cfg->num_out_ifs; i++) {
210                 if(!ipstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT, &a, &alen) &&
211                    !netblockstrtoaddr(cfg->out_ifs[i], UNBOUND_DNS_PORT, &a, &alen, &d)) {
212                         fatal_exit("cannot parse outgoing-interface "
213                                 "specified as '%s'", cfg->out_ifs[i]);
214                 }
215                 for(j=0; j<cfg->num_out_ifs; j++) {
216                         if(i!=j && strcmp(cfg->out_ifs[i], cfg->out_ifs[j])==0)
217                                 fatal_exit("outgoing-interface: %s present "
218                                         "twice, cannot bind same ports twice.",
219                                         cfg->out_ifs[i]);
220                 }
221         }
222 }
223
224 /** check acl ips */
225 static void
226 aclchecks(struct config_file* cfg)
227 {
228         int d;
229         struct sockaddr_storage a;
230         socklen_t alen;
231         struct config_str2list* acl;
232         for(acl=cfg->acls; acl; acl = acl->next) {
233                 if(!netblockstrtoaddr(acl->str, UNBOUND_DNS_PORT, &a, &alen,
234                         &d)) {
235                         fatal_exit("cannot parse access control address %s %s",
236                                 acl->str, acl->str2);
237                 }
238         }
239 }
240
241 /** true if fname is a file */
242 static int
243 is_file(const char* fname)
244 {
245         struct stat buf;
246         if(stat(fname, &buf) < 0) {
247                 if(errno==EACCES) {
248                         printf("warning: no search permission for one of the directories in path: %s\n", fname);
249                         return 1;
250                 }
251                 perror(fname);
252                 return 0;
253         }
254         if(S_ISDIR(buf.st_mode)) {
255                 printf("%s is not a file\n", fname);
256                 return 0;
257         }
258         return 1;
259 }
260
261 /** true if fname is a directory */
262 static int
263 is_dir(const char* fname)
264 {
265         struct stat buf;
266         if(stat(fname, &buf) < 0) {
267                 if(errno==EACCES) {
268                         printf("warning: no search permission for one of the directories in path: %s\n", fname);
269                         return 1;
270                 }
271                 perror(fname);
272                 return 0;
273         }
274         if(!(S_ISDIR(buf.st_mode))) {
275                 printf("%s is not a directory\n", fname);
276                 return 0;
277         }
278         return 1;
279 }
280
281 /** get base dir of a fname */
282 static char*
283 basedir(char* fname)
284 {
285         char* rev;
286         if(!fname) fatal_exit("out of memory");
287         rev = strrchr(fname, '/');
288         if(!rev) return NULL;
289         if(fname == rev) return NULL;
290         rev[0] = 0;
291         return fname;
292 }
293
294 /** check chroot for a file string */
295 static void
296 check_chroot_string(const char* desc, char** ss,
297         const char* chrootdir, struct config_file* cfg)
298 {
299         char* str = *ss;
300         if(str && str[0]) {
301                 *ss = fname_after_chroot(str, cfg, 1);
302                 if(!*ss) fatal_exit("out of memory");
303                 if(!is_file(*ss)) {
304                         if(chrootdir && chrootdir[0])
305                                 fatal_exit("%s: \"%s\" does not exist in "
306                                         "chrootdir %s", desc, str, chrootdir);
307                         else
308                                 fatal_exit("%s: \"%s\" does not exist",
309                                         desc, str);
310                 }
311                 /* put in a new full path for continued checking */
312                 free(str);
313         }
314 }
315
316 /** check file list, every file must be inside the chroot location */
317 static void
318 check_chroot_filelist(const char* desc, struct config_strlist* list,
319         const char* chrootdir, struct config_file* cfg)
320 {
321         struct config_strlist* p;
322         for(p=list; p; p=p->next) {
323                 check_chroot_string(desc, &p->str, chrootdir, cfg);
324         }
325 }
326
327 /** check file list, with wildcard processing */
328 static void
329 check_chroot_filelist_wild(const char* desc, struct config_strlist* list,
330         const char* chrootdir, struct config_file* cfg)
331 {
332         struct config_strlist* p;
333         for(p=list; p; p=p->next) {
334 #ifdef HAVE_GLOB
335                 if(strchr(p->str, '*') || strchr(p->str, '[') ||
336                         strchr(p->str, '?') || strchr(p->str, '{') ||
337                         strchr(p->str, '~')) {
338                         char* s = p->str;
339                         /* adjust whole pattern for chroot and check later */
340                         p->str = fname_after_chroot(p->str, cfg, 1);
341                         free(s);
342                 } else
343 #endif /* HAVE_GLOB */
344                         check_chroot_string(desc, &p->str, chrootdir, cfg);
345         }
346 }
347
348 /** check configuration for errors */
349 static void
350 morechecks(struct config_file* cfg, const char* fname)
351 {
352         warn_hosts("stub-host", cfg->stubs);
353         warn_hosts("forward-host", cfg->forwards);
354         interfacechecks(cfg);
355         aclchecks(cfg);
356
357         if(cfg->verbosity < 0)
358                 fatal_exit("verbosity value < 0");
359         if(cfg->num_threads <= 0 || cfg->num_threads > 10000)
360                 fatal_exit("num_threads value weird");
361         if(!cfg->do_ip4 && !cfg->do_ip6)
362                 fatal_exit("ip4 and ip6 are both disabled, pointless");
363         if(!cfg->do_ip6 && cfg->prefer_ip6)
364                 fatal_exit("cannot prefer and disable ip6, pointless");
365         if(!cfg->do_udp && !cfg->do_tcp)
366                 fatal_exit("udp and tcp are both disabled, pointless");
367         if(cfg->edns_buffer_size > cfg->msg_buffer_size)
368                 fatal_exit("edns-buffer-size larger than msg-buffer-size, "
369                         "answers will not fit in processing buffer");
370 #ifdef UB_ON_WINDOWS
371         w_config_adjust_directory(cfg);
372 #endif
373         if(cfg->chrootdir && cfg->chrootdir[0] &&
374                 cfg->chrootdir[strlen(cfg->chrootdir)-1] == '/')
375                 fatal_exit("chootdir %s has trailing slash '/' please remove.",
376                         cfg->chrootdir);
377         if(cfg->chrootdir && cfg->chrootdir[0] &&
378                 !is_dir(cfg->chrootdir)) {
379                 fatal_exit("bad chroot directory");
380         }
381         if(cfg->chrootdir && cfg->chrootdir[0]) {
382                 char buf[10240];
383                 buf[0] = 0;
384                 if(fname[0] != '/') {
385                         if(getcwd(buf, sizeof(buf)) == NULL)
386                                 fatal_exit("getcwd: %s", strerror(errno));
387                         (void)strlcat(buf, "/", sizeof(buf));
388                 }
389                 (void)strlcat(buf, fname, sizeof(buf));
390                 if(strncmp(buf, cfg->chrootdir, strlen(cfg->chrootdir)) != 0)
391                         fatal_exit("config file %s is not inside chroot %s",
392                                 buf, cfg->chrootdir);
393         }
394         if(cfg->directory && cfg->directory[0]) {
395                 char* ad = fname_after_chroot(cfg->directory, cfg, 0);
396                 if(!ad) fatal_exit("out of memory");
397                 if(!is_dir(ad)) fatal_exit("bad chdir directory");
398                 free(ad);
399         }
400         if( (cfg->chrootdir && cfg->chrootdir[0]) ||
401             (cfg->directory && cfg->directory[0])) {
402                 if(cfg->pidfile && cfg->pidfile[0]) {
403                         char* ad = (cfg->pidfile[0]=='/')?strdup(cfg->pidfile):
404                                 fname_after_chroot(cfg->pidfile, cfg, 1);
405                         char* bd = basedir(ad);
406                         if(bd && !is_dir(bd))
407                                 fatal_exit("pidfile directory does not exist");
408                         free(ad);
409                 }
410                 if(cfg->logfile && cfg->logfile[0]) {
411                         char* ad = fname_after_chroot(cfg->logfile, cfg, 1);
412                         char* bd = basedir(ad);
413                         if(bd && !is_dir(bd))
414                                 fatal_exit("logfile directory does not exist");
415                         free(ad);
416                 }
417         }
418
419         check_chroot_filelist("file with root-hints",
420                 cfg->root_hints, cfg->chrootdir, cfg);
421         check_chroot_filelist("trust-anchor-file",
422                 cfg->trust_anchor_file_list, cfg->chrootdir, cfg);
423         check_chroot_filelist("auto-trust-anchor-file",
424                 cfg->auto_trust_anchor_file_list, cfg->chrootdir, cfg);
425         check_chroot_filelist_wild("trusted-keys-file",
426                 cfg->trusted_keys_file_list, cfg->chrootdir, cfg);
427         check_chroot_string("dlv-anchor-file", &cfg->dlv_anchor_file,
428                 cfg->chrootdir, cfg);
429 #ifdef USE_IPSECMOD
430         check_chroot_string("ipsecmod-hook", &cfg->ipsecmod_hook, cfg->chrootdir,
431                 cfg);
432 #endif
433         /* remove chroot setting so that modules are not stripping pathnames*/
434         free(cfg->chrootdir);
435         cfg->chrootdir = NULL;
436
437         /* There should be no reason for 'respip' module not to work with
438          * dns64, but it's not explicitly confirmed,  so the combination is
439          * excluded below.   It's simply unknown yet for the combination of
440          * respip and other modules. */
441         if(strcmp(cfg->module_conf, "iterator") != 0
442                 && strcmp(cfg->module_conf, "validator iterator") != 0
443                 && strcmp(cfg->module_conf, "dns64 validator iterator") != 0
444                 && strcmp(cfg->module_conf, "dns64 iterator") != 0
445                 && strcmp(cfg->module_conf, "respip iterator") != 0
446                 && strcmp(cfg->module_conf, "respip validator iterator") != 0
447 #ifdef WITH_PYTHONMODULE
448                 && strcmp(cfg->module_conf, "python iterator") != 0
449                 && strcmp(cfg->module_conf, "python validator iterator") != 0
450                 && strcmp(cfg->module_conf, "validator python iterator") != 0
451                 && strcmp(cfg->module_conf, "dns64 python iterator") != 0
452                 && strcmp(cfg->module_conf, "dns64 python validator iterator") != 0
453                 && strcmp(cfg->module_conf, "dns64 validator python iterator") != 0
454                 && strcmp(cfg->module_conf, "python dns64 iterator") != 0
455                 && strcmp(cfg->module_conf, "python dns64 validator iterator") != 0
456 #endif
457 #ifdef USE_CACHEDB
458                 && strcmp(cfg->module_conf, "validator cachedb iterator") != 0
459                 && strcmp(cfg->module_conf, "cachedb iterator") != 0
460                 && strcmp(cfg->module_conf, "dns64 validator cachedb iterator") != 0
461                 && strcmp(cfg->module_conf, "dns64 cachedb iterator") != 0
462 #endif
463 #if defined(WITH_PYTHONMODULE) && defined(USE_CACHEDB)
464                 && strcmp(cfg->module_conf, "python dns64 cachedb iterator") != 0
465                 && strcmp(cfg->module_conf, "python dns64 validator cachedb iterator") != 0
466                 && strcmp(cfg->module_conf, "dns64 python cachedb iterator") != 0
467                 && strcmp(cfg->module_conf, "dns64 python validator cachedb iterator") != 0
468                 && strcmp(cfg->module_conf, "python cachedb iterator") != 0
469                 && strcmp(cfg->module_conf, "python validator cachedb iterator") != 0
470                 && strcmp(cfg->module_conf, "cachedb python iterator") != 0
471                 && strcmp(cfg->module_conf, "validator cachedb python iterator") != 0
472                 && strcmp(cfg->module_conf, "validator python cachedb iterator") != 0
473 #endif
474 #ifdef CLIENT_SUBNET
475                 && strcmp(cfg->module_conf, "subnetcache iterator") != 0
476                 && strcmp(cfg->module_conf, "subnetcache validator iterator") != 0
477 #endif
478 #if defined(WITH_PYTHONMODULE) && defined(CLIENT_SUBNET)
479                 && strcmp(cfg->module_conf, "python subnetcache iterator") != 0
480                 && strcmp(cfg->module_conf, "subnetcache python iterator") != 0
481                 && strcmp(cfg->module_conf, "subnetcache validator iterator") != 0
482                 && strcmp(cfg->module_conf, "python subnetcache validator iterator") != 0
483                 && strcmp(cfg->module_conf, "subnetcache python validator iterator") != 0
484                 && strcmp(cfg->module_conf, "subnetcache validator python iterator") != 0
485 #endif
486 #ifdef USE_IPSECMOD
487                 && strcmp(cfg->module_conf, "ipsecmod iterator") != 0
488                 && strcmp(cfg->module_conf, "ipsecmod validator iterator") != 0
489 #endif
490 #if defined(WITH_PYTHONMODULE) && defined(USE_IPSECMOD)
491                 && strcmp(cfg->module_conf, "python ipsecmod iterator") != 0
492                 && strcmp(cfg->module_conf, "ipsecmod python iterator") != 0
493                 && strcmp(cfg->module_conf, "ipsecmod validator iterator") != 0
494                 && strcmp(cfg->module_conf, "python ipsecmod validator iterator") != 0
495                 && strcmp(cfg->module_conf, "ipsecmod python validator iterator") != 0
496                 && strcmp(cfg->module_conf, "ipsecmod validator python iterator") != 0
497 #endif
498                 ) {
499                 fatal_exit("module conf '%s' is not known to work",
500                         cfg->module_conf);
501         }
502
503 #ifdef HAVE_GETPWNAM
504         if(cfg->username && cfg->username[0]) {
505                 if(getpwnam(cfg->username) == NULL)
506                         fatal_exit("user '%s' does not exist.", cfg->username);
507 #  ifdef HAVE_ENDPWENT
508                 endpwent();
509 #  endif
510         }
511 #endif
512         if(cfg->remote_control_enable && cfg->remote_control_use_cert) {
513                 check_chroot_string("server-key-file", &cfg->server_key_file,
514                         cfg->chrootdir, cfg);
515                 check_chroot_string("server-cert-file", &cfg->server_cert_file,
516                         cfg->chrootdir, cfg);
517                 if(!is_file(cfg->control_key_file))
518                         fatal_exit("control-key-file: \"%s\" does not exist",
519                                 cfg->control_key_file);
520                 if(!is_file(cfg->control_cert_file))
521                         fatal_exit("control-cert-file: \"%s\" does not exist",
522                                 cfg->control_cert_file);
523         }
524
525         localzonechecks(cfg);
526         view_and_respipchecks(cfg);
527 }
528
529 /** check forwards */
530 static void
531 check_fwd(struct config_file* cfg)
532 {
533         struct iter_forwards* fwd = forwards_create();
534         if(!fwd || !forwards_apply_cfg(fwd, cfg)) {
535                 fatal_exit("Could not set forward zones");
536         }
537         forwards_delete(fwd);
538 }
539
540 /** check hints */
541 static void
542 check_hints(struct config_file* cfg)
543 {
544         struct iter_hints* hints = hints_create();
545         if(!hints || !hints_apply_cfg(hints, cfg)) {
546                 fatal_exit("Could not set root or stub hints");
547         }
548         hints_delete(hints);
549 }
550
551 /** check config file */
552 static void
553 checkconf(const char* cfgfile, const char* opt, int final)
554 {
555         char oldwd[4096];
556         struct config_file* cfg = config_create();
557         if(!cfg)
558                 fatal_exit("out of memory");
559         oldwd[0] = 0;
560         if(!getcwd(oldwd, sizeof(oldwd))) {
561                 log_err("cannot getcwd: %s", strerror(errno));
562                 oldwd[0] = 0;
563         }
564         if(!config_read(cfg, cfgfile, NULL)) {
565                 /* config_read prints messages to stderr */
566                 config_delete(cfg);
567                 exit(1);
568         }
569         if(oldwd[0] && chdir(oldwd) == -1)
570                 log_err("cannot chdir(%s): %s", oldwd, strerror(errno));
571         if(opt) {
572                 print_option(cfg, opt, final);
573                 config_delete(cfg);
574                 return;
575         }
576         morechecks(cfg, cfgfile);
577         check_mod(cfg, iter_get_funcblock());
578         check_mod(cfg, val_get_funcblock());
579 #ifdef WITH_PYTHONMODULE
580         if(strstr(cfg->module_conf, "python"))
581                 check_mod(cfg, pythonmod_get_funcblock());
582 #endif
583         check_fwd(cfg);
584         check_hints(cfg);
585         printf("unbound-checkconf: no errors in %s\n", cfgfile);
586         config_delete(cfg);
587 }
588
589 /** getopt global, in case header files fail to declare it. */
590 extern int optind;
591 /** getopt global, in case header files fail to declare it. */
592 extern char* optarg;
593
594 /** Main routine for checkconf */
595 int main(int argc, char* argv[])
596 {
597         int c;
598         int final = 0;
599         const char* f;
600         const char* opt = NULL;
601         const char* cfgfile = CONFIGFILE;
602         log_ident_set("unbound-checkconf");
603         log_init(NULL, 0, NULL);
604         checklock_start();
605 #ifdef USE_WINSOCK
606         /* use registry config file in preference to compiletime location */
607         if(!(cfgfile=w_lookup_reg_str("Software\\Unbound", "ConfigFile")))
608                 cfgfile = CONFIGFILE;
609 #endif /* USE_WINSOCK */
610         /* parse the options */
611         while( (c=getopt(argc, argv, "fho:")) != -1) {
612                 switch(c) {
613                 case 'f':
614                         final = 1;
615                         break;
616                 case 'o':
617                         opt = optarg;
618                         break;
619                 case '?':
620                 case 'h':
621                 default:
622                         usage();
623                 }
624         }
625         argc -= optind;
626         argv += optind;
627         if(argc != 0 && argc != 1)
628                 usage();
629         if(argc == 1)
630                 f = argv[0];
631         else    f = cfgfile;
632         checkconf(f, opt, final);
633         checklock_stop();
634         return 0;
635 }