chiark / gitweb /
Install and distribute new manual page.
[fwd] / bres.c
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
68 static bres_server servers[BRES_MAX];   /* Statically allocated servers */
69
70 #define FREE ((bres_server *)&freelist)
71 static struct { bres_server *next, *prev; } freelist = { FREE, FREE };
72
73 #define QUEUE ((bres_client *)&queue)
74 static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE };
75
76 static 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
90 static 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
122 void 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
143 static 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
189 static 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
205 static void attach(bres_client */*rc*/);
206
207 static 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
259 static 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
293 fail_2:
294   close(cfd[0]);
295   close(cfd[1]);
296 fail_1:
297   close(rfd[0]);
298   close(rfd[1]);
299 fail_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
313 static 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
326 again:
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
377 lost:
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
394 void 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
424 void 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 -------------------------------------------------*/