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