chiark / gitweb /
Perform a forward resolution to verify result of reverse lookup.
[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 -------------------------------------------------*/