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 | |
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); |
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 | |
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 -------------------------------------------------*/ |