chiark / gitweb /
Various Debian fixes.
[mLib] / bres.c
CommitLineData
a759efa6 1/* -*-c-*-
2 *
5d6d2d94 3 * $Id: bres.c,v 1.6 2001/06/22 19:33:38 mdw Exp $
a759efa6 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 $
5d6d2d94 33 * Revision 1.6 2001/06/22 19:33:38 mdw
34 * Hack if there is no @_SC_OPEN_MAX@ or @OPEN_MAX@.
35 *
364308dc 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 *
d8fe3fc2 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 *
e2a18bd0 45 * Revision 1.3 2000/06/17 10:38:35 mdw
46 * Track changes to selbuf interface.
47 *
6c3a7cf7 48 * Revision 1.2 1999/10/30 11:28:39 mdw
49 * Fix include error, pointed out by Chris Rutter.
50 *
a759efa6 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
6c3a7cf7 74#include "alloc.h"
a759efa6 75#include "bres.h"
6c3a7cf7 76#include "report.h"
77#include "sel.h"
a759efa6 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
88static bres_server servers[BRES_MAX]; /* Statically allocated servers */
89
90#define FREE ((bres_server *)&freelist)
91static struct { bres_server *next, *prev; } freelist = { FREE, FREE };
92
93#define QUEUE ((bres_client *)&queue)
94static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE };
95
96static sel_state *sel;
97
98static 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
138typedef 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
158static 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
177static 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
205static 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
230static 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
250static 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++) {
a759efa6 318 *p++ = a;
364308dc 319 a += hsk.addrsz;
a759efa6 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);
d8fe3fc2 333 free(name);
a759efa6 334 h->h_aliases = p;
335 for (i = 0; i < hsk.nalias; i++) {
336 *p++ = a;
337 PUT(alias[i]);
d8fe3fc2 338 free(alias[i]);
a759efa6 339 }
340 *p++ = 0;
d8fe3fc2 341 free(alias);
a759efa6 342
343#undef PUT
344 }
345
346 return (h);
347
348 /* --- Tidy up after various types of failure --- */
349
350tidy_2:
351 {
352 int i;
353 for (i = 0; i < hsk.nalias && alias[i]; i++)
d8fe3fc2 354 free(alias[i]);
355 free(alias);
a759efa6 356 }
357tidy_1:
d8fe3fc2 358 free(name);
a759efa6 359tidy_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
375static 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
426static void child(int rfd, int cfd)
427{
428 /* --- Close other file descriptors --- */
429
430 {
431 int i;
5d6d2d94 432#if defined(_SC_OPEN_MAX)
a759efa6 433 int maxfd = sysconf(_SC_OPEN_MAX);
5d6d2d94 434#elif defined(OPEN_MAX)
435 int maxfd = OPEN_MAX;
436#else
437 int maxfd = -1;
438#endif
a759efa6 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
364308dc 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
a759efa6 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
d8fe3fc2 479 /* --- Reverse lookup --- */
a759efa6 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
541lose:
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
558int 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
591static 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
622void bres_abort(bres_client *rc)
623{
624 if (rc->q == BRES_BYNAME)
e2a18bd0 625 xfree(rc->u.name);
a759efa6 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
646static 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
663static void attach(bres_client */*rc*/);
664
665static 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)
e2a18bd0 692 xfree(rc->u.name);
a759efa6 693 }
694 if (h)
d8fe3fc2 695 free(h);
a759efa6 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
739static 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
782fail_2:
783 close(cfd[0]);
784 close(cfd[1]);
785fail_1:
786 close(rfd[0]);
787 close(rfd[1]);
788fail_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
802static 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
815again:
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
880lost:
881 rc->func(0, rc->p);
882 if (rc->q == BRES_BYNAME)
e2a18bd0 883 xfree(rc->u.name);
a759efa6 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
895static 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
923void 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
948void 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
973void 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
990void 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 -------------------------------------------------*/