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