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