xref: /openbsd-src/usr.sbin/ntpd/ntp_dns.c (revision f1b790a5738b7375271fee81f99119b1f82f2cfd)
1*f1b790a5Sclaudio /*	$OpenBSD: ntp_dns.c,v 1.36 2024/11/21 13:38:14 claudio Exp $ */
25f5af31aShenning 
35f5af31aShenning /*
45f5af31aShenning  * Copyright (c) 2003-2008 Henning Brauer <henning@openbsd.org>
55f5af31aShenning  *
65f5af31aShenning  * Permission to use, copy, modify, and distribute this software for any
75f5af31aShenning  * purpose with or without fee is hereby granted, provided that the above
85f5af31aShenning  * copyright notice and this permission notice appear in all copies.
95f5af31aShenning  *
105f5af31aShenning  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
115f5af31aShenning  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
125f5af31aShenning  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
135f5af31aShenning  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14774da4d1Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15774da4d1Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16774da4d1Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
175f5af31aShenning  */
185f5af31aShenning 
1961f07045Sderaadt #include <sys/types.h>
20832033eaSdtucker #include <sys/resource.h>
215f5af31aShenning #include <sys/time.h>
2271ba36b6Sotto #include <netinet/in.h>
2371ba36b6Sotto #include <arpa/nameser.h>
2471ba36b6Sotto #include <resolv.h>
25832033eaSdtucker 
26832033eaSdtucker #include <err.h>
275f5af31aShenning #include <errno.h>
285f5af31aShenning #include <poll.h>
2965d995a8Sderaadt #include <fcntl.h>
305f5af31aShenning #include <signal.h>
315f5af31aShenning #include <stdlib.h>
325f5af31aShenning #include <string.h>
33579813e4Sreyk #include <syslog.h>
345f5af31aShenning #include <unistd.h>
355f5af31aShenning 
365f5af31aShenning #include "ntpd.h"
375f5af31aShenning 
385f5af31aShenning volatile sig_atomic_t	 quit_dns = 0;
396fe90a5aSotto static struct imsgbuf	*ibuf_dns;
40911793feSotto extern int		 non_numeric;
415f5af31aShenning 
425f5af31aShenning void	sighdlr_dns(int);
43c9addb91Sotto int	dns_dispatch_imsg(struct ntpd_conf *);
4471ba36b6Sotto int	probe_root_ns(void);
4571ba36b6Sotto void	probe_root(void);
465f5af31aShenning 
475f5af31aShenning void
485f5af31aShenning sighdlr_dns(int sig)
495f5af31aShenning {
505f5af31aShenning 	switch (sig) {
515f5af31aShenning 	case SIGTERM:
525f5af31aShenning 	case SIGINT:
535f5af31aShenning 		quit_dns = 1;
545f5af31aShenning 		break;
555f5af31aShenning 	}
565f5af31aShenning }
575f5af31aShenning 
584e840e7aSrzalamena void
594e840e7aSrzalamena ntp_dns(struct ntpd_conf *nconf, struct passwd *pw)
605f5af31aShenning {
615f5af31aShenning 	struct pollfd		 pfd[1];
6265d995a8Sderaadt 	int			 nfds, nullfd;
635f5af31aShenning 
64b98b0a5cSotto 	res_init();
65832033eaSdtucker 	if (setpriority(PRIO_PROCESS, 0, 0) == -1)
66579813e4Sreyk 		log_warn("could not set priority");
67832033eaSdtucker 
688d2ac903Sotto 	log_init(nconf->debug ? LOG_TO_STDERR : LOG_TO_SYSLOG, nconf->verbose,
698d2ac903Sotto 	    LOG_DAEMON);
70c9addb91Sotto 	if (!nconf->debug && setsid() == -1)
715f5af31aShenning 		fatal("setsid");
72579813e4Sreyk 	log_procinit("dns");
735f5af31aShenning 
74b7041c07Sderaadt 	if ((nullfd = open("/dev/null", O_RDWR)) == -1)
7565d995a8Sderaadt 		fatal(NULL);
7665d995a8Sderaadt 
7765d995a8Sderaadt 	if (!nconf->debug) {
7865d995a8Sderaadt 		dup2(nullfd, STDIN_FILENO);
7965d995a8Sderaadt 		dup2(nullfd, STDOUT_FILENO);
8065d995a8Sderaadt 		dup2(nullfd, STDERR_FILENO);
8165d995a8Sderaadt 	}
8265d995a8Sderaadt 	close(nullfd);
8365d995a8Sderaadt 
845f5af31aShenning 	setproctitle("dns engine");
85579813e4Sreyk 
865f5af31aShenning 	if (setgroups(1, &pw->pw_gid) ||
875f5af31aShenning 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
885f5af31aShenning 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
895f5af31aShenning 		fatal("can't drop privileges");
905f5af31aShenning 
915f5af31aShenning 	signal(SIGTERM, sighdlr_dns);
925f5af31aShenning 	signal(SIGINT, sighdlr_dns);
93892d8569Shenning 	signal(SIGHUP, SIG_IGN);
945f5af31aShenning 
955f5af31aShenning 	if ((ibuf_dns = malloc(sizeof(struct imsgbuf))) == NULL)
965f5af31aShenning 		fatal(NULL);
97*f1b790a5Sclaudio 	if (imsgbuf_init(ibuf_dns, PARENT_SOCK_FILENO) == -1)
98*f1b790a5Sclaudio 		fatal(NULL);
995f5af31aShenning 
100b5b9767fSderaadt 	if (pledge("stdio dns", NULL) == -1)
1010bd1216cSderaadt 		err(1, "pledge");
1022a8590bcSderaadt 
103911793feSotto 	if (non_numeric)
10471ba36b6Sotto 		probe_root();
105911793feSotto 	else
106911793feSotto 		log_debug("all addresses numeric, no dns probe");
10771ba36b6Sotto 
1085f5af31aShenning 	while (quit_dns == 0) {
1095f5af31aShenning 		pfd[0].fd = ibuf_dns->fd;
1105f5af31aShenning 		pfd[0].events = POLLIN;
11131be28caSclaudio 		if (imsgbuf_queuelen(ibuf_dns) > 0)
1125f5af31aShenning 			pfd[0].events |= POLLOUT;
1135f5af31aShenning 
1145f5af31aShenning 		if ((nfds = poll(pfd, 1, INFTIM)) == -1)
1155f5af31aShenning 			if (errno != EINTR) {
1165f5af31aShenning 				log_warn("poll error");
1175f5af31aShenning 				quit_dns = 1;
1185f5af31aShenning 			}
1195f5af31aShenning 
1205f5af31aShenning 		if (nfds > 0 && (pfd[0].revents & POLLOUT))
121dd7efffeSclaudio 			if (imsgbuf_write(ibuf_dns) == -1) {
1225f5af31aShenning 				log_warn("pipe write error (to ntp engine)");
1235f5af31aShenning 				quit_dns = 1;
1245f5af31aShenning 			}
1255f5af31aShenning 
1265f5af31aShenning 		if (nfds > 0 && pfd[0].revents & POLLIN) {
1275f5af31aShenning 			nfds--;
128c9addb91Sotto 			if (dns_dispatch_imsg(nconf) == -1)
1295f5af31aShenning 				quit_dns = 1;
1305f5af31aShenning 		}
1315f5af31aShenning 	}
1325f5af31aShenning 
1339cbf9e90Sclaudio 	imsgbuf_clear(ibuf_dns);
1345f5af31aShenning 	free(ibuf_dns);
1354e840e7aSrzalamena 	exit(0);
1365f5af31aShenning }
1375f5af31aShenning 
1385f5af31aShenning int
139c9addb91Sotto dns_dispatch_imsg(struct ntpd_conf *nconf)
1405f5af31aShenning {
1415f5af31aShenning 	struct imsg		 imsg;
1425f5af31aShenning 	int			 n, cnt;
1435f5af31aShenning 	char			*name;
1445f5af31aShenning 	struct ntp_addr		*h, *hn;
145e39620e5Snicm 	struct ibuf		*buf;
146b775b3eeSreyk 	const char		*str;
1474ffc0b90Sotto 	size_t			 len;
1485f5af31aShenning 
149f6bd242eSclaudio 	if (imsgbuf_read(ibuf_dns) != 1)
1505f5af31aShenning 		return (-1);
1515f5af31aShenning 
1525f5af31aShenning 	for (;;) {
1535f5af31aShenning 		if ((n = imsg_get(ibuf_dns, &imsg)) == -1)
1545f5af31aShenning 			return (-1);
1555f5af31aShenning 
1565f5af31aShenning 		if (n == 0)
1575f5af31aShenning 			break;
1585f5af31aShenning 
1595f5af31aShenning 		switch (imsg.hdr.type) {
1605f5af31aShenning 		case IMSG_HOST_DNS:
161b775b3eeSreyk 		case IMSG_CONSTRAINT_DNS:
162b775b3eeSreyk 			if (imsg.hdr.type == IMSG_HOST_DNS)
163b775b3eeSreyk 				str = "IMSG_HOST_DNS";
164b775b3eeSreyk 			else
165b775b3eeSreyk 				str = "IMSG_CONSTRAINT_DNS";
1665f5af31aShenning 			name = imsg.data;
1675f5af31aShenning 			if (imsg.hdr.len < 1 + IMSG_HEADER_SIZE)
168b775b3eeSreyk 				fatalx("invalid %s received", str);
1694ffc0b90Sotto 			len = imsg.hdr.len - 1 - IMSG_HEADER_SIZE;
1704ffc0b90Sotto 			if (name[len] != '\0' ||
1714ffc0b90Sotto 			    strlen(name) != len)
172b775b3eeSreyk 				fatalx("invalid %s received", str);
173c9addb91Sotto 			if ((cnt = host_dns(name, nconf->status.synced,
174c9addb91Sotto 			    &hn)) == -1)
1755f5af31aShenning 				break;
176b775b3eeSreyk 			buf = imsg_create(ibuf_dns, imsg.hdr.type,
1775f5af31aShenning 			    imsg.hdr.peerid, 0,
178b98b0a5cSotto 			    cnt * (sizeof(struct sockaddr_storage) + sizeof(int)));
179c0cb3bf1Sbcook 			if (cnt > 0) {
180c0cb3bf1Sbcook 				if (buf) {
181b98b0a5cSotto 					for (h = hn; h != NULL; h = h->next) {
182c0cb3bf1Sbcook 						if (imsg_add(buf, &h->ss,
183c0cb3bf1Sbcook 						    sizeof(h->ss)) == -1) {
184c0cb3bf1Sbcook 							buf = NULL;
185c0cb3bf1Sbcook 							break;
186c0cb3bf1Sbcook 						}
187b98b0a5cSotto 						if (imsg_add(buf, &h->notauth,
188b98b0a5cSotto 						    sizeof(int)) == -1) {
189b98b0a5cSotto 							buf = NULL;
190b98b0a5cSotto 							break;
191b98b0a5cSotto 						}
192b98b0a5cSotto 					}
193c0cb3bf1Sbcook 				}
194c0cb3bf1Sbcook 				host_dns_free(hn);
195c0cb3bf1Sbcook 				hn = NULL;
196c0cb3bf1Sbcook 			}
19744624868Sbcook 			if (buf)
19844624868Sbcook 				imsg_close(ibuf_dns, buf);
1995f5af31aShenning 			break;
200c9addb91Sotto 		case IMSG_SYNCED:
201c9addb91Sotto 			nconf->status.synced = 1;
202c9addb91Sotto 			break;
203c9addb91Sotto 		case IMSG_UNSYNCED:
204c9addb91Sotto 			nconf->status.synced = 0;
205c9addb91Sotto 			break;
2065f5af31aShenning 		default:
2075f5af31aShenning 			break;
2085f5af31aShenning 		}
2095f5af31aShenning 		imsg_free(&imsg);
2105f5af31aShenning 	}
2115f5af31aShenning 	return (0);
2125f5af31aShenning }
21371ba36b6Sotto 
21471ba36b6Sotto int
21571ba36b6Sotto probe_root_ns(void)
21671ba36b6Sotto {
21771ba36b6Sotto 	int ret;
21871ba36b6Sotto 	int old_retrans, old_retry, old_options;
21971ba36b6Sotto 	unsigned char buf[4096];
22071ba36b6Sotto 
22171ba36b6Sotto 	old_retrans = _res.retrans;
22271ba36b6Sotto 	old_retry = _res.retry;
22371ba36b6Sotto 	old_options = _res.options;
22471ba36b6Sotto 	_res.retrans = 1;
22571ba36b6Sotto 	_res.retry = 1;
22671ba36b6Sotto 	_res.options |= RES_USE_CD;
22771ba36b6Sotto 
22871ba36b6Sotto 	ret = res_query(".", C_IN, T_NS, buf, sizeof(buf));
22971ba36b6Sotto 
23071ba36b6Sotto 	_res.retrans = old_retrans;
23171ba36b6Sotto 	_res.retry = old_retry;
23271ba36b6Sotto 	_res.options = old_options;
23371ba36b6Sotto 
23471ba36b6Sotto 	return ret;
23571ba36b6Sotto }
23671ba36b6Sotto 
23771ba36b6Sotto void
23871ba36b6Sotto probe_root(void)
23971ba36b6Sotto {
240be5c6adaSotto 	int		i, n;
241be5c6adaSotto 	struct timespec	start, probe_start, probe_end;
242be5c6adaSotto 	struct timespec	duration;
24371ba36b6Sotto 
244be5c6adaSotto 	clock_gettime(CLOCK_MONOTONIC, &start);
245be5c6adaSotto 	for (i = 0; ; i++) {
246be5c6adaSotto 		clock_gettime(CLOCK_MONOTONIC, &probe_start);
24771ba36b6Sotto 		n = probe_root_ns();
248be5c6adaSotto 		clock_gettime(CLOCK_MONOTONIC, &probe_end);
249be5c6adaSotto 		if (n >= 0)
250be5c6adaSotto 			break;
251be5c6adaSotto 		timespecsub(&probe_end, &start, &duration);
252be5c6adaSotto 		if (duration.tv_sec > 5)
253be5c6adaSotto 			break;
254be5c6adaSotto 		timespecsub(&probe_end, &probe_start, &duration);
255be5c6adaSotto 		/* normally the probe takes 1s * nscount, but
256be5c6adaSotto 		   sleep a little if the probe returned quickly */
257be5c6adaSotto 		if (duration.tv_sec == 0)
25871ba36b6Sotto 			sleep(1);
25971ba36b6Sotto 	}
260be5c6adaSotto 	if (i > 0)
261be5c6adaSotto 		log_warnx("DNS root probe failed %d times (%s)", i,
262be5c6adaSotto 		    n >= 0 ? "eventually succeeded": "gave up");
26371ba36b6Sotto 	if (imsg_compose(ibuf_dns, IMSG_PROBE_ROOT, 0, 0, -1, &n,
26471ba36b6Sotto 	    sizeof(int)) == -1)
26571ba36b6Sotto 		fatalx("probe_root");
26671ba36b6Sotto }
267