1290931Srodrigc/*	$OpenBSD: ypldap_dns.c,v 1.8 2015/01/16 06:40:22 deraadt Exp $ */
2290931Srodrigc/*	$FreeBSD: stable/11/usr.sbin/ypldap/ypldap_dns.c 309872 2016-12-12 02:24:54Z araujo $ */
3290931Srodrigc
4290931Srodrigc/*
5290931Srodrigc * Copyright (c) 2003-2008 Henning Brauer <henning@openbsd.org>
6290931Srodrigc *
7290931Srodrigc * Permission to use, copy, modify, and distribute this software for any
8290931Srodrigc * purpose with or without fee is hereby granted, provided that the above
9290931Srodrigc * copyright notice and this permission notice appear in all copies.
10290931Srodrigc *
11290931Srodrigc * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12290931Srodrigc * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13290931Srodrigc * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14290931Srodrigc * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15290931Srodrigc * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16290931Srodrigc * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17290931Srodrigc * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18290931Srodrigc */
19290931Srodrigc
20290931Srodrigc#include <sys/types.h>
21290937Srodrigc#include <sys/param.h>
22290931Srodrigc#include <sys/socket.h>
23290931Srodrigc#include <sys/stat.h>
24290931Srodrigc#include <sys/time.h>
25290931Srodrigc#include <sys/tree.h>
26290931Srodrigc#include <sys/queue.h>
27290931Srodrigc
28290931Srodrigc#include <netinet/in.h>
29290931Srodrigc#include <arpa/nameser.h>
30290931Srodrigc
31290931Srodrigc#include <netdb.h>
32290931Srodrigc#include <pwd.h>
33290931Srodrigc#include <errno.h>
34290931Srodrigc#include <event.h>
35290931Srodrigc#include <resolv.h>
36290931Srodrigc#include <poll.h>
37290931Srodrigc#include <signal.h>
38290931Srodrigc#include <stdlib.h>
39290931Srodrigc#include <string.h>
40290931Srodrigc#include <unistd.h>
41290931Srodrigc#include <limits.h>
42290931Srodrigc
43290931Srodrigc#include "ypldap.h"
44290931Srodrigc
45290931Srodrigcvolatile sig_atomic_t	 quit_dns = 0;
46290931Srodrigcstruct imsgev		*iev_dns;
47290931Srodrigc
48290931Srodrigcvoid	dns_dispatch_imsg(int, short, void *);
49290931Srodrigcvoid	dns_sig_handler(int, short, void *);
50290931Srodrigcvoid	dns_shutdown(void);
51297907Saraujoint	host_dns(const char *, struct ypldap_addr_list *);
52290931Srodrigc
53290931Srodrigcvoid
54290931Srodrigcdns_sig_handler(int sig, short event, void *p)
55290931Srodrigc{
56290931Srodrigc	switch (sig) {
57290931Srodrigc	case SIGINT:
58290931Srodrigc	case SIGTERM:
59290931Srodrigc		dns_shutdown();
60290931Srodrigc		break;
61290931Srodrigc	default:
62290931Srodrigc		fatalx("unexpected signal");
63290931Srodrigc	}
64290931Srodrigc}
65290931Srodrigc
66290931Srodrigcvoid
67290931Srodrigcdns_shutdown(void)
68290931Srodrigc{
69290931Srodrigc	log_info("dns engine exiting");
70290931Srodrigc	_exit(0);
71290931Srodrigc}
72290931Srodrigc
73290931Srodrigcpid_t
74290931Srodrigcypldap_dns(int pipe_ntp[2], struct passwd *pw)
75290931Srodrigc{
76290931Srodrigc	pid_t			 pid;
77290931Srodrigc	struct event	 ev_sigint;
78290931Srodrigc	struct event	 ev_sigterm;
79290931Srodrigc	struct event	 ev_sighup;
80290931Srodrigc	struct env	 env;
81290931Srodrigc
82290931Srodrigc	switch (pid = fork()) {
83290931Srodrigc	case -1:
84290931Srodrigc		fatal("cannot fork");
85290931Srodrigc		break;
86290931Srodrigc	case 0:
87290931Srodrigc		break;
88290931Srodrigc	default:
89290931Srodrigc		return (pid);
90290931Srodrigc	}
91290931Srodrigc
92290931Srodrigc	setproctitle("dns engine");
93290931Srodrigc	close(pipe_ntp[0]);
94290931Srodrigc
95290931Srodrigc	if (setgroups(1, &pw->pw_gid) ||
96290931Srodrigc	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
97290931Srodrigc	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
98290931Srodrigc		fatal("can't drop privileges");
99290931Srodrigc	endservent();
100290931Srodrigc
101290931Srodrigc	event_init();
102290931Srodrigc	signal_set(&ev_sigint, SIGINT, dns_sig_handler, NULL);
103290931Srodrigc	signal_set(&ev_sigterm, SIGTERM, dns_sig_handler, NULL);
104290931Srodrigc	signal_set(&ev_sighup, SIGHUP, dns_sig_handler, NULL);
105290931Srodrigc	signal_add(&ev_sigint, NULL);
106290931Srodrigc	signal_add(&ev_sigterm, NULL);
107290931Srodrigc	signal_add(&ev_sighup, NULL);
108290931Srodrigc
109290931Srodrigc	if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
110290931Srodrigc		fatal(NULL);
111290931Srodrigc
112290931Srodrigc	env.sc_iev->events = EV_READ;
113290931Srodrigc	env.sc_iev->data = &env;
114290931Srodrigc	imsg_init(&env.sc_iev->ibuf, pipe_ntp[1]);
115290931Srodrigc	env.sc_iev->handler = dns_dispatch_imsg;
116290931Srodrigc	event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
117290931Srodrigc	    env.sc_iev->handler, &env);
118290931Srodrigc	event_add(&env.sc_iev->ev, NULL);
119290931Srodrigc
120290931Srodrigc	event_dispatch();
121290931Srodrigc	dns_shutdown();
122290931Srodrigc
123290931Srodrigc	return (0);
124290931Srodrigc}
125290931Srodrigc
126290931Srodrigcvoid
127290931Srodrigcdns_dispatch_imsg(int fd, short events, void *p)
128290931Srodrigc{
129290931Srodrigc	struct imsg		 imsg;
130290931Srodrigc	int			 n, cnt;
131290931Srodrigc	char			*name;
132297907Saraujo	struct ypldap_addr_list	hn = TAILQ_HEAD_INITIALIZER(hn);
133297907Saraujo	struct ypldap_addr	*h;
134290931Srodrigc	struct ibuf		*buf;
135290931Srodrigc	struct env		*env = p;
136290931Srodrigc	struct imsgev		*iev = env->sc_iev;
137290931Srodrigc	struct imsgbuf		*ibuf = &iev->ibuf;
138290931Srodrigc	int			 shut = 0;
139290931Srodrigc
140290931Srodrigc	if ((events & (EV_READ | EV_WRITE)) == 0)
141290931Srodrigc		fatalx("unknown event");
142290931Srodrigc
143290931Srodrigc	if (events & EV_READ) {
144292270Saraujo		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
145290931Srodrigc			fatal("imsg_read error");
146290931Srodrigc		if (n == 0)
147290931Srodrigc			shut = 1;
148290931Srodrigc	}
149290931Srodrigc	if (events & EV_WRITE) {
150290931Srodrigc		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
151290931Srodrigc			fatal("msgbuf_write");
152290931Srodrigc		if (n == 0)
153290931Srodrigc			shut = 1;
154290931Srodrigc		goto done;
155290931Srodrigc	}
156290931Srodrigc
157290931Srodrigc	for (;;) {
158290931Srodrigc		if ((n = imsg_get(ibuf, &imsg)) == -1)
159290931Srodrigc			fatal("client_dispatch_imsg: imsg_get error");
160290931Srodrigc		if (n == 0)
161290931Srodrigc			break;
162290931Srodrigc
163290931Srodrigc		switch (imsg.hdr.type) {
164290931Srodrigc		case IMSG_HOST_DNS:
165290931Srodrigc			name = imsg.data;
166290931Srodrigc			if (imsg.hdr.len < 1 + IMSG_HEADER_SIZE)
167290931Srodrigc				fatalx("invalid IMSG_HOST_DNS received");
168290931Srodrigc			imsg.hdr.len -= 1 + IMSG_HEADER_SIZE;
169290931Srodrigc			if (name[imsg.hdr.len] != '\0' ||
170290931Srodrigc			    strlen(name) != imsg.hdr.len)
171290931Srodrigc				fatalx("invalid IMSG_HOST_DNS received");
172290931Srodrigc			if ((cnt = host_dns(name, &hn)) == -1)
173290931Srodrigc				break;
174290931Srodrigc			buf = imsg_create(ibuf, IMSG_HOST_DNS,
175290931Srodrigc			    imsg.hdr.peerid, 0,
176290931Srodrigc			    cnt * sizeof(struct sockaddr_storage));
177290931Srodrigc			if (buf == NULL)
178290931Srodrigc				break;
179290931Srodrigc			if (cnt > 0) {
180297907Saraujo				while(!TAILQ_EMPTY(&hn)) {
181297907Saraujo					h = TAILQ_FIRST(&hn);
182297907Saraujo					TAILQ_REMOVE(&hn, h, next);
183290931Srodrigc					imsg_add(buf, &h->ss, sizeof(h->ss));
184290931Srodrigc					free(h);
185290931Srodrigc				}
186290931Srodrigc			}
187290931Srodrigc
188290931Srodrigc			imsg_close(ibuf, buf);
189290931Srodrigc			break;
190290931Srodrigc		default:
191290931Srodrigc			break;
192290931Srodrigc		}
193290931Srodrigc		imsg_free(&imsg);
194290931Srodrigc	}
195290931Srodrigc
196290931Srodrigcdone:
197290931Srodrigc	if (!shut)
198290931Srodrigc		imsg_event_add(iev);
199290931Srodrigc	else {
200290931Srodrigc		/* this pipe is dead, so remove the event handler */
201290931Srodrigc		event_del(&iev->ev);
202290931Srodrigc		event_loopexit(NULL);
203290931Srodrigc	}
204290931Srodrigc}
205290931Srodrigc
206290931Srodrigcint
207297907Saraujohost_dns(const char *s, struct ypldap_addr_list *hn)
208290931Srodrigc{
209290931Srodrigc	struct addrinfo		 hints, *res0, *res;
210290931Srodrigc	int			 error, cnt = 0;
211290931Srodrigc	struct sockaddr_in	*sa_in;
212290931Srodrigc	struct sockaddr_in6	*sa_in6;
213297907Saraujo	struct ypldap_addr	*h;
214290931Srodrigc
215309872Saraujo	memset(&hints, 0, sizeof(hints));
216290931Srodrigc	hints.ai_family = PF_UNSPEC;
217290931Srodrigc	hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
218290931Srodrigc	error = getaddrinfo(s, NULL, &hints, &res0);
219290934Srodrigc	if (error == EAI_AGAIN || error == EAI_NONAME)
220290931Srodrigc			return (0);
221290931Srodrigc	if (error) {
222290931Srodrigc		log_warnx("could not parse \"%s\": %s", s,
223290931Srodrigc		    gai_strerror(error));
224290931Srodrigc		return (-1);
225290931Srodrigc	}
226290931Srodrigc
227290931Srodrigc	for (res = res0; res && cnt < MAX_SERVERS_DNS; res = res->ai_next) {
228290931Srodrigc		if (res->ai_family != AF_INET &&
229290931Srodrigc		    res->ai_family != AF_INET6)
230290931Srodrigc			continue;
231290931Srodrigc		if ((h = calloc(1, sizeof(struct ypldap_addr))) == NULL)
232290931Srodrigc			fatal(NULL);
233290931Srodrigc		h->ss.ss_family = res->ai_family;
234290931Srodrigc		if (res->ai_family == AF_INET) {
235290931Srodrigc			sa_in = (struct sockaddr_in *)&h->ss;
236290931Srodrigc			sa_in->sin_len = sizeof(struct sockaddr_in);
237290931Srodrigc			sa_in->sin_addr.s_addr = ((struct sockaddr_in *)
238290931Srodrigc			    res->ai_addr)->sin_addr.s_addr;
239290931Srodrigc		} else {
240290931Srodrigc			sa_in6 = (struct sockaddr_in6 *)&h->ss;
241290931Srodrigc			sa_in6->sin6_len = sizeof(struct sockaddr_in6);
242290931Srodrigc			memcpy(&sa_in6->sin6_addr, &((struct sockaddr_in6 *)
243290931Srodrigc			    res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
244290931Srodrigc		}
245290931Srodrigc
246297907Saraujo		TAILQ_INSERT_HEAD(hn, h, next);
247290931Srodrigc		cnt++;
248290931Srodrigc	}
249290931Srodrigc	freeaddrinfo(res0);
250290931Srodrigc	return (cnt);
251290931Srodrigc}
252