xref: /openbsd-src/sbin/resolvd/resolvd.c (revision 8475ebac2505d97cb3bdff76dc6b72401d329e63)
1*8475ebacStb /*	$OpenBSD: resolvd.c,v 1.32 2022/12/09 18:22:35 tb Exp $	*/
257184814Sflorian /*
357184814Sflorian  * Copyright (c) 2021 Florian Obser <florian@openbsd.org>
484b09d08Sderaadt  * Copyright (c) 2021 Theo de Raadt <deraadt@openbsd.org>
557184814Sflorian  *
657184814Sflorian  * Permission to use, copy, modify, and distribute this software for any
757184814Sflorian  * purpose with or without fee is hereby granted, provided that the above
857184814Sflorian  * copyright notice and this permission notice appear in all copies.
957184814Sflorian  *
1057184814Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1157184814Sflorian  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1257184814Sflorian  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1357184814Sflorian  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1457184814Sflorian  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1557184814Sflorian  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1657184814Sflorian  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1757184814Sflorian  */
1857184814Sflorian 
1957184814Sflorian #include <sys/types.h>
2057184814Sflorian #include <sys/event.h>
2157184814Sflorian #include <sys/socket.h>
2257184814Sflorian #include <sys/stat.h>
2357184814Sflorian #include <sys/syslog.h>
2457184814Sflorian #include <sys/time.h>
2557184814Sflorian #include <sys/un.h>
26162e527eSkn #include <netdb.h>
2757184814Sflorian 
2857184814Sflorian #include <arpa/inet.h>
2981590d99Sderaadt #include <netinet/in.h>
3057184814Sflorian #include <net/if.h>
3157184814Sflorian #include <net/route.h>
3257184814Sflorian 
3357184814Sflorian #include <err.h>
3457184814Sflorian #include <errno.h>
3584b09d08Sderaadt #include <fcntl.h>
3657184814Sflorian #include <event.h>
37b32a2ee4Skn #include <signal.h>
3857184814Sflorian #include <stddef.h>
3957184814Sflorian #include <stdio.h>
4057184814Sflorian #include <stdlib.h>
4157184814Sflorian #include <string.h>
4257184814Sflorian #include <unistd.h>
4357184814Sflorian 
4457184814Sflorian #define	ROUTE_SOCKET_BUF_SIZE	16384
4584b09d08Sderaadt #define	ASR_MAXNS		10
46bc9eb55cSkn #define	_PATH_LOCKFILE		"/dev/resolvd.lock"
4784b09d08Sderaadt #define	_PATH_UNWIND_SOCKET	"/dev/unwind.sock"
4884b09d08Sderaadt #define	_PATH_RESCONF		"/etc/resolv.conf"
4984b09d08Sderaadt #define	_PATH_RESCONF_NEW	"/etc/resolv.conf.new"
5057184814Sflorian 
5157184814Sflorian #ifndef nitems
5257184814Sflorian #define	nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
5357184814Sflorian #endif
5457184814Sflorian 
5557184814Sflorian __dead void	usage(void);
5657184814Sflorian 
5784b09d08Sderaadt struct rdns_proposal {
5884b09d08Sderaadt 	uint32_t	 if_index;
5984b09d08Sderaadt 	int		 af;
6084b09d08Sderaadt 	int		 prio;
6184b09d08Sderaadt 	char		 ip[INET6_ADDRSTRLEN];
6284b09d08Sderaadt };
6384b09d08Sderaadt 
6457184814Sflorian void		route_receive(int);
6557184814Sflorian void		handle_route_message(struct rt_msghdr *, struct sockaddr **);
6657184814Sflorian void		get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
6757184814Sflorian void		solicit_dns_proposals(int);
686a845ff2Skn void		regen_resolvconf(const char *reason);
6957184814Sflorian int		cmp(const void *, const void *);
7084b09d08Sderaadt int		findslot(struct rdns_proposal *);
7184b09d08Sderaadt void		zeroslot(struct rdns_proposal *);
7257184814Sflorian 
7384b09d08Sderaadt struct rdns_proposal	 learned[ASR_MAXNS];
7484b09d08Sderaadt int			 resolvfd = -1;
7584b09d08Sderaadt int			 newkevent = 1;
7657184814Sflorian 
7757184814Sflorian #ifndef SMALL
7857184814Sflorian int			 open_unwind_ctl(void);
7957184814Sflorian int			 check_unwind = 1, unwind_running = 0;
8057184814Sflorian 
8157184814Sflorian struct loggers {
8257184814Sflorian 	__dead void (*err)(int, const char *, ...)
8357184814Sflorian 	    __attribute__((__format__ (printf, 2, 3)));
8457184814Sflorian 	__dead void (*errx)(int, const char *, ...)
8557184814Sflorian 	    __attribute__((__format__ (printf, 2, 3)));
8657184814Sflorian 	void (*warn)(const char *, ...)
8757184814Sflorian 	    __attribute__((__format__ (printf, 1, 2)));
8857184814Sflorian 	void (*warnx)(const char *, ...)
8957184814Sflorian 	    __attribute__((__format__ (printf, 1, 2)));
9057184814Sflorian 	void (*info)(const char *, ...)
9157184814Sflorian 	    __attribute__((__format__ (printf, 1, 2)));
9257184814Sflorian 	void (*debug)(const char *, ...)
9357184814Sflorian 	    __attribute__((__format__ (printf, 1, 2)));
9457184814Sflorian };
9557184814Sflorian 
9684b09d08Sderaadt void		warnx_verbose(const char *, ...)
9784b09d08Sderaadt 		    __attribute__((__format__ (printf, 1, 2)));
9884b09d08Sderaadt 
9957184814Sflorian const struct loggers conslogger = {
10057184814Sflorian 	err,
10157184814Sflorian 	errx,
10257184814Sflorian 	warn,
10357184814Sflorian 	warnx,
10484b09d08Sderaadt 	warnx_verbose, /* info */
10584b09d08Sderaadt 	warnx_verbose /* debug */
10657184814Sflorian };
10757184814Sflorian 
10857184814Sflorian __dead void	syslog_err(int, const char *, ...)
10957184814Sflorian 		    __attribute__((__format__ (printf, 2, 3)));
11057184814Sflorian __dead void	syslog_errx(int, const char *, ...)
11157184814Sflorian 		    __attribute__((__format__ (printf, 2, 3)));
11257184814Sflorian void		syslog_warn(const char *, ...)
11357184814Sflorian 		    __attribute__((__format__ (printf, 1, 2)));
11457184814Sflorian void		syslog_warnx(const char *, ...)
11557184814Sflorian 		    __attribute__((__format__ (printf, 1, 2)));
11657184814Sflorian void		syslog_info(const char *, ...)
11757184814Sflorian 		    __attribute__((__format__ (printf, 1, 2)));
11857184814Sflorian void		syslog_debug(const char *, ...)
11957184814Sflorian 		    __attribute__((__format__ (printf, 1, 2)));
12057184814Sflorian void		syslog_vstrerror(int, int, const char *, va_list)
12157184814Sflorian 		    __attribute__((__format__ (printf, 3, 0)));
12257184814Sflorian 
12384b09d08Sderaadt int verbose = 0;
12484b09d08Sderaadt 
12557184814Sflorian const struct loggers syslogger = {
12657184814Sflorian 	syslog_err,
12757184814Sflorian 	syslog_errx,
12857184814Sflorian 	syslog_warn,
12957184814Sflorian 	syslog_warnx,
13057184814Sflorian 	syslog_info,
13157184814Sflorian 	syslog_debug
13257184814Sflorian };
13357184814Sflorian 
13457184814Sflorian const struct loggers *logger = &conslogger;
13557184814Sflorian 
13657184814Sflorian #define lerr(_e, _f...) logger->err((_e), _f)
13757184814Sflorian #define lerrx(_e, _f...) logger->errx((_e), _f)
13857184814Sflorian #define lwarn(_f...) logger->warn(_f)
13957184814Sflorian #define lwarnx(_f...) logger->warnx(_f)
14057184814Sflorian #define linfo(_f...) logger->info(_f)
14157184814Sflorian #define ldebug(_f...) logger->debug(_f)
14257184814Sflorian #else
14357184814Sflorian #define lerr(x...) do {} while(0)
14457184814Sflorian #define lerrx(x...) do {} while(0)
14557184814Sflorian #define lwarn(x...) do {} while(0)
14657184814Sflorian #define lwarnx(x...) do {} while(0)
14757184814Sflorian #define linfo(x...) do {} while(0)
14857184814Sflorian #define ldebug(x...) do {} while(0)
14957184814Sflorian #endif /* SMALL */
15057184814Sflorian 
15184b09d08Sderaadt enum {
15284b09d08Sderaadt 	KQ_ROUTE,
15384b09d08Sderaadt 	KQ_RESOLVE_CONF,
154089d2496Skn #ifndef SMALL
15584b09d08Sderaadt 	KQ_UNWIND,
156089d2496Skn #endif
157089d2496Skn 	KQ_TOTAL
15884b09d08Sderaadt };
15984b09d08Sderaadt 
16057184814Sflorian int
main(int argc,char * argv[])16157184814Sflorian main(int argc, char *argv[])
16257184814Sflorian {
16357184814Sflorian 	struct timespec		 one = {1, 0};
16484b09d08Sderaadt 	int			 kq, ch, debug = 0, routesock;
16584b09d08Sderaadt 	int			 rtfilter, nready, lockfd;
166089d2496Skn 	struct kevent		 kev[KQ_TOTAL];
16784b09d08Sderaadt #ifndef SMALL
1686700498eSderaadt 	int			 unwindsock = -1;
16984b09d08Sderaadt #endif
17057184814Sflorian 
17184b09d08Sderaadt 	while ((ch = getopt(argc, argv, "dv")) != -1) {
17257184814Sflorian 		switch (ch) {
17357184814Sflorian 		case 'd':
17457184814Sflorian 			debug = 1;
17557184814Sflorian 			break;
17657184814Sflorian 		case 'v':
1778cd3cf43Skn #ifndef SMALL
17857184814Sflorian 			verbose++;
1798cd3cf43Skn #endif
18057184814Sflorian 			break;
18157184814Sflorian 		default:
18257184814Sflorian 			usage();
18357184814Sflorian 		}
18457184814Sflorian 	}
18557184814Sflorian 
18657184814Sflorian 	argc -= optind;
18757184814Sflorian 	argv += optind;
18857184814Sflorian 	if (argc > 0)
18957184814Sflorian 		usage();
19057184814Sflorian 
19157184814Sflorian 	/* Check for root privileges. */
19257184814Sflorian 	if (geteuid())
19357184814Sflorian 		errx(1, "need root privileges");
19457184814Sflorian 
19584b09d08Sderaadt 	lockfd = open(_PATH_LOCKFILE, O_CREAT|O_RDWR|O_EXLOCK|O_NONBLOCK, 0600);
1961c0cb702Skn 	if (lockfd == -1) {
1971c0cb702Skn 		if (errno == EAGAIN)
198bc9eb55cSkn 			errx(1, "already running");
1991c0cb702Skn 		err(1, "%s", _PATH_LOCKFILE);
2001c0cb702Skn 	}
20184b09d08Sderaadt 
20257184814Sflorian 	if (!debug)
20357184814Sflorian 		daemon(0, 0);
20457184814Sflorian 
2058cd3cf43Skn #ifndef SMALL
20657184814Sflorian 	if (!debug) {
20757184814Sflorian 		openlog("resolvd", LOG_PID|LOG_NDELAY, LOG_DAEMON);
20857184814Sflorian 		logger = &syslogger;
20957184814Sflorian 	}
2108cd3cf43Skn #endif
21157184814Sflorian 
212b32a2ee4Skn 	signal(SIGHUP, SIG_IGN);
213b32a2ee4Skn 
21457184814Sflorian 	if ((routesock = socket(AF_ROUTE, SOCK_RAW, 0)) == -1)
21557184814Sflorian 		lerr(1, "route socket");
21657184814Sflorian 
21757184814Sflorian 	rtfilter = ROUTE_FILTER(RTM_PROPOSAL) | ROUTE_FILTER(RTM_IFANNOUNCE);
21857184814Sflorian 	if (setsockopt(routesock, AF_ROUTE, ROUTE_MSGFILTER, &rtfilter,
21957184814Sflorian 	    sizeof(rtfilter)) == -1)
22057184814Sflorian 		lerr(1, "setsockopt(ROUTE_MSGFILTER)");
22157184814Sflorian 
22284b09d08Sderaadt 	solicit_dns_proposals(routesock);
22357184814Sflorian 
2244a8e8d61Skn 	if (unveil(_PATH_RESCONF, "rwc") == -1)
2254a8e8d61Skn 		lerr(1, "unveil " _PATH_RESCONF);
2264a8e8d61Skn 	if (unveil(_PATH_RESCONF_NEW, "rwc") == -1)
2274a8e8d61Skn 		lerr(1, "unveil " _PATH_RESCONF_NEW);
228c6a3fbaeSkn #ifndef SMALL
2296feeaaf0Smartijn 	if (unveil(_PATH_UNWIND_SOCKET, "w") == -1)
23084b09d08Sderaadt 		lerr(1, "unveil " _PATH_UNWIND_SOCKET);
231c6a3fbaeSkn #endif
23284b09d08Sderaadt 
23384b09d08Sderaadt 	if (pledge("stdio unix rpath wpath cpath", NULL) == -1)
23457184814Sflorian 		lerr(1, "pledge");
23557184814Sflorian 
23684b09d08Sderaadt 	if ((kq = kqueue()) == -1)
23757184814Sflorian 		lerr(1, "kqueue");
23857184814Sflorian 
23957184814Sflorian 	for(;;) {
24084b09d08Sderaadt 		int	i;
24184b09d08Sderaadt 
24257184814Sflorian #ifndef SMALL
24357184814Sflorian 		if (!unwind_running && check_unwind) {
24457184814Sflorian 			check_unwind = 0;
24584b09d08Sderaadt 			unwindsock = open_unwind_ctl();
24684b09d08Sderaadt 			unwind_running = unwindsock != -1;
24757184814Sflorian 			if (unwind_running)
24884b09d08Sderaadt 				regen_resolvconf("new unwind");
24984b09d08Sderaadt 		}
25084b09d08Sderaadt #endif
25184b09d08Sderaadt 
25284b09d08Sderaadt 		if (newkevent) {
25384b09d08Sderaadt 			int kevi = 0;
25484b09d08Sderaadt 
25584b09d08Sderaadt 			if (routesock != -1)
25684b09d08Sderaadt 				EV_SET(&kev[kevi++], routesock, EVFILT_READ,
25784b09d08Sderaadt 				    EV_ADD, 0, 0,
25884b09d08Sderaadt 				    (void *)KQ_ROUTE);
25984b09d08Sderaadt 			if (resolvfd != -1)
26084b09d08Sderaadt 				EV_SET(&kev[kevi++], resolvfd, EVFILT_VNODE,
26184b09d08Sderaadt 				    EV_ADD | EV_CLEAR,
26284b09d08Sderaadt 				    NOTE_DELETE | NOTE_RENAME | NOTE_TRUNCATE | NOTE_WRITE, 0,
26384b09d08Sderaadt 				    (void *)KQ_RESOLVE_CONF);
26484b09d08Sderaadt 
26584b09d08Sderaadt #ifndef SMALL
26684b09d08Sderaadt 			if (unwind_running) {
26784b09d08Sderaadt 				EV_SET(&kev[kevi++], unwindsock, EVFILT_READ,
26884b09d08Sderaadt 				    EV_ADD, 0, 0,
26984b09d08Sderaadt 				    (void *)KQ_UNWIND);
27057184814Sflorian 			}
27157184814Sflorian #endif /* SMALL */
27284b09d08Sderaadt 
27384b09d08Sderaadt 			if (kevent(kq, kev, kevi, NULL, 0, NULL) == -1)
27484b09d08Sderaadt 				lerr(1, "kevent");
27584b09d08Sderaadt 			newkevent = 0;
27657184814Sflorian 		}
27784b09d08Sderaadt 
278089d2496Skn 		nready = kevent(kq, NULL, 0, kev, KQ_TOTAL, NULL);
27957184814Sflorian 		if (nready == -1) {
28057184814Sflorian 			if (errno == EINTR)
28157184814Sflorian 				continue;
28284b09d08Sderaadt 			lerr(1, "kevent");
28357184814Sflorian 		}
28457184814Sflorian 
28557184814Sflorian 		if (nready == 0)
28657184814Sflorian 			continue;
28757184814Sflorian 
28884b09d08Sderaadt 		for (i = 0; i < nready; i++) {
28984b09d08Sderaadt 			unsigned short fflags = kev[i].fflags;
29057184814Sflorian 
2916700498eSderaadt 			switch ((int)(long)kev[i].udata) {
29284b09d08Sderaadt 			case KQ_ROUTE:
29384b09d08Sderaadt 				route_receive(routesock);
29484b09d08Sderaadt 				break;
29584b09d08Sderaadt 
29684b09d08Sderaadt 			case KQ_RESOLVE_CONF:
29784b09d08Sderaadt 				if (fflags & (NOTE_DELETE | NOTE_RENAME)) {
29884b09d08Sderaadt 					close(resolvfd);
29984b09d08Sderaadt 					resolvfd = -1;
30084b09d08Sderaadt 					regen_resolvconf("file delete/rename");
30157184814Sflorian 				}
30284b09d08Sderaadt 				if (fflags & (NOTE_TRUNCATE | NOTE_WRITE)) {
30384b09d08Sderaadt 					/* some editors truncate and write */
30484b09d08Sderaadt 					if (fflags & NOTE_TRUNCATE)
30584b09d08Sderaadt 						nanosleep(&one, NULL);
30684b09d08Sderaadt 					regen_resolvconf("file trunc/write");
30757184814Sflorian 				}
30884b09d08Sderaadt 				break;
30984b09d08Sderaadt 
31057184814Sflorian #ifndef SMALL
31184b09d08Sderaadt 			case KQ_UNWIND: {
31284b09d08Sderaadt 				uint8_t buf[1024];
31357184814Sflorian 				ssize_t	n;
31484b09d08Sderaadt 
31584b09d08Sderaadt 				n = read(unwindsock, buf, sizeof(buf));
31684b09d08Sderaadt 				if (n == -1) {
31757184814Sflorian 					if (errno == EAGAIN || errno == EINTR)
31857184814Sflorian 						continue;
31957184814Sflorian 				}
32057184814Sflorian 				if (n == 0 || n == -1) {
32157184814Sflorian 					if (n == -1)
32257184814Sflorian 						check_unwind = 1;
32384b09d08Sderaadt 					newkevent = 1;
32484b09d08Sderaadt 					close(unwindsock);
32584b09d08Sderaadt 					unwindsock = -1;
32657184814Sflorian 					unwind_running = 0;
32784b09d08Sderaadt 					regen_resolvconf("unwind closed");
32857184814Sflorian 				} else
32957184814Sflorian 					lwarnx("read %ld from unwind ctl", n);
33084b09d08Sderaadt 				break;
33157184814Sflorian 			}
33284b09d08Sderaadt #endif
33384b09d08Sderaadt 
33484b09d08Sderaadt 			default:
33584b09d08Sderaadt 				lwarnx("unknown kqueue event on %lu",
33684b09d08Sderaadt 				    kev[i].ident);
33757184814Sflorian 			}
33884b09d08Sderaadt 		}
33984b09d08Sderaadt 	}
34084b09d08Sderaadt 	return 0;
34157184814Sflorian }
34257184814Sflorian 
34357184814Sflorian __dead void
usage(void)34457184814Sflorian usage(void)
34557184814Sflorian {
34657184814Sflorian 	fprintf(stderr, "usage: resolvd [-dv]\n");
34757184814Sflorian 	exit(1);
34857184814Sflorian }
34957184814Sflorian 
35057184814Sflorian void
route_receive(int fd)35157184814Sflorian route_receive(int fd)
35257184814Sflorian {
35384b09d08Sderaadt 	uint8_t			 rsock_buf[ROUTE_SOCKET_BUF_SIZE];
35457184814Sflorian 	struct sockaddr		*sa, *rti_info[RTAX_MAX];
35584b09d08Sderaadt 	struct rt_msghdr	*rtm;
35657184814Sflorian 	ssize_t			 n;
35757184814Sflorian 
35857184814Sflorian 	rtm = (struct rt_msghdr *) rsock_buf;
35957184814Sflorian 	if ((n = read(fd, rsock_buf, sizeof(rsock_buf))) == -1) {
36057184814Sflorian 		if (errno == EAGAIN || errno == EINTR)
36157184814Sflorian 			return;
362186cf4deSkn 		lwarn("%s: read error", __func__);
36357184814Sflorian 		return;
36457184814Sflorian 	}
36557184814Sflorian 
36657184814Sflorian 	if (n == 0)
36757184814Sflorian 		lerr(1, "routing socket closed");
36857184814Sflorian 
36957184814Sflorian 	if (n < (ssize_t)sizeof(rtm->rtm_msglen) || n < rtm->rtm_msglen) {
37057184814Sflorian 		lwarnx("partial rtm of %zd in buffer", n);
37157184814Sflorian 		return;
37257184814Sflorian 	}
37357184814Sflorian 
37457184814Sflorian 	if (rtm->rtm_version != RTM_VERSION)
37557184814Sflorian 		return;
37657184814Sflorian 
37784b09d08Sderaadt 	if (rtm->rtm_pid == getpid())
37884b09d08Sderaadt 		return;
37984b09d08Sderaadt 
38057184814Sflorian 	sa = (struct sockaddr *)(rsock_buf + rtm->rtm_hdrlen);
38157184814Sflorian 	get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
38257184814Sflorian 	handle_route_message(rtm, rti_info);
38357184814Sflorian }
38457184814Sflorian 
38557184814Sflorian void
zeroslot(struct rdns_proposal * tab)38684b09d08Sderaadt zeroslot(struct rdns_proposal *tab)
38784b09d08Sderaadt {
38884b09d08Sderaadt 	tab->prio = 0;
38984b09d08Sderaadt 	tab->af = 0;
39084b09d08Sderaadt 	tab->if_index = 0;
39184b09d08Sderaadt 	tab->ip[0] = '\0';
39284b09d08Sderaadt }
39384b09d08Sderaadt 
39484b09d08Sderaadt int
findslot(struct rdns_proposal * tab)39584b09d08Sderaadt findslot(struct rdns_proposal *tab)
39684b09d08Sderaadt {
39784b09d08Sderaadt 	int i;
39884b09d08Sderaadt 
39984b09d08Sderaadt 	for (i = 0; i < ASR_MAXNS; i++)
40084b09d08Sderaadt 		if (tab[i].prio == 0)
40184b09d08Sderaadt 			return i;
40284b09d08Sderaadt 
40384b09d08Sderaadt 	/* New proposals might be important, so replace the last slot */
40484b09d08Sderaadt 	i = ASR_MAXNS - 1;
40584b09d08Sderaadt 	zeroslot(&tab[i]);
40684b09d08Sderaadt 	return i;
40784b09d08Sderaadt }
40884b09d08Sderaadt 
40984b09d08Sderaadt void
handle_route_message(struct rt_msghdr * rtm,struct sockaddr ** rti_info)41057184814Sflorian handle_route_message(struct rt_msghdr *rtm, struct sockaddr **rti_info)
41157184814Sflorian {
41239a47af7Sderaadt 	struct rdns_proposal		 learning[nitems(learned)];
41357184814Sflorian 	struct sockaddr_rtdns		*rtdns;
41457184814Sflorian 	struct if_announcemsghdr	*ifan;
4159be8707cSkn 	size_t				 addrsz;
41684b09d08Sderaadt 	int				 rdns_count, af, i;
41757184814Sflorian 	char				*src;
41857184814Sflorian 
41984b09d08Sderaadt 	memcpy(learning, learned, sizeof learned);
42057184814Sflorian 
42157184814Sflorian 	switch (rtm->rtm_type) {
42257184814Sflorian 	case RTM_IFANNOUNCE:
42357184814Sflorian 		ifan = (struct if_announcemsghdr *)rtm;
42457184814Sflorian 		if (ifan->ifan_what == IFAN_ARRIVAL)
42557184814Sflorian 			return;
42684b09d08Sderaadt 		/* Delete proposals learned from departing interfaces */
42784b09d08Sderaadt 		for (i = 0; i < ASR_MAXNS; i++)
42884b09d08Sderaadt 			if (learning[i].if_index == ifan->ifan_index)
42984b09d08Sderaadt 				zeroslot(&learning[i]);
43057184814Sflorian 		break;
43157184814Sflorian 	case RTM_PROPOSAL:
43257184814Sflorian 		if (rtm->rtm_priority == RTP_PROPOSAL_SOLICIT) {
43357184814Sflorian #ifndef SMALL
43457184814Sflorian 			check_unwind = 1;
43557184814Sflorian #endif /* SMALL */
43657184814Sflorian 			return;
43757184814Sflorian 		}
43884b09d08Sderaadt 
43957184814Sflorian 		if (!(rtm->rtm_addrs & RTA_DNS))
44057184814Sflorian 			return;
44157184814Sflorian 
44257184814Sflorian 		rtdns = (struct sockaddr_rtdns*)rti_info[RTAX_DNS];
44357184814Sflorian 		src = rtdns->sr_dns;
44457184814Sflorian 		af = rtdns->sr_family;
44557184814Sflorian 
44657184814Sflorian 		switch (af) {
44757184814Sflorian 		case AF_INET:
4489be8707cSkn 			addrsz = sizeof(struct in_addr);
44957184814Sflorian 			break;
45057184814Sflorian 		case AF_INET6:
4519be8707cSkn 			addrsz = sizeof(struct in6_addr);
45257184814Sflorian 			break;
45357184814Sflorian 		default:
45457184814Sflorian 			lwarnx("ignoring invalid RTM_PROPOSAL");
45557184814Sflorian 			return;
45657184814Sflorian 		}
45757184814Sflorian 
4589be8707cSkn 		if ((rtdns->sr_len - 2) % addrsz != 0) {
4599be8707cSkn 			lwarnx("ignoring invalid RTM_PROPOSAL");
4609be8707cSkn 			return;
4619be8707cSkn 		}
4629be8707cSkn 		rdns_count = (rtdns->sr_len -
4639be8707cSkn 		    offsetof(struct sockaddr_rtdns, sr_dns)) / addrsz;
4649be8707cSkn 
46584b09d08Sderaadt 		/* New proposal from interface means previous proposals expire */
46684b09d08Sderaadt 		for (i = 0; i < ASR_MAXNS; i++)
46784b09d08Sderaadt 			if (learning[i].af == af &&
46884b09d08Sderaadt 			    learning[i].if_index == rtm->rtm_index)
46984b09d08Sderaadt 				zeroslot(&learning[i]);
47084b09d08Sderaadt 
47184b09d08Sderaadt 		/* Add the new proposals */
47284b09d08Sderaadt 		for (i = 0; i < rdns_count; i++) {
4733a8def13Skn 			struct sockaddr_storage ss;
4743a8def13Skn 			struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
4753a8def13Skn 			struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
4769be8707cSkn 			int new, err;
47784b09d08Sderaadt 
4783a8def13Skn 			memset(&ss, 0, sizeof(ss));
4793a8def13Skn 			ss.ss_family = af;
4809be8707cSkn 			new = findslot(learning);
48157184814Sflorian 			switch (af) {
48257184814Sflorian 			case AF_INET:
4833a8def13Skn 				memcpy(&sin->sin_addr, src, addrsz);
4840df14189Skn 				ss.ss_len = sizeof(*sin);
48557184814Sflorian 				break;
48657184814Sflorian 			case AF_INET6:
4873a8def13Skn 				memcpy(&sin6->sin6_addr, src, addrsz);
4883a8def13Skn 				if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
4893a8def13Skn 					sin6->sin6_scope_id = rtm->rtm_index;
4900df14189Skn 				ss.ss_len = sizeof(*sin6);
4919be8707cSkn 				break;
4929be8707cSkn 			}
4939be8707cSkn 			src += addrsz;
4949be8707cSkn 
4953a8def13Skn 			if ((err = getnameinfo((struct sockaddr *)&ss, ss.ss_len,
496162e527eSkn 			    learning[new].ip, sizeof(learning[new].ip),
4979be8707cSkn 			    NULL, 0, NI_NUMERICHOST)) == 0) {
49884b09d08Sderaadt 				learning[new].prio = rtm->rtm_priority;
49984b09d08Sderaadt 				learning[new].if_index = rtm->rtm_index;
50084b09d08Sderaadt 				learning[new].af = af;
50157184814Sflorian 			} else
5029be8707cSkn 				lwarnx("getnameinfo: %s", gai_strerror(err));
50357184814Sflorian 		}
50457184814Sflorian 		break;
50557184814Sflorian 	default:
50657184814Sflorian 		return;
50757184814Sflorian 	}
50857184814Sflorian 
5096a845ff2Skn 	/* Sort proposals, based upon priority */
5106a845ff2Skn 	if (mergesort(learning, ASR_MAXNS, sizeof(learning[0]), cmp) == -1) {
5116a845ff2Skn 		lwarn("mergesort");
5126a845ff2Skn 		return;
5136a845ff2Skn 	}
51457184814Sflorian 
5156a845ff2Skn 	/* Eliminate duplicate IPs per interface */
51684b09d08Sderaadt 	for (i = 0; i < ASR_MAXNS - 1; i++) {
5176a845ff2Skn 		int j;
5186a845ff2Skn 
51984b09d08Sderaadt 		if (learning[i].prio == 0)
52084b09d08Sderaadt 			continue;
5216a845ff2Skn 
5226a845ff2Skn 		for (j = i + 1; j < ASR_MAXNS; j++) {
5236a845ff2Skn 			if (learning[i].if_index == learning[j].if_index &&
5246a845ff2Skn 			    strcmp(learning[i].ip, learning[j].ip) == 0) {
5256a845ff2Skn 				zeroslot(&learning[j]);
5266a845ff2Skn 			}
52784b09d08Sderaadt 		}
52857184814Sflorian 	}
52957184814Sflorian 
53084b09d08Sderaadt 	/* If proposal result is different, rebuild the file */
53184b09d08Sderaadt 	if (memcmp(learned, learning, sizeof(learned)) != 0) {
53284b09d08Sderaadt 		memcpy(learned, learning, sizeof(learned));
53384b09d08Sderaadt 		regen_resolvconf("route proposals");
53484b09d08Sderaadt 	}
53557184814Sflorian }
53657184814Sflorian 
53757184814Sflorian #define ROUNDUP(a) \
53857184814Sflorian 	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
53957184814Sflorian 
54057184814Sflorian void
get_rtaddrs(int addrs,struct sockaddr * sa,struct sockaddr ** rti_info)54157184814Sflorian get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
54257184814Sflorian {
54357184814Sflorian 	int	i;
54457184814Sflorian 
54557184814Sflorian 	for (i = 0; i < RTAX_MAX; i++) {
54657184814Sflorian 		if (addrs & (1 << i)) {
54757184814Sflorian 			rti_info[i] = sa;
54857184814Sflorian 			sa = (struct sockaddr *)((char *)(sa) +
54957184814Sflorian 			    ROUNDUP(sa->sa_len));
55057184814Sflorian 		} else
55157184814Sflorian 			rti_info[i] = NULL;
55257184814Sflorian 	}
55357184814Sflorian }
55457184814Sflorian 
55557184814Sflorian void
solicit_dns_proposals(int routesock)55657184814Sflorian solicit_dns_proposals(int routesock)
55757184814Sflorian {
55857184814Sflorian 	struct rt_msghdr	 rtm;
55957184814Sflorian 	struct iovec		 iov[1];
56057184814Sflorian 	int			 iovcnt = 0;
56157184814Sflorian 
56257184814Sflorian 	memset(&rtm, 0, sizeof(rtm));
56357184814Sflorian 
56457184814Sflorian 	rtm.rtm_version = RTM_VERSION;
56557184814Sflorian 	rtm.rtm_type = RTM_PROPOSAL;
56657184814Sflorian 	rtm.rtm_msglen = sizeof(rtm);
56757184814Sflorian 	rtm.rtm_tableid = 0;
56857184814Sflorian 	rtm.rtm_index = 0;
56957184814Sflorian 	rtm.rtm_seq = arc4random();
57057184814Sflorian 	rtm.rtm_priority = RTP_PROPOSAL_SOLICIT;
57157184814Sflorian 
57257184814Sflorian 	iov[iovcnt].iov_base = &rtm;
57357184814Sflorian 	iov[iovcnt++].iov_len = sizeof(rtm);
57457184814Sflorian 
57557184814Sflorian 	if (writev(routesock, iov, iovcnt) == -1)
57657184814Sflorian 		lwarn("failed to send solicitation");
57757184814Sflorian }
57857184814Sflorian 
57957184814Sflorian void
regen_resolvconf(const char * why)5806a845ff2Skn regen_resolvconf(const char *why)
58157184814Sflorian {
5823a57ef91Sflorian 	struct iovec	 iov[UIO_MAXIOV];
5833a57ef91Sflorian 	int		 i, fd, len, iovcnt = 0;
58457184814Sflorian 
58584b09d08Sderaadt 	linfo("rebuilding: %s", why);
58657184814Sflorian 
58784b09d08Sderaadt 	if ((fd = open(_PATH_RESCONF_NEW, O_CREAT|O_TRUNC|O_RDWR, 0644)) == -1) {
58884b09d08Sderaadt 		lwarn(_PATH_RESCONF_NEW);
58957184814Sflorian 		return;
59057184814Sflorian 	}
59184b09d08Sderaadt 
5923a57ef91Sflorian 	memset(iov, 0, sizeof(iov));
5933a57ef91Sflorian 
59457184814Sflorian #ifndef SMALL
5953a57ef91Sflorian 	if (unwind_running) {
5963a57ef91Sflorian 		len = asprintf((char **)&iov[iovcnt].iov_base,
5973a57ef91Sflorian 		    "nameserver 127.0.0.1 # resolvd: unwind\n");
5983a57ef91Sflorian 		if (len < 0) {
5993a57ef91Sflorian 			lwarn("asprintf");
6003a57ef91Sflorian 			goto err;
6013a57ef91Sflorian 		}
6023a57ef91Sflorian 		iov[iovcnt++].iov_len = len;
6033a57ef91Sflorian 	}
60484b09d08Sderaadt 
60557184814Sflorian #endif /* SMALL */
60657184814Sflorian 	for (i = 0; i < ASR_MAXNS; i++) {
60784b09d08Sderaadt 		if (learned[i].prio != 0) {
60884b09d08Sderaadt 			char ifnambuf[IF_NAMESIZE], *ifnam;
60984b09d08Sderaadt 
61084b09d08Sderaadt 			ifnam = if_indextoname(learned[i].if_index,
61184b09d08Sderaadt 			    ifnambuf);
6123a57ef91Sflorian 			len = asprintf((char **)&iov[iovcnt].iov_base,
6133a57ef91Sflorian 			    "%snameserver %s # resolvd: %s\n",
6148cd3cf43Skn #ifndef SMALL
61584b09d08Sderaadt 			    unwind_running ? "#" : "",
6168cd3cf43Skn #else
6178cd3cf43Skn 			    "",
6188cd3cf43Skn #endif
61984b09d08Sderaadt 			    learned[i].ip,
62084b09d08Sderaadt 			    ifnam ? ifnam : "");
6213a57ef91Sflorian 			if (len < 0) {
6223a57ef91Sflorian 				lwarn("asprintf");
6233a57ef91Sflorian 				goto err;
6243a57ef91Sflorian 			}
6253a57ef91Sflorian 			iov[iovcnt++].iov_len = len;
62657184814Sflorian 		}
62757184814Sflorian 	}
62857184814Sflorian 
62984b09d08Sderaadt 	/* Replay user-managed lines from old resolv.conf file */
63084b09d08Sderaadt 	if (resolvfd == -1)
63184b09d08Sderaadt 		resolvfd = open(_PATH_RESCONF, O_RDWR);
63284b09d08Sderaadt 	if (resolvfd != -1) {
63384b09d08Sderaadt 		char *line = NULL;
63484b09d08Sderaadt 		size_t linesize = 0;
63584b09d08Sderaadt 		ssize_t linelen;
63684b09d08Sderaadt 		FILE *fp;
637b46cfa51Sderaadt 		int fd2;
63884b09d08Sderaadt 
639b46cfa51Sderaadt 		if ((fd2 = dup(resolvfd)) == -1)
64084b09d08Sderaadt 			goto err;
641b46cfa51Sderaadt 		lseek(fd2, 0, SEEK_SET);
642b46cfa51Sderaadt 		fp = fdopen(fd2, "r");
643b46cfa51Sderaadt 		if (fp == NULL) {
644b46cfa51Sderaadt 			close(fd2);
645b46cfa51Sderaadt 			goto err;
646b46cfa51Sderaadt 		}
64784b09d08Sderaadt 		while ((linelen = getline(&line, &linesize, fp)) != -1) {
64884b09d08Sderaadt 			char *end = strchr(line, '\n');
64984b09d08Sderaadt 			if (end)
65084b09d08Sderaadt 				*end = '\0';
65184b09d08Sderaadt 			if (strstr(line, "# resolvd: "))
65284b09d08Sderaadt 				continue;
6533a57ef91Sflorian 			len = asprintf((char **)&iov[iovcnt].iov_base, "%s\n",
6543a57ef91Sflorian 			    line);
6553a57ef91Sflorian 			if (len < 0) {
6563a57ef91Sflorian 				lwarn("asprintf");
657*8475ebacStb 				free(line);
658*8475ebacStb 				fclose(fp);
6593a57ef91Sflorian 				goto err;
6603a57ef91Sflorian 			}
6613a57ef91Sflorian 			iov[iovcnt++].iov_len = len;
6623a57ef91Sflorian 			if (iovcnt >= UIO_MAXIOV) {
6633a57ef91Sflorian 				lwarnx("too many user-managed lines");
664*8475ebacStb 				free(line);
665*8475ebacStb 				fclose(fp);
6663a57ef91Sflorian 				goto err;
6673a57ef91Sflorian 			}
66857184814Sflorian 		}
66984b09d08Sderaadt 		free(line);
670b46cfa51Sderaadt 		fclose(fp);
67157184814Sflorian 	}
67257184814Sflorian 
6736f6c5cbbSotto 	if (iovcnt > 0) {
6743a57ef91Sflorian 		if (writev(fd, iov, iovcnt) == -1) {
6753a57ef91Sflorian 			lwarn("writev");
6763a57ef91Sflorian 			goto err;
6773a57ef91Sflorian 		}
6786f6c5cbbSotto 	}
6793a57ef91Sflorian 
6803a57ef91Sflorian 	if (fsync(fd) == -1) {
6813a57ef91Sflorian 		lwarn("fsync");
6823a57ef91Sflorian 		goto err;
6833a57ef91Sflorian 	}
68484b09d08Sderaadt 	if (rename(_PATH_RESCONF_NEW, _PATH_RESCONF) == -1)
68557184814Sflorian 		goto err;
68657184814Sflorian 
68784b09d08Sderaadt 	if (resolvfd == -1) {
68857184814Sflorian 		close(fd);
6896f6c5cbbSotto 		resolvfd = open(_PATH_RESCONF, O_RDWR);
69084b09d08Sderaadt 	} else {
69184b09d08Sderaadt 		dup2(fd, resolvfd);
69284b09d08Sderaadt 		close(fd);
69357184814Sflorian 	}
69457184814Sflorian 
69584b09d08Sderaadt 	newkevent = 1;
6963a57ef91Sflorian 	goto out;
69784b09d08Sderaadt 
69857184814Sflorian  err:
69957184814Sflorian 	if (fd != -1)
70057184814Sflorian 		close(fd);
70184b09d08Sderaadt 	unlink(_PATH_RESCONF_NEW);
7023a57ef91Sflorian  out:
7033a57ef91Sflorian 	for (i = 0; i < iovcnt; i++)
7043a57ef91Sflorian 		free(iov[i].iov_base);
7053a57ef91Sflorian 
70657184814Sflorian }
70757184814Sflorian 
70857184814Sflorian int
cmp(const void * a,const void * b)70957184814Sflorian cmp(const void *a, const void *b)
71057184814Sflorian {
711ef53e9e5Sderaadt 	const struct rdns_proposal	*rpa = a, *rpb = b;
71257184814Sflorian 
7136a845ff2Skn 	return (rpa->prio < rpb->prio) ? -1 : (rpa->prio > rpb->prio);
71457184814Sflorian }
71557184814Sflorian 
71657184814Sflorian #ifndef SMALL
71757184814Sflorian int
open_unwind_ctl(void)71857184814Sflorian open_unwind_ctl(void)
71957184814Sflorian {
72057184814Sflorian 	static struct sockaddr_un	 sun;
72157184814Sflorian 	int				 s;
72257184814Sflorian 
72357184814Sflorian 	if (sun.sun_family == 0) {
72457184814Sflorian 		sun.sun_family = AF_UNIX;
72584b09d08Sderaadt 		strlcpy(sun.sun_path, _PATH_UNWIND_SOCKET, sizeof(sun.sun_path));
72657184814Sflorian 	}
72757184814Sflorian 
72857184814Sflorian 	if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) {
72957184814Sflorian 		if (connect(s, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
73057184814Sflorian 			close(s);
73157184814Sflorian 			s = -1;
73257184814Sflorian 		}
73357184814Sflorian 	}
73484b09d08Sderaadt 	newkevent = 1;
73557184814Sflorian 	return s;
73657184814Sflorian }
73757184814Sflorian 
73857184814Sflorian void
syslog_vstrerror(int e,int priority,const char * fmt,va_list ap)73957184814Sflorian syslog_vstrerror(int e, int priority, const char *fmt, va_list ap)
74057184814Sflorian {
74157184814Sflorian 	char *s;
74257184814Sflorian 
74357184814Sflorian 	if (vasprintf(&s, fmt, ap) == -1) {
74457184814Sflorian 		syslog(LOG_EMERG, "unable to alloc in syslog_vstrerror");
74557184814Sflorian 		exit(1);
74657184814Sflorian 	}
74757184814Sflorian 	syslog(priority, "%s: %s", s, strerror(e));
74857184814Sflorian 	free(s);
74957184814Sflorian }
75057184814Sflorian 
75157184814Sflorian __dead void
syslog_err(int ecode,const char * fmt,...)75257184814Sflorian syslog_err(int ecode, const char *fmt, ...)
75357184814Sflorian {
75457184814Sflorian 	va_list ap;
75557184814Sflorian 
75657184814Sflorian 	va_start(ap, fmt);
75757184814Sflorian 	syslog_vstrerror(errno, LOG_CRIT, fmt, ap);
75857184814Sflorian 	va_end(ap);
75957184814Sflorian 	exit(ecode);
76057184814Sflorian }
76157184814Sflorian 
76257184814Sflorian __dead void
syslog_errx(int ecode,const char * fmt,...)76357184814Sflorian syslog_errx(int ecode, const char *fmt, ...)
76457184814Sflorian {
76557184814Sflorian 	va_list ap;
76657184814Sflorian 
76757184814Sflorian 	va_start(ap, fmt);
76857184814Sflorian 	vsyslog(LOG_CRIT, fmt, ap);
76957184814Sflorian 	va_end(ap);
77057184814Sflorian 	exit(ecode);
77157184814Sflorian }
77257184814Sflorian 
77357184814Sflorian void
syslog_warn(const char * fmt,...)77457184814Sflorian syslog_warn(const char *fmt, ...)
77557184814Sflorian {
77657184814Sflorian 	va_list ap;
77757184814Sflorian 
77857184814Sflorian 	va_start(ap, fmt);
77957184814Sflorian 	syslog_vstrerror(errno, LOG_ERR, fmt, ap);
78057184814Sflorian 	va_end(ap);
78157184814Sflorian }
78257184814Sflorian 
78357184814Sflorian void
syslog_warnx(const char * fmt,...)78457184814Sflorian syslog_warnx(const char *fmt, ...)
78557184814Sflorian {
78657184814Sflorian 	va_list ap;
78757184814Sflorian 
78857184814Sflorian 	va_start(ap, fmt);
78957184814Sflorian 	vsyslog(LOG_ERR, fmt, ap);
79057184814Sflorian 	va_end(ap);
79157184814Sflorian }
79257184814Sflorian 
79357184814Sflorian void
syslog_info(const char * fmt,...)79457184814Sflorian syslog_info(const char *fmt, ...)
79557184814Sflorian {
79657184814Sflorian 	va_list ap;
79757184814Sflorian 
79857184814Sflorian 	va_start(ap, fmt);
79957184814Sflorian 	vsyslog(LOG_INFO, fmt, ap);
80057184814Sflorian 	va_end(ap);
80157184814Sflorian }
80257184814Sflorian 
80357184814Sflorian void
syslog_debug(const char * fmt,...)80457184814Sflorian syslog_debug(const char *fmt, ...)
80557184814Sflorian {
80657184814Sflorian 	va_list ap;
80757184814Sflorian 
80857184814Sflorian 	va_start(ap, fmt);
80957184814Sflorian 	vsyslog(LOG_DEBUG, fmt, ap);
81057184814Sflorian 	va_end(ap);
81157184814Sflorian }
81284b09d08Sderaadt 
81384b09d08Sderaadt void
warnx_verbose(const char * fmt,...)81484b09d08Sderaadt warnx_verbose(const char *fmt, ...)
81584b09d08Sderaadt {
81684b09d08Sderaadt 	va_list ap;
81784b09d08Sderaadt 
81884b09d08Sderaadt 	va_start(ap, fmt);
81984b09d08Sderaadt 	if (verbose)
82084b09d08Sderaadt 		vwarnx(fmt, ap);
82184b09d08Sderaadt 	va_end(ap);
82284b09d08Sderaadt }
82384b09d08Sderaadt 
82457184814Sflorian #endif /* SMALL */
825