chiark / gitweb /
Added background resolver from `fw'.
authormdw <mdw>
Mon, 4 Oct 1999 21:40:42 +0000 (21:40 +0000)
committermdw <mdw>
Mon, 4 Oct 1999 21:40:42 +0000 (21:40 +0000)
bres.c [new file with mode: 0644]
bres.h [new file with mode: 0644]

diff --git a/bres.c b/bres.c
new file mode 100644 (file)
index 0000000..c482262
--- /dev/null
+++ b/bres.c
@@ -0,0 +1,979 @@
+/* -*-c-*-
+ *
+ * $Id: bres.c,v 1.1 1999/10/04 21:40:42 mdw Exp $
+ *
+ * Background reverse name resolution
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * mLib 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 Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: bres.c,v $
+ * Revision 1.1  1999/10/04 21:40:42  mdw
+ * Added background resolver from `fw'.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <mLib/alloc.h>
+#include <mLib/report.h>
+#include <mLib/sel.h>
+
+#include "bres.h"
+
+/*----- Magic numbers -----------------------------------------------------*/
+
+#define BRES_MAX 15                    /* Maximum number of resolvers */
+#define BRES_IDLE 60                   /* Lifetime of an idle resolver */
+
+/*----- Static variables --------------------------------------------------*/
+
+#ifndef BRES_STANDALONE
+
+static bres_server servers[BRES_MAX];  /* Statically allocated servers */
+
+#define FREE ((bres_server *)&freelist)
+static struct { bres_server *next, *prev; } freelist = { FREE, FREE };
+
+#define QUEUE ((bres_client *)&queue)
+static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE };
+
+static sel_state *sel;
+
+static const char *server = 0;
+
+#endif
+
+/*----- Background resolver protocol --------------------------------------*/
+
+/* --- Requests and responses --- *
+ *
+ * There are two types of requests: name and addr, corresponding to the
+ * standard @gethostbyname@ and @gethostbyaddr@ calls.  There are two types
+ * of responses too: a positive response consists of an encoded equivalent of
+ * a @struct hostent@ structure containing the requested information; a
+ * negative response consists of an @h_errno@ value explaining the problem.
+ */
+
+#define BRES_BYNAME 0                  /* Request: resolve given name */
+#define BRES_BYADDR 1                  /* Request: resolve given address */
+
+#define BRES_HOSTENT 0                 /* Response: resolved ok */
+#define BRES_ERROR 1                   /* Response: resolution failed */
+
+/* --- Encodings --- *
+ *
+ * A string is encoded as a @size_t@ length followed by the actual data.  The
+ * null terminator is not transmitted.
+ *
+ * Addresses for resolution are transmitted as raw @struct in_addr@
+ * structures.
+ *
+ * A @hostent@ structure is transmitted as a header containing fixed-size
+ * information, followed by the official name, an array of aliases, and an
+ * array of addresses.  The number of items in the arrays is specified in the
+ * header.
+ *
+ * The implementation assumes that a complete request or reply is always
+ * sent.  Undesirable blocking will occur if this is not the case.  Both ends
+ * are assumed to trust each other.  A protocol failure results in the child
+ * in question being terminated.
+ */
+
+typedef struct hostskel {
+  size_t nalias;
+  int addrtype;
+  size_t addrsz;
+  size_t naddr;
+} hostskel;
+
+/* --- @doread@, @dowrite@ --- *
+ *
+ * Arguments:  @int fd@ = file descriptor
+ *             @void *buf@ = buffer for data
+ *             @size_t sz@ = size of data
+ *
+ * Returns:    Zero if successful, nonzero otherwise.
+ *
+ * Use:                Reads or writes a chunk of data.  @EINTR@ errors are retried;
+ *             incomplete reads and writes are continued from where they
+ *             left off.  End-of-file is considered an I/O error.
+ */
+
+static int doread(int fd, void *buf, size_t sz)
+{
+  char *p = buf;
+  while (sz) {
+    int r = read(fd, p, sz);
+    if (r < 0) {
+      if (errno == EINTR)
+       continue;
+      return (-1);
+    } else if (r == 0) {
+      errno = EIO;
+      return (-1);
+    }
+    sz -= r;
+    p += r;
+  }
+  return (0);
+}
+
+static int dowrite(int fd, const void *buf, size_t sz)
+{
+  const char *p = buf;
+  while (sz) {
+    int r = write(fd, p, sz);
+    if (r < 0) {
+      if (errno == EINTR)
+       continue;
+      return (-1);
+    } else if (r == 0) {
+      errno = EIO;
+      return (-1);
+    }
+    sz -= r;
+    p += r;
+  }
+  return (0);
+}
+
+/* --- @getstring@ --- *
+ *
+ * Arguments:  @int fd@ = file descriptor to read
+ *
+ * Returns:    String in heap-allocated block, or a null pointer.
+ *
+ * Use:                Decodes a string.
+ */
+
+static char *getstring(int fd)
+{
+  size_t sz;
+  char *p;
+
+  if (doread(fd, &sz, sizeof(sz)) || (p = malloc(sz + 1)) == 0)
+    return (0);
+  if (doread(fd, p, sz)) {
+    free(p);
+    return (0);
+  }
+  p[sz] = 0;
+  return (p);
+}
+
+/* --- @putstring@ --- *
+ *
+ * Arguments:  @int fd@ = file descriptor to write on
+ *             @const char *p@ = pointer to string to write
+ *
+ * Returns:    Zero if successful.
+ *
+ * Use:                Encodes a string.
+ */
+
+static int putstring(int fd, const char *p)
+{
+  size_t sz = strlen(p);
+  if (dowrite(fd, &sz, sizeof(sz)) || dowrite(fd, p, sz))
+    return (-1);
+  return (0);
+}
+
+/* --- @gethost@ --- *
+ *
+ * Arguments:  @int fd@ = file descriptor to read
+ *
+ * Returns:    Pointer to heap-allocated @struct hostent@, or null.
+ *
+ * Use:                Decodes a host structure.  The resulting structure is all in
+ *             one big heap block.
+ */
+
+#ifndef BRES_STANDALONE
+
+static struct hostent *gethost(int fd)
+{
+  hostskel hsk;
+  struct hostent *h;
+  char *name;
+  char **alias = 0;
+  
+  /* --- Read the skeleton structure --- */
+
+  if (doread(fd, &hsk, sizeof(hsk)))
+    goto tidy_0;
+
+  /* --- Read the hostname and alias strings --- *
+   *
+   * Count the length of the strings as we go.
+   */
+
+  {
+    size_t sz =
+      sizeof(struct hostent) +
+      hsk.naddr * hsk.addrsz +
+      (hsk.naddr + hsk.nalias + 2) * sizeof(char *);
+
+    /* --- Read the primary host name --- */
+
+    if ((name = getstring(fd)) == 0)
+      goto tidy_0;
+    sz += strlen(name) + 1;
+
+    /* --- Read in the alias names --- */
+
+    if (hsk.nalias) {
+      int i;
+      if ((alias = malloc(hsk.nalias * sizeof(char *))) == 0)
+       goto tidy_1;      
+      for (i = 0; i < hsk.nalias; i++)
+       alias[i] = 0;
+      for (i = 0; i < hsk.nalias; i++) {
+       if ((alias[i] = getstring(fd)) == 0)
+         goto tidy_2;
+       sz += strlen(alias[i]) + 1;
+      }
+    }
+
+    /* --- Allocate the output structure --- */
+
+    if ((h = malloc(sz)) == 0)
+      goto tidy_2;
+  }
+
+  /* --- Fill in the base structure --- */
+
+  h->h_addrtype = hsk.addrtype;
+  h->h_length = hsk.addrsz;
+
+  /* --- Start putting everything else in --- */
+
+  {
+    char **p = (char **)(h + 1);
+    char *a = (char *)(p + hsk.nalias + hsk.naddr + 2);
+    int i;
+
+    /* --- Start with the address table --- */
+
+    h->h_addr_list = p;
+    if (doread(fd, a, hsk.naddr * hsk.addrsz))
+      goto tidy_2;
+    for (i = 0; i < hsk.naddr; i++) {
+      struct in_addr in;
+      *p++ = a;
+      memcpy(&in, a, sizeof(in));
+    }
+    *p++ = 0;
+
+    /* --- Finally copy the strings over --- */
+
+#define PUT(_p) do {                                                   \
+  size_t _len = strlen(_p) + 1;                                                \
+  memcpy(a, (_p), _len);                                               \
+  a += _len;                                                           \
+} while (0)
+
+    h->h_name = a;
+    PUT(name);
+    free(name);
+    h->h_aliases = p;
+    for (i = 0; i < hsk.nalias; i++) {
+      *p++ = a;
+      PUT(alias[i]);
+      free(alias[i]);
+    }
+    *p++ = 0;
+    free(alias);
+#undef PUT
+  }
+
+  return (h);
+
+  /* --- Tidy up after various types of failure --- */
+
+tidy_2:
+  {
+    int i;
+    for (i = 0; i < hsk.nalias && alias[i]; i++)
+      free(alias[i]);
+    free(alias);
+  }
+tidy_1:
+  free(name);
+tidy_0:
+  return (0);
+}
+
+#endif
+
+/* --- @puthost@ --- *
+ *
+ * Arguments:  @int fd@ = file descriptor
+ *             @struct hostent *h@ = pointer to host structure
+ *
+ * Returns:    Zero if successful.
+ *
+ * Use:                Encodes a host structure.
+ */
+
+static int puthost(int fd, struct hostent *h)
+{
+  hostskel hsk;
+  int i;
+
+  /* --- Fill in and send the skeleton structure --- */
+
+  for (i = 0; h->h_aliases[i]; i++)
+    ;
+  hsk.nalias = i;
+  for (i = 0; h->h_addr_list[i]; i++)
+    ;
+  hsk.naddr = i;
+  hsk.addrtype = h->h_addrtype;
+  hsk.addrsz = h->h_length;
+  if (dowrite(fd, &hsk, sizeof(hsk)))
+    return (-1);
+
+  /* --- Send the name and alias strings --- */
+
+  if (putstring(fd, h->h_name))
+    return (-1);
+  for (i = 0; h->h_aliases[i]; i++) {
+    if (putstring(fd, h->h_aliases[i]))
+      return (-1);
+  }
+
+  /* --- Send the address data --- */
+
+  for (i = 0; h->h_addr_list[i]; i++) {
+    if (dowrite(fd, h->h_addr_list[i], hsk.addrsz))
+      return (-1);
+  }
+
+  /* --- OK, done --- */
+
+  return (0);
+}
+
+/*----- Resolver server ---------------------------------------------------*/
+
+/* --- @child@ --- *
+ *
+ * Arguments:  @int rfd@ = output file descriptor for resolved hostnames
+ *             @int cfd@ = input file descriptor for raw addresses
+ *
+ * Returns:    Never.
+ *
+ * Use:                Asynchronous name resolving process.
+ */
+
+static void child(int rfd, int cfd)
+{
+  /* --- Close other file descriptors --- */
+
+  {
+    int i;
+    int maxfd = sysconf(_SC_OPEN_MAX);
+
+    if (maxfd < 0)
+      maxfd = 256; /* Fingers crossed... */
+    for (i = 0; i < maxfd; i++) {
+      if (i != rfd && i != cfd && i != 1)
+       close(i);
+    }
+  }
+
+  /* --- Main request/response loop --- */
+
+  for (;;) {
+    int req, resp;
+    struct hostent *h;
+
+    /* --- Read the request --- */
+
+    if (doread(cfd, &req, sizeof(req)))
+      goto lose;
+
+    /* --- Process it into a host structure --- */
+
+    switch (req) {
+
+      /* --- Normal forward lookup --- */
+
+      case BRES_BYNAME: {
+       char *name = getstring(cfd);
+       if (!name)
+         goto lose;
+       h = gethostbyname(name);
+       free(name);
+      }        break;
+
+      /* --- Forward lookup --- */
+
+      case BRES_BYADDR: {
+       struct in_addr addr;
+       char *p;
+       if (doread(cfd, &addr, sizeof(addr)))
+         goto lose;
+       if ((h = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == 0)
+         goto fail;
+
+       /* --- Do a forward lookup to confirm --- */
+
+       {
+         size_t sz = strlen(h->h_name) + 1;
+         if ((p = malloc(sz)) == 0)
+           goto fail;
+         memcpy(p, h->h_name, sz);
+       }
+
+       h = gethostbyname(p);
+       free(p);
+       if (!h)
+         goto fail;
+       p = 0;
+       if (h) {
+         char **pp;
+         for (pp = h->h_addr_list; *pp; pp++) {
+           struct in_addr a;
+           memcpy(&a, *pp, sizeof(a));
+           if (a.s_addr == addr.s_addr) {
+             p = h->h_name;
+             break;
+           }
+         }
+       }
+       if (!p) {
+         h = 0;
+         h_errno = NO_RECOVERY;
+       }
+      fail:;
+      }        break;
+
+      /* --- Unknown request -- may have lost sync --- */
+
+      default:
+       goto lose;
+    }
+
+    /* --- Transmit the response --- */
+
+    if (h) {
+      resp = BRES_HOSTENT;
+      if (dowrite(rfd, &resp, sizeof(resp)) || puthost(rfd, h))
+       goto lose;
+    } else {
+      resp = BRES_ERROR;
+      if (dowrite(rfd, &resp, sizeof(resp)) ||
+         dowrite(rfd, &h_errno, sizeof(h_errno)))
+       goto lose;
+    }
+  }
+
+lose:
+  _exit(1);
+}
+
+/* --- @main@ --- *
+ *
+ * Arguments:  @int argc@ = number of command line arguments
+ *             @char *argv[]@ = array of arguments
+ *
+ * Returns:    Runs until killed or an error occurs.
+ *
+ * Use:                A name resolver server process for mLib programs which need
+ *             this sort of thing.
+ */
+
+#ifdef BRES_STANDALONE
+
+int main(int argc, char *argv[])
+{
+  if (isatty(STDIN_FILENO)) {
+    char *p = strrchr(argv[0], '/');
+    if (p)
+      p++;
+    else
+      p = argv[0];
+    fprintf(stderr,
+           "%s: don't run this program unless you know what you're doing.\n",
+           p);
+    exit(1);
+  }
+  child(STDOUT_FILENO, STDIN_FILENO);
+  return (1);
+}
+
+#endif
+
+/*----- Main code ---------------------------------------------------------*/
+
+#ifndef BRES_STANDALONE
+
+/* --- @zap@ --- *
+ *
+ * Arguments:  @bres_server *rs@ = pointer to server block
+ *
+ * Returns:    ---
+ *
+ * Use:                Kills a server process, reaps the losing child and makes
+ *             things generally clean again.
+ */
+
+static void zap(bres_server *rs)
+{
+  /* --- Close the pipes, kill the child, and reap it --- */
+
+  if (rs->kid != -1) {
+    close(rs->fd);
+    close(rs->f.fd);
+    kill(rs->kid, SIGTERM);
+    waitpid(rs->kid, 0, 0);
+    rs->kid = -1;
+  }
+
+  /* --- Move the server to the back of the list --- */
+
+  rs->next->prev = rs->prev;
+  rs->prev->next = rs->next;
+  rs->next = FREE;
+  rs->prev = FREE->prev;
+  FREE->prev->next = rs;
+  FREE->prev = rs;
+}
+
+/* --- @bres_abort@ --- *
+ *
+ * Arguments:  @bres_client *rc@ = pointer to client block
+ *
+ * Returns:    ---
+ *
+ * Use:                Removes a queued job.
+ */
+
+void bres_abort(bres_client *rc)
+{
+  if (rc->q == BRES_BYNAME)
+    free(rc->u.name);
+  if (rc->rs) {
+    sel_rmfile(&rc->rs->f);
+    zap(rc->rs);
+    rc->rs = 0;
+  } else {
+    rc->next->prev = rc->prev;
+    rc->prev->next = rc->next;
+  }
+}
+
+/* --- @idle@ --- *
+ *
+ * Arguments:  @struct timeval *tv@ = pointer to the current time
+ *             @void *vp@ = pointer to a server block
+ *
+ * Returns:    ---
+ *
+ * Use:                Kills off a child which has been idle for too long.
+ */
+
+static void idle(struct timeval *tv, void *vp)
+{
+  bres_server *rs = vp;
+  zap(rs);
+}
+
+/* --- @answer@ --- *
+ *
+ * Arguments:  @int fd@ = file descriptor which is ready
+ *             @unsigned mode@ = what it's doing now
+ *             @void *vp@ = pointer to server block
+ *
+ * Returns:    ---
+ *
+ * Use:                Retrieves an answer from a name resolver process.
+ */
+
+static void attach(bres_client */*rc*/);
+
+static void answer(int fd, unsigned mode, void *vp)
+{
+  bres_server *rs = vp;
+  bres_client *rc = rs->rc;
+  struct hostent *h = 0;
+  int resp;
+  int fail = 1;
+
+  /* --- Report the result to my client --- */
+
+  sel_rmfile(&rs->f);
+  h_errno = -1;
+  if (doread(fd, &resp, sizeof(resp)) == 0) {
+    switch (resp) {
+      case BRES_ERROR:
+       doread(fd, &h_errno, sizeof(h_errno));
+       fail = 0;
+       break;
+      case BRES_HOSTENT:
+       h = gethost(fd);
+       fail = 0;
+       break;
+    }
+  }
+  if (rc) {
+    rc->func(h, rc->p);
+    if (rc->q == BRES_BYNAME)
+      free(rc->u.name);
+  }
+  if (h)
+    free(h);
+  if (fail)
+    zap(rs);
+  if (!rc)
+    return;
+
+  /* --- Wrap up the various structures --- */
+
+  rs->rc = 0;
+  rc->rs = 0;
+  rs->next = FREE->next;
+  rs->prev = FREE;
+  FREE->next->prev = rs;
+  FREE->next = rs;
+
+  /* --- Tie a timer onto the server block --- */
+
+  {
+    struct timeval tv;
+
+    gettimeofday(&tv, 0);
+    tv.tv_sec += BRES_IDLE;
+    sel_addtimer(sel, &rs->t, &tv, idle, rs);
+  }
+
+  /* --- If there are any clients waiting, attach one --- */
+
+  if (QUEUE->next != QUEUE) {
+    rc = QUEUE->next;
+    QUEUE->next = rc->next;
+    rc->next->prev = QUEUE;
+    attach(rc);
+  }
+}
+
+/* --- @start@ --- *
+ *
+ * Arguments:  @bres_server *rs@ = pointer to a server block
+ *
+ * Returns:    Zero if OK, nonzero if something failed.
+ *
+ * Use:                Starts up a child resolver process.
+ */
+
+static int start(bres_server *rs)
+{
+  int rfd[2], cfd[2];
+  pid_t kid;
+
+  /* --- Make the pipes --- */
+
+  if (pipe(rfd))
+    goto fail_0;
+  if (pipe(cfd))
+    goto fail_1;
+
+  /* --- Start up the child process --- */
+
+  if ((kid = fork()) < 0)
+    goto fail_2;
+  if (kid == 0) {
+    close(cfd[1]);
+    close(rfd[0]);
+
+    if (server) {
+      dup2(cfd[0], STDIN_FILENO);
+      dup2(rfd[1], STDOUT_FILENO);
+      close(cfd[0]);
+      close(rfd[1]);
+      execlp(server, server, (char *)0);
+      child(STDOUT_FILENO, STDIN_FILENO);
+    } else
+      child(rfd[1], cfd[0]);
+    _exit(1);
+  }
+
+  /* --- Fix up everything in the server block --- */
+
+  close(cfd[0]);
+  close(rfd[1]);
+  rs->fd = cfd[1];
+  sel_initfile(sel, &rs->f, rfd[0], SEL_READ, answer, rs);
+  rs->kid = kid;
+  return (0);
+
+  /* --- Fix up after errors --- */
+
+fail_2:
+  close(cfd[0]);
+  close(cfd[1]);
+fail_1:
+  close(rfd[0]);
+  close(rfd[1]);
+fail_0:
+  return (-1);
+}
+
+/* --- @attach@ --- *
+ *
+ * Arguments:  @bres_client *rc@ = pointer to a client block
+ *
+ * Returns:    ---
+ *
+ * Use:                Attaches a client to a spare server (which is assumed to
+ *             exist).
+ */
+
+static void attach(bres_client *rc)
+{
+  bres_server *rs;
+  int lose = 0;
+
+  /* --- Fix up the server ready for the job --- *
+   *
+   * If the server has a process, remove its timer.  Otherwise, fork off a
+   * new resolver process.  This is also where I go if I find that the child
+   * resolver process has lost while I wasn't looking.  Only one attempt at
+   * forking is performed.
+   */
+
+again:
+  rs = FREE->next;
+  if (rs->kid != -1)
+    sel_rmtimer(&rs->t);
+  else {
+    if (lose || start(rs))
+      goto lost;
+    lose = 1;
+  }
+
+  /* --- Submit the job to the resolver --- */
+
+  {
+    struct sigaction sa, osa;
+    int e;
+
+    /* --- Ignore @SIGPIPE@ for now --- *
+     *
+     * This way I can trap @EPIPE@ and reap a losing child, if there was one.
+     */
+
+    sa.sa_handler = SIG_IGN;
+    sa.sa_flags = 0;
+    sigemptyset(&sa.sa_mask);
+    sigaction(SIGPIPE, &sa, &osa);
+
+    /* --- Write the new job to the child --- */
+
+    e = 0;
+    if (dowrite(rs->fd, &rc->q, sizeof(rc->q)))
+      e = errno;
+    else switch (rc->q) {
+      case BRES_BYADDR:
+       if (dowrite(rs->fd, &rc->u.addr, sizeof(rc->u.addr)))
+         e = errno;
+       break;
+      case BRES_BYNAME:
+       if (putstring(rs->fd, rc->u.name))
+         e = errno;
+       break;
+    }
+    sigaction(SIGPIPE, &osa, 0);
+
+    /* --- Sort out various errors --- *
+     *
+     * This was once more complicated, handling @EPIPE@ separately from other
+     * errors.  Now everything's handled the same way.
+     */
+
+    if (e) {
+      zap(rs);
+      goto again;
+    }
+  }
+
+  /* --- Fiddle with lists so that everything's OK --- */
+
+  sel_addfile(&rs->f);
+  rs->next->prev = FREE;
+  FREE->next = rs->next;
+  rs->next = rs->prev = rs;
+  rs->rc = rc;
+  rc->rs = rs;
+  return;
+
+lost:
+  rc->func(0, rc->p);
+  if (rc->q == BRES_BYNAME)
+    free(rc->u.name);
+}
+
+/* --- @resolve@ --- *
+ *
+ * Arguments:  @bres_client *rc@ = pointer to filled-in client block
+ *
+ * Returns:    ---
+ *
+ * Use:                Dispatcher for incoming resolution jobs.
+ */
+
+static void resolve(bres_client *rc)
+{
+  /* --- If there's a free server, plug it in --- */
+
+  rc->rs = 0;
+  if (FREE->next == FREE) {
+    rc->next = QUEUE;
+    rc->prev = QUEUE->prev;
+    QUEUE->prev->next = rc;
+    QUEUE->prev = rc;
+  } else
+    attach(rc);
+}
+
+/* --- @bres_byaddr@ --- *
+ *
+ * Arguments:  @bres_client *rc@ = pointer to client block
+ *             @struct in_addr addr@ = address to resolve
+ *             @void (*func)(struct hostent *h, void *p)@ = handler function
+ *             @void *p@ = argument for handler function
+ *
+ * Returns:    ---
+ *
+ * Use:                Adds an address lookup job to the queue.  The job will be
+ *             processed when there's a spare resolver process to deal with
+ *             it.
+ */
+
+void bres_byaddr(bres_client *rc, struct in_addr addr,
+                void (*func)(struct hostent */*h*/, void */*p*/),
+                void *p)
+{
+  rc->q = BRES_BYADDR;
+  rc->u.addr = addr;
+  rc->func = func;
+  rc->p = p;
+  resolve(rc);
+}
+
+/* --- @bres_byname@ --- *
+ *
+ * Arguments:  @bres_client *rc@ = pointer to client block
+ *             @const char *name@ = name to resolve
+ *             @void (*func)(struct hostent *h, void *p)@ = handler function
+ *             @void *p@ = argument for handler function
+ *
+ * Returns:    ---
+ *
+ * Use:                Adds a name lookup job to the queue.  The job will be
+ *             processed when there's a spare resolver process to deal with
+ *             it.
+ */
+
+void bres_byname(bres_client *rc, const char *name,
+                void (*func)(struct hostent */*h*/, void */*p*/),
+                void *p)
+{
+  rc->q = BRES_BYNAME;
+  rc->u.name = xstrdup(name);
+  rc->func = func;
+  rc->p = p;
+  resolve(rc);
+}
+
+/* --- @bres_exec@ --- *
+ *
+ * Arguments:  @const char *file@ = file containing server code or null
+ *
+ * Returns:    ---
+ *
+ * Use:                Makes `bres' use a standalone server rather than copies of
+ *             the current process.  This can reduce memory consumption for
+ *             large processes, at the expense of startup time (which
+ *             shouldn't be too bad anyway, because of the resolver design).
+ *             If the filename is null, a default set up at install time is
+ *             used.  It's probably a good idea to leave it alone.
+ */
+
+void bres_exec(const char *file)
+{
+  if (file)
+    server = file;
+  else
+    server = BRES_SERVER;
+}
+
+/* --- @bres_init@ --- *
+ *
+ * Arguments:  @sel_state *s@ = pointer to select multiplexor
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes the background resolver for use.
+ */
+
+void bres_init(sel_state *s)
+{
+  int i;
+
+  sel = s;
+  for (i = 0; i < BRES_MAX; i++) {
+    servers[i].next = FREE;
+    servers[i].prev = FREE->prev;
+    servers[i].kid = -1;
+    servers[i].rc = 0;
+    FREE->prev->next = &servers[i];
+    FREE->prev = &servers[i];
+  }
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/bres.h b/bres.h
new file mode 100644 (file)
index 0000000..451f0fd
--- /dev/null
+++ b/bres.h
@@ -0,0 +1,166 @@
+/* -*-c-*-
+ *
+ * $Id: bres.h,v 1.1 1999/10/04 21:40:42 mdw Exp $
+ *
+ * Background reverse name resolution
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ * 
+ * mLib 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 Library General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Library General Public
+ * License along with mLib; if not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: bres.h,v $
+ * Revision 1.1  1999/10/04 21:40:42  mdw
+ * Added background resolver from `fw'.
+ *
+ */
+
+#ifndef RES_H
+#define RES_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <sys/types.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <mLib/sel.h>
+#include <mLib/selbuf.h>
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- Client allocated request block --- */
+
+typedef struct bres_client {
+  struct bres_client *next, *prev;     /* Queue of waiting resolve jobs */
+  struct bres_server *rs;              /* Pointer to attached server */
+  int q;                               /* Query type (name or address) */
+  union {
+    struct in_addr addr;               /* Address to resolve */
+    char *name;                                /* Name to resolve */
+  } u;
+  void (*func)(struct hostent */*h*/, void */*p*/); /* Handler function */
+  void *p;                             /* Argument for handler function */
+} bres_client;
+
+/* --- Server maintained resolver blocks --- */
+
+typedef struct bres_server {
+  struct bres_server *next, *prev;     /* Doubly-linked list of servers */
+  pid_t kid;                           /* Process id of server process */
+  int fd;                              /* File descriptors */
+  struct bres_client *rc;              /* Pointer to attached client */
+  sel_timer t;                         /* Timeout for idle servers */
+  sel_file f;                          /* Read selector for server */
+} bres_server;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @bres_abort@ --- *
+ *
+ * Arguments:  @bres_client *rc@ = pointer to client block
+ *
+ * Returns:    ---
+ *
+ * Use:                Removes a queued job.
+ */
+
+extern void bres_abort(bres_client */*rc*/);
+
+/* --- @bres_byaddr@ --- *
+ *
+ * Arguments:  @bres_client *rc@ = pointer to client block
+ *             @struct in_addr addr@ = address to resolve
+ *             @void (*func)(struct hostent *h, void *p)@ = handler function
+ *             @void *p@ = argument for handler function
+ *
+ * Returns:    ---
+ *
+ * Use:                Adds an address lookup job to the queue.  The job will be
+ *             processed when there's a spare resolver process to deal with
+ *             it.
+ */
+
+extern void bres_byaddr(bres_client */*rc*/, struct in_addr /*addr*/,
+                       void (*/*func*/)(struct hostent */*h*/, void */*p*/),
+                       void */*p*/);
+
+/* --- @bres_byname@ --- *
+ *
+ * Arguments:  @bres_client *rc@ = pointer to client block
+ *             @const char *name@ = name to resolve
+ *             @void (*func)(struct hostent *h, void *p)@ = handler function
+ *             @void *p@ = argument for handler function
+ *
+ * Returns:    ---
+ *
+ * Use:                Adds a name lookup job to the queue.  The job will be
+ *             processed when there's a spare resolver process to deal with
+ *             it.
+ */
+
+extern void bres_byname(bres_client */*rc*/, const char */*name*/,
+                       void (*/*func*/)(struct hostent */*h*/, void */*p*/),
+                       void */*p*/);
+
+/* --- @bres_exec@ --- *
+ *
+ * Arguments:  @const char *file@ = file containing server code or null
+ *
+ * Returns:    ---
+ *
+ * Use:                Makes `bres' use a standalone server rather than copies of
+ *             the current process.  This can reduce memory consumption for
+ *             large processes, at the expense of startup time (which
+ *             shouldn't be too bad anyway, because of the resolver design).
+ *             If the filename is null, a default set up at install time is
+ *             used.  It's probably a good idea to leave it alone.
+ */
+
+extern void bres_exec(const char */*file*/);
+
+/* --- @bres_init@ --- *
+ *
+ * Arguments:  @sel_state *s@ = pointer to select multiplexor
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes the background resolver for use.
+ */
+
+extern void bres_init(sel_state */*s*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif