unbound-control.c revision 276699
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 24266114Sdes * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25266114Sdes * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26266114Sdes * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 27266114Sdes * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28266114Sdes * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 29266114Sdes * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 30266114Sdes * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 31266114Sdes * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 32266114Sdes * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 33266114Sdes * 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 62276699Sdes#ifdef HAVE_SYS_UN_H 63276699Sdes#include <sys/un.h> 64276699Sdes#endif 65276699Sdes 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"); 102276605Sdes 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"); 112238106Sdes printf(" list_local_zones list local-zones in use\n"); 113238106Sdes printf(" list_local_data list local-data RRs in use\n"); 114266114Sdes printf(" insecure_add zone add domain-insecure zone\n"); 115266114Sdes printf(" insecure_remove zone remove domain-insecure zone\n"); 116238106Sdes printf(" forward_add [+i] zone addr.. add forward-zone with servers\n"); 117238106Sdes printf(" forward_remove [+i] zone remove forward zone\n"); 118238106Sdes printf(" stub_add [+ip] zone addr.. add stub-zone with servers\n"); 119238106Sdes printf(" stub_remove [+i] zone remove stub zone\n"); 120238106Sdes printf(" +i also do dnssec insecure point\n"); 121238106Sdes printf(" +p set stub to use priming\n"); 122238106Sdes printf(" forward [off | addr ...] without arg show forward setup\n"); 123238106Sdes printf(" or off to turn off root forwarding\n"); 124238106Sdes printf(" or give list of ip addresses\n"); 125238106Sdes printf("Version %s\n", PACKAGE_VERSION); 126238106Sdes printf("BSD licensed, see LICENSE in source package for details.\n"); 127238106Sdes printf("Report bugs to %s\n", PACKAGE_BUGREPORT); 128238106Sdes exit(1); 129238106Sdes} 130238106Sdes 131238106Sdes/** exit with ssl error */ 132238106Sdesstatic void ssl_err(const char* s) 133238106Sdes{ 134238106Sdes fprintf(stderr, "error: %s\n", s); 135238106Sdes ERR_print_errors_fp(stderr); 136238106Sdes exit(1); 137238106Sdes} 138238106Sdes 139238106Sdes/** setup SSL context */ 140238106Sdesstatic SSL_CTX* 141238106Sdessetup_ctx(struct config_file* cfg) 142238106Sdes{ 143238106Sdes char* s_cert, *c_key, *c_cert; 144238106Sdes SSL_CTX* ctx; 145238106Sdes 146276699Sdes if(cfg->remote_control_use_cert) { 147276699Sdes s_cert = fname_after_chroot(cfg->server_cert_file, cfg, 1); 148276699Sdes c_key = fname_after_chroot(cfg->control_key_file, cfg, 1); 149276699Sdes c_cert = fname_after_chroot(cfg->control_cert_file, cfg, 1); 150276699Sdes if(!s_cert || !c_key || !c_cert) 151276699Sdes fatal_exit("out of memory"); 152276699Sdes } 153238106Sdes ctx = SSL_CTX_new(SSLv23_client_method()); 154238106Sdes if(!ctx) 155238106Sdes ssl_err("could not allocate SSL_CTX pointer"); 156238106Sdes if(!(SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)) 157238106Sdes ssl_err("could not set SSL_OP_NO_SSLv2"); 158276699Sdes if(cfg->remote_control_use_cert) { 159276699Sdes if(!(SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3)) 160276699Sdes ssl_err("could not set SSL_OP_NO_SSLv3"); 161276699Sdes if(!SSL_CTX_use_certificate_file(ctx,c_cert,SSL_FILETYPE_PEM) || 162276699Sdes !SSL_CTX_use_PrivateKey_file(ctx,c_key,SSL_FILETYPE_PEM) 163276699Sdes || !SSL_CTX_check_private_key(ctx)) 164276699Sdes ssl_err("Error setting up SSL_CTX client key and cert"); 165276699Sdes if (SSL_CTX_load_verify_locations(ctx, s_cert, NULL) != 1) 166276699Sdes ssl_err("Error setting up SSL_CTX verify, server cert"); 167276699Sdes SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); 168238106Sdes 169276699Sdes free(s_cert); 170276699Sdes free(c_key); 171276699Sdes free(c_cert); 172276699Sdes } else { 173276699Sdes /* Use ciphers that don't require authentication */ 174276699Sdes if(!SSL_CTX_set_cipher_list(ctx, "aNULL")) 175276699Sdes ssl_err("Error setting NULL cipher!"); 176276699Sdes } 177238106Sdes return ctx; 178238106Sdes} 179238106Sdes 180238106Sdes/** contact the server with TCP connect */ 181238106Sdesstatic int 182238106Sdescontact_server(const char* svr, struct config_file* cfg, int statuscmd) 183238106Sdes{ 184238106Sdes struct sockaddr_storage addr; 185238106Sdes socklen_t addrlen; 186276699Sdes int addrfamily = 0; 187238106Sdes int fd; 188238106Sdes /* use svr or the first config entry */ 189238106Sdes if(!svr) { 190238106Sdes if(cfg->control_ifs) 191238106Sdes svr = cfg->control_ifs->str; 192238106Sdes else svr = "127.0.0.1"; 193238106Sdes /* config 0 addr (everything), means ask localhost */ 194238106Sdes if(strcmp(svr, "0.0.0.0") == 0) 195238106Sdes svr = "127.0.0.1"; 196238106Sdes else if(strcmp(svr, "::0") == 0 || 197238106Sdes strcmp(svr, "0::0") == 0 || 198238106Sdes strcmp(svr, "0::") == 0 || 199238106Sdes strcmp(svr, "::") == 0) 200238106Sdes svr = "::1"; 201238106Sdes } 202238106Sdes if(strchr(svr, '@')) { 203238106Sdes if(!extstrtoaddr(svr, &addr, &addrlen)) 204238106Sdes fatal_exit("could not parse IP@port: %s", svr); 205276699Sdes#ifdef HAVE_SYS_UN_H 206276699Sdes } else if(svr[0] == '/') { 207276699Sdes struct sockaddr_un* sun = (struct sockaddr_un *) &addr; 208276699Sdes sun->sun_family = AF_LOCAL; 209276699Sdes sun->sun_len = sizeof(sun); 210276699Sdes strlcpy(sun->sun_path, svr, 104); 211276699Sdes addrlen = sizeof(struct sockaddr_un); 212276699Sdes addrfamily = AF_LOCAL; 213276699Sdes#endif 214238106Sdes } else { 215238106Sdes if(!ipstrtoaddr(svr, cfg->control_port, &addr, &addrlen)) 216238106Sdes fatal_exit("could not parse IP: %s", svr); 217238106Sdes } 218276699Sdes 219276699Sdes if(addrfamily == 0) 220276699Sdes addrfamily = addr_is_ip6(&addr, addrlen)?AF_INET6:AF_INET; 221276699Sdes fd = socket(addrfamily, SOCK_STREAM, 0); 222238106Sdes if(fd == -1) { 223238106Sdes#ifndef USE_WINSOCK 224238106Sdes fatal_exit("socket: %s", strerror(errno)); 225238106Sdes#else 226238106Sdes fatal_exit("socket: %s", wsa_strerror(WSAGetLastError())); 227238106Sdes#endif 228238106Sdes } 229238106Sdes if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) { 230238106Sdes#ifndef USE_WINSOCK 231276605Sdes log_err_addr("connect", strerror(errno), &addr, addrlen); 232238106Sdes if(errno == ECONNREFUSED && statuscmd) { 233238106Sdes printf("unbound is stopped\n"); 234238106Sdes exit(3); 235238106Sdes } 236238106Sdes#else 237276605Sdes log_err_addr("connect", wsa_strerror(WSAGetLastError()), &addr, addrlen); 238238106Sdes if(WSAGetLastError() == WSAECONNREFUSED && statuscmd) { 239238106Sdes printf("unbound is stopped\n"); 240238106Sdes exit(3); 241238106Sdes } 242238106Sdes#endif 243238106Sdes exit(1); 244238106Sdes } 245238106Sdes return fd; 246238106Sdes} 247238106Sdes 248238106Sdes/** setup SSL on the connection */ 249238106Sdesstatic SSL* 250276699Sdessetup_ssl(SSL_CTX* ctx, int fd, struct config_file* cfg) 251238106Sdes{ 252238106Sdes SSL* ssl; 253238106Sdes X509* x; 254238106Sdes int r; 255238106Sdes 256238106Sdes ssl = SSL_new(ctx); 257238106Sdes if(!ssl) 258238106Sdes ssl_err("could not SSL_new"); 259238106Sdes SSL_set_connect_state(ssl); 260238106Sdes (void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); 261238106Sdes if(!SSL_set_fd(ssl, fd)) 262238106Sdes ssl_err("could not SSL_set_fd"); 263238106Sdes while(1) { 264238106Sdes ERR_clear_error(); 265238106Sdes if( (r=SSL_do_handshake(ssl)) == 1) 266238106Sdes break; 267238106Sdes r = SSL_get_error(ssl, r); 268238106Sdes if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE) 269238106Sdes ssl_err("SSL handshake failed"); 270238106Sdes /* wants to be called again */ 271238106Sdes } 272238106Sdes 273238106Sdes /* check authenticity of server */ 274238106Sdes if(SSL_get_verify_result(ssl) != X509_V_OK) 275238106Sdes ssl_err("SSL verification failed"); 276276699Sdes if(cfg->remote_control_use_cert) { 277276699Sdes x = SSL_get_peer_certificate(ssl); 278276699Sdes if(!x) 279276699Sdes ssl_err("Server presented no peer certificate"); 280276699Sdes X509_free(x); 281276699Sdes } 282276699Sdes 283238106Sdes return ssl; 284238106Sdes} 285238106Sdes 286238106Sdes/** send stdin to server */ 287238106Sdesstatic void 288238106Sdessend_file(SSL* ssl, FILE* in, char* buf, size_t sz) 289238106Sdes{ 290238106Sdes while(fgets(buf, (int)sz, in)) { 291238106Sdes if(SSL_write(ssl, buf, (int)strlen(buf)) <= 0) 292238106Sdes ssl_err("could not SSL_write contents"); 293238106Sdes } 294238106Sdes} 295238106Sdes 296238106Sdes/** send command and display result */ 297238106Sdesstatic int 298249141Sdesgo_cmd(SSL* ssl, int quiet, int argc, char* argv[]) 299238106Sdes{ 300238106Sdes char pre[10]; 301238106Sdes const char* space=" "; 302238106Sdes const char* newline="\n"; 303238106Sdes int was_error = 0, first_line = 1; 304238106Sdes int r, i; 305238106Sdes char buf[1024]; 306238106Sdes snprintf(pre, sizeof(pre), "UBCT%d ", UNBOUND_CONTROL_VERSION); 307238106Sdes if(SSL_write(ssl, pre, (int)strlen(pre)) <= 0) 308238106Sdes ssl_err("could not SSL_write"); 309238106Sdes for(i=0; i<argc; i++) { 310238106Sdes if(SSL_write(ssl, space, (int)strlen(space)) <= 0) 311238106Sdes ssl_err("could not SSL_write"); 312238106Sdes if(SSL_write(ssl, argv[i], (int)strlen(argv[i])) <= 0) 313238106Sdes ssl_err("could not SSL_write"); 314238106Sdes } 315238106Sdes if(SSL_write(ssl, newline, (int)strlen(newline)) <= 0) 316238106Sdes ssl_err("could not SSL_write"); 317238106Sdes 318238106Sdes if(argc == 1 && strcmp(argv[0], "load_cache") == 0) { 319238106Sdes send_file(ssl, stdin, buf, sizeof(buf)); 320238106Sdes } 321238106Sdes 322238106Sdes while(1) { 323238106Sdes ERR_clear_error(); 324238106Sdes if((r = SSL_read(ssl, buf, (int)sizeof(buf)-1)) <= 0) { 325238106Sdes if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) { 326238106Sdes /* EOF */ 327238106Sdes break; 328238106Sdes } 329238106Sdes ssl_err("could not SSL_read"); 330238106Sdes } 331238106Sdes buf[r] = 0; 332249141Sdes if(first_line && strncmp(buf, "error", 5) == 0) { 333249141Sdes printf("%s", buf); 334238106Sdes was_error = 1; 335249141Sdes } else if (!quiet) 336249141Sdes printf("%s", buf); 337249141Sdes 338238106Sdes first_line = 0; 339238106Sdes } 340238106Sdes return was_error; 341238106Sdes} 342238106Sdes 343238106Sdes/** go ahead and read config, contact server and perform command and display */ 344238106Sdesstatic int 345249141Sdesgo(const char* cfgfile, char* svr, int quiet, int argc, char* argv[]) 346238106Sdes{ 347238106Sdes struct config_file* cfg; 348238106Sdes int fd, ret; 349238106Sdes SSL_CTX* ctx; 350238106Sdes SSL* ssl; 351238106Sdes 352238106Sdes /* read config */ 353238106Sdes if(!(cfg = config_create())) 354238106Sdes fatal_exit("out of memory"); 355238106Sdes if(!config_read(cfg, cfgfile, NULL)) 356238106Sdes fatal_exit("could not read config file"); 357238106Sdes if(!cfg->remote_control_enable) 358238106Sdes log_warn("control-enable is 'no' in the config file."); 359238106Sdes ctx = setup_ctx(cfg); 360276699Sdes 361238106Sdes /* contact server */ 362238106Sdes fd = contact_server(svr, cfg, argc>0&&strcmp(argv[0],"status")==0); 363276699Sdes ssl = setup_ssl(ctx, fd, cfg); 364276699Sdes 365238106Sdes /* send command */ 366249141Sdes ret = go_cmd(ssl, quiet, argc, argv); 367238106Sdes 368238106Sdes SSL_free(ssl); 369238106Sdes#ifndef USE_WINSOCK 370238106Sdes close(fd); 371238106Sdes#else 372238106Sdes closesocket(fd); 373238106Sdes#endif 374238106Sdes SSL_CTX_free(ctx); 375238106Sdes config_delete(cfg); 376238106Sdes return ret; 377238106Sdes} 378238106Sdes 379238106Sdes/** getopt global, in case header files fail to declare it. */ 380238106Sdesextern int optind; 381238106Sdes/** getopt global, in case header files fail to declare it. */ 382238106Sdesextern char* optarg; 383238106Sdes 384238106Sdes/** Main routine for unbound-control */ 385238106Sdesint main(int argc, char* argv[]) 386238106Sdes{ 387238106Sdes int c, ret; 388249141Sdes int quiet = 0; 389238106Sdes const char* cfgfile = CONFIGFILE; 390238106Sdes char* svr = NULL; 391238106Sdes#ifdef USE_WINSOCK 392238106Sdes int r; 393238106Sdes WSADATA wsa_data; 394238106Sdes#endif 395238106Sdes#ifdef USE_THREAD_DEBUG 396238106Sdes /* stop the file output from unbound-control, overwites the servers */ 397238106Sdes extern int check_locking_order; 398238106Sdes check_locking_order = 0; 399238106Sdes#endif /* USE_THREAD_DEBUG */ 400238106Sdes log_ident_set("unbound-control"); 401238106Sdes log_init(NULL, 0, NULL); 402238106Sdes checklock_start(); 403238106Sdes#ifdef USE_WINSOCK 404238106Sdes if((r = WSAStartup(MAKEWORD(2,2), &wsa_data)) != 0) 405238106Sdes fatal_exit("WSAStartup failed: %s", wsa_strerror(r)); 406238106Sdes /* use registry config file in preference to compiletime location */ 407238106Sdes if(!(cfgfile=w_lookup_reg_str("Software\\Unbound", "ConfigFile"))) 408238106Sdes cfgfile = CONFIGFILE; 409238106Sdes#endif 410238106Sdes 411238106Sdes ERR_load_crypto_strings(); 412238106Sdes ERR_load_SSL_strings(); 413238106Sdes OpenSSL_add_all_algorithms(); 414238106Sdes (void)SSL_library_init(); 415238106Sdes 416238106Sdes if(!RAND_status()) { 417238106Sdes /* try to seed it */ 418238106Sdes unsigned char buf[256]; 419249141Sdes unsigned int seed=(unsigned)time(NULL) ^ (unsigned)getpid(); 420249141Sdes unsigned int v = seed; 421238106Sdes size_t i; 422238106Sdes for(i=0; i<256/sizeof(v); i++) { 423238106Sdes memmove(buf+i*sizeof(v), &v, sizeof(v)); 424238106Sdes v = v*seed + (unsigned int)i; 425238106Sdes } 426238106Sdes RAND_seed(buf, 256); 427238106Sdes log_warn("no entropy, seeding openssl PRNG with time\n"); 428238106Sdes } 429238106Sdes 430238106Sdes /* parse the options */ 431249141Sdes while( (c=getopt(argc, argv, "c:s:qh")) != -1) { 432238106Sdes switch(c) { 433238106Sdes case 'c': 434238106Sdes cfgfile = optarg; 435238106Sdes break; 436238106Sdes case 's': 437238106Sdes svr = optarg; 438238106Sdes break; 439249141Sdes case 'q': 440249141Sdes quiet = 1; 441249141Sdes break; 442238106Sdes case '?': 443238106Sdes case 'h': 444238106Sdes default: 445238106Sdes usage(); 446238106Sdes } 447238106Sdes } 448238106Sdes argc -= optind; 449238106Sdes argv += optind; 450238106Sdes if(argc == 0) 451238106Sdes usage(); 452238106Sdes if(argc >= 1 && strcmp(argv[0], "start")==0) { 453238106Sdes if(execlp("unbound", "unbound", "-c", cfgfile, 454238106Sdes (char*)NULL) < 0) { 455238106Sdes fatal_exit("could not exec unbound: %s", 456238106Sdes strerror(errno)); 457238106Sdes } 458238106Sdes } 459238106Sdes 460249141Sdes ret = go(cfgfile, svr, quiet, argc, argv); 461238106Sdes 462238106Sdes#ifdef USE_WINSOCK 463238106Sdes WSACleanup(); 464238106Sdes#endif 465238106Sdes checklock_stop(); 466238106Sdes return ret; 467238106Sdes} 468