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