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