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