1/*
2 * nsd-control.c - remote control utility for nsd.
3 *
4 * Copyright (c) 2011, 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 remote control utility contacts the nsd server over ssl and
40 * sends the command, receives the answer, and displays the result
41 * from the commandline.
42 */
43
44#include "config.h"
45#include <stdio.h>
46#include <stdlib.h>
47
48struct region;
49struct domain_table;
50struct zone;
51struct domain;
52int zonec_parse_string(struct region* ATTR_UNUSED(region),
53	struct domain_table* ATTR_UNUSED(domains),
54	struct zone* ATTR_UNUSED(zone), char* ATTR_UNUSED(str),
55	struct domain** ATTR_UNUSED(parsed), int* ATTR_UNUSED(num_rrs))
56{
57	return 0;
58}
59
60#include <sys/types.h>
61#include <unistd.h>
62#include <string.h>
63#include <errno.h>
64#ifdef HAVE_SSL
65#ifdef HAVE_OPENSSL_SSL_H
66#include <openssl/ssl.h>
67#endif
68#ifdef HAVE_OPENSSL_ERR_H
69#include <openssl/err.h>
70#endif
71#ifdef HAVE_OPENSSL_RAND_H
72#include <openssl/rand.h>
73#endif
74#endif /* HAVE_SSL */
75#ifdef HAVE_SYS_UN_H
76#include <sys/un.h>
77#endif
78#include <fcntl.h>
79#ifndef AF_LOCAL
80#define AF_LOCAL AF_UNIX
81#endif
82#include "util.h"
83#include "tsig.h"
84#include "options.h"
85#include "zonec.h"
86
87static void usage(void) ATTR_NORETURN;
88#ifdef HAVE_SSL
89static void ssl_err(const char* s) ATTR_NORETURN;
90static void ssl_path_err(const char* s, const char *path) ATTR_NORETURN;
91#else
92/* define SSL to use as a boolean to turn it off in function calls. */
93#define SSL int
94#endif
95
96/** timeout to wait for connection over stream, in msec */
97#define NSD_CONTROL_CONNECT_TIMEOUT 5000
98
99/** Give nsd-control usage, and exit (1). */
100static void
101usage()
102{
103	printf("Usage:	nsd-control [options] command\n");
104	printf("	Remote control utility for nsd server.\n");
105	printf("Version %s. Report bugs to <%s>.\n",
106		PACKAGE_VERSION, PACKAGE_BUGREPORT);
107	printf("Options:\n");
108	printf("  -c file	config file, default is %s\n", CONFIGFILE);
109	printf("  -s ip[@port]	server address, if omitted config is used.\n");
110	printf("  -h		show this usage help.\n");
111	printf("Commands:\n");
112	printf("  start				start server; runs nsd(8)\n");
113	printf("  stop				stops the server\n");
114	printf("  reload [<zone>]		reload modified zonefiles from disk\n");
115	printf("  reconfig			reload the config file\n");
116	printf("  repattern			the same as reconfig\n");
117	printf("  log_reopen			reopen logfile (for log rotate)\n");
118	printf("  status			display status of server\n");
119	printf("  stats				print statistics\n");
120	printf("  stats_noreset			peek at statistics\n");
121	printf("  addzone <name> <pattern>	add a new zone\n");
122	printf("  delzone <name>		remove a zone\n");
123	printf("  changezone <name> <pattern>	change zone to use pattern\n");
124	printf("  addzones			add zone list on stdin {name space pattern newline}\n");
125	printf("  delzones			remove zone list on stdin {name newline}\n");
126	printf("  write [<zone>]		write changed zonefiles to disk\n");
127	printf("  notify [<zone>]		send NOTIFY messages to secondary servers\n");
128	printf("  transfer [<zone>]		try to update secondary zones to newer serial\n");
129	printf("  force_transfer [<zone>]	update secondary zones with AXFR, no serial check\n");
130	printf("  zonestatus [<zone>]		print state, serial, activity\n");
131	printf("  serverpid			get pid of server process\n");
132	printf("  verbosity <number>		change logging detail\n");
133	printf("  print_tsig [<key_name>]	print tsig with <name> the secret and algo\n");
134	printf("  update_tsig <name> <secret>	change existing tsig with <name> to a new <secret>\n");
135	printf("  add_tsig <name> <secret> [algo] add new key with the given parameters\n");
136	printf("  assoc_tsig <zone> <key_name>	associate <zone> with given tsig <key_name> name\n");
137	printf("  del_tsig <key_name>		delete tsig <key_name> from configuration\n");
138	printf("  add_cookie_secret <secret>	add (or replace) a new cookie secret <secret>\n");
139	printf("  drop_cookie_secret		drop a staging cookie secret\n");
140	printf("  activate_cookie_secret	make a staging cookie secret active\n");
141	printf("  print_cookie_secrets		show all cookie secrets with their status\n");
142	exit(1);
143}
144
145#ifdef HAVE_SSL
146/** exit with ssl error */
147static void ssl_err(const char* s)
148{
149	fprintf(stderr, "error: %s\n", s);
150	ERR_print_errors_fp(stderr);
151	exit(1);
152}
153
154/** exit with ssl error related to a file path */
155static void ssl_path_err(const char* s, const char *path)
156{
157	unsigned long err;
158	err = ERR_peek_error();
159	if (ERR_GET_LIB(err) == ERR_LIB_SYS) {
160		fprintf(stderr, "error: %s\n%s: %s\n",
161			s, path, ERR_reason_error_string(err));
162		exit(1);
163	} else {
164		ssl_err(s);
165	}
166}
167
168/** setup SSL context */
169static SSL_CTX*
170setup_ctx(struct nsd_options* cfg)
171{
172	char* s_cert, *c_key, *c_cert;
173	SSL_CTX* ctx;
174
175	if(!options_remote_is_address(cfg))
176		return NULL;
177	s_cert = cfg->server_cert_file;
178	c_key = cfg->control_key_file;
179	c_cert = cfg->control_cert_file;
180
181	/* filenames may be relative to zonesdir */
182	if (cfg->zonesdir && cfg->zonesdir[0] &&
183		(s_cert[0] != '/' || c_key[0] != '/' || c_cert[0] != '/')) {
184		if(chdir(cfg->zonesdir))
185			error("could not chdir to zonesdir: %s %s",
186				cfg->zonesdir, strerror(errno));
187	}
188
189        ctx = SSL_CTX_new(SSLv23_client_method());
190	if(!ctx)
191		ssl_err("could not allocate SSL_CTX pointer");
192#if SSL_OP_NO_SSLv2 != 0
193        if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2) & SSL_OP_NO_SSLv2)
194		!= SSL_OP_NO_SSLv2)
195		ssl_err("could not set SSL_OP_NO_SSLv2");
196#endif
197        if((SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3) & SSL_OP_NO_SSLv3)
198		!= SSL_OP_NO_SSLv3)
199		ssl_err("could not set SSL_OP_NO_SSLv3");
200#if defined(SSL_OP_NO_RENEGOTIATION)
201	/* disable client renegotiation */
202	if((SSL_CTX_set_options(ctx, SSL_OP_NO_RENEGOTIATION) &
203		SSL_OP_NO_RENEGOTIATION) != SSL_OP_NO_RENEGOTIATION)
204		ssl_err("could not set SSL_OP_NO_RENEGOTIATION");
205#endif
206	if(!SSL_CTX_use_certificate_file(ctx,c_cert,SSL_FILETYPE_PEM))
207		ssl_path_err("Error setting up SSL_CTX client cert", c_cert);
208	if(!SSL_CTX_use_PrivateKey_file(ctx,c_key,SSL_FILETYPE_PEM))
209		ssl_path_err("Error setting up SSL_CTX client key", c_key);
210	if(!SSL_CTX_check_private_key(ctx))
211		ssl_err("Error setting up SSL_CTX client key");
212	if (SSL_CTX_load_verify_locations(ctx, s_cert, NULL) != 1)
213		ssl_path_err("Error setting up SSL_CTX verify, server cert",
214			s_cert);
215	SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
216
217	return ctx;
218}
219#endif /* HAVE_SSL */
220
221/** check connect error */
222static void
223checkconnecterr(int err, const char* svr, int port, int statuscmd)
224{
225	if(!port) fprintf(stderr, "error: connect (%s): %s\n", svr,
226		strerror(err));
227	else fprintf(stderr, "error: connect (%s@%d): %s\n", svr, port,
228		strerror(err));
229	if(err == ECONNREFUSED && statuscmd) {
230		printf("nsd is stopped\n");
231		exit(3);
232	}
233	exit(1);
234}
235
236/** contact the server with TCP connect */
237static int
238contact_server(const char* svr, struct nsd_options* cfg, int statuscmd)
239{
240#ifdef INET6
241	struct sockaddr_storage addr;
242#else
243	struct sockaddr_in addr;
244#endif
245	socklen_t addrlen;
246	int fd;
247	int port = cfg->control_port;
248	int addrfamily = 0;
249	/* use svr or a config entry */
250	if(!svr) {
251		if(cfg->control_interface) {
252			svr = cfg->control_interface->address;
253		} else if(cfg->do_ip4) {
254			svr = "127.0.0.1";
255		} else {
256			svr = "::1";
257		}
258		/* config 0 addr (everything), means ask localhost */
259		if(strcmp(svr, "0.0.0.0") == 0)
260			svr = "127.0.0.1";
261		else if(strcmp(svr, "::0") == 0 ||
262			strcmp(svr, "0::0") == 0 ||
263			strcmp(svr, "0::") == 0 ||
264			strcmp(svr, "::") == 0)
265			svr = "::1";
266	}
267	if(strchr(svr, '@')) {
268		char* ps = strchr(svr, '@');
269		*ps++ = 0;
270		port = atoi(ps);
271		if(!port) {
272			fprintf(stderr, "could not parse port %s\n", ps);
273			exit(1);
274		}
275	}
276	if(svr[0] == '/') {
277#ifdef HAVE_SYS_UN_H
278		struct sockaddr_un* usock = (struct sockaddr_un *) &addr;
279		usock->sun_family = AF_LOCAL;
280#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
281		usock->sun_len = (unsigned)sizeof(usock);
282#endif
283		(void)strlcpy(usock->sun_path, svr, sizeof(usock->sun_path));
284		addrlen = (socklen_t)sizeof(struct sockaddr_un);
285		addrfamily = AF_LOCAL;
286		port = 0;
287#endif
288#ifdef INET6
289	} else if(strchr(svr, ':')) {
290		struct sockaddr_in6 sa;
291		addrlen = (socklen_t)sizeof(struct sockaddr_in6);
292		memset(&sa, 0, addrlen);
293		sa.sin6_family = AF_INET6;
294		sa.sin6_port = (in_port_t)htons((uint16_t)port);
295		if(inet_pton((int)sa.sin6_family, svr, &sa.sin6_addr) <= 0) {
296			fprintf(stderr, "could not parse IP: %s\n", svr);
297			exit(1);
298		}
299		memcpy(&addr, &sa, addrlen);
300		addrfamily = AF_INET6;
301#endif
302	} else { /* ip4 */
303		struct sockaddr_in sa;
304		addrlen = (socklen_t)sizeof(struct sockaddr_in);
305		memset(&sa, 0, addrlen);
306		sa.sin_family = AF_INET;
307		sa.sin_port = (in_port_t)htons((uint16_t)port);
308		if(inet_pton((int)sa.sin_family, svr, &sa.sin_addr) <= 0) {
309			fprintf(stderr, "could not parse IP: %s\n", svr);
310			exit(1);
311		}
312		memcpy(&addr, &sa, addrlen);
313		addrfamily = AF_INET;
314	}
315
316	fd = socket(addrfamily, SOCK_STREAM, 0);
317	if(fd == -1) {
318		fprintf(stderr, "socket: %s\n", strerror(errno));
319		exit(1);
320	}
321	if(fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
322		fprintf(stderr, "error: set nonblocking: fcntl: %s",
323			strerror(errno));
324	}
325	if(connect(fd, (struct sockaddr*)&addr, addrlen) < 0) {
326		if(errno != EINPROGRESS) {
327			checkconnecterr(errno, svr, port, statuscmd);
328		}
329	}
330	while(1) {
331		fd_set rset, wset, eset;
332		struct timeval tv;
333		FD_ZERO(&rset);
334		FD_SET(fd, &rset);
335		FD_ZERO(&wset);
336		FD_SET(fd, &wset);
337		FD_ZERO(&eset);
338		FD_SET(fd, &eset);
339		tv.tv_sec = NSD_CONTROL_CONNECT_TIMEOUT/1000;
340		tv.tv_usec= (NSD_CONTROL_CONNECT_TIMEOUT%1000)*1000;
341		if(select(fd+1, &rset, &wset, &eset, &tv) == -1) {
342			fprintf(stderr, "select: %s\n", strerror(errno));
343			exit(1);
344		}
345		if(!FD_ISSET(fd, &rset) && !FD_ISSET(fd, &wset) &&
346			!FD_ISSET(fd, &eset)) {
347			fprintf(stderr, "timeout: could not connect to server\n");
348			exit(1);
349		} else {
350			/* check nonblocking connect error */
351			int error = 0;
352			socklen_t len = (socklen_t)sizeof(error);
353			if(getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)&error,
354				&len) < 0) {
355				error = errno; /* on solaris errno is error */
356			}
357			if(error != 0) {
358				if(error == EINPROGRESS || error == EWOULDBLOCK)
359					continue; /* try again later */
360				checkconnecterr(error, svr, port, statuscmd);
361			}
362		}
363		break;
364	}
365	if(fcntl(fd, F_SETFL, 0) == -1) {
366		fprintf(stderr, "error: set blocking: fcntl: %s",
367			strerror(errno));
368	}
369	return fd;
370}
371
372#ifdef HAVE_SSL
373/** setup SSL on the connection */
374static SSL*
375setup_ssl(SSL_CTX* ctx, int fd)
376{
377	SSL* ssl;
378	X509* x;
379	int r;
380
381	if(!ctx) return NULL;
382	ssl = SSL_new(ctx);
383	if(!ssl)
384		ssl_err("could not SSL_new");
385	SSL_set_connect_state(ssl);
386	(void)SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
387	if(!SSL_set_fd(ssl, fd))
388		ssl_err("could not SSL_set_fd");
389	while(1) {
390		ERR_clear_error();
391		if( (r=SSL_do_handshake(ssl)) == 1)
392			break;
393		r = SSL_get_error(ssl, r);
394		if(r != SSL_ERROR_WANT_READ && r != SSL_ERROR_WANT_WRITE)
395			ssl_err("SSL handshake failed");
396		/* wants to be called again */
397	}
398
399	/* check authenticity of server */
400	if(SSL_get_verify_result(ssl) != X509_V_OK)
401		ssl_err("SSL verification failed");
402	x = SSL_get_peer_certificate(ssl);
403	if(!x)
404		ssl_err("Server presented no peer certificate");
405	X509_free(x);
406	return ssl;
407}
408#endif /* HAVE_SSL */
409
410/** read from ssl or fd, fatalexit on error, 0 EOF, 1 success */
411static int
412remote_read(SSL* ssl, int fd, char* buf, size_t len)
413{
414	if(ssl) {
415#ifdef HAVE_SSL
416		int r;
417		ERR_clear_error();
418		if((r = SSL_read(ssl, buf, (int)len-1)) <= 0) {
419			if(SSL_get_error(ssl, r) == SSL_ERROR_ZERO_RETURN) {
420				/* EOF */
421				return 0;
422			}
423			ssl_err("could not SSL_read");
424		}
425		buf[r] = 0;
426#endif /* HAVE_SSL */
427	} else {
428		ssize_t rr = read(fd, buf, len-1);
429		if(rr <= 0) {
430			if(rr == 0) {
431				/* EOF */
432				return 0;
433			}
434			fprintf(stderr, "could not read: %s\n",
435				strerror(errno));
436			exit(1);
437		}
438		buf[rr] = 0;
439	}
440	return 1;
441}
442
443/** write to ssl or fd, fatalexit on error */
444static void
445remote_write(SSL* ssl, int fd, const char* buf, size_t len)
446{
447	if(ssl) {
448#ifdef HAVE_SSL
449		if(SSL_write(ssl, buf, (int)len) <= 0)
450			ssl_err("could not SSL_write");
451#endif /* HAVE_SSL */
452	} else {
453		if(write(fd, buf, len) < (ssize_t)len) {
454			fprintf(stderr, "could not write: %s\n",
455				strerror(errno));
456			exit(1);
457		}
458	}
459}
460
461/** send stdin to server */
462static void
463send_file(SSL* ssl, int fd, FILE* in, char* buf, size_t sz)
464{
465	char e[] = {0x04, 0x0a};
466	while(fgets(buf, (int)sz, in)) {
467		remote_write(ssl, fd, buf, strlen(buf));
468	}
469	/* send end-of-file marker */
470	remote_write(ssl, fd, e, sizeof(e));
471}
472
473/** send command and display result */
474static int
475go_cmd(SSL* ssl, int fd, int argc, char* argv[])
476{
477	char pre[10];
478	const char* space=" ";
479	const char* newline="\n";
480	int was_error = 0, first_line = 1;
481	int i;
482	char buf[1024];
483	snprintf(pre, sizeof(pre), "NSDCT%d ", NSD_CONTROL_VERSION);
484	remote_write(ssl, fd, pre, strlen(pre));
485	for(i=0; i<argc; i++) {
486		remote_write(ssl, fd, space, strlen(space));
487		remote_write(ssl, fd, argv[i], strlen(argv[i]));
488	}
489	remote_write(ssl, fd, newline, strlen(newline));
490
491	/* send contents to server */
492	if(argc == 1 && (strcmp(argv[0], "addzones") == 0 ||
493		strcmp(argv[0], "delzones") == 0)) {
494		send_file(ssl, fd, stdin, buf, sizeof(buf));
495	}
496
497	while(1) {
498		if(remote_read(ssl, fd, buf, sizeof(buf)) == 0) {
499			break; /* EOF */
500		}
501		printf("%s", buf);
502		if(first_line && strncmp(buf, "error", 5) == 0)
503			was_error = 1;
504		first_line = 0;
505	}
506	return was_error;
507}
508
509/** go ahead and read config, contact server and perform command and display */
510static int
511go(const char* cfgfile, char* svr, int argc, char* argv[])
512{
513	struct nsd_options* opt;
514	int fd, ret;
515#ifdef HAVE_SSL
516	SSL_CTX* ctx = NULL;
517#endif
518	SSL* ssl = NULL;
519
520	/* read config */
521	if(!(opt = nsd_options_create(region_create(xalloc, free)))) {
522		fprintf(stderr, "out of memory\n");
523		exit(1);
524	}
525	tsig_init(opt->region);
526	if(!parse_options_file(opt, cfgfile, NULL, NULL, NULL)) {
527		fprintf(stderr, "could not read config file\n");
528		exit(1);
529	}
530	if(!opt->control_enable)
531		fprintf(stderr, "warning: control-enable is 'no' in the config file.\n");
532	resolve_interface_names(opt);
533#ifdef HAVE_SSL
534	ctx = setup_ctx(opt);
535#else
536	if(options_remote_is_address(opt)) {
537		fprintf(stderr, "error: NSD was compiled without SSL.\n");
538		exit(1);
539	}
540#endif /* HAVE_SSL */
541
542	/* contact server */
543	fd = contact_server(svr, opt, argc>0&&strcmp(argv[0],"status")==0);
544#ifdef HAVE_SSL
545	ssl = setup_ssl(ctx, fd);
546#endif
547
548	/* send command */
549	ret = go_cmd(ssl, fd, argc, argv);
550
551#ifdef HAVE_SSL
552	if(ssl) SSL_free(ssl);
553#endif
554	close(fd);
555#ifdef HAVE_SSL
556	if(ctx) SSL_CTX_free(ctx);
557#endif
558	region_destroy(opt->region);
559	return ret;
560}
561
562/** getopt global, in case header files fail to declare it. */
563extern int optind;
564/** getopt global, in case header files fail to declare it. */
565extern char* optarg;
566
567/** Main routine for nsd-control */
568int main(int argc, char* argv[])
569{
570	int c;
571	const char* cfgfile = CONFIGFILE;
572	char* svr = NULL;
573	log_init("nsd-control");
574
575#ifdef HAVE_SSL
576#ifdef HAVE_ERR_LOAD_CRYPTO_STRINGS
577	ERR_load_crypto_strings();
578#endif
579#if defined(HAVE_ERR_LOAD_SSL_STRINGS) && !defined(DEPRECATED_ERR_LOAD_SSL_STRINGS)
580	ERR_load_SSL_strings();
581#endif
582#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_CRYPTO)
583	OpenSSL_add_all_algorithms();
584#else
585	OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS
586		| OPENSSL_INIT_ADD_ALL_DIGESTS
587		| OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
588#endif
589#if OPENSSL_VERSION_NUMBER < 0x10100000 || !defined(HAVE_OPENSSL_INIT_SSL)
590	(void)SSL_library_init();
591#else
592	OPENSSL_init_ssl(0, NULL);
593#endif
594
595	if(!RAND_status()) {
596                /* try to seed it */
597                unsigned char buf[256];
598                unsigned int v, seed=(unsigned)time(NULL) ^ (unsigned)getpid();
599                size_t i;
600		v = seed;
601                for(i=0; i<256/sizeof(v); i++) {
602                        memmove(buf+i*sizeof(v), &v, sizeof(v));
603                        v = v*seed + (unsigned int)i;
604                }
605                RAND_seed(buf, 256);
606		fprintf(stderr, "warning: no entropy, seeding openssl PRNG with time\n");
607	}
608#endif /* HAVE_SSL */
609
610	/* parse the options */
611	while( (c=getopt(argc, argv, "c:s:h")) != -1) {
612		switch(c) {
613		case 'c':
614			cfgfile = optarg;
615			break;
616		case 's':
617			svr = optarg;
618			break;
619		case '?':
620		case 'h':
621		default:
622			usage();
623		}
624	}
625	argc -= optind;
626	argv += optind;
627	if(argc == 0)
628		usage();
629	if(argc >= 1 && strcmp(argv[0], "start")==0) {
630		const char *path;
631		if((path = getenv("NSD_PATH")) == NULL) {
632			path = NSD_START_PATH;
633		}
634		if(execl(path, "nsd", "-c", cfgfile, (char*)NULL) < 0) {
635			fprintf(stderr, "could not exec %s: %s\n",
636				NSD_START_PATH, strerror(errno));
637			exit(1);
638		}
639	}
640
641	return go(cfgfile, svr, argc, argv);
642}
643