summaryrefslogtreecommitdiff
path: root/src/pluto/adns.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pluto/adns.c')
-rw-r--r--src/pluto/adns.c610
1 files changed, 0 insertions, 610 deletions
diff --git a/src/pluto/adns.c b/src/pluto/adns.c
deleted file mode 100644
index 76b459216..000000000
--- a/src/pluto/adns.c
+++ /dev/null
@@ -1,610 +0,0 @@
-/* Pluto Asynchronous DNS Helper Program -- for internal use only!
- * Copyright (C) 2002 D. Hugh Redelmeier.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * for more details.
- */
-
-/* This program executes as multiple processes. The Master process
- * receives queries (struct adns_query messages) from Pluto and distributes
- * them amongst Worker processes. These Worker processes are created
- * by the Master whenever a query arrives and no existing Worker is free.
- * At most MAX_WORKERS will be created; after that, the Master will queue
- * queries until a Worker becomes free. When a Worker has an answer from
- * the resolver, it sends the answer as a struct adns_answer message to the
- * Master. The Master then forwards the answer to Pluto, noting that
- * the Worker is free to accept another query.
- *
- * The protocol is simple: Pluto sends a sequence of queries and receives
- * a sequence of answers. select(2) is used by Pluto and by the Master
- * process to decide when to read, but writes are done without checking
- * for readiness. Communications is via pipes. Since only one process
- * can write to each pipe, messages will not be interleaved. Fixed length
- * records are used for simplicity.
- *
- * Pluto needs a way to indicate to the Master when to shut down
- * and the Master needs to indicate this to each worker. EOF on the pipe
- * signifies this.
- *
- * The interfaces between these components are considered private to
- * Pluto. This allows us to get away with less checking. This is a
- * reason to use pipes instead of TCP/IP.
- *
- * Although the code uses plain old UNIX processes, it could be modified
- * to use threads. That might reduce resource requirements. It would
- * preclude running on systems without thread-safe resolvers.
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <string.h>
-#include <errno.h>
-#include <unistd.h>
-#include <syslog.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-#include <netdb.h> /* ??? for h_errno */
-
-#include <freeswan.h>
-
-/* GCC magic! */
-#ifdef GCC_LINT
-# define UNUSED __attribute__ ((unused))
-#else
-# define UNUSED /* ignore */
-#endif
-
-#include "constants.h"
-#include "adns.h" /* needs <resolv.h> */
-
-/* shared by all processes */
-
-static const char *name; /* program name, for messages */
-
-static bool debug = FALSE;
-
-/* Read a variable-length record from a pipe (and no more!).
- * First bytes must be a size_t containing the length.
- * HES_CONTINUE if record read
- * HES_OK if EOF
- * HES_IO_ERROR_IN if errno tells the tale.
- * Others are errors.
- */
-static enum helper_exit_status
-read_pipe(int fd, unsigned char *stuff, size_t minlen, size_t maxlen)
-{
- size_t n = 0;
- size_t goal = minlen;
-
- do {
- ssize_t m = read(fd, stuff + n, goal - n);
-
- if (m == -1)
- {
- if (errno != EINTR)
- {
- syslog(LOG_ERR, "Input error on pipe: %s", strerror(errno));
- return HES_IO_ERROR_IN;
- }
- }
- else if (m == 0)
- {
- return HES_OK; /* treat empty message as EOF */
- }
- else
- {
- n += m;
- if (n >= sizeof(size_t))
- {
- goal = *(size_t *)(void *)stuff;
- if (goal < minlen || maxlen < goal)
- {
- if (debug)
- fprintf(stderr, "%lu : [%lu, %lu]\n"
- , (unsigned long)goal
- , (unsigned long)minlen, (unsigned long)maxlen);
- return HES_BAD_LEN;
- }
- }
- }
- } while (n < goal);
-
- return HES_CONTINUE;
-}
-
-/* Write a variable-length record to a pipe.
- * First bytes must be a size_t containing the length.
- * HES_CONTINUE if record written
- * Others are errors.
- */
-static enum helper_exit_status
-write_pipe(int fd, const unsigned char *stuff)
-{
- size_t len = *(const size_t *)(const void *)stuff;
- size_t n = 0;
-
- do {
- ssize_t m = write(fd, stuff + n, len - n);
-
- if (m == -1)
- {
- /* error, but ignore and retry if EINTR */
- if (errno != EINTR)
- {
- syslog(LOG_ERR, "Output error from master: %s", strerror(errno));
- return HES_IO_ERROR_OUT;
- }
- }
- else
- {
- n += m;
- }
- } while (n != len);
- return HES_CONTINUE;
-}
-
-/**************** worker process ****************/
-
-/* The interface in RHL6.x and BIND distribution 8.2.2 are different,
- * so we build some of our own :-(
- */
-
-/* Support deprecated interface to allow for older releases of the resolver.
- * Fake new interface!
- * See resolver(3) bind distribution (should be in RHL6.1, but isn't).
- * __RES was 19960801 in RHL6.2, an old resolver.
- */
-
-#if (__RES) <= 19960801
-# define OLD_RESOLVER 1
-#endif
-
-#ifdef OLD_RESOLVER
-
-# define res_ninit(statp) res_init()
-# define res_nquery(statp, dname, class, type, answer, anslen) \
- res_query(dname, class, type, answer, anslen)
-# define res_nclose(statp) res_close()
-
-static struct __res_state *statp = &_res;
-
-#else /* !OLD_RESOLVER */
-
-static struct __res_state my_res_state /* = { 0 } */;
-static res_state statp = &my_res_state;
-
-#endif /* !OLD_RESOLVER */
-
-static int
-worker(int qfd, int afd)
-{
- {
- int r = res_ninit(statp);
-
- if (r != 0)
- {
- syslog(LOG_ERR, "cannot initialize resolver");
- return HES_RES_INIT;
- }
-#ifndef OLD_RESOLVER
- statp->options |= RES_ROTATE;
-#endif
- statp->options |= RES_DEBUG;
- }
-
- for (;;)
- {
- struct adns_query q;
- struct adns_answer a;
-
- enum helper_exit_status r = read_pipe(qfd, (unsigned char *)&q
- , sizeof(q), sizeof(q));
-
- if (r != HES_CONTINUE)
- return r; /* some kind of exit */
-
- if (q.qmagic != ADNS_Q_MAGIC)
- {
- syslog(LOG_ERR, "error in input from master: bad magic");
- return HES_BAD_MAGIC;
- }
-
- a.amagic = ADNS_A_MAGIC;
- a.serial = q.serial;
- a.continuation = NULL;
-
- a.result = res_nquery(statp, q.name_buf, C_IN, q.type, a.ans, sizeof(a.ans));
- a.h_errno_val = h_errno;
-
- a.len = offsetof(struct adns_answer, ans) + (a.result < 0? 0 : a.result);
-
-#ifdef DEBUG
- if (((q.debugging & IMPAIR_DELAY_ADNS_KEY_ANSWER) && q.type == T_KEY)
- || ((q.debugging & IMPAIR_DELAY_ADNS_TXT_ANSWER) && q.type == T_TXT))
- sleep(30); /* delay the answer */
-#endif
-
- /* write answer, possibly a bit at a time */
- r = write_pipe(afd, (const unsigned char *)&a);
-
- if (r != HES_CONTINUE)
- return r; /* some kind of exit */
- }
-}
-
-/**************** master process ****************/
-
-bool eof_from_pluto = FALSE;
-#define PLUTO_QFD 0 /* queries come on stdin */
-#define PLUTO_AFD 1 /* answers go out on stdout */
-
-#ifndef MAX_WORKERS
-# define MAX_WORKERS 10 /* number of in-flight queries */
-#endif
-
-struct worker_info {
- int qfd; /* query pipe's file descriptor */
- int afd; /* answer pipe's file descriptor */
- pid_t pid;
- bool busy;
- void *continuation; /* of outstanding request */
-};
-
-static struct worker_info wi[MAX_WORKERS];
-static struct worker_info *wi_roof = wi;
-
-/* request FIFO */
-
-struct query_list {
- struct query_list *next;
- struct adns_query aq;
-};
-
-static struct query_list *oldest_query = NULL;
-static struct query_list *newest_query; /* undefined when oldest == NULL */
-static struct query_list *free_queries = NULL;
-
-static bool
-spawn_worker(void)
-{
- int qfds[2];
- int afds[2];
- pid_t p;
-
- if (pipe(qfds) != 0 || pipe(afds) != 0)
- {
- syslog(LOG_ERR, "pipe(2) failed: %s", strerror(errno));
- exit(HES_PIPE);
- }
-
- wi_roof->qfd = qfds[1]; /* write end of query pipe */
- wi_roof->afd = afds[0]; /* read end of answer pipe */
-
- p = fork();
- if (p == -1)
- {
- /* fork failed: ignore if at least one worker exists */
- if (wi_roof == wi)
- {
- syslog(LOG_ERR, "fork(2) error creating first worker: %s", strerror(errno));
- exit(HES_FORK);
- }
- close(qfds[0]);
- close(qfds[1]);
- close(afds[0]);
- close(afds[1]);
- return FALSE;
- }
- else if (p == 0)
- {
- /* child */
- struct worker_info *w;
-
- close(PLUTO_QFD);
- close(PLUTO_AFD);
- /* close all master pipes, including ours */
- for (w = wi; w <= wi_roof; w++)
- {
- close(w->qfd);
- close(w->afd);
- }
- exit(worker(qfds[0], afds[1]));
- }
- else
- {
- /* parent */
- struct worker_info *w = wi_roof++;
-
- w->pid = p;
- w->busy = FALSE;
- close(qfds[0]);
- close(afds[1]);
- return TRUE;
- }
-}
-
-static void
-send_eof(struct worker_info *w)
-{
- pid_t p;
- int status;
-
- close(w->qfd);
- w->qfd = NULL_FD;
-
- close(w->afd);
- w->afd = NULL_FD;
-
- /* reap child */
- p = waitpid(w->pid, &status, 0);
- /* ignore result -- what could we do with it? */
-}
-
-static void
-forward_query(struct worker_info *w)
-{
- struct query_list *q = oldest_query;
-
- if (q == NULL)
- {
- if (eof_from_pluto)
- send_eof(w);
- }
- else
- {
- enum helper_exit_status r
- = write_pipe(w->qfd, (const unsigned char *) &q->aq);
-
- if (r != HES_CONTINUE)
- exit(r);
-
- w->busy = TRUE;
-
- oldest_query = q->next;
- q->next = free_queries;
- free_queries = q;
- }
-}
-
-static void
-query(void)
-{
- struct query_list *q = free_queries;
- enum helper_exit_status r;
-
- /* find an unused queue entry */
- if (q == NULL)
- {
- q = malloc(sizeof(*q));
- if (q == NULL)
- {
- syslog(LOG_ERR, "malloc(3) failed");
- exit(HES_MALLOC);
- }
- }
- else
- {
- free_queries = q->next;
- }
-
- r = read_pipe(PLUTO_QFD, (unsigned char *)&q->aq
- , sizeof(q->aq), sizeof(q->aq));
-
- if (r == HES_OK)
- {
- /* EOF: we're done, except for unanswered queries */
- struct worker_info *w;
-
- eof_from_pluto = TRUE;
- q->next = free_queries;
- free_queries = q;
-
- /* Send bye-bye to unbusy processes.
- * Note that if there are queued queries, there won't be
- * any non-busy workers.
- */
- for (w = wi; w != wi_roof; w++)
- if (!w->busy)
- send_eof(w);
- }
- else if (r != HES_CONTINUE)
- {
- exit(r);
- }
- else if (q->aq.qmagic != ADNS_Q_MAGIC)
- {
- syslog(LOG_ERR, "error in query from Pluto: bad magic");
- exit(HES_BAD_MAGIC);
- }
- else
- {
- struct worker_info *w;
-
- /* got a query */
-
- /* add it to FIFO */
- q->next = NULL;
- if (oldest_query == NULL)
- oldest_query = q;
- else
- newest_query->next = q;
- newest_query = q;
-
- /* See if any worker available */
- for (w = wi; ; w++)
- {
- if (w == wi_roof)
- {
- /* no free worker */
- if (w == wi + MAX_WORKERS)
- break; /* no more to be created */
- /* make a new one */
- if (!spawn_worker())
- break; /* cannot create one at this time */
- }
- if (!w->busy)
- {
- /* assign first to free worker */
- forward_query(w);
- break;
- }
- }
- }
- return;
-}
-
-static void
-answer(struct worker_info *w)
-{
- struct adns_answer a;
- enum helper_exit_status r = read_pipe(w->afd, (unsigned char *)&a
- , offsetof(struct adns_answer, ans), sizeof(a));
-
- if (r == HES_OK)
- {
- /* unexpected EOF */
- syslog(LOG_ERR, "unexpected EOF from worker");
- exit(HES_IO_ERROR_IN);
- }
- else if (r != HES_CONTINUE)
- {
- exit(r);
- }
- else if (a.amagic != ADNS_A_MAGIC)
- {
- syslog(LOG_ERR, "Input from worker error: bad magic");
- exit(HES_BAD_MAGIC);
- }
- else if (a.continuation != w->continuation)
- {
- /* answer doesn't match query */
- syslog(LOG_ERR, "Input from worker error: continuation mismatch");
- exit(HES_SYNC);
- }
- else
- {
- /* pass the answer on to Pluto */
- enum helper_exit_status r
- = write_pipe(PLUTO_AFD, (const unsigned char *) &a);
-
- if (r != HES_CONTINUE)
- exit(r);
- w->busy = FALSE;
- forward_query(w);
- }
-}
-
-/* assumption: input limited; accept blocking on output */
-static int
-master(void)
-{
- for (;;)
- {
- fd_set readfds;
- int maxfd = PLUTO_QFD; /* approximate lower bound */
- int ndes = 0;
- struct worker_info *w;
-
- FD_ZERO(&readfds);
- if (!eof_from_pluto)
- {
- FD_SET(PLUTO_QFD, &readfds);
- ndes++;
- }
- for (w = wi; w != wi_roof; w++)
- {
- if (w->busy)
- {
- FD_SET(w->afd, &readfds);
- ndes++;
- if (maxfd < w->afd)
- maxfd = w->afd;
- }
- }
-
- if (ndes == 0)
- return HES_OK; /* done! */
-
- do {
- ndes = select(maxfd + 1, &readfds, NULL, NULL, NULL);
- } while (ndes == -1 && errno == EINTR);
- if (ndes == -1)
- {
- syslog(LOG_ERR, "select(2) error: %s", strerror(errno));
- exit(HES_IO_ERROR_SELECT);
- }
- else if (ndes > 0)
- {
- if (FD_ISSET(PLUTO_QFD, &readfds))
- {
- query();
- ndes--;
- }
- for (w = wi; ndes > 0 && w != wi_roof; w++)
- {
- if (w->busy && FD_ISSET(w->afd, &readfds))
- {
- answer(w);
- ndes--;
- }
- }
- }
- }
-}
-
-/* Not to be invoked by strangers -- user hostile.
- * Mandatory args: query-fd answer-fd
- * Optional arg: -d, signifying "debug".
- */
-
-static void
-adns_usage(const char *fmt, const char *arg)
-{
- const char **sp = ipsec_copyright_notice();
-
- fprintf(stderr, "INTERNAL TO PLUTO: DO NOT EXECUTE\n");
-
- fprintf(stderr, fmt, arg);
- fprintf(stderr, "\nstrongSwan "VERSION"\n");
-
- for (; *sp != NULL; sp++)
- fprintf(stderr, "%s\n", *sp);
-
- syslog(LOG_ERR, fmt, arg);
- exit(HES_INVOCATION);
-}
-
-int
-main(int argc UNUSED, char **argv)
-{
- int i = 1;
-
- name = argv[0];
-
- while (i < argc)
- {
- if (streq(argv[i], "-d"))
- {
- i++;
- debug = TRUE;
- }
- else
- {
- adns_usage("unexpected argument \"%s\"", argv[i]);
- /*NOTREACHED*/
- }
- }
-
- return master();
-}