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