3 * $Id: bres.c,v 1.5 2001/02/03 16:21:08 mdw Exp $
5 * Background reverse name resolution
7 * (c) 1999 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of the mLib utilities library.
14 * mLib is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
19 * mLib is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
24 * You should have received a copy of the GNU Library General Public
25 * License along with mLib; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
30 /*----- Revision history --------------------------------------------------*
33 * Revision 1.5 2001/02/03 16:21:08 mdw
34 * Bug fixes: restore signals to their default dispositions, and set up the
35 * addresses properly when unpacking @hostent@ structures.
37 * Revision 1.4 2000/08/15 17:35:02 mdw
38 * (gethost, and others): Since @gethost@ actually uses @malloc@ rather
39 * than @xmalloc@, it's wrong to use @xfree@ on the result. Fixed the code
40 * to use the right freeing function on the right data.
42 * Revision 1.3 2000/06/17 10:38:35 mdw
43 * Track changes to selbuf interface.
45 * Revision 1.2 1999/10/30 11:28:39 mdw
46 * Fix include error, pointed out by Chris Rutter.
48 * Revision 1.1 1999/10/04 21:40:42 mdw
49 * Added background resolver from `fw'.
53 /*----- Header files ------------------------------------------------------*/
61 #include <sys/types.h>
66 #include <sys/socket.h>
67 #include <netinet/in.h>
68 #include <arpa/inet.h>
76 /*----- Magic numbers -----------------------------------------------------*/
78 #define BRES_MAX 15 /* Maximum number of resolvers */
79 #define BRES_IDLE 60 /* Lifetime of an idle resolver */
81 /*----- Static variables --------------------------------------------------*/
83 #ifndef BRES_STANDALONE
85 static bres_server servers[BRES_MAX]; /* Statically allocated servers */
87 #define FREE ((bres_server *)&freelist)
88 static struct { bres_server *next, *prev; } freelist = { FREE, FREE };
90 #define QUEUE ((bres_client *)&queue)
91 static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE };
93 static sel_state *sel;
95 static const char *server = 0;
99 /*----- Background resolver protocol --------------------------------------*/
101 /* --- Requests and responses --- *
103 * There are two types of requests: name and addr, corresponding to the
104 * standard @gethostbyname@ and @gethostbyaddr@ calls. There are two types
105 * of responses too: a positive response consists of an encoded equivalent of
106 * a @struct hostent@ structure containing the requested information; a
107 * negative response consists of an @h_errno@ value explaining the problem.
110 #define BRES_BYNAME 0 /* Request: resolve given name */
111 #define BRES_BYADDR 1 /* Request: resolve given address */
113 #define BRES_HOSTENT 0 /* Response: resolved ok */
114 #define BRES_ERROR 1 /* Response: resolution failed */
116 /* --- Encodings --- *
118 * A string is encoded as a @size_t@ length followed by the actual data. The
119 * null terminator is not transmitted.
121 * Addresses for resolution are transmitted as raw @struct in_addr@
124 * A @hostent@ structure is transmitted as a header containing fixed-size
125 * information, followed by the official name, an array of aliases, and an
126 * array of addresses. The number of items in the arrays is specified in the
129 * The implementation assumes that a complete request or reply is always
130 * sent. Undesirable blocking will occur if this is not the case. Both ends
131 * are assumed to trust each other. A protocol failure results in the child
132 * in question being terminated.
135 typedef struct hostskel {
142 /* --- @doread@, @dowrite@ --- *
144 * Arguments: @int fd@ = file descriptor
145 * @void *buf@ = buffer for data
146 * @size_t sz@ = size of data
148 * Returns: Zero if successful, nonzero otherwise.
150 * Use: Reads or writes a chunk of data. @EINTR@ errors are retried;
151 * incomplete reads and writes are continued from where they
152 * left off. End-of-file is considered an I/O error.
155 static int doread(int fd, void *buf, size_t sz)
159 int r = read(fd, p, sz);
174 static int dowrite(int fd, const void *buf, size_t sz)
178 int r = write(fd, p, sz);
193 /* --- @getstring@ --- *
195 * Arguments: @int fd@ = file descriptor to read
197 * Returns: String in heap-allocated block, or a null pointer.
199 * Use: Decodes a string.
202 static char *getstring(int fd)
207 if (doread(fd, &sz, sizeof(sz)) || (p = malloc(sz + 1)) == 0)
209 if (doread(fd, p, sz)) {
217 /* --- @putstring@ --- *
219 * Arguments: @int fd@ = file descriptor to write on
220 * @const char *p@ = pointer to string to write
222 * Returns: Zero if successful.
224 * Use: Encodes a string.
227 static int putstring(int fd, const char *p)
229 size_t sz = strlen(p);
230 if (dowrite(fd, &sz, sizeof(sz)) || dowrite(fd, p, sz))
235 /* --- @gethost@ --- *
237 * Arguments: @int fd@ = file descriptor to read
239 * Returns: Pointer to heap-allocated @struct hostent@, or null.
241 * Use: Decodes a host structure. The resulting structure is all in
242 * one big heap block.
245 #ifndef BRES_STANDALONE
247 static struct hostent *gethost(int fd)
254 /* --- Read the skeleton structure --- */
256 if (doread(fd, &hsk, sizeof(hsk)))
259 /* --- Read the hostname and alias strings --- *
261 * Count the length of the strings as we go.
266 sizeof(struct hostent) +
267 hsk.naddr * hsk.addrsz +
268 (hsk.naddr + hsk.nalias + 2) * sizeof(char *);
270 /* --- Read the primary host name --- */
272 if ((name = getstring(fd)) == 0)
274 sz += strlen(name) + 1;
276 /* --- Read in the alias names --- */
280 if ((alias = malloc(hsk.nalias * sizeof(char *))) == 0)
282 for (i = 0; i < hsk.nalias; i++)
284 for (i = 0; i < hsk.nalias; i++) {
285 if ((alias[i] = getstring(fd)) == 0)
287 sz += strlen(alias[i]) + 1;
291 /* --- Allocate the output structure --- */
293 if ((h = malloc(sz)) == 0)
297 /* --- Fill in the base structure --- */
299 h->h_addrtype = hsk.addrtype;
300 h->h_length = hsk.addrsz;
302 /* --- Start putting everything else in --- */
305 char **p = (char **)(h + 1);
306 char *a = (char *)(p + hsk.nalias + hsk.naddr + 2);
309 /* --- Start with the address table --- */
312 if (doread(fd, a, hsk.naddr * hsk.addrsz))
314 for (i = 0; i < hsk.naddr; i++) {
320 /* --- Finally copy the strings over --- */
322 #define PUT(_p) do { \
323 size_t _len = strlen(_p) + 1; \
324 memcpy(a, (_p), _len); \
332 for (i = 0; i < hsk.nalias; i++) {
345 /* --- Tidy up after various types of failure --- */
350 for (i = 0; i < hsk.nalias && alias[i]; i++)
362 /* --- @puthost@ --- *
364 * Arguments: @int fd@ = file descriptor
365 * @struct hostent *h@ = pointer to host structure
367 * Returns: Zero if successful.
369 * Use: Encodes a host structure.
372 static int puthost(int fd, struct hostent *h)
377 /* --- Fill in and send the skeleton structure --- */
379 for (i = 0; h->h_aliases[i]; i++)
382 for (i = 0; h->h_addr_list[i]; i++)
385 hsk.addrtype = h->h_addrtype;
386 hsk.addrsz = h->h_length;
387 if (dowrite(fd, &hsk, sizeof(hsk)))
390 /* --- Send the name and alias strings --- */
392 if (putstring(fd, h->h_name))
394 for (i = 0; h->h_aliases[i]; i++) {
395 if (putstring(fd, h->h_aliases[i]))
399 /* --- Send the address data --- */
401 for (i = 0; h->h_addr_list[i]; i++) {
402 if (dowrite(fd, h->h_addr_list[i], hsk.addrsz))
406 /* --- OK, done --- */
411 /*----- Resolver server ---------------------------------------------------*/
415 * Arguments: @int rfd@ = output file descriptor for resolved hostnames
416 * @int cfd@ = input file descriptor for raw addresses
420 * Use: Asynchronous name resolving process.
423 static void child(int rfd, int cfd)
425 /* --- Close other file descriptors --- */
429 int maxfd = sysconf(_SC_OPEN_MAX);
432 maxfd = 256; /* Fingers crossed... */
433 for (i = 0; i < maxfd; i++) {
434 if (i != rfd && i != cfd && i != 1)
439 signal(SIGTERM, SIG_DFL);
440 signal(SIGHUP, SIG_DFL);
441 signal(SIGQUIT, SIG_DFL);
442 signal(SIGALRM, SIG_DFL);
443 signal(SIGINT, SIG_DFL);
445 /* --- Main request/response loop --- */
451 /* --- Read the request --- */
453 if (doread(cfd, &req, sizeof(req)))
456 /* --- Process it into a host structure --- */
460 /* --- Normal forward lookup --- */
463 char *name = getstring(cfd);
466 h = gethostbyname(name);
470 /* --- Reverse lookup --- */
475 if (doread(cfd, &addr, sizeof(addr)))
477 if ((h = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == 0)
480 /* --- Do a forward lookup to confirm --- */
483 size_t sz = strlen(h->h_name) + 1;
484 if ((p = malloc(sz)) == 0)
486 memcpy(p, h->h_name, sz);
489 h = gethostbyname(p);
496 for (pp = h->h_addr_list; *pp; pp++) {
498 memcpy(&a, *pp, sizeof(a));
499 if (a.s_addr == addr.s_addr) {
507 h_errno = NO_RECOVERY;
512 /* --- Unknown request -- may have lost sync --- */
518 /* --- Transmit the response --- */
522 if (dowrite(rfd, &resp, sizeof(resp)) || puthost(rfd, h))
526 if (dowrite(rfd, &resp, sizeof(resp)) ||
527 dowrite(rfd, &h_errno, sizeof(h_errno)))
538 * Arguments: @int argc@ = number of command line arguments
539 * @char *argv[]@ = array of arguments
541 * Returns: Runs until killed or an error occurs.
543 * Use: A name resolver server process for mLib programs which need
544 * this sort of thing.
547 #ifdef BRES_STANDALONE
549 int main(int argc, char *argv[])
551 if (isatty(STDIN_FILENO)) {
552 char *p = strrchr(argv[0], '/');
558 "%s: don't run this program unless you know what you're doing.\n",
562 child(STDOUT_FILENO, STDIN_FILENO);
568 /*----- Main code ---------------------------------------------------------*/
570 #ifndef BRES_STANDALONE
574 * Arguments: @bres_server *rs@ = pointer to server block
578 * Use: Kills a server process, reaps the losing child and makes
579 * things generally clean again.
582 static void zap(bres_server *rs)
584 /* --- Close the pipes, kill the child, and reap it --- */
589 kill(rs->kid, SIGTERM);
590 waitpid(rs->kid, 0, 0);
594 /* --- Move the server to the back of the list --- */
596 rs->next->prev = rs->prev;
597 rs->prev->next = rs->next;
599 rs->prev = FREE->prev;
600 FREE->prev->next = rs;
604 /* --- @bres_abort@ --- *
606 * Arguments: @bres_client *rc@ = pointer to client block
610 * Use: Removes a queued job.
613 void bres_abort(bres_client *rc)
615 if (rc->q == BRES_BYNAME)
618 sel_rmfile(&rc->rs->f);
622 rc->next->prev = rc->prev;
623 rc->prev->next = rc->next;
629 * Arguments: @struct timeval *tv@ = pointer to the current time
630 * @void *vp@ = pointer to a server block
634 * Use: Kills off a child which has been idle for too long.
637 static void idle(struct timeval *tv, void *vp)
639 bres_server *rs = vp;
643 /* --- @answer@ --- *
645 * Arguments: @int fd@ = file descriptor which is ready
646 * @unsigned mode@ = what it's doing now
647 * @void *vp@ = pointer to server block
651 * Use: Retrieves an answer from a name resolver process.
654 static void attach(bres_client */*rc*/);
656 static void answer(int fd, unsigned mode, void *vp)
658 bres_server *rs = vp;
659 bres_client *rc = rs->rc;
660 struct hostent *h = 0;
664 /* --- Report the result to my client --- */
668 if (doread(fd, &resp, sizeof(resp)) == 0) {
671 doread(fd, &h_errno, sizeof(h_errno));
682 if (rc->q == BRES_BYNAME)
692 /* --- Wrap up the various structures --- */
696 rs->next = FREE->next;
698 FREE->next->prev = rs;
701 /* --- Tie a timer onto the server block --- */
706 gettimeofday(&tv, 0);
707 tv.tv_sec += BRES_IDLE;
708 sel_addtimer(sel, &rs->t, &tv, idle, rs);
711 /* --- If there are any clients waiting, attach one --- */
713 if (QUEUE->next != QUEUE) {
715 QUEUE->next = rc->next;
716 rc->next->prev = QUEUE;
723 * Arguments: @bres_server *rs@ = pointer to a server block
725 * Returns: Zero if OK, nonzero if something failed.
727 * Use: Starts up a child resolver process.
730 static int start(bres_server *rs)
735 /* --- Make the pipes --- */
742 /* --- Start up the child process --- */
744 if ((kid = fork()) < 0)
751 dup2(cfd[0], STDIN_FILENO);
752 dup2(rfd[1], STDOUT_FILENO);
755 execlp(server, server, (char *)0);
756 child(STDOUT_FILENO, STDIN_FILENO);
758 child(rfd[1], cfd[0]);
762 /* --- Fix up everything in the server block --- */
767 sel_initfile(sel, &rs->f, rfd[0], SEL_READ, answer, rs);
771 /* --- Fix up after errors --- */
783 /* --- @attach@ --- *
785 * Arguments: @bres_client *rc@ = pointer to a client block
789 * Use: Attaches a client to a spare server (which is assumed to
793 static void attach(bres_client *rc)
798 /* --- Fix up the server ready for the job --- *
800 * If the server has a process, remove its timer. Otherwise, fork off a
801 * new resolver process. This is also where I go if I find that the child
802 * resolver process has lost while I wasn't looking. Only one attempt at
803 * forking is performed.
811 if (lose || start(rs))
816 /* --- Submit the job to the resolver --- */
819 struct sigaction sa, osa;
822 /* --- Ignore @SIGPIPE@ for now --- *
824 * This way I can trap @EPIPE@ and reap a losing child, if there was one.
827 sa.sa_handler = SIG_IGN;
829 sigemptyset(&sa.sa_mask);
830 sigaction(SIGPIPE, &sa, &osa);
832 /* --- Write the new job to the child --- */
835 if (dowrite(rs->fd, &rc->q, sizeof(rc->q)))
837 else switch (rc->q) {
839 if (dowrite(rs->fd, &rc->u.addr, sizeof(rc->u.addr)))
843 if (putstring(rs->fd, rc->u.name))
847 sigaction(SIGPIPE, &osa, 0);
849 /* --- Sort out various errors --- *
851 * This was once more complicated, handling @EPIPE@ separately from other
852 * errors. Now everything's handled the same way.
861 /* --- Fiddle with lists so that everything's OK --- */
864 rs->next->prev = FREE;
865 FREE->next = rs->next;
866 rs->next = rs->prev = rs;
873 if (rc->q == BRES_BYNAME)
877 /* --- @resolve@ --- *
879 * Arguments: @bres_client *rc@ = pointer to filled-in client block
883 * Use: Dispatcher for incoming resolution jobs.
886 static void resolve(bres_client *rc)
888 /* --- If there's a free server, plug it in --- */
891 if (FREE->next == FREE) {
893 rc->prev = QUEUE->prev;
894 QUEUE->prev->next = rc;
900 /* --- @bres_byaddr@ --- *
902 * Arguments: @bres_client *rc@ = pointer to client block
903 * @struct in_addr addr@ = address to resolve
904 * @void (*func)(struct hostent *h, void *p)@ = handler function
905 * @void *p@ = argument for handler function
909 * Use: Adds an address lookup job to the queue. The job will be
910 * processed when there's a spare resolver process to deal with
914 void bres_byaddr(bres_client *rc, struct in_addr addr,
915 void (*func)(struct hostent */*h*/, void */*p*/),
925 /* --- @bres_byname@ --- *
927 * Arguments: @bres_client *rc@ = pointer to client block
928 * @const char *name@ = name to resolve
929 * @void (*func)(struct hostent *h, void *p)@ = handler function
930 * @void *p@ = argument for handler function
934 * Use: Adds a name lookup job to the queue. The job will be
935 * processed when there's a spare resolver process to deal with
939 void bres_byname(bres_client *rc, const char *name,
940 void (*func)(struct hostent */*h*/, void */*p*/),
944 rc->u.name = xstrdup(name);
950 /* --- @bres_exec@ --- *
952 * Arguments: @const char *file@ = file containing server code or null
956 * Use: Makes `bres' use a standalone server rather than copies of
957 * the current process. This can reduce memory consumption for
958 * large processes, at the expense of startup time (which
959 * shouldn't be too bad anyway, because of the resolver design).
960 * If the filename is null, a default set up at install time is
961 * used. It's probably a good idea to leave it alone.
964 void bres_exec(const char *file)
969 server = BRES_SERVER;
972 /* --- @bres_init@ --- *
974 * Arguments: @sel_state *s@ = pointer to select multiplexor
978 * Use: Initializes the background resolver for use.
981 void bres_init(sel_state *s)
986 for (i = 0; i < BRES_MAX; i++) {
987 servers[i].next = FREE;
988 servers[i].prev = FREE->prev;
991 FREE->prev->next = &servers[i];
992 FREE->prev = &servers[i];
998 /*----- That's all, folks -------------------------------------------------*/