chiark / gitweb /
New socket address types.
[fwd] / bres.c
1 /* -*-c-*-
2  *
3  * $Id: bres.c,v 1.3 1999/07/26 23:27:22 mdw Exp $
4  *
5  * Background reverse name resolution
6  *
7  * (c) 1999 Straylight/Edgeware
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.3  1999/07/26 23:27:22  mdw
33  * Change copyright notice.
34  *
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.
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
61 #include <mLib/alloc.h>
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
75 static bres_server servers[BRES_MAX];   /* Statically allocated servers */
76
77 #define FREE ((bres_server *)&freelist)
78 static struct { bres_server *next, *prev; } freelist = { FREE, FREE };
79
80 #define QUEUE ((bres_client *)&queue)
81 static struct { bres_client *next, *prev; } queue = { QUEUE, QUEUE };
82
83 static 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
97 static 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
129 void 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
150 static 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);
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)
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
213 static 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
229 static void attach(bres_client */*rc*/);
230
231 static 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
283 static 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
317 fail_2:
318   close(cfd[0]);
319   close(cfd[1]);
320 fail_1:
321   close(rfd[0]);
322   close(rfd[1]);
323 fail_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
337 static 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
350 again:
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
401 lost:
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
418 void 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
448 void 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 -------------------------------------------------*/