chiark / gitweb /
Expunge CVS cruft.
[mLib] / bres.c
1 /* -*-c-*-
2  *
3  * $Id: bres.c,v 1.7 2004/04/08 01:36:11 mdw Exp $
4  *
5  * Background reverse name resolution
6  *
7  * (c) 1999 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of the mLib utilities library.
13  *
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.
18  * 
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.
23  * 
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,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Header files ------------------------------------------------------*/
31
32 #include <errno.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include <sys/types.h>
39 #include <sys/time.h>
40 #include <unistd.h>
41 #include <sys/wait.h>
42
43 #include <sys/socket.h>
44 #include <netinet/in.h>
45 #include <arpa/inet.h>
46 #include <netdb.h>
47
48 #include "alloc.h"
49 #include "bres.h"
50 #include "report.h"
51 #include "sel.h"
52
53 /*----- Magic numbers -----------------------------------------------------*/
54
55 #define BRES_MAX 15                     /* Maximum number of resolvers */
56 #define BRES_IDLE 60                    /* Lifetime of an idle resolver */
57
58 /*----- Static variables --------------------------------------------------*/
59
60 #ifndef BRES_STANDALONE
61
62 static bres_server servers[BRES_MAX];   /* Statically allocated servers */
63
64 #define FREE ((bres_server *)&freelist)
65 static struct { bres_server *next, *prev; } freelist = { FREE, FREE };
66
67 #define QUEUE ((bres_client *)&queue)
68 static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE };
69
70 static sel_state *sel;
71
72 static const char *server = 0;
73
74 #endif
75
76 /*----- Background resolver protocol --------------------------------------*/
77
78 /* --- Requests and responses --- *
79  *
80  * There are two types of requests: name and addr, corresponding to the
81  * standard @gethostbyname@ and @gethostbyaddr@ calls.  There are two types
82  * of responses too: a positive response consists of an encoded equivalent of
83  * a @struct hostent@ structure containing the requested information; a
84  * negative response consists of an @h_errno@ value explaining the problem.
85  */
86
87 #define BRES_BYNAME 0                   /* Request: resolve given name */
88 #define BRES_BYADDR 1                   /* Request: resolve given address */
89
90 #define BRES_HOSTENT 0                  /* Response: resolved ok */
91 #define BRES_ERROR 1                    /* Response: resolution failed */
92
93 /* --- Encodings --- *
94  *
95  * A string is encoded as a @size_t@ length followed by the actual data.  The
96  * null terminator is not transmitted.
97  *
98  * Addresses for resolution are transmitted as raw @struct in_addr@
99  * structures.
100  *
101  * A @hostent@ structure is transmitted as a header containing fixed-size
102  * information, followed by the official name, an array of aliases, and an
103  * array of addresses.  The number of items in the arrays is specified in the
104  * header.
105  *
106  * The implementation assumes that a complete request or reply is always
107  * sent.  Undesirable blocking will occur if this is not the case.  Both ends
108  * are assumed to trust each other.  A protocol failure results in the child
109  * in question being terminated.
110  */
111
112 typedef struct hostskel {
113   size_t nalias;
114   int addrtype;
115   size_t addrsz;
116   size_t naddr;
117 } hostskel;
118
119 /* --- @doread@, @dowrite@ --- *
120  *
121  * Arguments:   @int fd@ = file descriptor
122  *              @void *buf@ = buffer for data
123  *              @size_t sz@ = size of data
124  *
125  * Returns:     Zero if successful, nonzero otherwise.
126  *
127  * Use:         Reads or writes a chunk of data.  @EINTR@ errors are retried;
128  *              incomplete reads and writes are continued from where they
129  *              left off.  End-of-file is considered an I/O error.
130  */
131
132 static int doread(int fd, void *buf, size_t sz)
133 {
134   char *p = buf;
135   while (sz) {
136     int r = read(fd, p, sz);
137     if (r < 0) {
138       if (errno == EINTR)
139         continue;
140       return (-1);
141     } else if (r == 0) {
142       errno = EIO;
143       return (-1);
144     }
145     sz -= r;
146     p += r;
147   }
148   return (0);
149 }
150
151 static int dowrite(int fd, const void *buf, size_t sz)
152 {
153   const char *p = buf;
154   while (sz) {
155     int r = write(fd, p, sz);
156     if (r < 0) {
157       if (errno == EINTR)
158         continue;
159       return (-1);
160     } else if (r == 0) {
161       errno = EIO;
162       return (-1);
163     }
164     sz -= r;
165     p += r;
166   }
167   return (0);
168 }
169
170 /* --- @getstring@ --- *
171  *
172  * Arguments:   @int fd@ = file descriptor to read
173  *
174  * Returns:     String in heap-allocated block, or a null pointer.
175  *
176  * Use:         Decodes a string.
177  */
178
179 static char *getstring(int fd)
180 {
181   size_t sz;
182   char *p;
183
184   if (doread(fd, &sz, sizeof(sz)) || (p = malloc(sz + 1)) == 0)
185     return (0);
186   if (doread(fd, p, sz)) {
187     free(p);
188     return (0);
189   }
190   p[sz] = 0;
191   return (p);
192 }
193
194 /* --- @putstring@ --- *
195  *
196  * Arguments:   @int fd@ = file descriptor to write on
197  *              @const char *p@ = pointer to string to write
198  *
199  * Returns:     Zero if successful.
200  *
201  * Use:         Encodes a string.
202  */
203
204 static int putstring(int fd, const char *p)
205 {
206   size_t sz = strlen(p);
207   if (dowrite(fd, &sz, sizeof(sz)) || dowrite(fd, p, sz))
208     return (-1);
209   return (0);
210 }
211
212 /* --- @gethost@ --- *
213  *
214  * Arguments:   @int fd@ = file descriptor to read
215  *
216  * Returns:     Pointer to heap-allocated @struct hostent@, or null.
217  *
218  * Use:         Decodes a host structure.  The resulting structure is all in
219  *              one big heap block.
220  */
221
222 #ifndef BRES_STANDALONE
223
224 static struct hostent *gethost(int fd)
225 {
226   hostskel hsk;
227   struct hostent *h;
228   char *name;
229   char **alias = 0;
230   
231   /* --- Read the skeleton structure --- */
232
233   if (doread(fd, &hsk, sizeof(hsk)))
234     goto tidy_0;
235
236   /* --- Read the hostname and alias strings --- *
237    *
238    * Count the length of the strings as we go.
239    */
240
241   {
242     size_t sz =
243       sizeof(struct hostent) +
244       hsk.naddr * hsk.addrsz +
245       (hsk.naddr + hsk.nalias + 2) * sizeof(char *);
246
247     /* --- Read the primary host name --- */
248
249     if ((name = getstring(fd)) == 0)
250       goto tidy_0;
251     sz += strlen(name) + 1;
252
253     /* --- Read in the alias names --- */
254
255     if (hsk.nalias) {
256       int i;
257       if ((alias = malloc(hsk.nalias * sizeof(char *))) == 0)
258         goto tidy_1;      
259       for (i = 0; i < hsk.nalias; i++)
260         alias[i] = 0;
261       for (i = 0; i < hsk.nalias; i++) {
262         if ((alias[i] = getstring(fd)) == 0)
263           goto tidy_2;
264         sz += strlen(alias[i]) + 1;
265       }
266     }
267
268     /* --- Allocate the output structure --- */
269
270     if ((h = malloc(sz)) == 0)
271       goto tidy_2;
272   }
273
274   /* --- Fill in the base structure --- */
275
276   h->h_addrtype = hsk.addrtype;
277   h->h_length = hsk.addrsz;
278
279   /* --- Start putting everything else in --- */
280
281   {
282     char **p = (char **)(h + 1);
283     char *a = (char *)(p + hsk.nalias + hsk.naddr + 2);
284     int i;
285
286     /* --- Start with the address table --- */
287
288     h->h_addr_list = p;
289     if (doread(fd, a, hsk.naddr * hsk.addrsz))
290       goto tidy_2;
291     for (i = 0; i < hsk.naddr; i++) {
292       *p++ = a;
293       a += hsk.addrsz;
294     }
295     *p++ = 0;
296
297     /* --- Finally copy the strings over --- */
298
299 #define PUT(_p) do {                                                    \
300   size_t _len = strlen(_p) + 1;                                         \
301   memcpy(a, (_p), _len);                                                \
302   a += _len;                                                            \
303 } while (0)
304
305     h->h_name = a;
306     PUT(name);
307     free(name);
308     h->h_aliases = p;
309     for (i = 0; i < hsk.nalias; i++) {
310       *p++ = a;
311       PUT(alias[i]);
312       free(alias[i]);
313     }
314     *p++ = 0;
315     free(alias);
316  
317 #undef PUT
318   }
319
320   return (h);
321
322   /* --- Tidy up after various types of failure --- */
323
324 tidy_2:
325   {
326     int i;
327     for (i = 0; i < hsk.nalias && alias[i]; i++)
328       free(alias[i]);
329     free(alias);
330   }
331 tidy_1:
332   free(name);
333 tidy_0:
334   return (0);
335 }
336
337 #endif
338
339 /* --- @puthost@ --- *
340  *
341  * Arguments:   @int fd@ = file descriptor
342  *              @struct hostent *h@ = pointer to host structure
343  *
344  * Returns:     Zero if successful.
345  *
346  * Use:         Encodes a host structure.
347  */
348
349 static int puthost(int fd, struct hostent *h)
350 {
351   hostskel hsk;
352   int i;
353
354   /* --- Fill in and send the skeleton structure --- */
355
356   for (i = 0; h->h_aliases[i]; i++)
357     ;
358   hsk.nalias = i;
359   for (i = 0; h->h_addr_list[i]; i++)
360     ;
361   hsk.naddr = i;
362   hsk.addrtype = h->h_addrtype;
363   hsk.addrsz = h->h_length;
364   if (dowrite(fd, &hsk, sizeof(hsk)))
365     return (-1);
366
367   /* --- Send the name and alias strings --- */
368
369   if (putstring(fd, h->h_name))
370     return (-1);
371   for (i = 0; h->h_aliases[i]; i++) {
372     if (putstring(fd, h->h_aliases[i]))
373       return (-1);
374   }
375
376   /* --- Send the address data --- */
377
378   for (i = 0; h->h_addr_list[i]; i++) {
379     if (dowrite(fd, h->h_addr_list[i], hsk.addrsz))
380       return (-1);
381   }
382
383   /* --- OK, done --- */
384
385   return (0);
386 }
387
388 /*----- Resolver server ---------------------------------------------------*/
389
390 /* --- @child@ --- *
391  *
392  * Arguments:   @int rfd@ = output file descriptor for resolved hostnames
393  *              @int cfd@ = input file descriptor for raw addresses
394  *
395  * Returns:     Never.
396  *
397  * Use:         Asynchronous name resolving process.
398  */
399
400 static void child(int rfd, int cfd)
401 {
402   /* --- Close other file descriptors --- */
403
404   {
405     int i;
406 #if defined(_SC_OPEN_MAX)
407     int maxfd = sysconf(_SC_OPEN_MAX);
408 #elif defined(OPEN_MAX)
409     int maxfd = OPEN_MAX;
410 #else
411     int maxfd = -1;
412 #endif
413
414     if (maxfd < 0)
415       maxfd = 256; /* Fingers crossed... */
416     for (i = 0; i < maxfd; i++) {
417       if (i != rfd && i != cfd && i != 1)
418         close(i);
419     }
420   }
421
422   signal(SIGTERM, SIG_DFL);
423   signal(SIGHUP, SIG_DFL);
424   signal(SIGQUIT, SIG_DFL);
425   signal(SIGALRM, SIG_DFL);
426   signal(SIGINT, SIG_DFL);
427
428   /* --- Main request/response loop --- */
429
430   for (;;) {
431     int req, resp;
432     struct hostent *h;
433
434     /* --- Read the request --- */
435
436     if (doread(cfd, &req, sizeof(req)))
437       goto lose;
438
439     /* --- Process it into a host structure --- */
440
441     switch (req) {
442
443       /* --- Normal forward lookup --- */
444
445       case BRES_BYNAME: {
446         char *name = getstring(cfd);
447         if (!name)
448           goto lose;
449         h = gethostbyname(name);
450         free(name);
451       } break;
452
453       /* --- Reverse lookup --- */
454
455       case BRES_BYADDR: {
456         struct in_addr addr;
457         char *p;
458         if (doread(cfd, &addr, sizeof(addr)))
459           goto lose;
460         if ((h = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == 0)
461           goto fail;
462
463         /* --- Do a forward lookup to confirm --- */
464
465         {
466           size_t sz = strlen(h->h_name) + 1;
467           if ((p = malloc(sz)) == 0)
468             goto fail;
469           memcpy(p, h->h_name, sz);
470         }
471
472         h = gethostbyname(p);
473         free(p);
474         if (!h)
475           goto fail;
476         p = 0;
477         if (h) {
478           char **pp;
479           for (pp = h->h_addr_list; *pp; pp++) {
480             struct in_addr a;
481             memcpy(&a, *pp, sizeof(a));
482             if (a.s_addr == addr.s_addr) {
483               p = h->h_name;
484               break;
485             }
486           }
487         }
488         if (!p) {
489           h = 0;
490           h_errno = NO_RECOVERY;
491         }
492       fail:;
493       } break;
494
495       /* --- Unknown request -- may have lost sync --- */
496
497       default:
498         goto lose;
499     }
500
501     /* --- Transmit the response --- */
502
503     if (h) {
504       resp = BRES_HOSTENT;
505       if (dowrite(rfd, &resp, sizeof(resp)) || puthost(rfd, h))
506         goto lose;
507     } else {
508       resp = BRES_ERROR;
509       if (dowrite(rfd, &resp, sizeof(resp)) ||
510           dowrite(rfd, &h_errno, sizeof(h_errno)))
511         goto lose;
512     }
513   }
514
515 lose:
516   _exit(1);
517 }
518
519 /* --- @main@ --- *
520  *
521  * Arguments:   @int argc@ = number of command line arguments
522  *              @char *argv[]@ = array of arguments
523  *
524  * Returns:     Runs until killed or an error occurs.
525  *
526  * Use:         A name resolver server process for mLib programs which need
527  *              this sort of thing.
528  */
529
530 #ifdef BRES_STANDALONE
531
532 int main(int argc, char *argv[])
533 {
534   if (isatty(STDIN_FILENO)) {
535     char *p = strrchr(argv[0], '/');
536     if (p)
537       p++;
538     else
539       p = argv[0];
540     fprintf(stderr,
541             "%s: don't run this program unless you know what you're doing.\n",
542             p);
543     exit(1);
544   }
545   child(STDOUT_FILENO, STDIN_FILENO);
546   return (1);
547 }
548
549 #endif
550
551 /*----- Main code ---------------------------------------------------------*/
552
553 #ifndef BRES_STANDALONE
554
555 /* --- @zap@ --- *
556  *
557  * Arguments:   @bres_server *rs@ = pointer to server block
558  *
559  * Returns:     ---
560  *
561  * Use:         Kills a server process, reaps the losing child and makes
562  *              things generally clean again.
563  */
564
565 static void zap(bres_server *rs)
566 {
567   /* --- Close the pipes, kill the child, and reap it --- */
568
569   if (rs->kid != -1) {
570     close(rs->fd);
571     close(rs->f.fd);
572     kill(rs->kid, SIGTERM);
573     waitpid(rs->kid, 0, 0);
574     rs->kid = -1;
575   }
576
577   /* --- Move the server to the back of the list --- */
578
579   rs->next->prev = rs->prev;
580   rs->prev->next = rs->next;
581   rs->next = FREE;
582   rs->prev = FREE->prev;
583   FREE->prev->next = rs;
584   FREE->prev = rs;
585 }
586
587 /* --- @bres_abort@ --- *
588  *
589  * Arguments:   @bres_client *rc@ = pointer to client block
590  *
591  * Returns:     ---
592  *
593  * Use:         Removes a queued job.
594  */
595
596 void bres_abort(bres_client *rc)
597 {
598   if (rc->q == BRES_BYNAME)
599     xfree(rc->u.name);
600   if (rc->rs) {
601     sel_rmfile(&rc->rs->f);
602     zap(rc->rs);
603     rc->rs = 0;
604   } else {
605     rc->next->prev = rc->prev;
606     rc->prev->next = rc->next;
607   }
608 }
609
610 /* --- @idle@ --- *
611  *
612  * Arguments:   @struct timeval *tv@ = pointer to the current time
613  *              @void *vp@ = pointer to a server block
614  *
615  * Returns:     ---
616  *
617  * Use:         Kills off a child which has been idle for too long.
618  */
619
620 static void idle(struct timeval *tv, void *vp)
621 {
622   bres_server *rs = vp;
623   zap(rs);
624 }
625
626 /* --- @answer@ --- *
627  *
628  * Arguments:   @int fd@ = file descriptor which is ready
629  *              @unsigned mode@ = what it's doing now
630  *              @void *vp@ = pointer to server block
631  *
632  * Returns:     ---
633  *
634  * Use:         Retrieves an answer from a name resolver process.
635  */
636
637 static void attach(bres_client */*rc*/);
638
639 static void answer(int fd, unsigned mode, void *vp)
640 {
641   bres_server *rs = vp;
642   bres_client *rc = rs->rc;
643   struct hostent *h = 0;
644   int resp;
645   int fail = 1;
646
647   /* --- Report the result to my client --- */
648
649   sel_rmfile(&rs->f);
650   h_errno = -1;
651   if (doread(fd, &resp, sizeof(resp)) == 0) {
652     switch (resp) {
653       case BRES_ERROR:
654         doread(fd, &h_errno, sizeof(h_errno));
655         fail = 0;
656         break;
657       case BRES_HOSTENT:
658         h = gethost(fd);
659         fail = 0;
660         break;
661     }
662   }
663   if (rc) {
664     rc->func(h, rc->p);
665     if (rc->q == BRES_BYNAME)
666       xfree(rc->u.name);
667   }
668   if (h)
669     free(h);
670   if (fail)
671     zap(rs);
672   if (!rc)
673     return;
674
675   /* --- Wrap up the various structures --- */
676
677   rs->rc = 0;
678   rc->rs = 0;
679   rs->next = FREE->next;
680   rs->prev = FREE;
681   FREE->next->prev = rs;
682   FREE->next = rs;
683
684   /* --- Tie a timer onto the server block --- */
685
686   {
687     struct timeval tv;
688
689     gettimeofday(&tv, 0);
690     tv.tv_sec += BRES_IDLE;
691     sel_addtimer(sel, &rs->t, &tv, idle, rs);
692   }
693
694   /* --- If there are any clients waiting, attach one --- */
695
696   if (QUEUE->next != QUEUE) {
697     rc = QUEUE->next;
698     QUEUE->next = rc->next;
699     rc->next->prev = QUEUE;
700     attach(rc);
701   }
702 }
703
704 /* --- @start@ --- *
705  *
706  * Arguments:   @bres_server *rs@ = pointer to a server block
707  *
708  * Returns:     Zero if OK, nonzero if something failed.
709  *
710  * Use:         Starts up a child resolver process.
711  */
712
713 static int start(bres_server *rs)
714 {
715   int rfd[2], cfd[2];
716   pid_t kid;
717
718   /* --- Make the pipes --- */
719
720   if (pipe(rfd))
721     goto fail_0;
722   if (pipe(cfd))
723     goto fail_1;
724
725   /* --- Start up the child process --- */
726
727   if ((kid = fork()) < 0)
728     goto fail_2;
729   if (kid == 0) {
730     close(cfd[1]);
731     close(rfd[0]);
732
733     if (server) {
734       dup2(cfd[0], STDIN_FILENO);
735       dup2(rfd[1], STDOUT_FILENO);
736       close(cfd[0]);
737       close(rfd[1]);
738       execlp(server, server, (char *)0);
739       child(STDOUT_FILENO, STDIN_FILENO);
740     } else
741       child(rfd[1], cfd[0]);
742     _exit(1);
743   }
744
745   /* --- Fix up everything in the server block --- */
746
747   close(cfd[0]);
748   close(rfd[1]);
749   rs->fd = cfd[1];
750   sel_initfile(sel, &rs->f, rfd[0], SEL_READ, answer, rs);
751   rs->kid = kid;
752   return (0);
753
754   /* --- Fix up after errors --- */
755
756 fail_2:
757   close(cfd[0]);
758   close(cfd[1]);
759 fail_1:
760   close(rfd[0]);
761   close(rfd[1]);
762 fail_0:
763   return (-1);
764 }
765
766 /* --- @attach@ --- *
767  *
768  * Arguments:   @bres_client *rc@ = pointer to a client block
769  *
770  * Returns:     ---
771  *
772  * Use:         Attaches a client to a spare server (which is assumed to
773  *              exist).
774  */
775
776 static void attach(bres_client *rc)
777 {
778   bres_server *rs;
779   int lose = 0;
780
781   /* --- Fix up the server ready for the job --- *
782    *
783    * If the server has a process, remove its timer.  Otherwise, fork off a
784    * new resolver process.  This is also where I go if I find that the child
785    * resolver process has lost while I wasn't looking.  Only one attempt at
786    * forking is performed.
787    */
788
789 again:
790   rs = FREE->next;
791   if (rs->kid != -1)
792     sel_rmtimer(&rs->t);
793   else {
794     if (lose || start(rs))
795       goto lost;
796     lose = 1;
797   }
798
799   /* --- Submit the job to the resolver --- */
800
801   {
802     struct sigaction sa, osa;
803     int e;
804
805     /* --- Ignore @SIGPIPE@ for now --- *
806      *
807      * This way I can trap @EPIPE@ and reap a losing child, if there was one.
808      */
809
810     sa.sa_handler = SIG_IGN;
811     sa.sa_flags = 0;
812     sigemptyset(&sa.sa_mask);
813     sigaction(SIGPIPE, &sa, &osa);
814
815     /* --- Write the new job to the child --- */
816
817     e = 0;
818     if (dowrite(rs->fd, &rc->q, sizeof(rc->q)))
819       e = errno;
820     else switch (rc->q) {
821       case BRES_BYADDR:
822         if (dowrite(rs->fd, &rc->u.addr, sizeof(rc->u.addr)))
823           e = errno;
824         break;
825       case BRES_BYNAME:
826         if (putstring(rs->fd, rc->u.name))
827           e = errno;
828         break;
829     }
830     sigaction(SIGPIPE, &osa, 0);
831
832     /* --- Sort out various errors --- *
833      *
834      * This was once more complicated, handling @EPIPE@ separately from other
835      * errors.  Now everything's handled the same way.
836      */
837
838     if (e) {
839       zap(rs);
840       goto again;
841     }
842   }
843
844   /* --- Fiddle with lists so that everything's OK --- */
845
846   sel_addfile(&rs->f);
847   rs->next->prev = FREE;
848   FREE->next = rs->next;
849   rs->next = rs->prev = rs;
850   rs->rc = rc;
851   rc->rs = rs;
852   return;
853
854 lost:
855   rc->func(0, rc->p);
856   if (rc->q == BRES_BYNAME)
857     xfree(rc->u.name);
858 }
859
860 /* --- @resolve@ --- *
861  *
862  * Arguments:   @bres_client *rc@ = pointer to filled-in client block
863  *
864  * Returns:     ---
865  *
866  * Use:         Dispatcher for incoming resolution jobs.
867  */
868
869 static void resolve(bres_client *rc)
870 {
871   /* --- If there's a free server, plug it in --- */
872
873   rc->rs = 0;
874   if (FREE->next == FREE) {
875     rc->next = QUEUE;
876     rc->prev = QUEUE->prev;
877     QUEUE->prev->next = rc;
878     QUEUE->prev = rc;
879   } else
880     attach(rc);
881 }
882
883 /* --- @bres_byaddr@ --- *
884  *
885  * Arguments:   @bres_client *rc@ = pointer to client block
886  *              @struct in_addr addr@ = address to resolve
887  *              @void (*func)(struct hostent *h, void *p)@ = handler function
888  *              @void *p@ = argument for handler function
889  *
890  * Returns:     ---
891  *
892  * Use:         Adds an address lookup job to the queue.  The job will be
893  *              processed when there's a spare resolver process to deal with
894  *              it.
895  */
896
897 void bres_byaddr(bres_client *rc, struct in_addr addr,
898                  void (*func)(struct hostent */*h*/, void */*p*/),
899                  void *p)
900 {
901   rc->q = BRES_BYADDR;
902   rc->u.addr = addr;
903   rc->func = func;
904   rc->p = p;
905   resolve(rc);
906 }
907
908 /* --- @bres_byname@ --- *
909  *
910  * Arguments:   @bres_client *rc@ = pointer to client block
911  *              @const char *name@ = name to resolve
912  *              @void (*func)(struct hostent *h, void *p)@ = handler function
913  *              @void *p@ = argument for handler function
914  *
915  * Returns:     ---
916  *
917  * Use:         Adds a name lookup job to the queue.  The job will be
918  *              processed when there's a spare resolver process to deal with
919  *              it.
920  */
921
922 void bres_byname(bres_client *rc, const char *name,
923                  void (*func)(struct hostent */*h*/, void */*p*/),
924                  void *p)
925 {
926   rc->q = BRES_BYNAME;
927   rc->u.name = xstrdup(name);
928   rc->func = func;
929   rc->p = p;
930   resolve(rc);
931 }
932
933 /* --- @bres_exec@ --- *
934  *
935  * Arguments:   @const char *file@ = file containing server code or null
936  *
937  * Returns:     ---
938  *
939  * Use:         Makes `bres' use a standalone server rather than copies of
940  *              the current process.  This can reduce memory consumption for
941  *              large processes, at the expense of startup time (which
942  *              shouldn't be too bad anyway, because of the resolver design).
943  *              If the filename is null, a default set up at install time is
944  *              used.  It's probably a good idea to leave it alone.
945  */
946
947 void bres_exec(const char *file)
948 {
949   if (file)
950     server = file;
951   else
952     server = BRES_SERVER;
953 }
954
955 /* --- @bres_init@ --- *
956  *
957  * Arguments:   @sel_state *s@ = pointer to select multiplexor
958  *
959  * Returns:     ---
960  *
961  * Use:         Initializes the background resolver for use.
962  */
963
964 void bres_init(sel_state *s)
965 {
966   int i;
967
968   sel = s;
969   for (i = 0; i < BRES_MAX; i++) {
970     servers[i].next = FREE;
971     servers[i].prev = FREE->prev;
972     servers[i].kid = -1;
973     servers[i].rc = 0;
974     FREE->prev->next = &servers[i];
975     FREE->prev = &servers[i];
976   }
977 }
978
979 #endif
980
981 /*----- That's all, folks -------------------------------------------------*/