]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - smallapp/unbound-checkconf.c
Vendor import of Unbound 1.6.2.
[FreeBSD/FreeBSD.git] / 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         /* remove chroot setting so that modules are not stripping pathnames*/
430         free(cfg->chrootdir);
431         cfg->chrootdir = NULL;
432
433         /* There should be no reason for 'respip' module not to work with
434          * dns64, but it's not explicitly confirmed,  so the combination is
435          * excluded below.   It's simply unknown yet for the combination of
436          * respip and other modules. */
437         if(strcmp(cfg->module_conf, "iterator") != 0 
438                 && strcmp(cfg->module_conf, "validator iterator") != 0
439                 && strcmp(cfg->module_conf, "dns64 validator iterator") != 0
440                 && strcmp(cfg->module_conf, "dns64 iterator") != 0
441                 && strcmp(cfg->module_conf, "respip iterator") != 0
442                 && strcmp(cfg->module_conf, "respip validator iterator") != 0
443 #ifdef WITH_PYTHONMODULE
444                 && strcmp(cfg->module_conf, "python iterator") != 0 
445                 && strcmp(cfg->module_conf, "python validator iterator") != 0 
446                 && strcmp(cfg->module_conf, "validator python iterator") != 0
447                 && strcmp(cfg->module_conf, "dns64 python iterator") != 0 
448                 && strcmp(cfg->module_conf, "dns64 python validator iterator") != 0 
449                 && strcmp(cfg->module_conf, "dns64 validator python iterator") != 0
450                 && strcmp(cfg->module_conf, "python dns64 iterator") != 0 
451                 && strcmp(cfg->module_conf, "python dns64 validator iterator") != 0 
452 #endif
453 #ifdef USE_CACHEDB
454                 && strcmp(cfg->module_conf, "validator cachedb iterator") != 0
455                 && strcmp(cfg->module_conf, "cachedb iterator") != 0
456                 && strcmp(cfg->module_conf, "dns64 validator cachedb iterator") != 0
457                 && strcmp(cfg->module_conf, "dns64 cachedb iterator") != 0
458 #endif
459 #if defined(WITH_PYTHONMODULE) && defined(USE_CACHEDB)
460                 && strcmp(cfg->module_conf, "python dns64 cachedb iterator") != 0
461                 && strcmp(cfg->module_conf, "python dns64 validator cachedb iterator") != 0
462                 && strcmp(cfg->module_conf, "dns64 python cachedb iterator") != 0
463                 && strcmp(cfg->module_conf, "dns64 python validator cachedb iterator") != 0
464                 && strcmp(cfg->module_conf, "python cachedb iterator") != 0
465                 && strcmp(cfg->module_conf, "python validator cachedb iterator") != 0
466                 && strcmp(cfg->module_conf, "cachedb python iterator") != 0
467                 && strcmp(cfg->module_conf, "validator cachedb python iterator") != 0
468                 && strcmp(cfg->module_conf, "validator python cachedb iterator") != 0
469 #endif
470 #ifdef CLIENT_SUBNET
471                 && strcmp(cfg->module_conf, "subnetcache iterator") != 0 
472                 && strcmp(cfg->module_conf, "subnetcache validator iterator") != 0
473 #endif
474 #if defined(WITH_PYTHONMODULE) && defined(CLIENT_SUBNET)
475                 && strcmp(cfg->module_conf, "python subnetcache iterator") != 0
476                 && strcmp(cfg->module_conf, "subnetcache python iterator") != 0 
477                 && strcmp(cfg->module_conf, "subnetcache validator iterator") != 0
478                 && strcmp(cfg->module_conf, "python subnetcache validator iterator") != 0
479                 && strcmp(cfg->module_conf, "subnetcache python validator iterator") != 0
480                 && strcmp(cfg->module_conf, "subnetcache validator python iterator") != 0
481 #endif
482                 ) {
483                 fatal_exit("module conf '%s' is not known to work",
484                         cfg->module_conf);
485         }
486
487 #ifdef HAVE_GETPWNAM
488         if(cfg->username && cfg->username[0]) {
489                 if(getpwnam(cfg->username) == NULL)
490                         fatal_exit("user '%s' does not exist.", cfg->username);
491 #  ifdef HAVE_ENDPWENT
492                 endpwent();
493 #  endif
494         }
495 #endif
496         if(cfg->remote_control_enable && cfg->remote_control_use_cert) {
497                 check_chroot_string("server-key-file", &cfg->server_key_file,
498                         cfg->chrootdir, cfg);
499                 check_chroot_string("server-cert-file", &cfg->server_cert_file,
500                         cfg->chrootdir, cfg);
501                 if(!is_file(cfg->control_key_file))
502                         fatal_exit("control-key-file: \"%s\" does not exist",
503                                 cfg->control_key_file);
504                 if(!is_file(cfg->control_cert_file))
505                         fatal_exit("control-cert-file: \"%s\" does not exist",
506                                 cfg->control_cert_file);
507         }
508
509         localzonechecks(cfg);
510         view_and_respipchecks(cfg);
511 }
512
513 /** check forwards */
514 static void
515 check_fwd(struct config_file* cfg)
516 {
517         struct iter_forwards* fwd = forwards_create();
518         if(!fwd || !forwards_apply_cfg(fwd, cfg)) {
519                 fatal_exit("Could not set forward zones");
520         }
521         forwards_delete(fwd);
522 }
523
524 /** check hints */
525 static void
526 check_hints(struct config_file* cfg)
527 {
528         struct iter_hints* hints = hints_create();
529         if(!hints || !hints_apply_cfg(hints, cfg)) {
530                 fatal_exit("Could not set root or stub hints");
531         }
532         hints_delete(hints);
533 }
534
535 /** check config file */
536 static void
537 checkconf(const char* cfgfile, const char* opt, int final)
538 {
539         char oldwd[4096];
540         struct config_file* cfg = config_create();
541         if(!cfg)
542                 fatal_exit("out of memory");
543         oldwd[0] = 0;
544         if(!getcwd(oldwd, sizeof(oldwd))) {
545                 log_err("cannot getcwd: %s", strerror(errno));
546                 oldwd[0] = 0;
547         }
548         if(!config_read(cfg, cfgfile, NULL)) {
549                 /* config_read prints messages to stderr */
550                 config_delete(cfg);
551                 exit(1);
552         }
553         if(oldwd[0] && chdir(oldwd) == -1)
554                 log_err("cannot chdir(%s): %s", oldwd, strerror(errno));
555         if(opt) {
556                 print_option(cfg, opt, final);
557                 config_delete(cfg);
558                 return;
559         }
560         morechecks(cfg, cfgfile);
561         check_mod(cfg, iter_get_funcblock());
562         check_mod(cfg, val_get_funcblock());
563 #ifdef WITH_PYTHONMODULE
564         if(strstr(cfg->module_conf, "python"))
565                 check_mod(cfg, pythonmod_get_funcblock());
566 #endif
567         check_fwd(cfg);
568         check_hints(cfg);
569         printf("unbound-checkconf: no errors in %s\n", cfgfile);
570         config_delete(cfg);
571 }
572
573 /** getopt global, in case header files fail to declare it. */
574 extern int optind;
575 /** getopt global, in case header files fail to declare it. */
576 extern char* optarg;
577
578 /** Main routine for checkconf */
579 int main(int argc, char* argv[])
580 {
581         int c;
582         int final = 0;
583         const char* f;
584         const char* opt = NULL;
585         const char* cfgfile = CONFIGFILE;
586         log_ident_set("unbound-checkconf");
587         log_init(NULL, 0, NULL);
588         checklock_start();
589 #ifdef USE_WINSOCK
590         /* use registry config file in preference to compiletime location */
591         if(!(cfgfile=w_lookup_reg_str("Software\\Unbound", "ConfigFile")))
592                 cfgfile = CONFIGFILE;
593 #endif /* USE_WINSOCK */
594         /* parse the options */
595         while( (c=getopt(argc, argv, "fho:")) != -1) {
596                 switch(c) {
597                 case 'f':
598                         final = 1;
599                         break;
600                 case 'o':
601                         opt = optarg;
602                         break;
603                 case '?':
604                 case 'h':
605                 default:
606                         usage();
607                 }
608         }
609         argc -= optind;
610         argv += optind;
611         if(argc != 0 && argc != 1)
612                 usage();
613         if(argc == 1)
614                 f = argv[0];
615         else    f = cfgfile;
616         checkconf(f, opt, final);
617         checklock_stop();
618         return 0;
619 }