unbound-control.c revision 238106
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 24238106Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25238106Sdes * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26238106Sdes * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 27238106Sdes * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28238106Sdes * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29238106Sdes * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30238106Sdes * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31238106Sdes * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32238106Sdes * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33238106Sdes * 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 62238106Sdes/** Give unbound-control usage, and exit (1). */ 63238106Sdesstatic void 64238106Sdesusage() 65238106Sdes{ 66238106Sdes printf("Usage: unbound-control [options] command\n"); 67238106Sdes printf(" Remote control utility for unbound server.\n"); 68238106Sdes printf("Options:\n"); 69238106Sdes printf(" -c file config file, default is %s\n", CONFIGFILE); 70238106Sdes printf(" -s ip[@port] server address, if omitted config is used.\n"); 71238106Sdes printf(" -h show this usage help.\n"); 72238106Sdes printf("Commands:\n"); 73238106Sdes printf(" start start server; runs unbound(8)\n"); 74238106Sdes printf(" stop stops the server\n"); 75238106Sdes printf(" reload reloads the server\n"); 76238106Sdes printf(" (this flushes data, stats, requestlist)\n"); 77238106Sdes printf(" stats print statistics\n"); 78238106Sdes printf(" stats_noreset peek at statistics\n"); 79238106Sdes printf(" status display status of server\n"); 80238106Sdes printf(" verbosity <number> change logging detail\n"); 81238106Sdes printf(" log_reopen close and open the logfile\n"); 82238106Sdes printf(" local_zone <name> <type> add new local zone\n"); 83238106Sdes printf(" local_zone_remove <name> remove local zone and its contents\n"); 84238106Sdes printf(" local_data <RR data...> add local data, for example\n"); 85238106Sdes printf(" local_data www.example.com A 192.0.2.1\n"); 86238106Sdes printf(" local_data_remove <name> remove local RR data from name\n"); 87238106Sdes printf(" dump_cache print cache to stdout\n"); 88238106Sdes printf(" load_cache load cache from stdin\n"); 89238106Sdes printf(" lookup <name> print nameservers for name\n"); 90238106Sdes printf(" flush <name> flushes common types for name from cache\n"); 91238106Sdes printf(" types: A, AAAA, MX, PTR, NS,\n"); 92238106Sdes printf(" SOA, CNAME, DNAME, SRV, NAPTR\n"); 93238106Sdes printf(" flush_type <name> <type> flush name, type from cache\n"); 94238106Sdes printf(" flush_zone <name> flush everything at or under name\n"); 95238106Sdes printf(" from rr and dnssec caches\n"); 96238106Sdes printf(" flush_stats flush statistics, make zero\n"); 97238106Sdes printf(" flush_requestlist drop queries that are worked on\n"); 98238106Sdes printf(" dump_requestlist show what is worked on\n"); 99238106Sdes printf(" flush_infra [all | ip] remove ping, edns for one IP or all\n"); 100238106Sdes printf(" dump_infra show ping and edns entries\n"); 101238106Sdes printf(" set_option opt: val set option to value, no reload\n"); 102238106Sdes printf(" get_option opt get option value\n"); 103238106Sdes printf(" list_stubs list stub-zones and root hints in use\n"); 104238106Sdes printf(" list_forwards list forward-zones in use\n"); 105238106Sdes printf(" list_local_zones list local-zones in use\n"); 106238106Sdes printf(" list_local_data list local-data RRs in use\n"); 107238106Sdes printf(" forward_add [+i] zone addr.. add forward-zone with servers\n"); 108238106Sdes printf(" forward_remove [+i] zone remove forward zone\n"); 109238106Sdes printf(" stub_add [+ip] zone addr.. add stub-zone with servers\n"); 110238106Sdes printf(" stub_remove [+i] zone remove stub zone\n"); 111238106Sdes printf(" +i also do dnssec insecure point\n"); 112238106Sdes printf(" +p set stub to use priming\n"); 113238106Sdes printf(" forward [off | addr ...] without arg show forward setup\n"); 114238106Sdes printf(" or off to turn off root forwarding\n"); 115238106Sdes printf(" or give list of ip addresses\n"); 116238106Sdes printf("Version %s\n", PACKAGE_VERSION); 117238106Sdes printf("BSD licensed, see LICENSE in source package for details.\n"); 118238106Sdes printf("Report bugs to %s\n", PACKAGE_BUGREPORT); 119238106Sdes exit(1); 120238106Sdes} 121238106Sdes 122238106Sdes/** exit with ssl error */ 123238106Sdesstatic void ssl_err(const char* s) 124238106Sdes{ 125238106Sdes fprintf(stderr, "error: %s\n", s); 126238106Sdes ERR_print_errors_fp(stderr); 127238106Sdes exit(1); 128238106Sdes} 129238106Sdes 130238106Sdes/** setup SSL context */ 131238106Sdesstatic SSL_CTX* 132238106Sdessetup_ctx(struct config_file* cfg) 133238106Sdes{ 134238106Sdes char* s_cert, *c_key, *c_cert; 135238106Sdes SSL_CTX* ctx; 136238106Sdes 137238106Sdes s_cert = fname_after_chroot(cfg->server_cert_file, cfg, 1); 138238106Sdes c_key = fname_after_chroot(cfg->control_key_file, cfg, 1); 139238106Sdes c_cert = fname_after_chroot(cfg->control_cert_file, cfg, 1); 140238106Sdes if(!s_cert || !c_key || !c_cert) 141238106Sdes fatal_exit("out of memory"); 142238106Sdes ctx = SSL_CTX_new(SSLv23_client_method()); 143238106Sdes if(!ctx) 144238106Sdes ssl_err("could not allocate SSL_CTX pointer"); 145238106Sdes if(!(SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)) 146238106Sdes ssl_err("could not set SSL_OP_NO_SSLv2"); 147238106Sdes if(!SSL_CTX_use_certificate_file(ctx,c_cert,SSL_FILETYPE_PEM) || 148238106Sdes !SSL_CTX_use_PrivateKey_file(ctx,c_key,SSL_FILETYPE_PEM) 149238106Sdes || !SSL_CTX_check_private_key(ctx)) 150238106Sdes ssl_err("Error setting up SSL_CTX client key and cert"); 151238106Sdes if (SSL_CTX_load_verify_locations(ctx, s_cert, NULL) != 1) 152238106Sdes ssl_err("Error setting up SSL_CTX verify, server cert"); 153238106Sdes SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); 154238106Sdes 155238106Sdes free(s_cert); 156238106Sdes free(c_key); 157238106Sdes free(c_cert); 158238106Sdes return ctx; 159238106Sdes} 160238106Sdes 161238106Sdes/** contact the server with TCP connect */ 162238106Sdesstatic int 163238106Sdescontact_server(const char* svr, struct config_file* cfg, int statuscmd) 164238106Sdes{ 165238106Sdes struct sockaddr_storage addr; 166238106Sdes socklen_t addrlen; 167238106Sdes int fd; 168238106Sdes /* use svr or the first config entry */ 169238106Sdes if(!svr) { 170238106Sdes if(cfg->control_ifs) 171238106Sdes svr = cfg->control_ifs->str; 172238106Sdes else svr = "127.0.0.1"; 173238106Sdes /* config 0 addr (everything), means ask localhost */ 174238106Sdes if(strcmp(svr, "0.0.0.0") == 0) 175238106Sdes svr = "127.0.0.1"; 176238106Sdes else if(strcmp(svr, "::0") == 0 || 177238106Sdes strcmp(svr, "0::0") == 0 || 178238106Sdes strcmp(svr, "0::") == 0 || 179238106Sdes strcmp(svr, "::") == 0) 180238106Sdes svr = "::1"; 181238106Sdes } 182238106Sdes if(strchr(svr, '@')) { 183238106Sdes if(!extstrtoaddr(svr, &addr, &addrlen)) 184238106Sdes fatal_exit("could not parse IP@port: %s", svr); 185238106Sdes } else { 186238106Sdes if(!ipstrtoaddr(svr, cfg->control_port, &addr, &addrlen)) 187238106Sdes fatal_exit("could not parse IP: %s", svr); 188238106Sdes } 189238106Sdes fd = socket(addr_is_ip6(&addr, addrlen)?AF_INET6:AF_INET, 190238106Sdes SOCK_STREAM, 0); 191238106Sdes if(fd == -1) { 192238106Sdes#ifndef USE_WINSOCK 193238106Sdes fatal_exit("socket: %s", strerror(errno)); 194238106Sdes#else 195238106Sdes fatal_exit("socket: %s", wsa_strerror(WSAGetLastError())); 196238106Sdes#endif 197238106Sdes } 198238106Sdes if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) { 199238106Sdes log_addr(0, "address", &addr, addrlen); 200238106Sdes#ifndef USE_WINSOCK 201238106Sdes log_err("connect: %s", strerror(errno)); 202238106Sdes if(errno == ECONNREFUSED && statuscmd) { 203238106Sdes printf("unbound is stopped\n"); 204238106Sdes exit(3); 205238106Sdes } 206238106Sdes#else 207238106Sdes log_err("connect: %s", wsa_strerror(WSAGetLastError())); 208238106Sdes if(WSAGetLastError() == WSAECONNREFUSED && statuscmd) { 209238106Sdes printf("unbound is stopped\n"); 210238106Sdes exit(3); 211238106Sdes } 212238106Sdes#endif 213238106Sdes exit(1); 214238106Sdes } 215238106Sdes return fd; 216238106Sdes} 217238106Sdes 218238106Sdes/** setup SSL on the connection */ 219238106Sdesstatic SSL* 220238106Sdessetup_ssl(SSL_CTX* ctx, int fd) 221238106Sdes{ 222238106Sdes SSL* ssl; 223238106Sdes X509* x; 224238106Sdes int r; 225238106Sdes 226238106Sdes ssl = SSL_new(ctx); 227238106Sdes if(!ssl) 228238106Sdes ssl_err("could not SSL_new"); 229238106Sdes SSL_set_connect_state(ssl); 230238106Sdes (void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); 231238106Sdes if(!SSL_set_fd(ssl, fd)) 232238106Sdes ssl_err("could not SSL_set_fd"); 233238106Sdes while(1) { 234238106Sdes ERR_clear_error(); 235238106Sdes if( (r=SSL_do_handshake(ssl)) == 1) 236238106Sdes break; 237238106Sdes r = SSL_get_error(ssl, r); 238238106Sdes if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) 239238106Sdes ssl_err("SSL handshake failed"); 240238106Sdes /* wants to be called again */ 241238106Sdes } 242238106Sdes 243238106Sdes /* check authenticity of server */ 244238106Sdes if(SSL_get_verify_result(ssl) != X509_V_OK) 245238106Sdes ssl_err("SSL verification failed"); 246238106Sdes x = SSL_get_peer_certificate(ssl); 247238106Sdes if(!x) 248238106Sdes ssl_err("Server presented no peer certificate"); 249238106Sdes X509_free(x); 250238106Sdes return ssl; 251238106Sdes} 252238106Sdes 253238106Sdes/** send stdin to server */ 254238106Sdesstatic void 255238106Sdessend_file(SSL* ssl, FILE* in, char* buf, size_t sz) 256238106Sdes{ 257238106Sdes while(fgets(buf, (int)sz, in)) { 258238106Sdes if(SSL_write(ssl, buf, (int)strlen(buf)) <= 0) 259238106Sdes ssl_err("could not SSL_write contents"); 260238106Sdes } 261238106Sdes} 262238106Sdes 263238106Sdes/** send command and display result */ 264238106Sdesstatic int 265238106Sdesgo_cmd(SSL* ssl, int argc, char* argv[]) 266238106Sdes{ 267238106Sdes char pre[10]; 268238106Sdes const char* space=" "; 269238106Sdes const char* newline="\n"; 270238106Sdes int was_error = 0, first_line = 1; 271238106Sdes int r, i; 272238106Sdes char buf[1024]; 273238106Sdes snprintf(pre, sizeof(pre), "UBCT%d ", UNBOUND_CONTROL_VERSION); 274238106Sdes if(SSL_write(ssl, pre, (int)strlen(pre)) <= 0) 275238106Sdes ssl_err("could not SSL_write"); 276238106Sdes for(i=0; i<argc; i++) { 277238106Sdes if(SSL_write(ssl, space, (int)strlen(space)) <= 0) 278238106Sdes ssl_err("could not SSL_write"); 279238106Sdes if(SSL_write(ssl, argv[i], (int)strlen(argv[i])) <= 0) 280238106Sdes ssl_err("could not SSL_write"); 281238106Sdes } 282238106Sdes if(SSL_write(ssl, newline, (int)strlen(newline)) <= 0) 283238106Sdes ssl_err("could not SSL_write"); 284238106Sdes 285238106Sdes if(argc == 1 && strcmp(argv[0], "load_cache") == 0) { 286238106Sdes send_file(ssl, stdin, buf, sizeof(buf)); 287238106Sdes } 288238106Sdes 289238106Sdes while(1) { 290238106Sdes ERR_clear_error(); 291238106Sdes if((r = SSL_read(ssl, buf, (int)sizeof(buf)-1)) <= 0) { 292238106Sdes if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { 293238106Sdes /* EOF */ 294238106Sdes break; 295238106Sdes } 296238106Sdes ssl_err("could not SSL_read"); 297238106Sdes } 298238106Sdes buf[r] = 0; 299238106Sdes printf("%s", buf); 300238106Sdes if(first_line && strncmp(buf, "error", 5) == 0) 301238106Sdes was_error = 1; 302238106Sdes first_line = 0; 303238106Sdes } 304238106Sdes return was_error; 305238106Sdes} 306238106Sdes 307238106Sdes/** go ahead and read config, contact server and perform command and display */ 308238106Sdesstatic int 309238106Sdesgo(const char* cfgfile, char* svr, int argc, char* argv[]) 310238106Sdes{ 311238106Sdes struct config_file* cfg; 312238106Sdes int fd, ret; 313238106Sdes SSL_CTX* ctx; 314238106Sdes SSL* ssl; 315238106Sdes 316238106Sdes /* read config */ 317238106Sdes if(!(cfg = config_create())) 318238106Sdes fatal_exit("out of memory"); 319238106Sdes if(!config_read(cfg, cfgfile, NULL)) 320238106Sdes fatal_exit("could not read config file"); 321238106Sdes if(!cfg->remote_control_enable) 322238106Sdes log_warn("control-enable is 'no' in the config file."); 323238106Sdes ctx = setup_ctx(cfg); 324238106Sdes 325238106Sdes /* contact server */ 326238106Sdes fd = contact_server(svr, cfg, argc>0&&strcmp(argv[0],"status")==0); 327238106Sdes ssl = setup_ssl(ctx, fd); 328238106Sdes 329238106Sdes /* send command */ 330238106Sdes ret = go_cmd(ssl, argc, argv); 331238106Sdes 332238106Sdes SSL_free(ssl); 333238106Sdes#ifndef USE_WINSOCK 334238106Sdes close(fd); 335238106Sdes#else 336238106Sdes closesocket(fd); 337238106Sdes#endif 338238106Sdes SSL_CTX_free(ctx); 339238106Sdes config_delete(cfg); 340238106Sdes return ret; 341238106Sdes} 342238106Sdes 343238106Sdes/** getopt global, in case header files fail to declare it. */ 344238106Sdesextern int optind; 345238106Sdes/** getopt global, in case header files fail to declare it. */ 346238106Sdesextern char* optarg; 347238106Sdes 348238106Sdes/** Main routine for unbound-control */ 349238106Sdesint main(int argc, char* argv[]) 350238106Sdes{ 351238106Sdes int c, ret; 352238106Sdes const char* cfgfile = CONFIGFILE; 353238106Sdes char* svr = NULL; 354238106Sdes#ifdef USE_WINSOCK 355238106Sdes int r; 356238106Sdes WSADATA wsa_data; 357238106Sdes#endif 358238106Sdes#ifdef USE_THREAD_DEBUG 359238106Sdes /* stop the file output from unbound-control, overwites the servers */ 360238106Sdes extern int check_locking_order; 361238106Sdes check_locking_order = 0; 362238106Sdes#endif /* USE_THREAD_DEBUG */ 363238106Sdes log_ident_set("unbound-control"); 364238106Sdes log_init(NULL, 0, NULL); 365238106Sdes checklock_start(); 366238106Sdes#ifdef USE_WINSOCK 367238106Sdes if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) 368238106Sdes fatal_exit("WSAStartup failed: %s", wsa_strerror(r)); 369238106Sdes /* use registry config file in preference to compiletime location */ 370238106Sdes if(!(cfgfile=w_lookup_reg_str("Software\\Unbound", "ConfigFile"))) 371238106Sdes cfgfile = CONFIGFILE; 372238106Sdes#endif 373238106Sdes 374238106Sdes ERR_load_crypto_strings(); 375238106Sdes ERR_load_SSL_strings(); 376238106Sdes OpenSSL_add_all_algorithms(); 377238106Sdes (void)SSL_library_init(); 378238106Sdes 379238106Sdes if(!RAND_status()) { 380238106Sdes /* try to seed it */ 381238106Sdes unsigned char buf[256]; 382238106Sdes unsigned int v, seed=(unsigned)time(NULL) ^ (unsigned)getpid(); 383238106Sdes size_t i; 384238106Sdes for(i=0; i<256/sizeof(v); i++) { 385238106Sdes memmove(buf+i*sizeof(v), &v, sizeof(v)); 386238106Sdes v = v*seed + (unsigned int)i; 387238106Sdes } 388238106Sdes RAND_seed(buf, 256); 389238106Sdes log_warn("no entropy, seeding openssl PRNG with time\n"); 390238106Sdes } 391238106Sdes 392238106Sdes /* parse the options */ 393238106Sdes while( (c=getopt(argc, argv, "c:s:h")) != -1) { 394238106Sdes switch(c) { 395238106Sdes case 'c': 396238106Sdes cfgfile = optarg; 397238106Sdes break; 398238106Sdes case 's': 399238106Sdes svr = optarg; 400238106Sdes break; 401238106Sdes case '?': 402238106Sdes case 'h': 403238106Sdes default: 404238106Sdes usage(); 405238106Sdes } 406238106Sdes } 407238106Sdes argc -= optind; 408238106Sdes argv += optind; 409238106Sdes if(argc == 0) 410238106Sdes usage(); 411238106Sdes if(argc >= 1 && strcmp(argv[0], "start")==0) { 412238106Sdes if(execlp("unbound", "unbound", "-c", cfgfile, 413238106Sdes (char*)NULL) < 0) { 414238106Sdes fatal_exit("could not exec unbound: %s", 415238106Sdes strerror(errno)); 416238106Sdes } 417238106Sdes } 418238106Sdes 419238106Sdes ret = go(cfgfile, svr, argc, argv); 420238106Sdes 421238106Sdes#ifdef USE_WINSOCK 422238106Sdes WSACleanup(); 423238106Sdes#endif 424238106Sdes checklock_stop(); 425238106Sdes return ret; 426238106Sdes} 427