1/*	$OpenBSD: ypldap_dns.c,v 1.8 2015/01/16 06:40:22 deraadt Exp $ */
2/*	$FreeBSD$ */
3
4/*
5 * Copyright (c) 2003-2008 Henning Brauer <henning@openbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/types.h>
21#include <sys/param.h>
22#include <sys/socket.h>
23#include <sys/stat.h>
24#include <sys/time.h>
25#include <sys/tree.h>
26#include <sys/queue.h>
27
28#include <netinet/in.h>
29#include <arpa/nameser.h>
30
31#include <netdb.h>
32#include <pwd.h>
33#include <errno.h>
34#include <event.h>
35#include <resolv.h>
36#include <poll.h>
37#include <signal.h>
38#include <stdlib.h>
39#include <string.h>
40#include <unistd.h>
41#include <limits.h>
42
43#include "ypldap.h"
44
45volatile sig_atomic_t	 quit_dns = 0;
46struct imsgev		*iev_dns;
47
48void	dns_dispatch_imsg(int, short, void *);
49void	dns_sig_handler(int, short, void *);
50void	dns_shutdown(void);
51int	host_dns(const char *, struct ypldap_addr_list *);
52
53void
54dns_sig_handler(int sig, short event, void *p)
55{
56	switch (sig) {
57	case SIGINT:
58	case SIGTERM:
59		dns_shutdown();
60		break;
61	default:
62		fatalx("unexpected signal");
63	}
64}
65
66void
67dns_shutdown(void)
68{
69	log_info("dns engine exiting");
70	_exit(0);
71}
72
73pid_t
74ypldap_dns(int pipe_ntp[2], struct passwd *pw)
75{
76	pid_t			 pid;
77	struct event	 ev_sigint;
78	struct event	 ev_sigterm;
79	struct event	 ev_sighup;
80	struct env	 env;
81
82	switch (pid = fork()) {
83	case -1:
84		fatal("cannot fork");
85		break;
86	case 0:
87		break;
88	default:
89		return (pid);
90	}
91
92	setproctitle("dns engine");
93	close(pipe_ntp[0]);
94
95	if (setgroups(1, &pw->pw_gid) ||
96	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
97	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
98		fatal("can't drop privileges");
99	endservent();
100
101	event_init();
102	signal_set(&ev_sigint, SIGINT, dns_sig_handler, NULL);
103	signal_set(&ev_sigterm, SIGTERM, dns_sig_handler, NULL);
104	signal_set(&ev_sighup, SIGHUP, dns_sig_handler, NULL);
105	signal_add(&ev_sigint, NULL);
106	signal_add(&ev_sigterm, NULL);
107	signal_add(&ev_sighup, NULL);
108
109	if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
110		fatal(NULL);
111
112	env.sc_iev->events = EV_READ;
113	env.sc_iev->data = &env;
114	imsg_init(&env.sc_iev->ibuf, pipe_ntp[1]);
115	env.sc_iev->handler = dns_dispatch_imsg;
116	event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
117	    env.sc_iev->handler, &env);
118	event_add(&env.sc_iev->ev, NULL);
119
120	event_dispatch();
121	dns_shutdown();
122
123	return (0);
124}
125
126void
127dns_dispatch_imsg(int fd, short events, void *p)
128{
129	struct imsg		 imsg;
130	int			 n, cnt;
131	char			*name;
132	struct ypldap_addr_list	hn = TAILQ_HEAD_INITIALIZER(hn);
133	struct ypldap_addr	*h;
134	struct ibuf		*buf;
135	struct env		*env = p;
136	struct imsgev		*iev = env->sc_iev;
137	struct imsgbuf		*ibuf = &iev->ibuf;
138	int			 shut = 0;
139
140	if ((events & (EV_READ | EV_WRITE)) == 0)
141		fatalx("unknown event");
142
143	if (events & EV_READ) {
144		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
145			fatal("imsg_read error");
146		if (n == 0)
147			shut = 1;
148	}
149	if (events & EV_WRITE) {
150		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
151			fatal("msgbuf_write");
152		if (n == 0)
153			shut = 1;
154		goto done;
155	}
156
157	for (;;) {
158		if ((n = imsg_get(ibuf, &imsg)) == -1)
159			fatal("client_dispatch_imsg: imsg_get error");
160		if (n == 0)
161			break;
162
163		switch (imsg.hdr.type) {
164		case IMSG_HOST_DNS:
165			name = imsg.data;
166			if (imsg.hdr.len < 1 + IMSG_HEADER_SIZE)
167				fatalx("invalid IMSG_HOST_DNS received");
168			imsg.hdr.len -= 1 + IMSG_HEADER_SIZE;
169			if (name[imsg.hdr.len] != '\0' ||
170			    strlen(name) != imsg.hdr.len)
171				fatalx("invalid IMSG_HOST_DNS received");
172			if ((cnt = host_dns(name, &hn)) == -1)
173				break;
174			buf = imsg_create(ibuf, IMSG_HOST_DNS,
175			    imsg.hdr.peerid, 0,
176			    cnt * sizeof(struct sockaddr_storage));
177			if (buf == NULL)
178				break;
179			if (cnt > 0) {
180				while(!TAILQ_EMPTY(&hn)) {
181					h = TAILQ_FIRST(&hn);
182					TAILQ_REMOVE(&hn, h, next);
183					imsg_add(buf, &h->ss, sizeof(h->ss));
184					free(h);
185				}
186			}
187
188			imsg_close(ibuf, buf);
189			break;
190		default:
191			break;
192		}
193		imsg_free(&imsg);
194	}
195
196done:
197	if (!shut)
198		imsg_event_add(iev);
199	else {
200		/* this pipe is dead, so remove the event handler */
201		event_del(&iev->ev);
202		event_loopexit(NULL);
203	}
204}
205
206int
207host_dns(const char *s, struct ypldap_addr_list *hn)
208{
209	struct addrinfo		 hints, *res0, *res;
210	int			 error, cnt = 0;
211	struct sockaddr_in	*sa_in;
212	struct sockaddr_in6	*sa_in6;
213	struct ypldap_addr	*h;
214
215	memset(&hints, 0, sizeof(hints));
216	hints.ai_family = PF_UNSPEC;
217	hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
218	error = getaddrinfo(s, NULL, &hints, &res0);
219	if (error == EAI_AGAIN || error == EAI_NONAME)
220			return (0);
221	if (error) {
222		log_warnx("could not parse \"%s\": %s", s,
223		    gai_strerror(error));
224		return (-1);
225	}
226
227	for (res = res0; res && cnt < MAX_SERVERS_DNS; res = res->ai_next) {
228		if (res->ai_family != AF_INET &&
229		    res->ai_family != AF_INET6)
230			continue;
231		if ((h = calloc(1, sizeof(struct ypldap_addr))) == NULL)
232			fatal(NULL);
233		h->ss.ss_family = res->ai_family;
234		if (res->ai_family == AF_INET) {
235			sa_in = (struct sockaddr_in *)&h->ss;
236			sa_in->sin_len = sizeof(struct sockaddr_in);
237			sa_in->sin_addr.s_addr = ((struct sockaddr_in *)
238			    res->ai_addr)->sin_addr.s_addr;
239		} else {
240			sa_in6 = (struct sockaddr_in6 *)&h->ss;
241			sa_in6->sin6_len = sizeof(struct sockaddr_in6);
242			memcpy(&sa_in6->sin6_addr, &((struct sockaddr_in6 *)
243			    res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
244		}
245
246		TAILQ_INSERT_HEAD(hn, h, next);
247		cnt++;
248	}
249	freeaddrinfo(res0);
250	return (cnt);
251}
252