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