Minor modifications for new design.
[fwd] / bres.c
1 /* -*-c-*-
2 *
3 * $Id: bres.c,v 1.2 1999/07/03 13:56:04 mdw Exp $
4 *
5 * Background reverse name resolution
6 *
7 * (c) 1999 Mark Wooding
8 */
9
10 /*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of the `fw' port forwarder.
13 *
14 * `fw' is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * `fw' 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 General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with `fw'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29 /*----- Revision history --------------------------------------------------*
30 *
31 * $Log: bres.c,v $
32 * Revision 1.2 1999/07/03 13:56:04 mdw
33 * Perform a forward resolution to verify result of reverse lookup.
34 *
35 * Revision 1.1.1.1 1999/07/01 08:56:23 mdw
36 * Initial revision.
37 *
38 */
39
40 /*----- Header files ------------------------------------------------------*/
41
42 #include <errno.h>
43 #include <signal.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47
48 #include <sys/types.h>
49 #include <sys/time.h>
50 #include <unistd.h>
51 #include <sys/wait.h>
52
53 #include <sys/socket.h>
54 #include <netinet/in.h>
55 #include <arpa/inet.h>
56 #include <netdb.h>
57
58 #include <mLib/alloc.h>
59 #include <mLib/report.h>
60 #include <mLib/sel.h>
61 #include <mLib/selbuf.h>
62
63 #include "bres.h"
64
65 /*----- Magic numbers -----------------------------------------------------*/
66
67 #define BRES_MAX 5 /* Maximum number of resolvers */
68 #define BRES_IDLE 60 /* Lifetime of an idle resolver */
69
70 /*----- Static variables --------------------------------------------------*/
71
72 static bres_server servers[BRES_MAX]; /* Statically allocated servers */
73
74 #define FREE ((bres_server *)&freelist)
75 static struct { bres_server *next, *prev; } freelist = { FREE, FREE };
76
77 #define QUEUE ((bres_client *)&queue)
78 static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE };
79
80 static sel_state *sel;
81
82 /*----- Main code ---------------------------------------------------------*/
83
84 /* --- @zap@ --- *
85 *
86 * Arguments: @bres_server *rs@ = pointer to server block
87 *
88 * Returns: ---
89 *
90 * Use: Kills a server process, reaps the losing child and makes
91 * things generally clean again.
92 */
93
94 static void zap(bres_server *rs)
95 {
96 /* --- Close the pipes, kill the child, and reap it --- */
97
98 if (rs->kid != -1) {
99 selbuf_disable(&rs->b);
100 close(rs->fd);
101 close(rs->b.reader.fd);
102 kill(rs->kid, SIGTERM);
103 waitpid(rs->kid, 0, 0);
104 rs->kid = -1;
105 }
106
107 /* --- Move the server to the back of the list --- */
108
109 rs->next->prev = rs->prev;
110 rs->prev->next = rs->next;
111 rs->next = FREE;
112 rs->prev = FREE->prev;
113 FREE->prev->next = rs;
114 FREE->prev = rs;
115 }
116
117 /* --- @bres_abort@ --- *
118 *
119 * Arguments: @bres_client *rc@ = pointer to client block
120 *
121 * Returns: ---
122 *
123 * Use: Removes a queued job.
124 */
125
126 void bres_abort(bres_client *rc)
127 {
128 if (rc->rs) {
129 zap(rc->rs);
130 rc->rs = 0;
131 } else {
132 rc->next->prev = rc->prev;
133 rc->prev->next = rc->next;
134 }
135 }
136
137 /* --- @child@ --- *
138 *
139 * Arguments: @int rfd@ = output file descriptor for resolved hostnames
140 * @int cfd@ = input file descriptor for raw addresses
141 *
142 * Returns: Never.
143 *
144 * Use: Asynchronous name resolving process.
145 */
146
147 static void child(int rfd, int cfd)
148 {
149 struct in_addr addr;
150 FILE *fp = fdopen(rfd, "w");
151
152 {
153 int i;
154 int maxfd = sysconf(_SC_OPEN_MAX);
155
156 for (i = 0; i < maxfd; i++) {
157 if (i != rfd && i != cfd)
158 close(i);
159 }
160 }
161
162 for (;;) {
163 int r = read(cfd, &addr, sizeof(addr));
164 struct hostent *h;
165 char *p;
166
167 if (r <= 0)
168 break;
169 else if (r != sizeof(addr))
170 continue;
171
172 h = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
173 if (h) {
174 char **pp;
175
176 p = xstrdup(h->h_name);
177 h = gethostbyname(p);
178 free(p);
179 p = 0;
180 if (h) {
181 for (pp = h->h_addr_list; *pp; pp++) {
182 struct in_addr a;
183 memcpy(&a, *pp, sizeof(a));
184 if (a.s_addr == addr.s_addr) {
185 p = h->h_name;
186 break;
187 }
188 }
189 }
190 }
191
192 if (!p)
193 p = inet_ntoa(addr);
194 fprintf(fp, "%s\n", p);
195 fflush(fp);
196 }
197 _exit(0);
198 }
199
200 /* --- @idle@ --- *
201 *
202 * Arguments: @struct timeval *tv@ = pointer to the current time
203 * @void *vp@ = pointer to a server block
204 *
205 * Returns: ---
206 *
207 * Use: Kills off a child which has been idle for too long.
208 */
209
210 static void idle(struct timeval *tv, void *vp)
211 {
212 bres_server *rs = vp;
213 zap(rs);
214 }
215
216 /* --- @answer@ --- *
217 *
218 * Arguments: @char *p@ = pointer to string read
219 * @void *vp@ = pointer to server block
220 *
221 * Returns: ---
222 *
223 * Use: Retrieves an answer from a name resolver process.
224 */
225
226 static void attach(bres_client */*rc*/);
227
228 static void answer(char *p, void *vp)
229 {
230 bres_server *rs = vp;
231 bres_client *rc = rs->rc;
232
233 /* --- Report the result to my client --- */
234
235 if (rc)
236 rc->func(p, rc->p);
237 if (!p)
238 zap(rs);
239 if (!rc)
240 return;
241
242 /* --- Wrap up the various structures --- */
243
244 rs->rc = 0;
245 rc->rs = 0;
246 rs->next = FREE->next;
247 rs->prev = FREE;
248 FREE->next->prev = rs;
249 FREE->next = rs;
250
251 /* --- Tie a timer onto the server block --- */
252
253 {
254 struct timeval tv;
255
256 gettimeofday(&tv, 0);
257 tv.tv_sec += BRES_IDLE;
258 sel_addtimer(sel, &rs->t, &tv, idle, rs);
259 }
260
261 /* --- If there are any clients waiting, attach one --- */
262
263 if (QUEUE->next != QUEUE) {
264 rc = QUEUE->next;
265 QUEUE->next = rc->next;
266 rc->next->prev = QUEUE;
267 attach(rc);
268 }
269 }
270
271 /* --- @start@ --- *
272 *
273 * Arguments: @bres_server *rs@ = pointer to a server block
274 *
275 * Returns: Zero if OK, nonzero if something failed.
276 *
277 * Use: Starts up a child resolver process.
278 */
279
280 static int start(bres_server *rs)
281 {
282 int rfd[2], cfd[2];
283 pid_t kid;
284
285 /* --- Make the pipes --- */
286
287 if (pipe(rfd))
288 goto fail_0;
289 if (pipe(cfd))
290 goto fail_1;
291
292 /* --- Start up the child process --- */
293
294 if ((kid = fork()) < 0)
295 goto fail_2;
296 if (kid == 0) {
297 close(cfd[1]);
298 close(rfd[0]);
299 child(rfd[1], cfd[0]);
300 _exit(1);
301 }
302
303 /* --- Fix up everything in the server block --- */
304
305 close(cfd[0]);
306 close(rfd[1]);
307 rs->fd = cfd[1];
308 selbuf_init(&rs->b, sel, rfd[0], answer, rs);
309 rs->kid = kid;
310 return (0);
311
312 /* --- Fix up after errors --- */
313
314 fail_2:
315 close(cfd[0]);
316 close(cfd[1]);
317 fail_1:
318 close(rfd[0]);
319 close(rfd[1]);
320 fail_0:
321 return (-1);
322 }
323
324 /* --- @attach@ --- *
325 *
326 * Arguments: @bres_client *rc@ = pointer to a client block
327 *
328 * Returns: ---
329 *
330 * Use: Attaches a client to a spare server (which is assumed to
331 * exist).
332 */
333
334 static void attach(bres_client *rc)
335 {
336 bres_server *rs;
337 int lose = 0;
338
339 /* --- Fix up the server ready for the job --- *
340 *
341 * If the server has a process, remove its timer. Otherwise, fork off a
342 * new resolver process. This is also where I go if I find that the child
343 * resolver process has lost while I wasn't looking. Only one attempt at
344 * forking is performed.
345 */
346
347 again:
348 rs = FREE->next;
349 if (rs->kid != -1)
350 sel_rmtimer(&rs->t);
351 else {
352 if (lose || start(rs))
353 goto lost;
354 lose = 1;
355 }
356
357 /* --- Submit the job to the resolver --- */
358
359 {
360 struct sigaction sa, osa;
361 int e;
362
363 /* --- Ignore @SIGPIPE@ for now --- *
364 *
365 * This way I can trap @EPIPE@ and reap a losing child, if there was one.
366 */
367
368 sa.sa_handler = SIG_IGN;
369 sa.sa_flags = 0;
370 sigemptyset(&sa.sa_mask);
371 sigaction(SIGPIPE, &sa, &osa);
372
373 /* --- Write the new job to the child --- */
374
375 e = 0;
376 if (write(rs->fd, &rc->addr, sizeof(rc->addr)) < 0)
377 e = errno;
378 sigaction(SIGPIPE, &osa, 0);
379
380 /* --- Sort out various errors --- */
381
382 if (e == EPIPE) {
383 zap(rs);
384 goto again;
385 } else if (e)
386 goto lost;
387 }
388
389 /* --- Fiddle with lists so that everything's OK --- */
390
391 rs->next->prev = FREE;
392 FREE->next = rs->next;
393 rs->next = rs->prev = rs;
394 rs->rc = rc;
395 rc->rs = rs;
396 return;
397
398 lost:
399 rc->func(0, rc->p);
400 }
401
402 /* --- @bres_resolve@ --- *
403 *
404 * Arguments: @bres_client *rc@ = pointer to client block
405 * @struct in_addr addr@ = address to resolve
406 * @void (*func)(const char *host, void *p)@ = handler function
407 * @void *p@ = argument for handler function
408 *
409 * Returns: ---
410 *
411 * Use: Adds a resolver job to the queue. The job will be processed
412 * when there's a spare resolver process to deal with it.
413 */
414
415 void bres_resolve(bres_client *rc, struct in_addr addr,
416 void (*func)(const char */*host*/, void */*p*/), void *p)
417 {
418 /* --- Fill in the structure --- */
419
420 rc->addr = addr;
421 rc->func = func;
422 rc->p = p;
423 rc->rs = 0;
424
425 /* --- If there's a free server, plug it in --- */
426
427 if (FREE->next == FREE) {
428 rc->next = QUEUE;
429 rc->prev = QUEUE->prev;
430 QUEUE->prev->next = rc;
431 QUEUE->prev = rc;
432 } else
433 attach(rc);
434 }
435
436 /* --- @bres_init@ --- *
437 *
438 * Arguments: @sel_state *s@ = pointer to select multiplexor
439 *
440 * Returns: ---
441 *
442 * Use: Initializes the background resolver for use.
443 */
444
445 void bres_init(sel_state *s)
446 {
447 int i;
448
449 sel = s;
450 for (i = 0; i < BRES_MAX; i++) {
451 servers[i].next = FREE;
452 servers[i].prev = FREE->prev;
453 servers[i].kid = -1;
454 servers[i].rc = 0;
455 FREE->prev->next = &servers[i];
456 FREE->prev = &servers[i];
457 }
458 }
459
460 /*----- That's all, folks -------------------------------------------------*/