--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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