OSDN Git Service

2013.10.24
[uclinux-h8/uClinux-dist.git] / freeswan / 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  * RCSID $Id: adns.c,v 1.5 2002/03/23 03:33:19 dhr Exp $
15  */
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 "adns.h"       /* needs <resolv.h> */
71
72 typedef int bool;
73 #define FALSE   0
74 #define TRUE    1
75
76 #define NULL_FD (-1)
77
78 /* shared by all processes */
79
80 static const char *name;        /* program name, for messages */
81
82 static bool debug = FALSE;
83
84 /* Read a variable-length record from a pipe (and no more!).
85  * First bytes must be a size_t containing the length.
86  * HES_CONTINUE if record read
87  * HES_OK if EOF
88  * HES_IO_ERROR_IN if errno tells the tale.
89  * Others are errors.
90  */
91 static enum helper_exit_status
92 read_pipe(int fd, unsigned char *stuff, size_t minlen, size_t maxlen)
93 {
94     size_t n = 0;
95     size_t goal = minlen;
96
97     do {
98         ssize_t m = read(fd, stuff + n, goal - n);
99
100         if (m == -1)
101         {
102             if (errno != EINTR)
103             {
104                 syslog(LOG_ERR, "Input error on pipe: %s", strerror(errno));
105                 return HES_IO_ERROR_IN;
106             }
107         }
108         else if (m == 0)
109         {
110             return HES_OK;      /* treat empty message as EOF */
111         }
112         else
113         {
114             n += m;
115             if (n >= sizeof(size_t))
116             {
117                 goal = *(size_t *)(void *)stuff;
118                 if (goal < minlen || maxlen < goal)
119                 {
120                     if (debug)
121                         fprintf(stderr, "%lu : [%lu, %lu]\n"
122                             , (unsigned long)goal
123                             , (unsigned long)minlen, (unsigned long)maxlen);
124                     return HES_BAD_LEN;
125                 }
126             }
127         }
128     } while (n < goal);
129
130     return HES_CONTINUE;
131 }
132
133 /* Write a variable-length record to a pipe.
134  * First bytes must be a size_t containing the length.
135  * HES_CONTINUE if record written
136  * Others are errors.
137  */
138 static enum helper_exit_status
139 write_pipe(int fd, const unsigned char *stuff)
140 {
141     size_t len = *(const size_t *)(const void *)stuff;
142     size_t n = 0;
143
144     do {
145         ssize_t m = write(fd, stuff + n, len - n);
146
147         if (m == -1)
148         {
149             /* error, but ignore and retry if EINTR */
150             if (errno != EINTR)
151             {
152                 syslog(LOG_ERR, "Output error from master: %s", strerror(errno));
153                 return HES_IO_ERROR_OUT;
154             }
155         }
156         else
157         {
158             n += m;
159         }
160     } while (n != len);
161     return HES_CONTINUE;
162 }
163
164 /**************** worker process ****************/
165
166 /* The interface in RHL6.x and BIND distribution 8.2.2 are different,
167  * so we build some of our own :-(
168  */
169
170 /* Support deprecated interface to allow for older releases of the resolver.
171  * Fake new interface!
172  * See resolver(3) bind distribution (should be in RHL6.1, but isn't).
173  * __RES was 19960801 in RHL6.2, an old resolver.
174  */
175
176 #if (__RES) <= 19960801
177 # define OLD_RESOLVER   1
178 #endif
179
180 #ifdef OLD_RESOLVER
181
182 # define res_ninit(statp) res_init()
183 # define res_nquery(statp, dname, class, type, answer, anslen) \
184     res_query(dname, class, type, answer, anslen)
185 # define res_nclose(statp) res_close()
186
187 static struct __res_state *statp = &_res;
188
189 #else /* !OLD_RESOLVER */
190
191 static struct __res_state my_res_state /* = { 0 } */;
192 static res_state statp = &my_res_state;
193
194 #endif /* !OLD_RESOLVER */
195
196 static int
197 worker(int qfd, int afd)
198 {
199     {
200         int r = res_ninit(statp);
201
202         if (r != 0)
203         {
204             syslog(LOG_ERR, "cannot initialize resolver");
205             return HES_RES_INIT;
206         }
207 #ifndef OLD_RESOLVER
208         statp->options |= RES_ROTATE;
209 #endif
210         statp->options |= RES_DEBUG;
211     }
212
213     for (;;)
214     {
215         struct adns_query q;
216         struct adns_answer a;
217
218         enum helper_exit_status r = read_pipe(qfd, (unsigned char *)&q
219             , sizeof(q), sizeof(q));
220
221         if (r != HES_CONTINUE)
222             return r;   /* some kind of exit */
223
224         if (q.qmagic != ADNS_Q_MAGIC)
225         {
226             syslog(LOG_ERR, "error in input from master: bad magic");
227             return HES_BAD_MAGIC;
228         }
229
230         a.amagic = ADNS_A_MAGIC;
231         a.continuation = q.continuation;
232
233         a.result = res_nquery(statp, q.name_buf, C_IN, q.type, a.ans, sizeof(a.ans));
234         a.h_errno_val = h_errno;
235
236         a.len = offsetof(struct adns_answer, ans) + (a.result < 0? 0 : a.result);
237
238         /* write answer, possibly a bit at a time */
239         r = write_pipe(afd, (const unsigned char *)&a);
240
241         if (r != HES_CONTINUE)
242             return r;   /* some kind of exit */
243     }
244 }
245
246 /**************** master process ****************/
247
248 bool eof_from_pluto = FALSE;
249 static int pluto_qfd = NULL_FD; /* fd for queries from Pluto */
250 static int pluto_afd = NULL_FD; /* fd for answers for Pluto */
251
252 #ifndef MAX_WORKERS
253 # define MAX_WORKERS 10 /* number of in-flight queries */
254 #endif
255
256 struct worker_info {
257     int qfd;    /* query pipe's file descriptor */
258     int afd;    /* answer pipe's file descriptor */
259     pid_t pid;
260     bool busy;
261     void *continuation; /* of outstanding request */
262 };
263
264 static struct worker_info wi[MAX_WORKERS];
265 static struct worker_info *wi_roof = wi;
266
267 /* request FIFO */
268
269 struct query_list {
270     struct query_list *next;
271     struct adns_query aq;
272 };
273
274 static struct query_list *oldest_query = NULL;
275 static struct query_list *newest_query; /* undefined when oldest == NULL */
276 static struct query_list *free_queries = NULL;
277
278 static bool
279 spawn_worker(void)
280 {
281     int qfds[2];
282     int afds[2];
283     pid_t p;
284
285     if (pipe(qfds) != 0 || pipe(afds) != 0)
286     {
287         syslog(LOG_ERR, "pipe(2) failed: %s", strerror(errno));
288         exit(HES_PIPE);
289     }
290
291     wi_roof->qfd = qfds[1];     /* write end of query pipe */
292     wi_roof->afd = afds[0];     /* read end of answer pipe */
293
294     p = fork();
295     if (p == -1)
296     {
297         /* fork failed: ignore if at least one worker exists */
298         if (wi_roof == wi)
299         {
300             syslog(LOG_ERR, "for(2) error creating first worker: %s", strerror(errno));
301             exit(HES_FORK);
302         }
303         close(qfds[0]);
304         close(qfds[1]);
305         close(afds[0]);
306         close(afds[1]);
307         return FALSE;
308     }
309     else if (p == 0)
310     {
311         /* child */
312         struct worker_info *w;
313
314         close(pluto_qfd);
315         close(pluto_afd);
316         /* close all master pipes, including ours */
317         for (w = wi; w <= wi_roof; w++)
318         {
319             close(w->qfd);
320             close(w->afd);
321         }
322         exit(worker(qfds[0], afds[1]));
323     }
324     else
325     {
326         /* parent */
327         struct worker_info *w = wi_roof++;
328
329         w->pid = p;
330         w->busy = FALSE;
331         close(qfds[0]);
332         close(afds[1]);
333         return TRUE;
334     }
335 }
336
337 static void
338 send_eof(struct worker_info *w)
339 {
340     pid_t p;
341     int status;
342
343     close(w->qfd);
344     w->qfd = NULL_FD;
345
346     close(w->afd);
347     w->afd = NULL_FD;
348
349     /* reap child */
350     p = waitpid(w->pid, &status, 0);
351     /* ignore result -- what could we do with it? */
352 }
353
354 static void
355 forward_query(struct worker_info *w)
356 {
357     struct query_list *q = oldest_query;
358
359     if (q == NULL)
360     {
361         if (eof_from_pluto)
362             send_eof(w);
363     }
364     else
365     {
366         enum helper_exit_status r
367             = write_pipe(w->qfd, (const unsigned char *) &q->aq);
368
369         if (r != HES_CONTINUE)
370             exit(r);
371
372         w->continuation = q->aq.continuation;
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     char **a;
593     unsigned long fd;
594     char *endptr;
595
596     name = argv[0];
597
598     for (a = argv + 1; *a != NULL; a++)
599     {
600         if (**a == '-')
601         {
602             if (strcmp(*a, "-d") == 0)
603                 debug = TRUE;
604             else
605                 adns_usage("unrecognized flag: %s", *a);
606         }
607         else
608         {
609             fd = strtoul(*a, &endptr, 0);
610
611             if (*endptr != 0 || fd <= 2 || (unsigned long)(int)fd != fd)
612                 adns_usage("unacceptable file descriptor arg: %s", *a);
613
614             if (pluto_qfd == NULL_FD)
615                 pluto_qfd = fd;
616             else if (pluto_afd == NULL_FD)
617                 pluto_afd = fd;
618             else
619                 adns_usage("too many file descriptor arguments", NULL);
620         }
621     }
622
623     if (pluto_afd == NULL_FD)
624         adns_usage("too few file descriptor arguments", NULL);
625
626     return master();
627 }