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