d7f35f915047cdee9b5b6449d5d4e680eb7654c4
[strongswan.git] / src / pluto / adns.c
1 /* Pluto Asynchronous DNS Helper Program -- for internal use only!
2 * Copyright (C) 2002 D. Hugh Redelmeier.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 */
14
15 #ifndef USE_LWRES /* whole file! */
16
17 /* This program executes as multiple processes. The Master process
18 * receives queries (struct adns_query messages) from Pluto and distributes
19 * them amongst Worker processes. These Worker processes are created
20 * by the Master whenever a query arrives and no existing Worker is free.
21 * At most MAX_WORKERS will be created; after that, the Master will queue
22 * queries until a Worker becomes free. When a Worker has an answer from
23 * the resolver, it sends the answer as a struct adns_answer message to the
24 * Master. The Master then forwards the answer to Pluto, noting that
25 * the Worker is free to accept another query.
26 *
27 * The protocol is simple: Pluto sends a sequence of queries and receives
28 * a sequence of answers. select(2) is used by Pluto and by the Master
29 * process to decide when to read, but writes are done without checking
30 * for readiness. Communications is via pipes. Since only one process
31 * can write to each pipe, messages will not be interleaved. Fixed length
32 * records are used for simplicity.
33 *
34 * Pluto needs a way to indicate to the Master when to shut down
35 * and the Master needs to indicate this to each worker. EOF on the pipe
36 * signifies this.
37 *
38 * The interfaces between these components are considered private to
39 * Pluto. This allows us to get away with less checking. This is a
40 * reason to use pipes instead of TCP/IP.
41 *
42 * Although the code uses plain old UNIX processes, it could be modified
43 * to use threads. That might reduce resource requirements. It would
44 * preclude running on systems without thread-safe resolvers.
45 */
46
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <stddef.h>
50 #include <string.h>
51 #include <errno.h>
52 #include <unistd.h>
53 #include <syslog.h>
54 #include <sys/types.h>
55 #include <sys/wait.h>
56 #include <netinet/in.h>
57 #include <arpa/nameser.h>
58 #include <resolv.h>
59 #include <netdb.h> /* ??? for h_errno */
60
61 #include <freeswan.h>
62
63 /* GCC magic! */
64 #ifdef GCC_LINT
65 # define UNUSED __attribute__ ((unused))
66 #else
67 # define UNUSED /* ignore */
68 #endif
69
70 #include "constants.h"
71 #include "adns.h" /* needs <resolv.h> */
72
73 /* shared by all processes */
74
75 static const char *name; /* program name, for messages */
76
77 static bool debug = FALSE;
78
79 /* Read a variable-length record from a pipe (and no more!).
80 * First bytes must be a size_t containing the length.
81 * HES_CONTINUE if record read
82 * HES_OK if EOF
83 * HES_IO_ERROR_IN if errno tells the tale.
84 * Others are errors.
85 */
86 static enum helper_exit_status
87 read_pipe(int fd, unsigned char *stuff, size_t minlen, size_t maxlen)
88 {
89 size_t n = 0;
90 size_t goal = minlen;
91
92 do {
93 ssize_t m = read(fd, stuff + n, goal - n);
94
95 if (m == -1)
96 {
97 if (errno != EINTR)
98 {
99 syslog(LOG_ERR, "Input error on pipe: %s", strerror(errno));
100 return HES_IO_ERROR_IN;
101 }
102 }
103 else if (m == 0)
104 {
105 return HES_OK; /* treat empty message as EOF */
106 }
107 else
108 {
109 n += m;
110 if (n >= sizeof(size_t))
111 {
112 goal = *(size_t *)(void *)stuff;
113 if (goal < minlen || maxlen < goal)
114 {
115 if (debug)
116 fprintf(stderr, "%lu : [%lu, %lu]\n"
117 , (unsigned long)goal
118 , (unsigned long)minlen, (unsigned long)maxlen);
119 return HES_BAD_LEN;
120 }
121 }
122 }
123 } while (n < goal);
124
125 return HES_CONTINUE;
126 }
127
128 /* Write a variable-length record to a pipe.
129 * First bytes must be a size_t containing the length.
130 * HES_CONTINUE if record written
131 * Others are errors.
132 */
133 static enum helper_exit_status
134 write_pipe(int fd, const unsigned char *stuff)
135 {
136 size_t len = *(const size_t *)(const void *)stuff;
137 size_t n = 0;
138
139 do {
140 ssize_t m = write(fd, stuff + n, len - n);
141
142 if (m == -1)
143 {
144 /* error, but ignore and retry if EINTR */
145 if (errno != EINTR)
146 {
147 syslog(LOG_ERR, "Output error from master: %s", strerror(errno));
148 return HES_IO_ERROR_OUT;
149 }
150 }
151 else
152 {
153 n += m;
154 }
155 } while (n != len);
156 return HES_CONTINUE;
157 }
158
159 /**************** worker process ****************/
160
161 /* The interface in RHL6.x and BIND distribution 8.2.2 are different,
162 * so we build some of our own :-(
163 */
164
165 /* Support deprecated interface to allow for older releases of the resolver.
166 * Fake new interface!
167 * See resolver(3) bind distribution (should be in RHL6.1, but isn't).
168 * __RES was 19960801 in RHL6.2, an old resolver.
169 */
170
171 #if (__RES) <= 19960801
172 # define OLD_RESOLVER 1
173 #endif
174
175 #ifdef OLD_RESOLVER
176
177 # define res_ninit(statp) res_init()
178 # define res_nquery(statp, dname, class, type, answer, anslen) \
179 res_query(dname, class, type, answer, anslen)
180 # define res_nclose(statp) res_close()
181
182 static struct __res_state *statp = &_res;
183
184 #else /* !OLD_RESOLVER */
185
186 static struct __res_state my_res_state /* = { 0 } */;
187 static res_state statp = &my_res_state;
188
189 #endif /* !OLD_RESOLVER */
190
191 static int
192 worker(int qfd, int afd)
193 {
194 {
195 int r = res_ninit(statp);
196
197 if (r != 0)
198 {
199 syslog(LOG_ERR, "cannot initialize resolver");
200 return HES_RES_INIT;
201 }
202 #ifndef OLD_RESOLVER
203 statp->options |= RES_ROTATE;
204 #endif
205 statp->options |= RES_DEBUG;
206 }
207
208 for (;;)
209 {
210 struct adns_query q;
211 struct adns_answer a;
212
213 enum helper_exit_status r = read_pipe(qfd, (unsigned char *)&q
214 , sizeof(q), sizeof(q));
215
216 if (r != HES_CONTINUE)
217 return r; /* some kind of exit */
218
219 if (q.qmagic != ADNS_Q_MAGIC)
220 {
221 syslog(LOG_ERR, "error in input from master: bad magic");
222 return HES_BAD_MAGIC;
223 }
224
225 a.amagic = ADNS_A_MAGIC;
226 a.serial = q.serial;
227
228 a.result = res_nquery(statp, q.name_buf, C_IN, q.type, a.ans, sizeof(a.ans));
229 a.h_errno_val = h_errno;
230
231 a.len = offsetof(struct adns_answer, ans) + (a.result < 0? 0 : a.result);
232
233 #ifdef DEBUG
234 if (((q.debugging & IMPAIR_DELAY_ADNS_KEY_ANSWER) && q.type == T_KEY)
235 || ((q.debugging & IMPAIR_DELAY_ADNS_TXT_ANSWER) && q.type == T_TXT))
236 sleep(30); /* delay the answer */
237 #endif
238
239 /* write answer, possibly a bit at a time */
240 r = write_pipe(afd, (const unsigned char *)&a);
241
242 if (r != HES_CONTINUE)
243 return r; /* some kind of exit */
244 }
245 }
246
247 /**************** master process ****************/
248
249 bool eof_from_pluto = FALSE;
250 #define PLUTO_QFD 0 /* queries come on stdin */
251 #define PLUTO_AFD 1 /* answers go out on stdout */
252
253 #ifndef MAX_WORKERS
254 # define MAX_WORKERS 10 /* number of in-flight queries */
255 #endif
256
257 struct worker_info {
258 int qfd; /* query pipe's file descriptor */
259 int afd; /* answer pipe's file descriptor */
260 pid_t pid;
261 bool busy;
262 void *continuation; /* of outstanding request */
263 };
264
265 static struct worker_info wi[MAX_WORKERS];
266 static struct worker_info *wi_roof = wi;
267
268 /* request FIFO */
269
270 struct query_list {
271 struct query_list *next;
272 struct adns_query aq;
273 };
274
275 static struct query_list *oldest_query = NULL;
276 static struct query_list *newest_query; /* undefined when oldest == NULL */
277 static struct query_list *free_queries = NULL;
278
279 static bool
280 spawn_worker(void)
281 {
282 int qfds[2];
283 int afds[2];
284 pid_t p;
285
286 if (pipe(qfds) != 0 || pipe(afds) != 0)
287 {
288 syslog(LOG_ERR, "pipe(2) failed: %s", strerror(errno));
289 exit(HES_PIPE);
290 }
291
292 wi_roof->qfd = qfds[1]; /* write end of query pipe */
293 wi_roof->afd = afds[0]; /* read end of answer pipe */
294
295 p = fork();
296 if (p == -1)
297 {
298 /* fork failed: ignore if at least one worker exists */
299 if (wi_roof == wi)
300 {
301 syslog(LOG_ERR, "fork(2) error creating first worker: %s", strerror(errno));
302 exit(HES_FORK);
303 }
304 close(qfds[0]);
305 close(qfds[1]);
306 close(afds[0]);
307 close(afds[1]);
308 return FALSE;
309 }
310 else if (p == 0)
311 {
312 /* child */
313 struct worker_info *w;
314
315 close(PLUTO_QFD);
316 close(PLUTO_AFD);
317 /* close all master pipes, including ours */
318 for (w = wi; w <= wi_roof; w++)
319 {
320 close(w->qfd);
321 close(w->afd);
322 }
323 exit(worker(qfds[0], afds[1]));
324 }
325 else
326 {
327 /* parent */
328 struct worker_info *w = wi_roof++;
329
330 w->pid = p;
331 w->busy = FALSE;
332 close(qfds[0]);
333 close(afds[1]);
334 return TRUE;
335 }
336 }
337
338 static void
339 send_eof(struct worker_info *w)
340 {
341 pid_t p;
342 int status;
343
344 close(w->qfd);
345 w->qfd = NULL_FD;
346
347 close(w->afd);
348 w->afd = NULL_FD;
349
350 /* reap child */
351 p = waitpid(w->pid, &status, 0);
352 /* ignore result -- what could we do with it? */
353 }
354
355 static void
356 forward_query(struct worker_info *w)
357 {
358 struct query_list *q = oldest_query;
359
360 if (q == NULL)
361 {
362 if (eof_from_pluto)
363 send_eof(w);
364 }
365 else
366 {
367 enum helper_exit_status r
368 = write_pipe(w->qfd, (const unsigned char *) &q->aq);
369
370 if (r != HES_CONTINUE)
371 exit(r);
372
373 w->busy = TRUE;
374
375 oldest_query = q->next;
376 q->next = free_queries;
377 free_queries = q;
378 }
379 }
380
381 static void
382 query(void)
383 {
384 struct query_list *q = free_queries;
385 enum helper_exit_status r;
386
387 /* find an unused queue entry */
388 if (q == NULL)
389 {
390 q = malloc(sizeof(*q));
391 if (q == NULL)
392 {
393 syslog(LOG_ERR, "malloc(3) failed");
394 exit(HES_MALLOC);
395 }
396 }
397 else
398 {
399 free_queries = q->next;
400 }
401
402 r = read_pipe(PLUTO_QFD, (unsigned char *)&q->aq
403 , sizeof(q->aq), sizeof(q->aq));
404
405 if (r == HES_OK)
406 {
407 /* EOF: we're done, except for unanswered queries */
408 struct worker_info *w;
409
410 eof_from_pluto = TRUE;
411 q->next = free_queries;
412 free_queries = q;
413
414 /* Send bye-bye to unbusy processes.
415 * Note that if there are queued queries, there won't be
416 * any non-busy workers.
417 */
418 for (w = wi; w != wi_roof; w++)
419 if (!w->busy)
420 send_eof(w);
421 }
422 else if (r != HES_CONTINUE)
423 {
424 exit(r);
425 }
426 else if (q->aq.qmagic != ADNS_Q_MAGIC)
427 {
428 syslog(LOG_ERR, "error in query from Pluto: bad magic");
429 exit(HES_BAD_MAGIC);
430 }
431 else
432 {
433 struct worker_info *w;
434
435 /* got a query */
436
437 /* add it to FIFO */
438 q->next = NULL;
439 if (oldest_query == NULL)
440 oldest_query = q;
441 else
442 newest_query->next = q;
443 newest_query = q;
444
445 /* See if any worker available */
446 for (w = wi; ; w++)
447 {
448 if (w == wi_roof)
449 {
450 /* no free worker */
451 if (w == wi + MAX_WORKERS)
452 break; /* no more to be created */
453 /* make a new one */
454 if (!spawn_worker())
455 break; /* cannot create one at this time */
456 }
457 if (!w->busy)
458 {
459 /* assign first to free worker */
460 forward_query(w);
461 break;
462 }
463 }
464 }
465 return;
466 }
467
468 static void
469 answer(struct worker_info *w)
470 {
471 struct adns_answer a;
472 enum helper_exit_status r = read_pipe(w->afd, (unsigned char *)&a
473 , offsetof(struct adns_answer, ans), sizeof(a));
474
475 if (r == HES_OK)
476 {
477 /* unexpected EOF */
478 syslog(LOG_ERR, "unexpected EOF from worker");
479 exit(HES_IO_ERROR_IN);
480 }
481 else if (r != HES_CONTINUE)
482 {
483 exit(r);
484 }
485 else if (a.amagic != ADNS_A_MAGIC)
486 {
487 syslog(LOG_ERR, "Input from worker error: bad magic");
488 exit(HES_BAD_MAGIC);
489 }
490 else if (a.continuation != w->continuation)
491 {
492 /* answer doesn't match query */
493 syslog(LOG_ERR, "Input from worker error: continuation mismatch");
494 exit(HES_SYNC);
495 }
496 else
497 {
498 /* pass the answer on to Pluto */
499 enum helper_exit_status r
500 = write_pipe(PLUTO_AFD, (const unsigned char *) &a);
501
502 if (r != HES_CONTINUE)
503 exit(r);
504 w->busy = FALSE;
505 forward_query(w);
506 }
507 }
508
509 /* assumption: input limited; accept blocking on output */
510 static int
511 master(void)
512 {
513 for (;;)
514 {
515 fd_set readfds;
516 int maxfd = PLUTO_QFD; /* approximate lower bound */
517 int ndes = 0;
518 struct worker_info *w;
519
520 FD_ZERO(&readfds);
521 if (!eof_from_pluto)
522 {
523 FD_SET(PLUTO_QFD, &readfds);
524 ndes++;
525 }
526 for (w = wi; w != wi_roof; w++)
527 {
528 if (w->busy)
529 {
530 FD_SET(w->afd, &readfds);
531 ndes++;
532 if (maxfd < w->afd)
533 maxfd = w->afd;
534 }
535 }
536
537 if (ndes == 0)
538 return HES_OK; /* done! */
539
540 do {
541 ndes = select(maxfd + 1, &readfds, NULL, NULL, NULL);
542 } while (ndes == -1 && errno == EINTR);
543 if (ndes == -1)
544 {
545 syslog(LOG_ERR, "select(2) error: %s", strerror(errno));
546 exit(HES_IO_ERROR_SELECT);
547 }
548 else if (ndes > 0)
549 {
550 if (FD_ISSET(PLUTO_QFD, &readfds))
551 {
552 query();
553 ndes--;
554 }
555 for (w = wi; ndes > 0 && w != wi_roof; w++)
556 {
557 if (w->busy && FD_ISSET(w->afd, &readfds))
558 {
559 answer(w);
560 ndes--;
561 }
562 }
563 }
564 }
565 }
566
567 /* Not to be invoked by strangers -- user hostile.
568 * Mandatory args: query-fd answer-fd
569 * Optional arg: -d, signifying "debug".
570 */
571
572 static void
573 adns_usage(const char *fmt, const char *arg)
574 {
575 const char **sp = ipsec_copyright_notice();
576
577 fprintf(stderr, "INTERNAL TO PLUTO: DO NOT EXECUTE\n");
578
579 fprintf(stderr, fmt, arg);
580 fprintf(stderr, "\n%s\n", ipsec_version_string());
581
582 for (; *sp != NULL; sp++)
583 fprintf(stderr, "%s\n", *sp);
584
585 syslog(LOG_ERR, fmt, arg);
586 exit(HES_INVOCATION);
587 }
588
589 int
590 main(int argc UNUSED, char **argv)
591 {
592 int i = 1;
593
594 name = argv[0];
595
596 while (i < argc)
597 {
598 if (streq(argv[i], "-d"))
599 {
600 i++;
601 debug = TRUE;
602 }
603 else
604 {
605 adns_usage("unexpected argument \"%s\"", argv[i]);
606 /*NOTREACHED*/
607 }
608 }
609
610 return master();
611 }
612
613 #endif /* !USE_LWRES */