"Fossies" - the Fresh Open Source Software Archive 
Member "darkstat-3.0.721/dns.c" (12 Jan 2022, 11015 Bytes) of package /linux/privat/darkstat-3.0.721.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "dns.c" see the
Fossies "Dox" file reference documentation and the latest
Fossies "Diffs" side-by-side code changes report:
3.0.719_vs_3.0.721.
1 /* darkstat 3
2 * copyright (c) 2001-2014 Emil Mikulic.
3 *
4 * dns.c: synchronous DNS in a child process.
5 *
6 * You may use, modify and redistribute this file under the terms of the
7 * GNU General Public License version 2. (see COPYING.GPL)
8 */
9
10 #include "cdefs.h"
11 #include "cap.h"
12 #include "conv.h"
13 #include "decode.h"
14 #include "dns.h"
15 #include "err.h"
16 #include "hosts_db.h"
17 #include "queue.h"
18 #include "str.h"
19 #include "tree.h"
20 #include "bsd.h" /* for setproctitle, strlcpy */
21
22 #include <sys/param.h>
23 #include <sys/socket.h>
24 #include <sys/wait.h>
25 #include <assert.h>
26 #include <errno.h>
27 #include <netdb.h>
28 #include <signal.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #ifdef __NetBSD__
34 # define gethostbyaddr(addr, len, type) \
35 gethostbyaddr((const char *)(addr), len, type)
36 #endif
37
38 static void dns_main(void) _noreturn_; /* the child process runs this */
39
40 #define CHILD 0 /* child process uses this socket */
41 #define PARENT 1
42 static int dns_sock[2];
43 static pid_t pid = -1;
44
45 struct dns_reply {
46 struct addr addr;
47 int error; /* for gai_strerror(), or 0 if no error */
48 char name[256]; /* http://tools.ietf.org/html/rfc1034#section-3.1 */
49 };
50
51 void
52 dns_init(const char *privdrop_user)
53 {
54 if (socketpair(AF_UNIX, SOCK_STREAM, 0, dns_sock) == -1)
55 err(1, "socketpair");
56
57 pid = fork();
58 if (pid == -1)
59 err(1, "fork");
60
61 if (pid == 0) {
62 /* We are the child. */
63 privdrop(NULL /* don't chroot */, privdrop_user);
64 close(dns_sock[PARENT]);
65 dns_sock[PARENT] = -1;
66 daemonize_finish(); /* drop our copy of the lifeline! */
67 if (signal(SIGUSR1, SIG_IGN) == SIG_ERR)
68 errx(1, "signal(SIGUSR1, ignore) failed");
69 cap_free_args();
70 dns_main();
71 } else {
72 /* We are the parent. */
73 close(dns_sock[CHILD]);
74 dns_sock[CHILD] = -1;
75 fd_set_nonblock(dns_sock[PARENT]);
76 verbosef("DNS child has PID %d", pid);
77 }
78 }
79
80 void
81 dns_stop(void)
82 {
83 if (pid == -1)
84 return; /* no child was started */
85 close(dns_sock[PARENT]);
86 if (kill(pid, SIGINT) == -1)
87 err(1, "kill");
88 verbosef("dns_stop() waiting for child");
89 if (waitpid(pid, NULL, 0) == -1)
90 err(1, "waitpid");
91 verbosef("dns_stop() done waiting for child");
92 }
93
94 struct tree_rec {
95 RB_ENTRY(tree_rec) ptree;
96 struct addr ip;
97 };
98
99 static int
100 tree_cmp(struct tree_rec *a, struct tree_rec *b)
101 {
102 if (a->ip.family != b->ip.family)
103 /* Sort IPv4 to the left of IPv6. */
104 return ((a->ip.family == IPv4) ? -1 : +1);
105
106 if (a->ip.family == IPv4)
107 return (memcmp(&a->ip.ip.v4, &b->ip.ip.v4, sizeof(a->ip.ip.v4)));
108 else {
109 assert(a->ip.family == IPv6);
110 return (memcmp(&a->ip.ip.v6, &b->ip.ip.v6, sizeof(a->ip.ip.v6)));
111 }
112 }
113
114 static RB_HEAD(tree_t, tree_rec) ip_tree = RB_INITIALIZER(&tree_rec);
115 RB_GENERATE_STATIC(tree_t, tree_rec, ptree, tree_cmp)
116
117 void
118 dns_queue(const struct addr *const ipaddr)
119 {
120 struct tree_rec *rec;
121 ssize_t num_w;
122
123 if (pid == -1)
124 return; /* no child was started - we're not doing any DNS */
125
126 if ((ipaddr->family != IPv4) && (ipaddr->family != IPv6)) {
127 verbosef("dns_queue() for unknown family %d", ipaddr->family);
128 return;
129 }
130
131 rec = xmalloc(sizeof(*rec));
132 memcpy(&rec->ip, ipaddr, sizeof(rec->ip));
133
134 if (RB_INSERT(tree_t, &ip_tree, rec) != NULL) {
135 /* Already queued - this happens seldom enough that we don't care about
136 * the performance hit of needlessly malloc()ing. */
137 verbosef("already queued %s", addr_to_str(ipaddr));
138 free(rec);
139 return;
140 }
141
142 num_w = write(dns_sock[PARENT], ipaddr, sizeof(*ipaddr)); /* won't block */
143 if (num_w == 0)
144 warnx("dns_queue: write: ignoring end of file");
145 else if (num_w == -1)
146 warn("dns_queue: ignoring write error");
147 else if (num_w != sizeof(*ipaddr))
148 err(1, "dns_queue: wrote %zu instead of %zu", num_w, sizeof(*ipaddr));
149 }
150
151 static void
152 dns_unqueue(const struct addr *const ipaddr)
153 {
154 struct tree_rec tmp, *rec;
155
156 memcpy(&tmp.ip, ipaddr, sizeof(tmp.ip));
157 if ((rec = RB_FIND(tree_t, &ip_tree, &tmp)) != NULL) {
158 RB_REMOVE(tree_t, &ip_tree, rec);
159 free(rec);
160 }
161 else
162 verbosef("couldn't unqueue %s - not in queue!", addr_to_str(ipaddr));
163 }
164
165 /*
166 * Returns non-zero if result waiting, stores IP and name into given pointers
167 * (name buffer is allocated by dns_poll)
168 */
169 static int
170 dns_get_result(struct addr *ipaddr, char **name)
171 {
172 struct dns_reply reply;
173 ssize_t numread;
174
175 numread = read(dns_sock[PARENT], &reply, sizeof(reply));
176 if (numread == -1) {
177 if (errno == EAGAIN)
178 return (0); /* no input waiting */
179 else
180 goto error;
181 }
182 if (numread == 0)
183 goto error; /* EOF */
184 if (numread != sizeof(reply))
185 errx(1, "dns_get_result read got %zu, expected %zu",
186 numread, sizeof(reply));
187
188 /* Return successful reply. */
189 memcpy(ipaddr, &reply.addr, sizeof(*ipaddr));
190 if (reply.error != 0) {
191 /* Identify common special cases. */
192 const char *type = "none";
193
194 if (reply.addr.family == IPv6) {
195 if (IN6_IS_ADDR_LINKLOCAL(&reply.addr.ip.v6))
196 type = "link-local";
197 else if (IN6_IS_ADDR_SITELOCAL(&reply.addr.ip.v6))
198 type = "site-local";
199 else if (IN6_IS_ADDR_MULTICAST(&reply.addr.ip.v6))
200 type = "multicast";
201 } else {
202 assert(reply.addr.family == IPv4);
203 if (IN_MULTICAST(htonl(reply.addr.ip.v4)))
204 type = "multicast";
205 }
206 xasprintf(name, "(%s)", type);
207 }
208 else /* Correctly resolved name. */
209 *name = xstrdup(reply.name);
210
211 dns_unqueue(&reply.addr);
212 return (1);
213
214 error:
215 warn("dns_get_result: ignoring read error");
216 /* FIXME: re-align to stream? restart dns child? */
217 return (0);
218 }
219
220 void
221 dns_poll(void)
222 {
223 struct addr ip;
224 char *name;
225
226 if (pid == -1)
227 return; /* no child was started - we're not doing any DNS */
228
229 while (dns_get_result(&ip, &name)) {
230 /* push into hosts_db */
231 struct bucket *b = host_find(&ip);
232
233 if (b == NULL) {
234 verbosef("resolved %s to %s but it's not in the DB!",
235 addr_to_str(&ip), name);
236 return;
237 }
238 if (b->u.host.dns != NULL) {
239 verbosef("resolved %s to %s but it's already in the DB!",
240 addr_to_str(&ip), name);
241 return;
242 }
243 b->u.host.dns = name;
244 }
245 }
246
247 /* ------------------------------------------------------------------------ */
248
249 struct qitem {
250 STAILQ_ENTRY(qitem) entries;
251 struct addr ip;
252 };
253
254 static STAILQ_HEAD(qhead, qitem) queue = STAILQ_HEAD_INITIALIZER(queue);
255
256 static void
257 enqueue(const struct addr *const ip)
258 {
259 struct qitem *i;
260
261 i = xmalloc(sizeof(*i));
262 memcpy(&i->ip, ip, sizeof(i->ip));
263 STAILQ_INSERT_TAIL(&queue, i, entries);
264 verbosef("DNS: enqueued %s", addr_to_str(ip));
265 }
266
267 /* Return non-zero and populate <ip> pointer if queue isn't empty. */
268 static int
269 dequeue(struct addr *ip)
270 {
271 struct qitem *i;
272
273 i = STAILQ_FIRST(&queue);
274 if (i == NULL)
275 return (0);
276 STAILQ_REMOVE_HEAD(&queue, entries);
277 memcpy(ip, &i->ip, sizeof(*ip));
278 free(i);
279 verbosef("DNS: dequeued %s", addr_to_str(ip));
280 return 1;
281 }
282
283 static void
284 xwrite(const int d, const void *buf, const size_t nbytes)
285 {
286 ssize_t ret = write(d, buf, nbytes);
287
288 if (ret == -1)
289 err(1, "write");
290 if (ret != (ssize_t)nbytes)
291 err(1, "wrote %d bytes instead of all %d bytes", (int)ret, (int)nbytes);
292 }
293
294 static void
295 dns_main(void)
296 {
297 struct addr ip;
298
299 setproctitle("DNS child");
300 fd_set_nonblock(dns_sock[CHILD]);
301 verbosef("DNS child entering main DNS loop");
302 for (;;) {
303 int blocking;
304
305 if (STAILQ_EMPTY(&queue)) {
306 blocking = 1;
307 fd_set_block(dns_sock[CHILD]);
308 verbosef("entering blocking read loop");
309 } else {
310 blocking = 0;
311 fd_set_nonblock(dns_sock[CHILD]);
312 verbosef("non-blocking poll");
313 }
314 for (;;) {
315 /* While we have input to process... */
316 ssize_t numread = read(dns_sock[CHILD], &ip, sizeof(ip));
317 if (numread == 0)
318 exit(0); /* end of file, nothing more to do here. */
319 if (numread == -1) {
320 if (!blocking && (errno == EAGAIN))
321 break; /* ran out of input */
322 /* else error */
323 err(1, "DNS: read failed");
324 }
325 if (numread != sizeof(ip))
326 err(1, "DNS: read got %zu bytes, expecting %zu",
327 numread, sizeof(ip));
328 enqueue(&ip);
329 if (blocking) {
330 /* After one blocking read, become non-blocking so that when we
331 * run out of input we fall through to queue processing.
332 */
333 blocking = 0;
334 fd_set_nonblock(dns_sock[CHILD]);
335 }
336 }
337
338 /* Process queue. */
339 if (dequeue(&ip)) {
340 struct dns_reply reply;
341 struct sockaddr_in sin;
342 struct sockaddr_in6 sin6;
343 struct hostent *he;
344 char host[NI_MAXHOST];
345 int ret, flags;
346
347 reply.addr = ip;
348 flags = NI_NAMEREQD;
349 # ifdef NI_IDN
350 flags |= NI_IDN;
351 # endif
352 switch (ip.family) {
353 case IPv4:
354 sin.sin_family = AF_INET;
355 sin.sin_addr.s_addr = ip.ip.v4;
356 ret = getnameinfo((struct sockaddr *) &sin, sizeof(sin),
357 host, sizeof(host), NULL, 0, flags);
358 if (ret == EAI_FAMILY) {
359 verbosef("getnameinfo error %s, trying gethostbyname",
360 gai_strerror(ret));
361 he = gethostbyaddr(&sin.sin_addr.s_addr,
362 sizeof(sin.sin_addr.s_addr), sin.sin_family);
363 if (he == NULL) {
364 ret = EAI_FAIL;
365 verbosef("gethostbyname error %s", hstrerror(h_errno));
366 } else {
367 ret = 0;
368 strlcpy(host, he->h_name, sizeof(host));
369 }
370 }
371 break;
372 case IPv6:
373 sin6.sin6_family = AF_INET6;
374 memcpy(&sin6.sin6_addr, &ip.ip.v6, sizeof(sin6.sin6_addr));
375 ret = getnameinfo((struct sockaddr *) &sin6, sizeof(sin6),
376 host, sizeof(host), NULL, 0, flags);
377 break;
378 default:
379 errx(1, "unexpected ip.family = %d", ip.family);
380 }
381
382 if (ret != 0) {
383 reply.name[0] = '\0';
384 reply.error = ret;
385 } else {
386 assert(sizeof(reply.name) > sizeof(char *)); /* not just a ptr */
387 strlcpy(reply.name, host, sizeof(reply.name));
388 reply.error = 0;
389 }
390 fd_set_block(dns_sock[CHILD]);
391 xwrite(dns_sock[CHILD], &reply, sizeof(reply));
392 verbosef("DNS: %s is \"%s\".", addr_to_str(&reply.addr),
393 (ret == 0) ? reply.name : gai_strerror(ret));
394 }
395 }
396 }
397
398 /* vim:set ts=3 sw=3 tw=78 expandtab: */