chiark / gitweb /
Headers: Guard inclusion of mLib headers.
[mLib] / bres.c
1 /* -*-c-*-
2  *
3  * Background reverse name resolution
4  *
5  * (c) 1999 Straylight/Edgeware
6  */
7
8 /*----- Licensing notice --------------------------------------------------*
9  *
10  * This file is part of the mLib utilities library.
11  *
12  * mLib is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Library General Public License as
14  * published by the Free Software Foundation; either version 2 of the
15  * License, or (at your option) any later version.
16  *
17  * mLib is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License along with mLib; if not, write to the Free
24  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25  * MA 02111-1307, USA.
26  */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #include "config.h"
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 "mdup.h"
51 #include "report.h"
52 #include "sel.h"
53
54 /*----- Magic numbers -----------------------------------------------------*/
55
56 #define BRES_MAX 15                     /* Maximum number of resolvers */
57 #define BRES_IDLE 60                    /* Lifetime of an idle resolver */
58
59 /*----- Static variables --------------------------------------------------*/
60
61 #ifndef BRES_STANDALONE
62
63 static bres_server servers[BRES_MAX];   /* Statically allocated servers */
64
65 #define FREE ((bres_server *)&freelist)
66 static struct { bres_server *next, *prev; } freelist = { FREE, FREE };
67
68 #define QUEUE ((bres_client *)&queue)
69 static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE };
70
71 static sel_state *sel;
72
73 static const char *server = 0;
74
75 #endif
76
77 /*----- Background resolver protocol --------------------------------------*/
78
79 /* --- Requests and responses --- *
80  *
81  * There are two types of requests: name and addr, corresponding to the
82  * standard @gethostbyname@ and @gethostbyaddr@ calls.  There are two types
83  * of responses too: a positive response consists of an encoded equivalent of
84  * a @struct hostent@ structure containing the requested information; a
85  * negative response consists of an @h_errno@ value explaining the problem.
86  */
87
88 #define BRES_BYNAME 0                   /* Request: resolve given name */
89 #define BRES_BYADDR 1                   /* Request: resolve given address */
90
91 #define BRES_HOSTENT 0                  /* Response: resolved ok */
92 #define BRES_ERROR 1                    /* Response: resolution failed */
93
94 /* --- Encodings --- *
95  *
96  * A string is encoded as a @size_t@ length followed by the actual data.  The
97  * null terminator is not transmitted.
98  *
99  * Addresses for resolution are transmitted as raw @struct in_addr@
100  * structures.
101  *
102  * A @hostent@ structure is transmitted as a header containing fixed-size
103  * information, followed by the official name, an array of aliases, and an
104  * array of addresses.  The number of items in the arrays is specified in the
105  * header.
106  *
107  * The implementation assumes that a complete request or reply is always
108  * sent.  Undesirable blocking will occur if this is not the case.  Both ends
109  * are assumed to trust each other.  A protocol failure results in the child
110  * in question being terminated.
111  */
112
113 typedef struct hostskel {
114   size_t nalias;
115   int addrtype;
116   size_t addrsz;
117   size_t naddr;
118 } hostskel;
119
120 /* --- @doread@, @dowrite@ --- *
121  *
122  * Arguments:   @int fd@ = file descriptor
123  *              @void *buf@ = buffer for data
124  *              @size_t sz@ = size of data
125  *
126  * Returns:     Zero if successful, nonzero otherwise.
127  *
128  * Use:         Reads or writes a chunk of data.  @EINTR@ errors are retried;
129  *              incomplete reads and writes are continued from where they
130  *              left off.  End-of-file is considered an I/O error.
131  */
132
133 static int doread(int fd, void *buf, size_t sz)
134 {
135   char *p = buf;
136   while (sz) {
137     int r = read(fd, p, sz);
138     if (r < 0) {
139       if (errno == EINTR)
140         continue;
141       return (-1);
142     } else if (r == 0) {
143       errno = EIO;
144       return (-1);
145     }
146     sz -= r;
147     p += r;
148   }
149   return (0);
150 }
151
152 static int dowrite(int fd, const void *buf, size_t sz)
153 {
154   const char *p = buf;
155   while (sz) {
156     int r = write(fd, p, sz);
157     if (r < 0) {
158       if (errno == EINTR)
159         continue;
160       return (-1);
161     } else if (r == 0) {
162       errno = EIO;
163       return (-1);
164     }
165     sz -= r;
166     p += r;
167   }
168   return (0);
169 }
170
171 /* --- @getstring@ --- *
172  *
173  * Arguments:   @int fd@ = file descriptor to read
174  *
175  * Returns:     String in heap-allocated block, or a null pointer.
176  *
177  * Use:         Decodes a string.
178  */
179
180 static char *getstring(int fd)
181 {
182   size_t sz;
183   char *p;
184
185   if (doread(fd, &sz, sizeof(sz)) || (p = malloc(sz + 1)) == 0)
186     return (0);
187   if (doread(fd, p, sz)) {
188     free(p);
189     return (0);
190   }
191   p[sz] = 0;
192   return (p);
193 }
194
195 /* --- @putstring@ --- *
196  *
197  * Arguments:   @int fd@ = file descriptor to write on
198  *              @const char *p@ = pointer to string to write
199  *
200  * Returns:     Zero if successful.
201  *
202  * Use:         Encodes a string.
203  */
204
205 static int putstring(int fd, const char *p)
206 {
207   size_t sz = strlen(p);
208   if (dowrite(fd, &sz, sizeof(sz)) || dowrite(fd, p, sz))
209     return (-1);
210   return (0);
211 }
212
213 /* --- @gethost@ --- *
214  *
215  * Arguments:   @int fd@ = file descriptor to read
216  *
217  * Returns:     Pointer to heap-allocated @struct hostent@, or null.
218  *
219  * Use:         Decodes a host structure.  The resulting structure is all in
220  *              one big heap block.
221  */
222
223 #ifndef BRES_STANDALONE
224
225 static struct hostent *gethost(int fd)
226 {
227   hostskel hsk;
228   struct hostent *h;
229   char *name;
230   char **alias = 0;
231
232   /* --- Read the skeleton structure --- */
233
234   if (doread(fd, &hsk, sizeof(hsk)))
235     goto tidy_0;
236
237   /* --- Read the hostname and alias strings --- *
238    *
239    * Count the length of the strings as we go.
240    */
241
242   {
243     size_t sz =
244       sizeof(struct hostent) +
245       hsk.naddr * hsk.addrsz +
246       (hsk.naddr + hsk.nalias + 2) * sizeof(char *);
247
248     /* --- Read the primary host name --- */
249
250     if ((name = getstring(fd)) == 0)
251       goto tidy_0;
252     sz += strlen(name) + 1;
253
254     /* --- Read in the alias names --- */
255
256     if (hsk.nalias) {
257       int i;
258       if ((alias = malloc(hsk.nalias * sizeof(char *))) == 0)
259         goto tidy_1;
260       for (i = 0; i < hsk.nalias; i++)
261         alias[i] = 0;
262       for (i = 0; i < hsk.nalias; i++) {
263         if ((alias[i] = getstring(fd)) == 0)
264           goto tidy_2;
265         sz += strlen(alias[i]) + 1;
266       }
267     }
268
269     /* --- Allocate the output structure --- */
270
271     if ((h = malloc(sz)) == 0)
272       goto tidy_2;
273   }
274
275   /* --- Fill in the base structure --- */
276
277   h->h_addrtype = hsk.addrtype;
278   h->h_length = hsk.addrsz;
279
280   /* --- Start putting everything else in --- */
281
282   {
283     char **p = (char **)(h + 1);
284     char *a = (char *)(p + hsk.nalias + hsk.naddr + 2);
285     int i;
286
287     /* --- Start with the address table --- */
288
289     h->h_addr_list = p;
290     if (doread(fd, a, hsk.naddr * hsk.addrsz))
291       goto tidy_2;
292     for (i = 0; i < hsk.naddr; i++) {
293       *p++ = a;
294       a += hsk.addrsz;
295     }
296     *p++ = 0;
297
298     /* --- Finally copy the strings over --- */
299
300 #define PUT(_p) do {                                                    \
301   size_t _len = strlen(_p) + 1;                                         \
302   memcpy(a, (_p), _len);                                                \
303   a += _len;                                                            \
304 } while (0)
305
306     h->h_name = a;
307     PUT(name);
308     free(name);
309     h->h_aliases = p;
310     for (i = 0; i < hsk.nalias; i++) {
311       *p++ = a;
312       PUT(alias[i]);
313       free(alias[i]);
314     }
315     *p++ = 0;
316     free(alias);
317
318 #undef PUT
319   }
320
321   return (h);
322
323   /* --- Tidy up after various types of failure --- */
324
325 tidy_2:
326   {
327     int i;
328     for (i = 0; i < hsk.nalias && alias[i]; i++)
329       free(alias[i]);
330     free(alias);
331   }
332 tidy_1:
333   free(name);
334 tidy_0:
335   return (0);
336 }
337
338 #endif
339
340 /* --- @puthost@ --- *
341  *
342  * Arguments:   @int fd@ = file descriptor
343  *              @struct hostent *h@ = pointer to host structure
344  *
345  * Returns:     Zero if successful.
346  *
347  * Use:         Encodes a host structure.
348  */
349
350 static int puthost(int fd, struct hostent *h)
351 {
352   hostskel hsk;
353   int i;
354
355   /* --- Fill in and send the skeleton structure --- */
356
357   for (i = 0; h->h_aliases[i]; i++)
358     ;
359   hsk.nalias = i;
360   for (i = 0; h->h_addr_list[i]; i++)
361     ;
362   hsk.naddr = i;
363   hsk.addrtype = h->h_addrtype;
364   hsk.addrsz = h->h_length;
365   if (dowrite(fd, &hsk, sizeof(hsk)))
366     return (-1);
367
368   /* --- Send the name and alias strings --- */
369
370   if (putstring(fd, h->h_name))
371     return (-1);
372   for (i = 0; h->h_aliases[i]; i++) {
373     if (putstring(fd, h->h_aliases[i]))
374       return (-1);
375   }
376
377   /* --- Send the address data --- */
378
379   for (i = 0; h->h_addr_list[i]; i++) {
380     if (dowrite(fd, h->h_addr_list[i], hsk.addrsz))
381       return (-1);
382   }
383
384   /* --- OK, done --- */
385
386   return (0);
387 }
388
389 /*----- Resolver server ---------------------------------------------------*/
390
391 /* --- @child@ --- *
392  *
393  * Arguments:   @int rfd@ = output file descriptor for resolved hostnames
394  *              @int cfd@ = input file descriptor for raw addresses
395  *
396  * Returns:     Never.
397  *
398  * Use:         Asynchronous name resolving process.
399  */
400
401 static void child(int rfd, int cfd)
402 {
403   /* --- Close other file descriptors --- */
404
405   {
406     int i;
407 #if defined(_SC_OPEN_MAX)
408     int maxfd = sysconf(_SC_OPEN_MAX);
409 #elif defined(OPEN_MAX)
410     int maxfd = OPEN_MAX;
411 #else
412     int maxfd = -1;
413 #endif
414
415     if (maxfd < 0)
416       maxfd = 256; /* Fingers crossed... */
417     for (i = 0; i < maxfd; i++) {
418       if (i != rfd && i != cfd && i != 1)
419         close(i);
420     }
421   }
422
423   signal(SIGTERM, SIG_DFL);
424   signal(SIGHUP, SIG_DFL);
425   signal(SIGQUIT, SIG_DFL);
426   signal(SIGALRM, SIG_DFL);
427   signal(SIGINT, SIG_DFL);
428
429   /* --- Main request/response loop --- */
430
431   for (;;) {
432     int req, resp;
433     struct hostent *h;
434
435     /* --- Read the request --- */
436
437     if (doread(cfd, &req, sizeof(req)))
438       goto lose;
439
440     /* --- Process it into a host structure --- */
441
442     switch (req) {
443
444       /* --- Normal forward lookup --- */
445
446       case BRES_BYNAME: {
447         char *name = getstring(cfd);
448         if (!name)
449           goto lose;
450         h = gethostbyname(name);
451         free(name);
452       } break;
453
454       /* --- Reverse lookup --- */
455
456       case BRES_BYADDR: {
457         struct in_addr addr;
458         char *p;
459         if (doread(cfd, &addr, sizeof(addr)))
460           goto lose;
461         if ((h = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET)) == 0)
462           goto fail;
463
464         /* --- Do a forward lookup to confirm --- */
465
466         {
467           size_t sz = strlen(h->h_name) + 1;
468           if ((p = malloc(sz)) == 0)
469             goto fail;
470           memcpy(p, h->h_name, sz);
471         }
472
473         h = gethostbyname(p);
474         free(p);
475         if (!h)
476           goto fail;
477         p = 0;
478         if (h) {
479           char **pp;
480           for (pp = h->h_addr_list; *pp; pp++) {
481             struct in_addr a;
482             memcpy(&a, *pp, sizeof(a));
483             if (a.s_addr == addr.s_addr) {
484               p = h->h_name;
485               break;
486             }
487           }
488         }
489         if (!p) {
490           h = 0;
491           h_errno = NO_RECOVERY;
492         }
493       fail:;
494       } break;
495
496       /* --- Unknown request -- may have lost sync --- */
497
498       default:
499         goto lose;
500     }
501
502     /* --- Transmit the response --- */
503
504     if (h) {
505       resp = BRES_HOSTENT;
506       if (dowrite(rfd, &resp, sizeof(resp)) || puthost(rfd, h))
507         goto lose;
508     } else {
509       resp = BRES_ERROR;
510       if (dowrite(rfd, &resp, sizeof(resp)) ||
511           dowrite(rfd, &h_errno, sizeof(h_errno)))
512         goto lose;
513     }
514   }
515
516 lose:
517   _exit(1);
518 }
519
520 /* --- @main@ --- *
521  *
522  * Arguments:   @int argc@ = number of command line arguments
523  *              @char *argv[]@ = array of arguments
524  *
525  * Returns:     Runs until killed or an error occurs.
526  *
527  * Use:         A name resolver server process for mLib programs which need
528  *              this sort of thing.
529  */
530
531 #ifdef BRES_STANDALONE
532
533 int main(int argc, char *argv[])
534 {
535   if (isatty(STDIN_FILENO)) {
536     char *p = strrchr(argv[0], '/');
537     if (p)
538       p++;
539     else
540       p = argv[0];
541     fprintf(stderr,
542             "%s: don't run this program unless you know what you're doing.\n",
543             p);
544     exit(1);
545   }
546   child(STDOUT_FILENO, STDIN_FILENO);
547   return (1);
548 }
549
550 #endif
551
552 /*----- Main code ---------------------------------------------------------*/
553
554 #ifndef BRES_STANDALONE
555
556 /* --- @zap@ --- *
557  *
558  * Arguments:   @bres_server *rs@ = pointer to server block
559  *
560  * Returns:     ---
561  *
562  * Use:         Kills a server process, reaps the losing child and makes
563  *              things generally clean again.
564  */
565
566 static void zap(bres_server *rs)
567 {
568   /* --- Close the pipes, kill the child, and reap it --- */
569
570   if (rs->kid != -1) {
571     close(rs->fd);
572     close(rs->f.fd);
573     kill(rs->kid, SIGTERM);
574     waitpid(rs->kid, 0, 0);
575     rs->kid = -1;
576   }
577
578   /* --- Move the server to the back of the list --- */
579
580   rs->next->prev = rs->prev;
581   rs->prev->next = rs->next;
582   rs->next = FREE;
583   rs->prev = FREE->prev;
584   FREE->prev->next = rs;
585   FREE->prev = rs;
586 }
587
588 /* --- @bres_abort@ --- *
589  *
590  * Arguments:   @bres_client *rc@ = pointer to client block
591  *
592  * Returns:     ---
593  *
594  * Use:         Removes a queued job.
595  */
596
597 void bres_abort(bres_client *rc)
598 {
599   if (rc->q == BRES_BYNAME)
600     xfree(rc->u.name);
601   if (rc->rs) {
602     sel_rmfile(&rc->rs->f);
603     zap(rc->rs);
604     rc->rs = 0;
605   } else {
606     rc->next->prev = rc->prev;
607     rc->prev->next = rc->next;
608   }
609 }
610
611 /* --- @idle@ --- *
612  *
613  * Arguments:   @struct timeval *tv@ = pointer to the current time
614  *              @void *vp@ = pointer to a server block
615  *
616  * Returns:     ---
617  *
618  * Use:         Kills off a child which has been idle for too long.
619  */
620
621 static void idle(struct timeval *tv, void *vp)
622 {
623   bres_server *rs = vp;
624   zap(rs);
625 }
626
627 /* --- @answer@ --- *
628  *
629  * Arguments:   @int fd@ = file descriptor which is ready
630  *              @unsigned mode@ = what it's doing now
631  *              @void *vp@ = pointer to server block
632  *
633  * Returns:     ---
634  *
635  * Use:         Retrieves an answer from a name resolver process.
636  */
637
638 static void attach(bres_client */*rc*/);
639
640 static void answer(int fd, unsigned mode, void *vp)
641 {
642   bres_server *rs = vp;
643   bres_client *rc = rs->rc;
644   struct hostent *h = 0;
645   int resp;
646   int fail = 1;
647
648   /* --- Report the result to my client --- */
649
650   sel_rmfile(&rs->f);
651   h_errno = -1;
652   if (doread(fd, &resp, sizeof(resp)) == 0) {
653     switch (resp) {
654       case BRES_ERROR:
655         doread(fd, &h_errno, sizeof(h_errno));
656         fail = 0;
657         break;
658       case BRES_HOSTENT:
659         h = gethost(fd);
660         fail = 0;
661         break;
662     }
663   }
664   if (rc) {
665     rc->func(h, rc->p);
666     if (rc->q == BRES_BYNAME)
667       xfree(rc->u.name);
668   }
669   if (h)
670     free(h);
671   if (fail)
672     zap(rs);
673   if (!rc)
674     return;
675
676   /* --- Wrap up the various structures --- */
677
678   rs->rc = 0;
679   rc->rs = 0;
680   rs->next = FREE->next;
681   rs->prev = FREE;
682   FREE->next->prev = rs;
683   FREE->next = rs;
684
685   /* --- Tie a timer onto the server block --- */
686
687   {
688     struct timeval tv;
689
690     gettimeofday(&tv, 0);
691     tv.tv_sec += BRES_IDLE;
692     sel_addtimer(sel, &rs->t, &tv, idle, rs);
693   }
694
695   /* --- If there are any clients waiting, attach one --- */
696
697   if (QUEUE->next != QUEUE) {
698     rc = QUEUE->next;
699     QUEUE->next = rc->next;
700     rc->next->prev = QUEUE;
701     attach(rc);
702   }
703 }
704
705 /* --- @start@ --- *
706  *
707  * Arguments:   @bres_server *rs@ = pointer to a server block
708  *
709  * Returns:     Zero if OK, nonzero if something failed.
710  *
711  * Use:         Starts up a child resolver process.
712  */
713
714 static int start(bres_server *rs)
715 {
716   int rfd[2], cfd[2];
717   pid_t kid;
718   mdup_fd md[2];
719
720   /* --- Make the pipes --- */
721
722   if (pipe(rfd))
723     goto fail_0;
724   if (pipe(cfd))
725     goto fail_1;
726
727   /* --- Start up the child process --- */
728
729   if ((kid = fork()) < 0)
730     goto fail_2;
731   if (kid == 0) {
732     close(cfd[1]);
733     close(rfd[0]);
734
735     if (server) {
736       md[0].cur = cfd[0]; md[0].want = STDIN_FILENO;
737       md[1].cur = rfd[1]; md[1].want = STDOUT_FILENO;
738       if (mdup(md, 2) ||  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 -------------------------------------------------*/