From a759efa6dcc67b035726a4428157c35627653966 Mon Sep 17 00:00:00 2001 From: mdw Date: Mon, 4 Oct 1999 21:40:42 +0000 Subject: [PATCH] Added background resolver from `fw'. --- bres.c | 979 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ bres.h | 166 +++++++++++ 2 files changed, 1145 insertions(+) create mode 100644 bres.c create mode 100644 bres.h diff --git a/bres.c b/bres.c new file mode 100644 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#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 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 + +#include +#include +#include +#include + +#include +#include + +/*----- 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 -- 2.11.0