chiark / gitweb /
Basic arena management.
[mLib] / bres.c
1 /* -*-c-*-
2  *
3  * $Id: bres.c,v 1.2 1999/10/30 11:28:39 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 /*----- Revision history --------------------------------------------------* 
31  *
32  * $Log: bres.c,v $
33  * Revision 1.2  1999/10/30 11:28:39  mdw
34  * Fix include error, pointed out by Chris Rutter.
35  *
36  * Revision 1.1  1999/10/04 21:40:42  mdw
37  * Added background resolver from `fw'.
38  *
39  */
40
41 /*----- Header files ------------------------------------------------------*/
42
43 #include <errno.h>
44 #include <signal.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48
49 #include <sys/types.h>
50 #include <sys/time.h>
51 #include <unistd.h>
52 #include <sys/wait.h>
53
54 #include <sys/socket.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
57 #include <netdb.h>
58
59 #include "alloc.h"
60 #include "bres.h"
61 #include "report.h"
62 #include "sel.h"
63
64 /*----- Magic numbers -----------------------------------------------------*/
65
66 #define BRES_MAX 15                     /* Maximum number of resolvers */
67 #define BRES_IDLE 60                    /* Lifetime of an idle resolver */
68
69 /*----- Static variables --------------------------------------------------*/
70
71 #ifndef BRES_STANDALONE
72
73 static bres_server servers[BRES_MAX];   /* Statically allocated servers */
74
75 #define FREE ((bres_server *)&freelist)
76 static struct { bres_server *next, *prev; } freelist = { FREE, FREE };
77
78 #define QUEUE ((bres_client *)&queue)
79 static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE };
80
81 static sel_state *sel;
82
83 static const char *server = 0;
84
85 #endif
86
87 /*----- Background resolver protocol --------------------------------------*/
88
89 /* --- Requests and responses --- *
90  *
91  * There are two types of requests: name and addr, corresponding to the
92  * standard @gethostbyname@ and @gethostbyaddr@ calls.  There are two types
93  * of responses too: a positive response consists of an encoded equivalent of
94  * a @struct hostent@ structure containing the requested information; a
95  * negative response consists of an @h_errno@ value explaining the problem.
96  */
97
98 #define BRES_BYNAME 0                   /* Request: resolve given name */
99 #define BRES_BYADDR 1                   /* Request: resolve given address */
100
101 #define BRES_HOSTENT 0                  /* Response: resolved ok */
102 #define BRES_ERROR 1                    /* Response: resolution failed */
103
104 /* --- Encodings --- *
105  *
106  * A string is encoded as a @size_t@ length followed by the actual data.  The
107  * null terminator is not transmitted.
108  *
109  * Addresses for resolution are transmitted as raw @struct in_addr@
110  * structures.
111  *
112  * A @hostent@ structure is transmitted as a header containing fixed-size
113  * information, followed by the official name, an array of aliases, and an
114  * array of addresses.  The number of items in the arrays is specified in the
115  * header.
116  *
117  * The implementation assumes that a complete request or reply is always
118  * sent.  Undesirable blocking will occur if this is not the case.  Both ends
119  * are assumed to trust each other.  A protocol failure results in the child
120  * in question being terminated.
121  */
122
123 typedef struct hostskel {
124   size_t nalias;
125   int addrtype;
126   size_t addrsz;
127   size_t naddr;
128 } hostskel;
129
130 /* --- @doread@, @dowrite@ --- *
131  *
132  * Arguments:   @int fd@ = file descriptor
133  *              @void *buf@ = buffer for data
134  *              @size_t sz@ = size of data
135  *
136  * Returns:     Zero if successful, nonzero otherwise.
137  *
138  * Use:         Reads or writes a chunk of data.  @EINTR@ errors are retried;
139  *              incomplete reads and writes are continued from where they
140  *              left off.  End-of-file is considered an I/O error.
141  */
142
143 static int doread(int fd, void *buf, size_t sz)
144 {
145   char *p = buf;
146   while (sz) {
147     int r = read(fd, p, sz);
148     if (r < 0) {
149       if (errno == EINTR)
150         continue;
151       return (-1);
152     } else if (r == 0) {
153       errno = EIO;
154       return (-1);
155     }
156     sz -= r;
157     p += r;
158   }
159   return (0);
160 }
161
162 static int dowrite(int fd, const void *buf, size_t sz)
163 {
164   const char *p = buf;
165   while (sz) {
166     int r = write(fd, p, sz);
167     if (r < 0) {
168       if (errno == EINTR)
169         continue;
170       return (-1);
171     } else if (r == 0) {
172       errno = EIO;
173       return (-1);
174     }
175     sz -= r;
176     p += r;
177   }
178   return (0);
179 }
180
181 /* --- @getstring@ --- *
182  *
183  * Arguments:   @int fd@ = file descriptor to read
184  *
185  * Returns:     String in heap-allocated block, or a null pointer.
186  *
187  * Use:         Decodes a string.
188  */
189
190 static char *getstring(int fd)
191 {
192   size_t sz;
193   char *p;
194
195   if (doread(fd, &sz, sizeof(sz)) || (p = malloc(sz + 1)) == 0)
196     return (0);
197   if (doread(fd, p, sz)) {
198     free(p);
199     return (0);
200   }
201   p[sz] = 0;
202   return (p);
203 }
204
205 /* --- @putstring@ --- *
206  *
207  * Arguments:   @int fd@ = file descriptor to write on
208  *              @const char *p@ = pointer to string to write
209  *
210  * Returns:     Zero if successful.
211  *
212  * Use:         Encodes a string.
213  */
214
215 static int putstring(int fd, const char *p)
216 {
217   size_t sz = strlen(p);
218   if (dowrite(fd, &sz, sizeof(sz)) || dowrite(fd, p, sz))
219     return (-1);
220   return (0);
221 }
222
223 /* --- @gethost@ --- *
224  *
225  * Arguments:   @int fd@ = file descriptor to read
226  *
227  * Returns:     Pointer to heap-allocated @struct hostent@, or null.
228  *
229  * Use:         Decodes a host structure.  The resulting structure is all in
230  *              one big heap block.
231  */
232
233 #ifndef BRES_STANDALONE
234
235 static struct hostent *gethost(int fd)
236 {
237   hostskel hsk;
238   struct hostent *h;
239   char *name;
240   char **alias = 0;
241   
242   /* --- Read the skeleton structure --- */
243
244   if (doread(fd, &hsk, sizeof(hsk)))
245     goto tidy_0;
246
247   /* --- Read the hostname and alias strings --- *
248    *
249    * Count the length of the strings as we go.
250    */
251
252   {
253     size_t sz =
254       sizeof(struct hostent) +
255       hsk.naddr * hsk.addrsz +
256       (hsk.naddr + hsk.nalias + 2) * sizeof(char *);
257
258     /* --- Read the primary host name --- */
259
260     if ((name = getstring(fd)) == 0)
261       goto tidy_0;
262     sz += strlen(name) + 1;
263
264     /* --- Read in the alias names --- */
265
266     if (hsk.nalias) {
267       int i;
268       if ((alias = malloc(hsk.nalias * sizeof(char *))) == 0)
269         goto tidy_1;      
270       for (i = 0; i < hsk.nalias; i++)
271         alias[i] = 0;
272       for (i = 0; i < hsk.nalias; i++) {
273         if ((alias[i] = getstring(fd)) == 0)
274           goto tidy_2;
275         sz += strlen(alias[i]) + 1;
276       }
277     }
278
279     /* --- Allocate the output structure --- */
280
281     if ((h = malloc(sz)) == 0)
282       goto tidy_2;
283   }
284
285   /* --- Fill in the base structure --- */
286
287   h->h_addrtype = hsk.addrtype;
288   h->h_length = hsk.addrsz;
289
290   /* --- Start putting everything else in --- */
291
292   {
293     char **p = (char **)(h + 1);
294     char *a = (char *)(p + hsk.nalias + hsk.naddr + 2);
295     int i;
296
297     /* --- Start with the address table --- */
298
299     h->h_addr_list = p;
300     if (doread(fd, a, hsk.naddr * hsk.addrsz))
301       goto tidy_2;
302     for (i = 0; i < hsk.naddr; i++) {
303       struct in_addr in;
304       *p++ = a;
305       memcpy(&in, a, sizeof(in));
306     }
307     *p++ = 0;
308
309     /* --- Finally copy the strings over --- */
310
311 #define PUT(_p) do {                                                    \
312   size_t _len = strlen(_p) + 1;                                         \
313   memcpy(a, (_p), _len);                                                \
314   a += _len;                                                            \
315 } while (0)
316
317     h->h_name = a;
318     PUT(name);
319     free(name);
320     h->h_aliases = p;
321     for (i = 0; i < hsk.nalias; i++) {
322       *p++ = a;
323       PUT(alias[i]);
324       free(alias[i]);
325     }
326     *p++ = 0;
327     free(alias);
328  
329 #undef PUT
330   }
331
332   return (h);
333
334   /* --- Tidy up after various types of failure --- */
335
336 tidy_2:
337   {
338     int i;
339     for (i = 0; i < hsk.nalias && alias[i]; i++)
340       free(alias[i]);
341     free(alias);
342   }
343 tidy_1:
344   free(name);
345 tidy_0:
346   return (0);
347 }
348
349 #endif
350
351 /* --- @puthost@ --- *
352  *
353  * Arguments:   @int fd@ = file descriptor
354  *              @struct hostent *h@ = pointer to host structure
355  *
356  * Returns:     Zero if successful.
357  *
358  * Use:         Encodes a host structure.
359  */
360
361 static int puthost(int fd, struct hostent *h)
362 {
363   hostskel hsk;
364   int i;
365
366   /* --- Fill in and send the skeleton structure --- */
367
368   for (i = 0; h->h_aliases[i]; i++)
369     ;
370   hsk.nalias = i;
371   for (i = 0; h->h_addr_list[i]; i++)
372     ;
373   hsk.naddr = i;
374   hsk.addrtype = h->h_addrtype;
375   hsk.addrsz = h->h_length;
376   if (dowrite(fd, &hsk, sizeof(hsk)))
377     return (-1);
378
379   /* --- Send the name and alias strings --- */
380
381   if (putstring(fd, h->h_name))
382     return (-1);
383   for (i = 0; h->h_aliases[i]; i++) {
384     if (putstring(fd, h->h_aliases[i]))
385       return (-1);
386   }
387
388   /* --- Send the address data --- */
389
390   for (i = 0; h->h_addr_list[i]; i++) {
391     if (dowrite(fd, h->h_addr_list[i], hsk.addrsz))
392       return (-1);
393   }
394
395   /* --- OK, done --- */
396
397   return (0);
398 }
399
400 /*----- Resolver server ---------------------------------------------------*/
401
402 /* --- @child@ --- *
403  *
404  * Arguments:   @int rfd@ = output file descriptor for resolved hostnames
405  *              @int cfd@ = input file descriptor for raw addresses
406  *
407  * Returns:     Never.
408  *
409  * Use:         Asynchronous name resolving process.
410  */
411
412 static void child(int rfd, int cfd)
413 {
414   /* --- Close other file descriptors --- */
415
416   {
417     int i;
418     int maxfd = sysconf(_SC_OPEN_MAX);
419
420     if (maxfd < 0)
421       maxfd = 256; /* Fingers crossed... */
422     for (i = 0; i < maxfd; i++) {
423       if (i != rfd && i != cfd && i != 1)
424         close(i);
425     }
426   }
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       /* --- Forward 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     free(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       free(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     free(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 -------------------------------------------------*/