sel/bres.c: Don't capture `h->h_name' in non-`const' pointer.
[mLib] / sel / bres.c
CommitLineData
a759efa6 1/* -*-c-*-
2 *
a759efa6 3 * Background reverse name resolution
4 *
5 * (c) 1999 Straylight/Edgeware
6 */
7
d4efbcd9 8/*----- Licensing notice --------------------------------------------------*
a759efa6 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.
d4efbcd9 16 *
a759efa6 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.
d4efbcd9 21 *
a759efa6 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
a759efa6 28/*----- Header files ------------------------------------------------------*/
29
a0150d8d
MW
30#include "config.h"
31
a759efa6 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
6c3a7cf7 48#include "alloc.h"
a759efa6 49#include "bres.h"
ff1e93ac 50#include "mdup.h"
6c3a7cf7 51#include "report.h"
52#include "sel.h"
a759efa6 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
63static bres_server servers[BRES_MAX]; /* Statically allocated servers */
64
ffb41122
MW
65static bres_server *freelist, *freetail;
66static bres_client *qhead, *qtail;
67static sel_state *sel;
68static const char *server = 0;
a759efa6 69
ffb41122
MW
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)
a759efa6 74
ffb41122
MW
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)
a759efa6 81
ffb41122
MW
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)
a759efa6 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
127typedef 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
147static 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
166static 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
194static 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
219static 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
239static struct hostent *gethost(int fd)
240{
241 hostskel hsk;
242 struct hostent *h;
243 char *name;
244 char **alias = 0;
d4efbcd9 245
a759efa6 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)
d4efbcd9 273 goto tidy_1;
a759efa6 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++) {
a759efa6 307 *p++ = a;
364308dc 308 a += hsk.addrsz;
a759efa6 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);
d8fe3fc2 322 free(name);
a759efa6 323 h->h_aliases = p;
324 for (i = 0; i < hsk.nalias; i++) {
325 *p++ = a;
326 PUT(alias[i]);
d8fe3fc2 327 free(alias[i]);
a759efa6 328 }
329 *p++ = 0;
d8fe3fc2 330 free(alias);
d4efbcd9 331
a759efa6 332#undef PUT
333 }
334
335 return (h);
336
337 /* --- Tidy up after various types of failure --- */
338
339tidy_2:
340 {
341 int i;
342 for (i = 0; i < hsk.nalias && alias[i]; i++)
d8fe3fc2 343 free(alias[i]);
344 free(alias);
a759efa6 345 }
346tidy_1:
d8fe3fc2 347 free(name);
a759efa6 348tidy_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
364static 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
415static void child(int rfd, int cfd)
416{
417 /* --- Close other file descriptors --- */
418
419 {
420 int i;
5d6d2d94 421#if defined(_SC_OPEN_MAX)
a759efa6 422 int maxfd = sysconf(_SC_OPEN_MAX);
5d6d2d94 423#elif defined(OPEN_MAX)
424 int maxfd = OPEN_MAX;
425#else
426 int maxfd = -1;
427#endif
a759efa6 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
364308dc 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
a759efa6 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
d8fe3fc2 468 /* --- Reverse lookup --- */
a759efa6 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)
47aacebf 476 goto skip;
a759efa6 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)
47aacebf 483 goto skip;
a759efa6 484 memcpy(p, h->h_name, sz);
485 }
486
487 h = gethostbyname(p);
488 free(p);
489 if (!h)
47aacebf 490 goto skip;
a759efa6 491 if (h) {
492 char **pp;
493 for (pp = h->h_addr_list; *pp; pp++) {
494 struct in_addr a;
495 memcpy(&a, *pp, sizeof(a));
47aacebf
MW
496 if (a.s_addr == addr.s_addr)
497 goto skip;
a759efa6 498 }
499 }
47aacebf
MW
500 h = 0;
501 h_errno = NO_RECOVERY;
502 skip:;
a759efa6 503 } break;
504
505 /* --- Unknown request -- may have lost sync --- */
506
507 default:
508 goto lose;
509 }
510
511 /* --- Transmit the response --- */
512
513 if (h) {
514 resp = BRES_HOSTENT;
515 if (dowrite(rfd, &resp, sizeof(resp)) || puthost(rfd, h))
516 goto lose;
517 } else {
518 resp = BRES_ERROR;
519 if (dowrite(rfd, &resp, sizeof(resp)) ||
520 dowrite(rfd, &h_errno, sizeof(h_errno)))
521 goto lose;
522 }
523 }
524
525lose:
526 _exit(1);
527}
528
529/* --- @main@ --- *
530 *
531 * Arguments: @int argc@ = number of command line arguments
532 * @char *argv[]@ = array of arguments
533 *
534 * Returns: Runs until killed or an error occurs.
535 *
536 * Use: A name resolver server process for mLib programs which need
537 * this sort of thing.
538 */
539
540#ifdef BRES_STANDALONE
541
542int main(int argc, char *argv[])
543{
544 if (isatty(STDIN_FILENO)) {
545 char *p = strrchr(argv[0], '/');
546 if (p)
547 p++;
548 else
549 p = argv[0];
550 fprintf(stderr,
551 "%s: don't run this program unless you know what you're doing.\n",
552 p);
553 exit(1);
554 }
555 child(STDOUT_FILENO, STDIN_FILENO);
556 return (1);
557}
558
559#endif
560
561/*----- Main code ---------------------------------------------------------*/
562
563#ifndef BRES_STANDALONE
564
565/* --- @zap@ --- *
566 *
567 * Arguments: @bres_server *rs@ = pointer to server block
568 *
569 * Returns: ---
570 *
571 * Use: Kills a server process, reaps the losing child and makes
572 * things generally clean again.
573 */
574
575static void zap(bres_server *rs)
576{
577 /* --- Close the pipes, kill the child, and reap it --- */
578
579 if (rs->kid != -1) {
580 close(rs->fd);
581 close(rs->f.fd);
582 kill(rs->kid, SIGTERM);
583 waitpid(rs->kid, 0, 0);
584 rs->kid = -1;
585 }
586
587 /* --- Move the server to the back of the list --- */
588
a736dc9c 589 if (!rs->rc) UNLINK(freelist, freetail, rs);
ffb41122 590 LINKTAIL(freelist, freetail, rs);
a759efa6 591}
592
593/* --- @bres_abort@ --- *
594 *
595 * Arguments: @bres_client *rc@ = pointer to client block
596 *
597 * Returns: ---
598 *
599 * Use: Removes a queued job.
600 */
601
602void bres_abort(bres_client *rc)
603{
604 if (rc->q == BRES_BYNAME)
e2a18bd0 605 xfree(rc->u.name);
ffb41122
MW
606 if (!rc->rs)
607 UNLINK(qhead, qtail, rc);
608 else {
a759efa6 609 sel_rmfile(&rc->rs->f);
610 zap(rc->rs);
611 rc->rs = 0;
a759efa6 612 }
613}
614
615/* --- @idle@ --- *
616 *
617 * Arguments: @struct timeval *tv@ = pointer to the current time
618 * @void *vp@ = pointer to a server block
619 *
620 * Returns: ---
621 *
622 * Use: Kills off a child which has been idle for too long.
623 */
624
625static void idle(struct timeval *tv, void *vp)
626{
627 bres_server *rs = vp;
628 zap(rs);
629}
630
631/* --- @answer@ --- *
632 *
633 * Arguments: @int fd@ = file descriptor which is ready
634 * @unsigned mode@ = what it's doing now
635 * @void *vp@ = pointer to server block
636 *
637 * Returns: ---
638 *
639 * Use: Retrieves an answer from a name resolver process.
640 */
641
642static void attach(bres_client */*rc*/);
643
644static void answer(int fd, unsigned mode, void *vp)
645{
646 bres_server *rs = vp;
647 bres_client *rc = rs->rc;
648 struct hostent *h = 0;
649 int resp;
650 int fail = 1;
651
652 /* --- Report the result to my client --- */
653
654 sel_rmfile(&rs->f);
655 h_errno = -1;
656 if (doread(fd, &resp, sizeof(resp)) == 0) {
657 switch (resp) {
658 case BRES_ERROR:
659 doread(fd, &h_errno, sizeof(h_errno));
660 fail = 0;
661 break;
662 case BRES_HOSTENT:
663 h = gethost(fd);
664 fail = 0;
665 break;
666 }
667 }
668 if (rc) {
669 rc->func(h, rc->p);
670 if (rc->q == BRES_BYNAME)
e2a18bd0 671 xfree(rc->u.name);
a759efa6 672 }
673 if (h)
d8fe3fc2 674 free(h);
a759efa6 675 if (fail)
676 zap(rs);
677 if (!rc)
678 return;
679
680 /* --- Wrap up the various structures --- */
681
682 rs->rc = 0;
683 rc->rs = 0;
ffb41122 684 LINKHEAD(freelist, freetail, rs);
a759efa6 685
686 /* --- Tie a timer onto the server block --- */
687
688 {
689 struct timeval tv;
690
691 gettimeofday(&tv, 0);
692 tv.tv_sec += BRES_IDLE;
693 sel_addtimer(sel, &rs->t, &tv, idle, rs);
694 }
695
696 /* --- If there are any clients waiting, attach one --- */
697
ffb41122
MW
698 if (qhead) {
699 rc = qhead;
700 UNLINK(qhead, qtail, rc);
a759efa6 701 attach(rc);
702 }
703}
704
705/* --- @start@ --- *
706 *
707 * Arguments: @bres_server *rs@ = pointer to a server block
708 *
709 * Returns: Zero if OK, nonzero if something failed.
710 *
711 * Use: Starts up a child resolver process.
712 */
713
714static int start(bres_server *rs)
715{
716 int rfd[2], cfd[2];
717 pid_t kid;
ff1e93ac 718 mdup_fd md[2];
a759efa6 719
720 /* --- Make the pipes --- */
721
722 if (pipe(rfd))
723 goto fail_0;
724 if (pipe(cfd))
725 goto fail_1;
726
727 /* --- Start up the child process --- */
728
729 if ((kid = fork()) < 0)
730 goto fail_2;
731 if (kid == 0) {
732 close(cfd[1]);
733 close(rfd[0]);
734
735 if (server) {
ff1e93ac
MW
736 md[0].cur = cfd[0]; md[0].want = STDIN_FILENO;
737 md[1].cur = rfd[1]; md[1].want = STDOUT_FILENO;
738 if (mdup(md, 2) || execlp(server, server, (char *)0))
739 child(STDOUT_FILENO, STDIN_FILENO);
a759efa6 740 } else
741 child(rfd[1], cfd[0]);
742 _exit(1);
743 }
744
745 /* --- Fix up everything in the server block --- */
746
747 close(cfd[0]);
748 close(rfd[1]);
749 rs->fd = cfd[1];
750 sel_initfile(sel, &rs->f, rfd[0], SEL_READ, answer, rs);
751 rs->kid = kid;
752 return (0);
753
754 /* --- Fix up after errors --- */
755
756fail_2:
757 close(cfd[0]);
758 close(cfd[1]);
759fail_1:
760 close(rfd[0]);
761 close(rfd[1]);
762fail_0:
763 return (-1);
764}
765
766/* --- @attach@ --- *
767 *
768 * Arguments: @bres_client *rc@ = pointer to a client block
769 *
770 * Returns: ---
771 *
772 * Use: Attaches a client to a spare server (which is assumed to
773 * exist).
774 */
775
776static void attach(bres_client *rc)
777{
778 bres_server *rs;
779 int lose = 0;
780
781 /* --- Fix up the server ready for the job --- *
782 *
783 * If the server has a process, remove its timer. Otherwise, fork off a
784 * new resolver process. This is also where I go if I find that the child
785 * resolver process has lost while I wasn't looking. Only one attempt at
786 * forking is performed.
787 */
788
789again:
ffb41122 790 rs = freelist;
a759efa6 791 if (rs->kid != -1)
792 sel_rmtimer(&rs->t);
793 else {
794 if (lose || start(rs))
795 goto lost;
796 lose = 1;
797 }
798
799 /* --- Submit the job to the resolver --- */
800
801 {
802 struct sigaction sa, osa;
803 int e;
804
805 /* --- Ignore @SIGPIPE@ for now --- *
806 *
807 * This way I can trap @EPIPE@ and reap a losing child, if there was one.
808 */
809
810 sa.sa_handler = SIG_IGN;
811 sa.sa_flags = 0;
812 sigemptyset(&sa.sa_mask);
813 sigaction(SIGPIPE, &sa, &osa);
814
815 /* --- Write the new job to the child --- */
816
817 e = 0;
818 if (dowrite(rs->fd, &rc->q, sizeof(rc->q)))
819 e = errno;
820 else switch (rc->q) {
821 case BRES_BYADDR:
822 if (dowrite(rs->fd, &rc->u.addr, sizeof(rc->u.addr)))
823 e = errno;
824 break;
825 case BRES_BYNAME:
826 if (putstring(rs->fd, rc->u.name))
827 e = errno;
828 break;
829 }
830 sigaction(SIGPIPE, &osa, 0);
831
832 /* --- Sort out various errors --- *
833 *
834 * This was once more complicated, handling @EPIPE@ separately from other
835 * errors. Now everything's handled the same way.
836 */
837
838 if (e) {
839 zap(rs);
840 goto again;
841 }
842 }
843
844 /* --- Fiddle with lists so that everything's OK --- */
845
846 sel_addfile(&rs->f);
ffb41122 847 UNLINK(freelist, freetail, rs);
a759efa6 848 rs->rc = rc;
849 rc->rs = rs;
850 return;
851
852lost:
853 rc->func(0, rc->p);
854 if (rc->q == BRES_BYNAME)
e2a18bd0 855 xfree(rc->u.name);
a759efa6 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
867static void resolve(bres_client *rc)
868{
869 /* --- If there's a free server, plug it in --- */
870
871 rc->rs = 0;
ffb41122 872 if (freelist)
a759efa6 873 attach(rc);
ffb41122
MW
874 else
875 LINKTAIL(qhead, qtail, rc);
a759efa6 876}
877
878/* --- @bres_byaddr@ --- *
879 *
880 * Arguments: @bres_client *rc@ = pointer to client block
881 * @struct in_addr addr@ = address to resolve
882 * @void (*func)(struct hostent *h, void *p)@ = handler function
883 * @void *p@ = argument for handler function
884 *
885 * Returns: ---
886 *
887 * Use: Adds an address lookup job to the queue. The job will be
888 * processed when there's a spare resolver process to deal with
889 * it.
890 */
891
892void bres_byaddr(bres_client *rc, struct in_addr addr,
893 void (*func)(struct hostent */*h*/, void */*p*/),
894 void *p)
895{
896 rc->q = BRES_BYADDR;
897 rc->u.addr = addr;
898 rc->func = func;
899 rc->p = p;
900 resolve(rc);
901}
902
903/* --- @bres_byname@ --- *
904 *
905 * Arguments: @bres_client *rc@ = pointer to client block
906 * @const char *name@ = name to resolve
907 * @void (*func)(struct hostent *h, void *p)@ = handler function
908 * @void *p@ = argument for handler function
909 *
910 * Returns: ---
911 *
912 * Use: Adds a name lookup job to the queue. The job will be
913 * processed when there's a spare resolver process to deal with
914 * it.
915 */
916
917void bres_byname(bres_client *rc, const char *name,
918 void (*func)(struct hostent */*h*/, void */*p*/),
919 void *p)
920{
921 rc->q = BRES_BYNAME;
922 rc->u.name = xstrdup(name);
923 rc->func = func;
924 rc->p = p;
925 resolve(rc);
926}
927
928/* --- @bres_exec@ --- *
929 *
930 * Arguments: @const char *file@ = file containing server code or null
931 *
932 * Returns: ---
933 *
934 * Use: Makes `bres' use a standalone server rather than copies of
935 * the current process. This can reduce memory consumption for
936 * large processes, at the expense of startup time (which
937 * shouldn't be too bad anyway, because of the resolver design).
938 * If the filename is null, a default set up at install time is
939 * used. It's probably a good idea to leave it alone.
940 */
941
942void bres_exec(const char *file)
943{
944 if (file)
945 server = file;
946 else
947 server = BRES_SERVER;
948}
949
950/* --- @bres_init@ --- *
951 *
952 * Arguments: @sel_state *s@ = pointer to select multiplexor
953 *
954 * Returns: ---
955 *
956 * Use: Initializes the background resolver for use.
957 */
958
959void bres_init(sel_state *s)
960{
961 int i;
962
963 sel = s;
964 for (i = 0; i < BRES_MAX; i++) {
a759efa6 965 servers[i].kid = -1;
966 servers[i].rc = 0;
ffb41122 967 LINKTAIL(freelist, freetail, &servers[i]);
a759efa6 968 }
969}
970
971#endif
972
973/*----- That's all, folks -------------------------------------------------*/