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