xref: /freebsd/usr.sbin/ypldap/ypldap_dns.c (revision 49b49cda41feabe3439f7318e8bf40e3896c7bf4)
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 
45 volatile sig_atomic_t	 quit_dns = 0;
46 struct imsgev		*iev_dns;
47 
48 void	dns_dispatch_imsg(int, short, void *);
49 void	dns_sig_handler(int, short, void *);
50 void	dns_shutdown(void);
51 int	host_dns(const char *s, struct ypldap_addr **hn);
52 
53 void
54 dns_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 
66 void
67 dns_shutdown(void)
68 {
69 	log_info("dns engine exiting");
70 	_exit(0);
71 }
72 
73 pid_t
74 ypldap_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 
126 void
127 dns_dispatch_imsg(int fd, short events, void *p)
128 {
129 	struct imsg		 imsg;
130 	int			 n, cnt;
131 	char			*name;
132 	struct ypldap_addr	*h, *hn;
133 	struct ibuf		*buf;
134 	struct env		*env = p;
135 	struct imsgev		*iev = env->sc_iev;
136 	struct imsgbuf		*ibuf = &iev->ibuf;
137 	int			 shut = 0;
138 
139 	if ((events & (EV_READ | EV_WRITE)) == 0)
140 		fatalx("unknown event");
141 
142 	if (events & EV_READ) {
143 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
144 			fatal("imsg_read error");
145 		if (n == 0)
146 			shut = 1;
147 	}
148 	if (events & EV_WRITE) {
149 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
150 			fatal("msgbuf_write");
151 		if (n == 0)
152 			shut = 1;
153 		goto done;
154 	}
155 
156 	for (;;) {
157 		if ((n = imsg_get(ibuf, &imsg)) == -1)
158 			fatal("client_dispatch_imsg: imsg_get error");
159 		if (n == 0)
160 			break;
161 
162 		switch (imsg.hdr.type) {
163 		case IMSG_HOST_DNS:
164 			name = imsg.data;
165 			if (imsg.hdr.len < 1 + IMSG_HEADER_SIZE)
166 				fatalx("invalid IMSG_HOST_DNS received");
167 			imsg.hdr.len -= 1 + IMSG_HEADER_SIZE;
168 			if (name[imsg.hdr.len] != '\0' ||
169 			    strlen(name) != imsg.hdr.len)
170 				fatalx("invalid IMSG_HOST_DNS received");
171 			if ((cnt = host_dns(name, &hn)) == -1)
172 				break;
173 			buf = imsg_create(ibuf, IMSG_HOST_DNS,
174 			    imsg.hdr.peerid, 0,
175 			    cnt * sizeof(struct sockaddr_storage));
176 			if (buf == NULL)
177 				break;
178 			if (cnt > 0) {
179 				h = hn;
180 				while (h != NULL) {
181 					imsg_add(buf, &h->ss, sizeof(h->ss));
182 					hn = h->next;
183 					free(h);
184 					h = hn;
185 				}
186 			}
187 
188 			imsg_close(ibuf, buf);
189 			break;
190 		default:
191 			break;
192 		}
193 		imsg_free(&imsg);
194 	}
195 
196 done:
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 
206 int
207 host_dns(const char *s, struct ypldap_addr **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, *hh = NULL;
214 
215 	bzero(&hints, 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 		h->next = hh;
247 		hh = h;
248 		cnt++;
249 	}
250 	freeaddrinfo(res0);
251 
252 	*hn = hh;
253 	return (cnt);
254 }
255