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 | |
88 | static bres_server servers[BRES_MAX]; /* Statically allocated servers */ |
89 | |
90 | #define FREE ((bres_server *)&freelist) |
91 | static struct { bres_server *next, *prev; } freelist = { FREE, FREE }; |
92 | |
93 | #define QUEUE ((bres_client *)&queue) |
94 | static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE }; |
95 | |
96 | static sel_state *sel; |
97 | |
98 | static 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 | |
138 | typedef 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 | |
158 | static 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 | |
177 | static 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 | |
205 | static 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 | |
230 | static 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 | |
250 | static 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 | |
350 | tidy_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 | } |
357 | tidy_1: |
d8fe3fc2 |
358 | free(name); |
a759efa6 |
359 | tidy_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 | |
375 | static 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 | |
426 | static 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 | |
541 | lose: |
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 | |
558 | int 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 | |
591 | static 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 | |
622 | void 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 | |
646 | static 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 | |
663 | static void attach(bres_client */*rc*/); |
664 | |
665 | static 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 | |
739 | static 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 | |
782 | fail_2: |
783 | close(cfd[0]); |
784 | close(cfd[1]); |
785 | fail_1: |
786 | close(rfd[0]); |
787 | close(rfd[1]); |
788 | fail_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 | |
802 | static 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 | |
815 | again: |
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 | |
880 | lost: |
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 | |
895 | static 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 | |
923 | void 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 | |
948 | void 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 | |
973 | void 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 | |
990 | void 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 -------------------------------------------------*/ |