1238106Sdes/* 2238106Sdes * checkconf/unbound-control.c - remote control utility for unbound. 3238106Sdes * 4238106Sdes * Copyright (c) 2008, 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 remote control utility contacts the unbound server over ssl and 40238106Sdes * sends the command, receives the answer, and displays the result 41238106Sdes * from the commandline. 42238106Sdes */ 43238106Sdes 44238106Sdes#include "config.h" 45238106Sdes#ifdef HAVE_GETOPT_H 46238106Sdes#include <getopt.h> 47238106Sdes#endif 48238106Sdes#ifdef HAVE_OPENSSL_SSL_H 49238106Sdes#include <openssl/ssl.h> 50238106Sdes#endif 51238106Sdes#ifdef HAVE_OPENSSL_ERR_H 52238106Sdes#include <openssl/err.h> 53238106Sdes#endif 54238106Sdes#ifdef HAVE_OPENSSL_RAND_H 55238106Sdes#include <openssl/rand.h> 56238106Sdes#endif 57238106Sdes#include "util/log.h" 58238106Sdes#include "util/config_file.h" 59238106Sdes#include "util/locks.h" 60238106Sdes#include "util/net_help.h" 61238106Sdes 62285206Sdes#ifdef HAVE_SYS_UN_H 63285206Sdes#include <sys/un.h> 64285206Sdes#endif 65285206Sdes 66238106Sdes/** Give unbound-control usage, and exit (1). */ 67238106Sdesstatic void 68238106Sdesusage() 69238106Sdes{ 70238106Sdes printf("Usage: unbound-control [options] command\n"); 71238106Sdes printf(" Remote control utility for unbound server.\n"); 72238106Sdes printf("Options:\n"); 73238106Sdes printf(" -c file config file, default is %s\n", CONFIGFILE); 74238106Sdes printf(" -s ip[@port] server address, if omitted config is used.\n"); 75249141Sdes printf(" -q quiet (don't print anything if it works ok).\n"); 76238106Sdes printf(" -h show this usage help.\n"); 77238106Sdes printf("Commands:\n"); 78238106Sdes printf(" start start server; runs unbound(8)\n"); 79238106Sdes printf(" stop stops the server\n"); 80238106Sdes printf(" reload reloads the server\n"); 81238106Sdes printf(" (this flushes data, stats, requestlist)\n"); 82238106Sdes printf(" stats print statistics\n"); 83238106Sdes printf(" stats_noreset peek at statistics\n"); 84238106Sdes printf(" status display status of server\n"); 85238106Sdes printf(" verbosity <number> change logging detail\n"); 86238106Sdes printf(" log_reopen close and open the logfile\n"); 87238106Sdes printf(" local_zone <name> <type> add new local zone\n"); 88238106Sdes printf(" local_zone_remove <name> remove local zone and its contents\n"); 89238106Sdes printf(" local_data <RR data...> add local data, for example\n"); 90238106Sdes printf(" local_data www.example.com A 192.0.2.1\n"); 91238106Sdes printf(" local_data_remove <name> remove local RR data from name\n"); 92238106Sdes printf(" dump_cache print cache to stdout\n"); 93238106Sdes printf(" load_cache load cache from stdin\n"); 94238106Sdes printf(" lookup <name> print nameservers for name\n"); 95238106Sdes printf(" flush <name> flushes common types for name from cache\n"); 96238106Sdes printf(" types: A, AAAA, MX, PTR, NS,\n"); 97238106Sdes printf(" SOA, CNAME, DNAME, SRV, NAPTR\n"); 98238106Sdes printf(" flush_type <name> <type> flush name, type from cache\n"); 99238106Sdes printf(" flush_zone <name> flush everything at or under name\n"); 100238106Sdes printf(" from rr and dnssec caches\n"); 101249141Sdes printf(" flush_bogus flush all bogus data\n"); 102285206Sdes printf(" flush_negative flush all negative data\n"); 103238106Sdes printf(" flush_stats flush statistics, make zero\n"); 104238106Sdes printf(" flush_requestlist drop queries that are worked on\n"); 105238106Sdes printf(" dump_requestlist show what is worked on\n"); 106238106Sdes printf(" flush_infra [all | ip] remove ping, edns for one IP or all\n"); 107238106Sdes printf(" dump_infra show ping and edns entries\n"); 108238106Sdes printf(" set_option opt: val set option to value, no reload\n"); 109238106Sdes printf(" get_option opt get option value\n"); 110238106Sdes printf(" list_stubs list stub-zones and root hints in use\n"); 111238106Sdes printf(" list_forwards list forward-zones in use\n"); 112291767Sdes printf(" list_insecure list domain-insecure zones\n"); 113238106Sdes printf(" list_local_zones list local-zones in use\n"); 114238106Sdes printf(" list_local_data list local-data RRs in use\n"); 115269257Sdes printf(" insecure_add zone add domain-insecure zone\n"); 116269257Sdes printf(" insecure_remove zone remove domain-insecure zone\n"); 117238106Sdes printf(" forward_add [+i] zone addr.. add forward-zone with servers\n"); 118238106Sdes printf(" forward_remove [+i] zone remove forward zone\n"); 119238106Sdes printf(" stub_add [+ip] zone addr.. add stub-zone with servers\n"); 120238106Sdes printf(" stub_remove [+i] zone remove stub zone\n"); 121238106Sdes printf(" +i also do dnssec insecure point\n"); 122238106Sdes printf(" +p set stub to use priming\n"); 123238106Sdes printf(" forward [off | addr ...] without arg show forward setup\n"); 124238106Sdes printf(" or off to turn off root forwarding\n"); 125238106Sdes printf(" or give list of ip addresses\n"); 126291767Sdes printf(" ratelimit_list [+a] list ratelimited domains\n"); 127291767Sdes printf(" +a list all, also not ratelimited\n"); 128238106Sdes printf("Version %s\n", PACKAGE_VERSION); 129238106Sdes printf("BSD licensed, see LICENSE in source package for details.\n"); 130238106Sdes printf("Report bugs to %s\n", PACKAGE_BUGREPORT); 131238106Sdes exit(1); 132238106Sdes} 133238106Sdes 134238106Sdes/** exit with ssl error */ 135238106Sdesstatic void ssl_err(const char* s) 136238106Sdes{ 137238106Sdes fprintf(stderr, "error: %s\n", s); 138238106Sdes ERR_print_errors_fp(stderr); 139238106Sdes exit(1); 140238106Sdes} 141238106Sdes 142238106Sdes/** setup SSL context */ 143238106Sdesstatic SSL_CTX* 144238106Sdessetup_ctx(struct config_file* cfg) 145238106Sdes{ 146285206Sdes char* s_cert=NULL, *c_key=NULL, *c_cert=NULL; 147238106Sdes SSL_CTX* ctx; 148238106Sdes 149285206Sdes if(cfg->remote_control_use_cert) { 150285206Sdes s_cert = fname_after_chroot(cfg->server_cert_file, cfg, 1); 151285206Sdes c_key = fname_after_chroot(cfg->control_key_file, cfg, 1); 152285206Sdes c_cert = fname_after_chroot(cfg->control_cert_file, cfg, 1); 153285206Sdes if(!s_cert || !c_key || !c_cert) 154285206Sdes fatal_exit("out of memory"); 155285206Sdes } 156238106Sdes ctx = SSL_CTX_new(SSLv23_client_method()); 157238106Sdes if(!ctx) 158238106Sdes ssl_err("could not allocate SSL_CTX pointer"); 159294190Sdes if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2) 160294190Sdes != SSL_OP_NO_SSLv2) 161238106Sdes ssl_err("could not set SSL_OP_NO_SSLv2"); 162285206Sdes if(cfg->remote_control_use_cert) { 163294190Sdes if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3) 164294190Sdes != SSL_OP_NO_SSLv3) 165285206Sdes ssl_err("could not set SSL_OP_NO_SSLv3"); 166291767Sdes if(!SSL_CTX_use_certificate_chain_file(ctx,c_cert) || 167285206Sdes !SSL_CTX_use_PrivateKey_file(ctx,c_key,SSL_FILETYPE_PEM) 168285206Sdes || !SSL_CTX_check_private_key(ctx)) 169285206Sdes ssl_err("Error setting up SSL_CTX client key and cert"); 170285206Sdes if (SSL_CTX_load_verify_locations(ctx, s_cert, NULL) != 1) 171285206Sdes ssl_err("Error setting up SSL_CTX verify, server cert"); 172285206Sdes SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); 173238106Sdes 174285206Sdes free(s_cert); 175285206Sdes free(c_key); 176285206Sdes free(c_cert); 177285206Sdes } else { 178285206Sdes /* Use ciphers that don't require authentication */ 179285206Sdes if(!SSL_CTX_set_cipher_list(ctx, "aNULL")) 180285206Sdes ssl_err("Error setting NULL cipher!"); 181285206Sdes } 182238106Sdes return ctx; 183238106Sdes} 184238106Sdes 185238106Sdes/** contact the server with TCP connect */ 186238106Sdesstatic int 187238106Sdescontact_server(const char* svr, struct config_file* cfg, int statuscmd) 188238106Sdes{ 189238106Sdes struct sockaddr_storage addr; 190238106Sdes socklen_t addrlen; 191285206Sdes int addrfamily = 0; 192238106Sdes int fd; 193238106Sdes /* use svr or the first config entry */ 194238106Sdes if(!svr) { 195238106Sdes if(cfg->control_ifs) 196238106Sdes svr = cfg->control_ifs->str; 197238106Sdes else svr = "127.0.0.1"; 198238106Sdes /* config 0 addr (everything), means ask localhost */ 199238106Sdes if(strcmp(svr, "0.0.0.0") == 0) 200238106Sdes svr = "127.0.0.1"; 201238106Sdes else if(strcmp(svr, "::0") == 0 || 202238106Sdes strcmp(svr, "0::0") == 0 || 203238106Sdes strcmp(svr, "0::") == 0 || 204238106Sdes strcmp(svr, "::") == 0) 205238106Sdes svr = "::1"; 206238106Sdes } 207238106Sdes if(strchr(svr, '@')) { 208238106Sdes if(!extstrtoaddr(svr, &addr, &addrlen)) 209238106Sdes fatal_exit("could not parse IP@port: %s", svr); 210285206Sdes#ifdef HAVE_SYS_UN_H 211285206Sdes } else if(svr[0] == '/') { 212285206Sdes struct sockaddr_un* usock = (struct sockaddr_un *) &addr; 213285206Sdes usock->sun_family = AF_LOCAL; 214285206Sdes#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN 215285206Sdes usock->sun_len = (socklen_t)sizeof(usock); 216285206Sdes#endif 217285206Sdes (void)strlcpy(usock->sun_path, svr, sizeof(usock->sun_path)); 218285206Sdes addrlen = (socklen_t)sizeof(struct sockaddr_un); 219285206Sdes addrfamily = AF_LOCAL; 220285206Sdes#endif 221238106Sdes } else { 222238106Sdes if(!ipstrtoaddr(svr, cfg->control_port, &addr, &addrlen)) 223238106Sdes fatal_exit("could not parse IP: %s", svr); 224238106Sdes } 225285206Sdes 226285206Sdes if(addrfamily == 0) 227285206Sdes addrfamily = addr_is_ip6(&addr, addrlen)?AF_INET6:AF_INET; 228285206Sdes fd = socket(addrfamily, SOCK_STREAM, 0); 229238106Sdes if(fd == -1) { 230238106Sdes#ifndef USE_WINSOCK 231238106Sdes fatal_exit("socket: %s", strerror(errno)); 232238106Sdes#else 233238106Sdes fatal_exit("socket: %s", wsa_strerror(WSAGetLastError())); 234238106Sdes#endif 235238106Sdes } 236238106Sdes if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) { 237238106Sdes#ifndef USE_WINSOCK 238285206Sdes log_err_addr("connect", strerror(errno), &addr, addrlen); 239238106Sdes if(errno == ECONNREFUSED && statuscmd) { 240238106Sdes printf("unbound is stopped\n"); 241238106Sdes exit(3); 242238106Sdes } 243238106Sdes#else 244285206Sdes log_err_addr("connect", wsa_strerror(WSAGetLastError()), &addr, addrlen); 245238106Sdes if(WSAGetLastError() == WSAECONNREFUSED && statuscmd) { 246238106Sdes printf("unbound is stopped\n"); 247238106Sdes exit(3); 248238106Sdes } 249238106Sdes#endif 250238106Sdes exit(1); 251238106Sdes } 252238106Sdes return fd; 253238106Sdes} 254238106Sdes 255238106Sdes/** setup SSL on the connection */ 256238106Sdesstatic SSL* 257285206Sdessetup_ssl(SSL_CTX* ctx, int fd, struct config_file* cfg) 258238106Sdes{ 259238106Sdes SSL* ssl; 260238106Sdes X509* x; 261238106Sdes int r; 262238106Sdes 263238106Sdes ssl = SSL_new(ctx); 264238106Sdes if(!ssl) 265238106Sdes ssl_err("could not SSL_new"); 266238106Sdes SSL_set_connect_state(ssl); 267238106Sdes (void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); 268238106Sdes if(!SSL_set_fd(ssl, fd)) 269238106Sdes ssl_err("could not SSL_set_fd"); 270238106Sdes while(1) { 271238106Sdes ERR_clear_error(); 272238106Sdes if( (r=SSL_do_handshake(ssl)) == 1) 273238106Sdes break; 274238106Sdes r = SSL_get_error(ssl, r); 275238106Sdes if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) 276238106Sdes ssl_err("SSL handshake failed"); 277238106Sdes /* wants to be called again */ 278238106Sdes } 279238106Sdes 280238106Sdes /* check authenticity of server */ 281238106Sdes if(SSL_get_verify_result(ssl) != X509_V_OK) 282238106Sdes ssl_err("SSL verification failed"); 283285206Sdes if(cfg->remote_control_use_cert) { 284285206Sdes x = SSL_get_peer_certificate(ssl); 285285206Sdes if(!x) 286285206Sdes ssl_err("Server presented no peer certificate"); 287285206Sdes X509_free(x); 288285206Sdes } 289285206Sdes 290238106Sdes return ssl; 291238106Sdes} 292238106Sdes 293238106Sdes/** send stdin to server */ 294238106Sdesstatic void 295238106Sdessend_file(SSL* ssl, FILE* in, char* buf, size_t sz) 296238106Sdes{ 297238106Sdes while(fgets(buf, (int)sz, in)) { 298238106Sdes if(SSL_write(ssl, buf, (int)strlen(buf)) <= 0) 299238106Sdes ssl_err("could not SSL_write contents"); 300238106Sdes } 301238106Sdes} 302238106Sdes 303238106Sdes/** send command and display result */ 304238106Sdesstatic int 305249141Sdesgo_cmd(SSL* ssl, int quiet, int argc, char* argv[]) 306238106Sdes{ 307238106Sdes char pre[10]; 308238106Sdes const char* space=" "; 309238106Sdes const char* newline="\n"; 310238106Sdes int was_error = 0, first_line = 1; 311238106Sdes int r, i; 312238106Sdes char buf[1024]; 313238106Sdes snprintf(pre, sizeof(pre), "UBCT%d ", UNBOUND_CONTROL_VERSION); 314238106Sdes if(SSL_write(ssl, pre, (int)strlen(pre)) <= 0) 315238106Sdes ssl_err("could not SSL_write"); 316238106Sdes for(i=0; i<argc; i++) { 317238106Sdes if(SSL_write(ssl, space, (int)strlen(space)) <= 0) 318238106Sdes ssl_err("could not SSL_write"); 319238106Sdes if(SSL_write(ssl, argv[i], (int)strlen(argv[i])) <= 0) 320238106Sdes ssl_err("could not SSL_write"); 321238106Sdes } 322238106Sdes if(SSL_write(ssl, newline, (int)strlen(newline)) <= 0) 323238106Sdes ssl_err("could not SSL_write"); 324238106Sdes 325238106Sdes if(argc == 1 && strcmp(argv[0], "load_cache") == 0) { 326238106Sdes send_file(ssl, stdin, buf, sizeof(buf)); 327238106Sdes } 328238106Sdes 329238106Sdes while(1) { 330238106Sdes ERR_clear_error(); 331238106Sdes if((r = SSL_read(ssl, buf, (int)sizeof(buf)-1)) <= 0) { 332238106Sdes if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { 333238106Sdes /* EOF */ 334238106Sdes break; 335238106Sdes } 336238106Sdes ssl_err("could not SSL_read"); 337238106Sdes } 338238106Sdes buf[r] = 0; 339249141Sdes if(first_line && strncmp(buf, "error", 5) == 0) { 340249141Sdes printf("%s", buf); 341238106Sdes was_error = 1; 342249141Sdes } else if (!quiet) 343249141Sdes printf("%s", buf); 344249141Sdes 345238106Sdes first_line = 0; 346238106Sdes } 347238106Sdes return was_error; 348238106Sdes} 349238106Sdes 350238106Sdes/** go ahead and read config, contact server and perform command and display */ 351238106Sdesstatic int 352249141Sdesgo(const char* cfgfile, char* svr, int quiet, int argc, char* argv[]) 353238106Sdes{ 354238106Sdes struct config_file* cfg; 355238106Sdes int fd, ret; 356238106Sdes SSL_CTX* ctx; 357238106Sdes SSL* ssl; 358238106Sdes 359238106Sdes /* read config */ 360238106Sdes if(!(cfg = config_create())) 361238106Sdes fatal_exit("out of memory"); 362238106Sdes if(!config_read(cfg, cfgfile, NULL)) 363238106Sdes fatal_exit("could not read config file"); 364238106Sdes if(!cfg->remote_control_enable) 365238106Sdes log_warn("control-enable is 'no' in the config file."); 366294190Sdes#ifdef UB_ON_WINDOWS 367294190Sdes w_config_adjust_directory(cfg); 368294190Sdes#endif 369238106Sdes ctx = setup_ctx(cfg); 370285206Sdes 371238106Sdes /* contact server */ 372238106Sdes fd = contact_server(svr, cfg, argc>0&&strcmp(argv[0],"status")==0); 373285206Sdes ssl = setup_ssl(ctx, fd, cfg); 374285206Sdes 375238106Sdes /* send command */ 376249141Sdes ret = go_cmd(ssl, quiet, argc, argv); 377238106Sdes 378238106Sdes SSL_free(ssl); 379238106Sdes#ifndef USE_WINSOCK 380238106Sdes close(fd); 381238106Sdes#else 382238106Sdes closesocket(fd); 383238106Sdes#endif 384238106Sdes SSL_CTX_free(ctx); 385238106Sdes config_delete(cfg); 386238106Sdes return ret; 387238106Sdes} 388238106Sdes 389238106Sdes/** getopt global, in case header files fail to declare it. */ 390238106Sdesextern int optind; 391238106Sdes/** getopt global, in case header files fail to declare it. */ 392238106Sdesextern char* optarg; 393238106Sdes 394238106Sdes/** Main routine for unbound-control */ 395238106Sdesint main(int argc, char* argv[]) 396238106Sdes{ 397238106Sdes int c, ret; 398249141Sdes int quiet = 0; 399238106Sdes const char* cfgfile = CONFIGFILE; 400238106Sdes char* svr = NULL; 401238106Sdes#ifdef USE_WINSOCK 402238106Sdes int r; 403238106Sdes WSADATA wsa_data; 404238106Sdes#endif 405238106Sdes#ifdef USE_THREAD_DEBUG 406238106Sdes /* stop the file output from unbound-control, overwites the servers */ 407238106Sdes extern int check_locking_order; 408238106Sdes check_locking_order = 0; 409238106Sdes#endif /* USE_THREAD_DEBUG */ 410238106Sdes log_ident_set("unbound-control"); 411238106Sdes log_init(NULL, 0, NULL); 412238106Sdes checklock_start(); 413238106Sdes#ifdef USE_WINSOCK 414238106Sdes if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) 415238106Sdes fatal_exit("WSAStartup failed: %s", wsa_strerror(r)); 416238106Sdes /* use registry config file in preference to compiletime location */ 417238106Sdes if(!(cfgfile=w_lookup_reg_str("Software\\Unbound", "ConfigFile"))) 418238106Sdes cfgfile = CONFIGFILE; 419238106Sdes#endif 420238106Sdes 421238106Sdes ERR_load_crypto_strings(); 422238106Sdes ERR_load_SSL_strings(); 423238106Sdes OpenSSL_add_all_algorithms(); 424238106Sdes (void)SSL_library_init(); 425238106Sdes 426238106Sdes if(!RAND_status()) { 427238106Sdes /* try to seed it */ 428238106Sdes unsigned char buf[256]; 429249141Sdes unsigned int seed=(unsigned)time(NULL) ^ (unsigned)getpid(); 430249141Sdes unsigned int v = seed; 431238106Sdes size_t i; 432238106Sdes for(i=0; i<256/sizeof(v); i++) { 433238106Sdes memmove(buf+i*sizeof(v), &v, sizeof(v)); 434238106Sdes v = v*seed + (unsigned int)i; 435238106Sdes } 436238106Sdes RAND_seed(buf, 256); 437238106Sdes log_warn("no entropy, seeding openssl PRNG with time\n"); 438238106Sdes } 439238106Sdes 440238106Sdes /* parse the options */ 441249141Sdes while( (c=getopt(argc, argv, "c:s:qh")) != -1) { 442238106Sdes switch(c) { 443238106Sdes case 'c': 444238106Sdes cfgfile = optarg; 445238106Sdes break; 446238106Sdes case 's': 447238106Sdes svr = optarg; 448238106Sdes break; 449249141Sdes case 'q': 450249141Sdes quiet = 1; 451249141Sdes break; 452238106Sdes case '?': 453238106Sdes case 'h': 454238106Sdes default: 455238106Sdes usage(); 456238106Sdes } 457238106Sdes } 458238106Sdes argc -= optind; 459238106Sdes argv += optind; 460238106Sdes if(argc == 0) 461238106Sdes usage(); 462238106Sdes if(argc >= 1 && strcmp(argv[0], "start")==0) { 463238106Sdes if(execlp("unbound", "unbound", "-c", cfgfile, 464238106Sdes (char*)NULL) < 0) { 465238106Sdes fatal_exit("could not exec unbound: %s", 466238106Sdes strerror(errno)); 467238106Sdes } 468238106Sdes } 469238106Sdes 470249141Sdes ret = go(cfgfile, svr, quiet, argc, argv); 471238106Sdes 472238106Sdes#ifdef USE_WINSOCK 473238106Sdes WSACleanup(); 474238106Sdes#endif 475238106Sdes checklock_stop(); 476238106Sdes return ret; 477238106Sdes} 478