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