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