xref: /openbsd-src/usr.sbin/nsd/nsd.c (revision bf87c3c07c3ad89262e2b8cae09f17e70aa9e1ee)
162ac0c33Sjakob /*
262ac0c33Sjakob  * nsd.c -- nsd(8)
362ac0c33Sjakob  *
4dd5b221eSsthen  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
562ac0c33Sjakob  *
662ac0c33Sjakob  * See LICENSE for the license.
762ac0c33Sjakob  *
862ac0c33Sjakob  */
962ac0c33Sjakob 
10d11a62c8Ssthen #include "config.h"
1162ac0c33Sjakob 
1262ac0c33Sjakob #include <sys/types.h>
1362ac0c33Sjakob #include <sys/param.h>
1462ac0c33Sjakob #include <sys/socket.h>
1562ac0c33Sjakob #include <sys/stat.h>
1662ac0c33Sjakob #include <sys/uio.h>
1762ac0c33Sjakob #include <sys/wait.h>
1862ac0c33Sjakob #include <netinet/in.h>
1962ac0c33Sjakob #include <arpa/inet.h>
2062ac0c33Sjakob #ifdef HAVE_GRP_H
2162ac0c33Sjakob #include <grp.h>
2262ac0c33Sjakob #endif /* HAVE_GRP_H */
2362ac0c33Sjakob #ifdef HAVE_SETUSERCONTEXT
24a1bac035Sflorian #ifdef HAVE_LOGIN_CAP_H
2562ac0c33Sjakob #include <login_cap.h>
26a1bac035Sflorian #endif /* HAVE_LOGIN_CAP_H */
2762ac0c33Sjakob #endif /* HAVE_SETUSERCONTEXT */
28063644e9Sflorian #ifdef HAVE_OPENSSL_RAND_H
29063644e9Sflorian #include <openssl/rand.h>
30063644e9Sflorian #endif
3162ac0c33Sjakob 
3262ac0c33Sjakob #include <assert.h>
3362ac0c33Sjakob #include <ctype.h>
3462ac0c33Sjakob #include <errno.h>
3562ac0c33Sjakob #include <fcntl.h>
3662ac0c33Sjakob #include <limits.h>
3762ac0c33Sjakob #include <netdb.h>
3862ac0c33Sjakob #include <pwd.h>
3962ac0c33Sjakob #include <signal.h>
4062ac0c33Sjakob #include <stdarg.h>
4162ac0c33Sjakob #include <stddef.h>
4262ac0c33Sjakob #include <stdio.h>
4362ac0c33Sjakob #include <stdlib.h>
4462ac0c33Sjakob #include <string.h>
4562ac0c33Sjakob #include <time.h>
4662ac0c33Sjakob #include <unistd.h>
478d298c9fSsthen #ifdef HAVE_IFADDRS_H
48308d2509Sflorian #include <ifaddrs.h>
498d298c9fSsthen #endif
5062ac0c33Sjakob 
5162ac0c33Sjakob #include "nsd.h"
5262ac0c33Sjakob #include "options.h"
5362ac0c33Sjakob #include "tsig.h"
54dd5b221eSsthen #include "remote.h"
558d8f1862Ssthen #include "xfrd-disk.h"
56b71395eaSflorian #include "ipc.h"
57e02bc0dfSflorian #ifdef USE_DNSTAP
58e02bc0dfSflorian #include "dnstap/dnstap_collector.h"
59e02bc0dfSflorian #endif
60b71395eaSflorian #include "util/proxy_protocol.h"
6162ac0c33Sjakob 
6262ac0c33Sjakob /* The server handler... */
63533110e2Sbrad struct nsd nsd;
6462ac0c33Sjakob static char hostname[MAXHOSTNAMELEN];
65fe5fe5f6Sflorian extern config_parser_state_type* cfg_parser;
664b6a9f59Sflorian static void version(void) ATTR_NORETURN;
6762ac0c33Sjakob 
6862ac0c33Sjakob /*
6962ac0c33Sjakob  * Print the help text.
7062ac0c33Sjakob  *
7162ac0c33Sjakob  */
7262ac0c33Sjakob static void
usage(void)7362ac0c33Sjakob usage (void)
7462ac0c33Sjakob {
7562ac0c33Sjakob 	fprintf(stderr, "Usage: nsd [OPTION]...\n");
7662ac0c33Sjakob 	fprintf(stderr, "Name Server Daemon.\n\n");
7762ac0c33Sjakob 	fprintf(stderr,
7862ac0c33Sjakob 		"Supported options:\n"
7962ac0c33Sjakob 		"  -4                   Only listen to IPv4 connections.\n"
8062ac0c33Sjakob 		"  -6                   Only listen to IPv6 connections.\n"
81c3fd4e2aSjakob 		"  -a ip-address[@port] Listen to the specified incoming IP address (and port)\n"
82c3fd4e2aSjakob 		"                       May be specified multiple times).\n"
8362ac0c33Sjakob 		"  -c configfile        Read specified configfile instead of %s.\n"
84dd5b221eSsthen 		"  -d                   do not fork as a daemon process.\n"
8562ac0c33Sjakob #ifndef NDEBUG
8662ac0c33Sjakob 		"  -F facilities        Specify the debug facilities.\n"
8762ac0c33Sjakob #endif /* NDEBUG */
8862ac0c33Sjakob 		"  -h                   Print this help information.\n"
8962ac0c33Sjakob 		, CONFIGFILE);
9062ac0c33Sjakob 	fprintf(stderr,
9162ac0c33Sjakob 		"  -i identity          Specify the identity when queried for id.server CHAOS TXT.\n"
9262ac0c33Sjakob 		"  -I nsid              Specify the NSID. This must be a hex string.\n"
9362ac0c33Sjakob #ifndef NDEBUG
9462ac0c33Sjakob 		"  -L level             Specify the debug level.\n"
9562ac0c33Sjakob #endif /* NDEBUG */
9662ac0c33Sjakob 		"  -l filename          Specify the log file.\n"
9762ac0c33Sjakob 		"  -N server-count      The number of servers to start.\n"
9862ac0c33Sjakob 		"  -n tcp-count         The maximum number of TCP connections per server.\n"
9962ac0c33Sjakob 		"  -P pidfile           Specify the PID file to write.\n"
10062ac0c33Sjakob 		"  -p port              Specify the port to listen to.\n"
10162ac0c33Sjakob 		"  -s seconds           Dump statistics every SECONDS seconds.\n"
10262ac0c33Sjakob 		"  -t chrootdir         Change root to specified directory on startup.\n"
10362ac0c33Sjakob 		);
10462ac0c33Sjakob 	fprintf(stderr,
10562ac0c33Sjakob 		"  -u user              Change effective uid to the specified user.\n"
10662ac0c33Sjakob 		"  -V level             Specify verbosity level.\n"
10762ac0c33Sjakob 		"  -v                   Print version information.\n"
10862ac0c33Sjakob 		);
10962ac0c33Sjakob 	fprintf(stderr, "Version %s. Report bugs to <%s>.\n",
11062ac0c33Sjakob 		PACKAGE_VERSION, PACKAGE_BUGREPORT);
11162ac0c33Sjakob }
11262ac0c33Sjakob 
11362ac0c33Sjakob /*
11462ac0c33Sjakob  * Print the version exit.
11562ac0c33Sjakob  *
11662ac0c33Sjakob  */
11762ac0c33Sjakob static void
version(void)11862ac0c33Sjakob version(void)
11962ac0c33Sjakob {
12062ac0c33Sjakob 	fprintf(stderr, "%s version %s\n", PACKAGE_NAME, PACKAGE_VERSION);
12162ac0c33Sjakob 	fprintf(stderr, "Written by NLnet Labs.\n\n");
122ac5517e4Sflorian 	fprintf(stderr, "Configure line: %s\n", CONFCMDLINE);
123ac5517e4Sflorian #ifdef USE_MINI_EVENT
124ac5517e4Sflorian 	fprintf(stderr, "Event loop: internal (uses select)\n");
125ac5517e4Sflorian #else
126ac5517e4Sflorian #  if defined(HAVE_EV_LOOP) || defined(HAVE_EV_DEFAULT_LOOP)
127ac5517e4Sflorian 	fprintf(stderr, "Event loop: %s %s (uses %s)\n",
128ac5517e4Sflorian 		"libev",
129ac5517e4Sflorian 		nsd_event_vs(),
130ac5517e4Sflorian 		nsd_event_method());
131ac5517e4Sflorian #  else
132ac5517e4Sflorian 	fprintf(stderr, "Event loop: %s %s (uses %s)\n",
133ac5517e4Sflorian 		"libevent",
134ac5517e4Sflorian 		nsd_event_vs(),
135ac5517e4Sflorian 		nsd_event_method());
136ac5517e4Sflorian #  endif
137ac5517e4Sflorian #endif
138ac5517e4Sflorian #ifdef HAVE_SSL
139ac5517e4Sflorian 	fprintf(stderr, "Linked with %s\n\n",
140ac5517e4Sflorian #  ifdef SSLEAY_VERSION
141ac5517e4Sflorian 		SSLeay_version(SSLEAY_VERSION)
142ac5517e4Sflorian #  else
143ac5517e4Sflorian 		OpenSSL_version(OPENSSL_VERSION)
144ac5517e4Sflorian #  endif
145ac5517e4Sflorian 		);
146ac5517e4Sflorian #endif
14762ac0c33Sjakob 	fprintf(stderr,
1483b24e79eSsthen 		"Copyright (C) 2001-2020 NLnet Labs.  This is free software.\n"
14962ac0c33Sjakob 		"There is NO warranty; not even for MERCHANTABILITY or FITNESS\n"
15062ac0c33Sjakob 		"FOR A PARTICULAR PURPOSE.\n");
15162ac0c33Sjakob 	exit(0);
15262ac0c33Sjakob }
15362ac0c33Sjakob 
1545435475dSsthen static void
setup_verifier_environment(void)1553f21e8ccSflorian setup_verifier_environment(void)
1563f21e8ccSflorian {
1573f21e8ccSflorian 	size_t i;
1583f21e8ccSflorian 	int ret, ip4, ip6;
1593f21e8ccSflorian 	char *buf, host[NI_MAXHOST], serv[NI_MAXSERV];
1603f21e8ccSflorian 	size_t size, cnt = 0;
1613f21e8ccSflorian 
1623f21e8ccSflorian 	/* allocate large enough buffer to hold a list of all ip addresses.
1633f21e8ccSflorian 	   ((" " + INET6_ADDRSTRLEN + "@" + "65535") * n) + "\0" */
1643f21e8ccSflorian 	size = ((INET6_ADDRSTRLEN + 1 + 5 + 1) * nsd.verify_ifs) + 1;
1653f21e8ccSflorian 	buf = xalloc(size);
1663f21e8ccSflorian 
1673f21e8ccSflorian 	ip4 = ip6 = 0;
1683f21e8ccSflorian 	for(i = 0; i < nsd.verify_ifs; i++) {
1693f21e8ccSflorian 		ret = getnameinfo(
1703f21e8ccSflorian 			(struct sockaddr *)&nsd.verify_udp[i].addr.ai_addr,
1713f21e8ccSflorian 			nsd.verify_udp[i].addr.ai_addrlen,
1723f21e8ccSflorian 			host, sizeof(host), serv, sizeof(serv),
1733f21e8ccSflorian 			NI_NUMERICHOST | NI_NUMERICSERV);
1743f21e8ccSflorian 		if(ret != 0) {
1753f21e8ccSflorian 			log_msg(LOG_ERR, "error in getnameinfo: %s",
1763f21e8ccSflorian 				gai_strerror(ret));
1773f21e8ccSflorian 			continue;
1783f21e8ccSflorian 		}
1793f21e8ccSflorian 		buf[cnt++] = ' ';
1803f21e8ccSflorian 		cnt += strlcpy(&buf[cnt], host, size - cnt);
1813f21e8ccSflorian 		assert(cnt < size);
1823f21e8ccSflorian 		buf[cnt++] = '@';
1833f21e8ccSflorian 		cnt += strlcpy(&buf[cnt], serv, size - cnt);
1843f21e8ccSflorian 		assert(cnt < size);
1853f21e8ccSflorian #ifdef INET6
1863f21e8ccSflorian 		if (nsd.verify_udp[i].addr.ai_family == AF_INET6 && !ip6) {
1873f21e8ccSflorian 			setenv("VERIFY_IPV6_ADDRESS", host, 1);
1883f21e8ccSflorian 			setenv("VERIFY_IPV6_PORT", serv, 1);
1893f21e8ccSflorian 			setenv("VERIFY_IP_ADDRESS", host, 1);
1903f21e8ccSflorian 			setenv("VERIFY_PORT", serv, 1);
1913f21e8ccSflorian 			ip6 = 1;
1923f21e8ccSflorian 		} else
1933f21e8ccSflorian #endif
1943f21e8ccSflorian 		if (!ip4) {
1953f21e8ccSflorian 			assert(nsd.verify_udp[i].addr.ai_family == AF_INET);
1963f21e8ccSflorian 			setenv("VERIFY_IPV4_ADDRESS", host, 1);
1973f21e8ccSflorian 			setenv("VERIFY_IPV4_PORT", serv, 1);
1983f21e8ccSflorian 			if (!ip6) {
1993f21e8ccSflorian 				setenv("VERIFY_IP_ADDRESS", host, 1);
2003f21e8ccSflorian 				setenv("VERIFY_PORT", serv, 1);
2013f21e8ccSflorian 			}
2023f21e8ccSflorian 			ip4 = 1;
2033f21e8ccSflorian 		}
2043f21e8ccSflorian 	}
2053f21e8ccSflorian 
2063f21e8ccSflorian 	setenv("VERIFY_IP_ADDRESSES", &buf[1], 1);
2073f21e8ccSflorian 	free(buf);
2083f21e8ccSflorian }
2093f21e8ccSflorian 
2103f21e8ccSflorian static void
copyaddrinfo(struct nsd_addrinfo * dest,struct addrinfo * src)2115435475dSsthen copyaddrinfo(struct nsd_addrinfo *dest, struct addrinfo *src)
212c3fd4e2aSjakob {
2135435475dSsthen 	dest->ai_flags = src->ai_flags;
2145435475dSsthen 	dest->ai_family = src->ai_family;
2155435475dSsthen 	dest->ai_socktype = src->ai_socktype;
2165435475dSsthen 	dest->ai_addrlen = src->ai_addrlen;
2175435475dSsthen 	memcpy(&dest->ai_addr, src->ai_addr, src->ai_addrlen);
218c3fd4e2aSjakob }
219c3fd4e2aSjakob 
2205435475dSsthen static void
setup_socket(struct nsd_socket * sock,const char * node,const char * port,struct addrinfo * hints)221308d2509Sflorian setup_socket(
222308d2509Sflorian 	struct nsd_socket *sock, const char *node, const char *port,
223308d2509Sflorian 	struct addrinfo *hints)
224e3d8a0a5Ssthen {
2255435475dSsthen 	int ret;
22697f343bdSmillert 	char *host;
22797f343bdSmillert 	char host_buf[sizeof("65535") + INET6_ADDRSTRLEN + 1 /* '\0' */];
2285435475dSsthen 	const char *service;
2295435475dSsthen 	struct addrinfo *addr = NULL;
2305435475dSsthen 
231308d2509Sflorian 	sock->fib = -1;
2325435475dSsthen 	if(node) {
23397f343bdSmillert 		char *sep;
23497f343bdSmillert 
23597f343bdSmillert 		if (strlcpy(host_buf, node, sizeof(host_buf)) >= sizeof(host_buf)) {
23697f343bdSmillert 			error("cannot parse address '%s': %s", node,
23797f343bdSmillert 			    strerror(ENAMETOOLONG));
2385435475dSsthen 		}
23997f343bdSmillert 
24097f343bdSmillert 		host = host_buf;
24197f343bdSmillert 		sep = strchr(host_buf, '@');
24297f343bdSmillert 		if(sep != NULL) {
24397f343bdSmillert 			*sep = '\0';
24497f343bdSmillert 			service = sep + 1;
245e3d8a0a5Ssthen 		} else {
2465435475dSsthen 			service = port;
247e3d8a0a5Ssthen 		}
2485435475dSsthen 	} else {
2495435475dSsthen 		host = NULL;
2505435475dSsthen 		service = port;
2515435475dSsthen 	}
252e3d8a0a5Ssthen 
2535435475dSsthen 	if((ret = getaddrinfo(host, service, hints, &addr)) == 0) {
2545435475dSsthen 		copyaddrinfo(&sock->addr, addr);
2555435475dSsthen 		freeaddrinfo(addr);
2565435475dSsthen 	} else {
2575435475dSsthen 		error("cannot parse address '%s': getaddrinfo: %s %s",
2585435475dSsthen 		      host ? host : "(null)",
2595435475dSsthen 		      gai_strerror(ret),
2605435475dSsthen 		      ret==EAI_SYSTEM ? strerror(errno) : "");
2615435475dSsthen 	}
2625435475dSsthen }
2635435475dSsthen 
2645435475dSsthen static void
figure_socket_servers(struct nsd_socket * sock,struct ip_address_option * ip)265308d2509Sflorian figure_socket_servers(
266308d2509Sflorian 	struct nsd_socket *sock, struct ip_address_option *ip)
267308d2509Sflorian {
268308d2509Sflorian 	int i;
269308d2509Sflorian 	struct range_option *server;
270308d2509Sflorian 
271308d2509Sflorian 	sock->servers = xalloc_zero(nsd_bitset_size(nsd.child_count));
272308d2509Sflorian 	region_add_cleanup(nsd.region, free, sock->servers);
273308d2509Sflorian 	nsd_bitset_init(sock->servers, nsd.child_count);
274308d2509Sflorian 
275308d2509Sflorian 	if(!ip || !ip->servers) {
276308d2509Sflorian 		/* every server must listen on this socket */
277308d2509Sflorian 		for(i = 0; i < (int)nsd.child_count; i++) {
278308d2509Sflorian 			nsd_bitset_set(sock->servers, i);
279308d2509Sflorian 		}
280308d2509Sflorian 		return;
281308d2509Sflorian 	}
282308d2509Sflorian 
283308d2509Sflorian 	/* only specific servers must listen on this socket */
284308d2509Sflorian 	for(server = ip->servers; server; server = server->next) {
285308d2509Sflorian 		if(server->first == server->last) {
286308d2509Sflorian 			if(server->first <= 0) {
287308d2509Sflorian 				error("server %d specified for ip-address %s "
288308d2509Sflorian 				      "is invalid; server ranges are 1-based",
289308d2509Sflorian 				      server->first, ip->address);
290308d2509Sflorian 			} else if(server->last > (int)nsd.child_count) {
291308d2509Sflorian 				error("server %d specified for ip-address %s "
292308d2509Sflorian 				      "exceeds number of servers configured "
293308d2509Sflorian 				      "in server-count",
294308d2509Sflorian 				      server->first, ip->address);
295308d2509Sflorian 			}
296308d2509Sflorian 		} else {
297308d2509Sflorian 			/* parse_range must ensure range itself is valid */
298308d2509Sflorian 			assert(server->first < server->last);
299308d2509Sflorian 			if(server->first <= 0) {
300308d2509Sflorian 				error("server range %d-%d specified for "
301308d2509Sflorian 				      "ip-address %s is invalid; server "
302308d2509Sflorian 				      "ranges are 1-based",
303308d2509Sflorian 				      server->first, server->last, ip->address);
304308d2509Sflorian 			} else if(server->last > (int)nsd.child_count) {
305308d2509Sflorian 				error("server range %d-%d specified for "
306308d2509Sflorian 				      "ip-address %s exceeds number of servers "
307308d2509Sflorian 				      "configured in server-count",
308308d2509Sflorian 				      server->first, server->last, ip->address);
309308d2509Sflorian 			}
310308d2509Sflorian 		}
311308d2509Sflorian 		for(i = server->first - 1; i < server->last; i++) {
312308d2509Sflorian 			nsd_bitset_set(sock->servers, i);
313308d2509Sflorian 		}
314308d2509Sflorian 	}
315308d2509Sflorian }
316308d2509Sflorian 
317308d2509Sflorian static void
figure_default_sockets(struct nsd_socket ** udp,struct nsd_socket ** tcp,size_t * ifs,const char * node,const char * udp_port,const char * tcp_port,const struct addrinfo * hints)3185435475dSsthen figure_default_sockets(
3195435475dSsthen 	struct nsd_socket **udp, struct nsd_socket **tcp, size_t *ifs,
3203f21e8ccSflorian 	const char *node, const char *udp_port, const char *tcp_port,
3215435475dSsthen 	const struct addrinfo *hints)
3225435475dSsthen {
3235435475dSsthen 	size_t i = 0, n = 1;
3245435475dSsthen 	struct addrinfo ai[2] = { *hints, *hints };
3255435475dSsthen 
3265435475dSsthen 	assert(udp != NULL);
3275435475dSsthen 	assert(tcp != NULL);
3285435475dSsthen 	assert(ifs != NULL);
3295435475dSsthen 
3305435475dSsthen 	ai[0].ai_socktype = SOCK_DGRAM;
3315435475dSsthen 	ai[1].ai_socktype = SOCK_STREAM;
3325435475dSsthen 
3335435475dSsthen #ifdef INET6
3345435475dSsthen #ifdef IPV6_V6ONLY
3355435475dSsthen 	if (hints->ai_family == AF_UNSPEC) {
3365435475dSsthen 		ai[0].ai_family = AF_INET6;
3375435475dSsthen 		ai[1].ai_family = AF_INET6;
3385435475dSsthen 		n++;
3395435475dSsthen 	}
3405435475dSsthen #endif /* IPV6_V6ONLY */
3415435475dSsthen #endif /* INET6 */
3425435475dSsthen 
3435435475dSsthen 	*udp = xalloc_zero((n + 1) * sizeof(struct nsd_socket));
3445435475dSsthen 	*tcp = xalloc_zero((n + 1) * sizeof(struct nsd_socket));
3455435475dSsthen 	region_add_cleanup(nsd.region, free, *udp);
3465435475dSsthen 	region_add_cleanup(nsd.region, free, *tcp);
3475435475dSsthen 
3485435475dSsthen #ifdef INET6
3495435475dSsthen 	if(hints->ai_family == AF_UNSPEC) {
3505435475dSsthen 		/*
3513f21e8ccSflorian 		 * With IPv6 we'd like to open two separate sockets, one for
3523f21e8ccSflorian 		 * IPv4 and one for IPv6, both listening to the wildcard
3533f21e8ccSflorian 		 * address (unless the -4 or -6 flags are specified).
3545435475dSsthen 		 *
3553f21e8ccSflorian 		 * However, this is only supported on platforms where we can
3563f21e8ccSflorian 		 * turn the socket option IPV6_V6ONLY _on_. Otherwise we just
3573f21e8ccSflorian 		 * listen to a single IPv6 socket and any incoming IPv4
3583f21e8ccSflorian 		 * connections will be automatically mapped to our IPv6
3593f21e8ccSflorian 		 * socket.
3605435475dSsthen 		 */
3615435475dSsthen #ifdef IPV6_V6ONLY
362063644e9Sflorian 		int r;
3635435475dSsthen 		struct addrinfo *addrs[2] = { NULL, NULL };
3645435475dSsthen 
3653f21e8ccSflorian 		if((r = getaddrinfo(node, udp_port, &ai[0], &addrs[0])) == 0 &&
3663f21e8ccSflorian 		   (r = getaddrinfo(node, tcp_port, &ai[1], &addrs[1])) == 0)
3675435475dSsthen 		{
3685435475dSsthen 			(*udp)[i].flags |= NSD_SOCKET_IS_OPTIONAL;
369308d2509Sflorian 			(*udp)[i].fib = -1;
3705435475dSsthen 			copyaddrinfo(&(*udp)[i].addr, addrs[0]);
371308d2509Sflorian 			figure_socket_servers(&(*udp)[i], NULL);
3725435475dSsthen 			(*tcp)[i].flags |= NSD_SOCKET_IS_OPTIONAL;
373308d2509Sflorian 			(*tcp)[i].fib = -1;
3745435475dSsthen 			copyaddrinfo(&(*tcp)[i].addr, addrs[1]);
375308d2509Sflorian 			figure_socket_servers(&(*tcp)[i], NULL);
3765435475dSsthen 			i++;
3775435475dSsthen 		} else {
3785435475dSsthen 			log_msg(LOG_WARNING, "No IPv6, fallback to IPv4. getaddrinfo: %s",
3795435475dSsthen 			  r == EAI_SYSTEM ? strerror(errno) : gai_strerror(r));
3805435475dSsthen 		}
3815435475dSsthen 
3825435475dSsthen 		if(addrs[0])
3835435475dSsthen 			freeaddrinfo(addrs[0]);
3845435475dSsthen 		if(addrs[1])
3855435475dSsthen 			freeaddrinfo(addrs[1]);
3865435475dSsthen 
3875435475dSsthen 		ai[0].ai_family = AF_INET;
3885435475dSsthen 		ai[1].ai_family = AF_INET;
3895435475dSsthen #endif /* IPV6_V6ONLY */
3905435475dSsthen 	}
3915435475dSsthen #endif /* INET6 */
3925435475dSsthen 
3935435475dSsthen 	*ifs = i + 1;
3943f21e8ccSflorian 	setup_socket(&(*udp)[i], node, udp_port, &ai[0]);
395308d2509Sflorian 	figure_socket_servers(&(*udp)[i], NULL);
3963f21e8ccSflorian 	setup_socket(&(*tcp)[i], node, tcp_port, &ai[1]);
397308d2509Sflorian 	figure_socket_servers(&(*tcp)[i], NULL);
398308d2509Sflorian }
399308d2509Sflorian 
4008d298c9fSsthen #ifdef HAVE_GETIFADDRS
401308d2509Sflorian static int
find_device(struct nsd_socket * sock,const struct ifaddrs * ifa)402308d2509Sflorian find_device(
403308d2509Sflorian 	struct nsd_socket *sock,
404308d2509Sflorian 	const struct ifaddrs *ifa)
405308d2509Sflorian {
406308d2509Sflorian 	for(; ifa != NULL; ifa = ifa->ifa_next) {
407308d2509Sflorian 		if((ifa->ifa_addr == NULL) ||
408308d2509Sflorian 		   (ifa->ifa_addr->sa_family != sock->addr.ai_family) ||
409308d2509Sflorian 		   ((ifa->ifa_flags & IFF_UP) == 0 ||
410308d2509Sflorian 		    (ifa->ifa_flags & IFF_LOOPBACK) != 0 ||
411308d2509Sflorian 		    (ifa->ifa_flags & IFF_RUNNING) == 0))
412308d2509Sflorian 		{
413308d2509Sflorian 			continue;
414308d2509Sflorian 		}
415308d2509Sflorian 
416308d2509Sflorian #ifdef INET6
417308d2509Sflorian 		if(ifa->ifa_addr->sa_family == AF_INET6) {
418308d2509Sflorian 			struct sockaddr_in6 *sa1, *sa2;
419308d2509Sflorian 			size_t sz = sizeof(struct in6_addr);
420308d2509Sflorian 			sa1 = (struct sockaddr_in6 *)ifa->ifa_addr;
421308d2509Sflorian 			sa2 = (struct sockaddr_in6 *)&sock->addr.ai_addr;
422308d2509Sflorian 			if(memcmp(&sa1->sin6_addr, &sa2->sin6_addr, sz) == 0) {
423308d2509Sflorian 				break;
424308d2509Sflorian 			}
425308d2509Sflorian 		} else
426308d2509Sflorian #endif
427308d2509Sflorian 		if(ifa->ifa_addr->sa_family == AF_INET) {
428308d2509Sflorian 			struct sockaddr_in *sa1, *sa2;
429308d2509Sflorian 			sa1 = (struct sockaddr_in *)ifa->ifa_addr;
430308d2509Sflorian 			sa2 = (struct sockaddr_in *)&sock->addr.ai_addr;
431308d2509Sflorian 			if(sa1->sin_addr.s_addr == sa2->sin_addr.s_addr) {
432308d2509Sflorian 				break;
433308d2509Sflorian 			}
434308d2509Sflorian 		}
435308d2509Sflorian 	}
436308d2509Sflorian 
437308d2509Sflorian 	if(ifa != NULL) {
43897f343bdSmillert 		size_t len = strlcpy(sock->device, ifa->ifa_name, sizeof(sock->device));
439308d2509Sflorian 		if(len < sizeof(sock->device)) {
44097f343bdSmillert 			char *colon = strchr(sock->device, ':');
44197f343bdSmillert 			if(colon != NULL)
44297f343bdSmillert 				*colon = '\0';
443308d2509Sflorian 			return 1;
444308d2509Sflorian 		}
445308d2509Sflorian 	}
446308d2509Sflorian 
447308d2509Sflorian 	return 0;
4485435475dSsthen }
4498d298c9fSsthen #endif /* HAVE_GETIFADDRS */
4505435475dSsthen 
4515435475dSsthen static void
figure_sockets(struct nsd_socket ** udp,struct nsd_socket ** tcp,size_t * ifs,struct ip_address_option * ips,const char * node,const char * udp_port,const char * tcp_port,const struct addrinfo * hints)4525435475dSsthen figure_sockets(
4535435475dSsthen 	struct nsd_socket **udp, struct nsd_socket **tcp, size_t *ifs,
4545435475dSsthen 	struct ip_address_option *ips,
4553f21e8ccSflorian 	const char *node, const char *udp_port, const char *tcp_port,
4565435475dSsthen 	const struct addrinfo *hints)
4575435475dSsthen {
4585435475dSsthen 	size_t i = 0;
4595435475dSsthen 	struct addrinfo ai = *hints;
4605435475dSsthen 	struct ip_address_option *ip;
4618d298c9fSsthen #ifdef HAVE_GETIFADDRS
462308d2509Sflorian 	struct ifaddrs *ifa = NULL;
4638d298c9fSsthen #endif
464308d2509Sflorian 	int bind_device = 0;
4655435475dSsthen 
4665435475dSsthen 	if(!ips) {
467308d2509Sflorian 		figure_default_sockets(
4683f21e8ccSflorian 			udp, tcp, ifs, node, udp_port, tcp_port, hints);
4695435475dSsthen 		return;
4705435475dSsthen 	}
4715435475dSsthen 
4725435475dSsthen 	*ifs = 0;
4735435475dSsthen 	for(ip = ips; ip; ip = ip->next) {
4745435475dSsthen 		(*ifs)++;
475308d2509Sflorian 		bind_device |= (ip->dev != 0);
476308d2509Sflorian 	}
477308d2509Sflorian 
4788d298c9fSsthen #ifdef HAVE_GETIFADDRS
479308d2509Sflorian 	if(bind_device && getifaddrs(&ifa) == -1) {
480308d2509Sflorian 		error("getifaddrs failed: %s", strerror(errno));
4815435475dSsthen 	}
4828d298c9fSsthen #endif
4835435475dSsthen 
4845435475dSsthen 	*udp = xalloc_zero((*ifs + 1) * sizeof(struct nsd_socket));
4855435475dSsthen 	*tcp = xalloc_zero((*ifs + 1) * sizeof(struct nsd_socket));
4865435475dSsthen 	region_add_cleanup(nsd.region, free, *udp);
4875435475dSsthen 	region_add_cleanup(nsd.region, free, *tcp);
4885435475dSsthen 
4895435475dSsthen 	ai.ai_flags |= AI_NUMERICHOST;
4905435475dSsthen 	for(ip = ips, i = 0; ip; ip = ip->next, i++) {
4915435475dSsthen 		ai.ai_socktype = SOCK_DGRAM;
4925435475dSsthen 		setup_socket(&(*udp)[i], ip->address, udp_port, &ai);
493308d2509Sflorian 		figure_socket_servers(&(*udp)[i], ip);
4945435475dSsthen 		ai.ai_socktype = SOCK_STREAM;
4955435475dSsthen 		setup_socket(&(*tcp)[i], ip->address, tcp_port, &ai);
496308d2509Sflorian 		figure_socket_servers(&(*tcp)[i], ip);
497308d2509Sflorian 		if(ip->fib != -1) {
498308d2509Sflorian 			(*udp)[i].fib = ip->fib;
499308d2509Sflorian 			(*tcp)[i].fib = ip->fib;
500308d2509Sflorian 		}
5018d298c9fSsthen #ifdef HAVE_GETIFADDRS
502308d2509Sflorian 		if(ip->dev != 0) {
503308d2509Sflorian 			(*udp)[i].flags |= NSD_BIND_DEVICE;
504308d2509Sflorian 			(*tcp)[i].flags |= NSD_BIND_DEVICE;
505308d2509Sflorian 			if(ifa != NULL && (find_device(&(*udp)[i], ifa) == 0 ||
506308d2509Sflorian 			                   find_device(&(*tcp)[i], ifa) == 0))
507308d2509Sflorian 			{
508308d2509Sflorian 				error("cannot find device for ip-address %s",
509308d2509Sflorian 				      ip->address);
510308d2509Sflorian 			}
511308d2509Sflorian 		}
5128d298c9fSsthen #endif
5135435475dSsthen 	}
5145435475dSsthen 
5155435475dSsthen 	assert(i == *ifs);
516308d2509Sflorian 
5178d298c9fSsthen #ifdef HAVE_GETIFADDRS
518308d2509Sflorian 	if(ifa != NULL) {
519308d2509Sflorian 		freeifaddrs(ifa);
520e3d8a0a5Ssthen 	}
5218d298c9fSsthen #endif
522308d2509Sflorian }
523308d2509Sflorian 
524308d2509Sflorian /* print server affinity for given socket. "*" if socket has no affinity with
525308d2509Sflorian    any specific server, "x-y" if socket has affinity with more than two
526308d2509Sflorian    consecutively numbered servers, "x" if socket has affinity with a specific
527308d2509Sflorian    server number, which is not necessarily just one server. e.g. "1 3" is
528308d2509Sflorian    printed if socket has affinity with servers number one and three, but not
529308d2509Sflorian    server number two. */
530308d2509Sflorian static ssize_t
print_socket_servers(struct nsd_socket * sock,char * buf,size_t bufsz)531308d2509Sflorian print_socket_servers(struct nsd_socket *sock, char *buf, size_t bufsz)
532308d2509Sflorian {
533308d2509Sflorian 	int i, x, y, z, n = (int)(sock->servers->size);
534308d2509Sflorian 	char *sep = "";
535308d2509Sflorian 	size_t off, tot;
536308d2509Sflorian 	ssize_t cnt = 0;
537308d2509Sflorian 
538308d2509Sflorian 	assert(bufsz != 0);
539308d2509Sflorian 
540308d2509Sflorian 	off = tot = 0;
541308d2509Sflorian 	x = y = z = -1;
542308d2509Sflorian 	for (i = 0; i <= n; i++) {
543308d2509Sflorian 		if (i == n || !nsd_bitset_isset(sock->servers, i)) {
544308d2509Sflorian 			cnt = 0;
545308d2509Sflorian 			if (i == n && x == -1) {
546308d2509Sflorian 				assert(y == -1);
547308d2509Sflorian 				assert(z == -1);
548308d2509Sflorian 				cnt = snprintf(buf, bufsz, "-");
549308d2509Sflorian 			} else if (y > z) {
550308d2509Sflorian 				assert(x > z);
551308d2509Sflorian 				if (x == 0 && y == (n - 1)) {
552308d2509Sflorian 					assert(z == -1);
553308d2509Sflorian 					cnt = snprintf(buf+off, bufsz-off,
554308d2509Sflorian 					               "*");
555308d2509Sflorian 				} else if (x == y) {
556308d2509Sflorian 					cnt = snprintf(buf+off, bufsz-off,
557308d2509Sflorian 					               "%s%d", sep, x+1);
558308d2509Sflorian 				} else if (x == (y - 1)) {
559308d2509Sflorian 					cnt = snprintf(buf+off, bufsz-off,
560308d2509Sflorian 					               "%s%d %d", sep, x+1, y+1);
561308d2509Sflorian 				} else {
562308d2509Sflorian 					assert(y > (x + 1));
563308d2509Sflorian 					cnt = snprintf(buf+off, bufsz-off,
564308d2509Sflorian 					               "%s%d-%d", sep, x+1, y+1);
565308d2509Sflorian 				}
566308d2509Sflorian 			}
567308d2509Sflorian 			z = i;
568308d2509Sflorian 			if (cnt > 0) {
569308d2509Sflorian 				tot += (size_t)cnt;
570308d2509Sflorian 				off = (tot < bufsz) ? tot : bufsz - 1;
571308d2509Sflorian 				sep = " ";
572308d2509Sflorian 			} else if (cnt < 0) {
573308d2509Sflorian 				return -1;
574308d2509Sflorian 			}
575308d2509Sflorian 		} else if (x <= z) {
576308d2509Sflorian 			x = y = i;
577308d2509Sflorian 		} else {
578308d2509Sflorian 			assert(x > z);
579308d2509Sflorian 			y = i;
580308d2509Sflorian 		}
581308d2509Sflorian 	}
582308d2509Sflorian 
583308d2509Sflorian 	return tot;
584308d2509Sflorian }
585308d2509Sflorian 
586308d2509Sflorian static void
print_sockets(struct nsd_socket * udp,struct nsd_socket * tcp,size_t ifs)587308d2509Sflorian print_sockets(
588308d2509Sflorian 	struct nsd_socket *udp, struct nsd_socket *tcp, size_t ifs)
589308d2509Sflorian {
590308d2509Sflorian 	char sockbuf[INET6_ADDRSTRLEN + 6 + 1];
591308d2509Sflorian 	char *serverbuf;
592308d2509Sflorian 	size_t i, serverbufsz, servercnt;
593308d2509Sflorian 	const char *fmt = "listen on ip-address %s (%s) with server(s): %s";
594308d2509Sflorian 	struct nsd_bitset *servers;
595308d2509Sflorian 
596308d2509Sflorian 	if(ifs == 0) {
597308d2509Sflorian 		return;
598308d2509Sflorian 	}
599308d2509Sflorian 
600308d2509Sflorian 	assert(udp != NULL);
601308d2509Sflorian 	assert(tcp != NULL);
602308d2509Sflorian 
603308d2509Sflorian 	servercnt = udp[0].servers->size;
604308d2509Sflorian 	serverbufsz = (((servercnt / 10) * servercnt) + servercnt) + 1;
605308d2509Sflorian 	serverbuf = xalloc(serverbufsz);
606308d2509Sflorian 
607308d2509Sflorian 	/* warn user of unused servers */
608308d2509Sflorian 	servers = xalloc(nsd_bitset_size(servercnt));
609308d2509Sflorian 	nsd_bitset_init(servers, (size_t)servercnt);
610308d2509Sflorian 
611308d2509Sflorian 	for(i = 0; i < ifs; i++) {
612308d2509Sflorian 		assert(udp[i].servers->size == servercnt);
613063644e9Sflorian 		addrport2str((void*)&udp[i].addr.ai_addr, sockbuf, sizeof(sockbuf));
614308d2509Sflorian 		print_socket_servers(&udp[i], serverbuf, serverbufsz);
615308d2509Sflorian 		nsd_bitset_or(servers, servers, udp[i].servers);
616ac5517e4Sflorian 		VERBOSITY(3, (LOG_NOTICE, fmt, sockbuf, "udp", serverbuf));
617308d2509Sflorian 		assert(tcp[i].servers->size == servercnt);
618063644e9Sflorian 		addrport2str((void*)&tcp[i].addr.ai_addr, sockbuf, sizeof(sockbuf));
619308d2509Sflorian 		print_socket_servers(&tcp[i], serverbuf, serverbufsz);
620308d2509Sflorian 		nsd_bitset_or(servers, servers, tcp[i].servers);
621ac5517e4Sflorian 		VERBOSITY(3, (LOG_NOTICE, fmt, sockbuf, "tcp", serverbuf));
622308d2509Sflorian 	}
623308d2509Sflorian 
624308d2509Sflorian 
625308d2509Sflorian 	/* warn user of unused servers */
626308d2509Sflorian 	for(i = 0; i < servercnt; i++) {
627308d2509Sflorian 		if(!nsd_bitset_isset(servers, i)) {
628308d2509Sflorian 			log_msg(LOG_WARNING, "server %zu will not listen on "
629308d2509Sflorian 			                     "any specified ip-address", i+1);
630308d2509Sflorian 		}
631308d2509Sflorian 	}
632308d2509Sflorian 	free(serverbuf);
633308d2509Sflorian 	free(servers);
634308d2509Sflorian }
635308d2509Sflorian 
636308d2509Sflorian #ifdef HAVE_CPUSET_T
free_cpuset(void * ptr)637308d2509Sflorian static void free_cpuset(void *ptr)
638308d2509Sflorian {
639308d2509Sflorian 	cpuset_t *set = (cpuset_t *)ptr;
640308d2509Sflorian 	cpuset_destroy(set);
641308d2509Sflorian }
642308d2509Sflorian #endif
64362ac0c33Sjakob 
64462ac0c33Sjakob /*
64562ac0c33Sjakob  * Fetch the nsd parent process id from the nsd pidfile
64662ac0c33Sjakob  *
64762ac0c33Sjakob  */
64862ac0c33Sjakob pid_t
readpid(const char * file)64962ac0c33Sjakob readpid(const char *file)
65062ac0c33Sjakob {
65162ac0c33Sjakob 	int fd;
65262ac0c33Sjakob 	pid_t pid;
65362ac0c33Sjakob 	char pidbuf[16];
65462ac0c33Sjakob 	char *t;
65562ac0c33Sjakob 	int l;
65662ac0c33Sjakob 
65762ac0c33Sjakob 	if ((fd = open(file, O_RDONLY)) == -1) {
65862ac0c33Sjakob 		return -1;
65962ac0c33Sjakob 	}
66062ac0c33Sjakob 
66162ac0c33Sjakob 	if (((l = read(fd, pidbuf, sizeof(pidbuf)))) == -1) {
66262ac0c33Sjakob 		close(fd);
66362ac0c33Sjakob 		return -1;
66462ac0c33Sjakob 	}
66562ac0c33Sjakob 
66662ac0c33Sjakob 	close(fd);
66762ac0c33Sjakob 
66862ac0c33Sjakob 	/* Empty pidfile means no pidfile... */
66962ac0c33Sjakob 	if (l == 0) {
67062ac0c33Sjakob 		errno = ENOENT;
67162ac0c33Sjakob 		return -1;
67262ac0c33Sjakob 	}
67362ac0c33Sjakob 
67472f0a8e9Ssthen 	pid = (pid_t) strtol(pidbuf, &t, 10);
67562ac0c33Sjakob 
67662ac0c33Sjakob 	if (*t && *t != '\n') {
67762ac0c33Sjakob 		return -1;
67862ac0c33Sjakob 	}
67962ac0c33Sjakob 	return pid;
68062ac0c33Sjakob }
68162ac0c33Sjakob 
68262ac0c33Sjakob /*
68362ac0c33Sjakob  * Store the nsd parent process id in the nsd pidfile
68462ac0c33Sjakob  *
68562ac0c33Sjakob  */
68662ac0c33Sjakob int
writepid(struct nsd * nsd)68762ac0c33Sjakob writepid(struct nsd *nsd)
68862ac0c33Sjakob {
689977db6e5Sflorian 	int fd;
69062ac0c33Sjakob 	char pidbuf[32];
691977db6e5Sflorian 	size_t count = 0;
6925435475dSsthen 	if(!nsd->pidfile || !nsd->pidfile[0])
6935435475dSsthen 		return 0;
69462ac0c33Sjakob 
69562ac0c33Sjakob 	snprintf(pidbuf, sizeof(pidbuf), "%lu\n", (unsigned long) nsd->pid);
69662ac0c33Sjakob 
697977db6e5Sflorian 	if((fd = open(nsd->pidfile, O_WRONLY | O_CREAT | O_TRUNC
698977db6e5Sflorian #ifdef O_NOFOLLOW
699977db6e5Sflorian 		| O_NOFOLLOW
700977db6e5Sflorian #endif
701977db6e5Sflorian 		, 0644)) == -1) {
70262ac0c33Sjakob 		log_msg(LOG_ERR, "cannot open pidfile %s: %s",
70362ac0c33Sjakob 			nsd->pidfile, strerror(errno));
70462ac0c33Sjakob 		return -1;
70562ac0c33Sjakob 	}
70662ac0c33Sjakob 
707977db6e5Sflorian 	while(count < strlen(pidbuf)) {
708977db6e5Sflorian 		ssize_t r = write(fd, pidbuf+count, strlen(pidbuf)-count);
709977db6e5Sflorian 		if(r == -1) {
710977db6e5Sflorian 			if(errno == EAGAIN || errno == EINTR)
711977db6e5Sflorian 				continue;
71262ac0c33Sjakob 			log_msg(LOG_ERR, "cannot write pidfile %s: %s",
71362ac0c33Sjakob 				nsd->pidfile, strerror(errno));
714977db6e5Sflorian 			close(fd);
715977db6e5Sflorian 			return -1;
716977db6e5Sflorian 		} else if(r == 0) {
717977db6e5Sflorian 			log_msg(LOG_ERR, "cannot write any bytes to "
718977db6e5Sflorian 				"pidfile %s: write returns 0 bytes written",
719977db6e5Sflorian 				nsd->pidfile);
720977db6e5Sflorian 			close(fd);
72162ac0c33Sjakob 			return -1;
72262ac0c33Sjakob 		}
723977db6e5Sflorian 		count += r;
724977db6e5Sflorian 	}
725977db6e5Sflorian 	close(fd);
72662ac0c33Sjakob 
72762ac0c33Sjakob 	if (chown(nsd->pidfile, nsd->uid, nsd->gid) == -1) {
72862ac0c33Sjakob 		log_msg(LOG_ERR, "cannot chown %u.%u %s: %s",
72962ac0c33Sjakob 			(unsigned) nsd->uid, (unsigned) nsd->gid,
73062ac0c33Sjakob 			nsd->pidfile, strerror(errno));
73162ac0c33Sjakob 		return -1;
73262ac0c33Sjakob 	}
73362ac0c33Sjakob 
73462ac0c33Sjakob 	return 0;
73562ac0c33Sjakob }
73662ac0c33Sjakob 
73762ac0c33Sjakob void
unlinkpid(const char * file)73862ac0c33Sjakob unlinkpid(const char* file)
73962ac0c33Sjakob {
74061eb1089Ssthen 	int fd = -1;
74161eb1089Ssthen 
7425435475dSsthen 	if (file && file[0]) {
74361eb1089Ssthen 		/* truncate pidfile */
74461eb1089Ssthen 		fd = open(file, O_WRONLY | O_TRUNC, 0644);
7459c620270Ssthen 		if (fd == -1) {
7469c620270Ssthen 			/* Truncate the pid file.  */
7479c620270Ssthen 			log_msg(LOG_ERR, "can not truncate the pid file %s: %s", file, strerror(errno));
7483b24e79eSsthen 		} else {
74961eb1089Ssthen 			close(fd);
7503b24e79eSsthen 		}
7519c620270Ssthen 
75261eb1089Ssthen 		/* unlink pidfile */
753ac5517e4Sflorian 		if (unlink(file) == -1) {
754ac5517e4Sflorian 			/* this unlink may not work if the pidfile is located
755ac5517e4Sflorian 			 * outside of the chroot/workdir or we no longer
756ac5517e4Sflorian 			 * have permissions */
757ac5517e4Sflorian 			VERBOSITY(3, (LOG_WARNING,
758ac5517e4Sflorian 				"failed to unlink pidfile %s: %s",
759ac5517e4Sflorian 				file, strerror(errno)));
760ac5517e4Sflorian 		}
76162ac0c33Sjakob 	}
76261eb1089Ssthen }
76362ac0c33Sjakob 
76462ac0c33Sjakob /*
76562ac0c33Sjakob  * Incoming signals, set appropriate actions.
76662ac0c33Sjakob  *
76762ac0c33Sjakob  */
76862ac0c33Sjakob void
sig_handler(int sig)76962ac0c33Sjakob sig_handler(int sig)
77062ac0c33Sjakob {
77162ac0c33Sjakob 	/* To avoid race cond. We really don't want to use log_msg() in this handler */
77262ac0c33Sjakob 
77362ac0c33Sjakob 	/* Are we a child server? */
77462ac0c33Sjakob 	if (nsd.server_kind != NSD_SERVER_MAIN) {
77562ac0c33Sjakob 		switch (sig) {
77662ac0c33Sjakob 		case SIGCHLD:
77762ac0c33Sjakob 			nsd.signal_hint_child = 1;
77862ac0c33Sjakob 			break;
77962ac0c33Sjakob 		case SIGALRM:
78062ac0c33Sjakob 			break;
78162ac0c33Sjakob 		case SIGINT:
78262ac0c33Sjakob 		case SIGTERM:
78362ac0c33Sjakob 			nsd.signal_hint_quit = 1;
78462ac0c33Sjakob 			break;
78562ac0c33Sjakob 		case SIGILL:
78662ac0c33Sjakob 		case SIGUSR1:	/* Dump stats on SIGUSR1.  */
78762ac0c33Sjakob 			nsd.signal_hint_statsusr = 1;
78862ac0c33Sjakob 			break;
78962ac0c33Sjakob 		default:
79062ac0c33Sjakob 			break;
79162ac0c33Sjakob 		}
79262ac0c33Sjakob 		return;
79362ac0c33Sjakob 	}
79462ac0c33Sjakob 
79562ac0c33Sjakob 	/* We are the main process */
79662ac0c33Sjakob 	switch (sig) {
79762ac0c33Sjakob 	case SIGCHLD:
79862ac0c33Sjakob 		nsd.signal_hint_child = 1;
79962ac0c33Sjakob 		return;
80062ac0c33Sjakob 	case SIGHUP:
801dd5b221eSsthen 		nsd.signal_hint_reload_hup = 1;
80262ac0c33Sjakob 		return;
80362ac0c33Sjakob 	case SIGALRM:
80462ac0c33Sjakob 		nsd.signal_hint_stats = 1;
80562ac0c33Sjakob 		break;
80662ac0c33Sjakob 	case SIGILL:
80762ac0c33Sjakob 		/*
80862ac0c33Sjakob 		 * For backwards compatibility with BIND 8 and older
80962ac0c33Sjakob 		 * versions of NSD.
81062ac0c33Sjakob 		 */
81162ac0c33Sjakob 		nsd.signal_hint_statsusr = 1;
81262ac0c33Sjakob 		break;
81362ac0c33Sjakob 	case SIGUSR1:
81462ac0c33Sjakob 		/* Dump statistics.  */
81562ac0c33Sjakob 		nsd.signal_hint_statsusr = 1;
81662ac0c33Sjakob 		break;
81762ac0c33Sjakob 	case SIGINT:
81862ac0c33Sjakob 	case SIGTERM:
81962ac0c33Sjakob 	default:
82062ac0c33Sjakob 		nsd.signal_hint_shutdown = 1;
82162ac0c33Sjakob 		break;
82262ac0c33Sjakob 	}
82362ac0c33Sjakob }
82462ac0c33Sjakob 
82562ac0c33Sjakob /*
82662ac0c33Sjakob  * Statistic output...
82762ac0c33Sjakob  *
82862ac0c33Sjakob  */
82962ac0c33Sjakob #ifdef BIND8_STATS
83062ac0c33Sjakob void
bind8_stats(struct nsd * nsd)83162ac0c33Sjakob bind8_stats (struct nsd *nsd)
83262ac0c33Sjakob {
83362ac0c33Sjakob 	char buf[MAXSYSLOGMSGLEN];
83462ac0c33Sjakob 	char *msg, *t;
83562ac0c33Sjakob 	int i, len;
836b71395eaSflorian 	struct nsdst st;
83762ac0c33Sjakob 
83862ac0c33Sjakob 	/* Current time... */
83962ac0c33Sjakob 	time_t now;
840b71395eaSflorian 	if(!nsd->st_period)
8419c620270Ssthen 		return;
84262ac0c33Sjakob 	time(&now);
84362ac0c33Sjakob 
844b71395eaSflorian 	memcpy(&st, nsd->st, sizeof(st));
845b71395eaSflorian 	stats_subtract(&st, &nsd->stat_proc);
846b71395eaSflorian 
84762ac0c33Sjakob 	/* NSTATS */
848e3932aeeSsthen 	t = msg = buf + snprintf(buf, MAXSYSLOGMSGLEN, "NSTATS %lld %lu",
849b71395eaSflorian 				 (long long) now, (unsigned long) st.boot);
85062ac0c33Sjakob 	for (i = 0; i <= 255; i++) {
85162ac0c33Sjakob 		/* How much space left? */
85262ac0c33Sjakob 		if ((len = buf + MAXSYSLOGMSGLEN - t) < 32) {
85362ac0c33Sjakob 			log_msg(LOG_INFO, "%s", buf);
85462ac0c33Sjakob 			t = msg;
85562ac0c33Sjakob 			len = buf + MAXSYSLOGMSGLEN - t;
85662ac0c33Sjakob 		}
85762ac0c33Sjakob 
858b71395eaSflorian 		if (st.qtype[i] != 0) {
859b71395eaSflorian 			t += snprintf(t, len, " %s=%lu", rrtype_to_string(i), st.qtype[i]);
86062ac0c33Sjakob 		}
86162ac0c33Sjakob 	}
86262ac0c33Sjakob 	if (t > msg)
86362ac0c33Sjakob 		log_msg(LOG_INFO, "%s", buf);
86462ac0c33Sjakob 
86562ac0c33Sjakob 	/* XSTATS */
86662ac0c33Sjakob 	/* Only print it if we're in the main daemon or have anything to report... */
86762ac0c33Sjakob 	if (nsd->server_kind == NSD_SERVER_MAIN
868b71395eaSflorian 	    || st.dropped || st.raxfr || st.rixfr || (st.qudp + st.qudp6 - st.dropped)
869b71395eaSflorian 	    || st.txerr || st.opcode[OPCODE_QUERY] || st.opcode[OPCODE_IQUERY]
870b71395eaSflorian 	    || st.wrongzone || st.ctcp + st.ctcp6 || st.rcode[RCODE_SERVFAIL]
871b71395eaSflorian 	    || st.rcode[RCODE_FORMAT] || st.nona || st.rcode[RCODE_NXDOMAIN]
872b71395eaSflorian 	    || st.opcode[OPCODE_UPDATE]) {
87362ac0c33Sjakob 
874e3932aeeSsthen 		log_msg(LOG_INFO, "XSTATS %lld %lu"
8754564029fSflorian 			" RR=%lu RNXD=%lu RFwdR=%lu RDupR=%lu RFail=%lu RFErr=%lu RErr=%lu RAXFR=%lu RIXFR=%lu"
87662ac0c33Sjakob 			" RLame=%lu ROpts=%lu SSysQ=%lu SAns=%lu SFwdQ=%lu SDupQ=%lu SErr=%lu RQ=%lu"
87762ac0c33Sjakob 			" RIQ=%lu RFwdQ=%lu RDupQ=%lu RTCP=%lu SFwdR=%lu SFail=%lu SFErr=%lu SNaAns=%lu"
87862ac0c33Sjakob 			" SNXD=%lu RUQ=%lu RURQ=%lu RUXFR=%lu RUUpd=%lu",
879b71395eaSflorian 			(long long) now, (unsigned long) st.boot,
880b71395eaSflorian 			st.dropped, (unsigned long)0, (unsigned long)0, (unsigned long)0, (unsigned long)0,
881b71395eaSflorian 			(unsigned long)0, (unsigned long)0, st.raxfr, st.rixfr, (unsigned long)0, (unsigned long)0,
882b71395eaSflorian 			(unsigned long)0, st.qudp + st.qudp6 - st.dropped, (unsigned long)0,
883b71395eaSflorian 			(unsigned long)0, st.txerr,
884b71395eaSflorian 			st.opcode[OPCODE_QUERY], st.opcode[OPCODE_IQUERY], st.wrongzone,
885b71395eaSflorian 			(unsigned long)0, st.ctcp + st.ctcp6,
886b71395eaSflorian 			(unsigned long)0, st.rcode[RCODE_SERVFAIL], st.rcode[RCODE_FORMAT],
887b71395eaSflorian 			st.nona, st.rcode[RCODE_NXDOMAIN],
888b71395eaSflorian 			(unsigned long)0, (unsigned long)0, (unsigned long)0, st.opcode[OPCODE_UPDATE]);
88962ac0c33Sjakob 	}
89062ac0c33Sjakob 
89162ac0c33Sjakob }
89262ac0c33Sjakob #endif /* BIND8_STATS */
89362ac0c33Sjakob 
894063644e9Sflorian static
cookie_secret_file_read(nsd_type * nsd)895063644e9Sflorian int cookie_secret_file_read(nsd_type* nsd) {
896063644e9Sflorian 	char secret[NSD_COOKIE_SECRET_SIZE * 2 + 2/*'\n' and '\0'*/];
897063644e9Sflorian 	char const* file = nsd->options->cookie_secret_file;
898063644e9Sflorian 	FILE* f;
899063644e9Sflorian 	int corrupt = 0;
900063644e9Sflorian 	size_t count;
901063644e9Sflorian 
902063644e9Sflorian 	assert( nsd->options->cookie_secret_file != NULL );
903063644e9Sflorian 	f = fopen(file, "r");
904063644e9Sflorian 	/* a non-existing cookie file is not an error */
905063644e9Sflorian 	if( f == NULL ) { return errno != EPERM; }
906063644e9Sflorian 	/* cookie secret file exists and is readable */
907063644e9Sflorian 	nsd->cookie_count = 0;
908063644e9Sflorian 	for( count = 0; count < NSD_COOKIE_HISTORY_SIZE; count++ ) {
909063644e9Sflorian 		size_t secret_len = 0;
910063644e9Sflorian 		ssize_t decoded_len = 0;
911063644e9Sflorian 		if( fgets(secret, sizeof(secret), f) == NULL ) { break; }
912063644e9Sflorian 		secret_len = strlen(secret);
913063644e9Sflorian 		if( secret_len == 0 ) { break; }
914063644e9Sflorian 		assert( secret_len <= sizeof(secret) );
915063644e9Sflorian 		secret_len = secret[secret_len - 1] == '\n' ? secret_len - 1 : secret_len;
916063644e9Sflorian 		if( secret_len != NSD_COOKIE_SECRET_SIZE * 2 ) { corrupt++; break; }
917063644e9Sflorian 		/* needed for `hex_pton`; stripping potential `\n` */
918063644e9Sflorian 		secret[secret_len] = '\0';
919063644e9Sflorian 		decoded_len = hex_pton(secret, nsd->cookie_secrets[count].cookie_secret,
920063644e9Sflorian 		                       NSD_COOKIE_SECRET_SIZE);
921063644e9Sflorian 		if( decoded_len != NSD_COOKIE_SECRET_SIZE ) { corrupt++; break; }
922063644e9Sflorian 		nsd->cookie_count++;
923063644e9Sflorian 	}
924063644e9Sflorian 	fclose(f);
925063644e9Sflorian 	return corrupt == 0;
926063644e9Sflorian }
927063644e9Sflorian 
92862ac0c33Sjakob extern char *optarg;
92962ac0c33Sjakob extern int optind;
93062ac0c33Sjakob 
93162ac0c33Sjakob int
main(int argc,char * argv[])93262ac0c33Sjakob main(int argc, char *argv[])
93362ac0c33Sjakob {
93462ac0c33Sjakob 	/* Scratch variables... */
93562ac0c33Sjakob 	int c;
93662ac0c33Sjakob 	pid_t	oldpid;
93762ac0c33Sjakob 	size_t i;
93862ac0c33Sjakob 	struct sigaction action;
93962ac0c33Sjakob #ifdef HAVE_GETPWNAM
94075343be4Ssthen 	struct passwd *pwd = NULL;
94162ac0c33Sjakob #endif /* HAVE_GETPWNAM */
94262ac0c33Sjakob 
9435435475dSsthen 	struct ip_address_option *ip;
9445435475dSsthen 	struct addrinfo hints;
94562ac0c33Sjakob 	const char *udp_port = 0;
94662ac0c33Sjakob 	const char *tcp_port = 0;
9473f21e8ccSflorian 	const char *verify_port = 0;
94862ac0c33Sjakob 
94962ac0c33Sjakob 	const char *configfile = CONFIGFILE;
95062ac0c33Sjakob 
95162ac0c33Sjakob 	char* argv0 = (argv0 = strrchr(argv[0], '/')) ? argv0 + 1 : argv[0];
95262ac0c33Sjakob 
9539c620270Ssthen 	log_init(argv0);
95462ac0c33Sjakob 
95562ac0c33Sjakob 	/* Initialize the server handler... */
95662ac0c33Sjakob 	memset(&nsd, 0, sizeof(struct nsd));
95762ac0c33Sjakob 	nsd.region      = region_create(xalloc, free);
95862ac0c33Sjakob 	nsd.pidfile	= 0;
95962ac0c33Sjakob 	nsd.server_kind = NSD_SERVER_MAIN;
9605435475dSsthen 	memset(&hints, 0, sizeof(hints));
9615435475dSsthen 	hints.ai_family = DEFAULT_AI_FAMILY;
9625435475dSsthen 	hints.ai_flags = AI_PASSIVE;
96362ac0c33Sjakob 	nsd.identity	= 0;
96462ac0c33Sjakob 	nsd.version	= VERSION;
96562ac0c33Sjakob 	nsd.username	= 0;
96662ac0c33Sjakob 	nsd.chrootdir	= 0;
96762ac0c33Sjakob 	nsd.nsid 	= NULL;
96862ac0c33Sjakob 	nsd.nsid_len 	= 0;
969063644e9Sflorian 	nsd.cookie_count = 0;
97062ac0c33Sjakob 
97162ac0c33Sjakob 	nsd.child_count = 0;
97262ac0c33Sjakob 	nsd.maximum_tcp_count = 0;
97362ac0c33Sjakob 	nsd.current_tcp_count = 0;
97462ac0c33Sjakob 	nsd.file_rotation_ok = 0;
97562ac0c33Sjakob 
976063644e9Sflorian 	nsd.do_answer_cookie = 1;
977063644e9Sflorian 
97862ac0c33Sjakob 	/* Set up our default identity to gethostname(2) */
97962ac0c33Sjakob 	if (gethostname(hostname, MAXHOSTNAMELEN) == 0) {
98062ac0c33Sjakob 		nsd.identity = hostname;
98162ac0c33Sjakob 	} else {
98262ac0c33Sjakob 		log_msg(LOG_ERR,
98362ac0c33Sjakob 			"failed to get the host name: %s - using default identity",
98462ac0c33Sjakob 			strerror(errno));
98562ac0c33Sjakob 		nsd.identity = IDENTITY;
98662ac0c33Sjakob 	}
98762ac0c33Sjakob 
9885435475dSsthen 	/* Create region where options will be stored and set defaults */
9895435475dSsthen 	nsd.options = nsd_options_create(region_create_custom(xalloc, free,
9905435475dSsthen 		DEFAULT_CHUNK_SIZE, DEFAULT_LARGE_OBJECT_SIZE,
9915435475dSsthen 		DEFAULT_INITIAL_CLEANUP_SIZE, 1));
9925435475dSsthen 
99362ac0c33Sjakob 	/* Parse the command line... */
99462ac0c33Sjakob 	while ((c = getopt(argc, argv, "46a:c:df:hi:I:l:N:n:P:p:s:u:t:X:V:v"
99562ac0c33Sjakob #ifndef NDEBUG /* <mattthijs> only when configured with --enable-checking */
99662ac0c33Sjakob 		"F:L:"
99762ac0c33Sjakob #endif /* NDEBUG */
99862ac0c33Sjakob 		)) != -1) {
99962ac0c33Sjakob 		switch (c) {
100062ac0c33Sjakob 		case '4':
10015435475dSsthen 			hints.ai_family = AF_INET;
100262ac0c33Sjakob 			break;
100362ac0c33Sjakob 		case '6':
100462ac0c33Sjakob #ifdef INET6
10055435475dSsthen 			hints.ai_family = AF_INET6;
100662ac0c33Sjakob #else /* !INET6 */
100762ac0c33Sjakob 			error("IPv6 support not enabled.");
100862ac0c33Sjakob #endif /* INET6 */
100962ac0c33Sjakob 			break;
101062ac0c33Sjakob 		case 'a':
10115435475dSsthen 			ip = region_alloc_zero(
10125435475dSsthen 				nsd.options->region, sizeof(*ip));
10135435475dSsthen 			ip->address = region_strdup(
10145435475dSsthen 				nsd.options->region, optarg);
10155435475dSsthen 			ip->next = nsd.options->ip_addresses;
10165435475dSsthen 			nsd.options->ip_addresses = ip;
101762ac0c33Sjakob 			break;
101862ac0c33Sjakob 		case 'c':
101962ac0c33Sjakob 			configfile = optarg;
102062ac0c33Sjakob 			break;
102162ac0c33Sjakob 		case 'd':
102262ac0c33Sjakob 			nsd.debug = 1;
102362ac0c33Sjakob 			break;
102462ac0c33Sjakob 		case 'f':
102562ac0c33Sjakob 			break;
102662ac0c33Sjakob 		case 'h':
102762ac0c33Sjakob 			usage();
102862ac0c33Sjakob 			exit(0);
102962ac0c33Sjakob 		case 'i':
103062ac0c33Sjakob 			nsd.identity = optarg;
103162ac0c33Sjakob 			break;
103262ac0c33Sjakob 		case 'I':
103362ac0c33Sjakob 			if (nsd.nsid_len != 0) {
103462ac0c33Sjakob 				/* can only be given once */
103562ac0c33Sjakob 				break;
103662ac0c33Sjakob 			}
1037a302926fSbrad 			if (strncasecmp(optarg, "ascii_", 6) == 0) {
1038a302926fSbrad 				nsd.nsid = xalloc(strlen(optarg+6));
1039a302926fSbrad 				nsd.nsid_len = strlen(optarg+6);
1040a302926fSbrad 				memmove(nsd.nsid, optarg+6, nsd.nsid_len);
1041a302926fSbrad 			} else {
104262ac0c33Sjakob 				if (strlen(optarg) % 2 != 0) {
104362ac0c33Sjakob 					error("the NSID must be a hex string of an even length.");
104462ac0c33Sjakob 				}
104562ac0c33Sjakob 				nsd.nsid = xalloc(strlen(optarg) / 2);
104662ac0c33Sjakob 				nsd.nsid_len = strlen(optarg) / 2;
104762ac0c33Sjakob 				if (hex_pton(optarg, nsd.nsid, nsd.nsid_len) == -1) {
104862ac0c33Sjakob 					error("hex string cannot be parsed '%s' in NSID.", optarg);
104962ac0c33Sjakob 				}
1050a302926fSbrad 			}
105162ac0c33Sjakob 			break;
105262ac0c33Sjakob 		case 'l':
105362ac0c33Sjakob 			nsd.log_filename = optarg;
105462ac0c33Sjakob 			break;
105562ac0c33Sjakob 		case 'N':
105662ac0c33Sjakob 			i = atoi(optarg);
105762ac0c33Sjakob 			if (i <= 0) {
10584ab91c82Sjakob 				error("number of child servers must be greater than zero.");
105962ac0c33Sjakob 			} else {
106062ac0c33Sjakob 				nsd.child_count = i;
106162ac0c33Sjakob 			}
106262ac0c33Sjakob 			break;
106362ac0c33Sjakob 		case 'n':
106462ac0c33Sjakob 			i = atoi(optarg);
106562ac0c33Sjakob 			if (i <= 0) {
106662ac0c33Sjakob 				error("number of concurrent TCP connections must greater than zero.");
106762ac0c33Sjakob 			} else {
106862ac0c33Sjakob 				nsd.maximum_tcp_count = i;
106962ac0c33Sjakob 			}
107062ac0c33Sjakob 			break;
107162ac0c33Sjakob 		case 'P':
107262ac0c33Sjakob 			nsd.pidfile = optarg;
107362ac0c33Sjakob 			break;
107462ac0c33Sjakob 		case 'p':
107562ac0c33Sjakob 			if (atoi(optarg) == 0) {
107662ac0c33Sjakob 				error("port argument must be numeric.");
107762ac0c33Sjakob 			}
107862ac0c33Sjakob 			tcp_port = optarg;
107962ac0c33Sjakob 			udp_port = optarg;
108062ac0c33Sjakob 			break;
108162ac0c33Sjakob 		case 's':
108262ac0c33Sjakob #ifdef BIND8_STATS
1083b71395eaSflorian 			nsd.st_period = atoi(optarg);
108462ac0c33Sjakob #else /* !BIND8_STATS */
108562ac0c33Sjakob 			error("BIND 8 statistics not enabled.");
108662ac0c33Sjakob #endif /* BIND8_STATS */
108762ac0c33Sjakob 			break;
108862ac0c33Sjakob 		case 't':
108962ac0c33Sjakob #ifdef HAVE_CHROOT
109062ac0c33Sjakob 			nsd.chrootdir = optarg;
109162ac0c33Sjakob #else /* !HAVE_CHROOT */
109262ac0c33Sjakob 			error("chroot not supported on this platform.");
109362ac0c33Sjakob #endif /* HAVE_CHROOT */
109462ac0c33Sjakob 			break;
109562ac0c33Sjakob 		case 'u':
109662ac0c33Sjakob 			nsd.username = optarg;
109762ac0c33Sjakob 			break;
109862ac0c33Sjakob 		case 'V':
109962ac0c33Sjakob 			verbosity = atoi(optarg);
110062ac0c33Sjakob 			break;
110162ac0c33Sjakob 		case 'v':
110262ac0c33Sjakob 			version();
110362ac0c33Sjakob 			/* version exits */
1104ee5153b7Sflorian 			break;
110562ac0c33Sjakob #ifndef NDEBUG
110662ac0c33Sjakob 		case 'F':
110762ac0c33Sjakob 			sscanf(optarg, "%x", &nsd_debug_facilities);
110862ac0c33Sjakob 			break;
110962ac0c33Sjakob 		case 'L':
111062ac0c33Sjakob 			sscanf(optarg, "%d", &nsd_debug_level);
111162ac0c33Sjakob 			break;
111262ac0c33Sjakob #endif /* NDEBUG */
111362ac0c33Sjakob 		case '?':
111462ac0c33Sjakob 		default:
111562ac0c33Sjakob 			usage();
111662ac0c33Sjakob 			exit(1);
111762ac0c33Sjakob 		}
111862ac0c33Sjakob 	}
111962ac0c33Sjakob 	argc -= optind;
1120b90bb40eSsthen 	/* argv += optind; */
112162ac0c33Sjakob 
112262ac0c33Sjakob 	/* Commandline parse error */
112362ac0c33Sjakob 	if (argc != 0) {
112462ac0c33Sjakob 		usage();
112562ac0c33Sjakob 		exit(1);
112662ac0c33Sjakob 	}
112762ac0c33Sjakob 
112862ac0c33Sjakob 	if (strlen(nsd.identity) > UCHAR_MAX) {
112962ac0c33Sjakob 		error("server identity too long (%u characters)",
113062ac0c33Sjakob 		      (unsigned) strlen(nsd.identity));
113162ac0c33Sjakob 	}
1132dd5b221eSsthen 	if(!tsig_init(nsd.region))
1133dd5b221eSsthen 		error("init tsig failed");
1134b71395eaSflorian 	pp_init(&write_uint16, &write_uint32);
113562ac0c33Sjakob 
113662ac0c33Sjakob 	/* Read options */
1137*bf87c3c0Sflorian 	if(!parse_options_file(nsd.options, configfile, NULL, NULL, NULL)) {
113862ac0c33Sjakob 		error("could not read config: %s\n", configfile);
113962ac0c33Sjakob 	}
1140dd5b221eSsthen 	if(!parse_zone_list_file(nsd.options)) {
1141dd5b221eSsthen 		error("could not read zonelist file %s\n",
1142dd5b221eSsthen 			nsd.options->zonelistfile);
1143dd5b221eSsthen 	}
1144dd5b221eSsthen 	if(nsd.options->do_ip4 && !nsd.options->do_ip6) {
11455435475dSsthen 		hints.ai_family = AF_INET;
114662ac0c33Sjakob 	}
114762ac0c33Sjakob #ifdef INET6
1148dd5b221eSsthen 	if(nsd.options->do_ip6 && !nsd.options->do_ip4) {
11495435475dSsthen 		hints.ai_family = AF_INET6;
115062ac0c33Sjakob 	}
115162ac0c33Sjakob #endif /* INET6 */
115262ac0c33Sjakob 	if (verbosity == 0)
115362ac0c33Sjakob 		verbosity = nsd.options->verbosity;
115462ac0c33Sjakob #ifndef NDEBUG
115562ac0c33Sjakob 	if (nsd_debug_level > 0 && verbosity == 0)
115662ac0c33Sjakob 		verbosity = nsd_debug_level;
115762ac0c33Sjakob #endif /* NDEBUG */
115862ac0c33Sjakob 	if(nsd.options->debug_mode) nsd.debug=1;
115962ac0c33Sjakob 	if(!nsd.pidfile)
116062ac0c33Sjakob 	{
116162ac0c33Sjakob 		if(nsd.options->pidfile)
116262ac0c33Sjakob 			nsd.pidfile = nsd.options->pidfile;
116362ac0c33Sjakob 		else
116462ac0c33Sjakob 			nsd.pidfile = PIDFILE;
116562ac0c33Sjakob 	}
116662ac0c33Sjakob 	if(strcmp(nsd.identity, hostname)==0 || strcmp(nsd.identity,IDENTITY)==0)
116762ac0c33Sjakob 	{
116862ac0c33Sjakob 		if(nsd.options->identity)
116962ac0c33Sjakob 			nsd.identity = nsd.options->identity;
117062ac0c33Sjakob 	}
11713126abd5Ssthen 	if(nsd.options->version) {
11723126abd5Ssthen 		nsd.version = nsd.options->version;
11733126abd5Ssthen 	}
117462ac0c33Sjakob 	if (nsd.options->logfile && !nsd.log_filename) {
117562ac0c33Sjakob 		nsd.log_filename = nsd.options->logfile;
117662ac0c33Sjakob 	}
117762ac0c33Sjakob 	if(nsd.child_count == 0) {
117862ac0c33Sjakob 		nsd.child_count = nsd.options->server_count;
117962ac0c33Sjakob 	}
1180308d2509Sflorian 
1181e3d8a0a5Ssthen #ifdef SO_REUSEPORT
1182e3d8a0a5Ssthen 	if(nsd.options->reuseport && nsd.child_count > 1) {
1183e3d8a0a5Ssthen 		nsd.reuseport = nsd.child_count;
1184e3d8a0a5Ssthen 	}
1185e3d8a0a5Ssthen #endif /* SO_REUSEPORT */
118662ac0c33Sjakob 	if(nsd.maximum_tcp_count == 0) {
118762ac0c33Sjakob 		nsd.maximum_tcp_count = nsd.options->tcp_count;
118862ac0c33Sjakob 	}
118962ac0c33Sjakob 	nsd.tcp_timeout = nsd.options->tcp_timeout;
119062ac0c33Sjakob 	nsd.tcp_query_count = nsd.options->tcp_query_count;
1191275a8d89Sflorian 	nsd.tcp_mss = nsd.options->tcp_mss;
1192275a8d89Sflorian 	nsd.outgoing_tcp_mss = nsd.options->outgoing_tcp_mss;
119362ac0c33Sjakob 	nsd.ipv4_edns_size = nsd.options->ipv4_edns_size;
119462ac0c33Sjakob 	nsd.ipv6_edns_size = nsd.options->ipv6_edns_size;
1195eab1363eSsthen #ifdef HAVE_SSL
1196eab1363eSsthen 	nsd.tls_ctx = NULL;
1197eab1363eSsthen #endif
119862ac0c33Sjakob 
119962ac0c33Sjakob 	if(udp_port == 0)
120062ac0c33Sjakob 	{
120162ac0c33Sjakob 		if(nsd.options->port != 0) {
120262ac0c33Sjakob 			udp_port = nsd.options->port;
120362ac0c33Sjakob 			tcp_port = nsd.options->port;
120462ac0c33Sjakob 		} else {
120562ac0c33Sjakob 			udp_port = UDP_PORT;
120662ac0c33Sjakob 			tcp_port = TCP_PORT;
120762ac0c33Sjakob 		}
120862ac0c33Sjakob 	}
12093f21e8ccSflorian 	if(nsd.options->verify_port != 0) {
12103f21e8ccSflorian 		verify_port = nsd.options->verify_port;
12113f21e8ccSflorian 	} else {
12123f21e8ccSflorian 		verify_port = VERIFY_PORT;
12133f21e8ccSflorian 	}
121462ac0c33Sjakob #ifdef BIND8_STATS
1215b71395eaSflorian 	if(nsd.st_period == 0) {
1216b71395eaSflorian 		nsd.st_period = nsd.options->statistics;
121762ac0c33Sjakob 	}
121862ac0c33Sjakob #endif /* BIND8_STATS */
121962ac0c33Sjakob #ifdef HAVE_CHROOT
122062ac0c33Sjakob 	if(nsd.chrootdir == 0) nsd.chrootdir = nsd.options->chroot;
1221a58d140aSjakob #ifdef CHROOTDIR
12224ab91c82Sjakob 	/* if still no chrootdir, fallback to default */
12234ab91c82Sjakob 	if(nsd.chrootdir == 0) nsd.chrootdir = CHROOTDIR;
1224a58d140aSjakob #endif /* CHROOTDIR */
122562ac0c33Sjakob #endif /* HAVE_CHROOT */
122662ac0c33Sjakob 	if(nsd.username == 0) {
122762ac0c33Sjakob 		if(nsd.options->username) nsd.username = nsd.options->username;
122862ac0c33Sjakob 		else nsd.username = USER;
122962ac0c33Sjakob 	}
123062ac0c33Sjakob 	if(nsd.options->zonesdir && nsd.options->zonesdir[0]) {
123162ac0c33Sjakob 		if(chdir(nsd.options->zonesdir)) {
123262ac0c33Sjakob 			error("cannot chdir to '%s': %s",
123362ac0c33Sjakob 				nsd.options->zonesdir, strerror(errno));
123462ac0c33Sjakob 		}
123562ac0c33Sjakob 		DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed directory to %s",
123662ac0c33Sjakob 			nsd.options->zonesdir));
123762ac0c33Sjakob 	}
123862ac0c33Sjakob 
123962ac0c33Sjakob 	/* EDNS0 */
124062ac0c33Sjakob 	edns_init_data(&nsd.edns_ipv4, nsd.options->ipv4_edns_size);
124162ac0c33Sjakob #if defined(INET6)
124262ac0c33Sjakob #if defined(IPV6_USE_MIN_MTU) || defined(IPV6_MTU)
124362ac0c33Sjakob 	edns_init_data(&nsd.edns_ipv6, nsd.options->ipv6_edns_size);
124462ac0c33Sjakob #else /* no way to set IPV6 MTU, send no bigger than that. */
124562ac0c33Sjakob 	if (nsd.options->ipv6_edns_size < IPV6_MIN_MTU)
124662ac0c33Sjakob 		edns_init_data(&nsd.edns_ipv6, nsd.options->ipv6_edns_size);
124762ac0c33Sjakob 	else
124862ac0c33Sjakob 		edns_init_data(&nsd.edns_ipv6, IPV6_MIN_MTU);
124962ac0c33Sjakob #endif /* IPV6 MTU) */
125062ac0c33Sjakob #endif /* defined(INET6) */
125162ac0c33Sjakob 
1252063644e9Sflorian 	nsd.do_answer_cookie = nsd.options->answer_cookie;
1253063644e9Sflorian 	if (nsd.cookie_count > 0)
1254063644e9Sflorian 		; /* pass */
1255063644e9Sflorian 
1256063644e9Sflorian 	else if (nsd.options->cookie_secret) {
1257063644e9Sflorian 		ssize_t len = hex_pton(nsd.options->cookie_secret,
1258063644e9Sflorian 			nsd.cookie_secrets[0].cookie_secret, NSD_COOKIE_SECRET_SIZE);
1259063644e9Sflorian 		if (len != NSD_COOKIE_SECRET_SIZE ) {
1260063644e9Sflorian 			error("A cookie secret must be a "
1261063644e9Sflorian 			      "128 bit hex string");
1262063644e9Sflorian 		}
1263063644e9Sflorian 		nsd.cookie_count = 1;
1264063644e9Sflorian 	} else {
1265063644e9Sflorian 		size_t j;
1266063644e9Sflorian 		size_t const cookie_secret_len = NSD_COOKIE_SECRET_SIZE;
1267063644e9Sflorian 		/* Calculate a new random secret */
1268063644e9Sflorian 		srandom(getpid() ^ time(NULL));
1269063644e9Sflorian 
1270063644e9Sflorian 		for( j = 0; j < NSD_COOKIE_HISTORY_SIZE; j++) {
1271063644e9Sflorian #if defined(HAVE_SSL)
1272063644e9Sflorian 			if (!RAND_status()
1273063644e9Sflorian 			    || !RAND_bytes(nsd.cookie_secrets[j].cookie_secret, cookie_secret_len))
1274063644e9Sflorian #endif
1275063644e9Sflorian 			for (i = 0; i < cookie_secret_len; i++)
1276063644e9Sflorian 				nsd.cookie_secrets[j].cookie_secret[i] = random_generate(256);
1277063644e9Sflorian 		}
1278063644e9Sflorian 		// XXX: all we have is a random cookie, still pretend we have one
1279063644e9Sflorian 		nsd.cookie_count = 1;
1280063644e9Sflorian 	}
1281063644e9Sflorian 
12824ab91c82Sjakob 	if (nsd.nsid_len == 0 && nsd.options->nsid) {
12834ab91c82Sjakob 		if (strlen(nsd.options->nsid) % 2 != 0) {
12844ab91c82Sjakob 			error("the NSID must be a hex string of an even length.");
12854ab91c82Sjakob 		}
12864ab91c82Sjakob 		nsd.nsid = xalloc(strlen(nsd.options->nsid) / 2);
12874ab91c82Sjakob 		nsd.nsid_len = strlen(nsd.options->nsid) / 2;
12884ab91c82Sjakob 		if (hex_pton(nsd.options->nsid, nsd.nsid, nsd.nsid_len) == -1) {
12894ab91c82Sjakob 			error("hex string cannot be parsed '%s' in NSID.", nsd.options->nsid);
12904ab91c82Sjakob 		}
12914ab91c82Sjakob 	}
129262ac0c33Sjakob 	edns_init_nsid(&nsd.edns_ipv4, nsd.nsid_len);
129362ac0c33Sjakob #if defined(INET6)
129462ac0c33Sjakob 	edns_init_nsid(&nsd.edns_ipv6, nsd.nsid_len);
129562ac0c33Sjakob #endif /* defined(INET6) */
1296c3fd4e2aSjakob 
1297308d2509Sflorian #ifdef HAVE_CPUSET_T
1298308d2509Sflorian 	nsd.use_cpu_affinity = (nsd.options->cpu_affinity != NULL);
1299308d2509Sflorian 	if(nsd.use_cpu_affinity) {
1300308d2509Sflorian 		int ncpus;
1301308d2509Sflorian 		struct cpu_option* opt = nsd.options->cpu_affinity;
1302308d2509Sflorian 
1303308d2509Sflorian 		if((ncpus = number_of_cpus()) == -1) {
1304308d2509Sflorian 			error("cannot retrieve number of cpus: %s",
1305308d2509Sflorian 			      strerror(errno));
1306308d2509Sflorian 		}
1307308d2509Sflorian 		nsd.cpuset = cpuset_create();
1308308d2509Sflorian 		region_add_cleanup(nsd.region, free_cpuset, nsd.cpuset);
1309308d2509Sflorian 		for(; opt; opt = opt->next) {
1310308d2509Sflorian 			assert(opt->cpu >= 0);
1311308d2509Sflorian 			if(opt->cpu >= ncpus) {
1312308d2509Sflorian 				error("invalid cpu %d specified in "
1313308d2509Sflorian 				      "cpu-affinity", opt->cpu);
1314308d2509Sflorian 			}
1315308d2509Sflorian 			cpuset_set((cpuid_t)opt->cpu, nsd.cpuset);
1316308d2509Sflorian 		}
1317308d2509Sflorian 	}
1318308d2509Sflorian 	if(nsd.use_cpu_affinity) {
1319308d2509Sflorian 		int cpu;
1320308d2509Sflorian 		struct cpu_map_option *opt
1321308d2509Sflorian 			= nsd.options->service_cpu_affinity;
1322308d2509Sflorian 
1323308d2509Sflorian 		cpu = -1;
1324308d2509Sflorian 		for(; opt && cpu == -1; opt = opt->next) {
1325308d2509Sflorian 			if(opt->service == -1) {
1326308d2509Sflorian 				cpu = opt->cpu;
1327308d2509Sflorian 				assert(cpu >= 0);
1328308d2509Sflorian 			}
1329308d2509Sflorian 		}
1330308d2509Sflorian 		nsd.xfrd_cpuset = cpuset_create();
1331308d2509Sflorian 		region_add_cleanup(nsd.region, free_cpuset, nsd.xfrd_cpuset);
1332308d2509Sflorian 		if(cpu == -1) {
1333308d2509Sflorian 			cpuset_or(nsd.xfrd_cpuset,
1334308d2509Sflorian 			          nsd.cpuset);
1335308d2509Sflorian 		} else {
1336308d2509Sflorian 			if(!cpuset_isset(cpu, nsd.cpuset)) {
1337308d2509Sflorian 				error("cpu %d specified in xfrd-cpu-affinity "
1338308d2509Sflorian 				      "is not specified in cpu-affinity", cpu);
1339308d2509Sflorian 			}
1340308d2509Sflorian 			cpuset_set((cpuid_t)cpu, nsd.xfrd_cpuset);
1341308d2509Sflorian 		}
1342308d2509Sflorian 	}
1343308d2509Sflorian #endif /* HAVE_CPUSET_T */
1344308d2509Sflorian 
134562ac0c33Sjakob 	/* Number of child servers to fork.  */
13468d8f1862Ssthen 	nsd.children = (struct nsd_child *) region_alloc_array(
13478d8f1862Ssthen 		nsd.region, nsd.child_count, sizeof(struct nsd_child));
134862ac0c33Sjakob 	for (i = 0; i < nsd.child_count; ++i) {
134962ac0c33Sjakob 		nsd.children[i].kind = NSD_SERVER_BOTH;
135062ac0c33Sjakob 		nsd.children[i].pid = -1;
135162ac0c33Sjakob 		nsd.children[i].child_fd = -1;
135262ac0c33Sjakob 		nsd.children[i].parent_fd = -1;
135362ac0c33Sjakob 		nsd.children[i].handler = NULL;
135462ac0c33Sjakob 		nsd.children[i].need_to_send_STATS = 0;
135562ac0c33Sjakob 		nsd.children[i].need_to_send_QUIT = 0;
135662ac0c33Sjakob 		nsd.children[i].need_to_exit = 0;
135762ac0c33Sjakob 		nsd.children[i].has_exited = 0;
1358b90bb40eSsthen #ifdef BIND8_STATS
1359b90bb40eSsthen 		nsd.children[i].query_count = 0;
1360b90bb40eSsthen #endif
1361308d2509Sflorian 
1362308d2509Sflorian #ifdef HAVE_CPUSET_T
1363308d2509Sflorian 		if(nsd.use_cpu_affinity) {
1364308d2509Sflorian 			int cpu, server;
1365308d2509Sflorian 			struct cpu_map_option *opt
1366308d2509Sflorian 				= nsd.options->service_cpu_affinity;
1367308d2509Sflorian 
1368308d2509Sflorian 			cpu = -1;
1369308d2509Sflorian 			server = i+1;
1370308d2509Sflorian 			for(; opt && cpu == -1; opt = opt->next) {
1371308d2509Sflorian 				if(opt->service == server) {
1372308d2509Sflorian 					cpu = opt->cpu;
1373308d2509Sflorian 					assert(cpu >= 0);
1374308d2509Sflorian 				}
1375308d2509Sflorian 			}
1376308d2509Sflorian 			nsd.children[i].cpuset = cpuset_create();
1377308d2509Sflorian 			region_add_cleanup(nsd.region,
1378308d2509Sflorian 			                   free_cpuset,
1379308d2509Sflorian 			                   nsd.children[i].cpuset);
1380308d2509Sflorian 			if(cpu == -1) {
1381308d2509Sflorian 				cpuset_or(nsd.children[i].cpuset,
1382308d2509Sflorian 				          nsd.cpuset);
1383308d2509Sflorian 			} else {
1384308d2509Sflorian 				if(!cpuset_isset((cpuid_t)cpu, nsd.cpuset)) {
1385308d2509Sflorian 					error("cpu %d specified in "
1386308d2509Sflorian 					      "server-%d-cpu-affinity is not "
1387308d2509Sflorian 					      "specified in cpu-affinity",
1388308d2509Sflorian 					      cpu, server);
1389308d2509Sflorian 				}
1390308d2509Sflorian 				cpuset_set(
1391308d2509Sflorian 					(cpuid_t)cpu, nsd.children[i].cpuset);
1392308d2509Sflorian 			}
1393308d2509Sflorian 		}
1394308d2509Sflorian #endif /* HAVE_CPUSET_T */
139562ac0c33Sjakob 	}
139662ac0c33Sjakob 
139762ac0c33Sjakob 	nsd.this_child = NULL;
139862ac0c33Sjakob 
13993b24e79eSsthen 	resolve_interface_names(nsd.options);
14005435475dSsthen 	figure_sockets(&nsd.udp, &nsd.tcp, &nsd.ifs,
14013f21e8ccSflorian 		nsd.options->ip_addresses, NULL, udp_port, tcp_port, &hints);
14023f21e8ccSflorian 
14033f21e8ccSflorian 	if(nsd.options->verify_enable) {
14043f21e8ccSflorian 		figure_sockets(&nsd.verify_udp, &nsd.verify_tcp, &nsd.verify_ifs,
14053f21e8ccSflorian 			nsd.options->verify_ip_addresses, "localhost", verify_port, verify_port, &hints);
14063f21e8ccSflorian 		setup_verifier_environment();
14073f21e8ccSflorian 	}
140862ac0c33Sjakob 
140962ac0c33Sjakob 	/* Parse the username into uid and gid */
141062ac0c33Sjakob 	nsd.gid = getgid();
141162ac0c33Sjakob 	nsd.uid = getuid();
141262ac0c33Sjakob #ifdef HAVE_GETPWNAM
141362ac0c33Sjakob 	/* Parse the username into uid and gid */
141462ac0c33Sjakob 	if (*nsd.username) {
141582cafdebSmillert 		if (isdigit((unsigned char)*nsd.username)) {
141662ac0c33Sjakob 			char *t;
141762ac0c33Sjakob 			nsd.uid = strtol(nsd.username, &t, 10);
141862ac0c33Sjakob 			if (*t != 0) {
141982cafdebSmillert 				if (*t != '.' || !isdigit((unsigned char)*++t)) {
142062ac0c33Sjakob 					error("-u user or -u uid or -u uid.gid");
142162ac0c33Sjakob 				}
142262ac0c33Sjakob 				nsd.gid = strtol(t, &t, 10);
142362ac0c33Sjakob 			} else {
142462ac0c33Sjakob 				/* Lookup the group id in /etc/passwd */
142562ac0c33Sjakob 				if ((pwd = getpwuid(nsd.uid)) == NULL) {
142662ac0c33Sjakob 					error("user id %u does not exist.", (unsigned) nsd.uid);
142762ac0c33Sjakob 				} else {
142862ac0c33Sjakob 					nsd.gid = pwd->pw_gid;
142962ac0c33Sjakob 				}
143062ac0c33Sjakob 			}
143162ac0c33Sjakob 		} else {
143262ac0c33Sjakob 			/* Lookup the user id in /etc/passwd */
143362ac0c33Sjakob 			if ((pwd = getpwnam(nsd.username)) == NULL) {
143462ac0c33Sjakob 				error("user '%s' does not exist.", nsd.username);
143562ac0c33Sjakob 			} else {
143662ac0c33Sjakob 				nsd.uid = pwd->pw_uid;
143762ac0c33Sjakob 				nsd.gid = pwd->pw_gid;
143862ac0c33Sjakob 			}
143962ac0c33Sjakob 		}
144062ac0c33Sjakob 	}
144162ac0c33Sjakob 	/* endpwent(); */
144262ac0c33Sjakob #endif /* HAVE_GETPWNAM */
144362ac0c33Sjakob 
1444c3fd4e2aSjakob #if defined(HAVE_SSL)
144562ac0c33Sjakob 	key_options_tsig_add(nsd.options);
1446c3fd4e2aSjakob #endif
144762ac0c33Sjakob 
1448dd5b221eSsthen 	append_trailing_slash(&nsd.options->xfrdir, nsd.options->region);
1449dd5b221eSsthen 	/* Check relativity of pathnames to chroot */
1450dd5b221eSsthen 	if (nsd.chrootdir && nsd.chrootdir[0]) {
145162ac0c33Sjakob 		/* existing chrootdir: append trailing slash for strncmp checking */
1452dd5b221eSsthen 		append_trailing_slash(&nsd.chrootdir, nsd.region);
1453dd5b221eSsthen 		append_trailing_slash(&nsd.options->zonesdir, nsd.options->region);
145462ac0c33Sjakob 
1455dd5b221eSsthen 		/* zonesdir must be absolute and within chroot,
1456dd5b221eSsthen 		 * all other pathnames may be relative to zonesdir */
1457dd5b221eSsthen 		if (strncmp(nsd.options->zonesdir, nsd.chrootdir, strlen(nsd.chrootdir)) != 0) {
1458c1404d4fSbrad 			error("zonesdir %s has to be an absolute path that starts with the chroot path %s",
14599c620270Ssthen 				nsd.options->zonesdir, nsd.chrootdir);
1460dd5b221eSsthen 		} else if (!file_inside_chroot(nsd.pidfile, nsd.chrootdir)) {
1461dd5b221eSsthen 			error("pidfile %s is not relative to %s: chroot not possible",
1462dd5b221eSsthen 				nsd.pidfile, nsd.chrootdir);
1463dd5b221eSsthen 		} else if (!file_inside_chroot(nsd.options->xfrdfile, nsd.chrootdir)) {
1464dd5b221eSsthen 			error("xfrdfile %s is not relative to %s: chroot not possible",
1465dd5b221eSsthen 				nsd.options->xfrdfile, nsd.chrootdir);
1466dd5b221eSsthen 		} else if (!file_inside_chroot(nsd.options->zonelistfile, nsd.chrootdir)) {
1467dd5b221eSsthen 			error("zonelistfile %s is not relative to %s: chroot not possible",
1468dd5b221eSsthen 				nsd.options->zonelistfile, nsd.chrootdir);
1469dd5b221eSsthen 		} else if (!file_inside_chroot(nsd.options->xfrdir, nsd.chrootdir)) {
1470dd5b221eSsthen 			error("xfrdir %s is not relative to %s: chroot not possible",
1471dd5b221eSsthen 				nsd.options->xfrdir, nsd.chrootdir);
147262ac0c33Sjakob 		}
147362ac0c33Sjakob 	}
147462ac0c33Sjakob 
14754ab91c82Sjakob 	/* Set up the logging */
14764ab91c82Sjakob 	log_open(LOG_PID, FACILITY, nsd.log_filename);
1477ac5517e4Sflorian 	if(nsd.options->log_only_syslog)
1478ac5517e4Sflorian 		log_set_log_function(log_only_syslog);
1479ac5517e4Sflorian 	else if (!nsd.log_filename)
14804ab91c82Sjakob 		log_set_log_function(log_syslog);
14815bcb494bSjakob 	else if (nsd.uid && nsd.gid) {
14825bcb494bSjakob 		if(chown(nsd.log_filename, nsd.uid, nsd.gid) != 0)
14835bcb494bSjakob 			VERBOSITY(2, (LOG_WARNING, "chown %s failed: %s",
14845bcb494bSjakob 				nsd.log_filename, strerror(errno)));
14855bcb494bSjakob 	}
1486275a8d89Sflorian 	log_msg(LOG_NOTICE, "%s starting (%s)", argv0, PACKAGE_STRING);
14874ab91c82Sjakob 
148862ac0c33Sjakob 	/* Do we have a running nsd? */
14895435475dSsthen 	if(nsd.pidfile && nsd.pidfile[0]) {
149062ac0c33Sjakob 		if ((oldpid = readpid(nsd.pidfile)) == -1) {
149162ac0c33Sjakob 			if (errno != ENOENT) {
149262ac0c33Sjakob 				log_msg(LOG_ERR, "can't read pidfile %s: %s",
149362ac0c33Sjakob 					nsd.pidfile, strerror(errno));
149462ac0c33Sjakob 			}
149562ac0c33Sjakob 		} else {
149662ac0c33Sjakob 			if (kill(oldpid, 0) == 0 || errno == EPERM) {
149762ac0c33Sjakob 				log_msg(LOG_WARNING,
149862ac0c33Sjakob 					"%s is already running as %u, continuing",
149962ac0c33Sjakob 					argv0, (unsigned) oldpid);
150062ac0c33Sjakob 			} else {
150162ac0c33Sjakob 				log_msg(LOG_ERR,
150262ac0c33Sjakob 					"...stale pid file from process %u",
150362ac0c33Sjakob 					(unsigned) oldpid);
150462ac0c33Sjakob 			}
150562ac0c33Sjakob 		}
15065435475dSsthen 	}
150762ac0c33Sjakob 
1508308d2509Sflorian #ifdef HAVE_SETPROCTITLE
1509308d2509Sflorian 	setproctitle("main");
1510308d2509Sflorian #endif
1511308d2509Sflorian #ifdef HAVE_CPUSET_T
1512308d2509Sflorian 	if(nsd.use_cpu_affinity) {
1513308d2509Sflorian 		set_cpu_affinity(nsd.cpuset);
1514308d2509Sflorian 	}
1515308d2509Sflorian #endif
1516308d2509Sflorian 
1517308d2509Sflorian 	print_sockets(nsd.udp, nsd.tcp, nsd.ifs);
1518308d2509Sflorian 
15199c620270Ssthen 	/* Setup the signal handling... */
15209c620270Ssthen 	action.sa_handler = sig_handler;
15219c620270Ssthen 	sigfillset(&action.sa_mask);
15229c620270Ssthen 	action.sa_flags = 0;
15239c620270Ssthen 	sigaction(SIGTERM, &action, NULL);
15249c620270Ssthen 	sigaction(SIGHUP, &action, NULL);
15259c620270Ssthen 	sigaction(SIGINT, &action, NULL);
15269c620270Ssthen 	sigaction(SIGILL, &action, NULL);
15279c620270Ssthen 	sigaction(SIGUSR1, &action, NULL);
15289c620270Ssthen 	sigaction(SIGALRM, &action, NULL);
15299c620270Ssthen 	sigaction(SIGCHLD, &action, NULL);
15309c620270Ssthen 	action.sa_handler = SIG_IGN;
15319c620270Ssthen 	sigaction(SIGPIPE, &action, NULL);
15329c620270Ssthen 
15339c620270Ssthen 	/* Initialize... */
15349c620270Ssthen 	nsd.mode = NSD_RUN;
15359c620270Ssthen 	nsd.signal_hint_child = 0;
15369c620270Ssthen 	nsd.signal_hint_reload = 0;
1537dd5b221eSsthen 	nsd.signal_hint_reload_hup = 0;
15389c620270Ssthen 	nsd.signal_hint_quit = 0;
15399c620270Ssthen 	nsd.signal_hint_shutdown = 0;
15409c620270Ssthen 	nsd.signal_hint_stats = 0;
15419c620270Ssthen 	nsd.signal_hint_statsusr = 0;
15429c620270Ssthen 	nsd.quit_sync_done = 0;
15439c620270Ssthen 
15449c620270Ssthen 	/* Initialize the server... */
15459c620270Ssthen 	if (server_init(&nsd) != 0) {
1546dd5b221eSsthen 		error("server initialization failed, %s could "
15479c620270Ssthen 			"not be started", argv0);
15489c620270Ssthen 	}
1549dd5b221eSsthen #if defined(HAVE_SSL)
1550eab1363eSsthen 	if(nsd.options->control_enable || (nsd.options->tls_service_key && nsd.options->tls_service_key[0])) {
1551eab1363eSsthen 		perform_openssl_init();
1552eab1363eSsthen 	}
15533efee2e1Sflorian #endif /* HAVE_SSL */
1554dd5b221eSsthen 	if(nsd.options->control_enable) {
1555dd5b221eSsthen 		/* read ssl keys while superuser and outside chroot */
1556dd5b221eSsthen 		if(!(nsd.rc = daemon_remote_create(nsd.options)))
1557dd5b221eSsthen 			error("could not perform remote control setup");
1558dd5b221eSsthen 	}
15593efee2e1Sflorian #if defined(HAVE_SSL)
1560eab1363eSsthen 	if(nsd.options->tls_service_key && nsd.options->tls_service_key[0]
1561eab1363eSsthen 	   && nsd.options->tls_service_pem && nsd.options->tls_service_pem[0]) {
1562eab1363eSsthen 		if(!(nsd.tls_ctx = server_tls_ctx_create(&nsd, NULL,
1563eab1363eSsthen 			nsd.options->tls_service_ocsp)))
1564eab1363eSsthen 			error("could not set up tls SSL_CTX");
1565eab1363eSsthen 	}
1566dd5b221eSsthen #endif /* HAVE_SSL */
15679c620270Ssthen 
1568063644e9Sflorian 	if(nsd.options->cookie_secret_file && nsd.options->cookie_secret_file[0]
1569063644e9Sflorian 	   && !cookie_secret_file_read(&nsd) ) {
1570063644e9Sflorian 		log_msg(LOG_ERR, "cookie secret file corrupt or not readable");
1571063644e9Sflorian 	}
1572063644e9Sflorian 
157362ac0c33Sjakob 	/* Unless we're debugging, fork... */
157462ac0c33Sjakob 	if (!nsd.debug) {
157562ac0c33Sjakob 		int fd;
157662ac0c33Sjakob 
157762ac0c33Sjakob 		/* Take off... */
1578a1bac035Sflorian 		switch (fork()) {
157962ac0c33Sjakob 		case 0:
158062ac0c33Sjakob 			/* Child */
158162ac0c33Sjakob 			break;
158262ac0c33Sjakob 		case -1:
1583dd5b221eSsthen 			error("fork() failed: %s", strerror(errno));
1584ee5153b7Sflorian 			break;
158562ac0c33Sjakob 		default:
158662ac0c33Sjakob 			/* Parent is done */
15879c620270Ssthen 			server_close_all_sockets(nsd.udp, nsd.ifs);
15889c620270Ssthen 			server_close_all_sockets(nsd.tcp, nsd.ifs);
158962ac0c33Sjakob 			exit(0);
159062ac0c33Sjakob 		}
159162ac0c33Sjakob 
159262ac0c33Sjakob 		/* Detach ourselves... */
159362ac0c33Sjakob 		if (setsid() == -1) {
1594dd5b221eSsthen 			error("setsid() failed: %s", strerror(errno));
159562ac0c33Sjakob 		}
159662ac0c33Sjakob 
159762ac0c33Sjakob 		if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
159862ac0c33Sjakob 			(void)dup2(fd, STDIN_FILENO);
159962ac0c33Sjakob 			(void)dup2(fd, STDOUT_FILENO);
160062ac0c33Sjakob 			(void)dup2(fd, STDERR_FILENO);
160162ac0c33Sjakob 			if (fd > 2)
160262ac0c33Sjakob 				(void)close(fd);
160362ac0c33Sjakob 		}
160462ac0c33Sjakob 	}
160562ac0c33Sjakob 
160662ac0c33Sjakob 	/* Get our process id */
160762ac0c33Sjakob 	nsd.pid = getpid();
160862ac0c33Sjakob 
16095bd3eb9dSjakob 	/* Set user context */
16105bd3eb9dSjakob #ifdef HAVE_GETPWNAM
16115bd3eb9dSjakob 	if (*nsd.username) {
16125bd3eb9dSjakob #ifdef HAVE_SETUSERCONTEXT
16133b0b19f7Sjakob 		/* setusercontext does initgroups, setuid, setgid, and
16143b0b19f7Sjakob 		 * also resource limits from login config, but we
16153b0b19f7Sjakob 		 * still call setresuid, setresgid to be sure to set all uid */
16165bd3eb9dSjakob 		if (setusercontext(NULL, pwd, nsd.uid,
16175bd3eb9dSjakob 			LOGIN_SETALL & ~LOGIN_SETUSER & ~LOGIN_SETGROUP) != 0)
16185bd3eb9dSjakob 			log_msg(LOG_WARNING, "unable to setusercontext %s: %s",
16195bd3eb9dSjakob 				nsd.username, strerror(errno));
16205bd3eb9dSjakob #endif /* HAVE_SETUSERCONTEXT */
16215bd3eb9dSjakob 	}
16225bd3eb9dSjakob #endif /* HAVE_GETPWNAM */
16235bd3eb9dSjakob 
162462ac0c33Sjakob 	/* Chroot */
16253b0b19f7Sjakob #ifdef HAVE_CHROOT
1626dd5b221eSsthen 	if (nsd.chrootdir && nsd.chrootdir[0]) {
1627dd5b221eSsthen 		int l = strlen(nsd.chrootdir)-1; /* ends in trailing slash */
162862ac0c33Sjakob 
1629dd5b221eSsthen 		if (file_inside_chroot(nsd.log_filename, nsd.chrootdir))
163062ac0c33Sjakob 			nsd.file_rotation_ok = 1;
1631dd5b221eSsthen 
1632dd5b221eSsthen 		/* strip chroot from pathnames if they're absolute */
1633dd5b221eSsthen 		nsd.options->zonesdir += l;
1634dd5b221eSsthen 		if (nsd.log_filename){
1635dd5b221eSsthen 			if (nsd.log_filename[0] == '/')
163662ac0c33Sjakob 				nsd.log_filename += l;
163762ac0c33Sjakob 		}
16385435475dSsthen 		if (nsd.pidfile && nsd.pidfile[0] == '/')
163962ac0c33Sjakob 			nsd.pidfile += l;
1640dd5b221eSsthen 		if (nsd.options->xfrdfile[0] == '/')
164162ac0c33Sjakob 			nsd.options->xfrdfile += l;
1642dd5b221eSsthen 		if (nsd.options->zonelistfile[0] == '/')
1643dd5b221eSsthen 			nsd.options->zonelistfile += l;
1644dd5b221eSsthen 		if (nsd.options->xfrdir[0] == '/')
1645dd5b221eSsthen 			nsd.options->xfrdir += l;
1646dd5b221eSsthen 
1647dd5b221eSsthen 		/* strip chroot from pathnames of "include:" statements
1648dd5b221eSsthen 		 * on subsequent repattern commands */
1649dd5b221eSsthen 		cfg_parser->chroot = nsd.chrootdir;
165062ac0c33Sjakob 
165172f0a8e9Ssthen #ifdef HAVE_TZSET
165272f0a8e9Ssthen 		/* set timezone whilst not yet in chroot */
165372f0a8e9Ssthen 		tzset();
165472f0a8e9Ssthen #endif
165562ac0c33Sjakob 		if (chroot(nsd.chrootdir)) {
1656dd5b221eSsthen 			error("unable to chroot: %s", strerror(errno));
165762ac0c33Sjakob 		}
165872f0a8e9Ssthen 		if (chdir("/")) {
1659dd5b221eSsthen 			error("unable to chdir to chroot: %s", strerror(errno));
166072f0a8e9Ssthen 		}
166162ac0c33Sjakob 		DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed root directory to %s",
166262ac0c33Sjakob 			nsd.chrootdir));
16639c620270Ssthen 		/* chdir to zonesdir again after chroot */
16649c620270Ssthen 		if(nsd.options->zonesdir && nsd.options->zonesdir[0]) {
16659c620270Ssthen 			if(chdir(nsd.options->zonesdir)) {
16669c620270Ssthen 				error("unable to chdir to '%s': %s",
16679c620270Ssthen 					nsd.options->zonesdir, strerror(errno));
16689c620270Ssthen 			}
16699c620270Ssthen 			DEBUG(DEBUG_IPC,1, (LOG_INFO, "changed directory to %s",
16709c620270Ssthen 				nsd.options->zonesdir));
16719c620270Ssthen 		}
167262ac0c33Sjakob 	}
167362ac0c33Sjakob 	else
167462ac0c33Sjakob #endif /* HAVE_CHROOT */
167562ac0c33Sjakob 		nsd.file_rotation_ok = 1;
167662ac0c33Sjakob 
167762ac0c33Sjakob 	DEBUG(DEBUG_IPC,1, (LOG_INFO, "file rotation on %s %sabled",
167862ac0c33Sjakob 		nsd.log_filename, nsd.file_rotation_ok?"en":"dis"));
167962ac0c33Sjakob 
168062ac0c33Sjakob 	/* Write pidfile */
168162ac0c33Sjakob 	if (writepid(&nsd) == -1) {
168262ac0c33Sjakob 		log_msg(LOG_ERR, "cannot overwrite the pidfile %s: %s",
168362ac0c33Sjakob 			nsd.pidfile, strerror(errno));
168462ac0c33Sjakob 	}
168562ac0c33Sjakob 
168662ac0c33Sjakob 	/* Drop the permissions */
168762ac0c33Sjakob #ifdef HAVE_GETPWNAM
168862ac0c33Sjakob 	if (*nsd.username) {
168962ac0c33Sjakob #ifdef HAVE_INITGROUPS
169062ac0c33Sjakob 		if(initgroups(nsd.username, nsd.gid) != 0)
169162ac0c33Sjakob 			log_msg(LOG_WARNING, "unable to initgroups %s: %s",
169262ac0c33Sjakob 				nsd.username, strerror(errno));
169362ac0c33Sjakob #endif /* HAVE_INITGROUPS */
169462ac0c33Sjakob 		endpwent();
169562ac0c33Sjakob 
169662ac0c33Sjakob #ifdef HAVE_SETRESGID
169762ac0c33Sjakob 		if(setresgid(nsd.gid,nsd.gid,nsd.gid) != 0)
169862ac0c33Sjakob #elif defined(HAVE_SETREGID) && !defined(DARWIN_BROKEN_SETREUID)
169962ac0c33Sjakob 			if(setregid(nsd.gid,nsd.gid) != 0)
170062ac0c33Sjakob #else /* use setgid */
170162ac0c33Sjakob 				if(setgid(nsd.gid) != 0)
170262ac0c33Sjakob #endif /* HAVE_SETRESGID */
170362ac0c33Sjakob 					error("unable to set group id of %s: %s",
170462ac0c33Sjakob 						nsd.username, strerror(errno));
170562ac0c33Sjakob 
170662ac0c33Sjakob #ifdef HAVE_SETRESUID
170762ac0c33Sjakob 		if(setresuid(nsd.uid,nsd.uid,nsd.uid) != 0)
170862ac0c33Sjakob #elif defined(HAVE_SETREUID) && !defined(DARWIN_BROKEN_SETREUID)
170962ac0c33Sjakob 			if(setreuid(nsd.uid,nsd.uid) != 0)
171062ac0c33Sjakob #else /* use setuid */
171162ac0c33Sjakob 				if(setuid(nsd.uid) != 0)
171262ac0c33Sjakob #endif /* HAVE_SETRESUID */
171362ac0c33Sjakob 					error("unable to set user id of %s: %s",
171462ac0c33Sjakob 						nsd.username, strerror(errno));
171562ac0c33Sjakob 
171662ac0c33Sjakob 		DEBUG(DEBUG_IPC,1, (LOG_INFO, "dropped user privileges, run as %s",
171762ac0c33Sjakob 			nsd.username));
171862ac0c33Sjakob 	}
171962ac0c33Sjakob #endif /* HAVE_GETPWNAM */
1720434a9738Sflorian 
1721434a9738Sflorian 	if (pledge("stdio rpath wpath cpath dns inet proc", NULL) == -1)
1722434a9738Sflorian 		error("pledge");
1723434a9738Sflorian 
17248d8f1862Ssthen 	xfrd_make_tempdir(&nsd);
1725c1404d4fSbrad #ifdef USE_ZONE_STATS
1726c1404d4fSbrad 	options_zonestatnames_create(nsd.options);
1727c1404d4fSbrad 	server_zonestat_alloc(&nsd);
1728c1404d4fSbrad #endif /* USE_ZONE_STATS */
1729b71395eaSflorian #ifdef BIND8_STATS
1730b71395eaSflorian 	server_stat_alloc(&nsd);
1731b71395eaSflorian #endif /* BIND8_STATS */
1732dd5b221eSsthen 	if(nsd.server_kind == NSD_SERVER_MAIN) {
1733dd5b221eSsthen 		server_prepare_xfrd(&nsd);
1734dd5b221eSsthen 		/* xfrd forks this before reading database, so it does not get
1735dd5b221eSsthen 		 * the memory size of the database */
1736dd5b221eSsthen 		server_start_xfrd(&nsd, 0, 0);
1737b90bb40eSsthen 		/* close zonelistfile in non-xfrd processes */
1738b90bb40eSsthen 		zone_list_close(nsd.options);
1739063644e9Sflorian #ifdef USE_DNSTAP
1740063644e9Sflorian 		if(nsd.options->dnstap_enable) {
1741063644e9Sflorian 			nsd.dt_collector = dt_collector_create(&nsd);
1742063644e9Sflorian 			dt_collector_start(nsd.dt_collector, &nsd);
1743063644e9Sflorian 		}
1744063644e9Sflorian #endif /* USE_DNSTAP */
1745dd5b221eSsthen 	}
174662ac0c33Sjakob 	if (server_prepare(&nsd) != 0) {
174762ac0c33Sjakob 		unlinkpid(nsd.pidfile);
1748dd5b221eSsthen 		error("server preparation failed, %s could "
1749dd5b221eSsthen 			"not be started", argv0);
1750dd5b221eSsthen 	}
1751dd5b221eSsthen 	if(nsd.server_kind == NSD_SERVER_MAIN) {
1752dd5b221eSsthen 		server_send_soa_xfrd(&nsd, 0);
175362ac0c33Sjakob 	}
175462ac0c33Sjakob 
175562ac0c33Sjakob 	/* Really take off */
175662ac0c33Sjakob 	log_msg(LOG_NOTICE, "%s started (%s), pid %d",
175762ac0c33Sjakob 		argv0, PACKAGE_STRING, (int) nsd.pid);
175862ac0c33Sjakob 
175962ac0c33Sjakob 	if (nsd.server_kind == NSD_SERVER_MAIN) {
176062ac0c33Sjakob 		server_main(&nsd);
176162ac0c33Sjakob 	} else {
176262ac0c33Sjakob 		server_child(&nsd);
176362ac0c33Sjakob 	}
176462ac0c33Sjakob 
176562ac0c33Sjakob 	/* NOTREACH */
176662ac0c33Sjakob 	exit(0);
176762ac0c33Sjakob }
1768