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